Added RoomOptions component (#25)

This commit is contained in:
unknown 2021-09-09 17:49:57 +05:30
parent 652f8227b5
commit 80551124f1
11 changed files with 322 additions and 3 deletions

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<g>
<path d="M12,22c1.1,0,2-0.9,2-2h-4C10,21.1,10.9,22,12,22z"/>
<path d="M20.1,18.1L20.1,18.1L16,14L9.2,7.2L7.8,5.8L5.9,3.9L4.5,5.3l2.1,2.1C6.2,8.2,6,9.1,6,10v6H4v2h13.2l1.5,1.5L20.1,18.1z
M8,16v-6c0-0.4,0.1-0.7,0.1-1l7,7H8z"/>
<path d="M12,6c2.2,0,4,1.8,4,4v1.2l2,2V10c0-3-2.2-5.4-5-5.9V3h-2v1.1c-0.6,0.1-1.1,0.3-1.6,0.5L11,6.1C11.3,6.1,11.6,6,12,6z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 810 B

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<g>
<circle cx="17" cy="8" r="3"/>
<path d="M12,22c1.1,0,2-0.9,2-2h-4C10,21.1,10.9,22,12,22z"/>
<path d="M18,12.9C17.7,13,17.3,13,17,13s-0.7,0-1-0.1V16H8v-6c0-2.2,1.8-4,4-4c0.1,0,0.3,0,0.4,0c0.3-0.7,0.7-1.3,1.3-1.8
c-0.2-0.1-0.5-0.1-0.7-0.2V3h-2v1.1C8.2,4.6,6,7,6,10v6H4v2h16v-2h-2V12.9z"/>
<path d="M6.3,4.3L4.9,2.9C3.1,4.7,2,7.2,2,10h2C4,7.8,4.9,5.8,6.3,4.3z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 819 B

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<g>
<path d="M12,22c1.1,0,2-0.9,2-2h-4C10,21.1,10.9,22,12,22z"/>
<path d="M18,10c0-3-2.2-5.4-5-5.9V3h-2v1.1C8.2,4.6,6,7,6,10v6H4v2h16v-2h-2V10z M16,16H8v-6c0-2.2,1.8-4,4-4s4,1.8,4,4V16z"/>
<path d="M6.3,4.3L4.9,2.9C3.1,4.7,2,7.2,2,10h2C4,7.8,4.9,5.8,6.3,4.3z"/>
<path d="M19.1,2.9l-1.4,1.4C19.1,5.8,20,7.8,20,10h2C22,7.2,20.9,4.7,19.1,2.9z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 796 B

View file

@ -4,8 +4,7 @@
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve"> viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<g> <g>
<path d="M12,4c2.8,0,5,2.2,5,5v4v0.8l0.6,0.6l0.6,0.6H5.8l0.6-0.6L7,13.8V13V9C7,6.2,9.2,4,12,4 M12,2C8.1,2,5,5.1,5,9v4l-2,2v2h18 <path d="M12,22c1.1,0,2-0.9,2-2h-4C10,21.1,10.9,22,12,22z"/>
v-2l-2-2V9C19,5.1,15.9,2,12,2L12,2z"/> <path d="M18,16v-6c0-3-2.2-5.4-5-5.9V3h-2v1.1C8.2,4.6,6,7,6,10v6H4v2h16v-2H18z M16,16H8v-6c0-2.2,1.8-4,4-4s4,1.8,4,4V16z"/>
<path d="M9,19c0,1.7,1.3,3,3,3s3-1.3,3-3H9z"/>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 671 B

After

Width:  |  Height:  |  Size: 640 B

View file

@ -0,0 +1,229 @@
import React, { useState, useEffect, useRef } from 'react';
import './RoomOptions.scss';
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 BellIC from '../../../../public/res/ic/outlined/bell.svg';
import BellRingIC from '../../../../public/res/ic/outlined/bell-ring.svg';
import BellPingIC from '../../../../public/res/ic/outlined/bell-ping.svg';
import BellOffIC from '../../../../public/res/ic/outlined/bell-off.svg';
import AddUserIC from '../../../../public/res/ic/outlined/add-user.svg';
import LeaveArrowIC from '../../../../public/res/ic/outlined/leave-arrow.svg';
function getNotifState(roomId) {
const mx = initMatrix.matrixClient;
const pushRule = mx.getRoomPushRule('global', roomId);
if (typeof pushRule === 'undefined') {
const overridePushRules = mx.getAccountData('m.push_rules')?.getContent()?.global?.override;
if (typeof overridePushRules === 'undefined') return 0;
const isMuteOverride = overridePushRules.find((rule) => (
rule.rule_id === roomId
&& rule.actions[0] === 'dont_notify'
&& rule.conditions[0].kind === 'event_match'
));
return isMuteOverride ? cons.notifs.MUTE : cons.notifs.DEFAULT;
}
if (pushRule.actions[0] === 'notify') return cons.notifs.ALL_MESSAGES;
return cons.notifs.MENTIONS_AND_KEYWORDS;
}
function setRoomNotifMute(roomId) {
const mx = initMatrix.matrixClient;
const roomPushRule = mx.getRoomPushRule('global', roomId);
const promises = [];
if (roomPushRule) {
promises.push(mx.deletePushRule('global', 'room', roomPushRule.rule_id));
}
promises.push(mx.addPushRule('global', 'override', roomId, {
conditions: [
{
kind: 'event_match',
key: 'room_id',
pattern: roomId,
},
],
actions: [
'dont_notify',
],
}));
return Promise.all(promises);
}
function setRoomNotifsState(newState, roomId) {
const mx = initMatrix.matrixClient;
const promises = [];
const oldState = getNotifState(roomId);
if (oldState === cons.notifs.MUTE) {
promises.push(mx.deletePushRule('global', 'override', roomId));
}
if (newState === cons.notifs.DEFAULT) {
const roomPushRule = mx.getRoomPushRule('global', roomId);
if (roomPushRule) {
promises.push(mx.deletePushRule('global', 'room', roomPushRule.rule_id));
}
return Promise.all(promises);
}
if (newState === cons.notifs.MENTIONS_AND_KEYWORDS) {
promises.push(mx.addPushRule('global', 'room', roomId, {
actions: [
'dont_notify',
],
}));
promises.push(mx.setPushRuleEnabled('global', 'room', roomId, true));
return Promise.all(promises);
}
// cons.notifs.ALL_MESSAGES
promises.push(mx.addPushRule('global', 'room', roomId, {
actions: [
'notify',
{
set_tweak: 'sound',
value: 'default',
},
],
}));
promises.push(mx.setPushRuleEnabled('global', 'room', roomId, true));
return Promise.all(promises);
}
function setRoomNotifPushRule(notifState, roomId) {
if (notifState === cons.notifs.MUTE) {
setRoomNotifMute(roomId);
return;
}
setRoomNotifsState(notifState, roomId);
}
let isRoomOptionVisible = false;
let roomId = null;
function RoomOptions() {
const openerRef = useRef(null);
const [notifState, setNotifState] = useState(cons.notifs.DEFAULT);
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;
setNotifState(getNotifState(roomId));
openerRef.current.click();
}
function 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 handleInviteClick = () => openInviteUser(roomId);
const handleLeaveClick = () => {
if (confirm('Are you really want to leave this room?')) roomActions.leave(roomId);
};
function setNotif(nState, currentNState) {
if (nState === currentNState) return;
setRoomNotifPushRule(nState, roomId);
setNotifState(nState);
}
return (
<ContextMenu
afterToggle={afterRoomOptionsToggle}
maxWidth={298}
content={(toggleMenu) => (
<>
<MenuHeader>{`Options for ${initMatrix.matrixClient.getRoom(roomId)?.name}`}</MenuHeader>
<MenuItem
iconSrc={AddUserIC}
onClick={() => {
handleInviteClick(); toggleMenu();
}}
>
Invite
</MenuItem>
<MenuItem iconSrc={LeaveArrowIC} variant="danger" onClick={handleLeaveClick}>Leave</MenuItem>
<MenuHeader>Notification</MenuHeader>
<MenuItem
variant={notifState === cons.notifs.DEFAULT ? 'positive' : 'surface'}
iconSrc={BellIC}
onClick={() => setNotif(cons.notifs.DEFAULT, notifState)}
>
Default
</MenuItem>
<MenuItem
variant={notifState === cons.notifs.ALL_MESSAGES ? 'positive' : 'surface'}
iconSrc={BellRingIC}
onClick={() => setNotif(cons.notifs.ALL_MESSAGES, notifState)}
>
All messages
</MenuItem>
<MenuItem
variant={notifState === cons.notifs.MENTIONS_AND_KEYWORDS ? 'positive' : 'surface'}
iconSrc={BellPingIC}
onClick={() => setNotif(cons.notifs.MENTIONS_AND_KEYWORDS, notifState)}
>
Mentions & Keywords
</MenuItem>
<MenuItem
variant={notifState === cons.notifs.MUTE ? 'positive' : 'surface'}
iconSrc={BellOffIC}
onClick={() => setNotif(cons.notifs.MUTE, notifState)}
>
Mute
</MenuItem>
</>
)}
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

@ -0,0 +1,20 @@
.context-menu__item {
position: relative;
}
.context-menu__item .btn-positive::before {
content: '';
display: inline-block;
width: 3px;
height: 12px;
background: var(--bg-positive);
border-radius: 0 4px 4px 0;
position: absolute;
left: 0;
[dir=rtl] & {
left: unset;
right: 0;
border-radius: 4px 0 0 4px;
}
}

View file

@ -77,6 +77,14 @@ function openReadReceipts(roomId, eventId) {
}); });
} }
function openRoomOptions(cords, roomId) {
appDispatcher.dispatch({
type: cons.actions.navigation.OPEN_ROOMOPTIONS,
cords,
roomId,
});
}
export { export {
selectTab, selectTab,
selectSpace, selectSpace,
@ -89,4 +97,5 @@ export {
openSettings, openSettings,
openEmojiBoard, openEmojiBoard,
openReadReceipts, openReadReceipts,
openRoomOptions,
}; };

View file

@ -386,6 +386,7 @@ class RoomList extends EventEmitter {
const lastTimelineEvent = room.timeline[room.timeline.length - 1]; const lastTimelineEvent = room.timeline[room.timeline.length - 1];
if (lastTimelineEvent.getId() !== event.getId()) return; if (lastTimelineEvent.getId() !== event.getId()) return;
if (event.getSender() === this.matrixClient.getUserId()) return;
this.emit(cons.events.roomList.EVENT_ARRIVED, room.roomId); this.emit(cons.events.roomList.EVENT_ARRIVED, room.roomId);
}); });
} }

