diff --git a/android/app/src/main/java/io/cozy/flagship/mobile/MainApplication.java b/android/app/src/main/java/io/cozy/flagship/mobile/MainApplication.java
index 3b26efec0..18e959089 100644
--- a/android/app/src/main/java/io/cozy/flagship/mobile/MainApplication.java
+++ b/android/app/src/main/java/io/cozy/flagship/mobile/MainApplication.java
@@ -65,15 +65,15 @@ public void onCreate() {
WebView.setWebContentsDebuggingEnabled(true);
OkHttpClientProvider.setOkHttpClientFactory(new UserAgentClientFactory());
- try {
- Field field = CursorWindow.class.getDeclaredField("sCursorWindowSize");
- field.setAccessible(true);
- field.set(null, 50 * 1024 * 1024); // 50MB
- } catch (Exception e) {
- if (BuildConfig.DEBUG) {
- e.printStackTrace();
- }
- }
+ // try {
+ // Field field = CursorWindow.class.getDeclaredField("sCursorWindowSize");
+ // field.setAccessible(true);
+ // field.set(null, 50 * 1024 * 1024); // 50MB
+ // } catch (Exception e) {
+ // if (BuildConfig.DEBUG) {
+ // e.printStackTrace();
+ // }
+ // }
}
/**
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index e5db4d523..98be8e217 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -173,6 +173,9 @@ PODS:
- MLImage (= 1.0.0-beta2)
- MLKitCommon (~> 5.0)
- Protobuf (~> 3.12)
+ - MMKV (1.3.2):
+ - MMKVCore (~> 1.3.2)
+ - MMKVCore (1.3.2)
- nanopb (2.30908.0):
- nanopb/decode (= 2.30908.0)
- nanopb/encode (= 2.30908.0)
@@ -423,6 +426,9 @@ PODS:
- react-native-mlkit-ocr (0.3.0):
- GoogleMLKit/TextRecognition (= 2.6.0)
- React
+ - react-native-mmkv (2.5.1):
+ - MMKV (>= 1.2.13)
+ - React-Core
- react-native-netinfo (9.3.7):
- React-Core
- react-native-print (0.11.0):
@@ -616,6 +622,7 @@ DEPENDENCIES:
- "react-native-gzip (from `../node_modules/@fengweichong/react-native-gzip`)"
- react-native-idle-timer (from `../node_modules/react-native-idle-timer`)
- react-native-mlkit-ocr (from `../node_modules/react-native-mlkit-ocr`)
+ - react-native-mmkv (from `../node_modules/react-native-mmkv`)
- "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
- react-native-print (from `../node_modules/react-native-print`)
- "react-native-receive-sharing-intent (from `../node_modules/@mythologi/react-native-receive-sharing-intent`)"
@@ -689,6 +696,8 @@ SPEC REPOS:
- MLKitTextRecognition
- MLKitTextRecognitionCommon
- MLKitVision
+ - MMKV
+ - MMKVCore
- nanopb
- NVHTarGzip
- OpenSSL-Universal
@@ -756,6 +765,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-idle-timer"
react-native-mlkit-ocr:
:path: "../node_modules/react-native-mlkit-ocr"
+ react-native-mmkv:
+ :path: "../node_modules/react-native-mmkv"
react-native-netinfo:
:path: "../node_modules/@react-native-community/netinfo"
react-native-print:
@@ -872,6 +883,8 @@ SPEC CHECKSUMS:
MLKitTextRecognition: 8b0e0023a4babc66ca83d8b82864e57931164445
MLKitTextRecognitionCommon: 3e84602c928fe2b775fae81376f2136324cbd763
MLKitVision: e87dc3f2e456a6ab32361ebd985e078dd2746143
+ MMKV: f21593c0af4b3f2a0ceb8f820f28bb639ea22bb7
+ MMKVCore: 31b4cb83f8266467eef20a35b6d78e409a11060d
nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96
NVHTarGzip: 74cc227b902e5725900d37eb6d79b57e93005a73
OpenSSL-Universal: 1aa4f6a6ee7256b83db99ec1ccdaa80d10f9af9b
@@ -901,6 +914,7 @@ SPEC CHECKSUMS:
react-native-gzip: 5ffb84bf191c7cd135338eca748317bc466d41a1
react-native-idle-timer: f1920a59fe776340d004ff9de13c4a6eedcc8807
react-native-mlkit-ocr: 72cdbde86f8d29cba26cf9fa0a1865fe45c8f8d6
+ react-native-mmkv: 69b9c003f10afdd01addf7c6ee784ce42ee2eff3
react-native-netinfo: 2517ad504b3d303e90d7a431b0fcaef76d207983
react-native-print: f704aef52d931bfce6d1d84351dbb5232d7ecb89
react-native-receive-sharing-intent: 0c21b8e80f629a73341f2566ce9b99df8124bb10
diff --git a/package.json b/package.json
index 523f57909..cc509d191 100644
--- a/package.json
+++ b/package.json
@@ -94,6 +94,8 @@
"react-native-localize": "2.2.6",
"react-native-mask-input": "1.2.1",
"react-native-mlkit-ocr": "^0.3.0",
+ "react-native-mmkv": "2.5.1",
+ "react-native-mmkv-flipper-plugin": "^1.0.0",
"react-native-permissions": "^3.9.3",
"react-native-play-install-referrer": "^1.1.8",
"react-native-print": "0.11.0",
diff --git a/src/App.js b/src/App.js
index 5aae902a8..fcae628c8 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,7 +1,15 @@
import { NavigationContainer } from '@react-navigation/native'
import { decode, encode } from 'base-64'
import React, { useEffect, useState } from 'react'
-import { StatusBar, StyleSheet, View } from 'react-native'
+import {
+ StatusBar,
+ StyleSheet,
+ View,
+ ActivityIndicator,
+ InteractionManager
+} from 'react-native'
+import { MMKV } from 'react-native-mmkv'
+import { initializeMMKVFlipper } from 'react-native-mmkv-flipper-plugin'
import { Provider } from 'react-redux'
import { PersistGate } from 'redux-persist/integration/react'
import FlipperAsyncStorage from 'rn-flipper-async-storage-advanced'
@@ -55,6 +63,17 @@ import {
import LauncherView from '/screens/konnectors/LauncherView'
+import {
+ hasMigratedFromAsyncStorage,
+ migrateFromAsyncStorage,
+ storage
+} from '/libs/localStore/storage'
+
+// add this line inside your App.tsx
+if (__DEV__) {
+ initializeMMKVFlipper({ default: storage })
+}
+
// Polyfill needed for cozy-client connection
if (!global.btoa) {
global.btoa = encode
@@ -207,6 +226,30 @@ const WrappedApp = () => {
const Wrapper = () => {
const [hasCrypto, setHasCrypto] = useState(false)
+ const [hasMigrated, setHasMigrated] = useState(hasMigratedFromAsyncStorage)
+
+ useEffect(() => {
+ if (!hasMigratedFromAsyncStorage) {
+ InteractionManager.runAfterInteractions(async () => {
+ try {
+ await migrateFromAsyncStorage()
+ setHasMigrated(true)
+ } catch (e) {
+ // TODO: fall back to AsyncStorage? Wipe storage clean and use MMKV? Crash app?
+ }
+ })
+ }
+ }, [])
+
+ if (!hasMigrated) {
+ // show loading indicator while app is migrating storage...
+ return (
+
+
+
+ )
+ }
+
return (
<>
{__DEV__ && }
diff --git a/src/app/domain/backup/services/manageLocalBackupConfig.ts b/src/app/domain/backup/services/manageLocalBackupConfig.ts
index 6bea3ee6e..4a23f5e2a 100644
--- a/src/app/domain/backup/services/manageLocalBackupConfig.ts
+++ b/src/app/domain/backup/services/manageLocalBackupConfig.ts
@@ -52,11 +52,19 @@ const INITIAL_BACKUP_CONFIG: LocalBackupConfig = {
export const getLocalBackupConfig = async (
client: CozyClient
): Promise => {
+ const startTime = performance.now()
+
const backupConfig = await getUserPersistedData(
client,
UserPersistedStorageKeys.LocalBackupConfig
)
+ const endTime = performance.now()
+
+ console.log(
+ `🟢 getLocalBackupConfig took ${endTime - startTime} milliseconds.`
+ )
+
if (backupConfig === null) {
throw new Error(t('services.backup.errors.configNotInitialized'))
}
@@ -142,7 +150,7 @@ export const setMediaAsBackuped = async (
uri: media.uri,
creationDate: media.creationDate,
modificationDate: media.modificationDate,
- remoteId: documentCreated.id as string,
+ remoteId: documentCreated.id!,
md5: documentCreated.attributes.md5sum
}
diff --git a/src/libs/localStore/storage.ts b/src/libs/localStore/storage.ts
index bc3f3522a..ea3ad499b 100644
--- a/src/libs/localStore/storage.ts
+++ b/src/libs/localStore/storage.ts
@@ -3,7 +3,9 @@ import { BiometryType } from 'react-native-biometrics'
import { logger } from '/libs/functions/logger'
const log = logger('storage.ts')
+import { MMKV } from 'react-native-mmkv'
+export const storage = new MMKV()
const { setItem, getItem, removeItem } = AsyncStorage
export enum StorageKeys {
AutoLockEnabled = '@cozy_AmiralApp_autoLockEnabled',
@@ -37,7 +39,27 @@ export const storeData = async (
value: StorageItems[keyof StorageItems]
): Promise => {
try {
- await setItem(name, JSON.stringify(value))
+ const startTime = performance.now()
+
+ const res = JSON.stringify(value)
+
+ const endTime = performance.now()
+
+ console.log(`🔴 storeData parse took ${endTime - startTime} milliseconds.`)
+
+ const startTime2 = performance.now()
+
+ if (name.includes('AmiralAppLocalBackupConfig')) {
+ storage.set(name, res)
+ } else {
+ await setItem(name, res)
+ }
+
+ const endTime2 = performance.now()
+
+ console.log(
+ `🔴 storeData write took ${endTime2 - startTime2} milliseconds.`
+ )
} catch (error) {
log.error(`Failed to store key "${name}" to persistent storage`, error)
}
@@ -45,9 +67,29 @@ export const storeData = async (
export const getData = async (name: StorageKeys): Promise => {
try {
- const value = await getItem(name)
+ const startTime = performance.now()
+
+ let value
+
+ if (name.includes('AmiralAppLocalBackupConfig')) {
+ value = storage.getString(name)
+ } else {
+ value = await getItem(name)
+ }
+
+ const endTime = performance.now()
+
+ console.log(`🟢 getData read took ${endTime - startTime} milliseconds.`)
+
+ const startTime2 = performance.now()
- return value !== null ? (JSON.parse(value) as T) : null
+ const res = value !== null ? (JSON.parse(value) as T) : null
+
+ const endTime2 = performance.now()
+
+ console.log(`🟢 getData parse took ${endTime2 - startTime2} milliseconds.`)
+
+ return res
} catch (error) {
log.error(`Failed to get key "${name}" from persistent storage`, error)
return null
@@ -65,3 +107,41 @@ export const clearData = async (): Promise => {
log.error(`Failed to clear data from persistent storage`, error)
}
}
+
+// TODO: Remove `hasMigratedFromAsyncStorage` after a while (when everyone has migrated)
+export const hasMigratedFromAsyncStorage = storage.getBoolean(
+ 'hasMigratedFromAsyncStorage'
+)
+
+// TODO: Remove `hasMigratedFromAsyncStorage` after a while (when everyone has migrated)
+export async function migrateFromAsyncStorage(): Promise {
+ console.log('Migrating from AsyncStorage -> MMKV...')
+ const start = global.performance.now()
+
+ const keys = await AsyncStorage.getAllKeys()
+
+ for (const key of keys) {
+ try {
+ const value = await AsyncStorage.getItem(key)
+
+ if (value != null) {
+ if (['true', 'false'].includes(value)) {
+ storage.set(key, value === 'true')
+ } else {
+ storage.set(key, value)
+ }
+ }
+ } catch (error) {
+ console.error(
+ `Failed to migrate key "${key}" from AsyncStorage to MMKV!`,
+ error
+ )
+ throw error
+ }
+ }
+
+ storage.set('hasMigratedFromAsyncStorage', true)
+
+ const end = global.performance.now()
+ console.log(`Migrated from AsyncStorage -> MMKV in ${end - start}ms!`)
+}
diff --git a/yarn.lock b/yarn.lock
index f12e9ec8a..90aee61e3 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -16712,6 +16712,16 @@ react-native-mlkit-ocr@^0.3.0:
resolved "https://registry.yarnpkg.com/react-native-mlkit-ocr/-/react-native-mlkit-ocr-0.3.0.tgz#b5b65dbe1fffdca0ca2012b1cab56cca6ea1bf58"
integrity sha512-8oNJwNMGsUup41nWlKA01iW6xiNkCcXM3ANDsOKKijvsrd3eWNs7kL0Yhpt3Cq64zSyjoeqkdTdOCDiI6Pv6KA==
+react-native-mmkv-flipper-plugin@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/react-native-mmkv-flipper-plugin/-/react-native-mmkv-flipper-plugin-1.0.0.tgz#f87f747d8cea51d2b12a1e711287feff5f212788"
+ integrity sha512-e3owMIBzXez45Wz8Ac84Vf1FmfwMXFVUpf/gCDWDLq19w7iCipVezQjlZo8ISVza8aQf1G4ggaK/r+qqtGmRlg==
+
+react-native-mmkv@2.5.1:
+ version "2.5.1"
+ resolved "https://registry.yarnpkg.com/react-native-mmkv/-/react-native-mmkv-2.5.1.tgz#29fc462077fab16a5e1b79570fbf8acaac9d87b4"
+ integrity sha512-5eQu25z3H6zf6w0NkJoTuFEFrbOu6luZxZ6+rK1W+XwY/rjPSFZFQPVtMaz3im90RbILFXXM/KrFGZrpaJJRoQ==
+
react-native-modal-datetime-picker@^14.0.0:
version "14.0.1"
resolved "https://registry.yarnpkg.com/react-native-modal-datetime-picker/-/react-native-modal-datetime-picker-14.0.1.tgz#d9c6df4ff85bf1cfbe108c756dc26dcca4cc5f2f"