From f4856563df136ca84e180483d132d1005c91abc3 Mon Sep 17 00:00:00 2001 From: plebeius Date: Fri, 7 Nov 2025 12:49:49 +0100 Subject: [PATCH] feat(routing): add hooks and utilities for directory-based board routing --- src/hooks/use-resolved-subplebbit-address.ts | 48 ++++++++++ src/lib/utils/route-utils.ts | 99 ++++++++++++++++++++ 2 files changed, 147 insertions(+) create mode 100644 src/hooks/use-resolved-subplebbit-address.ts create mode 100644 src/lib/utils/route-utils.ts diff --git a/src/hooks/use-resolved-subplebbit-address.ts b/src/hooks/use-resolved-subplebbit-address.ts new file mode 100644 index 00000000..9dd2e43b --- /dev/null +++ b/src/hooks/use-resolved-subplebbit-address.ts @@ -0,0 +1,48 @@ +import { useMemo } from 'react'; +import { useParams } from 'react-router-dom'; +import { useDefaultSubplebbits } from './use-default-subplebbits'; +import { getSubplebbitAddress, getBoardPath } from '../lib/utils/route-utils'; + +/** + * Hook to resolve boardIdentifier from URL params to subplebbitAddress + * Handles both directory codes (e.g., "biz") and full addresses (e.g., "someboard.eth") + * + * Performance: Uses useMemo to avoid recalculating when params/defaultSubplebbits haven't changed. + * The defaultSubplebbits reference is stable (from cache) after initial load, so memoization works effectively. + */ +export const useResolvedSubplebbitAddress = (): string | undefined => { + const params = useParams(); + const defaultSubplebbits = useDefaultSubplebbits(); + + // Try boardIdentifier first (new format), then subplebbitAddress (old format for backward compatibility) + const boardIdentifier = params.boardIdentifier || params.subplebbitAddress; + + return useMemo(() => { + if (!boardIdentifier) { + return undefined; + } + + // Resolve directory code to address if needed + // getSubplebbitAddress uses internal caching, so this is efficient + return getSubplebbitAddress(boardIdentifier, defaultSubplebbits); + }, [boardIdentifier, defaultSubplebbits]); +}; + +/** + * Hook to get the board path (directory code or address) for use in links + * + * Performance: Uses useMemo to avoid recalculating when subplebbitAddress/defaultSubplebbits haven't changed. + * The defaultSubplebbits reference is stable (from cache) after initial load, so memoization works effectively. + */ +export const useBoardPath = (subplebbitAddress: string | undefined): string | undefined => { + const defaultSubplebbits = useDefaultSubplebbits(); + + return useMemo(() => { + if (!subplebbitAddress) { + return undefined; + } + + // getBoardPath uses internal caching, so this is efficient + return getBoardPath(subplebbitAddress, defaultSubplebbits); + }, [subplebbitAddress, defaultSubplebbits]); +}; diff --git a/src/lib/utils/route-utils.ts b/src/lib/utils/route-utils.ts new file mode 100644 index 00000000..9329682c --- /dev/null +++ b/src/lib/utils/route-utils.ts @@ -0,0 +1,99 @@ +import { MultisubSubplebbit } from '../../hooks/use-default-subplebbits'; + +/** + * Extract directory short code from title (e.g., "/biz/ - Business & Finance" -> "biz") + */ +export const extractDirectoryFromTitle = (title: string): string | null => { + const match = title.match(/^\/([^/]+)\//); + return match ? match[1] : null; +}; + +// Cache for maps to avoid recreating them on every call +let cachedSubplebbits: MultisubSubplebbit[] | null = null; +let cachedDirectoryToAddressMap: Map | null = null; +let cachedAddressToDirectoryMap: Map | null = null; + +/** + * Create a map from directory codes to subplebbit addresses + * Uses caching to avoid recreating the map when subplebbits array hasn't changed + */ +export const getDirectoryToAddressMap = (subplebbits: MultisubSubplebbit[]): Map => { + // Check if we can use cached map (same array reference) + if (cachedDirectoryToAddressMap && cachedSubplebbits === subplebbits) { + return cachedDirectoryToAddressMap; + } + + const map = new Map(); + for (const subplebbit of subplebbits) { + if (subplebbit.title) { + const directory = extractDirectoryFromTitle(subplebbit.title); + if (directory && subplebbit.address) { + map.set(directory, subplebbit.address); + } + } + } + + // Cache the map and array reference + cachedDirectoryToAddressMap = map; + cachedSubplebbits = subplebbits; + return map; +}; + +/** + * Create a map from subplebbit addresses to directory codes + * Uses caching to avoid recreating the map when subplebbits array hasn't changed + */ +export const getAddressToDirectoryMap = (subplebbits: MultisubSubplebbit[]): Map => { + // Check if we can use cached map (same array reference) + if (cachedAddressToDirectoryMap && cachedSubplebbits === subplebbits) { + return cachedAddressToDirectoryMap; + } + + const map = new Map(); + for (const subplebbit of subplebbits) { + if (subplebbit.title && subplebbit.address) { + const directory = extractDirectoryFromTitle(subplebbit.title); + if (directory) { + map.set(subplebbit.address, directory); + } + } + } + + // Cache the map and array reference + cachedAddressToDirectoryMap = map; + cachedSubplebbits = subplebbits; + return map; +}; + +/** + * Convert subplebbit address to URL path (directory code if available, otherwise full address) + */ +export const getBoardPath = (subplebbitAddress: string, subplebbits: MultisubSubplebbit[]): string => { + const addressToDirectory = getAddressToDirectoryMap(subplebbits); + const directory = addressToDirectory.get(subplebbitAddress); + return directory || subplebbitAddress; +}; + +/** + * Convert URL path (directory code or address) to subplebbit address + */ +export const getSubplebbitAddress = (boardIdentifier: string, subplebbits: MultisubSubplebbit[]): string => { + const directoryToAddress = getDirectoryToAddressMap(subplebbits); + + // Check if it's a directory code + const address = directoryToAddress.get(boardIdentifier); + if (address) { + return address; + } + + // Otherwise, assume it's already an address + return boardIdentifier; +}; + +/** + * Check if an identifier is a directory short code + */ +export const isDirectoryBoard = (identifier: string, subplebbits: MultisubSubplebbit[]): boolean => { + const directoryToAddress = getDirectoryToAddressMap(subplebbits); + return directoryToAddress.has(identifier); +};