From 36022df189fd53c8cfec69ace163695b97e1319f Mon Sep 17 00:00:00 2001 From: 01zulfi <85733202+01zulfi@users.noreply.github.com> Date: Mon, 28 Apr 2025 09:45:59 +0500 Subject: [PATCH] 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> --- apps/web/__e2e__/models/app.model.ts | 6 ++ .../__e2e__/models/note-properties.model.ts | 21 +++++ apps/web/__e2e__/notes.test.ts | 76 +++++++++++++++++++ apps/web/src/components/icons/index.tsx | 2 + .../src/components/list-container/types.ts | 2 +- .../src/components/navigation-menu/index.tsx | 15 +++- apps/web/src/components/note/index.tsx | 10 +++ apps/web/src/components/properties/index.tsx | 22 +++++- apps/web/src/hooks/use-tip.ts | 4 + apps/web/src/navigation/routes.tsx | 9 +++ apps/web/src/stores/editor-store.ts | 2 + apps/web/src/stores/note-store.ts | 13 +++- apps/web/src/views/notes.tsx | 9 ++- packages/core/__tests__/notes.test.ts | 51 +++++++++++++ packages/core/src/collections/notes.ts | 35 ++++++++- packages/core/src/collections/relations.ts | 10 +++ packages/core/src/collections/settings.ts | 1 + packages/core/src/database/index.ts | 3 +- packages/core/src/database/migrations.ts | 8 ++ packages/core/src/types.ts | 4 +- packages/core/src/utils/array.ts | 7 ++ packages/intl/locale/en.po | 8 ++ packages/intl/locale/pseudo-LOCALE.po | 8 ++ packages/intl/src/strings.ts | 4 +- 24 files changed, 317 insertions(+), 13 deletions(-) diff --git a/apps/web/__e2e__/models/app.model.ts b/apps/web/__e2e__/models/app.model.ts index ff2a0796e..884229813 100644 --- a/apps/web/__e2e__/models/app.model.ts +++ b/apps/web/__e2e__/models/app.model.ts @@ -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")), diff --git a/apps/web/__e2e__/models/note-properties.model.ts b/apps/web/__e2e__/models/note-properties.model.ts index d2d183555..21623126f 100644 --- a/apps/web/__e2e__/models/note-properties.model.ts +++ b/apps/web/__e2e__/models/note-properties.model.ts @@ -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; abstract color(color: string): Promise; abstract open(): Promise; diff --git a/apps/web/__e2e__/notes.test.ts b/apps/web/__e2e__/notes.test.ts index 8506375e6..600da61ed 100644 --- a/apps/web/__e2e__/notes.test.ts +++ b/apps/web/__e2e__/notes.test.ts @@ -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(); +}); diff --git a/apps/web/src/components/icons/index.tsx b/apps/web/src/components/icons/index.tsx index 7825eedeb..36487e67a 100644 --- a/apps/web/src/components/icons/index.tsx +++ b/apps/web/src/components/icons/index.tsx @@ -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); diff --git a/apps/web/src/components/list-container/types.ts b/apps/web/src/components/list-container/types.ts index 312ccef37..9e99d34f5 100644 --- a/apps/web/src/components/list-container/types.ts +++ b/apps/web/src/components/list-container/types.ts @@ -36,7 +36,7 @@ export type Context = } | NotebookContext | { - type: "favorite" | "monographs"; + type: "favorite" | "monographs" | "archive"; }; export type WithDateEdited = { items: T[]; dateEdited: number }; diff --git a/apps/web/src/components/navigation-menu/index.tsx b/apps/web/src/components/navigation-menu/index.tsx index 1056d733e..b596d0cb8 100644 --- a/apps/web/src/components/navigation-menu/index.tsx +++ b/apps/web/src/components/navigation-menu/index.tsx @@ -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; } diff --git a/apps/web/src/components/note/index.tsx b/apps/web/src/components/note/index.tsx index f443d6ba0..200641f09 100644 --- a/apps/web/src/components/note/index.tsx +++ b/apps/web/src/components/note/index.tsx @@ -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", diff --git a/apps/web/src/components/properties/index.tsx b/apps/web/src/components/properties/index.tsx index 32a09ccc7..902dddd03 100644 --- a/apps/web/src/components/properties/index.tsx +++ b/apps/web/src/components/properties/index.tsx @@ -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; } diff --git a/apps/web/src/hooks/use-tip.ts b/apps/web/src/hooks/use-tip.ts index 052f25499..c18235f62 100644 --- a/apps/web/src/hooks/use-tip.ts +++ b/apps/web/src/hooks/use-tip.ts @@ -48,6 +48,7 @@ export type TipContext = | "reminders" | "monographs" | "trash" + | "archive" | "attachments"; export type Tip = { @@ -207,5 +208,8 @@ const DEFAULT_TIPS: Record> = { trash: { text: "" }, + archive: { + text: strings.yourArchiveIsEmpty() + }, search: { text: "" } }; diff --git a/apps/web/src/navigation/routes.tsx b/apps/web/src/navigation/routes.tsx index 28a279de6..6d714e565 100644 --- a/apps/web/src/navigation/routes.tsx +++ b/apps/web/src/navigation/routes.tsx @@ -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; diff --git a/apps/web/src/stores/editor-store.ts b/apps/web/src/stores/editor-store.ts index 9ef75de49..a8cdfcb9d 100644 --- a/apps/web/src/stores/editor-store.ts +++ b/apps/web/src/stores/editor-store.ts @@ -382,6 +382,8 @@ class EditorStore extends BaseStore { 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; }); diff --git a/apps/web/src/stores/note-store.ts b/apps/web/src/stores/note-store.ts index 9a1ce6621..fb57118aa 100644 --- a/apps/web/src/stores/note-store.ts +++ b/apps/web/src/stores/note-store.ts @@ -62,7 +62,11 @@ class NoteStore extends BaseStore { 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 { 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; } diff --git a/apps/web/src/views/notes.tsx b/apps/web/src/views/notes.tsx index c6f9bcbea..351bbb414 100644 --- a/apps/web/src/views/notes.tsx +++ b/apps/web/src/views/notes.tsx @@ -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" diff --git a/packages/core/__tests__/notes.test.ts b/packages/core/__tests__/notes.test.ts index c2fe7a278..0c7af14f4 100644 --- a/packages/core/__tests__/notes.test.ts +++ b/packages/core/__tests__/notes.test.ts @@ -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); + })); diff --git a/packages/core/src/collections/notes.ts b/packages/core/src/collections/notes.ts index ff1833fca..f9df87dda 100644 --- a/packages/core/src/collections/notes.ts +++ b/packages/core/src/collections/notes.ts @@ -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( - (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( + (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 }); } diff --git a/packages/core/src/collections/relations.ts b/packages/core/src/collections/relations.ts index 69c00bbb3..cd9acf7c2 100644 --- a/packages/core/src/collections/relations.ts +++ b/packages/core/src/collections/relations.ts @@ -395,6 +395,11 @@ class RelationsArray { 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 { 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, diff --git a/packages/core/src/collections/settings.ts b/packages/core/src/collections/settings.ts index e6f7cb282..5a1cd137f 100644 --- a/packages/core/src/collections/settings.ts +++ b/packages/core/src/collections/settings.ts @@ -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"), diff --git a/packages/core/src/database/index.ts b/packages/core/src/database/index.ts index 2032cffd5..03e198ec1 100644 --- a/packages/core/src/database/index.ts +++ b/packages/core/src/database/index.ts @@ -232,7 +232,8 @@ const BooleanProperties: Set = new Set([ "readonly", "remote", "synced", - "isGeneratedTitle" + "isGeneratedTitle", + "archived" ]); const DataMappers: Partial void>> = { diff --git a/packages/core/src/database/migrations.ts b/packages/core/src/database/migrations.ts index 6fbdf1bd3..2e0c1ecc8 100644 --- a/packages/core/src/database/migrations.ts +++ b/packages/core/src/database/migrations.ts @@ -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(); + } } }; } diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index f96cd04c0..75a736cb3 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -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"> { diff --git a/packages/core/src/utils/array.ts b/packages/core/src/utils/array.ts index 7a9608193..d274322d0 100644 --- a/packages/core/src/utils/array.ts +++ b/packages/core/src/utils/array.ts @@ -31,6 +31,13 @@ export function addItem(array: T[], item: T) { return true; } +export function addItems(array: T[], ...items: T[]) { + for (const item of items) { + addItem(array, item); + } + return array; +} + export function deleteItem(array: T[], item: T) { return deleteAtIndex(array, array.indexOf(item)); } diff --git a/packages/intl/locale/en.po b/packages/intl/locale/en.po index 4e64d916b..02e9ce1a8 100644 --- a/packages/intl/locale/en.po +++ b/packages/intl/locale/en.po @@ -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" diff --git a/packages/intl/locale/pseudo-LOCALE.po b/packages/intl/locale/pseudo-LOCALE.po index 3f47f42a3..319adfd95 100644 --- a/packages/intl/locale/pseudo-LOCALE.po +++ b/packages/intl/locale/pseudo-LOCALE.po @@ -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 "" diff --git a/packages/intl/src/strings.ts b/packages/intl/src/strings.ts index 6c5d2a668..43289a807 100644 --- a/packages/intl/src/strings.ts +++ b/packages/intl/src/strings.ts @@ -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` };