fix(media-gallery): dock as a flex sibling like MembersDrawer (was floating)
CI / Build & Quality Checks (push) Successful in 10m34s
CI / Trigger Desktop Build (push) Successful in 6s

The real reason the gallery didn't look or function like the Members drawer
or Saved Messages: it was a position:fixed overlay floating over the timeline,
mounted from RoomViewHeader. Now it docks into the room layout row exactly like
MembersDrawer.

- new mediaGalleryAtom (mirrors bookmarksPanelAtom) holds the open state
- RoomViewHeader toggles the atom instead of local useState and no longer
  renders the panel
- Room.tsx renders <MediaGallery> as a flex sibling of the timeline with a
  vertical Line separator on desktop and key={room.roomId} to reset per room
- MediaGallery.css: static width on desktop, position:fixed inset:0 full-screen
  only on mobile (identical strategy to MembersDrawer.css); root Box shrink="No"

The panel now shares the row with the timeline instead of overlapping it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-19 16:14:50 -04:00
parent b818d3fc5a
commit 4a4dede105
6 changed files with 52 additions and 41 deletions
+8 -12
View File
@@ -1,24 +1,20 @@
import { style } from '@vanilla-extract/css';
import { color, config, toRem } from 'folds';
// Right-side drawer that floats over the room view. 320px is wider than the
// 266px member/bookmark drawers because it hosts a media grid; on narrow
// viewports it expands to fill the screen, matching the app's other drawers.
// Right-side drawer DOCKED into the room layout row (a flex sibling of the
// timeline), exactly like MembersDrawer — not a floating overlay. 320px is a
// little wider than the 266px member/bookmark drawers because it hosts a media
// grid. On narrow viewports it becomes a full-screen fixed panel, matching the
// app's other drawers.
export const MediaGalleryDrawer = style({
position: 'fixed',
top: 0,
right: 0,
bottom: 0,
width: toRem(320),
zIndex: 500,
overflow: 'hidden',
borderLeftWidth: config.borderWidth.B300,
borderLeftStyle: 'solid',
borderLeftColor: color.Background.ContainerLine,
'@media': {
'(max-width: 750px)': {
position: 'fixed',
inset: 0,
width: '100%',
borderLeftWidth: 0,
zIndex: 500,
},
},
});
+1
View File
@@ -625,6 +625,7 @@ export function MediaGallery({ room, onClose }: MediaGalleryProps) {
<>
<Box
className={classNames(css.MediaGalleryDrawer, ContainerColor({ variant: 'Background' }))}
shrink="No"
direction="Column"
>
{/* Header */}
+13 -1
View File
@@ -2,9 +2,11 @@ import React, { useCallback } from 'react';
import { Box, Line } from 'folds';
import { useParams } from 'react-router-dom';
import { isKeyHotkey } from 'is-hotkey';
import { useAtomValue } from 'jotai';
import { useAtomValue, useSetAtom } from 'jotai';
import { RoomView } from './RoomView';
import { MembersDrawer } from './MembersDrawer';
import { MediaGallery } from './MediaGallery';
import { mediaGalleryAtom } from '../../state/mediaGallery';
import { ScreenSize, useScreenSizeContext } from '../../hooks/useScreenSize';
import { useSetting } from '../../state/hooks/settings';
import { settingsAtom } from '../../state/settings';
@@ -31,6 +33,8 @@ export function Room() {
const callEmbed = useCallEmbed();
const [isDrawer] = useSetting(settingsAtom, 'isPeopleDrawer');
const galleryOpen = useAtomValue(mediaGalleryAtom);
const setGalleryOpen = useSetAtom(mediaGalleryAtom);
const [hideActivity] = useSetting(settingsAtom, 'hideActivity');
const screenSize = useScreenSizeContext();
const powerLevels = usePowerLevels(room);
@@ -78,6 +82,14 @@ export function Room() {
<CallChatView />
</>
)}
{!callView && galleryOpen && (
<>
{screenSize === ScreenSize.Desktop && (
<Line variant="Background" direction="Vertical" size="300" />
)}
<MediaGallery key={room.roomId} room={room} onClose={() => setGalleryOpen(false)} />
</>
)}
{!callView && isDrawer && (
<>
{screenSize === ScreenSize.Desktop && (
+3 -4
View File
@@ -73,7 +73,7 @@ import { RoomSettingsPage } from '../../state/roomSettings';
import { useCallEmbed, useCallStart } from '../../hooks/useCallEmbed';
import { useLivekitSupport } from '../../hooks/useLivekitSupport';
import { webRTCSupported } from '../../utils/rtc';
import { MediaGallery } from './MediaGallery';
import { mediaGalleryAtom } from '../../state/mediaGallery';
import { usePendingKnocks } from '../../hooks/usePendingKnocks';
import { bookmarksPanelAtom } from '../../state/bookmarksPanel';
@@ -488,7 +488,7 @@ export function RoomViewHeader({ callView }: { callView?: boolean }) {
: undefined;
const [peopleDrawer, setPeopleDrawer] = useSetting(settingsAtom, 'isPeopleDrawer');
const [galleryOpen, setGalleryOpen] = useState(false);
const [galleryOpen, setGalleryOpen] = useAtom(mediaGalleryAtom);
const pendingKnocks = usePendingKnocks(room);
const handleSearchClick = () => {
@@ -820,7 +820,7 @@ export function RoomViewHeader({ callView }: { callView?: boolean }) {
room={room}
requestClose={() => setMenuAnchor(undefined)}
galleryOpen={galleryOpen}
onToggleGallery={() => setGalleryOpen((v) => !v)}
onToggleGallery={() => setGalleryOpen((v: boolean) => !v)}
/>
</FocusTrap>
}
@@ -828,7 +828,6 @@ export function RoomViewHeader({ callView }: { callView?: boolean }) {
</Box>
</Box>
</PageHeader>
{galleryOpen && <MediaGallery room={room} onClose={() => setGalleryOpen(false)} />}
</>
);
}
+3
View File
@@ -0,0 +1,3 @@
import { atom } from 'jotai';
export const mediaGalleryAtom = atom<boolean>(false);