From 4c671fbf3a92d48cf1c4aa7390da61a80f8c7a53 Mon Sep 17 00:00:00 2001 From: Jared Vititoe Date: Fri, 18 Sep 2026 17:45:43 -0400 Subject: [PATCH] fix(mobile): member list no longer opens on top of every room on phones (#175) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Room.tsx showed the MembersDrawer on phone widths whenever the persisted desktop preference isPeopleDrawer was true — which is the default — so a new mobile session opened every room behind a full-screen member list. Mobile now uses a transient mobileMembersPanelAtom (header ⋮ → Members toggles it, the drawer's X closes it, leaving the room resets it); the desktop drawer keeps using the setting. Verified with Playwright at 400px. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA --- src/app/features/room/MembersDrawer.tsx | 9 ++++++++- src/app/features/room/Room.tsx | 14 +++++++++++--- src/app/features/room/RoomViewHeader.tsx | 9 +++++---- src/app/state/mobileMembersPanel.ts | 8 ++++++++ 4 files changed, 32 insertions(+), 8 deletions(-) create mode 100644 src/app/state/mobileMembersPanel.ts diff --git a/src/app/features/room/MembersDrawer.tsx b/src/app/features/room/MembersDrawer.tsx index eb62e2b23..af054bbb4 100644 --- a/src/app/features/room/MembersDrawer.tsx +++ b/src/app/features/room/MembersDrawer.tsx @@ -31,6 +31,8 @@ import { MatrixClient, Room, RoomMember } from 'matrix-js-sdk'; import { useVirtualizer } from '@tanstack/react-virtual'; import classNames from 'classnames'; import { useSetAtom } from 'jotai'; +import { mobileMembersPanelAtom } from '../../state/mobileMembersPanel'; +import { ScreenSize, useScreenSizeContext } from '../../hooks/useScreenSize'; import { MobileTouchTarget } from '../../styles/mobile.css'; import { Membership } from '../../../types/matrix/room'; @@ -79,6 +81,8 @@ type MemberDrawerHeaderProps = { }; function MemberDrawerHeader({ room }: MemberDrawerHeaderProps) { const setPeopleDrawer = useSetSetting(settingsAtom, 'isPeopleDrawer'); + const setMobileMembers = useSetAtom(mobileMembersPanelAtom); + const screenSize = useScreenSizeContext(); return (
@@ -104,7 +108,10 @@ function MemberDrawerHeader({ room }: MemberDrawerHeaderProps) { ref={triggerRef} variant="Background" aria-label="Close member list" - onClick={() => setPeopleDrawer(false)} + onClick={() => { + if (screenSize === ScreenSize.Mobile) setMobileMembers(false); + else setPeopleDrawer(false); + }} > diff --git a/src/app/features/room/Room.tsx b/src/app/features/room/Room.tsx index 55eb0126e..e91a84bad 100644 --- a/src/app/features/room/Room.tsx +++ b/src/app/features/room/Room.tsx @@ -2,13 +2,14 @@ import React, { useCallback, useEffect, useRef } from 'react'; import { Box, Line } from 'folds'; import { useParams } from 'react-router-dom'; import { isKeyHotkey } from 'is-hotkey'; -import { useAtomValue, useSetAtom } from 'jotai'; +import { useAtom, useAtomValue, useSetAtom } from 'jotai'; import { RoomView } from './RoomView'; import { MembersDrawer } from './MembersDrawer'; import { MediaGallery } from './MediaGallery'; import { mediaGalleryAtom } from '../../state/mediaGallery'; import { WidgetsPanel } from './widgets/WidgetsPanel'; import { widgetsPanelAtom } from '../../state/widgetsPanel'; +import { mobileMembersPanelAtom } from '../../state/mobileMembersPanel'; import { ScreenSize, useScreenSizeContext } from '../../hooks/useScreenSize'; import { useSetting } from '../../state/hooks/settings'; import { settingsAtom } from '../../state/settings'; @@ -46,6 +47,7 @@ export function Room() { const galleryOpen = useAtomValue(mediaGalleryAtom); const setGalleryOpen = useSetAtom(mediaGalleryAtom); const widgetsOpen = useAtomValue(widgetsPanelAtom); + const [mobileMembersOpen, setMobileMembersOpen] = useAtom(mobileMembersPanelAtom); const setWidgetsOpen = useSetAtom(widgetsPanelAtom); const threadsListOpen = useAtomValue(threadsListAtom); const setThreadsListOpen = useSetAtom(threadsListAtom); @@ -146,10 +148,16 @@ export function Room() { threadsListOpen && !activeThreadId && (isDesktop || (!galleryOpen && !widgetsOpen)); + // Desktop: the persisted drawer preference. Mobile: a transient panel the + // user opened from the header menu (never the desktop default). const showMembers = !callView && - isDrawer && - (isDesktop || (!activeThreadId && !galleryOpen && !widgetsOpen && !threadsListOpen)); + (isDesktop + ? isDrawer + : mobileMembersOpen && !activeThreadId && !galleryOpen && !widgetsOpen && !threadsListOpen); + + // Leaving a room on a phone closes its member panel. + useEffect(() => () => setMobileMembersOpen(false), [room.roomId, setMobileMembersOpen]); return ( diff --git a/src/app/features/room/RoomViewHeader.tsx b/src/app/features/room/RoomViewHeader.tsx index 43fa4f9bd..d5c6a5ac5 100644 --- a/src/app/features/room/RoomViewHeader.tsx +++ b/src/app/features/room/RoomViewHeader.tsx @@ -75,6 +75,7 @@ import { useLivekitSupport } from '../../hooks/useLivekitSupport'; import { webRTCSupported } from '../../utils/rtc'; import { mediaGalleryAtom } from '../../state/mediaGallery'; import { widgetsPanelAtom } from '../../state/widgetsPanel'; +import { mobileMembersPanelAtom } from '../../state/mobileMembersPanel'; import { threadsListAtom } from '../../state/threadsList'; import { usePendingKnocks } from '../../hooks/usePendingKnocks'; import { bookmarksPanelAtom } from '../../state/bookmarksPanel'; @@ -105,7 +106,7 @@ const RoomMenu = forwardRef( const [invitePrompt, setInvitePrompt] = useState(false); const [reportRoomOpen, setReportRoomOpen] = useState(false); const [bookmarksOpen, setBookmarksOpen] = useAtom(bookmarksPanelAtom); - const [peopleDrawer, setPeopleDrawer] = useSetting(settingsAtom, 'isPeopleDrawer'); + const [mobileMembers, setMobileMembers] = useAtom(mobileMembersPanelAtom); const handleMarkAsRead = () => { markAsRead(mx, room.roomId, hideActivity); @@ -196,13 +197,13 @@ const RoomMenu = forwardRef( {screenSize === ScreenSize.Mobile && ( { - setPeopleDrawer(!peopleDrawer); + setMobileMembers(!mobileMembers); requestClose(); }} size="300" - after={} + after={} radii="300" - aria-pressed={peopleDrawer} + aria-pressed={mobileMembers} > Members diff --git a/src/app/state/mobileMembersPanel.ts b/src/app/state/mobileMembersPanel.ts new file mode 100644 index 000000000..9a902c0b7 --- /dev/null +++ b/src/app/state/mobileMembersPanel.ts @@ -0,0 +1,8 @@ +import { atom } from 'jotai'; + +// Whether the member list is open on a phone-width layout, where it is a +// full-screen panel rather than a side drawer. Deliberately NOT the persisted +// `isPeopleDrawer` setting: that one remembers the desktop drawer (default +// on), and reusing it on mobile made every room open on top of the member +// list until the user found the X (Gitea #175). +export const mobileMembersPanelAtom = atom(false);