aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--packages/mobile/assets/blur.jpegbin0 -> 178818 bytes
-rw-r--r--packages/mobile/components/bookmarks/BookmarkCard.tsx58
-rw-r--r--packages/mobile/components/ui/Skeleton.tsx38
-rw-r--r--packages/mobile/package.json1
-rw-r--r--pnpm-lock.yaml12
5 files changed, 103 insertions, 6 deletions
diff --git a/packages/mobile/assets/blur.jpeg b/packages/mobile/assets/blur.jpeg
new file mode 100644
index 00000000..387ce697
--- /dev/null
+++ b/packages/mobile/assets/blur.jpeg
Binary files differ
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