diff options
| author | MohamedBassem <me@mbassem.com> | 2024-03-13 02:43:57 +0000 |
|---|---|---|
| committer | MohamedBassem <me@mbassem.com> | 2024-03-13 02:43:57 +0000 |
| commit | be518d51eaf57a1f0e04b7608d9a4362a516eb85 (patch) | |
| tree | 26661e3383c495006720d212304556072ad8f006 /packages | |
| parent | f1d86812e9a045b474f4a1c8cd3621fe17b8b806 (diff) | |
| download | karakeep-be518d51eaf57a1f0e04b7608d9a4362a516eb85.tar.zst | |
mobile: All bookmarks homepage
Diffstat (limited to 'packages')
| -rw-r--r-- | packages/mobile/app/_layout.tsx | 2 | ||||
| -rw-r--r-- | packages/mobile/app/dashboard/(tabs)/_layout.tsx | 2 | ||||
| -rw-r--r-- | packages/mobile/app/dashboard/(tabs)/index.tsx | 5 | ||||
| -rw-r--r-- | packages/mobile/app/sharing.tsx | 4 | ||||
| -rw-r--r-- | packages/mobile/components/bookmarks/BookmarkCard.tsx | 124 | ||||
| -rw-r--r-- | packages/mobile/components/bookmarks/BookmarkList.tsx | 48 |
6 files changed, 182 insertions, 3 deletions
diff --git a/packages/mobile/app/_layout.tsx b/packages/mobile/app/_layout.tsx index e8244867..063ad58e 100644 --- a/packages/mobile/app/_layout.tsx +++ b/packages/mobile/app/_layout.tsx @@ -2,11 +2,11 @@ import "@/globals.css"; import "expo-dev-client"; import { useRouter } from "expo-router"; +import { Stack } from "expo-router/stack"; import { useShareIntent } from "expo-share-intent"; import { StatusBar } from "expo-status-bar"; import { useEffect } from "react"; import { View } from "react-native"; -import { Stack } from "expo-router/stack"; import { Providers } from "@/lib/providers"; diff --git a/packages/mobile/app/dashboard/(tabs)/_layout.tsx b/packages/mobile/app/dashboard/(tabs)/_layout.tsx index 49d95b35..c15f37f1 100644 --- a/packages/mobile/app/dashboard/(tabs)/_layout.tsx +++ b/packages/mobile/app/dashboard/(tabs)/_layout.tsx @@ -1,6 +1,6 @@ -import React from "react"; import { Tabs } from "expo-router"; import { Home, Settings } from "lucide-react-native"; +import React from "react"; export default function TabLayout() { return ( diff --git a/packages/mobile/app/dashboard/(tabs)/index.tsx b/packages/mobile/app/dashboard/(tabs)/index.tsx index d043a9c4..28fa39de 100644 --- a/packages/mobile/app/dashboard/(tabs)/index.tsx +++ b/packages/mobile/app/dashboard/(tabs)/index.tsx @@ -1,8 +1,11 @@ import { View } from "react-native"; +import BookmarkList from "@/components/bookmarks/BookmarkList"; + export default function Home() { return ( - <View className="flex h-full items-center justify-center gap-4 px-4"> + <View> + <BookmarkList archived={false} /> </View> ); } diff --git a/packages/mobile/app/sharing.tsx b/packages/mobile/app/sharing.tsx index c9b6c0bb..89f1a168 100644 --- a/packages/mobile/app/sharing.tsx +++ b/packages/mobile/app/sharing.tsx @@ -22,9 +22,13 @@ function SaveBookmark({ setMode }: { setMode: (mode: Mode) => void }) { return null; }, [params]); + const invalidateAllBookmarks = + api.useUtils().bookmarks.getBookmarks.invalidate; + useEffect(() => { if (!isPending && shareIntent?.text) { mutate({ type: "link", url: shareIntent.text }); + invalidateAllBookmarks(); } }, []); diff --git a/packages/mobile/components/bookmarks/BookmarkCard.tsx b/packages/mobile/components/bookmarks/BookmarkCard.tsx new file mode 100644 index 00000000..17e675df --- /dev/null +++ b/packages/mobile/components/bookmarks/BookmarkCard.tsx @@ -0,0 +1,124 @@ +import { ZBookmark } from "@hoarder/trpc/types/bookmarks"; +import { ZBookmarkTags } from "@hoarder/trpc/types/tags"; +import { Star, Archive, Trash } from "lucide-react-native"; +import { View, Text, Image, ScrollView, Pressable } from "react-native"; + +import { api } from "@/lib/trpc"; + +function ActionBar({ bookmark }: { bookmark: ZBookmark }) { + const apiUtils = api.useUtils(); + + const { mutate: deleteBookmark } = api.bookmarks.deleteBookmark.useMutation({ + onSuccess: () => { + apiUtils.bookmarks.getBookmarks.invalidate(); + }, + }); + const { mutate: updateBookmark, variables } = + api.bookmarks.updateBookmark.useMutation({ + onSuccess: () => { + apiUtils.bookmarks.getBookmarks.invalidate(); + apiUtils.bookmarks.getBookmark.invalidate({ bookmarkId: bookmark.id }); + }, + }); + + return ( + <View className="flex flex-row gap-4"> + <Pressable + onPress={() => + updateBookmark({ + bookmarkId: bookmark.id, + favourited: !bookmark.favourited, + }) + } + > + {(variables ? variables.favourited : bookmark.favourited) ? ( + <Star fill="#ebb434" color="#ebb434" /> + ) : ( + <Star /> + )} + </Pressable> + <Pressable + onPress={() => + updateBookmark({ + bookmarkId: bookmark.id, + archived: !bookmark.archived, + }) + } + > + <Archive /> + </Pressable> + <Pressable + onPress={() => + deleteBookmark({ + bookmarkId: bookmark.id, + }) + } + > + <Trash /> + </Pressable> + </View> + ); +} + +function TagList({ tags }: { tags: ZBookmarkTags[] }) { + return ( + <ScrollView horizontal showsHorizontalScrollIndicator={false}> + <View className="flex flex-row gap-2"> + {tags.map((t) => ( + <View className="rounded-full border border-gray-200 px-2.5 py-0.5 text-xs font-semibold"> + <Text>{t.name}</Text> + </View> + ))} + </View> + </ScrollView> + ); +} + +function LinkCard({ bookmark }: { bookmark: ZBookmark }) { + if (bookmark.content.type !== "link") { + throw new Error("Wrong content type rendered"); + } + + const parsedUrl = new URL(bookmark.content.url); + + return ( + <View className="flex gap-2 rounded bg-white p-4"> + <Image + source={{ uri: bookmark.content.imageUrl || "" }} + className="h-56 min-h-56 w-full" + /> + <Text className="line-clamp-2 text-xl font-bold"> + {bookmark.content.title || parsedUrl.host} + </Text> + <TagList tags={bookmark.tags} /> + <View className="mt-2 flex flex-row justify-between"> + <Text className="my-auto line-clamp-1">{parsedUrl.host}</Text> + <ActionBar bookmark={bookmark} /> + </View> + </View> + ); +} + +function TextCard({ bookmark }: { bookmark: ZBookmark }) { + return <View />; +} + +export default function BookmarkCard({ + bookmark: initialData, +}: { + bookmark: ZBookmark; +}) { + const { data: bookmark } = api.bookmarks.getBookmark.useQuery( + { + bookmarkId: initialData.id, + }, + { initialData }, + ); + + switch (bookmark.content.type) { + case "link": + return <LinkCard bookmark={bookmark} />; + case "text": + return <TextCard bookmark={bookmark} />; + } +} diff --git a/packages/mobile/components/bookmarks/BookmarkList.tsx b/packages/mobile/components/bookmarks/BookmarkList.tsx new file mode 100644 index 00000000..bb4b8668 --- /dev/null +++ b/packages/mobile/components/bookmarks/BookmarkList.tsx @@ -0,0 +1,48 @@ +import { useEffect, useState } from "react"; +import { FlatList } from "react-native"; + +import BookmarkCard from "./BookmarkCard"; + +import { api } from "@/lib/trpc"; + +export default function BookmarkList({ + favourited, + archived, +}: { + favourited?: boolean; + archived?: boolean; +}) { + const apiUtils = api.useUtils(); + const [refreshing, setRefreshing] = useState(false); + const { data, isPending, isPlaceholderData } = + api.bookmarks.getBookmarks.useQuery({ + favourited, + archived, + }); + + useEffect(() => { + setRefreshing(isPending || isPlaceholderData); + }, [isPending, isPlaceholderData]); + + if (isPending || !data) { + // TODO: Add a spinner + return; + } + + const onRefresh = () => { + apiUtils.bookmarks.getBookmarks.invalidate(); + apiUtils.bookmarks.getBookmark.invalidate(); + }; + + return ( + <FlatList + contentContainerStyle={{ + gap: 10, + }} + renderItem={(b) => <BookmarkCard key={b.item.id} bookmark={b.item} />} + data={data.bookmarks} + refreshing={refreshing} + onRefresh={onRefresh} + /> + ); +} |
