diff --git a/apps/web/__e2e__/models/app.model.ts b/apps/web/__e2e__/models/app.model.ts
index 5a01aea04..b152e11fa 100644
--- a/apps/web/__e2e__/models/app.model.ts
+++ b/apps/web/__e2e__/models/app.model.ts
@@ -61,7 +61,7 @@ export class AppModel {
async goToNotes() {
await this.navigateTo("Notes");
- return new NotesViewModel(this.page, "home");
+ return new NotesViewModel(this.page, "home", "home");
}
async goToNotebooks() {
@@ -71,7 +71,7 @@ export class AppModel {
async goToFavorites() {
await this.navigateTo("Favorites");
- return new NotesViewModel(this.page, "notes");
+ return new NotesViewModel(this.page, "notes", "favorites");
}
async goToReminders() {
@@ -86,7 +86,7 @@ export class AppModel {
async goToColor(color: string) {
await this.navigateTo(color);
- return new NotesViewModel(this.page, "notes");
+ return new NotesViewModel(this.page, "notes", "notes");
}
async goToTrash() {
diff --git a/apps/web/__e2e__/models/base-view.model.ts b/apps/web/__e2e__/models/base-view.model.ts
index 676a0f3b6..65cffb08d 100644
--- a/apps/web/__e2e__/models/base-view.model.ts
+++ b/apps/web/__e2e__/models/base-view.model.ts
@@ -29,12 +29,15 @@ export class BaseViewModel {
private readonly listPlaceholder: Locator;
private readonly sortByButton: Locator;
- constructor(page: Page, pageId: string, listType: string) {
+ constructor(page: Page, pageId: string, readonly listType: string) {
this.page = page;
- this.list = page.locator(`#${pageId} >> ${getTestId(`${listType}-list`)}`);
- this.listPlaceholder = page.locator(
- `#${pageId} >> ${getTestId("list-placeholder")}`
- );
+ this.list = page
+ .locator(`#${pageId}`)
+ .locator(getTestId(`${listType}-list`));
+
+ this.listPlaceholder = page
+ .locator(`#${pageId}`)
+ .locator(getTestId("list-placeholder"));
this.sortByButton = this.page.locator(
// TODO:
@@ -43,9 +46,9 @@ export class BaseViewModel {
}
async findGroup(groupName: string) {
- const locator = this.list.locator(
- `${getTestId(`virtuoso-item-list`)} >> ${getTestId("group-header")}`
- );
+ const locator = this.list
+ .locator(getTestId(`virtualized-list`))
+ .locator(getTestId("group-header"));
for await (const item of iterateList(locator)) {
if ((await item.locator(getTestId("title")).textContent()) === groupName)
@@ -56,13 +59,10 @@ export class BaseViewModel {
protected async *iterateItems() {
await this.waitForList();
- const locator = this.list.locator(
- `${getTestId(`virtuoso-item-list`)} >> ${getTestId("list-item")}`
- );
- for await (const _item of iterateList(locator)) {
+ for await (const _item of iterateList(this.items)) {
const id = await _item.getAttribute("id");
- if (!id) return;
+ if (!id) continue;
yield this.list.locator(`#${id}`);
}
@@ -82,11 +82,8 @@ export class BaseViewModel {
}
async focus() {
- const items = this.list.locator(
- `${getTestId(`virtuoso-item-list`)} >> ${getTestId("list-item")}`
- );
- await items.nth(0).click();
- await items.nth(0).click();
+ await this.items.nth(0).click();
+ await this.items.nth(0).click();
}
// async selectAll() {
@@ -98,7 +95,7 @@ export class BaseViewModel {
// }
async press(key: string) {
- const itemList = this.list.locator(getTestId(`virtuoso-item-list`));
+ const itemList = this.list.locator(getTestId(`virtualized-list`));
await itemList.press(key);
await this.page.waitForTimeout(300);
}
@@ -135,11 +132,11 @@ export class BaseViewModel {
return true;
}
+ get items() {
+ return this.list.locator(getTestId("list-item"));
+ }
+
async isEmpty() {
- const items = this.list.locator(
- `${getTestId(`virtuoso-item-list`)} >> ${getTestId("list-item")}`
- );
- const totalItems = await items.count();
- return totalItems <= 0;
+ return (await this.items.count()) <= 0;
}
}
diff --git a/apps/web/__e2e__/models/editor.model.ts b/apps/web/__e2e__/models/editor.model.ts
index c98c06f29..22bf5ff6d 100644
--- a/apps/web/__e2e__/models/editor.model.ts
+++ b/apps/web/__e2e__/models/editor.model.ts
@@ -44,7 +44,7 @@ export class EditorModel {
this.title = page.locator(getTestId("editor-title"));
this.content = page.locator(".ProseMirror");
this.tagInput = page.locator(getTestId("editor-tag-input"));
- this.tags = page.locator(`${getTestId("tags")} > ${getTestId("tag")}`);
+ this.tags = page.locator(`${getTestId("tags")} >> ${getTestId("tag")}`);
this.focusModeButton = page.locator(getTestId("Focus mode"));
this.normalModeButton = page.locator(getTestId("Normal mode"));
this.darkModeButton = page.locator(getTestId("Dark mode"));
diff --git a/apps/web/__e2e__/models/item.model.ts b/apps/web/__e2e__/models/item.model.ts
index 3b9371abd..b9a29aa48 100644
--- a/apps/web/__e2e__/models/item.model.ts
+++ b/apps/web/__e2e__/models/item.model.ts
@@ -23,6 +23,7 @@ import { ContextMenuModel } from "./context-menu.model";
import { NotesViewModel } from "./notes-view.model";
import { Item } from "./types";
import { confirmDialog, fillItemDialog } from "./utils";
+import { getTestId } from "../utils";
export class ItemModel extends BaseItemModel {
private readonly contextMenu: ContextMenuModel;
@@ -35,7 +36,8 @@ export class ItemModel extends BaseItemModel {
await this.locator.click();
return new NotesViewModel(
this.page,
- this.id === "topic" ? "notebook" : "notes"
+ this.id === "topic" ? "notebook" : "notes",
+ "notes"
);
}
@@ -53,7 +55,7 @@ export class ItemModel extends BaseItemModel {
if (deleteContainedNotes)
await this.page.locator("#deleteContainingNotes").check({ force: true });
- await confirmDialog(this.page);
+ await confirmDialog(this.page.locator(getTestId("confirm-dialog")));
await this.waitFor("detached");
}
diff --git a/apps/web/__e2e__/models/note-properties.model.ts b/apps/web/__e2e__/models/note-properties.model.ts
index c0b441f0e..760b11944 100644
--- a/apps/web/__e2e__/models/note-properties.model.ts
+++ b/apps/web/__e2e__/models/note-properties.model.ts
@@ -22,7 +22,12 @@ import { downloadAndReadFile, getTestId } from "../utils";
import { ContextMenuModel } from "./context-menu.model";
import { ToggleModel } from "./toggle.model";
import { Notebook } from "./types";
-import { fillPasswordDialog, iterateList } from "./utils";
+import {
+ confirmDialog,
+ fillNotebookDialog,
+ fillPasswordDialog,
+ iterateList
+} from "./utils";
abstract class BaseProperties {
protected readonly page: Page;
@@ -248,51 +253,59 @@ export class NoteContextMenuModel extends BaseProperties {
}
async addToNotebook(notebook: Notebook) {
+ async function addSubNotebooks(
+ page: Page,
+ dialog: Locator,
+ item: Locator,
+ notebook: Notebook
+ ) {
+ if (notebook.subNotebooks) {
+ const addSubNotebookButton = item.locator(
+ getTestId("add-sub-notebook")
+ );
+ for (const subNotebook of notebook.subNotebooks) {
+ await addSubNotebookButton.click();
+
+ await fillNotebookDialog(page, subNotebook);
+
+ const subNotebookItem = dialog.locator(getTestId("notebook"), {
+ hasText: subNotebook.title
+ });
+ await subNotebookItem.waitFor();
+
+ await page.keyboard.down("Control");
+ await subNotebookItem.click();
+ await page.keyboard.up("Control");
+
+ await addSubNotebooks(page, dialog, subNotebookItem, subNotebook);
+ }
+ }
+ }
+
await this.open();
await this.menu.clickOnItem("notebooks");
await this.menu.clickOnItem("link-notebooks");
- const filterInput = this.page.locator(getTestId("filter-input"));
- await filterInput.type(notebook.title);
- await filterInput.press("Enter");
+ const dialog = this.page.locator(getTestId("move-note-dialog"));
- await this.page.waitForSelector(getTestId("notebook"), {
- state: "visible",
- strict: false
+ await dialog.locator(getTestId("add-new-notebook")).click();
+
+ await fillNotebookDialog(this.page, notebook);
+
+ const notebookItem = dialog.locator(getTestId("notebook"), {
+ hasText: notebook.title
});
- const notebookItems = this.page.locator(getTestId("notebook"));
- for await (const item of iterateList(notebookItems)) {
- await item.locator(getTestId("notebook-tools")).click();
- const title = item.locator(getTestId("notebook-title"));
- const createTopicButton = item.locator(getTestId("create-topic"));
- const notebookTitle = await title.textContent();
+ await notebookItem.waitFor({ state: "visible" });
- if (notebookTitle?.includes(notebook.title)) {
- for (const topic of notebook.topics) {
- await createTopicButton.click();
- const newItemInput = item.locator(getTestId("new-topic-input"));
+ await this.page.keyboard.down("Control");
+ await notebookItem.click();
+ await this.page.keyboard.up("Control");
- await newItemInput.waitFor({ state: "visible" });
- await newItemInput.fill(topic);
- await newItemInput.press("Enter");
+ await addSubNotebooks(this.page, dialog, notebookItem, notebook);
- await item.locator(getTestId("topic"), { hasText: topic }).waitFor();
- }
-
- const topicItems = item.locator(getTestId("topic"));
- for await (const topicItem of iterateList(topicItems)) {
- await this.page.keyboard.down("Control");
- await topicItem.click();
- await this.page.keyboard.up("Control");
- }
- }
- }
-
- const dialogConfirm = this.page.locator(getTestId("dialog-yes"));
- await dialogConfirm.click();
- await dialogConfirm.waitFor({ state: "detached" });
+ await confirmDialog(dialog);
}
async open() {
diff --git a/apps/web/__e2e__/models/notebook-item.model.ts b/apps/web/__e2e__/models/notebook-item.model.ts
index eb240f8ac..d310b5e6b 100644
--- a/apps/web/__e2e__/models/notebook-item.model.ts
+++ b/apps/web/__e2e__/models/notebook-item.model.ts
@@ -25,6 +25,7 @@ import { ItemsViewModel } from "./items-view.model";
import { Notebook } from "./types";
import { confirmDialog, fillNotebookDialog } from "./utils";
import { NotesViewModel } from "./notes-view.model";
+import { getTestId } from "../utils";
export class NotebookItemModel extends BaseItemModel {
private readonly contextMenu: ContextMenuModel;
@@ -37,7 +38,7 @@ export class NotebookItemModel extends BaseItemModel {
await this.locator.click();
return {
topics: new ItemsViewModel(this.page, "topics"),
- notes: new NotesViewModel(this.page, "notebook")
+ notes: new NotesViewModel(this.page, "notebook", "notes")
};
}
@@ -45,7 +46,7 @@ export class NotebookItemModel extends BaseItemModel {
await this.contextMenu.open(this.locator);
await this.contextMenu.clickOnItem("edit");
- await fillNotebookDialog(this.page, notebook, true);
+ await fillNotebookDialog(this.page, notebook);
}
async moveToTrash(deleteContainedNotes = false) {
@@ -55,7 +56,7 @@ export class NotebookItemModel extends BaseItemModel {
if (deleteContainedNotes)
await this.page.locator("#deleteContainingNotes").check({ force: true });
- await confirmDialog(this.page);
+ await confirmDialog(this.page.locator(getTestId("confirm-dialog")));
await this.waitFor("detached");
}
diff --git a/apps/web/__e2e__/models/notes-view.model.ts b/apps/web/__e2e__/models/notes-view.model.ts
index e1a72977e..3c7de4528 100644
--- a/apps/web/__e2e__/models/notes-view.model.ts
+++ b/apps/web/__e2e__/models/notes-view.model.ts
@@ -32,8 +32,12 @@ export class NotesViewModel extends BaseViewModel {
private readonly createButton: Locator;
readonly editor: EditorModel;
- constructor(page: Page, pageId: "home" | "notes" | "notebook") {
- super(page, pageId, pageId === "home" ? "home" : "notes");
+ constructor(
+ page: Page,
+ pageId: "home" | "notes" | "favorites" | "notebook",
+ listType: string
+ ) {
+ super(page, pageId, listType);
this.createButton = page.locator(
// TODO:
getTestId(`notes-action-button`)
diff --git a/apps/web/__e2e__/models/reminder-item.model.ts b/apps/web/__e2e__/models/reminder-item.model.ts
index aa2391507..3b81cf9cb 100644
--- a/apps/web/__e2e__/models/reminder-item.model.ts
+++ b/apps/web/__e2e__/models/reminder-item.model.ts
@@ -47,7 +47,7 @@ export class ReminderItemModel extends BaseItemModel {
await this.contextMenu.open(this.locator);
await this.contextMenu.clickOnItem("delete");
- await confirmDialog(this.page);
+ await confirmDialog(this.page.locator(getTestId("confirm-dialog")));
await this.waitFor("detached");
}
diff --git a/apps/web/__e2e__/models/settings-view.model.ts b/apps/web/__e2e__/models/settings-view.model.ts
index 7fa558a8f..ddcdca2df 100644
--- a/apps/web/__e2e__/models/settings-view.model.ts
+++ b/apps/web/__e2e__/models/settings-view.model.ts
@@ -52,7 +52,7 @@ export class SettingsViewModel {
.locator("button");
await logoutButton.click();
- await confirmDialog(this.page);
+ await confirmDialog(this.page.locator(getTestId("confirm-dialog")));
await this.page
.locator(getTestId("not-logged-in"))
@@ -75,7 +75,9 @@ export class SettingsViewModel {
const key = await this.page
.locator(getTestId("recovery-key"))
.textContent();
- await confirmDialog(this.page);
+
+ const dialog = this.page.locator(getTestId("recovery-key-dialog"));
+ await confirmDialog(dialog);
return key;
}
diff --git a/apps/web/__e2e__/models/types.ts b/apps/web/__e2e__/models/types.ts
index eb8520681..82092f975 100644
--- a/apps/web/__e2e__/models/types.ts
+++ b/apps/web/__e2e__/models/types.ts
@@ -19,8 +19,8 @@ along with this program. If not, see .
export type Notebook = {
title: string;
- topics: string[];
description?: string;
+ subNotebooks?: Notebook[];
};
export type Item = {
diff --git a/apps/web/__e2e__/models/utils.ts b/apps/web/__e2e__/models/utils.ts
index 4ea1ab11d..b5642a474 100644
--- a/apps/web/__e2e__/models/utils.ts
+++ b/apps/web/__e2e__/models/utils.ts
@@ -30,42 +30,28 @@ export async function* iterateList(list: Locator) {
return null;
}
-export async function fillNotebookDialog(
- page: Page,
- notebook: Notebook,
- editing = false
-) {
- const titleInput = page.locator(getTestId("title-input"));
- const descriptionInput = page.locator(getTestId("description-input"));
- const topicInput = page.locator(getTestId(`edit-topic-input`));
- const topicInputAction = page.locator(getTestId(`edit-topic-action`));
+export async function fillNotebookDialog(page: Page, notebook: Notebook) {
+ const dialog = page.locator(getTestId("add-notebook-dialog"));
+ const titleInput = dialog.locator(getTestId("title-input"));
+ const descriptionInput = dialog.locator(getTestId("description-input"));
await titleInput.waitFor({ state: "visible" });
await titleInput.fill(notebook.title);
if (notebook.description) await descriptionInput.fill(notebook.description);
- const topicItems = page.locator(getTestId("topic-item"));
- for (let i = 0; i < notebook.topics.length; ++i) {
- if (editing) {
- const topicItem = topicItems.nth(i);
- await topicItem.click();
- }
- await topicInput.fill(notebook.topics[i]);
- await topicInputAction.click();
- }
-
- await confirmDialog(page);
+ await confirmDialog(dialog);
}
export async function fillReminderDialog(
page: Page,
reminder: Partial
) {
- const titleInput = page.locator(getTestId("title-input"));
- const descriptionInput = page.locator(getTestId("description-input"));
- const dateInput = page.locator(getTestId("date-input"));
- const timeInput = page.locator(getTestId("time-input"));
+ const dialog = page.locator(getTestId("reminder-dialog"));
+ const titleInput = dialog.locator(getTestId("title-input"));
+ const descriptionInput = dialog.locator(getTestId("description-input"));
+ const dateInput = dialog.locator(getTestId("date-input"));
+ const timeInput = dialog.locator(getTestId("time-input"));
if (reminder.title) {
await titleInput.waitFor({ state: "visible" });
@@ -73,10 +59,10 @@ export async function fillReminderDialog(
}
if (reminder.description) await descriptionInput.fill(reminder.description);
if (reminder.mode)
- await page.locator(getTestId(`mode-${reminder.mode}`)).click();
+ await dialog.locator(getTestId(`mode-${reminder.mode}`)).click();
if (reminder.priority)
- await page.locator(getTestId(`priority-${reminder.priority}`)).click();
+ await dialog.locator(getTestId(`priority-${reminder.priority}`)).click();
if (reminder.recurringMode && reminder.mode === "repeat") {
await page
@@ -89,7 +75,7 @@ export async function fillReminderDialog(
reminder.recurringMode !== "day"
) {
for (const day of reminder.selectedDays) {
- await page.locator(getTestId(`day-${day}`)).click();
+ await dialog.locator(getTestId(`day-${day}`)).click();
}
}
}
@@ -111,25 +97,27 @@ export async function fillReminderDialog(
await timeInput.fill(time);
}
- await confirmDialog(page);
+ await confirmDialog(dialog);
}
export async function fillItemDialog(page: Page, item: Item) {
- const titleInput = page.locator(getTestId("title-input"));
+ const dialog = page.locator(getTestId("item-dialog"));
+ const titleInput = dialog.locator(getTestId("title-input"));
await titleInput.waitFor({ state: "visible" });
await titleInput.fill(item.title);
- await confirmDialog(page);
+ await confirmDialog(dialog);
}
export async function fillPasswordDialog(page: Page, password: string) {
- await page.locator(getTestId("dialog-password")).fill(password);
- await confirmDialog(page);
+ const dialog = page.locator(getTestId("password-dialog"));
+ await dialog.locator(getTestId("dialog-password")).fill(password);
+ await confirmDialog(dialog);
}
-export async function confirmDialog(page: Page) {
- const dialogConfirm = page.locator(getTestId("dialog-yes"));
+export async function confirmDialog(dialog: Locator) {
+ const dialogConfirm = dialog.locator(getTestId("dialog-yes"));
await dialogConfirm.click();
// await dialogConfirm.waitFor({ state: "detached" });
}
diff --git a/apps/web/__e2e__/notes.test.ts b/apps/web/__e2e__/notes.test.ts
index 3d6d8c2fc..051a0ed6d 100644
--- a/apps/web/__e2e__/notes.test.ts
+++ b/apps/web/__e2e__/notes.test.ts
@@ -92,12 +92,15 @@ test("add a note to notebook", async ({ page }) => {
await note?.contextMenu.addToNotebook({
title: "Notebook 1",
- topics: ["Hello", "World", "Did", "what"]
+ subNotebooks: [
+ { title: "Hello" },
+ { title: "World", subNotebooks: [{ title: "Did" }, { title: "what" }] }
+ ]
});
- expect(
- await app.toasts.waitForToast("1 note added to Hello and 3 others.")
- ).toBe(true);
+ expect(await app.toasts.waitForToast("1 note added to 5 notebooks.")).toBe(
+ true
+ );
});
const actors = ["contextMenu", "properties"] as const;
@@ -165,7 +168,7 @@ for (const actor of actors) {
await note?.[actor].color("red");
- const coloredNotes = await app.goToColor("red");
+ const coloredNotes = await app.goToColor("Red");
const coloredNote = await coloredNotes.findNote(NOTE);
expect(coloredNote).toBeDefined();
expect(await coloredNote?.contextMenu.isColored("red")).toBe(true);
@@ -307,7 +310,7 @@ test(`sort notes`, async ({ page }, info) => {
});
if (!sortResult) return;
- expect(await notes.isEmpty()).toBeFalsy();
+ await expect(notes.items).toHaveCount(titles.length);
});
}
}
diff --git a/apps/web/__e2e__/notes.test.ts-snapshots/export-txt-Chromium-win32.txt b/apps/web/__e2e__/notes.test.ts-snapshots/export-txt-Chromium-win32.txt
index aa657e922..24dab4114 100644
--- a/apps/web/__e2e__/notes.test.ts-snapshots/export-txt-Chromium-win32.txt
+++ b/apps/web/__e2e__/notes.test.ts-snapshots/export-txt-Chromium-win32.txt
@@ -1,7 +1,3 @@
Test 1
-----------
-This is Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1
-
-----------
-Tags:
\ No newline at end of file
+ This is Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1
\ No newline at end of file
diff --git a/apps/web/src/common/vault.js b/apps/web/src/common/vault.js
index 38235da40..cf2052555 100644
--- a/apps/web/src/common/vault.js
+++ b/apps/web/src/common/vault.js
@@ -20,6 +20,7 @@ along with this program. If not, see .
import { db } from "./db";
import { showPasswordDialog } from "./dialog-controller";
import { showToast } from "../utils/toast";
+import { VAULT_ERRORS } from "@notesnook/core/dist/api/vault";
class Vault {
static async createVault() {
diff --git a/apps/web/src/components/dialog/index.tsx b/apps/web/src/components/dialog/index.tsx
index c096b3fd6..398005e0b 100644
--- a/apps/web/src/components/dialog/index.tsx
+++ b/apps/web/src/components/dialog/index.tsx
@@ -36,6 +36,7 @@ type DialogButtonProps = ButtonProps & {
};
type DialogProps = SxProp & {
+ testId?: string;
isOpen?: boolean;
onClose?: (
event?: React.MouseEvent | React.KeyboardEvent
@@ -65,6 +66,9 @@ function BaseDialog(props: React.PropsWithChildren) {
shouldFocusAfterRender
onAfterOpen={(e) => onAfterOpen(e, props)}
overlayClassName={"theme-scope-dialog"}
+ data={{
+ "test-id": props.testId
+ }}
style={{
content: {
top: 0,
diff --git a/apps/web/src/components/field/index.tsx b/apps/web/src/components/field/index.tsx
index 0b1f092a0..566be4aa9 100644
--- a/apps/web/src/components/field/index.tsx
+++ b/apps/web/src/components/field/index.tsx
@@ -44,8 +44,17 @@ export type FieldProps = InputProps & {
};
function Field(props: FieldProps) {
- const { label, styles, helpText, action, sx, id, type, ...inputProps } =
- props;
+ const {
+ label,
+ styles,
+ helpText,
+ action,
+ sx,
+ id,
+ type,
+ inputRef,
+ ...inputProps
+ } = props;
const [isPasswordVisible, setIsPasswordVisible] = useState(false);
const colorScheme = useThemeStore((state) => state.colorScheme);
@@ -87,6 +96,7 @@ function Field(props: FieldProps) {
{
+ const isChecked = !!noteColor && noteColor.title === color.title;
return {
type: "button",
key: color.key,
title: color.title,
icon: Circle.path,
- styles: { icon: { color: StaticColors[color.key] } },
- isChecked: noteColor && noteColor.title === color.title,
- onClick: () => store.setColor(color.title, ...ids)
+ styles: { icon: { color: DefaultColors[color.key] } },
+ isChecked,
+ onClick: () => store.setColor(color, isChecked, ...ids)
} satisfies MenuItem;
});
}
diff --git a/apps/web/src/components/properties/index.tsx b/apps/web/src/components/properties/index.tsx
index 3774482e2..64d499202 100644
--- a/apps/web/src/components/properties/index.tsx
+++ b/apps/web/src/components/properties/index.tsx
@@ -204,12 +204,15 @@ function EditorProperties(props: EditorPropertiesProps) {
export default React.memo(EditorProperties);
function Colors({ noteId }: { noteId: string }) {
- const result = usePromise(async () =>
- (
- await db.relations.to({ id: noteId, type: "note" }, "color").resolve(1)
- ).at(0)
+ const color = useStore((store) => store.color);
+ const result = usePromise(
+ async () =>
+ (
+ await db.relations.to({ id: noteId, type: "note" }, "color").resolve(1)
+ ).at(0),
+ [color]
);
-
+ console.log(result);
return (
- {COLORS.map((label) => (
- noteStore.get().setColor(label.key, noteId)}
- sx={{
- cursor: "pointer",
- position: "relative",
- alignItems: "center",
- justifyContent: "space-between"
- }}
- data-test-id={`properties-${label}`}
- >
-
- {result.status === "fulfilled" &&
- label.key === result.value?.colorCode && (
+ {COLORS.map((label) => {
+ const isChecked =
+ result.status === "fulfilled" &&
+ DefaultColors[label.key] === result.value?.colorCode;
+ return (
+ noteStore.get().setColor(label, isChecked, noteId)}
+ sx={{
+ cursor: "pointer",
+ position: "relative",
+ alignItems: "center",
+ justifyContent: "space-between"
+ }}
+ data-test-id={`properties-${label.key}`}
+ >
+
+ {isChecked && (
)}
-
- ))}
+
+ );
+ })}
);
}
@@ -271,9 +273,9 @@ function Notebooks({ noteId }: { noteId: string }) {
mode="fixed"
estimatedSize={50}
getItemKey={(index) => result.value.getKey(index)}
- items={result.value.ids}
- renderItem={(id) => (
-
+ items={result.value.ungrouped}
+ renderItem={({ item: id }) => (
+
)}
/>
@@ -294,9 +296,9 @@ function Reminders({ noteId }: { noteId: string }) {
mode="fixed"
estimatedSize={54}
getItemKey={(index) => result.value.getKey(index)}
- items={result.value.ids}
- renderItem={(id) => (
-
+ items={result.value.ungrouped}
+ renderItem={({ item: id }) => (
+
)}
/>
@@ -352,20 +354,18 @@ function SessionHistory({
mode="fixed"
estimatedSize={28}
getItemKey={(index) => result.value.getKey(index)}
- items={result.value.ids}
- renderItem={(id) => (
-
- {({ item }) =>
- item.type === "session" ? (
-
- ) : null
- }
+ items={result.value.ungrouped}
+ renderItem={({ item: id }) => (
+
+ {({ item }) => (
+
+ )}
)}
/>
diff --git a/apps/web/src/components/trash-item/index.tsx b/apps/web/src/components/trash-item/index.tsx
index 106978225..344216838 100644
--- a/apps/web/src/components/trash-item/index.tsx
+++ b/apps/web/src/components/trash-item/index.tsx
@@ -24,7 +24,6 @@ import { store } from "../../stores/trash-store";
import { Flex, Text } from "@theme-ui/components";
import TimeAgo from "../time-ago";
import { pluralize, toTitleCase } from "@notesnook/common";
-import { showUndoableToast } from "../../common/toasts";
import { showToast } from "../../utils/toast";
import { hashNavigate } from "../../navigation";
import { useStore } from "../../stores/note-store";
@@ -78,8 +77,8 @@ const menuItems: (item: TrashItem, ids?: string[]) => MenuItem[] = (
key: "restore",
title: "Restore",
icon: Restore.path,
- onClick: () => {
- store.restore(ids);
+ onClick: async () => {
+ await store.restore(...ids);
showToast("success", `${pluralize(ids.length, "item")} restored`);
},
multiSelect: true
@@ -92,11 +91,10 @@ const menuItems: (item: TrashItem, ids?: string[]) => MenuItem[] = (
variant: "dangerous",
onClick: async () => {
if (!(await showMultiPermanentDeleteConfirmation(ids.length))) return;
- showUndoableToast(
- `${pluralize(ids.length, "item")} permanently deleted`,
- () => store.delete(ids),
- () => store.delete(ids, true),
- () => store.refresh()
+ await store.delete(...ids);
+ showToast(
+ "success",
+ `${pluralize(ids.length, "item")} permanently deleted`
);
},
multiSelect: true
diff --git a/apps/web/src/components/unlock/index.tsx b/apps/web/src/components/unlock/index.tsx
index ea0106f50..4f243633c 100644
--- a/apps/web/src/components/unlock/index.tsx
+++ b/apps/web/src/components/unlock/index.tsx
@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-import { useRef, useState, useCallback, useEffect, useMemo } from "react";
+import { useRef, useState, useCallback, useEffect } from "react";
import { Flex, Text, Button } from "@theme-ui/components";
import { Lock } from "../icons";
import { db } from "../../common/db";
@@ -26,6 +26,7 @@ import { useStore as useAppStore } from "../../stores/app-store";
import Field from "../field";
import { showToast } from "../../utils/toast";
import { ErrorText } from "../error-text";
+import usePromise from "../../hooks/use-promise";
type UnlockProps = {
noteId: string;
@@ -35,25 +36,23 @@ function Unlock(props: UnlockProps) {
const [isWrong, setIsWrong] = useState(false);
const [isUnlocking, setIsUnlocking] = useState(false);
- const [isLoading, setIsLoading] = useState(true);
- const passwordRef = useRef();
+ const passwordRef = useRef(null);
- const note = useMemo(
- () => (!isLoading ? db.notes.note(noteId)?.data : undefined),
- [noteId, isLoading]
- );
+ const note = usePromise(() => db.notes.note(noteId), [noteId]);
const openLockedSession = useEditorStore((store) => store.openLockedSession);
const openSession = useEditorStore((store) => store.openSession);
const setIsEditorOpen = useAppStore((store) => store.setIsEditorOpen);
const submit = useCallback(async () => {
+ console.log("HELO", passwordRef.current);
if (!passwordRef.current) return;
setIsUnlocking(true);
const password = passwordRef.current.value;
try {
if (!password) return;
const note = await db.vault.open(noteId, password);
+ console.log(note);
openLockedSession(note);
} catch (e) {
if (
@@ -72,12 +71,8 @@ function Unlock(props: UnlockProps) {
useEffect(() => {
(async () => {
- setIsLoading(true);
-
await openSession(noteId);
setIsEditorOpen(true);
-
- setIsLoading(false);
})();
}, [openSession, setIsEditorOpen, noteId]);
@@ -106,7 +101,9 @@ function Unlock(props: UnlockProps) {
mt={25}
sx={{ fontSize: 36, textAlign: "center" }}
>
- {note?.title || "Open note"}
+ {note.status === "fulfilled" && note.value
+ ? note.value.title
+ : "Open note"}
{
+ onKeyUp={async (e) => {
if (e.key === "Enter") {
await submit();
} else if (isWrong) {
diff --git a/apps/web/src/components/virtualized-list/index.tsx b/apps/web/src/components/virtualized-list/index.tsx
index d883a56a4..2d0035f1e 100644
--- a/apps/web/src/components/virtualized-list/index.tsx
+++ b/apps/web/src/components/virtualized-list/index.tsx
@@ -78,6 +78,7 @@ export function VirtualizedList(props: VirtualizedListProps) {
position: "relative",
gap: itemGap
}}
+ data-test-id="virtualized-list"
>
{virtualItems.map((row) => (
(
return (