Page MenuHomePhorge

D14251.1768786908.diff
No OneTemporary

Size
13 KB
Referenced Files
None
Subscribers
None

D14251.1768786908.diff

diff --git a/lib/selectors/user-selectors.js b/lib/selectors/user-selectors.js
--- a/lib/selectors/user-selectors.js
+++ b/lib/selectors/user-selectors.js
@@ -18,6 +18,7 @@
import {
type IdentityPlatformDetails,
identityDeviceTypes,
+ type RawDeviceList,
} from '../types/identity-service-types.js';
import type {
RelativeMemberInfo,
@@ -278,6 +279,19 @@
},
);
+const getOwnRawDeviceList: (state: BaseAppState<>) => ?RawDeviceList =
+ createSelector(
+ (state: BaseAppState<>) => state.auxUserStore.auxUserInfos,
+ (state: BaseAppState<>) =>
+ state.currentUserInfo && state.currentUserInfo.id,
+ (auxUserInfos: AuxUserInfos, currentUserID: ?string): ?RawDeviceList => {
+ if (!currentUserID) {
+ return null;
+ }
+ return auxUserInfos[currentUserID]?.deviceList;
+ },
+ );
+
function getKeyserverDeviceID(
devices: $ReadOnlyArray<DeviceIDAndPlatformDetails>,
): ?string {
@@ -340,4 +354,5 @@
getAllPeerDevices,
getAllPeerUserIDAndDeviceIDs,
getOwnPrimaryDeviceID,
+ getOwnRawDeviceList,
};
diff --git a/native/backup/backup-handler.js b/native/backup/backup-handler.js
--- a/native/backup/backup-handler.js
+++ b/native/backup/backup-handler.js
@@ -2,17 +2,23 @@
import * as React from 'react';
+import { setPeerDeviceListsActionType } from 'lib/actions/aux-user-actions.js';
import { createUserKeysBackupActionTypes } from 'lib/actions/backup-actions.js';
import {
useCurrentIdentityUserState,
type CurrentIdentityUserState,
} from 'lib/hooks/peer-list-hooks.js';
-import { useDeviceKind } from 'lib/hooks/primary-device-hooks.js';
-import { isLoggedIn } from 'lib/selectors/user-selectors.js';
+import { usePersistedStateLoaded } from 'lib/selectors/app-state-selectors.js';
+import {
+ getOwnRawDeviceList,
+ isLoggedIn,
+} from 'lib/selectors/user-selectors.js';
import { useStaffAlert } from 'lib/shared/staff-utils.js';
import { useTunnelbroker } from 'lib/tunnelbroker/tunnelbroker-context.js';
+import { rawDeviceListFromSignedList } from 'lib/utils/device-list-utils.js';
import { getMessageForException } from 'lib/utils/errors.js';
import { useDispatchActionPromise } from 'lib/utils/redux-promise-utils.js';
+import { useDispatch } from 'lib/utils/redux-utils.js';
import { usingRestoreFlow } from 'lib/utils/services-utils.js';
import { useClientBackup } from './use-client-backup.js';
@@ -22,182 +28,196 @@
import { useStaffCanSee } from '../utils/staff-utils.js';
function BackupHandler(): null {
- const loggedIn = useSelector(isLoggedIn);
const staffCanSee = useStaffCanSee();
- const isBackground = useSelector(
- state => state.lifecycleState === 'background',
- );
- const canPerformBackupOperation = loggedIn && !isBackground;
- const deviceKind = useDeviceKind();
const { showAlertToStaff } = useStaffAlert();
- const latestBackupInfo = useSelector(
- state => state.backupStore.latestBackupInfo,
- );
- const dispatchActionPromise = useDispatchActionPromise();
- const { createUserKeysBackup } = useClientBackup();
- const backupUploadInProgress = React.useRef<boolean>(false);
- const startingBackupHandlerInProgress = React.useRef<boolean>(false);
- const [handlerStarted, setHandlerStarted] = React.useState(false);
const getCurrentIdentityUserState = useCurrentIdentityUserState();
- const migrateToNewFlow = useMigrationToNewFlow();
const { socketState } = useTunnelbroker();
+ const migrateToNewFlow = useMigrationToNewFlow();
+ const { createUserKeysBackup } = useClientBackup();
+ const dispatchActionPromise = useDispatchActionPromise();
+ const dispatch = useDispatch();
- const startBackupHandler = React.useCallback(() => {
- try {
- commCoreModule.startBackupHandler();
- setHandlerStarted(true);
- } catch (err) {
- const message = getMessageForException(err) ?? 'unknown error';
- showAlertToStaff('Error starting backup handler', message);
- console.log('Error starting backup handler:', message);
- }
- }, [showAlertToStaff]);
-
- const stopBackupHandler = React.useCallback(() => {
- try {
- commCoreModule.stopBackupHandler();
- setHandlerStarted(false);
- } catch (err) {
- const message = getMessageForException(err) ?? 'unknown error';
- showAlertToStaff('Error stopping backup handler', message);
- console.log('Error stopping backup handler:', message);
- }
- }, [showAlertToStaff]);
-
- React.useEffect(() => {
- if (
- !staffCanSee ||
- startingBackupHandlerInProgress.current ||
- deviceKind !== 'primary'
- ) {
- return;
- }
-
- if (!handlerStarted && canPerformBackupOperation) {
- startBackupHandler();
- }
+ const persistedStateLoaded = usePersistedStateLoaded();
+ const ownRawDeviceList = useSelector(getOwnRawDeviceList);
+ const latestBackupInfo = useSelector(
+ state => state.backupStore.latestBackupInfo,
+ );
+ const loggedIn = useSelector(isLoggedIn);
+ const isBackground = useSelector(
+ state => state.lifecycleState === 'background',
+ );
+ const canPerformBackupOperation = loggedIn && !isBackground;
- if (handlerStarted && !canPerformBackupOperation) {
- stopBackupHandler();
- }
- }, [
- canPerformBackupOperation,
- deviceKind,
- handlerStarted,
- staffCanSee,
- startBackupHandler,
- stopBackupHandler,
- ]);
+ // State to force re-render.
+ const [renderCount, setRenderCount] = React.useState(0);
+ const completionRef = React.useRef({ running: false, required: false });
+ const handlerStartedRef = React.useRef(false);
const performMigrationToNewFlow = React.useCallback(
async (currentIdentityUserState: CurrentIdentityUserState) => {
- try {
- const promise = migrateToNewFlow(currentIdentityUserState);
- void dispatchActionPromise(createUserKeysBackupActionTypes, promise);
- await promise;
- } catch (err) {
- const errorMessage = getMessageForException(err) ?? 'unknown error';
- showAlertToStaff(
- 'Error migrating to signed device lists',
- errorMessage,
- );
- console.log('Error migrating to signed device lists', errorMessage);
- }
+ const promise = migrateToNewFlow(currentIdentityUserState);
+ void dispatchActionPromise(createUserKeysBackupActionTypes, promise);
+ await promise;
},
- [dispatchActionPromise, migrateToNewFlow, showAlertToStaff],
+ [dispatchActionPromise, migrateToNewFlow],
);
const performBackupUpload = React.useCallback(async () => {
- try {
- const promise = (async () => {
- const backupID = await createUserKeysBackup();
- return {
- backupID,
- timestamp: Date.now(),
- };
- })();
- void dispatchActionPromise(createUserKeysBackupActionTypes, promise);
- await promise;
- } catch (err) {
- const errorMessage = getMessageForException(err) ?? 'unknown error';
- showAlertToStaff('Error creating User Keys backup', errorMessage);
- console.log('Error creating User Keys backup', errorMessage);
- }
- }, [createUserKeysBackup, dispatchActionPromise, showAlertToStaff]);
+ const promise = (async () => {
+ const backupID = await createUserKeysBackup();
+ return {
+ backupID,
+ timestamp: Date.now(),
+ };
+ })();
+ void dispatchActionPromise(createUserKeysBackupActionTypes, promise);
+ await promise;
+ }, [createUserKeysBackup, dispatchActionPromise]);
- React.useEffect(() => {
- if (
- !staffCanSee ||
- !canPerformBackupOperation ||
- deviceKind === 'unknown'
- ) {
+ const startBackupHandler = React.useCallback(() => {
+ if (handlerStartedRef.current) {
return;
}
- // In case of primary we need to wait for starting the handler.
- // In case of secondary, we want to proceed and start handler
- // on demand.
- if (deviceKind === 'primary' && !handlerStarted) {
+ commCoreModule.startBackupHandler();
+ handlerStartedRef.current = true;
+ }, []);
+
+ const stopBackupHandler = React.useCallback(() => {
+ if (!handlerStartedRef.current) {
return;
}
- void (async () => {
- // CurrentIdentityUserState is required to check if migration to
- // new flow is needed.
- let currentIdentityUserState: ?CurrentIdentityUserState = null;
- try {
- currentIdentityUserState = await getCurrentIdentityUserState();
- } catch (err) {
- const message = getMessageForException(err) ?? 'unknown error';
- showAlertToStaff('Error fetching current device list:', message);
- console.log('Error fetching current device list:', message);
+ commCoreModule.stopBackupHandler();
+ handlerStartedRef.current = false;
+ }, []);
+
+ React.useEffect(() => {
+ if (!canPerformBackupOperation) {
+ stopBackupHandler();
+ }
+ }, [canPerformBackupOperation, stopBackupHandler]);
+
+ const process = React.useCallback(async () => {
+ let step = 'starting backup handler';
+ try {
+ if (!canPerformBackupOperation) {
return;
}
+ // It is important to use Identity Service as a source of truth
+ // in case of a migration process because local device lists
+ // might be out-of-date.
+ step = 'fetching current device list';
+ const currentIdentityUserState = await getCurrentIdentityUserState();
+ const {
+ currentDeviceList,
+ currentUserPlatformDetails,
+ userID,
+ deviceID,
+ } = currentIdentityUserState;
+
+ // After fetching, it is worth checking if the local state is the same,
+ // in case of inconsistency perform the update.
+ step = 'validating current device list consistency';
+ const currentRawDeviceList =
+ rawDeviceListFromSignedList(currentDeviceList);
+ if (
+ JSON.stringify(ownRawDeviceList?.devices) !==
+ JSON.stringify(currentRawDeviceList.devices)
+ ) {
+ console.log(
+ "Local device list state wasn't updated yet - updating it now",
+ );
+ dispatch({
+ type: setPeerDeviceListsActionType,
+ payload: {
+ deviceLists: {
+ [userID]: currentRawDeviceList,
+ },
+ usersPlatformDetails: {
+ [userID]: currentUserPlatformDetails,
+ },
+ },
+ });
+ }
+
+ // Based on the response from Identity determine if the device
+ // is primary.
+ step = 'checking if device is primary';
+ const isPrimary =
+ currentRawDeviceList.devices.length > 0 &&
+ currentRawDeviceList.devices[0] === deviceID;
+
+ step = 'computing conditions';
const shouldDoMigration =
- usingRestoreFlow &&
- !currentIdentityUserState.currentDeviceList.curPrimarySignature;
+ usingRestoreFlow && !currentDeviceList.curPrimarySignature;
+ const shouldUploadBackup = isPrimary && !latestBackupInfo;
+ // Tunnelbroker connection is required to broadcast
+ // device list updates after migration.
if (shouldDoMigration && !socketState.isAuthorized) {
return;
}
- try {
- if (backupUploadInProgress.current) {
- return;
- }
-
- backupUploadInProgress.current = true;
-
- if (shouldDoMigration && deviceKind === 'primary') {
- await performMigrationToNewFlow(currentIdentityUserState);
- } else if (shouldDoMigration && deviceKind === 'secondary') {
- startBackupHandler();
- await performMigrationToNewFlow(currentIdentityUserState);
- } else if (deviceKind === 'primary' && !latestBackupInfo) {
- await performBackupUpload();
- }
- } catch (e) {
- console.log(e);
- } finally {
- backupUploadInProgress.current = false;
+ // Migration or backup upload are not needed.
+ if (!shouldDoMigration && !shouldUploadBackup) {
+ return;
}
- })();
+
+ step = 'starting backup handler';
+ startBackupHandler();
+
+ // Migration is performed regardless of device kind.
+ if (shouldDoMigration) {
+ step = 'migrating to signed device lists';
+ await performMigrationToNewFlow(currentIdentityUserState);
+ } else if (shouldUploadBackup) {
+ step = 'creating User Keys backup';
+ await performBackupUpload();
+ }
+ } catch (err) {
+ const errorMessage = getMessageForException(err) ?? 'unknown error';
+ const title = `Error ${step}`;
+ showAlertToStaff(title, errorMessage);
+ console.log(title, errorMessage);
+ } finally {
+ completionRef.current.running = false;
+ if (completionRef.current.required) {
+ // Incrementing to trigger re-render. Using state instead of ref
+ // to make sure the next execution is using updated version
+ // of the `process()` callback.
+ setRenderCount(prev => prev + 1);
+ }
+ }
}, [
canPerformBackupOperation,
- deviceKind,
+ dispatch,
getCurrentIdentityUserState,
- handlerStarted,
latestBackupInfo,
+ ownRawDeviceList,
performBackupUpload,
performMigrationToNewFlow,
showAlertToStaff,
socketState.isAuthorized,
- staffCanSee,
startBackupHandler,
]);
+ React.useEffect(() => {
+ if (!staffCanSee || !persistedStateLoaded) {
+ return;
+ }
+
+ if (completionRef.current.running) {
+ completionRef.current.required = true;
+ } else {
+ completionRef.current.running = true;
+ completionRef.current.required = false;
+ void process();
+ }
+ }, [persistedStateLoaded, process, renderCount, staffCanSee]);
+
return null;
}

File Metadata

Mime Type
text/plain
Expires
Mon, Jan 19, 1:41 AM (7 h, 46 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5953821
Default Alt Text
D14251.1768786908.diff (13 KB)

Event Timeline