From 61904778f5db32404a8b3e7ff7627370a883803c Mon Sep 17 00:00:00 2001
From: Kerry 
Date: Thu, 8 Sep 2022 17:35:53 +0200
Subject: [PATCH] Device manager - add verify current session button (PSG-527)
 (#9252)
* add verify current session button
* i18n
* strict type issues
---
 .../settings/devices/CurrentDeviceSection.tsx |  5 ++-
 .../devices/DeviceVerificationStatusCard.tsx  | 17 +++++++-
 .../views/settings/devices/useOwnDevices.ts   | 41 ++++++++++---------
 .../settings/tabs/user/SessionManagerTab.tsx  | 20 ++++++++-
 src/i18n/strings/en_EN.json                   |  2 +-
 .../devices/CurrentDeviceSection-test.tsx     |  1 +
 .../CurrentDeviceSection-test.tsx.snap        | 24 +++++++++++
 .../tabs/user/SessionManagerTab-test.tsx      | 16 ++++++++
 .../SessionManagerTab-test.tsx.snap           | 12 ++++++
 9 files changed, 113 insertions(+), 25 deletions(-)
diff --git a/src/components/views/settings/devices/CurrentDeviceSection.tsx b/src/components/views/settings/devices/CurrentDeviceSection.tsx
index cebbed64e6..ca4e149490 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 a59fd64d63..11e806e54e 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 ec5ee1ca18..7252b053b3 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 07f60e2725..3b6cefc15b 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,16 @@ const SessionManagerTab: React.FC = () => {
     const { [currentDeviceId]: currentDevice, ...otherDevices } = devices;
     const shouldShowOtherSessions = Object.keys(otherDevices).length > 0;
 
+    const onVerifyCurrentDevice = () => {
+        if (!currentDevice) {
+            return;
+        }
+        Modal.createDialog(
+            SetupEncryptionDialog as unknown as React.ComponentType,
+            { onFinished: refreshDevices },
+        );
+    };
+
     useEffect(() => () => {
         clearTimeout(scrollIntoViewTimeoutRef.current);
     }, [scrollIntoViewTimeoutRef]);
@@ -70,6 +87,7 @@ const SessionManagerTab: React.FC = () => {
         
         {
             shouldShowOtherSessions &&
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index c2ee03575d..e0d68b5a87 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -1720,6 +1720,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",
@@ -2766,7 +2767,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/CurrentDeviceSection-test.tsx b/test/components/views/settings/devices/CurrentDeviceSection-test.tsx
index 966db9f7f2..197e50a4bf 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/devices/__snapshots__/CurrentDeviceSection-test.tsx.snap b/test/components/views/settings/devices/__snapshots__/CurrentDeviceSection-test.tsx.snap
index 043fadc016..d65122f82f 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.
           
+          
         
       
     
diff --git a/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx b/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx
index aad6fbcd38..183aba540d 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 468556195f..ad796130e6 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.
         
+