73 lines · 2.6 KB · TypeScript
RawDownload
import type { ProjectId } from "@t3tools/contracts";import type { SidebarProjectSortOrder, SidebarThreadSortOrder } from "@t3tools/contracts/settings";import type { Thread } from "../types"; export type ThreadSortInput = Pick<Thread, "createdAt" | "updatedAt"> & {  latestUserMessageAt?: string | null;  messages?: Pick<Thread["messages"][number], "createdAt" | "role">[];}; export function toSortableTimestamp(iso: string | undefined): number | null {  if (!iso) return null;  const ms = Date.parse(iso);  return Number.isFinite(ms) ? ms : null;} function getLatestUserMessageTimestamp(thread: ThreadSortInput): number {  if (thread.latestUserMessageAt) {    return toSortableTimestamp(thread.latestUserMessageAt) ?? Number.NEGATIVE_INFINITY;  }   let latestUserMessageTimestamp: number | null = null;   for (const message of thread.messages ?? []) {    if (message.role !== "user") continue;    const messageTimestamp = toSortableTimestamp(message.createdAt);    if (messageTimestamp === null) continue;    latestUserMessageTimestamp =      latestUserMessageTimestamp === null        ? messageTimestamp        : Math.max(latestUserMessageTimestamp, messageTimestamp);  }   if (latestUserMessageTimestamp !== null) {    return latestUserMessageTimestamp;  }   return toSortableTimestamp(thread.updatedAt ?? thread.createdAt) ?? Number.NEGATIVE_INFINITY;} export function getThreadSortTimestamp(  thread: ThreadSortInput,  sortOrder: SidebarThreadSortOrder | Exclude<SidebarProjectSortOrder, "manual">,): number {  if (sortOrder === "created_at") {    return toSortableTimestamp(thread.createdAt) ?? Number.NEGATIVE_INFINITY;  }  return getLatestUserMessageTimestamp(thread);} export function sortThreads<T extends Pick<Thread, "id"> & ThreadSortInput>(  threads: readonly T[],  sortOrder: SidebarThreadSortOrder,): T[] {  return threads.toSorted((left, right) => {    const rightTimestamp = getThreadSortTimestamp(right, sortOrder);    const leftTimestamp = getThreadSortTimestamp(left, sortOrder);    const byTimestamp =      rightTimestamp === leftTimestamp ? 0 : rightTimestamp > leftTimestamp ? 1 : -1;    if (byTimestamp !== 0) return byTimestamp;    return right.id.localeCompare(left.id);  });} export function getLatestThreadForProject<  T extends Pick<Thread, "id" | "projectId" | "archivedAt"> & ThreadSortInput,>(threads: readonly T[], projectId: ProjectId, sortOrder: SidebarThreadSortOrder): T | null {  return (    sortThreads(      threads.filter((thread) => thread.projectId === projectId && thread.archivedAt === null),      sortOrder,    )[0] ?? null  );}