From e686328db5c295ab491c660dc5b4b0437253bb26 Mon Sep 17 00:00:00 2001
From: Kerry Archibald
Date: Tue, 6 Sep 2022 16:39:53 +0200
Subject: [PATCH 1/3] add verify current session button
---
.../settings/devices/CurrentDeviceSection.tsx | 5 ++-
.../devices/DeviceVerificationStatusCard.tsx | 17 +++++++-
.../views/settings/devices/useOwnDevices.ts | 41 ++++++++++---------
.../settings/tabs/user/SessionManagerTab.tsx | 19 ++++++++-
.../devices/CurrentDeviceSection-test.tsx | 1 +
.../tabs/user/SessionManagerTab-test.tsx | 16 ++++++++
.../SessionManagerTab-test.tsx.snap | 12 ++++++
7 files changed, 87 insertions(+), 24 deletions(-)
diff --git a/src/components/views/settings/devices/CurrentDeviceSection.tsx b/src/components/views/settings/devices/CurrentDeviceSection.tsx
index cebbed64e6b..ca4e1494901 100644
--- a/src/components/views/settings/devices/CurrentDeviceSection.tsx
+++ b/src/components/views/settings/devices/CurrentDeviceSection.tsx
@@ -28,10 +28,11 @@ import { DeviceWithVerification } from './types';
interface Props {
device?: DeviceWithVerification;
isLoading: boolean;
+ onVerifyCurrentDevice: () => void;
}
const CurrentDeviceSection: React.FC = ({
- device, isLoading,
+ device, isLoading, onVerifyCurrentDevice,
}) => {
const [isExpanded, setIsExpanded] = useState(false);
@@ -52,7 +53,7 @@ const CurrentDeviceSection: React.FC = ({
{ isExpanded && }
-
+
>
}
;
diff --git a/src/components/views/settings/devices/DeviceVerificationStatusCard.tsx b/src/components/views/settings/devices/DeviceVerificationStatusCard.tsx
index a59fd64d638..11e806e54e4 100644
--- a/src/components/views/settings/devices/DeviceVerificationStatusCard.tsx
+++ b/src/components/views/settings/devices/DeviceVerificationStatusCard.tsx
@@ -17,6 +17,7 @@ limitations under the License.
import React from 'react';
import { _t } from '../../../../languageHandler';
+import AccessibleButton from '../../elements/AccessibleButton';
import DeviceSecurityCard from './DeviceSecurityCard';
import {
DeviceSecurityVariation,
@@ -25,12 +26,14 @@ import {
interface Props {
device: DeviceWithVerification;
+ onVerifyDevice?: () => void;
}
export const DeviceVerificationStatusCard: React.FC = ({
device,
+ onVerifyDevice,
}) => {
- const securityCardProps = device?.isVerified ? {
+ const securityCardProps = device.isVerified ? {
variation: DeviceSecurityVariation.Verified,
heading: _t('Verified session'),
description: _t('This session is ready for secure messaging.'),
@@ -41,5 +44,15 @@ export const DeviceVerificationStatusCard: React.FC = ({
};
return ;
+ >
+ { !device.isVerified && !!onVerifyDevice &&
+
+ { _t('Verify session') }
+
+ }
+ ;
};
diff --git a/src/components/views/settings/devices/useOwnDevices.ts b/src/components/views/settings/devices/useOwnDevices.ts
index ec5ee1ca189..7252b053b35 100644
--- a/src/components/views/settings/devices/useOwnDevices.ts
+++ b/src/components/views/settings/devices/useOwnDevices.ts
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-import { useContext, useEffect, useState } from "react";
+import { useCallback, useContext, useEffect, useState } from "react";
import { IMyDevice, MatrixClient } from "matrix-js-sdk/src/matrix";
import { CrossSigningInfo } from "matrix-js-sdk/src/crypto/CrossSigning";
import { logger } from "matrix-js-sdk/src/logger";
@@ -64,6 +64,7 @@ type DevicesState = {
devices: DevicesDictionary;
currentDeviceId: string;
isLoading: boolean;
+ refreshDevices: () => Promise;
error?: OwnDevicesError;
};
export const useOwnDevices = (): DevicesState => {
@@ -75,30 +76,32 @@ export const useOwnDevices = (): DevicesState => {
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState();
- useEffect(() => {
- const getDevicesAsync = async () => {
- setIsLoading(true);
- try {
- const devices = await fetchDevicesWithVerification(matrixClient);
- setDevices(devices);
- setIsLoading(false);
- } catch (error) {
- if (error.httpStatus == 404) {
- // 404 probably means the HS doesn't yet support the API.
- setError(OwnDevicesError.Unsupported);
- } else {
- logger.error("Error loading sessions:", error);
- setError(OwnDevicesError.Default);
- }
- setIsLoading(false);
+ const refreshDevices = useCallback(async () => {
+ setIsLoading(true);
+ try {
+ const devices = await fetchDevicesWithVerification(matrixClient);
+ setDevices(devices);
+ setIsLoading(false);
+ } catch (error) {
+ if (error.httpStatus == 404) {
+ // 404 probably means the HS doesn't yet support the API.
+ setError(OwnDevicesError.Unsupported);
+ } else {
+ logger.error("Error loading sessions:", error);
+ setError(OwnDevicesError.Default);
}
- };
- getDevicesAsync();
+ setIsLoading(false);
+ }
}, [matrixClient]);
+ useEffect(() => {
+ refreshDevices();
+ }, [refreshDevices]);
+
return {
devices,
currentDeviceId,
+ refreshDevices,
isLoading,
error,
};
diff --git a/src/components/views/settings/tabs/user/SessionManagerTab.tsx b/src/components/views/settings/tabs/user/SessionManagerTab.tsx
index 07f60e27250..118fe9268f9 100644
--- a/src/components/views/settings/tabs/user/SessionManagerTab.tsx
+++ b/src/components/views/settings/tabs/user/SessionManagerTab.tsx
@@ -24,9 +24,16 @@ import CurrentDeviceSection from '../../devices/CurrentDeviceSection';
import SecurityRecommendations from '../../devices/SecurityRecommendations';
import { DeviceSecurityVariation, DeviceWithVerification } from '../../devices/types';
import SettingsTab from '../SettingsTab';
+import Modal from '../../../../../Modal';
+import SetupEncryptionDialog from '../../../dialogs/security/SetupEncryptionDialog';
const SessionManagerTab: React.FC = () => {
- const { devices, currentDeviceId, isLoading } = useOwnDevices();
+ const {
+ devices,
+ currentDeviceId,
+ isLoading,
+ refreshDevices,
+ } = useOwnDevices();
const [filter, setFilter] = useState();
const [expandedDeviceIds, setExpandedDeviceIds] = useState([]);
const filteredDeviceListRef = useRef(null);
@@ -57,6 +64,15 @@ const SessionManagerTab: React.FC = () => {
const { [currentDeviceId]: currentDevice, ...otherDevices } = devices;
const shouldShowOtherSessions = Object.keys(otherDevices).length > 0;
+ const onVerifyCurrentDevice = () => {
+ if (!currentDevice) {
+ return;
+ }
+ Modal.createDialog(SetupEncryptionDialog, {
+ onFinished: refreshDevices,
+ });
+ };
+
useEffect(() => () => {
clearTimeout(scrollIntoViewTimeoutRef.current);
}, [scrollIntoViewTimeoutRef]);
@@ -70,6 +86,7 @@ const SessionManagerTab: React.FC = () => {
{
shouldShowOtherSessions &&
diff --git a/test/components/views/settings/devices/CurrentDeviceSection-test.tsx b/test/components/views/settings/devices/CurrentDeviceSection-test.tsx
index 966db9f7f2a..197e50a4bf4 100644
--- a/test/components/views/settings/devices/CurrentDeviceSection-test.tsx
+++ b/test/components/views/settings/devices/CurrentDeviceSection-test.tsx
@@ -34,6 +34,7 @@ describe('', () => {
const defaultProps = {
device: alicesVerifiedDevice,
+ onVerifyCurrentDevice: jest.fn(),
isLoading: false,
};
const getComponent = (props = {}): React.ReactElement =>
diff --git a/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx b/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx
index aad6fbcd380..183aba540df 100644
--- a/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx
+++ b/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx
@@ -28,6 +28,7 @@ import {
getMockClientWithEventEmitter,
mockClientMethodsUser,
} from '../../../../../test-utils';
+import Modal from '../../../../../../src/Modal';
jest.useFakeTimers();
@@ -154,6 +155,21 @@ describe('', () => {
expect(getByTestId('current-session-section')).toMatchSnapshot();
});
+ it('opens encryption setup dialog when verifiying current session', async () => {
+ mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] });
+ const { getByTestId } = render(getComponent());
+ const modalSpy = jest.spyOn(Modal, 'createDialog');
+
+ await act(async () => {
+ await flushPromisesWithFakeTimers();
+ });
+
+ // click verify button from current session section
+ fireEvent.click(getByTestId(`verification-status-button-${alicesDevice.device_id}`));
+
+ expect(modalSpy).toHaveBeenCalled();
+ });
+
it('renders current session section with a verified session', async () => {
mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] });
mockClient.getStoredDevice.mockImplementation(() => new DeviceInfo(alicesDevice.device_id));
diff --git a/test/components/views/settings/tabs/user/__snapshots__/SessionManagerTab-test.tsx.snap b/test/components/views/settings/tabs/user/__snapshots__/SessionManagerTab-test.tsx.snap
index 468556195fc..ad796130e62 100644
--- a/test/components/views/settings/tabs/user/__snapshots__/SessionManagerTab-test.tsx.snap
+++ b/test/components/views/settings/tabs/user/__snapshots__/SessionManagerTab-test.tsx.snap
@@ -226,6 +226,18 @@ exports[` renders current session section with an unverifie
>
Verify or sign out from this session for best security and reliability.
+
From 40b4f0696aa2e73c1bfc1fc77f50921ecd0baee3 Mon Sep 17 00:00:00 2001
From: Kerry Archibald
Date: Tue, 6 Sep 2022 16:57:08 +0200
Subject: [PATCH 2/3] i18n
---
src/i18n/strings/en_EN.json | 2 +-
.../CurrentDeviceSection-test.tsx.snap | 24 +++++++++++++++++++
2 files changed, 25 insertions(+), 1 deletion(-)
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index 6afb549b160..4abfd17cd3e 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -1719,6 +1719,7 @@
"This session is ready for secure messaging.": "This session is ready for secure messaging.",
"Unverified session": "Unverified session",
"Verify or sign out from this session for best security and reliability.": "Verify or sign out from this session for best security and reliability.",
+ "Verify session": "Verify session",
"Verified sessions": "Verified sessions",
"For best security, sign out from any session that you don't recognize or use anymore.": "For best security, sign out from any session that you don't recognize or use anymore.",
"Unverified sessions": "Unverified sessions",
@@ -2765,7 +2766,6 @@
"Session name": "Session name",
"Session key": "Session key",
"If they don't match, the security of your communication may be compromised.": "If they don't match, the security of your communication may be compromised.",
- "Verify session": "Verify session",
"Your homeserver doesn't seem to support this feature.": "Your homeserver doesn't seem to support this feature.",
"Message edits": "Message edits",
"Modal Widget": "Modal Widget",
diff --git a/test/components/views/settings/devices/__snapshots__/CurrentDeviceSection-test.tsx.snap b/test/components/views/settings/devices/__snapshots__/CurrentDeviceSection-test.tsx.snap
index 043fadc0162..d65122f82fc 100644
--- a/test/components/views/settings/devices/__snapshots__/CurrentDeviceSection-test.tsx.snap
+++ b/test/components/views/settings/devices/__snapshots__/CurrentDeviceSection-test.tsx.snap
@@ -216,6 +216,18 @@ exports[` renders device and correct security card when
>
Verify or sign out from this session for best security and reliability.
+
@@ -316,6 +328,18 @@ exports[` renders device and correct security card when
>
Verify or sign out from this session for best security and reliability.
+
From 2d108a5998bc7abe1e429b919435acb21df0b3a6 Mon Sep 17 00:00:00 2001
From: Kerry Archibald
Date: Tue, 6 Sep 2022 17:28:11 +0200
Subject: [PATCH 3/3] strict type issues
---
.../views/settings/tabs/user/SessionManagerTab.tsx | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/src/components/views/settings/tabs/user/SessionManagerTab.tsx b/src/components/views/settings/tabs/user/SessionManagerTab.tsx
index 118fe9268f9..3b6cefc15b7 100644
--- a/src/components/views/settings/tabs/user/SessionManagerTab.tsx
+++ b/src/components/views/settings/tabs/user/SessionManagerTab.tsx
@@ -68,9 +68,10 @@ const SessionManagerTab: React.FC = () => {
if (!currentDevice) {
return;
}
- Modal.createDialog(SetupEncryptionDialog, {
- onFinished: refreshDevices,
- });
+ Modal.createDialog(
+ SetupEncryptionDialog as unknown as React.ComponentType,
+ { onFinished: refreshDevices },
+ );
};
useEffect(() => () => {