mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-02-24 20:20:21 +01:00
web: add support for archiving notes (#7810)
* web: archive notes Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com> * global: arhives -> archived Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
This commit is contained in:
@@ -108,6 +108,12 @@ export class AppModel {
|
||||
return new TrashViewModel(this.page);
|
||||
}
|
||||
|
||||
async goToArchive() {
|
||||
await this.page.locator(getTestId("tab-home")).click();
|
||||
await this.navigateTo("Archive");
|
||||
return new NotesViewModel(this.page, "notes", "archive");
|
||||
}
|
||||
|
||||
async goToSettings() {
|
||||
await this.profileDropdown.open(
|
||||
this.page.locator(getTestId("profile-dropdown")),
|
||||
|
||||
@@ -37,6 +37,7 @@ abstract class BaseProperties {
|
||||
private readonly pinToggle: ToggleModel;
|
||||
private readonly favoriteToggle: ToggleModel;
|
||||
private readonly lockToggle: ToggleModel;
|
||||
private readonly archiveToggle: ToggleModel;
|
||||
|
||||
constructor(
|
||||
page: Page,
|
||||
@@ -47,6 +48,7 @@ abstract class BaseProperties {
|
||||
this.pinToggle = new ToggleModel(page, `${itemPrefix}-pin`);
|
||||
this.lockToggle = new ToggleModel(page, `${itemPrefix}-lock`);
|
||||
this.favoriteToggle = new ToggleModel(page, `${itemPrefix}-favorite`);
|
||||
this.archiveToggle = new ToggleModel(page, `${itemPrefix}-archive`);
|
||||
}
|
||||
|
||||
async isPinned() {
|
||||
@@ -126,6 +128,25 @@ abstract class BaseProperties {
|
||||
await this.close();
|
||||
}
|
||||
|
||||
async isArchived() {
|
||||
await this.open();
|
||||
const state = await this.archiveToggle.isToggled();
|
||||
await this.close();
|
||||
return state;
|
||||
}
|
||||
|
||||
async archive() {
|
||||
await this.open();
|
||||
await this.archiveToggle.on();
|
||||
await this.close();
|
||||
}
|
||||
|
||||
async unarchive() {
|
||||
await this.open();
|
||||
await this.archiveToggle.off();
|
||||
await this.close();
|
||||
}
|
||||
|
||||
abstract isColored(color: string): Promise<boolean>;
|
||||
abstract color(color: string): Promise<void>;
|
||||
abstract open(): Promise<void>;
|
||||
|
||||
@@ -201,6 +201,37 @@ for (const actor of actors) {
|
||||
expect(await note?.getDescription()).toContain(NOTE.content);
|
||||
expect(await note?.contextMenu.isLocked()).toBe(false);
|
||||
});
|
||||
|
||||
test(`archive a note using ${actor}`, async ({ page }) => {
|
||||
const app = new AppModel(page);
|
||||
await app.goto();
|
||||
const notes = await app.goToNotes();
|
||||
const note = await notes.createNote(NOTE);
|
||||
|
||||
await note?.[actor].archive();
|
||||
|
||||
const archive = await app.goToArchive();
|
||||
const archivedNote = await archive.findNote(NOTE);
|
||||
expect(await archivedNote?.contextMenu.isArchived()).toBe(true);
|
||||
expect(await archivedNote?.properties.isArchived()).toBe(true);
|
||||
});
|
||||
|
||||
test(`unarchive a note using ${actor}`, async ({ page }) => {
|
||||
const app = new AppModel(page);
|
||||
await app.goto();
|
||||
let notes = await app.goToNotes();
|
||||
let note = await notes.createNote(NOTE);
|
||||
await note?.contextMenu.archive();
|
||||
|
||||
const archive = await app.goToArchive();
|
||||
const archivedNote = await archive.findNote(NOTE);
|
||||
await archivedNote?.[actor].unarchive();
|
||||
|
||||
notes = await app.goToNotes();
|
||||
note = await notes.findNote(NOTE);
|
||||
expect(await note?.contextMenu.isArchived()).toBe(false);
|
||||
expect(await note?.properties.isArchived()).toBe(false);
|
||||
});
|
||||
}
|
||||
|
||||
test("open a locked note", async ({ page }) => {
|
||||
@@ -339,3 +370,48 @@ test(`sort notes`, async ({ page }, info) => {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test("archived favorite note shouldn't be in favorites note list", async ({
|
||||
page
|
||||
}) => {
|
||||
const app = new AppModel(page);
|
||||
await app.goto();
|
||||
const notes = await app.goToNotes();
|
||||
const note = await notes.createNote(NOTE);
|
||||
|
||||
await note?.contextMenu.favorite();
|
||||
await note?.contextMenu.archive();
|
||||
|
||||
const favorites = await app.goToFavorites();
|
||||
expect(await favorites.findNote(NOTE)).toBeUndefined();
|
||||
});
|
||||
|
||||
test("archived tag note shouldn't be in tags note list", async ({ page }) => {
|
||||
const app = new AppModel(page);
|
||||
await app.goto();
|
||||
const tags = await app.goToTags();
|
||||
const tag = await tags.createItem({ title: "my-tag" });
|
||||
const notes = await tag?.open();
|
||||
|
||||
const note = await notes?.createNote(NOTE);
|
||||
expect(await notes?.findNote(NOTE)).toBeDefined();
|
||||
await note?.contextMenu.archive();
|
||||
|
||||
expect(await notes?.findNote(NOTE)).toBeUndefined();
|
||||
});
|
||||
|
||||
test("archived notebook note shouldn't be in notebooks note list", async ({
|
||||
page
|
||||
}) => {
|
||||
const app = new AppModel(page);
|
||||
await app.goto();
|
||||
const notebooks = await app.goToNotebooks();
|
||||
const notebook = await notebooks.createNotebook({ title: "my-notebook" });
|
||||
const notes = await notebook?.openNotebook();
|
||||
|
||||
const note = await notes?.createNote(NOTE);
|
||||
expect(await notes?.findNote(NOTE)).toBeDefined();
|
||||
await note?.contextMenu.archive();
|
||||
|
||||
expect(await notes?.findNote(NOTE)).toBeUndefined();
|
||||
});
|
||||
|
||||
@@ -40,6 +40,7 @@ import {
|
||||
mdiClose,
|
||||
mdiDotsVertical,
|
||||
mdiTrashCanOutline,
|
||||
mdiArchiveOutline,
|
||||
mdiBookRemoveOutline,
|
||||
mdiMagnify,
|
||||
mdiMenu,
|
||||
@@ -351,6 +352,7 @@ export const Cross = createIcon(mdiClose);
|
||||
export const MoreVertical = createIcon(mdiDotsVertical);
|
||||
export const MoreHorizontal = createIcon(mdiDotsHorizontal);
|
||||
export const Trash = createIcon(mdiTrashCanOutline);
|
||||
export const Archive = createIcon(mdiArchiveOutline);
|
||||
export const TopicRemove = createIcon(mdiBookmarkRemoveOutline);
|
||||
export const NotebookRemove = createIcon(mdiBookRemoveOutline);
|
||||
export const Search = createIcon(mdiMagnify);
|
||||
|
||||
@@ -36,7 +36,7 @@ export type Context =
|
||||
}
|
||||
| NotebookContext
|
||||
| {
|
||||
type: "favorite" | "monographs";
|
||||
type: "favorite" | "monographs" | "archive";
|
||||
};
|
||||
|
||||
export type WithDateEdited<T> = { items: T[]; dateEdited: number };
|
||||
|
||||
@@ -46,7 +46,8 @@ import {
|
||||
Reset,
|
||||
Rename,
|
||||
ExpandSidebar,
|
||||
HamburgerMenu
|
||||
HamburgerMenu,
|
||||
Archive
|
||||
} from "../icons";
|
||||
import { SortableNavigationItem } from "./navigation-item";
|
||||
import {
|
||||
@@ -111,7 +112,7 @@ import { showSortMenu } from "../group-header";
|
||||
import { Freeze } from "react-freeze";
|
||||
|
||||
type Route = {
|
||||
id: "notes" | "favorites" | "reminders" | "monographs" | "trash";
|
||||
id: "notes" | "favorites" | "reminders" | "monographs" | "trash" | "archive";
|
||||
title: string;
|
||||
path: string;
|
||||
icon: Icon;
|
||||
@@ -138,7 +139,13 @@ const routes: Route[] = [
|
||||
path: "/monographs",
|
||||
icon: Monographs
|
||||
},
|
||||
{ id: "trash", title: strings.routes.Trash(), path: "/trash", icon: Trash }
|
||||
{ id: "trash", title: strings.routes.Trash(), path: "/trash", icon: Trash },
|
||||
{
|
||||
id: "archive",
|
||||
title: strings.archive(),
|
||||
path: "/archive",
|
||||
icon: Archive
|
||||
}
|
||||
];
|
||||
|
||||
const tabs = [
|
||||
@@ -704,6 +711,8 @@ function ItemCount({ item }: { item: Route | Color | Notebook | Tag }) {
|
||||
return trash?.length || 0;
|
||||
case "monographs":
|
||||
return monographs?.length || 0;
|
||||
case "archive":
|
||||
return db.notes.archived.count();
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ import {
|
||||
AddReminder,
|
||||
AddToNotebook,
|
||||
Alert,
|
||||
Archive,
|
||||
Attachment,
|
||||
AttachmentError,
|
||||
Circle,
|
||||
@@ -395,6 +396,15 @@ export const noteMenuItems: (
|
||||
await AddReminderDialog.show({ note });
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "button",
|
||||
key: "archive",
|
||||
title: strings.archive(),
|
||||
isChecked: note.archived,
|
||||
icon: Archive.path,
|
||||
onClick: () => store.archive(!note.archived, ...ids),
|
||||
multiSelect: true
|
||||
},
|
||||
{ key: "sep1", type: "separator" },
|
||||
{
|
||||
type: "button",
|
||||
|
||||
@@ -31,7 +31,8 @@ import {
|
||||
ChevronRight,
|
||||
LinkedTo,
|
||||
ReferencedIn as ReferencedInIcon,
|
||||
Note as NoteIcon
|
||||
Note as NoteIcon,
|
||||
Archive
|
||||
} from "../icons";
|
||||
import { Box, Button, Flex, Text, FlexProps } from "@theme-ui/components";
|
||||
import {
|
||||
@@ -83,6 +84,12 @@ const tools = [
|
||||
label: strings.readOnly(),
|
||||
property: "readonly"
|
||||
},
|
||||
{
|
||||
key: "archive",
|
||||
icon: Archive,
|
||||
label: strings.archive(),
|
||||
property: "archived"
|
||||
},
|
||||
{
|
||||
key: "local-only",
|
||||
icon: SyncOff,
|
||||
@@ -837,10 +844,17 @@ export function Section({
|
||||
}
|
||||
|
||||
function changeToggleState(
|
||||
prop: "lock" | "readonly" | "local-only" | "pin" | "favorite",
|
||||
prop: "lock" | "readonly" | "local-only" | "pin" | "favorite" | "archive",
|
||||
session: ReadonlyEditorSession | DefaultEditorSession
|
||||
) {
|
||||
const { id: sessionId, readonly, localOnly, pinned, favorite } = session.note;
|
||||
const {
|
||||
id: sessionId,
|
||||
readonly,
|
||||
localOnly,
|
||||
pinned,
|
||||
favorite,
|
||||
archived
|
||||
} = session.note;
|
||||
if (!sessionId) return;
|
||||
switch (prop) {
|
||||
case "lock":
|
||||
@@ -855,6 +869,8 @@ function changeToggleState(
|
||||
return noteStore.pin(!pinned, sessionId);
|
||||
case "favorite":
|
||||
return noteStore.favorite(!favorite, sessionId);
|
||||
case "archive":
|
||||
return noteStore.archive(!archived, sessionId);
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ export type TipContext =
|
||||
| "reminders"
|
||||
| "monographs"
|
||||
| "trash"
|
||||
| "archive"
|
||||
| "attachments";
|
||||
|
||||
export type Tip = {
|
||||
@@ -207,5 +208,8 @@ const DEFAULT_TIPS: Record<TipContext, Omit<Tip, "contexts">> = {
|
||||
trash: {
|
||||
text: ""
|
||||
},
|
||||
archive: {
|
||||
text: strings.yourArchiveIsEmpty()
|
||||
},
|
||||
search: { text: "" }
|
||||
};
|
||||
|
||||
@@ -105,6 +105,15 @@ const routes = defineRoutes({
|
||||
component: Trash
|
||||
});
|
||||
},
|
||||
"/archive": () => {
|
||||
notestore.setContext({ type: "archive" });
|
||||
return defineRoute({
|
||||
key: "notes",
|
||||
title: strings.archive(),
|
||||
type: "notes",
|
||||
component: Notes
|
||||
});
|
||||
},
|
||||
"/tags/:tagId": async ({ tagId }) => {
|
||||
const tag = await db.tags.tag(tagId);
|
||||
if (!tag) return NOT_FOUND_ROUTE;
|
||||
|
||||
@@ -382,6 +382,8 @@ class EditorStore extends BaseStore<EditorStore> {
|
||||
event.item.localOnly ?? session.note.localOnly;
|
||||
session.note.favorite =
|
||||
event.item.favorite ?? session.note.favorite;
|
||||
session.note.archived =
|
||||
event.item.archived ?? session.note.archived;
|
||||
session.note.dateEdited =
|
||||
event.item.dateEdited ?? session.note.dateEdited;
|
||||
});
|
||||
|
||||
@@ -62,7 +62,11 @@ class NoteStore extends BaseStore<NoteStore> {
|
||||
contextNotes: context
|
||||
? await notesFromContext(context).grouped(
|
||||
db.settings.getGroupOptions(
|
||||
context.type === "favorite" ? "favorites" : "notes"
|
||||
context.type === "favorite"
|
||||
? "favorites"
|
||||
: context.type === "archive"
|
||||
? "archive"
|
||||
: "notes"
|
||||
)
|
||||
)
|
||||
: undefined
|
||||
@@ -85,6 +89,11 @@ class NoteStore extends BaseStore<NoteStore> {
|
||||
await this.refresh();
|
||||
};
|
||||
|
||||
archive = async (state: boolean, ...ids: string[]) => {
|
||||
await db.notes.archive(state, ...ids);
|
||||
await this.refresh();
|
||||
};
|
||||
|
||||
unlock = async (id: string) => {
|
||||
return await Vault.unlockNote(id).then(async (res) => {
|
||||
await this.refresh();
|
||||
@@ -141,6 +150,8 @@ export function notesFromContext(context: Context) {
|
||||
.selector;
|
||||
case "favorite":
|
||||
return db.notes.favorites;
|
||||
case "archive":
|
||||
return db.notes.archived;
|
||||
case "monographs":
|
||||
return db.monographs.all;
|
||||
}
|
||||
|
||||
@@ -35,7 +35,12 @@ function Notes(props: NotesProps) {
|
||||
const context = useNotesStore((store) => store.context);
|
||||
const contextNotes = useNotesStore((store) => store.contextNotes);
|
||||
const refreshContext = useNotesStore((store) => store.refreshContext);
|
||||
const type = context?.type === "favorite" ? "favorites" : "notes";
|
||||
const type =
|
||||
context?.type === "favorite"
|
||||
? "favorites"
|
||||
: context?.type === "archive"
|
||||
? "archive"
|
||||
: "notes";
|
||||
const isCompact = useNotesStore((store) => store.viewMode === "compact");
|
||||
const filteredItems = useSearch(
|
||||
context?.type === "notebook" ? "notebook" : "notes",
|
||||
@@ -62,6 +67,8 @@ function Notes(props: NotesProps) {
|
||||
context={
|
||||
context.type === "favorite"
|
||||
? "favorites"
|
||||
: context.type === "archive"
|
||||
? "archive"
|
||||
: context.type === "monographs"
|
||||
? "monographs"
|
||||
: "notes"
|
||||
|
||||
@@ -682,3 +682,54 @@ for (const group of groups) {
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
test("get archived notes", () =>
|
||||
noteTest().then(async ({ db, id }) => {
|
||||
await db.notes.archive(true, id);
|
||||
expect(await db.notes.archived.count()).toBeGreaterThan(0);
|
||||
}));
|
||||
|
||||
test("archive note", () =>
|
||||
noteTest().then(async ({ db, id }) => {
|
||||
await db.notes.archive(true, id);
|
||||
const note = await db.notes.note(id);
|
||||
expect(note?.archived).toBe(true);
|
||||
}));
|
||||
|
||||
test("unarchive note", () =>
|
||||
noteTest().then(async ({ db, id }) => {
|
||||
await db.notes.archive(true, id);
|
||||
await db.notes.archive(false, id);
|
||||
const note = await db.notes.note(id);
|
||||
expect(note?.archived).toBe(false);
|
||||
}));
|
||||
|
||||
test("archiving note should update cache.archived", () =>
|
||||
noteTest().then(async ({ db, id }) => {
|
||||
await db.notes.archive(true, id);
|
||||
const note = await db.notes.note(id);
|
||||
expect(db.notes.cache.archived).toEqual([note?.id]);
|
||||
}));
|
||||
|
||||
test("un-archiving note should update cache.archived", () =>
|
||||
noteTest().then(async ({ db, id }) => {
|
||||
await db.notes.archive(true, id);
|
||||
const note = await db.notes.note(id);
|
||||
expect(db.notes.cache.archived).toEqual([note?.id]);
|
||||
|
||||
await db.notes.archive(false, id);
|
||||
expect(db.notes.cache.archived).toEqual([]);
|
||||
}));
|
||||
|
||||
test("archived note shouldn't be in all notes", () =>
|
||||
noteTest().then(async ({ db, id }) => {
|
||||
await db.notes.archive(true, id);
|
||||
expect(await db.notes.all.count()).toBe(0);
|
||||
}));
|
||||
|
||||
test("archived note shouldn't be in favorites", () =>
|
||||
noteTest().then(async ({ db, id }) => {
|
||||
await db.notes.favorite(true, id);
|
||||
await db.notes.archive(true, id);
|
||||
expect(await db.notes.favorites.count()).toBe(0);
|
||||
}));
|
||||
|
||||
@@ -40,6 +40,7 @@ import { ICollection } from "./collection.js";
|
||||
import { SQLCollection } from "../database/sql-collection.js";
|
||||
import { isFalse } from "../database/index.js";
|
||||
import { logger } from "../logger.js";
|
||||
import { addItems, deleteItems } from "../utils/array.js";
|
||||
|
||||
export type ExportOptions = {
|
||||
format: "html" | "md" | "txt" | "md-frontmatter";
|
||||
@@ -51,6 +52,7 @@ export type ExportOptions = {
|
||||
|
||||
export class Notes implements ICollection {
|
||||
name = "notes";
|
||||
cache: { archived: string[] } = { archived: [] };
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
@@ -69,6 +71,13 @@ export class Notes implements ICollection {
|
||||
async init() {
|
||||
await this.collection.init();
|
||||
this.totalNotes = await this.collection.count();
|
||||
await this.buildCache();
|
||||
}
|
||||
|
||||
async buildCache() {
|
||||
this.cache.archived = [];
|
||||
const archived = await this.archived.ids();
|
||||
this.cache.archived = archived;
|
||||
}
|
||||
|
||||
async add(
|
||||
@@ -234,7 +243,11 @@ export class Notes implements ICollection {
|
||||
|
||||
get all() {
|
||||
return this.collection.createFilter<Note>(
|
||||
(qb) => qb.where(isFalse("dateDeleted")).where(isFalse("deleted")),
|
||||
(qb) =>
|
||||
qb
|
||||
.where(isFalse("dateDeleted"))
|
||||
.where(isFalse("deleted"))
|
||||
.where(isFalse("archived")),
|
||||
this.db.options?.batchSize
|
||||
);
|
||||
}
|
||||
@@ -277,11 +290,23 @@ export class Notes implements ICollection {
|
||||
qb
|
||||
.where(isFalse("dateDeleted"))
|
||||
.where(isFalse("deleted"))
|
||||
.where(isFalse("archived"))
|
||||
.where("favorite", "==", true),
|
||||
this.db.options?.batchSize
|
||||
);
|
||||
}
|
||||
|
||||
get archived() {
|
||||
return this.collection.createFilter<Note>(
|
||||
(qb) =>
|
||||
qb
|
||||
.where(isFalse("dateDeleted"))
|
||||
.where(isFalse("deleted"))
|
||||
.where("archived", "==", true),
|
||||
this.db.options?.batchSize
|
||||
);
|
||||
}
|
||||
|
||||
exists(id: string) {
|
||||
return this.collection.exists(id);
|
||||
}
|
||||
@@ -300,6 +325,14 @@ export class Notes implements ICollection {
|
||||
favorite(state: boolean, ...ids: string[]) {
|
||||
return this.collection.update(ids, { favorite: state });
|
||||
}
|
||||
async archive(state: boolean, ...ids: string[]) {
|
||||
await this.collection.update(ids, { archived: state });
|
||||
if (state) {
|
||||
addItems(this.cache.archived, ...ids);
|
||||
} else {
|
||||
deleteItems(this.cache.archived, ...ids);
|
||||
}
|
||||
}
|
||||
readonly(state: boolean, ...ids: string[]) {
|
||||
return this.collection.update(ids, { readonly: state });
|
||||
}
|
||||
|
||||
@@ -395,6 +395,11 @@ class RelationsArray<TType extends keyof RelatableTable> {
|
||||
this.db.trash.cache.notes.length > 0,
|
||||
(b) => b.where("fromId", "not in", this.db.trash.cache.notes)
|
||||
)
|
||||
.$if(
|
||||
!!this.types?.includes("note" as TType) &&
|
||||
this.db.notes.cache.archived.length > 0,
|
||||
(b) => b.where("fromId", "not in", this.db.notes.cache.archived)
|
||||
)
|
||||
.$if(
|
||||
!!this.types?.includes("notebook" as TType) &&
|
||||
this.db.trash.cache.notebooks.length > 0,
|
||||
@@ -424,6 +429,11 @@ class RelationsArray<TType extends keyof RelatableTable> {
|
||||
this.db.trash.cache.notes.length > 0,
|
||||
(b) => b.where("toId", "not in", this.db.trash.cache.notes)
|
||||
)
|
||||
.$if(
|
||||
!!this.types?.includes("note" as TType) &&
|
||||
this.db.notes.cache.archived.length > 0,
|
||||
(b) => b.where("toId", "not in", this.db.notes.cache.archived)
|
||||
)
|
||||
.$if(
|
||||
!!this.types?.includes("notebook" as TType) &&
|
||||
this.db.trash.cache.notebooks.length > 0,
|
||||
|
||||
@@ -62,6 +62,7 @@ const defaultSettings: SettingItemMap = {
|
||||
"groupOptions:notes": DEFAULT_GROUP_OPTIONS("notes"),
|
||||
"groupOptions:notebooks": DEFAULT_GROUP_OPTIONS("notebooks"),
|
||||
"groupOptions:favorites": DEFAULT_GROUP_OPTIONS("favorites"),
|
||||
"groupOptions:archive": DEFAULT_GROUP_OPTIONS("archive"),
|
||||
"groupOptions:home": DEFAULT_GROUP_OPTIONS("home"),
|
||||
"groupOptions:reminders": DEFAULT_GROUP_OPTIONS("reminders"),
|
||||
|
||||
|
||||
@@ -232,7 +232,8 @@ const BooleanProperties: Set<BooleanFields> = new Set([
|
||||
"readonly",
|
||||
"remote",
|
||||
"synced",
|
||||
"isGeneratedTitle"
|
||||
"isGeneratedTitle",
|
||||
"archived"
|
||||
]);
|
||||
|
||||
const DataMappers: Partial<Record<ItemType, (row: any) => void>> = {
|
||||
|
||||
@@ -391,6 +391,14 @@ export class NNMigrationProvider implements MigrationProvider {
|
||||
.addColumn("isGeneratedTitle", "boolean")
|
||||
.execute();
|
||||
}
|
||||
},
|
||||
"9": {
|
||||
async up(db) {
|
||||
await db.schema
|
||||
.alterTable("notes")
|
||||
.addColumn("archived", "boolean")
|
||||
.execute();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -49,7 +49,8 @@ export const GroupingKey = [
|
||||
"tags",
|
||||
"trash",
|
||||
"favorites",
|
||||
"reminders"
|
||||
"reminders",
|
||||
"archive"
|
||||
] as const;
|
||||
export type GroupingKey = (typeof GroupingKey)[number];
|
||||
|
||||
@@ -200,6 +201,7 @@ export interface Note extends BaseItem<"note"> {
|
||||
deletedBy: null;
|
||||
|
||||
isGeneratedTitle?: boolean;
|
||||
archived?: boolean;
|
||||
}
|
||||
|
||||
export interface Notebook extends BaseItem<"notebook"> {
|
||||
|
||||
@@ -31,6 +31,13 @@ export function addItem<T>(array: T[], item: T) {
|
||||
return true;
|
||||
}
|
||||
|
||||
export function addItems<T>(array: T[], ...items: T[]) {
|
||||
for (const item of items) {
|
||||
addItem(array, item);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
export function deleteItem<T>(array: T[], item: T) {
|
||||
return deleteAtIndex(array, array.indexOf(item));
|
||||
}
|
||||
|
||||
@@ -866,6 +866,10 @@ msgstr "Apply changes"
|
||||
msgid "Applying changes"
|
||||
msgstr "Applying changes"
|
||||
|
||||
#: src/strings.ts:2468
|
||||
msgid "Archive"
|
||||
msgstr "Archive"
|
||||
|
||||
#: src/strings.ts:1431
|
||||
msgid "Are you scrolling a lot to find a specific note? Pin it to the top from Note properties."
|
||||
msgstr "Are you scrolling a lot to find a specific note? Pin it to the top from Note properties."
|
||||
@@ -7007,6 +7011,10 @@ msgstr "Your account password must be strong & unique."
|
||||
msgid "Your account will be downgraded in {days} days"
|
||||
msgstr "Your account will be downgraded in {days} days"
|
||||
|
||||
#: src/strings.ts:2469
|
||||
msgid "Your archive is empty"
|
||||
msgstr "Your archive is empty"
|
||||
|
||||
#: src/strings.ts:1914
|
||||
msgid "Your backup is ready to download"
|
||||
msgstr "Your backup is ready to download"
|
||||
|
||||
@@ -866,6 +866,10 @@ msgstr ""
|
||||
msgid "Applying changes"
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:2468
|
||||
msgid "Archive"
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:1431
|
||||
msgid "Are you scrolling a lot to find a specific note? Pin it to the top from Note properties."
|
||||
msgstr ""
|
||||
@@ -6953,6 +6957,10 @@ msgstr ""
|
||||
msgid "Your account will be downgraded in {days} days"
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:2469
|
||||
msgid "Your archive is empty"
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:1914
|
||||
msgid "Your backup is ready to download"
|
||||
msgstr ""
|
||||
|
||||
@@ -2464,5 +2464,7 @@ Use this if changes from other devices are not appearing on this device. This wi
|
||||
setAsHomepage: () => t`Set as homepage`,
|
||||
defaultSidebarTab: () => t`Default sidebar tab`,
|
||||
defaultSidebarTabDesc: () => t`Select the default sidebar tab`,
|
||||
unsetAsHomepage: () => t`Reset homepage`
|
||||
unsetAsHomepage: () => t`Reset homepage`,
|
||||
archive: () => t`Archive`,
|
||||
yourArchiveIsEmpty: () => t`Your archive is empty`
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user