Device manager - logout of other session (PSG-744) (#9280)

* add sign out of current device section in device details

* lint

* add sign out cta for other sessions

* test other device sign out

* add pending sign out loader

* tidy

* fix strict error

* use gap instead of nbsp

* use more specific assertions in tests, tweak formatting

* tweak test
This commit is contained in:
Kerry 2022-09-14 18:18:32 +02:00 committed by GitHub
parent 0c22b15bba
commit 10bb10539b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 371 additions and 49 deletions

View file

@ -14,10 +14,12 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { useCallback, useEffect, useRef, useState } from 'react';
import React, { useCallback, useContext, useEffect, useRef, useState } from 'react';
import { MatrixClient } from 'matrix-js-sdk/src/client';
import { logger } from 'matrix-js-sdk/src/logger';
import { _t } from "../../../../../languageHandler";
import { useOwnDevices } from '../../devices/useOwnDevices';
import { DevicesState, useOwnDevices } from '../../devices/useOwnDevices';
import SettingsSubsection from '../../shared/SettingsSubsection';
import { FilteredDeviceList } from '../../devices/FilteredDeviceList';
import CurrentDeviceSection from '../../devices/CurrentDeviceSection';
@ -28,12 +30,64 @@ import Modal from '../../../../../Modal';
import SetupEncryptionDialog from '../../../dialogs/security/SetupEncryptionDialog';
import VerificationRequestDialog from '../../../dialogs/VerificationRequestDialog';
import LogoutDialog from '../../../dialogs/LogoutDialog';
import MatrixClientContext from '../../../../../contexts/MatrixClientContext';
import { deleteDevicesWithInteractiveAuth } from '../../devices/deleteDevices';
const useSignOut = (
matrixClient: MatrixClient,
refreshDevices: DevicesState['refreshDevices'],
): {
onSignOutCurrentDevice: () => void;
onSignOutOtherDevices: (deviceIds: DeviceWithVerification['device_id'][]) => Promise<void>;
signingOutDeviceIds: DeviceWithVerification['device_id'][];
} => {
const [signingOutDeviceIds, setSigningOutDeviceIds] = useState<DeviceWithVerification['device_id'][]>([]);
const onSignOutCurrentDevice = () => {
Modal.createDialog(
LogoutDialog,
{}, // props,
undefined, // className
false, // isPriority
true, // isStatic
);
};
const onSignOutOtherDevices = async (deviceIds: DeviceWithVerification['device_id'][]) => {
if (!deviceIds.length) {
return;
}
try {
setSigningOutDeviceIds([...signingOutDeviceIds, ...deviceIds]);
await deleteDevicesWithInteractiveAuth(
matrixClient,
deviceIds,
async (success) => {
if (success) {
// @TODO(kerrya) clear selection if was bulk deletion
// when added in PSG-659
await refreshDevices();
}
setSigningOutDeviceIds(signingOutDeviceIds.filter(deviceId => !deviceIds.includes(deviceId)));
},
);
} catch (error) {
logger.error("Error deleting sessions", error);
setSigningOutDeviceIds(signingOutDeviceIds.filter(deviceId => !deviceIds.includes(deviceId)));
}
};
return {
onSignOutCurrentDevice,
onSignOutOtherDevices,
signingOutDeviceIds,
};
};
const SessionManagerTab: React.FC = () => {
const {
devices,
currentDeviceId,
currentUserMember,
isLoading,
requestDeviceVerification,
refreshDevices,
@ -43,6 +97,10 @@ const SessionManagerTab: React.FC = () => {
const filteredDeviceListRef = useRef<HTMLDivElement>(null);
const scrollIntoViewTimeoutRef = useRef<ReturnType<typeof setTimeout>>();
const matrixClient = useContext(MatrixClientContext);
const userId = matrixClient.getUserId();
const currentUserMember = userId && matrixClient.getUser(userId) || undefined;
const onDeviceExpandToggle = (deviceId: DeviceWithVerification['device_id']): void => {
if (expandedDeviceIds.includes(deviceId)) {
setExpandedDeviceIds(expandedDeviceIds.filter(id => id !== deviceId));
@ -91,14 +149,11 @@ const SessionManagerTab: React.FC = () => {
});
}, [requestDeviceVerification, refreshDevices, currentUserMember]);
const onSignOutCurrentDevice = () => {
if (!currentDevice) {
return;
}
Modal.createDialog(LogoutDialog,
/* props= */{}, /* className= */undefined,
/* isPriority= */false, /* isStatic= */true);
};
const {
onSignOutCurrentDevice,
onSignOutOtherDevices,
signingOutDeviceIds,
} = useSignOut(matrixClient, refreshDevices);
useEffect(() => () => {
clearTimeout(scrollIntoViewTimeoutRef.current);
@ -113,6 +168,7 @@ const SessionManagerTab: React.FC = () => {
<CurrentDeviceSection
device={currentDevice}
isLoading={isLoading}
isSigningOut={signingOutDeviceIds.includes(currentDevice?.device_id)}
onVerifyCurrentDevice={onVerifyCurrentDevice}
onSignOutCurrentDevice={onSignOutCurrentDevice}
/>
@ -130,9 +186,11 @@ const SessionManagerTab: React.FC = () => {
devices={otherDevices}
filter={filter}
expandedDeviceIds={expandedDeviceIds}
signingOutDeviceIds={signingOutDeviceIds}
onFilterChange={setFilter}
onDeviceExpandToggle={onDeviceExpandToggle}
onRequestDeviceVerification={requestDeviceVerification ? onTriggerDeviceVerification : undefined}
onSignOutDevices={onSignOutOtherDevices}
ref={filteredDeviceListRef}
/>
</SettingsSubsection>