aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/mobile/components/bookmarks/BookmarkCard.tsx81
-rw-r--r--apps/mobile/package.json4
-rw-r--r--pnpm-lock.yaml25
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