2025-11-07 12:49:49 +01:00
|
|
|
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;
|
|
|
|
|
};
|
|
|
|
|
|
2025-11-07 18:34:06 +01:00
|
|
|
// Cache for directory-to-address map
|
|
|
|
|
let cachedSubplebbitsForDirectory: MultisubSubplebbit[] | null = null;
|
2025-11-07 12:49:49 +01:00
|
|
|
let cachedDirectoryToAddressMap: Map<string, string> | null = null;
|
2025-11-07 18:34:06 +01:00
|
|
|
|
|
|
|
|
// Cache for address-to-directory map
|
|
|
|
|
let cachedSubplebbitsForAddress: MultisubSubplebbit[] | null = null;
|
2025-11-07 12:49:49 +01:00
|
|
|
let cachedAddressToDirectoryMap: Map<string, string> | 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<string, string> => {
|
|
|
|
|
// Check if we can use cached map (same array reference)
|
2025-11-07 18:34:06 +01:00
|
|
|
if (cachedDirectoryToAddressMap && cachedSubplebbitsForDirectory === subplebbits) {
|
2025-11-07 12:49:49 +01:00
|
|
|
return cachedDirectoryToAddressMap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const map = new Map<string, string>();
|
|
|
|
|
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;
|
2025-11-07 18:34:06 +01:00
|
|
|
cachedSubplebbitsForDirectory = subplebbits;
|
2025-11-07 12:49:49 +01:00
|
|
|
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<string, string> => {
|
|
|
|
|
// Check if we can use cached map (same array reference)
|
2025-11-07 18:34:06 +01:00
|
|
|
if (cachedAddressToDirectoryMap && cachedSubplebbitsForAddress === subplebbits) {
|
2025-11-07 12:49:49 +01:00
|
|
|
return cachedAddressToDirectoryMap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const map = new Map<string, string>();
|
|
|
|
|
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;
|
2025-11-07 18:34:06 +01:00
|
|
|
cachedSubplebbitsForAddress = subplebbits;
|
2025-11-07 12:49:49 +01:00
|
|
|
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);
|
|
|
|
|
};
|
2025-12-08 22:40:16 +01:00
|
|
|
|
|
|
|
|
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;
|
2025-12-08 22:40:16 +01:00
|
|
|
|
|
|
|
|
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;
|
2025-12-08 23:36:55 +01:00
|
|
|
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;
|
2025-12-08 22:40:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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');
|
|
|
|
|
};
|
|
|
|
|
|
2025-12-08 22:40:16 +01:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-08 22:40:16 +01:00
|
|
|
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;
|
|
|
|
|
};
|