mirror of
https://github.com/cinnyapp/cinny.git
synced 2025-02-12 08:43:38 +01:00
account settings - WIP
This commit is contained in:
parent
97a0fede51
commit
5401b2e821
8 changed files with 297 additions and 76 deletions
|
@ -2,7 +2,7 @@ import React, { ReactNode } from 'react';
|
||||||
import { Box, Text } from 'folds';
|
import { Box, Text } from 'folds';
|
||||||
|
|
||||||
type SettingTileProps = {
|
type SettingTileProps = {
|
||||||
title: ReactNode;
|
title?: ReactNode;
|
||||||
description?: ReactNode;
|
description?: ReactNode;
|
||||||
before?: ReactNode;
|
before?: ReactNode;
|
||||||
after?: ReactNode;
|
after?: ReactNode;
|
||||||
|
@ -13,7 +13,7 @@ export function SettingTile({ title, description, before, after, children }: Set
|
||||||
<Box alignItems="Center" gap="300">
|
<Box alignItems="Center" gap="300">
|
||||||
{before && <Box shrink="No">{before}</Box>}
|
{before && <Box shrink="No">{before}</Box>}
|
||||||
<Box grow="Yes" direction="Column" gap="100">
|
<Box grow="Yes" direction="Column" gap="100">
|
||||||
<Text size="T300">{title}</Text>
|
{title && <Text size="T300">{title}</Text>}
|
||||||
{description && (
|
{description && (
|
||||||
<Text size="T200" priority="300">
|
<Text size="T200" priority="300">
|
||||||
{description}
|
{description}
|
||||||
|
|
184
src/app/features/settings/Account.tsx
Normal file
184
src/app/features/settings/Account.tsx
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
import React, { useCallback, useEffect } from 'react';
|
||||||
|
import { Box, Text, IconButton, Icon, Icons, Scroll, Input, Avatar, Button, Chip } 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 { useMatrixClient } from '../../hooks/useMatrixClient';
|
||||||
|
import { useUserProfile } from '../../hooks/useUserProfile';
|
||||||
|
import { getMxIdLocalPart, mxcUrlToHttp } from '../../utils/matrix';
|
||||||
|
import { UserAvatar } from '../../components/user-avatar';
|
||||||
|
import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
|
||||||
|
import { nameInitials } from '../../utils/common';
|
||||||
|
import { copyToClipboard } from '../../utils/dom';
|
||||||
|
import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback';
|
||||||
|
|
||||||
|
function MatrixId() {
|
||||||
|
const mx = useMatrixClient();
|
||||||
|
const userId = mx.getUserId()!;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box direction="Column" gap="100">
|
||||||
|
<Text size="L400">Matrix ID</Text>
|
||||||
|
<SequenceCard
|
||||||
|
className={SequenceCardStyle}
|
||||||
|
variant="SurfaceVariant"
|
||||||
|
direction="Column"
|
||||||
|
gap="400"
|
||||||
|
>
|
||||||
|
<SettingTile
|
||||||
|
title={userId}
|
||||||
|
after={
|
||||||
|
<Chip variant="Secondary" radii="Pill" onClick={() => copyToClipboard(userId)}>
|
||||||
|
<Text size="T200">Copy</Text>
|
||||||
|
</Chip>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</SequenceCard>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Profile() {
|
||||||
|
const mx = useMatrixClient();
|
||||||
|
const useAuthentication = useMediaAuthentication();
|
||||||
|
const userId = mx.getUserId()!;
|
||||||
|
const profile = useUserProfile(userId);
|
||||||
|
|
||||||
|
const defaultDisplayName = profile.displayName ?? getMxIdLocalPart(userId) ?? userId;
|
||||||
|
const avatarUrl = profile.avatarUrl
|
||||||
|
? mxcUrlToHttp(mx, profile.avatarUrl, useAuthentication, 96, 96, 'crop') ?? undefined
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box direction="Column" gap="100">
|
||||||
|
<Text size="L400">Profile</Text>
|
||||||
|
<SequenceCard
|
||||||
|
className={SequenceCardStyle}
|
||||||
|
variant="SurfaceVariant"
|
||||||
|
direction="Column"
|
||||||
|
gap="400"
|
||||||
|
>
|
||||||
|
<SettingTile
|
||||||
|
title={
|
||||||
|
<Text as="span" size="L400">
|
||||||
|
Avatar
|
||||||
|
</Text>
|
||||||
|
}
|
||||||
|
before={
|
||||||
|
<Avatar size="500" radii="300">
|
||||||
|
<UserAvatar
|
||||||
|
userId={userId}
|
||||||
|
src={avatarUrl}
|
||||||
|
renderFallback={() => <Text size="H4">{nameInitials(defaultDisplayName)}</Text>}
|
||||||
|
/>
|
||||||
|
</Avatar>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Box gap="200">
|
||||||
|
<Button size="300" variant="Secondary" fill="Soft" outlined radii="300">
|
||||||
|
<Text size="B300">Upload</Text>
|
||||||
|
</Button>
|
||||||
|
{avatarUrl && (
|
||||||
|
<Button size="300" variant="Critical" fill="None" radii="300">
|
||||||
|
<Text size="B300">Remove</Text>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</SettingTile>
|
||||||
|
<SettingTile
|
||||||
|
title={
|
||||||
|
<Text as="span" size="L400">
|
||||||
|
Display Name
|
||||||
|
</Text>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Box direction="Column" grow="Yes" gap="100">
|
||||||
|
<Box gap="200">
|
||||||
|
<Box grow="Yes" direction="Column">
|
||||||
|
<Input defaultValue={defaultDisplayName} variant="Secondary" radii="300" />
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Button size="400" variant="Secondary" fill="Soft" outlined radii="300">
|
||||||
|
<Text size="B400">Save</Text>
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</SettingTile>
|
||||||
|
</SequenceCard>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ContactInformation() {
|
||||||
|
const mx = useMatrixClient();
|
||||||
|
const [threePIdsState, loadThreePIds] = useAsyncCallback(
|
||||||
|
useCallback(() => mx.getThreePids(), [mx])
|
||||||
|
);
|
||||||
|
const threePIds =
|
||||||
|
threePIdsState.status === AsyncStatus.Success ? threePIdsState.data.threepids : undefined;
|
||||||
|
|
||||||
|
const emailIds = threePIds?.filter((id) => id.medium === 'email');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
loadThreePIds();
|
||||||
|
}, [loadThreePIds]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box direction="Column" gap="100">
|
||||||
|
<Text size="L400">Contact Information</Text>
|
||||||
|
<SequenceCard
|
||||||
|
className={SequenceCardStyle}
|
||||||
|
variant="SurfaceVariant"
|
||||||
|
direction="Column"
|
||||||
|
gap="400"
|
||||||
|
>
|
||||||
|
<SettingTile title="Email Address" description="Email address attached to your account.">
|
||||||
|
<Box>
|
||||||
|
{emailIds?.map((email) => (
|
||||||
|
<Chip as="span" variant="Secondary" radii="Pill">
|
||||||
|
<Text size="T200">{email.address}</Text>
|
||||||
|
</Chip>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
{/* <Input defaultValue="" variant="Secondary" radii="300" /> */}
|
||||||
|
</SettingTile>
|
||||||
|
</SequenceCard>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
type AccountProps = {
|
||||||
|
requestClose: () => void;
|
||||||
|
};
|
||||||
|
export function Account({ requestClose }: AccountProps) {
|
||||||
|
return (
|
||||||
|
<Page>
|
||||||
|
<PageHeader outlined={false}>
|
||||||
|
<Box grow="Yes" gap="200">
|
||||||
|
<Box grow="Yes" alignItems="Center" gap="200">
|
||||||
|
<Text size="H3" truncate>
|
||||||
|
Account
|
||||||
|
</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">
|
||||||
|
<Profile />
|
||||||
|
<MatrixId />
|
||||||
|
<ContactInformation />
|
||||||
|
</Box>
|
||||||
|
</PageContent>
|
||||||
|
</Scroll>
|
||||||
|
</Box>
|
||||||
|
</Page>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
import React, {
|
import React, {
|
||||||
ChangeEventHandler,
|
ChangeEventHandler,
|
||||||
CSSProperties,
|
|
||||||
KeyboardEventHandler,
|
KeyboardEventHandler,
|
||||||
MouseEventHandler,
|
MouseEventHandler,
|
||||||
useState,
|
useState,
|
||||||
|
@ -45,10 +44,7 @@ import {
|
||||||
import { stopPropagation } from '../../utils/keyboard';
|
import { stopPropagation } from '../../utils/keyboard';
|
||||||
import { useMessageLayoutItems } from '../../hooks/useMessageLayout';
|
import { useMessageLayoutItems } from '../../hooks/useMessageLayout';
|
||||||
import { useMessageSpacingItems } from '../../hooks/useMessageSpacing';
|
import { useMessageSpacingItems } from '../../hooks/useMessageSpacing';
|
||||||
|
import { SequenceCardStyle } from './styles.css';
|
||||||
const SequenceCardStyle: CSSProperties = {
|
|
||||||
padding: config.space.S300,
|
|
||||||
};
|
|
||||||
|
|
||||||
type ThemeSelectorProps = {
|
type ThemeSelectorProps = {
|
||||||
themeNames: Record<string, string>;
|
themeNames: Record<string, string>;
|
||||||
|
@ -286,7 +282,7 @@ function PageZoomInput() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Input
|
<Input
|
||||||
style={{ width: toRem(150) }}
|
style={{ width: toRem(100) }}
|
||||||
variant={pageZoom === parseInt(currentZoom, 10) ? 'Secondary' : 'Success'}
|
variant={pageZoom === parseInt(currentZoom, 10) ? 'Secondary' : 'Success'}
|
||||||
size="300"
|
size="300"
|
||||||
radii="300"
|
radii="300"
|
||||||
|
@ -309,7 +305,12 @@ function Appearance() {
|
||||||
return (
|
return (
|
||||||
<Box direction="Column" gap="100">
|
<Box direction="Column" gap="100">
|
||||||
<Text size="L400">Appearance</Text>
|
<Text size="L400">Appearance</Text>
|
||||||
<SequenceCard style={SequenceCardStyle} variant="SurfaceVariant" direction="Column" gap="400">
|
<SequenceCard
|
||||||
|
className={SequenceCardStyle}
|
||||||
|
variant="SurfaceVariant"
|
||||||
|
direction="Column"
|
||||||
|
gap="400"
|
||||||
|
>
|
||||||
<SettingTile
|
<SettingTile
|
||||||
title="System Theme"
|
title="System Theme"
|
||||||
description="Choose between light and dark theme based on system preference."
|
description="Choose between light and dark theme based on system preference."
|
||||||
|
@ -318,7 +319,7 @@ function Appearance() {
|
||||||
{systemTheme && <SystemThemePreferences />}
|
{systemTheme && <SystemThemePreferences />}
|
||||||
</SequenceCard>
|
</SequenceCard>
|
||||||
|
|
||||||
<SequenceCard style={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||||
<SettingTile
|
<SettingTile
|
||||||
title="Theme"
|
title="Theme"
|
||||||
description="Theme to use when system theme is not enabled."
|
description="Theme to use when system theme is not enabled."
|
||||||
|
@ -326,14 +327,14 @@ function Appearance() {
|
||||||
/>
|
/>
|
||||||
</SequenceCard>
|
</SequenceCard>
|
||||||
|
|
||||||
<SequenceCard style={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||||
<SettingTile
|
<SettingTile
|
||||||
title="Twitter Emoji"
|
title="Twitter Emoji"
|
||||||
after={<Switch variant="Primary" value={twitterEmoji} onChange={setTwitterEmoji} />}
|
after={<Switch variant="Primary" value={twitterEmoji} onChange={setTwitterEmoji} />}
|
||||||
/>
|
/>
|
||||||
</SequenceCard>
|
</SequenceCard>
|
||||||
|
|
||||||
<SequenceCard style={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||||
<SettingTile title="Page Zoom" after={<PageZoomInput />} />
|
<SettingTile title="Page Zoom" after={<PageZoomInput />} />
|
||||||
</SequenceCard>
|
</SequenceCard>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -347,7 +348,7 @@ function Editor() {
|
||||||
return (
|
return (
|
||||||
<Box direction="Column" gap="100">
|
<Box direction="Column" gap="100">
|
||||||
<Text size="L400">Editor</Text>
|
<Text size="L400">Editor</Text>
|
||||||
<SequenceCard style={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||||
<SettingTile
|
<SettingTile
|
||||||
title="ENTER for Newline"
|
title="ENTER for Newline"
|
||||||
description={`Use ${
|
description={`Use ${
|
||||||
|
@ -356,7 +357,7 @@ function Editor() {
|
||||||
after={<Switch variant="Primary" value={enterForNewline} onChange={setEnterForNewline} />}
|
after={<Switch variant="Primary" value={enterForNewline} onChange={setEnterForNewline} />}
|
||||||
/>
|
/>
|
||||||
</SequenceCard>
|
</SequenceCard>
|
||||||
<SequenceCard style={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||||
<SettingTile
|
<SettingTile
|
||||||
title="Markdown Formatting"
|
title="Markdown Formatting"
|
||||||
after={<Switch variant="Primary" value={isMarkdown} onChange={setIsMarkdown} />}
|
after={<Switch variant="Primary" value={isMarkdown} onChange={setIsMarkdown} />}
|
||||||
|
@ -521,13 +522,13 @@ function Messages() {
|
||||||
return (
|
return (
|
||||||
<Box direction="Column" gap="100">
|
<Box direction="Column" gap="100">
|
||||||
<Text size="L400">Messages</Text>
|
<Text size="L400">Messages</Text>
|
||||||
<SequenceCard style={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||||
<SettingTile title="Message Layout" after={<SelectMessageLayout />} />
|
<SettingTile title="Message Layout" after={<SelectMessageLayout />} />
|
||||||
</SequenceCard>
|
</SequenceCard>
|
||||||
<SequenceCard style={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||||
<SettingTile title="Message Spacing" after={<SelectMessageSpacing />} />
|
<SettingTile title="Message Spacing" after={<SelectMessageSpacing />} />
|
||||||
</SequenceCard>
|
</SequenceCard>
|
||||||
<SequenceCard style={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||||
<SettingTile
|
<SettingTile
|
||||||
title="Hide Membership Change"
|
title="Hide Membership Change"
|
||||||
after={
|
after={
|
||||||
|
@ -539,7 +540,7 @@ function Messages() {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</SequenceCard>
|
</SequenceCard>
|
||||||
<SequenceCard style={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||||
<SettingTile
|
<SettingTile
|
||||||
title="Hide Profile Change"
|
title="Hide Profile Change"
|
||||||
after={
|
after={
|
||||||
|
@ -551,25 +552,25 @@ function Messages() {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</SequenceCard>
|
</SequenceCard>
|
||||||
<SequenceCard style={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||||
<SettingTile
|
<SettingTile
|
||||||
title="Disable Media Auto Load"
|
title="Disable Media Auto Load"
|
||||||
after={<Switch variant="Primary" value={mediaAutoLoad} onChange={setMediaAutoLoad} />}
|
after={<Switch variant="Primary" value={mediaAutoLoad} onChange={setMediaAutoLoad} />}
|
||||||
/>
|
/>
|
||||||
</SequenceCard>
|
</SequenceCard>
|
||||||
<SequenceCard style={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||||
<SettingTile
|
<SettingTile
|
||||||
title="Url Preview"
|
title="Url Preview"
|
||||||
after={<Switch variant="Primary" value={urlPreview} onChange={setUrlPreview} />}
|
after={<Switch variant="Primary" value={urlPreview} onChange={setUrlPreview} />}
|
||||||
/>
|
/>
|
||||||
</SequenceCard>
|
</SequenceCard>
|
||||||
<SequenceCard style={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||||
<SettingTile
|
<SettingTile
|
||||||
title="Url Preview in Encrypted Room"
|
title="Url Preview in Encrypted Room"
|
||||||
after={<Switch variant="Primary" value={encUrlPreview} onChange={setEncUrlPreview} />}
|
after={<Switch variant="Primary" value={encUrlPreview} onChange={setEncUrlPreview} />}
|
||||||
/>
|
/>
|
||||||
</SequenceCard>
|
</SequenceCard>
|
||||||
<SequenceCard style={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||||
<SettingTile
|
<SettingTile
|
||||||
title="Show Hidden Events"
|
title="Show Hidden Events"
|
||||||
after={
|
after={
|
||||||
|
|
|
@ -1,8 +1,15 @@
|
||||||
import React, { useMemo, useState } from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
import { Box, config, Icon, IconButton, Icons, IconSrc, MenuItem, Text } from 'folds';
|
import { Avatar, Box, config, Icon, IconButton, Icons, IconSrc, MenuItem, Text } from 'folds';
|
||||||
import { General } from './General';
|
import { General } from './General';
|
||||||
import { PageNav, PageNavContent, PageNavHeader, PageRoot } from '../../components/page';
|
import { PageNav, PageNavContent, PageNavHeader, PageRoot } from '../../components/page';
|
||||||
import { ScreenSize, useScreenSizeContext } from '../../hooks/useScreenSize';
|
import { ScreenSize, useScreenSizeContext } from '../../hooks/useScreenSize';
|
||||||
|
import { Account } from './Account';
|
||||||
|
import { useUserProfile } from '../../hooks/useUserProfile';
|
||||||
|
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
||||||
|
import { getMxIdLocalPart, mxcUrlToHttp } from '../../utils/matrix';
|
||||||
|
import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
|
||||||
|
import { UserAvatar } from '../../components/user-avatar';
|
||||||
|
import { nameInitials } from '../../utils/common';
|
||||||
|
|
||||||
enum SettingsPages {
|
enum SettingsPages {
|
||||||
GeneralPage,
|
GeneralPage,
|
||||||
|
@ -72,6 +79,15 @@ type SettingsProps = {
|
||||||
requestClose: () => void;
|
requestClose: () => void;
|
||||||
};
|
};
|
||||||
export function Settings({ requestClose }: SettingsProps) {
|
export function Settings({ requestClose }: SettingsProps) {
|
||||||
|
const mx = useMatrixClient();
|
||||||
|
const useAuthentication = useMediaAuthentication();
|
||||||
|
const userId = mx.getUserId()!;
|
||||||
|
const profile = useUserProfile(userId);
|
||||||
|
const displayName = profile.displayName ?? getMxIdLocalPart(userId) ?? userId;
|
||||||
|
const avatarUrl = profile.avatarUrl
|
||||||
|
? mxcUrlToHttp(mx, profile.avatarUrl, useAuthentication, 96, 96, 'crop') ?? undefined
|
||||||
|
: undefined;
|
||||||
|
|
||||||
const screenSize = useScreenSizeContext();
|
const screenSize = useScreenSizeContext();
|
||||||
const [activePage, setActivePage] = useState<SettingsPages | undefined>(
|
const [activePage, setActivePage] = useState<SettingsPages | undefined>(
|
||||||
screenSize === ScreenSize.Mobile ? undefined : SettingsPages.GeneralPage
|
screenSize === ScreenSize.Mobile ? undefined : SettingsPages.GeneralPage
|
||||||
|
@ -92,8 +108,17 @@ export function Settings({ requestClose }: SettingsProps) {
|
||||||
screenSize === ScreenSize.Mobile && activePage !== undefined ? undefined : (
|
screenSize === ScreenSize.Mobile && activePage !== undefined ? undefined : (
|
||||||
<PageNav size="300">
|
<PageNav size="300">
|
||||||
<PageNavHeader outlined={false}>
|
<PageNavHeader outlined={false}>
|
||||||
<Box grow="Yes">
|
<Box grow="Yes" gap="200">
|
||||||
<Text size="H4">Settings</Text>
|
<Avatar size="200" radii="300">
|
||||||
|
<UserAvatar
|
||||||
|
userId={userId}
|
||||||
|
src={avatarUrl}
|
||||||
|
renderFallback={() => <Text size="H6">{nameInitials(displayName)}</Text>}
|
||||||
|
/>
|
||||||
|
</Avatar>
|
||||||
|
<Text size="H4" truncate>
|
||||||
|
Settings
|
||||||
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box shrink="No">
|
<Box shrink="No">
|
||||||
{screenSize === ScreenSize.Mobile && (
|
{screenSize === ScreenSize.Mobile && (
|
||||||
|
@ -133,6 +158,9 @@ export function Settings({ requestClose }: SettingsProps) {
|
||||||
{activePage === SettingsPages.GeneralPage && (
|
{activePage === SettingsPages.GeneralPage && (
|
||||||
<General requestClose={handlePageRequestClose} />
|
<General requestClose={handlePageRequestClose} />
|
||||||
)}
|
)}
|
||||||
|
{activePage === SettingsPages.AccountPage && (
|
||||||
|
<Account requestClose={handlePageRequestClose} />
|
||||||
|
)}
|
||||||
</PageRoot>
|
</PageRoot>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1 @@
|
||||||
export * from './Settings';
|
export * from './Settings';
|
||||||
export * from './General';
|
|
||||||
|
|
6
src/app/features/settings/styles.css.ts
Normal file
6
src/app/features/settings/styles.css.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import { style } from '@vanilla-extract/css';
|
||||||
|
import { config } from 'folds';
|
||||||
|
|
||||||
|
export const SequenceCardStyle = style({
|
||||||
|
padding: config.space.S300,
|
||||||
|
});
|
51
src/app/hooks/useUserProfile.ts
Normal file
51
src/app/hooks/useUserProfile.ts
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { UserEvent, UserEventHandlerMap } from 'matrix-js-sdk';
|
||||||
|
import { useMatrixClient } from './useMatrixClient';
|
||||||
|
|
||||||
|
export type UserProfile = {
|
||||||
|
avatarUrl?: string;
|
||||||
|
displayName?: string;
|
||||||
|
};
|
||||||
|
export const useUserProfile = (userId: string): UserProfile => {
|
||||||
|
const mx = useMatrixClient();
|
||||||
|
|
||||||
|
const [profile, setProfile] = useState<UserProfile>(() => {
|
||||||
|
const user = mx.getUser(userId);
|
||||||
|
return {
|
||||||
|
avatarUrl: user?.avatarUrl,
|
||||||
|
displayName: user?.displayName,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const user = mx.getUser(userId);
|
||||||
|
const onAvatarChange: UserEventHandlerMap[UserEvent.AvatarUrl] = (event, myUser) => {
|
||||||
|
setProfile((cp) => ({
|
||||||
|
...cp,
|
||||||
|
avatarUrl: myUser.avatarUrl,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
const onDisplayNameChange: UserEventHandlerMap[UserEvent.DisplayName] = (event, myUser) => {
|
||||||
|
setProfile((cp) => ({
|
||||||
|
...cp,
|
||||||
|
displayName: myUser.displayName,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
mx.getProfileInfo(userId).then((info) =>
|
||||||
|
setProfile({
|
||||||
|
avatarUrl: info.avatar_url,
|
||||||
|
displayName: info.displayname,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
user?.on(UserEvent.AvatarUrl, onAvatarChange);
|
||||||
|
user?.on(UserEvent.DisplayName, onDisplayNameChange);
|
||||||
|
return () => {
|
||||||
|
user?.removeListener(UserEvent.AvatarUrl, onAvatarChange);
|
||||||
|
user?.removeListener(UserEvent.DisplayName, onDisplayNameChange);
|
||||||
|
};
|
||||||
|
}, [mx, userId]);
|
||||||
|
|
||||||
|
return profile;
|
||||||
|
};
|
|
@ -1,7 +1,6 @@
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Modal, Overlay, OverlayBackdrop, OverlayCenter, Text } from 'folds';
|
import { Modal, Overlay, OverlayBackdrop, OverlayCenter, Text } from 'folds';
|
||||||
import FocusTrap from 'focus-trap-react';
|
import FocusTrap from 'focus-trap-react';
|
||||||
import { UserEvent, UserEventHandlerMap } from 'matrix-js-sdk';
|
|
||||||
import { SidebarItem, SidebarItemTooltip, SidebarAvatar } from '../../../components/sidebar';
|
import { SidebarItem, SidebarItemTooltip, SidebarAvatar } from '../../../components/sidebar';
|
||||||
import { UserAvatar } from '../../../components/user-avatar';
|
import { UserAvatar } from '../../../components/user-avatar';
|
||||||
import { useMatrixClient } from '../../../hooks/useMatrixClient';
|
import { useMatrixClient } from '../../../hooks/useMatrixClient';
|
||||||
|
@ -10,54 +9,7 @@ import { nameInitials } from '../../../utils/common';
|
||||||
import { useMediaAuthentication } from '../../../hooks/useMediaAuthentication';
|
import { useMediaAuthentication } from '../../../hooks/useMediaAuthentication';
|
||||||
import { Settings } from '../../../features/settings';
|
import { Settings } from '../../../features/settings';
|
||||||
import { stopPropagation } from '../../../utils/keyboard';
|
import { stopPropagation } from '../../../utils/keyboard';
|
||||||
|
import { useUserProfile } from '../../../hooks/useUserProfile';
|
||||||
type UserProfile = {
|
|
||||||
avatarUrl?: string;
|
|
||||||
displayName?: string;
|
|
||||||
};
|
|
||||||
const useUserProfile = (userId: string): UserProfile => {
|
|
||||||
const mx = useMatrixClient();
|
|
||||||
|
|
||||||
const [profile, setProfile] = useState<UserProfile>(() => {
|
|
||||||
const user = mx.getUser(userId);
|
|
||||||
return {
|
|
||||||
avatarUrl: user?.avatarUrl,
|
|
||||||
displayName: user?.displayName,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const user = mx.getUser(userId);
|
|
||||||
const onAvatarChange: UserEventHandlerMap[UserEvent.AvatarUrl] = (event, myUser) => {
|
|
||||||
setProfile((cp) => ({
|
|
||||||
...cp,
|
|
||||||
avatarUrl: myUser.avatarUrl,
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
const onDisplayNameChange: UserEventHandlerMap[UserEvent.DisplayName] = (event, myUser) => {
|
|
||||||
setProfile((cp) => ({
|
|
||||||
...cp,
|
|
||||||
displayName: myUser.displayName,
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
mx.getProfileInfo(userId).then((info) =>
|
|
||||||
setProfile({
|
|
||||||
avatarUrl: info.avatar_url,
|
|
||||||
displayName: info.displayname,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
user?.on(UserEvent.AvatarUrl, onAvatarChange);
|
|
||||||
user?.on(UserEvent.DisplayName, onDisplayNameChange);
|
|
||||||
return () => {
|
|
||||||
user?.removeListener(UserEvent.AvatarUrl, onAvatarChange);
|
|
||||||
user?.removeListener(UserEvent.DisplayName, onDisplayNameChange);
|
|
||||||
};
|
|
||||||
}, [mx, userId]);
|
|
||||||
|
|
||||||
return profile;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function SettingsTab() {
|
export function SettingsTab() {
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
|
|
Loading…
Reference in a new issue