@pancakeswap/token-lists 0.0.2
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 +22 -0
- package/CHANGELOG.md +9 -0
- package/dist/chunk-YBVZDUCZ.mjs +70 -0
- package/dist/getTokenList-T45DK2AY.mjs +455 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.js +99 -0
- package/dist/index.mjs +12 -0
- package/dist/react.d.ts +55 -0
- package/dist/react.js +781 -0
- package/dist/react.mjs +249 -0
- package/dist/types-b620c9be.d.ts +39 -0
- package/package.json +55 -0
- package/react/actions.ts +25 -0
- package/react/getTokenList.ts +46 -0
- package/react/index.ts +4 -0
- package/react/lists.ts +65 -0
- package/react/package.json +5 -0
- package/react/reducer.test.ts +543 -0
- package/react/reducer.ts +186 -0
- package/react/useFetchListCallback.ts +37 -0
- package/schema/pancakeswap.json +392 -0
- package/schema/uniswap.json +302 -0
- package/src/getVersionUpgrade.ts +33 -0
- package/src/index.ts +3 -0
- package/src/types.ts +45 -0
- package/src/validator.ts +4 -0
- package/src/wrappedTokenInfo.ts +63 -0
- package/tsconfig.json +9 -0
- package/tsup.config.ts +10 -0
package/dist/react.mjs
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getVersionUpgrade
|
|
3
|
+
} from "./chunk-YBVZDUCZ.mjs";
|
|
4
|
+
|
|
5
|
+
// react/reducer.ts
|
|
6
|
+
import { createReducer } from "@reduxjs/toolkit";
|
|
7
|
+
|
|
8
|
+
// react/actions.ts
|
|
9
|
+
import { createAction } from "@reduxjs/toolkit";
|
|
10
|
+
var fetchTokenList = {
|
|
11
|
+
pending: createAction("lists/fetchTokenList/pending"),
|
|
12
|
+
fulfilled: createAction("lists/fetchTokenList/fulfilled"),
|
|
13
|
+
rejected: createAction("lists/fetchTokenList/rejected")
|
|
14
|
+
};
|
|
15
|
+
var addList = createAction("lists/addList");
|
|
16
|
+
var removeList = createAction("lists/removeList");
|
|
17
|
+
var enableList = createAction("lists/enableList");
|
|
18
|
+
var disableList = createAction("lists/disableList");
|
|
19
|
+
var acceptListUpdate = createAction("lists/acceptListUpdate");
|
|
20
|
+
var rejectVersionUpdate = createAction("lists/rejectVersionUpdate");
|
|
21
|
+
var updateListVersion = createAction("lists/updateListVersion");
|
|
22
|
+
|
|
23
|
+
// react/reducer.ts
|
|
24
|
+
var NEW_LIST_STATE = {
|
|
25
|
+
error: null,
|
|
26
|
+
current: null,
|
|
27
|
+
loadingRequestId: null,
|
|
28
|
+
pendingUpdate: null
|
|
29
|
+
};
|
|
30
|
+
var createTokenListReducer = (initialState, DEFAULT_LIST_OF_LISTS, DEFAULT_ACTIVE_LIST_URLS) => createReducer(
|
|
31
|
+
initialState,
|
|
32
|
+
(builder) => builder.addCase(fetchTokenList.pending, (state, { payload: { requestId, url } }) => {
|
|
33
|
+
var _a, _b;
|
|
34
|
+
const current = ((_a = state.byUrl[url]) == null ? void 0 : _a.current) ?? null;
|
|
35
|
+
const pendingUpdate = ((_b = state.byUrl[url]) == null ? void 0 : _b.pendingUpdate) ?? null;
|
|
36
|
+
state.byUrl[url] = {
|
|
37
|
+
current,
|
|
38
|
+
pendingUpdate,
|
|
39
|
+
loadingRequestId: requestId,
|
|
40
|
+
error: null
|
|
41
|
+
};
|
|
42
|
+
}).addCase(fetchTokenList.fulfilled, (state, { payload: { requestId, tokenList, url } }) => {
|
|
43
|
+
var _a, _b, _c;
|
|
44
|
+
const current = (_a = state.byUrl[url]) == null ? void 0 : _a.current;
|
|
45
|
+
const loadingRequestId = (_b = state.byUrl[url]) == null ? void 0 : _b.loadingRequestId;
|
|
46
|
+
if (current) {
|
|
47
|
+
const upgradeType = getVersionUpgrade(current.version, tokenList.version);
|
|
48
|
+
if (upgradeType === 0 /* NONE */)
|
|
49
|
+
return;
|
|
50
|
+
if (loadingRequestId === null || loadingRequestId === requestId) {
|
|
51
|
+
state.byUrl[url] = {
|
|
52
|
+
...state.byUrl[url],
|
|
53
|
+
loadingRequestId: null,
|
|
54
|
+
error: null,
|
|
55
|
+
current,
|
|
56
|
+
pendingUpdate: tokenList
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
} else {
|
|
60
|
+
if (DEFAULT_ACTIVE_LIST_URLS.includes(url)) {
|
|
61
|
+
(_c = state.activeListUrls) == null ? void 0 : _c.push(url);
|
|
62
|
+
}
|
|
63
|
+
state.byUrl[url] = {
|
|
64
|
+
...state.byUrl[url],
|
|
65
|
+
loadingRequestId: null,
|
|
66
|
+
error: null,
|
|
67
|
+
current: tokenList,
|
|
68
|
+
pendingUpdate: null
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}).addCase(fetchTokenList.rejected, (state, { payload: { url, requestId, errorMessage } }) => {
|
|
72
|
+
var _a;
|
|
73
|
+
if (((_a = state.byUrl[url]) == null ? void 0 : _a.loadingRequestId) !== requestId) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
state.byUrl[url] = {
|
|
77
|
+
...state.byUrl[url],
|
|
78
|
+
loadingRequestId: null,
|
|
79
|
+
error: errorMessage,
|
|
80
|
+
current: null,
|
|
81
|
+
pendingUpdate: null
|
|
82
|
+
};
|
|
83
|
+
}).addCase(addList, (state, { payload: url }) => {
|
|
84
|
+
if (!state.byUrl[url]) {
|
|
85
|
+
state.byUrl[url] = NEW_LIST_STATE;
|
|
86
|
+
}
|
|
87
|
+
}).addCase(removeList, (state, { payload: url }) => {
|
|
88
|
+
if (state.byUrl[url]) {
|
|
89
|
+
delete state.byUrl[url];
|
|
90
|
+
}
|
|
91
|
+
if (state.activeListUrls && state.activeListUrls.includes(url)) {
|
|
92
|
+
state.activeListUrls = state.activeListUrls.filter((u) => u !== url);
|
|
93
|
+
}
|
|
94
|
+
}).addCase(enableList, (state, { payload: url }) => {
|
|
95
|
+
if (!state.byUrl[url]) {
|
|
96
|
+
state.byUrl[url] = NEW_LIST_STATE;
|
|
97
|
+
}
|
|
98
|
+
if (state.activeListUrls && !state.activeListUrls.includes(url)) {
|
|
99
|
+
state.activeListUrls.push(url);
|
|
100
|
+
}
|
|
101
|
+
if (!state.activeListUrls) {
|
|
102
|
+
state.activeListUrls = [url];
|
|
103
|
+
}
|
|
104
|
+
}).addCase(disableList, (state, { payload: url }) => {
|
|
105
|
+
if (state.activeListUrls && state.activeListUrls.includes(url)) {
|
|
106
|
+
state.activeListUrls = state.activeListUrls.filter((u) => u !== url);
|
|
107
|
+
}
|
|
108
|
+
}).addCase(acceptListUpdate, (state, { payload: url }) => {
|
|
109
|
+
var _a;
|
|
110
|
+
if (!((_a = state.byUrl[url]) == null ? void 0 : _a.pendingUpdate)) {
|
|
111
|
+
throw new Error("accept list update called without pending update");
|
|
112
|
+
}
|
|
113
|
+
state.byUrl[url] = {
|
|
114
|
+
...state.byUrl[url],
|
|
115
|
+
pendingUpdate: null,
|
|
116
|
+
current: state.byUrl[url].pendingUpdate
|
|
117
|
+
};
|
|
118
|
+
}).addCase(updateListVersion, (state) => {
|
|
119
|
+
if (!state.lastInitializedDefaultListOfLists) {
|
|
120
|
+
state.byUrl = initialState.byUrl;
|
|
121
|
+
state.activeListUrls = initialState.activeListUrls;
|
|
122
|
+
} else if (state.lastInitializedDefaultListOfLists) {
|
|
123
|
+
const lastInitializedSet = state.lastInitializedDefaultListOfLists.reduce(
|
|
124
|
+
(s, l) => s.add(l),
|
|
125
|
+
/* @__PURE__ */ new Set()
|
|
126
|
+
);
|
|
127
|
+
const newListOfListsSet = DEFAULT_LIST_OF_LISTS.reduce((s, l) => s.add(l), /* @__PURE__ */ new Set());
|
|
128
|
+
DEFAULT_LIST_OF_LISTS.forEach((listUrl) => {
|
|
129
|
+
if (!lastInitializedSet.has(listUrl)) {
|
|
130
|
+
state.byUrl[listUrl] = NEW_LIST_STATE;
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
state.lastInitializedDefaultListOfLists.forEach((listUrl) => {
|
|
134
|
+
if (!newListOfListsSet.has(listUrl)) {
|
|
135
|
+
delete state.byUrl[listUrl];
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
state.lastInitializedDefaultListOfLists = DEFAULT_LIST_OF_LISTS;
|
|
140
|
+
if (!state.activeListUrls) {
|
|
141
|
+
state.activeListUrls = DEFAULT_ACTIVE_LIST_URLS;
|
|
142
|
+
DEFAULT_ACTIVE_LIST_URLS.forEach((listUrl) => {
|
|
143
|
+
if (!state.byUrl[listUrl]) {
|
|
144
|
+
state.byUrl[listUrl] = NEW_LIST_STATE;
|
|
145
|
+
}
|
|
146
|
+
return true;
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
})
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
// react/lists.ts
|
|
153
|
+
import { atomWithStorage, useReducerAtom } from "jotai/utils";
|
|
154
|
+
import localForage from "localforage";
|
|
155
|
+
function noop() {
|
|
156
|
+
}
|
|
157
|
+
var noopStorage = {
|
|
158
|
+
getItem: noop,
|
|
159
|
+
setItem: noop,
|
|
160
|
+
removeItem: noop,
|
|
161
|
+
keys: [],
|
|
162
|
+
getAllKeys: noop
|
|
163
|
+
};
|
|
164
|
+
var createListsAtom = (storeName, reducer, initialState) => {
|
|
165
|
+
let gotOnce = false;
|
|
166
|
+
function IndexedDBStorage(dbName) {
|
|
167
|
+
if (typeof window !== "undefined") {
|
|
168
|
+
const db = localForage.createInstance({
|
|
169
|
+
name: dbName,
|
|
170
|
+
storeName
|
|
171
|
+
});
|
|
172
|
+
return {
|
|
173
|
+
db,
|
|
174
|
+
getItem: async (key) => {
|
|
175
|
+
const value = await db.getItem(key);
|
|
176
|
+
gotOnce = true;
|
|
177
|
+
if (value) {
|
|
178
|
+
return value;
|
|
179
|
+
}
|
|
180
|
+
return initialState;
|
|
181
|
+
},
|
|
182
|
+
setItem: (k, v) => {
|
|
183
|
+
if (gotOnce) {
|
|
184
|
+
return db.setItem(k, v);
|
|
185
|
+
}
|
|
186
|
+
return void 0;
|
|
187
|
+
},
|
|
188
|
+
removeItem: db.removeItem,
|
|
189
|
+
delayInit: true
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
return noopStorage;
|
|
193
|
+
}
|
|
194
|
+
const listsAtom = atomWithStorage(
|
|
195
|
+
"lists",
|
|
196
|
+
initialState,
|
|
197
|
+
IndexedDBStorage("lists")
|
|
198
|
+
);
|
|
199
|
+
function useListState() {
|
|
200
|
+
return useReducerAtom(listsAtom, reducer);
|
|
201
|
+
}
|
|
202
|
+
return {
|
|
203
|
+
listsAtom,
|
|
204
|
+
useListState
|
|
205
|
+
};
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
// react/useFetchListCallback.ts
|
|
209
|
+
import { nanoid } from "@reduxjs/toolkit";
|
|
210
|
+
import { useCallback } from "react";
|
|
211
|
+
function useFetchListCallback(dispatch) {
|
|
212
|
+
return useCallback(
|
|
213
|
+
async (listUrl, sendDispatch = true) => {
|
|
214
|
+
const requestId = nanoid();
|
|
215
|
+
if (sendDispatch) {
|
|
216
|
+
dispatch(fetchTokenList.pending({ requestId, url: listUrl }));
|
|
217
|
+
}
|
|
218
|
+
const getTokenList = (await import("./getTokenList-T45DK2AY.mjs")).default;
|
|
219
|
+
return getTokenList(listUrl).then((tokenList) => {
|
|
220
|
+
if (sendDispatch) {
|
|
221
|
+
dispatch(fetchTokenList.fulfilled({ url: listUrl, tokenList, requestId }));
|
|
222
|
+
}
|
|
223
|
+
return tokenList;
|
|
224
|
+
}).catch((error) => {
|
|
225
|
+
console.error(`Failed to get list at url ${listUrl}`, error);
|
|
226
|
+
if (sendDispatch) {
|
|
227
|
+
dispatch(fetchTokenList.rejected({ url: listUrl, requestId, errorMessage: error.message }));
|
|
228
|
+
}
|
|
229
|
+
throw error;
|
|
230
|
+
});
|
|
231
|
+
},
|
|
232
|
+
[dispatch]
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
var useFetchListCallback_default = useFetchListCallback;
|
|
236
|
+
export {
|
|
237
|
+
NEW_LIST_STATE,
|
|
238
|
+
acceptListUpdate,
|
|
239
|
+
addList,
|
|
240
|
+
createListsAtom,
|
|
241
|
+
createTokenListReducer,
|
|
242
|
+
disableList,
|
|
243
|
+
enableList,
|
|
244
|
+
fetchTokenList,
|
|
245
|
+
rejectVersionUpdate,
|
|
246
|
+
removeList,
|
|
247
|
+
updateListVersion,
|
|
248
|
+
useFetchListCallback_default as useFetchListCallback
|
|
249
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
declare type ExtensionValue = string | number | boolean | null | undefined;
|
|
2
|
+
interface TokenInfo {
|
|
3
|
+
readonly chainId: number;
|
|
4
|
+
readonly address: string;
|
|
5
|
+
readonly name: string;
|
|
6
|
+
readonly decimals: number;
|
|
7
|
+
readonly symbol: string;
|
|
8
|
+
readonly logoURI?: string;
|
|
9
|
+
readonly tags?: string[];
|
|
10
|
+
readonly extensions?: {
|
|
11
|
+
readonly [key: string]: {
|
|
12
|
+
[key: string]: {
|
|
13
|
+
[key: string]: ExtensionValue;
|
|
14
|
+
} | ExtensionValue;
|
|
15
|
+
} | ExtensionValue;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
interface Version {
|
|
19
|
+
readonly major: number;
|
|
20
|
+
readonly minor: number;
|
|
21
|
+
readonly patch: number;
|
|
22
|
+
}
|
|
23
|
+
interface Tags {
|
|
24
|
+
readonly [tagId: string]: {
|
|
25
|
+
readonly name: string;
|
|
26
|
+
readonly description: string;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
interface TokenList {
|
|
30
|
+
readonly name: string;
|
|
31
|
+
readonly timestamp: string;
|
|
32
|
+
readonly version: Version;
|
|
33
|
+
readonly tokens: TokenInfo[];
|
|
34
|
+
readonly keywords?: string[];
|
|
35
|
+
readonly tags?: Tags;
|
|
36
|
+
readonly logoURI?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export { TokenInfo as T, Version as V, TokenList as a, Tags as b };
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pancakeswap/token-lists",
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"sideEffects": false,
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsup",
|
|
10
|
+
"dev": "tsup --watch",
|
|
11
|
+
"test": "vitest --run",
|
|
12
|
+
"prepublishOnly": "yarn run build",
|
|
13
|
+
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"ajv": "^6.12.3",
|
|
17
|
+
"@pancakeswap/swap-sdk-core": "*"
|
|
18
|
+
},
|
|
19
|
+
"peerDependencies": {
|
|
20
|
+
"@reduxjs/toolkit": "^1.8.6",
|
|
21
|
+
"localforage": "^1.10.0",
|
|
22
|
+
"jotai": "^1.8.6",
|
|
23
|
+
"react": "^18.2.0"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@reduxjs/toolkit": "^1.8.6",
|
|
27
|
+
"react": "^18.2.0"
|
|
28
|
+
},
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"import": "./dist/index.mjs",
|
|
33
|
+
"require": "./dist/index.js"
|
|
34
|
+
},
|
|
35
|
+
"./react": {
|
|
36
|
+
"types": "./dist/react.d.ts",
|
|
37
|
+
"import": "./dist/react.mjs",
|
|
38
|
+
"require": "./dist/react.js"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"peerDependenciesMeta": {
|
|
42
|
+
"react": {
|
|
43
|
+
"optional": true
|
|
44
|
+
},
|
|
45
|
+
"@reduxjs/toolkit": {
|
|
46
|
+
"optional": true
|
|
47
|
+
},
|
|
48
|
+
"localforage": {
|
|
49
|
+
"optional": true
|
|
50
|
+
},
|
|
51
|
+
"jotai": {
|
|
52
|
+
"optional": true
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
package/react/actions.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ActionCreatorWithPayload, createAction } from '@reduxjs/toolkit'
|
|
2
|
+
import { TokenList, Version } from '../src/types'
|
|
3
|
+
|
|
4
|
+
export const fetchTokenList: Readonly<{
|
|
5
|
+
pending: ActionCreatorWithPayload<{ url: string; requestId: string }>
|
|
6
|
+
fulfilled: ActionCreatorWithPayload<{ url: string; tokenList: TokenList; requestId: string }>
|
|
7
|
+
rejected: ActionCreatorWithPayload<{ url: string; errorMessage: string; requestId: string }>
|
|
8
|
+
}> = {
|
|
9
|
+
pending: createAction('lists/fetchTokenList/pending'),
|
|
10
|
+
fulfilled: createAction('lists/fetchTokenList/fulfilled'),
|
|
11
|
+
rejected: createAction('lists/fetchTokenList/rejected'),
|
|
12
|
+
}
|
|
13
|
+
// add and remove from list options
|
|
14
|
+
export const addList = createAction<string>('lists/addList')
|
|
15
|
+
export const removeList = createAction<string>('lists/removeList')
|
|
16
|
+
|
|
17
|
+
// select which lists to search across from loaded lists
|
|
18
|
+
export const enableList = createAction<string>('lists/enableList')
|
|
19
|
+
export const disableList = createAction<string>('lists/disableList')
|
|
20
|
+
|
|
21
|
+
// versioning
|
|
22
|
+
export const acceptListUpdate = createAction<string>('lists/acceptListUpdate')
|
|
23
|
+
export const rejectVersionUpdate = createAction<Version>('lists/rejectVersionUpdate')
|
|
24
|
+
|
|
25
|
+
export const updateListVersion = createAction('lists/updateListVersion')
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/* eslint-disable no-continue */
|
|
2
|
+
/* eslint-disable no-await-in-loop */
|
|
3
|
+
import { TokenList } from '@pancakeswap/token-lists'
|
|
4
|
+
import uriToHttp from '@pancakeswap/utils/uriToHttp'
|
|
5
|
+
import Ajv from 'ajv'
|
|
6
|
+
import schema from '../schema/pancakeswap.json'
|
|
7
|
+
|
|
8
|
+
export const tokenListValidator = new Ajv({ allErrors: true }).compile(schema)
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Contains the logic for resolving a list URL to a validated token list
|
|
12
|
+
* @param listUrl list url
|
|
13
|
+
*/
|
|
14
|
+
export default async function getTokenList(listUrl: string): Promise<TokenList> {
|
|
15
|
+
const urls: string[] = uriToHttp(listUrl)
|
|
16
|
+
|
|
17
|
+
for (let i = 0; i < urls.length; i++) {
|
|
18
|
+
const url = urls[i]
|
|
19
|
+
const isLast = i === urls.length - 1
|
|
20
|
+
let response
|
|
21
|
+
try {
|
|
22
|
+
response = await fetch(url)
|
|
23
|
+
} catch (error) {
|
|
24
|
+
console.error('Failed to fetch list', listUrl, error)
|
|
25
|
+
if (isLast) throw new Error(`Failed to download list ${listUrl}`)
|
|
26
|
+
continue
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!response.ok) {
|
|
30
|
+
if (isLast) throw new Error(`Failed to download list ${listUrl}`)
|
|
31
|
+
continue
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const json = await response.json()
|
|
35
|
+
if (!tokenListValidator(json)) {
|
|
36
|
+
const validationErrors: string =
|
|
37
|
+
tokenListValidator.errors?.reduce<string>((memo, error) => {
|
|
38
|
+
const add = `${(error as any).dataPath} ${error.message ?? ''}`
|
|
39
|
+
return memo.length > 0 ? `${memo}; ${add}` : `${add}`
|
|
40
|
+
}, '') ?? 'unknown error'
|
|
41
|
+
throw new Error(`Token list failed validation: ${validationErrors}`)
|
|
42
|
+
}
|
|
43
|
+
return json as TokenList
|
|
44
|
+
}
|
|
45
|
+
throw new Error('Unrecognized list URL protocol.')
|
|
46
|
+
}
|
package/react/index.ts
ADDED
package/react/lists.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { atomWithStorage, useReducerAtom } from 'jotai/utils'
|
|
2
|
+
import localForage from 'localforage'
|
|
3
|
+
import { ListsState } from './reducer'
|
|
4
|
+
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
6
|
+
function noop() {}
|
|
7
|
+
const noopStorage = {
|
|
8
|
+
getItem: noop,
|
|
9
|
+
setItem: noop,
|
|
10
|
+
removeItem: noop,
|
|
11
|
+
keys: [],
|
|
12
|
+
getAllKeys: noop,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const createListsAtom = (storeName: string, reducer: any, initialState: any) => {
|
|
16
|
+
let gotOnce = false
|
|
17
|
+
/**
|
|
18
|
+
* Persist you redux state using IndexedDB
|
|
19
|
+
* @param {string} dbName - IndexedDB database name
|
|
20
|
+
*/
|
|
21
|
+
function IndexedDBStorage(dbName: string) {
|
|
22
|
+
if (typeof window !== 'undefined') {
|
|
23
|
+
const db = localForage.createInstance({
|
|
24
|
+
name: dbName,
|
|
25
|
+
storeName,
|
|
26
|
+
})
|
|
27
|
+
return {
|
|
28
|
+
db,
|
|
29
|
+
getItem: async (key: string) => {
|
|
30
|
+
const value = await db.getItem(key)
|
|
31
|
+
gotOnce = true
|
|
32
|
+
if (value) {
|
|
33
|
+
return value
|
|
34
|
+
}
|
|
35
|
+
return initialState
|
|
36
|
+
},
|
|
37
|
+
setItem: (k: string, v: any) => {
|
|
38
|
+
if (gotOnce) {
|
|
39
|
+
return db.setItem(k, v)
|
|
40
|
+
}
|
|
41
|
+
return undefined
|
|
42
|
+
},
|
|
43
|
+
removeItem: db.removeItem,
|
|
44
|
+
delayInit: true,
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return noopStorage
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const listsAtom = atomWithStorage<ListsState>(
|
|
51
|
+
'lists',
|
|
52
|
+
initialState,
|
|
53
|
+
// @ts-ignore
|
|
54
|
+
IndexedDBStorage('lists'),
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
function useListState() {
|
|
58
|
+
return useReducerAtom(listsAtom, reducer)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
listsAtom,
|
|
63
|
+
useListState,
|
|
64
|
+
}
|
|
65
|
+
}
|