Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions web/src/components/MemoView/components/MemoHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import VisibilityIcon from "../../VisibilityIcon";
import { useMemoActions } from "../hooks";
import { useMemoViewContext, useMemoViewDerived } from "../MemoViewContext";
import type { MemoHeaderProps } from "../types";
import MemoQuickActions from "./MemoQuickActions";

const MemoHeader: React.FC<MemoHeaderProps> = ({ showCreator, showVisibility, showPinned }) => {
const t = useTranslate();
Expand Down Expand Up @@ -105,6 +106,7 @@ const MemoHeader: React.FC<MemoHeaderProps> = ({ showCreator, showVisibility, sh
</TooltipProvider>
)}

<MemoQuickActions memo={memo} readonly={readonly} onEdit={openEditor} />
<MemoActionMenu memo={memo} readonly={readonly} onEdit={openEditor} />
</div>
</div>
Expand Down
109 changes: 109 additions & 0 deletions web/src/components/MemoView/components/MemoQuickActions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { ArchiveIcon, ArchiveRestoreIcon, BookmarkMinusIcon, BookmarkPlusIcon, Edit3Icon, TrashIcon } from "lucide-react";
import { useState } from "react";
import ConfirmDialog from "@/components/ConfirmDialog";
import { Button } from "@/components/ui/button";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import { useLocalStorage } from "@/hooks";
import { State } from "@/types/proto/api/v1/common_pb";
import type { Memo } from "@/types/proto/api/v1/memo_service_pb";
import { useTranslate } from "@/utils/i18n";
import { useMemoActionHandlers } from "../../MemoActionMenu/hooks";

export const QUICK_ACTIONS_STORAGE_KEY = "memo-quick-actions-enabled";

interface MemoQuickActionsProps {
memo: Memo;
readonly: boolean;
onEdit?: () => void;
}

const MemoQuickActions: React.FC<MemoQuickActionsProps> = ({ memo, readonly, onEdit }) => {
const t = useTranslate();
const [enabled] = useLocalStorage(QUICK_ACTIONS_STORAGE_KEY, false);
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);

const isComment = Boolean(memo.parent);
const isArchived = memo.state === State.ARCHIVED;

const { handleTogglePinMemoBtnClick, handleEditMemoClick, handleToggleMemoStatusClick, handleDeleteMemoClick, confirmDeleteMemo } =
useMemoActionHandlers({ memo, onEdit, setDeleteDialogOpen });

if (!enabled || readonly) return null;

return (
<>
<div className="hidden sm:group-hover:flex items-center gap-0.5">
{!isComment && !isArchived && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon" className="size-6" onClick={handleTogglePinMemoBtnClick}>
{memo.pinned ? (
<BookmarkMinusIcon className="w-4 h-auto text-muted-foreground" />
) : (
<BookmarkPlusIcon className="w-4 h-auto text-muted-foreground" />
)}
</Button>
</TooltipTrigger>
<TooltipContent>{memo.pinned ? t("common.unpin") : t("common.pin")}</TooltipContent>
</Tooltip>
</TooltipProvider>
)}

{!isArchived && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon" className="size-6" onClick={handleEditMemoClick}>
<Edit3Icon className="w-4 h-auto text-muted-foreground" />
</Button>
</TooltipTrigger>
<TooltipContent>{t("common.edit")}</TooltipContent>
</Tooltip>
</TooltipProvider>
)}

{!isComment && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon" className="size-6" onClick={handleToggleMemoStatusClick}>
{isArchived ? (
<ArchiveRestoreIcon className="w-4 h-auto text-muted-foreground" />
) : (
<ArchiveIcon className="w-4 h-auto text-muted-foreground" />
)}
</Button>
</TooltipTrigger>
<TooltipContent>{isArchived ? t("common.restore") : t("common.archive")}</TooltipContent>
</Tooltip>
</TooltipProvider>
)}

<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon" className="size-6" onClick={handleDeleteMemoClick}>
<TrashIcon className="w-4 h-auto text-muted-foreground" />
</Button>
</TooltipTrigger>
<TooltipContent>{t("common.delete")}</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>

<ConfirmDialog
open={deleteDialogOpen}
onOpenChange={setDeleteDialogOpen}
title={t("memo.delete-confirm")}
confirmLabel={t("common.delete")}
description={t("memo.delete-confirm-description")}
cancelLabel={t("common.cancel")}
onConfirm={confirmDeleteMemo}
confirmVariant="destructive"
/>
</>
);
};

export default MemoQuickActions;
12 changes: 12 additions & 0 deletions web/src/components/Settings/PreferencesSection.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { create } from "@bufbuild/protobuf";
import { QUICK_ACTIONS_STORAGE_KEY } from "@/components/MemoView/components/MemoQuickActions";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Switch } from "@/components/ui/switch";
import { useAuth } from "@/contexts/AuthContext";
import { useLocalStorage } from "@/hooks";
import { useUpdateUserGeneralSetting } from "@/hooks/useUserQueries";
import { Visibility } from "@/types/proto/api/v1/memo_service_pb";
import { UserSetting_GeneralSetting, UserSetting_GeneralSettingSchema } from "@/types/proto/api/v1/user_service_pb";
Expand All @@ -18,6 +21,7 @@ const PreferencesSection = () => {
const t = useTranslate();
const { currentUser, userGeneralSetting: generalSetting, refetchSettings } = useAuth();
const { mutate: updateUserGeneralSetting } = useUpdateUserGeneralSetting(currentUser?.name);
const [quickActionsEnabled, setQuickActionsEnabled] = useLocalStorage(QUICK_ACTIONS_STORAGE_KEY, false);

const handleLocaleSelectChange = (locale: Locale) => {
// Apply locale immediately for instant UI feedback and persist to localStorage
Expand Down Expand Up @@ -81,6 +85,14 @@ const PreferencesSection = () => {
</SettingList>
</SettingGroup>

<SettingGroup title={t("setting.preference.behavior-title")} description={t("setting.preference.behavior-description")} showSeparator>
<SettingList>
<SettingListItem label={t("setting.preference.quick-actions")} description={t("setting.preference.quick-actions-description")}>
<Switch checked={quickActionsEnabled} onCheckedChange={setQuickActionsEnabled} />
</SettingListItem>
</SettingList>
</SettingGroup>

<SettingGroup
title={t("setting.preference.memo-defaults-title")}
description={t("setting.preference.memo-defaults-description")}
Expand Down
4 changes: 4 additions & 0 deletions web/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -582,13 +582,17 @@
"preference": {
"appearance-description": "Choose how the app looks and which language it uses for your account.",
"appearance-title": "Appearance",
"behavior-description": "Adjust interaction shortcuts for memos. Settings are stored in this browser.",
"behavior-title": "Behavior",
"default-memo-sort-option": "Memo display time",
"default-memo-visibility": "Default memo visibility",
"default-memo-visibility-description": "Visibility applied to newly created memos unless changed in the editor.",
"label": "Preferences",
"language-description": "Updates the interface language immediately and saves it to your account.",
"memo-defaults-description": "Set the defaults used when composing new memos.",
"memo-defaults-title": "Memo defaults",
"quick-actions": "Quick action buttons",
"quick-actions-description": "Show pin, edit, archive, and delete shortcuts when hovering over a memo on desktop.",
"theme-description": "Applies the selected theme immediately on this device.",
"theme": "Theme"
},
Expand Down
Loading