refac
This commit is contained in:
@@ -57,6 +57,7 @@
|
|||||||
items={folderOptions.map((folder) => ({ value: folder.id, label: folderName(folder) }))}
|
items={folderOptions.map((folder) => ({ value: folder.id, label: folderName(folder) }))}
|
||||||
placeholder={$i18n.t('Choose folder')}
|
placeholder={$i18n.t('Choose folder')}
|
||||||
{align}
|
{align}
|
||||||
|
{side}
|
||||||
triggerClass="relative h-8 max-w-[11rem] flex items-center gap-1.5 px-2.5 py-1.5 bg-transparent rounded-2xl text-xs font-normal text-gray-600 transition hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-100"
|
triggerClass="relative h-8 max-w-[11rem] flex items-center gap-1.5 px-2.5 py-1.5 bg-transparent rounded-2xl text-xs font-normal text-gray-600 transition hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-100"
|
||||||
contentClass="w-72 shadow-lg"
|
contentClass="w-72 shadow-lg"
|
||||||
maxHeight="18rem"
|
maxHeight="18rem"
|
||||||
|
|||||||
@@ -952,6 +952,9 @@
|
|||||||
if ($chatId && !$temporaryChatEnabled && hasPendingAssistantLeaf()) {
|
if ($chatId && !$temporaryChatEnabled && hasPendingAssistantLeaf()) {
|
||||||
await loadChat();
|
await loadChat();
|
||||||
}
|
}
|
||||||
|
if ($chatId && !$temporaryChatEnabled) {
|
||||||
|
updateLastReadAt($chatId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (type === 'chat:completion') {
|
} else if (type === 'chat:completion') {
|
||||||
chatCompletionEventHandler(data, message, event.chat_id);
|
chatCompletionEventHandler(data, message, event.chat_id);
|
||||||
|
|||||||
@@ -2,7 +2,11 @@
|
|||||||
import { getContext } from 'svelte';
|
import { getContext } from 'svelte';
|
||||||
import type { Writable } from 'svelte/store';
|
import type { Writable } from 'svelte/store';
|
||||||
|
|
||||||
const i18n: Writable<any> = getContext('i18n');
|
type ChatListI18n = {
|
||||||
|
t: (key: string, options?: Record<string, unknown>) => string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const i18n: Writable<ChatListI18n> = getContext('i18n');
|
||||||
|
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import localizedFormat from 'dayjs/plugin/localizedFormat';
|
import localizedFormat from 'dayjs/plugin/localizedFormat';
|
||||||
@@ -13,11 +17,24 @@
|
|||||||
import ChevronRight from '$lib/components/icons/ChevronRight.svelte';
|
import ChevronRight from '$lib/components/icons/ChevronRight.svelte';
|
||||||
import Tooltip from '$lib/components/common/Tooltip.svelte';
|
import Tooltip from '$lib/components/common/Tooltip.svelte';
|
||||||
import Spinner from '$lib/components/common/Spinner.svelte';
|
import Spinner from '$lib/components/common/Spinner.svelte';
|
||||||
import { chatId } from '$lib/stores';
|
import { chatId, socket } from '$lib/stores';
|
||||||
|
|
||||||
dayjs.extend(localizedFormat);
|
dayjs.extend(localizedFormat);
|
||||||
|
|
||||||
export let chats: any[] = [];
|
type ChatListItem = {
|
||||||
|
id: string;
|
||||||
|
title?: string;
|
||||||
|
updated_at?: number | null;
|
||||||
|
created_at?: number | null;
|
||||||
|
last_read_at?: number | null;
|
||||||
|
active?: boolean;
|
||||||
|
time_range?: string;
|
||||||
|
user_id?: string;
|
||||||
|
owner_name?: string;
|
||||||
|
[key: string]: unknown;
|
||||||
|
};
|
||||||
|
|
||||||
|
export let chats: ChatListItem[] = [];
|
||||||
|
|
||||||
export let chatListLoading = false;
|
export let chatListLoading = false;
|
||||||
export let showOwnerInfo = false;
|
export let showOwnerInfo = false;
|
||||||
@@ -26,10 +43,10 @@
|
|||||||
export let perPage = 10;
|
export let perPage = 10;
|
||||||
export let orderBy: 'title' | 'updated_at' = 'updated_at';
|
export let orderBy: 'title' | 'updated_at' = 'updated_at';
|
||||||
export let direction: 'asc' | 'desc' = 'desc';
|
export let direction: 'asc' | 'desc' = 'desc';
|
||||||
export let onPageChange: Function = () => {};
|
export let onPageChange: (page: number) => void | Promise<void> = () => {};
|
||||||
export let onSort: Function = () => {};
|
export let onSort: (key: 'title' | 'updated_at') => void | Promise<void> = () => {};
|
||||||
|
|
||||||
let chatList: any[] | null = null;
|
let chatList: ChatListItem[] | null = null;
|
||||||
let totalPages = 1;
|
let totalPages = 1;
|
||||||
let pages: (number | 'ellipsis')[] = [];
|
let pages: (number | 'ellipsis')[] = [];
|
||||||
|
|
||||||
@@ -48,6 +65,22 @@
|
|||||||
onSort(key);
|
onSort(key);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const markChatRead = (chat: ChatListItem, unread: boolean) => {
|
||||||
|
if (!unread) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const lastReadAt = Date.now() / 1000;
|
||||||
|
chatList = (chatList ?? []).map((item) =>
|
||||||
|
item.id === chat.id ? { ...item, last_read_at: lastReadAt } : item
|
||||||
|
);
|
||||||
|
|
||||||
|
$socket?.emit('events:chat', {
|
||||||
|
chat_id: chat.id,
|
||||||
|
data: { type: 'last_read_at' }
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const buildPages = (currentPage: number, pageCount: number): (number | 'ellipsis')[] => {
|
const buildPages = (currentPage: number, pageCount: number): (number | 'ellipsis')[] => {
|
||||||
if (pageCount <= 7) {
|
if (pageCount <= 7) {
|
||||||
return Array.from({ length: pageCount }, (_, i) => i + 1);
|
return Array.from({ length: pageCount }, (_, i) => i + 1);
|
||||||
@@ -177,6 +210,7 @@
|
|||||||
class=" w-full flex justify-between items-center rounded-lg text-sm py-2 px-3 hover:bg-gray-50 dark:hover:bg-gray-850"
|
class=" w-full flex justify-between items-center rounded-lg text-sm py-2 px-3 hover:bg-gray-50 dark:hover:bg-gray-850"
|
||||||
draggable="false"
|
draggable="false"
|
||||||
href={`/c/${chat.id}`}
|
href={`/c/${chat.id}`}
|
||||||
|
on:click={() => markChatRead(chat, unread)}
|
||||||
>
|
>
|
||||||
<div class="flex min-w-0 items-center w-full sm:basis-3/5">
|
<div class="flex min-w-0 items-center w-full sm:basis-3/5">
|
||||||
{#if chat.active}
|
{#if chat.active}
|
||||||
|
|||||||
@@ -1,19 +1,27 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { getContext, onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import type { Writable } from 'svelte/store';
|
|
||||||
|
|
||||||
const i18n: Writable<any> = getContext('i18n');
|
import { socket, user } from '$lib/stores';
|
||||||
|
|
||||||
import { user } from '$lib/stores';
|
|
||||||
|
|
||||||
import { fade } from 'svelte/transition';
|
|
||||||
|
|
||||||
import ChatList from './ChatList.svelte';
|
import ChatList from './ChatList.svelte';
|
||||||
import FolderKnowledge from './FolderKnowledge.svelte';
|
import FolderKnowledge from './FolderKnowledge.svelte';
|
||||||
import Spinner from '$lib/components/common/Spinner.svelte';
|
import Spinner from '$lib/components/common/Spinner.svelte';
|
||||||
import { getSharedFolderChats } from '$lib/apis/folders';
|
import { getSharedFolderChats } from '$lib/apis/folders';
|
||||||
|
|
||||||
export let folder: any = null;
|
type FolderPlaceholderFolder = {
|
||||||
|
id?: string;
|
||||||
|
shared?: boolean;
|
||||||
|
user_id?: string;
|
||||||
|
access_grants?: unknown[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type FolderChat = {
|
||||||
|
id: string;
|
||||||
|
active?: boolean;
|
||||||
|
[key: string]: unknown;
|
||||||
|
};
|
||||||
|
|
||||||
|
export let folder: FolderPlaceholderFolder | null = null;
|
||||||
|
|
||||||
let selectedTab = 'chats';
|
let selectedTab = 'chats';
|
||||||
|
|
||||||
@@ -24,8 +32,9 @@
|
|||||||
let direction: 'asc' | 'desc' = 'desc';
|
let direction: 'asc' | 'desc' = 'desc';
|
||||||
let currentFolderId: string | null = null;
|
let currentFolderId: string | null = null;
|
||||||
|
|
||||||
let chats: any[] | null = null;
|
let chats: FolderChat[] | null = null;
|
||||||
let chatListLoading = false;
|
let chatListLoading = false;
|
||||||
|
let refreshQueued = false;
|
||||||
|
|
||||||
$: showOwnerInfo = Boolean(
|
$: showOwnerInfo = Boolean(
|
||||||
folder?.shared ||
|
folder?.shared ||
|
||||||
@@ -53,9 +62,44 @@
|
|||||||
setChatList();
|
setChatList();
|
||||||
};
|
};
|
||||||
|
|
||||||
const setChatList = async () => {
|
const updateChatActive = (chatId: string, active: boolean) => {
|
||||||
|
if (!chats) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let found = false;
|
||||||
|
chats = chats.map((chat) => {
|
||||||
|
if (chat.id !== chatId) {
|
||||||
|
return chat;
|
||||||
|
}
|
||||||
|
found = true;
|
||||||
|
return { ...chat, active };
|
||||||
|
});
|
||||||
|
return found;
|
||||||
|
};
|
||||||
|
|
||||||
|
const refreshChatListSoon = (resetPage = false) => {
|
||||||
|
if (refreshQueued) {
|
||||||
|
if (resetPage) {
|
||||||
|
page = 1;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (resetPage) {
|
||||||
|
page = 1;
|
||||||
|
}
|
||||||
|
refreshQueued = true;
|
||||||
|
queueMicrotask(async () => {
|
||||||
|
refreshQueued = false;
|
||||||
|
await setChatList();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const setChatList = async (clear = false) => {
|
||||||
const folderId = folder?.id;
|
const folderId = folder?.id;
|
||||||
chats = null;
|
if (clear) {
|
||||||
|
chats = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (folderId) {
|
if (folderId) {
|
||||||
// Always use the shared folder endpoint so owners also see
|
// Always use the shared folder endpoint so owners also see
|
||||||
@@ -69,7 +113,6 @@
|
|||||||
console.error(error);
|
console.error(error);
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
chatListLoading = false;
|
|
||||||
|
|
||||||
if (res && res.chats) {
|
if (res && res.chats) {
|
||||||
chats = res.chats;
|
chats = res.chats;
|
||||||
@@ -78,16 +121,43 @@
|
|||||||
chats = [];
|
chats = [];
|
||||||
totalChats = 0;
|
totalChats = 0;
|
||||||
}
|
}
|
||||||
|
chatListLoading = false;
|
||||||
} else {
|
} else {
|
||||||
chats = [];
|
chats = [];
|
||||||
totalChats = 0;
|
totalChats = 0;
|
||||||
|
chatListLoading = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const chatEventHandler = (event: {
|
||||||
|
chat_id?: string;
|
||||||
|
data?: { type?: string; data?: { active?: boolean } };
|
||||||
|
}) => {
|
||||||
|
if (event.data?.type === 'chat:active' && event.chat_id) {
|
||||||
|
const active = event.data.data?.active ?? false;
|
||||||
|
if (!updateChatActive(event.chat_id, active) && active) {
|
||||||
|
refreshChatListSoon(true);
|
||||||
|
}
|
||||||
|
} else if (event.data?.type === 'chat:list') {
|
||||||
|
refreshChatListSoon(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
const socketInstance = $socket;
|
||||||
|
socketInstance?.on('events', chatEventHandler);
|
||||||
|
socketInstance?.on('connect', refreshChatListSoon);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
socketInstance?.off('events', chatEventHandler);
|
||||||
|
socketInstance?.off('connect', refreshChatListSoon);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
$: if (folder?.id && folder.id !== currentFolderId) {
|
$: if (folder?.id && folder.id !== currentFolderId) {
|
||||||
currentFolderId = folder.id;
|
currentFolderId = folder.id;
|
||||||
page = 1;
|
page = 1;
|
||||||
setChatList();
|
setChatList(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
$: if (!folder?.id && currentFolderId !== null) {
|
$: if (!folder?.id && currentFolderId !== null) {
|
||||||
|
|||||||
Reference in New Issue
Block a user