@youversion/platform-react-hooks 0.6.0 → 0.7.0
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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +38 -0
- package/README.md +3 -3
- package/dist/__tests__/mocks/auth.d.ts +12 -0
- package/dist/__tests__/mocks/auth.d.ts.map +1 -0
- package/dist/__tests__/mocks/auth.js +44 -0
- package/dist/__tests__/mocks/auth.js.map +1 -0
- package/dist/__tests__/utils/test-utils.d.ts +15 -0
- package/dist/__tests__/utils/test-utils.d.ts.map +1 -0
- package/dist/__tests__/utils/test-utils.js +24 -0
- package/dist/__tests__/utils/test-utils.js.map +1 -0
- package/dist/context/YouVersionAuthContext.d.ts +4 -0
- package/dist/context/YouVersionAuthContext.d.ts.map +1 -0
- package/dist/context/YouVersionAuthContext.js +13 -0
- package/dist/context/YouVersionAuthContext.js.map +1 -0
- package/dist/context/YouVersionAuthProvider.d.ts +8 -0
- package/dist/context/YouVersionAuthProvider.d.ts.map +1 -0
- package/dist/context/YouVersionAuthProvider.js +80 -0
- package/dist/context/YouVersionAuthProvider.js.map +1 -0
- package/dist/context/YouVersionContext.d.ts +8 -0
- package/dist/context/YouVersionContext.d.ts.map +1 -0
- package/dist/context/YouVersionContext.js +4 -0
- package/dist/context/YouVersionContext.js.map +1 -0
- package/dist/context/YouVersionProvider.d.ts +17 -0
- package/dist/context/YouVersionProvider.d.ts.map +1 -0
- package/dist/context/YouVersionProvider.js +25 -0
- package/dist/context/YouVersionProvider.js.map +1 -0
- package/dist/context/index.d.ts +2 -2
- package/dist/context/index.d.ts.map +1 -1
- package/dist/context/index.js +2 -2
- package/dist/context/index.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/types/auth.d.ts +18 -0
- package/dist/types/auth.d.ts.map +1 -0
- package/dist/types/auth.js +2 -0
- package/dist/types/auth.js.map +1 -0
- package/dist/useBibleClient.js +3 -3
- package/dist/useBibleClient.js.map +1 -1
- package/dist/useHighlights.js +3 -3
- package/dist/useHighlights.js.map +1 -1
- package/dist/useLanguages.js +3 -3
- package/dist/useLanguages.js.map +1 -1
- package/dist/usePassage.d.ts +3 -3
- package/dist/usePassage.d.ts.map +1 -1
- package/dist/usePassage.js +5 -5
- package/dist/usePassage.js.map +1 -1
- package/dist/useYVAuth.d.ts +97 -0
- package/dist/useYVAuth.d.ts.map +1 -0
- package/dist/useYVAuth.js +135 -0
- package/dist/useYVAuth.js.map +1 -0
- package/package.json +5 -5
- package/src/__tests__/mocks/auth.ts +48 -0
- package/src/__tests__/utils/test-utils.tsx +43 -0
- package/src/context/YouVersionAuthContext.test.tsx +88 -0
- package/src/context/YouVersionAuthContext.tsx +20 -0
- package/src/context/YouVersionAuthProvider.test.tsx +373 -0
- package/src/context/YouVersionAuthProvider.tsx +90 -0
- package/src/context/{BibleSDKContext.tsx → YouVersionContext.tsx} +2 -2
- package/src/context/YouVersionProvider.tsx +65 -0
- package/src/context/index.ts +2 -2
- package/src/index.ts +6 -0
- package/src/types/auth.ts +20 -0
- package/src/useBibleClient.test.tsx +14 -14
- package/src/useBibleClient.ts +3 -3
- package/src/useHighlights.test.tsx +6 -6
- package/src/useHighlights.ts +3 -3
- package/src/useLanguages.test.tsx +6 -6
- package/src/useLanguages.ts +3 -3
- package/src/usePassage.ts +8 -15
- package/src/useVOTD.test.tsx +6 -6
- package/src/useYVAuth.test.tsx +378 -0
- package/src/useYVAuth.ts +179 -0
- package/dist/context/BibleSDKContext.d.ts +0 -8
- package/dist/context/BibleSDKContext.d.ts.map +0 -1
- package/dist/context/BibleSDKContext.js +0 -4
- package/dist/context/BibleSDKContext.js.map +0 -1
- package/dist/context/BibleSDKProvider.d.ts +0 -9
- package/dist/context/BibleSDKProvider.d.ts.map +0 -1
- package/dist/context/BibleSDKProvider.js +0 -18
- package/dist/context/BibleSDKProvider.js.map +0 -1
- package/src/context/BibleSDKProvider.tsx +0 -35
package/dist/usePassage.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
import { useBibleClient } from
|
|
3
|
-
import { useApiData } from
|
|
4
|
-
export function usePassage({ versionId, usfm, format =
|
|
1
|
+
'use client';
|
|
2
|
+
import { useBibleClient } from './useBibleClient';
|
|
3
|
+
import { useApiData } from './useApiData';
|
|
4
|
+
export function usePassage({ versionId, usfm, format = 'html', include_headings = false, include_notes = false, options, }) {
|
|
5
5
|
const bibleClient = useBibleClient();
|
|
6
6
|
// Don't attempt to fetch if usfm is invalid
|
|
7
|
-
const isValidUsfm = Boolean(usfm) && usfm !==
|
|
7
|
+
const isValidUsfm = Boolean(usfm) && usfm !== 'undefined' && usfm !== 'null';
|
|
8
8
|
const { data, loading, error, refetch } = useApiData(() => bibleClient.getPassage(versionId, usfm, format, include_headings, include_notes), [bibleClient, versionId, usfm, format, include_headings, include_notes], { enabled: options?.enabled !== false && isValidUsfm });
|
|
9
9
|
return { passage: data, loading, error, refetch };
|
|
10
10
|
}
|
package/dist/usePassage.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usePassage.js","sourceRoot":"","sources":["../src/usePassage.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,UAAU,EAA0B,MAAM,cAAc,CAAC;AAYlE,MAAM,UAAU,UAAU,CAAC,EACzB,SAAS,EACT,IAAI,EACJ,MAAM,GAAG,MAAM,EACf,gBAAgB,GAAG,KAAK,EACxB,aAAa,GAAG,KAAK,EACrB,OAAO,GACS;IAMhB,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;IAErC,4CAA4C;IAC5C,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,KAAK,WAAW,IAAI,IAAI,KAAK,MAAM,CAAC;IAE7E,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,UAAU,CAClD,GAAG,EAAE,
|
|
1
|
+
{"version":3,"file":"usePassage.js","sourceRoot":"","sources":["../src/usePassage.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,UAAU,EAA0B,MAAM,cAAc,CAAC;AAYlE,MAAM,UAAU,UAAU,CAAC,EACzB,SAAS,EACT,IAAI,EACJ,MAAM,GAAG,MAAM,EACf,gBAAgB,GAAG,KAAK,EACxB,aAAa,GAAG,KAAK,EACrB,OAAO,GACS;IAMhB,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;IAErC,4CAA4C;IAC5C,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,KAAK,WAAW,IAAI,IAAI,KAAK,MAAM,CAAC;IAE7E,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,UAAU,CAClD,GAAG,EAAE,CAAC,WAAW,CAAC,UAAU,CAAC,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,gBAAgB,EAAE,aAAa,CAAC,EACtF,CAAC,WAAW,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,gBAAgB,EAAE,aAAa,CAAC,EACvE,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,KAAK,KAAK,IAAI,WAAW,EAAE,CACvD,CAAC;IAEF,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;AACpD,CAAC"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { type AuthenticationState, type SignInWithYouVersionPermissionValues, type SignInWithYouVersionResult, type YouVersionUserInfo } from '@youversion/platform-core';
|
|
2
|
+
export interface UseYVAuthReturn {
|
|
3
|
+
auth: AuthenticationState;
|
|
4
|
+
signIn: (params: {
|
|
5
|
+
redirectUrl: string;
|
|
6
|
+
permissions?: SignInWithYouVersionPermissionValues[];
|
|
7
|
+
}) => Promise<void>;
|
|
8
|
+
signOut: () => void;
|
|
9
|
+
processCallback: () => Promise<SignInWithYouVersionResult | null>;
|
|
10
|
+
userInfo: YouVersionUserInfo | null;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Comprehensive YouVersion authentication hook using vanilla React.
|
|
14
|
+
*
|
|
15
|
+
* Provides complete auth functionality without external dependencies.
|
|
16
|
+
* Sign-in redirects to YouVersion, callback processing caches user info.
|
|
17
|
+
*
|
|
18
|
+
* @returns Complete auth state and functionality
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```tsx
|
|
22
|
+
* // Sign-in page
|
|
23
|
+
* import { useYVAuth, SignInWithYouVersionPermission } from '@youversion/platform-react-hooks';
|
|
24
|
+
*
|
|
25
|
+
* function SignInPage() {
|
|
26
|
+
* const { auth, signIn } = useYVAuth();
|
|
27
|
+
*
|
|
28
|
+
* const handleSignIn = async () => {
|
|
29
|
+
* try {
|
|
30
|
+
* await signIn({
|
|
31
|
+
* redirectUrl: 'https://myapp.com/callback',
|
|
32
|
+
* permissions: [SignInWithYouVersionPermission.bibles]
|
|
33
|
+
* });
|
|
34
|
+
* } catch (error) {
|
|
35
|
+
* console.error('Sign in failed:', error);
|
|
36
|
+
* }
|
|
37
|
+
* };
|
|
38
|
+
*
|
|
39
|
+
* if (auth.isAuthenticated) {
|
|
40
|
+
* return <div>Already signed in!</div>;
|
|
41
|
+
* }
|
|
42
|
+
*
|
|
43
|
+
* return (
|
|
44
|
+
* <button onClick={handleSignIn} disabled={auth.isLoading}>
|
|
45
|
+
* {auth.isLoading ? 'Signing in...' : 'Sign In'}
|
|
46
|
+
* </button>
|
|
47
|
+
* );
|
|
48
|
+
* }
|
|
49
|
+
*
|
|
50
|
+
* // Callback page
|
|
51
|
+
* function CallbackPage() {
|
|
52
|
+
* const { processCallback } = useYVAuth();
|
|
53
|
+
* const [isProcessing, setIsProcessing] = useState(true);
|
|
54
|
+
*
|
|
55
|
+
* useEffect(() => {
|
|
56
|
+
* processCallback()
|
|
57
|
+
* .then(result => {
|
|
58
|
+
* if (result) {
|
|
59
|
+
* console.log('Auth success:', result.name);
|
|
60
|
+
* // User info is now cached
|
|
61
|
+
* // Redirect to app
|
|
62
|
+
* }
|
|
63
|
+
* })
|
|
64
|
+
* .catch(error => {
|
|
65
|
+
* console.error('Auth failed:', error);
|
|
66
|
+
* })
|
|
67
|
+
* .finally(() => {
|
|
68
|
+
* setIsProcessing(false);
|
|
69
|
+
* });
|
|
70
|
+
* }, [processCallback]);
|
|
71
|
+
*
|
|
72
|
+
* return (
|
|
73
|
+
* <div>
|
|
74
|
+
* {isProcessing ? 'Processing authentication...' : 'Authentication complete'}
|
|
75
|
+
* </div>
|
|
76
|
+
* );
|
|
77
|
+
* }
|
|
78
|
+
*
|
|
79
|
+
* // App with cached user info
|
|
80
|
+
* function App() {
|
|
81
|
+
* const { auth, userInfo, signOut } = useYVAuth();
|
|
82
|
+
*
|
|
83
|
+
* if (!auth.isAuthenticated) {
|
|
84
|
+
* return <SignInPage />;
|
|
85
|
+
* }
|
|
86
|
+
*
|
|
87
|
+
* return (
|
|
88
|
+
* <div>
|
|
89
|
+
* <p>Welcome, {userInfo?.name || 'User'}!</p>
|
|
90
|
+
* <button onClick={signOut}>Sign Out</button>
|
|
91
|
+
* </div>
|
|
92
|
+
* );
|
|
93
|
+
* }
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
export declare function useYVAuth(): UseYVAuthReturn;
|
|
97
|
+
//# sourceMappingURL=useYVAuth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useYVAuth.d.ts","sourceRoot":"","sources":["../src/useYVAuth.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,mBAAmB,EACxB,KAAK,oCAAoC,EACzC,KAAK,0BAA0B,EAC/B,KAAK,kBAAkB,EACxB,MAAM,2BAA2B,CAAC;AAGnC,MAAM,WAAW,eAAe;IAE9B,IAAI,EAAE,mBAAmB,CAAC;IAG1B,MAAM,EAAE,CAAC,MAAM,EAAE;QACf,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,CAAC,EAAE,oCAAoC,EAAE,CAAC;KACtD,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpB,OAAO,EAAE,MAAM,IAAI,CAAC;IAGpB,eAAe,EAAE,MAAM,OAAO,CAAC,0BAA0B,GAAG,IAAI,CAAC,CAAC;IAGlE,QAAQ,EAAE,kBAAkB,GAAG,IAAI,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmFG;AACH,wBAAgB,SAAS,IAAI,eAAe,CAiE3C"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { useMemo, useCallback } from 'react';
|
|
2
|
+
import { YouVersionAPIUsers, YouVersionPlatformConfiguration, } from '@youversion/platform-core';
|
|
3
|
+
import { useYouVersionAuthContext } from './context/YouVersionAuthContext';
|
|
4
|
+
/**
|
|
5
|
+
* Comprehensive YouVersion authentication hook using vanilla React.
|
|
6
|
+
*
|
|
7
|
+
* Provides complete auth functionality without external dependencies.
|
|
8
|
+
* Sign-in redirects to YouVersion, callback processing caches user info.
|
|
9
|
+
*
|
|
10
|
+
* @returns Complete auth state and functionality
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```tsx
|
|
14
|
+
* // Sign-in page
|
|
15
|
+
* import { useYVAuth, SignInWithYouVersionPermission } from '@youversion/platform-react-hooks';
|
|
16
|
+
*
|
|
17
|
+
* function SignInPage() {
|
|
18
|
+
* const { auth, signIn } = useYVAuth();
|
|
19
|
+
*
|
|
20
|
+
* const handleSignIn = async () => {
|
|
21
|
+
* try {
|
|
22
|
+
* await signIn({
|
|
23
|
+
* redirectUrl: 'https://myapp.com/callback',
|
|
24
|
+
* permissions: [SignInWithYouVersionPermission.bibles]
|
|
25
|
+
* });
|
|
26
|
+
* } catch (error) {
|
|
27
|
+
* console.error('Sign in failed:', error);
|
|
28
|
+
* }
|
|
29
|
+
* };
|
|
30
|
+
*
|
|
31
|
+
* if (auth.isAuthenticated) {
|
|
32
|
+
* return <div>Already signed in!</div>;
|
|
33
|
+
* }
|
|
34
|
+
*
|
|
35
|
+
* return (
|
|
36
|
+
* <button onClick={handleSignIn} disabled={auth.isLoading}>
|
|
37
|
+
* {auth.isLoading ? 'Signing in...' : 'Sign In'}
|
|
38
|
+
* </button>
|
|
39
|
+
* );
|
|
40
|
+
* }
|
|
41
|
+
*
|
|
42
|
+
* // Callback page
|
|
43
|
+
* function CallbackPage() {
|
|
44
|
+
* const { processCallback } = useYVAuth();
|
|
45
|
+
* const [isProcessing, setIsProcessing] = useState(true);
|
|
46
|
+
*
|
|
47
|
+
* useEffect(() => {
|
|
48
|
+
* processCallback()
|
|
49
|
+
* .then(result => {
|
|
50
|
+
* if (result) {
|
|
51
|
+
* console.log('Auth success:', result.name);
|
|
52
|
+
* // User info is now cached
|
|
53
|
+
* // Redirect to app
|
|
54
|
+
* }
|
|
55
|
+
* })
|
|
56
|
+
* .catch(error => {
|
|
57
|
+
* console.error('Auth failed:', error);
|
|
58
|
+
* })
|
|
59
|
+
* .finally(() => {
|
|
60
|
+
* setIsProcessing(false);
|
|
61
|
+
* });
|
|
62
|
+
* }, [processCallback]);
|
|
63
|
+
*
|
|
64
|
+
* return (
|
|
65
|
+
* <div>
|
|
66
|
+
* {isProcessing ? 'Processing authentication...' : 'Authentication complete'}
|
|
67
|
+
* </div>
|
|
68
|
+
* );
|
|
69
|
+
* }
|
|
70
|
+
*
|
|
71
|
+
* // App with cached user info
|
|
72
|
+
* function App() {
|
|
73
|
+
* const { auth, userInfo, signOut } = useYVAuth();
|
|
74
|
+
*
|
|
75
|
+
* if (!auth.isAuthenticated) {
|
|
76
|
+
* return <SignInPage />;
|
|
77
|
+
* }
|
|
78
|
+
*
|
|
79
|
+
* return (
|
|
80
|
+
* <div>
|
|
81
|
+
* <p>Welcome, {userInfo?.name || 'User'}!</p>
|
|
82
|
+
* <button onClick={signOut}>Sign Out</button>
|
|
83
|
+
* </div>
|
|
84
|
+
* );
|
|
85
|
+
* }
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
export function useYVAuth() {
|
|
89
|
+
// Get auth state from provider context
|
|
90
|
+
const { userInfo, setUserInfo, isLoading, error } = useYouVersionAuthContext();
|
|
91
|
+
// Derive authentication state
|
|
92
|
+
const isAuthenticated = !!userInfo;
|
|
93
|
+
// Get current tokens for actions
|
|
94
|
+
const authTokens = useMemo(() => {
|
|
95
|
+
if (typeof window !== 'undefined') {
|
|
96
|
+
return {
|
|
97
|
+
accessToken: YouVersionPlatformConfiguration.accessToken,
|
|
98
|
+
idToken: YouVersionPlatformConfiguration.idToken,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
return { accessToken: null, idToken: null };
|
|
102
|
+
}, []);
|
|
103
|
+
// Sign in function
|
|
104
|
+
const signIn = useCallback(async ({ redirectUrl, permissions = [], }) => {
|
|
105
|
+
await YouVersionAPIUsers.signIn(new Set(permissions), redirectUrl);
|
|
106
|
+
// Note: This will redirect, so code after this won't execute
|
|
107
|
+
}, []);
|
|
108
|
+
// Process callback function
|
|
109
|
+
const processCallback = useCallback(async () => {
|
|
110
|
+
const result = await YouVersionAPIUsers.handleAuthCallback();
|
|
111
|
+
return result;
|
|
112
|
+
}, []);
|
|
113
|
+
// Derive auth state
|
|
114
|
+
const auth = useMemo(() => ({
|
|
115
|
+
isAuthenticated,
|
|
116
|
+
isLoading,
|
|
117
|
+
accessToken: authTokens.accessToken,
|
|
118
|
+
idToken: authTokens.idToken,
|
|
119
|
+
result: null,
|
|
120
|
+
error,
|
|
121
|
+
}), [isAuthenticated, isLoading, authTokens.accessToken, authTokens.idToken, error]);
|
|
122
|
+
// Sign out function
|
|
123
|
+
const signOut = useCallback(() => {
|
|
124
|
+
YouVersionPlatformConfiguration.clearAuthTokens();
|
|
125
|
+
setUserInfo(null);
|
|
126
|
+
}, []);
|
|
127
|
+
return {
|
|
128
|
+
auth,
|
|
129
|
+
signIn,
|
|
130
|
+
signOut,
|
|
131
|
+
processCallback,
|
|
132
|
+
userInfo,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=useYVAuth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useYVAuth.js","sourceRoot":"","sources":["../src/useYVAuth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EACL,kBAAkB,EAClB,+BAA+B,GAKhC,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC;AAoB3E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmFG;AACH,MAAM,UAAU,SAAS;IACvB,uCAAuC;IACvC,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,wBAAwB,EAAE,CAAC;IAE/E,8BAA8B;IAC9B,MAAM,eAAe,GAAG,CAAC,CAAC,QAAQ,CAAC;IAEnC,iCAAiC;IACjC,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,EAAE;QAC9B,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;YAClC,OAAO;gBACL,WAAW,EAAE,+BAA+B,CAAC,WAAW;gBACxD,OAAO,EAAE,+BAA+B,CAAC,OAAO;aACjD,CAAC;QACJ,CAAC;QACD,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC9C,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,mBAAmB;IACnB,MAAM,MAAM,GAAG,WAAW,CACxB,KAAK,EAAE,EACL,WAAW,EACX,WAAW,GAAG,EAAE,GAIjB,EAAE,EAAE;QACH,MAAM,kBAAkB,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC,CAAC;QACnE,6DAA6D;IAC/D,CAAC,EACD,EAAE,CACH,CAAC;IAEF,4BAA4B;IAC5B,MAAM,eAAe,GAAG,WAAW,CAAC,KAAK,IAAgD,EAAE;QACzF,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,kBAAkB,EAAE,CAAC;QAC7D,OAAO,MAAM,CAAC;IAChB,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,oBAAoB;IACpB,MAAM,IAAI,GAAwB,OAAO,CACvC,GAAG,EAAE,CAAC,CAAC;QACL,eAAe;QACf,SAAS;QACT,WAAW,EAAE,UAAU,CAAC,WAAW;QACnC,OAAO,EAAE,UAAU,CAAC,OAAO;QAC3B,MAAM,EAAE,IAAI;QACZ,KAAK;KACN,CAAC,EACF,CAAC,eAAe,EAAE,SAAS,EAAE,UAAU,CAAC,WAAW,EAAE,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,CAChF,CAAC;IAEF,oBAAoB;IACpB,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE;QAC/B,+BAA+B,CAAC,eAAe,EAAE,CAAC;QAClD,WAAW,CAAC,IAAI,CAAC,CAAC;IACpB,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO;QACL,IAAI;QACJ,MAAM;QACN,OAAO;QACP,eAAe;QACf,QAAQ;KACT,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@youversion/platform-react-hooks",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@youversion/platform-core": "0.
|
|
25
|
+
"@youversion/platform-core": "0.7.0"
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
28
28
|
"react": ">=19.1.0 <20.0.0"
|
|
@@ -36,14 +36,14 @@
|
|
|
36
36
|
"eslint": "9.38.0",
|
|
37
37
|
"typescript": "5.9.3",
|
|
38
38
|
"vitest": "4.0.4",
|
|
39
|
-
"@internal/
|
|
40
|
-
"@internal/
|
|
39
|
+
"@internal/tsconfig": "0.0.0",
|
|
40
|
+
"@internal/eslint-config": "0.0.0"
|
|
41
41
|
},
|
|
42
42
|
"scripts": {
|
|
43
43
|
"dev": "tsc --watch",
|
|
44
44
|
"build": "tsc -p tsconfig.build.json",
|
|
45
45
|
"lint": "eslint . --max-warnings 0",
|
|
46
|
-
"
|
|
46
|
+
"typecheck": "tsc --noEmit",
|
|
47
47
|
"test": "vitest run",
|
|
48
48
|
"test:watch": "vitest",
|
|
49
49
|
"test:coverage": "vitest run --coverage"
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { vi } from 'vitest';
|
|
2
|
+
import { SignInWithYouVersionResult, YouVersionUserInfo } from '@youversion/platform-core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Creates a mock user info object for testing
|
|
6
|
+
* @param overrides Optional properties to override the default values
|
|
7
|
+
*/
|
|
8
|
+
export const createMockUserInfo = (overrides: Record<string, any> = {}): YouVersionUserInfo => {
|
|
9
|
+
const mockUserInfo = new YouVersionUserInfo({
|
|
10
|
+
id: '123',
|
|
11
|
+
name: 'John Doe',
|
|
12
|
+
email: 'john@example.com',
|
|
13
|
+
avatar_url: 'https://example.com/avatar.jpg?w={width}&h={height}',
|
|
14
|
+
...overrides,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// Mock the getAvatarUrl method for testing
|
|
18
|
+
vi.spyOn(mockUserInfo, 'getAvatarUrl').mockImplementation((width = 200, height = 200) => {
|
|
19
|
+
try {
|
|
20
|
+
return new URL(`https://example.com/avatar.jpg?w=${width}&h=${height}`);
|
|
21
|
+
} catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
return mockUserInfo;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Creates a mock auth result for testing
|
|
31
|
+
* @param overrides Optional properties to override the default values
|
|
32
|
+
*/
|
|
33
|
+
export const createMockAuthResult = (
|
|
34
|
+
overrides: Record<string, unknown> = {},
|
|
35
|
+
): SignInWithYouVersionResult => {
|
|
36
|
+
const mockUserInfo = createMockUserInfo();
|
|
37
|
+
return new SignInWithYouVersionResult({
|
|
38
|
+
...mockUserInfo,
|
|
39
|
+
accessToken: 'access-token',
|
|
40
|
+
idToken: 'id-token',
|
|
41
|
+
refreshToken: 'refresh-token',
|
|
42
|
+
expiresIn: 3600,
|
|
43
|
+
permissions: ['bibles', 'highlights'],
|
|
44
|
+
yvpUserId: 'test-yvp-user-id',
|
|
45
|
+
profilePicture: 'https://example.com/profile.jpg',
|
|
46
|
+
...overrides,
|
|
47
|
+
});
|
|
48
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { YouVersionProvider } from '../../context/YouVersionProvider';
|
|
3
|
+
import { useYouVersionAuthContext } from '../../context/YouVersionAuthContext';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Creates a test wrapper component with YouVersionAuthProvider
|
|
7
|
+
* @param config Optional auth configuration
|
|
8
|
+
*/
|
|
9
|
+
export const createAuthProviderWrapper = (): React.ComponentType<{ children: React.ReactNode }> => {
|
|
10
|
+
return ({ children }: { children: React.ReactNode }) => (
|
|
11
|
+
<YouVersionProvider
|
|
12
|
+
appKey="test-app-key"
|
|
13
|
+
apiHost="test-api.example.com"
|
|
14
|
+
includeAuth={true}
|
|
15
|
+
authRedirectUrl="http://test.example.com"
|
|
16
|
+
>
|
|
17
|
+
{children}
|
|
18
|
+
</YouVersionProvider>
|
|
19
|
+
);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Test component to access auth context for testing
|
|
24
|
+
*/
|
|
25
|
+
export function TestAuthChild({ onRender }: { onRender?: (data: any) => void }): React.JSX.Element {
|
|
26
|
+
const context = useYouVersionAuthContext();
|
|
27
|
+
|
|
28
|
+
React.useEffect(() => {
|
|
29
|
+
if (onRender) {
|
|
30
|
+
onRender(context);
|
|
31
|
+
}
|
|
32
|
+
}, [context, onRender]);
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<div>
|
|
36
|
+
<div data-testid="user-info">
|
|
37
|
+
{context.userInfo ? JSON.stringify(context.userInfo) : 'null'}
|
|
38
|
+
</div>
|
|
39
|
+
<div data-testid="is-loading">{context.isLoading.toString()}</div>
|
|
40
|
+
<div data-testid="error">{context.error ? context.error.message : 'null'}</div>
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { renderHook } from '@testing-library/react';
|
|
3
|
+
import { type ReactNode } from 'react';
|
|
4
|
+
import { YouVersionAuthContext, useYouVersionAuthContext } from './YouVersionAuthContext';
|
|
5
|
+
import type { AuthContextValue } from '../types/auth';
|
|
6
|
+
|
|
7
|
+
const mockContextValue: AuthContextValue = {
|
|
8
|
+
userInfo: {
|
|
9
|
+
userId: '123',
|
|
10
|
+
name: 'John Doe',
|
|
11
|
+
email: 'john@example.com',
|
|
12
|
+
avatarUrlFormat: 'https://example.com/avatar.jpg',
|
|
13
|
+
getAvatarUrl: () => new URL('https://example.com/avatar.jpg'),
|
|
14
|
+
avatarUrl: new URL('https://example.com/avatar.jpg'),
|
|
15
|
+
},
|
|
16
|
+
setUserInfo: () => {
|
|
17
|
+
// Mock function for testing
|
|
18
|
+
},
|
|
19
|
+
isLoading: false,
|
|
20
|
+
error: null,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
function TestWrapper({
|
|
24
|
+
children,
|
|
25
|
+
value = mockContextValue,
|
|
26
|
+
}: {
|
|
27
|
+
children: ReactNode;
|
|
28
|
+
value?: AuthContextValue | null;
|
|
29
|
+
}) {
|
|
30
|
+
return <YouVersionAuthContext.Provider value={value}>{children}</YouVersionAuthContext.Provider>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
describe('YouVersionAuthContext', () => {
|
|
34
|
+
describe('useYouVersionAuthContext', () => {
|
|
35
|
+
it('should return context value when used within provider', () => {
|
|
36
|
+
const { result } = renderHook(() => useYouVersionAuthContext(), {
|
|
37
|
+
wrapper: ({ children }) => <TestWrapper>{children}</TestWrapper>,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
expect(result.current).toEqual(mockContextValue);
|
|
41
|
+
expect(result.current.userInfo).toEqual(mockContextValue.userInfo);
|
|
42
|
+
expect(result.current.isLoading).toBe(false);
|
|
43
|
+
expect(result.current.error).toBe(null);
|
|
44
|
+
expect(typeof result.current.setUserInfo).toBe('function');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should throw error when used outside provider', () => {
|
|
48
|
+
expect(() => {
|
|
49
|
+
renderHook(() => useYouVersionAuthContext());
|
|
50
|
+
}).toThrow(
|
|
51
|
+
'useYouVersionAuthContext must be used within an auth provider. ' +
|
|
52
|
+
'Make sure your app is wrapped with <YouVersionAuthProvider> from @youversion/platform-react-hooks ' +
|
|
53
|
+
'or create your own provider using YouVersionAuthContext from @youversion/platform-react-hooks.',
|
|
54
|
+
);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should throw error when context value is null', () => {
|
|
58
|
+
expect(() => {
|
|
59
|
+
renderHook(() => useYouVersionAuthContext(), {
|
|
60
|
+
wrapper: ({ children }) => <TestWrapper value={null}>{children}</TestWrapper>,
|
|
61
|
+
});
|
|
62
|
+
}).toThrow(
|
|
63
|
+
'useYouVersionAuthContext must be used within an auth provider. ' +
|
|
64
|
+
'Make sure your app is wrapped with <YouVersionAuthProvider> from @youversion/platform-react-hooks ' +
|
|
65
|
+
'or create your own provider using YouVersionAuthContext from @youversion/platform-react-hooks.',
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should work with different context values', () => {
|
|
70
|
+
const customContextValue: AuthContextValue = {
|
|
71
|
+
userInfo: null,
|
|
72
|
+
setUserInfo: () => {
|
|
73
|
+
// Mock function for testing
|
|
74
|
+
},
|
|
75
|
+
isLoading: true,
|
|
76
|
+
error: new Error('Test error'),
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const { result } = renderHook(() => useYouVersionAuthContext(), {
|
|
80
|
+
wrapper: ({ children }) => <TestWrapper value={customContextValue}>{children}</TestWrapper>,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
expect(result.current.userInfo).toBe(null);
|
|
84
|
+
expect(result.current.isLoading).toBe(true);
|
|
85
|
+
expect(result.current.error?.message).toBe('Test error');
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { createContext, useContext } from 'react';
|
|
4
|
+
import type { AuthContextValue } from '../types/auth';
|
|
5
|
+
|
|
6
|
+
export const YouVersionAuthContext = createContext<AuthContextValue | null>(null);
|
|
7
|
+
|
|
8
|
+
export function useYouVersionAuthContext(): AuthContextValue {
|
|
9
|
+
const context = useContext(YouVersionAuthContext);
|
|
10
|
+
|
|
11
|
+
if (!context) {
|
|
12
|
+
throw new Error(
|
|
13
|
+
'useYouVersionAuthContext must be used within an auth provider. ' +
|
|
14
|
+
'Make sure your app is wrapped with <YouVersionAuthProvider> from @youversion/platform-react-hooks ' +
|
|
15
|
+
'or create your own provider using YouVersionAuthContext from @youversion/platform-react-hooks.',
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return context;
|
|
20
|
+
}
|