From c45cb62e2d4772cdad6a75295bfac34ab57242c0 Mon Sep 17 00:00:00 2001 From: Ginger Date: Fri, 5 Sep 2025 12:14:02 -0400 Subject: [PATCH] Use consistent "bookmark" wording in code and UI --- src/app/hooks/useBookmarkedServers.ts | 43 +++++++ src/app/hooks/useExploreServers.ts | 43 ------- src/app/pages/client/explore/Explore.tsx | 135 +++++++++++----------- src/app/pages/client/explore/Featured.tsx | 2 +- src/app/pages/client/explore/Server.tsx | 77 +++--------- src/types/matrix/accountData.ts | 2 +- 6 files changed, 129 insertions(+), 173 deletions(-) create mode 100644 src/app/hooks/useBookmarkedServers.ts delete mode 100644 src/app/hooks/useExploreServers.ts diff --git a/src/app/hooks/useBookmarkedServers.ts b/src/app/hooks/useBookmarkedServers.ts new file mode 100644 index 00000000..fe49f974 --- /dev/null +++ b/src/app/hooks/useBookmarkedServers.ts @@ -0,0 +1,43 @@ +import { useCallback, useMemo } from 'react'; +import { AccountDataEvent } from '../../types/matrix/accountData'; +import { useAccountData } from './useAccountData'; +import { useMatrixClient } from './useMatrixClient'; + +export type InCinnyBookmarkedServersContent = { + servers?: string[]; +}; + +export const useBookmarkedServers = (): [ + string[], + (server: string) => Promise, + (server: string) => Promise +] => { + const mx = useMatrixClient(); + const accountData = useAccountData(AccountDataEvent.CinnyBookmarkedServers); + const bookmarkedServers = useMemo( + () => accountData?.getContent()?.servers ?? [], + [accountData] + ); + + const addServerBookmark = useCallback( + async (server: string) => { + if (bookmarkedServers.indexOf(server) === -1) { + await mx.setAccountData(AccountDataEvent.CinnyBookmarkedServers, { + servers: [...bookmarkedServers, server], + }); + } + }, + [mx, bookmarkedServers] + ); + + const removeServerBookmark = useCallback( + async (server: string) => { + await mx.setAccountData(AccountDataEvent.CinnyBookmarkedServers, { + servers: bookmarkedServers.filter((addedServer) => server !== addedServer), + }); + }, + [mx, bookmarkedServers] + ); + + return [bookmarkedServers, addServerBookmark, removeServerBookmark]; +}; diff --git a/src/app/hooks/useExploreServers.ts b/src/app/hooks/useExploreServers.ts deleted file mode 100644 index cc27baec..00000000 --- a/src/app/hooks/useExploreServers.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { useCallback, useMemo } from 'react'; -import { AccountDataEvent } from '../../types/matrix/accountData'; -import { useAccountData } from './useAccountData'; -import { useMatrixClient } from './useMatrixClient'; - -export type InCinnyExploreServersContent = { - servers?: string[]; -}; - -export const useExploreServers = (): [ - string[], - (server: string) => Promise, - (server: string) => Promise -] => { - const mx = useMatrixClient(); - const accountData = useAccountData(AccountDataEvent.CinnyExplore); - const userAddedServers = useMemo( - () => accountData?.getContent()?.servers ?? [], - [accountData] - ); - - const addServer = useCallback( - async (server: string) => { - if (userAddedServers.indexOf(server) === -1) { - await mx.setAccountData(AccountDataEvent.CinnyExplore, { - servers: [...userAddedServers, server], - }); - } - }, - [mx, userAddedServers] - ); - - const removeServer = useCallback( - async (server: string) => { - await mx.setAccountData(AccountDataEvent.CinnyExplore, { - servers: userAddedServers.filter((addedServer) => server !== addedServer), - }); - }, - [mx, userAddedServers] - ); - - return [userAddedServers, addServer, removeServer]; -}; diff --git a/src/app/pages/client/explore/Explore.tsx b/src/app/pages/client/explore/Explore.tsx index 71138fc1..644665b4 100644 --- a/src/app/pages/client/explore/Explore.tsx +++ b/src/app/pages/client/explore/Explore.tsx @@ -41,21 +41,21 @@ import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback'; import { useNavToActivePathMapper } from '../../../hooks/useNavToActivePathMapper'; import { PageNav, PageNavContent, PageNavHeader } from '../../../components/page'; import { stopPropagation } from '../../../utils/keyboard'; -import { useExploreServers } from '../../../hooks/useExploreServers'; +import { useBookmarkedServers } from '../../../hooks/useBookmarkedServers'; import { useAlive } from '../../../hooks/useAlive'; -type AddExploreServerPromptProps = { +type ExploreServerPromptProps = { onSubmit: (server: string, save: boolean) => Promise; header: ReactNode; children: ReactNode; selected?: boolean; }; -export function AddExploreServerPrompt({ +export function ExploreServerPrompt({ onSubmit, header, children, selected = false, -}: AddExploreServerPromptProps) { +}: ExploreServerPromptProps) { const mx = useMatrixClient(); const [dialog, setDialog] = useState(false); const alive = useAlive(); @@ -68,13 +68,13 @@ export function AddExploreServerPrompt({ return server || undefined; }; - const submit = useCallback( - async (save: boolean) => { + const handleSubmit = useCallback( + async (saveBookmark: boolean) => { const server = getInputServer(); if (!server) return; await mx.publicRooms({ server, limit: 1 }); - await onSubmit(server, save); + await onSubmit(server, saveBookmark); if (alive()) { setDialog(false); } @@ -82,10 +82,12 @@ export function AddExploreServerPrompt({ [alive, onSubmit, mx] ); - const [viewState, handleView] = useAsyncCallback(() => submit(false)); - const [saveViewState, handleSaveView] = useAsyncCallback(() => submit(true)); + const [viewState, handleView] = useAsyncCallback(() => handleSubmit(false)); + const [saveViewState, handleSaveView] = useAsyncCallback(() => handleSubmit(true)); const busy = viewState.status === AsyncStatus.Loading || saveViewState.status === AsyncStatus.Loading; + const failed = + viewState.status === AsyncStatus.Error || saveViewState.status === AsyncStatus.Error; return ( <> @@ -113,46 +115,46 @@ export function AddExploreServerPrompt({ - + Add server name to explore public communities. Server Name - {viewState.status === AsyncStatus.Error && ( + {failed && ( Failed to load public rooms. Please try again. )} - - - - + + + + @@ -171,6 +173,7 @@ export function AddExploreServerPrompt({ type ExploreServerNavItemAction = { onClick: () => Promise; icon: IconSrc; + filled?: boolean; alwaysVisible: boolean; }; type ExploreServerNavItemProps = { @@ -222,16 +225,17 @@ export function ExploreServerNavItem({ {actionInProgress ? ( - + ) : ( - + )} @@ -246,7 +250,7 @@ export function Explore() { useNavToActivePathMapper('explore'); const userId = mx.getUserId(); const userServer = userId ? getMxIdServer(userId) : undefined; - const [exploreServers, addServer, removeServer] = useExploreServers(); + const [bookmarkedServers, addServerBookmark, removeServerBookmark] = useBookmarkedServers(); const selectedServer = useExploreServer(); const exploringFeaturedRooms = useExploreFeaturedRooms(); @@ -255,26 +259,26 @@ export function Explore() { !( selectedServer === undefined || selectedServer === userServer || - exploreServers.includes(selectedServer) + bookmarkedServers.includes(selectedServer) ), - [exploreServers, selectedServer, userServer] + [bookmarkedServers, selectedServer, userServer] ); - const addServerCallback = useCallback( - async (server: string, save: boolean) => { - if (save && server !== userServer && selectedServer) { - await addServer(server); + const viewServerCallback = useCallback( + async (server: string, saveBookmark: boolean) => { + if (saveBookmark && server !== userServer && selectedServer) { + await addServerBookmark(server); } navigate(getExploreServerPath(server)); }, - [addServer, navigate, userServer, selectedServer] + [addServerBookmark, navigate, userServer, selectedServer] ); - const removeServerCallback = useCallback( + const removeServerBookmarkCallback = useCallback( async (server: string) => { - await removeServer(server); + await removeServerBookmark(server); }, - [removeServer] + [removeServerBookmark] ); return ( @@ -319,11 +323,11 @@ export function Explore() { addServerCallback(selectedServer, true), + icon: Icons.Bookmark, + onClick: () => viewServerCallback(selectedServer, true), }} /> )} @@ -331,10 +335,10 @@ export function Explore() { - Servers + Bookmarks - {exploreServers.map((server) => ( + {bookmarkedServers.map((server) => ( removeServerCallback(server), + icon: Icons.Bookmark, + filled: true, + onClick: () => removeServerBookmarkCallback(server), }} /> ))} - Add Server} > @@ -361,7 +366,7 @@ export function Explore() { - + diff --git a/src/app/pages/client/explore/Featured.tsx b/src/app/pages/client/explore/Featured.tsx index 2c136a52..19b18dc9 100644 --- a/src/app/pages/client/explore/Featured.tsx +++ b/src/app/pages/client/explore/Featured.tsx @@ -62,7 +62,7 @@ function ServerCard({ serverName, onExplore }: ServerCardProps) { diff --git a/src/app/pages/client/explore/Server.tsx b/src/app/pages/client/explore/Server.tsx index b0a8c54f..b26522c0 100644 --- a/src/app/pages/client/explore/Server.tsx +++ b/src/app/pages/client/explore/Server.tsx @@ -48,7 +48,7 @@ import { stopPropagation } from '../../../utils/keyboard'; import { ScreenSize, useScreenSizeContext } from '../../../hooks/useScreenSize'; import { BackRouteHandler } from '../../../components/BackRouteHandler'; import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback'; -import { useExploreServers } from '../../../hooks/useExploreServers'; +import { useBookmarkedServers } from '../../../hooks/useBookmarkedServers'; const useServerSearchParams = (searchParams: URLSearchParams): ExploreServerPathSearchParams => useMemo( @@ -352,7 +352,6 @@ export function PublicRooms() { const allRooms = useAtomValue(allRoomsAtom); const { navigateSpace, navigateRoom } = useRoomNavigate(); const screenSize = useScreenSizeContext(); - const [menuAnchor, setMenuAnchor] = useState(); const [searchParams] = useSearchParams(); const serverSearchParams = useServerSearchParams(searchParams); @@ -361,9 +360,9 @@ export function PublicRooms() { const searchInputRef = useRef(null); const navigate = useNavigate(); const roomTypeFilters = useRoomTypeFilters(); - const [exploreServers, addServer, removeServer] = useExploreServers(); - const isUserHomeserver = server && server === userServer; - const isBookmarkedServer = server && exploreServers.includes(server); + const [bookmarkedServers, addServerBookmark, removeServerBookmark] = useBookmarkedServers(); + const isUserHomeserver = server !== undefined && server === userServer; + const isBookmarkedServer = server !== undefined && bookmarkedServers.includes(server); const currentLimit: number = useMemo(() => { const limitParam = serverSearchParams.limit; @@ -476,22 +475,17 @@ export function PublicRooms() { explore({ instance: instanceId, since: undefined }); }; - const handleOpenMenu: MouseEventHandler = (evt) => { - setMenuAnchor(evt.currentTarget.getBoundingClientRect()); - }; - - const [menuActionState, handleMenuAction] = useAsyncCallback( + const [bookmarkActionState, handleBookmarkAction] = useAsyncCallback( useCallback( async (action: (server: string) => Promise) => { if (!server) return; - setMenuAnchor(undefined); await action(server); }, [server] ) ); - const menuActionBusy = menuActionState.status === AsyncStatus.Loading; + const bookmarkActionLoading = bookmarkActionState.status === AsyncStatus.Loading; return ( @@ -546,69 +540,26 @@ export function PublicRooms() { offset={4} tooltip={ - More Options + {isBookmarkedServer ? 'Remove Bookmark' : 'Add Bookmark'} } > {(triggerRef) => !isUserHomeserver && ( + handleBookmarkAction( + isBookmarkedServer ? removeServerBookmark : addServerBookmark + ) + } ref={triggerRef} - aria-pressed={!!menuAnchor} + disabled={bookmarkActionLoading} > - + ) } - setMenuAnchor(undefined), - clickOutsideDeactivates: true, - isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown', - isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp', - escapeDeactivates: stopPropagation, - }} - > - - - - handleMenuAction(isBookmarkedServer ? removeServer : addServer) - } - variant={isBookmarkedServer ? 'Critical' : 'Primary'} - fill="None" - size="300" - after={ - menuActionBusy ? ( - - ) : ( - - ) - } - radii="300" - disabled={menuActionBusy} - > - - {isBookmarkedServer ? 'Remove Server' : 'Add Server'} - - - - - - } - /> )} diff --git a/src/types/matrix/accountData.ts b/src/types/matrix/accountData.ts index b9d81492..4bb6f5c2 100644 --- a/src/types/matrix/accountData.ts +++ b/src/types/matrix/accountData.ts @@ -4,7 +4,7 @@ export enum AccountDataEvent { IgnoredUserList = 'm.ignored_user_list', CinnySpaces = 'in.cinny.spaces', - CinnyExplore = 'in.cinny.explore', + CinnyBookmarkedServers = 'in.cinny.bookmarked_servers', ElementRecentEmoji = 'io.element.recent_emoji',