@superfan-app/spotify-auth 0.1.77 → 0.1.80

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superfan-app/spotify-auth",
3
- "version": "0.1.77",
3
+ "version": "0.1.80",
4
4
  "description": "Spotify OAuth module for Expo",
5
5
  "main": "src/index.tsx",
6
6
  "types": "build/index.d.ts",
@@ -111,8 +111,16 @@ const withSpotifyAndroidManifest = (config, props) => {
111
111
  if (!mainActivity['intent-filter']) {
112
112
  mainActivity['intent-filter'] = [];
113
113
  }
114
- // Check if we already have a Spotify redirect intent filter
115
- const hasSpotifyIntentFilter = mainActivity['intent-filter'].some((filter) => filter.data?.some((d) => d.$?.['android:scheme'] === props.scheme && d.$?.['android:host'] === callbackHost));
114
+ // Check if we already have an intent filter covering the Spotify redirect.
115
+ // A broad scheme-only filter (no android:host) already covers superfan://callback,
116
+ // so we must treat it as sufficient to avoid registering two overlapping filters
117
+ // (which causes Android to show the app twice in the disambiguation dialog).
118
+ const hasSpotifyIntentFilter = mainActivity['intent-filter'].some((filter) => filter.data?.some((d) => {
119
+ const scheme = d.$?.['android:scheme'];
120
+ const host = d.$?.['android:host'];
121
+ // Matches if same scheme + matching host, OR same scheme with no host restriction
122
+ return scheme === props.scheme && (!host || host === callbackHost);
123
+ }));
116
124
  if (!hasSpotifyIntentFilter) {
117
125
  // Build the data element: android:host must be the hostname only (no path).
118
126
  // If the callback has a path component (e.g. "auth/spotify"), add android:pathPrefix.
@@ -135,6 +143,26 @@ const withSpotifyAndroidManifest = (config, props) => {
135
143
  return config;
136
144
  });
137
145
  };
146
+ const withSpotifyManifestPlaceholders = (config, props) => {
147
+ return (0, config_plugins_1.withAppBuildGradle)(config, (config) => {
148
+ const { host: callbackHost, path: callbackPath } = parseCallback(props.callback);
149
+ const pathPattern = callbackPath ? callbackPath.replace(/\//g, '\\/') + '.*' : '.*';
150
+ const placeholderBlock = `manifestPlaceholders = [
151
+ redirectSchemeName: "${props.scheme}",
152
+ redirectHostName: "${callbackHost}",
153
+ redirectPathPattern: "${pathPattern}"
154
+ ]`;
155
+ if (config.modResults.contents.includes('manifestPlaceholders')) {
156
+ // Replace existing block
157
+ config.modResults.contents = config.modResults.contents.replace(/manifestPlaceholders\s*=\s*\[[\s\S]*?\]/, placeholderBlock);
158
+ }
159
+ else {
160
+ // Inject into defaultConfig
161
+ config.modResults.contents = config.modResults.contents.replace(/defaultConfig\s*\{/, `defaultConfig {\n ${placeholderBlock}`);
162
+ }
163
+ return config;
164
+ });
165
+ };
138
166
  // endregion
139
167
  const withSpotifyAuth = (config, props) => {
140
168
  // Ensure the config exists
@@ -147,6 +175,7 @@ const withSpotifyAuth = (config, props) => {
147
175
  config = withSpotifyURLSchemes(config, props);
148
176
  // Apply Android configurations
149
177
  config = withSpotifyAndroidManifest(config, props);
178
+ config = withSpotifyManifestPlaceholders(config, props);
150
179
  return config;
151
180
  };
152
181
  exports.default = (0, config_plugins_1.createRunOncePlugin)(withSpotifyAuth, pkg.name, pkg.version);
@@ -1,6 +1,6 @@
1
1
  // plugin/src/index.ts
2
2
 
3
- import { type ConfigPlugin, createRunOncePlugin, withInfoPlist, withAndroidManifest, AndroidConfig } from '@expo/config-plugins'
3
+ import { type ConfigPlugin, createRunOncePlugin, withInfoPlist, withAndroidManifest, withAppBuildGradle, AndroidConfig } from '@expo/config-plugins'
4
4
  import { SpotifyConfig } from './types.js'
5
5
 
6
6
  const pkg = require('../../package.json');
@@ -128,12 +128,18 @@ const withSpotifyAndroidManifest: ConfigPlugin<SpotifyConfig> = (config, props)
128
128
  mainActivity['intent-filter'] = [];
129
129
  }
130
130
 
131
- // Check if we already have a Spotify redirect intent filter
131
+ // Check if we already have an intent filter covering the Spotify redirect.
132
+ // A broad scheme-only filter (no android:host) already covers superfan://callback,
133
+ // so we must treat it as sufficient to avoid registering two overlapping filters
134
+ // (which causes Android to show the app twice in the disambiguation dialog).
132
135
  const hasSpotifyIntentFilter = mainActivity['intent-filter'].some(
133
136
  (filter: any) =>
134
- filter.data?.some(
135
- (d: any) => d.$?.['android:scheme'] === props.scheme && d.$?.['android:host'] === callbackHost
136
- )
137
+ filter.data?.some((d: any) => {
138
+ const scheme = d.$?.['android:scheme'];
139
+ const host = d.$?.['android:host'];
140
+ // Matches if same scheme + matching host, OR same scheme with no host restriction
141
+ return scheme === props.scheme && (!host || host === callbackHost);
142
+ })
137
143
  );
138
144
 
139
145
  if (!hasSpotifyIntentFilter) {
@@ -161,6 +167,35 @@ const withSpotifyAndroidManifest: ConfigPlugin<SpotifyConfig> = (config, props)
161
167
  });
162
168
  };
163
169
 
170
+ const withSpotifyManifestPlaceholders: ConfigPlugin<SpotifyConfig> = (config, props) => {
171
+ return withAppBuildGradle(config, (config) => {
172
+ const { host: callbackHost, path: callbackPath } = parseCallback(props.callback)
173
+ const pathPattern = callbackPath ? callbackPath.replace(/\//g, '\\/') + '.*' : '.*'
174
+
175
+ const placeholderBlock = `manifestPlaceholders = [
176
+ redirectSchemeName: "${props.scheme}",
177
+ redirectHostName: "${callbackHost}",
178
+ redirectPathPattern: "${pathPattern}"
179
+ ]`
180
+
181
+ if (config.modResults.contents.includes('manifestPlaceholders')) {
182
+ // Replace existing block
183
+ config.modResults.contents = config.modResults.contents.replace(
184
+ /manifestPlaceholders\s*=\s*\[[\s\S]*?\]/,
185
+ placeholderBlock
186
+ )
187
+ } else {
188
+ // Inject into defaultConfig
189
+ config.modResults.contents = config.modResults.contents.replace(
190
+ /defaultConfig\s*\{/,
191
+ `defaultConfig {\n ${placeholderBlock}`
192
+ )
193
+ }
194
+
195
+ return config
196
+ })
197
+ }
198
+
164
199
  // endregion
165
200
 
166
201
  const withSpotifyAuth: ConfigPlugin<SpotifyConfig> = (config, props) => {
@@ -179,6 +214,7 @@ const withSpotifyAuth: ConfigPlugin<SpotifyConfig> = (config, props) => {
179
214
 
180
215
  // Apply Android configurations
181
216
  config = withSpotifyAndroidManifest(config, props);
217
+ config = withSpotifyManifestPlaceholders(config, props);
182
218
 
183
219
  return config;
184
220
  };