Skip to content

fix(mobile): missing items and fixes in long-press message menu#879

Closed
Just-Insane wants to merge 21 commits into
SableClient:devfrom
Just-Insane:fix/mobile-message-menu
Closed

fix(mobile): missing items and fixes in long-press message menu#879
Just-Insane wants to merge 21 commits into
SableClient:devfrom
Just-Insane:fix/mobile-message-menu

Conversation

@Just-Insane
Copy link
Copy Markdown
Contributor

@Just-Insane Just-Insane commented May 19, 2026

Description

The mobile long-press message menu was missing several actions available on desktop. This adds the missing items (bookmark toggle, report, etc.), fixes the bookmark toggle handler, and resolves two iOS-specific issues: the virtual keyboard covering the menu on open, and the nickname input not receiving focus correctly.

Fixes #

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings

AI disclosure:

  • Fully AI generated (explain what all the generated code does in moderate detail).
  • Partially AI assisted (clarify which code was AI assisted and briefly explain what it does).

The missing menu items and iOS fixes were drafted with AI assistance. The bookmark toggle was wired to the correct handler (the previous one was a no-op). The iOS keyboard issue is addressed by calling blur() on any focused input before opening the bottom sheet, preventing the virtual keyboard from pushing the menu above the viewport. The nickname input focus fix schedules a focus() call in a requestAnimationFrame callback after the sheet animation completes.

Tracks Issue #9 with root cause analysis and proposed fix using visualViewport API.
Implementation will follow after proper testing on iOS devices.
When the thread drawer is open alongside the main room view, two
RoomInput components each mount their own useKeyboardHeight instance.
On keyboard open the thread instance had savedHeight=0 (freshly mounted),
so its immediate-estimate branch fell back to viewport.height — the
wrong mid-animation value — and overwrote the correct estimate already
written by the main room instance.  This produced a third layout change
(wrong height → correct height) visible as jank on every keyboard open.

Fix: promote savedHeight, cssVarsSet, and the mount reference counter to
module-level variables so all instances share them.

- sharedSavedHeight: all instances read and write the same value, so the
  estimate is always correct even for newly-mounted instances.
- cssVarsApplied: the 'only set once while keyboard open' guard now works
  across instances, preventing double setCSSVars calls.
- mountCount: reference-counted so only the last instance to unmount
  clears the CSS variables — prevents the thread drawer unmounting while
  the main room keyboard is still open from wiping --sable-visible-height.
- Increase swipe thresholds (velocity 0.5→1.2, distance 100-120→150-180)
  in SwipeableChatWrapper, SwipeableOverlayWrapper, SwipeableMessageWrapper
  to reduce accidental navigation triggers

- Replace double-tap context menu trigger with 500ms long-press
  (useMobileLongPress) that cancels on scroll or >10px movement

- Add 'Copy Text' menu item (MessageCopyTextItem) to message context menus;
  prefers m.new_content.body for edited messages, returns null if redacted

- Fix file picker on iOS Safari: append hidden input to document.body before
  .click() and remove after selection so the native dialog reliably appears

- Add autoCorrect="on" to Slate Editable alongside autoCapitalize="sentences"
  for correct iOS sentence-case and autocorrect behaviour

- Use height:100dvh on <html> so the layout shrinks when the on-screen
  keyboard opens (iOS/Android), keeping the app anchored at the top;
  add interactive-widget=resizes-content to viewport meta for Android Chrome

- Keep EmojiBoard mounted after first open via createPortal + display:none
  toggling instead of unmounting through PopOut; add active prop to
  EmojiBoard to deactivate FocusTrap when hidden, avoiding re-initialisation
  of the virtualizer on every open
Two root causes for notifications stopping after a while:

1. sw.ts hasVisibleClient used OR logic (appIsVisible || matchAll visible):
   On iOS the SW is killed between pushes so appIsVisible resets to false
   on restart.  With OR logic, a stale matchAll() result with
   visibilityState='visible' still set hasVisibleClient=true, silently
   suppressing every notification after the first.

   Fix: switch to AND logic so BOTH appIsVisible AND a visible client are
   required to suppress.  A cold-start SW (appIsVisible=false) never
   suppresses, regardless of stale matchAll() data.

2. useAppVisibility.ts was passing isMobile as keepEnabledWhenVisible,
   meaning on desktop the pusher was deleted from the homeserver whenever
   the tab was visible.  If the async re-enable in enablePushNotifications
   didn't complete before the page was torn down, the homeserver was left
   with no pusher — so no more push notifications until a manual
   background/foreground cycle.

   Fix: always pass true for keepEnabledWhenVisible so the pusher stays
   registered permanently.  The SW's hasVisibleClient check handles
   OS-notification suppression in the foreground; the homeserver never
   needs to be without a pusher.
- SlidingSyncManager.onConnectionChange now calls slidingSync.resend()
  when the device comes back online, so the sync retries immediately
  instead of staying idle.
- Add SlidingSyncManager.retryNow() public method that calls resend().
- useAppVisibility: on visibilitychange → visible, call
  mx.retryImmediately() (classic sync) and getSlidingSyncManager(mx)?.retryNow()
  (sliding sync) so the PWA reconnects when opened from the home screen.

The SDK's SlidingSyncSdk.retryImmediately() is a no-op stub, so the
visibility path was previously a dead end for sliding sync users.
SyncStatus initialised stateData.current as null, so when the splash was
dismissed via the fast-path (cached rooms before first sync), the component
would miss the null→Syncing transition if sync had already started by the
time it mounted. Initialise current from mx.getSyncState() so the banner
correctly shows "Connecting..." whenever the component first renders while a
sync is already in progress.
When a sync gap triggers TimelineReset (e.g. mobile PWA returning from
background or opening from notification), getInitialTimeline() loads new
events directly into timeline state. useLiveEventArrive never fires for
events that arrived via reset, so if the user was scrolled up the unread
jump bar was never shown. Call setUnreadInfo explicitly in that case.
- View Reactions (when relations are available)
- Pin / Unpin Message (when canPinEvent)
- Set / Edit Nickname for other users (inline edit within the sheet)
- Kick from Room for moderators with sufficient power level

Also passes canPinEvent and relations from MessageInternal to
MobileMessageMenu so the desktop and mobile menus stay in parity.

Fix missing mobileOrTablet import in useAppVisibility.
- Add bookmark add/remove action to mobile long-press menu (respects
  enableMessageBookmarks setting, same as desktop menu)
- Fix: when the nickname input opens, use visualViewport resize events
  to lift the bottom sheet above the virtual keyboard so the input is
  not covered on iOS/Android
@Just-Insane Just-Insane force-pushed the fix/mobile-message-menu branch from a6444a7 to 6354bbe Compare May 19, 2026 23:35
@Just-Insane
Copy link
Copy Markdown
Contributor Author

This PR has been superseded by #874 (feat/mobile), which includes the mobile message menu fixes along with broader mobile UI improvements. Closing in favor of that PR.

@Just-Insane Just-Insane deleted the fix/mobile-message-menu branch May 21, 2026 03:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant