diff options
| -rw-r--r-- | packages/mobile/assets/blur.jpeg | bin | 0 -> 178818 bytes | |||
| -rw-r--r-- | packages/mobile/components/bookmarks/BookmarkCard.tsx | 58 | ||||
| -rw-r--r-- | packages/mobile/components/ui/Skeleton.tsx | 38 | ||||
| -rw-r--r-- | packages/mobile/package.json | 1 | ||||
| -rw-r--r-- | pnpm-lock.yaml | 12 |
5 files changed, 103 insertions, 6 deletions
diff --git a/packages/mobile/assets/blur.jpeg b/packages/mobile/assets/blur.jpeg Binary files differnew file mode 100644 index 00000000..387ce697 --- /dev/null +++ b/packages/mobile/assets/blur.jpeg diff --git a/packages/mobile/components/bookmarks/BookmarkCard.tsx b/packages/mobile/components/bookmarks/BookmarkCard.tsx index 607c2fc8..30c6724a 100644 --- a/packages/mobile/components/bookmarks/BookmarkCard.tsx +++ b/packages/mobile/components/bookmarks/BookmarkCard.tsx @@ -1,11 +1,33 @@ 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 Markdown from "react-native-markdown-display"; +import { Skeleton } from "../ui/Skeleton"; + import { api } from "@/lib/trpc"; +const MAX_LOADING_MSEC = 30 * 1000; + +export function isBookmarkStillCrawling(bookmark: ZBookmark) { + return ( + bookmark.content.type === "link" && + !bookmark.content.crawledAt && + Date.now().valueOf() - bookmark.createdAt.valueOf() < MAX_LOADING_MSEC + ); +} + +export function isBookmarkStillTagging(bookmark: ZBookmark) { + return ( + bookmark.taggingStatus === "pending" && + Date.now().valueOf() - bookmark.createdAt.valueOf() < MAX_LOADING_MSEC + ); +} + +export function isBookmarkStillLoading(bookmark: ZBookmark) { + return isBookmarkStillTagging(bookmark) || isBookmarkStillCrawling(bookmark); +} + function ActionBar({ bookmark }: { bookmark: ZBookmark }) { const apiUtils = api.useUtils(); @@ -61,7 +83,18 @@ function ActionBar({ bookmark }: { bookmark: ZBookmark }) { ); } -function TagList({ tags }: { tags: ZBookmarkTags[] }) { +function TagList({ bookmark }: { bookmark: ZBookmark }) { + const tags = bookmark.tags; + + if (isBookmarkStillTagging(bookmark)) { + return ( + <> + <Skeleton className="h-4 w-full" /> + <Skeleton className="h-4 w-full" /> + </> + ); + } + return ( <ScrollView horizontal showsHorizontalScrollIndicator={false}> <View className="flex flex-row gap-2"> @@ -91,7 +124,7 @@ function LinkCard({ bookmark }: { bookmark: ZBookmark }) { className="h-56 min-h-56 w-full" /> ) : ( - <View className="h-56" /> + <Image source={require("@/assets/blur.jpeg")} className="h-56 w-full" /> ); return ( @@ -101,7 +134,7 @@ function LinkCard({ bookmark }: { bookmark: ZBookmark }) { <Text className="line-clamp-2 text-xl font-bold"> {bookmark.content.title || parsedUrl.host} </Text> - <TagList tags={bookmark.tags} /> + <TagList bookmark={bookmark} /> <View className="mt-2 flex flex-row justify-between"> <Text className="my-auto line-clamp-1">{parsedUrl.host}</Text> <ActionBar bookmark={bookmark} /> @@ -120,7 +153,7 @@ function TextCard({ bookmark }: { bookmark: ZBookmark }) { <View className="max-h-56 overflow-hidden pb-2"> <Markdown>{bookmark.content.text}</Markdown> </View> - <TagList tags={bookmark.tags} /> + <TagList bookmark={bookmark} /> <View className="flex flex-row justify-between"> <View /> <ActionBar bookmark={bookmark} /> @@ -138,7 +171,20 @@ export default function BookmarkCard({ { bookmarkId: initialData.id, }, - { initialData }, + { + initialData, + refetchInterval: (query) => { + const data = query.state.data; + if (!data) { + return false; + } + // If the link is not crawled or not tagged + if (isBookmarkStillLoading(data)) { + return 1000; + } + return false; + }, + }, ); let comp; diff --git a/packages/mobile/components/ui/Skeleton.tsx b/packages/mobile/components/ui/Skeleton.tsx new file mode 100644 index 00000000..68b22e1e --- /dev/null +++ b/packages/mobile/components/ui/Skeleton.tsx @@ -0,0 +1,38 @@ +import { useEffect, useRef } from "react"; +import { Animated, type View } from "react-native"; + +import { cn } from "@/lib/utils"; + +function Skeleton({ + className, + ...props +}: { className?: string } & React.ComponentPropsWithoutRef<typeof View>) { + const fadeAnim = useRef(new Animated.Value(0.5)).current; + + useEffect(() => { + Animated.loop( + Animated.sequence([ + Animated.timing(fadeAnim, { + toValue: 1, + duration: 1000, + useNativeDriver: true, + }), + Animated.timing(fadeAnim, { + toValue: 0.5, + duration: 1000, + useNativeDriver: true, + }), + ]), + ).start(); + }, [fadeAnim]); + + return ( + <Animated.View + className={cn("bg-muted rounded-md", className)} + style={[{ opacity: fadeAnim }]} + {...props} + /> + ); +} + +export { Skeleton }; diff --git a/packages/mobile/package.json b/packages/mobile/package.json index 613bb3c3..2d43346b 100644 --- a/packages/mobile/package.json +++ b/packages/mobile/package.json @@ -17,6 +17,7 @@ "expo-config-plugin-ios-share-extension": "^0.0.4", "expo-constants": "~15.4.5", "expo-dev-client": "^3.3.9", + "expo-image": "^1.10.6", "expo-linking": "~6.2.2", "expo-router": "~3.4.8", "expo-secure-store": "^12.8.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b11597fa..df5602d9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -214,6 +214,9 @@ importers: expo-dev-client: specifier: ^3.3.9 version: 3.3.9(expo@50.0.11) + expo-image: + specifier: ^1.10.6 + version: 1.10.6(expo@50.0.11) expo-linking: specifier: ~6.2.2 version: 6.2.2(expo@50.0.11) @@ -9244,6 +9247,15 @@ packages: fontfaceobserver: 2.3.0 dev: false + /expo-image@1.10.6(expo@50.0.11): + resolution: {integrity: sha512-vcnAIym1eU8vQgV1re1E7rVQZStJimBa4aPDhjFfzMzbddAF7heJuagyewiUkTzbZUwYzPaZAie6VJPyWx9Ueg==} + peerDependencies: + expo: '*' + dependencies: + '@react-native/assets-registry': 0.73.1 + expo: 50.0.11(@babel/core@7.23.9)(@react-native/babel-preset@0.73.21) + dev: false + /expo-json-utils@0.12.3: resolution: {integrity: sha512-4pypQdinpNc6XY9wsZk56njvzDh+B/9mISr7FPP3CVk1QGB1nSLh883/BCDSgnsephATZkC5HG+cdE60kCAR6A==} dev: false |
