105 lines
3.0 KiB
Svelte
105 lines
3.0 KiB
Svelte
<script lang="ts">
|
|
import { cubicOut } from "svelte/easing";
|
|
import { fly } from "svelte/transition";
|
|
import { goto } from "$app/navigation";
|
|
import { page } from "$app/state";
|
|
import ChatListItem from "$lib/components/ChatListItem.svelte";
|
|
import EmptyState from "$lib/components/ui/EmptyState.svelte";
|
|
import Skeleton from "$lib/components/ui/Skeleton.svelte";
|
|
import { folderContains } from "$lib/format/folders";
|
|
import { accounts } from "$lib/stores/accounts.svelte";
|
|
import { chats } from "$lib/stores/chats.svelte";
|
|
import { folders } from "$lib/stores/folders.svelte";
|
|
import { toasts } from "$lib/stores/toasts.svelte";
|
|
|
|
const skeletonRows = Array.from({ length: 9 }, (_, index) => index);
|
|
|
|
const activeChatId = $derived(
|
|
page.params.chatId ? Number(page.params.chatId) : null
|
|
);
|
|
|
|
const selectedFolder = $derived(folders.selected);
|
|
const visibleChats = $derived(
|
|
selectedFolder === null
|
|
? chats.list
|
|
: chats.list.filter((chat) => folderContains(selectedFolder, chat))
|
|
);
|
|
|
|
const SCROLL_THRESHOLD = 600;
|
|
|
|
$effect(() => {
|
|
if (accounts.selectedId === null) {
|
|
return;
|
|
}
|
|
chats.load().catch(() => toasts.error("Failed to load chats"));
|
|
folders.load().catch(() => toasts.error("Failed to load folders"));
|
|
});
|
|
|
|
function onScroll(event: Event) {
|
|
const el = event.currentTarget as HTMLElement;
|
|
if (el.scrollTop + el.clientHeight >= el.scrollHeight - SCROLL_THRESHOLD) {
|
|
chats.loadMore().catch(() => undefined);
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<div class="chat-list custom-scroll" onscroll={onScroll}>
|
|
{#if chats.loading && chats.list.length === 0}
|
|
{#each skeletonRows as index (index)}
|
|
<div class="row-skeleton">
|
|
<Skeleton width="3rem" height="3rem" circle />
|
|
<div class="row-skeleton-lines">
|
|
<Skeleton width="55%" height="0.875rem" />
|
|
<Skeleton width="80%" height="0.8125rem" />
|
|
</div>
|
|
</div>
|
|
{/each}
|
|
{:else if chats.list.length === 0}
|
|
<EmptyState title="No chats yet" />
|
|
{:else}
|
|
{#key folders.selectedId}
|
|
<div
|
|
class="folder-view"
|
|
in:fly={{ x: folders.direction * 24, duration: 200, easing: cubicOut }}
|
|
>
|
|
{#if visibleChats.length === 0 && !chats.hasMore}
|
|
<EmptyState
|
|
title="Empty folder"
|
|
description="No chats match this folder yet"
|
|
/>
|
|
{:else}
|
|
{#each visibleChats as chat (chat.chat_id)}
|
|
<ChatListItem
|
|
{chat}
|
|
selected={chat.chat_id === activeChatId}
|
|
onclick={() => goto(`/app/${chat.chat_id}`)}
|
|
/>
|
|
{/each}
|
|
{/if}
|
|
</div>
|
|
{/key}
|
|
{/if}
|
|
</div>
|
|
|
|
<style lang="scss">
|
|
.chat-list {
|
|
overflow-y: auto;
|
|
flex: 1;
|
|
padding: 0.25rem 0.4375rem;
|
|
}
|
|
|
|
.row-skeleton {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.625rem;
|
|
padding: 0.5625rem 0.5rem;
|
|
}
|
|
|
|
.row-skeleton-lines {
|
|
display: flex;
|
|
flex: 1;
|
|
flex-direction: column;
|
|
gap: 0.5rem;
|
|
}
|
|
</style>
|