Added RoomOptions component (#25)
authorunknown <ajbura@gmail.com>
Thu, 9 Sep 2021 12:19:57 +0000 (17:49 +0530)
committerunknown <ajbura@gmail.com>
Thu, 9 Sep 2021 12:19:57 +0000 (17:49 +0530)
public/res/ic/outlined/bell-off.svg [new file with mode: 0644]
public/res/ic/outlined/bell-ping.svg [new file with mode: 0644]
public/res/ic/outlined/bell-ring.svg [new file with mode: 0644]
public/res/ic/outlined/bell.svg
src/app/organisms/room-optons/RoomOptions.jsx [new file with mode: 0644]
src/app/organisms/room-optons/RoomOptions.scss [new file with mode: 0644]
src/client/action/navigation.js
src/client/state/RoomList.js
src/client/state/cons.js
src/client/state/navigation.js
src/util/common.js

diff --git a/public/res/ic/outlined/bell-off.svg b/public/res/ic/outlined/bell-off.svg
new file mode 100644 (file)
index 0000000..79ce8a3
--- /dev/null
@@ -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>
diff --git a/public/res/ic/outlined/bell-ping.svg b/public/res/ic/outlined/bell-ping.svg
new file mode 100644 (file)
index 0000000..3431bea
--- /dev/null
@@ -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>
diff --git a/public/res/ic/outlined/bell-ring.svg b/public/res/ic/outlined/bell-ring.svg
new file mode 100644 (file)
index 0000000..57fc267
--- /dev/null
@@ -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>
index d3d2f6db05c3ed677a86ff3a47eaefbf45089e6c..43d470b5326f652372d674ed44c7fe048fe566dc 100644 (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"
         viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
 <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
-               v-2l-2-2V9C19,5.1,15.9,2,12,2L12,2z"/>
-       <path d="M9,19c0,1.7,1.3,3,3,3s3-1.3,3-3H9z"/>
+       <path d="M12,22c1.1,0,2-0.9,2-2h-4C10,21.1,10.9,22,12,22z"/>
+       <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"/>
 </g>
 </svg>
diff --git a/src/app/organisms/room-optons/RoomOptions.jsx b/src/app/organisms/room-optons/RoomOptions.jsx
new file mode 100644 (file)
index 0000000..0c89008
--- /dev/null
@@ -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;
diff --git a/src/app/organisms/room-optons/RoomOptions.scss b/src/app/organisms/room-optons/RoomOptions.scss
new file mode 100644 (file)
index 0000000..ae3f9c3
--- /dev/null
@@ -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;
+  }
+}
\ No newline at end of file
index 5fa13040a5d8ede0f660f7f22632d0abae93e4dc..d11aceb7204e4fe598dadae6104eb0272e970501 100644 (file)
@@ -77,6 +77,14 @@ function openReadReceipts(roomId, eventId) {
   });
 }
 
+function openRoomOptions(cords, roomId) {
+  appDispatcher.dispatch({
+    type: cons.actions.navigation.OPEN_ROOMOPTIONS,
+    cords,
+    roomId,
+  });
+}
+
 export {
   selectTab,
   selectSpace,
@@ -89,4 +97,5 @@ export {
   openSettings,
   openEmojiBoard,
   openReadReceipts,
+  openRoomOptions,
 };
index a47bf46980b985fb44eb82f93f52a9de36e755fd..244c7352ad91e8c5f58b798d672e5fedf74d0d5e 100644 (file)
@@ -386,6 +386,7 @@ class RoomList extends EventEmitter {
 
       const lastTimelineEvent = room.timeline[room.timeline.length - 1];
       if (lastTimelineEvent.getId() !== event.getId()) return;
+      if (event.getSender() === this.matrixClient.getUserId()) return;
       this.emit(cons.events.roomList.EVENT_ARRIVED, room.roomId);
     });
   }
index 7587120192cf24d3c0ea68e82de143815606df25..6c00668a1ceed2695a44c78047b486e16222fbda 100644 (file)
@@ -11,6 +11,12 @@ const cons = {
     HOME: 'home',
     DIRECTS: 'dm',
   },
+  notifs: {
+    DEFAULT: 'default',
+    ALL_MESSAGES: 'all_messages',
+    MENTIONS_AND_KEYWORDS: 'mentions_and_keywords',
+    MUTE: 'mute',
+  },
   actions: {
     navigation: {
       SELECT_TAB: 'SELECT_TAB',
@@ -24,6 +30,7 @@ const cons = {
       OPEN_SETTINGS: 'OPEN_SETTINGS',
       OPEN_EMOJIBOARD: 'OPEN_EMOJIBOARD',
       OPEN_READRECEIPTS: 'OPEN_READRECEIPTS',
+      OPEN_ROOMOPTIONS: 'OPEN_ROOMOPTIONS',
     },
     room: {
       JOIN: 'JOIN',
@@ -52,6 +59,7 @@ const cons = {
       SETTINGS_OPENED: 'SETTINGS_OPENED',
       EMOJIBOARD_OPENED: 'EMOJIBOARD_OPENED',
       READRECEIPTS_OPENED: 'READRECEIPTS_OPENED',
+      ROOMOPTIONS_OPENED: 'ROOMOPTIONS_OPENED',
     },
     roomList: {
       ROOMLIST_UPDATED: 'ROOMLIST_UPDATED',
index 5188aad886e1a6c83a3cc2f310fc4f9196abced6..d7dabd7851eb5c19ec119f1cab668c2b835bde1f 100644 (file)
@@ -85,6 +85,13 @@ class Navigation extends EventEmitter {
           action.eventId,
         );
       },
+      [cons.actions.navigation.OPEN_ROOMOPTIONS]: () => {
+        this.emit(
+          cons.events.navigation.ROOMOPTIONS_OPENED,
+          action.cords,
+          action.roomId,
+        );
+      },
     };
     actions[action.type]?.();
   }
index 78bb349b5f3d59e2d92fcde7a8c4e9b21d07fa20..938ced54480b7420719da67a3a2cee681aa34e78 100644 (file)
@@ -19,3 +19,12 @@ export function isNotInSameDay(dt2, dt1) {
     || dt2.getYear() !== dt1.getYear()
   );
 }
+
+export function getEventCords(ev) {
+  const boxInfo = ev.target.getBoundingClientRect();
+  return {
+    x: boxInfo.x,
+    y: boxInfo.y,
+    detail: ev.detail,
+  };
+}