diff --git a/frontend/src/components/room/RoomToolbar.tsx b/frontend/src/components/room/RoomToolbar.tsx index 257b969..6dc3342 100644 --- a/frontend/src/components/room/RoomToolbar.tsx +++ b/frontend/src/components/room/RoomToolbar.tsx @@ -17,6 +17,7 @@ import { Track, type ScreenShareCaptureOptions } from 'livekit-client' import { DisconnectButton, useLocalParticipant, useTrackToggle } from '@livekit/components-react' import { useToast } from '@/components/ui/ToastProvider' import { useIsCompactViewport } from '@/hooks/useIsCompactViewport' +import { useIsOrganizer } from '@/hooks/useIsOrganizer' import type { HandQueueEntry } from '@/hooks/useChat' import { StageViewMenu, type StageViewProps } from '@/components/room/StageViewOptions' import { HandQueueMenu } from '@/components/room/HandQueueMenu' @@ -68,6 +69,8 @@ interface RoomToolbarProps extends StageViewProps { onLowerHand: () => void /** Опустить ЧУЖУЮ руку по identity — только организатору (панель очереди, `HandQueueMenu`). */ onLowerHandById: (identity: string) => void + /** Тулбар в оверлее полноэкранного режима — см. докстринг `RoomTopbar.overlayVisible`, тот же механизм. */ + overlayVisible?: boolean } /** @@ -77,16 +80,20 @@ interface RoomToolbarProps extends StageViewProps { * панели чата, стилизованные по design/mockups/room.html. * * Кнопка «Вид» (режимы показа и скрытие остальных) рендерится ТОЛЬКО на - * широком экране — условным рендерингом, а не скрытием через CSS: тулбар на - * мобильном и так ужат до пяти «безусловных» кнопок (демонстрация/ - * полноэкранный режим/мини-плеер скрыты на узком экране через CSS, см. - * `styles/room.css`), а те же настройки там доступны секцией «Вид» в шторке - * настроек (`DeviceSettingsDialog`). «Рука» — сознательное исключение из этой + * широком экране — условным рендерингом, а не скрытием через CSS: демонстрация + * и мини-плеер скрыты на узком экране через CSS (см. `styles/room.css`), а те + * же настройки показа сцены доступны секцией «Вид» в шторке настроек + * (`DeviceSettingsDialog`). «Рука» — сознательное исключение из этой * экономии: поднять руку посреди разговора — действие со временем жизни в * секунды, прятать его в шторку настроек означало бы делать его практически * недоступным с телефона. «Очередь» показывается только организатору — * встречается редко, но по той же причине оставлена в тулбаре, а не в * шторке: организатору с телефона тоже нужно видеть очередь сразу. + * Полноэкранный режим на мобильном ОСТАЁТСЯ в тулбаре (не спрятан в шторку, + * как демонстрация/мини-плеер) — по решению оператора вход в него должен + * быть по аналогии с десктопом; если из-за этого кнопки не помещаются в один + * ряд, `mobileButtonCount`/`.tb-wrap-grid` ниже раскладывают их равномерной + * сеткой, а не как получится через `flex-wrap`. */ export function RoomToolbar({ chatVisible, @@ -108,11 +115,29 @@ export function RoomToolbar({ onLayoutModeChange, hideOthers, onHideOthersChange, + overlayVisible, }: RoomToolbarProps) { const toast = useToast() const isCompact = useIsCompactViewport() + const isOrganizer = useIsOrganizer() const { localParticipant } = useLocalParticipant() const handRaised = handQueue.some((entry) => entry.identity === localParticipant.identity) + + // Сколько кнопок реально видно на мобильном (демонстрация/мини-окно там + // скрыты через CSS всегда, «Вид» не рендерится вовсе — см. докстринг) — + // считаем в JS, а не через CSS-селекторы вида `:nth-child`: у «Очереди» + // (только у организатора) в DOM есть своя обёртка `.tb-menu-wrap`, из-за + // которой позиция остальных кнопок «плывёт», и подсчёт по структуре DOM + // был бы хрупким. От порога зависит `.tb-wrap-grid` в room.css — ниже он + // переключает перенос на 2 строки с «как поместится» (flex-wrap) на + // равномерную сетку 4 колонки (7 → 4+3, 8 → 4+4). + const mobileButtonCount = + 3 /* микрофон, камера, рука */ + + (isOrganizer ? 1 : 0) /* очередь */ + + 1 /* настройки */ + + (fullscreenSupported ? 1 : 0) + + (chatVisible ? 1 : 0) + + 1 /* выйти */ const mic = useTrackToggle({ source: Track.Source.Microphone }) const camera = useTrackToggle({ source: Track.Source.Camera }) const screenShare = useTrackToggle({ @@ -129,7 +154,9 @@ export function RoomToolbar({ }) return ( -