Skip to content

Commit 7e2c3ed

Browse files
feat(storage): Implement MMKV storage adapter for redux-persist and enhance migration utilities
- Refactored storage implementation to create a custom adapter for MMKV compatible with redux-persist. - Improved migration utility functions to handle data migration from AsyncStorage to MMKV with better error handling and logging. - Added diagnostic and recovery options for users to recover lost data during migration. - Enhanced Settings screen with a data recovery option for users who may have lost their reading history and bookmarks. - Introduced a new MigrationHelper utility for checking storage status and manual recovery of data.
1 parent 30bd979 commit 7e2c3ed

7 files changed

Lines changed: 126610 additions & 174 deletions

File tree

App.js

Lines changed: 89 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
import React, { useEffect, useState } from 'react';
22
import { Provider } from 'react-redux';
33
import { PersistGate } from 'redux-persist/integration/react';
4-
import { persistor, store, performStorageMigration } from './src/Redux/Store';
4+
import {
5+
initializeStore,
6+
performStorageMigration,
7+
store,
8+
persistor,
9+
isStoreReady
10+
} from './src/Redux/Store';
511
import { GestureHandlerRootView } from 'react-native-gesture-handler';
612
import { RootNavigation } from './src/Navigation';
713
import Loading from './src/Components/UIComp/Loading';
@@ -20,102 +26,115 @@ import {
2026
import NotificationSubscriptionBootstrapper from './src/InkNest-Externals/Notifications/components/NotificationSubscriptionBootstrapper';
2127

2228
/**
23-
* Migration gate component that handles AsyncStorage -> MMKV migration
24-
* before rendering the app
29+
* AppContent component - rendered after store is initialized
2530
*/
26-
function MigrationGate({ children }) {
27-
const [isMigrating, setIsMigrating] = useState(true);
28-
const [migrationError, setMigrationError] = useState(null);
29-
31+
function AppContent() {
3032
useEffect(() => {
31-
async function runMigration() {
32-
try {
33-
// Perform migration from AsyncStorage to MMKV
34-
await performStorageMigration();
35-
setIsMigrating(false);
36-
} catch (error) {
37-
console.error('Migration failed:', error);
38-
crashlytics().recordError(error);
39-
setMigrationError(error);
40-
// Even on error, allow app to continue (may lose old data but app works)
41-
setIsMigrating(false);
42-
}
43-
}
44-
45-
runMigration();
46-
}, []);
47-
48-
if (isMigrating) {
49-
return <Loading />;
50-
}
51-
52-
return children;
53-
}
54-
55-
/**
56-
* The main App component that sets up the root of the application.
57-
* It includes the GestureHandlerRootView for gesture handling,
58-
* the Redux Provider for state management, and the PersistGate
59-
* for persisting the Redux store.
60-
*
61-
* @returns {JSX.Element} The root component of the application.
62-
*/
63-
const App = () => {
64-
useEffect(() => {
65-
// Configure Google Sign-In
6633
configureGoogleSignIn();
67-
68-
// Listen to auth state changes
6934
const unsubscribeAuth = store.dispatch(listenToAuthChanges());
7035

7136
if (!__DEV__) {
72-
// Initialize Firebase Crashlytics
7337
crashlytics().log('App mounted.');
74-
75-
// Enable analytics collection
7638
analytics().setAnalyticsCollectionEnabled(true);
7739

78-
// Catch JS errors and report to Crashlytics
7940
const errorHandler = (error, isFatal) => {
8041
if (isFatal) {
8142
crashlytics().recordError(error);
8243
}
8344
return false;
8445
};
85-
86-
// Set error handlers
8746
ErrorUtils.setGlobalHandler(errorHandler);
88-
89-
return () => {
90-
// Clean up auth listener
91-
if (unsubscribeAuth) unsubscribeAuth();
92-
};
9347
}
9448

9549
return () => {
96-
// Clean up auth listener
9750
if (unsubscribeAuth) unsubscribeAuth();
9851
};
9952
}, []);
10053

54+
return (
55+
<Provider store={store}>
56+
<PersistGate loading={<Loading />} persistor={persistor}>
57+
<PaperProvider>
58+
<BannerProvider>
59+
<NotificationSubscriptionBootstrapper />
60+
<RootNavigation />
61+
<Toast />
62+
<ForceUpdate />
63+
</BannerProvider>
64+
</PaperProvider>
65+
</PersistGate>
66+
</Provider>
67+
);
68+
}
69+
70+
/**
71+
* Main App component
72+
*/
73+
const App = () => {
74+
const [isReady, setIsReady] = useState(false);
75+
const [migrationError, setMigrationError] = useState(null);
76+
77+
useEffect(() => {
78+
let isMounted = true;
79+
80+
async function setupApp() {
81+
try {
82+
console.log('[App] Starting app setup...');
83+
84+
// Run migration before anything else
85+
console.log('[App] Running storage migration...');
86+
await performStorageMigration();
87+
88+
// Initialize store after migration
89+
console.log('[App] Initializing store...');
90+
initializeStore();
91+
92+
if (!isStoreReady()) {
93+
throw new Error('Store initialization failed');
94+
}
95+
96+
console.log('[App] Setup complete');
97+
if (isMounted) {
98+
setIsReady(true);
99+
}
100+
} catch (error) {
101+
console.error('[App] Setup failed:', error);
102+
crashlytics().recordError(error);
103+
104+
// Try to recover by initializing store anyway
105+
try {
106+
initializeStore();
107+
if (isStoreReady() && isMounted) {
108+
setIsReady(true);
109+
}
110+
} catch (recoveryError) {
111+
if (isMounted) {
112+
setMigrationError(recoveryError);
113+
}
114+
}
115+
}
116+
}
117+
118+
setupApp();
119+
120+
return () => {
121+
isMounted = false;
122+
};
123+
}, []);
124+
125+
if (!isReady) {
126+
return (
127+
<GestureHandlerRootView style={{ flex: 1 }}>
128+
<Loading />
129+
</GestureHandlerRootView>
130+
);
131+
}
132+
101133
return (
102134
<GestureHandlerRootView style={{ flex: 1 }}>
103135
<ConfigCatProvider
104136
sdkKey={__DEV__ ? CONFIGCAT_SDK_KEY_TEST : CONFIGCAT_SDK_KEY_PROD}>
105-
<Provider store={store}>
106-
<MigrationGate>
107-
<PersistGate loading={<Loading />} persistor={persistor}>
108-
<PaperProvider>
109-
<BannerProvider>
110-
<NotificationSubscriptionBootstrapper />
111-
<RootNavigation />
112-
<Toast />
113-
<ForceUpdate />
114-
</BannerProvider>
115-
</PaperProvider>
116-
</PersistGate>
117-
</MigrationGate>
118-
</Provider>
137+
<AppContent />
119138
</ConfigCatProvider>
120139
</GestureHandlerRootView>
121140
);

0 commit comments

Comments
 (0)