58 lines
1.8 KiB
TypeScript
58 lines
1.8 KiB
TypeScript
import { clsx, type ClassValue } from "clsx"
|
|
import { twMerge } from "tailwind-merge"
|
|
import { Thought, ThoughtThread as ThoughtThreadType } from "./api";
|
|
|
|
export function cn(...inputs: ClassValue[]) {
|
|
return twMerge(clsx(inputs))
|
|
}
|
|
|
|
/** Construct a full fediverse handle like `user@instance.social`.
|
|
* Falls back gracefully for existing DB rows that only stored the username. */
|
|
export function fullFediverseHandle(handle: string, actorUrl: string): string {
|
|
if (handle.includes("@")) return handle;
|
|
try {
|
|
return `${handle}@${new URL(actorUrl).hostname}`;
|
|
} catch {
|
|
return handle;
|
|
}
|
|
}
|
|
|
|
/** Returns the correct profile URL for an author.
|
|
* Local users go to /users/:username; remote actors go to /remote-actor?handle=. */
|
|
export function profileHref(username: string, local: boolean): string {
|
|
if (local) return `/users/${username}`;
|
|
const handle = username.startsWith("@") ? username : `@${username}`;
|
|
return `/remote-actor?handle=${encodeURIComponent(handle)}`;
|
|
}
|
|
|
|
export function buildThoughtThreads(thoughts: Thought[]): ThoughtThreadType[] {
|
|
const thoughtMap = new Map<string, Thought>();
|
|
thoughts.forEach((t) => thoughtMap.set(t.id, t));
|
|
|
|
const threads: ThoughtThreadType[] = [];
|
|
const repliesMap: Record<string, Thought[]> = {};
|
|
|
|
thoughts.forEach((thought) => {
|
|
if (thought.replyToId) {
|
|
if (!repliesMap[thought.replyToId]) {
|
|
repliesMap[thought.replyToId] = [];
|
|
}
|
|
repliesMap[thought.replyToId].push(thought);
|
|
}
|
|
});
|
|
|
|
function buildThread(thought: Thought): ThoughtThreadType {
|
|
return {
|
|
...thought,
|
|
replies: (repliesMap[thought.id] || []).map(buildThread),
|
|
};
|
|
}
|
|
|
|
thoughts.forEach((thought) => {
|
|
if (!thought.replyToId) {
|
|
threads.push(buildThread(thought));
|
|
}
|
|
});
|
|
|
|
return threads;
|
|
} |