@boneframework/native-components 1.0.14 → 1.0.16
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 +1 -1
- package/components/SessionProvider.tsx +37 -13
- package/contexts/auth.ts +2 -2
- package/hooks/useAuth.ts +6 -29
- package/hooks/useSecureStorageState.ts +95 -0
- package/hooks/useStorageState.ts +95 -0
- package/package.json +1 -1
- package/screens/WelcomeScreen.tsx +3 -1
- package/utilities/json.ts +13 -0
- package/Bone.ts +0 -6
- package/Components.ts +0 -1
- package/Contexts.ts +0 -1
- package/Hooks.ts +0 -3
- package/Screens.ts +0 -4
- package/Utilities.ts +0 -1
package/README.md
CHANGED
|
@@ -1,31 +1,55 @@
|
|
|
1
|
-
import React, {useState} from 'react';
|
|
1
|
+
import React, {useEffect, useState} from 'react';
|
|
2
2
|
|
|
3
|
-
import AuthContext from "
|
|
4
|
-
import useAuth from "
|
|
3
|
+
import AuthContext from "@boneframework/native-components/contexts/auth";
|
|
4
|
+
import useAuth from "@boneframework/native-components/hooks/useAuth";
|
|
5
|
+
import {useSecureStorageState} from "@boneframework/native-components/hooks/useSecureStorageState";
|
|
6
|
+
import {useStorageState} from "@boneframework/native-components/hooks/useStorageState";
|
|
7
|
+
import authStorage from "@boneframework/native-components/utilities/authStorage";
|
|
8
|
+
import useApi from "@boneframework/native-components/hooks/useApi";
|
|
9
|
+
import usersApi from "@boneframework/native-components/api/users";
|
|
5
10
|
|
|
6
11
|
function SessionProvider(props: object) {
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
setUser(
|
|
12
|
+
const profileApi = useApi(usersApi.getProfile);
|
|
13
|
+
const [[isAuthTokenLoading, authToken], setAuthToken] = useSecureStorageState('authToken');
|
|
14
|
+
const [[isUserLoading, user], setUser] = useStorageState('user');
|
|
15
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
16
|
+
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
setIsLoading(profileApi.loading || isAuthTokenLoading || isUserLoading);
|
|
19
|
+
}, [profileApi.loading || isAuthTokenLoading || isUserLoading]);
|
|
20
|
+
|
|
10
21
|
|
|
11
22
|
return (
|
|
12
23
|
<AuthContext.Provider
|
|
13
24
|
value={{
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
25
|
+
login: async (authToken: object) => {
|
|
26
|
+
setIsLoading(true);
|
|
27
|
+
await authStorage.storeAuthToken(authToken).then(async () => {
|
|
28
|
+
const userProfile = await profileApi.request(authToken.accessToken);
|
|
29
|
+
userProfile.data.authToken = authToken;
|
|
30
|
+
setAuthToken(authToken)
|
|
31
|
+
await setUser(userProfile.data);
|
|
32
|
+
setIsLoading(false);
|
|
33
|
+
console.log('token : ', authToken)
|
|
34
|
+
console.log('user : ', user)
|
|
35
|
+
console.log('profile : ', userProfile.data)
|
|
36
|
+
console.log('user : ', user)
|
|
37
|
+
|
|
38
|
+
});
|
|
17
39
|
},
|
|
18
|
-
|
|
19
|
-
|
|
40
|
+
logout: () => {
|
|
41
|
+
setAuthToken(null);
|
|
20
42
|
setUser(null);
|
|
21
43
|
},
|
|
44
|
+
updateUser: async data => {
|
|
45
|
+
setUser(data)
|
|
46
|
+
},
|
|
22
47
|
user,
|
|
48
|
+
isLoading
|
|
23
49
|
}}>
|
|
24
50
|
{props.children}
|
|
25
51
|
</AuthContext.Provider>
|
|
26
52
|
)
|
|
27
53
|
}
|
|
28
54
|
|
|
29
|
-
|
|
30
|
-
|
|
31
55
|
export default SessionProvider;
|
package/contexts/auth.ts
CHANGED
|
@@ -5,13 +5,13 @@ const AuthContext = createContext<{
|
|
|
5
5
|
logout: () => void;
|
|
6
6
|
updateUser: () => void;
|
|
7
7
|
user?: object | null;
|
|
8
|
-
isLoading:
|
|
8
|
+
isLoading: bool;
|
|
9
9
|
}>({
|
|
10
10
|
login: () => null,
|
|
11
11
|
logout: () => null,
|
|
12
12
|
updateUser: () => null,
|
|
13
13
|
user: null,
|
|
14
|
-
isLoading:
|
|
14
|
+
isLoading: true
|
|
15
15
|
});
|
|
16
16
|
|
|
17
17
|
export default AuthContext;
|
package/hooks/useAuth.ts
CHANGED
|
@@ -6,36 +6,13 @@ import useApi from "./useApi";
|
|
|
6
6
|
import usersApi from "../api/users";
|
|
7
7
|
|
|
8
8
|
export default useAuth = () => {
|
|
9
|
-
const
|
|
10
|
-
const {user, setUser} = useContext(AuthContext);
|
|
11
|
-
const {isLoading, setIsLoading} = useState(false);
|
|
9
|
+
const value = useContext(AuthContext);
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
authStorage.storeUser(user.data);
|
|
18
|
-
user.data.authToken = authToken;
|
|
19
|
-
setUser(user.data);
|
|
20
|
-
setIsLoading(false);
|
|
21
|
-
});
|
|
11
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
12
|
+
if (!value) {
|
|
13
|
+
throw new Error('useSession must be wrapped in a <SessionProvider />');
|
|
14
|
+
}
|
|
22
15
|
}
|
|
23
16
|
|
|
24
|
-
|
|
25
|
-
setIsLoading(true);
|
|
26
|
-
const authToken = user.authToken;
|
|
27
|
-
await delete user.authToken;
|
|
28
|
-
authStorage.storeUser(user);
|
|
29
|
-
user.authToken = authToken;
|
|
30
|
-
setUser({...user});
|
|
31
|
-
setIsLoading(false);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const logout = () => {
|
|
35
|
-
setUser(null);
|
|
36
|
-
authStorage.removeAuthToken();
|
|
37
|
-
authStorage.removeUser();
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return {login, logout, updateUser, user, isLoading};
|
|
17
|
+
return value;
|
|
41
18
|
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import * as SecureStore from 'expo-secure-store';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { Platform } from 'react-native';
|
|
4
|
+
|
|
5
|
+
type UseStateHook<T> = [[boolean, T | null], (value: T | null) => void];
|
|
6
|
+
|
|
7
|
+
function useAsyncState<T>(
|
|
8
|
+
initialValue: [boolean, T | null] = [true, null],
|
|
9
|
+
): UseStateHook<T> {
|
|
10
|
+
return React.useReducer(
|
|
11
|
+
(state: [boolean, T | null], action: T | null = null): [boolean, T | null] => [false, action],
|
|
12
|
+
initialValue
|
|
13
|
+
) as UseStateHook<T>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function setStorageItemAsync(key: string, value: string | null) {
|
|
17
|
+
if (typeof value === 'object') {
|
|
18
|
+
value = JSON.stringify(value);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (Platform.OS === 'web') {
|
|
22
|
+
try {
|
|
23
|
+
if (value === null) {
|
|
24
|
+
localStorage.removeItem(key);
|
|
25
|
+
} else {
|
|
26
|
+
localStorage.setItem(key, value);
|
|
27
|
+
}
|
|
28
|
+
} catch (e) {
|
|
29
|
+
console.error('Local storage is unavailable:', e);
|
|
30
|
+
}
|
|
31
|
+
} else {
|
|
32
|
+
if (value == null) {
|
|
33
|
+
await SecureStore.deleteItemAsync(key);
|
|
34
|
+
} else {
|
|
35
|
+
await SecureStore.setItemAsync(key, value);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function useSecureStorageState(key: string): UseStateHook<string> {
|
|
41
|
+
// Public
|
|
42
|
+
const [state, setState] = useAsyncState<string|object>();
|
|
43
|
+
|
|
44
|
+
const isJson = () => {
|
|
45
|
+
try {
|
|
46
|
+
const result = JSON.parse(str);
|
|
47
|
+
const type = Object.prototype.toString.call(result);
|
|
48
|
+
return type === '[object Object]'
|
|
49
|
+
|| type === '[object Array]';
|
|
50
|
+
} catch (err) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Get
|
|
56
|
+
React.useEffect(() => {
|
|
57
|
+
if (Platform.OS === 'web') {
|
|
58
|
+
try {
|
|
59
|
+
if (typeof localStorage !== 'undefined') {
|
|
60
|
+
value = localStorage.getItem(key);
|
|
61
|
+
|
|
62
|
+
if (isJson(value)) {
|
|
63
|
+
value = JSON.parse(value);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
setState(value);
|
|
67
|
+
}
|
|
68
|
+
} catch (e) {
|
|
69
|
+
console.error('Local storage is unavailable:', e);
|
|
70
|
+
}
|
|
71
|
+
} else {
|
|
72
|
+
console.log('fetching ' + key);
|
|
73
|
+
SecureStore.getItemAsync(key).then(value => {
|
|
74
|
+
console.log('value', value)
|
|
75
|
+
if (isJson(value)) {
|
|
76
|
+
value = JSON.parse(value);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
setState(value);
|
|
80
|
+
console.log('state ', state)
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}, [key]);
|
|
84
|
+
|
|
85
|
+
// Set
|
|
86
|
+
const setValue = React.useCallback(
|
|
87
|
+
(value: string | null) => {
|
|
88
|
+
setState(value);
|
|
89
|
+
setStorageItemAsync(key, value);
|
|
90
|
+
},
|
|
91
|
+
[key]
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
return [state, setValue];
|
|
95
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { Platform } from 'react-native';
|
|
4
|
+
|
|
5
|
+
type UseStateHook<T> = [[boolean, T | null], (value: T | null) => void];
|
|
6
|
+
|
|
7
|
+
function useAsyncState<T>(
|
|
8
|
+
initialValue: [boolean, T | null] = [true, null],
|
|
9
|
+
): UseStateHook<T> {
|
|
10
|
+
return React.useReducer(
|
|
11
|
+
(state: [boolean, T | null], action: T | null = null): [boolean, T | null] => [false, action],
|
|
12
|
+
initialValue
|
|
13
|
+
) as UseStateHook<T>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
export async function setStorageItemAsync(key: string, value: object | string | null) {
|
|
19
|
+
if (typeof value === 'object') {
|
|
20
|
+
value = JSON.stringify(value);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (Platform.OS === 'web') {
|
|
24
|
+
try {
|
|
25
|
+
if (value === null) {
|
|
26
|
+
localStorage.removeItem(key);
|
|
27
|
+
} else {
|
|
28
|
+
localStorage.setItem(key, value);
|
|
29
|
+
}
|
|
30
|
+
} catch (e) {
|
|
31
|
+
console.error('Local storage is unavailable:', e);
|
|
32
|
+
}
|
|
33
|
+
} else {
|
|
34
|
+
if (value == null) {
|
|
35
|
+
await AsyncStorage.removeItem(key);
|
|
36
|
+
} else {
|
|
37
|
+
await AsyncStorage.setItem(key, value);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function useStorageState(key: string): UseStateHook<string> {
|
|
43
|
+
const isJson = () => {
|
|
44
|
+
try {
|
|
45
|
+
const result = JSON.parse(str);
|
|
46
|
+
const type = Object.prototype.toString.call(result);
|
|
47
|
+
return type === '[object Object]'
|
|
48
|
+
|| type === '[object Array]';
|
|
49
|
+
} catch (err) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// Public
|
|
54
|
+
const [state, setState] = useAsyncState<string|object>();
|
|
55
|
+
// Get
|
|
56
|
+
|
|
57
|
+
React.useEffect(() => {
|
|
58
|
+
if (Platform.OS === 'web') {
|
|
59
|
+
try {
|
|
60
|
+
if (typeof localStorage !== 'undefined') {
|
|
61
|
+
value = localStorage.getItem(key);
|
|
62
|
+
|
|
63
|
+
if (isJson(value)) {
|
|
64
|
+
value = JSON.parse(value);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
setState(value);
|
|
68
|
+
}
|
|
69
|
+
} catch (e) {
|
|
70
|
+
console.error('Local storage is unavailable:', e);
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
AsyncStorage.getItem(key).then(value => {
|
|
74
|
+
|
|
75
|
+
if (isJson(value)) {
|
|
76
|
+
value = JSON.parse(value);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
setState(value);
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}, [key]);
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
// Set
|
|
86
|
+
const setValue = React.useCallback(
|
|
87
|
+
(value: object | string | null) => {
|
|
88
|
+
setState(value);
|
|
89
|
+
setStorageItemAsync(key, value);
|
|
90
|
+
},
|
|
91
|
+
[key]
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
return [state, setValue];
|
|
95
|
+
}
|
package/package.json
CHANGED
|
@@ -4,10 +4,12 @@ import {exchangeCodeAsync, makeRedirectUri, useAuthRequest} from "expo-auth-sess
|
|
|
4
4
|
|
|
5
5
|
import Button from '../components/Button'
|
|
6
6
|
import colors from '../../../../config/colors'
|
|
7
|
+
import ActivityIndicator from "@boneframework/native-components/components/ActivityIndicator";
|
|
7
8
|
|
|
8
|
-
function WelcomeScreen({loginOnPress = () => {}, registerOnPress = () => {}, title = 'BONE FRAMEWORK'}) {
|
|
9
|
+
function WelcomeScreen({loginOnPress = () => {}, registerOnPress = () => {}, title = 'BONE FRAMEWORK', isLoading = false}) {
|
|
9
10
|
return (
|
|
10
11
|
<ImageBackground blurRadius={10} style={styles.background} source={require('../../../../assets/background.png')} >
|
|
12
|
+
<ActivityIndicator visible={isLoading} type={'overlay'}/>
|
|
11
13
|
<View style={styles.logoContainer}>
|
|
12
14
|
<Image style={styles.logo} source={require('../../../../assets/logo.png')} />
|
|
13
15
|
<Text style={styles.tagline}>{ title }</Text>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const hasJsonStructure = (str) => {
|
|
2
|
+
if (typeof str !== 'string') return false;
|
|
3
|
+
try {
|
|
4
|
+
const result = JSON.parse(str);
|
|
5
|
+
const type = Object.prototype.toString.call(result);
|
|
6
|
+
return type === '[object Object]'
|
|
7
|
+
|| type === '[object Array]';
|
|
8
|
+
} catch (err) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default {hasJsonStructure};
|
package/Bone.ts
DELETED
package/Components.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export default {}
|
package/Contexts.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export default {}
|
package/Hooks.ts
DELETED
package/Screens.ts
DELETED
package/Utilities.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export default {}
|