feat: add annotations, user profiles, watchers, stories, search and more

This commit is contained in:
h
2026-06-01 17:15:09 +02:00
parent ed469ba8dd
commit 2465bcd184
47 changed files with 5009 additions and 242 deletions
@@ -0,0 +1,113 @@
<script lang="ts">
import { untrack } from "svelte";
import { type AvatarKind, loadAvatarVariant } from "$lib/api/avatars";
import { getAvatarHistory } from "$lib/api/endpoints";
import type { AvatarHistoryView } from "$lib/api/types";
import { formatFull } from "$lib/format/datetime";
import { accounts } from "$lib/stores/accounts.svelte";
interface Props {
id: number;
kind: AvatarKind;
name: string;
}
let { kind, id, name }: Props = $props();
let items = $state<AvatarHistoryView[]>([]);
const urls = $state<Record<string, string | null>>({});
$effect(() => {
if (accounts.selectedId === null) {
return;
}
let active = true;
items = [];
getAvatarHistory(kind, id)
.then((result) => {
if (active) {
items = result;
}
})
.catch(() => undefined);
return () => {
active = false;
};
});
$effect(() => {
const list = items;
let active = true;
untrack(() => {
for (const item of list) {
if (!(item.downloaded && urls[item.unique_id] === undefined)) {
continue;
}
urls[item.unique_id] = null;
loadAvatarVariant(kind, id, item.unique_id).then((url) => {
if (active) {
urls[item.unique_id] = url;
}
});
}
});
return () => {
active = false;
};
});
</script>
{#if items.length > 0}
<section>
<h3>История аватарок ({items.length})</h3>
<div class="grid">
{#each items as item (item.unique_id)}
<figure title={formatFull(item.first_seen_at)}>
{#if urls[item.unique_id]}
<img src={urls[item.unique_id]} alt={name}>
{:else}
<div class="placeholder"></div>
{/if}
</figure>
{/each}
</div>
</section>
{/if}
<style lang="scss">
section {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
h3 {
margin: 0;
font-size: 0.75rem;
font-weight: var(--font-weight-medium);
color: var(--color-text-secondary);
text-transform: uppercase;
letter-spacing: 0.03em;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(4rem, 1fr));
gap: 0.375rem;
}
figure {
aspect-ratio: 1;
margin: 0;
overflow: hidden;
border-radius: 0.5rem;
background-color: var(--color-background-secondary);
}
img,
.placeholder {
width: 100%;
height: 100%;
object-fit: cover;
}
</style>