Commit Graph
642 Commits
Author SHA1 Message Date
Jannis Braun 161e876df8 fix: IncomingCallModal calls connectFn directly for iOS gesture context 2026-03-23 21:09:37 +01:00
Jannis Braun 241451ca18 feat: gesture-aware voice connection, remove AppLayout auto-connect
Replace the useEffect-based auto-connect pattern in AppLayout with
direct connect/disconnect calls from user gesture contexts. This is
required for iOS (AudioContext.resume + getUserMedia must happen in
a gesture handler) and aligns with tightening autoplay policies on
desktop browsers.

Architecture:
- voiceStore gains connectFn/disconnectFn refs, registered by AppLayout
  from the single useLiveKit() instance.
- All voice join paths (ChannelSidebar, MobileSpacesScreen, MainContent,
  voice_moved WS handler) pass connectFn to joinVoiceChannel().
- All disconnect paths (VoiceControls, voiceActions, MobileVoiceFullScreen,
  MobileVoiceMiniBar, dm_call_ended/rejected WS handlers, ready handler)
  call disconnectFn() directly.
- dm_call_accepted WS handler calls connectFn() to initiate the DM call
  LiveKit connection.
- The 55-line auto-connect useEffect and lastAttemptedRef are removed.
2026-03-23 21:06:19 +01:00
Jannis Braun 8aa66816d8 feat: add MobileVoiceJoinSheet bottom sheet for voice preview 2026-03-23 20:59:26 +01:00
Jannis Braun acf6f6fe99 refactor: align MobileVoiceFullScreen voice state resolution
Brings mute/deafen state resolution in line with VoiceChannel.tsx:
- Use LiveKit participant state (then WS fallback) for mute/deafen instead of only voiceUserStates
- Use local isMuted/isDeafened store values for the current user (self)
- Add spaceMutedUserIds, spaceDeafenedUserIds, permissionMutedUserIds checks
- Render deafened indicator (headphones-slash) when user is deafened vs muted
- Add LiveKit participant username and userId as terminal fallbacks in display name chain
2026-03-23 20:55:51 +01:00
Jannis Braun cd30774596 feat: show inline voice users in mobile sidebar 2026-03-23 20:52:12 +01:00
Jannis Braun 7c1ec5e846 refactor: use VoiceUserRow in desktop VoiceChannel 2026-03-23 20:46:47 +01:00
Jannis Braun ba11eefb0c fix: add isUnwatchedCamera prop and remove unused React import
Adds camera slash overlay for unwatched cameras, matching
VoiceChannel.tsx. Removes unnecessary React import (Vite
automatic JSX transform).
2026-03-23 20:44:44 +01:00
Jannis Braun 00c5757311 feat: add VoiceUserRow pure UI component 2026-03-23 20:40:29 +01:00
Jannis Braun f27140f1c8 feat: add space folder support to MobileSpacesScreen
Folder-aware layout resolution, folder icons in space strip,
bottom sheet folder flyout with rename/color/ungroup management.
Full folder CRUD via context menus: create, move to, remove from.
2026-03-23 19:34:24 +01:00
Jannis Braun 0d9ee44eee feat: add federation badges to mobile surfaces
Globe/amber badge on federated space icons, @domain on federated
DM users, home instance on the You screen profile card.
2026-03-23 19:27:39 +01:00
Jannis Braun 32be1bceab feat: add voice participant context menus on mobile
Long-press on voice participants shows moderation items (space mute,
deafen, disconnect, move) and local audio controls (mute, volume).
Skips self and DM calls.
2026-03-23 19:25:41 +01:00
Jannis Braun 937c4b68b2 feat: add leave group context menu on MobileDmsScreen
Long-press on group DMs shows Leave Group option.
2026-03-23 19:24:17 +01:00
Jannis Braun f84b1726bf feat: add context menus to MobileSpacesScreen
Add onContextMenu handlers for space icons (invite, folder management,
transfer, leave), channel items (settings, delete), and category headers
(settings, delete). Includes full folder management handlers.
2026-03-23 19:21:14 +01:00
Jannis Braun 8f104e1c5b fix: derive spaceId from voice channel in buildVoiceModMenuItems
Use channelToSpaceMap.get(channelId) for permission checks instead of
currentSpaceId from the navigation store. On mobile, UI navigation
decouples from the active voice session.
2026-03-23 16:32:47 +01:00
Jannis Braun fd6be8c713 fix: suppress native iOS callout on mobile long-press
Add -webkit-touch-callout: none on mobile viewports to prevent the
native iOS copy/lookup popup from racing with our context menu.
Clear any text selection when the long-press timer fires.
2026-03-23 15:26:09 +01:00
Jannis Braun ce33d8f7ea feat: global long-press context menus for all mobile touch targets
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.
2026-03-23 15:12:58 +01:00
Jannis Braun 05a4b23d88 fix: prevent scroll snap-back during mobile momentum scrolling
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.
2026-03-23 15:04:44 +01:00
Jannis Braun 2c372a5d34 fix: three mobile bugs — scroll, GeneralPanel loading, activity tab
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)
2026-03-23 14:56:07 +01:00
Jannis Braun 80c32ae1e6 feat: add long-press context menus for mobile messages
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.
2026-03-23 14:22:38 +01:00
Jannis Braun 77a1a7920c feat: add standalone MobileMembersScreen replacing blank member panel
Create MobileMembersScreen that renders space members grouped by role
without depending on desktop memberListOpen flag or hidden md:block.
Add members button to voice full screen header for space channels.
2026-03-23 14:20:06 +01:00
Jannis Braun 54b367924d feat: add drill-down navigation for Instance settings on mobile
Replace inline InstancePanel render with MobileInstancePanel that shows
a navigation list for General, Streaming, Storage, and Users sub-panels.
Each sub-panel pushes onto the mobile screen stack with MobileScreenHeader.
2026-03-23 14:16:56 +01:00
Jannis Braun 7c40a230bc feat: add reusable MobileScreenHeader component
Extracts the back-button header pattern used across all mobile screens
into a single component. Props: title (string), rightActions (ReactNode).
2026-03-23 14:15:04 +01:00
Jannis Braun d8c5e7ff16 fix: restore mobile chat scrolling by fixing height constraint chain
Add overflow-hidden to MobileScreenStack stacked screen wrapper to
establish block formatting context. Add min-h-0 to MobileChatScreen
messages wrapper to override flex min-height: auto default.
2026-03-23 14:14:18 +01:00
Jannis Braun 9a5e613331 feat: add upload progress bars and error toasts
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.
2026-03-23 02:51:56 +01:00
Jannis Braun fdc8732ba7 fix: raise upload size cap from 500 MB to 5 GB
The 500 MB cap was arbitrary. Self-hosted admins should decide based
on their own disk space. Global multipart ceiling raised to 5 GB,
validation cap to 5120 MB.
2026-03-23 02:36:22 +01:00
Jannis Braun 9044bfd26f fix: use toasts instead of inline banners for Storage panel feedback
Replaces the layout-shifting error banner at the bottom of the panel
with non-intrusive toast notifications. Upload limit save, cleanup
success, and all error feedback now use the existing toast system.
Cleanup preview results stay inline as expected contextual responses.
2026-03-23 02:32:57 +01:00
Jannis Braun 7ba6c4074e feat: add upload limit setting and media retention cleanup to Storage panel 2026-03-23 02:24:25 +01:00
Jannis Braun ee11408060 feat: add age-based media cleanup endpoint
Adds cleanupOldMedia() to the storage janitor and a new endpoint
POST /api/admin/storage/cleanup-media { maxAgeDays, dryRun }.
Deletes chat attachments older than the specified threshold while
preserving profile images.
2026-03-23 02:20:28 +01:00
Jannis Braun 3c2c333a56 feat: show dangling attachment count in Storage panel
Adds a 'Dangling Records' card to the storage overview grid and
includes dangling records in the cleanup eligibility check.
2026-03-23 01:56:51 +01:00
Jannis Braun b3d4747126 chore: remove unused ConnectedInstance type import from federationOps 2026-03-23 00:57:36 +01:00
Jannis Braun aa2c6f7aa1 feat: add 'Sync now' button for connected instances with pending password sync 2026-03-23 00:53:48 +01:00
Jannis Braun 95cb9d4df2 feat: add background retry scheduler for federation password sync
Replace the give-up-after-3-retries path with a persistent background
scheduler: 10 retries every 30s (5 min), then 12 retries every 5 min
(1 hr). Active timers are keyed by origin so rapid password changes
cancel and replace the previous loop. All timers are cleared on logout
via clearPasswordSyncTimers() called from instanceStore.reset().
2026-03-23 00:52:40 +01:00
Jannis Braun 678e1bf358 refactor: make pendingSyncOrigins reactive in Zustand store
Add `pendingSyncOrigins: string[]` to InstanceState so components re-render
when pending password-sync flags change. Hydrate from localStorage on
autoConnectAll, clear on reset, and remove the localStorage-only read path
from `hasPendingPasswordSync`.
2026-03-23 00:50:58 +01:00
Jannis Braun a888828941 chore: gitignore installers and include pending web changes
Add installers/ to .gitignore and remove from tracking — binaries
should not be in git.

