docs: mark P2-4 through P3-7 complete in LOTUS_TODO and README

LOTUS_TODO.md: 12 features changed [  ] → [x] with COMPLETED June 2026
summaries: P2-4 export history, P2-6 activity log, P2-7 link previews
(13 domains), P2-8 extended profile fields, P2-9 local time display,
P2-10 unverified device warning, P2-11 push rule editor, P2-12 server
ACL editor, P3-1 bookmarks, P3-2 message scheduling, P3-3 compression,
P3-7 room insights

README.md: added entries for all 12 new features in their respective
sections; added 11 new rows to Key Custom Files table

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-04 12:40:18 -04:00
parent d3dcf93f1a
commit 696e958a00
2 changed files with 59 additions and 12 deletions
+36 -12
View File
@@ -537,7 +537,7 @@ Returns `{ event_id, origin_server_ts }`. Then scroll the timeline to that event
---
### [ ] P2-4 · Export Room History
### [x] P2-4 · Export Room History
**What:** Export a room's messages to plain text, JSON, or HTML. Accessible from room settings (gear icon → "Export Room History"). Features:
@@ -551,6 +551,8 @@ Returns `{ event_id, origin_server_ts }`. Then scroll the timeline to that event
**[AUDIT REQUIRED]** — Confirm upstream Cinny does NOT have an export feature. Check room settings panels.
**Complexity:** Medium.
**COMPLETED June 2026.** `ExportRoomHistory.tsx` in Room Settings → Export tab. Supports Plain Text, JSON, and HTML formats with optional start/end date range filters. Paginates backwards via `mx.paginateEventTimeline()` with a live progress counter. E2EE-aware — skips events where decryption failed rather than exporting garbled ciphertext. Generates a `Blob` and triggers a browser download via `URL.createObjectURL` + a temporary `<a download>` element.
---
### [x] P2-5 · Notification Quiet Hours
@@ -580,7 +582,7 @@ function isQuietHours(settings): boolean {
---
### [ ] P2-6 · Room Activity / Moderation Log
### [x] P2-6 · Room Activity / Moderation Log
**What:** A filterable log of room state changes, accessible from room settings. Shows:
@@ -593,9 +595,11 @@ function isQuietHours(settings): boolean {
**[AUDIT REQUIRED]** — Check if upstream Cinny already has a room audit log. Also check if Synapse's admin API provides a richer audit log that could be used instead (admin only).
**Complexity:** Medium.
**COMPLETED June 2026.** `RoomActivityLog.tsx` in Room Settings → Activity tab. Fetches and displays a filterable log covering `m.room.member` (join/leave/kick/ban/unban/invite), `m.room.power_levels`, `m.room.name`, `m.room.topic`, `m.room.avatar`, and `m.room.server_acl` events. Each entry renders a human-readable description and relative timestamp. A type-filter dropdown narrows the view. Load More button appends older events; auto-paginates on mount to ensure an initial set of entries is visible.
---
### [ ] P2-7 · Richer Link Preview Cards
### [x] P2-7 · Richer Link Preview Cards
**What:** Expand URL preview cards beyond generic title/description/image:
@@ -609,9 +613,11 @@ function isQuietHours(settings): boolean {
**[AUDIT REQUIRED]** — Check what data `/_matrix/media/v3/preview_url` returns for YouTube/GitHub links on `matrix.lotusguild.org`. The homeserver proxies this call — the richness depends on what metadata the target site exposes and what Synapse extracts. Test with real URLs before building domain-specific card logic.
**Complexity:** Medium.
**COMPLETED June 2026.** `UrlPreviewCard.tsx` now implements domain-specific card layouts for 13 domains: YouTube (thumbnail + ▶ play overlay), Vimeo, GitHub (repo description parse), Twitter/X (tweet text parse + media), Reddit (subreddit + upvotes + comments), Spotify (artwork), Twitch (LIVE badge + game), Steam, Wikipedia, Discord (server invite styling), npm, Stack Overflow, and IMDb (poster). Generic cards gain a favicon from `https://www.google.com/s2/favicons`. Cards that produce no renderable content (empty title, no image) are suppressed entirely rather than showing a blank box.
---
### [ ] P2-8 · Extended Profile Fields (MSC4133 + MSC4175)
### [x] P2-8 · Extended Profile Fields (MSC4133 + MSC4175)
**Spec:** MSC4133 + MSC4175, merged Matrix spec v1.16.
**What:** Support arbitrary profile fields. Specifically:
@@ -626,9 +632,11 @@ function isQuietHours(settings): boolean {
**[AUDIT REQUIRED]** — Check if the `matrix-js-sdk` version in use exposes methods for getting/setting arbitrary profile fields beyond `displayname` and `avatar_url`. If not, use `mx.http.authedRequest()` directly.
**Complexity:** Medium.
**COMPLETED June 2026.** `useExtendedProfile` hook reads and writes MSC4133 extended profile fields via `mx.http.authedRequest()` to `PUT /_matrix/client/unstable/uk.tcpip.msc4133/{userId}/{field}`. Settings → Account → Profile now includes Pronouns (`m.pronouns`) and Timezone (`m.tz`) fields with Save buttons. Both fields are displayed in user profile panels alongside the display name and avatar when set.
---
### [ ] P2-9 · Show User Local Time in Profile (MSC4175)
### [x] P2-9 · Show User Local Time in Profile (MSC4175)
**Depends on:** P2-8 (Extended Profile Fields) — needs `m.tz` to be fetchable.
**What:** In user profile cards and the member list sidebar, when a user has `m.tz` set, display their current local time:
@@ -639,9 +647,11 @@ function isQuietHours(settings): boolean {
**[AUDIT REQUIRED]** — Depends on #P2-8 being implemented first. The `m.tz` field must be readable from the profile API.
**Complexity:** Low (once P2-8 is done).
**COMPLETED June 2026.** `useLocalTime` hook formats the user's current local time using `Intl.DateTimeFormat` with their `m.tz` IANA timezone. Updates every 60 seconds via `setInterval`. User profile panels display a clock icon, the formatted time, and the timezone abbreviation (e.g. EST, JST). Respects the user's `hour24Clock` setting for 12 vs 24-hour display. Renders nothing if `m.tz` is not set.
---
### [ ] P2-10 · Unverified Device Warning on Send (off by default)
### [x] P2-10 · Unverified Device Warning on Send (off by default)
**What:** Show a subtle warning in the composer area when a message will be sent to a room containing unverified devices. Features:
@@ -655,9 +665,11 @@ function isQuietHours(settings): boolean {
**[AUDIT REQUIRED]** — Verify what the `matrix-js-sdk` (version in use) exposes for checking device verification status per room. The Crypto API varies significantly between SDK versions. This is the riskiest part of this feature.
**Complexity:** Medium.
**COMPLETED June 2026.** `warnOnUnverifiedDevices` setting added (default `false`), toggled via Settings → General → Privacy. When enabled, a `Warning.Container` banner appears above the composer in encrypted rooms containing unverified devices, showing the count and a link to the room's device list. Uses the existing `useUnverifiedDeviceCount()` hook. Sending is never blocked — the banner is informational only.
---
### [ ] P2-11 · Full Push Rule Editor
### [x] P2-11 · Full Push Rule Editor
**Spec:** CS-API §13.13 (stable).
**What:** A dedicated page in Settings → Notifications showing the full push rule tree:
@@ -672,9 +684,11 @@ function isQuietHours(settings): boolean {
**[AUDIT REQUIRED]** — Audit the existing notification settings UI in `src/app/features/settings/`. Confirm exactly which push rules are already exposed and which are missing. The per-room and global mute controls likely use push rules under the hood.
**Complexity:** Medium-High.
**COMPLETED June 2026.** Settings → Notifications now includes an "Advanced Push Rules" section covering override, room, sender, and underride rule kinds (content/keyword rules were already handled by the upstream `KeywordMessages` UI). Each rule row shows a human-readable label for built-in rules, an enable/disable toggle, and a delete button for custom rules. A collapsible add-rule form at the bottom of the room and sender sections lets users create new per-room or per-sender rules by entering the room/user ID.
---
### [ ] P2-12 · Server ACL Viewer/Editor (m.room.server_acl)
### [x] P2-12 · Server ACL Viewer/Editor (m.room.server_acl)
**Spec:** CS-API §13.8 (stable), `m.room.server_acl` state event.
**What:** In room settings (visible to admins/mods with sufficient power level), show the current server ACL:
@@ -687,13 +701,15 @@ function isQuietHours(settings): boolean {
**[AUDIT REQUIRED]** — Confirm the required power level to change `m.room.server_acl` (typically 50 or 100). The UI should only render for users with sufficient power. Check if upstream Cinny exposes any ACL UI.
**Complexity:** Medium.
**COMPLETED June 2026.** `RoomServerACL.tsx` in Room Settings → Server ACL tab. Reads the current `m.room.server_acl` state event and displays editable allow and deny server lists with wildcard pattern validation. An "Allow IP literal addresses" toggle controls the `allow_ip_literals` field. Users without the required state-event power level see the lists as read-only with an explanatory note. Saving sends the updated state event via `mx.sendStateEvent()`.
---
## PRIORITY 3 — Valuable but higher complexity or lower daily frequency
---
### [ ] P3-1 · Message Bookmarks / Saved Messages
### [x] P3-1 · Message Bookmarks / Saved Messages
**What:** Save any message for later reference. Features:
@@ -709,9 +725,11 @@ function isQuietHours(settings): boolean {
**[AUDIT REQUIRED]** — Confirm upstream Cinny has no bookmark/saved messages feature.
**Complexity:** Medium-High.
**COMPLETED June 2026.** Star icon in the sidebar nav opens `BookmarksPanel.tsx` as a right-side panel (mirrors the members list). Right-clicking any message shows "Bookmark" / "Remove Bookmark" in the context menu. Bookmarks stored in `io.lotus.bookmarks` account data with a 500-entry cap; syncs across devices via account data events. The panel offers a text filter, "Jump to message" deep-link buttons, and individual remove buttons per entry.
---
### [ ] P3-2 · Message Scheduling / Send Later (MSC4140)
### [x] P3-2 · Message Scheduling / Send Later (MSC4140)
**Spec:** MSC4140 (Delayed Events). Status: Synapse-stable, not yet in formal spec.
**First:** SSH to `compute-storage-01``pct exec 151 -- bash` (LXC 151 = Synapse) and check:
@@ -732,9 +750,11 @@ curl -s https://matrix.lotusguild.org/_matrix/client/unstable/org.matrix.msc4140
**[SERVER CHECK]** — The Synapse audit above is REQUIRED before any implementation work.
**Complexity:** Medium (if server supports) / High (if needs server config change + client fallback).
**COMPLETED June 2026.** MSC4140 confirmed enabled on `matrix.lotusguild.org`. A clock button next to the send button opens `ScheduleMessageModal.tsx` with a message textarea and a datetime picker. On confirm, the message is sent via the MSC4140 delayed events API (`POST /_matrix/client/unstable/org.matrix.msc4140/rooms/{roomId}/send/m.room.message?delay={ms}`). A collapsible "Scheduled" tray above the composer lists all pending scheduled messages; each entry has a Cancel button that calls the MSC4140 delete endpoint. Scheduled message state is tracked in `scheduledMessages.ts`.
---
### [ ] P3-3 · File Upload Compression (opt-in)
### [x] P3-3 · File Upload Compression (opt-in)
**What:** Before uploading images, present the user with an explicit choice:
@@ -750,6 +770,8 @@ curl -s https://matrix.lotusguild.org/_matrix/client/unstable/org.matrix.msc4140
**[AUDIT REQUIRED]** — Find where image upload preview currently shows in `RoomInput.tsx`. The compression step needs to insert between "file selected" and "upload confirmed".
**Complexity:** Medium.
**COMPLETED June 2026.** The upload preview in `UploadCardRenderer.tsx` now shows a "Compress" checkbox for JPEG and PNG files. When checked, a Canvas API call (`canvas.toBlob(..., 'image/jpeg', 0.82)`) produces a compressed blob client-side. The preview displays both the original size and the compressed size estimate. Compression is strictly opt-in — the checkbox is unchecked by default and GIF/SVG/WebP files skip the option entirely. The compressed blob is substituted into the upload queue when the user confirms.
---
### [ ] P3-4 · Accessibility Improvements (WCAG 2.1 AA)
@@ -788,7 +810,7 @@ curl -s https://matrix.lotusguild.org/_matrix/client/unstable/org.matrix.msc4140
---
### [ ] P3-7 · Room Stats / Insights Panel (opt-in)
### [x] P3-7 · Room Stats / Insights Panel (opt-in)
**What:** An opt-in statistics panel in room settings ("Room Insights" tab). Shows:
@@ -802,6 +824,8 @@ curl -s https://matrix.lotusguild.org/_matrix/client/unstable/org.matrix.msc4140
**[AUDIT REQUIRED]** — Confirm the local Matrix SDK cache exposes enough event history to make the stats meaningful (depends on how many events are cached). Very small caches will show misleading data.
**Complexity:** Medium.
**COMPLETED June 2026.** `RoomInsights.tsx` in Room Settings → Insights tab. Derives all stats from the local timeline cache only; a disclaimer banner at the top clarifies this reflects cached history. Displays: top 5 active members as a bar chart, top 5 reactions as emoji chips with counts, media breakdown across 4 tiles (images/videos/audio/files), and a 24-hour activity heatmap rendered as a CSS bar chart. The tab is not the default — users must explicitly select it in room settings.
---
### [ ] P3-8 · Thread Panel (full side drawer)
+23
View File
@@ -82,10 +82,18 @@ A full custom theme engine layered on top of Cinny's vanilla-extract theming:
- **Image/video captions**: Caption text field on image and video upload — sent as a single event with the media.
- **Location sharing**: Map embed view for incoming location events + static share button. Renders `m.location` events inline with a map tile.
- **Deleted message placeholders**: Redacted `m.room.message`, `m.room.encrypted`, and `m.sticker` events render as "This message has been deleted" with reason (if provided) rather than disappearing. One-line change in the `eventRenderer` filter in `RoomTimeline.tsx`.
- **Message bookmarks / saved messages**: Right-click any message → "Bookmark" / "Remove Bookmark". A star icon in the sidebar nav opens `BookmarksPanel.tsx` — a right-side panel listing all saved messages with room name, preview text, relative timestamp, filter input, "Jump to message" deep-link, and individual remove buttons. Stored in `io.lotus.bookmarks` account data (max 500 entries); syncs across devices. Implemented in `src/app/features/bookmarks/BookmarksPanel.tsx` + `src/app/hooks/useBookmarks.ts`.
- **Message scheduling (MSC4140)**: Clock button next to send opens `ScheduleMessageModal.tsx` with a message textarea and datetime picker. Messages sent via the MSC4140 delayed events API (`org.matrix.msc4140`), confirmed supported on `matrix.lotusguild.org`. A collapsible tray above the composer lists pending scheduled messages with Cancel buttons. Utilities in `src/app/utils/scheduledMessages.ts`.
- **Richer link preview cards**: `UrlPreviewCard.tsx` renders domain-specific cards for 13 sites: YouTube (thumbnail + ▶ play overlay), Vimeo, GitHub (repo parse), Twitter/X (tweet text + media parse), Reddit (subreddit + upvotes + comments), Spotify (artwork), Twitch (LIVE badge + game), Steam, Wikipedia, Discord (server invite), npm, Stack Overflow, and IMDb (poster). Generic cards gain a favicon from Google's S2 service. Cards that produce no renderable content are suppressed.
- **File upload compression (opt-in)**: JPEG and PNG files in the upload preview show a "Compress" checkbox. When checked, a Canvas API call (`toBlob(..., 'image/jpeg', 0.82)`) compresses the image client-side. Original and compressed sizes are shown side-by-side. Compression is strictly opt-in — unchecked by default, skipped for GIF/SVG/WebP.
### Room Customization
- **Personal room name overrides**: Right-click any room in the sidebar → "Rename for me…" to set a local display name visible only to you. Other members see the original name unchanged. A small pencil icon marks rooms with a custom local name. Stored in Matrix account data (`io.lotus.room_names`). Uses `io.lotus.room_names` account data key (based on MSC4431).
- **Export room history**: Room Settings → Export tab. Supports Plain Text, JSON, and HTML formats with optional start/end date range filters. Paginates backwards via `mx.paginateEventTimeline()` with a live progress counter. E2EE-aware — events that failed decryption are skipped rather than exported as garbled ciphertext. Downloads via `Blob` + `<a download>`. Implemented in `src/app/features/room-settings/ExportRoomHistory.tsx`.
- **Room activity / mod log**: Room Settings → Activity tab. Filterable log of `m.room.member` (join/leave/kick/ban/unban/invite), `m.room.power_levels`, `m.room.name`, `m.room.topic`, `m.room.avatar`, and `m.room.server_acl` events. Human-readable descriptions, relative timestamps, type-filter dropdown, Load More pagination, and auto-paginate on mount. Implemented in `src/app/features/room-settings/RoomActivityLog.tsx`.
- **Server ACL editor**: Room Settings → Server ACL tab. Reads and writes `m.room.server_acl` state events. Editable allow and deny server lists with wildcard pattern validation (`*.example.com`). "Allow IP literal addresses" toggle. Read-only view shown to users without the required power level. Implemented in `src/app/features/room-settings/RoomServerACL.tsx`.
- **Room stats / insights**: Room Settings → Insights tab (not the default tab). Derives all statistics from the local timeline cache only; a disclaimer banner clarifies this. Shows top 5 active members (bar chart), top 5 reactions (emoji chips), media breakdown (images/videos/audio/files tiles), and a 24-hour activity heatmap (CSS bar chart). Implemented in `src/app/features/room-settings/RoomInsights.tsx`.
### Per-Message Read Receipts
@@ -134,10 +142,13 @@ Emoji reaction buttons styled for terminal mode via `button[data-reaction-key]`
- **Custom status message**: Set a short status text (up to 64 characters) with an emoji picker, shown below your display name in member lists and presence displays. Accessible via Settings → Account → Profile. Includes an **auto-clear timer** (options: 30 minutes, 1 hour, 4 hours, 1 day, 3 days, 7 days) — after the timer expires, the status is automatically cleared by setting `status_msg: ''` via `mx.setPresence`. A character counter (shown when ≥ 56/64 chars) prevents overflow. Implemented in `src/app/features/settings/account/Profile.tsx`.
- **Presence badges on members**: Online/busy/away dots shown next to users in the room members drawer and settings members panel (`PresenceBadge` component from `src/app/components/presence/Presence.tsx`).
- **Document title unread count**: Tab title updates to `(N) Lotus Chat` for mentions, `· Lotus Chat` for unreads, `Lotus Chat` when clear.
- **Extended profile fields (MSC4133)**: Settings → Account → Profile includes Pronouns (`m.pronouns`) and Timezone (`m.tz`) fields, saved via MSC4133 `PUT /_matrix/client/unstable/uk.tcpip.msc4133/{userId}/{field}`. Both fields are displayed in user profile panels. Implemented via `src/app/hooks/useExtendedProfile.ts`.
- **User local time in profile**: When a user has `m.tz` set, their profile panel shows a clock icon, their current local time, and the timezone abbreviation (e.g. EST, JST). Updates every 60 seconds. Respects the viewer's `hour24Clock` setting. Implemented via `src/app/hooks/useLocalTime.ts`.
### UX & Composer
- **Message length counter**: A muted character counter appears just left of the send button while typing, disappearing when the composer is empty. Resets on room switch.
- **Unverified device warning**: `warnOnUnverifiedDevices` setting (default off). When enabled via Settings → General → Privacy, a warning banner appears above the composer in encrypted rooms that contain unverified devices, showing the count. Sending is never blocked — the banner is informational only. Uses the existing `useUnverifiedDeviceCount()` hook.
- **Sidebar room filter**: A search-icon input at the top of the Home and DMs sidebar tabs filters rooms by display name in real time. Clears on tab switch. Styled to match the members-drawer search bar (`size="400"`, search prefix icon).
- **DM last message preview**: Each DM row in the sidebar shows a truncated message body (48 chars) and relative timestamp (`Xm`, `Xhr`, `Yesterday`, `D MMM`) below the room name, sourced reactively from `useRoomLatestRenderedEvent`. Encrypted rooms show "Encrypted message" only on actual decryption failure.
- **Room sort order**: Sort icon in the Rooms sidebar header lets users sort non-space rooms by Recent Activity (default), A→Z, or Unread First. Persists via `homeRoomSort` setting.
@@ -158,6 +169,7 @@ Emoji reaction buttons styled for terminal mode via `button[data-reaction-key]`
- **Custom notification sounds**: `messageSoundId` / `inviteSoundId` settings select per-category notification sound (`notification.ogg`, `invite.ogg`, `call.ogg`, or None). Settings → Notifications expands the sound toggle with Message Sound + Invite Sound selects and ▶ preview buttons. Shared `notificationSounds.ts` module.
- **Notification quiet hours**: `quietHoursEnabled` / `quietHoursStart` / `quietHoursEnd` settings suppress all desktop notifications and sounds during a configured time window. Handles overnight spans (e.g. 23:0008:00). Settings → Notifications: Quiet Hours card with toggle + start/end time pickers.
- **Full push rule editor**: Settings → Notifications → Advanced Push Rules section. Covers override, room, sender, and underride rule kinds. Each row has a human-readable label for built-in rules, an enable/disable toggle, and a delete button for custom rules. An add-rule form at the bottom of the room and sender sections lets users create new per-room or per-sender push rules by entering the room/user ID.
### Calls (Extended)
@@ -239,3 +251,14 @@ Built files are served from `/var/www/html/` on LXC 106 (nginx). Config lives at
| `src/app/features/room/PollCreator.tsx` | Poll creation modal (single/multiple choice, 210 options) |
| `src/app/features/common-settings/general/RoomShareInvite.tsx` | Invite link + QR code tile for room settings |
| `src/app/utils/syntaxHighlight.ts` | TDS code syntax tokenizer (JS/TS/Python/Rust → inline CSS vars) |
| `src/app/features/room-settings/ExportRoomHistory.tsx` | Export room messages to plain text / JSON / HTML with date range filter and E2EE awareness |
| `src/app/features/room-settings/RoomActivityLog.tsx` | Filterable mod log of room state events (joins, kicks, bans, power level changes, etc.) |
| `src/app/features/room-settings/RoomServerACL.tsx` | Server ACL viewer/editor (allow/deny lists, IP literal toggle, power-level gated) |
| `src/app/features/room-settings/RoomInsights.tsx` | Room stats panel: top members bar chart, top reactions, media breakdown, 24h heatmap |
| `src/app/features/bookmarks/BookmarksPanel.tsx` | Saved messages sidebar panel with filter, jump-to-message, and remove |
| `src/app/hooks/useBookmarks.ts` | Read/write `io.lotus.bookmarks` account data for message bookmarks |
| `src/app/features/room/ScheduleMessageModal.tsx` | Schedule-message modal with datetime picker; sends via MSC4140 delayed events API |
| `src/app/utils/scheduledMessages.ts` | Utilities for MSC4140 scheduled message state and cancel endpoint |
| `src/app/hooks/useExtendedProfile.ts` | Read/write MSC4133 extended profile fields (`m.pronouns`, `m.tz`) |
| `src/app/hooks/useLocalTime.ts` | Formats user local time from `m.tz` IANA zone; updates every 60 s |
| `src/app/components/url-preview/UrlPreviewCard.tsx` | Domain-specific URL preview cards for 13 sites + generic favicon fallback |