aboutsummaryrefslogtreecommitdiffstats
path: root/apps/mobile/components/bookmarks
diff options
context:
space:
mode:
Diffstat (limited to 'apps/mobile/components/bookmarks')
-rw-r--r--apps/mobile/components/bookmarks/BookmarkCard.tsx202
-rw-r--r--apps/mobile/components/bookmarks/BottomActions.tsx11
-rw-r--r--apps/mobile/components/bookmarks/NotePreview.tsx33
-rw-r--r--apps/mobile/components/bookmarks/TagPill.tsx20
4 files changed, 164 insertions, 102 deletions
diff --git a/apps/mobile/components/bookmarks/BookmarkCard.tsx b/apps/mobile/components/bookmarks/BookmarkCard.tsx
index 98d8d3e2..922951e5 100644
--- a/apps/mobile/components/bookmarks/BookmarkCard.tsx
+++ b/apps/mobile/components/bookmarks/BookmarkCard.tsx
@@ -24,6 +24,7 @@ import {
useDeleteBookmark,
useUpdateBookmark,
} from "@karakeep/shared-react/hooks/bookmarks";
+import { useWhoAmI } from "@karakeep/shared-react/hooks/users";
import { BookmarkTypes } from "@karakeep/shared/types/bookmarks";
import {
getBookmarkLinkImageUrl,
@@ -42,6 +43,10 @@ import TagPill from "./TagPill";
function ActionBar({ bookmark }: { bookmark: ZBookmark }) {
const { toast } = useToast();
const { settings } = useAppSettings();
+ const { data: currentUser } = useWhoAmI();
+
+ // Check if the current user owns this bookmark
+ const isOwner = currentUser?.id === bookmark.userId;
const onError = () => {
toast({
@@ -156,24 +161,71 @@ function ActionBar({ bookmark }: { bookmark: ZBookmark }) {
}
};
+ // Build actions array based on ownership
+ const menuActions = [];
+ if (isOwner) {
+ menuActions.push(
+ {
+ id: "edit",
+ title: "Edit",
+ image: Platform.select({
+ ios: "pencil",
+ }),
+ },
+ {
+ id: "manage_list",
+ title: "Manage Lists",
+ image: Platform.select({
+ ios: "list.bullet",
+ }),
+ },
+ {
+ id: "manage_tags",
+ title: "Manage Tags",
+ image: Platform.select({
+ ios: "tag",
+ }),
+ },
+ {
+ id: "archive",
+ title: bookmark.archived ? "Un-archive" : "Archive",
+ image: Platform.select({
+ ios: "folder",
+ }),
+ },
+ {
+ id: "delete",
+ title: "Delete",
+ attributes: {
+ destructive: true,
+ },
+ image: Platform.select({
+ ios: "trash",
+ }),
+ },
+ );
+ }
+
return (
<View className="flex flex-row gap-4">
{(isArchivePending || isDeletionPending) && <ActivityIndicator />}
- <Pressable
- onPress={() => {
- Haptics.selectionAsync();
- favouriteBookmark({
- bookmarkId: bookmark.id,
- favourited: !bookmark.favourited,
- });
- }}
- >
- {(variables ? variables.favourited : bookmark.favourited) ? (
- <Star fill="#ebb434" color="#ebb434" />
- ) : (
- <Star color="gray" />
- )}
- </Pressable>
+ {isOwner && (
+ <Pressable
+ onPress={() => {
+ Haptics.selectionAsync();
+ favouriteBookmark({
+ bookmarkId: bookmark.id,
+ favourited: !bookmark.favourited,
+ });
+ }}
+ >
+ {(variables ? variables.favourited : bookmark.favourited) ? (
+ <Star fill="#ebb434" color="#ebb434" />
+ ) : (
+ <Star color="gray" />
+ )}
+ </Pressable>
+ )}
<Pressable
onPress={() => {
@@ -184,74 +236,39 @@ function ActionBar({ bookmark }: { bookmark: ZBookmark }) {
<ShareIcon color="gray" />
</Pressable>
- <MenuView
- onPressAction={({ nativeEvent }) => {
- Haptics.selectionAsync();
- if (nativeEvent.event === "delete") {
- deleteBookmarkAlert();
- } else if (nativeEvent.event === "archive") {
- archiveBookmark({
- bookmarkId: bookmark.id,
- archived: !bookmark.archived,
- });
- } else if (nativeEvent.event === "manage_list") {
- router.push(`/dashboard/bookmarks/${bookmark.id}/manage_lists`);
- } else if (nativeEvent.event === "manage_tags") {
- router.push(`/dashboard/bookmarks/${bookmark.id}/manage_tags`);
- } else if (nativeEvent.event === "edit") {
- router.push(`/dashboard/bookmarks/${bookmark.id}/info`);
- }
- }}
- actions={[
- {
- id: "edit",
- title: "Edit",
- image: Platform.select({
- ios: "pencil",
- }),
- },
- {
- id: "manage_list",
- title: "Manage Lists",
- image: Platform.select({
- ios: "list.bullet",
- }),
- },
- {
- id: "manage_tags",
- title: "Manage Tags",
- image: Platform.select({
- ios: "tag",
- }),
- },
- {
- id: "archive",
- title: bookmark.archived ? "Un-archive" : "Archive",
- image: Platform.select({
- ios: "folder",
- }),
- },
- {
- id: "delete",
- title: "Delete",
- attributes: {
- destructive: true,
- },
- image: Platform.select({
- ios: "trash",
- }),
- },
- ]}
- shouldOpenOnLongPress={false}
- >
- <Ellipsis onPress={() => Haptics.selectionAsync()} color="gray" />
- </MenuView>
+ {isOwner && menuActions.length > 0 && (
+ <MenuView
+ onPressAction={({ nativeEvent }) => {
+ Haptics.selectionAsync();
+ if (nativeEvent.event === "delete") {
+ deleteBookmarkAlert();
+ } else if (nativeEvent.event === "archive") {
+ archiveBookmark({
+ bookmarkId: bookmark.id,
+ archived: !bookmark.archived,
+ });
+ } else if (nativeEvent.event === "manage_list") {
+ router.push(`/dashboard/bookmarks/${bookmark.id}/manage_lists`);
+ } else if (nativeEvent.event === "manage_tags") {
+ router.push(`/dashboard/bookmarks/${bookmark.id}/manage_tags`);
+ } else if (nativeEvent.event === "edit") {
+ router.push(`/dashboard/bookmarks/${bookmark.id}/info`);
+ }
+ }}
+ actions={menuActions}
+ shouldOpenOnLongPress={false}
+ >
+ <Ellipsis onPress={() => Haptics.selectionAsync()} color="gray" />
+ </MenuView>
+ )}
</View>
);
}
function TagList({ bookmark }: { bookmark: ZBookmark }) {
const tags = bookmark.tags;
+ const { data: currentUser } = useWhoAmI();
+ const isOwner = currentUser?.id === bookmark.userId;
if (isBookmarkStillTagging(bookmark)) {
return (
@@ -266,7 +283,7 @@ function TagList({ bookmark }: { bookmark: ZBookmark }) {
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
<View className="flex flex-row gap-2">
{tags.map((t) => (
- <TagPill key={t.id} tag={t} />
+ <TagPill key={t.id} tag={t} clickable={isOwner} />
))}
</View>
</ScrollView>
@@ -281,6 +298,9 @@ function LinkCard({
onOpenBookmark: () => void;
}) {
const { settings } = useAppSettings();
+ const { data: currentUser } = useWhoAmI();
+ const isOwner = currentUser?.id === bookmark.userId;
+
if (bookmark.content.type !== BookmarkTypes.LINK) {
throw new Error("Wrong content type rendered");
}
@@ -330,7 +350,13 @@ function LinkCard({
>
{bookmark.title ?? bookmark.content.title ?? parsedUrl.host}
</Text>
- {note && <NotePreview note={note} bookmarkId={bookmark.id} />}
+ {note && (
+ <NotePreview
+ note={note}
+ bookmarkId={bookmark.id}
+ readOnly={!isOwner}
+ />
+ )}
<TagList bookmark={bookmark} />
<Divider orientation="vertical" className="mt-2 h-0.5 w-full" />
<View className="mt-2 flex flex-row justify-between px-2 pb-2">
@@ -350,6 +376,9 @@ function TextCard({
onOpenBookmark: () => void;
}) {
const { settings } = useAppSettings();
+ const { data: currentUser } = useWhoAmI();
+ const isOwner = currentUser?.id === bookmark.userId;
+
if (bookmark.content.type !== BookmarkTypes.TEXT) {
throw new Error("Wrong content type rendered");
}
@@ -369,7 +398,9 @@ function TextCard({
<BookmarkTextMarkdown text={content} />
</Pressable>
</View>
- {note && <NotePreview note={note} bookmarkId={bookmark.id} />}
+ {note && (
+ <NotePreview note={note} bookmarkId={bookmark.id} readOnly={!isOwner} />
+ )}
<TagList bookmark={bookmark} />
<Divider orientation="vertical" className="mt-2 h-0.5 w-full" />
<View className="flex flex-row justify-between p-2">
@@ -388,6 +419,9 @@ function AssetCard({
onOpenBookmark: () => void;
}) {
const { settings } = useAppSettings();
+ const { data: currentUser } = useWhoAmI();
+ const isOwner = currentUser?.id === bookmark.userId;
+
if (bookmark.content.type !== BookmarkTypes.ASSET) {
throw new Error("Wrong content type rendered");
}
@@ -412,7 +446,13 @@ function AssetCard({
<Text className="line-clamp-2 text-xl font-bold">{title}</Text>
)}
</Pressable>
- {note && <NotePreview note={note} bookmarkId={bookmark.id} />}
+ {note && (
+ <NotePreview
+ note={note}
+ bookmarkId={bookmark.id}
+ readOnly={!isOwner}
+ />
+ )}
<TagList bookmark={bookmark} />
<Divider orientation="vertical" className="mt-2 h-0.5 w-full" />
<View className="mt-2 flex flex-row justify-between px-2 pb-2">
diff --git a/apps/mobile/components/bookmarks/BottomActions.tsx b/apps/mobile/components/bookmarks/BottomActions.tsx
index 8cfa27c9..64653779 100644
--- a/apps/mobile/components/bookmarks/BottomActions.tsx
+++ b/apps/mobile/components/bookmarks/BottomActions.tsx
@@ -5,6 +5,7 @@ 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 { useWhoAmI } from "@karakeep/shared-react/hooks/users";
import { BookmarkTypes, ZBookmark } from "@karakeep/shared/types/bookmarks";
interface BottomActionsProps {
@@ -14,6 +15,10 @@ interface BottomActionsProps {
export default function BottomActions({ bookmark }: BottomActionsProps) {
const { toast } = useToast();
const router = useRouter();
+ const { data: currentUser } = useWhoAmI();
+
+ // Check if the current user owns this bookmark
+ const isOwner = currentUser?.id === bookmark.userId;
const { mutate: deleteBookmark, isPending: isDeletionPending } =
useDeleteBookmark({
@@ -56,7 +61,7 @@ export default function BottomActions({ bookmark }: BottomActionsProps) {
comp={(styles) => <ClipboardList color={styles?.color?.toString()} />}
/>
),
- shouldRender: true,
+ shouldRender: isOwner,
onClick: () =>
router.push(`/dashboard/bookmarks/${bookmark.id}/manage_lists`),
disabled: false,
@@ -69,7 +74,7 @@ export default function BottomActions({ bookmark }: BottomActionsProps) {
comp={(styles) => <Tag color={styles?.color?.toString()} />}
/>
),
- shouldRender: true,
+ shouldRender: isOwner,
onClick: () =>
router.push(`/dashboard/bookmarks/${bookmark.id}/manage_tags`),
disabled: false,
@@ -94,7 +99,7 @@ export default function BottomActions({ bookmark }: BottomActionsProps) {
comp={(styles) => <Trash2 color={styles?.color?.toString()} />}
/>
),
- shouldRender: true,
+ shouldRender: isOwner,
onClick: deleteBookmarkAlert,
disabled: isDeletionPending,
},
diff --git a/apps/mobile/components/bookmarks/NotePreview.tsx b/apps/mobile/components/bookmarks/NotePreview.tsx
index d529d56e..0283d179 100644
--- a/apps/mobile/components/bookmarks/NotePreview.tsx
+++ b/apps/mobile/components/bookmarks/NotePreview.tsx
@@ -10,9 +10,14 @@ import { Text } from "../ui/Text";
interface NotePreviewProps {
note: string;
bookmarkId: string;
+ readOnly?: boolean;
}
-export function NotePreview({ note, bookmarkId }: NotePreviewProps) {
+export function NotePreview({
+ note,
+ bookmarkId,
+ readOnly = false,
+}: NotePreviewProps) {
const [isModalVisible, setIsModalVisible] = useState(false);
const { colorScheme } = useColorScheme();
const iconColor = colorScheme === "dark" ? "#9ca3af" : "#6b7280";
@@ -63,18 +68,20 @@ export function NotePreview({ note, bookmarkId }: NotePreviewProps) {
</ScrollView>
{/* Action Button */}
- <View className="flex flex-row justify-end border-t border-border pt-4">
- <Button
- variant="secondary"
- onPress={() => {
- setIsModalVisible(false);
- router.push(`/dashboard/bookmarks/${bookmarkId}/info`);
- }}
- >
- <Text className="text-sm">Edit Notes</Text>
- <ExternalLink size={14} color={modalIconColor} />
- </Button>
- </View>
+ {!readOnly && (
+ <View className="flex flex-row justify-end border-t border-border pt-4">
+ <Button
+ variant="secondary"
+ onPress={() => {
+ setIsModalVisible(false);
+ router.push(`/dashboard/bookmarks/${bookmarkId}/info`);
+ }}
+ >
+ <Text className="text-sm">Edit Notes</Text>
+ <ExternalLink size={14} color={modalIconColor} />
+ </Button>
+ </View>
+ )}
</View>
</View>
</Modal>
diff --git a/apps/mobile/components/bookmarks/TagPill.tsx b/apps/mobile/components/bookmarks/TagPill.tsx
index caf0f636..2097daab 100644
--- a/apps/mobile/components/bookmarks/TagPill.tsx
+++ b/apps/mobile/components/bookmarks/TagPill.tsx
@@ -1,17 +1,27 @@
-import { View } from "react-native";
+import { Text, View } from "react-native";
import { Link } from "expo-router";
import { ZBookmarkTags } from "@karakeep/shared/types/tags";
-export default function TagPill({ tag }: { tag: ZBookmarkTags }) {
+export default function TagPill({
+ tag,
+ clickable = true,
+}: {
+ tag: ZBookmarkTags;
+ clickable?: boolean;
+}) {
return (
<View
key={tag.id}
className="rounded-full border border-input px-2.5 py-0.5 text-xs font-semibold"
>
- <Link className="text-foreground" href={`dashboard/tags/${tag.id}`}>
- {tag.name}
- </Link>
+ {clickable ? (
+ <Link className="text-foreground" href={`dashboard/tags/${tag.id}`}>
+ {tag.name}
+ </Link>
+ ) : (
+ <Text className="text-foreground">{tag.name}</Text>
+ )}
</View>
);
}