handle error in loading screen (#1823)
authorAjay Bura <32841439+ajbura@users.noreply.github.com>
Mon, 22 Jul 2024 10:47:19 +0000 (16:17 +0530)
committerGitHub <noreply@github.com>
Mon, 22 Jul 2024 10:47:19 +0000 (20:47 +1000)
* handle client boot error in loading screen

* use sync state hook in client root

* add loading screen options

* removed extra condition in loading finish

* add sync connection status bar

63 files changed:
src/app/features/room-nav/RoomNavItem.tsx
src/app/features/room/Room.tsx
src/app/features/room/RoomTimeline.tsx
src/app/features/room/RoomTombstone.tsx
src/app/features/room/RoomViewHeader.tsx
src/app/hooks/useAccountData.js
src/app/hooks/useCommands.ts
src/app/hooks/useCrossSigningStatus.js
src/app/hooks/useDeviceList.js
src/app/hooks/useSyncState.ts
src/app/molecules/global-notification/GlobalNotification.jsx
src/app/molecules/global-notification/IgnoreUserList.jsx
src/app/molecules/global-notification/KeywordNotification.jsx
src/app/molecules/image-pack/ImagePack.jsx
src/app/molecules/image-pack/ImagePackUpload.jsx
src/app/molecules/image-upload/ImageUpload.jsx
src/app/molecules/import-export-e2e-room-keys/ExportE2ERoomKeys.jsx
src/app/molecules/import-export-e2e-room-keys/ImportE2ERoomKeys.jsx
src/app/molecules/room-aliases/RoomAliases.jsx
src/app/molecules/room-emojis/RoomEmojis.jsx
src/app/molecules/room-encryption/RoomEncryption.jsx
src/app/molecules/room-history-visibility/RoomHistoryVisibility.jsx
src/app/molecules/room-members/RoomMembers.jsx
src/app/molecules/room-notification/RoomNotification.jsx
src/app/molecules/room-permissions/RoomPermissions.jsx
src/app/molecules/room-profile/RoomProfile.jsx
src/app/molecules/room-visibility/RoomVisibility.jsx
src/app/molecules/space-add-existing/SpaceAddExisting.jsx
src/app/organisms/create-room/CreateRoom.jsx
src/app/organisms/emoji-verification/EmojiVerification.jsx
src/app/organisms/invite-user/InviteUser.jsx
src/app/organisms/join-alias/JoinAlias.jsx
src/app/organisms/profile-editor/ProfileEditor.jsx
src/app/organisms/profile-viewer/ProfileViewer.jsx
src/app/organisms/room/RoomSettings.jsx
src/app/organisms/search/Search.jsx
src/app/organisms/settings/AuthRequest.jsx
src/app/organisms/settings/CrossSigning.jsx
src/app/organisms/settings/DeviceManage.jsx
src/app/organisms/settings/KeyBackup.jsx
src/app/organisms/settings/SecretStorageAccess.jsx
src/app/organisms/settings/Settings.jsx
src/app/organisms/space-settings/SpaceSettings.jsx
src/app/pages/client/ClientLayout.tsx
src/app/pages/client/ClientRoot.tsx
src/app/pages/client/SyncStatus.tsx [new file with mode: 0644]
src/app/pages/client/WelcomePage.tsx
src/app/pages/client/direct/Direct.tsx
src/app/pages/client/home/Home.tsx
src/app/pages/client/inbox/Notifications.tsx
src/app/pages/client/sidebar/DirectTab.tsx
src/app/pages/client/sidebar/HomeTab.tsx
src/app/pages/client/sidebar/SpaceTabs.tsx
src/app/pages/client/space/Space.tsx
src/client/action/notifications.js [deleted file]
src/client/action/notifications.ts [new file with mode: 0644]
src/client/action/room.js
src/client/initMatrix.js [deleted file]
src/client/initMatrix.ts [new file with mode: 0644]
src/client/mx.ts [deleted file]
src/index.scss
src/util/matrixUtil.js
src/util/sort.js

index 8ecc81a31e7a3acfc9e72329cd33ce57b2a3d83e..281c5b777b6843a4da48629c62f21f8895c0f0dc 100644 (file)
@@ -53,7 +53,7 @@ const RoomNavItemMenu = forwardRef<HTMLDivElement, RoomNavItemMenuProps>(
     const canInvite = canDoAction('invite', getPowerLevel(mx.getUserId() ?? ''));
 
     const handleMarkAsRead = () => {
-      markAsRead(room.roomId);
+      markAsRead(mx, room.roomId);
       requestClose();
     };
 
index fd578ec6cd5cf1d2e1513d8b9193ebee2bb901d2..19ac53e15dc5b9c15713ebb045d5d64dc693ce6a 100644 (file)
@@ -11,10 +11,12 @@ import { PowerLevelsContextProvider, usePowerLevels } from '../../hooks/usePower
 import { useRoom } from '../../hooks/useRoom';
 import { useKeyDown } from '../../hooks/useKeyDown';
 import { markAsRead } from '../../../client/action/notifications';
+import { useMatrixClient } from '../../hooks/useMatrixClient';
 
 export function Room() {
   const { eventId } = useParams();
   const room = useRoom();
+  const mx = useMatrixClient();
 
   const [isDrawer] = useSetting(settingsAtom, 'isPeopleDrawer');
   const screenSize = useScreenSizeContext();
@@ -25,7 +27,7 @@ export function Room() {
     useCallback(
       (evt) => {
         if (isKeyHotkey('escape', evt)) {
-          markAsRead(room.roomId);
+          markAsRead(mx, room.roomId);
         }
       },
       [room.roomId]
index 14cdb56e84727ed96e3b760bf5a1debd39efa5a9..b9bfb8434cd687228aae89398e9d6214b8adc9c5 100644 (file)
@@ -597,7 +597,7 @@ export function RoomTimeline({ room, eventId, roomInputRef, editor }: RoomTimeli
         // so timeline can be updated with evt like: edits, reactions etc
         if (atBottomRef.current) {
           if (document.hasFocus() && (!unreadInfo || mEvt.getSender() === mx.getUserId())) {
-            requestAnimationFrame(() => markAsRead(mEvt.getRoomId()));
+            requestAnimationFrame(() => markAsRead(mx, mEvt.getRoomId()));
           }
 
           if (document.hasFocus()) {
@@ -658,15 +658,15 @@ export function RoomTimeline({ room, eventId, roomInputRef, editor }: RoomTimeli
 
   const tryAutoMarkAsRead = useCallback(() => {
     if (!unreadInfo) {
-      requestAnimationFrame(() => markAsRead(room.roomId));
+      requestAnimationFrame(() => markAsRead(mx, room.roomId));
       return;
     }
     const evtTimeline = getEventTimeline(room, unreadInfo.readUptoEventId);
     const latestTimeline = evtTimeline && getFirstLinkedTimeline(evtTimeline, Direction.Forward);
     if (latestTimeline === room.getLiveTimeline()) {
-      requestAnimationFrame(() => markAsRead(room.roomId));
+      requestAnimationFrame(() => markAsRead(mx, room.roomId));
     }
-  }, [room, unreadInfo]);
+  }, [mx, room, unreadInfo]);
 
   const debounceSetAtBottom = useDebounce(
     useCallback((entry: IntersectionObserverEntry) => {
@@ -832,7 +832,7 @@ export function RoomTimeline({ room, eventId, roomInputRef, editor }: RoomTimeli
   };
 
   const handleMarkAsRead = () => {
-    markAsRead(room.roomId);
+    markAsRead(mx, room.roomId);
   };
 
   const handleOpenReply: MouseEventHandler<HTMLButtonElement> = useCallback(
index bd8afdad896189d0e23cb7071b9545db2de8f2d2..e3f8251f4b3bea0201c657657ccbcec41b0d28a9 100644 (file)
@@ -40,28 +40,30 @@ export function RoomTombstone({ roomId, body, replacementRoomId }: RoomTombstone
           </Text>
         )}
       </Box>
-      {replacementRoom?.getMyMembership() === Membership.Join ||
-      joinState.status === AsyncStatus.Success ? (
-        <Button onClick={handleOpen} size="300" variant="Success" fill="Solid" radii="300">
-          <Text size="B300">Open New Room</Text>
-        </Button>
-      ) : (
-        <Button
-          onClick={handleJoin}
-          size="300"
-          variant="Primary"
-          fill="Solid"
-          radii="300"
-          before={
-            joinState.status === AsyncStatus.Loading && (
-              <Spinner size="100" variant="Primary" fill="Solid" />
-            )
-          }
-          disabled={joinState.status === AsyncStatus.Loading}
-        >
-          <Text size="B300">Join New Room</Text>
-        </Button>
-      )}
+      <Box shrink="No">
+        {replacementRoom?.getMyMembership() === Membership.Join ||
+        joinState.status === AsyncStatus.Success ? (
+          <Button onClick={handleOpen} size="300" variant="Success" fill="Solid" radii="300">
+            <Text size="B300">Open New Room</Text>
+          </Button>
+        ) : (
+          <Button
+            onClick={handleJoin}
+            size="300"
+            variant="Primary"
+            fill="Solid"
+            radii="300"
+            before={
+              joinState.status === AsyncStatus.Loading && (
+                <Spinner size="100" variant="Primary" fill="Solid" />
+              )
+            }
+            disabled={joinState.status === AsyncStatus.Loading}
+          >
+            <Text size="B300">Join New Room</Text>
+          </Button>
+        )}
+      </Box>
     </RoomInputPlaceholder>
   );
 }
index aa267c5375edbd2134e71cbc3c4abbf0d9f5715a..6750f92370d075efe86bb51a45a50a350fcf3a56 100644 (file)
@@ -74,7 +74,7 @@ const RoomMenu = forwardRef<HTMLDivElement, RoomMenuProps>(
     const canInvite = canDoAction('invite', getPowerLevel(mx.getUserId() ?? ''));
 
     const handleMarkAsRead = () => {
-      markAsRead(room.roomId);
+      markAsRead(mx, room.roomId);
       requestClose();
     };
 
index ed654d977c96c4340bdd89db5625604311c86c20..43cc11ed74b3f4a9ecfaf6a9b8cd9f3115d8a136 100644 (file)
@@ -1,10 +1,9 @@
 /* eslint-disable import/prefer-default-export */
 import { useState, useEffect } from 'react';
-
-import initMatrix from '../../client/initMatrix';
+import { useMatrixClient } from './useMatrixClient';
 
 export function useAccountData(eventType) {
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const [event, setEvent] = useState(mx.getAccountData(eventType));
 
   useEffect(() => {
@@ -16,7 +15,7 @@ export function useAccountData(eventType) {
     return () => {
       mx.removeListener('accountData', handleChange);
     };
-  }, [eventType]);
+  }, [mx, eventType]);
 
   return event;
 }
index 3c82951492c4fa1c68225681d305dfd95d913399..182d13070407b163d1f2f671c2e662c713987cde 100644 (file)
@@ -92,9 +92,9 @@ export const useCommands = (mx: MatrixClient, room: Room): CommandRecord => {
               return;
             }
           }
-          const devices = await Promise.all(userIds.map(hasDevices));
+          const devices = await Promise.all(userIds.map(uid => hasDevices(mx, uid)));
           const isEncrypt = devices.every((hasDevice) => hasDevice);
-          const result = await roomActions.createDM(userIds, isEncrypt);
+          const result = await roomActions.createDM(mx, userIds, isEncrypt);
           navigateRoom(result.room_id);
         },
       },
@@ -106,7 +106,7 @@ export const useCommands = (mx: MatrixClient, room: Room): CommandRecord => {
           const roomIds = rawIds.filter(
             (idOrAlias) => isRoomId(idOrAlias) || isRoomAlias(idOrAlias)
           );
-          roomIds.map((id) => roomActions.join(id));
+          roomIds.map((id) => roomActions.join(mx, id));
         },
       },
       [Command.Leave]: {
@@ -127,7 +127,7 @@ export const useCommands = (mx: MatrixClient, room: Room): CommandRecord => {
         description: 'Invite user to room. Example: /invite userId1 userId2 [-r reason]',
         exe: async (payload) => {
           const { users, reason } = parseUsersAndReason(payload);
-          users.map((id) => roomActions.invite(room.roomId, id, reason));
+          users.map((id) => mx.invite(room.roomId, id, reason));
         },
       },
       [Command.DisInvite]: {
@@ -135,7 +135,7 @@ export const useCommands = (mx: MatrixClient, room: Room): CommandRecord => {
         description: 'Disinvite user to room. Example: /disinvite userId1 userId2 [-r reason]',
         exe: async (payload) => {
           const { users, reason } = parseUsersAndReason(payload);
-          users.map((id) => roomActions.kick(room.roomId, id, reason));
+          users.map((id) => mx.kick(room.roomId, id, reason));
         },
       },
       [Command.Kick]: {
@@ -143,7 +143,7 @@ export const useCommands = (mx: MatrixClient, room: Room): CommandRecord => {
         description: 'Kick user from room. Example: /kick userId1 userId2 [-r reason]',
         exe: async (payload) => {
           const { users, reason } = parseUsersAndReason(payload);
-          users.map((id) => roomActions.kick(room.roomId, id, reason));
+          users.map((id) => mx.kick(room.roomId, id, reason));
         },
       },
       [Command.Ban]: {
@@ -151,7 +151,7 @@ export const useCommands = (mx: MatrixClient, room: Room): CommandRecord => {
         description: 'Ban user from room. Example: /ban userId1 userId2 [-r reason]',
         exe: async (payload) => {
           const { users, reason } = parseUsersAndReason(payload);
-          users.map((id) => roomActions.ban(room.roomId, id, reason));
+          users.map((id) => mx.ban(room.roomId, id, reason));
         },
       },
       [Command.UnBan]: {
@@ -160,7 +160,7 @@ export const useCommands = (mx: MatrixClient, room: Room): CommandRecord => {
         exe: async (payload) => {
           const rawIds = payload.split(' ');
           const users = rawIds.filter((id) => isUserId(id));
-          users.map((id) => roomActions.unban(room.roomId, id));
+          users.map((id) => mx.unban(room.roomId, id));
         },
       },
       [Command.Ignore]: {
@@ -169,7 +169,7 @@ export const useCommands = (mx: MatrixClient, room: Room): CommandRecord => {
         exe: async (payload) => {
           const rawIds = payload.split(' ');
           const userIds = rawIds.filter((id) => isUserId(id));
-          if (userIds.length > 0) roomActions.ignore(userIds);
+          if (userIds.length > 0) roomActions.ignore(mx, userIds);
         },
       },
       [Command.UnIgnore]: {
@@ -178,7 +178,7 @@ export const useCommands = (mx: MatrixClient, room: Room): CommandRecord => {
         exe: async (payload) => {
           const rawIds = payload.split(' ');
           const userIds = rawIds.filter((id) => isUserId(id));
-          if (userIds.length > 0) roomActions.unignore(userIds);
+          if (userIds.length > 0) roomActions.unignore(mx, userIds);
         },
       },
       [Command.MyRoomNick]: {
@@ -187,7 +187,7 @@ export const useCommands = (mx: MatrixClient, room: Room): CommandRecord => {
         exe: async (payload) => {
           const nick = payload.trim();
           if (nick === '') return;
-          roomActions.setMyRoomNick(room.roomId, nick);
+          roomActions.setMyRoomNick(mx, room.roomId, nick);
         },
       },
       [Command.MyRoomAvatar]: {
@@ -195,7 +195,7 @@ export const useCommands = (mx: MatrixClient, room: Room): CommandRecord => {
         description: 'Change profile picture in current room. Example /myroomavatar mxc://xyzabc',
         exe: async (payload) => {
           if (payload.match(/^mxc:\/\/\S+$/)) {
-            roomActions.setMyRoomAvatar(room.roomId, payload);
+            roomActions.setMyRoomAvatar(mx, room.roomId, payload);
           }
         },
       },
@@ -203,14 +203,14 @@ export const useCommands = (mx: MatrixClient, room: Room): CommandRecord => {
         name: Command.ConvertToDm,
         description: 'Convert room to direct message',
         exe: async () => {
-          roomActions.convertToDm(room.roomId);
+          roomActions.convertToDm(mx, room.roomId);
         },
       },
       [Command.ConvertToRoom]: {
         name: Command.ConvertToRoom,
         description: 'Convert direct message to room',
         exe: async () => {
-          roomActions.convertToRoom(room.roomId);
+          roomActions.convertToRoom(mx, room.roomId);
         },
       },
     }),
index 845c54629e8aa53a5bc64872c688446dc887c84c..990076e6b3708795ebefa2d353a7fec242931e71 100644 (file)
@@ -1,12 +1,12 @@
 /* eslint-disable import/prefer-default-export */
 import { useState, useEffect } from 'react';
 
-import initMatrix from '../../client/initMatrix';
 import { hasCrossSigningAccountData } from '../../util/matrixUtil';
+import { useMatrixClient } from './useMatrixClient';
 
 export function useCrossSigningStatus() {
-  const mx = initMatrix.matrixClient;
-  const [isCSEnabled, setIsCSEnabled] = useState(hasCrossSigningAccountData());
+  const mx = useMatrixClient();
+  const [isCSEnabled, setIsCSEnabled] = useState(hasCrossSigningAccountData(mx));
 
   useEffect(() => {
     if (isCSEnabled) return undefined;
@@ -20,6 +20,6 @@ export function useCrossSigningStatus() {
     return () => {
       mx.removeListener('accountData', handleAccountData);
     };
-  }, [isCSEnabled === false]);
+  }, [mx, isCSEnabled]);
   return isCSEnabled;
 }
index 2cce0fe500500bb6b09fcc47ae48da6a4fe46f5b..7daaad1f3cab7c15fc91058a0bcb004558371834 100644 (file)
@@ -1,10 +1,9 @@
 /* eslint-disable import/prefer-default-export */
 import { useState, useEffect } from 'react';
-
-import initMatrix from '../../client/initMatrix';
+import { useMatrixClient } from './useMatrixClient';
 
 export function useDeviceList() {
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const [deviceList, setDeviceList] = useState(null);
 
   useEffect(() => {
@@ -27,6 +26,6 @@ export function useDeviceList() {
       mx.removeListener('crypto.devicesUpdated', handleDevicesUpdate);
       isMounted = false;
     };
-  }, []);
+  }, [mx]);
   return deviceList;
 }
index 4b2f4163261c9a58a73c11cd66d0f23fb20d74f2..be8a6ef2fbd1c09ff4d59493621e482c5a9c3683 100644 (file)
@@ -2,13 +2,13 @@ import { ClientEvent, ClientEventHandlerMap, MatrixClient } from 'matrix-js-sdk'
 import { useEffect } from 'react';
 
 export const useSyncState = (
-  mx: MatrixClient,
+  mx: MatrixClient | undefined,
   onChange: ClientEventHandlerMap[ClientEvent.Sync]
 ): void => {
   useEffect(() => {
-    mx.on(ClientEvent.Sync, onChange);
+    mx?.on(ClientEvent.Sync, onChange);
     return () => {
-      mx.removeListener(ClientEvent.Sync, onChange);
+      mx?.removeListener(ClientEvent.Sync, onChange);
     };
   }, [mx, onChange]);
 };
index 865582ce918234e825756e5bbb2352df199b0ddc..b115c9dcda4f2a7f8c8749e6372e9b6289c49160 100644 (file)
@@ -1,6 +1,5 @@
 import React from 'react';
 
-import initMatrix from '../../../client/initMatrix';
 import { openReusableContextMenu } from '../../../client/action/navigation';
 import { getEventCords } from '../../../util/common';
 
@@ -14,6 +13,7 @@ import NotificationSelector from './NotificationSelector';
 import ChevronBottomIC from '../../../../public/res/ic/outlined/chevron-bottom.svg';
 
 import { useAccountData } from '../../hooks/useAccountData';
+import { useMatrixClient } from '../../hooks/useMatrixClient';
 
 export const notifType = {
   ON: 'on',
@@ -52,7 +52,7 @@ export function getTypeActions(type, highlightValue = false) {
 }
 
 function useGlobalNotif() {
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const pushRules = useAccountData('m.push_rules')?.getContent();
   const underride = pushRules?.global?.underride ?? [];
   const rulesToType = {
index 87ee6272f407f1e1c1a80657c96c103d8c893cea..1b44a0417ae65e3259c9425bd96be16ff31688c5 100644 (file)
@@ -1,7 +1,6 @@
 import React from 'react';
 import './IgnoreUserList.scss';
 
-import initMatrix from '../../../client/initMatrix';
 import * as roomActions from '../../../client/action/room';
 
 import Text from '../../atoms/text/Text';
@@ -14,10 +13,12 @@ import SettingTile from '../setting-tile/SettingTile';
 import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
 
 import { useAccountData } from '../../hooks/useAccountData';
+import { useMatrixClient } from '../../hooks/useMatrixClient';
 
 function IgnoreUserList() {
   useAccountData('m.ignored_user_list');
-  const ignoredUsers = initMatrix.matrixClient.getIgnoredUsers();
+  const mx = useMatrixClient();
+  const ignoredUsers = mx.getIgnoredUsers();
 
   const handleSubmit = (evt) => {
     evt.preventDefault();
@@ -26,7 +27,7 @@ function IgnoreUserList() {
     const userIds = value.split(' ').filter((v) => v.match(/^@\S+:\S+$/));
     if (userIds.length === 0) return;
     ignoreInput.value = '';
-    roomActions.ignore(userIds);
+    roomActions.ignore(mx, userIds);
   };
 
   return (
@@ -49,7 +50,7 @@ function IgnoreUserList() {
                     key={uId}
                     text={uId}
                     iconColor={CrossIC}
-                    onClick={() => roomActions.unignore([uId])}
+                    onClick={() => roomActions.unignore(mx, [uId])}
                   />
                 ))}
               </div>
index 8484d41d16e07ffa7cf995aef100af67590fad84..7f7e4deadad61b16c440f433be74c96db42a1373 100644 (file)
@@ -1,7 +1,6 @@
 import React from 'react';
 import './KeywordNotification.scss';
 
-import initMatrix from '../../../client/initMatrix';
 import { openReusableContextMenu } from '../../../client/action/navigation';
 import { getEventCords } from '../../../util/common';
 
@@ -21,6 +20,7 @@ import { useAccountData } from '../../hooks/useAccountData';
 import {
   notifType, typeToLabel, getActionType, getTypeActions,
 } from './GlobalNotification';
+import { useMatrixClient } from '../../hooks/useMatrixClient';
 
 const DISPLAY_NAME = '.m.rule.contains_display_name';
 const ROOM_PING = '.m.rule.roomnotif';
@@ -28,7 +28,7 @@ const USERNAME = '.m.rule.contains_user_name';
 const KEYWORD = 'keyword';
 
 function useKeywordNotif() {
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const pushRules = useAccountData('m.push_rules')?.getContent();
   const override = pushRules?.global?.override ?? [];
   const content = pushRules?.global?.content ?? [];
index f88886c1b144f43e16f07515a7ca5d3a0a049802..51ffd0d35de82134fb6116fbc9bc06ce37686f71 100644 (file)
@@ -4,7 +4,6 @@ import React, {
 import PropTypes from 'prop-types';
 import './ImagePack.scss';
 
-import initMatrix from '../../../client/initMatrix';
 import { openReusableDialog } from '../../../client/action/navigation';
 import { suffixRename } from '../../../util/common';
 
@@ -19,6 +18,7 @@ import { confirmDialog } from '../confirm-dialog/ConfirmDialog';
 import ImagePackProfile from './ImagePackProfile';
 import ImagePackItem from './ImagePackItem';
 import ImagePackUpload from './ImagePackUpload';
+import { useMatrixClient } from '../../hooks/useMatrixClient';
 
 const renameImagePackItem = (shortcode) => new Promise((resolve) => {
   let isCompleted = false;
@@ -63,8 +63,7 @@ function getUsage(usage) {
   return 'both';
 }
 
-function isGlobalPack(roomId, stateKey) {
-  const mx = initMatrix.matrixClient;
+function isGlobalPack(mx, roomId, stateKey) {
   const globalContent = mx.getAccountData('im.ponies.emote_rooms')?.getContent();
   if (typeof globalContent !== 'object') return false;
 
@@ -75,13 +74,13 @@ function isGlobalPack(roomId, stateKey) {
 }
 
 function useRoomImagePack(roomId, stateKey) {
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const room = mx.getRoom(roomId);
 
-  const packEvent = room.currentState.getStateEvents('im.ponies.room_emotes', stateKey);
-  const pack = useMemo(() => (
-    ImagePackBuilder.parsePack(packEvent.getId(), packEvent.getContent())
-  ), [room, stateKey]);
+  const pack = useMemo(() => {
+    const packEvent = room.currentState.getStateEvents('im.ponies.room_emotes', stateKey);
+    return ImagePackBuilder.parsePack(packEvent.getId(), packEvent.getContent())
+  }, [room, stateKey]);
 
   const sendPackContent = (content) => {
     mx.sendStateEvent(roomId, 'im.ponies.room_emotes', content, stateKey);
@@ -94,14 +93,14 @@ function useRoomImagePack(roomId, stateKey) {
 }
 
 function useUserImagePack() {
-  const mx = initMatrix.matrixClient;
-  const packEvent = mx.getAccountData('im.ponies.user_emotes');
-  const pack = useMemo(() => (
-    ImagePackBuilder.parsePack(mx.getUserId(), packEvent?.getContent() ?? {
+  const mx = useMatrixClient();
+  const pack = useMemo(() => {
+    const packEvent = mx.getAccountData('im.ponies.user_emotes');
+    return ImagePackBuilder.parsePack(mx.getUserId(), packEvent?.getContent() ?? {
       pack: { display_name: 'Personal' },
       images: {},
     })
-  ), []);
+  }, [mx]);
 
   const sendPackContent = (content) => {
     mx.setAccountData('im.ponies.user_emotes', content);
@@ -223,10 +222,10 @@ function removeGlobalImagePack(mx, roomId, stateKey) {
 }
 
 function ImagePack({ roomId, stateKey, handlePackDelete }) {
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const room = mx.getRoom(roomId);
   const [viewMore, setViewMore] = useState(false);
-  const [isGlobal, setIsGlobal] = useState(isGlobalPack(roomId, stateKey));
+  const [isGlobal, setIsGlobal] = useState(isGlobalPack(mx, roomId, stateKey));
 
   const { pack, sendPackContent } = useRoomImagePack(roomId, stateKey);
 
@@ -331,7 +330,7 @@ ImagePack.propTypes = {
 };
 
 function ImagePackUser() {
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const [viewMore, setViewMore] = useState(false);
 
   const { pack, sendPackContent } = useUserImagePack();
@@ -397,7 +396,7 @@ function ImagePackUser() {
 
 function useGlobalImagePack() {
   const [, forceUpdate] = useReducer((count) => count + 1, 0);
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
 
   const roomIdToStateKeys = new Map();
   const globalContent = mx.getAccountData('im.ponies.emote_rooms')?.getContent() ?? { rooms: {} };
@@ -419,13 +418,13 @@ function useGlobalImagePack() {
     return () => {
       mx.removeListener('accountData', handleEvent);
     };
-  }, []);
+  }, [mx]);
 
   return roomIdToStateKeys;
 }
 
 function ImagePackGlobal() {
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const roomIdToStateKeys = useGlobalImagePack();
 
   const handleChange = (roomId, stateKey) => {
index 6295de1ce9ff574af833a670ec5b454243ecccc2..cdf2e13b68f6eb1fc4b463638a986a0350dfe7f3 100644 (file)
@@ -2,7 +2,6 @@ import React, { useState, useRef } from 'react';
 import PropTypes from 'prop-types';
 import './ImagePackUpload.scss';
 
-import initMatrix from '../../../client/initMatrix';
 import { scaleDownImage } from '../../../util/common';
 
 import Text from '../../atoms/text/Text';
@@ -10,9 +9,10 @@ import Button from '../../atoms/button/Button';
 import Input from '../../atoms/input/Input';
 import IconButton from '../../atoms/button/IconButton';
 import CirclePlusIC from '../../../../public/res/ic/outlined/circle-plus.svg';
+import { useMatrixClient } from '../../hooks/useMatrixClient';
 
 function ImagePackUpload({ onUpload }) {
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const inputRef = useRef(null);
   const shortcodeRef = useRef(null);
   const [imgFile, setImgFile] = useState(null);
index 53fc7e161bfdd95b552116bd33c4a6fd105dae03..5213381f8ce3ba8c3a7a9a70e8f865b27dbfe4b6 100644 (file)
@@ -2,7 +2,6 @@ import React, { useState, useRef } from 'react';
 import PropTypes from 'prop-types';
 import './ImageUpload.scss';
 
-import initMatrix from '../../../client/initMatrix';
 
 import Text from '../../atoms/text/Text';
 import Avatar from '../../atoms/avatar/Avatar';
@@ -10,6 +9,7 @@ import Spinner from '../../atoms/spinner/Spinner';
 import RawIcon from '../../atoms/system-icons/RawIcon';
 
 import PlusIC from '../../../../public/res/ic/outlined/plus.svg';
+import { useMatrixClient } from '../../hooks/useMatrixClient';
 
 function ImageUpload({
   text, bgColor, imageSrc, onUpload, onRequestRemove,
@@ -17,12 +17,13 @@ function ImageUpload({
 }) {
   const [uploadPromise, setUploadPromise] = useState(null);
   const uploadImageRef = useRef(null);
+  const mx = useMatrixClient();
 
   async function uploadImage(e) {
     const file = e.target.files.item(0);
     if (file === null) return;
     try {
-      const uPromise = initMatrix.matrixClient.uploadContent(file);
+      const uPromise = mx.uploadContent(file);
       setUploadPromise(uPromise);
 
       const res = await uPromise;
@@ -35,7 +36,7 @@ function ImageUpload({
   }
 
   function cancelUpload() {
-    initMatrix.matrixClient.cancelUpload(uploadPromise);
+    mx.cancelUpload(uploadPromise);
     setUploadPromise(null);
     uploadImageRef.current.value = null;
   }
index b7738a6a5fc96ced48c9a186871706a447233741..1ce7e78ad6bead7295948d578f6474b3b0a7e1b5 100644 (file)
@@ -3,7 +3,6 @@ import './ExportE2ERoomKeys.scss';
 
 import FileSaver from 'file-saver';
 
-import initMatrix from '../../../client/initMatrix';
 import cons from '../../../client/state/cons';
 import { encryptMegolmKeyFile } from '../../../util/cryptE2ERoomKeys';
 
@@ -13,8 +12,10 @@ import Input from '../../atoms/input/Input';
 import Spinner from '../../atoms/spinner/Spinner';
 
 import { useStore } from '../../hooks/useStore';
+import { useMatrixClient } from '../../hooks/useMatrixClient';
 
 function ExportE2ERoomKeys() {
+  const mx = useMatrixClient();
   const isMountStore = useStore();
   const [status, setStatus] = useState({
     isOngoing: false,
@@ -40,7 +41,7 @@ function ExportE2ERoomKeys() {
       type: cons.status.IN_FLIGHT,
     });
     try {
-      const keys = await initMatrix.matrixClient.exportRoomKeys();
+      const keys = await mx.exportRoomKeys();
       if (isMountStore.getItem()) {
         setStatus({
           isOngoing: true,
index b5a44b0eae85e97714f4b2578681b73bba27fbd4..9f0ab793e69a002fdd8884bb726350b3b084c092 100644 (file)
@@ -1,7 +1,6 @@
 import React, { useState, useEffect, useRef } from 'react';
 import './ImportE2ERoomKeys.scss';
 
-import initMatrix from '../../../client/initMatrix';
 import cons from '../../../client/state/cons';
 import { decryptMegolmKeyFile } from '../../../util/cryptE2ERoomKeys';
 
@@ -14,8 +13,10 @@ import Spinner from '../../atoms/spinner/Spinner';
 import CirclePlusIC from '../../../../public/res/ic/outlined/circle-plus.svg';
 
 import { useStore } from '../../hooks/useStore';
+import { useMatrixClient } from '../../hooks/useMatrixClient';
 
 function ImportE2ERoomKeys() {
+  const mx = useMatrixClient();
   const isMountStore = useStore();
   const [keyFile, setKeyFile] = useState(null);
   const [status, setStatus] = useState({
@@ -45,7 +46,7 @@ function ImportE2ERoomKeys() {
           type: cons.status.IN_FLIGHT,
         });
       }
-      await initMatrix.matrixClient.importRoomKeys(JSON.parse(keys));
+      await mx.importRoomKeys(JSON.parse(keys));
       if (isMountStore.getItem()) {
         setStatus({
           isOngoing: false,
index d573f7d6a484e15bf0ae139b0a96fba55faa48e4..876d063fab4b9817d6f6d883dc661df6b23250a2 100644 (file)
@@ -2,7 +2,6 @@ import React, { useState, useEffect } from 'react';
 import PropTypes from 'prop-types';
 import './RoomAliases.scss';
 
-import initMatrix from '../../../client/initMatrix';
 import cons from '../../../client/state/cons';
 import { Debounce } from '../../../util/common';
 import { isRoomAliasAvailable } from '../../../util/matrixUtil';
@@ -16,8 +15,10 @@ import { MenuHeader } from '../../atoms/context-menu/ContextMenu';
 import SettingTile from '../setting-tile/SettingTile';
 
 import { useStore } from '../../hooks/useStore';
+import { useMatrixClient } from '../../hooks/useMatrixClient';
 
 function useValidate(hsString) {
+  const mx = useMatrixClient();
   const [debounce] = useState(new Debounce());
   const [validate, setValidate] = useState({ alias: null, status: cons.status.PRE_FLIGHT });
 
@@ -62,7 +63,7 @@ function useValidate(hsString) {
         msg: `validating ${alias}...`,
       });
 
-      const isValid = await isRoomAliasAvailable(alias);
+      const isValid = await isRoomAliasAvailable(mx, alias);
       setValidate(() => {
         if (e.target.value !== value) {
           return { alias: null, status: cons.status.PRE_FLIGHT };
@@ -79,8 +80,7 @@ function useValidate(hsString) {
   return [validate, setValidateToDefault, handleAliasChange];
 }
 
-function getAliases(roomId) {
-  const mx = initMatrix.matrixClient;
+function getAliases(mx, roomId) {
   const room = mx.getRoom(roomId);
 
   const main = room.getCanonicalAlias();
@@ -95,7 +95,7 @@ function getAliases(roomId) {
 }
 
 function RoomAliases({ roomId }) {
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const room = mx.getRoom(roomId);
   const userId = mx.getUserId();
   const hsString = userId.slice(userId.indexOf(':') + 1);
@@ -103,7 +103,7 @@ function RoomAliases({ roomId }) {
   const isMountedStore = useStore();
   const [isPublic, setIsPublic] = useState(false);
   const [isLocalVisible, setIsLocalVisible] = useState(false);
-  const [aliases, setAliases] = useState(getAliases(roomId));
+  const [aliases, setAliases] = useState(getAliases(mx, roomId));
   const [selectedAlias, setSelectedAlias] = useState(null);
   const [deleteAlias, setDeleteAlias] = useState(null);
   const [validate, setValidateToDefault, handleAliasChange] = useValidate(hsString);
@@ -140,7 +140,7 @@ function RoomAliases({ roomId }) {
     return () => {
       isUnmounted = true;
     };
-  }, [roomId]);
+  }, [mx, roomId]);
 
   const toggleDirectoryVisibility = () => {
     mx.setRoomDirectoryVisibility(roomId, isPublic ? 'private' : 'public');
index 94ae610744716b3b3dc235dabd21021780aa33cd..da09ea8646e9b487cebf3010e7d032b488e2b9c0 100644 (file)
@@ -2,7 +2,6 @@ import React, { useReducer, useEffect } from 'react';
 import PropTypes from 'prop-types';
 import './RoomEmojis.scss';
 
-import initMatrix from '../../../client/initMatrix';
 import { suffixRename } from '../../../util/common';
 
 import { MenuHeader } from '../../atoms/context-menu/ContextMenu';
@@ -10,9 +9,10 @@ import Text from '../../atoms/text/Text';
 import Input from '../../atoms/input/Input';
 import Button from '../../atoms/button/Button';
 import ImagePack from '../image-pack/ImagePack';
+import { useMatrixClient } from '../../hooks/useMatrixClient';
 
 function useRoomPacks(room) {
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const [, forceUpdate] = useReducer((count) => count + 1, 0);
 
   const packEvents = room.currentState.getStateEvents('im.ponies.room_emotes');
@@ -75,7 +75,7 @@ function useRoomPacks(room) {
 }
 
 function RoomEmojis({ roomId }) {
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const room = mx.getRoom(roomId);
 
   const { usablePacks, createPack, deletePack } = useRoomPacks(room);
index 1657f3631d020481d09acbc5597228f0493364b3..47250f47233e108bce2ca0531050ab89790da76c 100644 (file)
@@ -2,16 +2,16 @@ import React, { useState } from 'react';
 import PropTypes from 'prop-types';
 import './RoomEncryption.scss';
 
-import initMatrix from '../../../client/initMatrix';
 
 import Text from '../../atoms/text/Text';
 import Toggle from '../../atoms/button/Toggle';
 import SettingTile from '../setting-tile/SettingTile';
 
 import { confirmDialog } from '../confirm-dialog/ConfirmDialog';
+import { useMatrixClient } from '../../hooks/useMatrixClient';
 
 function RoomEncryption({ roomId }) {
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const room = mx.getRoom(roomId);
   const encryptionEvents = room.currentState.getStateEvents('m.room.encryption');
   const [isEncrypted, setIsEncrypted] = useState(encryptionEvents.length > 0);
index d9dd9540fa0e2dc96a993baf4a5b82fc82559b8a..c897cb3584b35831efd8fcac754b38c85f8b1683 100644 (file)
@@ -2,11 +2,11 @@ import React, { useState, useEffect, useCallback } from 'react';
 import PropTypes from 'prop-types';
 import './RoomHistoryVisibility.scss';
 
-import initMatrix from '../../../client/initMatrix';
 
 import Text from '../../atoms/text/Text';
 import RadioButton from '../../atoms/button/RadioButton';
 import { MenuItem } from '../../atoms/context-menu/ContextMenu';
+import { useMatrixClient } from '../../hooks/useMatrixClient';
 
 const visibility = {
   WORLD_READABLE: 'world_readable',
@@ -33,38 +33,33 @@ const items = [{
   type: visibility.JOINED,
 }];
 
-function setHistoryVisibility(roomId, type) {
-  const mx = initMatrix.matrixClient;
-
-  return mx.sendStateEvent(
-    roomId, 'm.room.history_visibility',
-    {
-      history_visibility: type,
-    },
-  );
-}
 
 function useVisibility(roomId) {
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const room = mx.getRoom(roomId);
 
   const [activeType, setActiveType] = useState(room.getHistoryVisibility());
   useEffect(() => {
     setActiveType(room.getHistoryVisibility());
-  }, [roomId]);
+  }, [room]);
 
   const setVisibility = useCallback((item) => {
     if (item.type === activeType.type) return;
     setActiveType(item.type);
-    setHistoryVisibility(roomId, item.type);
-  }, [activeType, roomId]);
+    mx.sendStateEvent(
+      roomId, 'm.room.history_visibility',
+      {
+        history_visibility: item.type,
+      },
+    );
+  }, [mx, activeType, roomId]);
 
   return [activeType, setVisibility];
 }
 
 function RoomHistoryVisibility({ roomId }) {
   const [activeType, setVisibility] = useVisibility(roomId);
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const userId = mx.getUserId();
   const room = mx.getRoom(roomId);
   const { currentState } = room;
index f931f9ddecc0de74d4f9de7c83e67f9a107763c9..a3928fb7d7369f8faa1da411d296b323ec507dba 100644 (file)
@@ -4,7 +4,6 @@ import React, {
 import PropTypes from 'prop-types';
 import './RoomMembers.scss';
 
-import initMatrix from '../../../client/initMatrix';
 import colorMXID from '../../../util/colorMXID';
 import { openProfileViewer } from '../../../client/action/navigation';
 import { getUsernameOfRoomMember, getPowerLabel } from '../../../util/matrixUtil';
@@ -17,11 +16,11 @@ import Input from '../../atoms/input/Input';
 import { MenuHeader } from '../../atoms/context-menu/ContextMenu';
 import SegmentedControls from '../../atoms/segmented-controls/SegmentedControls';
 import PeopleSelector from '../people-selector/PeopleSelector';
+import { useMatrixClient } from '../../hooks/useMatrixClient';
 
 const PER_PAGE_MEMBER = 50;
 
-function normalizeMembers(members) {
-  const mx = initMatrix.matrixClient;
+function normalizeMembers(mx, members) {
   return members.map((member) => ({
     userId: member.userId,
     name: getUsernameOfRoomMember(member),
@@ -33,7 +32,7 @@ function normalizeMembers(members) {
 }
 
 function useMemberOfMembership(roomId, membership) {
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const room = mx.getRoom(roomId);
   const [members, setMembers] = useState([]);
 
@@ -45,6 +44,7 @@ function useMemberOfMembership(roomId, membership) {
       if (isLoadingMembers) return;
       if (event && event?.getRoomId() !== roomId) return;
       const memberOfMembership = normalizeMembers(
+        mx,
         room.getMembersWithMembership(membership)
           .sort(memberByAtoZ).sort(memberByPowerLevel),
       );
@@ -66,7 +66,7 @@ function useMemberOfMembership(roomId, membership) {
       mx.removeListener('RoomMember.membership', updateMemberList);
       mx.removeListener('RoomMember.powerLevel', updateMemberList);
     };
-  }, [membership]);
+  }, [mx, membership]);
 
   return [members];
 }
index 821ea50845e03b2968fc802113bcde6941798157..db9707083b0f1abf678aaf30f13102fe0b064190 100644 (file)
@@ -2,7 +2,6 @@ import React, { useState, useEffect, useCallback } from 'react';
 import PropTypes from 'prop-types';
 import './RoomNotification.scss';
 
-import initMatrix from '../../../client/initMatrix';
 import cons from '../../../client/state/cons';
 
 import Text from '../../atoms/text/Text';
@@ -14,6 +13,7 @@ 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 { getNotificationType } from '../../utils/room';
+import { useMatrixClient } from '../../hooks/useMatrixClient';
 
 const items = [
   {
@@ -38,8 +38,7 @@ const items = [
   },
 ];
 
-function setRoomNotifType(roomId, newType) {
-  const mx = initMatrix.matrixClient;
+function setRoomNotifType(mx, roomId, newType) {
   let roomPushRule;
   try {
     roomPushRule = mx.getRoomPushRule('global', roomId);
@@ -108,7 +107,7 @@ function setRoomNotifType(roomId, newType) {
 }
 
 function useNotifications(roomId) {
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const [activeType, setActiveType] = useState(getNotificationType(mx, roomId));
   useEffect(() => {
     setActiveType(getNotificationType(mx, roomId));
@@ -118,9 +117,9 @@ function useNotifications(roomId) {
     (item) => {
       if (item.type === activeType.type) return;
       setActiveType(item.type);
-      setRoomNotifType(roomId, item.type);
+      setRoomNotifType(mx, roomId, item.type);
     },
-    [activeType, roomId]
+    [mx, activeType, roomId]
   );
   return [activeType, setNotification];
 }
index da8720cdd92083ff1777f7301213eda3f25aa12a..f8cd048f54267107f03be911fb94fbe95c572d2a 100644 (file)
@@ -2,7 +2,6 @@ import React, { useEffect } from 'react';
 import PropTypes from 'prop-types';
 import './RoomPermissions.scss';
 
-import initMatrix from '../../../client/initMatrix';
 import { getPowerLabel } from '../../../util/matrixUtil';
 import { openReusableContextMenu } from '../../../client/action/navigation';
 import { getEventCords } from '../../../util/common';
@@ -16,6 +15,7 @@ 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';
 
 const permissionsInfo = {
   users_default: {
@@ -157,7 +157,7 @@ const spacePermsGroups = {
 
 function useRoomStateUpdate(roomId) {
   const [, forceUpdate] = useForceUpdate();
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
 
   useEffect(() => {
     const handleStateEvent = (event) => {
@@ -169,12 +169,12 @@ function useRoomStateUpdate(roomId) {
     return () => {
       mx.removeListener('RoomState.events', handleStateEvent);
     };
-  }, [roomId]);
+  }, [mx, roomId]);
 }
 
 function RoomPermissions({ roomId }) {
   useRoomStateUpdate(roomId);
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const room = mx.getRoom(roomId);
   const pLEvent = room.currentState.getStateEvents('m.room.power_levels')[0];
   const permissions = pLEvent.getContent();
index 15273ebfd553a9174db19c9ec7316b752b99fb2b..da8ce755a914f32e426aaf01f7afd3fafd7692ab 100644 (file)
@@ -4,7 +4,6 @@ import { useAtomValue } from 'jotai';
 import Linkify from 'linkify-react';
 import './RoomProfile.scss';
 
-import initMatrix from '../../../client/initMatrix';
 import cons from '../../../client/state/cons';
 import colorMXID from '../../../util/colorMXID';
 
@@ -22,6 +21,7 @@ import { useForceUpdate } from '../../hooks/useForceUpdate';
 import { confirmDialog } from '../confirm-dialog/ConfirmDialog';
 import { mDirectAtom } from '../../state/mDirectList';
 import { LINKIFY_OPTS } from '../../plugins/react-custom-html-parser';
+import { useMatrixClient } from '../../hooks/useMatrixClient';
 
 function RoomProfile({ roomId }) {
   const isMountStore = useStore();
@@ -32,7 +32,7 @@ function RoomProfile({ roomId }) {
     type: cons.status.PRE_FLIGHT,
   });
 
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const mDirects = useAtomValue(mDirectAtom);
   const isDM = mDirects.has(roomId);
   let avatarSrc = mx.getRoom(roomId).getAvatarUrl(mx.baseUrl, 36, 36, 'crop');
@@ -67,7 +67,7 @@ function RoomProfile({ roomId }) {
       });
       setIsEditing(false);
     };
-  }, [roomId]);
+  }, [mx, roomId]);
 
   const handleOnSubmit = async (e) => {
     e.preventDefault();
index a5e8e2d08e48d92d80c2c067e605e82b76931275..6579513a86aae4166b329ee9cf2379dcfc7fc686 100644 (file)
@@ -2,7 +2,6 @@ import React, { useState, useEffect, useCallback } from 'react';
 import PropTypes from 'prop-types';
 import './RoomVisibility.scss';
 
-import initMatrix from '../../../client/initMatrix';
 
 import Text from '../../atoms/text/Text';
 import RadioButton from '../../atoms/button/RadioButton';
@@ -14,6 +13,7 @@ import HashGlobeIC from '../../../../public/res/ic/outlined/hash-globe.svg';
 import SpaceIC from '../../../../public/res/ic/outlined/space.svg';
 import SpaceLockIC from '../../../../public/res/ic/outlined/space-lock.svg';
 import SpaceGlobeIC from '../../../../public/res/ic/outlined/space-globe.svg';
+import { useMatrixClient } from '../../hooks/useMatrixClient';
 
 const visibility = {
   INVITE: 'invite',
@@ -21,8 +21,7 @@ const visibility = {
   PUBLIC: 'public',
 };
 
-function setJoinRule(roomId, type) {
-  const mx = initMatrix.matrixClient;
+function setJoinRule(mx, roomId, type) {
   let allow;
   if (type === visibility.RESTRICTED) {
     const { currentState } = mx.getRoom(roomId);
@@ -46,26 +45,26 @@ function setJoinRule(roomId, type) {
 }
 
 function useVisibility(roomId) {
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const room = mx.getRoom(roomId);
 
   const [activeType, setActiveType] = useState(room.getJoinRule());
   useEffect(() => {
     setActiveType(room.getJoinRule());
-  }, [roomId]);
+  }, [room]);
 
   const setNotification = useCallback((item) => {
     if (item.type === activeType.type) return;
     setActiveType(item.type);
-    setJoinRule(roomId, item.type);
-  }, [activeType, roomId]);
+    setJoinRule(mx, roomId, item.type);
+  }, [mx, activeType, roomId]);
 
   return [activeType, setNotification];
 }
 
 function RoomVisibility({ roomId }) {
   const [activeType, setVisibility] = useVisibility(roomId);
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const room = mx.getRoom(roomId);
   const isSpace = room.isSpaceRoom();
   const { currentState } = room;
index 2eb94a5c0d0d7560734710106a747c2949b59a6e..ff338f3f8dc49b3bd358e042fb4ae68cea37fbac 100644 (file)
@@ -3,7 +3,6 @@ import PropTypes from 'prop-types';
 import { useAtomValue } from 'jotai';
 import './SpaceAddExisting.scss';
 
-import initMatrix from '../../../client/initMatrix';
 import cons from '../../../client/state/cons';
 import navigation from '../../../client/state/navigation';
 import { joinRuleToIconSrc, getIdServer, genRoomVia } from '../../../util/matrixUtil';
@@ -27,6 +26,7 @@ import { roomToParentsAtom } from '../../state/room/roomToParents';
 import { useDirects, useRooms, useSpaces } from '../../state/hooks/roomList';
 import { allRoomsAtom } from '../../state/room-list/roomList';
 import { mDirectAtom } from '../../state/mDirectList';
+import { useMatrixClient } from '../../hooks/useMatrixClient';
 
 function SpaceAddExistingContent({ roomId, spaces: onlySpaces }) {
   const mountStore = useStore(roomId);
@@ -35,7 +35,7 @@ function SpaceAddExistingContent({ roomId, spaces: onlySpaces }) {
   const [allRoomIds, setAllRoomIds] = useState([]);
   const [selected, setSelected] = useState([]);
   const [searchIds, setSearchIds] = useState(null);
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const roomIdToParents = useAtomValue(roomToParentsAtom);
   const mDirects = useAtomValue(mDirectAtom);
   const spaces = useSpaces(mx, allRoomsAtom);
@@ -48,7 +48,7 @@ function SpaceAddExistingContent({ roomId, spaces: onlySpaces }) {
       (rId) => rId !== roomId && !roomIdToParents.get(rId)?.has(roomId)
     );
     setAllRoomIds(allIds);
-  }, [roomId, onlySpaces]);
+  }, [spaces, rooms, directs, roomIdToParents, roomId, onlySpaces]);
 
   const toggleSelection = (rId) => {
     if (process !== null) return;
@@ -215,7 +215,7 @@ function useVisibilityToggle() {
 
 function SpaceAddExisting() {
   const [data, requestClose] = useVisibilityToggle();
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const room = mx.getRoom(data?.roomId);
 
   return (
index ff00cca19ec4587204cf7cc146c696472e104e80..04b2faebe7da9829662312f0e46fee76abc09ccd 100644 (file)
@@ -2,7 +2,6 @@ import React, { useState, useEffect, useRef } from 'react';
 import PropTypes from 'prop-types';
 import './CreateRoom.scss';
 
-import initMatrix from '../../../client/initMatrix';
 import cons from '../../../client/state/cons';
 import navigation from '../../../client/state/navigation';
 import { openReusableContextMenu } from '../../../client/action/navigation';
@@ -32,6 +31,7 @@ import SpaceGlobeIC from '../../../../public/res/ic/outlined/space-globe.svg';
 import ChevronBottomIC from '../../../../public/res/ic/outlined/chevron-bottom.svg';
 import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
 import { useRoomNavigate } from '../../hooks/useRoomNavigate';
+import { useMatrixClient } from '../../hooks/useMatrixClient';
 
 function CreateRoomContent({ isSpace, parentId, onRequestClose }) {
   const [joinRule, setJoinRule] = useState(parentId ? 'restricted' : 'invite');
@@ -46,7 +46,7 @@ function CreateRoomContent({ isSpace, parentId, onRequestClose }) {
 
   const addressRef = useRef(null);
 
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const userHs = getIdServer(mx.getUserId());
 
   const handleSubmit = async (evt) => {
@@ -69,7 +69,7 @@ function CreateRoomContent({ isSpace, parentId, onRequestClose }) {
     const powerLevel = roleIndex === 1 ? 101 : undefined;
 
     try {
-      const data = await roomActions.createRoom({
+      const data = await roomActions.createRoom(mx, {
         name,
         topic,
         joinRule,
@@ -113,7 +113,7 @@ function CreateRoomContent({ isSpace, parentId, onRequestClose }) {
       if (roomAlias === '') return;
       const roomAddress = `#${roomAlias}:${userHs}`;
 
-      if (await isRoomAliasAvailable(roomAddress)) {
+      if (await isRoomAliasAvailable(mx, roomAddress)) {
         setIsValidAddress(true);
       } else {
         setIsValidAddress(false);
@@ -278,7 +278,7 @@ function useWindowToggle() {
 function CreateRoom() {
   const [create, onRequestClose] = useWindowToggle();
   const { isSpace, parentId } = create ?? {};
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const room = mx.getRoom(parentId);
 
   return (
index 1b543c052ca5ab7ddd07c1ead8dc2af504b622ca..21be477f23ccbaea00f672ea3e257d90c1591240 100644 (file)
@@ -3,7 +3,6 @@ import React, { useState, useEffect } from 'react';
 import PropTypes from 'prop-types';
 import './EmojiVerification.scss';
 
-import initMatrix from '../../../client/initMatrix';
 import cons from '../../../client/state/cons';
 import navigation from '../../../client/state/navigation';
 import { hasPrivateKey } from '../../../client/state/secretStorageKeys';
@@ -18,23 +17,24 @@ import Dialog from '../../molecules/dialog/Dialog';
 import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
 import { useStore } from '../../hooks/useStore';
 import { accessSecretStorage } from '../settings/SecretStorageAccess';
+import { useMatrixClient } from '../../hooks/useMatrixClient';
 
 function EmojiVerificationContent({ data, requestClose }) {
   const [sas, setSas] = useState(null);
   const [process, setProcess] = useState(false);
   const { request, targetDevice } = data;
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const mountStore = useStore();
   const beginStore = useStore();
 
   const beginVerification = async () => {
     if (
-      isCrossVerified(mx.deviceId) &&
+      isCrossVerified(mx, mx.deviceId) &&
       (mx.getCrossSigningId() === null ||
         (await mx.crypto.crossSigningInfo.isStoredInKeyCache('self_signing')) === false)
     ) {
-      if (!hasPrivateKey(getDefaultSSKey())) {
-        const keyData = await accessSecretStorage('Emoji verification');
+      if (!hasPrivateKey(getDefaultSSKey(mx))) {
+        const keyData = await accessSecretStorage(mx, 'Emoji verification');
         if (!keyData) {
           request.cancel();
           return;
@@ -158,7 +158,7 @@ EmojiVerificationContent.propTypes = {
 
 function useVisibilityToggle() {
   const [data, setData] = useState(null);
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
 
   useEffect(() => {
     const handleOpen = (request, targetDevice) => {
@@ -170,7 +170,7 @@ function useVisibilityToggle() {
       navigation.removeListener(cons.events.navigation.EMOJI_VERIFICATION_OPENED, handleOpen);
       mx.removeListener('crypto.verification.request', handleOpen);
     };
-  }, []);
+  }, [mx]);
 
   const requestClose = () => setData(null);
 
index 10f55f9f644b81074e44dedd70d1ac37c2d4a142..284be72e1df801963380292d3d96240dbb8033ba 100644 (file)
@@ -2,7 +2,6 @@ import React, { useState, useEffect, useRef } from 'react';
 import PropTypes from 'prop-types';
 import './InviteUser.scss';
 
-import initMatrix from '../../../client/initMatrix';
 import * as roomActions from '../../../client/action/room';
 import { hasDevices } from '../../../util/matrixUtil';
 
@@ -18,6 +17,7 @@ import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
 import UserIC from '../../../../public/res/ic/outlined/user.svg';
 import { useRoomNavigate } from '../../hooks/useRoomNavigate';
 import { getDMRoomFor } from '../../utils/matrix';
+import { useMatrixClient } from '../../hooks/useMatrixClient';
 
 function InviteUser({ isOpen, roomId, searchTerm, onRequestClose }) {
   const [isSearching, updateIsSearching] = useState(false);
@@ -34,7 +34,7 @@ function InviteUser({ isOpen, roomId, searchTerm, onRequestClose }) {
 
   const usernameRef = useRef(null);
 
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const { navigateRoom } = useRoomNavigate();
 
   function getMapCopy(myMap) {
@@ -118,7 +118,7 @@ function InviteUser({ isOpen, roomId, searchTerm, onRequestClose }) {
       procUserError.delete(userId);
       updateUserProcError(getMapCopy(procUserError));
 
-      const result = await roomActions.createDM(userId, await hasDevices(userId));
+      const result = await roomActions.createDM(mx, userId, await hasDevices(mx, userId));
       roomIdToUserId.set(result.room_id, userId);
       updateRoomIdToUserId(getMapCopy(roomIdToUserId));
       onDMCreated(result.room_id);
@@ -137,7 +137,7 @@ function InviteUser({ isOpen, roomId, searchTerm, onRequestClose }) {
       procUserError.delete(userId);
       updateUserProcError(getMapCopy(procUserError));
 
-      await roomActions.invite(roomId, userId);
+      await mx.invite(roomId, userId);
 
       invitedUserIds.add(userId);
       updateInvitedUserIds(new Set(Array.from(invitedUserIds)));
index 9fa5542dfe1ba050d294f6bbb9906324228a2474..99cf6e6ee813ac52ae3b5968bad5e9cb03cdd3e3 100644 (file)
@@ -2,7 +2,6 @@ import React, { useState, useEffect } from 'react';
 import PropTypes from 'prop-types';
 import './JoinAlias.scss';
 
-import initMatrix from '../../../client/initMatrix';
 import cons from '../../../client/state/cons';
 import navigation from '../../../client/state/navigation';
 import { join } from '../../../client/action/room';
@@ -18,6 +17,7 @@ import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
 
 import { useStore } from '../../hooks/useStore';
 import { useRoomNavigate } from '../../hooks/useRoomNavigate';
+import { useMatrixClient } from '../../hooks/useMatrixClient';
 
 const ALIAS_OR_ID_REG = /^[#|!].+:.+\..+$/;
 
@@ -25,7 +25,7 @@ function JoinAliasContent({ term, requestClose }) {
   const [process, setProcess] = useState(false);
   const [error, setError] = useState(undefined);
 
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const mountStore = useStore();
 
   const { navigateRoom } = useRoomNavigate();
@@ -63,7 +63,7 @@ function JoinAliasContent({ term, requestClose }) {
       }
     }
     try {
-      const roomId = await join(alias, false, via);
+      const roomId = await join(mx, alias, false, via);
       if (!mountStore.getItem()) return;
       openRoom(roomId);
     } catch {
index c21c82fa479d08d68fad04a5c1fe38f5f869f65e..9570935574e91440ae6b5108a3630d3c719d49e8 100644 (file)
@@ -1,7 +1,6 @@
 import React, { useState, useEffect, useRef } from 'react';
 import PropTypes from 'prop-types';
 
-import initMatrix from '../../../client/initMatrix';
 import colorMXID from '../../../util/colorMXID';
 
 import Text from '../../atoms/text/Text';
@@ -14,10 +13,11 @@ import PencilIC from '../../../../public/res/ic/outlined/pencil.svg';
 import { confirmDialog } from '../../molecules/confirm-dialog/ConfirmDialog';
 
 import './ProfileEditor.scss';
+import { useMatrixClient } from '../../hooks/useMatrixClient';
 
 function ProfileEditor({ userId }) {
   const [isEditing, setIsEditing] = useState(false);
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const user = mx.getUser(mx.getUserId());
 
   const displayNameRef = useRef(null);
@@ -37,7 +37,7 @@ function ProfileEditor({ userId }) {
     return () => {
       isMounted = false;
     };
-  }, [userId]);
+  }, [mx, userId]);
 
   const handleAvatarUpload = async (url) => {
     if (url === null) {
index b19c9c8676c203c6c8089379657c4046ced8d52d..b4ab747304a5a393c1da5d1574659cda1a93a7ab 100644 (file)
@@ -2,7 +2,6 @@ import React, { useState, useEffect, useRef } from 'react';
 import PropTypes from 'prop-types';
 import './ProfileViewer.scss';
 
-import initMatrix from '../../../client/initMatrix';
 import cons from '../../../client/state/cons';
 import navigation from '../../../client/state/navigation';
 import { openReusableContextMenu } from '../../../client/action/navigation';
@@ -36,9 +35,10 @@ import { useForceUpdate } from '../../hooks/useForceUpdate';
 import { confirmDialog } from '../../molecules/confirm-dialog/ConfirmDialog';
 import { useRoomNavigate } from '../../hooks/useRoomNavigate';
 import { getDMRoomFor } from '../../utils/matrix';
+import { useMatrixClient } from '../../hooks/useMatrixClient';
 
 function ModerationTools({ roomId, userId }) {
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const room = mx.getRoom(roomId);
   const roomMember = room.getMember(userId);
 
@@ -56,13 +56,13 @@ function ModerationTools({ roomId, userId }) {
   const handleKick = (e) => {
     e.preventDefault();
     const kickReason = e.target.elements['kick-reason']?.value.trim();
-    roomActions.kick(roomId, userId, kickReason !== '' ? kickReason : undefined);
+    mx.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);
+    mx.ban(roomId, userId, banReason !== '' ? banReason : undefined);
   };
 
   return (
@@ -90,7 +90,7 @@ ModerationTools.propTypes = {
 function SessionInfo({ userId }) {
   const [devices, setDevices] = useState(null);
   const [isVisible, setIsVisible] = useState(false);
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
 
   useEffect(() => {
     let isUnmounted = false;
@@ -111,7 +111,7 @@ function SessionInfo({ userId }) {
     return () => {
       isUnmounted = true;
     };
-  }, [userId]);
+  }, [mx, userId]);
 
   function renderSessionChips() {
     if (!isVisible) return null;
@@ -139,7 +139,7 @@ function SessionInfo({ userId }) {
       >
         <Text variant="b2">{`View ${
           devices?.length > 0
-            ? `${devices.length} ${devices.length == 1 ? 'session' : 'sessions'}`
+            ? `${devices.length} ${devices.length === 1 ? 'session' : 'sessions'}`
             : 'sessions'
         }`}</Text>
       </MenuItem>
@@ -155,10 +155,10 @@ SessionInfo.propTypes = {
 function ProfileFooter({ roomId, userId, onRequestClose }) {
   const [isCreatingDM, setIsCreatingDM] = useState(false);
   const [isIgnoring, setIsIgnoring] = useState(false);
-  const [isUserIgnored, setIsUserIgnored] = useState(initMatrix.matrixClient.isUserIgnored(userId));
+  const mx = useMatrixClient();
+  const [isUserIgnored, setIsUserIgnored] = useState(mx.isUserIgnored(userId));
 
   const isMountedRef = useRef(true);
-  const mx = initMatrix.matrixClient;
   const { navigateRoom } = useRoomNavigate();
   const room = mx.getRoom(roomId);
   const member = room.getMember(userId);
@@ -182,10 +182,10 @@ function ProfileFooter({ roomId, userId, onRequestClose }) {
   };
 
   useEffect(() => {
-    setIsUserIgnored(initMatrix.matrixClient.isUserIgnored(userId));
+    setIsUserIgnored(mx.isUserIgnored(userId));
     setIsIgnoring(false);
     setIsInviting(false);
-  }, [userId]);
+  }, [mx, userId]);
 
   const openDM = async () => {
     // Check and open if user already have a DM with userId.
@@ -199,7 +199,7 @@ function ProfileFooter({ roomId, userId, onRequestClose }) {
     // Create new DM
     try {
       setIsCreatingDM(true);
-      const result = await roomActions.createDM(userId, await hasDevices(userId));
+      const result = await roomActions.createDM(mx, userId, await hasDevices(mx, userId));
       onCreated(result.room_id);
     } catch {
       if (isMountedRef.current === false) return;
@@ -213,9 +213,9 @@ function ProfileFooter({ roomId, userId, onRequestClose }) {
     try {
       setIsIgnoring(true);
       if (isIgnored) {
-        await roomActions.unignore([userId]);
+        await roomActions.unignore(mx, [userId]);
       } else {
-        await roomActions.ignore([userId]);
+        await roomActions.ignore(mx, [userId]);
       }
 
       if (isMountedRef.current === false) return;
@@ -230,9 +230,9 @@ function ProfileFooter({ roomId, userId, onRequestClose }) {
     try {
       setIsInviting(true);
       let isInviteSent = false;
-      if (isInvited) await roomActions.kick(roomId, userId);
+      if (isInvited) await mx.kick(roomId, userId);
       else {
-        await roomActions.invite(roomId, userId);
+        await mx.invite(roomId, userId);
         isInviteSent = true;
       }
       if (isMountedRef.current === false) return;
@@ -249,7 +249,7 @@ function ProfileFooter({ roomId, userId, onRequestClose }) {
         {isCreatingDM ? 'Creating room...' : 'Message'}
       </Button>
       {isBanned && canIKick && (
-        <Button variant="positive" onClick={() => roomActions.unban(roomId, userId)}>
+        <Button variant="positive" onClick={() => mx.unban(roomId, userId)}>
           Unban
         </Button>
       )}
@@ -306,7 +306,7 @@ function useToggleDialog() {
 }
 
 function useRerenderOnProfileChange(roomId, userId) {
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const [, forceUpdate] = useForceUpdate();
   useEffect(() => {
     const handleProfileChange = (mEvent, member) => {
@@ -323,19 +323,19 @@ function useRerenderOnProfileChange(roomId, userId) {
       mx.removeListener('RoomMember.powerLevel', handleProfileChange);
       mx.removeListener('RoomMember.membership', handleProfileChange);
     };
-  }, [roomId, userId]);
+  }, [mx, roomId, userId]);
 }
 
 function ProfileViewer() {
   const [isOpen, roomId, userId, closeDialog, handleAfterClose] = useToggleDialog();
   useRerenderOnProfileChange(roomId, userId);
 
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const room = mx.getRoom(roomId);
 
   const renderProfile = () => {
     const roomMember = room.getMember(userId);
-    const username = roomMember ? getUsernameOfRoomMember(roomMember) : getUsername(userId);
+    const username = roomMember ? getUsernameOfRoomMember(roomMember) : getUsername(mx, userId);
     const avatarMxc = roomMember?.getMxcAvatarUrl?.() || mx.getUser(userId)?.avatarUrl;
     const avatarUrl =
       avatarMxc && avatarMxc !== 'null' ? mx.mxcUrlToHttp(avatarMxc, 80, 80, 'crop') : null;
@@ -364,9 +364,9 @@ function ProfileViewer() {
           'caution'
         );
         if (!isConfirmed) return;
-        roomActions.setPowerLevel(roomId, userId, newPowerLevel);
+        roomActions.setPowerLevel(mx, roomId, userId, newPowerLevel);
       } else {
-        roomActions.setPowerLevel(roomId, userId, newPowerLevel);
+        roomActions.setPowerLevel(mx, roomId, userId, newPowerLevel);
       }
     };
 
index 2b8f28e66f7d77851be82808c75ae08aefa6d6a3..69f977b29130865cf1fcbc68fc9646af9ee47cb2 100644 (file)
@@ -2,7 +2,6 @@ import React, { useState, useEffect } from 'react';
 import PropTypes from 'prop-types';
 import './RoomSettings.scss';
 
-import initMatrix from '../../../client/initMatrix';
 import cons from '../../../client/state/cons';
 import navigation from '../../../client/state/navigation';
 
@@ -30,6 +29,7 @@ import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
 import { confirmDialog } from '../../molecules/confirm-dialog/ConfirmDialog';
 import PopupWindow from '../../molecules/popup-window/PopupWindow';
 import IconButton from '../../atoms/button/IconButton';
+import { useMatrixClient } from '../../hooks/useMatrixClient';
 
 const tabText = {
   GENERAL: 'General',
@@ -68,7 +68,7 @@ const tabItems = [
 ];
 
 function GeneralSettings({ roomId }) {
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const room = mx.getRoom(roomId);
 
   return (
@@ -155,7 +155,8 @@ function RoomSettings() {
   const [window, requestClose] = useWindowToggle(setSelectedTab);
   const isOpen = window !== null;
   const roomId = window?.roomId;
-  const room = initMatrix.matrixClient.getRoom(roomId);
+  const mx = useMatrixClient();
+  const room = mx.getRoom(roomId);
 
   const handleTabChange = (tabItem) => {
     setSelectedTab(tabItem);
index 0990a03fabbc7d54e33d8e753b44c42e8ff0b4c4..ebdac3962ee2fb0b3055feeb1f13338647d759ba 100644 (file)
@@ -2,12 +2,10 @@ import React, { useState, useEffect, useRef, useCallback } from 'react';
 import { useAtomValue } from 'jotai';
 import './Search.scss';
 
-import initMatrix from '../../../client/initMatrix';
 import cons from '../../../client/state/cons';
 import navigation from '../../../client/state/navigation';
 import AsyncSearch from '../../../util/AsyncSearch';
 import { joinRuleToIconSrc } from '../../../util/matrixUtil';
-import { roomIdByActivity } from '../../../util/sort';
 
 import Text from '../../atoms/text/Text';
 import RawIcon from '../../atoms/system-icons/RawIcon';
@@ -27,6 +25,8 @@ import { allRoomsAtom } from '../../state/room-list/roomList';
 import { mDirectAtom } from '../../state/mDirectList';
 import { useKeyDown } from '../../hooks/useKeyDown';
 import { openSearch } from '../../../client/action/navigation';
+import { useMatrixClient } from '../../hooks/useMatrixClient';
+import { factoryRoomIdByActivity } from '../../utils/sort';
 
 function useVisiblityToggle(setResult) {
   const [isOpen, setIsOpen] = useState(false);
@@ -77,9 +77,7 @@ function useVisiblityToggle(setResult) {
   return [isOpen, requestClose];
 }
 
-function mapRoomIds(roomIds, directs, roomIdToParents) {
-  const mx = initMatrix.matrixClient;
-
+function mapRoomIds(mx, roomIds, directs, roomIdToParents) {
   return roomIds.map((roomId) => {
     const room = mx.getRoom(roomId);
     const parentSet = roomIdToParents.get(roomId);
@@ -107,7 +105,7 @@ function Search() {
   const [asyncSearch] = useState(new AsyncSearch());
   const [isOpen, requestClose] = useVisiblityToggle(setResult);
   const searchRef = useRef(null);
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const { navigateRoom, navigateSpace } = useRoomNavigate();
   const mDirects = useAtomValue(mDirectAtom);
   const spaces = useSpaces(mx, allRoomsAtom);
@@ -141,8 +139,8 @@ function Search() {
       ids = [...rooms].concat([...directs], [...spaces]);
     }
 
-    ids.sort(roomIdByActivity);
-    const mappedIds = mapRoomIds(ids, directs, roomToParents);
+    ids.sort(factoryRoomIdByActivity(mx));
+    const mappedIds = mapRoomIds(mx, ids, directs, roomToParents);
     asyncSearch.setup(mappedIds, { keys: 'name', isContain: true, limit: 20 });
     if (prefix) handleSearchResults(mappedIds, prefix);
     else asyncSearch.search(term);
@@ -150,7 +148,7 @@ function Search() {
 
   const loadRecentRooms = () => {
     const recentRooms = [];
-    handleSearchResults(mapRoomIds(recentRooms, directs, roomToParents).reverse());
+    handleSearchResults(mapRoomIds(mx, recentRooms, directs, roomToParents).reverse());
   };
 
   const handleAfterOpen = () => {
index ca07c2a2213ab5984e1759b111bbe04938dae747..f897f83c2f798c63d75ec5511e3f97390519d6fb 100644 (file)
@@ -2,7 +2,6 @@ import React, { useState } from 'react';
 import PropTypes from 'prop-types';
 import './AuthRequest.scss';
 
-import initMatrix from '../../../client/initMatrix';
 import { openReusableDialog } from '../../../client/action/navigation';
 
 import Text from '../../atoms/text/Text';
@@ -11,6 +10,7 @@ import Input from '../../atoms/input/Input';
 import Spinner from '../../atoms/spinner/Spinner';
 
 import { useStore } from '../../hooks/useStore';
+import { getSecret } from '../../../client/state/auth';
 
 let lastUsedPassword;
 const getAuthId = (password) => ({
@@ -18,7 +18,7 @@ const getAuthId = (password) => ({
   password,
   identifier: {
     type: 'm.id.user',
-    user: initMatrix.matrixClient.getUserId(),
+    user: getSecret().userId,
   },
 });
 
index 9d848d5ae3b3e130fb1429baca3bc30abf21ddba..b993e32f81a0bff7a867c5477b5c62eeda6dc764 100644 (file)
@@ -4,7 +4,6 @@ import './CrossSigning.scss';
 import FileSaver from 'file-saver';
 import { Formik } from 'formik';
 
-import initMatrix from '../../../client/initMatrix';
 import { openReusableDialog } from '../../../client/action/navigation';
 import { copyToClipboard } from '../../../util/common';
 import { clearSecretStorageKeys } from '../../../client/state/secretStorageKeys';
@@ -17,6 +16,7 @@ import SettingTile from '../../molecules/setting-tile/SettingTile';
 
 import { authRequest } from './AuthRequest';
 import { useCrossSigningStatus } from '../../hooks/useCrossSigningStatus';
+import { useMatrixClient } from '../../hooks/useMatrixClient';
 
 const failedDialog = () => {
   const renderFailure = (requestClose) => (
@@ -73,9 +73,9 @@ const securityKeyDialog = (key) => {
 function CrossSigningSetup() {
   const initialValues = { phrase: '', confirmPhrase: '' };
   const [genWithPhrase, setGenWithPhrase] = useState(undefined);
+  const mx = useMatrixClient();
 
   const setup = async (securityPhrase = undefined) => {
-    const mx = initMatrix.matrixClient;
     setGenWithPhrase(typeof securityPhrase === 'string');
     const recoveryKey = await mx.createRecoveryKeyFromPassphrase(securityPhrase);
     clearSecretStorageKeys();
index 4a4f141cd141ed1040c59532a25d63d286b25860..9fa8273e77d69008ce03063e67c3e2837577ef7f 100644 (file)
@@ -2,7 +2,6 @@ import React, { useState, useEffect } from 'react';
 import './DeviceManage.scss';
 import dateFormat from 'dateformat';
 
-import initMatrix from '../../../client/initMatrix';
 import { isCrossVerified } from '../../../util/matrixUtil';
 import { openReusableDialog, openEmojiVerification } from '../../../client/action/navigation';
 
@@ -26,6 +25,7 @@ import { useStore } from '../../hooks/useStore';
 import { useDeviceList } from '../../hooks/useDeviceList';
 import { useCrossSigningStatus } from '../../hooks/useCrossSigningStatus';
 import { accessSecretStorage } from './SecretStorageAccess';
+import { useMatrixClient } from '../../hooks/useMatrixClient';
 
 const promptDeviceName = async (deviceName) => new Promise((resolve) => {
   let isCompleted = false;
@@ -63,14 +63,14 @@ const promptDeviceName = async (deviceName) => new Promise((resolve) => {
 
 function DeviceManage() {
   const TRUNCATED_COUNT = 4;
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const isCSEnabled = useCrossSigningStatus();
   const deviceList = useDeviceList();
   const [processing, setProcessing] = useState([]);
   const [truncated, setTruncated] = useState(true);
   const mountStore = useStore();
   mountStore.setItem(true);
-  const isMeVerified = isCrossVerified(mx.deviceId);
+  const isMeVerified = isCrossVerified(mx, mx.deviceId);
 
   useEffect(() => {
     setProcessing([]);
@@ -130,7 +130,7 @@ function DeviceManage() {
   };
 
   const verifyWithKey = async (device) => {
-    const keyData = await accessSecretStorage('Session verification');
+    const keyData = await accessSecretStorage(mx, 'Session verification');
     if (!keyData) return;
     addToProcessing(device);
     await mx.checkOwnCrossSigningTrust();
@@ -191,7 +191,7 @@ function DeviceManage() {
             )}
             {isCurrentDevice && (
               <Text style={{ marginTop: 'var(--sp-ultra-tight)' }} variant="b3">
-                {`Session Key: ${initMatrix.matrixClient.getDeviceEd25519Key().match(/.{1,4}/g).join(' ')}`}
+                {`Session Key: ${mx.getDeviceEd25519Key().match(/.{1,4}/g).join(' ')}`}
               </Text>
             )}
           </>
@@ -204,7 +204,7 @@ function DeviceManage() {
   const verified = [];
   const noEncryption = [];
   deviceList.sort((a, b) => b.last_seen_ts - a.last_seen_ts).forEach((device) => {
-    const isVerified = isCrossVerified(device.device_id);
+    const isVerified = isCrossVerified(mx, device.device_id);
     if (isVerified === true) {
       verified.push(device);
     } else if (isVerified === false) {
index b4f2125e206707f7a18bbb8840ae158469b5fe56..47be511a3c2aa59b71f2c4820a97c4ecbe2a690c 100644 (file)
@@ -3,7 +3,6 @@ import React, { useState, useEffect } from 'react';
 import PropTypes from 'prop-types';
 import './KeyBackup.scss';
 
-import initMatrix from '../../../client/initMatrix';
 import { openReusableDialog } from '../../../client/action/navigation';
 import { deletePrivateKey } from '../../../client/state/secretStorageKeys';
 
@@ -22,10 +21,11 @@ import DownloadIC from '../../../../public/res/ic/outlined/download.svg';
 
 import { useStore } from '../../hooks/useStore';
 import { useCrossSigningStatus } from '../../hooks/useCrossSigningStatus';
+import { useMatrixClient } from '../../hooks/useMatrixClient';
 
 function CreateKeyBackupDialog({ keyData }) {
   const [done, setDone] = useState(false);
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const mountStore = useStore();
 
   const doBackup = async () => {
@@ -80,7 +80,7 @@ CreateKeyBackupDialog.propTypes = {
 
 function RestoreKeyBackupDialog({ keyData }) {
   const [status, setStatus] = useState(false);
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const mountStore = useStore();
 
   const restoreBackup = async () => {
@@ -150,7 +150,7 @@ RestoreKeyBackupDialog.propTypes = {
 
 function DeleteKeyBackupDialog({ requestClose }) {
   const [isDeleting, setIsDeleting] = useState(false);
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const mountStore = useStore();
 
   const deleteBackup = async () => {
@@ -187,7 +187,7 @@ DeleteKeyBackupDialog.propTypes = {
 };
 
 function KeyBackup() {
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const isCSEnabled = useCrossSigningStatus();
   const [keyBackup, setKeyBackup] = useState(undefined);
   const mountStore = useStore();
@@ -215,7 +215,7 @@ function KeyBackup() {
   }, [isCSEnabled]);
 
   const openCreateKeyBackup = async () => {
-    const keyData = await accessSecretStorage('Create Key Backup');
+    const keyData = await accessSecretStorage(mx, 'Create Key Backup');
     if (keyData === null) return;
 
     openReusableDialog(
@@ -228,7 +228,7 @@ function KeyBackup() {
   };
 
   const openRestoreKeyBackup = async () => {
-    const keyData = await accessSecretStorage('Restore Key Backup');
+    const keyData = await accessSecretStorage(mx, 'Restore Key Backup');
     if (keyData === null) return;
 
     openReusableDialog(
index e4fceb078e11be65f4377511fc8a692b4dbbc2a4..02882a41c502dba686049844f421aeed56bb8627 100644 (file)
@@ -3,7 +3,6 @@ import PropTypes from 'prop-types';
 import './SecretStorageAccess.scss';
 import { deriveKey } from 'matrix-js-sdk/lib/crypto/key_passphrase';
 
-import initMatrix from '../../../client/initMatrix';
 import { openReusableDialog } from '../../../client/action/navigation';
 import { getDefaultSSKey, getSSKeyInfo } from '../../../util/matrixUtil';
 import { storePrivateKey, hasPrivateKey, getPrivateKey } from '../../../client/state/secretStorageKeys';
@@ -14,11 +13,12 @@ import Input from '../../atoms/input/Input';
 import Spinner from '../../atoms/spinner/Spinner';
 
 import { useStore } from '../../hooks/useStore';
+import { useMatrixClient } from '../../hooks/useMatrixClient';
 
 function SecretStorageAccess({ onComplete }) {
-  const mx = initMatrix.matrixClient;
-  const sSKeyId = getDefaultSSKey();
-  const sSKeyInfo = getSSKeyInfo(sSKeyId);
+  const mx = useMatrixClient();
+  const sSKeyId = getDefaultSSKey(mx);
+  const sSKeyInfo = getSSKeyInfo(mx, sSKeyId);
   const isPassphrase = !!sSKeyInfo.passphrase;
   const [withPhrase, setWithPhrase] = useState(isPassphrase);
   const [process, setProcess] = useState(false);
@@ -98,12 +98,13 @@ SecretStorageAccess.propTypes = {
 };
 
 /**
+ * @param {MatrixClient} mx Matrix client
  * @param {string} title Title of secret storage access dialog
  * @returns {Promise<keyData | null>} resolve to keyData or null
  */
-export const accessSecretStorage = (title) => new Promise((resolve) => {
+export const accessSecretStorage = (mx, title) => new Promise((resolve) => {
   let isCompleted = false;
-  const defaultSSKey = getDefaultSSKey();
+  const defaultSSKey = getDefaultSSKey(mx);
   if (hasPrivateKey(defaultSSKey)) {
     resolve({ keyId: defaultSSKey, privateKey: getPrivateKey(defaultSSKey) });
     return;
index 47abb45c01fd6796245a687c2c3be0e889999c19..f2951b812972a0bd23451fde3a202996e824c3db 100644 (file)
@@ -1,7 +1,7 @@
 import React, { useState, useEffect } from 'react';
 import './Settings.scss';
 
-import initMatrix from '../../../client/initMatrix';
+import { clearCacheAndReload, logoutClient } from '../../../client/initMatrix';
 import cons from '../../../client/state/cons';
 import settings from '../../../client/state/settings';
 import navigation from '../../../client/state/navigation';
@@ -47,6 +47,7 @@ import { useSetting } from '../../state/hooks/settings';
 import { settingsAtom } from '../../state/settings';
 import { isMacOS } from '../../utils/user-agent';
 import { KeySymbol } from '../../utils/key-symbol';
+import { useMatrixClient } from '../../hooks/useMatrixClient';
 
 function AppearanceSection() {
   const [, updateState] = useState({});
@@ -332,6 +333,8 @@ function SecuritySection() {
 }
 
 function AboutSection() {
+  const mx = useMatrixClient();
+  
   return (
     <div className="settings-about">
       <div className="settings-about__card">
@@ -348,7 +351,7 @@ function AboutSection() {
             <div className="settings-about__btns">
               <Button onClick={() => window.open('https://github.com/ajbura/cinny')}>Source code</Button>
               <Button onClick={() => window.open('https://cinny.in/#sponsor')}>Support</Button>
-              <Button onClick={() => initMatrix.clearCacheAndReload()} variant="danger">Clear cache & reload</Button>
+              <Button onClick={() => clearCacheAndReload(mx)} variant="danger">Clear cache & reload</Button>
             </div>
           </div>
         </div>
@@ -437,11 +440,12 @@ function useWindowToggle(setSelectedTab) {
 function Settings() {
   const [selectedTab, setSelectedTab] = useState(tabItems[0]);
   const [isOpen, requestClose] = useWindowToggle(setSelectedTab);
+  const mx = useMatrixClient();
 
   const handleTabChange = (tabItem) => setSelectedTab(tabItem);
   const handleLogout = async () => {
     if (await confirmDialog('Logout', 'Are you sure that you want to logout your session?', 'Logout', 'danger')) {
-      initMatrix.logout();
+      logoutClient(mx);
     }
   };
 
@@ -462,7 +466,7 @@ function Settings() {
     >
       {isOpen && (
         <div className="settings-window__content">
-          <ProfileEditor userId={initMatrix.matrixClient.getUserId()} />
+          <ProfileEditor userId={mx.getUserId()} />
           <Tabs
             items={tabItems}
             defaultSelected={tabItems.findIndex((tab) => tab.text === selectedTab.text)}
index ff6c18632750e19c3b5c68327d2ec3fda68347e9..9f2069d4729cebd57d6b7094bf1d0d13e6a5bc8c 100644 (file)
@@ -2,7 +2,6 @@ import React, { useState, useEffect } from 'react';
 import PropTypes from 'prop-types';
 import './SpaceSettings.scss';
 
-import initMatrix from '../../../client/initMatrix';
 import cons from '../../../client/state/cons';
 import navigation from '../../../client/state/navigation';
 
@@ -59,8 +58,8 @@ const tabItems = [
 ];
 
 function GeneralSettings({ roomId }) {
-  const roomName = initMatrix.matrixClient.getRoom(roomId)?.name;
   const mx = useMatrixClient();
+  const roomName = mx.getRoom(roomId)?.name;
 
   return (
     <>
@@ -124,7 +123,7 @@ function SpaceSettings() {
   const isOpen = window !== null;
   const roomId = window?.roomId;
 
-  const mx = initMatrix.matrixClient;
+  const mx = useMatrixClient();
   const room = mx.getRoom(roomId);
 
   const handleTabChange = (tabItem) => {
index 208d12e4806129eedf46826f110beb84f0c621b6..4a077ba6a0bceeca86728ff310fb8fcfbd659430 100644 (file)
@@ -7,7 +7,7 @@ type ClientLayoutProps = {
 };
 export function ClientLayout({ nav, children }: ClientLayoutProps) {
   return (
-    <Box style={{ height: '100%' }}>
+    <Box grow="Yes">
       <Box shrink="No">{nav}</Box>
       <Box grow="Yes">{children}</Box>
     </Box>
index c36adfc64696565cfe08c81d0e7beef5e4b30df0..a590e0bc5a09badb2f641e413b424aa8b5cda3fa 100644 (file)
@@ -1,6 +1,27 @@
-import { Box, Spinner, Text } from 'folds';
-import React, { ReactNode, useEffect, useState } from 'react';
-import initMatrix from '../../../client/initMatrix';
+import {
+  Box,
+  Button,
+  config,
+  Dialog,
+  Icon,
+  IconButton,
+  Icons,
+  Menu,
+  MenuItem,
+  PopOut,
+  RectCords,
+  Spinner,
+  Text,
+} from 'folds';
+import { HttpApiEvent, HttpApiEventHandlerMap, MatrixClient } from 'matrix-js-sdk';
+import FocusTrap from 'focus-trap-react';
+import React, { MouseEventHandler, ReactNode, useCallback, useEffect, useState } from 'react';
+import {
+  clearCacheAndReload,
+  initClient,
+  logoutClient,
+  startClient,
+} from '../../../client/initMatrix';
 import { getSecret } from '../../../client/state/auth';
 import { SplashScreen } from '../../components/splash-screen';
 import { CapabilitiesAndMediaConfigLoader } from '../../components/CapabilitiesAndMediaConfigLoader';
@@ -13,6 +34,10 @@ import Dialogs from '../../organisms/pw/Dialogs';
 import ReusableContextMenu from '../../atoms/context-menu/ReusableContextMenu';
 import { useSetting } from '../../state/hooks/settings';
 import { settingsAtom } from '../../state/settings';
+import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback';
+import { useSyncState } from '../../hooks/useSyncState';
+import { stopPropagation } from '../../utils/keyboard';
+import { SyncStatus } from './SyncStatus';
 
 function SystemEmojiFeature() {
   const [twitterEmoji] = useSetting(settingsAtom, 'twitterEmoji');
@@ -37,6 +62,89 @@ function ClientRootLoading() {
   );
 }
 
+function ClientRootOptions({ mx }: { mx: MatrixClient }) {
+  const [menuAnchor, setMenuAnchor] = useState<RectCords>();
+
+  const handleToggle: MouseEventHandler<HTMLButtonElement> = (evt) => {
+    const cords = evt.currentTarget.getBoundingClientRect();
+    setMenuAnchor((currentState) => {
+      if (currentState) return undefined;
+      return cords;
+    });
+  };
+
+  return (
+    <IconButton
+      style={{
+        position: 'absolute',
+        top: config.space.S100,
+        right: config.space.S100,
+      }}
+      variant="Background"
+      fill="None"
+      onClick={handleToggle}
+    >
+      <Icon size="200" src={Icons.VerticalDots} />
+      <PopOut
+        anchor={menuAnchor}
+        position="Bottom"
+        align="End"
+        offset={6}
+        content={
+          <FocusTrap
+            focusTrapOptions={{
+              initialFocus: false,
+              returnFocusOnDeactivate: false,
+              onDeactivate: () => setMenuAnchor(undefined),
+              clickOutsideDeactivates: true,
+              isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
+              isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
+              escapeDeactivates: stopPropagation,
+            }}
+          >
+            <Menu>
+              <Box direction="Column" gap="100" style={{ padding: config.space.S100 }}>
+                <MenuItem onClick={() => clearCacheAndReload(mx)} size="300" radii="300">
+                  <Text as="span" size="T300" truncate>
+                    Clear Cache and Reload
+                  </Text>
+                </MenuItem>
+                <MenuItem
+                  onClick={() => logoutClient(mx)}
+                  size="300"
+                  radii="300"
+                  variant="Critical"
+                  fill="None"
+                >
+                  <Text as="span" size="T300" truncate>
+                    Logout
+                  </Text>
+                </MenuItem>
+              </Box>
+            </Menu>
+          </FocusTrap>
+        }
+      />
+    </IconButton>
+  );
+}
+
+const useLogoutListener = (mx?: MatrixClient) => {
+  useEffect(() => {
+    const handleLogout: HttpApiEventHandlerMap[HttpApiEvent.SessionLoggedOut] = async () => {
+      mx?.stopClient();
+      await mx?.clearStores();
+      window.localStorage.clear();
+      window.location.reload();
+    };
+
+    mx?.on(HttpApiEvent.SessionLoggedOut, handleLogout);
+    return () => {
+      mx?.removeListener(HttpApiEvent.SessionLoggedOut, handleLogout);
+    };
+  }, [mx]);
+};
+
 type ClientRootProps = {
   children: ReactNode;
 };
@@ -44,30 +152,71 @@ export function ClientRoot({ children }: ClientRootProps) {
   const [loading, setLoading] = useState(true);
   const { baseUrl } = getSecret();
 
+  const [loadState, loadMatrix] = useAsyncCallback<MatrixClient, Error, []>(
+    useCallback(() => initClient(getSecret() as any), [])
+  );
+  const mx = loadState.status === AsyncStatus.Success ? loadState.data : undefined;
+  const [startState, startMatrix] = useAsyncCallback<void, Error, [MatrixClient]>(
+    useCallback((m) => startClient(m), [])
+  );
+
+  useLogoutListener(mx);
+
   useEffect(() => {
-    const handleStart = () => {
-      setLoading(false);
-    };
-    initMatrix.once('init_loading_finished', handleStart);
-    if (!initMatrix.matrixClient) initMatrix.init();
-    return () => {
-      initMatrix.removeListener('init_loading_finished', handleStart);
-    };
-  }, []);
+    if (loadState.status === AsyncStatus.Idle) {
+      loadMatrix();
+    }
+  }, [loadState, loadMatrix]);
+
+  useEffect(() => {
+    if (mx && !mx.clientRunning) {
+      startMatrix(mx);
+    }
+  }, [mx, startMatrix]);
+
+  useSyncState(
+    mx,
+    useCallback((state) => {
+      if (state === 'PREPARED') {
+        setLoading(false);
+      }
+    }, [])
+  );
 
   return (
     <SpecVersions baseUrl={baseUrl!}>
-      {loading ? (
+      {mx && <SyncStatus mx={mx} />}
+      {loading && mx && <ClientRootOptions mx={mx} />}
+      {(loadState.status === AsyncStatus.Error || startState.status === AsyncStatus.Error) && (
+        <SplashScreen>
+          <Box direction="Column" grow="Yes" alignItems="Center" justifyContent="Center" gap="400">
+            <Dialog>
+              <Box direction="Column" gap="400" style={{ padding: config.space.S400 }}>
+                {loadState.status === AsyncStatus.Error && (
+                  <Text>{`Failed to load. ${loadState.error.message}`}</Text>
+                )}
+                {startState.status === AsyncStatus.Error && (
+                  <Text>{`Failed to load. ${startState.error.message}`}</Text>
+                )}
+                <Button variant="Critical" onClick={loadMatrix}>
+                  <Text as="span" size="B400">
+                    Retry
+                  </Text>
+                </Button>
+              </Box>
+            </Dialog>
+          </Box>
+        </SplashScreen>
+      )}
+      {loading || !mx ? (
         <ClientRootLoading />
       ) : (
-        <MatrixClientProvider value={initMatrix.matrixClient!}>
+        <MatrixClientProvider value={mx}>
           <CapabilitiesAndMediaConfigLoader>
             {(capabilities, mediaConfig) => (
               <CapabilitiesProvider value={capabilities ?? {}}>
                 <MediaConfigProvider value={mediaConfig ?? {}}>
                   {children}
-
-                  {/* TODO: remove these components after navigation refactor */}
                   <Windows />
                   <Dialogs />
                   <ReusableContextMenu />
diff --git a/src/app/pages/client/SyncStatus.tsx b/src/app/pages/client/SyncStatus.tsx
new file mode 100644 (file)
index 0000000..9cd4b0b
--- /dev/null
@@ -0,0 +1,87 @@
+import { MatrixClient, SyncState } from 'matrix-js-sdk';
+import React, { useCallback, useState } from 'react';
+import { Box, config, Line, Text } from 'folds';
+import { useSyncState } from '../../hooks/useSyncState';
+import { ContainerColor } from '../../styles/ContainerColor.css';
+
+type StateData = {
+  current: SyncState | null;
+  previous: SyncState | null | undefined;
+};
+
+type SyncStatusProps = {
+  mx: MatrixClient;
+};
+export function SyncStatus({ mx }: SyncStatusProps) {
+  const [stateData, setStateData] = useState<StateData>({
+    current: null,
+    previous: undefined,
+  });
+
+  useSyncState(
+    mx,
+    useCallback((current, previous) => {
+      setStateData((s) => {
+        if (s.current === current && s.previous === previous) {
+          return s;
+        }
+        return { current, previous };
+      });
+    }, [])
+  );
+
+  if (
+    (stateData.current === SyncState.Prepared ||
+      stateData.current === SyncState.Syncing ||
+      stateData.current === SyncState.Catchup) &&
+    stateData.previous !== SyncState.Syncing
+  ) {
+    return (
+      <Box direction="Column" shrink="No">
+        <Box
+          className={ContainerColor({ variant: 'Success' })}
+          style={{ padding: `${config.space.S100} 0` }}
+          alignItems="Center"
+          justifyContent="Center"
+        >
+          <Text size="L400">Connecting...</Text>
+        </Box>
+        <Line variant="Success" size="300" />
+      </Box>
+    );
+  }
+
+  if (stateData.current === SyncState.Reconnecting) {
+    return (
+      <Box direction="Column" shrink="No">
+        <Box
+          className={ContainerColor({ variant: 'Warning' })}
+          style={{ padding: `${config.space.S100} 0` }}
+          alignItems="Center"
+          justifyContent="Center"
+        >
+          <Text size="L400">Connection Lost! Reconnecting...</Text>
+        </Box>
+        <Line variant="Warning" size="300" />
+      </Box>
+    );
+  }
+
+  if (stateData.current === SyncState.Error) {
+    return (
+      <Box direction="Column" shrink="No">
+        <Box
+          className={ContainerColor({ variant: 'Critical' })}
+          style={{ padding: `${config.space.S100} 0` }}
+          alignItems="Center"
+          justifyContent="Center"
+        >
+          <Text size="L400">Connection Lost!</Text>
+        </Box>
+        <Line variant="Critical" size="300" />
+      </Box>
+    );
+  }
+
+  return null;
+}
index 2486625f652fa9498993fa0e07b4e715599ce20c..5040d15f54a1ada3eafbf9cb049f41d13e227673 100644 (file)
@@ -18,7 +18,7 @@ export function WelcomePage() {
             title="Welcome to Cinny"
             subTitle={
               <span>
-                Yet anothor matrix client.{' '}
+                Yet another matrix client.{' '}
                 <a
                   href="https://github.com/cinnyapp/cinny/releases"
                   target="_blank"
index c62ef16016e3617cf7734a442107349922a233d2..cd19e33e80e864f6c23a65192575026044055d2e 100644 (file)
@@ -50,12 +50,13 @@ type DirectMenuProps = {
   requestClose: () => void;
 };
 const DirectMenu = forwardRef<HTMLDivElement, DirectMenuProps>(({ requestClose }, ref) => {
+  const mx = useMatrixClient();
   const orphanRooms = useDirectRooms();
   const unread = useRoomsUnread(orphanRooms, roomToUnreadAtom);
 
   const handleMarkAsRead = () => {
     if (!unread) return;
-    orphanRooms.forEach((rId) => markAsRead(rId));
+    orphanRooms.forEach((rId) => markAsRead(mx, rId));
     requestClose();
   };
 
index a06e108e9f8b7e871386cb1207e5e74c23abc80a..f9923f462bf05ab6ce636508e8f83e7a186b5ff7 100644 (file)
@@ -55,10 +55,11 @@ type HomeMenuProps = {
 const HomeMenu = forwardRef<HTMLDivElement, HomeMenuProps>(({ requestClose }, ref) => {
   const orphanRooms = useHomeRooms();
   const unread = useRoomsUnread(orphanRooms, roomToUnreadAtom);
+  const mx = useMatrixClient();
 
   const handleMarkAsRead = () => {
     if (!unread) return;
-    orphanRooms.forEach((rId) => markAsRead(rId));
+    orphanRooms.forEach((rId) => markAsRead(mx, rId));
     requestClose();
   };
 
index 8b9d1847c151e5665950c9d96a910615e23ae958..a01ecb8e0518e584361acce952e036d1b312ffbe 100644 (file)
@@ -356,7 +356,7 @@ function RoomNotificationsGroupComp({
     onOpen(room.roomId, eventId);
   };
   const handleMarkAsRead = () => {
-    markAsRead(room.roomId);
+    markAsRead(mx, room.roomId);
   };
 
   return (
index 0beb17d856a4554d3e5edb38320df01255c59ec4..849fc3656bf6d104c581a854fc481626f8a1b29c 100644 (file)
@@ -30,10 +30,11 @@ type DirectMenuProps = {
 const DirectMenu = forwardRef<HTMLDivElement, DirectMenuProps>(({ requestClose }, ref) => {
   const orphanRooms = useDirectRooms();
   const unread = useRoomsUnread(orphanRooms, roomToUnreadAtom);
+  const mx = useMatrixClient();
 
   const handleMarkAsRead = () => {
     if (!unread) return;
-    orphanRooms.forEach((rId) => markAsRead(rId));
+    orphanRooms.forEach((rId) => markAsRead(mx, rId));
     requestClose();
   };
 
index 41f7c64892d284fb99f2d14b59742023dd9eb5ef..dcb0a49862c434b43dae544bbc3821441482aa17 100644 (file)
@@ -31,10 +31,11 @@ type HomeMenuProps = {
 const HomeMenu = forwardRef<HTMLDivElement, HomeMenuProps>(({ requestClose }, ref) => {
   const orphanRooms = useHomeRooms();
   const unread = useRoomsUnread(orphanRooms, roomToUnreadAtom);
+  const mx = useMatrixClient();
 
   const handleMarkAsRead = () => {
     if (!unread) return;
-    orphanRooms.forEach((rId) => markAsRead(rId));
+    orphanRooms.forEach((rId) => markAsRead(mx, rId));
     requestClose();
   };
 
index 8635a35fd2b119dc3130af7c7c5ba6f27c9039aa..7b3e61e7825e4f2e9aa400b4a6a5020ee5f925b8 100644 (file)
@@ -114,7 +114,7 @@ const SpaceMenu = forwardRef<HTMLDivElement, SpaceMenuProps>(
     const unread = useRoomsUnread(allChild, roomToUnreadAtom);
 
     const handleMarkAsRead = () => {
-      allChild.forEach((childRoomId) => markAsRead(childRoomId));
+      allChild.forEach((childRoomId) => markAsRead(mx, childRoomId));
       requestClose();
     };
 
index e947373bdb4ab7ccd45b37d17626c9f19282a8bc..e280c603e50640b79b5b2cb6bec6874a5efc241c 100644 (file)
@@ -95,7 +95,7 @@ const SpaceMenu = forwardRef<HTMLDivElement, SpaceMenuProps>(({ room, requestClo
   const unread = useRoomsUnread(allChild, roomToUnreadAtom);
 
   const handleMarkAsRead = () => {
-    allChild.forEach((childRoomId) => markAsRead(childRoomId));
+    allChild.forEach((childRoomId) => markAsRead(mx, childRoomId));
     requestClose();
   };
 
diff --git a/src/client/action/notifications.js b/src/client/action/notifications.js
deleted file mode 100644 (file)
index 579c7c3..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-import initMatrix from '../initMatrix';
-
-// eslint-disable-next-line import/prefer-default-export
-export async function markAsRead(roomId) {
-  const mx = initMatrix.matrixClient;
-  const room = mx.getRoom(roomId);
-  if (!room) return;
-
-  const timeline = room.getLiveTimeline().getEvents();
-  const readEventId = room.getEventReadUpTo(mx.getUserId());
-
-  const getLatestValidEvent = () => {
-    for (let i = timeline.length - 1; i >= 0; i -= 1) {
-      const latestEvent = timeline[i];
-      if (latestEvent.getId() === readEventId) return null;
-      if (!latestEvent.isSending()) return latestEvent;
-    }
-    return null;
-  };
-  if (timeline.length === 0) return;
-  const latestEvent = getLatestValidEvent();
-  if (latestEvent === null) return;
-
-  await mx.sendReadReceipt(latestEvent);
-}
diff --git a/src/client/action/notifications.ts b/src/client/action/notifications.ts
new file mode 100644 (file)
index 0000000..17ea1ed
--- /dev/null
@@ -0,0 +1,23 @@
+import { MatrixClient } from "matrix-js-sdk";
+
+export async function markAsRead(mx: MatrixClient, roomId: string) {
+  const room = mx.getRoom(roomId);
+  if (!room) return;
+
+  const timeline = room.getLiveTimeline().getEvents();
+  const readEventId = room.getEventReadUpTo(mx.getUserId()!);
+
+  const getLatestValidEvent = () => {
+    for (let i = timeline.length - 1; i >= 0; i -= 1) {
+      const latestEvent = timeline[i];
+      if (latestEvent.getId() === readEventId) return null;
+      if (!latestEvent.isSending()) return latestEvent;
+    }
+    return null;
+  };
+  if (timeline.length === 0) return;
+  const latestEvent = getLatestValidEvent();
+  if (latestEvent === null) return;
+
+  await mx.sendReadReceipt(latestEvent);
+}
index c2d11438884fc007eed376747048cc5c3671afd4..cd4995b90163cd8e2b9670d5c2fd701a684d5925 100644 (file)
@@ -1,16 +1,13 @@
-import initMatrix from '../initMatrix';
-import appDispatcher from '../dispatcher';
-import cons from '../state/cons';
 import { getIdServer } from '../../util/matrixUtil';
 
 /**
  * https://github.com/matrix-org/matrix-react-sdk/blob/1e6c6e9d800890c732d60429449bc280de01a647/src/Rooms.js#L73
+ * @param {MatrixClient} mx Matrix client
  * @param {string} roomId Id of room to add
  * @param {string} userId User id to which dm || undefined to remove
  * @returns {Promise} A promise
  */
-function addRoomToMDirect(roomId, userId) {
-  const mx = initMatrix.matrixClient;
+function addRoomToMDirect(mx, roomId, userId) {
   const mDirectsEvent = mx.getAccountData('m.direct');
   let userIdToRoomIds = {};
 
@@ -79,24 +76,22 @@ function guessDMRoomTargetId(room, myUserId) {
   return oldestMember.userId;
 }
 
-function convertToDm(roomId) {
-  const mx = initMatrix.matrixClient;
+function convertToDm(mx, roomId) {
   const room = mx.getRoom(roomId);
-  return addRoomToMDirect(roomId, guessDMRoomTargetId(room, mx.getUserId()));
+  return addRoomToMDirect(mx, roomId, guessDMRoomTargetId(room, mx.getUserId()));
 }
 
-function convertToRoom(roomId) {
-  return addRoomToMDirect(roomId, undefined);
+function convertToRoom(mx, roomId) {
+  return addRoomToMDirect(mx, roomId, undefined);
 }
 
 /**
- *
+ * @param {MatrixClient} mx
  * @param {string} roomId
  * @param {boolean} isDM
  * @param {string[]} via
  */
-async function join(roomIdOrAlias, isDM = false, via = undefined) {
-  const mx = initMatrix.matrixClient;
+async function join(mx, roomIdOrAlias, isDM = false, via = undefined) {
   const roomIdParts = roomIdOrAlias.split(':');
   const viaServers = via || [roomIdParts[1]];
 
@@ -105,7 +100,7 @@ async function join(roomIdOrAlias, isDM = false, via = undefined) {
 
     if (isDM) {
       const targetUserId = guessDMRoomTargetId(mx.getRoom(resultRoom.roomId), mx.getUserId());
-      await addRoomToMDirect(resultRoom.roomId, targetUserId);
+      await addRoomToMDirect(mx, resultRoom.roomId, targetUserId);
     }
     return resultRoom.roomId;
   } catch (e) {
@@ -113,12 +108,11 @@ async function join(roomIdOrAlias, isDM = false, via = undefined) {
   }
 }
 
-async function create(options, isDM = false) {
-  const mx = initMatrix.matrixClient;
+async function create(mx, options, isDM = false) {
   try {
     const result = await mx.createRoom(options);
     if (isDM && typeof options.invite?.[0] === 'string') {
-      await addRoomToMDirect(result.room_id, options.invite[0]);
+      await addRoomToMDirect(mx, result.room_id, options.invite[0]);
     }
     return result;
   } catch (e) {
@@ -130,7 +124,7 @@ async function create(options, isDM = false) {
   }
 }
 
-async function createDM(userIdOrIds, isEncrypted = true) {
+async function createDM(mx, userIdOrIds, isEncrypted = true) {
   const options = {
     is_direct: true,
     invite: Array.isArray(userIdOrIds) ? userIdOrIds : [userIdOrIds],
@@ -148,11 +142,11 @@ async function createDM(userIdOrIds, isEncrypted = true) {
     });
   }
 
-  const result = await create(options, true);
+  const result = await create(mx, options, true);
   return result;
 }
 
-async function createRoom(opts) {
+async function createRoom(mx, opts) {
   // joinRule: 'public' | 'invite' | 'restricted'
   const { name, topic, joinRule } = opts;
   const alias = opts.alias ?? undefined;
@@ -162,7 +156,6 @@ async function createRoom(opts) {
   const powerLevel = opts.powerLevel ?? undefined;
   const blockFederation = opts.blockFederation ?? false;
 
-  const mx = initMatrix.matrixClient;
   const visibility = joinRule === 'public' ? 'public' : 'private';
   const options = {
     creation_content: undefined,
@@ -225,7 +218,7 @@ async function createRoom(opts) {
     });
   }
 
-  const result = await create(options);
+  const result = await create(mx, options);
 
   if (parentId) {
     await mx.sendStateEvent(parentId, 'm.space.child', {
@@ -238,51 +231,19 @@ async function createRoom(opts) {
   return result;
 }
 
-async function invite(roomId, userId, reason) {
-  const mx = initMatrix.matrixClient;
-
-  const result = await mx.invite(roomId, userId, undefined, reason);
-  return result;
-}
-
-async function kick(roomId, userId, reason) {
-  const mx = initMatrix.matrixClient;
-
-  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 ignore(userIds) {
-  const mx = initMatrix.matrixClient;
+async function ignore(mx, userIds) {
 
   let ignoredUsers = mx.getIgnoredUsers().concat(userIds);
   ignoredUsers = [...new Set(ignoredUsers)];
   await mx.setIgnoredUsers(ignoredUsers);
 }
 
-async function unignore(userIds) {
-  const mx = initMatrix.matrixClient;
-
+async function unignore(mx, userIds) {
   const ignoredUsers = mx.getIgnoredUsers();
   await mx.setIgnoredUsers(ignoredUsers.filter((id) => !userIds.includes(id)));
 }
 
-async function setPowerLevel(roomId, userId, powerLevel) {
-  const mx = initMatrix.matrixClient;
+async function setPowerLevel(mx, roomId, userId, powerLevel) {
   const room = mx.getRoom(roomId);
 
   const powerlevelEvent = room.currentState.getStateEvents('m.room.power_levels')[0];
@@ -291,8 +252,7 @@ async function setPowerLevel(roomId, userId, powerLevel) {
   return result;
 }
 
-async function setMyRoomNick(roomId, nick) {
-  const mx = initMatrix.matrixClient;
+async function setMyRoomNick(mx, roomId, nick) {
   const room = mx.getRoom(roomId);
   const mEvent = room.currentState.getStateEvents('m.room.member', mx.getUserId());
   const content = mEvent?.getContent();
@@ -303,8 +263,7 @@ async function setMyRoomNick(roomId, nick) {
   }, mx.getUserId());
 }
 
-async function setMyRoomAvatar(roomId, mxc) {
-  const mx = initMatrix.matrixClient;
+async function setMyRoomAvatar(mx, roomId, mxc) {
   const room = mx.getRoom(roomId);
   const mEvent = room.currentState.getStateEvents('m.room.member', mx.getUserId());
   const content = mEvent?.getContent();
@@ -320,7 +279,6 @@ export {
   convertToRoom,
   join,
   createDM, createRoom,
-  invite, kick, ban, unban,
   ignore, unignore,
   setPowerLevel,
   setMyRoomNick, setMyRoomAvatar,
diff --git a/src/client/initMatrix.js b/src/client/initMatrix.js
deleted file mode 100644 (file)
index 0352ff3..0000000
+++ /dev/null
@@ -1,128 +0,0 @@
-import EventEmitter from 'events';
-import * as sdk from 'matrix-js-sdk';
-import Olm from '@matrix-org/olm';
-import { logger } from 'matrix-js-sdk/lib/logger';
-
-import { getSecret } from './state/auth';
-import { cryptoCallbacks } from './state/secretStorageKeys';
-
-global.Olm = Olm;
-
-if (import.meta.env.PROD) {
-  logger.disableAll();
-}
-
-class InitMatrix extends EventEmitter {
-  async init() {
-    if (this.matrixClient || this.initializing) {
-      console.warn('Client is already initialized!')
-      return;
-    }
-    this.initializing = true;
-
-    try {
-      await this.startClient();
-      this.setupSync();
-      this.listenEvents();
-      this.initializing = false;
-    } catch {
-      this.initializing = false;
-    }
-  }
-
-  async startClient() {
-    const indexedDBStore = new sdk.IndexedDBStore({
-      indexedDB: global.indexedDB,
-      localStorage: global.localStorage,
-      dbName: 'web-sync-store',
-    });
-    await indexedDBStore.startup();
-    const secret = getSecret();
-
-    this.matrixClient = sdk.createClient({
-      baseUrl: secret.baseUrl,
-      accessToken: secret.accessToken,
-      userId: secret.userId,
-      store: indexedDBStore,
-      cryptoStore: new sdk.IndexedDBCryptoStore(global.indexedDB, 'crypto-store'),
-      deviceId: secret.deviceId,
-      timelineSupport: true,
-      cryptoCallbacks,
-      verificationMethods: [
-        'm.sas.v1',
-      ],
-    });
-
-    await this.matrixClient.initCrypto();
-
-    await this.matrixClient.startClient({
-      lazyLoadMembers: true,
-    });
-    this.matrixClient.setGlobalErrorOnUnknownDevices(false);
-    this.matrixClient.setMaxListeners(50);
-  }
-
-  setupSync() {
-    const sync = {
-      NULL: () => {
-        console.log('NULL state');
-      },
-      SYNCING: () => {
-        console.log('SYNCING state');
-      },
-      PREPARED: (prevState) => {
-        console.log('PREPARED state');
-        console.log('Previous state: ', prevState);
-        global.initMatrix = this;
-        if (prevState === null) {
-          this.emit('init_loading_finished');
-        }
-      },
-      RECONNECTING: () => {
-        console.log('RECONNECTING state');
-      },
-      CATCHUP: () => {
-        console.log('CATCHUP state');
-      },
-      ERROR: () => {
-        console.log('ERROR state');
-      },
-      STOPPED: () => {
-        console.log('STOPPED state');
-      },
-    };
-    this.matrixClient.on('sync', (state, prevState) => sync[state](prevState));
-  }
-
-  listenEvents() {
-    this.matrixClient.on('Session.logged_out', async () => {
-      this.matrixClient.stopClient();
-      await this.matrixClient.clearStores();
-      window.localStorage.clear();
-      window.location.reload();
-    });
-  }
-
-  async logout() {
-    this.matrixClient.stopClient();
-    try {
-      await this.matrixClient.logout();
-    } catch {
-      // ignore if failed to logout
-    }
-    await this.matrixClient.clearStores();
-    window.localStorage.clear();
-    window.location.reload();
-  }
-
-  clearCacheAndReload() {
-    this.matrixClient.stopClient();
-    this.matrixClient.store.deleteAllData().then(() => {
-      window.location.reload();
-    });
-  }
-}
-
-const initMatrix = new InitMatrix();
-
-export default initMatrix;
diff --git a/src/client/initMatrix.ts b/src/client/initMatrix.ts
new file mode 100644 (file)
index 0000000..5a156ad
--- /dev/null
@@ -0,0 +1,70 @@
+import { createClient, MatrixClient, IndexedDBStore, IndexedDBCryptoStore } from 'matrix-js-sdk';
+import Olm from '@matrix-org/olm';
+import { logger } from 'matrix-js-sdk/lib/logger';
+
+import { cryptoCallbacks } from './state/secretStorageKeys';
+
+global.Olm = Olm;
+
+if (import.meta.env.PROD) {
+  logger.disableAll();
+}
+
+type Session = {
+  baseUrl: string;
+  accessToken: string;
+  userId: string;
+  deviceId: string;
+};
+
+export const initClient = async (session: Session): Promise<MatrixClient> => {
+  const indexedDBStore = new IndexedDBStore({
+    indexedDB: global.indexedDB,
+    localStorage: global.localStorage,
+    dbName: 'web-sync-store',
+  });
+  await indexedDBStore.startup();
+
+  const mx = createClient({
+    baseUrl: session.baseUrl,
+    accessToken: session.accessToken,
+    userId: session.userId,
+    store: indexedDBStore,
+    cryptoStore: new IndexedDBCryptoStore(global.indexedDB, 'crypto-store'),
+    deviceId: session.deviceId,
+    timelineSupport: true,
+    cryptoCallbacks: cryptoCallbacks as any,
+    verificationMethods: ['m.sas.v1'],
+  });
+
+  await mx.initCrypto();
+
+  mx.setGlobalErrorOnUnknownDevices(false);
+  mx.setMaxListeners(50);
+
+  return mx;
+};
+
+export const startClient = async (mx: MatrixClient) => {
+  await mx.startClient({
+    lazyLoadMembers: true,
+  });
+};
+
+export const clearCacheAndReload = async (mx: MatrixClient) => {
+  mx.stopClient();
+  await mx.store.deleteAllData();
+  window.location.reload();
+};
+
+export const logoutClient = async (mx: MatrixClient) => {
+  mx.stopClient();
+  try {
+    await mx.logout();
+  } catch {
+    // ignore if failed to logout
+  }
+  await mx.clearStores();
+  window.localStorage.clear();
+  window.location.reload();
+};
diff --git a/src/client/mx.ts b/src/client/mx.ts
deleted file mode 100644 (file)
index 3090945..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-import { MatrixClient } from 'matrix-js-sdk';
-import initMatrix from './initMatrix';
-
-export const mx = (): MatrixClient => {
-  if (!initMatrix.matrixClient) console.error('Matrix client is used before initialization!');
-  return initMatrix.matrixClient!;
-};
index 5903858ae866afb57dfcaec9b43bc1edcb0821dd..1c223051d6fe2281060e9ff1aae4a82efb69a5fe 100644 (file)
@@ -409,6 +409,8 @@ body {
 #root {
   width: 100%;
   height: 100%;
+  display: flex;
+  flex-direction: column;
 }
 
 *,
index 74e56ec76006ae91274175551acdf31ef792c692..e4fd40c3971c04296eb4607a711c69a09398a3ac 100644 (file)
@@ -1,5 +1,3 @@
-import initMatrix from '../client/initMatrix';
-
 import HashIC from '../../public/res/ic/outlined/hash.svg';
 import HashGlobeIC from '../../public/res/ic/outlined/hash-globe.svg';
 import HashLockIC from '../../public/res/ic/outlined/hash-lock.svg';
@@ -24,8 +22,7 @@ export async function getBaseUrl(servername) {
   }
 }
 
-export function getUsername(userId) {
-  const mx = initMatrix.matrixClient;
+export function getUsername(mx, userId) {
   const user = mx.getUser(userId);
   if (user === null) return userId;
   let username = user.displayName;
@@ -39,9 +36,9 @@ export function getUsernameOfRoomMember(roomMember) {
   return roomMember.name || roomMember.userId;
 }
 
-export async function isRoomAliasAvailable(alias) {
+export async function isRoomAliasAvailable(mx, alias) {
   try {
-    const result = await initMatrix.matrixClient.getRoomIdForAlias(alias);
+    const result = await mx.getRoomIdForAlias(alias);
     if (result.room_id) return false;
     return false;
   } catch (e) {
@@ -159,9 +156,8 @@ export function genRoomVia(room) {
   return via.concat(mostPop3.slice(0, 2));
 }
 
-export function isCrossVerified(deviceId) {
+export function isCrossVerified(mx, deviceId) {
   try {
-    const mx = initMatrix.matrixClient;
     const crossSignInfo = mx.getStoredCrossSigningForUser(mx.getUserId());
     const deviceInfo = mx.getStoredDevice(mx.getUserId(), deviceId);
     const deviceTrust = crossSignInfo.checkDeviceTrust(crossSignInfo, deviceInfo, false, true);
@@ -172,14 +168,12 @@ export function isCrossVerified(deviceId) {
   }
 }
 
-export function hasCrossSigningAccountData() {
-  const mx = initMatrix.matrixClient;
+export function hasCrossSigningAccountData(mx) {
   const masterKeyData = mx.getAccountData('m.cross_signing.master');
   return !!masterKeyData;
 }
 
-export function getDefaultSSKey() {
-  const mx = initMatrix.matrixClient;
+export function getDefaultSSKey(mx) {
   try {
     return mx.getAccountData('m.secret_storage.default_key').getContent().key;
   } catch {
@@ -187,8 +181,7 @@ export function getDefaultSSKey() {
   }
 }
 
-export function getSSKeyInfo(key) {
-  const mx = initMatrix.matrixClient;
+export function getSSKeyInfo(mx, key) {
   try {
     return mx.getAccountData(`m.secret_storage.key.${key}`).getContent();
   } catch {
@@ -196,8 +189,7 @@ export function getSSKeyInfo(key) {
   }
 }
 
-export async function hasDevices(userId) {
-  const mx = initMatrix.matrixClient;
+export async function hasDevices(mx, userId) {
   try {
     const usersDeviceMap = await mx.downloadKeys([userId, mx.getUserId()]);
     return Object.values(usersDeviceMap)
index f9a0b7908338188a3d4b59a9a27472221c85bbec..cc73cf577c7f3b607f5e17e4bfce6ce25fc02ed4 100644 (file)
@@ -1,30 +1,3 @@
-import initMatrix from '../client/initMatrix';
-
-export function roomIdByActivity(id1, id2) {
-  const room1 = initMatrix.matrixClient.getRoom(id1);
-  const room2 = initMatrix.matrixClient.getRoom(id2);
-
-  return room2.getLastActiveTimestamp() - room1.getLastActiveTimestamp();
-}
-
-export function roomIdByAtoZ(aId, bId) {
-  let aName = initMatrix.matrixClient.getRoom(aId).name;
-  let bName = initMatrix.matrixClient.getRoom(bId).name;
-
-  // remove "#" from the room name
-  // To ignore it in sorting
-  aName = aName.replace(/#/g, '');
-  bName = bName.replace(/#/g, '');
-
-  if (aName.toLowerCase() < bName.toLowerCase()) {
-    return -1;
-  }
-  if (aName.toLowerCase() > bName.toLowerCase()) {
-    return 1;
-  }
-  return 0;
-}
-
 export function memberByAtoZ(m1, m2) {
   const aName = m1.name;
   const bName = m2.name;