91 lines
2.8 KiB
TypeScript
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;
|
|
}
|