Also includes:
- Re-push Electron activities after WebSocket reconnect (sleep/wake)
- Merge activity map instead of replacing to preserve local state
- Nori mascot design plan
2026-03-23 00:25:31 +01:00
Jannis Braun 100ac15939 fix: deduplicate keybind actions when native hook and web fallback both fire
When the app is focused and the native uiohook hook is active, both the
IPC bridge and the web fallback capture the same keypress, dispatching
the toggle action twice (~0-20ms apart). This double-toggles mute/deafen
back to the original state.

Add a 100ms dedup window in dispatchKeybindAction to suppress the second
dispatch. Safe for human input (key repeat starts at ~500ms).
2026-03-22 23:55:59 +01:00
Jannis Braun 313ff4ef87 feat: add Nori mascot to mobile empty states 2026-03-22 23:39:16 +01:00
Jannis Braun b3051c25cd feat: add lonely Nori mascot to empty Explore page 2026-03-22 23:39:15 +01:00
Jannis Braun 4e23cb5bf9 feat: add sleeping Nori mascot to empty DM sidebar 2026-03-22 23:39:13 +01:00
Jannis Braun a4ebd52900 feat: replace Wumpus empty states with Nori mascot on Friends page 2026-03-22 23:37:15 +01:00
Jannis Braun 43eaceadc0 fix: improve animation hook robustness
- Add .catch handler to scheduleNext to prevent unhandled promise
  rejections from killing the scheduling loop