View file

@ -11,6 +11,12 @@ const cons = {
HOME: 'home', HOME: 'home',
DIRECTS: 'dm', DIRECTS: 'dm',
}, },
notifs: {
DEFAULT: 'default',
ALL_MESSAGES: 'all_messages',
MENTIONS_AND_KEYWORDS: 'mentions_and_keywords',
MUTE: 'mute',
},
actions: { actions: {
navigation: { navigation: {
SELECT_TAB: 'SELECT_TAB', SELECT_TAB: 'SELECT_TAB',
@ -24,6 +30,7 @@ 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',
}, },
room: { room: {
JOIN: 'JOIN', JOIN: 'JOIN',
@ -52,6 +59,7 @@ const cons = {
SETTINGS_OPENED: 'SETTINGS_OPENED', SETTINGS_OPENED: 'SETTINGS_OPENED',
EMOJIBOARD_OPENED: 'EMOJIBOARD_OPENED', EMOJIBOARD_OPENED: 'EMOJIBOARD_OPENED',
READRECEIPTS_OPENED: 'READRECEIPTS_OPENED', READRECEIPTS_OPENED: 'READRECEIPTS_OPENED',
ROOMOPTIONS_OPENED: 'ROOMOPTIONS_OPENED',
}, },
roomList: { roomList: {
ROOMLIST_UPDATED: 'ROOMLIST_UPDATED', ROOMLIST_UPDATED: 'ROOMLIST_UPDATED',

View file

@ -85,6 +85,13 @@ class Navigation extends EventEmitter {
action.eventId, action.eventId,
); );
}, },
[cons.actions.navigation.OPEN_ROOMOPTIONS]: () => {
this.emit(
cons.events.navigation.ROOMOPTIONS_OPENED,
action.cords,
action.roomId,
);
},
}; };
actions[action.type]?.(); actions[action.type]?.();
} }

View file

@ -19,3 +19,12 @@ export function isNotInSameDay(dt2, dt1) {
|| dt2.getYear() !== dt1.getYear() || dt2.getYear() !== dt1.getYear()
); );
} }
export function getEventCords(ev) {
const boxInfo = ev.target.getBoundingClientRect();
return {
x: boxInfo.x,
y: boxInfo.y,
detail: ev.detail,
};
}