-
Notifications
You must be signed in to change notification settings - Fork 12
/
PhotoList.svelte
115 lines (94 loc) · 2.92 KB
/
PhotoList.svelte
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
<script lang="ts">
import Avatar from '$lib/components/Avatar.svelte';
import Image from '$lib/components/Image.svelte';
import Metadata from '$lib/components/Metadata.svelte';
import Scroller from '$lib/components/Scroller.svelte';
import Modal from '$lib/components/Modal.svelte';
import PhotoPage from '../../routes/[account]/[photo]/+page.svelte';
import { init_photos, state } from '$lib/state.js';
import { ago, now } from '$lib/utils.js';
import { page } from '$app/stores';
import { createEventDispatcher } from 'svelte';
import { goto, preloadData, pushState } from '$app/navigation';
import type { PhotoListItem } from '$lib/types';
export let photos: PhotoListItem[];
export let next: string | null;
export let endpoint: string;
export function capture() {
return scroller.capture();
}
export function restore(values: any) {
scroller.restore(values);
}
const dispatch = createEventDispatcher();
let scroller: Scroller;
let loading = false;
$: init_photos(photos);
</script>
<Scroller
bind:this={scroller}
items={photos}
on:more={async () => {
if (loading || !next) return;
loading = true;
const response = await fetch(`${endpoint}?start=${next}`);
const result = await response.json();
dispatch('loaded', result);
loading = false;
}}
>
<div slot="header" class="max-w-2xl px-4 mx-auto">
<slot name="header" />
</div>
<div slot="item" class="max-w-2xl px-4 mx-auto" let:item let:i>
<div class="my-8">
<a
href="/{item.name}/{item.id}"
class="block"
style="transform: rotate({0.5 + 1 * (i % 2 ? -1 : 1)}deg)"
on:click={async (e) => {
if (e.metaKey || innerWidth < 640) return;
e.preventDefault();
const { href } = e.currentTarget;
const result = await preloadData(href);
if (result.type === 'loaded' && result.status === 200) {
pushState({ selected: result.data }, href);
} else {
// something bad happened! try navigating
goto(href);
}
}}
>
<Image photo={item} />
</a>
<span class="flex text-sm mt-4 mb-2 h-8 justify-between gap-4 text-gray-500">
<span class="flex items-center gap-2">
<Avatar name={item.name} avatar={item.avatar} full />
<span>
<span class="hidden sm:inline">posted</span>
{ago(new Date(item.created_at), $now)}
</span>
</span>
<Metadata photo={item} />
</span>
<span>{item.description}</span>
</div>
</div>
<div slot="empty">
<slot name="empty" />
</div>
<div slot="footer" class="max-w-2xl px-4 mb-8 mx-auto text-right">
{#if next}
<a class="text-pink-600" href="{$page.url.pathname}?start={next}">next page</a>
{/if}
</div>
</Scroller>
{#if $page.state.selected}
<Modal on:close={() => history.back()}>
<div class="w-screen height-screen max-w-4xl max-h-[144rem] p-8">
<div class="flex flex-col bg-white dark:bg-zinc-800 shadow-xl p-8 w-sc rounded-md">
<PhotoPage data={$page.state.selected} />
</div>
</div>
</Modal>
{/if}