CI / Build & Quality Checks (pull_request) Successful in 1m47s
CI / Trigger Desktop Build (pull_request) Skipped
CI / Docker image build & smoke test (pull_request) Skipped
CI / Secret scan (gitleaks) (pull_request) Successful in 7s
CI / Playwright smoke (e2e) (pull_request) Successful in 12m18s
- Modal500 takes a label: Room/Space Settings ("<name> settings") and
Settings dialogs have accessible names.
- aria-selected on plain buttons (inbox filter chips, date/time picker
chips, verification/usage/notification menu items) -> aria-pressed; the
Permissions power chips open a menu -> aria-haspopup + aria-expanded.
- Labels tied to the room pack name input and the Export From/To dates.
- UserAvatar defaults to alt="" (decorative next to a name / in a
labelled button) unless a caller passes one.
- Composer placeholder: Slate's inline opacity 0.333 overrode the #222
AA-contrast class (2.66:1); it is dropped so the class (0.75) applies.
- e2e/a11y.spec.ts also audits Room Settings → Permissions, Emojis &
Stickers and Export, checks the dialog name, and audits the inbox.
Fixes #278
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
100 lines
3.3 KiB
TypeScript
100 lines
3.3 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { Badge, color, Icon, Icons, Text } from 'folds';
|
|
import {
|
|
SidebarAvatar,
|
|
SidebarItem,
|
|
SidebarItemBadge,
|
|
SidebarItemTooltip,
|
|
} from '../../../components/sidebar';
|
|
import { useDeviceIds, useDeviceList, useSplitCurrentDevice } from '../../../hooks/useDeviceList';
|
|
import { useMatrixClient } from '../../../hooks/useMatrixClient';
|
|
import * as css from './UnverifiedTab.css';
|
|
import {
|
|
useDeviceVerificationStatus,
|
|
useUnverifiedDeviceCount,
|
|
VerificationStatus,
|
|
} from '../../../hooks/useDeviceVerificationStatus';
|
|
import { useCrossSigningActive } from '../../../hooks/useCrossSigning';
|
|
import { Modal500 } from '../../../components/Modal500';
|
|
import { Settings, SettingsPages } from '../../../features/settings';
|
|
|
|
function UnverifiedIndicator() {
|
|
const mx = useMatrixClient();
|
|
|
|
const crypto = mx.getCrypto();
|
|
const [devices] = useDeviceList();
|
|
|
|
const [currentDevice, otherDevices] = useSplitCurrentDevice(devices);
|
|
|
|
const verificationStatus = useDeviceVerificationStatus(
|
|
crypto,
|
|
mx.getSafeUserId(),
|
|
currentDevice?.device_id,
|
|
);
|
|
const unverified = verificationStatus === VerificationStatus.Unverified;
|
|
|
|
const otherDevicesId = useDeviceIds(otherDevices);
|
|
const unverifiedDeviceCount = useUnverifiedDeviceCount(
|
|
crypto,
|
|
mx.getSafeUserId(),
|
|
otherDevicesId,
|
|
);
|
|
|
|
const [settings, setSettings] = useState(false);
|
|
const closeSettings = () => setSettings(false);
|
|
|
|
const hasUnverified =
|
|
unverified || (unverifiedDeviceCount !== undefined && unverifiedDeviceCount > 0);
|
|
return (
|
|
<>
|
|
{hasUnverified && (
|
|
<SidebarItem active={settings} className={css.UnverifiedTab}>
|
|
<SidebarItemTooltip tooltip={unverified ? 'Unverified Device' : 'Unverified Devices'}>
|
|
{(triggerRef) => (
|
|
<SidebarAvatar
|
|
className={unverified ? css.UnverifiedAvatar : css.UnverifiedOtherAvatar}
|
|
as="button"
|
|
aria-label={
|
|
unverified
|
|
? 'Unverified device — open settings'
|
|
: 'Other unverified devices — open settings'
|
|
}
|
|
ref={triggerRef}
|
|
outlined
|
|
onClick={() => setSettings(true)}
|
|
>
|
|
<Icon
|
|
style={{ color: unverified ? color.Critical.Main : color.Warning.Main }}
|
|
src={Icons.ShieldUser}
|
|
/>
|
|
</SidebarAvatar>
|
|
)}
|
|
</SidebarItemTooltip>
|
|
{!unverified && unverifiedDeviceCount && unverifiedDeviceCount > 0 && (
|
|
<SidebarItemBadge hasCount>
|
|
<Badge variant="Warning" size="400" fill="Solid" radii="Pill" outlined={false}>
|
|
<Text as="span" size="L400">
|
|
{unverifiedDeviceCount}
|
|
</Text>
|
|
</Badge>
|
|
</SidebarItemBadge>
|
|
)}
|
|
</SidebarItem>
|
|
)}
|
|
{settings && (
|
|
<Modal500 requestClose={closeSettings} label="Settings">
|
|
<Settings initialPage={SettingsPages.DevicesPage} requestClose={closeSettings} />
|
|
</Modal500>
|
|
)}
|
|
</>
|
|
);
|
|
}
|
|
|
|
export function UnverifiedTab() {
|
|
const crossSigningActive = useCrossSigningActive();
|
|
|
|
if (!crossSigningActive) return null;
|
|
|
|
return <UnverifiedIndicator />;
|
|
}
|