Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F33304410
D14251.1768786908.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
13 KB
Referenced Files
None
Subscribers
None
D14251.1768786908.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D14251: [native] refactor `BackupHandler`
Attached
Detach File
Event Timeline
Log In to Comment