aboutsummaryrefslogtreecommitdiffstats
path: root/apps/mobile/components
diff options
context:
space:
mode:
authorxuatz <xzlow10@gmail.com>2025-07-27 06:18:13 +0900
committerGitHub <noreply@github.com>2025-07-26 22:18:13 +0100
commit8db89bb09964b9aceb709e6bab5ff632f1cc3c1c (patch)
treedb16730fe60fe0ac232bb4126ea9f87dce9cbfd6 /apps/mobile/components
parent154efe17421ca96d433fcc1f820ad460e1675bdc (diff)
downloadkarakeep-8db89bb09964b9aceb709e6bab5ff632f1cc3c1c.tar.zst
feat(mobile): add context aware sharing option in mobile app (#1785)
* v1 inside menu * v2 outside menu with share icon
Diffstat (limited to 'apps/mobile/components')
-rw-r--r--apps/mobile/components/bookmarks/BookmarkCard.tsx81
1 files changed, 80 insertions, 1 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();