personal web client for Bluesky
typescript
solidjs
bluesky
atcute
1import { createSignal } from 'solid-js';
2
3import { createBookmarkFeedQuery, createBookmarkFolderMetaQuery } from '~/api/queries/bookmark-feed';
4
5import { useParams, useTitle } from '~/lib/navigation/router';
6
7import BookmarkFeedItem from '~/components/bookmarks/bookmark-feed-item';
8import MagnifyingGlassOutlinedIcon from '~/components/icons-central/magnifying-glass-outline';
9import * as Page from '~/components/page';
10import PagedList from '~/components/paged-list';
11import VirtualItem from '~/components/virtual-item';
12
13const BookmarksPage = () => {
14 const { tagId } = useParams();
15
16 const [search, setSearch] = createSignal('');
17
18 const meta = tagId !== 'all' ? createBookmarkFolderMetaQuery(() => tagId) : undefined;
19 const listing = createBookmarkFeedQuery(() => tagId, search);
20
21 useTitle(() => {
22 if (tagId === 'all') {
23 return `All Bookmarks — ${import.meta.env.VITE_APP_NAME}`;
24 }
25
26 const data = meta?.data;
27 if (data) {
28 return `Bookmarks (${data.name}) — ${import.meta.env.VITE_APP_NAME}`;
29 }
30
31 return `Bookmarks — ${import.meta.env.VITE_APP_NAME}`;
32 });
33
34 return (
35 <>
36 <Page.Header>
37 <Page.HeaderAccessory>
38 <Page.Back to="/bookmarks" />
39 </Page.HeaderAccessory>
40
41 <Page.Heading title={tagId === 'all' ? `All Bookmarks` : meta?.data ? meta.data.name : `Bookmarks`} />
42 </Page.Header>
43
44 <form
45 onSubmit={(ev) => {
46 ev.preventDefault();
47
48 const input = ev.currentTarget.elements.namedItem('search') as HTMLInputElement;
49 setSearch(input.value);
50 }}
51 class="px-4 pb-1 pt-0"
52 >
53 <div class="relative h-max grow">
54 <div class="pointer-events-none absolute inset-y-0 ml-px grid w-10 place-items-center">
55 <MagnifyingGlassOutlinedIcon class="text-lg text-contrast-muted" />
56 </div>
57
58 <input
59 type="search"
60 name="search"
61 value={search()}
62 placeholder="Search"
63 class="h-10 w-full rounded-full border border-outline-md bg-background px-3 pl-10 text-sm text-contrast outline-2 -outline-offset-2 outline-accent placeholder:text-contrast-muted focus:outline"
64 />
65 </div>
66 </form>
67
68 <PagedList
69 data={listing.data?.pages.map((page) => page.items)}
70 error={listing.error}
71 render={(item) => {
72 return (
73 <VirtualItem estimateHeight={99}>
74 <BookmarkFeedItem item={item} />
75 </VirtualItem>
76 );
77 }}
78 hasNextPage={listing.hasNextPage}
79 isFetchingNextPage={listing.isFetchingNextPage || listing.isLoading}
80 onEndReached={() => listing.fetchNextPage()}
81 />
82 </>
83 );
84};
85
86export default BookmarksPage;