diff --git a/native/backup/backup-handler.js b/native/backup/backup-handler.js new file mode 100644 --- /dev/null +++ b/native/backup/backup-handler.js @@ -0,0 +1,62 @@ +// @flow + +import AsyncStorage from '@react-native-async-storage/async-storage'; +import * as React from 'react'; +import { useSelector } from 'react-redux'; + +import { isLoggedIn } from 'lib/selectors/user-selectors.js'; +import { isDev } from 'lib/utils/dev-utils.js'; +import { getMessageForException } from 'lib/utils/errors.js'; + +import { BACKUP_HASH_STORAGE_KEY } from './constants.js'; +import { convertObjToBytes } from './conversion-utils.js'; +import { useClientBackup } from './use-client-backup.js'; +import { commUtilsModule } from '../native-modules.js'; +import Alert from '../utils/alert.js'; +import { useIsCurrentUserStaff } from '../utils/staff-utils.js'; + +function BackupHandler(): null { + const userStore = useSelector(state => state.userStore); + const currentUserID = useSelector( + state => state.currentUserInfo && state.currentUserInfo.id, + ); + const loggedIn = useSelector(isLoggedIn); + const isStaff = useIsCurrentUserStaff(); + + const { uploadBackupProtocol } = useClientBackup(); + + React.useEffect(() => { + (async () => { + if (!loggedIn || !(isStaff || isDev)) { + return; + } + + const userData = { userStore }; + const userDataBytes = convertObjToBytes(userData); + const sha256Hash = commUtilsModule.sha256(userDataBytes.buffer); + + const recentBackupHash = await AsyncStorage.getItem( + BACKUP_HASH_STORAGE_KEY, + ); + + if (!recentBackupHash || sha256Hash !== recentBackupHash) { + let message; + try { + await uploadBackupProtocol(userData); + message = 'Backup successfully uploaded'; + await AsyncStorage.setItem(BACKUP_HASH_STORAGE_KEY, sha256Hash); + } catch (e) { + console.error(`Backup uploading error: ${e}`); + message = `Backup uploading error: ${String( + getMessageForException(e), + )}`; + } + Alert.alert('Backup protocol result', message); + } + })(); + }, [currentUserID, isStaff, loggedIn, uploadBackupProtocol, userStore]); + + return null; +} + +export default BackupHandler; diff --git a/native/backup/constants.js b/native/backup/constants.js --- a/native/backup/constants.js +++ b/native/backup/constants.js @@ -1,3 +1,5 @@ // @flow export const BACKUP_ID_LENGTH = 32; //256 bits + +export const BACKUP_HASH_STORAGE_KEY = 'RECENT_USER_DATA_HASH'; diff --git a/native/root.react.js b/native/root.react.js --- a/native/root.react.js +++ b/native/root.react.js @@ -26,6 +26,7 @@ import { RegistrationContextProvider } from './account/registration/registration-context-provider.react.js'; import NativeEditThreadAvatarProvider from './avatars/native-edit-thread-avatar-provider.react.js'; +import BackupHandler from './backup/backup-handler.js'; import ChatContextProvider from './chat/chat-context-provider.react.js'; import MessageEditingContextProvider from './chat/message-editing-context-provider.react.js'; import { FeatureFlagsProvider } from './components/feature-flags-provider.react.js'; @@ -240,6 +241,7 @@ + ); let navigation;