diff options
Diffstat (limited to 'apps/mobile/app')
| -rw-r--r-- | apps/mobile/app/dashboard/bookmarks/[slug]/info.tsx | 154 |
1 files changed, 153 insertions, 1 deletions
diff --git a/apps/mobile/app/dashboard/bookmarks/[slug]/info.tsx b/apps/mobile/app/dashboard/bookmarks/[slug]/info.tsx index 1781ec74..15e2a082 100644 --- a/apps/mobile/app/dashboard/bookmarks/[slug]/info.tsx +++ b/apps/mobile/app/dashboard/bookmarks/[slug]/info.tsx @@ -1,11 +1,12 @@ import React from "react"; -import { Alert, Pressable, View } from "react-native"; +import { ActivityIndicator, Alert, Pressable, View } from "react-native"; import { KeyboardAwareScrollView, KeyboardGestureArea, } from "react-native-keyboard-controller"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { router, Stack, useLocalSearchParams } from "expo-router"; +import BookmarkTextMarkdown from "@/components/bookmarks/BookmarkTextMarkdown"; import TagPill from "@/components/bookmarks/TagPill"; import FullPageError from "@/components/FullPageError"; import { Button } from "@/components/ui/Button"; @@ -17,10 +18,12 @@ import { Skeleton } from "@/components/ui/Skeleton"; import { Text } from "@/components/ui/Text"; import { useToast } from "@/components/ui/Toast"; import { cn } from "@/lib/utils"; +import { ChevronUp, RefreshCw, Sparkles, Trash2 } from "lucide-react-native"; import { useAutoRefreshingBookmarkQuery, useDeleteBookmark, + useSummarizeBookmark, useUpdateBookmark, } from "@karakeep/shared-react/hooks/bookmarks"; import { BookmarkTypes, ZBookmark } from "@karakeep/shared/types/bookmarks"; @@ -138,6 +141,154 @@ function NotesEditor({ ); } +function AISummarySection({ bookmark }: { bookmark: ZBookmark }) { + const { toast } = useToast(); + const [isExpanded, setIsExpanded] = React.useState(false); + + const { mutate: summarize, isPending: isSummarizing } = useSummarizeBookmark({ + onSuccess: () => { + toast({ + message: "Summary generated successfully!", + showProgress: false, + }); + }, + onError: () => { + toast({ + message: "Failed to generate summary", + showProgress: false, + }); + }, + }); + + const { mutate: resummarize, isPending: isResummarizing } = + useSummarizeBookmark({ + onSuccess: () => { + toast({ + message: "Summary regenerated successfully!", + showProgress: false, + }); + }, + onError: () => { + toast({ + message: "Failed to regenerate summary", + showProgress: false, + }); + }, + }); + + const { mutate: updateBookmark, isPending: isDeletingSummary } = + useUpdateBookmark({ + onSuccess: () => { + toast({ + message: "Summary deleted!", + showProgress: false, + }); + }, + onError: () => { + toast({ + message: "Failed to delete summary", + showProgress: false, + }); + }, + }); + + // Only show for LINK bookmarks + if (bookmark.content.type !== BookmarkTypes.LINK) { + return null; + } + + // If there's a summary, show it + if (bookmark.summary) { + return ( + <InfoSection> + <View className={isExpanded ? "" : "max-h-20 overflow-hidden"}> + <BookmarkTextMarkdown text={bookmark.summary} /> + </View> + {!isExpanded && ( + <Pressable + onPress={() => setIsExpanded(true)} + className="rounded-md bg-gray-100 py-2 dark:bg-gray-800" + > + <Text className="text-center text-sm font-medium text-gray-600 dark:text-gray-400"> + Show more + </Text> + </Pressable> + )} + {isExpanded && ( + <View className="mt-2 flex flex-row justify-end gap-2"> + <Pressable + onPress={() => resummarize({ bookmarkId: bookmark.id })} + disabled={isResummarizing} + className="rounded-full bg-gray-200 p-2 dark:bg-gray-700" + > + {isResummarizing ? ( + <ActivityIndicator size="small" /> + ) : ( + <RefreshCw + size={16} + className="text-gray-600 dark:text-gray-400" + /> + )} + </Pressable> + <Pressable + onPress={() => + updateBookmark({ bookmarkId: bookmark.id, summary: null }) + } + disabled={isDeletingSummary} + className="rounded-full bg-gray-200 p-2 dark:bg-gray-700" + > + {isDeletingSummary ? ( + <ActivityIndicator size="small" /> + ) : ( + <Trash2 + size={16} + className="text-gray-600 dark:text-gray-400" + /> + )} + </Pressable> + <Pressable + onPress={() => setIsExpanded(false)} + className="rounded-full bg-gray-200 p-2 dark:bg-gray-700" + > + <ChevronUp + size={16} + className="text-gray-600 dark:text-gray-400" + /> + </Pressable> + </View> + )} + </InfoSection> + ); + } + + // If no summary, show button to generate one + return ( + <InfoSection> + <Pressable + onPress={() => summarize({ bookmarkId: bookmark.id })} + disabled={isSummarizing} + className="rounded-lg bg-purple-500 p-3 dark:bg-purple-600" + > + <View className="flex flex-row items-center justify-center gap-2"> + {isSummarizing ? ( + <> + <ActivityIndicator size="small" color="#fff" /> + <Text className="font-medium text-white"> + Generating summary... + </Text> + </> + ) : ( + <> + <Text className="font-medium text-white">Summarize with AI</Text> + <Sparkles size={16} color="#fff" /> + </> + )} + </View> + </Pressable> + </InfoSection> + ); +} + const ViewBookmarkPage = () => { const insets = useSafeAreaInsets(); const { slug } = useLocalSearchParams(); @@ -266,6 +417,7 @@ const ViewBookmarkPage = () => { } isPending={isEditPending} /> + <AISummarySection bookmark={bookmark} /> <TagList bookmark={bookmark} /> <ManageLists bookmark={bookmark} /> <NotesEditor |
