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 5734b24abf6144479b43424610fb112bcbca39bf
parent 02a035f8b25f0132d39a8c3a3e83c82d5c0100fe
Author: MTRNord <mtrnord1@gmail.com>
Date:   Thu,  4 May 2023 20:06:32 +0200

replace olm with rust-sdk-crypto, fix link colors, fix timeline scroll, prepare better timeline rendering, fix unknown room in sync.

Diffstat:
Mpackage-lock.json | 32+++++++++++++++++++++++++-------
Mpackage.json | 4+++-
Msrc/App.tsx | 37++++---------------------------------
Msrc/app/sdk/client.ts | 30++++++++++++++++++++++++++----
Msrc/components/events/messageEvent.tsx | 4++--
Msrc/index.scss | 12++++++++++++
Msrc/pages/MainPage.tsx | 12+++++++++---
Mvite.config.ts | 1+
8 files changed, 82 insertions(+), 50 deletions(-)

diff --git a/package-lock.json b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "@lexical/react": "^0.10.0", + "@matrix-org/matrix-sdk-crypto-js": "^0.1.0-alpha.8", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^14.4.3", @@ -20,8 +21,9 @@ "idb": "^7.1.1", "immer": "^10.0.1", "lexical": "^0.10.0", + "linkify-react": "^4.1.1", + "linkifyjs": "^4.1.1", "lucide-react": "^0.171.0", - "olm": "https://packages.matrix.org/npm/olm/olm-3.2.1.tgz", "react": "^18.2.0", "react-dom": "^18.2.0", "react-intersection-observer": "^9.4.3", @@ -3019,6 +3021,14 @@ "yjs": ">=13.5.22" } }, + "node_modules/@matrix-org/matrix-sdk-crypto-js": { + "version": "0.1.0-alpha.8", + "resolved": "https://registry.npmjs.org/@matrix-org/matrix-sdk-crypto-js/-/matrix-sdk-crypto-js-0.1.0-alpha.8.tgz", + "integrity": "sha512-hdmbbGXKrN6JNo3wdBaR5Zs3lXlzllT3U43ViNTlabB3nKkOZQnEAN/Isv+4EQSgz1+8897veI9Q8sqlQX22oA==", + "engines": { + "node": ">= 10" + } + }, "node_modules/@mdx-js/react": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-2.3.0.tgz", @@ -10418,6 +10428,20 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, + "node_modules/linkify-react": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/linkify-react/-/linkify-react-4.1.1.tgz", + "integrity": "sha512-2K9Y1cUdvq40dFWqCJ//X+WP19nlzIVITFGI93RjLnA0M7KbnxQ/ffC3AZIZaEIrLangF9Hjt3i0GQ9/anEG5A==", + "peerDependencies": { + "linkifyjs": "^4.0.0", + "react": ">= 15.0.0" + } + }, + "node_modules/linkifyjs": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.1.1.tgz", + "integrity": "sha512-zFN/CTVmbcVef+WaDXT63dNzzkfRBKT1j464NJQkV7iSgJU0sLBus9W0HBwnXK13/hf168pbrx/V/bjEHOXNHA==" + }, "node_modules/loader-runner": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", @@ -11098,12 +11122,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/olm": { - "version": "3.2.1", - "resolved": "https://packages.matrix.org/npm/olm/olm-3.2.1.tgz", - "integrity": "sha512-B87bTpGIGieuV2FNauChjjQtVltwTGagQFoHm+3Dcse4amKAAGJB/I54dnP/JtbHZ+RYVoApM2OQ46Z4VH6eNg==", - "license": "Apache-2.0" - }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", diff --git a/package.json b/package.json @@ -5,6 +5,7 @@ "type": "module", "dependencies": { "@lexical/react": "^0.10.0", + "@matrix-org/matrix-sdk-crypto-js": "^0.1.0-alpha.8", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^14.4.3", @@ -16,8 +17,9 @@ "idb": "^7.1.1", "immer": "^10.0.1", "lexical": "^0.10.0", + "linkify-react": "^4.1.1", + "linkifyjs": "^4.1.1", "lucide-react": "^0.171.0", - "olm": "https://packages.matrix.org/npm/olm/olm-3.2.1.tgz", "react": "^18.2.0", "react-dom": "^18.2.0", "react-intersection-observer": "^9.4.3", diff --git a/src/App.tsx b/src/App.tsx @@ -3,44 +3,15 @@ import { Route, Routes, } from "react-router-dom"; -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -import olmWasmPath from "olm/olm.wasm?url"; -import OlmLegacy from 'olm/olm_legacy.js?url'; -import Olm from "olm"; import LoginPage from './pages/LoginPage'; import MainPage from './pages/MainPage'; import { ProtectedRoute } from './app/protectedRoute'; import { memo } from "react"; +import { initAsync, start } from "@matrix-org/matrix-sdk-crypto-js"; -Olm - .init({ - locateFile: () => olmWasmPath, - }) - .then(() => { - console.log("Using WebAssembly Olm"); - }) - .catch((wasmLoadError: any) => { - console.log("Failed to load Olm: trying legacy version", wasmLoadError); - return new Promise((resolve, reject) => { - const s = document.createElement("script"); - s.src = OlmLegacy; - s.onload = resolve; - s.onerror = reject; - document.body.appendChild(s); - }) - .then(() => { - // Init window.Olm, ie. the one just loaded by the script tag, - // not 'Olm' which is still the failed wasm version. - return window.Olm.init(); - }) - .then(() => { - console.log("Using legacy Olm"); - }) - .catch((legacyLoadError) => { - console.log("Both WebAssembly and asm.js Olm failed!", legacyLoadError); - }); - }); +initAsync().then(() => { + start(); +}) function App() { return <BrowserRouter basename={import.meta.env.BASE_URL}> diff --git a/src/app/sdk/client.ts b/src/app/sdk/client.ts @@ -694,11 +694,33 @@ export class MatrixClient extends EventEmitter { const required_state = room.required_state; const is_dm = room.is_dm; - const roomObj = [...this.rooms].find(room => room.roomID === roomID); + let roomObj = [...this.rooms].find(room => room.roomID === roomID); if (!roomObj) { - // TODO: Warn but create missing room instead of failing on it - console.error("Could not find roomObj for roomID", roomID); - continue; + // Warn, check in the db and if that fails, create a new one. + console.warn("Could not find roomObj for roomID:", roomID); + const roomFromDB = await tx?.store.get(roomID); + if (roomFromDB) { + console.warn("Room in db but not in obj list.", roomID, "Updating obj list."); + + roomObj = new Room(roomID, this.hostname!); + roomObj.setName(roomFromDB.name); + roomObj.setNotificationCount(roomFromDB.notification_count); + roomObj.setNotificationHighlightCount(roomFromDB.highlight_count); + roomObj.setJoinedCount(roomFromDB.joined_count); + roomObj.setInvitedCount(roomFromDB.invited_count); + roomObj.setDM(roomFromDB.isDM || false); + if (roomFromDB.events) { + roomObj.addEvents(roomFromDB.events); + } + if (roomFromDB.stateEvents) { + roomObj.addStateEvents(roomFromDB.stateEvents); + } + roomObj.windowPos = roomFromDB.windowPos; + } else { + console.warn("Could not find room in db. Creating new one."); + roomObj = new Room(roomID, this.hostname!); + this.rooms.add(roomObj); + } } if (name) { diff --git a/src/components/events/messageEvent.tsx b/src/components/events/messageEvent.tsx @@ -30,7 +30,7 @@ const MessageEvent: FC<MessageEventProps> = memo(({ event, roomID }) => { if (event.content.format === "org.matrix.custom.html") { // TODO: Sanitize HTML return ( - <div className="flex flex-row gap-4 p-2 hover:bg-gray-300 rounded-md duration-200 ease-in-out"> + <div className="flex flex-row gap-4 p-2 hover:bg-gray-200 rounded-md duration-200 ease-in-out"> <Avatar displayname={room?.getMemberName(event.sender) || ""} avatarUrl={room?.getMemberAvatar(event.sender)} @@ -46,7 +46,7 @@ const MessageEvent: FC<MessageEventProps> = memo(({ event, roomID }) => { ) } else { return ( - <div className="flex flex-row gap-4 p-2 hover:bg-gray-300 rounded-md duration-200 ease-in-out"> + <div className="flex flex-row gap-4 p-2 hover:bg-gray-200 rounded-md duration-200 ease-in-out"> <Avatar displayname={room?.getMemberName(event.sender) || ""} avatarUrl={room?.getMemberAvatar(event.sender)} diff --git a/src/index.scss b/src/index.scss @@ -7,6 +7,18 @@ a { @apply text-blue-500; } +a:hover { + @apply text-blue-700; +} + +a:active { + @apply text-blue-700; +} + +a:visited { + @apply text-blue-500; +} + /* Font families defined by this CSS: diff --git a/src/pages/MainPage.tsx b/src/pages/MainPage.tsx @@ -32,7 +32,7 @@ const ChatView: FC<ChatViewProps> = memo(({ roomID }) => { const events = room?.getEvents(); // Render events based on the event type and content - const renderEvent = (event: IRoomEvent) => { + const renderEvent = (event: IRoomEvent, previousEventIsFromSameSender: boolean, previousEventType: string) => { switch (event.type) { case "m.room.message": return <MessageEvent event={event} roomID={roomID} key={event.event_id} /> @@ -49,7 +49,13 @@ const ChatView: FC<ChatViewProps> = memo(({ roomID }) => { return self.findIndex(e => e.event_id === event.event_id) === index; }); - const renderedEvents = dedupedEvents?.map(event => renderEvent(event)); + // Map events to components but also tell components if the previous event was from the same sender and which type it was + const renderedEvents = dedupedEvents?.map((event, index) => { + const previousEvent = dedupedEvents[index - 1]; + const previousEventIsFromSameSender = previousEvent?.sender === event.sender; + const previousEventType = previousEvent?.type; + return renderEvent(event, previousEventIsFromSameSender, previousEventType); + }); return <div className='max-w-[130ch] flex flex-col gap-2'>{renderedEvents}</div>; }); @@ -183,7 +189,7 @@ const MainPage = memo(() => { <p className='ml-4 text-slate-700 font-normal text-base'>{room.getTopic()}</p> </div> </div> - <div className='overflow-y-auto overflow-x-hidden scrollbarSmall mr-2 my-1 flex-1 w-full'> + <div className='overflow-y-auto overflow-x-hidden scrollbarSmall mr-2 my-1 flex-1 w-full flex flex-col-reverse'> <ChatView roomID={params.roomIdOrAlias} /> </div> <ChatInput namespace='Editor' onChange={(editorState, editor) => { diff --git a/vite.config.ts b/vite.config.ts @@ -4,6 +4,7 @@ import react from "@vitejs/plugin-react-swc"; // https://vitejs.dev/config/ export default defineConfig({ plugins: [react(), splitVendorChunkPlugin()], + appType: 'mpa', build: { target: 'esnext' }