Skip to content

Instantly share code, notes, and snippets.

@ethan605
Created August 2, 2017 13:11
Show Gist options
  • Save ethan605/35d3d52d91082bad53b5e7b7f8d8497a to your computer and use it in GitHub Desktop.
Save ethan605/35d3d52d91082bad53b5e7b7f8d8497a to your computer and use it in GitHub Desktop.
/**
* @providesModule WeFit.Redux.Store.createStorePersistor
*/
import { AsyncStorage, Platform } from 'react-native';
import { Logger } from '@onaclover/react-native-utils';
import { createPersistor, createTransform, getStoredState } from 'redux-persist';
import FilesystemStorage from 'redux-persist-filesystem-storage';
import _ from 'lodash';
// Constants
import { AVAILABLE_LANGUAGES, DEFAULT_LANGUAGE } from 'redux/constants';
import { DEBUGS } from 'redux/flags';
// Models
import { Amenity, City, District, FitnessType } from 'app/models/BaseStaticData';
import Country from 'app/models/Country';
import Location from 'app/models/Location';
import RemoteConfigs from 'app/models/RemoteConfigs';
import Studio from 'app/models/Studio';
import TimeRange from 'app/models/TimeRange';
import User from 'app/models/User';
// Persist to local storage
function inboundTransform(partialState, key) {
if (key === 'auth')
return {
...partialState,
emailAuth: undefined, facebookOauth: undefined, socialAuth: undefined,
};
// Exclude `rehydrated` state from being persisted
if (key === 'meta')
return { ...partialState, rehydrated: undefined };
if (key === 'shared')
return { ...partialState, globalAlert: undefined };
if (key === 'staticData')
return {
...partialState,
fitnessTypeIndices: undefined,
studioByBrandIndices: undefined,
studioIndices: undefined,
errorKey: undefined,
};
return partialState;
}
// Rehydrate from local storage
function outboundTransform(partialState, key) {
if (partialState == null) return null;
if (key === 'auth') {
const { userData: rawUserData } = partialState;
const userData = DEBUGS.UNAUTHORIZED ? null : User.build(rawUserData);
const { auth_token: authToken, membership, settings } = userData || {};
return { ...partialState, authToken, membership, settings, userData };
}
if (key === 'shared') {
const {
filters: { amenities, districts, fitnessTypes, timeRanges },
language, userLocation: { data } = {},
} = partialState;
// Check if current language is available or not, fallback to vi-VN
const availableLanguageCodes = _.map(AVAILABLE_LANGUAGES, 'code');
const lang = _.includes(availableLanguageCodes, language) ? language : DEFAULT_LANGUAGE;
return {
...partialState,
filters: {
amenities: Amenity.buildArray(amenities),
districts: District.buildArray(districts),
fitnessTypes: FitnessType.buildArray(fitnessTypes),
timeRanges: TimeRange.buildArray(timeRanges),
},
language: lang,
userLocation: {
data: Location.build(data),
error: null,
loading: false,
},
};
}
if (key === 'staticData') {
const {
amenities, cities, countries, districts, fitnessTypes, remoteConfigs,
studiosByCity: rawStudiosByCity,
} = partialState;
const studiosByCity = _.fromPairs(
_.map(rawStudiosByCity, (studios, city) => [city, Studio.buildArray(studios)])
);
return {
...partialState,
studiosByCity,
amenities: Amenity.buildArray(amenities),
cities: City.buildArray(cities),
countries: Country.buildArray(countries),
districts: District.buildArray(districts),
fitnessTypes: FitnessType.buildArray(fitnessTypes),
remoteConfigs: RemoteConfigs.build(remoteConfigs) || {},
// Indexing
fitnessTypeIndices: {},
studioByBrandIndices: {},
studioIndices: {},
// Keys of error loading to retry later
retryKeys: [],
// Loading progress
expectedRequests: [],
loadedRequests: [],
loadingProgress: 0,
};
}
return partialState;
}
export default function createStorePersistor(store) {
const defaultState = store.getState();
const configs = {
blacklist: ['appRouter', 'mainRouter', 'rootRouter', 'serviceApi'],
storage: Platform.select({
/**
* To mitigate storage size limitations on Android
* - https://github.com/rt2zz/redux-persist/issues/199
* - https://github.com/rt2zz/redux-persist/issues/284
*/
android: FilesystemStorage,
ios: AsyncStorage,
}),
transforms: [createTransform(inboundTransform, outboundTransform)],
};
getStoredState(configs, async (err, persistedState) => {
const persistor = createPersistor(store, configs);
const { meta: {
allDataVersion: latestAllDataVersion,
sharedDataVersion: latestSharedDataVersion,
staticDataVersion: latestStaticDataVersion,
} = {} } = defaultState;
const { meta: {
allDataVersion: currentAllDataVersion = 0,
sharedDataVersion: currentSharedDataVersion = 0,
staticDataVersion: currentStaticDataVersion = 0,
} = {} } = persistedState || {};
const { meta, shared, staticData } = defaultState;
const purgedKeys = [];
const newMeta = { ...meta };
const newPersistedState = { ...persistedState };
// Check & remove all data if needed
if (parseInt(currentAllDataVersion) < parseInt(latestAllDataVersion)) {
Logger.log(`Purging all data...(current version: ${currentAllDataVersion}, \
latest version: ${latestAllDataVersion})`);
// Purge all data
await persistor.purge();
await persistor.rehydrate(defaultState);
// Mark that the rehydration is done
await persistor.rehydrate({ meta: { rehydrated: true } });
return;
}
// Check and purge persisted states if data versions not match
if (parseInt(currentSharedDataVersion) < parseInt(latestSharedDataVersion)) {
Logger.log(`Purging shared data...(current version: ${currentSharedDataVersion}, \
latest version: ${latestSharedDataVersion})`);
purgedKeys.push('shared');
newMeta.sharedDataVersion = latestSharedDataVersion;
newPersistedState.shared = shared;
}
if (parseInt(currentStaticDataVersion) < parseInt(latestStaticDataVersion)) {
Logger.log(`Purging static data...(current version: ${currentStaticDataVersion}, \
latest version: ${latestStaticDataVersion})`);
purgedKeys.push('staticData');
newMeta.staticDataVersion = latestStaticDataVersion;
newPersistedState.staticData = staticData;
}
// Update latest meta
newPersistedState.meta = newMeta;
// Purge keys & rehydrate persisted state
await persistor.purge(purgedKeys);
await persistor.rehydrate(newPersistedState);
// Mark that the rehydration is done
await persistor.rehydrate({ meta: { rehydrated: true } });
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment