From bf49036a1b294f177c2abaade28109d3aa482149 Mon Sep 17 00:00:00 2001 From: Kerry Archibald Date: Wed, 10 Aug 2022 16:47:49 +0200 Subject: [PATCH 1/7] add device metadata table --- res/css/_components.pcss | 1 + .../settings/devices/_DeviceDetails.pcss | 70 ++++++++++++++++++ res/css/views/settings/_DevicesPanel.pcss | 2 + .../views/settings/DevicesPanelEntry.tsx | 9 +-- .../views/settings/devices/DeviceDetails.tsx | 72 +++++++++++++++++++ .../settings/tabs/user/SessionManagerTab.tsx | 2 - src/i18n/strings/en_EN.json | 5 +- .../settings/devices/DeviceDetails-test.tsx | 31 ++++++++ 8 files changed, 185 insertions(+), 7 deletions(-) create mode 100644 res/css/components/views/settings/devices/_DeviceDetails.pcss create mode 100644 src/components/views/settings/devices/DeviceDetails.tsx create mode 100644 test/components/views/settings/devices/DeviceDetails-test.tsx diff --git a/res/css/_components.pcss b/res/css/_components.pcss index d6445f01435..f9b3ac059dc 100644 --- a/res/css/_components.pcss +++ b/res/css/_components.pcss @@ -27,6 +27,7 @@ @import "./components/views/location/_ZoomButtons.pcss"; @import "./components/views/messages/_MBeaconBody.pcss"; @import "./components/views/messages/shared/_MediaProcessingError.pcss"; +@import "./components/views/settings/devices/_DeviceDetails.pcss"; @import "./components/views/settings/devices/_DeviceTile.pcss"; @import "./components/views/settings/devices/_SelectableDeviceTile.pcss"; @import "./components/views/settings/shared/_SettingsSubsection.pcss"; diff --git a/res/css/components/views/settings/devices/_DeviceDetails.pcss b/res/css/components/views/settings/devices/_DeviceDetails.pcss new file mode 100644 index 00000000000..df1341dbb17 --- /dev/null +++ b/res/css/components/views/settings/devices/_DeviceDetails.pcss @@ -0,0 +1,70 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_DeviceDetails { + display: flex; + flex-direction: column; + + width: 100%; + + padding: $spacing-16; + border-radius: 8px; + border: 1px solid $system; +} + +.mx_DeviceDetails_section { + padding-bottom: $spacing-16; + margin-bottom: $spacing-16; + border-bottom: 1px solid $system; + + &:last-child { + padding-bottom: 0; + border-bottom: 0; + margin-bottom: 0; + } +} + +.mx_DeviceDetails_sectionHeading { + margin: 0; +} + +.mxDeviceDetails_metadataTable { + font-size: $font-12px; + color: $secondary-content; + + width: 100%; + margin-top: $spacing-20; + + border-spacing: 0; + + th { + text-transform: uppercase; + font-weight: normal; + text-align: left; + } + + td { + padding-top: $spacing-8; + } + + .mxDeviceDetails_metadataLabel { + width: 160px; + } + + .mxDeviceDetails_metadataValue { + color: $primary-content; + } +} diff --git a/res/css/views/settings/_DevicesPanel.pcss b/res/css/views/settings/_DevicesPanel.pcss index 8581225cee8..898502c464e 100644 --- a/res/css/views/settings/_DevicesPanel.pcss +++ b/res/css/views/settings/_DevicesPanel.pcss @@ -53,6 +53,8 @@ limitations under the License. .mx_DevicesPanel_device { display: flex; + flex-direction: row; + flex-wrap: wrap; align-items: flex-start; margin-block: 10px; min-height: 35px; diff --git a/src/components/views/settings/DevicesPanelEntry.tsx b/src/components/views/settings/DevicesPanelEntry.tsx index b0301214b9b..c3b27f930c6 100644 --- a/src/components/views/settings/DevicesPanelEntry.tsx +++ b/src/components/views/settings/DevicesPanelEntry.tsx @@ -17,6 +17,7 @@ limitations under the License. import React from 'react'; import { IMyDevice } from 'matrix-js-sdk/src/client'; import { logger } from "matrix-js-sdk/src/logger"; +import classNames from 'classnames'; import { _t } from '../../../languageHandler'; import { MatrixClientPeg } from '../../../MatrixClientPeg'; @@ -28,6 +29,7 @@ import VerificationRequestDialog from '../../views/dialogs/VerificationRequestDi import LogoutDialog from '../dialogs/LogoutDialog'; import DeviceTile from './devices/DeviceTile'; import SelectableDeviceTile from './devices/SelectableDeviceTile'; +import DeviceDetails from './devices/DeviceDetails'; interface IProps { device: IMyDevice; @@ -113,8 +115,6 @@ export default class DevicesPanelEntry extends React.Component { }; public render(): JSX.Element { - const myDeviceClass = this.props.isOwnDevice ? " mx_DevicesPanel_myDevice" : ''; - let iconClass = ''; let verifyButton: JSX.Element; if (this.props.verified !== null) { @@ -155,18 +155,19 @@ export default class DevicesPanelEntry extends React.Component { ; if (this.props.isOwnDevice) { - return
+ return
{ buttons } +
; } return ( -
+
{ buttons } diff --git a/src/components/views/settings/devices/DeviceDetails.tsx b/src/components/views/settings/devices/DeviceDetails.tsx new file mode 100644 index 00000000000..fcdad671ad1 --- /dev/null +++ b/src/components/views/settings/devices/DeviceDetails.tsx @@ -0,0 +1,72 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from 'react'; +import { IMyDevice } from 'matrix-js-sdk/src/matrix'; + +import { formatDate } from '../../../../DateUtils'; +import { _t } from '../../../../languageHandler'; +import Heading from '../../typography/Heading'; + +interface Props { + device: IMyDevice; +} + +interface MetadataTable { + heading?: string; + values: { label: string, value: string | React.ReactNode }[]; +} + +const DeviceDetails: React.FC = ({ device }) => { + const metadata: MetadataTable[] = [ + { values: [ + { label: _t('Session ID'), value: device.device_id }, + { label: _t('Last activity'), value: formatDate(new Date(device.last_seen_ts)) }, + ], + }, + { + heading: _t('Device'), + values: [ + { label: _t('IP address'), value: device.last_seen_ip }, + ], + }, + ]; + return
+
+ { device.display_name ?? device.device_id } +
+
+

{ _t('Session details') }

+ { metadata.map(({ heading, values }, index) => + { heading && + } + + + { values.map(({ label, value }) => + + + ) } + +
{ heading }
{ label }{ value }
, + ) } +
+
; +}; + +export default DeviceDetails; diff --git a/src/components/views/settings/tabs/user/SessionManagerTab.tsx b/src/components/views/settings/tabs/user/SessionManagerTab.tsx index 17c09aeb7a3..bb32a4920a7 100644 --- a/src/components/views/settings/tabs/user/SessionManagerTab.tsx +++ b/src/components/views/settings/tabs/user/SessionManagerTab.tsx @@ -24,8 +24,6 @@ const SessionManagerTab: React.FC = () => { return ; }; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index e601003ecb4..652f289da30 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1692,7 +1692,11 @@ "Please enter verification code sent via text.": "Please enter verification code sent via text.", "Verification code": "Verification code", "Discovery options will appear once you have added a phone number above.": "Discovery options will appear once you have added a phone number above.", + "Session ID": "Session ID", "Last activity": "Last activity", + "Device": "Device", + "IP address": "IP address", + "Session details": "Session details", "Unable to remove contact information": "Unable to remove contact information", "Remove %(email)s?": "Remove %(email)s?", "Invalid Email Address": "Invalid Email Address", @@ -2718,7 +2722,6 @@ "Confirm by comparing the following with the User Settings in your other session:": "Confirm by comparing the following with the User Settings in your other session:", "Confirm this user's session by comparing the following with their User Settings:": "Confirm this user's session by comparing the following with their User Settings:", "Session name": "Session name", - "Session ID": "Session ID", "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", diff --git a/test/components/views/settings/devices/DeviceDetails-test.tsx b/test/components/views/settings/devices/DeviceDetails-test.tsx new file mode 100644 index 00000000000..9c8709c4073 --- /dev/null +++ b/test/components/views/settings/devices/DeviceDetails-test.tsx @@ -0,0 +1,31 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from 'react'; +import { mount } from 'enzyme'; + +import DeviceDetails from '../../../../../src/components/views/settings/devices/DeviceDetails'; + +describe('', () => { + const defaultProps = {}; + const getComponent = (props = {}) => + mount(); + + it('renders', () => { + const component = getComponent(); + expect(component).toBeTruthy(); + }); +}); From b794c7689444f8e327bdb8b0f4c697bfea24f06c Mon Sep 17 00:00:00 2001 From: Kerry Archibald Date: Wed, 10 Aug 2022 17:06:25 +0200 Subject: [PATCH 2/7] test devicedetails --- .../settings/devices/DeviceDetails-test.tsx | 36 +++- .../__snapshots__/DeviceDetails-test.tsx.snap | 161 ++++++++++++++++++ 2 files changed, 190 insertions(+), 7 deletions(-) create mode 100644 test/components/views/settings/devices/__snapshots__/DeviceDetails-test.tsx.snap diff --git a/test/components/views/settings/devices/DeviceDetails-test.tsx b/test/components/views/settings/devices/DeviceDetails-test.tsx index 9c8709c4073..49a44d67f5f 100644 --- a/test/components/views/settings/devices/DeviceDetails-test.tsx +++ b/test/components/views/settings/devices/DeviceDetails-test.tsx @@ -15,17 +15,39 @@ limitations under the License. */ import React from 'react'; -import { mount } from 'enzyme'; +import { render } from '@testing-library/react'; import DeviceDetails from '../../../../../src/components/views/settings/devices/DeviceDetails'; describe('', () => { - const defaultProps = {}; - const getComponent = (props = {}) => - mount(); + const baseDevice = { + device_id: 'my-device', + }; + const defaultProps = { + device: baseDevice, + }; + const getComponent = (props = {}) => ; + // 14.03.2022 16:15 + const now = 1647270879403; + jest.useFakeTimers(); - it('renders', () => { - const component = getComponent(); - expect(component).toBeTruthy(); + beforeEach(() => { + jest.setSystemTime(now); + }); + + it('renders device without metadata', () => { + const { container } = render(getComponent()); + expect(container).toMatchSnapshot(); + }); + + it('renders device with metadata', () => { + const device = { + ...baseDevice, + display_name: 'My Device', + last_seen_ip: '123.456.789', + last_seen_ts: now - 60000000, + }; + const { container } = render(getComponent({ device })); + expect(container).toMatchSnapshot(); }); }); diff --git a/test/components/views/settings/devices/__snapshots__/DeviceDetails-test.tsx.snap b/test/components/views/settings/devices/__snapshots__/DeviceDetails-test.tsx.snap new file mode 100644 index 00000000000..b84e9f08f55 --- /dev/null +++ b/test/components/views/settings/devices/__snapshots__/DeviceDetails-test.tsx.snap @@ -0,0 +1,161 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders device with metadata 1`] = ` +
+
+
+

+ My Device +

+
+
+

+ Session details +

+ + + + + + + + + + + +
+ Session ID + + my-device +
+ Last activity + + Sun 22:34 +
+ + + + + + + + + + + + +
+ Device +
+ IP address + + 123.456.789 +
+
+
+
+`; + +exports[` renders device without metadata 1`] = ` +
+
+
+

+ my-device +

+
+
+

+ Session details +

+ + + + + + + + + + +
+ Session ID + + my-device +
+ Last activity + +
+ + + + + + + + + + + +
+ Device +
+ IP address + +
+
+
+
+`; From 5713db5ef294e3e38cba73507e7a4a85630a2844 Mon Sep 17 00:00:00 2001 From: Kerry Archibald Date: Wed, 10 Aug 2022 17:08:50 +0200 Subject: [PATCH 3/7] handle missing last activity metadata --- src/components/views/settings/devices/DeviceDetails.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/settings/devices/DeviceDetails.tsx b/src/components/views/settings/devices/DeviceDetails.tsx index fcdad671ad1..266d34ffcda 100644 --- a/src/components/views/settings/devices/DeviceDetails.tsx +++ b/src/components/views/settings/devices/DeviceDetails.tsx @@ -27,14 +27,14 @@ interface Props { interface MetadataTable { heading?: string; - values: { label: string, value: string | React.ReactNode }[]; + values: { label: string, value?: string | React.ReactNode }[]; } const DeviceDetails: React.FC = ({ device }) => { const metadata: MetadataTable[] = [ { values: [ { label: _t('Session ID'), value: device.device_id }, - { label: _t('Last activity'), value: formatDate(new Date(device.last_seen_ts)) }, + { label: _t('Last activity'), value: device.last_seen_ts && formatDate(new Date(device.last_seen_ts)) }, ], }, { From 85d097c0680894008463ce95c089f3ef030b8963 Mon Sep 17 00:00:00 2001 From: Kerry Archibald Date: Wed, 10 Aug 2022 17:08:59 +0200 Subject: [PATCH 4/7] remove debugs --- src/components/views/settings/DevicesPanel.tsx | 1 - src/components/views/settings/DevicesPanelEntry.tsx | 2 -- 2 files changed, 3 deletions(-) diff --git a/src/components/views/settings/DevicesPanel.tsx b/src/components/views/settings/DevicesPanel.tsx index 5ae034d9fec..019ec593523 100644 --- a/src/components/views/settings/DevicesPanel.tsx +++ b/src/components/views/settings/DevicesPanel.tsx @@ -79,7 +79,6 @@ export default class DevicesPanel extends React.Component { crossSigningInfo: crossSigningInfo, }; }); - console.log(this.state); }, (error) => { if (this.unmounted) { return; } diff --git a/src/components/views/settings/DevicesPanelEntry.tsx b/src/components/views/settings/DevicesPanelEntry.tsx index c3b27f930c6..a1dbddc3093 100644 --- a/src/components/views/settings/DevicesPanelEntry.tsx +++ b/src/components/views/settings/DevicesPanelEntry.tsx @@ -29,7 +29,6 @@ import VerificationRequestDialog from '../../views/dialogs/VerificationRequestDi import LogoutDialog from '../dialogs/LogoutDialog'; import DeviceTile from './devices/DeviceTile'; import SelectableDeviceTile from './devices/SelectableDeviceTile'; -import DeviceDetails from './devices/DeviceDetails'; interface IProps { device: IMyDevice; @@ -162,7 +161,6 @@ export default class DevicesPanelEntry extends React.Component { { buttons } -
; } From 89c6eb5bf744d729f4517219abc6f4d9cab108c4 Mon Sep 17 00:00:00 2001 From: Kerry Archibald Date: Wed, 10 Aug 2022 17:12:01 +0200 Subject: [PATCH 5/7] revert tile style changes --- res/css/views/settings/_DevicesPanel.pcss | 2 -- 1 file changed, 2 deletions(-) diff --git a/res/css/views/settings/_DevicesPanel.pcss b/res/css/views/settings/_DevicesPanel.pcss index 898502c464e..8581225cee8 100644 --- a/res/css/views/settings/_DevicesPanel.pcss +++ b/res/css/views/settings/_DevicesPanel.pcss @@ -53,8 +53,6 @@ limitations under the License. .mx_DevicesPanel_device { display: flex; - flex-direction: row; - flex-wrap: wrap; align-items: flex-start; margin-block: 10px; min-height: 35px; From 01a34efcf73f5f946df8bbcdcc5b7a3e39873b85 Mon Sep 17 00:00:00 2001 From: Kerry Archibald Date: Wed, 10 Aug 2022 17:13:55 +0200 Subject: [PATCH 6/7] fuss with indentation --- .../views/settings/devices/DeviceDetails.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/components/views/settings/devices/DeviceDetails.tsx b/src/components/views/settings/devices/DeviceDetails.tsx index 266d34ffcda..4f117e2b201 100644 --- a/src/components/views/settings/devices/DeviceDetails.tsx +++ b/src/components/views/settings/devices/DeviceDetails.tsx @@ -34,7 +34,10 @@ const DeviceDetails: React.FC = ({ device }) => { const metadata: MetadataTable[] = [ { values: [ { label: _t('Session ID'), value: device.device_id }, - { label: _t('Last activity'), value: device.last_seen_ts && formatDate(new Date(device.last_seen_ts)) }, + { + label: _t('Last activity'), + value: device.last_seen_ts && formatDate(new Date(device.last_seen_ts)), + }, ], }, { @@ -54,8 +57,11 @@ const DeviceDetails: React.FC = ({ device }) => { className='mxDeviceDetails_metadataTable' key={index} > - { heading && - { heading } } + { heading && + + { heading } + + } { values.map(({ label, value }) => From 3c591c770b50bb04a8754130409688f5729f50d3 Mon Sep 17 00:00:00 2001 From: Kerry Archibald Date: Thu, 11 Aug 2022 09:35:58 +0200 Subject: [PATCH 7/7] code formatting --- .../views/settings/devices/DeviceDetails.tsx | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/components/views/settings/devices/DeviceDetails.tsx b/src/components/views/settings/devices/DeviceDetails.tsx index 4f117e2b201..6b5cd979288 100644 --- a/src/components/views/settings/devices/DeviceDetails.tsx +++ b/src/components/views/settings/devices/DeviceDetails.tsx @@ -32,13 +32,14 @@ interface MetadataTable { const DeviceDetails: React.FC = ({ device }) => { const metadata: MetadataTable[] = [ - { values: [ - { label: _t('Session ID'), value: device.device_id }, - { - label: _t('Last activity'), - value: device.last_seen_ts && formatDate(new Date(device.last_seen_ts)), - }, - ], + { + values: [ + { label: _t('Session ID'), value: device.device_id }, + { + label: _t('Last activity'), + value: device.last_seen_ts && formatDate(new Date(device.last_seen_ts)), + }, + ], }, { heading: _t('Device'),