Show a loading spinner while removing a server

This commit is contained in:
Ginger 2025-03-18 09:22:52 -04:00
parent a374a5fb94
commit 4c126e47b6
2 changed files with 61 additions and 35 deletions

View file

@ -1,3 +1,4 @@
import { useCallback, useMemo } from 'react';
import { AccountDataEvent } from '../../types/matrix/accountData'; import { AccountDataEvent } from '../../types/matrix/accountData';
import { useAccountData } from './useAccountData'; import { useAccountData } from './useAccountData';
import { useMatrixClient } from './useMatrixClient'; import { useMatrixClient } from './useMatrixClient';
@ -6,33 +7,37 @@ export type InCinnyExploreServersContent = {
servers?: string[]; servers?: string[];
}; };
export type ExploreServerListAction = export const useExploreServers = (): [
| { string[],
type: 'APPEND'; (server: string) => Promise<void>,
server: string; (server: string) => Promise<void>
} ] => {
| {
type: 'DELETE';
server: string;
};
export const useExploreServers = (): [string[], (action: ExploreServerListAction) => void] => {
const mx = useMatrixClient(); const mx = useMatrixClient();
const userAddedServers = const accountData = useAccountData(AccountDataEvent.CinnyExploreServers);
useAccountData(AccountDataEvent.CinnyExploreServers)?.getContent<InCinnyExploreServersContent>() const userAddedServers = useMemo(
?.servers ?? []; () => accountData?.getContent<InCinnyExploreServersContent>()?.servers ?? [],
[accountData]
);
const setUserAddedServers = (action: ExploreServerListAction) => { const addServer = useCallback(
if (action.type === 'APPEND') { async (server: string) => {
mx.setAccountData(AccountDataEvent.CinnyExploreServers, { if (userAddedServers.indexOf(server) === -1) {
servers: [...userAddedServers, action.server], await mx.setAccountData(AccountDataEvent.CinnyExploreServers, {
}); servers: [...userAddedServers, server],
} else if (action.type === 'DELETE') {
mx.setAccountData(AccountDataEvent.CinnyExploreServers, {
servers: userAddedServers.filter((server) => server !== action.server),
}); });
} }
}; },
[mx, userAddedServers]
);
return [userAddedServers, setUserAddedServers]; const removeServer = useCallback(
async (server: string) => {
await mx.setAccountData(AccountDataEvent.CinnyExploreServers, {
servers: userAddedServers.filter((addedServer) => server !== addedServer),
});
},
[mx, userAddedServers]
);
return [userAddedServers, addServer, removeServer];
}; };

View file

@ -14,6 +14,7 @@ import {
Overlay, Overlay,
OverlayBackdrop, OverlayBackdrop,
OverlayCenter, OverlayCenter,
Spinner,
Text, Text,
color, color,
config, config,
@ -45,7 +46,7 @@ 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 [, addServer] = useExploreServers();
const serverInputRef = useRef<HTMLInputElement>(null); const serverInputRef = useRef<HTMLInputElement>(null);
const [exploreState] = useAsyncCallback( const [exploreState] = useAsyncCallback(
@ -64,9 +65,9 @@ export function AddExploreServerPrompt() {
if (!server) return; if (!server) return;
setDialog(false); setDialog(false);
setExploreServers({ type: 'APPEND', server }); addServer(server);
navigate(getExploreServerPath(server)); navigate(getExploreServerPath(server));
}, [navigate, setExploreServers]); }, [navigate, addServer]);
return ( return (
<> <>
@ -157,7 +158,7 @@ export function AddExploreServerPrompt() {
type ExploreServerNavItemProps = { type ExploreServerNavItemProps = {
server: string; server: string;
selected: boolean; selected: boolean;
onRemove?: (() => void) | null; onRemove?: (() => Promise<void>) | null;
}; };
export function ExploreServerNavItem({ export function ExploreServerNavItem({
server, server,
@ -167,6 +168,15 @@ export function ExploreServerNavItem({
const [hover, setHover] = useState(false); const [hover, setHover] = useState(false);
const { hoverProps } = useHover({ onHoverChange: setHover }); const { hoverProps } = useHover({ onHoverChange: setHover });
const { focusWithinProps } = useFocusWithin({ onFocusWithinChange: setHover }); const { focusWithinProps } = useFocusWithin({ onFocusWithinChange: setHover });
const [removeState, removeCallback] = useAsyncCallback(
useCallback(async () => {
if (onRemove !== null) {
await onRemove();
}
}, [onRemove])
);
const removeInProgress =
removeState.status === AsyncStatus.Loading || removeState.status === AsyncStatus.Success;
return ( return (
<NavItem <NavItem
@ -190,10 +200,21 @@ export function ExploreServerNavItem({
</Box> </Box>
</NavItemContent> </NavItemContent>
</NavLink> </NavLink>
{onRemove !== null && hover && ( {onRemove !== null && (hover || removeInProgress) && (
<NavItemOptions> <NavItemOptions>
<IconButton onClick={onRemove} variant="Background" fill="None" size="300" radii="300"> <IconButton
onClick={removeCallback}
variant="Background"
fill="None"
size="300"
radii="300"
disabled={removeInProgress}
>
{removeInProgress ? (
<Spinner variant="Secondary" fill="Solid" size="200" />
) : (
<Icon size="50" src={Icons.Minus} /> <Icon size="50" src={Icons.Minus} />
)}
</IconButton> </IconButton>
</NavItemOptions> </NavItemOptions>
)} )}
@ -209,7 +230,7 @@ export function Explore() {
const userServer = userId ? getMxIdServer(userId) : undefined; const userServer = userId ? getMxIdServer(userId) : undefined;
const featuredServers = const featuredServers =
clientConfig.featuredCommunities?.servers?.filter((server) => server !== userServer) ?? []; clientConfig.featuredCommunities?.servers?.filter((server) => server !== userServer) ?? [];
const [exploreServers, setExploreServers] = useExploreServers(); const [exploreServers, , removeServer] = useExploreServers();
const featuredSelected = useExploreFeaturedSelected(); const featuredSelected = useExploreFeaturedSelected();
const selectedServer = useExploreServer(); const selectedServer = useExploreServer();
@ -290,7 +311,7 @@ export function Explore() {
key={server} key={server}
server={server} server={server}
selected={server === selectedServer} selected={server === selectedServer}
onRemove={() => setExploreServers({ type: 'DELETE', server })} onRemove={() => removeServer(server)}
/> />
))} ))}
</NavCategory> </NavCategory>