diff options
| -rw-r--r-- | apps/mobile/components/bookmarks/BookmarkCard.tsx | 81 | ||||
| -rw-r--r-- | apps/mobile/package.json | 4 | ||||
| -rw-r--r-- | pnpm-lock.yaml | 25 |
3 files changed, 103 insertions, 7 deletions
diff --git a/apps/mobile/components/bookmarks/BookmarkCard.tsx b/apps/mobile/components/bookmarks/BookmarkCard.tsx index 52d39c5c..7329aa46 100644 --- a/apps/mobile/components/bookmarks/BookmarkCard.tsx +++ b/apps/mobile/components/bookmarks/BookmarkCard.tsx @@ -6,15 +6,19 @@ import { Platform, Pressable, ScrollView, + Share, Text, View, } from "react-native"; +import * as Clipboard from "expo-clipboard"; +import * as FileSystem from "expo-file-system"; import * as Haptics from "expo-haptics"; import { router, useRouter } from "expo-router"; +import * as Sharing from "expo-sharing"; import useAppSettings from "@/lib/settings"; import { api } from "@/lib/trpc"; import { MenuView } from "@react-native-menu/menu"; -import { Ellipsis, Star } from "lucide-react-native"; +import { Ellipsis, Share2, Star } from "lucide-react-native"; import type { ZBookmark } from "@karakeep/shared/types/bookmarks"; import { @@ -37,6 +41,7 @@ import TagPill from "./TagPill"; function ActionBar({ bookmark }: { bookmark: ZBookmark }) { const { toast } = useToast(); + const { settings } = useAppSettings(); const onError = () => { toast({ @@ -86,6 +91,71 @@ function ActionBar({ bookmark }: { bookmark: ZBookmark }) { ], ); + const handleShare = async () => { + try { + switch (bookmark.content.type) { + case BookmarkTypes.LINK: + await Share.share({ + url: bookmark.content.url, + message: bookmark.content.url, + }); + break; + + case BookmarkTypes.TEXT: + await Clipboard.setStringAsync(bookmark.content.text); + toast({ + message: "Text copied to clipboard", + showProgress: false, + }); + break; + + case BookmarkTypes.ASSET: + if (bookmark.content.assetType === "image") { + if (await Sharing.isAvailableAsync()) { + const assetUrl = `${settings.address}/api/assets/${bookmark.content.assetId}`; + const fileUri = `${FileSystem.documentDirectory}temp_image.jpg`; + + const downloadResult = await FileSystem.downloadAsync( + assetUrl, + fileUri, + { + headers: { + Authorization: `Bearer ${settings.apiKey}`, + }, + }, + ); + + if (downloadResult.status === 200) { + await Sharing.shareAsync(downloadResult.uri); + // Clean up the temporary file + await FileSystem.deleteAsync(downloadResult.uri, { + idempotent: true, + }); + } else { + throw new Error("Failed to download image"); + } + } + } else { + // For PDFs, share the URL + const assetUrl = `${settings.address}/api/assets/${bookmark.content.assetId}`; + await Share.share({ + url: assetUrl, + message: + bookmark.title || bookmark.content.fileName || "PDF Document", + }); + } + break; + } + } catch (error) { + console.error("Share error:", error); + toast({ + message: "Failed to share", + variant: "destructive", + showProgress: false, + }); + } + }; + return ( <View className="flex flex-row gap-4"> {(isArchivePending || isDeletionPending) && <ActivityIndicator />} @@ -105,6 +175,15 @@ function ActionBar({ bookmark }: { bookmark: ZBookmark }) { )} </Pressable> + <Pressable + onPress={() => { + Haptics.selectionAsync(); + handleShare(); + }} + > + <Share2 color="gray" /> + </Pressable> + <MenuView onPressAction={({ nativeEvent }) => { Haptics.selectionAsync(); diff --git a/apps/mobile/package.json b/apps/mobile/package.json index d5c2262f..00010ba6 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -29,6 +29,7 @@ "expo-clipboard": "^7.0.1", "expo-constants": "~17.0.8", "expo-dev-client": "^5.0.20", + "expo-file-system": "~18.0.12", "expo-haptics": "^14.0.1", "expo-image": "^2.0.7", "expo-image-picker": "^16.0.6", @@ -36,7 +37,8 @@ "expo-navigation-bar": "^4.0.9", "expo-router": "~4.0.21", "expo-secure-store": "^14.0.1", - "expo-share-intent": "3.0.0", + "expo-share-intent": "3.2.3", + "expo-sharing": "~13.0.1", "expo-status-bar": "~2.0.1", "expo-system-ui": "^4.0.9", "expo-web-browser": "^14.0.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cc95ef3b..f09ed740 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -347,6 +347,9 @@ importers: expo-dev-client: specifier: ^5.0.20 version: 5.0.20(expo@52.0.46(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@expo/metro-runtime@4.0.1(react-native@0.76.9(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@types/react@18.3.12)(encoding@0.1.13)(react@18.3.1)))(encoding@0.1.13)(react-native-webview@13.14.1(react-native@0.76.9(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@types/react@18.3.12)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.9(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@types/react@18.3.12)(encoding@0.1.13)(react@18.3.1))(react@18.3.1)) + expo-file-system: + specifier: ~18.0.12 + version: 18.0.12(expo@52.0.46(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@expo/metro-runtime@4.0.1(react-native@0.76.9(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@types/react@18.3.12)(encoding@0.1.13)(react@18.3.1)))(encoding@0.1.13)(react-native-webview@13.14.1(react-native@0.76.9(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@types/react@18.3.12)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.9(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@types/react@18.3.12)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.9(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@types/react@18.3.12)(encoding@0.1.13)(react@18.3.1)) expo-haptics: specifier: ^14.0.1 version: 14.0.1(expo@52.0.46(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@expo/metro-runtime@4.0.1(react-native@0.76.9(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@types/react@18.3.12)(encoding@0.1.13)(react@18.3.1)))(encoding@0.1.13)(react-native-webview@13.14.1(react-native@0.76.9(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@types/react@18.3.12)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.9(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@types/react@18.3.12)(encoding@0.1.13)(react@18.3.1))(react@18.3.1)) @@ -369,8 +372,11 @@ importers: specifier: ^14.0.1 version: 14.0.1(expo@52.0.46(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@expo/metro-runtime@4.0.1(react-native@0.76.9(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@types/react@18.3.12)(encoding@0.1.13)(react@18.3.1)))(encoding@0.1.13)(react-native-webview@13.14.1(react-native@0.76.9(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@types/react@18.3.12)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.9(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@types/react@18.3.12)(encoding@0.1.13)(react@18.3.1))(react@18.3.1)) expo-share-intent: - specifier: 3.0.0 - version: 3.0.0(fjl2ls6nygrtgt5knmtjde2ww4) + specifier: 3.2.3 + version: 3.2.3(fjl2ls6nygrtgt5knmtjde2ww4) + expo-sharing: + specifier: ~13.0.1 + version: 13.0.1(expo@52.0.46(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@expo/metro-runtime@4.0.1(react-native@0.76.9(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@types/react@18.3.12)(encoding@0.1.13)(react@18.3.1)))(encoding@0.1.13)(react-native-webview@13.14.1(react-native@0.76.9(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@types/react@18.3.12)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.9(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@types/react@18.3.12)(encoding@0.1.13)(react@18.3.1))(react@18.3.1)) expo-status-bar: specifier: ~2.0.1 version: 2.0.1(react-native@0.76.9(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@types/react@18.3.12)(encoding@0.1.13)(react@18.3.1))(react@18.3.1) @@ -8202,8 +8208,8 @@ packages: peerDependencies: expo: '*' - expo-share-intent@3.0.0: - resolution: {integrity: sha512-uYgJ7QzdspU3ohCuJUepCu/0TtKjqcOxZTvY0gDA8PZu1ATq66AHY1CF424NSQsWFT6QaQdRp1uOC/F/tvPYBw==} + expo-share-intent@3.2.3: + resolution: {integrity: sha512-U0KxSeXv3f8yirYOMcYQJRXakE3uVZVEGXvujZzMdYbwHnMUKf02EJqMLkeK4xRwAfdkiQB7V8AAjy+4RAdIqg==} peerDependencies: expo: ^52 expo-constants: '>=17.0.2' @@ -8211,6 +8217,11 @@ packages: react: '*' react-native: '*' + expo-sharing@13.0.1: + resolution: {integrity: sha512-qych3Nw65wlFcnzE/gRrsdtvmdV0uF4U4qVMZBJYPG90vYyWh2QM9rp1gVu0KWOBc7N8CC2dSVYn4/BXqJy6Xw==} + peerDependencies: + expo: '*' + expo-status-bar@2.0.1: resolution: {integrity: sha512-AkIPX7jWHRPp83UBZ1iXtVvyr0g+DgBVvIXTtlmPtmUsm8Vq9Bb5IGj86PW8osuFlgoTVAg7HI/+Ok7yEYwiRg==} peerDependencies: @@ -24499,7 +24510,7 @@ snapshots: dependencies: expo: 52.0.46(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@expo/metro-runtime@4.0.1(react-native@0.76.9(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@types/react@18.3.12)(encoding@0.1.13)(react@18.3.1)))(encoding@0.1.13)(react-native-webview@13.14.1(react-native@0.76.9(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@types/react@18.3.12)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.9(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@types/react@18.3.12)(encoding@0.1.13)(react@18.3.1))(react@18.3.1) - expo-share-intent@3.0.0(fjl2ls6nygrtgt5knmtjde2ww4): + expo-share-intent@3.2.3(fjl2ls6nygrtgt5knmtjde2ww4): dependencies: '@expo/config-plugins': 9.1.7 expo: 52.0.46(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@expo/metro-runtime@4.0.1(react-native@0.76.9(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@types/react@18.3.12)(encoding@0.1.13)(react@18.3.1)))(encoding@0.1.13)(react-native-webview@13.14.1(react-native@0.76.9(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@types/react@18.3.12)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.9(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@types/react@18.3.12)(encoding@0.1.13)(react@18.3.1))(react@18.3.1) @@ -24510,6 +24521,10 @@ snapshots: transitivePeerDependencies: - supports-color + expo-sharing@13.0.1(expo@52.0.46(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@expo/metro-runtime@4.0.1(react-native@0.76.9(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@types/react@18.3.12)(encoding@0.1.13)(react@18.3.1)))(encoding@0.1.13)(react-native-webview@13.14.1(react-native@0.76.9(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@types/react@18.3.12)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.9(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@types/react@18.3.12)(encoding@0.1.13)(react@18.3.1))(react@18.3.1)): + dependencies: + expo: 52.0.46(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@expo/metro-runtime@4.0.1(react-native@0.76.9(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@types/react@18.3.12)(encoding@0.1.13)(react@18.3.1)))(encoding@0.1.13)(react-native-webview@13.14.1(react-native@0.76.9(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@types/react@18.3.12)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.9(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@types/react@18.3.12)(encoding@0.1.13)(react@18.3.1))(react@18.3.1) + expo-status-bar@2.0.1(react-native@0.76.9(@babel/core@7.26.0)(@babel/preset-env@7.27.2(@babel/core@7.26.0))(@types/react@18.3.12)(encoding@0.1.13)(react@18.3.1))(react@18.3.1): dependencies: react: 18.3.1 |
