iOS triggered both the app context menu and native text selection overlay
on long-press. Three-layer fix: CSS user-select:none on buttons/[role=button]/
[data-context-menu] elements; non-passive touchstart listener with conditional
preventDefault on context-menu targets; removeAllRanges() at timer fire to
clear any residual selection. Added data-context-menu to all mobile surfaces
that use onContextMenu handlers.
Add useGlobalLongPress to ContextMenuRenderer that detects 500ms hold
with <10px movement and dispatches a synthetic contextmenu event on the
touched element. All existing onContextMenu handlers (spaces, channels,
voice users, messages, etc.) now work on touch devices automatically.
Remove per-message useLongPress integration from Message.tsx since the
global handler covers it.
ResizeObserver and media load callbacks were using isNearBottomRef
(5000px threshold) to decide whether to auto-scroll to bottom. On
mobile, inertial flick scrolling hadn't moved far enough to clear
that threshold before the observer fired, snapping the user back.
Now these callbacks check actual scroll distance at fire time with
a tight 150px threshold — only auto-scrolls if truly at the bottom.
1. Chat scroll: add flex flex-col to MobileChatScreen messages wrapper
so MessageList's flex-1 actually works (parent must be flex container)
2. Instance General panel: add fetchInstanceSettings/fetchStreamingLimits
to MobileInstancePanel mount (mirrors desktop InstancePanel useEffect)
3. Friends activity: add Activity tab to mobile FriendsPage showing
friends grouped by activity status (active, online, offline)
Create useLongPress hook with 500ms delay, 10px movement cancellation
(Euclidean distance), and ghost click suppression. Integrate into
Message component to trigger the existing context menu bottom sheet
on touch devices.
Adds uploadWithProgress() using XMLHttpRequest for real-time upload
progress events. MessageInput now shows per-file progress bars with
percentage overlay during upload. Failed uploads show toast warnings
with the specific error instead of silently failing. Upload timeout
raised to 10 minutes for large files.
- Add role="status" and aria-label to all skeleton containers
- Refactor ChannelSidebar from conditional siblings to ternary
for consistent mutual exclusivity with MemberSidebar pattern
YouTube's embed API requires knowing the embedding origin to authorize
playback. The referrerPolicy="no-referrer" was stripping the Referer
header, and the missing origin param meant youtube-nocookie.com couldn't
verify the embed source, causing Error 153 (video player configuration
error). Fix: pass origin param explicitly and let the browser send the
default referrer.
1. Video aspect ratio: remove container border/overflow-hidden, use
preload="metadata" so browser knows dimensions before play
2. YouTube Error 153: remove sandbox attr (too restrictive), add full
allow permissions (encrypted-media, accelerometer, gyroscope, etc.)
3. Google Images not displaying: detect image Content-Type from HTTP
response in metadataFetcher, override classifier to create image
embed for URLs that serve image/* content
4. Audio seeking broken: add HTTP Range request support in uploads
route (Accept-Ranges, Content-Range, 206 Partial Content)
- Fix ?? to || in metadataFetcher.ts to handle empty strings from Cheerio
- Fix stale embeds on message edit: delete old embeds synchronously before
broadcast, then resolve new ones async (all 4 edit paths: REST+WS, msg+DM)
- Revert unrelated MessageList.tsx scroll threshold change (5000 not 150)
- Remove duplicate embed indexes from migrateAddIndexes (kept standalone ones)
Switches Message.tsx from client-side URL metadata fetching (old Embed.tsx)
to server-driven embed objects via message.embeds[]. Delegates attachment
rendering to AttachmentRenderer. Removes firstUrl extraction and urlRegex.
- Save the top-visible message ID on channel leave, restore via
scrollIntoView on return (immune to lazy-loaded image reflow)
- Add floating glass-bubble "Jump to Present" button when scrolled
5000px+ from bottom
- Clear stale scroll anchors when user returns to bottom
- Evict scroll positions alongside channel cache eviction
ResizeObserver alone misses scroll adjustments when multiple images load
in quick succession due to browser layout-loop suppression. A capture-phase
load event listener on the content wrapper reliably catches all descendant
image loads and re-scrolls to bottom.
Root cause: own messages echoed by the server marked channels unread when
the user had already navigated away. Seven related bugs compounded the
problem — stale read states, missing cleanup on space/DM removal, REST
broadcast ignoring VIEW_CHANNEL, and no validation on channel_ack writes.
Frontend:
- Skip markChannelUnread for the user's own messages (federation-aware)
- Walk backward past temp_ IDs in ackChannel instead of bailing
- Re-fire ack timer when temp message is replaced by server-confirmed ID
- Add removeChannelStates to clean up unread/read/message caches
- Clean up chatStore on removeSpace, removeDmChannel, removeInstanceSpaces
Server:
- Use sendToChannel instead of sendToSpace for REST message creation
- Clean up read_states on space deletion, member kick/leave, and ban
- Validate channel membership before accepting channel_ack writes
- Clean up read_states on DM leave and DM channel deletion
Click trash icon to arm (morphs to green checkmark), click again to
confirm. Auto-cancels after 3s or 2s after mouse leaves. Uses CSS
scale+opacity transitions for a smooth icon swap animation.
Remove width: 100% !important override that fought emoji-mart's shadow
DOM grid. Container now uses w-fit to wrap content tightly. GIF picker
gets explicit w-[390px] to maintain its own width independently.
- SSRF protection: DNS resolution + private IP blocking on metadata fetcher
- Upload security: CSP/X-Frame-Options headers, SVG forced download, nosniff
- Auth hardening: JWT secret min length, password min 8 chars, token revocation via password_changed_at
- Attachment ownership verification before linking to messages
- Message length limit (4000 chars) enforced on client and server
- Asset URL validation on avatar/banner updates
- Federation instance validation (domain regex, origin scheme, length limits)
- DB indexes on all FK columns for query performance
- Migrations: nullable moderator columns, dm_messages reply_to FK constraint
- File cleanup on avatar/banner replacement and space deletion
- Fastify trustProxy, AbortController on fetches, typing map size cap
- Add subtle border + inset shadow to input tiers for resting-state visibility
- Fix DmSearchBar and SearchPopover containers missing input depth treatment
- Fix focus ring clipping in settings panel scroll container
- Swap search and member list toggle button positions in channel/DM headers
- Add "Discover People" section to Add Friend tab with user cards, mutual counts, and inline actions
- Add discoverStore for fetching/searching discoverable users across local and federated instances
- Add PrivacyPanel to user settings with discoverability toggle
- Add is_discoverable column to users table with migration
- Fix "Send Friend Request" button vertical alignment using transform centering
Avatars/banners now resize to max 512px/1920px and convert to WebP before
upload (zero server cost). Chat image uploads generate an 800px-wide WebP
thumbnail via Sharp; the feed shows the thumbnail, click opens the full-res
original. Adds lazy loading to avatars. Federation-compatible: remote
instances without this feature fall back gracefully.
Gutter column was 56px (w-14) with the 16px gap baked into the width.
This worked for avatars but left only 4px between compact hover
timestamps and message text. Switch to w-10 (40px) gutter + gap-4
(16px flex gap), preserving the same 76px content offset while giving
timestamps proper breathing room.