83 lines
2.9 KiB
TypeScript
83 lines
2.9 KiB
TypeScript
export type ForkGroupingItem = {
|
|
kind: "chat" | "search";
|
|
id: string;
|
|
parentChatId: string | null;
|
|
};
|
|
|
|
type ChatTitleState = {
|
|
title: string | null;
|
|
titleGenerationPending: boolean;
|
|
};
|
|
|
|
type ForkPresentationItem = ForkGroupingItem & {
|
|
updatedAt: string;
|
|
starred: boolean;
|
|
starredAt: string | null;
|
|
};
|
|
|
|
export function groupForkedChatItems<T extends ForkGroupingItem>(items: T[]): T[][] {
|
|
const groups = new Map<string, { firstIndex: number; items: Array<{ item: T; index: number }> }>();
|
|
|
|
items.forEach((item, index) => {
|
|
const groupKey = item.kind === "chat" ? `chat:${item.parentChatId ?? item.id}` : `search:${item.id}`;
|
|
const group = groups.get(groupKey);
|
|
if (group) {
|
|
group.items.push({ item, index });
|
|
return;
|
|
}
|
|
groups.set(groupKey, { firstIndex: index, items: [{ item, index }] });
|
|
});
|
|
|
|
return [...groups.values()]
|
|
.sort((a, b) => a.firstIndex - b.firstIndex)
|
|
.map((group) =>
|
|
group.items
|
|
.sort((a, b) => {
|
|
const aIsRoot = a.item.kind === "chat" && a.item.parentChatId === null;
|
|
const bIsRoot = b.item.kind === "chat" && b.item.parentChatId === null;
|
|
if (aIsRoot !== bIsRoot) return aIsRoot ? -1 : 1;
|
|
return a.index - b.index;
|
|
})
|
|
.map(({ item }) => item)
|
|
);
|
|
}
|
|
|
|
export function filterSidebarItemsWithForkGroups<T extends ForkGroupingItem>(items: T[], matches: (item: T) => boolean): T[] {
|
|
return groupForkedChatItems(items)
|
|
.flatMap((group) => {
|
|
const matchingItems = group.filter(matches);
|
|
if (!matchingItems.length) return [];
|
|
const root = group.find((item) => item.kind === "chat" && item.parentChatId === null);
|
|
if (!root || matchingItems.includes(root)) return group;
|
|
return group.filter((item) => item === root || matchingItems.includes(item));
|
|
});
|
|
}
|
|
|
|
export function getForkGroupPresentation<T extends ForkPresentationItem>(groupItems: T[], allItems: T[]) {
|
|
const firstItem = groupItems[0];
|
|
if (!firstItem) return null;
|
|
|
|
const rootId = firstItem.kind === "chat" ? firstItem.parentChatId ?? firstItem.id : null;
|
|
const familyItems = rootId
|
|
? allItems.filter((item) => item.kind === "chat" && (item.id === rootId || item.parentChatId === rootId))
|
|
: groupItems;
|
|
const updatedAt = familyItems.reduce(
|
|
(newest, item) => (new Date(item.updatedAt).getTime() > new Date(newest).getTime() ? item.updatedAt : newest),
|
|
firstItem.updatedAt
|
|
);
|
|
const starOwner = rootId
|
|
? familyItems.find((item) => item.kind === "chat" && item.parentChatId === null)
|
|
: firstItem;
|
|
|
|
return {
|
|
updatedAt,
|
|
starred: starOwner?.starred ?? false,
|
|
starredAt: starOwner?.starredAt ?? null,
|
|
};
|
|
}
|
|
|
|
export function shouldRequestChatTitle(chat: ChatTitleState | null | undefined, isRequestInFlight: boolean) {
|
|
if (!chat || isRequestInFlight) return false;
|
|
return chat.titleGenerationPending || !chat.title?.trim();
|
|
}
|