Merge branch 'dev' into voicemail

This commit is contained in:
C0ffeeCode 2022-01-14 20:16:54 +01:00 committed by GitHub
commit 6dc7cf9af5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 464 additions and 237 deletions

View file

@ -24,11 +24,12 @@ You can serve the application with a webserver of your choosing by simply copyin
Execute the following commands to compile the app from its source code: Execute the following commands to compile the app from its source code:
```sh ```sh
npm install # Installs all dependencies npm ci # Installs all dependencies
npm run build # Compiles the app into the dist/ directory npm run build # Compiles the app into the dist/ directory
``` ```
You can then copy the files to a webserver's webroot of your choice. You can then copy the files to a webserver's webroot of your choice.
To serve a development version of the app locally for testing, you may also use the command `npm start`. To serve a development version of the app locally for testing, you may also use the command `npm start`.
### Running with Docker ### Running with Docker

View file

@ -23,12 +23,14 @@ function ReusableContextMenu() {
openerRef.current.style.height = `${cords.height}px`; openerRef.current.style.height = `${cords.height}px`;
openerRef.current.click(); openerRef.current.click();
} }
const handleContextMenuOpen = (placement, cords, render) => { const handleContextMenuOpen = (placement, cords, render, afterClose) => {
if (key) { if (key) {
closeMenu(); closeMenu();
return; return;
} }
setData({ placement, cords, render }); setData({
placement, cords, render, afterClose,
});
}; };
navigation.on(cons.events.navigation.REUSABLE_CONTEXT_MENU_OPENED, handleContextMenuOpen); navigation.on(cons.events.navigation.REUSABLE_CONTEXT_MENU_OPENED, handleContextMenuOpen);
return () => { return () => {
@ -44,6 +46,7 @@ function ReusableContextMenu() {
key = Math.random(); key = Math.random();
return; return;
} }
data?.afterClose?.();
if (setData) setData(null); if (setData) setData(null);
if (key === null) return; if (key === null) return;

View file

@ -303,23 +303,40 @@
// markdown formating // markdown formating
.message__body { .message__body {
& h1, h2, h3, h4, h5, h6 {
margin: 0;
margin-bottom: var(--sp-ultra-tight);
font-weight: var(--fw-medium);
&:first-child {
margin-top: 0;
}
&:last-child {
margin-bottom: 0;
}
}
& h1, & h1,
& h2 { & h2 {
color: var(--tc-surface-high); color: var(--tc-surface-high);
margin: var(--sp-loose) 0 var(--sp-normal); margin-top: var(--sp-normal);
line-height: var(--lh-h1); font-size: var(--fs-h2);
line-height: var(--lh-h2);
letter-spacing: var(--ls-h2);
} }
& h3, & h3,
& h4 { & h4 {
color: var(--tc-surface-high); color: var(--tc-surface-high);
margin: var(--sp-normal) 0 var(--sp-tight); margin-top: var(--sp-tight);
line-height: var(--lh-h2); font-size: var(--fs-s1);
line-height: var(--lh-s1);
letter-spacing: var(--ls-s1);
} }
& h5, & h5,
& h6 { & h6 {
color: var(--tc-surface-high); color: var(--tc-surface-high);
margin: var(--sp-tight) 0 var(--sp-extra-tight); margin-top: var(--sp-extra-tight);
line-height: var(--lh-s1); font-size: var(--fs-b1);
line-height: var(--lh-b1);
letter-spacing: var(--ls-b1);
} }
& hr { & hr {
border-color: var(--bg-divider); border-color: var(--bg-divider);
@ -365,7 +382,7 @@
@include scrollbar.scroll--auto-hide; @include scrollbar.scroll--auto-hide;
} }
& pre { & pre {
display: inline-block; width: fit-content;
max-width: 100%; max-width: 100%;
@include scrollbar.scroll; @include scrollbar.scroll;
@include scrollbar.scroll__h; @include scrollbar.scroll__h;
@ -376,7 +393,7 @@
} }
} }
& blockquote { & blockquote {
display: inline-block; width: fit-content;
max-width: 100%; max-width: 100%;
@include dir.side(border, 4px solid var(--bg-surface-active), 0); @include dir.side(border, 4px solid var(--bg-surface-active), 0);
white-space: initial !important; white-space: initial !important;

View file

@ -11,7 +11,7 @@ function PowerLevelSelector({
value, max, onSelect, value, max, onSelect,
}) { }) {
const handleSubmit = (e) => { const handleSubmit = (e) => {
const powerLevel = e.target.elements['power-level']; const powerLevel = e.target.elements['power-level']?.value;
if (!powerLevel) return; if (!powerLevel) return;
onSelect(Number(powerLevel)); onSelect(Number(powerLevel));
}; };

View file

@ -0,0 +1,67 @@
import React from 'react';
import PropTypes from 'prop-types';
import { twemojify } from '../../../util/twemojify';
import initMatrix from '../../../client/initMatrix';
import { openInviteUser } from '../../../client/action/navigation';
import * as roomActions from '../../../client/action/room';
import { MenuHeader, MenuItem } from '../../atoms/context-menu/ContextMenu';
import RoomNotification from '../room-notification/RoomNotification';
import TickMarkIC from '../../../../public/res/ic/outlined/tick-mark.svg';
import AddUserIC from '../../../../public/res/ic/outlined/add-user.svg';
import LeaveArrowIC from '../../../../public/res/ic/outlined/leave-arrow.svg';
function RoomOptions({ roomId, afterOptionSelect }) {
const mx = initMatrix.matrixClient;
const room = mx.getRoom(roomId);
const canInvite = room?.canInvite(mx.getUserId());
const handleMarkAsRead = () => {
afterOptionSelect();
if (!room) return;
const events = room.getLiveTimeline().getEvents();
mx.sendReadReceipt(events[events.length - 1]);
};
const handleInviteClick = () => {
openInviteUser(roomId);
afterOptionSelect();
};
const handleLeaveClick = () => {
if (confirm('Are you really want to leave this room?')) {
roomActions.leave(roomId);
afterOptionSelect();
}
};
return (
<>
<MenuHeader>{twemojify(`Options for ${initMatrix.matrixClient.getRoom(roomId)?.name}`)}</MenuHeader>
<MenuItem iconSrc={TickMarkIC} onClick={handleMarkAsRead}>Mark as read</MenuItem>
<MenuItem
iconSrc={AddUserIC}
onClick={handleInviteClick}
disabled={!canInvite}
>
Invite
</MenuItem>
<MenuItem iconSrc={LeaveArrowIC} variant="danger" onClick={handleLeaveClick}>Leave</MenuItem>
<MenuHeader>Notification</MenuHeader>
<RoomNotification roomId={roomId} />
</>
);
}
RoomOptions.defaultProps = {
afterOptionSelect: null,
};
RoomOptions.propTypes = {
roomId: PropTypes.string.isRequired,
afterOptionSelect: PropTypes.func,
};
export default RoomOptions;

View file

@ -1,17 +1,22 @@
import React from 'react'; import React, { useEffect } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './RoomPermissions.scss'; import './RoomPermissions.scss';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
import { getPowerLabel } from '../../../util/matrixUtil'; import { getPowerLabel } from '../../../util/matrixUtil';
import { openReusableContextMenu } from '../../../client/action/navigation';
import { getEventCords } from '../../../util/common';
import Text from '../../atoms/text/Text'; import Text from '../../atoms/text/Text';
import Button from '../../atoms/button/Button'; import Button from '../../atoms/button/Button';
import { MenuHeader } from '../../atoms/context-menu/ContextMenu'; import { MenuHeader } from '../../atoms/context-menu/ContextMenu';
import PowerLevelSelector from '../power-level-selector/PowerLevelSelector';
import SettingTile from '../setting-tile/SettingTile'; import SettingTile from '../setting-tile/SettingTile';
import ChevronBottomIC from '../../../../public/res/ic/outlined/chevron-bottom.svg'; import ChevronBottomIC from '../../../../public/res/ic/outlined/chevron-bottom.svg';
import { useForceUpdate } from '../../hooks/useForceUpdate';
const permissionsInfo = { const permissionsInfo = {
users_default: { users_default: {
name: 'Default role', name: 'Default role',
@ -23,6 +28,12 @@ const permissionsInfo = {
description: 'Set minimum power level to send messages in room.', description: 'Set minimum power level to send messages in room.',
default: 0, default: 0,
}, },
'm.reaction': {
parent: 'events',
name: 'Send reactions',
description: 'Set minimum power level to send reactions in room.',
default: 0,
},
redact: { redact: {
name: 'Delete messages sent by others', name: 'Delete messages sent by others',
description: 'Set minimum power level to delete messages in room.', description: 'Set minimum power level to delete messages in room.',
@ -130,7 +141,7 @@ const permissionsInfo = {
}; };
const roomPermsGroups = { const roomPermsGroups = {
'General Permissions': ['users_default', 'events_default', 'redact', 'notifications'], 'General Permissions': ['users_default', 'events_default', 'm.reaction', 'redact', 'notifications'],
'Manage members permissions': ['invite', 'kick', 'ban'], 'Manage members permissions': ['invite', 'kick', 'ban'],
'Room profile permissions': ['m.room.avatar', 'm.room.name', 'm.room.topic'], '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'], 'Settings permissions': ['state_default', 'm.room.canonical_alias', 'm.room.power_levels', 'm.room.encryption', 'm.room.history_visibility'],
@ -144,13 +155,69 @@ const spacePermsGroups = {
'Settings permissions': ['state_default', 'm.room.canonical_alias', 'm.room.power_levels'], 'Settings permissions': ['state_default', 'm.room.canonical_alias', 'm.room.power_levels'],
}; };
function useRoomStateUpdate(roomId) {
const [, forceUpdate] = useForceUpdate();
const mx = initMatrix.matrixClient;
useEffect(() => {
const handleStateEvent = (event) => {
if (event.getRoomId() !== roomId) return;
forceUpdate();
};
mx.on('RoomState.events', handleStateEvent);
return () => {
mx.removeListener('RoomState.events', handleStateEvent);
};
}, [roomId]);
}
function RoomPermissions({ roomId }) { function RoomPermissions({ roomId }) {
useRoomStateUpdate(roomId);
const mx = initMatrix.matrixClient; const mx = initMatrix.matrixClient;
const room = mx.getRoom(roomId); const room = mx.getRoom(roomId);
const pLEvent = room.currentState.getStateEvents('m.room.power_levels')[0]; const pLEvent = room.currentState.getStateEvents('m.room.power_levels')[0];
const permissions = pLEvent.getContent(); const permissions = pLEvent.getContent();
const canChangePermission = room.currentState.maySendStateEvent('m.room.power_levels', mx.getUserId()); const canChangePermission = room.currentState.maySendStateEvent('m.room.power_levels', mx.getUserId());
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={100}
onSelect={(pl) => {
closeMenu();
handlePowerLevelChange(pl);
}}
/>
),
);
};
return ( return (
<div className="room-permissions"> <div className="room-permissions">
{ {
@ -182,6 +249,11 @@ function RoomPermissions({ roomId }) {
content={<Text variant="b3">{permInfo.description}</Text>} content={<Text variant="b3">{permInfo.description}</Text>}
options={( options={(
<Button <Button
onClick={
canChangePermission
? (e) => handlePowerSelector(e, permKey, permInfo.parent, powerLevel)
: null
}
iconSrc={canChangePermission ? ChevronBottomIC : null} iconSrc={canChangePermission ? ChevronBottomIC : null}
> >
<Text variant="b2"> <Text variant="b2">

View file

@ -11,7 +11,8 @@ import NotificationBadge from '../../atoms/badge/NotificationBadge';
import { blurOnBubbling } from '../../atoms/button/script'; import { blurOnBubbling } from '../../atoms/button/script';
function RoomSelectorWrapper({ function RoomSelectorWrapper({
isSelected, isUnread, onClick, content, options, isSelected, isUnread, onClick,
content, options, onContextMenu,
}) { }) {
let myClass = isUnread ? ' room-selector--unread' : ''; let myClass = isUnread ? ' room-selector--unread' : '';
myClass += isSelected ? ' room-selector--selected' : ''; myClass += isSelected ? ' room-selector--selected' : '';
@ -22,6 +23,7 @@ function RoomSelectorWrapper({
type="button" type="button"
onClick={onClick} onClick={onClick}
onMouseUp={(e) => blurOnBubbling(e, '.room-selector__content')} onMouseUp={(e) => blurOnBubbling(e, '.room-selector__content')}
onContextMenu={onContextMenu}
> >
{content} {content}
</button> </button>
@ -31,6 +33,7 @@ function RoomSelectorWrapper({
} }
RoomSelectorWrapper.defaultProps = { RoomSelectorWrapper.defaultProps = {
options: null, options: null,
onContextMenu: null,
}; };
RoomSelectorWrapper.propTypes = { RoomSelectorWrapper.propTypes = {
isSelected: PropTypes.bool.isRequired, isSelected: PropTypes.bool.isRequired,
@ -38,12 +41,13 @@ RoomSelectorWrapper.propTypes = {
onClick: PropTypes.func.isRequired, onClick: PropTypes.func.isRequired,
content: PropTypes.node.isRequired, content: PropTypes.node.isRequired,
options: PropTypes.node, options: PropTypes.node,
onContextMenu: PropTypes.func,
}; };
function RoomSelector({ function RoomSelector({
name, parentName, roomId, imageSrc, iconSrc, name, parentName, roomId, imageSrc, iconSrc,
isSelected, isUnread, notificationCount, isAlert, isSelected, isUnread, notificationCount, isAlert,
options, onClick, options, onClick, onContextMenu,
}) { }) {
return ( return (
<RoomSelectorWrapper <RoomSelectorWrapper
@ -78,6 +82,7 @@ function RoomSelector({
)} )}
options={options} options={options}
onClick={onClick} onClick={onClick}
onContextMenu={onContextMenu}
/> />
); );
} }
@ -87,6 +92,7 @@ RoomSelector.defaultProps = {
imageSrc: null, imageSrc: null,
iconSrc: null, iconSrc: null,
options: null, options: null,
onContextMenu: null,
}; };
RoomSelector.propTypes = { RoomSelector.propTypes = {
name: PropTypes.string.isRequired, name: PropTypes.string.isRequired,
@ -103,6 +109,7 @@ RoomSelector.propTypes = {
isAlert: PropTypes.bool.isRequired, isAlert: PropTypes.bool.isRequired,
options: PropTypes.node, options: PropTypes.node,
onClick: PropTypes.func.isRequired, onClick: PropTypes.func.isRequired,
onContextMenu: PropTypes.func,
}; };
export default RoomSelector; export default RoomSelector;

View file

@ -4,12 +4,13 @@ import PropTypes from 'prop-types';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
import navigation from '../../../client/state/navigation'; import navigation from '../../../client/state/navigation';
import { openRoomOptions } from '../../../client/action/navigation'; import { openReusableContextMenu } from '../../../client/action/navigation';
import { createSpaceShortcut, deleteSpaceShortcut } from '../../../client/action/room'; import { createSpaceShortcut, deleteSpaceShortcut } from '../../../client/action/room';
import { getEventCords, abbreviateNumber } from '../../../util/common'; import { getEventCords, abbreviateNumber } from '../../../util/common';
import IconButton from '../../atoms/button/IconButton'; import IconButton from '../../atoms/button/IconButton';
import RoomSelector from '../../molecules/room-selector/RoomSelector'; import RoomSelector from '../../molecules/room-selector/RoomSelector';
import RoomOptions from '../../molecules/room-optons/RoomOptions';
import HashIC from '../../../../public/res/ic/outlined/hash.svg'; import HashIC from '../../../../public/res/ic/outlined/hash.svg';
import HashGlobeIC from '../../../../public/res/ic/outlined/hash-globe.svg'; import HashGlobeIC from '../../../../public/res/ic/outlined/hash-globe.svg';
@ -49,6 +50,15 @@ function Selector({
}; };
}, []); }, []);
const openRoomOptions = (e) => {
e.preventDefault();
openReusableContextMenu(
'right',
getEventCords(e, '.room-selector'),
(closeMenu) => <RoomOptions roomId={roomId} afterOptionSelect={closeMenu} />,
);
};
const joinRuleToIconSrc = (joinRule) => ({ const joinRuleToIconSrc = (joinRule) => ({
restricted: () => (room.isSpaceRoom() ? SpaceIC : HashIC), restricted: () => (room.isSpaceRoom() ? SpaceIC : HashIC),
invite: () => (room.isSpaceRoom() ? SpaceLockIC : HashLockIC), invite: () => (room.isSpaceRoom() ? SpaceLockIC : HashLockIC),
@ -96,13 +106,14 @@ function Selector({
notificationCount={abbreviateNumber(noti.getTotalNoti(roomId))} notificationCount={abbreviateNumber(noti.getTotalNoti(roomId))}
isAlert={noti.getHighlightNoti(roomId) !== 0} isAlert={noti.getHighlightNoti(roomId) !== 0}
onClick={onClick} onClick={onClick}
onContextMenu={openRoomOptions}
options={( options={(
<IconButton <IconButton
size="extra-small" size="extra-small"
tooltip="Options" tooltip="Options"
tooltipPlacement="right" tooltipPlacement="right"
src={VerticalMenuIC} src={VerticalMenuIC}
onClick={(e) => openRoomOptions(getEventCords(e), roomId)} onClick={openRoomOptions}
/> />
)} )}
/> />

View file

@ -7,26 +7,87 @@ import { twemojify } from '../../../util/twemojify';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
import cons from '../../../client/state/cons'; import cons from '../../../client/state/cons';
import navigation from '../../../client/state/navigation'; import navigation from '../../../client/state/navigation';
import { selectRoom } from '../../../client/action/navigation'; import { selectRoom, openReusableContextMenu } from '../../../client/action/navigation';
import * as roomActions from '../../../client/action/room'; import * as roomActions from '../../../client/action/room';
import { getUsername, getUsernameOfRoomMember, getPowerLabel } from '../../../util/matrixUtil'; import { getUsername, getUsernameOfRoomMember, getPowerLabel } from '../../../util/matrixUtil';
import { getEventCords } from '../../../util/common';
import colorMXID from '../../../util/colorMXID'; import colorMXID from '../../../util/colorMXID';
import Text from '../../atoms/text/Text'; import Text from '../../atoms/text/Text';
import Chip from '../../atoms/chip/Chip'; import Chip from '../../atoms/chip/Chip';
import IconButton from '../../atoms/button/IconButton'; import IconButton from '../../atoms/button/IconButton';
import Input from '../../atoms/input/Input';
import Avatar from '../../atoms/avatar/Avatar'; import Avatar from '../../atoms/avatar/Avatar';
import Button from '../../atoms/button/Button'; import Button from '../../atoms/button/Button';
import { MenuItem } from '../../atoms/context-menu/ContextMenu';
import PowerLevelSelector from '../../molecules/power-level-selector/PowerLevelSelector';
import Dialog from '../../molecules/dialog/Dialog'; import Dialog from '../../molecules/dialog/Dialog';
import SettingTile from '../../molecules/setting-tile/SettingTile';
import ShieldEmptyIC from '../../../../public/res/ic/outlined/shield-empty.svg'; import ShieldEmptyIC from '../../../../public/res/ic/outlined/shield-empty.svg';
import ChevronRightIC from '../../../../public/res/ic/outlined/chevron-right.svg';
import ChevronBottomIC from '../../../../public/res/ic/outlined/chevron-bottom.svg'; import ChevronBottomIC from '../../../../public/res/ic/outlined/chevron-bottom.svg';
import CrossIC from '../../../../public/res/ic/outlined/cross.svg'; import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
import { useForceUpdate } from '../../hooks/useForceUpdate';
function ModerationTools({
roomId, userId,
}) {
const mx = initMatrix.matrixClient;
const room = mx.getRoom(roomId);
const roomMember = room.getMember(userId);
const myPowerLevel = room.getMember(mx.getUserId()).powerLevel;
const powerLevel = roomMember?.powerLevel || 0;
const canIKick = (
roomMember?.membership === 'join'
&& room.currentState.hasSufficientPowerLevelFor('kick', myPowerLevel)
&& powerLevel < myPowerLevel
);
const canIBan = (
['join', 'leave'].includes(roomMember?.membership)
&& room.currentState.hasSufficientPowerLevelFor('ban', myPowerLevel)
&& powerLevel < myPowerLevel
);
const handleKick = (e) => {
e.preventDefault();
const kickReason = e.target.elements['kick-reason']?.value.trim();
roomActions.kick(roomId, userId, kickReason !== '' ? kickReason : undefined);
};
const handleBan = (e) => {
e.preventDefault();
const banReason = e.target.elements['ban-reason']?.value.trim();
roomActions.ban(roomId, userId, banReason !== '' ? banReason : undefined);
};
return (
<div className="moderation-tools">
{canIKick && (
<form onSubmit={handleKick}>
<Input label="Kick reason" name="kick-reason" />
<Button type="submit">Kick</Button>
</form>
)}
{canIBan && (
<form onSubmit={handleBan}>
<Input label="Ban reason" name="ban-reason" />
<Button type="submit">Ban</Button>
</form>
)}
</div>
);
}
ModerationTools.propTypes = {
roomId: PropTypes.string.isRequired,
userId: PropTypes.string.isRequired,
};
function SessionInfo({ userId }) { function SessionInfo({ userId }) {
const [devices, setDevices] = useState(null); const [devices, setDevices] = useState(null);
const [isVisible, setIsVisible] = useState(false);
const mx = initMatrix.matrixClient; const mx = initMatrix.matrixClient;
useEffect(() => { useEffect(() => {
@ -51,10 +112,11 @@ function SessionInfo({ userId }) {
}, [userId]); }, [userId]);
function renderSessionChips() { function renderSessionChips() {
if (!isVisible) return null;
return ( return (
<div className="session-info__chips"> <div className="session-info__chips">
{devices === null && <Text variant="b3">Loading sessions...</Text>} {devices === null && <Text variant="b2">Loading sessions...</Text>}
{devices?.length === 0 && <Text variant="b3">No session found.</Text>} {devices?.length === 0 && <Text variant="b2">No session found.</Text>}
{devices !== null && (devices.map((device) => ( {devices !== null && (devices.map((device) => (
<Chip <Chip
key={device.deviceId} key={device.deviceId}
@ -68,10 +130,13 @@ function SessionInfo({ userId }) {
return ( return (
<div className="session-info"> <div className="session-info">
<SettingTile <MenuItem
title="Sessions" onClick={() => setIsVisible(!isVisible)}
content={renderSessionChips()} iconSrc={isVisible ? ChevronBottomIC : ChevronRightIC}
/> >
<Text variant="b2">{`View ${devices?.length > 0 ? `${devices.length} ` : ''}sessions`}</Text>
</MenuItem>
{renderSessionChips()}
</div> </div>
); );
} }
@ -98,6 +163,8 @@ function ProfileFooter({ roomId, userId, onRequestClose }) {
const userPL = room.getMember(userId)?.powerLevel || 0; const userPL = room.getMember(userId)?.powerLevel || 0;
const canIKick = room.currentState.hasSufficientPowerLevelFor('kick', myPowerlevel) && userPL < myPowerlevel; const canIKick = room.currentState.hasSufficientPowerLevelFor('kick', myPowerlevel) && userPL < myPowerlevel;
const isBanned = member?.membership === 'ban';
const onCreated = (dmRoomId) => { const onCreated = (dmRoomId) => {
if (isMountedRef.current === false) return; if (isMountedRef.current === false) return;
setIsCreatingDM(false); setIsCreatingDM(false);
@ -119,7 +186,7 @@ function ProfileFooter({ roomId, userId, onRequestClose }) {
setIsInviting(false); setIsInviting(false);
}, [userId]); }, [userId]);
async function openDM() { const openDM = async () => {
const directIds = [...initMatrix.roomList.directs]; const directIds = [...initMatrix.roomList.directs];
// Check and open if user already have a DM with userId. // Check and open if user already have a DM with userId.
@ -145,9 +212,9 @@ function ProfileFooter({ roomId, userId, onRequestClose }) {
if (isMountedRef.current === false) return; if (isMountedRef.current === false) return;
setIsCreatingDM(false); setIsCreatingDM(false);
} }
} };
async function toggleIgnore() { const toggleIgnore = async () => {
const ignoredUsers = mx.getIgnoredUsers(); const ignoredUsers = mx.getIgnoredUsers();
const uIndex = ignoredUsers.indexOf(userId); const uIndex = ignoredUsers.indexOf(userId);
if (uIndex >= 0) { if (uIndex >= 0) {
@ -165,9 +232,9 @@ function ProfileFooter({ roomId, userId, onRequestClose }) {
} catch { } catch {
setIsIgnoring(false); setIsIgnoring(false);
} }
} };
async function toggleInvite() { const toggleInvite = async () => {
try { try {
setIsInviting(true); setIsInviting(true);
let isInviteSent = false; let isInviteSent = false;
@ -182,7 +249,7 @@ function ProfileFooter({ roomId, userId, onRequestClose }) {
} catch { } catch {
setIsInviting(false); setIsInviting(false);
} }
} };
return ( return (
<div className="profile-viewer__buttons"> <div className="profile-viewer__buttons">
@ -193,7 +260,14 @@ function ProfileFooter({ roomId, userId, onRequestClose }) {
> >
{isCreatingDM ? 'Creating room...' : 'Message'} {isCreatingDM ? 'Creating room...' : 'Message'}
</Button> </Button>
{ member?.membership === 'join' && <Button>Mention</Button>} { isBanned && canIKick && (
<Button
variant="positive"
onClick={() => roomActions.unban(roomId, userId)}
>
Unban
</Button>
)}
{ (isInvited ? canIKick : room.canInvite(mx.getUserId())) && isInvitable && ( { (isInvited ? canIKick : room.canInvite(mx.getUserId())) && isInvitable && (
<Button <Button
onClick={toggleInvite} onClick={toggleInvite}
@ -226,84 +300,137 @@ ProfileFooter.propTypes = {
onRequestClose: PropTypes.func.isRequired, onRequestClose: PropTypes.func.isRequired,
}; };
function ProfileViewer() { function useToggleDialog() {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const [roomId, setRoomId] = useState(null); const [roomId, setRoomId] = useState(null);
const [userId, setUserId] = useState(null); const [userId, setUserId] = useState(null);
const mx = initMatrix.matrixClient; useEffect(() => {
const room = roomId ? mx.getRoom(roomId) : null; const loadProfile = (uId, rId) => {
let username = '';
if (room !== null) {
const roomMember = room.getMember(userId);
if (roomMember) username = getUsernameOfRoomMember(roomMember);
else username = getUsername(userId);
}
function loadProfile(uId, rId) {
setIsOpen(true); setIsOpen(true);
setUserId(uId); setUserId(uId);
setRoomId(rId); setRoomId(rId);
} };
useEffect(() => {
navigation.on(cons.events.navigation.PROFILE_VIEWER_OPENED, loadProfile); navigation.on(cons.events.navigation.PROFILE_VIEWER_OPENED, loadProfile);
return () => { return () => {
navigation.removeListener(cons.events.navigation.PROFILE_VIEWER_OPENED, loadProfile); navigation.removeListener(cons.events.navigation.PROFILE_VIEWER_OPENED, loadProfile);
}; };
}, []); }, []);
const handleAfterClose = () => { const closeDialog = () => setIsOpen(false);
const afterClose = () => {
setUserId(null); setUserId(null);
setRoomId(null); setRoomId(null);
}; };
function renderProfile() { return [isOpen, roomId, userId, closeDialog, afterClose];
const member = room.getMember(userId) || mx.getUser(userId) || {}; }
const avatarMxc = member.getMxcAvatarUrl?.() || member.avatarUrl;
const powerLevel = member.powerLevel || 0; function useRerenderOnProfileChange(roomId, userId) {
const canChangeRole = room.currentState.maySendEvent('m.room.power_levels', mx.getUserId()); const mx = initMatrix.matrixClient;
const [, forceUpdate] = useForceUpdate();
useEffect(() => {
const handleProfileChange = (mEvent, member) => {
if (
mEvent.getRoomId() === roomId
&& (member.userId === userId || member.userId === mx.getUserId())
) {
forceUpdate();
}
};
mx.on('RoomMember.powerLevel', handleProfileChange);
mx.on('RoomMember.membership', handleProfileChange);
return () => {
mx.removeListener('RoomMember.powerLevel', handleProfileChange);
mx.removeListener('RoomMember.membership', handleProfileChange);
};
}, [roomId, userId]);
}
function ProfileViewer() {
const [isOpen, roomId, userId, closeDialog, handleAfterClose] = useToggleDialog();
useRerenderOnProfileChange(roomId, userId);
const mx = initMatrix.matrixClient;
const room = mx.getRoom(roomId);
const renderProfile = () => {
const roomMember = room.getMember(userId);
const username = roomMember ? getUsernameOfRoomMember(roomMember) : getUsername(userId);
const avatarMxc = roomMember?.getMxcAvatarUrl?.() || mx.getUser(userId)?.avatarUrl;
const avatarUrl = (avatarMxc && avatarMxc !== 'null') ? mx.mxcUrlToHttp(avatarMxc, 80, 80, 'crop') : null;
const powerLevel = roomMember.powerLevel || 0;
const myPowerLevel = room.getMember(mx.getUserId())?.powerLevel || 0;
const canChangeRole = (
room.currentState.maySendEvent('m.room.power_levels', mx.getUserId())
&& (powerLevel < myPowerLevel || userId === mx.getUserId())
);
const handleChangePowerLevel = (newPowerLevel) => {
if (newPowerLevel === powerLevel) return;
if (newPowerLevel === myPowerLevel
? confirm('You will not be able to undo this change as you are promoting the user to have the same power level as yourself. Are you sure?')
: true
) {
roomActions.setPowerLevel(roomId, userId, newPowerLevel);
}
};
const handlePowerSelector = (e) => {
openReusableContextMenu(
'bottom',
getEventCords(e, '.btn-surface'),
(closeMenu) => (
<PowerLevelSelector
value={powerLevel}
max={myPowerLevel}
onSelect={(pl) => {
closeMenu();
handleChangePowerLevel(pl);
}}
/>
),
);
};
return ( return (
<div className="profile-viewer"> <div className="profile-viewer">
<div className="profile-viewer__user"> <div className="profile-viewer__user">
<Avatar <Avatar imageSrc={avatarUrl} text={username} bgColor={colorMXID(userId)} size="large" />
imageSrc={!avatarMxc ? null : mx.mxcUrlToHttp(avatarMxc, 80, 80, 'crop')}
text={username}
bgColor={colorMXID(userId)}
size="large"
/>
<div className="profile-viewer__user__info"> <div className="profile-viewer__user__info">
<Text variant="s1" weight="medium">{twemojify(username)}</Text> <Text variant="s1" weight="medium">{twemojify(username)}</Text>
<Text variant="b2">{twemojify(userId)}</Text> <Text variant="b2">{twemojify(userId)}</Text>
</div> </div>
<div className="profile-viewer__user__role"> <div className="profile-viewer__user__role">
<Text variant="b3">Role</Text> <Text variant="b3">Role</Text>
<Button iconSrc={canChangeRole ? ChevronBottomIC : null}> <Button
onClick={canChangeRole ? handlePowerSelector : null}
iconSrc={canChangeRole ? ChevronBottomIC : null}
>
{`${getPowerLabel(powerLevel) || 'Member'} - ${powerLevel}`} {`${getPowerLabel(powerLevel) || 'Member'} - ${powerLevel}`}
</Button> </Button>
</div> </div>
</div> </div>
<ModerationTools roomId={roomId} userId={userId} />
<SessionInfo userId={userId} /> <SessionInfo userId={userId} />
{ userId !== mx.getUserId() && ( { userId !== mx.getUserId() && (
<ProfileFooter <ProfileFooter roomId={roomId} userId={userId} onRequestClose={closeDialog} />
roomId={roomId}
userId={userId}
onRequestClose={() => setIsOpen(false)}
/>
)} )}
</div> </div>
); );
} };
return ( return (
<Dialog <Dialog
className="profile-viewer__dialog" className="profile-viewer__dialog"
isOpen={isOpen} isOpen={isOpen}
title={`${username} in ${room?.name ?? ''}`} title={room?.name ?? ''}
onAfterClose={handleAfterClose} onAfterClose={handleAfterClose}
onRequestClose={() => setIsOpen(false)} onRequestClose={closeDialog}
contentOptions={<IconButton src={CrossIC} onClick={() => setIsOpen(false)} tooltip="Close" />} contentOptions={<IconButton src={CrossIC} onClick={closeDialog} tooltip="Close" />}
> >
{roomId ? renderProfile() : <div />} {roomId ? renderProfile() : <div />}
</Dialog> </Dialog>

View file

@ -1,3 +1,4 @@
@use '../../partials/flex';
@use '../../partials/dir'; @use '../../partials/dir';
.profile-viewer__dialog { .profile-viewer__dialog {
@ -15,7 +16,6 @@
&__user { &__user {
display: flex; display: flex;
padding-bottom: var(--sp-normal); padding-bottom: var(--sp-normal);
border-bottom: 1px solid var(--bg-surface-border);
&__info { &__info {
align-self: flex-end; align-self: flex-end;
@ -61,12 +61,47 @@
} }
} }
.session-info { .profile-viewer__admin-tool {
& .setting-tile__title .text { .setting-tile {
color: var(--tc-surface-high); margin-top: var(--sp-loose);
} }
}
.moderation-tools {
& > form {
margin: var(--sp-normal) 0;
display: flex;
align-items: flex-end;
& .input-container {
@extend .cp-fx__item-one;
@include dir.side(margin, 0, var(--sp-tight));
}
& button {
height: 46px;
}
}
}
.session-info {
box-shadow: var(--bs-surface-border);
border-radius: var(--bo-radius);
overflow: hidden;
& .context-menu__item button {
padding: var(--sp-extra-tight);
& .ic-raw {
@include dir.side(margin, 0, var(--sp-extra-tight));
}
}
&__chips { &__chips {
border-top: 1px solid var(--bg-surface-border);
padding: var(--sp-tight);
padding-top: var(--sp-ultra-tight); padding-top: var(--sp-ultra-tight);
& > .text {
margin-top: var(--sp-extra-tight);
}
& .chip { & .chip {
margin-top: var(--sp-extra-tight); margin-top: var(--sp-extra-tight);
@include dir.side(margin, 0, var(--sp-extra-tight)); @include dir.side(margin, 0, var(--sp-extra-tight));

View file

@ -1,125 +0,0 @@
import React, { useEffect, useRef } from 'react';
import { twemojify } from '../../../util/twemojify';
import initMatrix from '../../../client/initMatrix';
import cons from '../../../client/state/cons';
import navigation from '../../../client/state/navigation';
import { openInviteUser } from '../../../client/action/navigation';
import * as roomActions from '../../../client/action/room';
import ContextMenu, { MenuHeader, MenuItem } from '../../atoms/context-menu/ContextMenu';
import RoomNotification from '../../molecules/room-notification/RoomNotification';
import TickMarkIC from '../../../../public/res/ic/outlined/tick-mark.svg';
import AddUserIC from '../../../../public/res/ic/outlined/add-user.svg';
import LeaveArrowIC from '../../../../public/res/ic/outlined/leave-arrow.svg';
import { useForceUpdate } from '../../hooks/useForceUpdate';
let isRoomOptionVisible = false;
let roomId = null;
function RoomOptions() {
const openerRef = useRef(null);
const [, forceUpdate] = useForceUpdate();
function openRoomOptions(cords, rId) {
if (roomId !== null || isRoomOptionVisible) {
roomId = null;
if (cords.detail === 0) openerRef.current.click();
return;
}
openerRef.current.style.transform = `translate(${cords.x}px, ${cords.y}px)`;
roomId = rId;
openerRef.current.click();
forceUpdate();
}
const afterRoomOptionsToggle = (isVisible) => {
isRoomOptionVisible = isVisible;
if (!isVisible) {
setTimeout(() => {
if (!isRoomOptionVisible) roomId = null;
}, 500);
}
};
useEffect(() => {
navigation.on(cons.events.navigation.ROOMOPTIONS_OPENED, openRoomOptions);
return () => {
navigation.on(cons.events.navigation.ROOMOPTIONS_OPENED, openRoomOptions);
};
}, []);
const handleMarkAsRead = () => {
const mx = initMatrix.matrixClient;
const room = mx.getRoom(roomId);
if (!room) return;
const events = room.getLiveTimeline().getEvents();
mx.sendReadReceipt(events[events.length - 1]);
};
const handleInviteClick = () => openInviteUser(roomId);
const handleLeaveClick = (toggleMenu) => {
if (confirm('Are you really want to leave this room?')) {
roomActions.leave(roomId);
toggleMenu();
}
};
const mx = initMatrix.matrixClient;
const room = mx.getRoom(roomId);
const canInvite = room?.canInvite(mx.getUserId());
return (
<ContextMenu
afterToggle={afterRoomOptionsToggle}
maxWidth={298}
content={(toggleMenu) => (
<>
<MenuHeader>{twemojify(`Options for ${initMatrix.matrixClient.getRoom(roomId)?.name}`)}</MenuHeader>
<MenuItem
iconSrc={TickMarkIC}
onClick={() => {
handleMarkAsRead(); toggleMenu();
}}
>
Mark as read
</MenuItem>
<MenuItem
disabled={!canInvite}
iconSrc={AddUserIC}
onClick={() => {
handleInviteClick(); toggleMenu();
}}
>
Invite
</MenuItem>
<MenuItem iconSrc={LeaveArrowIC} variant="danger" onClick={() => handleLeaveClick(toggleMenu)}>Leave</MenuItem>
<MenuHeader>Notification</MenuHeader>
{roomId && <RoomNotification roomId={roomId} />}
</>
)}
render={(toggleMenu) => (
<input
ref={openerRef}
onClick={toggleMenu}
type="button"
style={{
width: '32px',
height: '32px',
backgroundColor: 'transparent',
position: 'absolute',
top: 0,
left: 0,
padding: 0,
border: 'none',
visibility: 'hidden',
}}
/>
)}
/>
);
}
export default RoomOptions;

View file

@ -107,7 +107,7 @@ function PeopleDrawer({ roomId }) {
let isRoomChanged = false; let isRoomChanged = false;
const updateMemberList = (event) => { const updateMemberList = (event) => {
if (isGettingMembers) return; if (isGettingMembers) return;
if (event && event?.event?.room_id !== roomId) return; if (event && event?.getRoomId() !== roomId) return;
setMemberList( setMemberList(
simplyfiMembers( simplyfiMembers(
getMembersWithMembership(membership) getMembersWithMembership(membership)
@ -125,6 +125,7 @@ function PeopleDrawer({ roomId }) {
asyncSearch.on(asyncSearch.RESULT_SENT, handleSearchData); asyncSearch.on(asyncSearch.RESULT_SENT, handleSearchData);
mx.on('RoomMember.membership', updateMemberList); mx.on('RoomMember.membership', updateMemberList);
mx.on('RoomMember.powerLevel', updateMemberList);
return () => { return () => {
isRoomChanged = true; isRoomChanged = true;
setMemberList([]); setMemberList([]);
@ -132,6 +133,7 @@ function PeopleDrawer({ roomId }) {
setItemCount(PER_PAGE_MEMBER); setItemCount(PER_PAGE_MEMBER);
asyncSearch.removeListener(asyncSearch.RESULT_SENT, handleSearchData); asyncSearch.removeListener(asyncSearch.RESULT_SENT, handleSearchData);
mx.removeListener('RoomMember.membership', updateMemberList); mx.removeListener('RoomMember.membership', updateMemberList);
mx.removeListener('RoomMember.powerLevel', updateMemberList);
}; };
}, [roomId, membership]); }, [roomId, membership]);

View file

@ -15,7 +15,7 @@
&--dropped { &--dropped {
transform: translateY(calc(100% - var(--header-height))); transform: translateY(calc(100% - var(--header-height)));
border-radius: var(--bo-radius) var(--bo-radius) 0 0; border-radius: var(--bo-radius) var(--bo-radius) 0 0;
box-shadow: 0 0 0 1px var(--bg-surface-border); box-shadow: var(--bs-popup);
} }
&__content-wrapper { &__content-wrapper {

View file

@ -8,7 +8,7 @@ import { blurOnBubbling } from '../../atoms/button/script';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
import cons from '../../../client/state/cons'; import cons from '../../../client/state/cons';
import navigation from '../../../client/state/navigation'; import navigation from '../../../client/state/navigation';
import { toggleRoomSettings, openRoomOptions } from '../../../client/action/navigation'; import { toggleRoomSettings, openReusableContextMenu } from '../../../client/action/navigation';
import { togglePeopleDrawer } from '../../../client/action/settings'; import { togglePeopleDrawer } from '../../../client/action/settings';
import colorMXID from '../../../util/colorMXID'; import colorMXID from '../../../util/colorMXID';
import { getEventCords } from '../../../util/common'; import { getEventCords } from '../../../util/common';
@ -18,6 +18,7 @@ import RawIcon from '../../atoms/system-icons/RawIcon';
import IconButton from '../../atoms/button/IconButton'; import IconButton from '../../atoms/button/IconButton';
import Header, { TitleWrapper } from '../../atoms/header/Header'; import Header, { TitleWrapper } from '../../atoms/header/Header';
import Avatar from '../../atoms/avatar/Avatar'; import Avatar from '../../atoms/avatar/Avatar';
import RoomOptions from '../../molecules/room-optons/RoomOptions';
import UserIC from '../../../../public/res/ic/outlined/user.svg'; import UserIC from '../../../../public/res/ic/outlined/user.svg';
import ChevronBottomIC from '../../../../public/res/ic/outlined/chevron-bottom.svg'; import ChevronBottomIC from '../../../../public/res/ic/outlined/chevron-bottom.svg';
@ -60,6 +61,14 @@ function RoomViewHeader({ roomId }) {
}; };
}, [roomId]); }, [roomId]);
const openRoomOptions = (e) => {
openReusableContextMenu(
'bottom',
getEventCords(e, '.ic-btn'),
(closeMenu) => <RoomOptions roomId={roomId} afterOptionSelect={closeMenu} />,
);
};
return ( return (
<Header> <Header>
<button <button
@ -77,7 +86,7 @@ function RoomViewHeader({ roomId }) {
</button> </button>
<IconButton onClick={togglePeopleDrawer} tooltip="People" src={UserIC} /> <IconButton onClick={togglePeopleDrawer} tooltip="People" src={UserIC} />
<IconButton <IconButton
onClick={(e) => openRoomOptions(getEventCords(e), roomId)} onClick={openRoomOptions}
tooltip="Options" tooltip="Options"
src={VerticalMenuIC} src={VerticalMenuIC}
/> />

View file

@ -4,11 +4,11 @@ import './Client.scss';
import Text from '../../atoms/text/Text'; import Text from '../../atoms/text/Text';
import Spinner from '../../atoms/spinner/Spinner'; import Spinner from '../../atoms/spinner/Spinner';
import Navigation from '../../organisms/navigation/Navigation'; import Navigation from '../../organisms/navigation/Navigation';
import ReusableContextMenu from '../../atoms/context-menu/ReusableContextMenu';
import Room from '../../organisms/room/Room'; import Room from '../../organisms/room/Room';
import Windows from '../../organisms/pw/Windows'; import Windows from '../../organisms/pw/Windows';
import Dialogs from '../../organisms/pw/Dialogs'; import Dialogs from '../../organisms/pw/Dialogs';
import EmojiBoardOpener from '../../organisms/emoji-board/EmojiBoardOpener'; import EmojiBoardOpener from '../../organisms/emoji-board/EmojiBoardOpener';
import RoomOptions from '../../organisms/room-optons/RoomOptions';
import logout from '../../../client/action/logout'; import logout from '../../../client/action/logout';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
@ -65,7 +65,7 @@ function Client() {
<Windows /> <Windows />
<Dialogs /> <Dialogs />
<EmojiBoardOpener /> <EmojiBoardOpener />
<RoomOptions /> <ReusableContextMenu />
</div> </div>
); );
} }

View file

@ -87,14 +87,6 @@ export function openReadReceipts(roomId, userIds) {
}); });
} }
export function openRoomOptions(cords, roomId) {
appDispatcher.dispatch({
type: cons.actions.navigation.OPEN_ROOMOPTIONS,
cords,
roomId,
});
}
export function openAttachmentTypeSelector(params) { export function openAttachmentTypeSelector(params) {
appDispatcher.dispatch({ appDispatcher.dispatch({
type: cons.actions.navigation.OPEN_ATTACHMENT_TYPE_SELECTOR, type: cons.actions.navigation.OPEN_ATTACHMENT_TYPE_SELECTOR,
@ -102,15 +94,6 @@ export function openAttachmentTypeSelector(params) {
}); });
} }
export function replyTo(userId, eventId, body) {
appDispatcher.dispatch({
type: cons.actions.navigation.CLICK_REPLY_TO,
userId,
eventId,
body,
});
}
export function openSearch(term) { export function openSearch(term) {
appDispatcher.dispatch({ appDispatcher.dispatch({
type: cons.actions.navigation.OPEN_SEARCH, type: cons.actions.navigation.OPEN_SEARCH,
@ -118,11 +101,12 @@ export function openSearch(term) {
}); });
} }
export function openReusableContextMenu(placement, cords, render) { export function openReusableContextMenu(placement, cords, render, afterClose) {
appDispatcher.dispatch({ appDispatcher.dispatch({
type: cons.actions.navigation.OPEN_REUSABLE_CONTEXT_MENU, type: cons.actions.navigation.OPEN_REUSABLE_CONTEXT_MENU,
placement, placement,
cords, cords,
render, render,
afterClose,
}); });
} }

View file

@ -192,10 +192,34 @@ async function invite(roomId, userId) {
return result; return result;
} }
async function kick(roomId, userId) { async function kick(roomId, userId, reason) {
const mx = initMatrix.matrixClient; const mx = initMatrix.matrixClient;
const result = await mx.kick(roomId, userId); const result = await mx.kick(roomId, userId, reason);
return result;
}
async function ban(roomId, userId, reason) {
const mx = initMatrix.matrixClient;
const result = await mx.ban(roomId, userId, reason);
return result;
}
async function unban(roomId, userId) {
const mx = initMatrix.matrixClient;
const result = await mx.unban(roomId, userId);
return result;
}
async function setPowerLevel(roomId, userId, powerLevel) {
const mx = initMatrix.matrixClient;
const room = mx.getRoom(roomId);
const powerlevelEvent = room.currentState.getStateEvents('m.room.power_levels')[0];
const result = await mx.setPowerLevel(roomId, userId, powerLevel, powerlevelEvent);
return result; return result;
} }
@ -215,6 +239,7 @@ function deleteSpaceShortcut(roomId) {
export { export {
join, leave, join, leave,
create, invite, kick, create, invite, kick, ban, unban,
setPowerLevel,
createSpaceShortcut, deleteSpaceShortcut, createSpaceShortcut, deleteSpaceShortcut,
}; };

View file

@ -39,7 +39,6 @@ const cons = {
OPEN_SETTINGS: 'OPEN_SETTINGS', OPEN_SETTINGS: 'OPEN_SETTINGS',
OPEN_EMOJIBOARD: 'OPEN_EMOJIBOARD', OPEN_EMOJIBOARD: 'OPEN_EMOJIBOARD',
OPEN_READRECEIPTS: 'OPEN_READRECEIPTS', OPEN_READRECEIPTS: 'OPEN_READRECEIPTS',
OPEN_ROOMOPTIONS: 'OPEN_ROOMOPTIONS',
CLICK_REPLY_TO: 'CLICK_REPLY_TO', CLICK_REPLY_TO: 'CLICK_REPLY_TO',
OPEN_SEARCH: 'OPEN_SEARCH', OPEN_SEARCH: 'OPEN_SEARCH',
OPEN_ATTACHMENT_TYPE_SELECTOR: 'OPEN_ATTACHMENT_TYPE_SELECTOR', OPEN_ATTACHMENT_TYPE_SELECTOR: 'OPEN_ATTACHMENT_TYPE_SELECTOR',
@ -77,7 +76,6 @@ const cons = {
PROFILE_VIEWER_OPENED: 'PROFILE_VIEWER_OPENED', PROFILE_VIEWER_OPENED: 'PROFILE_VIEWER_OPENED',
EMOJIBOARD_OPENED: 'EMOJIBOARD_OPENED', EMOJIBOARD_OPENED: 'EMOJIBOARD_OPENED',
READRECEIPTS_OPENED: 'READRECEIPTS_OPENED', READRECEIPTS_OPENED: 'READRECEIPTS_OPENED',
ROOMOPTIONS_OPENED: 'ROOMOPTIONS_OPENED',
REPLY_TO_CLICKED: 'REPLY_TO_CLICKED', REPLY_TO_CLICKED: 'REPLY_TO_CLICKED',
OPEN_ATTACHMENT_TYPE_SELECTOR: 'OPEN_ATTACHMENT_TYPE_SELECTOR', OPEN_ATTACHMENT_TYPE_SELECTOR: 'OPEN_ATTACHMENT_TYPE_SELECTOR',
SEARCH_OPENED: 'SEARCH_OPENED', SEARCH_OPENED: 'SEARCH_OPENED',

View file

@ -126,13 +126,6 @@ class Navigation extends EventEmitter {
action.userIds, action.userIds,
); );
}, },
[cons.actions.navigation.OPEN_ROOMOPTIONS]: () => {
this.emit(
cons.events.navigation.ROOMOPTIONS_OPENED,
action.cords,
action.roomId,
);
},
[cons.actions.navigation.OPEN_ATTACHMENT_TYPE_SELECTOR]: () => { [cons.actions.navigation.OPEN_ATTACHMENT_TYPE_SELECTOR]: () => {
this.emit( this.emit(
cons.events.navigation.OPEN_ATTACHMENT_TYPE_SELECTOR, cons.events.navigation.OPEN_ATTACHMENT_TYPE_SELECTOR,
@ -160,6 +153,7 @@ class Navigation extends EventEmitter {
action.placement, action.placement,
action.cords, action.cords,
action.render, action.render,
action.afterClose,
); );
}, },
}; };