UNPKG

@yoroi/common

Version:

The Common package of Yoroi SDK

145 lines (133 loc) 4.69 kB
import AsyncStorage from '@react-native-async-storage/async-storage' import {App} from '@yoroi/types' import {parseSafe} from '../../utils/parsers' import {isFolderKey} from '../helpers/is-folder-key' import {isFileKey} from '../helpers/is-file-key' export const mountAsyncStorage = ({ path, }: { path: App.StorageFolderName }): App.Storage => { const withPath = (key: string) => `${path}${key}` as `${App.StorageFolderName}${string}` const withoutPath = (value: string) => value.slice(path.length) function getItem<T>( key: string, parse: (item: string | null) => T, ): Promise<T> function getItem<T = unknown>(key: string): Promise<T> async function getItem(key: string, parse = parseSafe) { const item = await AsyncStorage.getItem(withPath(key)) return parse(item) } function multiGet<T>( keys: ReadonlyArray<string>, parse: (item: string | null) => T, ): Promise<Array<[string, T]>> function multiGet<T = unknown>( keys: ReadonlyArray<string>, ): Promise<Array<[string, T]>> async function multiGet(keys: ReadonlyArray<string>, parse = parseSafe) { const absolutePaths = keys.map((key) => withPath(key)) const items = await AsyncStorage.multiGet(absolutePaths) return items.map( ([key, value]) => [withoutPath(key), parse(value)] as const, ) } return { join: (folderName: App.StorageFolderName) => mountAsyncStorage({path: `${path}${folderName}`}), getItem, multiGet, setItem: async <T = unknown>( key: string, value: T, stringify: (data: T) => string = JSON.stringify, ) => { const item = stringify(value) await AsyncStorage.setItem(withPath(key), item) }, multiSet: async <T = unknown>( tuples: ReadonlyArray<[key: string, value: T]>, stringify: (data: T) => string = JSON.stringify, ) => { const items: Array<[string, string]> = tuples.map(([key, value]) => [ withPath(key), stringify(value), ]) await AsyncStorage.multiSet(items) }, removeItem: async (key: string) => { await AsyncStorage.removeItem(withPath(key)) }, removeFolder: async (folderName: App.StorageFolderName) => { const keys = await AsyncStorage.getAllKeys() const filteredKeys = keys.filter( (key) => key.startsWith(path) && withoutPath(key).startsWith(folderName) && isFolderKey({key, path}), ) await AsyncStorage.multiRemove(filteredKeys) }, multiRemove: async (keys: ReadonlyArray<string>) => { await AsyncStorage.multiRemove(keys.map((key) => withPath(key))) }, getAllKeys: () => { return AsyncStorage.getAllKeys() .then((keys) => keys.filter((key) => key.startsWith(path) && isFileKey({key, path})), ) .then( // temporary any until async interface is migrated to receive keys for multi storage (filteredKeys) => filteredKeys.map(withoutPath) as ReadonlyArray<any>, ) }, clear: async () => { const keys = await AsyncStorage.getAllKeys() const filteredKeys = keys.filter((key) => key.startsWith(path)) await AsyncStorage.multiRemove(filteredKeys) }, } as const } export const mountAsyncMultiStorage = <T = unknown>( options: App.MultiStorageOptions<T>, ): Readonly<App.MultiStorage<T>> => { const { storage, dataFolder, keyExtractor, serializer = JSON.stringify, deserializer = parseSafe as (item: string | null) => T | null, } = options const dataStorage = storage.join(dataFolder) const {getAllKeys: getAllKeysStorage, multiSet, multiGet} = dataStorage const clear = () => storage.removeFolder(dataFolder) const saveMany = (items: ReadonlyArray<NonNullable<T>>) => { const entries: [string, T][] = items.map((item) => { if (typeof keyExtractor === 'function') { return [keyExtractor(item), item] } return [String(item[keyExtractor]), item] }) const entriesWithKeys = entries.filter(([key]) => key != null && key !== '') return multiSet(entriesWithKeys, serializer as (item: unknown) => string) } const readAll = () => getAllKeys().then((keysToRead) => multiGet<T | null>(keysToRead, deserializer), ) const readMany = (keysToRead: ReadonlyArray<string>) => dataStorage.multiGet<T | null>(keysToRead, deserializer) const removeMany = (keysToRead: ReadonlyArray<string>) => dataStorage.multiRemove(keysToRead) const getAllKeys = () => getAllKeysStorage().then((keys) => keys) return { getAllKeys, clear, readAll, saveMany, readMany, removeMany, } as const }