2025-01-27 17:21:44 +01:00
|
|
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
|
|
|
import { DocumentDirectoryPath, exists, readFile, writeFile, unlink, mkdir } from 'react-native-fs';
|
|
|
|
|
import { Storage } from 'redux-persist';
|
|
|
|
|
|
|
|
|
|
const STORAGE_BASE_PATH = DocumentDirectoryPath + '/store/';
|
|
|
|
|
|
|
|
|
|
/** Retrieve the path of a store file for a given key */
|
|
|
|
|
function getFileByKey(key: string) {
|
|
|
|
|
return STORAGE_BASE_PATH + encodeURIComponent(key) + '.json';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Ensure that the store directory exists on the local filesystem */
|
|
|
|
|
async function ensureDirectoryExists() {
|
|
|
|
|
if (!(await exists(STORAGE_BASE_PATH))) {
|
|
|
|
|
await mkdir(STORAGE_BASE_PATH);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Migrates the Redux store from AsyncStorage to react-native-fs.
|
|
|
|
|
*/
|
|
|
|
|
const MigratedStorage: Storage = {
|
|
|
|
|
async getItem(key) {
|
2025-02-04 22:32:53 +01:00
|
|
|
// Calculate the path where the key should be stored
|
2025-01-27 17:21:44 +01:00
|
|
|
const path = getFileByKey(key);
|
|
|
|
|
|
2025-02-04 22:32:53 +01:00
|
|
|
// By default, the key for the persistor is "persist:root", which
|
|
|
|
|
// contains a special character. We run the key through
|
|
|
|
|
// `encodeURIcomponent`, so we don't trigger any special character
|
|
|
|
|
// filename errors. However, on Android, react-native-fs doesn't resolve
|
|
|
|
|
// the paths properly and fails to find the encoded variant. Hence,
|
|
|
|
|
// we'll also "unencode" the variant and check for existence of any of
|
|
|
|
|
// the two files.
|
|
|
|
|
const storeFileExists = (await Promise.all([
|
|
|
|
|
exists(path),
|
|
|
|
|
exists(decodeURIComponent(path))
|
|
|
|
|
])).some((d) => d === true);
|
|
|
|
|
|
2025-01-27 17:21:44 +01:00
|
|
|
// GUARD: Check whether a store already exists on the filesystem
|
2025-02-04 22:32:53 +01:00
|
|
|
if (storeFileExists) {
|
2025-01-27 17:21:44 +01:00
|
|
|
// In which case, we'll read it from disk
|
|
|
|
|
return readFile(path);
|
|
|
|
|
} else {
|
|
|
|
|
// If not, attempt to read the previous store from AsyncStorage
|
|
|
|
|
const oldStore = await AsyncStorage.getItem(key);
|
|
|
|
|
|
|
|
|
|
// GUARD: If it exists, migrate it to a file on the filesystem
|
|
|
|
|
if (oldStore) {
|
|
|
|
|
await ensureDirectoryExists();
|
|
|
|
|
await writeFile(path, oldStore);
|
|
|
|
|
return oldStore;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
removeItem(key) {
|
|
|
|
|
return unlink(getFileByKey(key));
|
|
|
|
|
},
|
|
|
|
|
async setItem(key, value) {
|
|
|
|
|
await ensureDirectoryExists();
|
|
|
|
|
return writeFile(getFileByKey(key), value);
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default MigratedStorage;
|