From 48ab8a194974e026104bb2566cf8abd693ba51c1 Mon Sep 17 00:00:00 2001
From: Mohamed Bassem <me@mbassem.com>
Date: Sun, 23 Nov 2025 10:15:23 +0000
Subject: feat(mobile): Add smart list creation in mobile app (#2153)

* feat: Add smart list creation and display in mobile app

This commit adds support for creating and displaying smart lists in the mobile application:

- Enhanced list creation screen to support both manual and smart list types
- Added type selector with manual/smart toggle buttons
- Implemented conditional search query input for smart lists
- Added query validation to ensure smart lists have valid queries
- Improved error handling to display validation errors from the backend
- Added visual indicators (sparkle icon) for smart lists in the lists tab
- Implemented smart list query display in list detail view with sparkle badge
- Enhanced UI with contextual help text for smart list queries

The implementation follows the web app pattern while adapting the UI for mobile best practices.

* fixes

---------

Co-authored-by: Claude <noreply@anthropic.com>
---
 apps/mobile/app/dashboard/lists/new.tsx | 71 +++++++++++++++++++++++++++++++--
 1 file changed, 68 insertions(+), 3 deletions(-)

(limited to 'apps/mobile/app/dashboard/lists/new.tsx')

diff --git a/apps/mobile/app/dashboard/lists/new.tsx b/apps/mobile/app/dashboard/lists/new.tsx
index 55315e70..af51ed15 100644
--- a/apps/mobile/app/dashboard/lists/new.tsx
+++ b/apps/mobile/app/dashboard/lists/new.tsx
@@ -9,35 +9,81 @@ import { useToast } from "@/components/ui/Toast";
 
 import { useCreateBookmarkList } from "@karakeep/shared-react/hooks/lists";
 
+type ListType = "manual" | "smart";
+
 const NewListPage = () => {
   const dismiss = () => {
     router.back();
   };
   const { toast } = useToast();
   const [text, setText] = useState("");
+  const [listType, setListType] = useState<ListType>("manual");
+  const [query, setQuery] = useState("");
 
   const { mutate, isPending } = useCreateBookmarkList({
     onSuccess: () => {
       dismiss();
     },
-    onError: () => {
+    onError: (error) => {
+      // Extract error message from the error object
+      let errorMessage = "Something went wrong";
+      if (error.data?.zodError) {
+        errorMessage = Object.values(error.data.zodError.fieldErrors)
+          .flat()
+          .join("\n");
+      } else if (error.message) {
+        errorMessage = error.message;
+      }
       toast({
-        message: "Something went wrong",
+        message: errorMessage,
         variant: "destructive",
       });
     },
   });
 
   const onSubmit = () => {
+    // Validate smart list has a query
+    if (listType === "smart" && !query.trim()) {
+      toast({
+        message: "Smart lists must have a search query",
+        variant: "destructive",
+      });
+      return;
+    }
+
     mutate({
       name: text,
       icon: "🚀",
+      type: listType,
+      query: listType === "smart" ? query : undefined,
     });
   };
 
   return (
     <CustomSafeAreaView>
-      <View className="gap-2 px-4">
+      <View className="gap-3 px-4">
+        {/* List Type Selector */}
+        <View className="gap-2">
+          <Text className="text-sm text-muted-foreground">List Type</Text>
+          <View className="flex flex-row gap-2">
+            <Button
+              variant={listType === "manual" ? "primary" : "secondary"}
+              onPress={() => setListType("manual")}
+              className="flex-1"
+            >
+              <Text>Manual</Text>
+            </Button>
+            <Button
+              variant={listType === "smart" ? "primary" : "secondary"}
+              onPress={() => setListType("smart")}
+              className="flex-1"
+            >
+              <Text>Smart</Text>
+            </Button>
+          </View>
+        </View>
+
+        {/* List Name */}
         <View className="flex flex-row items-center gap-1">
           <Text className="shrink p-2">🚀</Text>
           <Input
@@ -48,6 +94,25 @@ const NewListPage = () => {
             autoCapitalize={"none"}
           />
         </View>
+
+        {/* Smart List Query Input */}
+        {listType === "smart" && (
+          <View className="gap-2">
+            <Text className="text-sm text-muted-foreground">Search Query</Text>
+            <Input
+              className="bg-card"
+              onChangeText={setQuery}
+              value={query}
+              placeholder="e.g., #important OR list:work"
+              autoCapitalize={"none"}
+            />
+            <Text className="text-xs italic text-muted-foreground">
+              Smart lists automatically show bookmarks matching your search
+              query
+            </Text>
+          </View>
+        )}
+
         <Button disabled={isPending} onPress={onSubmit}>
           <Text>Save</Text>
         </Button>
-- 
cgit v1.2.3-70-g09d2

