Skip to content

Conversation

@uhoreg
Copy link
Member

@uhoreg uhoreg commented Nov 28, 2025

Fixes #31187 and parts of #30438

Best to review this commit-by-commit

Checklist

uhoreg and others added 7 commits November 21, 2025 17:00
If cross-signing keys are missing both locally and in 4S, show a new toast
saying that identity needs resetting, rather than saying that the device
needs to be verified.
- move enum from SetupEncryptionToast to DeviceListener
- DeviceListener has public method to get device state
- DeviceListener emits events to update device state
brings RecoveryPanelOutOfSync in line with SetupEncryptionToast behaviour
Copy link
Contributor

@kaylendog kaylendog left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some linting hints from Sonar, plus a few suggestions. Looks fine, otherwise!

Copy link
Contributor

@kaylendog kaylendog left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wonderful, I think this is fine now!

Comment on lines +68 to +96
/**
* The state of the device and the user's account.
*/
export enum DeviceState {
/**
* The device is in a good state.
*/
OK = "ok",
/**
* The user needs to set up recovery.
*/
SET_UP_RECOVERY = "set_up_recovery",
/**
* The device is not verified.
*/
VERIFY_THIS_SESSION = "verify_this_session",
/**
* Key storage is out of sync (keys are missing locally, from recovery, or both).
*/
KEY_STORAGE_OUT_OF_SYNC = "key_storage_out_of_sync",
/**
* Key storage is not enabled, and has not been marked as purposely disabled.
*/
TURN_ON_KEY_STORAGE = "turn_on_key_storage",
/**
* The user's identity needs resetting, due to missing keys.
*/
IDENTITY_NEEDS_RESET = "identity_needs_reset",
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use string union? It allows more flexibility

Comment on lines +76 to +85
await deviceListener.whilePaused(async () => {
await accessSecretStorage(async () => {
// Reset backup if needed.
if (needsBackupReset) {
await resetKeyBackupAndWait(crypto);
} else if (await matrixClient.isKeyBackupKeyStored()) {
await crypto.loadSessionBackupPrivateKeyFromSecretStorage();
}
});
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is happening if this promises are rejected?

Comment on lines +27 to +31
return render(
<MatrixClientContext.Provider value={MatrixClientPeg.safeGet()}>
<RecoveryPanelOutOfSync onFinish={onFinish} onForgotRecoveryKey={onForgotRecoveryKey} />
</MatrixClientContext.Provider>,
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return render(
<MatrixClientContext.Provider value={MatrixClientPeg.safeGet()}>
<RecoveryPanelOutOfSync onFinish={onFinish} onForgotRecoveryKey={onForgotRecoveryKey} />
</MatrixClientContext.Provider>,
);
return render(<RecoveryPanelOutOfSync onFinish={onFinish} onForgotRecoveryKey={onForgotRecoveryKey} />, withClientContextRenderOptions(matrixClient));

describe("<RecoveyPanelOutOfSync />", () => {
function renderComponent(onFinish = jest.fn(), onForgotRecoveryKey = jest.fn()) {
return render(<RecoveryPanelOutOfSync onFinish={onFinish} onForgotRecoveryKey={onForgotRecoveryKey} />);
stubClient();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid using MatrixClientPeg.safeGet(), prefer to create the matrix client and put it an a context

Suggested change
stubClient();
createTestClient();

Comment on lines +59 to +61
.mockImplementation(async (func = async (): Promise<void> => {}) => {
return await func();
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need to call the function in mockImplementation? Or returning a promise for accessSecretStorage is enough?

Suggested change
.mockImplementation(async (func = async (): Promise<void> => {}) => {
return await func();
});
.mockResolvedValue(undefined)

}

afterEach(() => {
jest.restoreAllMocks();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
jest.restoreAllMocks();
jest.clearAllMocks()


const user = userEvent.setup();
mocked(accessSecretStorage)
.mockClear()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With jest.clearAllMocks() in afteEach, this not necessary

expect(accessSecretStorage).toHaveBeenCalled();
expect(onFinish).toHaveBeenCalled();

expect(MatrixClientPeg.safeGet().getCrypto()!.resetKeyBackup).toHaveBeenCalled();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto matrix client

return this.deviceState;
}

private async setDeviceState(newState: DeviceState, logSpan: LogSpan): Promise<void> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing tsdoc

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SetupEncryptionToast and EncryptionSettingsUserTab say the device is unverified when it actually is

3 participants