Files
Jyotisha/frontend/src/lib/session-groups.ts
T

91 lines
2.8 KiB
TypeScript

import type { ChatSession } from "@/lib/home-types";
export const SESSION_RECENCY_LABELS = {
today: "今天",
yesterday: "昨天",
week: "最近 7 天",
month: "最近 30 天",
older: "更早",
} as const;
export type SessionRecencyKey = keyof typeof SESSION_RECENCY_LABELS;
export type SessionRecencyGroup<T extends { updatedAt: number } = ChatSession> = {
readonly key: SessionRecencyKey;
readonly label: string;
readonly sessions: readonly T[];
};
function startOfLocalDay(now: Date): number {
return new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
}
export function sortSessions<T extends { pinned: boolean; updatedAt: number }>(sessions: readonly T[]): T[] {
return sessions
.map((session, index) => ({ session, index }))
.sort((left, right) => {
const pinned = Number(right.session.pinned) - Number(left.session.pinned);
if (pinned !== 0) return pinned;
const time = right.session.updatedAt - left.session.updatedAt;
if (time !== 0) return time;
return left.index - right.index;
})
.map((item) => item.session);
}
export function recencyKeyFor(updatedAt: number, now = Date.now()): SessionRecencyKey {
const todayStart = startOfLocalDay(new Date(now));
const dayMs = 24 * 60 * 60 * 1000;
if (updatedAt >= todayStart) return "today";
if (updatedAt >= todayStart - dayMs) return "yesterday";
if (updatedAt >= todayStart - 6 * dayMs) return "week";
if (updatedAt >= todayStart - 29 * dayMs) return "month";
return "older";
}
export function groupSessionsByRecency<T extends { updatedAt: number }>(
sessions: readonly T[],
now = Date.now(),
): SessionRecencyGroup<T>[] {
const buckets: Record<SessionRecencyKey, T[]> = {
today: [],
yesterday: [],
week: [],
month: [],
older: [],
};
for (const session of sessions) {
buckets[recencyKeyFor(session.updatedAt, now)].push(session);
}
const order: SessionRecencyKey[] = ["today", "yesterday", "week", "month", "older"];
return order.flatMap((key) => {
const group = buckets[key];
if (group.length === 0) return [];
return [{ key, label: SESSION_RECENCY_LABELS[key], sessions: group }];
});
}
export function mergeSessionPage<T extends { id: string; updatedAt: number }>(
existing: readonly T[],
incoming: readonly T[],
): T[] {
const seen = new Map(existing.map((session) => [session.id, session]));
const next = [...existing];
for (const row of incoming) {
const current = seen.get(row.id);
if (current) continue;
seen.set(row.id, row);
next.push(row);
}
return next;
}
export function beginSessionPageLoad(
inFlight: { current: boolean },
cursor: string | null,
): boolean {
if (inFlight.current || !cursor) return false;
inFlight.current = true;
return true;
}