Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

upgrade to rust crypto #2168

Open
wants to merge 40 commits into
base: rework-user-settings
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
05859bf
update matrix js sdk
ajbura Aug 17, 2024
2feb4fe
remove dead code
ajbura Aug 17, 2024
b5dbb01
use rust crypto
ajbura Sep 2, 2024
f8053c8
update setPowerLevel usage
ajbura Sep 2, 2024
b36a2a1
fix types
ajbura Sep 2, 2024
0ca778c
Merge branch 'dev' into js-sdk-v34
ajbura Sep 2, 2024
b8f02f5
fix deprecated isRoomEncrypted method uses
ajbura Sep 2, 2024
56840ad
fix deprecated room.currentState uses
ajbura Sep 2, 2024
6835b12
fix deprecated import/export room keys func
ajbura Sep 2, 2024
fe4d0be
Merge branch 'dev' into js-sdk-v34
ajbura Sep 20, 2024
ca3389a
Merge branch 'dev' into js-sdk-v34
ajbura Jan 17, 2025
6bfd6e3
Merge branch 'rework-user-settings' into js-sdk-v34
ajbura Jan 17, 2025
6b77c4d
fix merge issues in image pack file
ajbura Jan 17, 2025
a20092d
fix remaining issues in image pack file
ajbura Jan 17, 2025
4070b97
start indexedDBStore
ajbura Jan 17, 2025
fff4a9c
update package lock and vite-plugin-top-level-await
ajbura Jan 17, 2025
f74f104
user session settings - WIP
ajbura Jan 18, 2025
0816165
add useAsync hook
ajbura Jan 19, 2025
6d4045e
add password stage uia
ajbura Jan 19, 2025
d512fc0
add uia flow matrix error hook
ajbura Jan 19, 2025
a2103a3
add UIA action component
ajbura Jan 19, 2025
e2dbf2e
add options to delete sessions
ajbura Jan 19, 2025
684c928
add sso uia stage
ajbura Jan 19, 2025
3d460bd
fix SSO stage complete error
ajbura Jan 19, 2025
9bc02fd
encryption - WIP
ajbura Jan 20, 2025
7c0f3ce
update user settings encryption terminology
ajbura Jan 24, 2025
aa359b7
add default variant to password input
ajbura Jan 25, 2025
b7a7a37
use password input in uia password stage
ajbura Jan 25, 2025
b1e78cc
add options for local backup in user settings
ajbura Jan 25, 2025
c1cdb97
remove typo in import local backup password input label
ajbura Jan 25, 2025
ee0ee79
online backup - WIP
ajbura Jan 26, 2025
4b3668c
fix uia sso action
ajbura Jan 26, 2025
6def5ab
move access token settings from about to developer tools
ajbura Jan 26, 2025
a50b6ef
merge encryption tab into sessions and rename it to devices
ajbura Jan 26, 2025
5716b75
add device placeholder tile
ajbura Jan 26, 2025
8b85102
add logout dialog
ajbura Jan 28, 2025
d817e11
add logout button for current device
ajbura Jan 28, 2025
18357ce
move other devices in component
ajbura Jan 28, 2025
5524837
render unverified device verification tile
ajbura Jan 29, 2025
64d80a8
add learn more section for current device verification
ajbura Jan 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7,568 changes: 3,495 additions & 4,073 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,6 @@
"vite": "5.0.13",
"vite-plugin-pwa": "0.20.5",
"vite-plugin-static-copy": "1.0.4",
"vite-plugin-top-level-await": "1.4.1"
"vite-plugin-top-level-await": "1.4.4"
}
}
73 changes: 73 additions & 0 deletions src/app/components/ActionUIA.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React, { ReactNode } from 'react';
import { AuthDict, AuthType, IAuthData, UIAFlow } from 'matrix-js-sdk';
import { getUIAFlowForStages } from '../utils/matrix-uia';
import { useSupportedUIAFlows, useUIACompleted, useUIAFlow } from '../hooks/useUIAFlows';
import { UIAFlowOverlay } from './UIAFlowOverlay';
import { PasswordStage, SSOStage } from './uia-stages';
import { useMatrixClient } from '../hooks/useMatrixClient';

export const SUPPORTED_IN_APP_UIA_STAGES = [AuthType.Password, AuthType.Sso];

export const pickUIAFlow = (uiaFlows: UIAFlow[]): UIAFlow | undefined => {
const passwordFlow = getUIAFlowForStages(uiaFlows, [AuthType.Password]);
if (passwordFlow) return passwordFlow;
return getUIAFlowForStages(uiaFlows, [AuthType.Sso]);
};

