cetirizine

An experimental matrix client written in reactjs utilizing tailwind and storybook
git clone git://archive.git.mtrnord.blog/MTRNord/cetirizine.git
Log | Files | Refs | README | LICENSE

commit 0932d6076cffce2a17cd011649c71e703bc7898c
parent 1ddd18664e42773d5f25b9cf1c527eb23b9f6f63
Author: MTRNord <mtrnord1@gmail.com>
Date:   Wed, 10 May 2023 02:55:07 +0200

Try to fix roomswitching input again. Also fix decryption

Diffstat:
Msrc/app/sdk/client.ts | 20+++++++++++++-------
Msrc/app/sdk/e2ee.ts | 35++++++++++++++++-------------------
Msrc/app/sdk/room.ts | 30+++++++++++++++++-------------
Msrc/components/events/messageEvent.tsx | 23++++++++++-------------
Msrc/components/events/unknownEvent.tsx | 26++++++++++----------------
Msrc/components/input/chat/input.scss | 8++------
Msrc/components/input/chat/input.tsx | 68+++++++++++++++++++++++++++++++++++++++-----------------------------
Msrc/pages/MainPage.tsx | 35+++++++++++++++++------------------
8 files changed, 124 insertions(+), 121 deletions(-)

diff --git a/src/app/sdk/client.ts b/src/app/sdk/client.ts @@ -110,8 +110,13 @@ export class MatrixClient extends EventEmitter { return this.user.mxid; } + private onSyncRooms(rooms: Set<Room>) { + this.emit("rooms", rooms); + } + public async passwordLogin(username: string, password: string) { await this.user.passwordLogin(username, password); + this.sync.on("rooms", (rooms) => this.onSyncRooms(rooms)); } public convertMXC(url: string): string { @@ -191,9 +196,8 @@ export class MatrixClient extends EventEmitter { instance.emit("rooms", instance.sync.rooms); } } - instance.sync.on("rooms", (rooms) => { - instance.emit("rooms", rooms); - }); + instance.setMaxListeners(60); + instance.sync.on("rooms", (rooms) => instance.onSyncRooms(rooms)); } @@ -231,6 +235,7 @@ export class MatrixClient extends EventEmitter { public async logout() { this.sync.logout(); + this.sync.off("rooms", this.onSyncRooms); await this.user.logout(); this.user.e2ee.logoutE2ee(); if (this.user.mxid) { @@ -435,15 +440,16 @@ export function useRooms() { // This is a no-op if there is already a sync client.startSync(); return () => { - client.removeListener("rooms", listenForRooms); + client.off("rooms", listenForRooms); } }, []) return rooms; } export function useRoom(roomID?: string): Room | undefined { - const client = useContext(MatrixContext); - return [...client.getRooms()].find(room => room.roomID === roomID); + const rooms = useRooms(); + + return [...rooms].find(room => room.roomID === roomID); } export function useSpaces() { @@ -461,7 +467,7 @@ export function useSpaces() { // This is a no-op if there is already a sync client.startSync(); return () => { - client.removeListener("rooms", listenForRooms); + client.off("rooms", listenForRooms); } }, []) return spacesWithRooms; diff --git a/src/app/sdk/e2ee.ts b/src/app/sdk/e2ee.ts @@ -25,7 +25,7 @@ export class MatrixE2EE { constructor(private client: MatrixClient, private user: OwnUser) { } - public async decryptRoomEvent(roomID: string, event: IRoomEvent<any>) { + public async decryptRoomEvent(roomID: string, event: IRoomEvent<any>): Promise<any> { return await this.olmMachine?.decryptRoomEvent(JSON.stringify(event), new RoomId(roomID)); } @@ -34,8 +34,8 @@ export class MatrixE2EE { changed_devices: DeviceLists, one_time_key_counts: Map<any, any>, unused_fallback_keys?: Set<any> - ) { - await this.olmMachine?.receiveSyncChanges( + ): Promise<any> { + return await this.olmMachine?.receiveSyncChanges( to_device_events, changed_devices, one_time_key_counts, @@ -43,32 +43,29 @@ export class MatrixE2EE { ); } - public async encryptRoomEvent(roomID: RoomId, type: string, content: any): Promise<any> { + public async encryptRoomEvent(roomID: RoomId, type: string, content: string): Promise<any> { if (!this.client.isLoggedIn) { throw Error("Not logged in"); } - if (!this.user.hostname) { - throw Error("Hostname must be set first"); - } if (!this.olmMachine) { throw Error("Olm machine must be set first"); } - await this.olmMachine?.encryptRoomEvent(roomID, type, content); + return await this.olmMachine?.encryptRoomEvent(roomID, type, content); } - public async initOlmMachine(userID: UserId, deviceID: DeviceId, storePassphrase?: string) { + public async initOlmMachine(userID: UserId, deviceID: DeviceId, storePassphrase?: string): Promise<void> { this.olmMachine = await OlmMachine.initialize(userID, deviceID, "cetirizine-crypto", storePassphrase); } - public async updateTrackedUsers(users: any[]) { + public async updateTrackedUsers(users: any[]): Promise<void> { await this.olmMachine?.updateTrackedUsers(users); } - public async sendIdentifyAndOneTimeKeys() { + public async sendIdentifyAndOneTimeKeys(): Promise<void> { if (!this.client.isLoggedIn) { throw Error("Not logged in"); } - if (!this.user.slidingSyncHostname) { + if (!this.user.hostname) { throw Error("Hostname must be set first"); } if (!this.olmMachine) { @@ -90,11 +87,11 @@ export class MatrixE2EE { this.outgoingRequestsBeingProcessed = false; } - public async shareKeysForRoom(room: Room) { + public async shareKeysForRoom(room: Room): Promise<void> { if (!this.client.isLoggedIn) { throw Error("Not logged in"); } - if (!this.user.slidingSyncHostname) { + if (!this.user.hostname) { throw Error("Hostname must be set first"); } if (!this.olmMachine) { @@ -109,11 +106,11 @@ export class MatrixE2EE { } } - public async getMissingSessions() { + public async getMissingSessions(): Promise<void> { if (!this.client.isLoggedIn) { throw Error("Not logged in"); } - if (!this.user.slidingSyncHostname) { + if (!this.user.hostname) { throw Error("Hostname must be set first"); } if (!this.olmMachine) { @@ -135,11 +132,11 @@ export class MatrixE2EE { this.missingSessionsBeingRequested = false; } - private async processRequest(request: any) { + private async processRequest(request: any): Promise<void> { if (!this.client.isLoggedIn) { throw Error("Not logged in"); } - if (!this.user.slidingSyncHostname) { + if (!this.user.hostname) { throw Error("Hostname must be set first"); } if (!this.olmMachine) { @@ -304,7 +301,7 @@ export class MatrixE2EE { } } - public logoutE2ee() { + public logoutE2ee(): void { this.missingSessionsBeingRequested = false; this.outgoingRequestsBeingProcessed = false; } diff --git a/src/app/sdk/room.ts b/src/app/sdk/room.ts @@ -51,11 +51,10 @@ export class Room extends EventEmitter { super(); } - public addEvents(events: IRoomEvent[]) { + public addEvents(events: IRoomEvent[]): void { + console.log("Adding events") events.forEach((newEvent) => { - console.log("newEvent", newEvent) if (newEvent.unsigned?.transaction_id) { - console.log("Found transaction id. Trying to remove pending event") this.pendingEvents = this.pendingEvents.filter((event) => event.unsigned?.transaction_id !== newEvent.unsigned?.transaction_id); } @@ -65,7 +64,7 @@ export class Room extends EventEmitter { this.emit("events", this.getEvents()); } - public addStateEvents(state: IRoomStateEvent[]) { + public addStateEvents(state: IRoomStateEvent[]): void { // if the state event id is already known then we update the event instead of pushing it on to the Array state.forEach((newEvent) => { const index = this.stateEvents.findIndex((oldEvent) => oldEvent.state_key === newEvent.state_key && oldEvent.type === newEvent.type); @@ -115,7 +114,7 @@ export class Room extends EventEmitter { return isSpace; } - public setName(name: string) { + public setName(name: string): void { this.name = name; } @@ -136,7 +135,7 @@ export class Room extends EventEmitter { return topic; } - public setNotificationCount(count: number) { + public setNotificationCount(count: number): void { this.notification_count = count; } @@ -144,7 +143,7 @@ export class Room extends EventEmitter { return this.notification_count; } - public setNotificationHighlightCount(count: number) { + public setNotificationHighlightCount(count: number): void { this.notification_highlight_count = count; } @@ -152,7 +151,7 @@ export class Room extends EventEmitter { return this.notification_highlight_count; } - public setJoinedCount(count: number) { + public setJoinedCount(count: number): void { this.joined_count = count; } @@ -160,7 +159,7 @@ export class Room extends EventEmitter { return this.joined_count; } - public setInvitedCount(count: number) { + public setInvitedCount(count: number): void { this.invited_count = count; } @@ -188,7 +187,7 @@ export class Room extends EventEmitter { return parents; } - public setDM(isDM: boolean) { + public setDM(isDM: boolean): void { this.is_dm = isDM; } @@ -247,8 +246,9 @@ export class Room extends EventEmitter { } // TODO: Workaround since txn id doesnt come down sync - private deletePendingByEventID(eventID: string) { + private deletePendingByEventID(eventID: string): void { this.pendingEvents = this.pendingEvents.filter((event) => event.eventID !== eventID); + this.emit("events", this.getEvents()); } public async sendHtmlMessage(html: string, plainText: string, callbackLocalEcho: () => void): Promise<string> { @@ -283,6 +283,7 @@ export class Room extends EventEmitter { body: JSON.stringify(event.content) }); if (!resp.ok) { + this.deletePendingByEventID(event.event_id); throw new Error(`Failed to send message: ${resp.status} ${resp.statusText}`); } const json = await resp.json(); @@ -306,6 +307,7 @@ export class Room extends EventEmitter { body: encrypted }); if (!resp.ok) { + this.deletePendingByEventID(event.event_id); throw new Error(`Failed to send message: ${resp.status} ${resp.statusText}`); } const json = await resp.json(); @@ -344,6 +346,7 @@ export class Room extends EventEmitter { body: JSON.stringify(event.content) }); if (!resp.ok) { + this.deletePendingByEventID(event.event_id); throw new Error(`Failed to send message: ${resp.status} ${resp.statusText}`); } const json = await resp.json(); @@ -367,6 +370,7 @@ export class Room extends EventEmitter { body: encrypted }); if (!resp.ok) { + this.deletePendingByEventID(event.event_id); throw new Error(`Failed to send message: ${resp.status} ${resp.statusText}`); } const json = await resp.json(); @@ -436,7 +440,7 @@ export function useEvents(room?: Room) { }; room.on("events", listenForEvents); return () => { - room.removeListener("events", listenForEvents); + room.off("events", listenForEvents); } } else { setEvents([]); @@ -457,7 +461,7 @@ export function useStateEvents(room?: Room) { }; room.on("state_events", listenForStateEvents); return () => { - room.removeListener("state_events", listenForStateEvents); + room.off("state_events", listenForStateEvents); } } else { setEvents([]); diff --git a/src/components/events/messageEvent.tsx b/src/components/events/messageEvent.tsx @@ -2,7 +2,7 @@ import { memo, useContext, useEffect, useState } from "react"; import { IRoomEvent, isRoomMessageAudioEvent, isRoomMessageImageEvent, isRoomMessageNoticeEvent, isRoomMessageTextEvent } from "../../app/sdk/api/events"; import { FC } from "react"; import Avatar from "../avatar/avatar"; -import { MatrixClient, MatrixContext, useRoom } from "../../app/sdk/client"; +import { MatrixClient, MatrixContext } from "../../app/sdk/client"; import Linkify from "linkify-react"; import linkifyHtml from 'linkify-html'; import DOMPurify from "dompurify"; @@ -12,6 +12,7 @@ import Waveform from './helpers/Waveform'; import hljs from 'highlight.js'; import 'highlight.js/styles/base16/solarized-dark.css'; import { OnlineState } from "../../app/sdk/api/otherEnums"; +import { Room } from "../../app/sdk/room"; type MessageEventProps = { /** @@ -19,9 +20,9 @@ type MessageEventProps = { */ event: IRoomEvent; /** - * The roomID of the event to display + * The room of the event to display */ - roomID?: string; + room?: Room; /** * If the previous event was sent by the same user */ @@ -76,16 +77,14 @@ const decryptMedia = (client: MatrixClient, event: IRoomEvent, decryptedCallback }); } -const MessageEvent: FC<MessageEventProps> = memo(({ event, roomID, hasPreviousEvent, reactions }) => { +const MessageEvent: FC<MessageEventProps> = memo(({ event, room, hasPreviousEvent, reactions }) => { const client = useContext(MatrixContext); - const room = useRoom(roomID); - const renderCorrectMessage = (event: IRoomEvent) => { if (isRoomMessageTextEvent(event)) { - return (<TextMessage event={event} roomID={roomID} hasPreviousEvent={hasPreviousEvent} reactions={reactions} />) + return (<TextMessage event={event} room={room} hasPreviousEvent={hasPreviousEvent} reactions={reactions} />) } else if (isRoomMessageNoticeEvent(event)) { - return (<TextMessage event={event} roomID={roomID} hasPreviousEvent={hasPreviousEvent} reactions={reactions} message_type={MessageType.Notice} />) + return (<TextMessage event={event} room={room} hasPreviousEvent={hasPreviousEvent} reactions={reactions} message_type={MessageType.Notice} />) } else if (isRoomMessageImageEvent(event)) { const [url, setUrl] = useState<string | undefined>(undefined); const [unableToDecrypt, setUnableToDecrypt] = useState<boolean>(event.content.file !== undefined); @@ -114,7 +113,7 @@ const MessageEvent: FC<MessageEventProps> = memo(({ event, roomID, hasPreviousEv }, [event]) if (unableToDecrypt) { - return (<UndecryptableEvent event={event} roomID={roomID} hasPreviousEvent={hasPreviousEvent} />) + return (<UndecryptableEvent event={event} room={room} hasPreviousEvent={hasPreviousEvent} />) } return ( @@ -166,7 +165,7 @@ const MessageEvent: FC<MessageEventProps> = memo(({ event, roomID, hasPreviousEv }, [event]) if (unableToDecrypt) { - return (<UndecryptableEvent event={event} roomID={roomID} hasPreviousEvent={hasPreviousEvent} />) + return (<UndecryptableEvent event={event} room={room} hasPreviousEvent={hasPreviousEvent} />) } return ( @@ -219,9 +218,7 @@ interface TextMessage extends MessageEventProps { message_type?: MessageType; } -const TextMessage: FC<TextMessage> = memo(({ event, roomID, hasPreviousEvent, message_type = MessageType.Text }) => { - const room = useRoom(roomID); - +const TextMessage: FC<TextMessage> = memo(({ event, room, hasPreviousEvent, message_type = MessageType.Text }) => { let text_color = "text-black"; if (message_type === MessageType.Notice) { text_color = "text-slate-500" diff --git a/src/components/events/unknownEvent.tsx b/src/components/events/unknownEvent.tsx @@ -1,10 +1,10 @@ import { memo } from "react"; import { IRoomEvent } from "../../app/sdk/api/events"; import { FC } from "react"; -import { useRoom } from "../../app/sdk/client"; import Linkify from "linkify-react"; import Avatar from "../avatar/avatar"; import { OnlineState } from "../../app/sdk/api/otherEnums"; +import { Room } from "../../app/sdk/room"; type UnknownEventProps = { /** @@ -12,9 +12,9 @@ type UnknownEventProps = { */ event: IRoomEvent; /** - * The roomID of the event to display + * The room of the event to display */ - roomID?: string; + room?: Room; /** * If the previous event was sent by the same user */ @@ -28,9 +28,7 @@ const linkifyOptions = { className: "text-blue-500 hover:text-blue-700 active:text-blue-700 visited:text-blue-500" } -const UnknownEvent: FC<UnknownEventProps> = memo(({ event, roomID, hasPreviousEvent }) => { - const room = useRoom(roomID); - +const UnknownEvent: FC<UnknownEventProps> = memo(({ event, room, hasPreviousEvent }) => { return ( <div className={!hasPreviousEvent ? "flex flex-row gap-4 p-2 pb-1 hover:bg-gray-200 rounded-md duration-200 ease-in-out items-start" : "flex flex-row p-2 pb-1 pt-0 hover:bg-gray-200 rounded-md duration-200 ease-in-out"}> {!hasPreviousEvent && <Avatar @@ -55,18 +53,16 @@ type UndecryptableEventProps = { */ event: IRoomEvent; /** - * The roomID of the event to display + * The room of the event to display */ - roomID?: string; + room?: Room; /** * If the previous event was sent by the same user */ hasPreviousEvent?: boolean; }; -export const UndecryptableEvent: FC<UndecryptableEventProps> = memo(({ event, roomID, hasPreviousEvent }) => { - const room = useRoom(roomID); - +export const UndecryptableEvent: FC<UndecryptableEventProps> = memo(({ event, room, hasPreviousEvent }) => { return ( <div className={!hasPreviousEvent ? "flex flex-row gap-4 p-2 pb-1 hover:bg-gray-200 rounded-md duration-200 ease-in-out items-start" : "flex flex-row p-2 pb-1 pt-0 hover:bg-gray-200 rounded-md duration-200 ease-in-out"}> {!hasPreviousEvent && <Avatar @@ -93,18 +89,16 @@ type RedactedEventProps = { */ redacted_because?: string; /** - * The roomID of the event to display + * The room of the event to display */ - roomID?: string; + room?: Room; /** * If the previous event was sent by the same user */ hasPreviousEvent?: boolean; }; -export const RedactedEvent: FC<RedactedEventProps> = memo(({ event, redacted_because, roomID, hasPreviousEvent }) => { - const room = useRoom(roomID); - +export const RedactedEvent: FC<RedactedEventProps> = memo(({ event, redacted_because, room, hasPreviousEvent }) => { return ( <div className={!hasPreviousEvent ? "flex flex-row gap-4 p-2 pb-1 hover:bg-gray-200 rounded-md duration-200 ease-in-out items-start" : "flex flex-row p-2 pb-1 pt-0 hover:bg-gray-200 rounded-md duration-200 ease-in-out"}> {!hasPreviousEvent && <Avatar diff --git a/src/components/input/chat/input.scss b/src/components/input/chat/input.scss @@ -1,15 +1,11 @@ .editor-container { - color: #000; - position: relative; + @apply text-black relative font-normal; line-height: 20px; - font-weight: 400; text-align: left; } .editor-inner { - background: #fff; - position: relative; - overflow-y: auto; + @apply bg-white relative overflow-y-auto; scrollbar-width: thin; max-height: 50lvh; } diff --git a/src/components/input/chat/input.tsx b/src/components/input/chat/input.tsx @@ -23,12 +23,12 @@ import CodeHighlightPlugin from './plugins/CodeHighlightPlugin'; import EditorTheme from './theme'; import './input.scss'; -import { FC, memo, useCallback, useEffect, useState } from 'react'; +import { FC, memo, useEffect, useState } from 'react'; import { Send } from 'lucide-react'; -import { useRoom } from '../../../app/sdk/client'; import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'; import { $getSelection, $isRangeSelection, CLEAR_EDITOR_COMMAND, CLEAR_HISTORY_COMMAND, COMMAND_PRIORITY_CRITICAL, INSERT_PARAGRAPH_COMMAND, KEY_ENTER_COMMAND, ParagraphNode } from 'lexical'; -import { useLocation } from 'react-router-dom'; +import { useLocation, } from 'react-router-dom'; +import { Room } from '../../../app/sdk/room'; export const CAN_USE_DOM: boolean = typeof window !== 'undefined' && @@ -80,7 +80,7 @@ type ChatInputProps = { /** * The current Room */ - roomID?: string + room: Room }; function Placeholder() { @@ -89,10 +89,6 @@ function Placeholder() { type SendButtonProps = { /** - * The current Room - */ - roomID?: string - /** * Callback when sending starts */ onStartSending: () => void @@ -100,15 +96,20 @@ type SendButtonProps = { * Callback when sending stops */ onStopSending: () => void + /** + * The current Room + */ + room: Room }; -const SendButton: FC<SendButtonProps> = memo(({ roomID, onStartSending, onStopSending }: SendButtonProps) => { - const room = useRoom(roomID || ""); +const SendButton: FC<SendButtonProps> = ({ onStartSending, onStopSending, room }: SendButtonProps) => { const [editor] = useLexicalComposerContext(); - const sendMessage = useCallback(() => { + //TODO: Room change fails + const sendMessage = (room?: Room) => { // TODO: Sanitize the html and send message to room if (!room) { + console.warn("Got no room") return; } @@ -151,13 +152,13 @@ const SendButton: FC<SendButtonProps> = memo(({ roomID, onStartSending, onStopSe return; } onStartSending(); - console.log("Sending message to: ", roomID) + console.log("Sending message to: ", room.roomID) if (htmlMessage !== '<p class="editor-paragraph"><br></p>') { room.sendHtmlMessage(htmlMessage, plainMessage, () => { editor.dispatchCommand(CLEAR_EDITOR_COMMAND, undefined); editor.dispatchCommand(CLEAR_HISTORY_COMMAND, undefined); - localStorage.removeItem(`editor-${roomID}`); + localStorage.removeItem(`editor-${room.roomID}`); onStopSending(); }).catch((e) => { console.log(e); @@ -167,7 +168,7 @@ const SendButton: FC<SendButtonProps> = memo(({ roomID, onStartSending, onStopSe room.sendTextMessage(plainMessage, () => { editor.dispatchCommand(CLEAR_EDITOR_COMMAND, undefined); editor.dispatchCommand(CLEAR_HISTORY_COMMAND, undefined); - localStorage.removeItem(`editor-${roomID}`); + localStorage.removeItem(`editor-${room.roomID}`); onStopSending(); }).catch((e) => { console.log(e); @@ -175,17 +176,21 @@ const SendButton: FC<SendButtonProps> = memo(({ roomID, onStartSending, onStopSe }) } }) - }, [room, roomID, editor]); + }; useEffect(() => { - editor.registerCommand<KeyboardEvent | null>( + if (!room) { + return; + } + const removeCommand = editor.registerCommand<KeyboardEvent | null>( KEY_ENTER_COMMAND, - (event) => { + (event: KeyboardEvent | null): boolean => { + console.log("Room changed", room.roomID) const selection = $getSelection(); if (!$isRangeSelection(selection)) { return false; } - if (event !== null) { + if (event !== null && event !== undefined) { // If we have beforeinput, then we can avoid blocking // the default behavior. This ensures that the iOS can // intercept that we're actually inserting a paragraph, @@ -203,36 +208,41 @@ const SendButton: FC<SendButtonProps> = memo(({ roomID, onStartSending, onStopSe if (event.shiftKey) { return editor.dispatchCommand(INSERT_PARAGRAPH_COMMAND, undefined); } + sendMessage(room); } - sendMessage(); return editor.dispatchCommand(INSERT_PARAGRAPH_COMMAND, undefined); }, COMMAND_PRIORITY_CRITICAL, ) - }, [editor]) + return () => { + console.log("Removing command since room or editor changed") + removeCommand() + } + }, [editor, room]) return <Send size={45} stroke='unset' className='stroke-slate-600 rounded m-4 hover:bg-slate-300 hover:stroke-slate-500 p-2 cursor-pointer' - onClick={sendMessage} /> -}); + onClick={() => { sendMessage(room) }} /> +}; type RoomChangeProps = { /** * The current Room */ - roomID?: string + room: Room }; -const RoomChangePlugin: FC<RoomChangeProps> = ({ roomID }) => { +const RoomChangePlugin: FC<RoomChangeProps> = ({ room }) => { const [editor] = useLexicalComposerContext(); const { pathname } = useLocation(); const [prevRoom, setPrevRoom] = useState<string | undefined>(undefined); useEffect(() => { - if (roomID) { + if (room) { + const roomID = room.roomID; if (roomID !== prevRoom) { console.log("Saving editor state") // Save the editor state to local storage @@ -249,12 +259,12 @@ const RoomChangePlugin: FC<RoomChangeProps> = ({ roomID }) => { editor.dispatchCommand(CLEAR_HISTORY_COMMAND, undefined); } } - }, [pathname, roomID]); + }, [pathname, room]); return <></> } -const ChatInput: FC<ChatInputProps> = memo(({ namespace, roomID }: ChatInputProps) => { +const ChatInput: FC<ChatInputProps> = memo(({ namespace, room }: ChatInputProps) => { const [sending, setSending] = useState(false); const initialConfig: InitialConfigType = { @@ -300,11 +310,11 @@ const ChatInput: FC<ChatInputProps> = memo(({ namespace, roomID }: ChatInputProp <AutoLinkPlugin /> <MarkdownShortcutPlugin transformers={TRANSFORMERS} /> <ClearEditorPlugin /> - <RoomChangePlugin roomID={roomID} /> + <RoomChangePlugin room={room} /> {/*<TreeViewPlugin />*/} </div> </div> - <SendButton roomID={roomID} onStartSending={() => { console.log("Sending"); setSending(true) }} onStopSending={() => { setSending(false) }} /> + <SendButton room={room} onStartSending={() => { console.log("Sending"); setSending(true) }} onStopSending={() => { setSending(false) }} /> </LexicalComposer> </div> diff --git a/src/pages/MainPage.tsx b/src/pages/MainPage.tsx @@ -24,15 +24,14 @@ type ChatViewProps = { * If the roomID is valid, but the room is not joined, the ChatView will display a placeholder * If the roomID is valid, but the room is not loaded, the ChatView will display a placeholder */ - roomID?: string + room?: Room /** * Ref of the Scroll Container */ scrollRef: React.RefObject<HTMLDivElement> }; -const ChatView: FC<ChatViewProps> = memo(({ roomID, scrollRef }) => { - const room = useRoom(decodeURIComponent(roomID || "")); +const ChatView: FC<ChatViewProps> = memo(({ room, scrollRef }) => { const client = useContext(MatrixContext); const events = useEvents(room); const { pathname } = useLocation(); @@ -50,7 +49,7 @@ const ChatView: FC<ChatViewProps> = memo(({ roomID, scrollRef }) => { event.content["m.relates_to"]?.["rel_type"] !== "m.replace" ); - + // TODO: Make decrypt run in parallel Promise.all(no_relations?.map(async (event, index) => { let previousEvent = no_relations?.[index - 1]; const previousEventIsFromSameSender = previousEvent?.sender === event.sender; @@ -95,9 +94,9 @@ const ChatView: FC<ChatViewProps> = memo(({ roomID, scrollRef }) => { // Decrypt the event if it is encrypte - if (event.type === "m.room.encrypted" && roomID) { + if (event.type === "m.room.encrypted" && room?.roomID) { try { - const decrypted_event = await client.decryptRoomEvent(roomID, event); + const decrypted_event = await client.decryptRoomEvent(room.roomID, event); if (decrypted_event) { event = JSON.parse(decrypted_event.event) as IRoomEvent; if (event.content["m.new_content"]) { @@ -107,23 +106,23 @@ const ChatView: FC<ChatViewProps> = memo(({ roomID, scrollRef }) => { } } else { if (redacted) { - return (<RedactedEvent event={event} redacted_because={redacted_because} key={redaction_id} roomID={roomID} hasPreviousEvent={previousEventIsFromSameSender} />) + return (<RedactedEvent event={event} redacted_because={redacted_because} key={redaction_id} room={room} hasPreviousEvent={previousEventIsFromSameSender} />) } - return (<UndecryptableEvent key={event.event_id} event={event} hasPreviousEvent={previousEventIsFromSameSender} roomID={roomID}></UndecryptableEvent>) + return (<UndecryptableEvent key={event.event_id} event={event} hasPreviousEvent={previousEventIsFromSameSender} room={room}></UndecryptableEvent>) } } catch (e: any) { - console.error(e); + console.error(e, event); if (redacted) { - return (<RedactedEvent event={event} redacted_because={redacted_because} key={redaction_id} roomID={roomID} hasPreviousEvent={previousEventIsFromSameSender} />) + return (<RedactedEvent event={event} redacted_because={redacted_because} key={redaction_id} room={room} hasPreviousEvent={previousEventIsFromSameSender} />) } - return (<UndecryptableEvent key={event.event_id} event={event} hasPreviousEvent={previousEventIsFromSameSender} roomID={roomID}></UndecryptableEvent>) + return (<UndecryptableEvent key={event.event_id} event={event} hasPreviousEvent={previousEventIsFromSameSender} room={room}></UndecryptableEvent>) } } // Decrypt previousEvent if it is encrypted - if (previousEvent?.type === "m.room.encrypted" && roomID) { + if (previousEvent?.type === "m.room.encrypted" && room?.roomID) { try { - const decrypted_event = await client.decryptRoomEvent(roomID, event); + const decrypted_event = await client.decryptRoomEvent(room.roomID, previousEvent); if (decrypted_event) { previousEvent = JSON.parse(decrypted_event.event) as IRoomEvent; previousEventType = previousEvent.type; @@ -134,7 +133,7 @@ const ChatView: FC<ChatViewProps> = memo(({ roomID, scrollRef }) => { } } } catch (e: any) { - console.error(e); + console.error(e, event); } } @@ -157,11 +156,11 @@ const ChatView: FC<ChatViewProps> = memo(({ roomID, scrollRef }) => { // Render events based on the event type and content const renderEvent = (event: IRoomEvent, previousEventIsFromSameSender: boolean, previousEventType: string, reactions: IRoomEvent[], redacted: boolean, redacted_because: string, redaction_id?: string) => { if (redacted) { - return (<RedactedEvent event={event} redacted_because={redacted_because} roomID={roomID} hasPreviousEvent={previousEventIsFromSameSender} key={redaction_id} />) + return (<RedactedEvent event={event} redacted_because={redacted_because} room={room} hasPreviousEvent={previousEventIsFromSameSender} key={redaction_id} />) } switch (event.type) { case "m.room.message": - return <MessageEvent reactions={reactions} event={event} roomID={roomID} key={event.event_id} hasPreviousEvent={previousEventIsFromSameSender && previousEventType === "m.room.message"} /> + return <MessageEvent reactions={reactions} event={event} room={room} key={event.event_id} hasPreviousEvent={previousEventIsFromSameSender && previousEventType === "m.room.message"} /> case "m.room.member": return <MemberEvent event={event as IRoomMemberEvent} key={event.event_id} /> default: @@ -363,9 +362,9 @@ const MainPage = memo(() => { </div> </div> <div ref={scrollRef} className='overflow-y-auto overflow-x-hidden scrollbarSmall mr-2 my-1 flex-1 w-full flex flex-col-reverse'> - <ChatView roomID={params.roomIdOrAlias} scrollRef={scrollRef} /> + <ChatView room={room} scrollRef={scrollRef} /> </div> - <ChatInput namespace='Editor' roomID={decodeURIComponent(params.roomIdOrAlias || "")} /> + <ChatInput namespace='Editor' room={room} /> </div> } </div >