Update sidebar on room/space switch (#768)
authorAjay Bura <32841439+ajbura@users.noreply.github.com>
Sat, 20 Aug 2022 15:21:37 +0000 (20:51 +0530)
committerGitHub <noreply@github.com>
Sat, 20 Aug 2022 15:21:37 +0000 (20:51 +0530)
* Select last room on space/tab change (#353)

* Update sidbar on room select from search (#374)

* Select last room on space/tab change (#353)

* Update sidbar on room select from search (#374)

* Fix wrong space gets selected with some rooms

* Fix auto select room in categorized space

* Fix room remain selected on leave

* Fix leaved room appear in category & search

* Remove globally exposed vars

* Hide pin spaces from home

* Fix selecting dm always open dm tab

* Order category by AtoZ (#769)

Co-authored-by: Krishan <33421343+kfiven@users.noreply.github.com>
src/app/molecules/space-options/SpaceOptions.jsx
src/app/organisms/join-alias/JoinAlias.jsx
src/app/organisms/navigation/DrawerBreadcrumb.jsx
src/app/organisms/navigation/Home.jsx
src/client/event/roomList.js
src/client/initMatrix.js
src/client/state/RoomList.js
src/client/state/navigation.js

index 56fdfd3f0b33e0f9b49235848df5060674d2e614..0c166c6a905cd321faf432080699ae7b82f8b37f 100644 (file)
@@ -38,10 +38,10 @@ function SpaceOptions({ roomId, afterOptionSelect }) {
 
   const handleMarkAsRead = () => {
     const spaceChildren = roomList.getCategorizedSpaces([roomId]);
-    spaceChildren?.forEach((childIds, spaceId) => {
+    spaceChildren?.forEach((childIds) => {
       childIds?.forEach((childId) => {
         markAsRead(childId);
-      })
+      });
     });
     afterOptionSelect();
   };
index 9e7f6df1a27e9a62f86074dca4d9a5b5eb40259d..bb90bf3545259192fcc57f587e6b2deaa9ff66e1 100644 (file)
@@ -6,7 +6,7 @@ import initMatrix from '../../../client/initMatrix';
 import cons from '../../../client/state/cons';
 import navigation from '../../../client/state/navigation';
 import { join } from '../../../client/action/room';
-import { selectRoom, selectSpace } from '../../../client/action/navigation';
+import { selectRoom, selectTab } from '../../../client/action/navigation';
 
 import Text from '../../atoms/text/Text';
 import IconButton from '../../atoms/button/IconButton';
@@ -32,7 +32,7 @@ function JoinAliasContent({ term, requestClose }) {
   const openRoom = (roomId) => {
     const room = mx.getRoom(roomId);
     if (!room) return;
-    if (room.isSpaceRoom()) selectSpace(roomId);
+    if (room.isSpaceRoom()) selectTab(roomId);
     else selectRoom(roomId);
     requestClose();
   };
index ca9ab6a096783123f0c2506818964a1b2d9ba49b..be5b345bf26fc75019c506d77531840d2db7035a 100644 (file)
@@ -6,7 +6,7 @@ import { twemojify } from '../../../util/twemojify';
 
 import initMatrix from '../../../client/initMatrix';
 import cons from '../../../client/state/cons';
-import { selectSpace } from '../../../client/action/navigation';
+import { selectTab, selectSpace } from '../../../client/action/navigation';
 import navigation from '../../../client/state/navigation';
 import { abbreviateNumber } from '../../../util/common';
 
@@ -107,7 +107,10 @@ function DrawerBreadcrumb({ spaceId }) {
                   { index !== 0 && <RawIcon size="extra-small" src={ChevronRightIC} />}
                   <Button
                     className={index === spacePath.length - 1 ? 'drawer-breadcrumb__btn--selected' : ''}
-                    onClick={() => selectSpace(id)}
+                    onClick={() => {
+                      if (id === cons.tabs.HOME) selectTab(id);
+                      else selectSpace(id);
+                    }}
                   >
                     <Text variant="b2">{id === cons.tabs.HOME ? 'Home' : twemojify(mx.getRoom(id).name)}</Text>
                     { noti !== null && (
index 5e26a48ba62ba279eb4eda6be22483e62381ba50..9e7495609abfdfc8205a621a6e9a82ae06a2ab3c 100644 (file)
@@ -30,7 +30,7 @@ function Home({ spaceId }) {
     roomIds = spaceChildIds.filter((roomId) => rooms.has(roomId));
     directIds = spaceChildIds.filter((roomId) => directs.has(roomId));
   } else {
-    spaceIds = roomList.getOrphanSpaces();
+    spaceIds = roomList.getOrphanSpaces().filter((id) => !accountData.spaceShortcut.has(id));
     roomIds = roomList.getOrphanRooms();
   }
 
@@ -80,10 +80,10 @@ function Home({ spaceId }) {
         <RoomsCategory name="People" roomIds={directIds.sort(roomIdByActivity)} drawerPostie={drawerPostie} />
       )}
 
-      { isCategorized && [...categories].map(([catId, childIds]) => {
+      { isCategorized && [...categories.keys()].sort(roomIdByAtoZ).map((catId) => {
         const rms = [];
         const dms = [];
-        childIds.forEach((id) => {
+        categories.get(catId).forEach((id) => {
           if (directs.has(id)) dms.push(id);
           else rms.push(id);
         });
index bc40a4c5c7f4f5ab0d22b7e75b9336ecc1d32009..6592d67f592b2183ddbeca6423d7fbbb6cd77391 100644 (file)
@@ -4,6 +4,17 @@ import { selectTab, selectSpace, selectRoom } from '../action/navigation';
 
 function initRoomListListener(roomList) {
   const listenRoomLeave = (roomId) => {
+    const parents = roomList.roomIdToParents.get(roomId);
+
+    if (parents) {
+      [...parents].forEach((pId) => {
+        const data = navigation.spaceToRoom.get(pId);
+        if (data?.roomId === roomId) {
+          navigation.spaceToRoom.delete(pId);
+        }
+      });
+    }
+
     if (navigation.selectedRoomId === roomId) {
       selectRoom(null);
     }
@@ -13,6 +24,8 @@ function initRoomListListener(roomList) {
       if (idIndex === 0) selectTab(cons.tabs.HOME);
       else selectSpace(navigation.selectedSpacePath[idIndex - 1]);
     }
+
+    navigation.removeRecentRoom(roomId);
   };
 
   roomList.on(cons.events.roomList.ROOM_LEAVED, listenRoomLeave);
index 936334cee9e331b4bc664d30ccc01a89ee6ed069..e219a777d485da4b683aae8cc2f848a4f96d0688 100644 (file)
@@ -8,12 +8,19 @@ import AccountData from './state/AccountData';
 import RoomsInput from './state/RoomsInput';
 import Notifications from './state/Notifications';
 import { cryptoCallbacks } from './state/secretStorageKeys';
+import navigation from './state/navigation';
 
 global.Olm = require('@matrix-org/olm');
 
 // logger.disableAll();
 
 class InitMatrix extends EventEmitter {
+  constructor() {
+    super();
+
+    navigation.initMatrix = this;
+  }
+
   async init() {
     await this.startClient();
     this.setupSync();
index 6ea4dad4b25994cec5029ed5a54ae900657f37ba..b24379b0ada49b3eea95c16ae95ff0325186412a 100644 (file)
@@ -89,7 +89,7 @@ class RoomList extends EventEmitter {
 
       child.forEach((childId) => {
         const room = this.matrixClient.getRoom(childId);
-        if (room === null) return;
+        if (room === null || room.getMyMembership() !== 'join') return;
         if (room.isSpaceRoom()) categorizeSpace(childId);
         else mappedChild.add(childId);
       });
index b39c043f6b9b2f382015f23f415729dd67f24ac4..5bed0956ca499cf9e53e568a7cca5105dc97a823 100644 (file)
@@ -5,6 +5,8 @@ import cons from './cons';
 class Navigation extends EventEmitter {
   constructor() {
     super();
+    // this will attached by initMatrix
+    this.initMatrix = {};
 
     this.selectedTab = cons.tabs.HOME;
     this.selectedSpaceId = null;
@@ -14,14 +16,20 @@ class Navigation extends EventEmitter {
     this.isRoomSettings = false;
     this.recentRooms = [];
 
+    this.spaceToRoom = new Map();
+
     this.rawModelStack = [];
   }
 
-  _setSpacePath(roomId) {
-    if (roomId === null || roomId === cons.tabs.HOME) {
+  _addToSpacePath(roomId, asRoot) {
+    if (typeof roomId !== 'string') {
       this.selectedSpacePath = [cons.tabs.HOME];
       return;
     }
+    if (asRoot) {
+      this.selectedSpacePath = [roomId];
+      return;
+    }
     if (this.selectedSpacePath.includes(roomId)) {
       const spIndex = this.selectedSpacePath.indexOf(roomId);
       this.selectedSpacePath = this.selectedSpacePath.slice(0, spIndex + 1);
@@ -30,6 +38,215 @@ class Navigation extends EventEmitter {
     this.selectedSpacePath.push(roomId);
   }
 
+  _mapRoomToSpace(roomId) {
+    const { roomList } = this.initMatrix;
+    if (
+      this.selectedTab === cons.tabs.HOME
+      && roomList.rooms.has(roomId)
+      && !roomList.roomIdToParents.has(roomId)
+    ) {
+      this.spaceToRoom.set(cons.tabs.HOME, {
+        roomId,
+        timestamp: Date.now(),
+      });
+      return;
+    }
+    if (this.selectedTab === cons.tabs.DIRECTS && roomList.directs.has(roomId)) {
+      this.spaceToRoom.set(cons.tabs.DIRECTS, {
+        roomId,
+        timestamp: Date.now(),
+      });
+      return;
+    }
+
+    const parents = roomList.roomIdToParents.get(roomId);
+    if (!parents) return;
+
+    [...parents].forEach((pId) => {
+      this.spaceToRoom.set(pId, {
+        roomId,
+        timestamp: Date.now(),
+      });
+    });
+  }
+
+  _selectRoom(roomId, eventId) {
+    const prevSelectedRoomId = this.selectedRoomId;
+    this.selectedRoomId = roomId;
+    if (prevSelectedRoomId !== roomId) this._mapRoomToSpace(roomId);
+    this.removeRecentRoom(prevSelectedRoomId);
+    this.addRecentRoom(prevSelectedRoomId);
+    this.removeRecentRoom(this.selectedRoomId);
+    if (this.isRoomSettings && typeof this.selectedRoomId === 'string') {
+      this.isRoomSettings = !this.isRoomSettings;
+      this.emit(cons.events.navigation.ROOM_SETTINGS_TOGGLED, this.isRoomSettings);
+    }
+    this.emit(
+      cons.events.navigation.ROOM_SELECTED,
+      this.selectedRoomId,
+      prevSelectedRoomId,
+      eventId,
+    );
+  }
+
+  _selectTabWithRoom(roomId) {
+    const { roomList, accountData } = this.initMatrix;
+    const { categorizedSpaces } = accountData;
+
+    if (roomList.isOrphan(roomId)) {
+      if (roomList.directs.has(roomId)) {
+        this._selectSpace(null, true, false);
+        this._selectTab(cons.tabs.DIRECTS, false);
+        return;
+      }
+      this._selectSpace(null, true, false);
+      this._selectTab(cons.tabs.HOME, false);
+      return;
+    }
+
+    const parents = roomList.roomIdToParents.get(roomId);
+
+    if (parents.has(this.selectedSpaceId)) {
+      return;
+    }
+
+    if (categorizedSpaces.has(this.selectedSpaceId)) {
+      const categories = roomList.getCategorizedSpaces([this.selectedSpaceId]);
+      if ([...parents].find((pId) => categories.has(pId))) {
+        // No need to select tab
+        // As one of parent is child of selected categorized space.
+        return;
+      }
+    }
+
+    const spaceInPath = [...this.selectedSpacePath].reverse().find((sId) => parents.has(sId));
+    if (spaceInPath) {
+      this._selectSpace(spaceInPath, false, false);
+      return;
+    }
+
+    if (roomList.directs.has(roomId)) {
+      this._selectSpace(null, true, false);
+      this._selectTab(cons.tabs.DIRECTS, false);
+      return;
+    }
+
+    if (parents.size > 0) {
+      const sortedParents = [...parents].sort((p1, p2) => {
+        const t1 = this.spaceToRoom.get(p1)?.timestamp ?? 0;
+        const t2 = this.spaceToRoom.get(p2)?.timestamp ?? 0;
+        return t2 - t1;
+      });
+      this._selectSpace(sortedParents[0], true, false);
+      this._selectTab(sortedParents[0], false);
+    }
+  }
+
+  _getLatestActiveRoomId(roomIds) {
+    const mx = this.initMatrix.matrixClient;
+
+    let ts = 0;
+    let roomId = null;
+    roomIds.forEach((childId) => {
+      const room = mx.getRoom(childId);
+      if (!room) return;
+      const newTs = room.getLastActiveTimestamp();
+      if (newTs > ts) {
+        ts = newTs;
+        roomId = childId;
+      }
+    });
+    return roomId;
+  }
+
+  _getLatestSelectedRoomId(spaceIds) {
+    let ts = 0;
+    let roomId = null;
+
+    spaceIds.forEach((sId) => {
+      const data = this.spaceToRoom.get(sId);
+      if (!data) return;
+      const newTs = data.timestamp;
+      if (newTs > ts) {
+        ts = newTs;
+        roomId = data.roomId;
+      }
+    });
+    return roomId;
+  }
+
+  _selectTab(tabId, selectRoom = true) {
+    this.selectedTab = tabId;
+    if (selectRoom) this._selectRoomWithTab(this.selectedTab);
+    this.emit(cons.events.navigation.TAB_SELECTED, this.selectedTab);
+  }
+
+  _selectSpace(roomId, asRoot, selectRoom = true) {
+    this._addToSpacePath(roomId, asRoot);
+    this.selectedSpaceId = roomId;
+    if (!asRoot && selectRoom) this._selectRoomWithSpace(this.selectedSpaceId);
+    this.emit(cons.events.navigation.SPACE_SELECTED, this.selectedSpaceId);
+  }
+
+  _selectRoomWithSpace(spaceId) {
+    if (!spaceId) return;
+    const { roomList, accountData, matrixClient } = this.initMatrix;
+    const { categorizedSpaces } = accountData;
+
+    const data = this.spaceToRoom.get(spaceId);
+    if (data && !categorizedSpaces.has(spaceId)) {
+      this._selectRoom(data.roomId);
+      return;
+    }
+
+    const children = [];
+
+    if (categorizedSpaces.has(spaceId)) {
+      const categories = roomList.getCategorizedSpaces([spaceId]);
+
+      const latestSelectedRoom = this._getLatestSelectedRoomId([...categories.keys()]);
+
+      if (latestSelectedRoom) {
+        this._selectRoom(latestSelectedRoom);
+        return;
+      }
+
+      categories?.forEach((categoryId) => {
+        categoryId?.forEach((childId) => {
+          children.push(childId);
+        });
+      });
+    } else {
+      roomList.getSpaceChildren(spaceId).forEach((id) => {
+        if (matrixClient.getRoom(id)?.isSpaceRoom() === false) {
+          children.push(id);
+        }
+      });
+    }
+
+    if (!children) {
+      this._selectRoom(null);
+      return;
+    }
+
+    this._selectRoom(this._getLatestActiveRoomId(children));
+  }
+
+  _selectRoomWithTab(tabId) {
+    const { roomList } = this.initMatrix;
+    if (tabId === cons.tabs.HOME || tabId === cons.tabs.DIRECTS) {
+      const data = this.spaceToRoom.get(tabId);
+      if (data) {
+        this._selectRoom(data.roomId);
+        return;
+      }
+      const children = tabId === cons.tabs.HOME ? roomList.getOrphanRooms() : [...roomList.directs];
+      this._selectRoom(this._getLatestActiveRoomId(children));
+      return;
+    }
+    this._selectRoomWithSpace(tabId);
+  }
+
   removeRecentRoom(roomId) {
     if (typeof roomId !== 'string') return;
     const roomIdIndex = this.recentRooms.indexOf(roomId);
@@ -59,40 +276,19 @@ class Navigation extends EventEmitter {
   navigate(action) {
     const actions = {
       [cons.actions.navigation.SELECT_TAB]: () => {
-        this.selectedTab = action.tabId;
-        if (this.selectedTab !== cons.tabs.DIRECTS) {
-          if (this.selectedTab === cons.tabs.HOME) {
-            this.selectedSpacePath = [cons.tabs.HOME];
-            this.selectedSpaceId = null;
-          } else {
-            this.selectedSpacePath = [this.selectedTab];
-            this.selectedSpaceId = this.selectedTab;
-          }
-          this.emit(cons.events.navigation.SPACE_SELECTED, this.selectedSpaceId);
-        } else this.selectedSpaceId = null;
-        this.emit(cons.events.navigation.TAB_SELECTED, this.selectedTab);
+        const roomId = (
+          action.tabId !== cons.tabs.HOME && action.tabId !== cons.tabs.DIRECTS
+        ) ? action.tabId : null;
+
+        this._selectSpace(roomId, true);
+        this._selectTab(action.tabId);
       },
       [cons.actions.navigation.SELECT_SPACE]: () => {
-        this._setSpacePath(action.roomId);
-        this.selectedSpaceId = action.roomId === cons.tabs.HOME ? null : action.roomId;
-        this.emit(cons.events.navigation.SPACE_SELECTED, this.selectedSpaceId);
+        this._selectSpace(action.roomId, false);
       },
       [cons.actions.navigation.SELECT_ROOM]: () => {
-        const prevSelectedRoomId = this.selectedRoomId;
-        this.selectedRoomId = action.roomId;
-        this.removeRecentRoom(prevSelectedRoomId);
-        this.addRecentRoom(prevSelectedRoomId);
-        this.removeRecentRoom(this.selectedRoomId);
-        if (this.isRoomSettings && typeof this.selectedRoomId === 'string') {
-          this.isRoomSettings = !this.isRoomSettings;
-          this.emit(cons.events.navigation.ROOM_SETTINGS_TOGGLED, this.isRoomSettings);
-        }
-        this.emit(
-          cons.events.navigation.ROOM_SELECTED,
-          this.selectedRoomId,
-          prevSelectedRoomId,
-          action.eventId,
-        );
+        if (action.roomId) this._selectTabWithRoom(action.roomId);
+        this._selectRoom(action.roomId, action.eventId);
       },
       [cons.actions.navigation.OPEN_SPACE_SETTINGS]: () => {
         this.emit(cons.events.navigation.SPACE_SETTINGS_OPENED, action.roomId, action.tabText);