diff --git a/packages/web/src/components/layout/MobileVoiceFullScreen.tsx b/packages/web/src/components/layout/MobileVoiceFullScreen.tsx
index e2164f02..a5f4f950 100644
--- a/packages/web/src/components/layout/MobileVoiceFullScreen.tsx
+++ b/packages/web/src/components/layout/MobileVoiceFullScreen.tsx
@@ -4,6 +4,8 @@ import { useVoiceStore } from '../../stores/voiceStore';
import { useSpaceStore } from '../../stores/spaceStore';
import { useAuthStore } from '../../stores/authStore';
import { Avatar } from '../ui/Avatar';
+import { useContextMenuStore, type ContextMenuItem } from '../../stores/contextMenuStore';
+import { buildVoiceModMenuItems, VolumeSliderItem } from '../voice/voiceMenuItems';
export function MobileVoiceFullScreen() {
const popMobileScreen = useUIStore((s) => s.popMobileScreen);
@@ -30,6 +32,8 @@ export function MobileVoiceFullScreen() {
const authUser = useAuthStore((s) => s.user);
+ const openContextMenu = useContextMenuStore((s) => s.open);
+
if (!currentVoiceChannelId) {
popMobileScreen();
return null;
@@ -87,6 +91,41 @@ export function MobileVoiceFullScreen() {
popMobileScreen();
};
+ const handleParticipantContextMenu = (e: React.MouseEvent, userId: string) => {
+ if (userId === authUser?.id || !currentVoiceChannelId || isDmCall) return;
+ e.preventDefault();
+ e.stopPropagation();
+
+ const modItems = buildVoiceModMenuItems(userId, currentVoiceChannelId);
+ const items: ContextMenuItem[] = [...modItems];
+
+ if (modItems.length > 0) {
+ items.push({ key: 'mod-end-sep', type: 'separator' });
+ }
+
+ // Local mute checkbox
+ const isUserMuted = useVoiceStore.getState().participantMutes.get(userId) ?? false;
+ items.push({
+ key: 'mute-user',
+ type: 'checkbox',
+ label: 'Mute User',
+ checked: isUserMuted,
+ onChange: (checked) => useVoiceStore.getState().setParticipantMute(userId, checked),
+ });
+
+ items.push({ key: 'vol-sep', type: 'separator' });
+
+ // Volume slider
+ items.push({
+ key: 'volume',
+ type: 'custom',
+ render: () =>