refac
This commit is contained in:
1 parent
a2000df253
commit
743b9fd3ce
12 files changed
+98
-91
No files matched your search
@@ -248,6 +248,21 @@ class ChatMessageTable:
|
|||||||
message = await db.get(ChatMessage, id)
|
message = await db.get(ChatMessage, id)
|
||||||
return ChatMessageModel.model_validate(message) if message else None
|
return ChatMessageModel.model_validate(message) if message else None
|
||||||
|
|
||||||
|
async def has_unfinished_assistant_by_chat_id(
|
||||||
|
self,
|
||||||
|
chat_id: str,
|
||||||
|
db: Optional[AsyncSession] = None,
|
||||||
|
) -> bool:
|
||||||
|
async with get_async_db_context(db) as db:
|
||||||
|
result = await db.execute(
|
||||||
|
select(ChatMessage.id)
|
||||||
|
.where(ChatMessage.chat_id == chat_id)
|
||||||
|
.where(ChatMessage.role == 'assistant')
|
||||||
|
.where(ChatMessage.done.is_(False))
|
||||||
|
.limit(1)
|
||||||
|
)
|
||||||
|
return result.scalar_one_or_none() is not None
|
||||||
|
|
||||||
async def get_messages_by_chat_id(self, chat_id: str, db: Optional[AsyncSession] = None) -> list[ChatMessageModel]:
|
async def get_messages_by_chat_id(self, chat_id: str, db: Optional[AsyncSession] = None) -> list[ChatMessageModel]:
|
||||||
async with get_async_db_context(db) as db:
|
async with get_async_db_context(db) as db:
|
||||||
result = await db.execute(
|
result = await db.execute(
|
||||||
|
|||||||
@@ -186,6 +186,7 @@ class ChatTitleIdResponse(BaseModel):
|
|||||||
created_at: int
|
created_at: int
|
||||||
last_read_at: int | None = None
|
last_read_at: int | None = None
|
||||||
snippet: str | None = None
|
snippet: str | None = None
|
||||||
|
active: bool = False
|
||||||
|
|
||||||
|
|
||||||
class SharedChatResponse(BaseModel):
|
class SharedChatResponse(BaseModel):
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ from open_webui.events import EVENTS, publish_event
|
|||||||
from open_webui.internal.db import get_async_session
|
from open_webui.internal.db import get_async_session
|
||||||
from open_webui.models.access_grants import AccessGrants
|
from open_webui.models.access_grants import AccessGrants
|
||||||
from open_webui.models.config import Config
|
from open_webui.models.config import Config
|
||||||
|
from open_webui.models.chat_messages import ChatMessages
|
||||||
from open_webui.models.chats import (
|
from open_webui.models.chats import (
|
||||||
AggregateChatStats,
|
AggregateChatStats,
|
||||||
ChatBody,
|
ChatBody,
|
||||||
@@ -57,6 +58,19 @@ CHAT_CONFIG_KEYS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def add_active_state_to_chat_list(
|
||||||
|
request: Request, chat_list: list[ChatTitleIdResponse]
|
||||||
|
) -> list[ChatTitleIdResponse]:
|
||||||
|
for chat in chat_list:
|
||||||
|
chat.active = False
|
||||||
|
if not await has_active_tasks(request.app.state.redis, chat.id):
|
||||||
|
continue
|
||||||
|
|
||||||
|
chat.active = await ChatMessages.has_unfinished_assistant_by_chat_id(chat.id)
|
||||||
|
|
||||||
|
return chat_list
|
||||||
|
|
||||||
|
|
||||||
class ChatConfigForm(BaseModel):
|
class ChatConfigForm(BaseModel):
|
||||||
ENABLE_CONTEXT_COMPACTION: bool
|
ENABLE_CONTEXT_COMPACTION: bool
|
||||||
CONTEXT_COMPACTION_TOKEN_THRESHOLD: int
|
CONTEXT_COMPACTION_TOKEN_THRESHOLD: int
|
||||||
@@ -137,6 +151,7 @@ async def require_chat_import_permission(request: Request, user, db: AsyncSessio
|
|||||||
@router.get('/', response_model=list[ChatTitleIdResponse])
|
@router.get('/', response_model=list[ChatTitleIdResponse])
|
||||||
@router.get('/list', response_model=list[ChatTitleIdResponse])
|
@router.get('/list', response_model=list[ChatTitleIdResponse])
|
||||||
async def get_session_user_chat_list(
|
async def get_session_user_chat_list(
|
||||||
|
request: Request,
|
||||||
user=Depends(get_verified_user),
|
user=Depends(get_verified_user),
|
||||||
page: int | None = None,
|
page: int | None = None,
|
||||||
include_pinned: bool | None = False,
|
include_pinned: bool | None = False,
|
||||||
@@ -148,7 +163,7 @@ async def get_session_user_chat_list(
|
|||||||
limit = 60
|
limit = 60
|
||||||
skip = (page - 1) * limit
|
skip = (page - 1) * limit
|
||||||
|
|
||||||
return await Chats.get_chat_title_id_list_by_user_id(
|
chats = await Chats.get_chat_title_id_list_by_user_id(
|
||||||
user.id,
|
user.id,
|
||||||
include_folders=include_folders,
|
include_folders=include_folders,
|
||||||
include_pinned=include_pinned,
|
include_pinned=include_pinned,
|
||||||
@@ -157,12 +172,13 @@ async def get_session_user_chat_list(
|
|||||||
db=db,
|
db=db,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
return await Chats.get_chat_title_id_list_by_user_id(
|
chats = await Chats.get_chat_title_id_list_by_user_id(
|
||||||
user.id,
|
user.id,
|
||||||
include_folders=include_folders,
|
include_folders=include_folders,
|
||||||
include_pinned=include_pinned,
|
include_pinned=include_pinned,
|
||||||
db=db,
|
db=db,
|
||||||
)
|
)
|
||||||
|
return await add_active_state_to_chat_list(request, chats)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.exception(e)
|
log.exception(e)
|
||||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT())
|
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT())
|
||||||
@@ -605,6 +621,7 @@ async def delete_all_user_chats(
|
|||||||
|
|
||||||
@router.get('/list/user/{user_id}', response_model=list[ChatTitleIdResponse])
|
@router.get('/list/user/{user_id}', response_model=list[ChatTitleIdResponse])
|
||||||
async def get_user_chat_list_by_user_id(
|
async def get_user_chat_list_by_user_id(
|
||||||
|
request: Request,
|
||||||
user_id: str,
|
user_id: str,
|
||||||
page: int | None = None,
|
page: int | None = None,
|
||||||
query: str | None = None,
|
query: str | None = None,
|
||||||
@@ -629,9 +646,10 @@ async def get_user_chat_list_by_user_id(
|
|||||||
if direction:
|
if direction:
|
||||||
filter['direction'] = direction
|
filter['direction'] = direction
|
||||||
|
|
||||||
return await Chats.get_chat_list_by_user_id(
|
chats = await Chats.get_chat_list_by_user_id(
|
||||||
user_id, include_archived=True, filter=filter, skip=skip, limit=limit, db=db
|
user_id, include_archived=True, filter=filter, skip=skip, limit=limit, db=db
|
||||||
)
|
)
|
||||||
|
return await add_active_state_to_chat_list(request, chats)
|
||||||
|
|
||||||
|
|
||||||
############################
|
############################
|
||||||
@@ -738,6 +756,7 @@ async def set_chat_config(form_data: ChatConfigForm, user=Depends(get_admin_user
|
|||||||
|
|
||||||
@router.get('/search', response_model=list[ChatTitleIdResponse])
|
@router.get('/search', response_model=list[ChatTitleIdResponse])
|
||||||
async def search_user_chats(
|
async def search_user_chats(
|
||||||
|
request: Request,
|
||||||
text: str,
|
text: str,
|
||||||
page: int | None = None,
|
page: int | None = None,
|
||||||
user=Depends(get_verified_user),
|
user=Depends(get_verified_user),
|
||||||
@@ -763,7 +782,7 @@ async def search_user_chats(
|
|||||||
log.debug(f'deleting tag: {tag_id}')
|
log.debug(f'deleting tag: {tag_id}')
|
||||||
await Tags.delete_tag_by_name_and_user_id(tag_id, user.id, db=db)
|
await Tags.delete_tag_by_name_and_user_id(tag_id, user.id, db=db)
|
||||||
|
|
||||||
return chat_list
|
return await add_active_state_to_chat_list(request, chat_list)
|
||||||
|
|
||||||
|
|
||||||
############################
|
############################
|
||||||
@@ -786,8 +805,9 @@ async def get_chats_by_folder_id(
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@router.get('/folder/{folder_id}/list')
|
@router.get('/folder/{folder_id}/list', response_model=list[ChatTitleIdResponse])
|
||||||
async def get_chat_list_by_folder_id(
|
async def get_chat_list_by_folder_id(
|
||||||
|
request: Request,
|
||||||
folder_id: str,
|
folder_id: str,
|
||||||
page: int | None = 1,
|
page: int | None = 1,
|
||||||
user=Depends(get_verified_user),
|
user=Depends(get_verified_user),
|
||||||
@@ -798,10 +818,7 @@ async def get_chat_list_by_folder_id(
|
|||||||
skip = (page - 1) * limit
|
skip = (page - 1) * limit
|
||||||
|
|
||||||
chats = await Chats.get_chats_by_folder_id_and_user_id(folder_id, user.id, skip=skip, limit=limit, db=db)
|
chats = await Chats.get_chats_by_folder_id_and_user_id(folder_id, user.id, skip=skip, limit=limit, db=db)
|
||||||
return [
|
return await add_active_state_to_chat_list(request, chats)
|
||||||
{'title': chat.title, 'id': chat.id, 'updated_at': chat.updated_at, 'last_read_at': chat.last_read_at}
|
|
||||||
for chat in chats
|
|
||||||
]
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.exception(e)
|
log.exception(e)
|
||||||
@@ -814,8 +831,11 @@ async def get_chat_list_by_folder_id(
|
|||||||
|
|
||||||
|
|
||||||
@router.get('/pinned', response_model=list[ChatTitleIdResponse])
|
@router.get('/pinned', response_model=list[ChatTitleIdResponse])
|
||||||
async def get_user_pinned_chats(user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session)):
|
async def get_user_pinned_chats(
|
||||||
return await Chats.get_pinned_chats_by_user_id(user.id, db=db)
|
request: Request, user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session)
|
||||||
|
):
|
||||||
|
chats = await Chats.get_pinned_chats_by_user_id(user.id, db=db)
|
||||||
|
return await add_active_state_to_chat_list(request, chats)
|
||||||
|
|
||||||
|
|
||||||
############################
|
############################
|
||||||
@@ -908,6 +928,7 @@ async def get_all_user_chats_in_db(user=Depends(get_admin_user), db: AsyncSessio
|
|||||||
|
|
||||||
@router.get('/archived', response_model=list[ChatTitleIdResponse])
|
@router.get('/archived', response_model=list[ChatTitleIdResponse])
|
||||||
async def get_archived_session_user_chat_list(
|
async def get_archived_session_user_chat_list(
|
||||||
|
request: Request,
|
||||||
page: int | None = None,
|
page: int | None = None,
|
||||||
query: str | None = None,
|
query: str | None = None,
|
||||||
order_by: str | None = None,
|
order_by: str | None = None,
|
||||||
@@ -929,13 +950,14 @@ async def get_archived_session_user_chat_list(
|
|||||||
if direction:
|
if direction:
|
||||||
filter['direction'] = direction
|
filter['direction'] = direction
|
||||||
|
|
||||||
return await Chats.get_archived_chat_list_by_user_id(
|
chats = await Chats.get_archived_chat_list_by_user_id(
|
||||||
user.id,
|
user.id,
|
||||||
filter=filter,
|
filter=filter,
|
||||||
skip=skip,
|
skip=skip,
|
||||||
limit=limit,
|
limit=limit,
|
||||||
db=db,
|
db=db,
|
||||||
)
|
)
|
||||||
|
return await add_active_state_to_chat_list(request, chats)
|
||||||
|
|
||||||
|
|
||||||
############################
|
############################
|
||||||
@@ -1103,6 +1125,7 @@ class TagFilterForm(TagForm):
|
|||||||
|
|
||||||
@router.post('/tags', response_model=list[ChatTitleIdResponse])
|
@router.post('/tags', response_model=list[ChatTitleIdResponse])
|
||||||
async def get_user_chat_list_by_tag_name(
|
async def get_user_chat_list_by_tag_name(
|
||||||
|
request: Request,
|
||||||
form_data: TagFilterForm,
|
form_data: TagFilterForm,
|
||||||
user=Depends(get_verified_user),
|
user=Depends(get_verified_user),
|
||||||
db: AsyncSession = Depends(get_async_session),
|
db: AsyncSession = Depends(get_async_session),
|
||||||
@@ -1113,7 +1136,7 @@ async def get_user_chat_list_by_tag_name(
|
|||||||
if len(chats) == 0:
|
if len(chats) == 0:
|
||||||
await Tags.delete_tag_by_name_and_user_id(form_data.name, user.id, db=db)
|
await Tags.delete_tag_by_name_and_user_id(form_data.name, user.id, db=db)
|
||||||
|
|
||||||
return chats
|
return await add_active_state_to_chat_list(request, chats)
|
||||||
|
|
||||||
|
|
||||||
############################
|
############################
|
||||||
|
|||||||
@@ -75,19 +75,6 @@ def config_updates(data: dict, key_map: dict[str, str]) -> dict:
|
|||||||
##################################
|
##################################
|
||||||
|
|
||||||
|
|
||||||
class ActiveChatsForm(BaseModel):
|
|
||||||
chat_ids: list[str]
|
|
||||||
|
|
||||||
|
|
||||||
@router.post('/active/chats')
|
|
||||||
async def check_active_chats(request: Request, form_data: ActiveChatsForm, user=Depends(get_verified_user)):
|
|
||||||
"""Check which chat IDs have active tasks."""
|
|
||||||
from open_webui.tasks import get_active_chat_ids
|
|
||||||
|
|
||||||
active = await get_active_chat_ids(request.app.state.redis, form_data.chat_ids)
|
|
||||||
return {'active_chat_ids': active}
|
|
||||||
|
|
||||||
|
|
||||||
@router.get('/config')
|
@router.get('/config')
|
||||||
async def get_task_config(request: Request, user=Depends(get_verified_user)):
|
async def get_task_config(request: Request, user=Depends(get_verified_user)):
|
||||||
return await get_config_values(TASK_CONFIG_KEYS)
|
return await get_config_values(TASK_CONFIG_KEYS)
|
||||||
|
|||||||
@@ -199,12 +199,3 @@ async def has_active_tasks(redis, chat_id: str) -> bool:
|
|||||||
"""Check if a chat has any active tasks."""
|
"""Check if a chat has any active tasks."""
|
||||||
task_ids = await list_task_ids_by_item_id(redis, chat_id)
|
task_ids = await list_task_ids_by_item_id(redis, chat_id)
|
||||||
return len(task_ids) > 0
|
return len(task_ids) > 0
|
||||||
|
|
||||||
|
|
||||||
async def get_active_chat_ids(redis, chat_ids: List[str]) -> List[str]:
|
|
||||||
"""Filter a list of chat_ids to only those with active tasks."""
|
|
||||||
active = []
|
|
||||||
for chat_id in chat_ids:
|
|
||||||
if await has_active_tasks(redis, chat_id):
|
|
||||||
active.append(chat_id)
|
|
||||||
return active
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import { WEBUI_API_BASE_URL } from '$lib/constants';
|
|
||||||
|
|
||||||
export const checkActiveChats = async (token: string, chatIds: string[]) => {
|
|
||||||
const res = await fetch(`${WEBUI_API_BASE_URL}/tasks/active/chats`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
Authorization: `Bearer ${token}`
|
|
||||||
},
|
|
||||||
body: JSON.stringify({ chat_ids: chatIds })
|
|
||||||
});
|
|
||||||
if (!res.ok) throw await res.json();
|
|
||||||
return res.json();
|
|
||||||
};
|
|
||||||
@@ -25,10 +25,9 @@
|
|||||||
models,
|
models,
|
||||||
selectedFolder,
|
selectedFolder,
|
||||||
WEBUI_NAME,
|
WEBUI_NAME,
|
||||||
sidebarWidth,
|
sidebarWidth
|
||||||
activeChatIds
|
|
||||||
} from '$lib/stores';
|
} from '$lib/stores';
|
||||||
import { loadNextChatListPage, refreshChatList } from '$lib/stores/chatList';
|
import { loadNextChatListPage, refreshChatList, setChatActive } from '$lib/stores/chatList';
|
||||||
import { onMount, getContext, tick, onDestroy } from 'svelte';
|
import { onMount, getContext, tick, onDestroy } from 'svelte';
|
||||||
|
|
||||||
const i18n = getContext('i18n');
|
const i18n = getContext('i18n');
|
||||||
@@ -52,7 +51,6 @@
|
|||||||
} from '$lib/apis/folders';
|
} from '$lib/apis/folders';
|
||||||
import { createNewNote, getPinnedNoteList, toggleNotePinnedStatusById } from '$lib/apis/notes';
|
import { createNewNote, getPinnedNoteList, toggleNotePinnedStatusById } from '$lib/apis/notes';
|
||||||
import { updateUserSettings } from '$lib/apis/users';
|
import { updateUserSettings } from '$lib/apis/users';
|
||||||
import { checkActiveChats } from '$lib/apis/tasks';
|
|
||||||
import { createNoteHandler } from '$lib/components/notes/utils';
|
import { createNoteHandler } from '$lib/components/notes/utils';
|
||||||
import { WEBUI_API_BASE_URL, WEBUI_BASE_URL } from '$lib/constants';
|
import { WEBUI_API_BASE_URL, WEBUI_BASE_URL } from '$lib/constants';
|
||||||
|
|
||||||
@@ -110,7 +108,7 @@
|
|||||||
let showSharedFolders = false;
|
let showSharedFolders = false;
|
||||||
|
|
||||||
let folders = {};
|
let folders = {};
|
||||||
let folderRegistry = {};
|
let folderRegistry: Record<string, { setFolderItems?: () => unknown }> = {};
|
||||||
|
|
||||||
let newFolderId = null;
|
let newFolderId = null;
|
||||||
|
|
||||||
@@ -366,18 +364,20 @@
|
|||||||
})(),
|
})(),
|
||||||
(async () => {
|
(async () => {
|
||||||
console.log('Init chat list');
|
console.log('Init chat list');
|
||||||
const result = await refreshChatList(localStorage.token, { refreshPinned: true });
|
await refreshChatRows();
|
||||||
if (result.accepted) {
|
|
||||||
await Promise.all(
|
|
||||||
Object.values(folderRegistry).map((folder: any) => folder?.setFolderItems?.())
|
|
||||||
);
|
|
||||||
allChatsLoaded = result.allLoaded;
|
|
||||||
chatListReady = true;
|
|
||||||
}
|
|
||||||
})()
|
})()
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const refreshChatRows = async () => {
|
||||||
|
const result = await refreshChatList(localStorage.token, { refreshPinned: true });
|
||||||
|
if (result.accepted) {
|
||||||
|
await Promise.all(Object.values(folderRegistry).map((folder) => folder?.setFolderItems?.()));
|
||||||
|
allChatsLoaded = result.allLoaded;
|
||||||
|
chatListReady = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const loadMoreChats = async () => {
|
const loadMoreChats = async () => {
|
||||||
chatListLoading = true;
|
chatListLoading = true;
|
||||||
|
|
||||||
@@ -610,17 +610,6 @@
|
|||||||
await initChannels();
|
await initChannels();
|
||||||
}
|
}
|
||||||
await initChatList();
|
await initChatList();
|
||||||
|
|
||||||
// Check which chats have active tasks
|
|
||||||
const allChatIds = [...$chats.map((c) => c.id), ...$pinnedChats.map((c) => c.id)];
|
|
||||||
if (allChatIds.length > 0) {
|
|
||||||
try {
|
|
||||||
const res = await checkActiveChats(localStorage.token, allChatIds);
|
|
||||||
activeChatIds.set(new Set(res.active_chat_ids || []));
|
|
||||||
} catch (e) {
|
|
||||||
console.debug('Failed to check active chats:', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
settings.subscribe((value) => {
|
settings.subscribe((value) => {
|
||||||
@@ -649,6 +638,7 @@
|
|||||||
|
|
||||||
const socketInstance = $socket;
|
const socketInstance = $socket;
|
||||||
socketInstance?.on('events', chatActiveEventHandler);
|
socketInstance?.on('events', chatActiveEventHandler);
|
||||||
|
socketInstance?.on('connect', refreshChatRows);
|
||||||
|
|
||||||
await tick();
|
await tick();
|
||||||
initPinnedMenuSortable();
|
initPinnedMenuSortable();
|
||||||
@@ -672,28 +662,24 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
socketInstance?.off('events', chatActiveEventHandler);
|
socketInstance?.off('events', chatActiveEventHandler);
|
||||||
|
socketInstance?.off('connect', refreshChatRows);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handler for chat events (defined outside onMount for proper cleanup)
|
// Handler for chat events (defined outside onMount for proper cleanup)
|
||||||
const chatActiveEventHandler = (event: {
|
const chatActiveEventHandler = async (event: {
|
||||||
chat_id: string;
|
chat_id: string;
|
||||||
message_id: string;
|
message_id: string;
|
||||||
data: { type: string; data: any };
|
data: { type: string; data: { active?: boolean } };
|
||||||
}) => {
|
}) => {
|
||||||
if (event.data?.type === 'chat:active') {
|
if (event.data?.type === 'chat:active') {
|
||||||
const { active } = event.data.data;
|
const active = event.data.data.active ?? false;
|
||||||
activeChatIds.update((ids) => {
|
const found = setChatActive(event.chat_id, active);
|
||||||
const newSet = new Set(ids);
|
if (!found && active) {
|
||||||
if (active) {
|
await refreshChatRows();
|
||||||
newSet.add(event.chat_id);
|
}
|
||||||
} else {
|
|
||||||
newSet.delete(event.chat_id);
|
|
||||||
}
|
|
||||||
return newSet;
|
|
||||||
});
|
|
||||||
} else if (event.data?.type === 'chat:list') {
|
} else if (event.data?.type === 'chat:list') {
|
||||||
initChatList();
|
refreshChatRows();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1416,6 +1402,7 @@
|
|||||||
createdAt={chat.created_at}
|
createdAt={chat.created_at}
|
||||||
updatedAt={chat.updated_at}
|
updatedAt={chat.updated_at}
|
||||||
lastReadAt={chat.last_read_at}
|
lastReadAt={chat.last_read_at}
|
||||||
|
active={chat.active ?? false}
|
||||||
{shiftKey}
|
{shiftKey}
|
||||||
selected={selectedChatId === chat.id}
|
selected={selectedChatId === chat.id}
|
||||||
on:select={() => {
|
on:select={() => {
|
||||||
@@ -1479,6 +1466,7 @@
|
|||||||
createdAt={chat.created_at}
|
createdAt={chat.created_at}
|
||||||
updatedAt={chat.updated_at}
|
updatedAt={chat.updated_at}
|
||||||
lastReadAt={chat.last_read_at}
|
lastReadAt={chat.last_read_at}
|
||||||
|
active={chat.active ?? false}
|
||||||
{shiftKey}
|
{shiftKey}
|
||||||
selected={selectedChatId === chat.id}
|
selected={selectedChatId === chat.id}
|
||||||
on:select={() => {
|
on:select={() => {
|
||||||
|
|||||||
@@ -26,7 +26,6 @@
|
|||||||
showSidebar,
|
showSidebar,
|
||||||
tags,
|
tags,
|
||||||
selectedFolder,
|
selectedFolder,
|
||||||
activeChatIds,
|
|
||||||
settings,
|
settings,
|
||||||
user
|
user
|
||||||
} from '$lib/stores';
|
} from '$lib/stores';
|
||||||
@@ -58,6 +57,7 @@
|
|||||||
export let createdAt: number | null = null;
|
export let createdAt: number | null = null;
|
||||||
export let updatedAt: number | null = null;
|
export let updatedAt: number | null = null;
|
||||||
export let lastReadAt: number | null = null;
|
export let lastReadAt: number | null = null;
|
||||||
|
export let active = false;
|
||||||
|
|
||||||
export let selected = false;
|
export let selected = false;
|
||||||
export let shiftKey = false;
|
export let shiftKey = false;
|
||||||
@@ -104,7 +104,7 @@
|
|||||||
|
|
||||||
$: unread =
|
$: unread =
|
||||||
id !== $chatId &&
|
id !== $chatId &&
|
||||||
!$activeChatIds.has(id) &&
|
!active &&
|
||||||
(effectiveReadAt === null || (updatedAt !== null && updatedAt > effectiveReadAt));
|
(effectiveReadAt === null || (updatedAt !== null && updatedAt > effectiveReadAt));
|
||||||
$: showInlineActions = id === $chatId || confirmEdit || mouseOver || selected;
|
$: showInlineActions = id === $chatId || confirmEdit || mouseOver || selected;
|
||||||
|
|
||||||
@@ -545,7 +545,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<!-- Loading spinner for active chat (left side) -->
|
<!-- Loading spinner for active chat (left side) -->
|
||||||
{#if $activeChatIds.has(id)}
|
{#if active}
|
||||||
<div class="shrink-0 self-center pr-2">
|
<div class="shrink-0 self-center pr-2">
|
||||||
<Spinner className="size-3" />
|
<Spinner className="size-3" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -721,6 +721,7 @@
|
|||||||
createdAt={chat.created_at}
|
createdAt={chat.created_at}
|
||||||
updatedAt={chat.updated_at}
|
updatedAt={chat.updated_at}
|
||||||
lastReadAt={chat.last_read_at}
|
lastReadAt={chat.last_read_at}
|
||||||
|
active={chat.active ?? false}
|
||||||
ownerName={folders[folderId]?.shared ? (chat.owner_name ?? null) : null}
|
ownerName={folders[folderId]?.shared ? (chat.owner_name ?? null) : null}
|
||||||
ownerUserId={folders[folderId]?.shared && chat.owner_name ? chat.user_id : null}
|
ownerUserId={folders[folderId]?.shared && chat.owner_name ? chat.user_id : null}
|
||||||
readonly={chat.user_id !== $user?.id}
|
readonly={chat.user_id !== $user?.id}
|
||||||
|
|||||||
@@ -104,6 +104,7 @@
|
|||||||
title={chat.title}
|
title={chat.title}
|
||||||
createdAt={chat.created_at}
|
createdAt={chat.created_at}
|
||||||
updatedAt={chat.updated_at}
|
updatedAt={chat.updated_at}
|
||||||
|
active={chat.active ?? false}
|
||||||
ownerName={chat.owner_name}
|
ownerName={chat.owner_name}
|
||||||
ownerUserId={chat.user_id}
|
ownerUserId={chat.user_id}
|
||||||
readonly={chat.readonly ?? !isWritable}
|
readonly={chat.readonly ?? !isWritable}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { getChatList, getPinnedChatList } from '$lib/apis/chats';
|
|||||||
|
|
||||||
type ChatListItem = {
|
type ChatListItem = {
|
||||||
id: string;
|
id: string;
|
||||||
[key: string]: any;
|
[key: string]: unknown;
|
||||||
};
|
};
|
||||||
|
|
||||||
const chatsStore = writable<ChatListItem[] | null>(null);
|
const chatsStore = writable<ChatListItem[] | null>(null);
|
||||||
@@ -90,6 +90,21 @@ export const loadNextChatListPage = async (token: string = ''): Promise<ChatList
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const setChatActive = (chatId: string, active: boolean): boolean => {
|
||||||
|
let found = false;
|
||||||
|
const updateChat = (chat: ChatListItem) => {
|
||||||
|
if (chat.id !== chatId) {
|
||||||
|
return chat;
|
||||||
|
}
|
||||||
|
found = true;
|
||||||
|
return { ...chat, active };
|
||||||
|
};
|
||||||
|
|
||||||
|
chatsStore.update((items) => (items ? items.map(updateChat) : items));
|
||||||
|
pinnedChatsStore.update((items) => items.map(updateChat));
|
||||||
|
return found;
|
||||||
|
};
|
||||||
|
|
||||||
export const resetChatListState = () => {
|
export const resetChatListState = () => {
|
||||||
requestGeneration += 1;
|
requestGeneration += 1;
|
||||||
currentPage = 1;
|
currentPage = 1;
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ export const mobile = writable(false);
|
|||||||
export const socket: Writable<null | Socket> = writable(null);
|
export const socket: Writable<null | Socket> = writable(null);
|
||||||
export const socketConnected: Writable<boolean> = writable(true);
|
export const socketConnected: Writable<boolean> = writable(true);
|
||||||
export const activeUserIds: Writable<null | string[]> = writable(null);
|
export const activeUserIds: Writable<null | string[]> = writable(null);
|
||||||
export const activeChatIds: Writable<Set<string>> = writable(new Set());
|
|
||||||
export const USAGE_POOL: Writable<null | string[]> = writable(null);
|
export const USAGE_POOL: Writable<null | string[]> = writable(null);
|
||||||
|
|
||||||
export const theme = writable('system');
|
export const theme = writable('system');
|
||||||
|
|||||||
Reference in new issue
Block a user