Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
3c1aa0e
Rework profile settings to show a preview and support more fields
gingershaped Sep 15, 2025
c390180
Add a chip and setting for user timezones
gingershaped Sep 15, 2025
5c2c898
Fix flickering issues when updating profile fields
gingershaped Sep 15, 2025
c7f6e33
Propery delete blank profile fields
gingershaped Sep 15, 2025
c5b59ea
Add a setting for user pronouns
gingershaped Sep 15, 2025
d4deba6
Use a consistent fallback icon in settings for users with no avatar
gingershaped Sep 15, 2025
c389365
Merge branch 'dev' into msc4133
gingershaped Sep 15, 2025
07df0c2
Use Tanstack Query when fetching extended profiles to improve caching
gingershaped Sep 16, 2025
984803c
Add slightly more padding above the profile save and cancel buttons
gingershaped Sep 16, 2025
aafd028
Fix support for MSC4133-less homeservers, add OIDC profile link
gingershaped Sep 18, 2025
317cd36
Hide profile fields which are blocked by a capability
gingershaped Sep 18, 2025
8a8443b
Move profile field elements into their own files
gingershaped Sep 18, 2025
4c515bb
Move timezone chip to a better position
gingershaped Sep 18, 2025
79b37e1
Improve text contrast in IDP profile settings element
gingershaped Sep 19, 2025
cfee62f
Fix profile field comparison
gingershaped Sep 20, 2025
4c5acc1
Use proper deep comparison for hasChanged
gingershaped Sep 20, 2025
458b1c0
Merge branch 'dev' into msc4133
gingershaped Sep 22, 2025
f9b0d8c
Add some explanatory comments
gingershaped Sep 24, 2025
4e7b64e
Merge branch 'dev' into msc4133
gingershaped Oct 1, 2025
5bc9654
Add a panel in Developer Tools for editing profile fields
gingershaped Oct 6, 2025
af9460e
Fix incorrect logic when checking for profile field changes
gingershaped Oct 6, 2025
d42bcc6
Use a common CollapsibleCard element for collapsible settings cards
gingershaped Oct 6, 2025
205ea16
Add a context menu option to view a user's raw extended profile fields
gingershaped Oct 6, 2025
13dd8fc
Allow account data to be deleted if the homeserver supports it
gingershaped Oct 6, 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
12 changes: 11 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@
"slate-dom": "0.112.2",
"slate-history": "0.110.3",
"slate-react": "0.112.1",
"ua-parser-js": "1.0.35"
"ua-parser-js": "1.0.35",
"zod": "4.1.8"
},
"devDependencies": {
"@esbuild-plugins/node-globals-polyfill": "0.2.3",
Expand Down
57 changes: 44 additions & 13 deletions src/app/components/AccountDataEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { useTextAreaCodeEditor } from '../hooks/useTextAreaCodeEditor';
const EDITOR_INTENT_SPACE_COUNT = 2;

export type AccountDataSubmitCallback = (type: string, content: object) => Promise<void>;
export type AccountDataDeleteCallback = (type: string) => Promise<void>;

type AccountDataInfo = {
type: string;
Expand Down Expand Up @@ -83,8 +84,7 @@ function AccountDataEdit({

if (
!typeStr ||
parsedContent === null ||
defaultContent === JSON.stringify(parsedContent, null, EDITOR_INTENT_SPACE_COUNT)
parsedContent === null
) {
return;
}
Expand Down Expand Up @@ -121,7 +121,7 @@ function AccountDataEdit({
aria-disabled={submitting}
>
<Box shrink="No" direction="Column" gap="100">
<Text size="L400">Account Data</Text>
<Text size="L400">Field Name</Text>
<Box gap="300">
<Box grow="Yes" direction="Column">
<Input
Expand Down Expand Up @@ -195,9 +195,22 @@ function AccountDataEdit({
type AccountDataViewProps = {
type: string;
defaultContent: string;
onEdit: () => void;
requestClose: () => void;
onEdit?: () => void;
submitDelete?: AccountDataDeleteCallback;
};
function AccountDataView({ type, defaultContent, onEdit }: AccountDataViewProps) {
function AccountDataView({ type, defaultContent, onEdit, requestClose, submitDelete }: AccountDataViewProps) {
const [deleteState, deleteCallback] = useAsyncCallback<void, MatrixError, []>(useCallback(
async () => {
if (submitDelete !== undefined) {
await submitDelete(type);
requestClose();
}
},
[type, submitDelete, requestClose],
));
const deleting = deleteState.status === AsyncStatus.Loading;

return (
<Box
direction="Column"
Expand All @@ -208,7 +221,7 @@ function AccountDataView({ type, defaultContent, onEdit }: AccountDataViewProps)
>
<Box shrink="No" gap="300" alignItems="End">
<Box grow="Yes" direction="Column" gap="100">
<Text size="L400">Account Data</Text>
<Text size="L400">Field Name</Text>
<Input
variant="SurfaceVariant"
size="400"
Expand All @@ -218,9 +231,23 @@ function AccountDataView({ type, defaultContent, onEdit }: AccountDataViewProps)
required
/>
</Box>
<Button variant="Secondary" size="400" radii="300" onClick={onEdit}>
<Text size="B400">Edit</Text>
</Button>
{onEdit && (
<Button variant="Secondary" size="400" radii="300" onClick={onEdit}>
<Text size="B400">Edit</Text>
</Button>
)}
{submitDelete && (
<Button
variant="Critical"
size="400"
radii="300"
disabled={deleting}
before={deleting && <Spinner variant="Critical" fill="Solid" size="300" />}
onClick={deleteCallback}
>
<Text size="B400">Delete</Text>
</Button>
)}
</Box>
<Box grow="Yes" direction="Column" gap="100">
<Text size="L400">JSON Content</Text>
Expand All @@ -243,15 +270,17 @@ function AccountDataView({ type, defaultContent, onEdit }: AccountDataViewProps)

export type AccountDataEditorProps = {
type?: string;
content?: object;
submitChange: AccountDataSubmitCallback;
content?: unknown;
submitChange?: AccountDataSubmitCallback;
submitDelete?: AccountDataDeleteCallback;
requestClose: () => void;
};

export function AccountDataEditor({
type,
content,
submitChange,
submitDelete,
requestClose,
}: AccountDataEditorProps) {
const [data, setData] = useState<AccountDataInfo>({
Expand Down Expand Up @@ -301,7 +330,7 @@ export function AccountDataEditor({
</Box>
</PageHeader>
<Box grow="Yes" direction="Column">
{edit ? (
{(edit && submitChange) ? (
<AccountDataEdit
type={data.type}
defaultContent={contentJSONStr}
Expand All @@ -313,7 +342,9 @@ export function AccountDataEditor({
<AccountDataView
type={data.type}
defaultContent={contentJSONStr}
onEdit={() => setEdit(true)}
requestClose={requestClose}
onEdit={submitChange ? () => setEdit(true) : undefined}
submitDelete={submitDelete}
/>
)}
</Box>
Expand Down
54 changes: 54 additions & 0 deletions src/app/components/CollapsibleCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React, { ReactNode } from 'react';
import { Button, Icon, Icons, Text } from 'folds';
import { SequenceCard } from './sequence-card';
import { SequenceCardStyle } from '../features/settings/styles.css';
import { SettingTile } from './setting-tile';

type CollapsibleCardProps = {
expand: boolean;
setExpand: (expand: boolean) => void;
title?: ReactNode;
description?: ReactNode;
before?: ReactNode;
children?: ReactNode;
};

export function CollapsibleCard({
expand,
setExpand,
title,
description,
before,
children,
}: CollapsibleCardProps) {
return (
<SequenceCard
className={SequenceCardStyle}
variant="SurfaceVariant"
direction="Column"
gap="400"
>
<SettingTile
title={title}
description={description}
before={before}
after={
<Button
onClick={() => setExpand(!expand)}
variant="Secondary"
fill="Soft"
size="300"
radii="300"
outlined
before={
<Icon src={expand ? Icons.ChevronTop : Icons.ChevronBottom} size="100" filled />
}
>
<Text size="B300">{expand ? 'Collapse' : 'Expand'}</Text>
</Button>
}
/>
{expand && children}
</SequenceCard>
);
}
Loading