@superfan-app/spotify-auth 0.1.13
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 +68 -0
- package/app.plugin.js +1 -0
- package/build/SpotifyAuth.types.d.ts +29 -0
- package/build/SpotifyAuth.types.d.ts.map +1 -0
- package/build/SpotifyAuth.types.js +6 -0
- package/build/SpotifyAuth.types.js.map +1 -0
- package/build/SpotifyAuthModule.d.ts +4 -0
- package/build/SpotifyAuthModule.d.ts.map +1 -0
- package/build/SpotifyAuthModule.js +5 -0
- package/build/SpotifyAuthModule.js.map +1 -0
- package/build/SpotifyAuthView.d.ts +3 -0
- package/build/SpotifyAuthView.d.ts.map +1 -0
- package/build/SpotifyAuthView.js +7 -0
- package/build/SpotifyAuthView.js.map +1 -0
- package/build/index.d.ts +9 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +41 -0
- package/build/index.js.map +1 -0
- package/package.json +53 -0
- package/plugin/build/index.d.ts +4 -0
- package/plugin/build/index.js +13 -0
- package/plugin/build/ios/withSpotifyQueryScheme.d.ts +3 -0
- package/plugin/build/ios/withSpotifyQueryScheme.js +15 -0
- package/plugin/build/ios/withSpotifyURLScheme.d.ts +3 -0
- package/plugin/build/ios/withSpotifyURLScheme.js +19 -0
- package/plugin/build/types.d.ts +9 -0
- package/plugin/build/types.js +2 -0
- package/plugin/build/withSpotifyConfig.d.ts +3 -0
- package/plugin/build/withSpotifyConfig.js +17 -0
- package/plugin/src/index.ts +18 -0
- package/plugin/src/ios/withSpotifyQueryScheme.ts +18 -0
- package/plugin/src/ios/withSpotifyURLScheme.ts +23 -0
- package/plugin/src/types.ts +28 -0
- package/plugin/src/withSpotifyConfig.ts +19 -0
- package/plugin/tsconfig.json +9 -0
- package/plugin/tsconfig.tsbuildinfo +1 -0
- package/src/SpotifyAuth.types.ts +36 -0
- package/src/SpotifyAuthModule.ts +6 -0
- package/src/SpotifyAuthView.tsx +14 -0
- package/src/index.tsx +70 -0
package/README.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# spotify-auth
|
|
2
|
+
|
|
3
|
+
A React Native module for Spotify authentication, built with Expo modules.
|
|
4
|
+
|
|
5
|
+
# Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @superfan-app/spotify-auth
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
# Configuration
|
|
12
|
+
|
|
13
|
+
### iOS
|
|
14
|
+
|
|
15
|
+
1. Run `npx pod-install` after installing the npm package.
|
|
16
|
+
|
|
17
|
+
2. Add the following to your `Info.plist`:
|
|
18
|
+
|
|
19
|
+
```xml
|
|
20
|
+
<key>SpotifyClientID</key>
|
|
21
|
+
<string>YOUR_CLIENT_ID</string>
|
|
22
|
+
<key>SpotifyScheme</key>
|
|
23
|
+
<string>YOUR_URL_SCHEME</string>
|
|
24
|
+
<key>SpotifyCallback</key>
|
|
25
|
+
<string>YOUR_CALLBACK_PATH</string>
|
|
26
|
+
<key>SpotifyScopes</key>
|
|
27
|
+
<array>
|
|
28
|
+
<string>user-read-private</string>
|
|
29
|
+
<!-- Add other required scopes -->
|
|
30
|
+
</array>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
# Usage
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
import { SpotifyAuthProvider, useSpotifyAuth } from '@superfan-app/spotify-auth';
|
|
37
|
+
|
|
38
|
+
// Wrap your app with the provider
|
|
39
|
+
function App() {
|
|
40
|
+
return (
|
|
41
|
+
<SpotifyAuthProvider>
|
|
42
|
+
<YourApp />
|
|
43
|
+
</SpotifyAuthProvider>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Use the hook in your components
|
|
48
|
+
function YourApp() {
|
|
49
|
+
const { accessToken, authorize } = useSpotifyAuth();
|
|
50
|
+
|
|
51
|
+
const handleLogin = () => {
|
|
52
|
+
// Optional playURI to start playing after auth
|
|
53
|
+
authorize();
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
if (!accessToken) {
|
|
57
|
+
return <Button onPress={handleLogin} title="Login with Spotify" />;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return <YourAuthenticatedApp />;
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The module provides:
|
|
65
|
+
- `SpotifyAuthProvider`: Context provider that manages the Spotify authentication state
|
|
66
|
+
- `useSpotifyAuth`: Hook that provides:
|
|
67
|
+
- `accessToken`: Current Spotify access token (null if not authenticated)
|
|
68
|
+
- `authorize(playURI?: string)`: Function to initiate Spotify authorization
|
package/app.plugin.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('./plugin/build');
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Data returned from the Spotify authorization process
|
|
3
|
+
*/
|
|
4
|
+
export interface SpotifyAuthorizationData {
|
|
5
|
+
/** Whether the authorization was successful */
|
|
6
|
+
success: boolean;
|
|
7
|
+
/** The access token if authorization was successful, null otherwise */
|
|
8
|
+
token: string | null;
|
|
9
|
+
/** Error message if authorization failed */
|
|
10
|
+
error?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Props for the SpotifyAuthView component
|
|
14
|
+
*/
|
|
15
|
+
export interface SpotifyAuthViewProps {
|
|
16
|
+
/** The name identifier for the auth view */
|
|
17
|
+
name: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Context for Spotify authentication state and actions
|
|
21
|
+
*/
|
|
22
|
+
export interface SpotifyAuthContext {
|
|
23
|
+
/** The current Spotify access token, null if not authenticated */
|
|
24
|
+
accessToken: string | null;
|
|
25
|
+
/** Function to initiate Spotify authorization */
|
|
26
|
+
authorize: (playURI?: string) => void;
|
|
27
|
+
}
|
|
28
|
+
export declare const SpotifyAuthContextInstance: import("react").Context<SpotifyAuthContext>;
|
|
29
|
+
//# sourceMappingURL=SpotifyAuth.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpotifyAuth.types.d.ts","sourceRoot":"","sources":["../src/SpotifyAuth.types.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,+CAA+C;IAC/C,OAAO,EAAE,OAAO,CAAC;IACjB,uEAAuE;IACvE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,4CAA4C;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,4CAA4C;IAC5C,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,kEAAkE;IAClE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,iDAAiD;IACjD,SAAS,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC;AAED,eAAO,MAAM,0BAA0B,6CAGrC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpotifyAuth.types.js","sourceRoot":"","sources":["../src/SpotifyAuth.types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAgCtC,MAAM,CAAC,MAAM,0BAA0B,GAAG,aAAa,CAAqB;IAC1E,WAAW,EAAE,IAAI;IACjB,SAAS,EAAE,GAAG,EAAE,GAAE,CAAC;CACpB,CAAC,CAAC","sourcesContent":["import { createContext } from \"react\";\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 * 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: (playURI?: string) => void;\n}\n\nexport const SpotifyAuthContextInstance = createContext<SpotifyAuthContext>({\n accessToken: null,\n authorize: () => {},\n});\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpotifyAuthModule.d.ts","sourceRoot":"","sources":["../src/SpotifyAuthModule.ts"],"names":[],"mappings":";AAGA,wBAAkD;AAElD,eAAO,MAAM,aAAa,EAAG,eAAwB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpotifyAuthModule.js","sourceRoot":"","sources":["../src/SpotifyAuthModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAExD,yDAAyD;AACzD,eAAe,mBAAmB,CAAC,aAAa,CAAC,CAAC;AAElD,MAAM,CAAC,MAAM,aAAa,GAAG,eAAwB,CAAC","sourcesContent":["import { requireNativeModule } from \"expo-modules-core\";\n\n// This call loads the native module object from the JSI.\nexport default requireNativeModule(\"SpotifyAuth\");\n\nexport const AuthEventName = \"onSpotifyAuth\" as const;\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpotifyAuthView.d.ts","sourceRoot":"","sources":["../src/SpotifyAuthView.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAK3D,MAAM,CAAC,OAAO,UAAU,eAAe,CACrC,KAAK,EAAE,oBAAoB,GAC1B,GAAG,CAAC,OAAO,CAEb"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { requireNativeViewManager } from "expo-modules-core";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
const NativeView = requireNativeViewManager("SpotifyAuth");
|
|
4
|
+
export default function SpotifyAuthView(props) {
|
|
5
|
+
return <NativeView {...props}/>;
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=SpotifyAuthView.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpotifyAuthView.js","sourceRoot":"","sources":["../src/SpotifyAuthView.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAK/B,MAAM,UAAU,GACd,wBAAwB,CAAC,aAAa,CAAC,CAAC;AAE1C,MAAM,CAAC,OAAO,UAAU,eAAe,CACrC,KAA2B;IAE3B,OAAO,CAAC,UAAU,CAAC,IAAI,KAAK,CAAC,EAAG,CAAC;AACnC,CAAC","sourcesContent":["import { requireNativeViewManager } from \"expo-modules-core\";\nimport * as React from \"react\";\nimport { ViewProps } from \"react-native\";\n\nimport { SpotifyAuthViewProps } from \"./SpotifyAuth.types\";\n\nconst NativeView: React.ComponentType<SpotifyAuthViewProps & ViewProps> =\n requireNativeViewManager(\"SpotifyAuth\");\n\nexport default function SpotifyAuthView(\n props: SpotifyAuthViewProps,\n): JSX.Element {\n return <NativeView {...props} />;\n}\n"]}
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { SpotifyAuthContext } from "./SpotifyAuth.types";
|
|
3
|
+
interface SpotifyAuthProviderProps {
|
|
4
|
+
children: React.ReactNode;
|
|
5
|
+
}
|
|
6
|
+
export declare function SpotifyAuthProvider({ children, }: SpotifyAuthProviderProps): JSX.Element;
|
|
7
|
+
export declare function useSpotifyAuth(): SpotifyAuthContext;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAA0C,MAAM,OAAO,CAAC;AAE/D,OAAO,EAEL,kBAAkB,EAEnB,MAAM,qBAAqB,CAAC;AA4B7B,UAAU,wBAAwB;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED,wBAAgB,mBAAmB,CAAC,EAClC,QAAQ,GACT,EAAE,wBAAwB,GAAG,GAAG,CAAC,OAAO,CAoBxC;AAED,wBAAgB,cAAc,IAAI,kBAAkB,CAMnD"}
|
package/build/index.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { EventEmitter } from "expo-modules-core";
|
|
2
|
+
import React, { useContext, useEffect, useState } from "react";
|
|
3
|
+
import { SpotifyAuthContextInstance, } from "./SpotifyAuth.types";
|
|
4
|
+
import SpotifyAuthModule from "./SpotifyAuthModule";
|
|
5
|
+
// Create a properly typed emitter
|
|
6
|
+
const emitter = new EventEmitter();
|
|
7
|
+
function addAuthListener(listener) {
|
|
8
|
+
// Assert the event name is of the correct type
|
|
9
|
+
const eventName = SpotifyAuthModule.AuthEventName;
|
|
10
|
+
return emitter.addListener(eventName, listener);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Prompts the user to log in to Spotify and authorize your application.
|
|
14
|
+
* @param playURI Optional URI to play after authorization
|
|
15
|
+
*/
|
|
16
|
+
function authorize(playURI) {
|
|
17
|
+
SpotifyAuthModule.authorize(playURI);
|
|
18
|
+
}
|
|
19
|
+
export function SpotifyAuthProvider({ children, }) {
|
|
20
|
+
const [token, setToken] = useState(null);
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
const subscription = addAuthListener((data) => {
|
|
23
|
+
setToken(data.token);
|
|
24
|
+
if (data.error) {
|
|
25
|
+
console.error(`Spotify auth error: ${data.error}`);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
return () => subscription.remove();
|
|
29
|
+
}, []);
|
|
30
|
+
return (<SpotifyAuthContextInstance.Provider value={{ accessToken: token, authorize }}>
|
|
31
|
+
{children}
|
|
32
|
+
</SpotifyAuthContextInstance.Provider>);
|
|
33
|
+
}
|
|
34
|
+
export function useSpotifyAuth() {
|
|
35
|
+
const context = useContext(SpotifyAuthContextInstance);
|
|
36
|
+
if (!context) {
|
|
37
|
+
throw new Error("useSpotifyAuth must be used within a SpotifyAuthProvider");
|
|
38
|
+
}
|
|
39
|
+
return context;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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,MAAM,OAAO,CAAC;AAE/D,OAAO,EAGL,0BAA0B,GAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,iBAAiB,MAAM,qBAAqB,CAAC;AAUpD,kCAAkC;AAClC,MAAM,OAAO,GAAG,IAAI,YAAY,EAAiB,CAAC;AAElD,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;;;GAGG;AACH,SAAS,SAAS,CAAC,OAAgB;IACjC,iBAAiB,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;AACvC,CAAC;AAMD,MAAM,UAAU,mBAAmB,CAAC,EAClC,QAAQ,GACiB;IACzB,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IAExD,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,CAAC,CAEzC;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 } from \"react\";\n\nimport {\n SpotifyAuthorizationData,\n SpotifyAuthContext,\n SpotifyAuthContextInstance,\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// Then use that type for events\ntype SpotifyEvents = {\n [K in SpotifyAuthEventName]: (data: SpotifyAuthorizationData) => void;\n};\n\n// Create a properly typed emitter\nconst emitter = new EventEmitter<SpotifyEvents>();\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 * @param playURI Optional URI to play after authorization\n */\nfunction authorize(playURI?: string): void {\n SpotifyAuthModule.authorize(playURI);\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\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 }}\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/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@superfan-app/spotify-auth",
|
|
3
|
+
"version": "0.1.13",
|
|
4
|
+
"description": "Spotify OAuth module for Expo",
|
|
5
|
+
"main": "build/index.js",
|
|
6
|
+
"module": "build/index.js",
|
|
7
|
+
"types": "build/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"build",
|
|
10
|
+
"app.plugin.js",
|
|
11
|
+
"plugin",
|
|
12
|
+
"src"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "expo-module build",
|
|
16
|
+
"clean": "expo-module clean",
|
|
17
|
+
"lint": "expo-module lint",
|
|
18
|
+
"test": "expo-module test",
|
|
19
|
+
"prepare": "expo-module prepare",
|
|
20
|
+
"prepublishOnly": "expo-module prepublishOnly",
|
|
21
|
+
"publish:npm": "npm publish --access public",
|
|
22
|
+
"expo-module": "expo-module",
|
|
23
|
+
"open:ios": "xed example/ios"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"react-native",
|
|
27
|
+
"expo",
|
|
28
|
+
"spotify-auth",
|
|
29
|
+
"SpotifyAuth"
|
|
30
|
+
],
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "https://github.com/Superfan-App/spotify-auth"
|
|
34
|
+
},
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"bugs": {
|
|
39
|
+
"url": "https://github.com/Superfan-App/spotify-auth/issues"
|
|
40
|
+
},
|
|
41
|
+
"author": "Superfan <support@superfan.app>",
|
|
42
|
+
"license": "MIT",
|
|
43
|
+
"homepage": "https://github.com/Superfan-App/spotify-auth#readme",
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@expo/config-plugins": "^9.0.11"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/react": "~18.3.12",
|
|
49
|
+
"@types/react-native": "^0.73.0",
|
|
50
|
+
"expo-module-scripts": "^4.0.2",
|
|
51
|
+
"expo-modules-core": "^2.1.1"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const withSpotifyQueryScheme_1 = require("./ios/withSpotifyQueryScheme");
|
|
4
|
+
const withSpotifyURLScheme_1 = require("./ios/withSpotifyURLScheme");
|
|
5
|
+
const withSpotifyConfig_1 = require("./withSpotifyConfig");
|
|
6
|
+
const withSpotifyRemote = (config, props) => {
|
|
7
|
+
config = (0, withSpotifyConfig_1.withSpotifyConfig)(config, props);
|
|
8
|
+
// iOS specific
|
|
9
|
+
config = (0, withSpotifyQueryScheme_1.withSpotifyQueryScheme)(config, props);
|
|
10
|
+
config = (0, withSpotifyURLScheme_1.withSpotifyURLScheme)(config, props);
|
|
11
|
+
return config;
|
|
12
|
+
};
|
|
13
|
+
exports.default = withSpotifyRemote;
|
|
@@ -0,0 +1,15 @@
|
|
|
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;
|
|
@@ -0,0 +1,19 @@
|
|
|
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
|
+
const withSpotifyURLScheme = (config, { scheme }) => {
|
|
6
|
+
return (0, config_plugins_1.withInfoPlist)(config, (config) => {
|
|
7
|
+
const bundleId = config.ios?.bundleIdentifier;
|
|
8
|
+
const urlType = {
|
|
9
|
+
CFBundleURLName: bundleId,
|
|
10
|
+
CFBundleURLSchemes: [scheme],
|
|
11
|
+
};
|
|
12
|
+
if (!config.modResults.CFBundleURLTypes) {
|
|
13
|
+
config.modResults.CFBundleURLTypes = [];
|
|
14
|
+
}
|
|
15
|
+
config.modResults.CFBundleURLTypes.push(urlType);
|
|
16
|
+
return config;
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
exports.withSpotifyURLScheme = withSpotifyURLScheme;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type SpotifyScopes = 'app-remote-control' | 'playlist-modify-private' | 'playlist-modify-public' | 'playlist-read-collaborative' | 'playlist-read-private' | 'streaming' | 'user-follow-modify' | 'user-follow-read' | 'user-library-modify' | 'user-library-read' | 'user-modify-playback-state' | 'user-read-currently-playing' | 'user-read-email' | 'user-read-playback-position' | 'user-read-playback-state' | 'user-read-private' | 'user-read-recently-played' | 'user-top-read';
|
|
2
|
+
export interface ISpotifyConfig {
|
|
3
|
+
clientID: string;
|
|
4
|
+
tokenRefreshURL: string;
|
|
5
|
+
tokenSwapURL: string;
|
|
6
|
+
scheme: string;
|
|
7
|
+
callback: string;
|
|
8
|
+
scopes: SpotifyScopes[];
|
|
9
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.withSpotifyConfig = void 0;
|
|
4
|
+
const config_plugins_1 = require("expo/config-plugins");
|
|
5
|
+
const withSpotifyConfigIOS = (config, spotifyConfig) => {
|
|
6
|
+
return (0, config_plugins_1.withInfoPlist)(config, (config) => {
|
|
7
|
+
Object.entries(spotifyConfig).forEach(([key, value]) => {
|
|
8
|
+
config.modResults[key] = value;
|
|
9
|
+
});
|
|
10
|
+
return config;
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
const withSpotifyConfig = (config, spotifyConfig) => {
|
|
14
|
+
config = withSpotifyConfigIOS(config, spotifyConfig);
|
|
15
|
+
return config;
|
|
16
|
+
};
|
|
17
|
+
exports.withSpotifyConfig = withSpotifyConfig;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ConfigPlugin } from '@expo/config-plugins'
|
|
2
|
+
|
|
3
|
+
import { withSpotifyQueryScheme } from './ios/withSpotifyQueryScheme'
|
|
4
|
+
import { withSpotifyURLScheme } from './ios/withSpotifyURLScheme'
|
|
5
|
+
import { ISpotifyConfig } from './types'
|
|
6
|
+
import { withSpotifyConfig } from './withSpotifyConfig'
|
|
7
|
+
|
|
8
|
+
const withSpotifyRemote: ConfigPlugin<ISpotifyConfig> = (config, props) => {
|
|
9
|
+
config = withSpotifyConfig(config, props)
|
|
10
|
+
|
|
11
|
+
// iOS specific
|
|
12
|
+
config = withSpotifyQueryScheme(config, props)
|
|
13
|
+
config = withSpotifyURLScheme(config, props)
|
|
14
|
+
|
|
15
|
+
return config
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default withSpotifyRemote
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ConfigPlugin, withInfoPlist } from "expo/config-plugins";
|
|
2
|
+
|
|
3
|
+
import { ISpotifyConfig } from "../types";
|
|
4
|
+
|
|
5
|
+
const spotifyScheme = "spotify";
|
|
6
|
+
|
|
7
|
+
export const withSpotifyQueryScheme: ConfigPlugin<ISpotifyConfig> = (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
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ConfigPlugin, withInfoPlist } from "expo/config-plugins";
|
|
2
|
+
|
|
3
|
+
import { ISpotifyConfig } from "../types";
|
|
4
|
+
|
|
5
|
+
export const withSpotifyURLScheme: ConfigPlugin<ISpotifyConfig> = (
|
|
6
|
+
config,
|
|
7
|
+
{ scheme }
|
|
8
|
+
) => {
|
|
9
|
+
return withInfoPlist(config, (config) => {
|
|
10
|
+
const bundleId = config.ios?.bundleIdentifier;
|
|
11
|
+
const urlType = {
|
|
12
|
+
CFBundleURLName: bundleId,
|
|
13
|
+
CFBundleURLSchemes: [scheme],
|
|
14
|
+
};
|
|
15
|
+
if (!config.modResults.CFBundleURLTypes) {
|
|
16
|
+
config.modResults.CFBundleURLTypes = [];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
config.modResults.CFBundleURLTypes.push(urlType);
|
|
20
|
+
|
|
21
|
+
return config;
|
|
22
|
+
});
|
|
23
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type SpotifyScopes =
|
|
2
|
+
| 'app-remote-control'
|
|
3
|
+
| 'playlist-modify-private'
|
|
4
|
+
| 'playlist-modify-public'
|
|
5
|
+
| 'playlist-read-collaborative'
|
|
6
|
+
| 'playlist-read-private'
|
|
7
|
+
| 'streaming'
|
|
8
|
+
| 'user-follow-modify'
|
|
9
|
+
| 'user-follow-read'
|
|
10
|
+
| 'user-library-modify'
|
|
11
|
+
| 'user-library-read'
|
|
12
|
+
| 'user-modify-playback-state'
|
|
13
|
+
| 'user-read-currently-playing'
|
|
14
|
+
| 'user-read-email'
|
|
15
|
+
| 'user-read-playback-position'
|
|
16
|
+
| 'user-read-playback-state'
|
|
17
|
+
| 'user-read-private'
|
|
18
|
+
| 'user-read-recently-played'
|
|
19
|
+
| 'user-top-read'
|
|
20
|
+
|
|
21
|
+
export interface ISpotifyConfig {
|
|
22
|
+
clientID: string
|
|
23
|
+
tokenRefreshURL: string
|
|
24
|
+
tokenSwapURL: string
|
|
25
|
+
scheme: string
|
|
26
|
+
callback: string
|
|
27
|
+
scopes: SpotifyScopes[]
|
|
28
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ConfigPlugin, withInfoPlist } from 'expo/config-plugins'
|
|
2
|
+
|
|
3
|
+
import { ISpotifyConfig } from './types'
|
|
4
|
+
|
|
5
|
+
const withSpotifyConfigIOS: ConfigPlugin<ISpotifyConfig> = (config, spotifyConfig) => {
|
|
6
|
+
return withInfoPlist(config, (config) => {
|
|
7
|
+
Object.entries(spotifyConfig).forEach(([key, value]) => {
|
|
8
|
+
config.modResults[key] = value
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
return config
|
|
12
|
+
})
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const withSpotifyConfig: ConfigPlugin<ISpotifyConfig> = (config, spotifyConfig) => {
|
|
16
|
+
config = withSpotifyConfigIOS(config, spotifyConfig)
|
|
17
|
+
|
|
18
|
+
return config
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"root":["./src/index.ts","./src/types.ts","./src/withspotifyconfig.ts","./src/ios/withspotifyqueryscheme.ts","./src/ios/withspotifyurlscheme.ts"],"version":"5.7.2"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { createContext } from "react";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Data returned from the Spotify authorization process
|
|
5
|
+
*/
|
|
6
|
+
export interface SpotifyAuthorizationData {
|
|
7
|
+
/** Whether the authorization was successful */
|
|
8
|
+
success: boolean;
|
|
9
|
+
/** The access token if authorization was successful, null otherwise */
|
|
10
|
+
token: string | null;
|
|
11
|
+
/** Error message if authorization failed */
|
|
12
|
+
error?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Props for the SpotifyAuthView component
|
|
17
|
+
*/
|
|
18
|
+
export interface SpotifyAuthViewProps {
|
|
19
|
+
/** The name identifier for the auth view */
|
|
20
|
+
name: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Context for Spotify authentication state and actions
|
|
25
|
+
*/
|
|
26
|
+
export interface SpotifyAuthContext {
|
|
27
|
+
/** The current Spotify access token, null if not authenticated */
|
|
28
|
+
accessToken: string | null;
|
|
29
|
+
/** Function to initiate Spotify authorization */
|
|
30
|
+
authorize: (playURI?: string) => void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const SpotifyAuthContextInstance = createContext<SpotifyAuthContext>({
|
|
34
|
+
accessToken: null,
|
|
35
|
+
authorize: () => {},
|
|
36
|
+
});
|
|
@@ -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,70 @@
|
|
|
1
|
+
import { EventEmitter } from "expo-modules-core";
|
|
2
|
+
import React, { useContext, useEffect, useState } from "react";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
SpotifyAuthorizationData,
|
|
6
|
+
SpotifyAuthContext,
|
|
7
|
+
SpotifyAuthContextInstance,
|
|
8
|
+
} from "./SpotifyAuth.types";
|
|
9
|
+
import SpotifyAuthModule from "./SpotifyAuthModule";
|
|
10
|
+
|
|
11
|
+
// First define the event name as a string literal type
|
|
12
|
+
type SpotifyAuthEventName = "onSpotifyAuth"; // This should match SpotifyAuthModule.AuthEventName
|
|
13
|
+
|
|
14
|
+
// Then use that type for events
|
|
15
|
+
type SpotifyEvents = {
|
|
16
|
+
[K in SpotifyAuthEventName]: (data: SpotifyAuthorizationData) => void;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// Create a properly typed emitter
|
|
20
|
+
const emitter = new EventEmitter<SpotifyEvents>();
|
|
21
|
+
|
|
22
|
+
function addAuthListener(listener: (data: SpotifyAuthorizationData) => void) {
|
|
23
|
+
// Assert the event name is of the correct type
|
|
24
|
+
const eventName = SpotifyAuthModule.AuthEventName as SpotifyAuthEventName;
|
|
25
|
+
return emitter.addListener(eventName, listener);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Prompts the user to log in to Spotify and authorize your application.
|
|
30
|
+
* @param playURI Optional URI to play after authorization
|
|
31
|
+
*/
|
|
32
|
+
function authorize(playURI?: string): void {
|
|
33
|
+
SpotifyAuthModule.authorize(playURI);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface SpotifyAuthProviderProps {
|
|
37
|
+
children: React.ReactNode;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function SpotifyAuthProvider({
|
|
41
|
+
children,
|
|
42
|
+
}: SpotifyAuthProviderProps): JSX.Element {
|
|
43
|
+
const [token, setToken] = useState<string | null>(null);
|
|
44
|
+
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
const subscription = addAuthListener((data) => {
|
|
47
|
+
setToken(data.token);
|
|
48
|
+
if (data.error) {
|
|
49
|
+
console.error(`Spotify auth error: ${data.error}`);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
return () => subscription.remove();
|
|
53
|
+
}, []);
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<SpotifyAuthContextInstance.Provider
|
|
57
|
+
value={{ accessToken: token, authorize }}
|
|
58
|
+
>
|
|
59
|
+
{children}
|
|
60
|
+
</SpotifyAuthContextInstance.Provider>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function useSpotifyAuth(): SpotifyAuthContext {
|
|
65
|
+
const context = useContext(SpotifyAuthContextInstance);
|
|
66
|
+
if (!context) {
|
|
67
|
+
throw new Error("useSpotifyAuth must be used within a SpotifyAuthProvider");
|
|
68
|
+
}
|
|
69
|
+
return context;
|
|
70
|
+
}
|