Hide bookmark entries for featured servers

This commit is contained in:
Ginger 2025-09-13 15:12:10 -04:00
parent be51dc5f12
commit 2f42ca99fc
No known key found for this signature in database
2 changed files with 85 additions and 84 deletions

View file

@ -7,16 +7,17 @@ export type InCinnyExploreServersContent = {
servers?: string[];
};
export const useExploreServers = (): [
string[],
(server: string) => Promise<void>,
(server: string) => Promise<void>
] => {
export const useExploreServers = (
exclude?: string[]
): [string[], (server: string) => Promise<void>, (server: string) => Promise<void>] => {
const mx = useMatrixClient();
const accountData = useAccountData(AccountDataEvent.CinnyExplore);
const userAddedServers = useMemo(
() => accountData?.getContent<InCinnyExploreServersContent>()?.servers ?? [],
[accountData]
() =>
accountData
?.getContent<InCinnyExploreServersContent>()
?.servers?.filter((server) => !exclude?.includes(server)) ?? [],
[exclude, accountData]
);
const addServer = useCallback(

View file

@ -31,7 +31,6 @@ import {
NavLink,
} from '../../../components/nav';
import { getExploreFeaturedPath, getExploreServerPath } from '../../pathUtils';
import { useClientConfig } from '../../../hooks/useClientConfig';
import {
useExploreFeaturedRooms,
useExploreServer,
@ -44,9 +43,10 @@ import { PageNav, PageNavContent, PageNavHeader } from '../../../components/page
import { stopPropagation } from '../../../utils/keyboard';
import { useExploreServers } from '../../../hooks/useExploreServers';
import { useAlive } from '../../../hooks/useAlive';
import { useClientConfig } from '../../../hooks/useClientConfig';
type AddExploreServerPromptProps = {
onSubmit: (server: string) => Promise<void>;
onSubmit: (server: string, save: boolean) => Promise<void>;
header: ReactNode;
children: ReactNode;
selected?: boolean;
@ -69,19 +69,25 @@ export function AddExploreServerPrompt({
return server || undefined;
};
const [submitState, handleSubmit] = useAsyncCallback(
useCallback(async () => {
const submit = useCallback(
async (save: boolean) => {
const server = getInputServer();
if (!server) return;
await mx.publicRooms({ server, limit: 1 });
await onSubmit(server);
await onSubmit(server, save);
if (alive()) {
setDialog(false);
}
}, [alive, onSubmit, mx])
},
[alive, onSubmit, mx]
);
const [viewState, handleView] = useAsyncCallback(() => submit(false));
const [saveViewState, handleSaveView] = useAsyncCallback(() => submit(true));
const busy =
viewState.status === AsyncStatus.Loading || saveViewState.status === AsyncStatus.Loading;
return (
<>
<Overlay open={dialog} backdrop={<OverlayBackdrop />}>
@ -108,21 +114,12 @@ export function AddExploreServerPrompt({
<Icon src={Icons.Cross} />
</IconButton>
</Header>
<Box
as="form"
onSubmit={(evt) => {
evt.preventDefault();
handleSubmit();
}}
style={{ padding: config.space.S400 }}
direction="Column"
gap="400"
>
<Box style={{ padding: config.space.S400 }} direction="Column" gap="400">
<Text priority="400">Add server name to explore public communities.</Text>
<Box direction="Column" gap="100">
<Text size="L400">Server Name</Text>
<Input ref={serverInputRef} name="serverInput" variant="Background" required />
{submitState.status === AsyncStatus.Error && (
{viewState.status === AsyncStatus.Error && (
<Text style={{ color: color.Critical.Main }} size="T300">
Failed to load public rooms. Please try again.
</Text>
@ -131,18 +128,32 @@ export function AddExploreServerPrompt({
<Box direction="Column" gap="200">
<Button
type="submit"
onClick={handleSubmit}
onClick={handleView}
variant="Secondary"
fill="Soft"
before={
submitState.status === AsyncStatus.Loading && (
viewState.status === AsyncStatus.Loading && (
<Spinner fill="Solid" variant="Secondary" size="200" />
)
}
disabled={submitState.status === AsyncStatus.Loading}
disabled={busy}
>
<Text size="B400">View</Text>
</Button>
<Button
type="submit"
onClick={handleSaveView}
variant="Primary"
fill="Soft"
before={
saveViewState.status === AsyncStatus.Loading && (
<Spinner fill="Solid" variant="Secondary" size="200" />
)
}
disabled={busy}
>
<Text size="B400">Save & View</Text>
</Button>
</Box>
</Box>
</Dialog>
@ -158,30 +169,33 @@ export function AddExploreServerPrompt({
);
}
type ExploreServerNavItemAction = {
onClick: () => Promise<void>;
icon: IconSrc;
alwaysVisible: boolean;
};
type ExploreServerNavItemProps = {
server: string;
selected: boolean;
icon: IconSrc;
onRemove?: (() => Promise<void>) | null;
action?: ExploreServerNavItemAction;
};
export function ExploreServerNavItem({
server,
selected,
icon,
onRemove = null,
action,
}: ExploreServerNavItemProps) {
const [hover, setHover] = useState(false);
const { hoverProps } = useHover({ onHoverChange: setHover });
const { focusWithinProps } = useFocusWithin({ onFocusWithinChange: setHover });
const [removeState, removeCallback] = useAsyncCallback(
const [actionState, actionCallback] = useAsyncCallback(
useCallback(async () => {
if (onRemove !== null) {
await onRemove();
}
}, [onRemove])
await action?.onClick();
}, [action])
);
const removeInProgress =
removeState.status === AsyncStatus.Loading || removeState.status === AsyncStatus.Success;
const actionInProgress =
actionState.status === AsyncStatus.Loading || actionState.status === AsyncStatus.Success;
return (
<NavItem
@ -205,20 +219,20 @@ export function ExploreServerNavItem({
</Box>
</NavItemContent>
</NavLink>
{onRemove !== null && (hover || removeInProgress) && (
{action !== undefined && (hover || actionInProgress || action.alwaysVisible) && (
<NavItemOptions>
<IconButton
onClick={removeCallback}
onClick={actionCallback}
variant="Background"
fill="None"
size="300"
radii="300"
disabled={removeInProgress}
disabled={actionInProgress}
>
{removeInProgress ? (
{actionInProgress ? (
<Spinner variant="Secondary" fill="Solid" size="200" />
) : (
<Icon size="50" src={Icons.Minus} />
<Icon size="50" src={action.icon} />
)}
</IconButton>
</NavItemOptions>
@ -239,7 +253,7 @@ export function Explore() {
clientConfig.featuredCommunities?.servers?.filter((server) => server !== userServer) ?? [],
[clientConfig, userServer]
);
const [exploreServers, addServer, removeServer] = useExploreServers();
const [exploreServers, addServer, removeServer] = useExploreServers(featuredServers);
const selectedServer = useExploreServer();
const exploringFeaturedRooms = useExploreFeaturedRooms();
@ -251,12 +265,12 @@ export function Explore() {
featuredServers.includes(selectedServer) ||
exploreServers.includes(selectedServer)
),
[exploreServers, featuredServers, selectedServer, userServer]
[exploreServers, selectedServer, userServer, featuredServers]
);
const addServerCallback = useCallback(
async (server: string) => {
if (server !== userServer && selectedServer && !featuredServers.includes(selectedServer)) {
async (server: string, save: boolean) => {
if (save && server !== userServer && !featuredServers.includes(server) && selectedServer) {
await addServer(server);
}
navigate(getExploreServerPath(server));
@ -271,13 +285,6 @@ export function Explore() {
[removeServer]
);
const exploreUnlistedServerCallback = useCallback(
async (server: string) => {
navigate(getExploreServerPath(server));
},
[navigate]
);
return (
<PageNav>
<PageNavHeader>
@ -293,29 +300,6 @@ export function Explore() {
<PageNavContent>
<Box direction="Column" gap="300">
<NavCategory>
<AddExploreServerPrompt
onSubmit={exploreUnlistedServerCallback}
header={<Text size="H4">View Server</Text>}
selected={exploringUnlistedServer}
>
<Box as="span" grow="Yes" alignItems="Center" gap="200">
<Avatar size="200" radii="400">
<Icon src={Icons.Link} size="100" />
</Avatar>
<Box as="span" grow="Yes">
<Text as="span" size="Inherit" truncate>
Explore with Address
</Text>
</Box>
</Box>
</AddExploreServerPrompt>
</NavCategory>
<NavCategory>
<NavCategoryHeader>
<Text size="O400" style={{ paddingLeft: config.space.S200 }}>
Featured
</Text>
</NavCategoryHeader>
<NavItem variant="Background" radii="400" aria-selected={exploringFeaturedRooms}>
<NavLink to={getExploreFeaturedPath()}>
<NavItemContent>
@ -325,7 +309,7 @@ export function Explore() {
</Avatar>
<Box as="span" grow="Yes">
<Text as="span" size="Inherit" truncate>
Featured Rooms
Featured
</Text>
</Box>
</Box>
@ -339,6 +323,25 @@ export function Explore() {
icon={Icons.Home}
/>
)}
{exploringUnlistedServer && selectedServer !== undefined && (
<ExploreServerNavItem
server={selectedServer}
selected
icon={Icons.Server}
action={{
alwaysVisible: true,
icon: Icons.Plus,
onClick: () => addServerCallback(selectedServer, true),
}}
/>
)}
</NavCategory>
<NavCategory>
<NavCategoryHeader>
<Text size="O400" style={{ paddingLeft: config.space.S200 }}>
Servers
</Text>
</NavCategoryHeader>
{featuredServers.map((server) => (
<ExploreServerNavItem
key={server}
@ -347,20 +350,17 @@ export function Explore() {
icon={Icons.Server}
/>
))}
</NavCategory>
<NavCategory>
<NavCategoryHeader>
<Text size="O400" style={{ paddingLeft: config.space.S200 }}>
Servers
</Text>
</NavCategoryHeader>
{exploreServers.map((server) => (
<ExploreServerNavItem
key={server}
server={server}
selected={server === selectedServer}
onRemove={() => removeServerCallback(server)}
icon={Icons.Server}
action={{
alwaysVisible: false,
icon: Icons.Minus,
onClick: () => removeServerCallback(server),
}}
/>
))}
<AddExploreServerPrompt