This commit is contained in:
Shibo Lyu 2024-08-28 17:08:32 +08:00
commit a6b8910b8e
69 changed files with 5445 additions and 0 deletions

33
src/routes/+layout.svelte Normal file
View file

@ -0,0 +1,33 @@
<script>
import '../app.css';
import { page } from '$app/stores';
import { onNavigate } from '$app/navigation';
import ChatList from './ChatList.svelte';
onNavigate((navigation) => {
if (!document.startViewTransition) return;
return new Promise((resolve) => {
document.startViewTransition(async () => {
resolve();
await navigation.complete;
});
});
});
</script>
<div
class="group h-screen sm:flex sm:items-stretch"
data-weblah-main-visible={$page.params.chatId ? 'true' : undefined}
>
<aside
class="h-screen min-h-0 overflow-hidden border-slate-300 bg-white shadow-lg sm:w-1/3 sm:border-e lg:w-1/4 dark:bg-black"
>
<ChatList />
</aside>
<main
class="absolute inset-0 w-full translate-x-[110vw] bg-slate-100 shadow-lg transition-transform duration-300 group-data-[weblah-main-visible]:translate-x-0 sm:relative sm:flex-1 sm:translate-x-0 dark:bg-slate-900"
>
<slot></slot>
</main>
</div>

8
src/routes/+page.svelte Normal file
View file

@ -0,0 +1,8 @@
<script>
import BgPattern from '$lib/components/BgPattern.svelte';
import ServiceMessage from '$lib/components/ServiceMessage.svelte';
</script>
<BgPattern class="flex h-screen flex-col items-center justify-center gap-4">
<ServiceMessage class="text-sm">Select a chat to start messaging</ServiceMessage>
</BgPattern>

View file

@ -0,0 +1,72 @@
<script>
import ChatListHeader from './ChatListHeader.svelte';
import ChatListItem from './ChatListItem.svelte';
</script>
<div class="flex h-screen flex-col justify-stretch">
<ChatListHeader />
<div class="min-h-0 flex-1 overflow-y-auto">
<ul>
<ChatListItem
chat={{
id: '1',
name: 'septs',
lastMessage: {
content: '验证 checksum 是否正确的代价还是可以接受的',
date: new Date('2024-08-28T02:54Z')
}
}}
/>
<ChatListItem
chat={{
id: '2',
name: 'oxa',
lastMessage: {
content: '但似乎现在大家都讨厌 pgp ,觉得太复杂',
date: new Date('2024-08-28T02:37Z')
}
}}
/>
<ChatListItem
chat={{
id: '3',
name: 'omo',
lastMessage: {
content: '我對 revalidate 的理解是不經過 cache 直接重拉一遍',
date: new Date('2024-08-28T02:11Z')
}
}}
/>
<ChatListItem
chat={{
id: '4',
name: 'Inno Aiolos',
lastMessage: {
content: '至少得把信息分发给所有广播自己是这个public key的destination',
date: new Date('2024-07-28T02:11Z')
}
}}
/>
<ChatListItem
chat={{
id: '5',
name: 'Gary です',
lastMessage: {
content: '没必要8长毛象那样挺麻烦的',
date: new Date('2023-07-28T02:11Z')
}
}}
/>
<ChatListItem
chat={{
id: '6',
name: 'Chtholly Nota Seniorious',
lastMessage: {
content: '遥遥领先!\n隔壁 nostr 最开始没有注意到这个问题,然后被狂灌置顶 spam',
date: new Date('2022-07-28T02:11Z')
}
}}
/>
</ul>
</div>
</div>

View file

@ -0,0 +1,47 @@
<script lang="ts">
import { AvatarBeam } from 'svelte-boring-avatars';
</script>
<header class="flex items-center justify-stretch gap-2 border-b border-slate-200 p-2 shadow-sm">
<div><AvatarBeam size={30} name="Shibo Lyu" /></div>
<div
class="flex flex-1 items-center gap-1 rounded-md bg-slate-50 px-2 py-1 ring-1 ring-slate-100"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="size-5 text-slate-400"
>
<path
fill-rule="evenodd"
d="M10.5 3.75a6.75 6.75 0 1 0 0 13.5 6.75 6.75 0 0 0 0-13.5ZM2.25 10.5a8.25 8.25 0 1 1 14.59 5.28l4.69 4.69a.75.75 0 1 1-1.06 1.06l-4.69-4.69A8.25 8.25 0 0 1 2.25 10.5Z"
clip-rule="evenodd"
/>
</svg>
<input
type="text"
placeholder="Search"
class="w-full flex-1 bg-transparent text-sm leading-4 text-slate-900 focus:outline-none"
/>
</div>
<button
class="flex size-7 cursor-default items-center justify-center rounded-md text-slate-500 ring-1 ring-slate-100 transition-shadow duration-200 hover:shadow-sm"
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="size-5"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0 1 15.75 21H5.25A2.25 2.25 0 0 1 3 18.75V8.25A2.25 2.25 0 0 1 5.25 6H10"
/>
</svg>
<span class="sr-only">Compose</span>
</button>
</header>

View file

@ -0,0 +1,49 @@
<script lang="ts">
import { AvatarBeam } from 'svelte-boring-avatars';
export let chat: { id: string; name: string; lastMessage: { content: string; date: Date } };
const sameDayFormatter = new Intl.DateTimeFormat('default', {
hour: '2-digit',
minute: '2-digit'
});
const sameYearFormatter = new Intl.DateTimeFormat('default', {
month: 'short',
day: 'numeric'
});
const otherYearFormatter = new Intl.DateTimeFormat('default', {
year: 'numeric',
month: 'short',
day: 'numeric'
});
const formatDate = (date: Date) => {
const now = new Date();
if (date.getFullYear() === now.getFullYear()) {
if (date.getMonth() === now.getMonth() && date.getDate() === now.getDate()) {
return sameDayFormatter.format(date);
} else {
return sameYearFormatter.format(date);
}
} else {
return otherYearFormatter.format(date);
}
};
</script>
<li
class="relative after:absolute after:bottom-0 after:end-0 after:start-14 after:border-t after:border-slate-100"
>
<a href="/chats/{chat.id}" class="flex h-16 cursor-default items-center gap-2 px-2">
<div class="size-10">
<AvatarBeam size={40} name={chat.name} />
</div>
<div class="relative h-16 min-w-0 flex-1 space-y-0.5 py-1">
<div class="flex items-center">
<h3 class="flex-1 truncate text-sm font-semibold">{chat.name}</h3>
<time class="truncate text-xs text-gray-500">{formatDate(chat.lastMessage.date)}</time>
</div>
<p class="line-clamp-2 text-xs text-gray-500">{chat.lastMessage.content}</p>
</div>
</a>
</li>

View file

@ -0,0 +1,5 @@
<script lang="ts">
import { page } from '$app/stores';
</script>
<p>Chat History Page for {$page.params.chatId}</p>