From ed68922d2cde6572961fe71c6896ffff33053d42 Mon Sep 17 00:00:00 2001 From: niki <295591807+nikiwastaken@users.noreply.github.com> Date: Wed, 24 Jun 2026 06:30:19 +0000 Subject: [PATCH 1/6] Redesign the user menu tab --- src/app/components/user-profile/UserHero.tsx | 3 +++ src/app/pages/client/sidebar/UserMenuTab.tsx | 1 + 2 files changed, 4 insertions(+) diff --git a/src/app/components/user-profile/UserHero.tsx b/src/app/components/user-profile/UserHero.tsx index da6f57fab..29c8de992 100644 --- a/src/app/components/user-profile/UserHero.tsx +++ b/src/app/components/user-profile/UserHero.tsx @@ -238,7 +238,10 @@ export function UserHero({ WebkitBoxOrient: 'vertical', overflow: 'hidden', fontStyle: allowEditing && !status ? 'italic' : 'normal', +<<<<<<< HEAD opacity: allowEditing && !status ? config.opacity.Placeholder : 1, +======= +>>>>>>> f873d334 (Redesign the user menu tab) }} > {status || (allowEditing && "What's on your mind?")} diff --git a/src/app/pages/client/sidebar/UserMenuTab.tsx b/src/app/pages/client/sidebar/UserMenuTab.tsx index efbb30283..39a3ab102 100644 --- a/src/app/pages/client/sidebar/UserMenuTab.tsx +++ b/src/app/pages/client/sidebar/UserMenuTab.tsx @@ -2,6 +2,7 @@ import type { MouseEventHandler } from 'react'; import { useCallback, useEffect, useState } from 'react'; import type { RectCords } from 'folds'; import { + Badge, Box, Button, Chip, From 2ff14d8f38879627f7cf78bad7e21b79fae91492 Mon Sep 17 00:00:00 2001 From: Shea Date: Fri, 26 Jun 2026 10:33:10 +0300 Subject: [PATCH 2/6] remove automatic status mode Signed-off-by: Shea --- src/app/pages/client/sidebar/UserMenuTab.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/pages/client/sidebar/UserMenuTab.tsx b/src/app/pages/client/sidebar/UserMenuTab.tsx index 39a3ab102..efbb30283 100644 --- a/src/app/pages/client/sidebar/UserMenuTab.tsx +++ b/src/app/pages/client/sidebar/UserMenuTab.tsx @@ -2,7 +2,6 @@ import type { MouseEventHandler } from 'react'; import { useCallback, useEffect, useState } from 'react'; import type { RectCords } from 'folds'; import { - Badge, Box, Button, Chip, From 34869f1c153870dcd6b62392da0df17e29423277 Mon Sep 17 00:00:00 2001 From: Shea Date: Sat, 27 Jun 2026 18:18:22 +0300 Subject: [PATCH 3/6] change modal styling and bar Signed-off-by: Shea --- .../message/modals/GlobalModalManager.tsx | 6 ++- src/app/features/search/Search.tsx | 54 +++++++++++-------- src/app/pages/client/SidebarNav.tsx | 1 - src/app/pages/client/sidebar/MessageTab.tsx | 31 +++++++++++ .../pages/client/sidebar/UserQuickTools.tsx | 42 +++++++++------ src/app/state/settings.ts | 2 +- 6 files changed, 97 insertions(+), 39 deletions(-) create mode 100644 src/app/pages/client/sidebar/MessageTab.tsx diff --git a/src/app/components/message/modals/GlobalModalManager.tsx b/src/app/components/message/modals/GlobalModalManager.tsx index be48d49be..a78506cad 100644 --- a/src/app/components/message/modals/GlobalModalManager.tsx +++ b/src/app/components/message/modals/GlobalModalManager.tsx @@ -34,7 +34,11 @@ export function GlobalModalManager() { focusTrapOptions={{ initialFocus: false, onDeactivate: close, - clickOutsideDeactivates: true, + allowOutsideClick: (e) => { + e.preventDefault(); + close(); + return false; + }, escapeDeactivates: stopPropagation, }} > diff --git a/src/app/features/search/Search.tsx b/src/app/features/search/Search.tsx index c6acf0d2a..51b05f186 100644 --- a/src/app/features/search/Search.tsx +++ b/src/app/features/search/Search.tsx @@ -3,6 +3,7 @@ import { Avatar, Box, config, + Header, IconButton, Input, Line, @@ -50,6 +51,7 @@ import { KeySymbol } from '$utils/key-symbol'; import { isMacOS } from '$utils/user-agent'; import { useSelectedSpace } from '$hooks/router/useSelectedSpace'; import { getMxIdServer } from '$utils/mxIdHelper'; +import { useScreenSizeContext, ScreenSize } from '$hooks/useScreenSize'; enum SearchRoomType { Rooms = '#', @@ -133,6 +135,7 @@ export type RoomSearchPickRoomConfig = { export type RoomSearchModalProps = { requestClose: () => void; pickRoom?: RoomSearchPickRoomConfig; + headerText?: string; }; export function RoomSearchModal({ requestClose, pickRoom }: RoomSearchModalProps) { @@ -274,6 +277,9 @@ export function RoomSearchModal({ requestClose, pickRoom }: RoomSearchModalProps } }, [listFocus.index]); + const screenSize = useScreenSizeContext(); + const isMobile = screenSize === ScreenSize.Mobile; + return ( @@ -285,34 +291,40 @@ export function RoomSearchModal({ requestClose, pickRoom }: RoomSearchModalProps clickOutsideDeactivates: true, onDeactivate: requestClose, escapeDeactivates: (evt: KeyboardEvent) => { - evt.stopPropagation(); + if (!isMobile) evt.stopPropagation(); return true; }, }} > - - {pickRoom && ( - - {pickRoom.title} - + +
+ + {pickRoom ? pickRoom.title : 'Search Message'} + + {composerIcon(X)} - )} +
{pickRoom?.errorMessage ? ( diff --git a/src/app/pages/client/SidebarNav.tsx b/src/app/pages/client/SidebarNav.tsx index d92ff4690..b82db3930 100644 --- a/src/app/pages/client/SidebarNav.tsx +++ b/src/app/pages/client/SidebarNav.tsx @@ -144,7 +144,6 @@ export function SidebarNav() {
- {/*PROBS ADD SETTINGSTAB HERE WHEN ADDING THE STATUSES*/}
diff --git a/src/app/pages/client/sidebar/MessageTab.tsx b/src/app/pages/client/sidebar/MessageTab.tsx new file mode 100644 index 000000000..7484187ee --- /dev/null +++ b/src/app/pages/client/sidebar/MessageTab.tsx @@ -0,0 +1,31 @@ +import { SidebarAvatar, SidebarItem, SidebarItemTooltip } from '$components/sidebar'; +import { getPhosphorIconSize } from '$components/icons/phosphor'; +import { matchPath, useNavigate } from 'react-router-dom'; +import { HOME_PATH, SETTINGS_PATH } from '$pages/paths'; +import { ChatTextIcon } from '@phosphor-icons/react'; +import { useAtom } from 'jotai'; +import { searchModalAtom } from '$state/searchModal'; +import { useInboxSelected } from '$hooks/router/useInbox'; + +export function MessageTab({ isBottom }: { isBottom?: boolean }) { + const navigate = useNavigate(); + const [searchSelected] = useAtom(searchModalAtom); + const inboxSelected = useInboxSelected(); + const opened = !(matchPath(SETTINGS_PATH, location.pathname) || searchSelected || inboxSelected); + const openSettings = () => navigate(HOME_PATH); + + return ( + + + {(triggerRef) => ( + + + + )} + + + ); +} diff --git a/src/app/pages/client/sidebar/UserQuickTools.tsx b/src/app/pages/client/sidebar/UserQuickTools.tsx index a6f96dcfc..bebbc3cab 100644 --- a/src/app/pages/client/sidebar/UserQuickTools.tsx +++ b/src/app/pages/client/sidebar/UserQuickTools.tsx @@ -7,6 +7,7 @@ import { isResizingSidebarAtom } from '$state/isResizingSidebar'; import * as css from './UserQuickTools.css'; import { ScreenSize, useScreenSizeContext } from '$hooks/useScreenSize'; import { UserMenuTab } from './UserMenuTab'; +import { MessageTab } from './MessageTab'; export function UserQuickTools({ width, @@ -28,7 +29,7 @@ export function UserQuickTools({
- - - {!isCollapsed && ( - <> - - - - - )} - + {compact ? ( + <> + + + + + + ) : ( + <> + + + {!isCollapsed && ( + <> + + + + + )} + + + )}
)} diff --git a/src/app/state/settings.ts b/src/app/state/settings.ts index 22445a35b..d1294b49d 100644 --- a/src/app/state/settings.ts +++ b/src/app/state/settings.ts @@ -344,7 +344,7 @@ export const defaultSettings: Settings = { saveStickerEmojiBandwidth: false, subspaceHierarchyLimit: 3, alwaysShowCallButton: false, - joinCallOnSingleClick: true, + joinCallOnSingleClick: false, faviconForMentionsOnly: false, highlightMentions: true, pkCompat: false, From e4be6ce343b4fb594f3cde43abfb96cbee383f9e Mon Sep 17 00:00:00 2001 From: Shea Date: Sun, 28 Jun 2026 16:59:05 +0300 Subject: [PATCH 4/6] style bottom bar and add navigation route Signed-off-by: Shea --- src/app/components/UserQuickToolsProvider.tsx | 9 + .../message/modals/MessageForward.tsx | 4 +- src/app/components/sidebar/Sidebar.css.ts | 2 +- src/app/components/user-profile/styles.css.ts | 1 - src/app/features/room/RoomTimeline.css.ts | 2 + src/app/features/search/Search.tsx | 486 +++++++++--------- src/app/hooks/router/useNavigateSelected.ts | 12 + src/app/pages/Router.tsx | 7 + src/app/pages/client/SidebarNav.tsx | 47 +- src/app/pages/client/navigate/Navigate.tsx | 91 ++++ src/app/pages/client/navigate/index.ts | 1 + src/app/pages/client/sidebar/InboxTab.tsx | 40 +- src/app/pages/client/sidebar/MessageTab.tsx | 39 +- src/app/pages/client/sidebar/NavigateTab.tsx | 49 ++ src/app/pages/client/sidebar/SearchTab.tsx | 26 - src/app/pages/client/sidebar/SettingsTab.tsx | 4 +- src/app/pages/client/sidebar/UserMenuTab.tsx | 47 +- .../client/sidebar/UserQuickTools.css.ts | 6 +- .../pages/client/sidebar/UserQuickTools.tsx | 43 +- src/app/pages/pathUtils.ts | 2 + src/app/pages/paths.ts | 1 + 21 files changed, 562 insertions(+), 357 deletions(-) create mode 100644 src/app/components/UserQuickToolsProvider.tsx create mode 100644 src/app/hooks/router/useNavigateSelected.ts create mode 100644 src/app/pages/client/navigate/Navigate.tsx create mode 100644 src/app/pages/client/navigate/index.ts create mode 100644 src/app/pages/client/sidebar/NavigateTab.tsx delete mode 100644 src/app/pages/client/sidebar/SearchTab.tsx diff --git a/src/app/components/UserQuickToolsProvider.tsx b/src/app/components/UserQuickToolsProvider.tsx new file mode 100644 index 000000000..23cf8c71d --- /dev/null +++ b/src/app/components/UserQuickToolsProvider.tsx @@ -0,0 +1,9 @@ +import { ScreenSize, useScreenSizeContext } from '$hooks/useScreenSize'; +import { UserQuickTools } from '$pages/client/sidebar/UserQuickTools'; + +export function UserQuickToolsProvider() { + const screenSize = useScreenSizeContext(); + const compact = screenSize === ScreenSize.Mobile; + if (!compact) return null; + return ; +} diff --git a/src/app/components/message/modals/MessageForward.tsx b/src/app/components/message/modals/MessageForward.tsx index 946f764a0..1bdcdebdd 100644 --- a/src/app/components/message/modals/MessageForward.tsx +++ b/src/app/components/message/modals/MessageForward.tsx @@ -17,7 +17,7 @@ import * as Sentry from '@sentry/react'; import { isRoomPrivate } from '$utils/roomVisibility'; import { canForwardEvent } from '$utils/room'; import * as prefix from '$unstable/prefixes'; -import { RoomSearchModal } from '$features/search'; +import { SearchWrapper } from '$features/search'; const debugLog = createDebugLogger('MessageForward'); // Message forwarding component @@ -270,7 +270,7 @@ export function MessageForwardInternal({ if (!forwardable) return null; - return ; + return ; } type MessageForwardItemProps = { diff --git a/src/app/components/sidebar/Sidebar.css.ts b/src/app/components/sidebar/Sidebar.css.ts index f97c0f9fc..db345edaa 100644 --- a/src/app/components/sidebar/Sidebar.css.ts +++ b/src/app/components/sidebar/Sidebar.css.ts @@ -132,7 +132,7 @@ export const SidebarItemBottom = recipe({ selectors: { '&:hover': { - transform: `translateY(${toRem(PUSH_Y)})`, + transform: `translateY(${toRem(-PUSH_Y)})`, }, '&::before': { content: '', diff --git a/src/app/components/user-profile/styles.css.ts b/src/app/components/user-profile/styles.css.ts index e7283d43b..133f0b7f8 100644 --- a/src/app/components/user-profile/styles.css.ts +++ b/src/app/components/user-profile/styles.css.ts @@ -44,7 +44,6 @@ export const UserAvatarContainer = style({ position: 'relative', top: 0, transform: 'translateY(-50%)', - backgroundColor: color.Surface.Container, }); export const UserHeroStatusContainer = style({ position: 'relative', diff --git a/src/app/features/room/RoomTimeline.css.ts b/src/app/features/room/RoomTimeline.css.ts index 9903033dc..4236685e9 100644 --- a/src/app/features/room/RoomTimeline.css.ts +++ b/src/app/features/room/RoomTimeline.css.ts @@ -12,6 +12,8 @@ export const TimelineFloat = recipe({ transform: 'translateX(-50%)', zIndex: 10, minWidth: 'max-content', + overflow: 'hidden', + borderRadius: config.radii.Pill, }, ], variants: { diff --git a/src/app/features/search/Search.tsx b/src/app/features/search/Search.tsx index 51b05f186..ccc083047 100644 --- a/src/app/features/search/Search.tsx +++ b/src/app/features/search/Search.tsx @@ -3,7 +3,6 @@ import { Avatar, Box, config, - Header, IconButton, Input, Line, @@ -51,7 +50,6 @@ import { KeySymbol } from '$utils/key-symbol'; import { isMacOS } from '$utils/user-agent'; import { useSelectedSpace } from '$hooks/router/useSelectedSpace'; import { getMxIdServer } from '$utils/mxIdHelper'; -import { useScreenSizeContext, ScreenSize } from '$hooks/useScreenSize'; enum SearchRoomType { Rooms = '#', @@ -133,16 +131,14 @@ export type RoomSearchPickRoomConfig = { }; export type RoomSearchModalProps = { - requestClose: () => void; + requestClose?: () => void; pickRoom?: RoomSearchPickRoomConfig; - headerText?: string; }; export function RoomSearchModal({ requestClose, pickRoom }: RoomSearchModalProps) { const mx = useMatrixClient(); const useAuthentication = useMediaAuthentication(); const scrollRef = useRef(null); - const inputRef = useRef(null); const { navigateRoom, navigateSpace } = useRoomNavigate(); const roomToUnread = useAtomValue(roomToUnreadAtom); @@ -217,7 +213,7 @@ export function RoomSearchModal({ requestClose, pickRoom }: RoomSearchModalProps } if (isSpace) navigateSpace(roomId); else navigateRoom(roomId); - requestClose(); + requestClose?.(); }; const handleInputChange: ChangeEventHandler = (evt) => { @@ -277,252 +273,227 @@ export function RoomSearchModal({ requestClose, pickRoom }: RoomSearchModalProps } }, [listFocus.index]); - const screenSize = useScreenSizeContext(); - const isMobile = screenSize === ScreenSize.Mobile; - return ( - - - inputRef.current, - returnFocusOnDeactivate: true, - allowOutsideClick: true, - clickOutsideDeactivates: true, - onDeactivate: requestClose, - escapeDeactivates: (evt: KeyboardEvent) => { - if (!isMobile) evt.stopPropagation(); - return true; - }, + + {pickRoom && ( + - {pickRoom.title}
+ + {composerIcon(X)} + +
+ )} + {pickRoom?.errorMessage ? ( + + + {pickRoom.errorMessage} + + + ) : null} + + + + + {roomsToRender.length === 0 && ( + -
+ {pickRoom + ? result + ? 'No Match Found' + : pickRoom.eligibleRoomIds.length === 0 + ? 'No rooms to forward to' + : 'No rooms match this filter' + : result + ? 'No Match Found' + : 'No Rooms'} + + + {pickRoom + ? result + ? `No match found for "${result.query}".` + : pickRoom.eligibleRoomIds.length === 0 + ? 'You cannot send messages in any joined room yet.' + : 'Try another search, or use # for group rooms and @ for direct messages.' + : result + ? `No match found for "${result.query}".` + : 'You do not have any Rooms to display yet.'} + + + )} + {roomsToRender.length > 0 && ( + +
- - {pickRoom ? pickRoom.title : 'Search Message'} - - - {composerIcon(X)} - - -
- {pickRoom?.errorMessage ? ( - - - {pickRoom.errorMessage} - - - ) : null} - - - - - {roomsToRender.length === 0 && ( - - - {pickRoom - ? result - ? 'No Match Found' - : pickRoom.eligibleRoomIds.length === 0 - ? 'No rooms to forward to' - : 'No rooms match this filter' - : result - ? 'No Match Found' - : 'No Rooms'} - - - {pickRoom - ? result - ? `No match found for "${result.query}".` - : pickRoom.eligibleRoomIds.length === 0 - ? 'You cannot send messages in any joined room yet.' - : 'Try another search, or use # for group rooms and @ for direct messages.' - : result - ? `No match found for "${result.query}".` - : 'You do not have any Rooms to display yet.'} - - - )} - {roomsToRender.length > 0 && ( - -
- {roomsToRender.map((roomId, index) => { - const room = getRoom(roomId); - if (!room) return null; - - const dm = mDirects.has(roomId); - const dmUserId = dm && getDmUserId(roomId, getRoom, mx.getSafeUserId()); - const dmUsername = dmUserId && getMxIdLocalPart(dmUserId); - const dmUserServer = dmUserId && getMxIdServer(dmUserId); - - const allParents = getAllParents(roomToParents, roomId); - const orphanParents = - allParents && orphanSpaces.filter((o) => allParents.has(o)); - const perfectOrphanParent = - orphanParents && guessPerfectParent(mx, roomId, orphanParents); - - const exactParents = roomToParents.get(roomId); - const perfectParent = - exactParents && guessPerfectParent(mx, roomId, Array.from(exactParents)); - - const unread = roomToUnread.get(roomId); - - return ( - - {dmUserServer && ( - - {dmUserServer} - - )} - {!dm && perfectOrphanParent && ( - - {getRoom(perfectOrphanParent)?.name ?? perfectOrphanParent} - - )} - {unread && ( - - 0} - count={unread.highlight > 0 ? unread.highlight : unread.total} - /> - - )} - - } - before={ - - {dm || room.isSpaceRoom() ? ( - ( - - {nameInitials(room.name)} - - )} - /> - ) : ( - - )} - - } - > - - - {queryHighlighRegex - ? highlightText(queryHighlighRegex, [room.name]) - : room.name} - - {dmUsername && ( - - @ - {queryHighlighRegex - ? highlightText(queryHighlighRegex, [dmUsername]) - : dmUsername} - - )} - {!dm && perfectParent && perfectParent !== perfectOrphanParent && ( - - — {getRoom(perfectParent)?.name ?? perfectParent} + {roomsToRender.map((roomId, index) => { + const room = getRoom(roomId); + if (!room) return null; + + const dm = mDirects.has(roomId); + const dmUserId = dm && getDmUserId(roomId, getRoom, mx.getSafeUserId()); + const dmUsername = dmUserId && getMxIdLocalPart(dmUserId); + const dmUserServer = dmUserId && getMxIdServer(dmUserId); + + const allParents = getAllParents(roomToParents, roomId); + const orphanParents = allParents && orphanSpaces.filter((o) => allParents.has(o)); + const perfectOrphanParent = + orphanParents && guessPerfectParent(mx, roomId, orphanParents); + + const exactParents = roomToParents.get(roomId); + const perfectParent = + exactParents && guessPerfectParent(mx, roomId, Array.from(exactParents)); + + const unread = roomToUnread.get(roomId); + + return ( + + {dmUserServer && ( + + {dmUserServer} + + )} + {!dm && perfectOrphanParent && ( + + {getRoom(perfectOrphanParent)?.name ?? perfectOrphanParent} + + )} + {unread && ( + + 0} + count={unread.highlight > 0 ? unread.highlight : unread.total} + /> + + )} + + } + before={ + + {dm || room.isSpaceRoom() ? ( + ( + + {nameInitials(room.name)} )} - - - ); - })} -
-
- )} -
- - - - {pickRoom ? ( - <> - Type # for rooms and @ for direct messages. Choose a room to - forward this message. - - ) : ( - <> - Type # for rooms, @ for DMs and * for spaces. Hotkey:{' '} - {isMacOS() ? KeySymbol.Command : 'Ctrl'} + k - {' / '} - {isMacOS() ? KeySymbol.Command : 'Ctrl'} + f - - )} - - -
- -
-
+ /> + ) : ( + + )} + + } + > + + + {queryHighlighRegex + ? highlightText(queryHighlighRegex, [room.name]) + : room.name} + + {dmUsername && ( + + @ + {queryHighlighRegex + ? highlightText(queryHighlighRegex, [dmUsername]) + : dmUsername} + + )} + {!dm && perfectParent && perfectParent !== perfectOrphanParent && ( + + — {getRoom(perfectParent)?.name ?? perfectParent} + + )} + + + ); + })} + + + )} + + + + + {pickRoom ? ( + <> + Type # for rooms and @ for direct messages. Choose a room to forward + this message. + + ) : ( + <> + Type # for rooms, @ for DMs and * for spaces. Hotkey:{' '} + {isMacOS() ? KeySymbol.Command : 'Ctrl'} + k + {' / '} + {isMacOS() ? KeySymbol.Command : 'Ctrl'} + f + + )} + + + ); } @@ -551,9 +522,34 @@ export function SearchModalRenderer() { ) ); - return opened && setOpen(false)} />; + return opened && setOpen(false)} />; +} + +export function SearchWrapper({ requestClose, pickRoom }: RoomSearchModalProps) { + return ( + + + { + evt.stopPropagation(); + return true; + }, + }} + > + + + + + + + ); } export function Search(props: { requestClose: () => void }) { - return ; + return ; } diff --git a/src/app/hooks/router/useNavigateSelected.ts b/src/app/hooks/router/useNavigateSelected.ts new file mode 100644 index 000000000..81e2168b9 --- /dev/null +++ b/src/app/hooks/router/useNavigateSelected.ts @@ -0,0 +1,12 @@ +import { useMatch } from 'react-router-dom'; +import { getNavigatePath } from '$pages/pathUtils'; + +export const useNavigateSelected = (): boolean => { + const match = useMatch({ + path: getNavigatePath(), + caseSensitive: true, + end: false, + }); + + return !!match; +}; diff --git a/src/app/pages/Router.tsx b/src/app/pages/Router.tsx index 56c24a899..33b55ee56 100644 --- a/src/app/pages/Router.tsx +++ b/src/app/pages/Router.tsx @@ -53,6 +53,7 @@ import { CREATE_PATH, TO_ROOM_EVENT_PATH, SETTINGS_PATH, + NAVIGATE_PATH, } from './paths'; import { getAppPathFromHref, @@ -81,6 +82,8 @@ import { HomeCreateRoom } from './client/home/CreateRoom'; import { Create } from './client/create'; import { ToRoomEvent } from './client/ToRoomEvent'; import { CallStatusRenderer } from './CallStatusRenderer'; +import { UserQuickToolsProvider } from '$components/UserQuickToolsProvider'; +import { Navigate } from './client/navigate'; /** * Returns true if there is at least one stored session. @@ -191,6 +194,9 @@ export const createRouter = (clientConfig: ClientConfig, screenSize: ScreenSize) + + + @@ -346,6 +352,7 @@ export const createRouter = (clientConfig: ClientConfig, screenSize: ScreenSize) } /> } /> + } /> } /> } sticky={ - - - {oldSidebar ? ( - <> - - -
- -
- - ) : ( - <> - {isCollapsed && ( + <> + {(oldSidebar || isCollapsed) && ( + + + {oldSidebar ? ( <> - + + + + ) : ( + <> + )} - - - - - + + )} + {!compact && ( +
+ +
)} -
+ } /> - {!oldSidebar && } + {!oldSidebar && !compact && } ); } diff --git a/src/app/pages/client/navigate/Navigate.tsx b/src/app/pages/client/navigate/Navigate.tsx new file mode 100644 index 000000000..39572b51b --- /dev/null +++ b/src/app/pages/client/navigate/Navigate.tsx @@ -0,0 +1,91 @@ +import { Box, Scroll, toRem, Text, color, config } from 'folds'; +import { SquaresFour, sizedIcon } from '$components/icons/phosphor'; +import { + Page, + PageContent, + PageContentCenter, + PageHeroSection, + PageNav, + PageNavHeader, +} from '$components/page'; +import { useEffect, useState } from 'react'; +import { ScreenSize, useScreenSizeContext } from '$hooks/useScreenSize'; +import { useSetting } from '$state/hooks/settings'; +import { settingsAtom } from '$state/settings'; +import { SidebarResizer } from '../sidebar/SidebarResizer'; +import { useSetAtom } from 'jotai'; +import { isResizingSidebarAtom } from '$state/isResizingSidebar'; +import { ListMagnifyingGlassIcon } from '@phosphor-icons/react'; +import { RoomSearchModal } from '$features/search'; + +export function Navigate() { + const setIsResizingSidebar = useSetAtom(isResizingSidebarAtom); + const [roomSidebarWidth, setRoomSidebarWidth] = useSetting(settingsAtom, 'roomSidebarWidth'); + const [curWidth, setCurWidth] = useState(roomSidebarWidth); + + useEffect(() => { + setCurWidth(roomSidebarWidth); + }, [roomSidebarWidth]); + const screenSize = useScreenSizeContext(); + const isMobile = screenSize === ScreenSize.Mobile; + const hideText = curWidth <= 80 && !isMobile; + + return ( + <> + {!isMobile && ( + + + + + {!hideText ? ( + + + Navigate + + + ) : ( + sizedIcon(SquaresFour, '200', { filled: true }) + )} + + + + + + )} + + + + + + + + {sizedIcon(ListMagnifyingGlassIcon, '600')} + + + + + + + + + + ); +} diff --git a/src/app/pages/client/navigate/index.ts b/src/app/pages/client/navigate/index.ts new file mode 100644 index 000000000..7571cfdec --- /dev/null +++ b/src/app/pages/client/navigate/index.ts @@ -0,0 +1 @@ +export * from './Navigate'; diff --git a/src/app/pages/client/sidebar/InboxTab.tsx b/src/app/pages/client/sidebar/InboxTab.tsx index fe3f636e9..8141fb759 100644 --- a/src/app/pages/client/sidebar/InboxTab.tsx +++ b/src/app/pages/client/sidebar/InboxTab.tsx @@ -17,13 +17,17 @@ import { ScreenSize, useScreenSizeContext } from '$hooks/useScreenSize'; import { useNavToActivePathAtom } from '$state/hooks/navToActivePath'; import { useInviteCount } from '$hooks/useInviteCount'; import { getPhosphorIconSize, Tray } from '$components/icons/phosphor'; +import { Text, Box, color } from 'folds'; +import { searchModalAtom } from '$state/searchModal'; -export function InboxTab({ isBottom }: { isBottom?: boolean }) { +export function InboxTab({ isBottom, isMobile }: { isBottom?: boolean; isMobile?: boolean }) { const screenSize = useScreenSizeContext(); const navigate = useNavigate(); const navToActivePath = useAtomValue(useNavToActivePathAtom()); const inboxSelected = useInboxSelected(); const inviteCount = useInviteCount(); + const isSearch = useAtomValue(searchModalAtom); + const opened = inboxSelected && !isSearch; const handleInboxClick = () => { if (screenSize === ScreenSize.Mobile) { @@ -41,21 +45,29 @@ export function InboxTab({ isBottom }: { isBottom?: boolean }) { }; return ( - + {(triggerRef) => ( - - - + + + + + {isMobile && ( + + Inbox + + )} + )} {inviteCount > 0 && } diff --git a/src/app/pages/client/sidebar/MessageTab.tsx b/src/app/pages/client/sidebar/MessageTab.tsx index 7484187ee..f743ded32 100644 --- a/src/app/pages/client/sidebar/MessageTab.tsx +++ b/src/app/pages/client/sidebar/MessageTab.tsx @@ -6,24 +6,47 @@ import { ChatTextIcon } from '@phosphor-icons/react'; import { useAtom } from 'jotai'; import { searchModalAtom } from '$state/searchModal'; import { useInboxSelected } from '$hooks/router/useInbox'; +import { Box, color, Text } from 'folds'; +import { useNavigateSelected } from '$hooks/router/useNavigateSelected'; -export function MessageTab({ isBottom }: { isBottom?: boolean }) { +export function MessageTab({ isBottom, isMobile }: { isBottom?: boolean; isMobile?: boolean }) { const navigate = useNavigate(); const [searchSelected] = useAtom(searchModalAtom); + const navigateRouteActive = useNavigateSelected(); const inboxSelected = useInboxSelected(); - const opened = !(matchPath(SETTINGS_PATH, location.pathname) || searchSelected || inboxSelected); + const opened = !( + matchPath(SETTINGS_PATH, location.pathname) || + searchSelected || + navigateRouteActive || + inboxSelected + ); const openSettings = () => navigate(HOME_PATH); return ( {(triggerRef) => ( - - - + + + + + {isMobile && ( + + Messages + + )} + )} diff --git a/src/app/pages/client/sidebar/NavigateTab.tsx b/src/app/pages/client/sidebar/NavigateTab.tsx new file mode 100644 index 000000000..e6e717958 --- /dev/null +++ b/src/app/pages/client/sidebar/NavigateTab.tsx @@ -0,0 +1,49 @@ +import { useAtom } from 'jotai'; +import { SidebarAvatar, SidebarItem, SidebarItemTooltip } from '$components/sidebar'; +import { searchModalAtom } from '$state/searchModal'; +import { ListMagnifyingGlassIcon } from '@phosphor-icons/react'; +import { getPhosphorIconSize } from '$components/icons/phosphor'; +import { Text, Box, color } from 'folds'; +import { useNavigate } from 'react-router-dom'; +import { getNavigatePath } from '$pages/pathUtils'; +import { useNavigateSelected } from '$hooks/router/useNavigateSelected'; + +export function NavigateTab({ isBottom, isMobile }: { isBottom?: boolean; isMobile?: boolean }) { + const [opened, setOpen] = useAtom(searchModalAtom); + const navigateRouteActive = useNavigateSelected(); + const isNavigate = opened || navigateRouteActive; + const navigate = useNavigate(); + const open = () => { + if (isMobile) navigate(getNavigatePath()); + else setOpen(true); + }; + + return ( + + + {(triggerRef) => ( + + + + + {isMobile && ( + + Navigate + + )} + + )} + + + ); +} diff --git a/src/app/pages/client/sidebar/SearchTab.tsx b/src/app/pages/client/sidebar/SearchTab.tsx deleted file mode 100644 index 0a8045860..000000000 --- a/src/app/pages/client/sidebar/SearchTab.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { useAtom } from 'jotai'; -import { SidebarAvatar, SidebarItem, SidebarItemTooltip } from '$components/sidebar'; -import { searchModalAtom } from '$state/searchModal'; -import { ListMagnifyingGlassIcon } from '@phosphor-icons/react'; -import { getPhosphorIconSize } from '$components/icons/phosphor'; - -export function SearchTab({ isBottom }: { isBottom?: boolean }) { - const [opened, setOpen] = useAtom(searchModalAtom); - - const open = () => setOpen(true); - - return ( - - - {(triggerRef) => ( - - - - )} - - - ); -} diff --git a/src/app/pages/client/sidebar/SettingsTab.tsx b/src/app/pages/client/sidebar/SettingsTab.tsx index 37372e2eb..7ca47d18a 100644 --- a/src/app/pages/client/sidebar/SettingsTab.tsx +++ b/src/app/pages/client/sidebar/SettingsTab.tsx @@ -3,8 +3,9 @@ import { GearSix, getPhosphorIconSize } from '$components/icons/phosphor'; import { useOpenSettings } from '$features/settings'; import { matchPath } from 'react-router-dom'; import { SETTINGS_PATH } from '$pages/paths'; +import { color } from 'folds'; -export function SettingsTab({ isBottom }: { isBottom?: boolean }) { +export function SettingsTab({ isBottom, isMobile }: { isBottom?: boolean; isMobile?: boolean }) { const opened = !!matchPath(SETTINGS_PATH, location.pathname); const openSettings = useOpenSettings(); @@ -16,6 +17,7 @@ export function SettingsTab({ isBottom }: { isBottom?: boolean }) { )} diff --git a/src/app/pages/client/sidebar/UserMenuTab.tsx b/src/app/pages/client/sidebar/UserMenuTab.tsx index efbb30283..a94cdded0 100644 --- a/src/app/pages/client/sidebar/UserMenuTab.tsx +++ b/src/app/pages/client/sidebar/UserMenuTab.tsx @@ -525,7 +525,7 @@ export function PresenceMenuOption() { ); } -export function UserMenuTab({ isBottom }: { isBottom?: boolean }) { +export function UserMenuTab({ isBottom, isMobile }: { isBottom?: boolean; isMobile?: boolean }) { const mx = useMatrixClient(); const useAuthentication = useMediaAuthentication(); @@ -552,7 +552,7 @@ export function UserMenuTab({ isBottom }: { isBottom?: boolean }) { ? (mxcUrlToHttp(mx, parsedBanner, useAuthentication, 640, 192, 'scale') ?? undefined) : undefined; - const handleToggle: MouseEventHandler = (evt) => { + const handleToggle: MouseEventHandler = (evt) => { const cords = evt.currentTarget.getBoundingClientRect(); setMenuAnchor((cur) => (cur ? undefined : cords)); }; @@ -561,21 +561,38 @@ export function UserMenuTab({ isBottom }: { isBottom?: boolean }) { return ( - + {(triggerRef) => ( - } - > - - {nameInitials(displayName)}} - /> + + + } + > + + {nameInitials(displayName)}} + /> + + - + {isMobile && ( + + Account + + )} + )} diff --git a/src/app/pages/client/sidebar/UserQuickTools.css.ts b/src/app/pages/client/sidebar/UserQuickTools.css.ts index 8cd733038..0e194fd8d 100644 --- a/src/app/pages/client/sidebar/UserQuickTools.css.ts +++ b/src/app/pages/client/sidebar/UserQuickTools.css.ts @@ -2,13 +2,11 @@ import { style } from '@vanilla-extract/css'; import { color, config, toRem } from 'folds'; export const UserQuickTools = style({ - backgroundColor: color.SurfaceVariant.Container, - color: color.SurfaceVariant.OnContainer, - position: 'absolute', + backgroundColor: color.Surface.Container, + color: color.Surface.OnContainer, zIndex: '1000', height: toRem(74), bottom: '0', - left: toRem(-66), padding: config.space.S300, borderTop: `${config.borderWidth.B300} solid ${color.Background.ContainerLine}`, }); diff --git a/src/app/pages/client/sidebar/UserQuickTools.tsx b/src/app/pages/client/sidebar/UserQuickTools.tsx index bebbc3cab..0792f0dd4 100644 --- a/src/app/pages/client/sidebar/UserQuickTools.tsx +++ b/src/app/pages/client/sidebar/UserQuickTools.tsx @@ -1,26 +1,24 @@ import { Box, config, toRem } from 'folds'; import { InboxTab } from './InboxTab'; -import { SearchTab } from './SearchTab'; +import { NavigateTab } from './NavigateTab'; import { SettingsTab } from './SettingsTab'; import { useAtom } from 'jotai'; import { isResizingSidebarAtom } from '$state/isResizingSidebar'; import * as css from './UserQuickTools.css'; -import { ScreenSize, useScreenSizeContext } from '$hooks/useScreenSize'; import { UserMenuTab } from './UserMenuTab'; import { MessageTab } from './MessageTab'; export function UserQuickTools({ width, + compact, }: { isCollapsed?: boolean; underOutstep?: boolean; - width: number; + width?: number; + compact: boolean; }) { - const screenSize = useScreenSizeContext(); - const compact = screenSize === ScreenSize.Mobile; - const [isResizingSidebar] = useAtom(isResizingSidebarAtom); - const isCollapsed = compact ? false : width < 190 + 66; + const isCollapsed = compact ? false : (width ?? 0) < 190 + 66; return ( <> @@ -32,19 +30,28 @@ export function UserQuickTools({ justifyContent={compact ? 'SpaceAround' : 'SpaceBetween'} alignItems="Center" className={css.UserQuickTools} - style={{ - opacity: isResizingSidebar ? '0%' : '100%', - transition: isResizingSidebar ? 'opacity 0.2s ease' : 'opacity 0.5s ease', - width: compact ? '100vw' : toRem(width), - paddingRight: config.space.S300, - }} + style={ + compact + ? { + borderTopLeftRadius: config.radii.R500, + borderTopRightRadius: config.radii.R500, + width: '100vw', + } + : { + opacity: isResizingSidebar ? '0%' : '100%', + transition: isResizingSidebar ? 'opacity 0.2s ease' : 'opacity 0.5s ease', + width: toRem(width ?? 100), + position: 'absolute', + left: toRem(-66), + } + } > {compact ? ( <> - - - - + + + + ) : ( <> @@ -57,7 +64,7 @@ export function UserQuickTools({ {!isCollapsed && ( <> - + )} diff --git a/src/app/pages/pathUtils.ts b/src/app/pages/pathUtils.ts index 4a95f47fc..35a248c2e 100644 --- a/src/app/pages/pathUtils.ts +++ b/src/app/pages/pathUtils.ts @@ -28,6 +28,7 @@ import { SPACE_ROOM_PATH, SPACE_SEARCH_PATH, CREATE_PATH, + NAVIGATE_PATH, } from './paths'; export const joinPathComponent = (path: Path): string => path.pathname + path.search + path.hash; @@ -154,6 +155,7 @@ export const getExploreServerPath = (server: string): string => { }; export const getCreatePath = (): string => CREATE_PATH; +export const getNavigatePath = (): string => NAVIGATE_PATH; export const getInboxPath = (): string => INBOX_PATH; export const getInboxNotificationsPath = (): string => INBOX_NOTIFICATIONS_PATH; diff --git a/src/app/pages/paths.ts b/src/app/pages/paths.ts index 1ac57b756..43921ea4a 100644 --- a/src/app/pages/paths.ts +++ b/src/app/pages/paths.ts @@ -79,6 +79,7 @@ export type ExploreServerPathSearchParams = { export const EXPLORE_SERVER_PATH = `/explore/${SERVER_PATH_SEGMENT}`; export const CREATE_PATH = '/create'; +export const NAVIGATE_PATH = '/navigate'; export const NOTIFICATIONS_PATH_SEGMENT = 'notifications/'; export const INVITES_PATH_SEGMENT = 'invites/'; From ba96581826b6ba256a928d5790cdeeb72e94a781 Mon Sep 17 00:00:00 2001 From: Shea Date: Mon, 29 Jun 2026 18:17:22 +0300 Subject: [PATCH 5/6] base settings Signed-off-by: Shea --- .../message/modals/MessageForward.tsx | 2 +- src/app/components/user-profile/styles.css.ts | 1 + .../Search.tsx => navigate/NavigateModal.tsx} | 17 +--- src/app/features/navigate/index.ts | 1 + src/app/features/room/RoomTimeline.css.ts | 2 - src/app/features/search/index.ts | 1 - src/app/hooks/router/useProfileSelected.ts | 12 +++ src/app/pages/Router.tsx | 5 +- src/app/pages/client/navigate/Navigate.tsx | 2 +- src/app/pages/client/profile/Profile.tsx | 96 +++++++++++++++++++ src/app/pages/client/profile/index.ts | 1 + src/app/pages/client/sidebar/MessageTab.tsx | 3 + src/app/pages/client/sidebar/UserMenuTab.tsx | 12 ++- src/app/pages/pathUtils.ts | 2 + src/app/pages/paths.ts | 1 + 15 files changed, 138 insertions(+), 20 deletions(-) rename src/app/features/{search/Search.tsx => navigate/NavigateModal.tsx} (98%) create mode 100644 src/app/features/navigate/index.ts delete mode 100644 src/app/features/search/index.ts create mode 100644 src/app/hooks/router/useProfileSelected.ts create mode 100644 src/app/pages/client/profile/Profile.tsx create mode 100644 src/app/pages/client/profile/index.ts diff --git a/src/app/components/message/modals/MessageForward.tsx b/src/app/components/message/modals/MessageForward.tsx index 1bdcdebdd..aef10a95c 100644 --- a/src/app/components/message/modals/MessageForward.tsx +++ b/src/app/components/message/modals/MessageForward.tsx @@ -17,7 +17,7 @@ import * as Sentry from '@sentry/react'; import { isRoomPrivate } from '$utils/roomVisibility'; import { canForwardEvent } from '$utils/room'; import * as prefix from '$unstable/prefixes'; -import { SearchWrapper } from '$features/search'; +import { SearchWrapper } from '$features/navigate'; const debugLog = createDebugLogger('MessageForward'); // Message forwarding component diff --git a/src/app/components/user-profile/styles.css.ts b/src/app/components/user-profile/styles.css.ts index 133f0b7f8..e7283d43b 100644 --- a/src/app/components/user-profile/styles.css.ts +++ b/src/app/components/user-profile/styles.css.ts @@ -44,6 +44,7 @@ export const UserAvatarContainer = style({ position: 'relative', top: 0, transform: 'translateY(-50%)', + backgroundColor: color.Surface.Container, }); export const UserHeroStatusContainer = style({ position: 'relative', diff --git a/src/app/features/search/Search.tsx b/src/app/features/navigate/NavigateModal.tsx similarity index 98% rename from src/app/features/search/Search.tsx rename to src/app/features/navigate/NavigateModal.tsx index ccc083047..8da2f7a3d 100644 --- a/src/app/features/search/Search.tsx +++ b/src/app/features/navigate/NavigateModal.tsx @@ -274,7 +274,7 @@ export function RoomSearchModal({ requestClose, pickRoom }: RoomSearchModalProps }, [listFocus.index]); return ( - + {pickRoom && ( {roomsToRender.length === 0 && ( { if (isKeyHotkey('mod+k', event)) { event.preventDefault(); - if (opened) { - setOpen(false); - return; - } - - const portalContainer = document.getElementById('portalContainer'); - if (portalContainer && portalContainer.children.length > 0) { - return; - } - setOpen(true); + setOpen(!opened); + return; } }, [opened, setOpen] diff --git a/src/app/features/navigate/index.ts b/src/app/features/navigate/index.ts new file mode 100644 index 000000000..d72d0d057 --- /dev/null +++ b/src/app/features/navigate/index.ts @@ -0,0 +1 @@ +export * from './NavigateModal'; diff --git a/src/app/features/room/RoomTimeline.css.ts b/src/app/features/room/RoomTimeline.css.ts index 4236685e9..9903033dc 100644 --- a/src/app/features/room/RoomTimeline.css.ts +++ b/src/app/features/room/RoomTimeline.css.ts @@ -12,8 +12,6 @@ export const TimelineFloat = recipe({ transform: 'translateX(-50%)', zIndex: 10, minWidth: 'max-content', - overflow: 'hidden', - borderRadius: config.radii.Pill, }, ], variants: { diff --git a/src/app/features/search/index.ts b/src/app/features/search/index.ts deleted file mode 100644 index addd53308..000000000 --- a/src/app/features/search/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Search'; diff --git a/src/app/hooks/router/useProfileSelected.ts b/src/app/hooks/router/useProfileSelected.ts new file mode 100644 index 000000000..9a7abdde2 --- /dev/null +++ b/src/app/hooks/router/useProfileSelected.ts @@ -0,0 +1,12 @@ +import { useMatch } from 'react-router-dom'; +import { getProfilePath } from '$pages/pathUtils'; + +export const useProfileSelected = (): boolean => { + const match = useMatch({ + path: getProfilePath(), + caseSensitive: true, + end: false, + }); + + return !!match; +}; diff --git a/src/app/pages/Router.tsx b/src/app/pages/Router.tsx index 33b55ee56..8cf657b9e 100644 --- a/src/app/pages/Router.tsx +++ b/src/app/pages/Router.tsx @@ -28,7 +28,7 @@ import type { Sessions } from '$state/sessions'; import { getFallbackSession, MATRIX_SESSIONS_KEY } from '$state/sessions'; import { getLocalStorageItem } from '$state/utils/atomWithLocalStorage'; import { NotificationJumper } from '$hooks/useNotificationJumper'; -import { SearchModalRenderer } from '$features/search'; +import { SearchModalRenderer } from '$features/navigate'; import { GlobalKeyboardShortcuts } from '$components/GlobalKeyboardShortcuts'; import { CallEmbedProvider } from '$components/CallEmbedProvider'; import { AuthLayout, Login, Register, ResetPassword } from './auth'; @@ -54,6 +54,7 @@ import { TO_ROOM_EVENT_PATH, SETTINGS_PATH, NAVIGATE_PATH, + PROFILE_PATH, } from './paths'; import { getAppPathFromHref, @@ -84,6 +85,7 @@ import { ToRoomEvent } from './client/ToRoomEvent'; import { CallStatusRenderer } from './CallStatusRenderer'; import { UserQuickToolsProvider } from '$components/UserQuickToolsProvider'; import { Navigate } from './client/navigate'; +import { ProfileMobile } from './client/profile'; /** * Returns true if there is at least one stored session. @@ -353,6 +355,7 @@ export const createRouter = (clientConfig: ClientConfig, screenSize: ScreenSize)
} /> } /> + } /> } /> { + setCurWidth(roomSidebarWidth); + }, [roomSidebarWidth]); + const screenSize = useScreenSizeContext(); + const isMobile = screenSize === ScreenSize.Mobile; + const hideText = curWidth <= 80 && !isMobile; + + return ( + <> + {!isMobile && ( + + + + + {!hideText ? ( + + + Navigate + + + ) : ( + sizedIcon(SquaresFour, '200', { filled: true }) + )} + + + + + + )} + + + + + + + + + + + + + + + + + + ); +} diff --git a/src/app/pages/client/profile/index.ts b/src/app/pages/client/profile/index.ts new file mode 100644 index 000000000..963df00a6 --- /dev/null +++ b/src/app/pages/client/profile/index.ts @@ -0,0 +1 @@ +export * from './Profile'; diff --git a/src/app/pages/client/sidebar/MessageTab.tsx b/src/app/pages/client/sidebar/MessageTab.tsx index f743ded32..212d8b48a 100644 --- a/src/app/pages/client/sidebar/MessageTab.tsx +++ b/src/app/pages/client/sidebar/MessageTab.tsx @@ -8,16 +8,19 @@ import { searchModalAtom } from '$state/searchModal'; import { useInboxSelected } from '$hooks/router/useInbox'; import { Box, color, Text } from 'folds'; import { useNavigateSelected } from '$hooks/router/useNavigateSelected'; +import { useProfileSelected } from '$hooks/router/useProfileSelected'; export function MessageTab({ isBottom, isMobile }: { isBottom?: boolean; isMobile?: boolean }) { const navigate = useNavigate(); const [searchSelected] = useAtom(searchModalAtom); const navigateRouteActive = useNavigateSelected(); + const profileRouteActive = useProfileSelected(); const inboxSelected = useInboxSelected(); const opened = !( matchPath(SETTINGS_PATH, location.pathname) || searchSelected || navigateRouteActive || + profileRouteActive || inboxSelected ); const openSettings = () => navigate(HOME_PATH); diff --git a/src/app/pages/client/sidebar/UserMenuTab.tsx b/src/app/pages/client/sidebar/UserMenuTab.tsx index a94cdded0..f926340aa 100644 --- a/src/app/pages/client/sidebar/UserMenuTab.tsx +++ b/src/app/pages/client/sidebar/UserMenuTab.tsx @@ -39,7 +39,7 @@ import { UnreadBadge, UnreadBadgeCenter } from '$components/unread-badge'; import { Check, chipIcon, Plus } from '$components/icons/phosphor'; import { useSessionProfiles } from '$hooks/useSessionProfiles'; import { useClientConfig } from '$hooks/useClientConfig'; -import { getHomePath, getLoginPath, withSearchParam } from '$pages/pathUtils'; +import { getHomePath, getLoginPath, getProfilePath, withSearchParam } from '$pages/pathUtils'; import { initClient, logoutClient, stopClient } from '$client/initMatrix'; import { useAtom, useAtomValue, useSetAtom } from 'jotai'; import { useNavigate } from 'react-router-dom'; @@ -48,6 +48,7 @@ import { ScreenSize, useScreenSizeContext } from '$hooks/useScreenSize'; import { setUserPresence } from '$utils/presence'; import { useSetting } from '$state/hooks/settings'; import { settingsAtom } from '$state/settings'; +import { useProfileSelected } from '$hooks/router/useProfileSelected'; const log = createLogger('AccountSwitcherTab'); @@ -528,6 +529,8 @@ export function PresenceMenuOption() { export function UserMenuTab({ isBottom, isMobile }: { isBottom?: boolean; isMobile?: boolean }) { const mx = useMatrixClient(); const useAuthentication = useMediaAuthentication(); + const profileSelected = useProfileSelected(); + const navigate = useNavigate(); const userId = mx.getUserId() ?? ''; const profile = useUserProfile(userId); @@ -553,6 +556,11 @@ export function UserMenuTab({ isBottom, isMobile }: { isBottom?: boolean; isMobi : undefined; const handleToggle: MouseEventHandler = (evt) => { + if(isMobile){ + navigate(getProfilePath()); + return; + } + const cords = evt.currentTarget.getBoundingClientRect(); setMenuAnchor((cur) => (cur ? undefined : cords)); }; @@ -560,7 +568,7 @@ export function UserMenuTab({ isBottom, isMobile }: { isBottom?: boolean; isMobi const handleCloseMenu = () => setMenuAnchor(undefined); return ( - + path.pathname + path.search + path.hash; @@ -156,6 +157,7 @@ export const getExploreServerPath = (server: string): string => { export const getCreatePath = (): string => CREATE_PATH; export const getNavigatePath = (): string => NAVIGATE_PATH; +export const getProfilePath = (): string => PROFILE_PATH; export const getInboxPath = (): string => INBOX_PATH; export const getInboxNotificationsPath = (): string => INBOX_NOTIFICATIONS_PATH; diff --git a/src/app/pages/paths.ts b/src/app/pages/paths.ts index 43921ea4a..2fd16b54f 100644 --- a/src/app/pages/paths.ts +++ b/src/app/pages/paths.ts @@ -80,6 +80,7 @@ export const EXPLORE_SERVER_PATH = `/explore/${SERVER_PATH_SEGMENT}`; export const CREATE_PATH = '/create'; export const NAVIGATE_PATH = '/navigate'; +export const PROFILE_PATH = '/profile'; export const NOTIFICATIONS_PATH_SEGMENT = 'notifications/'; export const INVITES_PATH_SEGMENT = 'invites/'; From 163575d78f390e79679844854b805fb36eced2ce Mon Sep 17 00:00:00 2001 From: Shea Date: Tue, 30 Jun 2026 22:45:26 +0300 Subject: [PATCH 6/6] sth Signed-off-by: Shea --- src/app/components/user-profile/UserHero.tsx | 3 - src/app/pages/MobileFriendly.tsx | 36 +++++++- src/app/pages/Router.tsx | 14 ++-- src/app/pages/client/SidebarNav.tsx | 6 +- src/app/pages/client/create/Create.tsx | 3 + src/app/pages/client/direct/Direct.tsx | 3 + src/app/pages/client/explore/Explore.tsx | 3 + src/app/pages/client/home/Home.tsx | 3 + src/app/pages/client/inbox/Inbox.tsx | 3 + src/app/pages/client/profile/Profile.tsx | 83 ++++++++++++++----- src/app/pages/client/sidebar/UserMenuTab.tsx | 10 +-- .../pages/client/sidebar/UserQuickTools.tsx | 7 +- src/app/pages/client/space/Space.tsx | 4 + src/app/pages/paths.ts | 2 +- 14 files changed, 128 insertions(+), 52 deletions(-) diff --git a/src/app/components/user-profile/UserHero.tsx b/src/app/components/user-profile/UserHero.tsx index 29c8de992..da6f57fab 100644 --- a/src/app/components/user-profile/UserHero.tsx +++ b/src/app/components/user-profile/UserHero.tsx @@ -238,10 +238,7 @@ export function UserHero({ WebkitBoxOrient: 'vertical', overflow: 'hidden', fontStyle: allowEditing && !status ? 'italic' : 'normal', -<<<<<<< HEAD opacity: allowEditing && !status ? config.opacity.Placeholder : 1, -======= ->>>>>>> f873d334 (Redesign the user menu tab) }} > {status || (allowEditing && "What's on your mind?")} diff --git a/src/app/pages/MobileFriendly.tsx b/src/app/pages/MobileFriendly.tsx index 83009cda5..b791bf906 100644 --- a/src/app/pages/MobileFriendly.tsx +++ b/src/app/pages/MobileFriendly.tsx @@ -1,22 +1,34 @@ import type { ReactNode } from 'react'; import { useMatch } from 'react-router-dom'; import { ScreenSize, useScreenSizeContext } from '$hooks/useScreenSize'; -import { DIRECT_PATH, EXPLORE_PATH, HOME_PATH, INBOX_PATH, SPACE_PATH } from './paths'; +import { + DIRECT_PATH, + EXPLORE_PATH, + HOME_PATH, + INBOX_PATH, + NAVIGATE_PATH, + PROFILE_PATH, + SPACE_PATH, +} from './paths'; type MobileFriendlyClientNavProps = { children: ReactNode; }; -export function MobileFriendlyClientNav({ children }: MobileFriendlyClientNavProps) { +export function MobileFriendlySidebarNav({ children }: MobileFriendlyClientNavProps) { const screenSize = useScreenSizeContext(); const homeMatch = useMatch({ path: HOME_PATH, caseSensitive: true, end: true }); const directMatch = useMatch({ path: DIRECT_PATH, caseSensitive: true, end: true }); const spaceMatch = useMatch({ path: SPACE_PATH, caseSensitive: true, end: true }); const exploreMatch = useMatch({ path: EXPLORE_PATH, caseSensitive: true, end: true }); const inboxMatch = useMatch({ path: INBOX_PATH, caseSensitive: true, end: true }); - + const profileMatch = useMatch({ path: PROFILE_PATH, caseSensitive: true, end: true }); + const navigateMatch = useMatch({ path: NAVIGATE_PATH, caseSensitive: true, end: true }); if ( screenSize === ScreenSize.Mobile && - !(homeMatch || directMatch || spaceMatch || exploreMatch || inboxMatch) + (!(homeMatch || directMatch || spaceMatch || exploreMatch) || + profileMatch || + inboxMatch || + navigateMatch) ) { return null; } @@ -24,6 +36,22 @@ export function MobileFriendlyClientNav({ children }: MobileFriendlyClientNavPro return children; } +export function MobileFriendlyBottomNav({ children }: MobileFriendlyClientNavProps) { + const screenSize = useScreenSizeContext(); + const homeMatch = useMatch({ path: HOME_PATH, caseSensitive: true, end: true }); + const directMatch = useMatch({ path: DIRECT_PATH, caseSensitive: true, end: true }); + const spaceMatch = useMatch({ path: SPACE_PATH, caseSensitive: true, end: true }); + const settingsMatch = useMatch({ path: '/settings/', caseSensitive: true, end: true }); + if ( + screenSize !== ScreenSize.Mobile || + (!homeMatch && !directMatch && !spaceMatch) || + settingsMatch + ) { + return null; + } + + return children; +} type MobileFriendlyPageNavProps = { path: string; children: ReactNode; diff --git a/src/app/pages/Router.tsx b/src/app/pages/Router.tsx index 8cf657b9e..1605b24ec 100644 --- a/src/app/pages/Router.tsx +++ b/src/app/pages/Router.tsx @@ -75,7 +75,11 @@ import { Notifications, Inbox, Invites } from './client/inbox'; import { setAfterLoginRedirectPath } from './afterLoginRedirectPath'; import { WelcomePage } from './client/WelcomePage'; import { SidebarNav } from './client/SidebarNav'; -import { MobileFriendlyPageNav, MobileFriendlyClientNav } from './MobileFriendly'; +import { + MobileFriendlyPageNav, + MobileFriendlySidebarNav, + MobileFriendlyBottomNav, +} from './MobileFriendly'; import { ClientInitStorageAtom } from './client/ClientInitStorageAtom'; import { AuthRouteThemeManager, UnAuthRouteThemeManager } from './ThemeManager'; import { ClientRoomsNotificationPreferences } from './client/ClientRoomsNotificationPreferences'; @@ -187,18 +191,18 @@ export const createRouter = (clientConfig: ClientConfig, screenSize: ScreenSize) + - + } > - + - + diff --git a/src/app/pages/client/SidebarNav.tsx b/src/app/pages/client/SidebarNav.tsx index 846499e5e..de4ea1628 100644 --- a/src/app/pages/client/SidebarNav.tsx +++ b/src/app/pages/client/SidebarNav.tsx @@ -10,7 +10,6 @@ import { DirectTab, DirectDMsList, HomeTab, SpaceTabs, InboxTab, UnverifiedTab } import { CreateTab } from './sidebar/CreateTab'; import { NavigateTab } from './sidebar/NavigateTab'; import { SettingsTab } from './sidebar/SettingsTab'; -import { UserQuickTools } from './sidebar/UserQuickTools'; import { useScreenSizeContext, ScreenSize } from '$hooks/useScreenSize'; import { UserMenuTab } from './sidebar/UserMenuTab'; @@ -22,11 +21,9 @@ export function SidebarNav() { const [showUnreadCounts, setShowUnreadCounts] = useSetting(settingsAtom, 'showUnreadCounts'); const [badgeCountDMsOnly, setBadgeCountDMsOnly] = useSetting(settingsAtom, 'badgeCountDMsOnly'); const [showPingCounts, setShowPingCounts] = useSetting(settingsAtom, 'showPingCounts'); - - const [oldSidebar] = useSetting(settingsAtom, 'oldSidebar'); - const [roomSidebarWidth] = useSetting(settingsAtom, 'roomSidebarWidth'); + const [oldSidebar] = useSetting(settingsAtom, 'oldSidebar'); const screenSize = useScreenSizeContext(); const compact = screenSize === ScreenSize.Mobile; @@ -169,7 +166,6 @@ export function SidebarNav() { } /> - {!oldSidebar && !compact && } ); } diff --git a/src/app/pages/client/create/Create.tsx b/src/app/pages/client/create/Create.tsx index d22ba50c8..c9a77e7f9 100644 --- a/src/app/pages/client/create/Create.tsx +++ b/src/app/pages/client/create/Create.tsx @@ -18,6 +18,7 @@ import { settingsAtom } from '$state/settings'; import { SidebarResizer } from '../sidebar/SidebarResizer'; import { useSetAtom } from 'jotai'; import { isResizingSidebarAtom } from '$state/isResizingSidebar'; +import { UserQuickTools } from '../sidebar/UserQuickTools'; export function Create() { const { navigateSpace } = useRoomNavigate(); @@ -32,6 +33,7 @@ export function Create() { const screenSize = useScreenSizeContext(); const isMobile = screenSize === ScreenSize.Mobile; const hideText = curWidth <= 80 && !isMobile; + const [oldSidebar] = useSetting(settingsAtom, 'oldSidebar'); return ( <> @@ -71,6 +73,7 @@ export function Create() { setAnnouncement={setIsResizingSidebar} /> + {!oldSidebar && !isMobile && } )} diff --git a/src/app/pages/client/direct/Direct.tsx b/src/app/pages/client/direct/Direct.tsx index c9a3bffdf..bc3bf0ed3 100644 --- a/src/app/pages/client/direct/Direct.tsx +++ b/src/app/pages/client/direct/Direct.tsx @@ -65,6 +65,7 @@ import { useDirectRooms } from './useDirectRooms'; import { SidebarResizer } from '$pages/client/sidebar/SidebarResizer'; import { useScreenSizeContext, ScreenSize } from '$hooks/useScreenSize'; import { isResizingSidebarAtom } from '$state/isResizingSidebar'; +import { UserQuickTools } from '../sidebar/UserQuickTools'; type DirectMenuProps = { requestClose: () => void; @@ -270,6 +271,7 @@ export function Direct() { const screenSize = useScreenSizeContext(); const isMobile = screenSize === ScreenSize.Mobile; const hideText = curWidth <= 80 && !isMobile; + const [oldSidebar] = useSetting(settingsAtom, 'oldSidebar'); return ( )} + {!oldSidebar && !isMobile && } ); } diff --git a/src/app/pages/client/explore/Explore.tsx b/src/app/pages/client/explore/Explore.tsx index 60796783a..c78aec24f 100644 --- a/src/app/pages/client/explore/Explore.tsx +++ b/src/app/pages/client/explore/Explore.tsx @@ -54,6 +54,7 @@ import { isServerName } from '$utils/matrix'; import { useScreenSizeContext, ScreenSize } from '$hooks/useScreenSize'; import { isResizingSidebarAtom } from '$state/isResizingSidebar'; import { useSetAtom } from 'jotai'; +import { UserQuickTools } from '../sidebar/UserQuickTools'; type AddServerProps = { hideText?: boolean; @@ -267,6 +268,7 @@ export function Explore() { const screenSize = useScreenSizeContext(); const isMobile = screenSize === ScreenSize.Mobile; const hideText = curWidth <= 80 && !isMobile; + const [oldSidebar] = useSetting(settingsAtom, 'oldSidebar'); return ( )} + {!oldSidebar && !isMobile && } ); } diff --git a/src/app/pages/client/home/Home.tsx b/src/app/pages/client/home/Home.tsx index 3b2da2945..c32f6766d 100644 --- a/src/app/pages/client/home/Home.tsx +++ b/src/app/pages/client/home/Home.tsx @@ -81,6 +81,7 @@ import { ScreenSize, useScreenSizeContext } from '$hooks/useScreenSize'; import { useClientConfig } from '$hooks/useClientConfig'; import { getMxIdServer } from '$utils/mxIdHelper'; import { isResizingSidebarAtom } from '$state/isResizingSidebar'; +import { UserQuickTools } from '../sidebar/UserQuickTools'; type HomeMenuProps = { requestClose: () => void; @@ -319,6 +320,7 @@ export function Home() { const screenSize = useScreenSizeContext(); const isMobile = screenSize === ScreenSize.Mobile; const hideText = curWidth <= 80 && !isMobile; + const [oldSidebar] = useSetting(settingsAtom, 'oldSidebar'); return ( )} + {!oldSidebar && !isMobile && } ); } diff --git a/src/app/pages/client/inbox/Inbox.tsx b/src/app/pages/client/inbox/Inbox.tsx index 7dca68e16..e0d809eda 100644 --- a/src/app/pages/client/inbox/Inbox.tsx +++ b/src/app/pages/client/inbox/Inbox.tsx @@ -14,6 +14,7 @@ import { ScreenSize, useScreenSizeContext } from '$hooks/useScreenSize'; import { useInviteCount } from '$hooks/useInviteCount'; import { isResizingSidebarAtom } from '$state/isResizingSidebar'; import { useSetAtom } from 'jotai'; +import { UserQuickTools } from '../sidebar/UserQuickTools'; function InvitesNavItem({ hideText }: { hideText?: boolean }) { const invitesSelected = useInboxInvitesSelected(); @@ -58,6 +59,7 @@ export function Inbox() { const setIsResizingSidebar = useSetAtom(isResizingSidebarAtom); const [roomSidebarWidth, setRoomSidebarWidth] = useSetting(settingsAtom, 'roomSidebarWidth'); const [curWidth, setCurWidth] = useState(roomSidebarWidth); + const [oldSidebar] = useSetting(settingsAtom, 'oldSidebar'); useEffect(() => { setCurWidth(roomSidebarWidth); @@ -131,6 +133,7 @@ export function Inbox() { setAnnouncement={setIsResizingSidebar} /> )} + {!oldSidebar && !isMobile && } ); } diff --git a/src/app/pages/client/profile/Profile.tsx b/src/app/pages/client/profile/Profile.tsx index 200195fe4..11f7b8a49 100644 --- a/src/app/pages/client/profile/Profile.tsx +++ b/src/app/pages/client/profile/Profile.tsx @@ -1,13 +1,6 @@ -import { Box, Scroll, toRem, Text, color, config } from 'folds'; +import { Box, Scroll, toRem, Text, color, config, Menu, Icon, Icons, Line, MenuItem } from 'folds'; import { SquaresFour, sizedIcon } from '$components/icons/phosphor'; -import { - Page, - PageContent, - PageContentCenter, - PageHeroSection, - PageNav, - PageNavHeader, -} from '$components/page'; +import { Page, PageHeroSection, PageNav, PageNavHeader } from '$components/page'; import { useEffect, useState } from 'react'; import { ScreenSize, useScreenSizeContext } from '$hooks/useScreenSize'; import { useSetting } from '$state/hooks/settings'; @@ -16,13 +9,33 @@ import { SidebarResizer } from '../sidebar/SidebarResizer'; import { useSetAtom } from 'jotai'; import { isResizingSidebarAtom } from '$state/isResizingSidebar'; import { AccountMenuOption, PresenceMenuOption } from '../sidebar/UserMenuTab'; -import { UserRoomProfile } from '$components/user-profile'; import { useMatrixClient } from '$hooks/useMatrixClient'; -import { UserRoomProfileRenderer } from '$components/UserRoomProfileRenderer'; +import { GlobalUserHeroName, UserHero } from '$components/user-profile/UserHero'; +import { getMxIdLocalPart, mxcUrlToHttp } from '$utils/matrix'; +import { useMediaAuthentication } from '$hooks/useMediaAuthentication'; +import { useUserPresence } from '$hooks/useUserPresence'; +import { useUserProfile } from '$hooks/useUserProfile'; +import { useOpenSettings } from '$features/settings'; export function ProfileMobile() { const mx = useMatrixClient(); + const useAuthentication = useMediaAuthentication(); + const openSettings = useOpenSettings(); + const userId = mx.getUserId() ?? ''; + const profile = useUserProfile(userId); + const presence = useUserPresence(userId); + + const displayName = profile.displayName ?? getMxIdLocalPart(userId) ?? userId; + const heroAvatarUrl = profile.avatarUrl + ? (mxcUrlToHttp(mx, profile.avatarUrl, useAuthentication, 160, 160, 'crop') ?? undefined) + : undefined; + + const parsedBanner = + typeof profile.bannerUrl === 'string' ? profile.bannerUrl.replace(/^"|"$/g, '') : undefined; + const heroBannerUrl = parsedBanner + ? (mxcUrlToHttp(mx, parsedBanner, useAuthentication, 640, 192, 'scale') ?? undefined) + : undefined; const setIsResizingSidebar = useSetAtom(isResizingSidebarAtom); const [roomSidebarWidth, setRoomSidebarWidth] = useSetting(settingsAtom, 'roomSidebarWidth'); @@ -54,7 +67,7 @@ export function ProfileMobile() { {!hideText ? ( - Navigate + Profile ) : ( @@ -76,18 +89,42 @@ export function ProfileMobile() { )} - - - - - - - - + + + + + + + + + - - - + + + + + + + } + onClick={() => openSettings()} + > + + Settings + + + + + diff --git a/src/app/pages/client/sidebar/UserMenuTab.tsx b/src/app/pages/client/sidebar/UserMenuTab.tsx index f926340aa..4b656b9c7 100644 --- a/src/app/pages/client/sidebar/UserMenuTab.tsx +++ b/src/app/pages/client/sidebar/UserMenuTab.tsx @@ -378,7 +378,7 @@ const PresenceOptions: Array<{ value: Presence; label: string }> = [ { value: Presence.Offline, label: 'Offline' }, ]; -export function PresenceMenuOption() { +export function PresenceMenuOption({ initialOpen }: { initialOpen: boolean }) { const mx = useMatrixClient(); const [sendPresence] = useSetting(settingsAtom, 'sendPresence'); @@ -388,7 +388,7 @@ export function PresenceMenuOption() { const isMobile = screenSize === ScreenSize.Mobile; const currentPresence = presence?.presence ?? Presence.Online; - const [isOpen, setIsOpen] = useState(false); + const [isOpen, setIsOpen] = useState(initialOpen); const { hoverProps } = useHover({ onHoverChange: (h) => { if (!isMobile) setIsOpen(h); @@ -556,11 +556,11 @@ export function UserMenuTab({ isBottom, isMobile }: { isBottom?: boolean; isMobi : undefined; const handleToggle: MouseEventHandler = (evt) => { - if(isMobile){ + if (isMobile) { navigate(getProfilePath()); return; } - + const cords = evt.currentTarget.getBoundingClientRect(); setMenuAnchor((cur) => (cur ? undefined : cords)); }; @@ -651,7 +651,7 @@ export function UserMenuTab({ isBottom, isMobile }: { isBottom?: boolean; isMobi - + diff --git a/src/app/pages/client/sidebar/UserQuickTools.tsx b/src/app/pages/client/sidebar/UserQuickTools.tsx index 0792f0dd4..e65799d61 100644 --- a/src/app/pages/client/sidebar/UserQuickTools.tsx +++ b/src/app/pages/client/sidebar/UserQuickTools.tsx @@ -2,8 +2,6 @@ import { Box, config, toRem } from 'folds'; import { InboxTab } from './InboxTab'; import { NavigateTab } from './NavigateTab'; import { SettingsTab } from './SettingsTab'; -import { useAtom } from 'jotai'; -import { isResizingSidebarAtom } from '$state/isResizingSidebar'; import * as css from './UserQuickTools.css'; import { UserMenuTab } from './UserMenuTab'; import { MessageTab } from './MessageTab'; @@ -17,7 +15,6 @@ export function UserQuickTools({ width?: number; compact: boolean; }) { - const [isResizingSidebar] = useAtom(isResizingSidebarAtom); const isCollapsed = compact ? false : (width ?? 0) < 190 + 66; return ( @@ -38,11 +35,9 @@ export function UserQuickTools({ width: '100vw', } : { - opacity: isResizingSidebar ? '0%' : '100%', - transition: isResizingSidebar ? 'opacity 0.2s ease' : 'opacity 0.5s ease', width: toRem(width ?? 100), position: 'absolute', - left: toRem(-66), + right: '0', } } > diff --git a/src/app/pages/client/space/Space.tsx b/src/app/pages/client/space/Space.tsx index 60aefa740..1a6fd24ab 100644 --- a/src/app/pages/client/space/Space.tsx +++ b/src/app/pages/client/space/Space.tsx @@ -109,6 +109,7 @@ import { ModalWide } from '$styles/Modal.css'; import { ImageViewer } from '$components/image-viewer'; import * as css from './styles.css'; import { isResizingSidebarAtom } from '$state/isResizingSidebar'; +import { UserQuickTools } from '../sidebar/UserQuickTools'; const debugLog = createDebugLogger('Space'); @@ -859,6 +860,8 @@ export function Space() { const screenSize = useScreenSizeContext(); const isMobile = screenSize === ScreenSize.Mobile; const hideText = curWidth <= 80 && !isMobile; + const [oldSidebar] = useSetting(settingsAtom, 'oldSidebar'); + return ( )} + {!oldSidebar && !isMobile && } ); } diff --git a/src/app/pages/paths.ts b/src/app/pages/paths.ts index 2fd16b54f..f465dd25e 100644 --- a/src/app/pages/paths.ts +++ b/src/app/pages/paths.ts @@ -80,7 +80,7 @@ export const EXPLORE_SERVER_PATH = `/explore/${SERVER_PATH_SEGMENT}`; export const CREATE_PATH = '/create'; export const NAVIGATE_PATH = '/navigate'; -export const PROFILE_PATH = '/profile'; +export const PROFILE_PATH = '/profile/'; export const NOTIFICATIONS_PATH_SEGMENT = 'notifications/'; export const INVITES_PATH_SEGMENT = 'invites/';