type ActionUIAProps = {
authData: IAuthData;
ongoingFlow: UIAFlow;
action: (authDict: AuthDict) => void;
onCancel: () => void;
};
export function ActionUIA({ authData, ongoingFlow, action, onCancel }: ActionUIAProps) {
const mx = useMatrixClient();
const completed = useUIACompleted(authData);
const { getStageToComplete } = useUIAFlow(authData, ongoingFlow);

const stageToComplete = getStageToComplete();

if (!stageToComplete) return null;
return (
<UIAFlowOverlay
currentStep={completed.length + 1}
stepCount={ongoingFlow.stages.length}
onCancel={onCancel}
>
{stageToComplete.type === AuthType.Password && (
<PasswordStage
userId={mx.getUserId()!}
stageData={stageToComplete}
onCancel={onCancel}
submitAuthDict={action}
/>
)}
{stageToComplete.type === AuthType.Sso && stageToComplete.session && (
<SSOStage
ssoRedirectURL={mx.getFallbackAuthUrl(AuthType.Sso, stageToComplete.session)}
stageData={stageToComplete}
onCancel={onCancel}
submitAuthDict={action}
/>
)}
</UIAFlowOverlay>
);
}

type ActionUIAFlowsLoaderProps = {
authData: IAuthData;
unsupported: () => ReactNode;
children: (ongoingFlow: UIAFlow) => ReactNode;
};
export function ActionUIAFlowsLoader({
authData,
unsupported,
children,
}: ActionUIAFlowsLoaderProps) {
const supportedFlows = useSupportedUIAFlows(authData.flows ?? [], SUPPORTED_IN_APP_UIA_STAGES);
const ongoingFlow = supportedFlows.length > 0 ? supportedFlows[0] : undefined;

if (!ongoingFlow) return unsupported();

return children(ongoingFlow);
}
2 changes: 1 addition & 1 deletion src/app/components/CapabilitiesAndMediaConfigLoader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export function CapabilitiesAndMediaConfigLoader({
[]
>(
useCallback(async () => {
const result = await Promise.allSettled([mx.getCapabilities(true), mx.getMediaConfig()]);
const result = await Promise.allSettled([mx.getCapabilities(), mx.getMediaConfig()]);
const capabilities = promiseFulfilledResult(result[0]);
const mediaConfig = promiseFulfilledResult(result[1]);
return [capabilities, mediaConfig];
Expand Down
2 changes: 1 addition & 1 deletion src/app/components/CapabilitiesLoader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ type CapabilitiesLoaderProps = {
export function CapabilitiesLoader({ children }: CapabilitiesLoaderProps) {
const mx = useMatrixClient();

const [state, load] = useAsyncCallback(useCallback(() => mx.getCapabilities(true), [mx]));
const [state, load] = useAsyncCallback(useCallback(() => mx.getCapabilities(), [mx]));

useEffect(() => {
load();
Expand Down
24 changes: 24 additions & 0 deletions src/app/components/DeviceVerificationStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { ReactNode } from 'react';
import { CryptoApi } from 'matrix-js-sdk/lib/crypto-api';
import {
useDeviceVerificationStatus,
VerificationStatus,
} from '../hooks/useDeviceVerificationStatus';

type DeviceVerificationStatusProps = {
crypto?: CryptoApi;
userId: string;
deviceId: string;
children: (verificationStatus: VerificationStatus) => ReactNode;
};

export function DeviceVerificationStatus({
crypto,
userId,
deviceId,
children,
}: DeviceVerificationStatusProps) {
const status = useDeviceVerificationStatus(crypto, userId, deviceId);

return children(status);
}
60 changes: 60 additions & 0 deletions src/app/components/LogoutDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React, { forwardRef, useCallback } from 'react';
import { Dialog, Header, config, Box, Text, Button, Spinner, color } from 'folds';
import { AsyncStatus, useAsyncCallback } from '../hooks/useAsyncCallback';
import { logoutClient } from '../../client/initMatrix';
import { useMatrixClient } from '../hooks/useMatrixClient';

type LogoutDialogProps = {
handleClose: () => void;
};
export const LogoutDialog = forwardRef<HTMLDivElement, LogoutDialogProps>(
({ handleClose }, ref) => {
const mx = useMatrixClient();

const [logoutState, logout] = useAsyncCallback<void, Error, []>(
useCallback(async () => {
await logoutClient(mx);
}, [mx])
);

const ongoingLogout = logoutState.status === AsyncStatus.Loading;

return (
<Dialog variant="Surface" ref={ref}>
<Header
style={{
padding: `0 ${config.space.S200} 0 ${config.space.S400}`,
borderBottomWidth: config.borderWidth.B300,
}}
variant="Surface"
size="500"
>
<Box grow="Yes">
<Text size="H4">Logout</Text>
</Box>
</Header>
<Box style={{ padding: config.space.S400 }} direction="Column" gap="400">
<Text priority="400">You’re about to log out. Are you sure?</Text>
{logoutState.status === AsyncStatus.Error && (
<Text style={{ color: color.Critical.Main }} size="T300">
Failed to logout! {logoutState.error.message}
</Text>
)}
<Box direction="Column" gap="200">
<Button
variant="Critical"
onClick={logout}
disabled={ongoingLogout}
before={ongoingLogout && <Spinner variant="Critical" fill="Solid" size="200" />}
>
<Text size="B400">Logout</Text>
</Button>
<Button variant="Secondary" fill="Soft" onClick={handleClose} disabled={ongoingLogout}>
<Text size="B400">Cancel</Text>
</Button>
</Box>
</Box>
</Dialog>
);
}
);
3 changes: 1 addition & 2 deletions src/app/components/UIAFlowOverlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
IconButton,
} from 'folds';
import FocusTrap from 'focus-trap-react';
import { stopPropagation } from '../utils/keyboard';

export type UIAFlowOverlayProps = {
currentStep: number;
Expand All @@ -29,7 +28,7 @@ export function UIAFlowOverlay({
}: UIAFlowOverlayProps) {
return (
<Overlay open backdrop={<OverlayBackdrop />}>
<FocusTrap focusTrapOptions={{ initialFocus: false, escapeDeactivates: stopPropagation }}>
<FocusTrap focusTrapOptions={{ initialFocus: false, escapeDeactivates: false }}>
<Box style={{ height: '100%' }} direction="Column" grow="Yes" gap="400">
<Box grow="Yes" direction="Column" alignItems="Center" justifyContent="Center">
{children}
Expand Down
2 changes: 1 addition & 1 deletion src/app/components/image-pack-view/ImagePackContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ export const ImagePackContent = as<'div', ImagePackContentProps>(
variant="Success"
radii="300"
disabled={!canApplyChanges || applying}
before={applying && <Spinner variant="Success" fill="Soft" size="100" />}
before={applying && <Spinner variant="Success" fill="Solid" size="100" />}
onClick={applyChanges}
>
<Text size="B300">Apply Changes</Text>
Expand Down
2 changes: 1 addition & 1 deletion src/app/components/password-input/PasswordInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ type PasswordInputProps = Omit<ComponentProps<typeof Input>, 'type' | 'size'> &
size: '400' | '500';
};
export const PasswordInput = forwardRef<HTMLInputElement, PasswordInputProps>(
({ variant, size, style, after, ...props }, ref) => {
({ variant = 'Background', size, style, after, ...props }, ref) => {
const paddingRight: string = size === '500' ? config.space.S300 : config.space.S200;

return (
Expand Down
89 changes: 89 additions & 0 deletions src/app/components/uia-stages/PasswordStage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { Box, Button, color, config, Dialog, Header, Icon, IconButton, Icons, Text } from 'folds';
import React, { FormEventHandler } from 'react';
import { AuthType } from 'matrix-js-sdk';
import { StageComponentProps } from './types';
import { ErrorCode } from '../../cs-errorcode';
import { PasswordInput } from '../password-input/PasswordInput';

export function PasswordStage({
stageData,
submitAuthDict,
onCancel,
userId,
}: StageComponentProps & {
userId: string;
}) {
const { errorCode, error, session } = stageData;

const handleFormSubmit: FormEventHandler<HTMLFormElement> = (evt) => {
evt.preventDefault();
const { passwordInput } = evt.target as HTMLFormElement & {
passwordInput: HTMLInputElement;
};
const password = passwordInput.value;
if (!password) return;
submitAuthDict({
type: AuthType.Password,
identifier: {
type: 'm.id.user',
user: userId,
},
password,
session,
});
};

return (
<Dialog>
<Header
style={{
padding: `0 ${config.space.S200} 0 ${config.space.S400}`,
}}
variant="Surface"
size="500"
>
<Box grow="Yes">
<Text size="H4">Account Password</Text>
</Box>
<IconButton size="300" onClick={onCancel} radii="300">
<Icon src={Icons.Cross} />
</IconButton>
</Header>
<Box
as="form"
onSubmit={handleFormSubmit}
style={{ padding: `0 ${config.space.S400} ${config.space.S400}` }}
direction="Column"
gap="400"
>
<Box direction="Column" gap="400">
<Text size="T200">
To perform this action you need to authenticate yourself by entering you account
password.
</Text>
<Box direction="Column" gap="100">
<Text size="L400">Password</Text>
<PasswordInput size="400" name="passwordInput" outlined autoFocus required />
{errorCode && (
<Box alignItems="Center" gap="100" style={{ color: color.Critical.Main }}>
<Icon size="50" src={Icons.Warning} filled />
<Text size="T200">
<b>
{errorCode === ErrorCode.M_FORBIDDEN
? 'Invalid Password!'
: `${errorCode}: ${error}`}
</b>
</Text>
</Box>
)}
</Box>
</Box>
<Button variant="Primary" type="submit">
<Text as="span" size="B400">
Continue
</Text>
</Button>
</Box>
</Dialog>
);
}
Loading