tangled
alpha
login
or
join now
zeu.dev
/
potatonet-app
12
fork
atom
Read-it-later social network
12
fork
atom
overview
issues
pulls
pipelines
update styles, implement explore home page
zeu.dev
4 months ago
fda2d81c
6b8a1f8f
+73
-6
4 changed files
expand all
collapse all
unified
split
src
lib
components
BookmarkCard.svelte
utils.ts
routes
+page.svelte
api
bookmarks
data.remote.ts
+3
-3
src/lib/components/BookmarkCard.svelte
···
11
11
let { isOwner, bookmark, onTagClick, onTagDeleteClick }: BookmarkCardProps = $props();
12
12
</script>
13
13
14
14
-
<article class="flex flex-col gap-4 border border-dashed hover:border-solid px-4 py-3 w-fit">
15
15
-
<a href={bookmark.subject} class="hover:cursor-pointer text-sm">{bookmark.subject}</a>
14
14
+
<article class="flex flex-col gap-4 border border-dashed hover:border-solid hover:shadow-lg px-4 py-3 w-fit">
15
15
+
<a href={bookmark.subject} class="hover:cursor-pointer text-xl visited:text-violet-600">{bookmark.subject}</a>
16
16
{#if bookmark.tags && bookmark.tags.length > 0}
17
17
<div class="flex gap-5">
18
18
{#each bookmark.tags as tag}
···
27
27
{/if}
28
28
<button
29
29
onclick={() => onTagClick(tag)}
30
30
-
class="bg-gray-200 w-fit px-2 py-1 hover:cursor-pointer"
30
30
+
class="bg-gray-200 w-fit px-2 py-1 hover:cursor-pointer font-comico text-sm"
31
31
>
32
32
{tag}
33
33
</button>
-2
src/lib/utils.ts
···
1
1
-
import { atclient } from "./atproto";
2
2
-
3
1
// --- UTILITIES ---
4
2
export type CommonSliceFields = {
5
3
indexedAt: string;
+59
-1
src/routes/+page.svelte
···
1
1
+
<script lang="ts">
2
2
+
import BookmarkCard from "$lib/components/BookmarkCard.svelte";
3
3
+
import { getAllBookmarks } from "./api/bookmarks/data.remote";
4
4
+
5
5
+
let { data } = $props();
6
6
+
let cursor = $state("");
7
7
+
const userBookmarksQuery = $derived(getAllBookmarks({ cursor }));
8
8
+
9
9
+
let query = $state("");
10
10
+
let filterTags = $state<string[]>([]);
11
11
+
12
12
+
function onTagClick(tag: string) {
13
13
+
const index = filterTags.findIndex((t) => t === tag);
14
14
+
if (index >= 0) { filterTags.splice(index, 1); }
15
15
+
else { filterTags.push(tag);
16
16
+
}
17
17
+
}
18
18
+
19
19
+
function onTagDeleteClick(tag: string) {
20
20
+
console.log("DELETE", tag);
21
21
+
}
22
22
+
</script>
23
23
+
1
24
<h1 class="text-3xl font-bold font-comico">explore</h1>
2
2
-
<p>coming soon...</p>
25
25
+
26
26
+
{#if userBookmarksQuery.loading}
27
27
+
<p>Loading...</p>
28
28
+
{:else if userBookmarksQuery.error}
29
29
+
<p>Error</p>
30
30
+
{:else}
31
31
+
{@const { cursor: returnedCursor, bookmarks } = userBookmarksQuery.current || { cursor: "", bookmarks: []}}
32
32
+
33
33
+
<div class="sticky top-0 flex flex-col gap-4 pt-4 bg-white z-50">
34
34
+
<menu class="flex justify-between font-comico">
35
35
+
<div class="flex gap-4">
36
36
+
<label class="flex items-center gap-2">
37
37
+
Search term:
38
38
+
<input type="text" bind:value={query} class="font-neco border px-2 py-1" placeholder="recipe" />
39
39
+
</label>
40
40
+
41
41
+
<label class="flex items-center gap-2">
42
42
+
Tags:
43
43
+
{#each filterTags as filtered}
44
44
+
<button onclick={() => onTagClick(filtered)}>{filtered}</button>
45
45
+
{/each}
46
46
+
</label>
47
47
+
<button onclick={() => userBookmarksQuery.refresh()}>Refresh</button>
48
48
+
</div>
49
49
+
</menu>
50
50
+
<hr />
51
51
+
</div>
52
52
+
53
53
+
<div class="flex flex-wrap gap-4">
54
54
+
{#each bookmarks as bookmark}
55
55
+
{#if bookmark.subject.includes(query) && (bookmark.tags?.some(t => filterTags.length > 0 ? filterTags.includes(t) : true))}
56
56
+
<BookmarkCard isOwner={false} {bookmark} {onTagClick} {onTagDeleteClick} />
57
57
+
{/if}
58
58
+
{/each}
59
59
+
</div>
60
60
+
{/if}
+11
src/routes/api/bookmarks/data.remote.ts
···
23
23
24
24
return { cursor: data.cursor, bookmarks: data.records.map((r) => r.value )};
25
25
});
26
26
+
27
27
+
28
28
+
const GetAllBookmarksValidator = v.object({
29
29
+
cursor: v.optional(v.string())
30
30
+
});
31
31
+
32
32
+
export const getAllBookmarks = query(GetAllBookmarksValidator, async ({ cursor }) => {
33
33
+
const data = await LexiconBookmarkSlicesAPI.getList({ cursor });
34
34
+
35
35
+
return { cursor: data.cursor, bookmarks: data.records.map((r) => r.value )};
36
36
+
});