feat: extended profile fields, push rule editor, server ACL editor

P2-8: Pronouns (m.pronouns) and Timezone (m.tz) fields in Settings →
Account → Profile; saved via MSC4133 PUT /profile/{userId}/{field};
useExtendedProfile hook fetches both in parallel; UserHero displays
pronouns below display name and timezone string below username

P2-11: Full push rule editor in Settings → Notifications below keyword
rules; covers override/room/sender/underride rule kinds; enable/disable
toggle per rule, human-readable labels for built-in rules, delete button
for custom rules, add-rule form for room and sender rules

P2-12: Server ACL viewer/editor in room settings (Server ACL tab);
reads m.room.server_acl state event; allow/deny server lists with
wildcard validation; allow IP literals toggle; power-level gated
(edit requires sufficient PL, otherwise read-only view)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-03 23:13:33 -04:00
parent 160db1eaef
commit 51a355fe77
9 changed files with 1157 additions and 43 deletions
+27 -1
View File
@@ -96,8 +96,16 @@ type UserHeroNameProps = {
displayName?: string;
userId: string;
status?: string;
pronouns?: string;
timezone?: string;
};
export function UserHeroName({ displayName, userId, status }: UserHeroNameProps) {
export function UserHeroName({
displayName,
userId,
status,
pronouns,
timezone,
}: UserHeroNameProps) {
const username = getMxIdLocalPart(userId);
return (
@@ -111,11 +119,29 @@ export function UserHeroName({ displayName, userId, status }: UserHeroNameProps)
{displayName ?? username ?? userId}
</Text>
</Box>
{pronouns && (
<Box alignItems="Center" gap="100" style={{ marginTop: '1px', overflow: 'hidden' }}>
<Text
size="T200"
className={classNames(BreakWord, LineClamp2)}
style={{ opacity: 0.6, fontStyle: 'italic' }}
>
{pronouns}
</Text>
</Box>
)}
<Box alignItems="Center" gap="100" wrap="Wrap">
<Text size="T200" className={classNames(BreakWord, LineClamp3)} title={username}>
@{username}
</Text>
</Box>
{timezone && (
<Box alignItems="Center" gap="100" style={{ marginTop: '1px', overflow: 'hidden' }}>
<Text size="T200" className={classNames(BreakWord, LineClamp2)} style={{ opacity: 0.6 }}>
{timezone}
</Text>
</Box>
)}
{status && (
<Box alignItems="Center" gap="100" style={{ marginTop: '2px', overflow: 'hidden' }}>
<Text