mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-05-18 13:16:11 +02:00
web: add clear trash option in trash nav item context menu
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com> web: disable clear trash if empty && update confirm message Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
This commit is contained in:
@@ -20,7 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import { Locator, Page } from "@playwright/test";
|
||||
import { getTestId } from "../utils";
|
||||
import { ContextMenuModel } from "./context-menu.model";
|
||||
import { fillItemDialog, iterateList } from "./utils";
|
||||
import { confirmDialog, fillItemDialog, iterateList } from "./utils";
|
||||
|
||||
export class NavigationMenuModel {
|
||||
protected readonly page: Page;
|
||||
@@ -101,4 +101,15 @@ class NavigationItemModel {
|
||||
await this.menu.open(this.locator);
|
||||
await this.menu.clickOnItem("remove-shortcut");
|
||||
}
|
||||
|
||||
async clearTrash() {
|
||||
await this.menu.open(this.locator);
|
||||
await this.menu.clickOnItem("clear-trash");
|
||||
await confirmDialog(this.page.locator(getTestId("confirm-dialog")));
|
||||
}
|
||||
|
||||
async getClearTrashItem() {
|
||||
await this.menu.open(this.locator);
|
||||
return this.menu.getItem("clear-trash");
|
||||
}
|
||||
}
|
||||
|
||||
49
apps/web/__e2e__/trash.test.ts
Normal file
49
apps/web/__e2e__/trash.test.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { AppModel } from "./models/app.model";
|
||||
import { expect, NOTE, test } from "./utils";
|
||||
|
||||
test("clear trash", async ({ page }) => {
|
||||
const app = new AppModel(page);
|
||||
await app.goto();
|
||||
const notes = await app.goToNotes();
|
||||
const note = await notes.createNote(NOTE);
|
||||
await note?.contextMenu.moveToTrash();
|
||||
const trash = await app.goToTrash();
|
||||
let trashedNote = await trash.findItem(NOTE.title);
|
||||
expect(trashedNote).toBeDefined();
|
||||
|
||||
const trashItem = await app.navigation.findItem("Trash");
|
||||
await trashItem?.clearTrash();
|
||||
|
||||
trashedNote = await trash.findItem(NOTE.title);
|
||||
expect(trashedNote).toBeUndefined();
|
||||
});
|
||||
|
||||
test("clear trash option should be disabled if trash is empty", async ({
|
||||
page
|
||||
}) => {
|
||||
const app = new AppModel(page);
|
||||
await app.goto();
|
||||
const trashItem = await app.navigation.findItem("Trash");
|
||||
|
||||
const clearTrashMenuItem = await trashItem?.getClearTrashItem();
|
||||
expect(await clearTrashMenuItem?.isDisabled()).toBe(true);
|
||||
});
|
||||
@@ -228,7 +228,8 @@ import {
|
||||
mdiNoteEditOutline,
|
||||
mdiArrowUp,
|
||||
mdiInbox,
|
||||
mdiConsoleLine
|
||||
mdiConsoleLine,
|
||||
mdiDeleteSweepOutline
|
||||
} from "@mdi/js";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { Theme } from "@notesnook/theme";
|
||||
@@ -584,3 +585,4 @@ export const ExpandSidebar = createIcon(mdiArrowCollapseRight);
|
||||
export const HamburgerMenu = createIcon(mdiMenu);
|
||||
export const ArrowUp = createIcon(mdiArrowUp);
|
||||
export const Inbox = createIcon(mdiInbox);
|
||||
export const ClearTrash = createIcon(mdiDeleteSweepOutline);
|
||||
|
||||
@@ -20,7 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import { forwardRef, useEffect, useRef, useState } from "react";
|
||||
import { Flex, Button } from "@theme-ui/components";
|
||||
import { SxProp } from "@theme-ui/core";
|
||||
import { Plus } from "../icons";
|
||||
import { Plus, Icon } from "../icons";
|
||||
import {
|
||||
useStore as useSelectionStore,
|
||||
store as selectionStore
|
||||
@@ -76,6 +76,7 @@ type ListContainerProps = {
|
||||
isSearching?: boolean;
|
||||
onDrop?: (e: React.DragEvent<HTMLDivElement>) => void;
|
||||
button?: {
|
||||
Icon?: Icon;
|
||||
onClick: () => void;
|
||||
};
|
||||
Scroller?: Components["Scroller"];
|
||||
@@ -275,7 +276,11 @@ function ListContainer(props: ListContainerProps) {
|
||||
height: 45
|
||||
}}
|
||||
>
|
||||
<Plus color="accentForeground" />
|
||||
{button.Icon ? (
|
||||
<button.Icon color="accentForeground" />
|
||||
) : (
|
||||
<Plus color="accentForeground" />
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
|
||||
@@ -47,7 +47,8 @@ import {
|
||||
Notebook as NotebookIcon,
|
||||
Plus,
|
||||
SortBy,
|
||||
Tag as TagIcon
|
||||
Tag as TagIcon,
|
||||
ClearTrash
|
||||
} from "../icons";
|
||||
import { SortableNavigationItem } from "./navigation-item";
|
||||
import {
|
||||
@@ -93,6 +94,8 @@ import { Color, Notebook, Tag } from "@notesnook/core";
|
||||
import { handleDrop } from "../../common/drop-handler";
|
||||
import { Menu, useMenuStore, useMenuTrigger } from "../../hooks/use-menu";
|
||||
import { RenameColorDialog } from "../../dialogs/item-dialog";
|
||||
import { ConfirmDialog } from "../../dialogs/confirm";
|
||||
import { showToast } from "../../utils/toast";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import Tags from "../../views/tags";
|
||||
import { Notebooks } from "../../views/notebooks";
|
||||
@@ -515,6 +518,7 @@ function RouteItem({
|
||||
context?: { isCollapsed: boolean; collapse: () => void };
|
||||
}) {
|
||||
const [location] = useLocation();
|
||||
const trash = useTrashStore((store) => store.trash);
|
||||
|
||||
return (
|
||||
<SortableNavigationItem
|
||||
@@ -545,6 +549,39 @@ function RouteItem({
|
||||
context?.collapse();
|
||||
}}
|
||||
menuItems={[
|
||||
...(item.id === "trash"
|
||||
? [
|
||||
{
|
||||
type: "button" as const,
|
||||
key: "clear-trash",
|
||||
title: strings.clearTrash(),
|
||||
isDisabled: !trash || trash.length === 0,
|
||||
icon: ClearTrash.path,
|
||||
onClick: async () => {
|
||||
const ok = await ConfirmDialog.show({
|
||||
title: strings.clearTrash(),
|
||||
positiveButtonText: strings.clear(),
|
||||
negativeButtonText: strings.cancel(),
|
||||
message: strings.clearTrashDesc()
|
||||
});
|
||||
if (!ok) return;
|
||||
|
||||
try {
|
||||
await useTrashStore.getState().clear();
|
||||
showToast("success", strings.trashCleared());
|
||||
} catch (e) {
|
||||
if (e instanceof Error)
|
||||
showToast(
|
||||
"error",
|
||||
`${strings.couldNotClearTrash()} ${strings.error()}: ${
|
||||
e.message
|
||||
}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
: []),
|
||||
{
|
||||
type: "lazy-loader",
|
||||
key: "sidebar-items-loader",
|
||||
|
||||
@@ -27,6 +27,7 @@ import { db } from "../common/db";
|
||||
import { ListLoader } from "../components/loaders/list-loader";
|
||||
import { ConfirmDialog } from "../dialogs/confirm";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { ClearTrash } from "../components/icons";
|
||||
|
||||
function Trash() {
|
||||
useNavigate("trash", store.refresh);
|
||||
@@ -47,13 +48,13 @@ function Trash() {
|
||||
placeholder={<Placeholder context={filteredItems ? "search" : "trash"} />}
|
||||
items={filteredItems || items}
|
||||
button={{
|
||||
Icon: ClearTrash,
|
||||
onClick: function () {
|
||||
ConfirmDialog.show({
|
||||
title: strings.clearTrash(),
|
||||
subtitle: strings.clearTrashDesc(),
|
||||
positiveButtonText: strings.clear(),
|
||||
negativeButtonText: strings.cancel(),
|
||||
message: strings.areYouSure()
|
||||
message: strings.clearTrashDesc()
|
||||
}).then(async (res) => {
|
||||
if (res) {
|
||||
try {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -2225,7 +2225,8 @@ Use this if changes from other devices are not appearing on this device. This wi
|
||||
goBackToNotebooks: () => t`Go back to notebooks`,
|
||||
goBackToTags: () => t`Go back to tags`,
|
||||
okay: () => t`Okay`,
|
||||
clearTrashDesc: () => t`Do you want to clear the trash?`,
|
||||
clearTrashDesc: () =>
|
||||
t`Clearing trash will permanently delete all the items in your trash. This action is IRREVERSIBLE.`,
|
||||
createdAt: () => t`Created at`,
|
||||
lastEditedAt: () => t`Last edited at`,
|
||||
enter6DigitCode: () =>
|
||||
|
||||
Reference in New Issue
Block a user