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:
@@ -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
|
||||
|
||||
@@ -31,6 +31,7 @@ import { useMemberPowerCompare } from '../../hooks/useMemberPowerCompare';
|
||||
import { CreatorChip } from './CreatorChip';
|
||||
import { getDirectCreatePath, withSearchParam } from '../../pages/pathUtils';
|
||||
import { DirectCreateSearchParams } from '../../pages/paths';
|
||||
import { useExtendedProfile } from '../../hooks/useExtendedProfile';
|
||||
|
||||
type VerifyDeviceButtonProps = {
|
||||
userId: string;
|
||||
@@ -243,6 +244,7 @@ export function UserRoomProfile({ userId }: UserRoomProfileProps) {
|
||||
const avatarUrl = (avatarMxc && mxcUrlToHttp(mx, avatarMxc, useAuthentication)) ?? undefined;
|
||||
|
||||
const presence = useUserPresence(userId);
|
||||
const extProfile = useExtendedProfile(userId);
|
||||
|
||||
const handleMessage = () => {
|
||||
closeUserRoomProfile();
|
||||
@@ -262,7 +264,13 @@ export function UserRoomProfile({ userId }: UserRoomProfileProps) {
|
||||
<Box direction="Column" gap="500" style={{ padding: config.space.S400 }}>
|
||||
<Box direction="Column" gap="400">
|
||||
<Box gap="400" alignItems="Center">
|
||||
<UserHeroName displayName={displayName} userId={userId} status={presence?.status} />
|
||||
<UserHeroName
|
||||
displayName={displayName}
|
||||
userId={userId}
|
||||
status={presence?.status}
|
||||
pronouns={extProfile.pronouns}
|
||||
timezone={extProfile.timezone}
|
||||
/>
|
||||
{showEncryption && <MemberVerificationBadge userId={userId} />}
|
||||
{userId !== myUserId && (
|
||||
<Box shrink="No">
|
||||
|
||||
Reference in New Issue
Block a user