From 33f407797213c56dd2f13e98228a5305efdf90fd Mon Sep 17 00:00:00 2001 From: xuatz Date: Mon, 3 Nov 2025 04:32:18 +0900 Subject: feat: display notes on bookmark card (#2083) * feat: display notes on bookmark card * apply styling * include mobile impl * apply pr comments * add display options menu into PR * put it under app setting * cleanup * address pr comments * change the default for show notes to false * make the in-card note font lighter --------- Co-authored-by: Mohamed Bassem --- apps/web/.oxlintrc.json | 2 + apps/web/components/dashboard/ViewOptions.tsx | 57 ++++++++++++++----- .../bookmarks/BookmarkLayoutAdaptingCard.tsx | 28 ++++++--- .../components/dashboard/bookmarks/NotePreview.tsx | 66 ++++++++++++++++++++++ apps/web/lib/i18n/locales/en/translation.json | 8 +++ apps/web/lib/userLocalSettings/bookmarksLayout.tsx | 8 +++ apps/web/lib/userLocalSettings/types.ts | 1 + .../web/lib/userLocalSettings/userLocalSettings.ts | 11 ++++ 8 files changed, 158 insertions(+), 23 deletions(-) create mode 100644 apps/web/components/dashboard/bookmarks/NotePreview.tsx (limited to 'apps/web') diff --git a/apps/web/.oxlintrc.json b/apps/web/.oxlintrc.json index d0a51846..4c437efb 100644 --- a/apps/web/.oxlintrc.json +++ b/apps/web/.oxlintrc.json @@ -27,6 +27,8 @@ ".next", "dist", "build", + "public/sw.js", + "public/workbox-*.js", "pnpm-lock.yaml" ] } diff --git a/apps/web/components/dashboard/ViewOptions.tsx b/apps/web/components/dashboard/ViewOptions.tsx index 6367421f..c86f43fb 100644 --- a/apps/web/components/dashboard/ViewOptions.tsx +++ b/apps/web/components/dashboard/ViewOptions.tsx @@ -1,6 +1,6 @@ "use client"; -import React from "react"; +import { createElement, useEffect, useState } from "react"; import { ButtonWithTooltip } from "@/components/ui/button"; import { DropdownMenu, @@ -9,14 +9,19 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; +import { Label } from "@/components/ui/label"; import { Slider } from "@/components/ui/slider"; +import { Switch } from "@/components/ui/switch"; +import { useTranslation } from "@/lib/i18n/client"; import { + useBookmarkDisplaySettings, useBookmarkLayout, useGridColumns, } from "@/lib/userLocalSettings/bookmarksLayout"; import { updateBookmarksLayout, updateGridColumns, + updateShowNotes, } from "@/lib/userLocalSettings/userLocalSettings"; import { Check, @@ -25,6 +30,7 @@ import { LayoutList, List, LucideIcon, + NotepadText, Settings, } from "lucide-react"; @@ -37,22 +43,17 @@ const iconMap: Record = { compact: List, }; -const layoutNames: Record = { - masonry: "Masonry", - grid: "Grid", - list: "List", - compact: "Compact", -}; - export default function ViewOptions() { + const { t } = useTranslation(); const layout = useBookmarkLayout(); const gridColumns = useGridColumns(); - const [tempColumns, setTempColumns] = React.useState(gridColumns); + const { showNotes } = useBookmarkDisplaySettings(); + const [tempColumns, setTempColumns] = useState(gridColumns); const showColumnSlider = layout === "grid" || layout === "masonry"; // Update temp value when actual value changes - React.useEffect(() => { + useEffect(() => { setTempColumns(gridColumns); }, [gridColumns]); @@ -60,7 +61,7 @@ export default function ViewOptions() { @@ -68,7 +69,9 @@ export default function ViewOptions() { -
Layout
+
+ {t("view_options.layout")} +
{(Object.keys(iconMap) as LayoutType[]).map((key) => ( await updateBookmarksLayout(key as LayoutType)} >
- {React.createElement(iconMap[key as LayoutType], { size: 18 })} - {layoutNames[key]} + {createElement(iconMap[key as LayoutType], { size: 18 })} + {t(`layouts.${key}`)}
{layout === key && }
@@ -88,7 +91,9 @@ export default function ViewOptions() {
- Columns + + {t("view_options.columns")} + {tempColumns} @@ -109,6 +114,28 @@ export default function ViewOptions() {
)} + + +
+ {t("view_options.display_options")} +
+ +
+
+ + +
+
); diff --git a/apps/web/components/dashboard/bookmarks/BookmarkLayoutAdaptingCard.tsx b/apps/web/components/dashboard/bookmarks/BookmarkLayoutAdaptingCard.tsx index 4b511a3c..26c1c72b 100644 --- a/apps/web/components/dashboard/bookmarks/BookmarkLayoutAdaptingCard.tsx +++ b/apps/web/components/dashboard/bookmarks/BookmarkLayoutAdaptingCard.tsx @@ -1,10 +1,14 @@ +"use client"; + import type { BookmarksLayoutTypes } from "@/lib/userLocalSettings/types"; -import React, { useEffect, useState } from "react"; +import type { ReactNode } from "react"; +import { useEffect, useState } from "react"; import Image from "next/image"; import Link from "next/link"; import useBulkActionsStore from "@/lib/bulkActions"; import { bookmarkLayoutSwitch, + useBookmarkDisplaySettings, useBookmarkLayout, } from "@/lib/userLocalSettings/bookmarksLayout"; import { cn } from "@/lib/utils"; @@ -17,14 +21,15 @@ import { isBookmarkStillTagging } from "@karakeep/shared/utils/bookmarkUtils"; import BookmarkActionBar from "./BookmarkActionBar"; import BookmarkFormattedCreatedAt from "./BookmarkFormattedCreatedAt"; +import { NotePreview } from "./NotePreview"; import TagList from "./TagList"; interface Props { bookmark: ZBookmark; - image: (layout: BookmarksLayoutTypes, className: string) => React.ReactNode; - title?: React.ReactNode; - content?: React.ReactNode; - footer?: React.ReactNode; + image: (layout: BookmarksLayoutTypes, className: string) => ReactNode; + title?: ReactNode; + content?: ReactNode; + footer?: ReactNode; className?: string; fitHeight?: boolean; wrapTags: boolean; @@ -34,7 +39,7 @@ function BottomRow({ footer, bookmark, }: { - footer?: React.ReactNode; + footer?: ReactNode; bookmark: ZBookmark; }) { return ( @@ -112,6 +117,9 @@ function ListView({ footer, className, }: Props) { + const { showNotes } = useBookmarkDisplaySettings(); + const note = showNotes ? bookmark.note?.trim() : undefined; + return (
)} {content &&
{content}
} + {note && }
- {img &&
{img}
} + {img &&
{img}
}
{title && ( @@ -175,6 +186,7 @@ function GridView({
)} {content &&
{content}
} + {note && }
+ +
+ +
+ {note} +
+
+
+ +
+
+ {note} +
+
+ + + +
+
+
+ + ); +} diff --git a/apps/web/lib/i18n/locales/en/translation.json b/apps/web/lib/i18n/locales/en/translation.json index 63e4ae2d..9b40416d 100644 --- a/apps/web/lib/i18n/locales/en/translation.json +++ b/apps/web/lib/i18n/locales/en/translation.json @@ -47,6 +47,13 @@ "list": "List", "compact": "Compact" }, + "view_options": { + "title": "View Options", + "layout": "Layout", + "columns": "Columns", + "display_options": "Display Options", + "show_note_previews": "Show Notes" + }, "actions": { "change_layout": "Change Layout", "archive": "Archive", @@ -59,6 +66,7 @@ "recrawl": "Recrawl", "download_full_page_archive": "Download Full Page Archive", "edit_tags": "Edit Tags", + "edit_notes": "Edit Notes", "add_to_list": "Add to List", "select_all": "Select All", "unselect_all": "Unselect All", diff --git a/apps/web/lib/userLocalSettings/bookmarksLayout.tsx b/apps/web/lib/userLocalSettings/bookmarksLayout.tsx index 346c85e0..504d8d8c 100644 --- a/apps/web/lib/userLocalSettings/bookmarksLayout.tsx +++ b/apps/web/lib/userLocalSettings/bookmarksLayout.tsx @@ -14,12 +14,20 @@ export const UserLocalSettingsCtx = createContext< bookmarkGridLayout: defaultLayout, lang: fallbackLng, gridColumns: 3, + showNotes: false, }); function useUserLocalSettings() { return useContext(UserLocalSettingsCtx); } +export function useBookmarkDisplaySettings() { + const settings = useUserLocalSettings(); + return { + showNotes: settings.showNotes, + }; +} + export function useBookmarkLayout() { const settings = useUserLocalSettings(); return settings.bookmarkGridLayout; diff --git a/apps/web/lib/userLocalSettings/types.ts b/apps/web/lib/userLocalSettings/types.ts index c87c8c33..54b75b80 100644 --- a/apps/web/lib/userLocalSettings/types.ts +++ b/apps/web/lib/userLocalSettings/types.ts @@ -9,6 +9,7 @@ export const zUserLocalSettings = z.object({ bookmarkGridLayout: zBookmarkGridLayout.optional().default("masonry"), lang: z.string().optional().default("en"), gridColumns: z.number().min(1).max(6).optional().default(3), + showNotes: z.boolean().optional().default(false), }); export type UserLocalSettings = z.infer; diff --git a/apps/web/lib/userLocalSettings/userLocalSettings.ts b/apps/web/lib/userLocalSettings/userLocalSettings.ts index 11bd0a84..25c10e1b 100644 --- a/apps/web/lib/userLocalSettings/userLocalSettings.ts +++ b/apps/web/lib/userLocalSettings/userLocalSettings.ts @@ -48,3 +48,14 @@ export async function updateGridColumns(gridColumns: number) { sameSite: "lax", }); } + +export async function updateShowNotes(showNotes: boolean) { + const userSettings = (await cookies()).get(USER_LOCAL_SETTINGS_COOKIE_NAME); + const parsed = parseUserLocalSettings(userSettings?.value); + (await cookies()).set({ + name: USER_LOCAL_SETTINGS_COOKIE_NAME, + value: JSON.stringify({ ...parsed, showNotes }), + maxAge: 34560000, // Chrome caps max age to 400 days + sameSite: "lax", + }); +} -- cgit v1.2.3-70-g09d2