-
Notifications
You must be signed in to change notification settings - Fork 0
/
capacitor.tsx
115 lines (106 loc) · 4.13 KB
/
capacitor.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import React, { Context, ReactNode, useState, useEffect, useRef, createContext, useMemo } from 'react';
import { EventEmitter } from 'events';
import { isEqual, isNull } from 'lodash';
import { Preferences } from '@capacitor/preferences';
import Debug from 'debug';
import { IStoreContext, IUseStore, defaultContext, useStore } from './store';
const debug = Debug('store:capacitor');
const capacitorStorageEvent = new EventEmitter();
export const CapacitorStoreContext = createContext(defaultContext);
export const CapacitorStoreProvider = ({
context = CapacitorStoreContext,
children,
fetchInterval = 0,
}: {
context?: Context<IStoreContext>;
children?: ReactNode;
fetchInterval?: number;
}) => {
const [useStore] = useState(() => {
return function useStore<T extends any>(
key: string,
defaultValue: T,
): ReturnType<IUseStore<T>> {
const getStateRef = useRef<any>();
const intervalRef = useRef<any>();
const memoDefaultValue = useMemo(() => defaultValue, []);
const [state, setState] = useState<T>(memoDefaultValue);
const [isLoading, setIsLoading] = useState<boolean>(true);
const stateRef = useRef<any>();
stateRef.current = state;
const [setValue] = useState(() => (value) => {
const _value = typeof(value) === 'function' ? value(stateRef.current) : value;
debug('setValue', { key, defaultValue: memoDefaultValue, value: _value });
Preferences.set({ key, value: JSON.stringify(_value) }).then(() => setState(_value));
capacitorStorageEvent.emit(key, JSON.stringify(_value));
});
const [unsetValue] = useState(() => () => {
debug('unsetValue', { key, defaultValue: memoDefaultValue });
Preferences.remove({ key }).then(() => setState(memoDefaultValue));
capacitorStorageEvent.emit(key, memoDefaultValue);
});
getStateRef.current = () => Preferences.get({ key }).then(async ({ value }) => {
const { keys } = await Preferences.keys();
if (!!~keys.indexOf(key)) {
let valueParsed: any;
try {
valueParsed = JSON.parse(value);
} catch (error) {
debug('setStore:error', { error, key, defaultValue: memoDefaultValue, value });
}
debug('getStore', { key, defaultValue: memoDefaultValue, valueParsed, value });
if (!isEqual(valueParsed, state)) {
if (typeof(valueParsed) === 'undefined' || isNull(value)) setState(defaultValue);
else setState(valueParsed);
}
}
});
useEffect(
() => {
debug('init', { key, defaultValue: memoDefaultValue });
getStateRef.current().then(() => setIsLoading(false));
const fn = (value) => {
let valueParsed;
try {
valueParsed = JSON.parse(value);
} catch (error) {
debug('fn:error', { error, key, defaultValue: memoDefaultValue, value });
}
if (typeof(valueParsed) === 'undefined' || isNull(valueParsed)) setState(memoDefaultValue);
else setState(valueParsed);
};
if (fetchInterval) intervalRef.current = setInterval(() => getStateRef.current(), fetchInterval);
capacitorStorageEvent.on(key, fn);
return () => {
clearInterval(intervalRef.current);
capacitorStorageEvent.off(key, fn);
};
},
[],
);
return [state, setValue, unsetValue, isLoading];
};
});
return <context.Provider value={{ useStore }}>
{children}
</context.Provider>;
};
/**
* A hook to use a capacitor store (capacitor/preferences)
*
* @example
* ```
* // Wrap your component with CapacitorStoreProvider to use useCapacitorStore hook.
* <CapacitorStoreProvider>
* <MyComponent />
* </CapacitorStoreProvider>
*
* function MyComponent() {
* const [value, setValue, unsetValue, isLoading] = useCapacitorStore('key', 'defaultValue');
* return <div>{value}</div>;
* }
* ```
*/
export function useCapacitorStore<T extends any>(key: string, defaultValue: T, context = CapacitorStoreContext) {
return useStore(key, defaultValue, context);
}