aboutsummaryrefslogtreecommitdiffstats
path: root/apps/mobile
diff options
context:
space:
mode:
authorMohamedBassem <me@mbassem.com>2025-08-31 16:09:12 +0100
committerMohamedBassem <me@mbassem.com>2025-08-31 16:09:12 +0100
commitbe7311a7db8c9dcc373090b06b825995a3682ee4 (patch)
tree40285d4094f00f931059f5fc73a867f0742821e8 /apps/mobile
parent1e0cce7e6f79ccea00fc740aabc2b05918d17984 (diff)
downloadkarakeep-be7311a7db8c9dcc373090b06b825995a3682ee4.tar.zst
fix(mobile): Fix text bookmark editor
Diffstat (limited to 'apps/mobile')
-rw-r--r--apps/mobile/app/dashboard/bookmarks/[slug]/index.tsx399
-rw-r--r--apps/mobile/components/bookmarks/BookmarkAssetView.tsx56
-rw-r--r--apps/mobile/components/bookmarks/BookmarkLinkTypeSelector.tsx85
-rw-r--r--apps/mobile/components/bookmarks/BookmarkLinkView.tsx35
-rw-r--r--apps/mobile/components/bookmarks/BookmarkTextView.tsx112
-rw-r--r--apps/mobile/components/bookmarks/BottomActions.tsx136
6 files changed, 444 insertions, 379 deletions
diff --git a/apps/mobile/app/dashboard/bookmarks/[slug]/index.tsx b/apps/mobile/app/dashboard/bookmarks/[slug]/index.tsx
index 3b1300ca..7bf0f118 100644
--- a/apps/mobile/app/dashboard/bookmarks/[slug]/index.tsx
+++ b/apps/mobile/app/dashboard/bookmarks/[slug]/index.tsx
@@ -1,384 +1,24 @@
import { useState } from "react";
-import {
- Alert,
- Keyboard,
- Linking,
- Pressable,
- ScrollView,
- View,
-} from "react-native";
-import ImageView from "react-native-image-viewing";
-import * as Haptics from "expo-haptics";
-import { Stack, useLocalSearchParams, useRouter } from "expo-router";
-import BookmarkAssetImage from "@/components/bookmarks/BookmarkAssetImage";
-import {
- BookmarkLinkArchivePreview,
- BookmarkLinkBrowserPreview,
- BookmarkLinkReaderPreview,
- BookmarkLinkScreenshotPreview,
-} from "@/components/bookmarks/BookmarkLinkPreview";
-import BookmarkTextMarkdown from "@/components/bookmarks/BookmarkTextMarkdown";
-import { PDFViewer } from "@/components/bookmarks/PDFViewer";
+import { KeyboardAvoidingView } from "react-native";
+import { useSafeAreaInsets } from "react-native-safe-area-context";
+import { Stack, useLocalSearchParams } from "expo-router";
+import BookmarkAssetView from "@/components/bookmarks/BookmarkAssetView";
+import BookmarkLinkTypeSelector, {
+ BookmarkLinkType,
+} from "@/components/bookmarks/BookmarkLinkTypeSelector";
+import BookmarkLinkView from "@/components/bookmarks/BookmarkLinkView";
+import BookmarkTextView from "@/components/bookmarks/BookmarkTextView";
+import BottomActions from "@/components/bookmarks/BottomActions";
import FullPageError from "@/components/FullPageError";
-import { TailwindResolver } from "@/components/TailwindResolver";
-import { Button } from "@/components/ui/Button";
-import CustomSafeAreaView from "@/components/ui/CustomSafeAreaView";
import FullPageSpinner from "@/components/ui/FullPageSpinner";
-import { Input } from "@/components/ui/Input";
-import { Text } from "@/components/ui/Text";
-import { useToast } from "@/components/ui/Toast";
-import { useAssetUrl } from "@/lib/hooks";
import useAppSettings from "@/lib/settings";
import { api } from "@/lib/trpc";
-import { MenuView } from "@react-native-menu/menu";
-import {
- ChevronDown,
- ClipboardList,
- Globe,
- Info,
- Tag,
- Trash2,
-} from "lucide-react-native";
import { useColorScheme } from "nativewind";
-import {
- useDeleteBookmark,
- useUpdateBookmark,
-} from "@karakeep/shared-react/hooks/bookmarks";
-import { BookmarkTypes, ZBookmark } from "@karakeep/shared/types/bookmarks";
+import { BookmarkTypes } from "@karakeep/shared/types/bookmarks";
-type BookmarkLinkType = "browser" | "reader" | "screenshot" | "archive";
-
-function getAvailableViewTypes(bookmark: ZBookmark): BookmarkLinkType[] {
- if (bookmark.content.type !== BookmarkTypes.LINK) {
- return [];
- }
-
- const availableTypes: BookmarkLinkType[] = ["browser", "reader"];
-
- if (bookmark.assets.some((asset) => asset.assetType === "screenshot")) {
- availableTypes.push("screenshot");
- }
-
- if (
- bookmark.assets.some(
- (asset) =>
- asset.assetType === "precrawledArchive" ||
- asset.assetType === "fullPageArchive",
- )
- ) {
- availableTypes.push("archive");
- }
-
- return availableTypes;
-}
-
-function BookmarkLinkTypeSelector({
- type,
- onChange,
- bookmark,
-}: {
- type: BookmarkLinkType;
- onChange: (type: BookmarkLinkType) => void;
- bookmark: ZBookmark;
-}) {
- const availableTypes = getAvailableViewTypes(bookmark);
-
- const allActions = [
- {
- id: "reader" as const,
- title: "Reader View",
- state: type === "reader" ? ("on" as const) : undefined,
- },
- {
- id: "browser" as const,
- title: "Browser",
- state: type === "browser" ? ("on" as const) : undefined,
- },
- {
- id: "screenshot" as const,
- title: "Screenshot",
- state: type === "screenshot" ? ("on" as const) : undefined,
- },
- {
- id: "archive" as const,
- title: "Archived Page",
- state: type === "archive" ? ("on" as const) : undefined,
- },
- ];
-
- const availableActions = allActions.filter((action) =>
- availableTypes.includes(action.id),
- );
-
- return (
- <MenuView
- onPressAction={({ nativeEvent }) => {
- Haptics.selectionAsync();
- onChange(nativeEvent.event as BookmarkLinkType);
- }}
- actions={availableActions}
- shouldOpenOnLongPress={false}
- >
- <ChevronDown onPress={() => Haptics.selectionAsync()} color="gray" />
- </MenuView>
- );
-}
-
-function BottomActions({ bookmark }: { bookmark: ZBookmark }) {
- const { toast } = useToast();
- const router = useRouter();
-
- const { mutate: deleteBookmark, isPending: isDeletionPending } =
- useDeleteBookmark({
- onSuccess: () => {
- router.back();
- toast({
- message: "The bookmark has been deleted!",
- showProgress: false,
- });
- },
- onError: () => {
- toast({
- message: "Something went wrong",
- variant: "destructive",
- showProgress: false,
- });
- },
- });
-
- const deleteBookmarkAlert = () =>
- Alert.alert(
- "Delete bookmark?",
- "Are you sure you want to delete this bookmark?",
- [
- { text: "Cancel", style: "cancel" },
- {
- text: "Delete",
- onPress: () => deleteBookmark({ bookmarkId: bookmark.id }),
- style: "destructive",
- },
- ],
- );
-
- const actions = [
- {
- id: "lists",
- icon: (
- <TailwindResolver
- className="text-foreground"
- comp={(styles) => <ClipboardList color={styles?.color?.toString()} />}
- />
- ),
- shouldRender: true,
- onClick: () =>
- router.push(`/dashboard/bookmarks/${bookmark.id}/manage_lists`),
- disabled: false,
- },
- {
- id: "tags",
- icon: (
- <TailwindResolver
- className="text-foreground"
- comp={(styles) => <Tag color={styles?.color?.toString()} />}
- />
- ),
- shouldRender: true,
- onClick: () =>
- router.push(`/dashboard/bookmarks/${bookmark.id}/manage_tags`),
- disabled: false,
- },
- {
- id: "open",
- icon: (
- <TailwindResolver
- className="text-foreground"
- comp={(styles) => <Info color={styles?.color?.toString()} />}
- />
- ),
- shouldRender: true,
- onClick: () => router.push(`/dashboard/bookmarks/${bookmark.id}/info`),
- disabled: false,
- },
- {
- id: "delete",
- icon: (
- <TailwindResolver
- className="text-foreground"
- comp={(styles) => <Trash2 color={styles?.color?.toString()} />}
- />
- ),
- shouldRender: true,
- onClick: deleteBookmarkAlert,
- disabled: isDeletionPending,
- },
- {
- id: "browser",
- icon: (
- <TailwindResolver
- className="text-foreground"
- comp={(styles) => <Globe color={styles?.color?.toString()} />}
- />
- ),
- shouldRender: bookmark.content.type == BookmarkTypes.LINK,
- onClick: () =>
- bookmark.content.type == BookmarkTypes.LINK &&
- Linking.openURL(bookmark.content.url),
- disabled: false,
- },
- ];
- return (
- <View>
- <View className="flex flex-row items-center justify-between px-10 pb-2 pt-4">
- {actions.map(
- (a) =>
- a.shouldRender && (
- <Pressable
- disabled={a.disabled}
- key={a.id}
- onPress={a.onClick}
- className="py-auto"
- >
- {a.icon}
- </Pressable>
- ),
- )}
- </View>
- </View>
- );
-}
-
-function BookmarkLinkView({
- bookmark,
- bookmarkPreviewType,
-}: {
- bookmark: ZBookmark;
- bookmarkPreviewType: BookmarkLinkType;
-}) {
- if (bookmark.content.type !== BookmarkTypes.LINK) {
- throw new Error("Wrong content type rendered");
- }
-
- switch (bookmarkPreviewType) {
- case "browser":
- return <BookmarkLinkBrowserPreview bookmark={bookmark} />;
- case "reader":
- return <BookmarkLinkReaderPreview bookmark={bookmark} />;
- case "screenshot":
- return <BookmarkLinkScreenshotPreview bookmark={bookmark} />;
- case "archive":
- return <BookmarkLinkArchivePreview bookmark={bookmark} />;
- }
-}
-
-function BookmarkTextView({ bookmark }: { bookmark: ZBookmark }) {
- if (bookmark.content.type !== BookmarkTypes.TEXT) {
- throw new Error("Wrong content type rendered");
- }
- const { toast } = useToast();
-
- const [isEditing, setIsEditing] = useState(false);
- const initialText = bookmark.content.text;
- const [content, setContent] = useState(initialText);
-
- const { mutate, isPending } = useUpdateBookmark({
- onError: () => {
- toast({
- message: "Something went wrong",
- variant: "destructive",
- });
- },
- onSuccess: () => {
- setIsEditing(false);
- },
- });
-
- return (
- <View className="flex-1">
- {isEditing && (
- <View className="absolute right-0 top-0 z-10 m-4 flex flex-row gap-1">
- <Button onPress={Keyboard.dismiss}>
- <Text>Save</Text>
- </Button>
- <Button
- onPress={() => {
- setContent(initialText);
- setIsEditing(false);
- }}
- >
- <Text>Discard</Text>
- </Button>
- </View>
- )}
- <ScrollView className="flex bg-background p-2">
- {isEditing ? (
- <Input
- loading={isPending}
- editable={!isPending}
- onBlur={() =>
- mutate({
- bookmarkId: bookmark.id,
- text: content,
- })
- }
- value={content}
- onChangeText={setContent}
- multiline
- autoFocus
- />
- ) : (
- <Pressable onPress={() => setIsEditing(true)}>
- <View className="mb-4 rounded-xl border border-accent p-2">
- <BookmarkTextMarkdown text={content} />
- </View>
- </Pressable>
- )}
- </ScrollView>
- </View>
- );
-}
-
-function BookmarkAssetView({ bookmark }: { bookmark: ZBookmark }) {
- const [imageZoom, setImageZoom] = useState(false);
- if (bookmark.content.type !== BookmarkTypes.ASSET) {
- throw new Error("Wrong content type rendered");
- }
- const assetSource = useAssetUrl(bookmark.content.assetId);
-
- // Check if this is a PDF asset
- if (bookmark.content.assetType === "pdf") {
- return (
- <View className="flex flex-1">
- <PDFViewer
- source={assetSource.uri ?? ""}
- headers={assetSource.headers}
- />
- </View>
- );
- }
-
- // Handle image assets as before
- return (
- <View className="flex flex-1 gap-2">
- <ImageView
- visible={imageZoom}
- imageIndex={0}
- onRequestClose={() => setImageZoom(false)}
- doubleTapToZoomEnabled={true}
- images={[assetSource]}
- />
-
- <Pressable onPress={() => setImageZoom(true)}>
- <BookmarkAssetImage
- assetId={bookmark.content.assetId}
- className="h-56 min-h-56 w-full object-cover"
- />
- </Pressable>
- </View>
- );
-}
-
-export default function ListView() {
+export default function BookmarkView() {
+ const insets = useSafeAreaInsets();
const { slug } = useLocalSearchParams();
const { colorScheme } = useColorScheme();
const isDark = colorScheme === "dark";
@@ -431,7 +71,10 @@ export default function ListView() {
break;
}
return (
- <CustomSafeAreaView edges={["bottom"]}>
+ <KeyboardAvoidingView
+ style={{ flex: 1, paddingBottom: insets.bottom + 8 }}
+ behavior="height"
+ >
<Stack.Screen
options={{
headerTitle: title ?? "",
@@ -452,10 +95,8 @@ export default function ListView() {
) : undefined,
}}
/>
- <View className="flex h-full">
- {comp}
- <BottomActions bookmark={bookmark} />
- </View>
- </CustomSafeAreaView>
+ {comp}
+ <BottomActions bookmark={bookmark} />
+ </KeyboardAvoidingView>
);
}
diff --git a/apps/mobile/components/bookmarks/BookmarkAssetView.tsx b/apps/mobile/components/bookmarks/BookmarkAssetView.tsx
new file mode 100644
index 00000000..5fe2f470
--- /dev/null
+++ b/apps/mobile/components/bookmarks/BookmarkAssetView.tsx
@@ -0,0 +1,56 @@
+import { useState } from "react";
+import { Pressable, View } from "react-native";
+import ImageView from "react-native-image-viewing";
+import BookmarkAssetImage from "@/components/bookmarks/BookmarkAssetImage";
+import { PDFViewer } from "@/components/bookmarks/PDFViewer";
+import { useAssetUrl } from "@/lib/hooks";
+
+import { BookmarkTypes, ZBookmark } from "@karakeep/shared/types/bookmarks";
+
+interface BookmarkAssetViewProps {
+ bookmark: ZBookmark;
+}
+
+export default function BookmarkAssetView({
+ bookmark,
+}: BookmarkAssetViewProps) {
+ const [imageZoom, setImageZoom] = useState(false);
+
+ if (bookmark.content.type !== BookmarkTypes.ASSET) {
+ throw new Error("Wrong content type rendered");
+ }
+
+ const assetSource = useAssetUrl(bookmark.content.assetId);
+
+ // Check if this is a PDF asset
+ if (bookmark.content.assetType === "pdf") {
+ return (
+ <View className="flex flex-1">
+ <PDFViewer
+ source={assetSource.uri ?? ""}
+ headers={assetSource.headers}
+ />
+ </View>
+ );
+ }
+
+ // Handle image assets as before
+ return (
+ <View className="flex flex-1 gap-2">
+ <ImageView
+ visible={imageZoom}
+ imageIndex={0}
+ onRequestClose={() => setImageZoom(false)}
+ doubleTapToZoomEnabled={true}
+ images={[assetSource]}
+ />
+
+ <Pressable onPress={() => setImageZoom(true)}>
+ <BookmarkAssetImage
+ assetId={bookmark.content.assetId}
+ className="h-56 min-h-56 w-full object-cover"
+ />
+ </Pressable>
+ </View>
+ );
+}
diff --git a/apps/mobile/components/bookmarks/BookmarkLinkTypeSelector.tsx b/apps/mobile/components/bookmarks/BookmarkLinkTypeSelector.tsx
new file mode 100644
index 00000000..58cbcc8d
--- /dev/null
+++ b/apps/mobile/components/bookmarks/BookmarkLinkTypeSelector.tsx
@@ -0,0 +1,85 @@
+import * as Haptics from "expo-haptics";
+import { MenuView } from "@react-native-menu/menu";
+import { ChevronDown } from "lucide-react-native";
+
+import { BookmarkTypes, ZBookmark } from "@karakeep/shared/types/bookmarks";
+
+export type BookmarkLinkType = "browser" | "reader" | "screenshot" | "archive";
+
+function getAvailableViewTypes(bookmark: ZBookmark): BookmarkLinkType[] {
+ if (bookmark.content.type !== BookmarkTypes.LINK) {
+ return [];
+ }
+
+ const availableTypes: BookmarkLinkType[] = ["browser", "reader"];
+
+ if (bookmark.assets.some((asset) => asset.assetType === "screenshot")) {
+ availableTypes.push("screenshot");
+ }
+
+ if (
+ bookmark.assets.some(
+ (asset) =>
+ asset.assetType === "precrawledArchive" ||
+ asset.assetType === "fullPageArchive",
+ )
+ ) {
+ availableTypes.push("archive");
+ }
+
+ return availableTypes;
+}
+
+interface BookmarkLinkTypeSelectorProps {
+ type: BookmarkLinkType;
+ onChange: (type: BookmarkLinkType) => void;
+ bookmark: ZBookmark;
+}
+
+export default function BookmarkLinkTypeSelector({
+ type,
+ onChange,
+ bookmark,
+}: BookmarkLinkTypeSelectorProps) {
+ const availableTypes = getAvailableViewTypes(bookmark);
+
+ const allActions = [
+ {
+ id: "reader" as const,
+ title: "Reader View",
+ state: type === "reader" ? ("on" as const) : undefined,
+ },
+ {
+ id: "browser" as const,
+ title: "Browser",
+ state: type === "browser" ? ("on" as const) : undefined,
+ },
+ {
+ id: "screenshot" as const,
+ title: "Screenshot",
+ state: type === "screenshot" ? ("on" as const) : undefined,
+ },
+ {
+ id: "archive" as const,
+ title: "Archived Page",
+ state: type === "archive" ? ("on" as const) : undefined,
+ },
+ ];
+
+ const availableActions = allActions.filter((action) =>
+ availableTypes.includes(action.id),
+ );
+
+ return (
+ <MenuView
+ onPressAction={({ nativeEvent }) => {
+ Haptics.selectionAsync();
+ onChange(nativeEvent.event as BookmarkLinkType);
+ }}
+ actions={availableActions}
+ shouldOpenOnLongPress={false}
+ >
+ <ChevronDown onPress={() => Haptics.selectionAsync()} color="gray" />
+ </MenuView>
+ );
+}
diff --git a/apps/mobile/components/bookmarks/BookmarkLinkView.tsx b/apps/mobile/components/bookmarks/BookmarkLinkView.tsx
new file mode 100644
index 00000000..e8a78029
--- /dev/null
+++ b/apps/mobile/components/bookmarks/BookmarkLinkView.tsx
@@ -0,0 +1,35 @@
+import {
+ BookmarkLinkArchivePreview,
+ BookmarkLinkBrowserPreview,
+ BookmarkLinkReaderPreview,
+ BookmarkLinkScreenshotPreview,
+} from "@/components/bookmarks/BookmarkLinkPreview";
+
+import { BookmarkTypes, ZBookmark } from "@karakeep/shared/types/bookmarks";
+
+import { BookmarkLinkType } from "./BookmarkLinkTypeSelector";
+
+interface BookmarkLinkViewProps {
+ bookmark: ZBookmark;
+ bookmarkPreviewType: BookmarkLinkType;
+}
+
+export default function BookmarkLinkView({
+ bookmark,
+ bookmarkPreviewType,
+}: BookmarkLinkViewProps) {
+ if (bookmark.content.type !== BookmarkTypes.LINK) {
+ throw new Error("Wrong content type rendered");
+ }
+
+ switch (bookmarkPreviewType) {
+ case "browser":
+ return <BookmarkLinkBrowserPreview bookmark={bookmark} />;
+ case "reader":
+ return <BookmarkLinkReaderPreview bookmark={bookmark} />;
+ case "screenshot":
+ return <BookmarkLinkScreenshotPreview bookmark={bookmark} />;
+ case "archive":
+ return <BookmarkLinkArchivePreview bookmark={bookmark} />;
+ }
+}
diff --git a/apps/mobile/components/bookmarks/BookmarkTextView.tsx b/apps/mobile/components/bookmarks/BookmarkTextView.tsx
new file mode 100644
index 00000000..0f7a7291
--- /dev/null
+++ b/apps/mobile/components/bookmarks/BookmarkTextView.tsx
@@ -0,0 +1,112 @@
+import { useState } from "react";
+import { Keyboard, Pressable, ScrollView, TextInput, View } from "react-native";
+import BookmarkTextMarkdown from "@/components/bookmarks/BookmarkTextMarkdown";
+import { Button } from "@/components/ui/Button";
+import { Text } from "@/components/ui/Text";
+import { useToast } from "@/components/ui/Toast";
+import { useColorScheme } from "nativewind";
+
+import { useUpdateBookmark } from "@karakeep/shared-react/hooks/bookmarks";
+import { BookmarkTypes, ZBookmark } from "@karakeep/shared/types/bookmarks";
+
+interface BookmarkTextViewProps {
+ bookmark: ZBookmark;
+}
+
+export default function BookmarkTextView({ bookmark }: BookmarkTextViewProps) {
+ if (bookmark.content.type !== BookmarkTypes.TEXT) {
+ throw new Error("Wrong content type rendered");
+ }
+ const { toast } = useToast();
+ const { colorScheme } = useColorScheme();
+
+ const [isEditing, setIsEditing] = useState(false);
+ const initialText = bookmark.content.text;
+ const [content, setContent] = useState(initialText);
+
+ const { mutate, isPending } = useUpdateBookmark({
+ onError: () => {
+ toast({
+ message: "Something went wrong",
+ variant: "destructive",
+ });
+ },
+ onSuccess: () => {
+ setIsEditing(false);
+ toast({
+ message: "Text updated successfully",
+ showProgress: false,
+ });
+ },
+ });
+
+ const handleSave = () => {
+ mutate({
+ bookmarkId: bookmark.id,
+ text: content,
+ });
+ };
+
+ const handleDiscard = () => {
+ setContent(initialText);
+ setIsEditing(false);
+ Keyboard.dismiss();
+ };
+
+ if (isEditing) {
+ return (
+ <View className="flex-1 p-4">
+ <View className="flex-row justify-end gap-2 px-4 py-2">
+ <Button
+ size="sm"
+ onPress={handleDiscard}
+ disabled={isPending}
+ variant="plain"
+ >
+ <Text>Cancel</Text>
+ </Button>
+ <Button size="sm" onPress={handleSave} disabled={isPending}>
+ <Text>{isPending ? "Saving..." : "Save"}</Text>
+ </Button>
+ </View>
+
+ <TextInput
+ value={content}
+ onChangeText={setContent}
+ multiline
+ autoFocus
+ editable={!isPending}
+ placeholder="Enter your text here..."
+ placeholderTextColor={colorScheme === "dark" ? "#666" : "#999"}
+ style={{
+ flex: 1,
+ fontSize: 16,
+ lineHeight: 24,
+ color: colorScheme === "dark" ? "#fff" : "#000",
+ textAlignVertical: "top",
+ padding: 12,
+ borderRadius: 8,
+ borderWidth: 1,
+ borderColor: colorScheme === "dark" ? "#333" : "#ddd",
+ backgroundColor: colorScheme === "dark" ? "#111" : "#fff",
+ }}
+ />
+ </View>
+ );
+ }
+
+ return (
+ <ScrollView className="m-4 flex-1 rounded-lg border border-border bg-card p-2">
+ <Pressable onPress={() => setIsEditing(true)}>
+ <View className="min-h-[200px] rounded-xl p-4">
+ <BookmarkTextMarkdown text={content} />
+ {content.trim() === "" && (
+ <Text className="italic text-muted-foreground">
+ Tap to add text...
+ </Text>
+ )}
+ </View>
+ </Pressable>
+ </ScrollView>
+ );
+}
diff --git a/apps/mobile/components/bookmarks/BottomActions.tsx b/apps/mobile/components/bookmarks/BottomActions.tsx
new file mode 100644
index 00000000..8cfa27c9
--- /dev/null
+++ b/apps/mobile/components/bookmarks/BottomActions.tsx
@@ -0,0 +1,136 @@
+import { Alert, Linking, Pressable, View } from "react-native";
+import { useRouter } from "expo-router";
+import { TailwindResolver } from "@/components/TailwindResolver";
+import { useToast } from "@/components/ui/Toast";
+import { ClipboardList, Globe, Info, Tag, Trash2 } from "lucide-react-native";
+
+import { useDeleteBookmark } from "@karakeep/shared-react/hooks/bookmarks";
+import { BookmarkTypes, ZBookmark } from "@karakeep/shared/types/bookmarks";
+
+interface BottomActionsProps {
+ bookmark: ZBookmark;
+}
+
+export default function BottomActions({ bookmark }: BottomActionsProps) {
+ const { toast } = useToast();
+ const router = useRouter();
+
+ const { mutate: deleteBookmark, isPending: isDeletionPending } =
+ useDeleteBookmark({
+ onSuccess: () => {
+ router.back();
+ toast({
+ message: "The bookmark has been deleted!",
+ showProgress: false,
+ });
+ },
+ onError: () => {
+ toast({
+ message: "Something went wrong",
+ variant: "destructive",
+ showProgress: false,
+ });
+ },
+ });
+
+ const deleteBookmarkAlert = () =>
+ Alert.alert(
+ "Delete bookmark?",
+ "Are you sure you want to delete this bookmark?",
+ [
+ { text: "Cancel", style: "cancel" },
+ {
+ text: "Delete",
+ onPress: () => deleteBookmark({ bookmarkId: bookmark.id }),
+ style: "destructive",
+ },
+ ],
+ );
+
+ const actions = [
+ {
+ id: "lists",
+ icon: (
+ <TailwindResolver
+ className="text-foreground"
+ comp={(styles) => <ClipboardList color={styles?.color?.toString()} />}
+ />
+ ),
+ shouldRender: true,
+ onClick: () =>
+ router.push(`/dashboard/bookmarks/${bookmark.id}/manage_lists`),
+ disabled: false,
+ },
+ {
+ id: "tags",
+ icon: (
+ <TailwindResolver
+ className="text-foreground"
+ comp={(styles) => <Tag color={styles?.color?.toString()} />}
+ />
+ ),
+ shouldRender: true,
+ onClick: () =>
+ router.push(`/dashboard/bookmarks/${bookmark.id}/manage_tags`),
+ disabled: false,
+ },
+ {
+ id: "open",
+ icon: (
+ <TailwindResolver
+ className="text-foreground"
+ comp={(styles) => <Info color={styles?.color?.toString()} />}
+ />
+ ),
+ shouldRender: true,
+ onClick: () => router.push(`/dashboard/bookmarks/${bookmark.id}/info`),
+ disabled: false,
+ },
+ {
+ id: "delete",
+ icon: (
+ <TailwindResolver
+ className="text-foreground"
+ comp={(styles) => <Trash2 color={styles?.color?.toString()} />}
+ />
+ ),
+ shouldRender: true,
+ onClick: deleteBookmarkAlert,
+ disabled: isDeletionPending,
+ },
+ {
+ id: "browser",
+ icon: (
+ <TailwindResolver
+ className="text-foreground"
+ comp={(styles) => <Globe color={styles?.color?.toString()} />}
+ />
+ ),
+ shouldRender: bookmark.content.type == BookmarkTypes.LINK,
+ onClick: () =>
+ bookmark.content.type == BookmarkTypes.LINK &&
+ Linking.openURL(bookmark.content.url),
+ disabled: false,
+ },
+ ];
+
+ return (
+ <View>
+ <View className="flex flex-row items-center justify-between px-10 pb-2 pt-4">
+ {actions.map(
+ (a) =>
+ a.shouldRender && (
+ <Pressable
+ disabled={a.disabled}
+ key={a.id}
+ onPress={a.onClick}
+ className="py-auto"
+ >
+ {a.icon}
+ </Pressable>
+ ),
+ )}
+ </View>
+ </View>
+ );
+}