Remove the unused USE_VOICE_ACTIVITY permission bit (was bit 25) and shift
STREAM to bit 25, DISCONNECT_MEMBERS to bit 26. Add a database migration to
remap stored permission values. Gate camera and screen share buttons in
VoiceControlBar behind canSpeak/canStream, matching VoiceControls behavior.
Add isServerDeafened to mic icon slash conditions in VoiceControlBar and
ChannelSidebar. Restructure VoiceUser badge rendering to gate on effective
state (intent OR server enforcement) instead of only LiveKit participant
intent, fixing missing badges in the voice grid for server-muted users.
When a moderator lifted a server mute/deafen, the client was involuntarily
turning on the user's microphone because isMuted/isDeafened conflated user
intent with server enforcement. Now intent (isMuted/isDeafened) is never
mutated by server events. Effective state (intent || serverEnforcement) is
computed at broadcast and hardware time via centralized helpers.
- The client now dynamically resolves the user's federated identity via `getMyUserIdForOrigin` when evaluating incoming `voice_server_muted` and `voice_server_deafened` events. Previously, the client incorrectly compared the remote event's federated `userId` against the local `authStore` home `userId`, causing federated users to silently drop restriction events.
- Client-side mic/deafen toggles (`toggleMic`, `toggleDeafen`) now accurately evaluate the user's origin-specific ID against the restriction sets, preventing federated users from bypassing locks.
- UI state selectors (`VoiceControlBar`, `ChannelSidebar`) now compute `myOriginId` to correctly render the yellow server-lockdown indicators for cross-instance users.
- Replaces global `userId` tracking with `spaceId:userId` composite keys across both backend and frontend, fixing the issue where server-muting a user in one space bled into others.
- Modifies client-side `ready` event hydration to merge voice states per-origin instead of completely overwriting the store, preventing federated connections from wiping out home instance mutes.
- Excludes server voice restrictions from `zustand/persist` so stale client caches don't override the server's authority on reload.
- Fixes React component reactivity by using reactive store selections for `spaceId` instead of imperative `getState()` calls, ensuring UI lockdown indicators accurately reflect the initial websocket handshake.
When a moderator server-deafens a user, the implied mute is server-imposed
and should display amber (not red) everywhere. Updated VoiceControlBar,
ChannelSidebar, VoiceChannel, and VoiceUser to check isServerDeafened
alongside isServerMuted for amber color and cursor-not-allowed state.
Also includes smart mute/deafen toggle logic (Discord-style coupling),
server-side enforcement of mute/deafen bypass, and cleanup of server
voice state on user departure.
Add ban/unban functionality with BansPanel in space settings, voice
moderation context menu (mute/deafen/disconnect), and fix federated
space settings panels to use origin-aware API client. Show domain
indicators for federated members in MembersPanel.
Replace inline Save/Discard/Reset buttons with sticky glass-bubble pills
that float at the bottom of scrollable modal content. Make SpaceSettings
tab sidebar sticky with glass material. Convert RoleEditView "Back to
roles" into a sticky glass pill at the top. UserSettings Log Out + Save
always visible in a glass pill with separator. Also includes floating
position hook and popover/tooltip improvements from prior work.
Phase 5 of multi-instance federation. Adds a two-layer fix:
Layer 1 — Data ingestion normalization: Remote instance user avatars,
server icons, and attachment filenames are rewritten to absolute URLs
when entering the app (via WebSocket events or API responses), so all
downstream components render them correctly without changes.
Layer 2 — Outbound action routing: wsSend calls (voice join/leave/status,
typing) and file uploads in UI components now route through the correct
instance based on the active channel's origin.
Migrate all 10 voice/video components from Discord visual language to
Aether Drift design system. Zero discord-* classes or hardcoded hex
values remain in components/voice/.
Key changes:
- Status indicators use pastel accents: mint (connected/speaking),
amber (connecting/idle), rose (muted/deafened/error)
- VoiceControlBar converted to frosted glass-bubble material
- ConnectionInfoPopover and ScreenShareSettingsPopover use glass popovers
- Speaking ring glow changed from Discord green to mint (#86efac)
- Slider accents use lavender instead of blurple
- All bg/text/border tokens mapped to surface-*/txt-*/interactive-* system
Replace rigid SCREEN_QUALITY_MAP (6 hardcoded VideoPreset strings) with a
builder function that computes bitrate, degradation preference, and content
hint from three independent axes (height, fps, content mode). Camera is
decoupled onto a fixed 720p30 preset so screen share changes no longer
affect camera quality. New ScreenShareSettingsPopover replaces the old
VideoQualityPopover with pill-style selectors. Store migrated to v5 with
backwards-compatible migration from videoQuality string.
Merge connectDm() into connect() with isDm flag, eliminating ~145 lines
of duplicated LiveKit room setup. DM calls now inherit all event handlers
(SpeakingDetector cleanup, deafen broadcasts, metadata changes). Replace
monolithic DmCallView with shared VoiceGrid + VoiceControlBar components,
making DM calls group-DM-ready with full feature parity.
Camera and LIVE badges in the channel sidebar were only visible to users
who had joined the same LiveKit room. Widen the voice_status WS event
from {isMuted, isDeafened} to {isMuted, isDeafened, isCameraOn, isScreenSharing}
so all server members see camera/screenshare indicators without joining voice.
Screen sharing was publishing at h360 (640x360) and never ramping to target
resolution due to stale closure in setTimeout, wrong initial quality anchor,
and 5 competing code paths with inconsistent bitrates.
- Create utils/screenShare.ts as single source of truth for all screen share ops
- Publish at target resolution from the start (not h360 → ramp)
- Read store at call time in timers (eliminates stale closure bug)
- Use maintain-resolution for screen content, maintain-framerate for camera
- Fix OS-level "Stop sharing" not resetting store or restoring AEC
- Enable dynacast for SFU quality signaling
- Reconcile QUALITY_MAP to canonical bitrates across all 7 files
Chrome's AEC uses getDisplayMedia audio as a reference signal and
aggressively ducks the microphone even when headphones are used.
This adds a screenShareActive flag to AudioManager that forces
echoCancellation off during screen share, plus Chromium-specific
goog* constraints as belt-and-suspenders.
Pass audio: true to setScreenShareEnabled so the browser offers the
"Share audio" checkbox. Track ScreenShareAudio from remote participants
and play it through a dedicated audio element with the same volume
pipeline (including boost >100%).
Shift entire color palette from Discord's Ash theme to the darker Dark theme.
Redesign VoiceControlBar as floating auto-show pill, add gradient Join Voice
screen, move invite icon to server header, add voice channel user status
badges, update auth pages with subtle gradient backgrounds, and sweep all
hardcoded hex colors across 14 files to match the new darker palette.
- Implemented 'Overdrive' logic to force high bitrates on Chrome
- Fixed 'Auto' preset to default to stable 720p60
- Added persistent 'Triple-Kick' hammer to prevent bitrate throttling
- Fixed sidebar connection status sync
- Added comprehensive diagnostic logger