Settings dropdowns (Bug #3): - Add reusable SettingsSelect component using Menu+PopOut+FocusTrap — exact same pattern as Message Layout, so all dropdowns look consistent - Replace raw <select> for Seasonal Theme, UI Font, AFK Timeout, and Join & Leave Sounds with SettingsSelect Animated chat backgrounds bleeding onto content (Bug #6 / #7): - Remove filter:brightness() and opacity animations from chatBackground.ts (animRainGlowKeyframe, animGridBrightnessKeyframe, animFirefliesGlowKeyframe, animFirefliesBlinkKeyframe). These were applied to the Page element which caused ALL descendants (messages, composer) to flash in sync. Also created a CSS stacking context on Page that pushed SeasonalEffect (position:fixed; z-index:9997) behind the animated background layer. - Only backgroundPosition / backgroundSize animations remain — safe, do not affect descendants, and do not create stacking contexts. - Remove now-unused animation keyframe imports from chatBackground.ts. Voice ringing in persistent rooms (Bug #5): - Narrow the ringing condition from (Invite|Knock|Restricted) to only Invite, matching exactly the rooms where the call button is visible. - Add room.isCallRoom() early-exit so m.join_rule:call rooms never ring. Avatar decoration images not loading (Bug #8): - Change loading="lazy" → loading="eager" in DecorationPreviewCell. Lazy loading does not reliably trigger for images inside nested overflow scroll containers (the settings panel scroll area), so images never loaded. Docs: LOTUS_BUGS.md updated with root cause and resolution for all 5 new bugs. Docs: LOTUS_TODO.md adds P5-35/P5-36 (deferred desktop notification/jump list). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
9.6 KiB
Lotus Chat — Bug Report & Technical Audit
Date: June 2026
This document tracks identified bugs, edge cases, and architectural discrepancies found during the audit of the Lotus Chat codebase. Recommended fixes are provided for each item.
🛡️ Critical Security & Privacy Regressions
1. E2EE Bypass in Media Gallery Downloads
File: src/app/features/room/MediaGallery.tsx (Line 855)
Status: CRITICAL
- Issue: The "Download" button in the Files tab uses
mxcUrlToHttpdirectly and clicks an<a>link. - Impact: In encrypted rooms, this downloads the encrypted ciphertext rather than the decrypted file. Users cannot open the downloaded files.
- Recommended Fix:
- Check if the event is encrypted.
- If encrypted, use the
decryptAttachmentlogic (similar touseDecryptedMediaUrl) to decrypt the file in memory. - Use
file-saveror a Blob URL to trigger the download of the decrypted plaintext.
2. Privacy Leak in URL Previews
File: src/app/components/url-preview/UrlPreviewCard.tsx (Line 1655)
Status: PRIVACY RISK
- Issue: Generic URL preview cards fetch favicons directly from
https://www.google.com/s2/favicons?domain=.... - Impact: This leaks the user's browsing/chat activity (domains of links they see) to Google. It bypasses the "proxied through Matrix" privacy standard.
- Recommended Fix: Use the proxy URL returned by the Matrix
/_matrix/media/v3/preview_urlendpoint instead of contacting Google directly.
🚩 Functional & Logic Bugs
1. Presence Updater Reverts Status Updates
File: src/app/hooks/usePresenceUpdater.ts (Line 20)
Status: ✅ RESOLVED (June 2026)
- Issue: The
storedStatusvariable was captured once when theuseEffectstarted. - Impact: If a user updated their status message in Profile Settings, the hook would continue broadcasting the old message on every activity event, silently reverting the change.
- Fix Applied: Replaced the single
localStorage.getItemread with areadStatus()function called inside everysetOnlineandsetUnavailableinvocation, ensuring the current value is always used.
2. Audio Playback Rate Reset
File: src/app/components/message/content/AudioContent.tsx (Line 97)
Status: UX Bug
- Issue: The
playbackRateis set in auseEffectthat only depends on[playbackSpeed]. - Impact: If a user selects a playback speed before the audio blob has finished loading, the
<audio>element may reset its rate to 1.0 once the<source>is added. - Recommended Fix: Add
srcState.datato theuseEffectdependencies or set theplaybackRatein anonCanPlayhandler on the audio element.
3. Room Insights are Static (No Live Updates)
File: src/app/features/room-settings/RoomInsights.tsx (Line 60)
Status: Medium Priority
- Issue: Stats are calculated in a
useMemothat only depends on[room]. - Impact: If new messages arrive while the Insights page is open, the statistics (message counts, top participants, etc.) do not update.
- Recommended Fix: Add a listener for
RoomEvent.Timelineinside the component to trigger a recalculation when new events are added to the room.
4. Incorrect Ringing in Voice Rooms
File: src/app/components/CallEmbedProvider.tsx
Status: ✅ RESOLVED (June 2026)
- Issue: Joining a static voice room (Public Space channel) triggered the "Incoming Call" ringing animation and sound.
- Root Cause:
getStateEvent(room, StateEvent.SpaceParent)always returnedundefinedbecausem.space.parentevents use the parent space's room ID as the state key, not an empty string. The ringing suppression check silently failed. - Fix Applied: Replaced
getStateEventwithgetStateEvents(plural), which returns all events of a given type regardless of state key. A room with anym.space.parentevent is correctly identified as a space channel and suppresses ringing.
🎨 UI/UX & Visual Consistency
1. Hardcoded Primary Color in Polls
File: src/app/components/message/content/PollContent.tsx (Line 245)
Status: TDS Violation
- Issue: Uses
rgba(var(--mx-primary-rgb, 0,132,255), ...)for selected options and borders. - Impact: Polls look like standard Cinny and ignore the Lotus Terminal Design System (TDS) colors.
- Recommended Fix: Use
var(--lt-accent-cyan)or the primary theme accent color.
2. Inaccessible Room Menu on Mobile
File: src/app/features/room-nav/RoomNavItem.tsx (Line 643)
Status: MOBILE BUG
- Issue: The room menu icon (
VerticalDots) is hidden on mobile becausehoveris never active. - Impact: Mobile users cannot access room settings, mark as read, or leave rooms from the sidebar.
- Recommended Fix: Ensure the menu button is visible on mobile for the active room or provide an alternative trigger.
3. Inconsistent Settings Dropdown Styling
File: src/app/features/settings/general/General.tsx
Status: ✅ RESOLVED (June 2026)
- Issue: The dropdowns for "Join & Leave Sounds", "UI Font", and "Seasonal Theme" used raw HTML
<select>elements, which render differently from the custom-styledMenu+PopOutused for "Message Layout" and other settings. - Fix Applied: All three raw
<select>elements replaced with a reusableSettingsSelectcomponent using the Menu+PopOut+FocusTrap pattern consistent with the rest of the settings UI.
4. No Camera Focus During Screenshare
File: src/app/features/call/CallControls.tsx
Status: UX Bug — blocked by Element Call internals
- Issue: When someone is screensharing and another participant turns on their camera, there is no way to switch the primary display to the camera or go fullscreen on it.
- Root Cause: Element Call's spotlight/layout is controlled internally by EC. Lotus Chat injects the call iframe and cannot easily override EC's participant tile behavior without forking the EC widget.
- Recommended Fix: Implement a "Pin/Focus" toggle on participant tiles that overrides the automatic screenshare spotlight — requires EC upstream changes or a custom message bridge.
5. Ringing Modal Fires in Persistent Voice Rooms
File: src/app/components/CallEmbedProvider.tsx (line 337–342)
Status: ✅ RESOLVED (June 2026)
- Issue: Joining a persistent voice room (not a DM or transient group call) showed the incoming call ringing modal and animation.
- Root Cause: The
isPrivateGroupcondition includedJoinRule.RestrictedandJoinRule.Knockrooms. Lotus Guild voice rooms areRestrictedjoin-rule rooms. Theirm.space.parentstate event was being checked but some rooms were set up with only the space-sidem.space.childrelationship, leaving nom.space.parenton the room itself — so they passed asisPrivateGroupand triggered ringing. - Fix Applied: Narrowed
isPrivateGroupto onlyJoinRule.Inviteto match the exact set of rooms where the call button is shown. Also addedroom.isCallRoom()early-exit so rooms withm.join_rule.calltype never ring.
6. Animated Chat Backgrounds Affect Message Content
File: src/app/features/lotus/chatBackground.ts
Status: ✅ RESOLVED (June 2026)
- Issue: Animated backgrounds that use
filter: brightness()oropacityanimations (Digital Rain glow, Grid Pulse brightness, Fireflies glow/blink) applied those effects to the entire<Page>element, causing all message content and the composer to flash/flicker in sync with the animation. - Root Cause:
filterandopacityCSS properties affect an element AND all its descendants. Applying these as part of theanimationshorthand on thePagecontainer made them "inherited" visually by everything inside the room view. - Side Effect:
filteranimation also created a CSS stacking context on Page, which pushed Seasonal Theme overlays (position:fixed; z-index:9997) behind the Page compositor layer. - Fix Applied: Removed
animRainGlowKeyframe,animGridBrightnessKeyframe,animFirefliesGlowKeyframe, andanimFirefliesBlinkKeyframefromchatBackground.ts. OnlybackgroundPosition/backgroundSizeanimations remain — these are safe and do not affect descendants or create stacking contexts.
7. Seasonal Themes Display Behind Chat Background
File: src/app/components/seasonal/SeasonalEffect.tsx
Status: ✅ RESOLVED (June 2026) — root cause was Bug #6
- Issue: Seasonal theme overlays (position:fixed; z-index:9997) appeared behind animated chat backgrounds.
- Root Cause: The
filteranimation on<Page>created a CSS stacking context, causing Page's GPU compositing layer to render above the fixed-position seasonal overlay in some browsers. Removing the filter animations (Bug #6 fix) resolves the stacking context issue. - Fix Applied: See Bug #6. No additional changes to SeasonalEffect required.
8. Avatar Decoration Images Not Rendering in Settings
File: src/app/features/settings/account/ProfileDecoration.tsx
Status: ✅ RESOLVED (June 2026)
- Issue: Under Settings → Account → Avatar Decoration, no decoration images were visible.
- Root Cause: The
DecorationPreviewCellusedloading="lazy"on decoration images. The browser's lazy-loading algorithm determines image visibility from the viewport, but the decoration grid is inside a nestedoverflowY: autoscroll container inside a settings panel — the browser did not correctly detect these images as near the viewport and never triggered them to load. - Fix Applied: Changed
loading="lazy"toloading="eager"inDecorationPreviewCell. The settings panel is user-initiated, so eager loading is appropriate.