From 2790e9e088286028048f6a80626c48c9358dda45 Mon Sep 17 00:00:00 2001 From: Jannis Braun <151788261+TheZwiss@users.noreply.github.com> Date: Tue, 17 Mar 2026 15:42:16 +0100 Subject: [PATCH] feat(mobile): add voice mini-bar with quick controls --- .../web/src/components/layout/MobileShell.tsx | 2 +- .../components/layout/MobileVoiceMiniBar.tsx | 104 ++++++++++++++++++ 2 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 packages/web/src/components/layout/MobileVoiceMiniBar.tsx diff --git a/packages/web/src/components/layout/MobileShell.tsx b/packages/web/src/components/layout/MobileShell.tsx index 3a8b0f28..ea7c945b 100644 --- a/packages/web/src/components/layout/MobileShell.tsx +++ b/packages/web/src/components/layout/MobileShell.tsx @@ -20,7 +20,7 @@ import { MobileDmsScreen } from './MobileDmsScreen'; import { MobileYouScreen } from './MobileYouScreen'; import { MobileChatScreen } from './MobileChatScreen'; import { MobileSettingsScreen } from './MobileSettingsScreen'; -const MobileVoiceMiniBar = () => null; +import { MobileVoiceMiniBar } from './MobileVoiceMiniBar'; const MobileVoiceFullScreen = () => ; const screenMap: Record) => React.ReactNode> = { diff --git a/packages/web/src/components/layout/MobileVoiceMiniBar.tsx b/packages/web/src/components/layout/MobileVoiceMiniBar.tsx new file mode 100644 index 00000000..776557d1 --- /dev/null +++ b/packages/web/src/components/layout/MobileVoiceMiniBar.tsx @@ -0,0 +1,104 @@ +import React from 'react'; +import { useUIStore } from '../../stores/uiStore'; +import { useVoiceStore } from '../../stores/voiceStore'; +import { useSpaceStore } from '../../stores/spaceStore'; + +export function MobileVoiceMiniBar() { + const pushMobileScreen = useUIStore((s) => s.pushMobileScreen); + const mobileStack = useUIStore((s) => s.mobileStack); + + const currentVoiceChannelId = useVoiceStore((s) => s.currentVoiceChannelId); + const isMuted = useVoiceStore((s) => s.isMuted); + const isDeafened = useVoiceStore((s) => s.isDeafened); + const toggleMute = useVoiceStore((s) => s.toggleMute); + const toggleDeafen = useVoiceStore((s) => s.toggleDeafen); + const voiceUsers = useVoiceStore((s) => s.voiceUsers); + const leaveChannel = useVoiceStore((s) => s.leaveChannel); + + const channels = useSpaceStore((s) => s.channels); + const dmChannels = useSpaceStore((s) => s.dmChannels); + + if (!currentVoiceChannelId) return null; + + // Don't show mini-bar if voice full-screen is on top of the stack + const topScreen = mobileStack.length > 0 ? mobileStack[mobileStack.length - 1].screen : null; + if (topScreen === 'voice-full') return null; + + // Resolve channel name + const isDmCall = currentVoiceChannelId.startsWith('dm-'); + let channelName = 'Voice Call'; + if (isDmCall) { + const dmId = currentVoiceChannelId.replace('dm-', ''); + const dm = dmChannels.find(d => d.id === dmId); + if (dm) channelName = 'DM Call'; + } else { + const ch = channels.find(c => c.id === currentVoiceChannelId); + if (ch) channelName = ch.name; + } + + const participantCount = voiceUsers.get(currentVoiceChannelId)?.length ?? 0; + + return ( +
+ {/* Tap to expand */} + + + {/* Quick controls */} +
+ + + + + +
+
+ ); +}