diff options
| author | Mohamed Bassem <me@mbassem.com> | 2025-01-02 13:00:58 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-01-02 13:00:58 +0200 |
| commit | 5ecdc36b7d60aa66b49e01e9fec8ba61ad537376 (patch) | |
| tree | 57577822bb104b95900ba577a265fb4f8cf70b78 /packages/shared | |
| parent | 5df0258b2cd884347eabfa866d7e7fbc7225cdb3 (diff) | |
| download | karakeep-5ecdc36b7d60aa66b49e01e9fec8ba61ad537376.tar.zst | |
feat: Add support for smart lists (#802)
* feat: Add support for smart lists
* i18n
* Fix update list endpoint
* Add a test for smart lists
* Add header to the query explainer
* Hide remove from lists in the smart context list
* Add proper validation to list form
---------
Co-authored-by: Deepak Kapoor <41769111+orthdron@users.noreply.github.com>
Diffstat (limited to '')
| -rw-r--r-- | packages/shared-react/hooks/bookmark-list-context.tsx | 27 | ||||
| -rw-r--r-- | packages/shared-react/hooks/lists.ts | 3 | ||||
| -rw-r--r-- | packages/shared/types/lists.ts | 82 |
3 files changed, 95 insertions, 17 deletions
diff --git a/packages/shared-react/hooks/bookmark-list-context.tsx b/packages/shared-react/hooks/bookmark-list-context.tsx new file mode 100644 index 00000000..d00e0567 --- /dev/null +++ b/packages/shared-react/hooks/bookmark-list-context.tsx @@ -0,0 +1,27 @@ +"use client"; + +import { createContext, useContext } from "react"; + +import { ZBookmarkList } from "@hoarder/shared/types/lists"; + +export const BookmarkListContext = createContext<ZBookmarkList | undefined>( + undefined, +); + +export function BookmarkListContextProvider({ + list, + children, +}: { + list: ZBookmarkList; + children: React.ReactNode; +}) { + return ( + <BookmarkListContext.Provider value={list}> + {children} + </BookmarkListContext.Provider> + ); +} + +export function useBookmarkListContext() { + return useContext(BookmarkListContext); +} diff --git a/packages/shared-react/hooks/lists.ts b/packages/shared-react/hooks/lists.ts index 10633a08..46477228 100644 --- a/packages/shared-react/hooks/lists.ts +++ b/packages/shared-react/hooks/lists.ts @@ -28,6 +28,9 @@ export function useEditBookmarkList( onSuccess: (res, req, meta) => { apiUtils.lists.list.invalidate(); apiUtils.lists.get.invalidate({ listId: req.listId }); + if (res.type === "smart") { + apiUtils.bookmarks.getBookmarks.invalidate({ listId: req.listId }); + } return opts[0]?.onSuccess?.(res, req, meta); }, }); diff --git a/packages/shared/types/lists.ts b/packages/shared/types/lists.ts index d2041907..bd6786b0 100644 --- a/packages/shared/types/lists.ts +++ b/packages/shared/types/lists.ts @@ -1,28 +1,76 @@ import { z } from "zod"; -export const zNewBookmarkListSchema = z.object({ - name: z - .string() - .min(1, "List name can't be empty") - .max(40, "List name is at most 40 chars"), - icon: z.string(), - parentId: z.string().nullish(), -}); +import { parseSearchQuery } from "../searchQueryParser"; + +export const zNewBookmarkListSchema = z + .object({ + name: z + .string() + .min(1, "List name can't be empty") + .max(40, "List name is at most 40 chars"), + icon: z.string(), + type: z.enum(["manual", "smart"]).optional().default("manual"), + query: z.string().min(1).optional(), + parentId: z.string().nullish(), + }) + .refine((val) => val.type === "smart" || !val.query, { + message: "Manual lists cannot have a query", + path: ["query"], + }) + .refine((val) => val.type === "manual" || val.query, { + message: "Smart lists must have a query", + path: ["query"], + }) + .refine( + (val) => !val.query || parseSearchQuery(val.query).result === "full", + { + message: "Smart search query is not valid", + path: ["query"], + }, + ) + .refine((val) => !val.query || parseSearchQuery(val.query).text.length == 0, { + message: + "Smart lists cannot have unqualified terms (aka full text search terms) in the query", + path: ["query"], + }); export const zBookmarkListSchema = z.object({ id: z.string(), name: z.string(), icon: z.string(), parentId: z.string().nullable(), + type: z.enum(["manual", "smart"]).default("manual"), + query: z.string().nullish(), }); -export const zBookmarkListWithBookmarksSchema = zBookmarkListSchema.merge( - z.object({ - bookmarks: z.array(z.string()), - }), -); - export type ZBookmarkList = z.infer<typeof zBookmarkListSchema>; -export type ZBookmarkListWithBookmarks = z.infer< - typeof zBookmarkListWithBookmarksSchema ->; + +export const zEditBookmarkListSchema = z.object({ + listId: z.string(), + name: z + .string() + .min(1, "List name can't be empty") + .max(40, "List name is at most 40 chars") + .optional(), + icon: z.string().optional(), + parentId: z.string().nullish(), + query: z.string().min(1).optional(), +}); + +export const zEditBookmarkListSchemaWithValidation = zEditBookmarkListSchema + .refine((val) => val.parentId != val.listId, { + message: "List can't be its own parent", + path: ["parentId"], + }) + .refine( + (val) => !val.query || parseSearchQuery(val.query).result === "full", + { + message: "Smart search query is not valid", + path: ["query"], + }, + ) + .refine((val) => !val.query || parseSearchQuery(val.query).text.length == 0, { + message: + "Smart lists cannot have unqualified terms (aka full text search terms) in the query", + path: ["query"], + }); |
