mirror of
https://github.com/cinnyapp/cinny.git
synced 2025-01-29 01:28:29 +00:00
merge encryption tab into sessions and rename it to devices
This commit is contained in:
parent
6def5ab17e
commit
a50b6efd46
|
@ -11,8 +11,7 @@ import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
|
|||
import { UserAvatar } from '../../components/user-avatar';
|
||||
import { nameInitials } from '../../utils/common';
|
||||
import { Notifications } from './notifications';
|
||||
import { Sessions } from './sessions';
|
||||
import { Encryption } from './encryption';
|
||||
import { Devices } from './devices';
|
||||
import { EmojisStickers } from './emojis-stickers';
|
||||
import { DeveloperTools } from './developer-tools';
|
||||
import { About } from './about';
|
||||
|
@ -21,8 +20,7 @@ enum SettingsPages {
|
|||
GeneralPage,
|
||||
AccountPage,
|
||||
NotificationPage,
|
||||
SessionsPage,
|
||||
EncryptionPage,
|
||||
DevicesPage,
|
||||
EmojisStickersPage,
|
||||
DeveloperToolsPage,
|
||||
AboutPage,
|
||||
|
@ -53,15 +51,10 @@ const useSettingsMenuItems = (): SettingsMenuItem[] =>
|
|||
icon: Icons.Bell,
|
||||
},
|
||||
{
|
||||
page: SettingsPages.SessionsPage,
|
||||
name: 'Sessions',
|
||||
page: SettingsPages.DevicesPage,
|
||||
name: 'Devices',
|
||||
icon: Icons.Category,
|
||||
},
|
||||
{
|
||||
page: SettingsPages.EncryptionPage,
|
||||
name: 'Encryption',
|
||||
icon: Icons.ShieldLock,
|
||||
},
|
||||
{
|
||||
page: SettingsPages.EmojisStickersPage,
|
||||
name: 'Emojis & Stickers',
|
||||
|
@ -173,11 +166,8 @@ export function Settings({ initialPage, requestClose }: SettingsProps) {
|
|||
{activePage === SettingsPages.NotificationPage && (
|
||||
<Notifications requestClose={handlePageRequestClose} />
|
||||
)}
|
||||
{activePage === SettingsPages.SessionsPage && (
|
||||
<Sessions requestClose={handlePageRequestClose} />
|
||||
)}
|
||||
{activePage === SettingsPages.EncryptionPage && (
|
||||
<Encryption requestClose={handlePageRequestClose} />
|
||||
{activePage === SettingsPages.DevicesPage && (
|
||||
<Devices requestClose={handlePageRequestClose} />
|
||||
)}
|
||||
{activePage === SettingsPages.EmojisStickersPage && (
|
||||
<EmojisStickers requestClose={handlePageRequestClose} />
|
||||
|
|
223
src/app/features/settings/devices/DeviceTile.tsx
Normal file
223
src/app/features/settings/devices/DeviceTile.tsx
Normal file
|
@ -0,0 +1,223 @@
|
|||
import React, { FormEventHandler, useCallback, useEffect, useState } from 'react';
|
||||
import { Box, Text, IconButton, Icon, Icons, Chip, Input, Button, color, Spinner } from 'folds';
|
||||
import { IMyDevice, MatrixError } from 'matrix-js-sdk';
|
||||
import { SettingTile } from '../../../components/setting-tile';
|
||||
import { useMatrixClient } from '../../../hooks/useMatrixClient';
|
||||
import { timeDayMonYear, timeHourMinute, today, yesterday } from '../../../utils/time';
|
||||
import { BreakWord } from '../../../styles/Text.css';
|
||||
import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback';
|
||||
|
||||
function DeviceActiveTime({ ts }: { ts: number }) {
|
||||
return (
|
||||
<Text className={BreakWord} size="T200">
|
||||
<Text size="Inherit" as="span" priority="300">
|
||||
{'Last activity: '}
|
||||
</Text>
|
||||
<>
|
||||
{today(ts) && 'Today'}
|
||||
{yesterday(ts) && 'Yesterday'}
|
||||
{!today(ts) && !yesterday(ts) && timeDayMonYear(ts)} {timeHourMinute(ts)}
|
||||
</>
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
function DeviceDetails({ device }: { device: IMyDevice }) {
|
||||
return (
|
||||
<>
|
||||
{typeof device.device_id === 'string' && (
|
||||
<Text className={BreakWord} size="T200" priority="300">
|
||||
Device ID: <i>{device.device_id}</i>
|
||||
</Text>
|
||||
)}
|
||||
{typeof device.last_seen_ip === 'string' && (
|
||||
<Text className={BreakWord} size="T200" priority="300">
|
||||
IP Address: <i>{device.last_seen_ip}</i>
|
||||
</Text>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
type DeviceRenameProps = {
|
||||
device: IMyDevice;
|
||||
onCancel: () => void;
|
||||
onRename: () => void;
|
||||
refreshDeviceList: () => Promise<void>;
|
||||
};
|
||||
function DeviceRename({ device, onCancel, onRename, refreshDeviceList }: DeviceRenameProps) {
|
||||
const mx = useMatrixClient();
|
||||
|
||||
const [renameState, rename] = useAsyncCallback<void, MatrixError, [string]>(
|
||||
useCallback(
|
||||
async (name: string) => {
|
||||
await mx.setDeviceDetails(device.device_id, { display_name: name });
|
||||
await refreshDeviceList();
|
||||
},
|
||||
[mx, device.device_id, refreshDeviceList]
|
||||
)
|
||||
);
|
||||
|
||||
const renaming = renameState.status === AsyncStatus.Loading;
|
||||
|
||||
useEffect(() => {
|
||||
if (renameState.status === AsyncStatus.Success) {
|
||||
onRename();
|
||||
}
|
||||
}, [renameState, onRename]);
|
||||
|
||||
const handleSubmit: FormEventHandler<HTMLFormElement> = (evt) => {
|
||||
evt.preventDefault();
|
||||
if (renaming) return;
|
||||
|
||||
const target = evt.target as HTMLFormElement | undefined;
|
||||
const nameInput = target?.nameInput as HTMLInputElement | undefined;
|
||||
if (!nameInput) return;
|
||||
const deviceName = nameInput.value.trim();
|
||||
if (!deviceName || deviceName === device.display_name) return;
|
||||
|
||||
rename(deviceName);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box as="form" onSubmit={handleSubmit} direction="Column" gap="100">
|
||||
<Text size="L400">Device Name</Text>
|
||||
<Box gap="200">
|
||||
<Box grow="Yes" direction="Column">
|
||||
<Input
|
||||
name="nameInput"
|
||||
size="300"
|
||||
variant="Secondary"
|
||||
radii="300"
|
||||
defaultValue={device.display_name}
|
||||
autoFocus
|
||||
required
|
||||
readOnly={renaming}
|
||||
/>
|
||||
</Box>
|
||||
<Box shrink="No" gap="200">
|
||||
<Button
|
||||
type="submit"
|
||||
size="300"
|
||||
variant="Success"
|
||||
radii="300"
|
||||
fill="Solid"
|
||||
disabled={renaming}
|
||||
before={renaming && <Spinner size="100" variant="Success" fill="Solid" />}
|
||||
>
|
||||
<Text size="B300">Save</Text>
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
size="300"
|
||||
variant="Secondary"
|
||||
radii="300"
|
||||
fill="Soft"
|
||||
onClick={onCancel}
|
||||
disabled={renaming}
|
||||
>
|
||||
<Text size="B300">Cancel</Text>
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
{renameState.status === AsyncStatus.Error ? (
|
||||
<Text size="T200" style={{ color: color.Critical.Main }}>
|
||||
{renameState.error.message}
|
||||
</Text>
|
||||
) : (
|
||||
<Text size="T200">Device names are visible to public.</Text>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
type DeviceTileProps = {
|
||||
device: IMyDevice;
|
||||
deleted: boolean;
|
||||
onDeleteToggle: (deviceId: string) => void;
|
||||
refreshDeviceList: () => Promise<void>;
|
||||
disabled?: boolean;
|
||||
};
|
||||
export function DeviceTile({
|
||||
device,
|
||||
deleted,
|
||||
onDeleteToggle,
|
||||
refreshDeviceList,
|
||||
disabled,
|
||||
}: DeviceTileProps) {
|
||||
const activeTs = device.last_seen_ts;
|
||||
const [details, setDetails] = useState(false);
|
||||
const [edit, setEdit] = useState(false);
|
||||
|
||||
const handleRename = useCallback(() => {
|
||||
setEdit(false);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SettingTile
|
||||
before={
|
||||
<IconButton
|
||||
variant={deleted ? 'Critical' : 'Secondary'}
|
||||
outlined={deleted}
|
||||
radii="300"
|
||||
onClick={() => setDetails(!details)}
|
||||
>
|
||||
<Icon size="50" src={details ? Icons.ChevronBottom : Icons.ChevronRight} />
|
||||
</IconButton>
|
||||
}
|
||||
after={
|
||||
!edit && (
|
||||
<Box shrink="No" alignItems="Center" gap="200">
|
||||
{deleted ? (
|
||||
<Chip
|
||||
variant="Critical"
|
||||
fill="None"
|
||||
radii="Pill"
|
||||
onClick={() => onDeleteToggle?.(device.device_id)}
|
||||
disabled={disabled}
|
||||
>
|
||||
<Text size="B300">Undo</Text>
|
||||
</Chip>
|
||||
) : (
|
||||
<>
|
||||
<Chip
|
||||
variant="Secondary"
|
||||
fill="None"
|
||||
radii="Pill"
|
||||
onClick={() => onDeleteToggle?.(device.device_id)}
|
||||
disabled={disabled}
|
||||
>
|
||||
<Icon size="50" src={Icons.Delete} />
|
||||
</Chip>
|
||||
<Chip
|
||||
variant="Secondary"
|
||||
radii="Pill"
|
||||
onClick={() => setEdit(true)}
|
||||
disabled={disabled}
|
||||
>
|
||||
<Text size="B300">Edit</Text>
|
||||
</Chip>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
>
|
||||
<Text size="T300">{device.display_name ?? device.device_id}</Text>
|
||||
<Box direction="Column">
|
||||
{typeof activeTs === 'number' && <DeviceActiveTime ts={activeTs} />}
|
||||
{details && <DeviceDetails device={device} />}
|
||||
</Box>
|
||||
</SettingTile>
|
||||
{edit && (
|
||||
<DeviceRename
|
||||
device={device}
|
||||
onCancel={() => setEdit(false)}
|
||||
onRename={handleRename}
|
||||
refreshDeviceList={refreshDeviceList}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import React, { FormEventHandler, useCallback, useEffect, useState } from 'react';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Text,
|
||||
|
@ -6,32 +6,24 @@ import {
|
|||
Icon,
|
||||
Icons,
|
||||
Scroll,
|
||||
Chip,
|
||||
Input,
|
||||
Button,
|
||||
color,
|
||||
Spinner,
|
||||
toRem,
|
||||
Menu,
|
||||
config,
|
||||
} from 'folds';
|
||||
import { AuthDict, IMyDevice, MatrixError } from 'matrix-js-sdk';
|
||||
import { AuthDict, MatrixError } from 'matrix-js-sdk';
|
||||
import { Page, PageContent, PageHeader } from '../../../components/page';
|
||||
import { SequenceCard } from '../../../components/sequence-card';
|
||||
import { SequenceCardStyle } from '../styles.css';
|
||||
import { SettingTile } from '../../../components/setting-tile';
|
||||
import { useDeviceList } from '../../../hooks/useDeviceList';
|
||||
import { useMatrixClient } from '../../../hooks/useMatrixClient';
|
||||
import { timeDayMonYear, timeHourMinute, today, yesterday } from '../../../utils/time';
|
||||
import { BreakWord } from '../../../styles/Text.css';
|
||||
import {
|
||||
AsyncState,
|
||||
AsyncStatus,
|
||||
useAsync,
|
||||
useAsyncCallback,
|
||||
} from '../../../hooks/useAsyncCallback';
|
||||
import { AsyncState, AsyncStatus, useAsync } from '../../../hooks/useAsyncCallback';
|
||||
import { ActionUIA, ActionUIAFlowsLoader } from '../../../components/ActionUIA';
|
||||
import { useUIAMatrixError } from '../../../hooks/useUIAFlows';
|
||||
import { LocalBackup } from './LocalBackup';
|
||||
import { DeviceTile } from './DeviceTile';
|
||||
|
||||
function DevicesPlaceholder() {
|
||||
return (
|
||||
|
@ -75,225 +67,10 @@ function DevicesPlaceholder() {
|
|||
);
|
||||
}
|
||||
|
||||
function DeviceActiveTime({ ts }: { ts: number }) {
|
||||
return (
|
||||
<Text className={BreakWord} size="T200">
|
||||
<Text size="Inherit" as="span" priority="300">
|
||||
{'Last activity: '}
|
||||
</Text>
|
||||
<>
|
||||
{today(ts) && 'Today'}
|
||||
{yesterday(ts) && 'Yesterday'}
|
||||
{!today(ts) && !yesterday(ts) && timeDayMonYear(ts)} {timeHourMinute(ts)}
|
||||
</>
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
function DeviceDetails({ device }: { device: IMyDevice }) {
|
||||
return (
|
||||
<>
|
||||
{typeof device.device_id === 'string' && (
|
||||
<Text className={BreakWord} size="T200" priority="300">
|
||||
Session ID: <i>{device.device_id}</i>
|
||||
</Text>
|
||||
)}
|
||||
{typeof device.last_seen_ip === 'string' && (
|
||||
<Text className={BreakWord} size="T200" priority="300">
|
||||
IP Address: <i>{device.last_seen_ip}</i>
|
||||
</Text>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
type DeviceRenameProps = {
|
||||
device: IMyDevice;
|
||||
onCancel: () => void;
|
||||
onRename: () => void;
|
||||
refreshDeviceList: () => Promise<void>;
|
||||
};
|
||||
function DeviceRename({ device, onCancel, onRename, refreshDeviceList }: DeviceRenameProps) {
|
||||
const mx = useMatrixClient();
|
||||
|
||||
const [renameState, rename] = useAsyncCallback<void, MatrixError, [string]>(
|
||||
useCallback(
|
||||
async (name: string) => {
|
||||
await mx.setDeviceDetails(device.device_id, { display_name: name });
|
||||
await refreshDeviceList();
|
||||
},
|
||||
[mx, device.device_id, refreshDeviceList]
|
||||
)
|
||||
);
|
||||
|
||||
const renaming = renameState.status === AsyncStatus.Loading;
|
||||
|
||||
useEffect(() => {
|
||||
if (renameState.status === AsyncStatus.Success) {
|
||||
onRename();
|
||||
}
|
||||
}, [renameState, onRename]);
|
||||
|
||||
const handleSubmit: FormEventHandler<HTMLFormElement> = (evt) => {
|
||||
evt.preventDefault();
|
||||
if (renaming) return;
|
||||
|
||||
const target = evt.target as HTMLFormElement | undefined;
|
||||
const nameInput = target?.nameInput as HTMLInputElement | undefined;
|
||||
if (!nameInput) return;
|
||||
const sessionName = nameInput.value.trim();
|
||||
if (!sessionName || sessionName === device.display_name) return;
|
||||
|
||||
rename(sessionName);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box as="form" onSubmit={handleSubmit} direction="Column" gap="100">
|
||||
<Text size="L400">Session Name</Text>
|
||||
<Box gap="200">
|
||||
<Box grow="Yes" direction="Column">
|
||||
<Input
|
||||
name="nameInput"
|
||||
size="300"
|
||||
variant="Secondary"
|
||||
radii="300"
|
||||
defaultValue={device.display_name}
|
||||
autoFocus
|
||||
required
|
||||
readOnly={renaming}
|
||||
/>
|
||||
</Box>
|
||||
<Box shrink="No" gap="200">
|
||||
<Button
|
||||
type="submit"
|
||||
size="300"
|
||||
variant="Success"
|
||||
radii="300"
|
||||
fill="Solid"
|
||||
disabled={renaming}
|
||||
before={renaming && <Spinner size="100" variant="Success" fill="Solid" />}
|
||||
>
|
||||
<Text size="B300">Save</Text>
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
size="300"
|
||||
variant="Secondary"
|
||||
radii="300"
|
||||
fill="Soft"
|
||||
onClick={onCancel}
|
||||
disabled={renaming}
|
||||
>
|
||||
<Text size="B300">Cancel</Text>
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
{renameState.status === AsyncStatus.Error ? (
|
||||
<Text size="T200" style={{ color: color.Critical.Main }}>
|
||||
{renameState.error.message}
|
||||
</Text>
|
||||
) : (
|
||||
<Text size="T200">Session names are visible to public.</Text>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
type DeviceTileProps = {
|
||||
device: IMyDevice;
|
||||
deleted: boolean;
|
||||
onDeleteToggle: (deviceId: string) => void;
|
||||
refreshDeviceList: () => Promise<void>;
|
||||
disabled?: boolean;
|
||||
};
|
||||
function DeviceTile({
|
||||
device,
|
||||
deleted,
|
||||
onDeleteToggle,
|
||||
refreshDeviceList,
|
||||
disabled,
|
||||
}: DeviceTileProps) {
|
||||
const activeTs = device.last_seen_ts;
|
||||
const [details, setDetails] = useState(false);
|
||||
const [edit, setEdit] = useState(false);
|
||||
|
||||
const handleRename = useCallback(() => {
|
||||
setEdit(false);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SettingTile
|
||||
before={
|
||||
<IconButton
|
||||
variant={deleted ? 'Critical' : 'Secondary'}
|
||||
outlined={deleted}
|
||||
radii="300"
|
||||
onClick={() => setDetails(!details)}
|
||||
>
|
||||
<Icon size="50" src={details ? Icons.ChevronBottom : Icons.ChevronRight} />
|
||||
</IconButton>
|
||||
}
|
||||
after={
|
||||
!edit && (
|
||||
<Box shrink="No" alignItems="Center" gap="200">
|
||||
{deleted ? (
|
||||
<Chip
|
||||
variant="Critical"
|
||||
fill="None"
|
||||
radii="Pill"
|
||||
onClick={() => onDeleteToggle?.(device.device_id)}
|
||||
disabled={disabled}
|
||||
>
|
||||
<Text size="B300">Undo</Text>
|
||||
</Chip>
|
||||
) : (
|
||||
<>
|
||||
<Chip
|
||||
variant="Secondary"
|
||||
fill="None"
|
||||
radii="Pill"
|
||||
onClick={() => onDeleteToggle?.(device.device_id)}
|
||||
disabled={disabled}
|
||||
>
|
||||
<Icon size="50" src={Icons.Delete} />
|
||||
</Chip>
|
||||
<Chip
|
||||
variant="Secondary"
|
||||
radii="Pill"
|
||||
onClick={() => setEdit(true)}
|
||||
disabled={disabled}
|
||||
>
|
||||
<Text size="B300">Edit</Text>
|
||||
</Chip>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
>
|
||||
<Text size="T300">{device.display_name ?? device.device_id}</Text>
|
||||
<Box direction="Column">
|
||||
{typeof activeTs === 'number' && <DeviceActiveTime ts={activeTs} />}
|
||||
{details && <DeviceDetails device={device} />}
|
||||
</Box>
|
||||
</SettingTile>
|
||||
{edit && (
|
||||
<DeviceRename
|
||||
device={device}
|
||||
onCancel={() => setEdit(false)}
|
||||
onRename={handleRename}
|
||||
refreshDeviceList={refreshDeviceList}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
type SessionsProps = {
|
||||
type DevicesProps = {
|
||||
requestClose: () => void;
|
||||
};
|
||||
export function Sessions({ requestClose }: SessionsProps) {
|
||||
export function Devices({ requestClose }: DevicesProps) {
|
||||
const mx = useMatrixClient();
|
||||
const [devices, refreshDeviceList] = useDeviceList();
|
||||
const currentDeviceId = mx.getDeviceId();
|
||||
|
@ -354,7 +131,7 @@ export function Sessions({ requestClose }: SessionsProps) {
|
|||
<Box grow="Yes" gap="200">
|
||||
<Box grow="Yes" alignItems="Center" gap="200">
|
||||
<Text size="H3" truncate>
|
||||
Sessions
|
||||
Devices
|
||||
</Text>
|
||||
</Box>
|
||||
<Box shrink="No">
|
||||
|
@ -385,11 +162,11 @@ export function Sessions({ requestClose }: SessionsProps) {
|
|||
<Box grow="Yes" direction="Column">
|
||||
{deleteError ? (
|
||||
<Text size="T200">
|
||||
<b>Failed to logout sessions! Please try again. {deleteError.message}</b>
|
||||
<b>Failed to logout devices! Please try again. {deleteError.message}</b>
|
||||
</Text>
|
||||
) : (
|
||||
<Text size="T200">
|
||||
<b>Logout from selected sessions. ({deleted.size} selected)</b>
|
||||
<b>Logout from selected devices. ({deleted.size} selected)</b>
|
||||
</Text>
|
||||
)}
|
||||
{authData && (
|
||||
|
@ -438,6 +215,27 @@ export function Sessions({ requestClose }: SessionsProps) {
|
|||
</Box>
|
||||
</Menu>
|
||||
)}
|
||||
<Box direction="Column" gap="100">
|
||||
<Text size="L400">Security</Text>
|
||||
<SequenceCard
|
||||
className={SequenceCardStyle}
|
||||
variant="SurfaceVariant"
|
||||
direction="Column"
|
||||
gap="400"
|
||||
>
|
||||
<SettingTile
|
||||
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">
|
||||
Setup
|
||||
</Text>
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
</SequenceCard>
|
||||
</Box>
|
||||
{devices === null && <DevicesPlaceholder />}
|
||||
{currentDevice && (
|
||||
<Box direction="Column" gap="100">
|
||||
|
@ -485,6 +283,7 @@ export function Sessions({ requestClose }: SessionsProps) {
|
|||
))}
|
||||
</Box>
|
||||
)}
|
||||
<LocalBackup />
|
||||
</Box>
|
||||
</PageContent>
|
||||
</Scroll>
|
1
src/app/features/settings/devices/index.ts
Normal file
1
src/app/features/settings/devices/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export * from './Devices';
|
|
@ -1,56 +0,0 @@
|
|||
import React from 'react';
|
||||
import { Box, Text, IconButton, Icon, Icons, Scroll } from 'folds';
|
||||
import { Page, PageContent, PageHeader } from '../../../components/page';
|
||||
import { SequenceCard } from '../../../components/sequence-card';
|
||||
import { SequenceCardStyle } from '../styles.css';
|
||||
import { SettingTile } from '../../../components/setting-tile';
|
||||
import { LocalBackup } from './LocalBackup';
|
||||
import { OnlineBackup } from './OnlineBackup';
|
||||
|
||||
type EncryptionProps = {
|
||||
requestClose: () => void;
|
||||
};
|
||||
export function Encryption({ requestClose }: EncryptionProps) {
|
||||
return (
|
||||
<Page>
|
||||
<PageHeader outlined={false}>
|
||||
<Box grow="Yes" gap="200">
|
||||
<Box grow="Yes" alignItems="Center" gap="200">
|
||||
<Text size="H3" truncate>
|
||||
Encryption
|
||||
</Text>
|
||||
</Box>
|
||||
<Box shrink="No">
|
||||
<IconButton onClick={requestClose} variant="Surface">
|
||||
<Icon src={Icons.Cross} />
|
||||
</IconButton>
|
||||
</Box>
|
||||
</Box>
|
||||
</PageHeader>
|
||||
<Box grow="Yes">
|
||||
<Scroll hideTrack visibility="Hover">
|
||||
<PageContent>
|
||||
<Box direction="Column" gap="700">
|
||||
<Box direction="Column" gap="100">
|
||||
<Text size="L400">Security</Text>
|
||||
<SequenceCard
|
||||
className={SequenceCardStyle}
|
||||
variant="SurfaceVariant"
|
||||
direction="Column"
|
||||
gap="400"
|
||||
>
|
||||
<SettingTile
|
||||
title="Session Verification"
|
||||
description="Keep all your sessions secured by prompting an alert to verify new session login."
|
||||
/>
|
||||
</SequenceCard>
|
||||
</Box>
|
||||
<OnlineBackup />
|
||||
<LocalBackup />
|
||||
</Box>
|
||||
</PageContent>
|
||||
</Scroll>
|
||||
</Box>
|
||||
</Page>
|
||||
);
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
import React from 'react';
|
||||
import { Box, Text, Button } from 'folds';
|
||||
import { SettingTile } from '../../../components/setting-tile';
|
||||
import { SequenceCard } from '../../../components/sequence-card';
|
||||
import { SequenceCardStyle } from '../styles.css';
|
||||
|
||||
export function OnlineBackup() {
|
||||
return (
|
||||
<Box direction="Column" gap="100">
|
||||
<Text size="L400">Online Backup</Text>
|
||||
<SequenceCard
|
||||
className={SequenceCardStyle}
|
||||
variant="SurfaceVariant"
|
||||
direction="Column"
|
||||
gap="400"
|
||||
>
|
||||
<SettingTile
|
||||
title="Automatic Backup"
|
||||
description="Continuously save encryption data on server to decrypt messages later."
|
||||
after={
|
||||
<Button size="300" variant="Success" radii="300">
|
||||
<Text as="span" size="B300">
|
||||
Restore
|
||||
</Text>
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
</SequenceCard>
|
||||
</Box>
|
||||
);
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export * from './Encryption';
|
|
@ -1 +0,0 @@
|
|||
export * from './Sessions';
|
Loading…
Reference in a new issue