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