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:
@@ -19,6 +19,10 @@ import { useRoom } from '../../hooks/useRoom';
|
||||
import { DeveloperTools } from '../common-settings/developer-tools';
|
||||
import { ExportRoomHistory } from './ExportRoomHistory';
|
||||
import { RoomActivityLog } from './RoomActivityLog';
|
||||
import { RoomServerACL } from './RoomServerACL';
|
||||
import { usePowerLevels, readPowerLevel } from '../../hooks/usePowerLevels';
|
||||
import { useRoomCreators } from '../../hooks/useRoomCreators';
|
||||
import { StateEvent } from '../../../types/matrix/room';
|
||||
|
||||
type RoomSettingsMenuItem = {
|
||||
page: RoomSettingsPage;
|
||||
@@ -26,47 +30,56 @@ type RoomSettingsMenuItem = {
|
||||
icon: IconSrc;
|
||||
};
|
||||
|
||||
const useRoomSettingsMenuItems = (): RoomSettingsMenuItem[] =>
|
||||
useMemo(
|
||||
() => [
|
||||
{
|
||||
page: RoomSettingsPage.GeneralPage,
|
||||
name: 'General',
|
||||
icon: Icons.Setting,
|
||||
},
|
||||
{
|
||||
page: RoomSettingsPage.MembersPage,
|
||||
name: 'Members',
|
||||
icon: Icons.User,
|
||||
},
|
||||
{
|
||||
page: RoomSettingsPage.PermissionsPage,
|
||||
name: 'Permissions',
|
||||
icon: Icons.Lock,
|
||||
},
|
||||
{
|
||||
page: RoomSettingsPage.EmojisStickersPage,
|
||||
name: 'Emojis & Stickers',
|
||||
icon: Icons.Smile,
|
||||
},
|
||||
{
|
||||
page: RoomSettingsPage.DeveloperToolsPage,
|
||||
name: 'Developer Tools',
|
||||
icon: Icons.Terminal,
|
||||
},
|
||||
{
|
||||
page: RoomSettingsPage.ExportPage,
|
||||
name: 'Export',
|
||||
icon: Icons.Download,
|
||||
},
|
||||
{
|
||||
page: RoomSettingsPage.ActivityLogPage,
|
||||
name: 'Activity',
|
||||
icon: Icons.RecentClock,
|
||||
},
|
||||
],
|
||||
[],
|
||||
const BASE_MENU_ITEMS: RoomSettingsMenuItem[] = [
|
||||
{
|
||||
page: RoomSettingsPage.GeneralPage,
|
||||
name: 'General',
|
||||
icon: Icons.Setting,
|
||||
},
|
||||
{
|
||||
page: RoomSettingsPage.MembersPage,
|
||||
name: 'Members',
|
||||
icon: Icons.User,
|
||||
},
|
||||
{
|
||||
page: RoomSettingsPage.PermissionsPage,
|
||||
name: 'Permissions',
|
||||
icon: Icons.Lock,
|
||||
},
|
||||
{
|
||||
page: RoomSettingsPage.EmojisStickersPage,
|
||||
name: 'Emojis & Stickers',
|
||||
icon: Icons.Smile,
|
||||
},
|
||||
{
|
||||
page: RoomSettingsPage.DeveloperToolsPage,
|
||||
name: 'Developer Tools',
|
||||
icon: Icons.Terminal,
|
||||
},
|
||||
{
|
||||
page: RoomSettingsPage.ExportPage,
|
||||
name: 'Export',
|
||||
icon: Icons.Download,
|
||||
},
|
||||
{
|
||||
page: RoomSettingsPage.ActivityLogPage,
|
||||
name: 'Activity',
|
||||
icon: Icons.RecentClock,
|
||||
},
|
||||
];
|
||||
|
||||
const SERVER_ACL_MENU_ITEM: RoomSettingsMenuItem = {
|
||||
page: RoomSettingsPage.ServerACLPage,
|
||||
name: 'Server ACL',
|
||||
icon: Icons.Shield,
|
||||
};
|
||||
|
||||
function useRoomSettingsMenuItems(canSeeServerACL: boolean): RoomSettingsMenuItem[] {
|
||||
return useMemo(
|
||||
() => (canSeeServerACL ? [...BASE_MENU_ITEMS, SERVER_ACL_MENU_ITEM] : BASE_MENU_ITEMS),
|
||||
[canSeeServerACL],
|
||||
);
|
||||
}
|
||||
|
||||
type RoomSettingsProps = {
|
||||
initialPage?: RoomSettingsPage;
|
||||
@@ -86,12 +99,24 @@ export function RoomSettings({ initialPage, requestClose }: RoomSettingsProps) {
|
||||
? (mxcUrlToHttp(mx, roomAvatar, useAuthentication, 96, 96, 'crop') ?? undefined)
|
||||
: undefined;
|
||||
|
||||
// Power level check: show Server ACL menu item to anyone who can read the state
|
||||
// (i.e. has at least state_default power level, or a custom ACL event power).
|
||||
// We show it to all users at or above the required power level; read-only view
|
||||
// for those who cannot edit.
|
||||
const powerLevels = usePowerLevels(room);
|
||||
const creators = useRoomCreators(room);
|
||||
const myUserId = mx.getSafeUserId();
|
||||
const myPL = readPowerLevel.user(powerLevels, myUserId);
|
||||
const requiredPL = readPowerLevel.state(powerLevels, StateEvent.RoomServerAcl);
|
||||
// Show the menu item if user meets the power level OR is a room creator.
|
||||
const canSeeServerACL = myPL >= requiredPL || creators.has(myUserId);
|
||||
|
||||
const screenSize = useScreenSizeContext();
|
||||
const [activePage, setActivePage] = useState<RoomSettingsPage | undefined>(() => {
|
||||
if (initialPage) return initialPage;
|
||||
return screenSize === ScreenSize.Mobile ? undefined : RoomSettingsPage.GeneralPage;
|
||||
});
|
||||
const menuItems = useRoomSettingsMenuItems();
|
||||
const menuItems = useRoomSettingsMenuItems(canSeeServerACL);
|
||||
|
||||
const handlePageRequestClose = () => {
|
||||
if (screenSize === ScreenSize.Mobile) {
|
||||
@@ -190,6 +215,9 @@ export function RoomSettings({ initialPage, requestClose }: RoomSettingsProps) {
|
||||
{activePage === RoomSettingsPage.ActivityLogPage && (
|
||||
<RoomActivityLog requestClose={handlePageRequestClose} />
|
||||
)}
|
||||
{activePage === RoomSettingsPage.ServerACLPage && (
|
||||
<RoomServerACL requestClose={handlePageRequestClose} />
|
||||
)}
|
||||
</PageRoot>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user