Files
5chan/src/lib/utils/route-utils.ts
T

187 lines
5.9 KiB
TypeScript
Raw Normal View History

import { DirectoryCommunity } from '../../hooks/use-directories';
/**
* 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 directory-to-address map
let cachedCommunitiesForDirectory: DirectoryCommunity[] | null = null;
let cachedDirectoryToAddressMap: Map<string, string> | null = null;
// Cache for address-to-directory map
let cachedCommunitiesForAddress: DirectoryCommunity[] | null = null;
let cachedAddressToDirectoryMap: Map<string, string> | null = null;
/**
* Create a map from directory codes to community addresses
* Uses caching to avoid recreating the map when communities array hasn't changed
*/
export const getDirectoryToAddressMap = (communities: DirectoryCommunity[]): Map<string, string> => {
// Check if we can use cached map (same array reference)
if (cachedDirectoryToAddressMap && cachedCommunitiesForDirectory === communities) {
return cachedDirectoryToAddressMap;
}
const map = new Map<string, string>();
for (const community of communities) {
if (community.title) {
const directory = extractDirectoryFromTitle(community.title);
if (directory && community.address) {
map.set(directory, community.address);
}
}
}
// Cache the map and array reference
cachedDirectoryToAddressMap = map;
cachedCommunitiesForDirectory = communities;
return map;
};
/**
* Create a map from community addresses to directory codes
* Uses caching to avoid recreating the map when communities array hasn't changed
*/
export const getAddressToDirectoryMap = (communities: DirectoryCommunity[]): Map<string, string> => {
// Check if we can use cached map (same array reference)
if (cachedAddressToDirectoryMap && cachedCommunitiesForAddress === communities) {
return cachedAddressToDirectoryMap;
}
const map = new Map<string, string>();
for (const community of communities) {
if (community.title && community.address) {
const directory = extractDirectoryFromTitle(community.title);
if (directory) {
map.set(community.address, directory);
}
}
}
// Cache the map and array reference
cachedAddressToDirectoryMap = map;
cachedCommunitiesForAddress = communities;
return map;
};
/**
* Convert community address to URL path (directory code if available, otherwise full address)
*/
export const getBoardPath = (communityAddress: string, communities: DirectoryCommunity[]): string => {
const addressToDirectory = getAddressToDirectoryMap(communities);
const directory = addressToDirectory.get(communityAddress);
return directory || communityAddress;
};
/**
* Convert URL path (directory code or address) to community address
*/
export const getSubplebbitAddress = (boardIdentifier: string, communities: DirectoryCommunity[]): string => {
const directoryToAddress = getDirectoryToAddressMap(communities);
// 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, communities: DirectoryCommunity[]): boolean => {
const directoryToAddress = getDirectoryToAddressMap(communities);
return directoryToAddress.has(identifier);
};
export const isFeedRoute = (pathname: string): boolean => {
const normalizedPath = pathname.endsWith('/') ? pathname.slice(0, -1) : pathname;
if (normalizedPath.includes('/thread/')) return false;
if (normalizedPath.startsWith('/pending/')) return false;
2026-01-07 16:03:02 +01:00
if (normalizedPath.includes('/queue')) return false;
const pathWithoutSettings = normalizedPath.replace(/\/settings$/, '');
if (pathWithoutSettings.startsWith('/all')) return true;
if (pathWithoutSettings.startsWith('/subs')) return true;
if (pathWithoutSettings.startsWith('/mod')) return true;
const segments = pathWithoutSettings.split('/').filter(Boolean);
if (segments.length >= 1) {
if (segments.length === 1) return true;
if (segments.length === 2 && segments[1] === 'catalog') return true;
if (segments.length === 2 && /^(?:\d+(?:h|d|w|m|y)|all)$/.test(segments[1])) return true;
if (segments.length === 3 && segments[1] === 'catalog' && /^(?:\d+(?:h|d|w|m|y)|all)$/.test(segments[2])) return true;
}
return false;
};
export const isPostRoute = (pathname: string): boolean => {
const normalizedPath = pathname.replace(/\/settings$/, '');
if (normalizedPath.includes('/thread/')) return true;
return false;
};
export const isPendingPostRoute = (pathname: string): boolean => {
const normalizedPath = pathname.replace(/\/settings$/, '');
return normalizedPath.startsWith('/pending/');
};
2026-01-07 16:03:02 +01:00
export const isModQueueRoute = (pathname: string): boolean => {
const normalizedPath = pathname.replace(/\/settings$/, '');
return normalizedPath.includes('/queue');
};
export const getFeedCacheKey = (pathname: string): string | null => {
let normalizedPath = pathname.endsWith('/') ? pathname.slice(0, -1) : pathname;
normalizedPath = normalizedPath.replace(/\/settings$/, '');
if (normalizedPath.includes('/thread/')) {
const parts = normalizedPath.split('/thread/');
return parts[0] || null;
}
if (normalizedPath.startsWith('/pending/')) {
return null;
}
2026-01-07 16:03:02 +01:00
if (normalizedPath.includes('/queue')) {
return null;
}
if (isFeedRoute(pathname)) {
return normalizedPath;
}
return null;
};
export const getFeedType = (pathname: string): 'board' | 'catalog' | null => {
const normalizedPath = pathname.replace(/\/settings$/, '');
if (normalizedPath.includes('/catalog')) {
return 'catalog';
}
if (isFeedRoute(pathname)) {
return 'board';
}
if (isPostRoute(pathname)) {
return 'board';
}
return null;
};