aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMohamedBassem <me@mbassem.com>2024-03-15 12:01:11 +0000
committerMohamedBassem <me@mbassem.com>2024-03-15 12:01:11 +0000
commitaa8df79ab48c2e2d9f8b10fbdf4d71197f41303e (patch)
tree8611b119d588bb3b1b5fb165ac22c68112164e7d
parentcd10200c64572ab09112ddda55fc1057bac233ab (diff)
downloadkarakeep-aa8df79ab48c2e2d9f8b10fbdf4d71197f41303e.tar.zst
mobile: Revamp bookmark card's action bar
-rw-r--r--apps/mobile/components/bookmarks/BookmarkCard.tsx145
-rw-r--r--apps/mobile/package.json4
-rw-r--r--pnpm-lock.yaml24
3 files changed, 116 insertions, 57 deletions
diff --git a/apps/mobile/components/bookmarks/BookmarkCard.tsx b/apps/mobile/components/bookmarks/BookmarkCard.tsx
index 2189b385..07d7f4fe 100644
--- a/apps/mobile/components/bookmarks/BookmarkCard.tsx
+++ b/apps/mobile/components/bookmarks/BookmarkCard.tsx
@@ -1,12 +1,21 @@
-import { Image, Pressable, ScrollView, Text, View } from "react-native";
+import {
+ ActivityIndicator,
+ Image,
+ Platform,
+ Pressable,
+ ScrollView,
+ Text,
+ View,
+} from "react-native";
import Markdown from "react-native-markdown-display";
+import * as Haptics from "expo-haptics";
import * as WebBrowser from "expo-web-browser";
import { api } from "@/lib/trpc";
-import { Archive, ArchiveRestore, Star, Trash } from "lucide-react-native";
+import { MenuView } from "@react-native-menu/menu";
+import { Ellipsis, Star } from "lucide-react-native";
import type { ZBookmark } from "@hoarder/trpc/types/bookmarks";
-import { ActionButton } from "../ui/ActionButton";
import { Divider } from "../ui/Divider";
import { Skeleton } from "../ui/Skeleton";
import { useToast } from "../ui/Toast";
@@ -36,46 +45,59 @@ function ActionBar({ bookmark }: { bookmark: ZBookmark }) {
const { toast } = useToast();
const apiUtils = api.useUtils();
+ const onError = () => {
+ toast({
+ message: "Something went wrong",
+ variant: "destructive",
+ showProgress: false,
+ });
+ };
+
const { mutate: deleteBookmark, isPending: isDeletionPending } =
api.bookmarks.deleteBookmark.useMutation({
onSuccess: () => {
+ toast({
+ message: 'The bookmark has been deleted!',
+ showProgress: false,
+ });
+ apiUtils.bookmarks.getBookmarks.invalidate();
+ },
+ onError,
+ });
+
+ const { mutate: favouriteBookmark, variables } =
+ api.bookmarks.updateBookmark.useMutation({
+ onSuccess: () => {
apiUtils.bookmarks.getBookmarks.invalidate();
+ apiUtils.bookmarks.getBookmark.invalidate({ bookmarkId: bookmark.id });
},
- onError: () => {
+ onError,
+ });
+
+ const { mutate: archiveBookmark, isPending: isArchivePending } =
+ api.bookmarks.updateBookmark.useMutation({
+ onSuccess: (resp) => {
toast({
- message: "Something went wrong",
- variant: "destructive",
+ message: `The bookmark has been ${resp.archived ? "archived" : "un-archived"}!`,
showProgress: false,
});
+ apiUtils.bookmarks.getBookmarks.invalidate();
+ apiUtils.bookmarks.getBookmark.invalidate({ bookmarkId: bookmark.id });
},
+ onError,
});
- const {
- mutate: updateBookmark,
- variables,
- isPending: isUpdatePending,
- } = api.bookmarks.updateBookmark.useMutation({
- onSuccess: () => {
- apiUtils.bookmarks.getBookmarks.invalidate();
- apiUtils.bookmarks.getBookmark.invalidate({ bookmarkId: bookmark.id });
- },
- onError: () => {
- toast({
- message: "Something went wrong",
- variant: "destructive",
- showProgress: false,
- });
- },
- });
return (
<View className="flex flex-row gap-4">
+ {(isArchivePending || isDeletionPending) && <ActivityIndicator />}
<Pressable
- onPress={() =>
- updateBookmark({
+ onPress={() => {
+ Haptics.selectionAsync();
+ favouriteBookmark({
bookmarkId: bookmark.id,
favourited: !bookmark.favourited,
- })
- }
+ });
+ }}
>
{(variables ? variables.favourited : bookmark.favourited) ? (
<Star fill="#ebb434" color="#ebb434" />
@@ -83,31 +105,46 @@ function ActionBar({ bookmark }: { bookmark: ZBookmark }) {
<Star color="gray" />
)}
</Pressable>
- <ActionButton
- loading={isUpdatePending}
- onPress={() =>
- updateBookmark({
- bookmarkId: bookmark.id,
- archived: !bookmark.archived,
- })
- }
- >
- {bookmark.archived ? (
- <ArchiveRestore color="gray" />
- ) : (
- <Archive color="gray" />
- )}
- </ActionButton>
- <ActionButton
- loading={isDeletionPending}
- onPress={() =>
- deleteBookmark({
- bookmarkId: bookmark.id,
- })
- }
+
+ <MenuView
+ onPressAction={({ nativeEvent }) => {
+ Haptics.selectionAsync();
+ if (nativeEvent.event === "delete") {
+ deleteBookmark({
+ bookmarkId: bookmark.id,
+ });
+ } else if (nativeEvent.event === "archive") {
+ archiveBookmark({
+ bookmarkId: bookmark.id,
+ archived: !bookmark.archived,
+ });
+ }
+ }}
+ actions={[
+ {
+ id: "archive",
+ title: bookmark.archived ? "Un-archive" : "Archive",
+ image: Platform.select({
+ ios: "folder",
+ android: "ic_menu_folder",
+ }),
+ },
+ {
+ id: "delete",
+ title: "Delete",
+ attributes: {
+ destructive: true,
+ },
+ image: Platform.select({
+ ios: "trash",
+ android: "ic_menu_delete",
+ }),
+ },
+ ]}
+ shouldOpenOnLongPress={false}
>
- <Trash color="gray" />
- </ActionButton>
+ <Ellipsis onPress={() => Haptics.selectionAsync()} color="gray" />
+ </MenuView>
</View>
);
}
@@ -236,9 +273,5 @@ export default function BookmarkCard({
break;
}
- return (
- <View className="border-b border-gray-300 bg-white">
- {comp}
- </View>
- );
+ return <View className="border-b border-gray-300 bg-white">{comp}</View>;
}
diff --git a/apps/mobile/package.json b/apps/mobile/package.json
index ead5e796..71dff6cc 100644
--- a/apps/mobile/package.json
+++ b/apps/mobile/package.json
@@ -13,6 +13,7 @@
},
"dependencies": {
"@hoarder/trpc": "0.1.0",
+ "@react-native-menu/menu": "^0.9.1",
"@tanstack/react-query": "^5.24.8",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.0",
@@ -20,6 +21,7 @@
"expo-config-plugin-ios-share-extension": "^0.0.4",
"expo-constants": "~15.4.5",
"expo-dev-client": "^3.3.9",
+ "expo-haptics": "^12.8.1",
"expo-image": "^1.10.6",
"expo-linking": "~6.2.2",
"expo-router": "~3.4.8",
@@ -42,11 +44,11 @@
"zustand": "^4.5.1"
},
"devDependencies": {
+ "@babel/core": "^7.20.0",
"@hoarder/eslint-config": "workspace:^0.2.0",
"@hoarder/prettier-config": "workspace:^0.1.0",
"@hoarder/tailwind-config": "workspace:^0.1.0",
"@hoarder/tsconfig": "workspace:^0.1.0",
- "@babel/core": "^7.20.0",
"@types/react": "^18.2.55",
"ajv": "latest",
"eslint": "^8.57.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 4b300e96..5babfe60 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -140,6 +140,9 @@ importers:
'@hoarder/trpc':
specifier: 0.1.0
version: link:../../packages/trpc
+ '@react-native-menu/menu':
+ specifier: ^0.9.1
+ version: 0.9.1(react-native@0.73.4)(react@18.2.0)
'@tanstack/react-query':
specifier: ^5.24.8
version: 5.24.8(react@18.2.0)
@@ -161,6 +164,9 @@ importers:
expo-dev-client:
specifier: ^3.3.9
version: 3.3.9(expo@50.0.11)
+ expo-haptics:
+ specifier: ^12.8.1
+ version: 12.8.1(expo@50.0.11)
expo-image:
specifier: ^1.10.6
version: 1.10.6(expo@50.0.11)
@@ -4801,6 +4807,16 @@ packages:
- utf-8-validate
dev: false
+ /@react-native-menu/menu@0.9.1(react-native@0.73.4)(react@18.2.0):
+ resolution: {integrity: sha512-PdZoN1P72ESQ7UTzm5YI4W+87KU5mv7vVJ3GsQVRtnrzzuVskfp5E9ds1biGClcTieXAdIZ7hvPD1HHHZPobdg==}
+ peerDependencies:
+ react: '*'
+ react-native: '*'
+ dependencies:
+ react: 18.2.0
+ react-native: 0.73.4(@babel/core@7.23.9)(@babel/preset-env@7.24.0)(react@18.2.0)
+ dev: false
+
/@react-native/assets-registry@0.73.1:
resolution: {integrity: sha512-2FgAbU7uKM5SbbW9QptPPZx8N9Ke2L7bsHb+EhAanZjFZunA9PaYtyjUQ1s7HD+zDVqOQIvjkpXSv7Kejd2tqg==}
engines: {node: '>=18'}
@@ -9511,6 +9527,14 @@ packages:
fontfaceobserver: 2.3.0
dev: false
+ /expo-haptics@12.8.1(expo@50.0.11):
+ resolution: {integrity: sha512-ntLsHkfle8K8w9MW8pZEw92ZN3sguaGUSSIxv30fPKNeQFu7Cq/h47Qv3tONv2MO3wU48N9FbKnant6XlfptpA==}
+ peerDependencies:
+ expo: '*'
+ dependencies:
+ expo: 50.0.11(@babel/core@7.23.9)(@react-native/babel-preset@0.73.21)
+ dev: false
+
/expo-image@1.10.6(expo@50.0.11):
resolution: {integrity: sha512-vcnAIym1eU8vQgV1re1E7rVQZStJimBa4aPDhjFfzMzbddAF7heJuagyewiUkTzbZUwYzPaZAie6VJPyWx9Ueg==}
peerDependencies: