@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.
Files changed (40) hide show
  1. package/README.md +68 -0
  2. package/app.plugin.js +1 -0
  3. package/build/SpotifyAuth.types.d.ts +29 -0
  4. package/build/SpotifyAuth.types.d.ts.map +1 -0
  5. package/build/SpotifyAuth.types.js +6 -0
  6. package/build/SpotifyAuth.types.js.map +1 -0
  7. package/build/SpotifyAuthModule.d.ts +4 -0
  8. package/build/SpotifyAuthModule.d.ts.map +1 -0
  9. package/build/SpotifyAuthModule.js +5 -0
  10. package/build/SpotifyAuthModule.js.map +1 -0
  11. package/build/SpotifyAuthView.d.ts +3 -0
  12. package/build/SpotifyAuthView.d.ts.map +1 -0
  13. package/build/SpotifyAuthView.js +7 -0
  14. package/build/SpotifyAuthView.js.map +1 -0
  15. package/build/index.d.ts +9 -0
  16. package/build/index.d.ts.map +1 -0
  17. package/build/index.js +41 -0
  18. package/build/index.js.map +1 -0
  19. package/package.json +53 -0
  20. package/plugin/build/index.d.ts +4 -0
  21. package/plugin/build/index.js +13 -0
  22. package/plugin/build/ios/withSpotifyQueryScheme.d.ts +3 -0
  23. package/plugin/build/ios/withSpotifyQueryScheme.js +15 -0
  24. package/plugin/build/ios/withSpotifyURLScheme.d.ts +3 -0
  25. package/plugin/build/ios/withSpotifyURLScheme.js +19 -0
  26. package/plugin/build/types.d.ts +9 -0
  27. package/plugin/build/types.js +2 -0
  28. package/plugin/build/withSpotifyConfig.d.ts +3 -0
  29. package/plugin/build/withSpotifyConfig.js +17 -0
  30. package/plugin/src/index.ts +18 -0
  31. package/plugin/src/ios/withSpotifyQueryScheme.ts +18 -0
  32. package/plugin/src/ios/withSpotifyURLScheme.ts +23 -0
  33. package/plugin/src/types.ts +28 -0
  34. package/plugin/src/withSpotifyConfig.ts +19 -0
  35. package/plugin/tsconfig.json +9 -0
  36. package/plugin/tsconfig.tsbuildinfo +1 -0
  37. package/src/SpotifyAuth.types.ts +36 -0
  38. package/src/SpotifyAuthModule.ts +6 -0
  39. package/src/SpotifyAuthView.tsx +14 -0
  40. 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,6 @@
1
+ import { createContext } from "react";
2
+ export const SpotifyAuthContextInstance = createContext({
3
+ accessToken: null,
4
+ authorize: () => { },
5
+ });
6
+ //# sourceMappingURL=SpotifyAuth.types.js.map
@@ -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,4 @@
1
+ declare const _default: any;
2
+ export default _default;
3
+ export declare const AuthEventName: "onSpotifyAuth";
4
+ //# sourceMappingURL=SpotifyAuthModule.d.ts.map
@@ -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,5 @@
1
+ import { requireNativeModule } from "expo-modules-core";
2
+ // This call loads the native module object from the JSI.
3
+ export default requireNativeModule("SpotifyAuth");
4
+ export const AuthEventName = "onSpotifyAuth";
5
+ //# sourceMappingURL=SpotifyAuthModule.js.map
@@ -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,3 @@
1
+ import { SpotifyAuthViewProps } from "./SpotifyAuth.types";
2
+ export default function SpotifyAuthView(props: SpotifyAuthViewProps): JSX.Element;
3
+ //# sourceMappingURL=SpotifyAuthView.d.ts.map
@@ -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"]}
@@ -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,4 @@
1
+ import { ConfigPlugin } from '@expo/config-plugins';
2
+ import { ISpotifyConfig } from './types';
3
+ declare const withSpotifyRemote: ConfigPlugin<ISpotifyConfig>;
4
+ export default withSpotifyRemote;
@@ -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,3 @@
1
+ import { ConfigPlugin } from "expo/config-plugins";
2
+ import { ISpotifyConfig } from "../types";
3
+ export declare const withSpotifyQueryScheme: ConfigPlugin<ISpotifyConfig>;
@@ -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,3 @@
1
+ import { ConfigPlugin } from "expo/config-plugins";
2
+ import { ISpotifyConfig } from "../types";
3
+ export declare const withSpotifyURLScheme: ConfigPlugin<ISpotifyConfig>;
@@ -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,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,3 @@
1
+ import { ConfigPlugin } from 'expo/config-plugins';
2
+ import { ISpotifyConfig } from './types';
3
+ export declare const withSpotifyConfig: ConfigPlugin<ISpotifyConfig>;
@@ -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,9 @@
1
+ {
2
+ "extends": "expo-module-scripts/tsconfig.plugin",
3
+ "compilerOptions": {
4
+ "outDir": "build",
5
+ "rootDir": "src"
6
+ },
7
+ "include": ["./src"],
8
+ "exclude": ["**/__mocks__/*", "**/__tests__/*"]
9
+ }
@@ -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,6 @@
1
+ import { requireNativeModule } from "expo-modules-core";
2
+
3
+ // This call loads the native module object from the JSI.
4
+ export default requireNativeModule("SpotifyAuth");
5
+
6
+ export const AuthEventName = "onSpotifyAuth" as const;
@@ -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
+ }