Allow more servers to be added to the list in the explore tab

This commit is contained in:
Ginger 2025-03-10 11:21:14 -04:00
parent d8009978e5
commit 25cfdbc6ff
3 changed files with 125 additions and 53 deletions

View file

@ -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<InCinnyExploreServersContent>()
?.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];
};

View file

@ -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 { useNavigate } from 'react-router-dom';
import FocusTrap from 'focus-trap-react'; import FocusTrap from 'focus-trap-react';
import { import {
@ -18,11 +18,13 @@ import {
color, color,
config, config,
} from 'folds'; } from 'folds';
import { useFocusWithin, useHover } from 'react-aria';
import { import {
NavCategory, NavCategory,
NavCategoryHeader, NavCategoryHeader,
NavItem, NavItem,
NavItemContent, NavItemContent,
NavItemOptions,
NavLink, NavLink,
} from '../../../components/nav'; } from '../../../components/nav';
import { getExploreFeaturedPath, getExploreServerPath } from '../../pathUtils'; import { getExploreFeaturedPath, getExploreServerPath } from '../../pathUtils';
@ -37,11 +39,13 @@ import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback';
import { useNavToActivePathMapper } from '../../../hooks/useNavToActivePathMapper'; import { useNavToActivePathMapper } from '../../../hooks/useNavToActivePathMapper';
import { PageNav, PageNavContent, PageNavHeader } from '../../../components/page'; import { PageNav, PageNavContent, PageNavHeader } from '../../../components/page';
import { stopPropagation } from '../../../utils/keyboard'; import { stopPropagation } from '../../../utils/keyboard';
import { useExploreServers } from '../../../hooks/useExploreServers';
export function AddServer() { export function AddExploreServerPrompt() {
const mx = useMatrixClient(); const mx = useMatrixClient();
const navigate = useNavigate(); const navigate = useNavigate();
const [dialog, setDialog] = useState(false); const [dialog, setDialog] = useState(false);
const [, setExploreServers] = useExploreServers();
const serverInputRef = useRef<HTMLInputElement>(null); const serverInputRef = useRef<HTMLInputElement>(null);
const [exploreState] = useAsyncCallback( const [exploreState] = useAsyncCallback(
@ -55,22 +59,14 @@ export function AddServer() {
return server || undefined; return server || undefined;
}; };
const handleSubmit: FormEventHandler<HTMLFormElement> = (evt) => { const handleSubmit = useCallback(() => {
evt.preventDefault();
const server = getInputServer(); const server = getInputServer();
if (!server) return; if (!server) return;
// explore(server);
navigate(getExploreServerPath(server));
setDialog(false); setDialog(false);
}; setExploreServers({ type: 'APPEND', server });
const handleView = () => {
const server = getInputServer();
if (!server) return;
navigate(getExploreServerPath(server)); navigate(getExploreServerPath(server));
setDialog(false); }, [navigate, setExploreServers]);
};
return ( return (
<> <>
@ -102,7 +98,10 @@ export function AddServer() {
</Header> </Header>
<Box <Box
as="form" as="form"
onSubmit={handleSubmit} onSubmit={(evt) => {
evt.preventDefault();
handleSubmit();
}}
style={{ padding: config.space.S400 }} style={{ padding: config.space.S400 }}
direction="Column" direction="Column"
gap="400" gap="400"
@ -131,7 +130,7 @@ export function AddServer() {
<Text size="B400">Save</Text> <Text size="B400">Save</Text>
</Button> */} </Button> */}
<Button type="submit" onClick={handleView} variant="Secondary" fill="Soft"> <Button type="submit" onClick={handleSubmit} variant="Secondary" fill="Soft">
<Text size="B400">View</Text> <Text size="B400">View</Text>
</Button> </Button>
</Box> </Box>
@ -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 (
<NavItem
variant="Background"
radii="400"
aria-selected={selected}
{...hoverProps}
{...focusWithinProps}
>
<NavLink to={getExploreServerPath(server)}>
<NavItemContent>
<Box as="span" grow="Yes" alignItems="Center" gap="200">
<Avatar size="200" radii="400">
<Icon src={Icons.Category} size="100" filled={selected} />
</Avatar>
<Box as="span" grow="Yes">
<Text as="span" size="Inherit" truncate>
{server}
</Text>
</Box>
</Box>
</NavItemContent>
</NavLink>
{onRemove !== null && hover && (
<NavItemOptions>
<IconButton onClick={onRemove} variant="Background" fill="None" size="300" radii="300">
<Icon size="50" src={Icons.Minus} />
</IconButton>
</NavItemOptions>
)}
</NavItem>
);
}
export function Explore() { export function Explore() {
const mx = useMatrixClient(); const mx = useMatrixClient();
useNavToActivePathMapper('explore'); useNavToActivePathMapper('explore');
const userId = mx.getUserId(); const userId = mx.getUserId();
const clientConfig = useClientConfig(); const clientConfig = useClientConfig();
const userServer = userId ? getMxIdServer(userId) : undefined; const userServer = userId ? getMxIdServer(userId) : undefined;
const servers = const featuredServers =
clientConfig.featuredCommunities?.servers?.filter((server) => server !== userServer) ?? []; clientConfig.featuredCommunities?.servers?.filter((server) => server !== userServer) ?? [];
const [exploreServers, setExploreServers] = useExploreServers();
const featuredSelected = useExploreFeaturedSelected(); const featuredSelected = useExploreFeaturedSelected();
const selectedServer = useExploreServer(); const selectedServer = useExploreServer();
@ -225,44 +272,30 @@ export function Explore() {
</NavItem> </NavItem>
)} )}
</NavCategory> </NavCategory>
{servers.length > 0 && (
<NavCategory> <NavCategory>
<NavCategoryHeader> <NavCategoryHeader>
<Text size="O400" style={{ paddingLeft: config.space.S200 }}> <Text size="O400" style={{ paddingLeft: config.space.S200 }}>
Servers Servers
</Text> </Text>
</NavCategoryHeader> </NavCategoryHeader>
{servers.map((server) => ( {featuredServers.map((server) => (
<NavItem <ExploreServerNavItem
key={server} key={server}
variant="Background" server={server}
radii="400" selected={server === selectedServer}
aria-selected={server === selectedServer} />
> ))}
<NavLink to={getExploreServerPath(server)}> {exploreServers.map((server) => (
<NavItemContent> <ExploreServerNavItem
<Box as="span" grow="Yes" alignItems="Center" gap="200"> key={server}
<Avatar size="200" radii="400"> server={server}
<Icon selected={server === selectedServer}
src={Icons.Category} onRemove={() => setExploreServers({ type: 'DELETE', server })}
size="100"
filled={server === selectedServer}
/> />
</Avatar>
<Box as="span" grow="Yes">
<Text as="span" size="Inherit" truncate>
{server}
</Text>
</Box>
</Box>
</NavItemContent>
</NavLink>
</NavItem>
))} ))}
</NavCategory> </NavCategory>
)}
<Box direction="Column"> <Box direction="Column">
<AddServer /> <AddExploreServerPrompt />
</Box> </Box>
</Box> </Box>
</PageNavContent> </PageNavContent>

View file

@ -4,6 +4,7 @@ export enum AccountDataEvent {
IgnoredUserList = 'm.ignored_user_list', IgnoredUserList = 'm.ignored_user_list',
CinnySpaces = 'in.cinny.spaces', CinnySpaces = 'in.cinny.spaces',
CinnyExploreServers = 'in.cinny.explore_servers',
ElementRecentEmoji = 'io.element.recent_emoji', ElementRecentEmoji = 'io.element.recent_emoji',