add device verification status badge

This commit is contained in:
Ajay Bura 2025-01-30 11:07:18 +05:30
parent 64d80a8684
commit dff90a1ae0
5 changed files with 104 additions and 16 deletions

View file

@ -1,4 +1,4 @@
import React from 'react';
import React, { useMemo } from 'react';
import { Box, Text, IconButton, Icon, Icons, Scroll, Button } from 'folds';
import { Page, PageContent, PageHeader } from '../../../components/page';
import { SequenceCard } from '../../../components/sequence-card';
@ -9,9 +9,10 @@ import { useMatrixClient } from '../../../hooks/useMatrixClient';
import { LocalBackup } from './LocalBackup';
import { DeviceLogoutBtn, DeviceTile, DeviceTilePlaceholder } from './DeviceTile';
import { OtherDevices } from './OtherDevices';
import { ManualVerificationTile } from './Verificaton';
import { ManualVerificationTile, VerificationStatusBadge } from './Verificaton';
import {
useDeviceVerificationStatus,
useUnverifiedDeviceCount,
VerificationStatus,
} from '../../../hooks/useDeviceVerificationStatus';
@ -43,6 +44,16 @@ export function Devices({ requestClose }: DevicesProps) {
currentDevice?.device_id
);
const otherDevicesId = useMemo(
() => otherDevices?.map((device) => device.device_id) ?? [],
[otherDevices]
);
const unverifiedDeviceCount = useUnverifiedDeviceCount(
crypto,
mx.getSafeUserId(),
otherDevicesId
);
return (
<Page>
<PageHeader outlined={false}>
@ -75,11 +86,18 @@ export function Devices({ requestClose }: DevicesProps) {
title="Device Verification"
description="To verify your identity and grant access to your encrypted messages on another device."
after={
<Button size="300" radii="300">
<Text as="span" size="B300">
Activate
</Text>
</Button>
true ? (
<VerificationStatusBadge
verificationStatus={verificationStatus}
otherUnverifiedCount={unverifiedDeviceCount}
/>
) : (
<Button size="300" radii="300">
<Text as="span" size="B300">
Activate
</Text>
</Button>
)
}
/>
</SequenceCard>
@ -111,7 +129,7 @@ export function Devices({ requestClose }: DevicesProps) {
<OtherDevices
devices={otherDevices}
refreshDeviceList={refreshDeviceList}
verified={verificationStatus === VerificationStatus.Verified}
showVerification={verificationStatus === VerificationStatus.Verified}
/>
)}
<LocalBackup />

View file

@ -15,9 +15,9 @@ import { VerificationStatus } from '../../../hooks/useDeviceVerificationStatus';
type OtherDevicesProps = {
devices: IMyDevice[];
refreshDeviceList: () => Promise<void>;
verified?: boolean;
showVerification?: boolean;
};
export function OtherDevices({ devices, refreshDeviceList, verified }: OtherDevicesProps) {
export function OtherDevices({ devices, refreshDeviceList, showVerification }: OtherDevicesProps) {
const mx = useMatrixClient();
const crypto = mx.getCrypto();
const [deleted, setDeleted] = useState<Set<string>>(new Set());
@ -97,7 +97,7 @@ export function OtherDevices({ devices, refreshDeviceList, verified }: OtherDevi
/>
}
/>
{verified && (
{showVerification && (
<DeviceVerificationStatus
crypto={crypto}
userId={mx.getSafeUserId()}

View file

@ -1,7 +1,42 @@
import React, { useState } from 'react';
import { Box, Button, config, Text } from 'folds';
import { Badge, Box, Button, config, Spinner, Text } from 'folds';
import { SettingTile } from '../../../components/setting-tile';
import * as css from './style.css';
import { VerificationStatus } from '../../../hooks/useDeviceVerificationStatus';
type VerificationStatusBadgeProps = {
verificationStatus: VerificationStatus;
otherUnverifiedCount?: number;
};
export function VerificationStatusBadge({
verificationStatus,
otherUnverifiedCount,
}: VerificationStatusBadgeProps) {
if (verificationStatus === VerificationStatus.Unknown || !otherUnverifiedCount) {
return <Spinner size="400" variant="Secondary" />;
}
if (verificationStatus === VerificationStatus.Unverified) {
return (
<Badge variant="Critical" fill="Solid" size="500">
<Text size="L400">Unverified</Text>
</Badge>
);
}
if (otherUnverifiedCount > 0) {
return (
<Badge variant="Critical" fill="Solid" size="500">
<Text size="L400">{otherUnverifiedCount} Unverified</Text>
</Badge>
);
}
return (
<Badge variant="Success" fill="Solid" size="500">
<Text size="L400">Verified</Text>
</Badge>
);
}
function LearnStartVerificationFromOtherDevice() {
return (
@ -39,7 +74,7 @@ export function ManualVerificationTile() {
<div className={css.UnverifiedCard}>
<SettingTile
after={
<Button size="300" variant="Warning" fill="Soft" radii="300">
<Button size="300" variant="Critical" fill="Soft" radii="300">
<Text size="B300">Verify Manually</Text>
</Button>
}
@ -62,7 +97,7 @@ export function StartVerificationTile() {
<div className={css.UnverifiedCard}>
<SettingTile
after={
<Button size="300" variant="Warning" radii="300">
<Button size="300" variant="Critical" radii="300">
<Text size="B300">Start Verification</Text>
</Button>
}

View file

@ -3,7 +3,7 @@ import { config } from 'folds';
import { ContainerColor } from '../../../styles/ContainerColor.css';
export const UnverifiedCard = style([
ContainerColor({ variant: 'Warning' }),
ContainerColor({ variant: 'Critical' }),
{
padding: config.space.S200,
borderRadius: config.radii.R300,

View file

@ -1,7 +1,9 @@
import { useEffect } from 'react';
import { useEffect, useState } from 'react';
import { CryptoApi } from 'matrix-js-sdk/lib/crypto-api';
import { AsyncStatus, useAsyncCallback } from './useAsyncCallback';
import { verifiedDevice } from '../utils/matrix-crypto';
import { useAlive } from './useAlive';
import { fulfilledPromiseSettledResult } from '../utils/common';
export enum VerificationStatus {
Unknown,
@ -29,3 +31,36 @@ export const useDeviceVerificationStatus = (
return VerificationStatus.Unknown;
};
export const useUnverifiedDeviceCount = (
crypto: CryptoApi | undefined,
userId: string,
devices: string[]
): number | undefined => {
const [unverifiedCount, setUnverifiedCount] = useState<number>();
const alive = useAlive();
useEffect(() => {
const findCount = async () => {
if (crypto) {
const promises = devices.map((deviceId) => verifiedDevice(crypto, userId, deviceId));
const result = await Promise.allSettled(promises);
const settledResult = fulfilledPromiseSettledResult(result);
return settledResult.reduce((count, status) => {
if (status === false) {
return count + 1;
}
return count;
}, 0);
}
return 0;
};
findCount().then((count) => {
if (alive()) {
setUnverifiedCount(count);
}
});
}, [alive, crypto, userId, devices]);
return unverifiedCount;
};