+++ /dev/null
-/* eslint-disable import/prefer-default-export */
-import { useState, useEffect } from 'react';
-import { useMatrixClient } from './useMatrixClient';
-
-export function useDeviceList() {
- const mx = useMatrixClient();
- const [deviceList, setDeviceList] = useState(null);
-
- useEffect(() => {
- let isMounted = true;
-
- const updateDevices = () => mx.getDevices().then((data) => {
- if (!isMounted) return;
- setDeviceList(data.devices || []);
- });
- updateDevices();
-
- const handleDevicesUpdate = (users) => {
- if (users.includes(mx.getUserId())) {
- updateDevices();
- }
- };
-
- mx.on('crypto.devicesUpdated', handleDevicesUpdate);
- return () => {
- mx.removeListener('crypto.devicesUpdated', handleDevicesUpdate);
- isMounted = false;
- };
- }, [mx]);
- return deviceList;
-}
--- /dev/null
+/* eslint-disable import/prefer-default-export */
+import { useState, useEffect } from 'react';
+import { CryptoEvent, IMyDevice } from 'matrix-js-sdk';
+import { CryptoEventHandlerMap } from 'matrix-js-sdk/lib/crypto';
+import { useMatrixClient } from './useMatrixClient';
+
+export function useDeviceList() {
+ const mx = useMatrixClient();
+ const [deviceList, setDeviceList] = useState<IMyDevice[] | null>(null);
+
+ useEffect(() => {
+ let isMounted = true;
+
+ const updateDevices = () =>
+ mx.getDevices().then((data) => {
+ if (!isMounted) return;
+ setDeviceList(data.devices || []);
+ });
+ updateDevices();
+
+ const handleDevicesUpdate: CryptoEventHandlerMap[CryptoEvent.DevicesUpdated] = (users) => {
+ const userId = mx.getUserId();
+ if (userId && users.includes(userId)) {
+ updateDevices();
+ }
+ };
+
+ mx.on(CryptoEvent.DevicesUpdated, handleDevicesUpdate);
+ return () => {
+ mx.removeListener(CryptoEvent.DevicesUpdated, handleDevicesUpdate);
+ isMounted = false;
+ };
+ }, [mx]);
+ return deviceList;
+}
SidebarItemTooltip,
SidebarItem,
} from '../../components/sidebar';
-import { DirectTab, HomeTab, SpaceTabs, InboxTab, ExploreTab, UserTab } from './sidebar';
+import {
+ DirectTab,
+ HomeTab,
+ SpaceTabs,
+ InboxTab,
+ ExploreTab,
+ UserTab,
+ UnverifiedTab,
+} from './sidebar';
import { openCreateRoom, openSearch } from '../../../client/action/navigation';
export function SidebarNav() {
</SidebarItemTooltip>
</SidebarItem>
+ <UnverifiedTab />
+
<InboxTab />
<UserTab />
</SidebarStack>
--- /dev/null
+import { keyframes, style } from '@vanilla-extract/css';
+import { color, toRem } from 'folds';
+
+const pushRight = keyframes({
+ from: {
+ transform: `translateX(${toRem(2)}) scale(1)`,
+ },
+ to: {
+ transform: 'translateX(0) scale(1)',
+ },
+});
+
+export const UnverifiedTab = style({
+ animationName: pushRight,
+ animationDuration: '400ms',
+ animationIterationCount: 30,
+ animationDirection: 'alternate',
+});
+
+export const UnverifiedAvatar = style({
+ backgroundColor: color.Critical.Container,
+ color: color.Critical.OnContainer,
+ borderColor: color.Critical.ContainerLine,
+});
--- /dev/null
+import React from 'react';
+import { Badge, color, Icon, Icons, Text } from 'folds';
+import { openSettings } from '../../../../client/action/navigation';
+import { isCrossVerified } from '../../../../util/matrixUtil';
+import {
+ SidebarAvatar,
+ SidebarItem,
+ SidebarItemBadge,
+ SidebarItemTooltip,
+} from '../../../components/sidebar';
+import { useDeviceList } from '../../../hooks/useDeviceList';
+import { tabText } from '../../../organisms/settings/Settings';
+import { useMatrixClient } from '../../../hooks/useMatrixClient';
+import * as css from './UnverifiedTab.css';
+
+export function UnverifiedTab() {
+ const mx = useMatrixClient();
+ const deviceList = useDeviceList();
+ console.log(deviceList);
+ const unverified = deviceList?.filter(
+ (device) => isCrossVerified(mx, device.device_id) === false
+ );
+ console.log(unverified);
+
+ if (!unverified?.length) return null;
+
+ return (
+ <SidebarItem className={css.UnverifiedTab}>
+ <SidebarItemTooltip tooltip="Unverified Sessions">
+ {(triggerRef) => (
+ <SidebarAvatar
+ className={css.UnverifiedAvatar}
+ as="button"
+ ref={triggerRef}
+ outlined
+ onClick={() => openSettings(tabText.SECURITY)}
+ >
+ <Icon style={{ color: color.Critical.Main }} src={Icons.ShieldUser} />
+ </SidebarAvatar>
+ )}
+ </SidebarItemTooltip>
+ <SidebarItemBadge hasCount>
+ <Badge variant="Critical" size="400" fill="Solid" radii="Pill" outlined={false}>
+ <Text as="span" size="L400">
+ {unverified.length}
+ </Text>
+ </Badge>
+ </SidebarItemBadge>
+ </SidebarItem>
+ );
+}
export * from './InboxTab';
export * from './ExploreTab';
export * from './UserTab';
+export * from './UnverifiedTab';
const deviceInfo = mx.getStoredDevice(mx.getUserId(), deviceId);
const deviceTrust = crossSignInfo.checkDeviceTrust(crossSignInfo, deviceInfo, false, true);
return deviceTrust.isCrossSigningVerified();
- } catch {
+ } catch (e) {
// device does not support encryption
return null;
}