Device manager - expandable session details in device list (PSG-644) (#9188)
* add expandable device details to session list * test device expansion in filtered list * test expanded device id management from sessionmanager tab * i18n * update snapshot * update snapshots * use css instead of br
This commit is contained in:
parent
fecc03289d
commit
e5fedfcd74
13 changed files with 155 additions and 12 deletions
|
@ -49,7 +49,7 @@ const DeviceDetails: React.FC<Props> = ({ device }) => {
|
|||
],
|
||||
},
|
||||
];
|
||||
return <div className='mx_DeviceDetails'>
|
||||
return <div className='mx_DeviceDetails' data-testid={`device-detail-${device.device_id}`}>
|
||||
<section className='mx_DeviceDetails_section'>
|
||||
<Heading size='h3'>{ device.display_name ?? device.device_id }</Heading>
|
||||
<DeviceVerificationStatusCard device={device} />
|
||||
|
|
|
@ -18,6 +18,7 @@ import classNames from 'classnames';
|
|||
import React from 'react';
|
||||
|
||||
import { Icon as CaretIcon } from '../../../../../res/img/feather-customised/dropdown-arrow.svg';
|
||||
import { _t } from '../../../../languageHandler';
|
||||
import AccessibleButton from '../../elements/AccessibleButton';
|
||||
|
||||
interface Props {
|
||||
|
@ -28,6 +29,7 @@ interface Props {
|
|||
const DeviceExpandDetailsButton: React.FC<Props> = ({ isExpanded, onClick, ...rest }) => {
|
||||
return <AccessibleButton
|
||||
{...rest}
|
||||
aria-label={_t('Toggle device details')}
|
||||
kind='icon'
|
||||
className={classNames('mx_DeviceExpandDetailsButton', {
|
||||
mx_DeviceExpandDetailsButton_expanded: isExpanded,
|
||||
|
|
|
@ -19,6 +19,8 @@ import React from 'react';
|
|||
import { _t } from '../../../../languageHandler';
|
||||
import AccessibleButton from '../../elements/AccessibleButton';
|
||||
import Dropdown from '../../elements/Dropdown';
|
||||
import DeviceDetails from './DeviceDetails';
|
||||
import DeviceExpandDetailsButton from './DeviceExpandDetailsButton';
|
||||
import DeviceSecurityCard from './DeviceSecurityCard';
|
||||
import DeviceTile from './DeviceTile';
|
||||
import {
|
||||
|
@ -33,8 +35,10 @@ import {
|
|||
|
||||
interface Props {
|
||||
devices: DevicesDictionary;
|
||||
expandedDeviceIds: DeviceWithVerification['device_id'][];
|
||||
filter?: DeviceSecurityVariation;
|
||||
onFilterChange: (filter: DeviceSecurityVariation | undefined) => void;
|
||||
onDeviceExpandToggle: (deviceId: DeviceWithVerification['device_id']) => void;
|
||||
}
|
||||
|
||||
// devices without timestamp metadata should be sorted last
|
||||
|
@ -123,11 +127,35 @@ const NoResults: React.FC<NoResultsProps> = ({ filter, clearFilter }) =>
|
|||
}
|
||||
</div>;
|
||||
|
||||
const DeviceListItem: React.FC<{
|
||||
device: DeviceWithVerification;
|
||||
isExpanded: boolean;
|
||||
onDeviceExpandToggle: () => void;
|
||||
}> = ({
|
||||
device, isExpanded, onDeviceExpandToggle,
|
||||
}) => <li className='mx_FilteredDeviceList_listItem'>
|
||||
<DeviceTile
|
||||
device={device}
|
||||
>
|
||||
<DeviceExpandDetailsButton
|
||||
isExpanded={isExpanded}
|
||||
onClick={onDeviceExpandToggle}
|
||||
/>
|
||||
</DeviceTile>
|
||||
{ isExpanded && <DeviceDetails device={device} /> }
|
||||
</li>;
|
||||
|
||||
/**
|
||||
* Filtered list of devices
|
||||
* Sorted by latest activity descending
|
||||
*/
|
||||
const FilteredDeviceList: React.FC<Props> = ({ devices, filter, onFilterChange }) => {
|
||||
const FilteredDeviceList: React.FC<Props> = ({
|
||||
devices,
|
||||
filter,
|
||||
expandedDeviceIds,
|
||||
onFilterChange,
|
||||
onDeviceExpandToggle,
|
||||
}) => {
|
||||
const sortedDevices = getFilteredSortedDevices(devices, filter);
|
||||
|
||||
const options = [
|
||||
|
@ -177,13 +205,12 @@ const FilteredDeviceList: React.FC<Props> = ({ devices, filter, onFilterChange }
|
|||
: <NoResults filter={filter} clearFilter={() => onFilterChange(undefined)} />
|
||||
}
|
||||
<ol className='mx_FilteredDeviceList_list'>
|
||||
{ sortedDevices.map((device) =>
|
||||
<li key={device.device_id}>
|
||||
<DeviceTile
|
||||
device={device}
|
||||
/>
|
||||
</li>,
|
||||
|
||||
{ sortedDevices.map((device) => <DeviceListItem
|
||||
key={device.device_id}
|
||||
device={device}
|
||||
isExpanded={expandedDeviceIds.includes(device.device_id)}
|
||||
onDeviceExpandToggle={() => onDeviceExpandToggle(device.device_id)}
|
||||
/>,
|
||||
) }
|
||||
</ol>
|
||||
</div>
|
||||
|
|
|
@ -22,12 +22,21 @@ import SettingsSubsection from '../../shared/SettingsSubsection';
|
|||
import FilteredDeviceList from '../../devices/FilteredDeviceList';
|
||||
import CurrentDeviceSection from '../../devices/CurrentDeviceSection';
|
||||
import SecurityRecommendations from '../../devices/SecurityRecommendations';
|
||||
import { DeviceSecurityVariation } from '../../devices/types';
|
||||
import { DeviceSecurityVariation, DeviceWithVerification } from '../../devices/types';
|
||||
import SettingsTab from '../SettingsTab';
|
||||
|
||||
const SessionManagerTab: React.FC = () => {
|
||||
const { devices, currentDeviceId, isLoading } = useOwnDevices();
|
||||
const [filter, setFilter] = useState<DeviceSecurityVariation>();
|
||||
const [expandedDeviceIds, setExpandedDeviceIds] = useState([]);
|
||||
|
||||
const onDeviceExpandToggle = (deviceId: DeviceWithVerification['device_id']): void => {
|
||||
if (expandedDeviceIds.includes(deviceId)) {
|
||||
setExpandedDeviceIds(expandedDeviceIds.filter(id => id !== deviceId));
|
||||
} else {
|
||||
setExpandedDeviceIds([...expandedDeviceIds, deviceId]);
|
||||
}
|
||||
};
|
||||
|
||||
const { [currentDeviceId]: currentDevice, ...otherDevices } = devices;
|
||||
const shouldShowOtherSessions = Object.keys(otherDevices).length > 0;
|
||||
|
@ -51,7 +60,9 @@ const SessionManagerTab: React.FC = () => {
|
|||
<FilteredDeviceList
|
||||
devices={otherDevices}
|
||||
filter={filter}
|
||||
expandedDeviceIds={expandedDeviceIds}
|
||||
onFilterChange={setFilter}
|
||||
onDeviceExpandToggle={onDeviceExpandToggle}
|
||||
/>
|
||||
</SettingsSubsection>
|
||||
}
|
||||
|
|
|
@ -1701,6 +1701,7 @@
|
|||
"Device": "Device",
|
||||
"IP address": "IP address",
|
||||
"Session details": "Session details",
|
||||
"Toggle device details": "Toggle device details",
|
||||
"Inactive for %(inactiveAgeDays)s+ days": "Inactive for %(inactiveAgeDays)s+ days",
|
||||
"Verified": "Verified",
|
||||
"Unverified": "Unverified",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue