docs: LOTUS_TODO.md — mark all P0 tasks complete, add June 2026 bug fix section
- P0-1 through P0-12: all marked [x] with implementation notes - P0-4, P0-7: remain [BLOCKED] (MSC3892, MSC3266 not on server) - P0-9, P0-10: remain [UPSTREAM — REMOVED] - New INFRASTRUCTURE/BUG FIXES section documents: GIF CSP fix, unhandled rejection pattern, Copy Link relocation, MaxListeners bump, TDS hex violations fixed, CORS for well-known/matrix/support, relations API v1 vs v3 path fix - Architecture facts table updated with 6 new confirmed findings Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+83
-17
@@ -90,8 +90,12 @@ DM last-message preview, Media gallery, Knock-to-join full UX
|
||||
| Chat backgrounds: `chatBackground.ts`, `<Page>` in `RoomView.tsx:106` | Task #77: animated backgrounds need CSS class + `::before` pseudo-element |
|
||||
| `KeywordMessages.tsx` already has custom keyword push rules | Task #61: only non-keyword rule types need new UI |
|
||||
| `StateEventEditor.tsx` in Developer Tools edits any state event | Task #69: build user-friendly ACL UI in Permissions tab |
|
||||
| URL preview defaults: `urlPreview: true`, `encUrlPreview: false` | Task #49: one-line default change + one warning string |
|
||||
| URL preview defaults: `urlPreview: true`, `encUrlPreview: false` | Task #49: DONE — default changed + Warning chip |
|
||||
| Private read receipts: `ReceiptType.ReadPrivate` + `markAsRead()` param exist | Task #34: trivially simple |
|
||||
| Relations API at `/_matrix/client/v1/` NOT v3 | EditHistoryModal uses raw fetch with explicit v1 path |
|
||||
| `mx.getContent()` returns post-edit content after SDK applies replacements | EditHistoryModal uses `mEvent.event.content` for the "Original" entry |
|
||||
| GIF CSP: `connect-src` on LXC 106 must include `https://*.giphy.com` | Fixed in nginx — live, no deploy needed |
|
||||
| `getLocalRoomNamesContent` is now `export`ed from `useRoomMeta.ts` | Used by `RoomNavItem` to avoid duplicating the parse logic |
|
||||
| Right-click room menu: 6 items in `RoomNavItem.tsx:70-220` | Task #102: add Mute-duration submenu via `PopOut` |
|
||||
| GIF links: render as generic OG preview cards, NOT auto-embedded | Task #42: needs explicit URL pattern detection + `<img>` render |
|
||||
| Toolbar buttons sequential array in `RoomInput.tsx` | Task #43: straightforward — no complex layout system |
|
||||
@@ -175,7 +179,7 @@ Attack these first; most are single-file changes or simple new components.
|
||||
|
||||
---
|
||||
|
||||
### [ ] P0-1 · Report Room (MSC4151)
|
||||
### [x] P0-1 · Report Room (MSC4151)
|
||||
|
||||
**Spec:** MSC4151, merged ~Matrix spec v1.12. Synapse supports it.
|
||||
**Confirmed:** NOT present in upstream Cinny mainline.
|
||||
@@ -188,11 +192,12 @@ Body: { "reason": "string" }
|
||||
|
||||
The homeserver forwards the report to server admins.
|
||||
**Where:** `src/app/features/room/RoomViewHeader.tsx` (add menu item), new `ReportRoomModal.tsx` component.
|
||||
**Complexity:** Low — one API call, one modal.
|
||||
**Complexity:** Low — one API call, one modal.
|
||||
**COMPLETED June 2026.** Uses `mx.reportRoom()` SDK method. Category dropdown (Spam/Harassment/Inappropriate/Other). `role="dialog"` + `aria-modal` accessibility. Error discrimination (M_LIMIT_EXCEEDED, M_FORBIDDEN, 404). Auto-close 1.5s after success. Hidden for own rooms, server-notice rooms, and rooms on servers not advertising v1.13 (Synapse 1.114+ has the endpoint regardless — spec gate removed). Shown only via Invite menu (not three-dot menu).
|
||||
|
||||
---
|
||||
|
||||
### [ ] P0-2 · Server support contact display (MSC1929)
|
||||
### [x] P0-2 · Server support contact display (MSC1929)
|
||||
|
||||
**Spec:** MSC1929, stable in Matrix spec.
|
||||
**What:** On load (or when Settings → Help & About is opened), fetch:
|
||||
@@ -207,11 +212,12 @@ Parse the `contacts` array and `support_page` URL. Display in Settings → Help
|
||||
- Link to support page if present
|
||||
Cache the response in component state; no repeated fetches.
|
||||
**Where:** `src/app/features/settings/` Help/About panel.
|
||||
**Complexity:** Low — one fetch, display result.
|
||||
**Complexity:** Low — one fetch, display result.
|
||||
**COMPLETED June 2026.** Fetches `/.well-known/matrix/support` via `mx.getHomeserverUrl()`. AbortController cleanup. JSON type guard. Loading state. Clickable `matrix_id` → `matrix.to` link; `email_address` → `mailto:` link (both rendered when both present). `formatRole()` handles all role strings. **Server-side:** CORS `Access-Control-Allow-Origin: *` added to LXC 139 (NPM) for `/.well-known/matrix/support`. Static JSON response configured: `@jared:matrix.lotusguild.org` (admin), support_page `https://matrix.lotusguild.org`.
|
||||
|
||||
---
|
||||
|
||||
### [ ] P0-3 · Server notices distinct rendering (m.server_notice)
|
||||
### [x] P0-3 · Server notices distinct rendering (m.server_notice)
|
||||
|
||||
**Spec:** CS-API §13.17, stable.
|
||||
**Audit result:** CONFIRMED MISSING. Only `M_CANNOT_LEAVE_SERVER_NOTICE_ROOM` error code exists in `src/app/cs-errorcode.ts` — no rendering differentiation. Server notices currently arrive as plain DMs.
|
||||
@@ -221,7 +227,8 @@ Parse the `contacts` array and `support_page` URL. Display in Settings → Help
|
||||
- Slightly different background color (use `color.Warning` or neutral surface)
|
||||
- Composer hidden/disabled in server notice rooms (check room type in `RoomInput.tsx`)
|
||||
**Where:** `src/app/features/room/RoomViewHeader.tsx` (badge), `src/app/features/room/RoomInput.tsx` (hide composer when `room.getType() === 'm.server_notice'`).
|
||||
**Complexity:** Low.
|
||||
**Complexity:** Low.
|
||||
**COMPLETED June 2026.** "Server Notice" `Chip variant="Warning"` in room header with tooltip. Read-only composer replaced by informational `Box` message. Invite, Room Settings, and Report Room menu items hidden for server-notice rooms (both header three-dot menu and sidebar context menu). Distinct `Icons.Warning` icon for server-notice rooms in `getRoomIconSrc()`. Detection via `room.getType() === 'm.server_notice'` (type-based, not name-based).
|
||||
|
||||
---
|
||||
|
||||
@@ -233,23 +240,25 @@ Parse the `contacts` array and `support_page` URL. Display in Settings → Help
|
||||
|
||||
---
|
||||
|
||||
### [ ] P0-5 · Rich room topic rendering (MSC3765)
|
||||
### [x] P0-5 · Rich room topic rendering (MSC3765)
|
||||
|
||||
**Spec:** MSC3765, merged Matrix spec v1.15.
|
||||
**Server status:** Server reports v1.12 — MSC3765 not formally available. However, the `formatted_body` field on `m.room.topic` state events is part of the generic Matrix content structure and can be stored/read regardless of spec version. The rendering improvement is worthwhile to build now: it will activate whenever any room admin sets a formatted topic.
|
||||
**What:** Pipe the `formatted_body` of the `m.room.topic` state event through the existing `sanitizeCustomHtml()` + `html-react-parser` pipeline (same as message bodies). Fall back to plain `body` if no `formatted_body` present.
|
||||
**Where:** `src/app/features/room/RoomViewHeader.tsx` — where the topic string is rendered.
|
||||
**Complexity:** Low — reuse existing HTML renderer pipeline.
|
||||
**Complexity:** Low — reuse existing HTML renderer pipeline.
|
||||
**COMPLETED June 2026.** `RoomTopicViewer` renders `formatted_body` via `sanitizeCustomHtml` + `html-react-parser`. Header shows clean plain-text preview (markdown symbols stripped); click opens full formatted modal. `RoomIntro.tsx` also shows clickable topic that opens the same viewer. **Room Settings topic editor:** `buildTopicContent()` auto-detects markdown syntax and saves `format: "org.matrix.custom.html"` + `formatted_body` + stripped plain `topic`; B/I/S/code formatting toolbar above the textarea. `sanitize.ts` hex regex updated to support 3/4/6/8-digit CSS4 hex colors.
|
||||
|
||||
---
|
||||
|
||||
### [ ] P0-6 · Edit history viewer
|
||||
### [x] P0-6 · Edit history viewer
|
||||
|
||||
**Spec:** CS-API §11.8.2 (stable). Edit history is stored as `m.replace` relation events.
|
||||
**Audit result:** Confirmed NOT in upstream Cinny. The "edited" label shows on messages (Message.tsx:1231) but clicking it does nothing. `getEventEdits()` is used internally but no history modal exists.
|
||||
**What:** Click the "edited" label → popover/modal lists every prior version with timestamps. Fetch via `GET /_matrix/client/v1/rooms/{roomId}/relations/{eventId}/m.replace`. Show each version's body + timestamp. Use folds `Overlay`+`Modal` pattern.
|
||||
**Where:** `src/app/features/room/message/Message.tsx` — find where "edited" label renders (line ~1231) and add onClick handler that opens the history modal.
|
||||
**Complexity:** Low — one API call, display list.
|
||||
**Complexity:** Low — one API call, display list.
|
||||
**COMPLETED June 2026.** `EditHistoryModal.tsx` — fetches via raw fetch to `/_matrix/client/v1/rooms/.../relations/.../m.replace` (v1, not v3 — Synapse only supports relations at v1). `isRawEditEvent` type guard, `next_batch` truncation indicator (50-edit limit). Formatted HTML rendered via `sanitizeCustomHtml` + `html-react-parser` with Linkify fallback. `role="dialog"` + `aria-modal` accessibility. **Critical fix:** "Original" entry uses `mEvent.event.content` (raw server data) not `mEvent.getContent()` (SDK returns post-edit content after applying replacements) — prevents original and edit-1 showing identical text.
|
||||
|
||||
---
|
||||
|
||||
@@ -263,13 +272,14 @@ Parse the `contacts` array and `support_page` URL. Display in Settings → Help
|
||||
|
||||
---
|
||||
|
||||
### [ ] P0-8 · Personal room name overrides (MSC4431)
|
||||
### [x] P0-8 · Personal room name overrides (MSC4431)
|
||||
|
||||
**Spec:** MSC4431, still in review.
|
||||
**Server check:** Uses standard account data (`PUT /user/{userId}/account_data/`) which is universally supported regardless of MSC status. Safe to implement now; use `io.lotus.room_names` as the account data key until MSC4431 is finalized (may need to rename key when merged).
|
||||
**What:** Right-click room in sidebar → "Rename for me…" → input dialog → saves to account data. Show a small ✏ icon next to locally-renamed rooms. `useRoomName()` hook in `useRoomMeta.ts:19-35` — override its return value when a local rename exists for that roomId.
|
||||
**Where:** Room nav item context menu (`RoomNavItem.tsx`), sidebar room list via `useRoomName()` hook.
|
||||
**Complexity:** Low.
|
||||
**Complexity:** Low.
|
||||
**COMPLETED June 2026.** `useLocalRoomName` + `useHasLocalRoomName` in `useRoomMeta.ts`. Account data key `io.lotus.room_names`. `RenameRoomDialog` with `maxLength={255}`, clear/save, pre-fills with current custom name. Pencil indicator with `config.opacity.P300`. FocusTrap nesting fixed (dialog rendered outside menu, menu closes before dialog opens). `getLocalRoomNamesContent` exported for shared use. Local name applied consistently across: room header (`RoomViewHeader`), sidebar nav (`RoomNavItem`), room intro (`RoomIntro`), call overlay (`CallRoomName`). Reactive to cross-device account data updates via `ClientEvent.AccountData`.
|
||||
|
||||
---
|
||||
|
||||
@@ -285,7 +295,7 @@ Parse the `contacts` array and `support_page` URL. Display in Settings → Help
|
||||
|
||||
---
|
||||
|
||||
### [ ] P0-11 · Spoiler text audit and fix if broken
|
||||
### [x] P0-11 · Spoiler text audit and fix if broken
|
||||
|
||||
**What:** The message composer toolbar has a spoiler mark (||spoiler text||). Verify the end-to-end flow:
|
||||
|
||||
@@ -294,18 +304,74 @@ Parse the `contacts` array and `support_page` URL. Display in Settings → Help
|
||||
3. Mobile touch works for reveal
|
||||
If any step is broken, fix it. If all working correctly, close this task with no changes.
|
||||
**[AUDIT REQUIRED]** — Full code audit of spoiler mark in toolbar → event content → timeline renderer.
|
||||
**Complexity:** Low (audit) — fix complexity TBD.
|
||||
**Complexity:** Low (audit) — fix complexity TBD.
|
||||
**COMPLETED June 2026 — NO CHANGES NEEDED.** Full audit confirmed: spoiler composing (`data-md` + `data-mx-spoiler` in `markdown/inline/rules.ts:99`), MSC4193 stable property name at `types/matrix/common.ts:5`, rendering in `react-custom-html-parser.tsx:461`, image/video spoiler support in `ImageContent.tsx` and `VideoContent.tsx`. All working correctly.
|
||||
|
||||
---
|
||||
|
||||
### [ ] P0-12 · URL Preview default settings + security warning
|
||||
### [x] P0-12 · URL Preview default settings + security warning
|
||||
|
||||
**Audit result:** `settings.ts` line 103: `urlPreview: true` (already on by default) · line 104: `encUrlPreview: false` (encrypted rooms OFF by default).
|
||||
**What:** Change `encUrlPreview` default to `true` in `src/app/state/settings.ts`. Add a one-sentence security note next to the encrypted-room toggle in the settings UI:
|
||||
|
||||
> "URL previews in encrypted rooms are fetched by your homeserver, which sees the URL but not the message content."
|
||||
> **Where:** `src/app/state/settings.ts` line 104 (change default), settings UI file for URL preview toggles (find via grep for `encUrlPreview`).
|
||||
> **Complexity:** Very Low — one default value change + one sentence of UI text.
|
||||
> **Complexity:** Very Low — one default value change + one sentence of UI text.
|
||||
**COMPLETED June 2026.** `encUrlPreview` default → `true`. Encrypted URL preview setting now shows: description text explaining homeserver sees all URLs + `Chip variant="Warning" fill="Soft"` badge "Privacy risk — enabled by default". Plain `urlPreview` setting also got a description. Both labels corrected to "URL Preview" (was "Url Preview").
|
||||
|
||||
---
|
||||
|
||||
## INFRASTRUCTURE / BUG FIXES — Resolved June 2026
|
||||
|
||||
These were not planned features but bugs and infrastructure gaps found during P0 testing.
|
||||
|
||||
---
|
||||
|
||||
### [x] BUG · GIF sending broken (CSP + domain allowlist)
|
||||
|
||||
**Root cause:** Two independent issues:
|
||||
1. `connect-src` CSP on LXC 106 nginx included `https://api.giphy.com` (search API) but NOT `https://*.giphy.com` (CDN). Browser blocked `fetch()` to `media2.giphy.com` with CSP violation.
|
||||
2. Domain allowlist in `RoomInput.tsx` was a hardcoded list of `media0-4.giphy.com` — didn't cover all Giphy CDN shards.
|
||||
|
||||
**Fix:** LXC 106 nginx CSP updated: `https://*.giphy.com` added to `connect-src` (live, no deploy needed). Domain check changed to `hostname.endsWith('.giphy.com')`. All silent failure paths now show user-facing error messages.
|
||||
|
||||
---
|
||||
|
||||
### [x] BUG · Unhandled promise rejections from fire-and-forget useEffect loads
|
||||
|
||||
**Root cause:** `useAsync` (in `useAsyncCallback.ts`) re-throws errors after storing them in `AsyncState.Error`. When `load()` / `loadThumbSrc()` etc. are called in `useEffect` without `.catch()`, the rejected promise hits the global `onunhandledrejection` handler → Sentry error JAVASCRIPT-REACT-M.
|
||||
|
||||
**Fix:** Added `.catch(() => undefined)` to all 6 fire-and-forget patterns: `useAsyncCallbackValue`, `ThumbnailContent`, `ImageContent`, `VideoContent`, `ClientConfigLoader`, `EditHistoryModal`. Error is already captured in state for UI display.
|
||||
|
||||
---
|
||||
|
||||
### [x] BUG · Copy Link buried in three-dot menu
|
||||
|
||||
**Fix:** Removed Copy Link from both `RoomViewHeader` three-dot menu and `RoomNavItem` sidebar context menu. Added "Copy Link" button with 2s "Copied!" confirmation to `InviteUserPrompt` modal header — naturally co-located with the invite flow.
|
||||
|
||||
---
|
||||
|
||||
### [x] BUG · `useLocalRoomName` MaxListeners warning (51+ rooms)
|
||||
|
||||
**Fix:** `initMatrix.ts` bumped `mx.setMaxListeners(50)` → `mx.setMaxListeners(150)`.
|
||||
|
||||
---
|
||||
|
||||
### [x] BUG · General.tsx hardcoded hex colors violating TDS
|
||||
|
||||
**Fix:** `#980000` → `color.Critical.Main` (chat background selector). `#FF6B00` / `rgba(255,107,0,0.35)` → `var(--accent-orange)` / `var(--accent-orange-border)` (Lotus Terminal replay button).
|
||||
|
||||
---
|
||||
|
||||
### [x] SERVER · CORS missing for `/.well-known/matrix/support`
|
||||
|
||||
**Fix:** LXC 139 (Nginx Proxy Manager) — added dedicated location block for `/.well-known/matrix/support` with `add_header Access-Control-Allow-Origin * always` and static JSON response: `{"contacts":[{"matrix_id":"@jared:matrix.lotusguild.org","role":"m.role.admin"}],"support_page":"https://matrix.lotusguild.org"}`. Matches existing pattern for `/.well-known/matrix/client` and `/server` blocks.
|
||||
|
||||
---
|
||||
|
||||
### [x] SERVER · Relations API returning 404 (wrong endpoint version)
|
||||
|
||||
**Fix:** `EditHistoryModal` was using `mx.http.authedRequest` which prepends `/_matrix/client/v3/`. Matrix relations API lives at `/_matrix/client/v1/`. Changed to raw `fetch()` with explicit `/_matrix/client/v1/rooms/.../relations/.../m.replace` path.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user