aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/web/app/dashboard/search/page.tsx10
-rw-r--r--apps/web/components/dashboard/SortOrderToggle.tsx36
-rw-r--r--apps/web/components/dashboard/bookmarks/UpdatableBookmarksGrid.tsx6
-rw-r--r--apps/web/lib/hooks/bookmark-search.ts9
-rw-r--r--apps/web/lib/i18n/locales/en/translation.json1
-rw-r--r--packages/shared/types/bookmarks.ts6
-rw-r--r--packages/trpc/routers/bookmarks.ts22
7 files changed, 74 insertions, 16 deletions
diff --git a/apps/web/app/dashboard/search/page.tsx b/apps/web/app/dashboard/search/page.tsx
index beae73b8..c3542a88 100644
--- a/apps/web/app/dashboard/search/page.tsx
+++ b/apps/web/app/dashboard/search/page.tsx
@@ -1,14 +1,22 @@
"use client";
-import { Suspense } from "react";
+import { Suspense, useEffect } from "react";
import BookmarksGrid from "@/components/dashboard/bookmarks/BookmarksGrid";
import { FullPageSpinner } from "@/components/ui/full-page-spinner";
import { useBookmarkSearch } from "@/lib/hooks/bookmark-search";
+import { useSortOrderStore } from "@/lib/store/useSortOrderStore";
function SearchComp() {
const { data, hasNextPage, fetchNextPage, isFetchingNextPage } =
useBookmarkSearch();
+ const { setSortOrder } = useSortOrderStore();
+
+ useEffect(() => {
+ // also see related cleanup code in SortOrderToggle.tsx
+ setSortOrder("relevance");
+ }, []);
+
return (
<div className="flex flex-col gap-3">
{data ? (
diff --git a/apps/web/components/dashboard/SortOrderToggle.tsx b/apps/web/components/dashboard/SortOrderToggle.tsx
index 8c0f617d..ba3385ac 100644
--- a/apps/web/components/dashboard/SortOrderToggle.tsx
+++ b/apps/web/components/dashboard/SortOrderToggle.tsx
@@ -1,3 +1,6 @@
+"use client";
+
+import { useEffect } from "react";
import { ButtonWithTooltip } from "@/components/ui/button";
import {
DropdownMenu,
@@ -5,15 +8,26 @@ import {
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
+import { useIsSearchPage } from "@/lib/hooks/bookmark-search";
import { useTranslation } from "@/lib/i18n/client";
import { useSortOrderStore } from "@/lib/store/useSortOrderStore";
-import { Check, SortAsc, SortDesc } from "lucide-react";
+import { Check, ListFilter, SortAsc, SortDesc } from "lucide-react";
export default function SortOrderToggle() {
const { t } = useTranslation();
+ const isInSearchPage = useIsSearchPage();
const { sortOrder: currentSort, setSortOrder } = useSortOrderStore();
+ // also see related on page enter sortOrder.relevance init
+ // in apps/web/app/dashboard/search/page.tsx
+ useEffect(() => {
+ if (!isInSearchPage && currentSort === "relevance") {
+ // reset to default sort order
+ setSortOrder("desc");
+ }
+ }, [isInSearchPage, currentSort]);
+
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
@@ -22,14 +36,24 @@ export default function SortOrderToggle() {
delayDuration={100}
variant="ghost"
>
- {currentSort === "asc" ? (
- <SortAsc size={18} />
- ) : (
- <SortDesc size={18} />
- )}
+ {currentSort === "relevance" && <ListFilter size={18} />}
+ {currentSort === "asc" && <SortAsc size={18} />}
+ {currentSort === "desc" && <SortDesc size={18} />}
</ButtonWithTooltip>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-fit">
+ {isInSearchPage && (
+ <DropdownMenuItem
+ className="cursor-pointer justify-between"
+ onClick={() => setSortOrder("relevance")}
+ >
+ <div className="flex items-center">
+ <ListFilter size={16} className="mr-2" />
+ <span>{t("actions.sort.relevant_first")}</span>
+ </div>
+ {currentSort === "relevance" && <Check className="ml-2 h-4 w-4" />}
+ </DropdownMenuItem>
+ )}
<DropdownMenuItem
className="cursor-pointer justify-between"
onClick={() => setSortOrder("desc")}
diff --git a/apps/web/components/dashboard/bookmarks/UpdatableBookmarksGrid.tsx b/apps/web/components/dashboard/bookmarks/UpdatableBookmarksGrid.tsx
index da65b9d9..968d0326 100644
--- a/apps/web/components/dashboard/bookmarks/UpdatableBookmarksGrid.tsx
+++ b/apps/web/components/dashboard/bookmarks/UpdatableBookmarksGrid.tsx
@@ -23,7 +23,11 @@ export default function UpdatableBookmarksGrid({
showEditorCard?: boolean;
itemsPerPage?: number;
}) {
- const sortOrder = useSortOrderStore((state) => state.sortOrder);
+ let sortOrder = useSortOrderStore((state) => state.sortOrder);
+ if (sortOrder === "relevance") {
+ // Relevance is not supported in the `getBookmarks` endpoint.
+ sortOrder = "desc";
+ }
const finalQuery = { ...query, sortOrder, includeContent: false };
diff --git a/apps/web/lib/hooks/bookmark-search.ts b/apps/web/lib/hooks/bookmark-search.ts
index 1bccd280..b6af94ee 100644
--- a/apps/web/lib/hooks/bookmark-search.ts
+++ b/apps/web/lib/hooks/bookmark-search.ts
@@ -6,6 +6,11 @@ import { keepPreviousData } from "@tanstack/react-query";
import { parseSearchQuery } from "@karakeep/shared/searchQueryParser";
+export function useIsSearchPage() {
+ const pathname = usePathname();
+ return pathname.startsWith("/dashboard/search");
+}
+
function useSearchQuery() {
const searchParams = useSearchParams();
const searchQuery = decodeURIComponent(searchParams.get("q") ?? "");
@@ -17,8 +22,8 @@ function useSearchQuery() {
export function useDoBookmarkSearch() {
const router = useRouter();
const { searchQuery, parsedSearchQuery } = useSearchQuery();
+ const isInSearchPage = useIsSearchPage();
const [timeoutId, setTimeoutId] = useState<NodeJS.Timeout | undefined>();
- const pathname = usePathname();
useEffect(() => {
return () => {
@@ -49,7 +54,7 @@ export function useDoBookmarkSearch() {
debounceSearch,
searchQuery,
parsedSearchQuery,
- isInSearchPage: pathname.startsWith("/dashboard/search"),
+ isInSearchPage,
};
}
diff --git a/apps/web/lib/i18n/locales/en/translation.json b/apps/web/lib/i18n/locales/en/translation.json
index 42a904a4..aef2d2a7 100644
--- a/apps/web/lib/i18n/locales/en/translation.json
+++ b/apps/web/lib/i18n/locales/en/translation.json
@@ -79,6 +79,7 @@
"ignore": "Ignore",
"sort": {
"title": "Sort",
+ "relevant_first": "Most Relevant First",
"newest_first": "Newest First",
"oldest_first": "Oldest First"
}
diff --git a/packages/shared/types/bookmarks.ts b/packages/shared/types/bookmarks.ts
index 3cac2845..709fd431 100644
--- a/packages/shared/types/bookmarks.ts
+++ b/packages/shared/types/bookmarks.ts
@@ -12,7 +12,7 @@ export const enum BookmarkTypes {
UNKNOWN = "unknown",
}
-export const zSortOrder = z.enum(["asc", "desc"]);
+export const zSortOrder = z.enum(["asc", "desc", "relevance"]);
export type ZSortOrder = z.infer<typeof zSortOrder>;
export const zAssetTypesSchema = z.enum([
@@ -178,7 +178,7 @@ export const zGetBookmarksRequestSchema = z.object({
// The value is currently not being used, but keeping it so that client can still set it to true for older
// servers.
useCursorV2: z.boolean().optional(),
- sortOrder: zSortOrder.optional().default("desc"),
+ sortOrder: zSortOrder.exclude(["relevance"]).optional().default("desc"),
includeContent: z.boolean().optional().default(false),
});
export type ZGetBookmarksRequest = z.infer<typeof zGetBookmarksRequestSchema>;
@@ -238,6 +238,6 @@ export const zSearchBookmarksRequestSchema = z.object({
text: z.string(),
limit: z.number().max(MAX_NUM_BOOKMARKS_PER_PAGE).optional(),
cursor: zSearchBookmarksCursor.nullish(),
- sortOrder: zSortOrder.optional().default("desc"),
+ sortOrder: zSortOrder.optional().default("relevance"),
includeContent: z.boolean().optional().default(false),
});
diff --git a/packages/trpc/routers/bookmarks.ts b/packages/trpc/routers/bookmarks.ts
index b9a21400..88386657 100644
--- a/packages/trpc/routers/bookmarks.ts
+++ b/packages/trpc/routers/bookmarks.ts
@@ -712,7 +712,7 @@ export const bookmarksAppRouter = router({
if (!input.limit) {
input.limit = DEFAULT_NUM_BOOKMARKS_PER_PAGE;
}
- const sortOrder = input.sortOrder || "desc";
+ const sortOrder = input.sortOrder || "relevance";
const client = await getSearchIdxClient();
if (!client) {
throw new TRPCError({
@@ -735,11 +735,16 @@ export const bookmarksAppRouter = router({
filter = [`userId = '${ctx.user.id}'`];
}
+ /**
+ * preserve legacy behaviour
+ */
+ const createdAtSortOrder = sortOrder === "relevance" ? "desc" : sortOrder;
+
const resp = await client.search(parsedQuery.text, {
filter,
showRankingScore: true,
attributesToRetrieve: ["id"],
- sort: [`createdAt:${sortOrder}`],
+ sort: [`createdAt:${createdAtSortOrder}`],
limit: input.limit,
...(input.cursor
? {
@@ -775,7 +780,18 @@ export const bookmarksAppRouter = router({
assets: true,
},
});
- results.sort((a, b) => idToRank[b.id] - idToRank[a.id]);
+
+ switch (true) {
+ case sortOrder === "relevance":
+ results.sort((a, b) => idToRank[b.id] - idToRank[a.id]);
+ break;
+ case sortOrder === "desc":
+ results.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
+ break;
+ case sortOrder === "asc":
+ results.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
+ break;
+ }
return {
bookmarks: results.map((b) => toZodSchema(b, input.includeContent)),