From 98557be52de4179f406bf3f7fb23dd66e7ffde55 Mon Sep 17 00:00:00 2001 From: 01zulfi <85733202+01zulfi@users.noreply.github.com> Date: Wed, 14 Jan 2026 14:10:44 +0500 Subject: [PATCH] web: replace note creation date edit popover with dialog Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com> --- .../__e2e__/models/note-properties.model.ts | 12 +- apps/web/src/components/properties/index.tsx | 40 ++- .../properties/note-date-created-editor.tsx | 237 ------------------ .../edit-note-creation-date-dialog.tsx | 177 +++++++++++++ 4 files changed, 210 insertions(+), 256 deletions(-) delete mode 100644 apps/web/src/components/properties/note-date-created-editor.tsx create mode 100644 apps/web/src/dialogs/edit-note-creation-date-dialog.tsx diff --git a/apps/web/__e2e__/models/note-properties.model.ts b/apps/web/__e2e__/models/note-properties.model.ts index a3d1718bd..accc00592 100644 --- a/apps/web/__e2e__/models/note-properties.model.ts +++ b/apps/web/__e2e__/models/note-properties.model.ts @@ -239,15 +239,15 @@ export class NotePropertiesModel extends BaseProperties { const editIcon = this.page.locator(getTestId("edit-date-created")); await editIcon.click(); - const editDateCreatedPopup = this.page.locator( - getTestId("edit-date-created-popup") + const editDateCreatedDialog = this.page.locator( + getTestId("edit-note-creation-date-dialog") ); - await editDateCreatedPopup.waitFor({ state: "visible" }); + await editDateCreatedDialog.waitFor({ state: "visible" }); - const dateInput = editDateCreatedPopup.locator( + const dateInput = editDateCreatedDialog.locator( getTestId("date-created-input") ); - const timeInput = editDateCreatedPopup.locator( + const timeInput = editDateCreatedDialog.locator( getTestId("time-created-input") ); @@ -255,7 +255,7 @@ export class NotePropertiesModel extends BaseProperties { await dateInput.fill(dayjs(date).format("DD-MM-YYYY")); await timeInput.fill(dayjs(date).format("hh:mm A")); - await editDateCreatedPopup.locator(getTestId("save-date-created")).click(); + await confirmDialog(editDateCreatedDialog); await this.close(); } diff --git a/apps/web/src/components/properties/index.tsx b/apps/web/src/components/properties/index.tsx index 01ae5f5c5..faae3b00b 100644 --- a/apps/web/src/components/properties/index.tsx +++ b/apps/web/src/components/properties/index.tsx @@ -24,7 +24,6 @@ import { Unlock, Readonly, SyncOff, - ArrowLeft, Circle, Checkmark, ChevronDown, @@ -32,9 +31,10 @@ import { LinkedTo, ReferencedIn as ReferencedInIcon, Note as NoteIcon, - Archive + Archive, + Edit } from "../icons"; -import { Box, Button, Flex, Text, FlexProps } from "@theme-ui/components"; +import { Button, Flex, Text, FlexProps } from "@theme-ui/components"; import { useEditorStore, ReadonlyEditorSession, @@ -45,13 +45,12 @@ import { useStore as useAppStore } from "../../stores/app-store"; import { useStore as useAttachmentStore } from "../../stores/attachment-store"; import { store as noteStore } from "../../stores/note-store"; import Toggle from "./toggle"; -import { NoteDateCreatedEditor } from "./note-date-created-editor"; +import { EditNoteCreationDateDialog } from "../../dialogs/edit-note-creation-date-dialog"; import ScrollContainer from "../scroll-container"; import { getFormattedDate, usePromise, ResolvedItem, - useResolvedItem, useUnresolvedItem } from "@notesnook/common"; import { ScopedThemeProvider } from "../theme-provider"; @@ -60,7 +59,6 @@ import { VirtualizedList } from "../virtualized-list"; import { SessionItem } from "../session-item"; import { ContentBlock, - InternalLink, Note, VirtualizedGrouping, createInternalLink, @@ -68,7 +66,6 @@ import { } from "@notesnook/core"; import { VirtualizedTable } from "../virtualized-table"; import { TextSlice } from "@notesnook/core"; -import { TITLE_BAR_HEIGHT } from "../title-bar"; import { strings } from "@notesnook/intl"; const tools = [ @@ -213,13 +210,30 @@ function EditorProperties(props: EditorPropertiesProps) { > {item.label} + {item.key === "dateCreated" ? ( - + + + {item.value(session.note[item.key])} + + { + EditNoteCreationDateDialog.show({ + noteId: session.note.id, + dateCreated: session.note.dateCreated, + dateEdited: session.note.dateEdited + }); + }} + data-test-id="edit-date-created" + /> + ) : ( . -*/ - -import { useState, useRef } from "react"; -import { Button, Flex, Text } from "@theme-ui/components"; -import { Calendar, Edit } from "../icons"; -import Field from "../field"; -import { DayPicker } from "../day-picker"; -import { PopupPresenter } from "@notesnook/ui"; -import { db } from "../../common/db"; -import { showToast } from "../../utils/toast"; -import { useStore as useThemeStore } from "../../stores/theme-store"; -import { store as noteStore } from "../../stores/note-store"; -import { getFormattedDate } from "@notesnook/common"; -import { getTimeFormat } from "@notesnook/core"; -import { setTimeOnly, setDateOnly } from "../../utils/date-time"; -import dayjs from "dayjs"; -import customParseFormat from "dayjs/plugin/customParseFormat"; -import { strings } from "@notesnook/intl"; - -dayjs.extend(customParseFormat); - -type NoteDateCreatedEditorProps = { - noteId: string; - dateCreated: number; - dateEdited: number; - displayValue: string; -}; - -export function NoteDateCreatedEditor({ - noteId, - dateCreated, - dateEdited, - displayValue -}: NoteDateCreatedEditorProps) { - const [isEditing, setIsEditing] = useState(false); - const [showCalendar, setShowCalendar] = useState(false); - const [editedDate, setEditedDate] = useState(dayjs()); - const dateTextRef = useRef(null); - const dateInputRef = useRef(null); - const theme = useThemeStore((store) => store.colorScheme); - - return ( - <> - - - {displayValue} - - { - setEditedDate(dayjs(dateCreated)); - setIsEditing(true); - }} - data-test-id="edit-date-created" - /> - - {isEditing && ( - { - setIsEditing(false); - setShowCalendar(false); - }} - position={{ - isTargetAbsolute: true, - target: dateTextRef.current, - location: "below" - }} - > - - {strings.editCreationDate()} - - - dayjs(t, db.settings.getDateFormat(), true).isValid() - } - defaultValue={editedDate.format(db.settings.getDateFormat())} - onChange={(e) => - setEditedDate((d) => setDateOnly(e.target.value, d)) - } - /> - setShowCalendar(false)} - position={{ - isTargetAbsolute: true, - target: dateInputRef.current, - location: "below" - }} - > - { - if (!day) return; - const date = getFormattedDate(day, "date"); - setEditedDate((d) => setDateOnly(date, d)); - if (dateInputRef.current) dateInputRef.current.value = date; - setShowCalendar(false); - }} - /> - - { - const format = - db.settings.getTimeFormat() === "12-hour" - ? "hh:mm a" - : "HH:mm"; - return dayjs(t.toLowerCase(), format, true).isValid(); - }} - defaultValue={editedDate.format( - getTimeFormat(db.settings.getTimeFormat()) - )} - onChange={(e) => - setEditedDate((d) => setTimeOnly(e.target.value, d)) - } - /> - - - - - - - - )} - - ); -} diff --git a/apps/web/src/dialogs/edit-note-creation-date-dialog.tsx b/apps/web/src/dialogs/edit-note-creation-date-dialog.tsx new file mode 100644 index 000000000..d73e76dd0 --- /dev/null +++ b/apps/web/src/dialogs/edit-note-creation-date-dialog.tsx @@ -0,0 +1,177 @@ +/* +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 . +*/ + +import { getFormattedDate } from "@notesnook/common"; +import { getTimeFormat } from "@notesnook/core"; +import { strings } from "@notesnook/intl"; +import { PopupPresenter } from "@notesnook/ui"; +import { Flex } from "@theme-ui/components"; +import dayjs from "dayjs"; +import customParseFormat from "dayjs/plugin/customParseFormat"; +import { useRef, useState } from "react"; +import { db } from "../common/db"; +import { BaseDialogProps, DialogManager } from "../common/dialog-manager"; +import { DayPicker } from "../components/day-picker"; +import Dialog from "../components/dialog"; +import Field from "../components/field"; +import { Calendar } from "../components/icons"; +import { store as noteStore } from "../stores/note-store"; +import { useStore as useThemeStore } from "../stores/theme-store"; +import { setDateOnly, setTimeOnly } from "../utils/date-time"; +import { showToast } from "../utils/toast"; + +dayjs.extend(customParseFormat); + +type EditNoteCreationDateDialogProps = BaseDialogProps & { + noteId: string; + dateCreated: number; + dateEdited: number; +}; + +export const EditNoteCreationDateDialog = DialogManager.register( + function EditNoteCreationDateDialog({ + noteId, + dateCreated, + dateEdited, + onClose + }: EditNoteCreationDateDialogProps) { + const [showCalendar, setShowCalendar] = useState(false); + const [date, setDate] = useState(dayjs(dateCreated)); + const dateInputRef = useRef(null); + const theme = useThemeStore((store) => store.colorScheme); + + return ( + { + setShowCalendar(false); + onClose(false); + }} + title={strings.editCreationDate()} + negativeButton={{ + text: strings.cancel(), + onClick: () => { + setShowCalendar(false); + onClose(false); + } + }} + positiveButton={{ + text: strings.save(), + onClick: async () => { + try { + if (date.isAfter(dayjs())) { + showToast("error", "Creation date cannot be in the future"); + return; + } + if (dateEdited && date.isAfter(dayjs(dateEdited))) { + showToast( + "error", + "Creation date cannot be after last edited date" + ); + return; + } + + await noteStore.get().updateDateCreated(noteId, date.valueOf()); + setShowCalendar(false); + onClose(false); + } catch (error) { + showToast( + "error", + "Failed to update creation date: " + (error as Error).message + ); + } + } + }} + > + + + dayjs(t, db.settings.getDateFormat(), true).isValid() + } + defaultValue={date.format(db.settings.getDateFormat())} + onChange={(e) => setDate((d) => setDateOnly(e.target.value, d))} + /> + setShowCalendar(false)} + position={{ + isTargetAbsolute: true, + target: dateInputRef.current, + location: "below" + }} + > + { + if (!day) return; + const date = getFormattedDate(day, "date"); + setDate((d) => setDateOnly(date, d)); + if (dateInputRef.current) dateInputRef.current.value = date; + setShowCalendar(false); + }} + /> + + { + const format = + db.settings.getTimeFormat() === "12-hour" ? "hh:mm a" : "HH:mm"; + return dayjs(t.toLowerCase(), format, true).isValid(); + }} + defaultValue={date.format( + getTimeFormat(db.settings.getTimeFormat()) + )} + onChange={(e) => setDate((d) => setTimeOnly(e.target.value, d))} + /> + + + ); + } +);