@superfan-app/spotify-auth 0.1.22 → 0.1.24
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/.eslintrc.js +1 -0
- package/app.plugin.js +1 -0
- package/babel.config.js +1 -0
- package/build/SpotifyAuth.types.js +1 -1
- package/build/SpotifyAuth.types.js.map +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +2 -2
- package/build/index.js.map +1 -1
- package/expo-module.config.json +13 -0
- package/ios/SpotifyAuth.podspec +1 -1
- package/package.json +8 -16
- package/plugin/build/index.d.ts +1 -1
- package/plugin/build/index.js +75 -16
- package/plugin/src/index.ts +88 -32
- package/plugin/tsconfig.tsbuildinfo +1 -1
- package/src/SpotifyAuth.types.ts +81 -0
- package/src/SpotifyAuthModule.ts +6 -0
- package/src/SpotifyAuthView.tsx +14 -0
- package/src/index.tsx +83 -0
- package/tsconfig.json +9 -0
- package/plugin/build/ios/withSpotifyQueryScheme.d.ts +0 -3
- package/plugin/build/ios/withSpotifyQueryScheme.js +0 -15
- package/plugin/build/ios/withSpotifyURLScheme.d.ts +0 -3
- package/plugin/build/ios/withSpotifyURLScheme.js +0 -29
- package/plugin/build/prebuild.d.ts +0 -3
- package/plugin/build/prebuild.js +0 -16
- package/plugin/build/withSpotifyConfig.d.ts +0 -4
- package/plugin/build/withSpotifyConfig.js +0 -75
- package/plugin/src/ios/withSpotifyQueryScheme.ts +0 -18
- package/plugin/src/ios/withSpotifyURLScheme.ts +0 -36
- package/plugin/src/prebuild.ts +0 -16
- package/plugin/src/withSpotifyConfig.ts +0 -81
package/.eslintrc.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('expo-module-scripts/eslintrc.base.js');
|
package/app.plugin.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require("./plugin/build");
|
package/babel.config.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('expo-module-scripts/babel.config.base');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SpotifyAuth.types.js","sourceRoot":"","sources":["../src/SpotifyAuth.types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAyDtC,MAAM,CAAC,MAAM,0BAA0B,GAAG,aAAa,CAAqB;IAC1E,WAAW,EAAE,IAAI;IACjB,SAAS,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC;IACzB,gBAAgB,EAAE,KAAK;IACvB,KAAK,EAAE,IAAI;CACZ,CAAC,CAAC","sourcesContent":["import { createContext } from \"react\";\n\n/**\n * Event data structure for Spotify authorization events\n */\nexport interface SpotifyAuthEvent {\n success: boolean;\n token: string | null;\n error?: string;\n}\n\n/**\n * Data returned from the Spotify authorization process\n */\nexport interface SpotifyAuthorizationData {\n /** Whether the authorization was successful */\n success: boolean;\n /** The access token if authorization was successful, null otherwise */\n token: string | null;\n /** Error message if authorization failed */\n error?: string;\n}\n\n/**\n * Configuration for the authorization request\n */\nexport interface AuthorizeConfig {\n /** Spotify Client ID */\n clientId: string;\n /** OAuth redirect URL */\n redirectUrl: string;\n /** Whether to show the auth dialog */\n showDialog?: boolean;\n}\n\n/**\n * Props for the SpotifyAuthView component\n */\nexport interface SpotifyAuthViewProps {\n /** The name identifier for the auth view */\n name: string;\n}\n\n/**\n * Context for Spotify authentication state and actions\n */\nexport interface SpotifyAuthContext {\n /** The current Spotify access token, null if not authenticated */\n accessToken: string | null;\n /** Function to initiate Spotify authorization */\n authorize: (config: AuthorizeConfig) => Promise<void>;\n /** Whether authorization is in progress */\n isAuthenticating: boolean;\n /** Last error that occurred during authentication */\n error: string | null;\n}\n\nexport const SpotifyAuthContextInstance = createContext<SpotifyAuthContext>({\n accessToken: null,\n authorize: async () => {},\n isAuthenticating: false,\n error: null
|
|
1
|
+
{"version":3,"file":"SpotifyAuth.types.js","sourceRoot":"","sources":["../src/SpotifyAuth.types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAyDtC,MAAM,CAAC,MAAM,0BAA0B,GAAG,aAAa,CAAqB;IAC1E,WAAW,EAAE,IAAI;IACjB,SAAS,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC;IACzB,gBAAgB,EAAE,KAAK;IACvB,KAAK,EAAE,IAAI;CACZ,CAAC,CAAC","sourcesContent":["import { createContext } from \"react\";\n\n/**\n * Event data structure for Spotify authorization events\n */\nexport interface SpotifyAuthEvent {\n success: boolean;\n token: string | null;\n error?: string;\n}\n\n/**\n * Data returned from the Spotify authorization process\n */\nexport interface SpotifyAuthorizationData {\n /** Whether the authorization was successful */\n success: boolean;\n /** The access token if authorization was successful, null otherwise */\n token: string | null;\n /** Error message if authorization failed */\n error?: string;\n}\n\n/**\n * Configuration for the authorization request\n */\nexport interface AuthorizeConfig {\n /** Spotify Client ID */\n clientId: string;\n /** OAuth redirect URL */\n redirectUrl: string;\n /** Whether to show the auth dialog */\n showDialog?: boolean;\n}\n\n/**\n * Props for the SpotifyAuthView component\n */\nexport interface SpotifyAuthViewProps {\n /** The name identifier for the auth view */\n name: string;\n}\n\n/**\n * Context for Spotify authentication state and actions\n */\nexport interface SpotifyAuthContext {\n /** The current Spotify access token, null if not authenticated */\n accessToken: string | null;\n /** Function to initiate Spotify authorization */\n authorize: (config: AuthorizeConfig) => Promise<void>;\n /** Whether authorization is in progress */\n isAuthenticating: boolean;\n /** Last error that occurred during authentication */\n error: string | null;\n}\n\nexport const SpotifyAuthContextInstance = createContext<SpotifyAuthContext>({\n accessToken: null,\n authorize: async () => {},\n isAuthenticating: false,\n error: null,\n});\n\nexport interface SpotifyAuthOptions {\n clientId: string;\n redirectUrl: string;\n showDialog?: boolean;\n tokenRefreshFunction?: (data: SpotifyTokenResponse) => void;\n}\n\n/**\n * Response data from Spotify token endpoint\n */\nexport interface SpotifyTokenResponse {\n access_token: string;\n token_type: string;\n expires_in: number;\n refresh_token?: string;\n scope: string;\n}\n"]}
|
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAuD,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAuD,MAAM,OAAO,CAAC;AAE5E,OAAO,EAEL,kBAAkB,EAElB,KAAK,eAAe,EACrB,MAAM,qBAAqB,CAAC;AAe7B;;GAEG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,CAEvD;AAED,UAAU,wBAAwB;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED,wBAAgB,mBAAmB,CAAC,EAClC,QAAQ,GACT,EAAE,wBAAwB,GAAG,GAAG,CAAC,OAAO,CAsCxC;AAED,wBAAgB,cAAc,IAAI,kBAAkB,CAMnD"}
|
package/build/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EventEmitter } from "expo-modules-core";
|
|
2
2
|
import React, { useContext, useEffect, useState, useCallback } from "react";
|
|
3
|
-
import { SpotifyAuthContextInstance } from "./SpotifyAuth.types";
|
|
3
|
+
import { SpotifyAuthContextInstance, } from "./SpotifyAuth.types";
|
|
4
4
|
import SpotifyAuthModule from "./SpotifyAuthModule";
|
|
5
5
|
// Create a properly typed emitter
|
|
6
6
|
const emitter = new EventEmitter(SpotifyAuthModule);
|
|
@@ -26,7 +26,7 @@ export function SpotifyAuthProvider({ children, }) {
|
|
|
26
26
|
await SpotifyAuthModule.authorize(config);
|
|
27
27
|
}
|
|
28
28
|
catch (err) {
|
|
29
|
-
setError(err instanceof Error ? err.message :
|
|
29
|
+
setError(err instanceof Error ? err.message : "Authorization failed");
|
|
30
30
|
throw err;
|
|
31
31
|
}
|
|
32
32
|
finally {
|
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAE5E,OAAO,EAGL,0BAA0B,GAE3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,iBAAiB,MAAM,qBAAqB,CAAC;AAKpD,kCAAkC;AAClC,MAAM,OAAO,GAAG,IAAI,YAAY,CAAC,iBAAiB,CAAC,CAAC;AAEpD,SAAS,eAAe,CAAC,QAAkD;IACzE,+CAA+C;IAC/C,MAAM,SAAS,GAAG,iBAAiB,CAAC,aAAqC,CAAC;IAC1E,OAAO,OAAO,CAAC,WAAW,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAClD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,MAAuB;IAC/C,iBAAiB,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;AACtC,CAAC;AAMD,MAAM,UAAU,mBAAmB,CAAC,EAClC,QAAQ,GACiB;IACzB,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IACxD,MAAM,CAAC,gBAAgB,EAAE,mBAAmB,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAChE,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IAExD,MAAM,SAAS,GAAG,WAAW,CAC3B,KAAK,EAAE,MAAuB,EAAiB,EAAE;QAC/C,IAAI,CAAC;YACH,mBAAmB,CAAC,IAAI,CAAC,CAAC;YAC1B,QAAQ,CAAC,IAAI,CAAC,CAAC;YACf,MAAM,iBAAiB,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAC5C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,QAAQ,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC;YACtE,MAAM,GAAG,CAAC;QACZ,CAAC;gBAAS,CAAC;YACT,mBAAmB,CAAC,KAAK,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC,EACD,EAAE,CACH,CAAC;IAEF,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,YAAY,GAAG,eAAe,CAAC,CAAC,IAAI,EAAE,EAAE;YAC5C,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACrB,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,KAAK,CAAC,uBAAuB,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;YACrD,CAAC;QACH,CAAC,CAAC,CAAC;QACH,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC;IACrC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,CACL,CAAC,0BAA0B,CAAC,QAAQ,CAClC,KAAK,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,KAAK,EAAE,CAAC,CAElE;MAAA,CAAC,QAAQ,CACX;IAAA,EAAE,0BAA0B,CAAC,QAAQ,CAAC,CACvC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,MAAM,OAAO,GAAG,UAAU,CAAC,0BAA0B,CAAC,CAAC;IACvD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAC;IAC9E,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC","sourcesContent":["import { EventEmitter } from \"expo-modules-core\";\nimport React, { useContext, useEffect, useState, useCallback } from \"react\";\n\nimport {\n SpotifyAuthorizationData,\n SpotifyAuthContext,\n SpotifyAuthContextInstance,\n type AuthorizeConfig,\n} from \"./SpotifyAuth.types\";\nimport SpotifyAuthModule from \"./SpotifyAuthModule\";\n\n// First define the event name as a string literal type\ntype SpotifyAuthEventName = \"onSpotifyAuth\"; // This should match SpotifyAuthModule.AuthEventName\n\n// Create a properly typed emitter\nconst emitter = new EventEmitter(SpotifyAuthModule);\n\nfunction addAuthListener(listener: (data: SpotifyAuthorizationData) => void) {\n // Assert the event name is of the correct type\n const eventName = SpotifyAuthModule.AuthEventName as SpotifyAuthEventName;\n return emitter.addListener(eventName, listener);\n}\n\n/**\n * Prompts the user to log in to Spotify and authorize your application.\n */\nexport function authorize(config: AuthorizeConfig): void {\n SpotifyAuthModule.authorize(config);\n}\n\ninterface SpotifyAuthProviderProps {\n children: React.ReactNode;\n}\n\nexport function SpotifyAuthProvider({\n children,\n}: SpotifyAuthProviderProps): JSX.Element {\n const [token, setToken] = useState<string | null>(null);\n const [isAuthenticating, setIsAuthenticating] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const authorize = useCallback(\n async (config: AuthorizeConfig): Promise<void> => {\n try {\n setIsAuthenticating(true);\n setError(null);\n await SpotifyAuthModule.authorize(config);\n } catch (err) {\n setError(err instanceof Error ? err.message : \"Authorization failed\");\n throw err;\n } finally {\n setIsAuthenticating(false);\n }\n },\n [],\n );\n\n useEffect(() => {\n const subscription = addAuthListener((data) => {\n setToken(data.token);\n if (data.error) {\n console.error(`Spotify auth error: ${data.error}`);\n }\n });\n return () => subscription.remove();\n }, []);\n\n return (\n <SpotifyAuthContextInstance.Provider\n value={{ accessToken: token, authorize, isAuthenticating, error }}\n >\n {children}\n </SpotifyAuthContextInstance.Provider>\n );\n}\n\nexport function useSpotifyAuth(): SpotifyAuthContext {\n const context = useContext(SpotifyAuthContextInstance);\n if (!context) {\n throw new Error(\"useSpotifyAuth must be used within a SpotifyAuthProvider\");\n }\n return context;\n}\n"]}
|
package/ios/SpotifyAuth.podspec
CHANGED
|
@@ -12,7 +12,7 @@ Pod::Spec.new do |s|
|
|
|
12
12
|
s.homepage = package['homepage']
|
|
13
13
|
s.platforms = { :ios => '13.0' } # Updated minimum iOS version
|
|
14
14
|
s.swift_version = '5.4'
|
|
15
|
-
s.source = { git: 'https://github.com/
|
|
15
|
+
s.source = { git: 'https://github.com/superfan-app/spotify-auth' }
|
|
16
16
|
s.static_framework = true
|
|
17
17
|
|
|
18
18
|
s.dependency 'ExpoModulesCore'
|
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@superfan-app/spotify-auth",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.24",
|
|
4
4
|
"description": "Spotify OAuth module for Expo",
|
|
5
|
-
"main": "
|
|
5
|
+
"main": "src/index.ts",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
7
|
+
"sideEffects": false,
|
|
7
8
|
"scripts": {
|
|
8
9
|
"build": "expo-module build",
|
|
9
10
|
"clean": "expo-module clean",
|
|
@@ -37,21 +38,12 @@
|
|
|
37
38
|
"@types/react": "^18.0.0",
|
|
38
39
|
"@types/react-native": "^0.70.0",
|
|
39
40
|
"expo-module-scripts": "^3.0.0",
|
|
40
|
-
"expo-modules-core": "^1.5.0"
|
|
41
|
+
"expo-modules-core": "^1.5.0",
|
|
42
|
+
"typescript": "^5.0.0"
|
|
41
43
|
},
|
|
42
44
|
"peerDependencies": {
|
|
43
|
-
"expo": "
|
|
45
|
+
"expo": "*",
|
|
44
46
|
"react": "*",
|
|
45
|
-
"react-native": "
|
|
46
|
-
}
|
|
47
|
-
"files": [
|
|
48
|
-
"build",
|
|
49
|
-
"ios",
|
|
50
|
-
"android",
|
|
51
|
-
"plugin",
|
|
52
|
-
"!**/__tests__",
|
|
53
|
-
"!**/__fixtures__",
|
|
54
|
-
"!**/__mocks__",
|
|
55
|
-
"README.md"
|
|
56
|
-
]
|
|
47
|
+
"react-native": "*"
|
|
48
|
+
}
|
|
57
49
|
}
|
package/plugin/build/index.d.ts
CHANGED
package/plugin/build/index.js
CHANGED
|
@@ -1,25 +1,84 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
const config_plugins_1 = require("@expo/config-plugins");
|
|
7
|
-
const withSpotifyQueryScheme_1 = require("./ios/withSpotifyQueryScheme");
|
|
8
|
-
const withSpotifyURLScheme_1 = require("./ios/withSpotifyURLScheme");
|
|
9
|
-
const withSpotifyConfig_1 = __importDefault(require("./withSpotifyConfig"));
|
|
10
4
|
const pkg = require('../../package.json');
|
|
11
|
-
function
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
5
|
+
function validateSpotifyConfig(config) {
|
|
6
|
+
if (!config.clientID)
|
|
7
|
+
throw new Error("Spotify clientID is required");
|
|
8
|
+
if (!config.scheme)
|
|
9
|
+
throw new Error("URL scheme is required");
|
|
10
|
+
if (!config.callback)
|
|
11
|
+
throw new Error("Callback path is required");
|
|
12
|
+
if (!config.tokenSwapURL)
|
|
13
|
+
throw new Error("Token swap URL is required");
|
|
14
|
+
if (!config.tokenRefreshURL)
|
|
15
|
+
throw new Error("Token refresh URL is required");
|
|
16
|
+
if (!Array.isArray(config.scopes) || config.scopes.length === 0) {
|
|
17
|
+
throw new Error("At least one scope is required");
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function validateScheme(scheme) {
|
|
21
|
+
if (!scheme) {
|
|
22
|
+
throw new Error("URL scheme is required");
|
|
23
|
+
}
|
|
24
|
+
// Ensure scheme follows URL scheme naming conventions
|
|
25
|
+
if (!/^[a-z][a-z0-9+.-]*$/i.test(scheme)) {
|
|
26
|
+
throw new Error("Invalid URL scheme format. Scheme should start with a letter and contain only letters, numbers, plus, period, or hyphen.");
|
|
15
27
|
}
|
|
16
28
|
}
|
|
29
|
+
const withSpotifyURLSchemes = (config, props) => {
|
|
30
|
+
return (0, config_plugins_1.withInfoPlist)(config, (config) => {
|
|
31
|
+
// Add URL scheme configuration
|
|
32
|
+
if (!config.modResults.CFBundleURLTypes) {
|
|
33
|
+
config.modResults.CFBundleURLTypes = [];
|
|
34
|
+
}
|
|
35
|
+
config.modResults.CFBundleURLTypes.push({
|
|
36
|
+
CFBundleURLSchemes: [props.scheme],
|
|
37
|
+
CFBundleURLName: props.scheme
|
|
38
|
+
});
|
|
39
|
+
// Add Spotify query scheme
|
|
40
|
+
if (!config.modResults.LSApplicationQueriesSchemes) {
|
|
41
|
+
config.modResults.LSApplicationQueriesSchemes = [];
|
|
42
|
+
}
|
|
43
|
+
if (!config.modResults.LSApplicationQueriesSchemes.includes('spotify')) {
|
|
44
|
+
config.modResults.LSApplicationQueriesSchemes.push('spotify');
|
|
45
|
+
}
|
|
46
|
+
return config;
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
const withSpotifyConfiguration = (config, props) => {
|
|
50
|
+
return (0, config_plugins_1.withInfoPlist)(config, (config) => {
|
|
51
|
+
// Add Spotify configuration
|
|
52
|
+
config.modResults.SpotifyClientID = props.clientID;
|
|
53
|
+
config.modResults.SpotifyScheme = props.scheme;
|
|
54
|
+
config.modResults.SpotifyCallback = props.callback;
|
|
55
|
+
config.modResults.SpotifyScopes = props.scopes;
|
|
56
|
+
config.modResults.tokenRefreshURL = props.tokenRefreshURL;
|
|
57
|
+
config.modResults.tokenSwapURL = props.tokenSwapURL;
|
|
58
|
+
return config;
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
const withIOSSettings = (config) => {
|
|
62
|
+
return (0, config_plugins_1.withInfoPlist)(config, (config) => {
|
|
63
|
+
// Set minimum iOS version and build settings
|
|
64
|
+
config.modResults.MinimumOSVersion = '13.0';
|
|
65
|
+
config.modResults.EnableBitcode = false;
|
|
66
|
+
config.modResults.SwiftVersion = '5.4';
|
|
67
|
+
config.modResults.IphoneosDeploymentTarget = '13.0';
|
|
68
|
+
return config;
|
|
69
|
+
});
|
|
70
|
+
};
|
|
17
71
|
const withSpotifyAuth = (config, props) => {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
72
|
+
// Ensure the config exists
|
|
73
|
+
if (!props) {
|
|
74
|
+
throw new Error('Missing Spotify configuration. Please provide clientID, scheme, callback, tokenSwapURL, tokenRefreshURL, and scopes in your app.config.js/app.json.');
|
|
75
|
+
}
|
|
76
|
+
validateSpotifyConfig(props);
|
|
77
|
+
validateScheme(props.scheme);
|
|
78
|
+
// Apply configurations in sequence
|
|
79
|
+
config = withSpotifyConfiguration(config, props);
|
|
80
|
+
config = withSpotifyURLSchemes(config, props);
|
|
81
|
+
config = withIOSSettings(config);
|
|
82
|
+
return config;
|
|
24
83
|
};
|
|
25
84
|
exports.default = (0, config_plugins_1.createRunOncePlugin)(withSpotifyAuth, pkg.name, pkg.version);
|
package/plugin/src/index.ts
CHANGED
|
@@ -1,40 +1,96 @@
|
|
|
1
|
-
import { type ConfigPlugin, createRunOncePlugin,
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
function ensureDevClientInstalled(config: any) {
|
|
16
|
-
const devClient = config.plugins?.find((plugin: any) =>
|
|
17
|
-
typeof plugin === 'string' ? plugin === 'expo-dev-client' : plugin[0] === 'expo-dev-client'
|
|
18
|
-
);
|
|
19
|
-
|
|
20
|
-
if (!devClient) {
|
|
21
|
-
WarningAggregator.addWarningIOS(
|
|
22
|
-
'spotify-auth',
|
|
23
|
-
'This module requires expo-dev-client to be installed. Please run: npx expo install expo-dev-client'
|
|
24
|
-
);
|
|
1
|
+
import { type ConfigPlugin, createRunOncePlugin, withInfoPlist } from '@expo/config-plugins'
|
|
2
|
+
import { SpotifyConfig } from './types.js'
|
|
3
|
+
|
|
4
|
+
const pkg = require('../../package.json');
|
|
5
|
+
|
|
6
|
+
function validateSpotifyConfig(config: SpotifyConfig) {
|
|
7
|
+
if (!config.clientID) throw new Error("Spotify clientID is required")
|
|
8
|
+
if (!config.scheme) throw new Error("URL scheme is required")
|
|
9
|
+
if (!config.callback) throw new Error("Callback path is required")
|
|
10
|
+
if (!config.tokenSwapURL) throw new Error("Token swap URL is required")
|
|
11
|
+
if (!config.tokenRefreshURL) throw new Error("Token refresh URL is required")
|
|
12
|
+
if (!Array.isArray(config.scopes) || config.scopes.length === 0) {
|
|
13
|
+
throw new Error("At least one scope is required")
|
|
25
14
|
}
|
|
26
15
|
}
|
|
27
16
|
|
|
28
|
-
|
|
29
|
-
|
|
17
|
+
function validateScheme(scheme: string) {
|
|
18
|
+
if (!scheme) {
|
|
19
|
+
throw new Error("URL scheme is required");
|
|
20
|
+
}
|
|
30
21
|
|
|
31
|
-
|
|
22
|
+
// Ensure scheme follows URL scheme naming conventions
|
|
23
|
+
if (!/^[a-z][a-z0-9+.-]*$/i.test(scheme)) {
|
|
24
|
+
throw new Error("Invalid URL scheme format. Scheme should start with a letter and contain only letters, numbers, plus, period, or hyphen.");
|
|
25
|
+
}
|
|
26
|
+
}
|
|
32
27
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
28
|
+
const withSpotifyURLSchemes: ConfigPlugin<SpotifyConfig> = (config, props) => {
|
|
29
|
+
return withInfoPlist(config, (config) => {
|
|
30
|
+
// Add URL scheme configuration
|
|
31
|
+
if (!config.modResults.CFBundleURLTypes) {
|
|
32
|
+
config.modResults.CFBundleURLTypes = [];
|
|
33
|
+
}
|
|
34
|
+
config.modResults.CFBundleURLTypes.push({
|
|
35
|
+
CFBundleURLSchemes: [props.scheme],
|
|
36
|
+
CFBundleURLName: props.scheme
|
|
37
|
+
});
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
// Add Spotify query scheme
|
|
40
|
+
if (!config.modResults.LSApplicationQueriesSchemes) {
|
|
41
|
+
config.modResults.LSApplicationQueriesSchemes = [];
|
|
42
|
+
}
|
|
43
|
+
if (!config.modResults.LSApplicationQueriesSchemes.includes('spotify')) {
|
|
44
|
+
config.modResults.LSApplicationQueriesSchemes.push('spotify');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return config;
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const withSpotifyConfiguration: ConfigPlugin<SpotifyConfig> = (config, props) => {
|
|
52
|
+
return withInfoPlist(config, (config) => {
|
|
53
|
+
// Add Spotify configuration
|
|
54
|
+
config.modResults.SpotifyClientID = props.clientID;
|
|
55
|
+
config.modResults.SpotifyScheme = props.scheme;
|
|
56
|
+
config.modResults.SpotifyCallback = props.callback;
|
|
57
|
+
config.modResults.SpotifyScopes = props.scopes;
|
|
58
|
+
config.modResults.tokenRefreshURL = props.tokenRefreshURL;
|
|
59
|
+
config.modResults.tokenSwapURL = props.tokenSwapURL;
|
|
60
|
+
|
|
61
|
+
return config;
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const withIOSSettings: ConfigPlugin = (config) => {
|
|
66
|
+
return withInfoPlist(config, (config) => {
|
|
67
|
+
// Set minimum iOS version and build settings
|
|
68
|
+
config.modResults.MinimumOSVersion = '13.0';
|
|
69
|
+
config.modResults.EnableBitcode = false;
|
|
70
|
+
config.modResults.SwiftVersion = '5.4';
|
|
71
|
+
config.modResults.IphoneosDeploymentTarget = '13.0';
|
|
72
|
+
|
|
73
|
+
return config;
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const withSpotifyAuth: ConfigPlugin<SpotifyConfig> = (config, props) => {
|
|
78
|
+
// Ensure the config exists
|
|
79
|
+
if (!props) {
|
|
80
|
+
throw new Error(
|
|
81
|
+
'Missing Spotify configuration. Please provide clientID, scheme, callback, tokenSwapURL, tokenRefreshURL, and scopes in your app.config.js/app.json.'
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
validateSpotifyConfig(props);
|
|
86
|
+
validateScheme(props.scheme);
|
|
87
|
+
|
|
88
|
+
// Apply configurations in sequence
|
|
89
|
+
config = withSpotifyConfiguration(config, props);
|
|
90
|
+
config = withSpotifyURLSchemes(config, props);
|
|
91
|
+
config = withIOSSettings(config);
|
|
92
|
+
|
|
93
|
+
return config;
|
|
94
|
+
};
|
|
39
95
|
|
|
40
96
|
export default createRunOncePlugin(withSpotifyAuth, pkg.name, pkg.version);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["./src/index.ts","./src/
|
|
1
|
+
{"root":["./src/index.ts","./src/types.ts"],"version":"5.7.2"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { createContext } from "react";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Event data structure for Spotify authorization events
|
|
5
|
+
*/
|
|
6
|
+
export interface SpotifyAuthEvent {
|
|
7
|
+
success: boolean;
|
|
8
|
+
token: string | null;
|
|
9
|
+
error?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Data returned from the Spotify authorization process
|
|
14
|
+
*/
|
|
15
|
+
export interface SpotifyAuthorizationData {
|
|
16
|
+
/** Whether the authorization was successful */
|
|
17
|
+
success: boolean;
|
|
18
|
+
/** The access token if authorization was successful, null otherwise */
|
|
19
|
+
token: string | null;
|
|
20
|
+
/** Error message if authorization failed */
|
|
21
|
+
error?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Configuration for the authorization request
|
|
26
|
+
*/
|
|
27
|
+
export interface AuthorizeConfig {
|
|
28
|
+
/** Spotify Client ID */
|
|
29
|
+
clientId: string;
|
|
30
|
+
/** OAuth redirect URL */
|
|
31
|
+
redirectUrl: string;
|
|
32
|
+
/** Whether to show the auth dialog */
|
|
33
|
+
showDialog?: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Props for the SpotifyAuthView component
|
|
38
|
+
*/
|
|
39
|
+
export interface SpotifyAuthViewProps {
|
|
40
|
+
/** The name identifier for the auth view */
|
|
41
|
+
name: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Context for Spotify authentication state and actions
|
|
46
|
+
*/
|
|
47
|
+
export interface SpotifyAuthContext {
|
|
48
|
+
/** The current Spotify access token, null if not authenticated */
|
|
49
|
+
accessToken: string | null;
|
|
50
|
+
/** Function to initiate Spotify authorization */
|
|
51
|
+
authorize: (config: AuthorizeConfig) => Promise<void>;
|
|
52
|
+
/** Whether authorization is in progress */
|
|
53
|
+
isAuthenticating: boolean;
|
|
54
|
+
/** Last error that occurred during authentication */
|
|
55
|
+
error: string | null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const SpotifyAuthContextInstance = createContext<SpotifyAuthContext>({
|
|
59
|
+
accessToken: null,
|
|
60
|
+
authorize: async () => {},
|
|
61
|
+
isAuthenticating: false,
|
|
62
|
+
error: null,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
export interface SpotifyAuthOptions {
|
|
66
|
+
clientId: string;
|
|
67
|
+
redirectUrl: string;
|
|
68
|
+
showDialog?: boolean;
|
|
69
|
+
tokenRefreshFunction?: (data: SpotifyTokenResponse) => void;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Response data from Spotify token endpoint
|
|
74
|
+
*/
|
|
75
|
+
export interface SpotifyTokenResponse {
|
|
76
|
+
access_token: string;
|
|
77
|
+
token_type: string;
|
|
78
|
+
expires_in: number;
|
|
79
|
+
refresh_token?: string;
|
|
80
|
+
scope: string;
|
|
81
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { requireNativeViewManager } from "expo-modules-core";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { ViewProps } from "react-native";
|
|
4
|
+
|
|
5
|
+
import { SpotifyAuthViewProps } from "./SpotifyAuth.types";
|
|
6
|
+
|
|
7
|
+
const NativeView: React.ComponentType<SpotifyAuthViewProps & ViewProps> =
|
|
8
|
+
requireNativeViewManager("SpotifyAuth");
|
|
9
|
+
|
|
10
|
+
export default function SpotifyAuthView(
|
|
11
|
+
props: SpotifyAuthViewProps,
|
|
12
|
+
): JSX.Element {
|
|
13
|
+
return <NativeView {...props} />;
|
|
14
|
+
}
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { EventEmitter } from "expo-modules-core";
|
|
2
|
+
import React, { useContext, useEffect, useState, useCallback } from "react";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
SpotifyAuthorizationData,
|
|
6
|
+
SpotifyAuthContext,
|
|
7
|
+
SpotifyAuthContextInstance,
|
|
8
|
+
type AuthorizeConfig,
|
|
9
|
+
} from "./SpotifyAuth.types";
|
|
10
|
+
import SpotifyAuthModule from "./SpotifyAuthModule";
|
|
11
|
+
|
|
12
|
+
// First define the event name as a string literal type
|
|
13
|
+
type SpotifyAuthEventName = "onSpotifyAuth"; // This should match SpotifyAuthModule.AuthEventName
|
|
14
|
+
|
|
15
|
+
// Create a properly typed emitter
|
|
16
|
+
const emitter = new EventEmitter(SpotifyAuthModule);
|
|
17
|
+
|
|
18
|
+
function addAuthListener(listener: (data: SpotifyAuthorizationData) => void) {
|
|
19
|
+
// Assert the event name is of the correct type
|
|
20
|
+
const eventName = SpotifyAuthModule.AuthEventName as SpotifyAuthEventName;
|
|
21
|
+
return emitter.addListener(eventName, listener);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Prompts the user to log in to Spotify and authorize your application.
|
|
26
|
+
*/
|
|
27
|
+
export function authorize(config: AuthorizeConfig): void {
|
|
28
|
+
SpotifyAuthModule.authorize(config);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface SpotifyAuthProviderProps {
|
|
32
|
+
children: React.ReactNode;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function SpotifyAuthProvider({
|
|
36
|
+
children,
|
|
37
|
+
}: SpotifyAuthProviderProps): JSX.Element {
|
|
38
|
+
const [token, setToken] = useState<string | null>(null);
|
|
39
|
+
const [isAuthenticating, setIsAuthenticating] = useState(false);
|
|
40
|
+
const [error, setError] = useState<string | null>(null);
|
|
41
|
+
|
|
42
|
+
const authorize = useCallback(
|
|
43
|
+
async (config: AuthorizeConfig): Promise<void> => {
|
|
44
|
+
try {
|
|
45
|
+
setIsAuthenticating(true);
|
|
46
|
+
setError(null);
|
|
47
|
+
await SpotifyAuthModule.authorize(config);
|
|
48
|
+
} catch (err) {
|
|
49
|
+
setError(err instanceof Error ? err.message : "Authorization failed");
|
|
50
|
+
throw err;
|
|
51
|
+
} finally {
|
|
52
|
+
setIsAuthenticating(false);
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
[],
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
const subscription = addAuthListener((data) => {
|
|
60
|
+
setToken(data.token);
|
|
61
|
+
if (data.error) {
|
|
62
|
+
console.error(`Spotify auth error: ${data.error}`);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
return () => subscription.remove();
|
|
66
|
+
}, []);
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<SpotifyAuthContextInstance.Provider
|
|
70
|
+
value={{ accessToken: token, authorize, isAuthenticating, error }}
|
|
71
|
+
>
|
|
72
|
+
{children}
|
|
73
|
+
</SpotifyAuthContextInstance.Provider>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function useSpotifyAuth(): SpotifyAuthContext {
|
|
78
|
+
const context = useContext(SpotifyAuthContextInstance);
|
|
79
|
+
if (!context) {
|
|
80
|
+
throw new Error("useSpotifyAuth must be used within a SpotifyAuthProvider");
|
|
81
|
+
}
|
|
82
|
+
return context;
|
|
83
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.withSpotifyQueryScheme = void 0;
|
|
4
|
-
const config_plugins_1 = require("expo/config-plugins");
|
|
5
|
-
const spotifyScheme = "spotify";
|
|
6
|
-
const withSpotifyQueryScheme = (config) => (0, config_plugins_1.withInfoPlist)(config, (config) => {
|
|
7
|
-
if (!config.modResults.LSApplicationQueriesSchemes) {
|
|
8
|
-
config.modResults.LSApplicationQueriesSchemes = [];
|
|
9
|
-
}
|
|
10
|
-
if (!config.modResults.LSApplicationQueriesSchemes.includes(spotifyScheme)) {
|
|
11
|
-
config.modResults.LSApplicationQueriesSchemes.push(spotifyScheme);
|
|
12
|
-
}
|
|
13
|
-
return config;
|
|
14
|
-
});
|
|
15
|
-
exports.withSpotifyQueryScheme = withSpotifyQueryScheme;
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.withSpotifyURLScheme = void 0;
|
|
4
|
-
const config_plugins_1 = require("expo/config-plugins");
|
|
5
|
-
function validateScheme(scheme) {
|
|
6
|
-
if (!scheme) {
|
|
7
|
-
throw new Error("URL scheme is required");
|
|
8
|
-
}
|
|
9
|
-
// Ensure scheme follows URL scheme naming conventions
|
|
10
|
-
if (!/^[a-z][a-z0-9+.-]*$/i.test(scheme)) {
|
|
11
|
-
throw new Error("Invalid URL scheme format. Scheme should start with a letter and contain only letters, numbers, plus, period, or hyphen.");
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
const withSpotifyURLScheme = (config, { scheme }) => {
|
|
15
|
-
validateScheme(scheme);
|
|
16
|
-
return (0, config_plugins_1.withInfoPlist)(config, (config) => {
|
|
17
|
-
const bundleId = config.ios?.bundleIdentifier;
|
|
18
|
-
const urlType = {
|
|
19
|
-
CFBundleURLName: bundleId,
|
|
20
|
-
CFBundleURLSchemes: [scheme],
|
|
21
|
-
};
|
|
22
|
-
if (!config.modResults.CFBundleURLTypes) {
|
|
23
|
-
config.modResults.CFBundleURLTypes = [];
|
|
24
|
-
}
|
|
25
|
-
config.modResults.CFBundleURLTypes.push(urlType);
|
|
26
|
-
return config;
|
|
27
|
-
});
|
|
28
|
-
};
|
|
29
|
-
exports.withSpotifyURLScheme = withSpotifyURLScheme;
|
package/plugin/build/prebuild.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.withPreBuildConfig = void 0;
|
|
4
|
-
const config_plugins_1 = require("@expo/config-plugins");
|
|
5
|
-
const withPreBuildConfig = (config, props) => {
|
|
6
|
-
return (0, config_plugins_1.withInfoPlist)(config, (config) => {
|
|
7
|
-
// Set minimum iOS version in Info.plist
|
|
8
|
-
config.modResults.MinimumOSVersion = '13.0';
|
|
9
|
-
// Add build-related keys
|
|
10
|
-
config.modResults.EnableBitcode = false;
|
|
11
|
-
config.modResults.SwiftVersion = '5.4';
|
|
12
|
-
config.modResults.IphoneosDeploymentTarget = '13.0';
|
|
13
|
-
return config;
|
|
14
|
-
});
|
|
15
|
-
};
|
|
16
|
-
exports.withPreBuildConfig = withPreBuildConfig;
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const config_plugins_1 = require("@expo/config-plugins");
|
|
7
|
-
const fs_1 = __importDefault(require("fs"));
|
|
8
|
-
const path_1 = __importDefault(require("path"));
|
|
9
|
-
function validateSpotifyConfig(config) {
|
|
10
|
-
if (!config.clientID)
|
|
11
|
-
throw new Error("Spotify clientID is required");
|
|
12
|
-
if (!config.scheme)
|
|
13
|
-
throw new Error("URL scheme is required");
|
|
14
|
-
if (!config.callback)
|
|
15
|
-
throw new Error("Callback path is required");
|
|
16
|
-
if (!config.tokenSwapURL)
|
|
17
|
-
throw new Error("Token swap URL is required");
|
|
18
|
-
if (!config.tokenRefreshURL)
|
|
19
|
-
throw new Error("Token refresh URL is required");
|
|
20
|
-
if (!Array.isArray(config.scopes) || config.scopes.length === 0) {
|
|
21
|
-
throw new Error("At least one scope is required");
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
const withSpotifyOAuthConfigIOS = (config, spotifyConfig) => {
|
|
25
|
-
validateSpotifyConfig(spotifyConfig);
|
|
26
|
-
return (0, config_plugins_1.withInfoPlist)(config, (config) => {
|
|
27
|
-
config.modResults.SpotifyClientID = spotifyConfig.clientID;
|
|
28
|
-
config.modResults.SpotifyScheme = spotifyConfig.scheme;
|
|
29
|
-
config.modResults.SpotifyCallback = spotifyConfig.callback;
|
|
30
|
-
config.modResults.SpotifyScopes = spotifyConfig.scopes;
|
|
31
|
-
config.modResults.tokenRefreshURL = spotifyConfig.tokenRefreshURL;
|
|
32
|
-
config.modResults.tokenSwapURL = spotifyConfig.tokenSwapURL;
|
|
33
|
-
// Add URL scheme to Info.plist
|
|
34
|
-
const urlTypes = config.modResults.CFBundleURLTypes || [];
|
|
35
|
-
urlTypes.push({
|
|
36
|
-
CFBundleURLSchemes: [spotifyConfig.scheme],
|
|
37
|
-
CFBundleURLName: spotifyConfig.scheme
|
|
38
|
-
});
|
|
39
|
-
config.modResults.CFBundleURLTypes = urlTypes;
|
|
40
|
-
return config;
|
|
41
|
-
});
|
|
42
|
-
};
|
|
43
|
-
const withCleanup = (config) => {
|
|
44
|
-
return (0, config_plugins_1.withDangerousMod)(config, [
|
|
45
|
-
'ios',
|
|
46
|
-
async (config) => {
|
|
47
|
-
const platformRoot = path_1.default.join(config.modRequest.projectRoot, 'ios');
|
|
48
|
-
// Clean build directories
|
|
49
|
-
const buildDirs = [
|
|
50
|
-
path_1.default.join(platformRoot, 'build'),
|
|
51
|
-
path_1.default.join(platformRoot, 'Pods'),
|
|
52
|
-
path_1.default.join(platformRoot, 'DerivedData')
|
|
53
|
-
];
|
|
54
|
-
for (const dir of buildDirs) {
|
|
55
|
-
if (fs_1.default.existsSync(dir)) {
|
|
56
|
-
fs_1.default.rmdirSync(dir, { recursive: true });
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
return config;
|
|
60
|
-
},
|
|
61
|
-
]);
|
|
62
|
-
};
|
|
63
|
-
const withSpotifyAuth = (config, props) => {
|
|
64
|
-
// Ensure the config exists
|
|
65
|
-
if (!props) {
|
|
66
|
-
throw new Error('Missing Spotify configuration. Please provide clientID, scheme, callback, tokenSwapURL, tokenRefreshURL, and scopes in your app.config.js/app.json.');
|
|
67
|
-
}
|
|
68
|
-
return (0, config_plugins_1.withPlugins)(config, [
|
|
69
|
-
// Clean up first
|
|
70
|
-
withCleanup,
|
|
71
|
-
// Apply Spotify configurations
|
|
72
|
-
[withSpotifyOAuthConfigIOS, props]
|
|
73
|
-
]);
|
|
74
|
-
};
|
|
75
|
-
exports.default = (0, config_plugins_1.createRunOncePlugin)(withSpotifyAuth, 'spotify-auth', '0.1.21');
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { ConfigPlugin, withInfoPlist } from "expo/config-plugins";
|
|
2
|
-
|
|
3
|
-
import { SpotifyConfig } from "../types";
|
|
4
|
-
|
|
5
|
-
const spotifyScheme = "spotify";
|
|
6
|
-
|
|
7
|
-
export const withSpotifyQueryScheme: ConfigPlugin<SpotifyConfig> = (config) =>
|
|
8
|
-
withInfoPlist(config, (config) => {
|
|
9
|
-
if (!config.modResults.LSApplicationQueriesSchemes) {
|
|
10
|
-
config.modResults.LSApplicationQueriesSchemes = [];
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
if (!config.modResults.LSApplicationQueriesSchemes.includes(spotifyScheme)) {
|
|
14
|
-
config.modResults.LSApplicationQueriesSchemes.push(spotifyScheme)
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return config;
|
|
18
|
-
});
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { type ConfigPlugin, withInfoPlist } from "expo/config-plugins";
|
|
2
|
-
|
|
3
|
-
import { SpotifyConfig } from "../types";
|
|
4
|
-
|
|
5
|
-
function validateScheme(scheme: string) {
|
|
6
|
-
if (!scheme) {
|
|
7
|
-
throw new Error("URL scheme is required");
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
// Ensure scheme follows URL scheme naming conventions
|
|
11
|
-
if (!/^[a-z][a-z0-9+.-]*$/i.test(scheme)) {
|
|
12
|
-
throw new Error("Invalid URL scheme format. Scheme should start with a letter and contain only letters, numbers, plus, period, or hyphen.");
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export const withSpotifyURLScheme: ConfigPlugin<SpotifyConfig> = (
|
|
17
|
-
config,
|
|
18
|
-
{ scheme }
|
|
19
|
-
) => {
|
|
20
|
-
validateScheme(scheme);
|
|
21
|
-
|
|
22
|
-
return withInfoPlist(config, (config) => {
|
|
23
|
-
const bundleId = config.ios?.bundleIdentifier;
|
|
24
|
-
const urlType = {
|
|
25
|
-
CFBundleURLName: bundleId,
|
|
26
|
-
CFBundleURLSchemes: [scheme],
|
|
27
|
-
};
|
|
28
|
-
if (!config.modResults.CFBundleURLTypes) {
|
|
29
|
-
config.modResults.CFBundleURLTypes = [];
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
config.modResults.CFBundleURLTypes.push(urlType);
|
|
33
|
-
|
|
34
|
-
return config;
|
|
35
|
-
});
|
|
36
|
-
};
|
package/plugin/src/prebuild.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { ConfigPlugin, withInfoPlist } from '@expo/config-plugins';
|
|
2
|
-
import { SpotifyConfig } from './types';
|
|
3
|
-
|
|
4
|
-
export const withPreBuildConfig: ConfigPlugin<SpotifyConfig> = (config, props) => {
|
|
5
|
-
return withInfoPlist(config, (config) => {
|
|
6
|
-
// Set minimum iOS version in Info.plist
|
|
7
|
-
config.modResults.MinimumOSVersion = '13.0';
|
|
8
|
-
|
|
9
|
-
// Add build-related keys
|
|
10
|
-
config.modResults.EnableBitcode = false;
|
|
11
|
-
config.modResults.SwiftVersion = '5.4';
|
|
12
|
-
config.modResults.IphoneosDeploymentTarget = '13.0';
|
|
13
|
-
|
|
14
|
-
return config;
|
|
15
|
-
});
|
|
16
|
-
};
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import { ConfigPlugin, withPlugins, createRunOncePlugin, withDangerousMod, withInfoPlist } from '@expo/config-plugins';
|
|
2
|
-
import { SpotifyConfig } from './types';
|
|
3
|
-
import fs from 'fs';
|
|
4
|
-
import path from 'path';
|
|
5
|
-
|
|
6
|
-
function validateSpotifyConfig(config: SpotifyConfig) {
|
|
7
|
-
if (!config.clientID) throw new Error("Spotify clientID is required")
|
|
8
|
-
if (!config.scheme) throw new Error("URL scheme is required")
|
|
9
|
-
if (!config.callback) throw new Error("Callback path is required")
|
|
10
|
-
if (!config.tokenSwapURL) throw new Error("Token swap URL is required")
|
|
11
|
-
if (!config.tokenRefreshURL) throw new Error("Token refresh URL is required")
|
|
12
|
-
if (!Array.isArray(config.scopes) || config.scopes.length === 0) {
|
|
13
|
-
throw new Error("At least one scope is required")
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const withSpotifyOAuthConfigIOS: ConfigPlugin<SpotifyConfig> = (config, spotifyConfig) => {
|
|
18
|
-
validateSpotifyConfig(spotifyConfig);
|
|
19
|
-
|
|
20
|
-
return withInfoPlist(config, (config) => {
|
|
21
|
-
config.modResults.SpotifyClientID = spotifyConfig.clientID;
|
|
22
|
-
config.modResults.SpotifyScheme = spotifyConfig.scheme;
|
|
23
|
-
config.modResults.SpotifyCallback = spotifyConfig.callback;
|
|
24
|
-
config.modResults.SpotifyScopes = spotifyConfig.scopes;
|
|
25
|
-
config.modResults.tokenRefreshURL = spotifyConfig.tokenRefreshURL;
|
|
26
|
-
config.modResults.tokenSwapURL = spotifyConfig.tokenSwapURL;
|
|
27
|
-
|
|
28
|
-
// Add URL scheme to Info.plist
|
|
29
|
-
const urlTypes = config.modResults.CFBundleURLTypes || [];
|
|
30
|
-
urlTypes.push({
|
|
31
|
-
CFBundleURLSchemes: [spotifyConfig.scheme],
|
|
32
|
-
CFBundleURLName: spotifyConfig.scheme
|
|
33
|
-
});
|
|
34
|
-
config.modResults.CFBundleURLTypes = urlTypes;
|
|
35
|
-
|
|
36
|
-
return config;
|
|
37
|
-
});
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
const withCleanup: ConfigPlugin = (config) => {
|
|
41
|
-
return withDangerousMod(config, [
|
|
42
|
-
'ios',
|
|
43
|
-
async (config) => {
|
|
44
|
-
const platformRoot = path.join(config.modRequest.projectRoot, 'ios');
|
|
45
|
-
|
|
46
|
-
// Clean build directories
|
|
47
|
-
const buildDirs = [
|
|
48
|
-
path.join(platformRoot, 'build'),
|
|
49
|
-
path.join(platformRoot, 'Pods'),
|
|
50
|
-
path.join(platformRoot, 'DerivedData')
|
|
51
|
-
];
|
|
52
|
-
|
|
53
|
-
for (const dir of buildDirs) {
|
|
54
|
-
if (fs.existsSync(dir)) {
|
|
55
|
-
fs.rmdirSync(dir, { recursive: true });
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
return config;
|
|
60
|
-
},
|
|
61
|
-
]);
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
const withSpotifyAuth: ConfigPlugin<SpotifyConfig> = (config, props) => {
|
|
65
|
-
// Ensure the config exists
|
|
66
|
-
if (!props) {
|
|
67
|
-
throw new Error(
|
|
68
|
-
'Missing Spotify configuration. Please provide clientID, scheme, callback, tokenSwapURL, tokenRefreshURL, and scopes in your app.config.js/app.json.'
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return withPlugins(config, [
|
|
73
|
-
// Clean up first
|
|
74
|
-
withCleanup,
|
|
75
|
-
|
|
76
|
-
// Apply Spotify configurations
|
|
77
|
-
[withSpotifyOAuthConfigIOS, props]
|
|
78
|
-
]);
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
export default createRunOncePlugin(withSpotifyAuth, 'spotify-auth', '0.1.21');
|