diff --git a/src/app/hooks/useExploreServers.ts b/src/app/hooks/useExploreServers.ts new file mode 100644 index 00000000..a5e293d4 --- /dev/null +++ b/src/app/hooks/useExploreServers.ts @@ -0,0 +1,38 @@ +import { AccountDataEvent } from '../../types/matrix/accountData'; +import { useAccountData } from './useAccountData'; +import { useMatrixClient } from './useMatrixClient'; + +export type InCinnyExploreServersContent = { + servers?: string[]; +}; + +export type ExploreServerListAction = + | { + type: 'APPEND'; + server: string; + } + | { + type: 'DELETE'; + server: string; + }; + +export const useExploreServers = (): [string[], (action: ExploreServerListAction) => void] => { + const mx = useMatrixClient(); + const userAddedServers = + useAccountData(AccountDataEvent.CinnyExploreServers)?.getContent() + ?.servers ?? []; + + const setUserAddedServers = (action: ExploreServerListAction) => { + if (action.type === 'APPEND') { + mx.setAccountData(AccountDataEvent.CinnyExploreServers, { + servers: [...userAddedServers, action.server], + }); + } else if (action.type === 'DELETE') { + mx.setAccountData(AccountDataEvent.CinnyExploreServers, { + servers: userAddedServers.filter((server) => server !== action.server), + }); + } + }; + + return [userAddedServers, setUserAddedServers]; +}; diff --git a/src/app/pages/client/explore/Explore.tsx b/src/app/pages/client/explore/Explore.tsx index 420e1a16..f42c6a6a 100644 --- a/src/app/pages/client/explore/Explore.tsx +++ b/src/app/pages/client/explore/Explore.tsx @@ -1,4 +1,4 @@ -import React, { FormEventHandler, useCallback, useRef, useState } from 'react'; +import React, { useCallback, useRef, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import FocusTrap from 'focus-trap-react'; import { @@ -18,11 +18,13 @@ import { color, config, } from 'folds'; +import { useFocusWithin, useHover } from 'react-aria'; import { NavCategory, NavCategoryHeader, NavItem, NavItemContent, + NavItemOptions, NavLink, } from '../../../components/nav'; import { getExploreFeaturedPath, getExploreServerPath } from '../../pathUtils'; @@ -37,11 +39,13 @@ 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'; -export function AddServer() { +export function AddExploreServerPrompt() { const mx = useMatrixClient(); const navigate = useNavigate(); const [dialog, setDialog] = useState(false); + const [, setExploreServers] = useExploreServers(); const serverInputRef = useRef(null); const [exploreState] = useAsyncCallback( @@ -55,22 +59,14 @@ export function AddServer() { return server || undefined; }; - const handleSubmit: FormEventHandler = (evt) => { - evt.preventDefault(); + const handleSubmit = useCallback(() => { const server = getInputServer(); if (!server) return; - // explore(server); - navigate(getExploreServerPath(server)); setDialog(false); - }; - - const handleView = () => { - const server = getInputServer(); - if (!server) return; + setExploreServers({ type: 'APPEND', server }); navigate(getExploreServerPath(server)); - setDialog(false); - }; + }, [navigate, setExploreServers]); return ( <> @@ -102,7 +98,10 @@ export function AddServer() { { + evt.preventDefault(); + handleSubmit(); + }} style={{ padding: config.space.S400 }} direction="Column" gap="400" @@ -131,7 +130,7 @@ export function AddServer() { Save */} - @@ -155,14 +154,62 @@ export function AddServer() { ); } +type ExploreServerNavItemProps = { + server: string; + selected: boolean; + onRemove?: (() => void) | null; +}; +export function ExploreServerNavItem({ + server, + selected, + onRemove = null, +}: ExploreServerNavItemProps) { + const [hover, setHover] = useState(false); + const { hoverProps } = useHover({ onHoverChange: setHover }); + const { focusWithinProps } = useFocusWithin({ onFocusWithinChange: setHover }); + + return ( + + + + + + + + + + {server} + + + + + + {onRemove !== null && hover && ( + + + + + + )} + + ); +} + export function Explore() { const mx = useMatrixClient(); useNavToActivePathMapper('explore'); const userId = mx.getUserId(); const clientConfig = useClientConfig(); const userServer = userId ? getMxIdServer(userId) : undefined; - const servers = + const featuredServers = clientConfig.featuredCommunities?.servers?.filter((server) => server !== userServer) ?? []; + const [exploreServers, setExploreServers] = useExploreServers(); const featuredSelected = useExploreFeaturedSelected(); const selectedServer = useExploreServer(); @@ -225,44 +272,30 @@ export function Explore() { )} - {servers.length > 0 && ( - - - - Servers - - - {servers.map((server) => ( - - - - - - - - - - {server} - - - - - - - ))} - - )} + + + + Servers + + + {featuredServers.map((server) => ( + + ))} + {exploreServers.map((server) => ( + setExploreServers({ type: 'DELETE', server })} + /> + ))} + - + diff --git a/src/types/matrix/accountData.ts b/src/types/matrix/accountData.ts index 20ce9419..ba5a07f4 100644 --- a/src/types/matrix/accountData.ts +++ b/src/types/matrix/accountData.ts @@ -4,6 +4,7 @@ export enum AccountDataEvent { IgnoredUserList = 'm.ignored_user_list', CinnySpaces = 'in.cinny.spaces', + CinnyExploreServers = 'in.cinny.explore_servers', ElementRecentEmoji = 'io.element.recent_emoji',