92 lines · 2.7 KB · TypeScript
RawDownload
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";import { useMemo, useState } from "react";import { useSelectedAccountId } from "./account-selection";import {	MAP_TYPES,	WORLD_VIEWPORT,	boundsContainFeature,	featureMatchesSearch,	fetchMap,	type MapViewport,} from "./network-map-model";import { fetchQueryEnvelope } from "#/lib/api-client";import type { NetworkMapKind } from "#/lib/network-map";import { queryKeys } from "#/lib/query-client";import type {	NetworkMapRouteSearch,	RouteSearchChange,} from "#/lib/route-search"; export function useNetworkMapController(	filters: NetworkMapRouteSearch,	onFiltersChange: RouteSearchChange<NetworkMapRouteSearch>,) {	const queryClient = useQueryClient();	const { type, q: visibleSearch } = filters;	const [viewport, setViewport] = useState<MapViewport>(WORLD_VIEWPORT);	const statusQuery = useQuery({		queryKey: queryKeys.status,		queryFn: ({ signal }) => fetchQueryEnvelope({ signal }),	});	const selectedAccountId = useSelectedAccountId(statusQuery.data?.accounts);	const mapQueryKey = [		...queryKeys.networkMap,		{ type, selectedAccountId: selectedAccountId ?? null },	] as const;	const mapQuery = useQuery({		queryKey: mapQueryKey,		queryFn: ({ signal }) => fetchMap(type, false, selectedAccountId, signal),		staleTime: 5 * 60_000,	});	const refreshMutation = useMutation({		mutationFn: () => fetchMap(type, true, selectedAccountId),		onSuccess: (nextData) => {			queryClient.setQueryData(mapQueryKey, nextData);		},	});	const data = mapQuery.data ?? null;	const loading = mapQuery.isPending || refreshMutation.isPending;	const queryError = refreshMutation.error ?? mapQuery.error; 	const visibleFeatures = useMemo(		() =>			(data?.features ?? [])				.slice()				.filter((feature) => boundsContainFeature(viewport.bounds, feature))				.sort(					(a, b) =>						b.properties.followersCount - a.properties.followersCount ||						a.properties.handle.localeCompare(b.properties.handle),				),		[data, viewport],	);	const filteredVisibleFeatures = useMemo(		() =>			visibleFeatures				.filter((feature) => featureMatchesSearch(feature, visibleSearch))				.slice(0, 160),		[visibleFeatures, visibleSearch],	); 	return {		type,		setType: (value: NetworkMapKind) =>			onFiltersChange({ ...filters, type: value }),		viewport,		setViewport,		visibleSearch,		setVisibleSearch: (value: string) =>			onFiltersChange({ ...filters, q: value }, { replace: true }),		data,		loading,		error: queryError			? queryError instanceof Error				? queryError.message				: "Map unavailable"			: null,		refresh: refreshMutation.mutate,		visibleFeatures,		filteredVisibleFeatures,		mapTypes: MAP_TYPES,	};}