@wwdrew/expo-spotify-sdk 0.6.0 → 0.7.1
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/README.md +14 -6
- package/android/src/main/java/expo/modules/spotifysdk/ExpoSpotifySDKModule.kt +1 -0
- package/android/src/main/java/expo/modules/spotifysdk/SpotifyAuthorizationContract.kt +1 -0
- package/android/src/main/java/expo/modules/spotifysdk/SpotifyConfig.kt +5 -1
- package/build/ExpoSpotifySDK.types.d.ts +8 -0
- package/build/ExpoSpotifySDK.types.d.ts.map +1 -1
- package/build/ExpoSpotifySDK.types.js.map +1 -1
- package/ios/ExpoSpotifySDKModule.swift +3 -1
- package/ios/SpotifyAuthCoordinator.swift +55 -2
- package/package.json +1 -1
- package/plugin/build/android/withSpotifyAndroidAppBuildGradle.js +1 -1
- package/plugin/build/types.d.ts +6 -3
package/README.md
CHANGED
|
@@ -80,7 +80,7 @@ android {
|
|
|
80
80
|
spotifyRedirectUri: "myapp://spotify-auth",
|
|
81
81
|
redirectSchemeName: "myapp",
|
|
82
82
|
redirectHostName: "spotify-auth",
|
|
83
|
-
redirectPathPattern: "
|
|
83
|
+
redirectPathPattern: ".*"
|
|
84
84
|
]
|
|
85
85
|
}
|
|
86
86
|
}
|
|
@@ -107,16 +107,23 @@ export default {
|
|
|
107
107
|
clientID: "your-spotify-client-id",
|
|
108
108
|
scheme: "myapp",
|
|
109
109
|
host: "spotify-auth",
|
|
110
|
-
// Optional: path pattern accepted by the Spotify Android SDK redirect.
|
|
111
|
-
// Defaults to "/.*" (matches any path). Change this only if you have a
|
|
112
|
-
// specific redirect path registered in your Spotify app settings.
|
|
113
|
-
redirectPathPattern: "/.*",
|
|
114
110
|
},
|
|
115
111
|
],
|
|
116
112
|
],
|
|
117
113
|
};
|
|
118
114
|
```
|
|
119
115
|
|
|
116
|
+
`redirectPathPattern` is optional and defaults to `".*"`, which matches every redirect URI shape Spotify will hand back. Only set it if you have a specific path registered in your Spotify app settings:
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
{
|
|
120
|
+
clientID: "your-spotify-client-id",
|
|
121
|
+
scheme: "myapp",
|
|
122
|
+
host: "spotify-auth",
|
|
123
|
+
redirectPathPattern: "/auth/.*",
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
120
127
|
### Plugin options
|
|
121
128
|
|
|
122
129
|
| Option | Type | Required | Description |
|
|
@@ -124,7 +131,7 @@ export default {
|
|
|
124
131
|
| `clientID` | `string` | ✅ | Your Spotify application's Client ID |
|
|
125
132
|
| `scheme` | `string` | ✅ | URL scheme registered for your app (e.g. `"myapp"`) |
|
|
126
133
|
| `host` | `string` | ✅ | Host component of the redirect URI (e.g. `"spotify-auth"`) |
|
|
127
|
-
| `redirectPathPattern` | `string` | — | Android redirect path regex. Defaults to `"
|
|
134
|
+
| `redirectPathPattern` | `string` | — | Android redirect path regex. Defaults to `".*"` |
|
|
128
135
|
|
|
129
136
|
The redirect URI registered in your [Spotify Developer Dashboard](https://developer.spotify.com/dashboard) must match `{scheme}://{host}` exactly (e.g. `myapp://spotify-auth`).
|
|
130
137
|
|
|
@@ -194,6 +201,7 @@ Starts a Spotify OAuth flow. If the Spotify app is installed it authenticates na
|
|
|
194
201
|
| `scopes` | `SpotifyScope[]` | ✅ | OAuth scopes to request. Must contain at least one entry. |
|
|
195
202
|
| `tokenSwapURL` | `string` | — | URL of your token swap server endpoint. Triggers CODE flow (recommended). Required on Android to receive a `refreshToken`. |
|
|
196
203
|
| `tokenRefreshURL` | `string` | — | URL of your token refresh server endpoint. Used by iOS SDK natively and by `refreshSessionAsync` on both platforms. |
|
|
204
|
+
| `showDialog` | `boolean` | — | Force Spotify to show the authorization dialog even when the user already has an active session. Defaults to `false`. Useful during development; avoid in production. |
|
|
197
205
|
|
|
198
206
|
**Returns (`SpotifySession`):**
|
|
199
207
|
|
|
@@ -12,6 +12,7 @@ class SpotifyAuthenticateOptions : Record {
|
|
|
12
12
|
@Field val scopes: List<String> = emptyList()
|
|
13
13
|
@Field val tokenSwapURL: String? = null
|
|
14
14
|
@Field val tokenRefreshURL: String? = null
|
|
15
|
+
@Field val showDialog: Boolean = false
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
/**
|
|
@@ -60,6 +61,7 @@ data class SpotifyAuthInput(
|
|
|
60
61
|
val redirectUri: String,
|
|
61
62
|
val responseType: AuthorizationResponse.Type,
|
|
62
63
|
val scopes: Array<String>,
|
|
64
|
+
val showDialog: Boolean,
|
|
63
65
|
) : Serializable {
|
|
64
66
|
override fun equals(other: Any?): Boolean {
|
|
65
67
|
if (this === other) return true
|
|
@@ -67,7 +69,8 @@ data class SpotifyAuthInput(
|
|
|
67
69
|
return clientId == other.clientId &&
|
|
68
70
|
redirectUri == other.redirectUri &&
|
|
69
71
|
responseType == other.responseType &&
|
|
70
|
-
scopes.contentEquals(other.scopes)
|
|
72
|
+
scopes.contentEquals(other.scopes) &&
|
|
73
|
+
showDialog == other.showDialog
|
|
71
74
|
}
|
|
72
75
|
|
|
73
76
|
override fun hashCode(): Int {
|
|
@@ -75,6 +78,7 @@ data class SpotifyAuthInput(
|
|
|
75
78
|
result = 31 * result + redirectUri.hashCode()
|
|
76
79
|
result = 31 * result + responseType.hashCode()
|
|
77
80
|
result = 31 * result + scopes.contentHashCode()
|
|
81
|
+
result = 31 * result + showDialog.hashCode()
|
|
78
82
|
return result
|
|
79
83
|
}
|
|
80
84
|
}
|
|
@@ -46,6 +46,14 @@ export interface SpotifyConfig {
|
|
|
46
46
|
* `refreshSessionAsync` on both platforms.
|
|
47
47
|
*/
|
|
48
48
|
tokenRefreshURL?: string;
|
|
49
|
+
/**
|
|
50
|
+
* If `true`, forces Spotify to show the authorization dialog even when
|
|
51
|
+
* the user already has an active session. Defaults to `false`.
|
|
52
|
+
*
|
|
53
|
+
* Maps to `SPTSessionManager.alwaysShowAuthorizationDialog` on iOS and
|
|
54
|
+
* `AuthorizationRequest.Builder.setShowDialog(true)` on Android.
|
|
55
|
+
*/
|
|
56
|
+
showDialog?: boolean;
|
|
49
57
|
}
|
|
50
58
|
/**
|
|
51
59
|
* Spotify OAuth scope identifiers that are valid through the iOS, Android
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoSpotifySDK.types.d.ts","sourceRoot":"","sources":["../src/ExpoSpotifySDK.types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,WAAW,cAAc;IAC7B,0BAA0B;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,uDAAuD;IACvD,cAAc,EAAE,MAAM,CAAC;IACvB,+EAA+E;IAC/E,MAAM,EAAE,YAAY,EAAE,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,kEAAkE;IAClE,YAAY,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,gEAAgE;IAChE,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"ExpoSpotifySDK.types.d.ts","sourceRoot":"","sources":["../src/ExpoSpotifySDK.types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,WAAW,cAAc;IAC7B,0BAA0B;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,uDAAuD;IACvD,cAAc,EAAE,MAAM,CAAC;IACvB,+EAA+E;IAC/E,MAAM,EAAE,YAAY,EAAE,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,kEAAkE;IAClE,YAAY,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,gEAAgE;IAChE,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;GAGG;AACH,MAAM,MAAM,YAAY,GACpB,kBAAkB,GAClB,0BAA0B,GAC1B,4BAA4B,GAC5B,6BAA6B,GAC7B,oBAAoB,GACpB,WAAW,GACX,uBAAuB,GACvB,6BAA6B,GAC7B,yBAAyB,GACzB,wBAAwB,GACxB,oBAAoB,GACpB,kBAAkB,GAClB,eAAe,GACf,2BAA2B,GAC3B,qBAAqB,GACrB,mBAAmB,GACnB,iBAAiB,GACjB,mBAAmB,CAAC;AAExB;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GACxB,gBAAgB,GAChB,kBAAkB,GAClB,gBAAgB,GAChB,eAAe,GACf,mBAAmB,GACnB,wBAAwB,GACxB,uBAAuB,GACvB,YAAY,GACZ,SAAS,CAAC;AAEd;;;;;;GAMG;AACH,MAAM,MAAM,yBAAyB,GACjC;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,cAAc,CAAA;CAAE,GAChD;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,cAAc,CAAA;CAAE,GAC7C;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE;QAAE,IAAI,EAAE,gBAAgB,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC;AAE5E;;;GAGG;AACH,qBAAa,YAAa,SAAQ,KAAK;IACrC,SAAgB,IAAI,EAAE,gBAAgB,CAAC;gBAE3B,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM;CAKpD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoSpotifySDK.types.js","sourceRoot":"","sources":["../src/ExpoSpotifySDK.types.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ExpoSpotifySDK.types.js","sourceRoot":"","sources":["../src/ExpoSpotifySDK.types.ts"],"names":[],"mappings":"AA+GA;;;GAGG;AACH,MAAM,OAAO,YAAa,SAAQ,KAAK;IACrB,IAAI,CAAmB;IAEvC,YAAY,IAAsB,EAAE,OAAe;QACjD,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;QAC3B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF","sourcesContent":["/**\n * Result of a successful Spotify authentication.\n *\n * The shape is identical across iOS and Android. On the Android implicit\n * (TOKEN) flow `refreshToken` is `null` and `scopes` reflects what was\n * *requested*, not granted — see the README's \"Android implicit flow is not\n * recommended\" section.\n */\nexport interface SpotifySession {\n /** OAuth access token. */\n accessToken: string;\n /**\n * OAuth refresh token. `null` on Android when no `tokenSwapURL` is provided\n * (the Spotify Android SDK does not expose a refresh token for implicit\n * grants — see the README).\n */\n refreshToken: string | null;\n /** Expiration timestamp as Unix epoch milliseconds. */\n expirationDate: number;\n /** Scopes the access token was granted (or requested, on Android implicit). */\n scopes: SpotifyScope[];\n}\n\n/**\n * Configuration accepted by `refreshSessionAsync`.\n */\nexport interface SpotifyRefreshConfig {\n /** The refresh token from a previous `authenticateAsync` call. */\n refreshToken: string;\n /** URL of your token refresh server endpoint. */\n tokenRefreshURL: string;\n}\n\n/**\n * Configuration accepted by `authenticateAsync`.\n */\nexport interface SpotifyConfig {\n /** OAuth scopes to request. Must contain at least one entry. */\n scopes: SpotifyScope[];\n /**\n * If supplied, requests an authorization code rather than an implicit\n * token, then POSTs the code to this URL to exchange it for tokens.\n * **Required on Android** to receive a usable `refreshToken`.\n */\n tokenSwapURL?: string;\n /**\n * Used by the iOS SDK to refresh access tokens automatically, and by\n * `refreshSessionAsync` on both platforms.\n */\n tokenRefreshURL?: string;\n /**\n * If `true`, forces Spotify to show the authorization dialog even when\n * the user already has an active session. Defaults to `false`.\n *\n * Maps to `SPTSessionManager.alwaysShowAuthorizationDialog` on iOS and\n * `AuthorizationRequest.Builder.setShowDialog(true)` on Android.\n */\n showDialog?: boolean;\n}\n\n/**\n * Spotify OAuth scope identifiers that are valid through the iOS, Android\n * and Web auth flows. See https://developer.spotify.com/documentation/web-api/concepts/scopes\n */\nexport type SpotifyScope =\n | \"ugc-image-upload\"\n | \"user-read-playback-state\"\n | \"user-modify-playback-state\"\n | \"user-read-currently-playing\"\n | \"app-remote-control\"\n | \"streaming\"\n | \"playlist-read-private\"\n | \"playlist-read-collaborative\"\n | \"playlist-modify-private\"\n | \"playlist-modify-public\"\n | \"user-follow-modify\"\n | \"user-follow-read\"\n | \"user-top-read\"\n | \"user-read-recently-played\"\n | \"user-library-modify\"\n | \"user-library-read\"\n | \"user-read-email\"\n | \"user-read-private\";\n\n/**\n * JS-side error code constants thrown via `Promise.reject(new Error(...))`\n * by the native modules.\n */\nexport type SpotifyErrorCode =\n | \"USER_CANCELLED\"\n | \"AUTH_IN_PROGRESS\"\n | \"INVALID_CONFIG\"\n | \"NETWORK_ERROR\"\n | \"TOKEN_SWAP_FAILED\"\n | \"TOKEN_SWAP_PARSE_ERROR\"\n | \"SPOTIFY_NOT_INSTALLED\"\n | \"AUTH_ERROR\"\n | \"UNKNOWN\";\n\n/**\n * Payload delivered to `addSessionChangeListener` subscribers.\n *\n * - `didInitiate` — a new session was created by `authenticateAsync`\n * - `didRenew` — an existing session was refreshed by `refreshSessionAsync`\n * - `didFail` — an auth or refresh attempt failed\n */\nexport type SpotifySessionChangeEvent =\n | { type: \"didInitiate\"; session: SpotifySession }\n | { type: \"didRenew\"; session: SpotifySession }\n | { type: \"didFail\"; error: { code: SpotifyErrorCode; message: string } };\n\n/**\n * Error subclass thrown by `authenticateAsync` and `refreshSessionAsync`\n * carrying a structured `code` field for branching.\n */\nexport class SpotifyError extends Error {\n public readonly code: SpotifyErrorCode;\n\n constructor(code: SpotifyErrorCode, message: string) {\n super(message);\n this.name = \"SpotifyError\";\n this.code = code;\n }\n}\n"]}
|
|
@@ -28,7 +28,8 @@ public class ExpoSpotifySDKModule: Module {
|
|
|
28
28
|
let session = try await coordinator.authenticate(
|
|
29
29
|
scopes: scopes,
|
|
30
30
|
tokenSwapURL: config.tokenSwapURL.flatMap(URL.init),
|
|
31
|
-
tokenRefreshURL: config.tokenRefreshURL.flatMap(URL.init)
|
|
31
|
+
tokenRefreshURL: config.tokenRefreshURL.flatMap(URL.init),
|
|
32
|
+
showDialog: config.showDialog
|
|
32
33
|
)
|
|
33
34
|
let map = self.sessionToMap(session)
|
|
34
35
|
self.sendEvent(EVENT_SESSION_CHANGE, ["type": "didInitiate", "session": map])
|
|
@@ -99,6 +100,7 @@ struct AuthenticateOptions: Record {
|
|
|
99
100
|
@Field var scopes: [String] = []
|
|
100
101
|
@Field var tokenSwapURL: String? = nil
|
|
101
102
|
@Field var tokenRefreshURL: String? = nil
|
|
103
|
+
@Field var showDialog: Bool = false
|
|
102
104
|
}
|
|
103
105
|
|
|
104
106
|
struct RefreshOptions: Record {
|
|
@@ -78,7 +78,8 @@ actor SpotifyAuthCoordinator {
|
|
|
78
78
|
func authenticate(
|
|
79
79
|
scopes: SPTScope,
|
|
80
80
|
tokenSwapURL: URL?,
|
|
81
|
-
tokenRefreshURL: URL
|
|
81
|
+
tokenRefreshURL: URL?,
|
|
82
|
+
showDialog: Bool = false
|
|
82
83
|
) async throws -> SPTSession {
|
|
83
84
|
guard pending == nil else { throw SpotifyError.authInProgress }
|
|
84
85
|
|
|
@@ -86,6 +87,17 @@ actor SpotifyAuthCoordinator {
|
|
|
86
87
|
// mutating it here takes effect for the upcoming initiateSession call.
|
|
87
88
|
sptConfiguration.tokenSwapURL = tokenSwapURL
|
|
88
89
|
sptConfiguration.tokenRefreshURL = tokenRefreshURL
|
|
90
|
+
// alwaysShowAuthorizationDialog is a property on SPTSessionManager, not an
|
|
91
|
+
// SPTAuthorizationOptions flag (the options enum has no such case).
|
|
92
|
+
sessionManager.alwaysShowAuthorizationDialog = showDialog
|
|
93
|
+
|
|
94
|
+
NSLog(
|
|
95
|
+
"[ExpoSpotifySDK] initiateSession redirectURL=%@ tokenSwapURL=%@ tokenRefreshURL=%@ showDialog=%d",
|
|
96
|
+
sptConfiguration.redirectURL.absoluteString,
|
|
97
|
+
tokenSwapURL?.absoluteString ?? "nil",
|
|
98
|
+
tokenRefreshURL?.absoluteString ?? "nil",
|
|
99
|
+
showDialog ? 1 : 0
|
|
100
|
+
)
|
|
89
101
|
|
|
90
102
|
return try await withCheckedThrowingContinuation { (cont: CheckedContinuation<SPTSession, Error>) in
|
|
91
103
|
self.pending = cont
|
|
@@ -109,15 +121,28 @@ final class SpotifySessionDelegateBridge: NSObject, SPTSessionManagerDelegate {
|
|
|
109
121
|
weak var coordinator: SpotifyAuthCoordinator?
|
|
110
122
|
|
|
111
123
|
func sessionManager(manager _: SPTSessionManager, didInitiate session: SPTSession) {
|
|
124
|
+
NSLog(
|
|
125
|
+
"[ExpoSpotifySDK] didInitiate accessToken.length=%d refreshToken.length=%d expirationDate=%@ scope=%lu",
|
|
126
|
+
session.accessToken.count,
|
|
127
|
+
session.refreshToken.count,
|
|
128
|
+
session.expirationDate as NSDate,
|
|
129
|
+
session.scope.rawValue
|
|
130
|
+
)
|
|
112
131
|
Task { await coordinator?.deliver(.success(session)) }
|
|
113
132
|
}
|
|
114
133
|
|
|
115
134
|
func sessionManager(manager _: SPTSessionManager, didFailWith error: Error) {
|
|
116
135
|
let mapped: Error = mapSDKError(error)
|
|
136
|
+
NSLog("[ExpoSpotifySDK] didFailWithError %@", String(describing: error))
|
|
117
137
|
Task { await coordinator?.deliver(.failure(mapped)) }
|
|
118
138
|
}
|
|
119
139
|
|
|
120
140
|
func sessionManager(manager _: SPTSessionManager, didRenew session: SPTSession) {
|
|
141
|
+
NSLog(
|
|
142
|
+
"[ExpoSpotifySDK] didRenew accessToken.length=%d refreshToken.length=%d",
|
|
143
|
+
session.accessToken.count,
|
|
144
|
+
session.refreshToken.count
|
|
145
|
+
)
|
|
121
146
|
Task { await coordinator?.deliver(.success(session)) }
|
|
122
147
|
}
|
|
123
148
|
|
|
@@ -128,6 +153,34 @@ final class SpotifySessionDelegateBridge: NSObject, SPTSessionManagerDelegate {
|
|
|
128
153
|
if description.contains("cancel") {
|
|
129
154
|
return SpotifyError.userCancelled
|
|
130
155
|
}
|
|
131
|
-
return SpotifyError.underlying(
|
|
156
|
+
return SpotifyError.underlying(NSError(
|
|
157
|
+
domain: nsError.domain,
|
|
158
|
+
code: nsError.code,
|
|
159
|
+
userInfo: [NSLocalizedDescriptionKey: describeError(nsError)]
|
|
160
|
+
))
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/// Build a diagnostic string from an NSError that includes the domain,
|
|
164
|
+
/// code, localized description, and the full chain of underlying errors.
|
|
165
|
+
/// Used because `SPTError` (and many `URLSession` errors it wraps) often
|
|
166
|
+
/// have an empty `localizedDescription`, surfacing as "undefined reason"
|
|
167
|
+
/// in JS without this expansion.
|
|
168
|
+
private func describeError(_ error: NSError) -> String {
|
|
169
|
+
var parts: [String] = []
|
|
170
|
+
parts.append("\(error.domain) code \(error.code)")
|
|
171
|
+
let desc = error.localizedDescription
|
|
172
|
+
if !desc.isEmpty {
|
|
173
|
+
parts.append("\"\(desc)\"")
|
|
174
|
+
}
|
|
175
|
+
var ui = error.userInfo
|
|
176
|
+
ui.removeValue(forKey: NSUnderlyingErrorKey)
|
|
177
|
+
ui.removeValue(forKey: NSLocalizedDescriptionKey)
|
|
178
|
+
if !ui.isEmpty {
|
|
179
|
+
parts.append("userInfo=\(ui)")
|
|
180
|
+
}
|
|
181
|
+
if let underlying = error.userInfo[NSUnderlyingErrorKey] as? NSError {
|
|
182
|
+
parts.append("→ underlying: \(describeError(underlying))")
|
|
183
|
+
}
|
|
184
|
+
return parts.joined(separator: " ")
|
|
132
185
|
}
|
|
133
186
|
}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.withSpotifyAndroidAppBuildGradle = void 0;
|
|
4
4
|
const config_plugins_1 = require("@expo/config-plugins");
|
|
5
5
|
const SENTINEL_KEY = "spotifyClientId";
|
|
6
|
-
const DEFAULT_REDIRECT_PATH_PATTERN = "
|
|
6
|
+
const DEFAULT_REDIRECT_PATH_PATTERN = ".*";
|
|
7
7
|
const withSpotifyAndroidAppBuildGradle = (config, spotifyConfig) => {
|
|
8
8
|
return (0, config_plugins_1.withAppBuildGradle)(config, (config) => {
|
|
9
9
|
if (config.modResults.contents.includes(SENTINEL_KEY)) {
|
package/plugin/build/types.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export type SpotifyScopes = "ugc-image-upload" | "user-read-playback-state" | "u
|
|
|
15
15
|
* clientID: "<spotify-client-id>",
|
|
16
16
|
* scheme: "myapp",
|
|
17
17
|
* host: "spotify-auth",
|
|
18
|
-
* redirectPathPattern: "
|
|
18
|
+
* redirectPathPattern: ".*"
|
|
19
19
|
* }]
|
|
20
20
|
* ]
|
|
21
21
|
* }
|
|
@@ -32,8 +32,11 @@ export interface SpotifyConfig {
|
|
|
32
32
|
scheme: string;
|
|
33
33
|
/**
|
|
34
34
|
* Path pattern Spotify will accept on the redirect URI. Required by the
|
|
35
|
-
* Spotify Android Auth SDK from version 3.0.0 onwards. Defaults to `"
|
|
36
|
-
* which matches any path
|
|
35
|
+
* Spotify Android Auth SDK from version 3.0.0 onwards. Defaults to `".*"`
|
|
36
|
+
* which matches any path including the empty string, so it works for both
|
|
37
|
+
* `scheme://host` and `scheme://host/path` redirect URIs. Use a more
|
|
38
|
+
* specific pattern (e.g. `"/auth/.*"`) only if you have a specific path
|
|
39
|
+
* registered in your Spotify app settings.
|
|
37
40
|
*
|
|
38
41
|
* See: https://developer.android.com/guide/topics/manifest/data-element#path
|
|
39
42
|
*/
|