chat/src/app/molecules/room-permissions/RoomPermissions.jsx
Ajay Bura 56b754153a
redesigned app settings and switch to rust crypto (#1988)
* rework general settings

* account settings - WIP

* add missing key prop

* add object url hook

* extract wide modal styles

* profile settings and image editor - WIP

* add outline style to upload card

* remove file param from bind upload atom hook

* add compact variant to upload card

* add  compact upload card renderer

* add option to update profile avatar

* add option to change profile displayname

* allow displayname change based on capabilities check

* rearrange settings components into folders

* add system notification settings

* add initial page param in settings

* convert account data hook to typescript

* add push rule hook

* add notification mode hook

* add notification mode switcher component

* add all messages notification settings options

* add special messages notification settings

* add keyword notifications

* add ignored users section

* improve ignore user list strings

* add about settings

* add access token option in about settings

* add developer tools settings

* add expand button to account data dev tool option

* update folds

* fix editable active element textarea check

* do not close dialog when editable element in focus

* add text area plugins

* add text area intent handler hook

* add newline intent mod in text area

* add next line hotkey in text area intent hook

* add syntax error position dom utility function

* add account data editor

* add button to send new account data in dev tools

* improve custom emoji plugin

* add more custom emojis hooks

* add text util css

* add word break in setting tile title and description

* emojis and sticker user settings - WIP

* view image packs from settings

* emoji pack editing - WIP

* add option to edit pack meta

* change saved changes message

* add image edit and delete controls

* add option to upload pack images and apply changes

* fix state event type when updating image pack

* lazy load pack image tile img

* hide upload image button when user can not edit pack

* add option to add or remove global image packs

* upgrade to rust crypto (#2168)

* update matrix js sdk

* remove dead code

* use rust crypto

* update setPowerLevel usage

* fix types

* fix deprecated isRoomEncrypted method uses

* fix deprecated room.currentState uses

* fix deprecated import/export room keys func

* fix merge issues in image pack file

* fix remaining issues in image pack file

* start indexedDBStore

* update package lock and vite-plugin-top-level-await

* user session settings - WIP

* add useAsync hook

* add password stage uia

* add uia flow matrix error hook

* add UIA action component

* add options to delete sessions

* add sso uia stage

* fix SSO stage complete error

* encryption - WIP

* update user settings encryption terminology

* add default variant to password input

* use password input in uia password stage

* add options for local backup in user settings

* remove typo in import local backup password input label

* online backup - WIP

* fix uia sso action

* move access token settings from about to developer tools

* merge encryption tab into sessions and rename it to devices

* add device placeholder tile

* add logout dialog

* add logout button for current device

* move other devices in component

* render unverified device verification tile

* add learn more section for current device verification

* add device verification status badge

* add info card component

* add index file for password input component

* add types for secret storage

* add component to access secret storage key

* manual verification - WIP

* update matrix-js-sdk to v35

* add manual verification

* use react query for device list

* show unverified tab on sidebar

* fix device list updates

* add session key details to current device

* render restore encryption backup

* fix loading state of restore backup

* fix unverified tab settings closes after verification

* key backup tile - WIP

* fix unverified tab badge

* rename session key to device key in device tile

* improve backup restore functionality

* fix restore button enabled after layout reload during restoring backup

* update backup info on status change

* add backup disconnection failures

* add device verification using sas

* restore backup after verification

* show option to logout on startup error screen

* fix key backup hook update on decryption key cached

* add option to enable device verification

* add device verification reset dialog

* add logout button in settings drawer

* add encrypted message lost on logout

* fix backup restore never finish with 0 keys

* fix setup dialog hides when enabling device verification

* show backup details in menu

* update setup device verification body copy

* replace deprecated method

* fix displayname appear as mxid in settings

* remove old refactored codes

* fix types
2025-02-10 16:49:47 +11:00

284 lines
8.9 KiB
JavaScript

import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import './RoomPermissions.scss';
import { EventTimeline } from 'matrix-js-sdk';
import { getPowerLabel } from '../../../util/matrixUtil';
import { openReusableContextMenu } from '../../../client/action/navigation';
import { getEventCords } from '../../../util/common';
import Text from '../../atoms/text/Text';
import Button from '../../atoms/button/Button';
import { MenuHeader } from '../../atoms/context-menu/ContextMenu';
import PowerLevelSelector from '../power-level-selector/PowerLevelSelector';
import SettingTile from '../setting-tile/SettingTile';
import ChevronBottomIC from '../../../../public/res/ic/outlined/chevron-bottom.svg';
import { useForceUpdate } from '../../hooks/useForceUpdate';
import { useMatrixClient } from '../../hooks/useMatrixClient';
import { getStateEvent } from '../../utils/room';
const permissionsInfo = {
users_default: {
name: 'Default role',
description: 'Set default role for all members.',
default: 0,
},
events_default: {
name: 'Send messages',
description: 'Set minimum power level to send messages in room.',
default: 0,
},
'm.reaction': {
parent: 'events',
name: 'Send reactions',
description: 'Set minimum power level to send reactions in room.',
default: 0,
},
redact: {
name: 'Delete messages sent by others',
description: 'Set minimum power level to delete messages in room.',
default: 50,
},
notifications: {
name: 'Ping room',
description: 'Set minimum power level to ping room.',
default: {
room: 50,
},
},
'm.space.child': {
parent: 'events',
name: 'Manage rooms in space',
description: 'Set minimum power level to manage rooms in space.',
default: 50,
},
invite: {
name: 'Invite',
description: 'Set minimum power level to invite members.',
default: 50,
},
kick: {
name: 'Kick',
description: 'Set minimum power level to kick members.',
default: 50,
},
ban: {
name: 'Ban',
description: 'Set minimum power level to ban members.',
default: 50,
},
'm.room.avatar': {
parent: 'events',
name: 'Change avatar',
description: 'Set minimum power level to change room/space avatar.',
default: 50,
},
'm.room.name': {
parent: 'events',
name: 'Change name',
description: 'Set minimum power level to change room/space name.',
default: 50,
},
'm.room.topic': {
parent: 'events',
name: 'Change topic',
description: 'Set minimum power level to change room/space topic.',
default: 50,
},
state_default: {
name: 'Change settings',
description: 'Set minimum power level to change settings.',
default: 50,
},
'm.room.canonical_alias': {
parent: 'events',
name: 'Change published address',
description: 'Set minimum power level to publish and set main address.',
default: 50,
},
'm.room.power_levels': {
parent: 'events',
name: 'Change permissions',
description: 'Set minimum power level to change permissions.',
default: 50,
},
'm.room.encryption': {
parent: 'events',
name: 'Enable room encryption',
description: 'Set minimum power level to enable room encryption.',
default: 50,
},
'm.room.history_visibility': {
parent: 'events',
name: 'Change history visibility',
description: 'Set minimum power level to change room messages history visibility.',
default: 50,
},
'm.room.tombstone': {
parent: 'events',
name: 'Upgrade room',
description: 'Set minimum power level to upgrade room.',
default: 50,
},
'm.room.pinned_events': {
parent: 'events',
name: 'Pin messages',
description: 'Set minimum power level to pin messages in room.',
default: 50,
},
'm.room.server_acl': {
parent: 'events',
name: 'Change server ACLs',
description: 'Set minimum power level to change server ACLs.',
default: 50,
},
'im.vector.modular.widgets': {
parent: 'events',
name: 'Modify widgets',
description: 'Set minimum power level to modify room widgets.',
default: 50,
},
};
const roomPermsGroups = {
'General Permissions': ['users_default', 'events_default', 'm.reaction', 'redact', 'notifications'],
'Manage members permissions': ['invite', 'kick', 'ban'],
'Room profile permissions': ['m.room.avatar', 'm.room.name', 'm.room.topic'],
'Settings permissions': ['state_default', 'm.room.canonical_alias', 'm.room.power_levels', 'm.room.encryption', 'm.room.history_visibility'],
'Other permissions': ['m.room.tombstone', 'm.room.pinned_events', 'm.room.server_acl', 'im.vector.modular.widgets'],
};
const spacePermsGroups = {
'General Permissions': ['users_default', 'm.space.child'],
'Manage members permissions': ['invite', 'kick', 'ban'],
'Space profile permissions': ['m.room.avatar', 'm.room.name', 'm.room.topic'],
'Settings permissions': ['state_default', 'm.room.canonical_alias', 'm.room.power_levels'],
};
function useRoomStateUpdate(roomId) {
const [, forceUpdate] = useForceUpdate();
const mx = useMatrixClient();
useEffect(() => {
const handleStateEvent = (event) => {
if (event.getRoomId() !== roomId) return;
forceUpdate();
};
mx.on('RoomState.events', handleStateEvent);
return () => {
mx.removeListener('RoomState.events', handleStateEvent);
};
}, [mx, roomId]);
}
function RoomPermissions({ roomId }) {
useRoomStateUpdate(roomId);
const mx = useMatrixClient();
const room = mx.getRoom(roomId);
const pLEvent = getStateEvent(room, 'm.room.power_levels');
const permissions = pLEvent.getContent();
const canChangePermission = room.getLiveTimeline().getState(EventTimeline.FORWARDS)?.maySendStateEvent('m.room.power_levels', mx.getUserId());
const myPowerLevel = room.getMember(mx.getUserId())?.powerLevel ?? 100;
const handlePowerSelector = (e, permKey, parentKey, powerLevel) => {
const handlePowerLevelChange = (newPowerLevel) => {
if (powerLevel === newPowerLevel) return;
const newPermissions = { ...permissions };
if (parentKey) {
newPermissions[parentKey] = {
...permissions[parentKey],
[permKey]: newPowerLevel,
};
} else if (permKey === 'notifications') {
newPermissions[permKey] = {
...permissions[permKey],
room: newPowerLevel,
};
} else {
newPermissions[permKey] = newPowerLevel;
}
mx.sendStateEvent(roomId, 'm.room.power_levels', newPermissions);
};
openReusableContextMenu(
'bottom',
getEventCords(e, '.btn-surface'),
(closeMenu) => (
<PowerLevelSelector
value={powerLevel}
max={myPowerLevel}
onSelect={(pl) => {
closeMenu();
handlePowerLevelChange(pl);
}}
/>
),
);
};
const permsGroups = room.isSpaceRoom() ? spacePermsGroups : roomPermsGroups;
return (
<div className="room-permissions">
{
Object.keys(permsGroups).map((groupKey) => {
const groupedPermKeys = permsGroups[groupKey];
return (
<div className="room-permissions__card" key={groupKey}>
<MenuHeader>{groupKey}</MenuHeader>
{
groupedPermKeys.map((permKey) => {
const permInfo = permissionsInfo[permKey];
let powerLevel = 0;
let permValue = permInfo.parent
? permissions[permInfo.parent]?.[permKey]
: permissions[permKey];
if (permValue === undefined) permValue = permInfo.default;
if (typeof permValue === 'number') {
powerLevel = permValue;
} else if (permKey === 'notifications') {
powerLevel = permValue.room ?? 50;
}
return (
<SettingTile
key={permKey}
title={permInfo.name}
content={<Text variant="b3">{permInfo.description}</Text>}
options={(
<Button
onClick={
canChangePermission
? (e) => handlePowerSelector(e, permKey, permInfo.parent, powerLevel)
: null
}
iconSrc={canChangePermission ? ChevronBottomIC : null}
>
<Text variant="b2">
{`${getPowerLabel(powerLevel) || 'Member'} - ${powerLevel}`}
</Text>
</Button>
)}
/>
);
})
}
</div>
);
})
}
</div>
);
}
RoomPermissions.propTypes = {
roomId: PropTypes.string.isRequired,
};
export default RoomPermissions;