- Replace `as unknown as Animation` cast with Cancellable interface
  for type-safe cleanup handles
2026-03-22 23:31:50 +01:00
Jannis Braun 977983eed1 feat: add useMascotAnimation hook with WAAPI state machine 2026-03-22 23:26:53 +01:00
Jannis Braun e5495ac0d8 fix: resolve TS2322 ref type error and Tailwind class conflict in Mascot
- Change SvgProps.svgRef to React.Ref<SVGSVGElement> for React 18 compat
- className now replaces default w-32/h-32 instead of appending (avoids
  Tailwind specificity conflicts)
- Replace inline style={{ position: 'relative' }} with Tailwind 'relative'
2026-03-22 23:22:06 +01:00
Jannis Braun 386bd85c8d fix: attach svgRef to SVG elements in Mascot sub-components
The svgRef was created but never passed to the sub-component SVG
elements, meaning it would always be null when the animation hook
tries to use it. Now passed via SvgProps interface.
2026-03-22 23:18:37 +01:00
Jannis Braun 7d5b9c1eee feat: add Mascot component with state-driven SVG rendering
Implement the <Mascot> component that renders Nori in four emotional
states (idle, sleeping, excited, lonely) with distinct SVG geometry,
color palettes, and facial expressions. Includes useMascotAnimation
hook stub for Task 2 and full test coverage (15 tests).
2026-03-22 23:15:46 +01:00
Jannis Braun 623928812d fix: always enable web fallback keybind listeners alongside Electron IPC
When the Electron main process can't start uiohook (macOS Accessibility
denied, Linux missing input group), the IPC path silently produces no
events. Previously this left zero keybind listeners active. Now the web
capture-phase fallback is always set up as a safety net, ensuring
keybinds work in-app even when the OS-level hook fails.
2026-03-22 22:31:59 +01:00
Jannis Braun 39ee894d77 fix: fall back to web keybind listeners when Electron lacks keybind APIs
When running in Electron but the desktop app hasn't been rebuilt with
keybind support (no syncKeybinds/onKeybindAction in preload), the hook
now falls through to the web capture-phase listener instead of silently
doing nothing. This ensures keybinds work immediately on the web-served
UI regardless of the desktop app version.
2026-03-22 21:36:05 +01:00
Jannis Braun 5554b1abc7 fix: replace broken 2-char hash with djb2 for keybind code mapping
The previous hash (charCodeAt(0)*256 + charCodeAt(1)) only used the
first 2 characters of KeyboardEvent.code strings, causing all Key*
codes (KeyA, KeyB, ..., KeyZ) to collide to the same numeric value.
This made Ctrl+D and Ctrl+M appear as identical bindings (false
conflict) and broke keybind matching in the web fallback.

djb2 hashes the full string, producing unique values for all codes.
2026-03-22 21:01:02 +01:00
Jannis Braun 11d802f86c feat(keybinds): add KeybindsPanel settings UI with recorder and conflict detection 2026-03-22 20:42:48 +01:00
Jannis Braun 895f665da9 feat(keybinds): wire useKeybinds hook into AppLayout 2026-03-22 20:37:45 +01:00
Jannis Braun c6d345048a feat(keybinds): add useKeybinds hook with Electron IPC bridge and web fallback 2026-03-22 20:35:31 +01:00