2025-03-19 23:14:54 +11:00
|
|
|
import React, { useMemo, useState } from 'react';
|
|
|
|
|
import { useAtomValue } from 'jotai';
|
|
|
|
|
import { Avatar, Box, config, Icon, IconButton, Icons, IconSrc, MenuItem, Text } from 'folds';
|
|
|
|
|
import { JoinRule } from 'matrix-js-sdk';
|
|
|
|
|
import { PageNav, PageNavContent, PageNavHeader, PageRoot } from '../../components/page';
|
|
|
|
|
import { ScreenSize, useScreenSizeContext } from '../../hooks/useScreenSize';
|
|
|
|
|
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
|
|
|
|
import { mxcUrlToHttp } from '../../utils/matrix';
|
|
|
|
|
import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
|
|
|
|
|
import { useRoomAvatar, useRoomJoinRule, useRoomName } from '../../hooks/useRoomMeta';
|
|
|
|
|
import { mDirectAtom } from '../../state/mDirectList';
|
|
|
|
|
import { RoomAvatar, RoomIcon } from '../../components/room-avatar';
|
|
|
|
|
import { General } from './general';
|
2025-03-27 19:54:13 +11:00
|
|
|
import { Members } from '../common-settings/members';
|
|
|
|
|
import { EmojisStickers } from '../common-settings/emojis-stickers';
|
2025-03-19 23:14:54 +11:00
|
|
|
import { Permissions } from './permissions';
|
|
|
|
|
import { RoomSettingsPage } from '../../state/roomSettings';
|
|
|
|
|
import { useRoom } from '../../hooks/useRoom';
|
2025-03-27 19:54:13 +11:00
|
|
|
import { DeveloperTools } from '../common-settings/developer-tools';
|
2026-06-03 22:13:22 -04:00
|
|
|
import { ExportRoomHistory } from './ExportRoomHistory';
|
|
|
|
|
import { RoomActivityLog } from './RoomActivityLog';
|
2026-06-03 23:13:33 -04:00
|
|
|
import { RoomServerACL } from './RoomServerACL';
|
|
|
|
|
import { usePowerLevels, readPowerLevel } from '../../hooks/usePowerLevels';
|
|
|
|
|
import { useRoomCreators } from '../../hooks/useRoomCreators';
|
|
|
|
|
import { StateEvent } from '../../../types/matrix/room';
|
2025-03-19 23:14:54 +11:00
|
|
|
|
|
|
|
|
type RoomSettingsMenuItem = {
|
|
|
|
|
page: RoomSettingsPage;
|
|
|
|
|
name: string;
|
|
|
|
|
icon: IconSrc;
|
|
|
|
|
};
|
|
|
|
|
|
2026-06-03 23:13:33 -04:00
|
|
|
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],
|
2025-03-19 23:14:54 +11:00
|
|
|
);
|
2026-06-03 23:13:33 -04:00
|
|
|
}
|
2025-03-19 23:14:54 +11:00
|
|
|
|
|
|
|
|
type RoomSettingsProps = {
|
|
|
|
|
initialPage?: RoomSettingsPage;
|
|
|
|
|
requestClose: () => void;
|
|
|
|
|
};
|
|
|
|
|
export function RoomSettings({ initialPage, requestClose }: RoomSettingsProps) {
|
|
|
|
|
const room = useRoom();
|
|
|
|
|
const mx = useMatrixClient();
|
|
|
|
|
const useAuthentication = useMediaAuthentication();
|
|
|
|
|
const mDirects = useAtomValue(mDirectAtom);
|
|
|
|
|
|
|
|
|
|
const roomAvatar = useRoomAvatar(room, mDirects.has(room.roomId));
|
|
|
|
|
const roomName = useRoomName(room);
|
|
|
|
|
const joinRuleContent = useRoomJoinRule(room);
|
|
|
|
|
|
|
|
|
|
const avatarUrl = roomAvatar
|
2026-05-21 23:30:50 -04:00
|
|
|
? (mxcUrlToHttp(mx, roomAvatar, useAuthentication, 96, 96, 'crop') ?? undefined)
|
2025-03-19 23:14:54 +11:00
|
|
|
: undefined;
|
|
|
|
|
|
2026-06-03 23:13:33 -04:00
|
|
|
// 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);
|
|
|
|
|
|
2025-03-19 23:14:54 +11:00
|
|
|
const screenSize = useScreenSizeContext();
|
|
|
|
|
const [activePage, setActivePage] = useState<RoomSettingsPage | undefined>(() => {
|
|
|
|
|
if (initialPage) return initialPage;
|
|
|
|
|
return screenSize === ScreenSize.Mobile ? undefined : RoomSettingsPage.GeneralPage;
|
|
|
|
|
});
|
2026-06-03 23:13:33 -04:00
|
|
|
const menuItems = useRoomSettingsMenuItems(canSeeServerACL);
|
2025-03-19 23:14:54 +11:00
|
|
|
|
|
|
|
|
const handlePageRequestClose = () => {
|
|
|
|
|
if (screenSize === ScreenSize.Mobile) {
|
|
|
|
|
setActivePage(undefined);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
requestClose();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<PageRoot
|
|
|
|
|
nav={
|
|
|
|
|
screenSize === ScreenSize.Mobile && activePage !== undefined ? undefined : (
|
|
|
|
|
<PageNav size="300">
|
|
|
|
|
<PageNavHeader outlined={false}>
|
|
|
|
|
<Box grow="Yes" gap="200">
|
|
|
|
|
<Avatar size="200" radii="300">
|
|
|
|
|
<RoomAvatar
|
|
|
|
|
roomId={room.roomId}
|
|
|
|
|
src={avatarUrl}
|
|
|
|
|
alt={roomName}
|
|
|
|
|
renderFallback={() => (
|
|
|
|
|
<RoomIcon
|
|
|
|
|
size="50"
|
2026-03-07 18:03:32 +11:00
|
|
|
roomType={room.getType()}
|
2025-03-19 23:14:54 +11:00
|
|
|
joinRule={joinRuleContent?.join_rule ?? JoinRule.Invite}
|
|
|
|
|
filled
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
/>
|
|
|
|
|
</Avatar>
|
|
|
|
|
<Text size="H4" truncate>
|
|
|
|
|
{roomName}
|
|
|
|
|
</Text>
|
|
|
|
|
</Box>
|
|
|
|
|
<Box shrink="No">
|
|
|
|
|
{screenSize === ScreenSize.Mobile && (
|
2026-05-21 20:49:33 -04:00
|
|
|
<IconButton
|
|
|
|
|
onClick={requestClose}
|
|
|
|
|
variant="Background"
|
|
|
|
|
aria-label="Close settings"
|
|
|
|
|
>
|
2025-03-19 23:14:54 +11:00
|
|
|
<Icon src={Icons.Cross} />
|
|
|
|
|
</IconButton>
|
|
|
|
|
)}
|
|
|
|
|
</Box>
|
|
|
|
|
</PageNavHeader>
|
|
|
|
|
<Box grow="Yes" direction="Column">
|
|
|
|
|
<PageNavContent>
|
|
|
|
|
<div style={{ flexGrow: 1 }}>
|
|
|
|
|
{menuItems.map((item) => (
|
|
|
|
|
<MenuItem
|
|
|
|
|
key={item.name}
|
|
|
|
|
variant="Background"
|
|
|
|
|
radii="400"
|
|
|
|
|
aria-pressed={activePage === item.page}
|
|
|
|
|
before={<Icon src={item.icon} size="100" filled={activePage === item.page} />}
|
|
|
|
|
onClick={() => setActivePage(item.page)}
|
|
|
|
|
>
|
|
|
|
|
<Text
|
|
|
|
|
style={{
|
|
|
|
|
fontWeight: activePage === item.page ? config.fontWeight.W600 : undefined,
|
|
|
|
|
}}
|
|
|
|
|
size="T300"
|
|
|
|
|
truncate
|
|
|
|
|
>
|
|
|
|
|
{item.name}
|
|
|
|
|
</Text>
|
|
|
|
|
</MenuItem>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</PageNavContent>
|
|
|
|
|
</Box>
|
|
|
|
|
</PageNav>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
>
|
|
|
|
|
{activePage === RoomSettingsPage.GeneralPage && (
|
|
|
|
|
<General requestClose={handlePageRequestClose} />
|
|
|
|
|
)}
|
|
|
|
|
{activePage === RoomSettingsPage.MembersPage && (
|
|
|
|
|
<Members requestClose={handlePageRequestClose} />
|
|
|
|
|
)}
|
|
|
|
|
{activePage === RoomSettingsPage.PermissionsPage && (
|
|
|
|
|
<Permissions requestClose={handlePageRequestClose} />
|
|
|
|
|
)}
|
|
|
|
|
{activePage === RoomSettingsPage.EmojisStickersPage && (
|
|
|
|
|
<EmojisStickers requestClose={handlePageRequestClose} />
|
|
|
|
|
)}
|
|
|
|
|
{activePage === RoomSettingsPage.DeveloperToolsPage && (
|
|
|
|
|
<DeveloperTools requestClose={handlePageRequestClose} />
|
|
|
|
|
)}
|
2026-06-03 22:13:22 -04:00
|
|
|
{activePage === RoomSettingsPage.ExportPage && (
|
|
|
|
|
<ExportRoomHistory requestClose={handlePageRequestClose} />
|
|
|
|
|
)}
|
|
|
|
|
{activePage === RoomSettingsPage.ActivityLogPage && (
|
|
|
|
|
<RoomActivityLog requestClose={handlePageRequestClose} />
|
|
|
|
|
)}
|
2026-06-03 23:13:33 -04:00
|
|
|
{activePage === RoomSettingsPage.ServerACLPage && (
|
|
|
|
|
<RoomServerACL requestClose={handlePageRequestClose} />
|
|
|
|
|
)}
|
2025-03-19 23:14:54 +11:00
|
|
|
</PageRoot>
|
|
|
|
|
);
|
|
|
|
|
}
|