From b58898687ef994997df92625b985432c3cffc269 Mon Sep 17 00:00:00 2001 From: Aman Harwara Date: Tue, 1 Aug 2023 18:23:20 +0530 Subject: [PATCH] chore: show toast when duplicating note and update last modified date for duplicated note (#2379) --- .../Services/Mutator/MutatorService.spec.ts | 10 ++++++- .../lib/Services/Mutator/MutatorService.ts | 11 +++++++- .../ApplicationView/ApplicationView.tsx | 1 + .../ContentListView/ContentListView.tsx | 1 + .../ContentTableView/ContentTableView.tsx | 9 +++++++ .../MultipleSelectedNotes.tsx | 1 + .../Components/NoteView/NoteView.tsx | 1 + .../NotesContextMenu/NotesContextMenu.tsx | 4 +++ .../Components/NotesOptions/NotesOptions.tsx | 27 +++++++++++++++++-- .../NotesOptions/NotesOptionsPanel.tsx | 4 +++ .../NotesOptions/NotesOptionsProps.ts | 2 ++ 11 files changed, 67 insertions(+), 4 deletions(-) diff --git a/packages/snjs/lib/Services/Mutator/MutatorService.spec.ts b/packages/snjs/lib/Services/Mutator/MutatorService.spec.ts index 5304922c9..a93a51728 100644 --- a/packages/snjs/lib/Services/Mutator/MutatorService.spec.ts +++ b/packages/snjs/lib/Services/Mutator/MutatorService.spec.ts @@ -11,7 +11,7 @@ import { import { ContentType } from '@standardnotes/domain-core' import { AlertService, InternalEventBusInterface } from '@standardnotes/services' import { MutatorService, PayloadManager, ItemManager } from '../' -import { UuidGenerator } from '@standardnotes/utils' +import { UuidGenerator, sleep } from '@standardnotes/utils' const setupRandomUuid = () => { UuidGenerator.SetGenerator(() => String(Math.random())) @@ -65,6 +65,14 @@ describe('mutator service', () => { expect(note.userModifiedDate).toEqual(pinnedNote?.userModifiedDate) }) + + it('should update the modification date of duplicated notes', async () => { + const note = await insertNote('hello') + await sleep(1, false, 'Delaying duplication by 1ms to create unique timestamps') + const duplicatedNote = await mutatorService.duplicateItem(note) + + expect(duplicatedNote.userModifiedDate.getTime()).toBeGreaterThan(note.userModifiedDate.getTime()) + }) }) describe('linking', () => { diff --git a/packages/snjs/lib/Services/Mutator/MutatorService.ts b/packages/snjs/lib/Services/Mutator/MutatorService.ts index 4e37fc288..af79eb9dc 100644 --- a/packages/snjs/lib/Services/Mutator/MutatorService.ts +++ b/packages/snjs/lib/Services/Mutator/MutatorService.ts @@ -12,6 +12,7 @@ import { PayloadManager } from '../Payloads/PayloadManager' import { TagsToFoldersMigrationApplicator } from '@Lib/Migrations/Applicators/TagsToFolders' import { ActionsExtensionMutator, + AppDataField, ComponentInterface, ComponentMutator, CreateDecryptedMutatorForItem, @@ -19,6 +20,7 @@ import { DecryptedItemMutator, DecryptedPayload, DecryptedPayloadInterface, + DefaultAppDomain, DeleteItemMutator, EncryptedItemInterface, FeatureRepoMutator, @@ -319,7 +321,14 @@ export class MutatorService extends AbstractService implements MutatorClientInte payload, baseCollection: this.payloadManager.getMasterCollection(), isConflict, - additionalContent, + additionalContent: { + appData: { + [DefaultAppDomain]: { + [AppDataField.UserModifiedDate]: new Date(), + }, + }, + ...additionalContent, + }, }) await this.payloadManager.emitPayloads(resultingPayloads, PayloadEmitSource.LocalChanged) diff --git a/packages/web/src/javascripts/Components/ApplicationView/ApplicationView.tsx b/packages/web/src/javascripts/Components/ApplicationView/ApplicationView.tsx index 3a55ce4af..6abae053c 100644 --- a/packages/web/src/javascripts/Components/ApplicationView/ApplicationView.tsx +++ b/packages/web/src/javascripts/Components/ApplicationView/ApplicationView.tsx @@ -261,6 +261,7 @@ const ApplicationView: FunctionComponent = ({ application, mainApplicatio notesController={viewControllerManager.notesController} linkingController={viewControllerManager.linkingController} historyModalController={viewControllerManager.historyModalController} + selectionController={viewControllerManager.selectionController} /> ( notesController={notesController} historyModalController={historyModalController} itemListController={itemListController} + selectionController={selectionController} /> ) : ( { const [contextMenuVisible, setContextMenuVisible] = useState(false) const anchorElementRef = useRef(null) @@ -117,6 +120,7 @@ const ContextMenuCell = ({ notesController={notesController} linkingController={linkingController} historyModalController={historyModalController} + selectionController={selectionController} closeMenu={() => { setContextMenuVisible(false) }} @@ -266,6 +270,7 @@ type Props = { notesController: NotesController historyModalController: HistoryModalController itemListController: ItemListController + selectionController: SelectedItemsController } const ContentTableView = ({ @@ -278,6 +283,7 @@ const ContentTableView = ({ notesController, historyModalController, itemListController, + selectionController, }: Props) => { const listHasFiles = items.some((item) => item instanceof FileItem) @@ -406,6 +412,7 @@ const ContentTableView = ({ navigationController={navigationController} notesController={notesController} historyModalController={historyModalController} + selectionController={selectionController} /> ) @@ -418,6 +425,7 @@ const ContentTableView = ({ navigationController={navigationController} notesController={notesController} historyModalController={historyModalController} + selectionController={selectionController} /> ), showSelectionActions: true, @@ -465,6 +473,7 @@ const ContentTableView = ({ notesController={notesController} linkingController={linkingController} historyModalController={historyModalController} + selectionController={selectionController} closeMenu={closeContextMenu} /> diff --git a/packages/web/src/javascripts/Components/MultipleSelectedNotes/MultipleSelectedNotes.tsx b/packages/web/src/javascripts/Components/MultipleSelectedNotes/MultipleSelectedNotes.tsx index ed904fb5d..1d54910a2 100644 --- a/packages/web/src/javascripts/Components/MultipleSelectedNotes/MultipleSelectedNotes.tsx +++ b/packages/web/src/javascripts/Components/MultipleSelectedNotes/MultipleSelectedNotes.tsx @@ -51,6 +51,7 @@ const MultipleSelectedNotes = ({ notesController={notesController} linkingController={linkingController} historyModalController={historyModalController} + selectionController={selectionController} /> diff --git a/packages/web/src/javascripts/Components/NoteView/NoteView.tsx b/packages/web/src/javascripts/Components/NoteView/NoteView.tsx index e204d1222..0553252f9 100644 --- a/packages/web/src/javascripts/Components/NoteView/NoteView.tsx +++ b/packages/web/src/javascripts/Components/NoteView/NoteView.tsx @@ -932,6 +932,7 @@ class NoteView extends AbstractComponent { notesController={this.viewControllerManager.notesController} linkingController={this.viewControllerManager.linkingController} historyModalController={this.viewControllerManager.historyModalController} + selectionController={this.viewControllerManager.selectionController} onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction} /> diff --git a/packages/web/src/javascripts/Components/NotesContextMenu/NotesContextMenu.tsx b/packages/web/src/javascripts/Components/NotesContextMenu/NotesContextMenu.tsx index 895f9d40c..b96618bbe 100644 --- a/packages/web/src/javascripts/Components/NotesContextMenu/NotesContextMenu.tsx +++ b/packages/web/src/javascripts/Components/NotesContextMenu/NotesContextMenu.tsx @@ -7,12 +7,14 @@ import { HistoryModalController } from '@/Controllers/NoteHistory/HistoryModalCo import Popover from '../Popover/Popover' import { LinkingController } from '@/Controllers/LinkingController' import Menu from '../Menu/Menu' +import { SelectedItemsController } from '@/Controllers/SelectedItemsController' type Props = { navigationController: NavigationController notesController: NotesController linkingController: LinkingController historyModalController: HistoryModalController + selectionController: SelectedItemsController } const NotesContextMenu = ({ @@ -20,6 +22,7 @@ const NotesContextMenu = ({ notesController, linkingController, historyModalController, + selectionController, }: Props) => { const { contextMenuOpen, contextMenuClickLocation, setContextMenuOpen } = notesController @@ -50,6 +53,7 @@ const NotesContextMenu = ({ notesController={notesController} linkingController={linkingController} historyModalController={historyModalController} + selectionController={selectionController} requestDisableClickOutside={handleDisableClickOutsideRequest} closeMenu={closeMenu} /> diff --git a/packages/web/src/javascripts/Components/NotesOptions/NotesOptions.tsx b/packages/web/src/javascripts/Components/NotesOptions/NotesOptions.tsx index a05d76524..420580266 100644 --- a/packages/web/src/javascripts/Components/NotesOptions/NotesOptions.tsx +++ b/packages/web/src/javascripts/Components/NotesOptions/NotesOptions.tsx @@ -55,6 +55,7 @@ const NotesOptions = ({ navigationController, notesController, linkingController, + selectionController, historyModalController, closeMenu, }: NotesOptionsProps) => { @@ -150,10 +151,32 @@ const NotesOptions = ({ }, [closeMenu, toggleAppPane]) const duplicateSelectedItems = useCallback(async () => { - await Promise.all(notes.map((note) => application.mutator.duplicateItem(note).catch(console.error))) + await Promise.all( + notes.map((note) => + application.mutator + .duplicateItem(note) + .then((duplicated) => + addToast({ + type: ToastType.Regular, + message: `Duplicated note "${duplicated.title}"`, + actions: [ + { + label: 'Open', + handler: (toastId) => { + selectionController.selectItem(duplicated.uuid, true).catch(console.error) + dismissToast(toastId) + }, + }, + ], + autoClose: true, + }), + ) + .catch(console.error), + ), + ) void application.sync.sync() closeMenuAndToggleNotesList() - }, [application.mutator, application.sync, closeMenuAndToggleNotesList, notes]) + }, [application.mutator, application.sync, closeMenuAndToggleNotesList, notes, selectionController]) const openRevisionHistoryModal = useCallback(() => { historyModalController.openModal(notesController.firstSelectedNote) diff --git a/packages/web/src/javascripts/Components/NotesOptions/NotesOptionsPanel.tsx b/packages/web/src/javascripts/Components/NotesOptions/NotesOptionsPanel.tsx index 9d33198bc..a397ddff6 100644 --- a/packages/web/src/javascripts/Components/NotesOptions/NotesOptionsPanel.tsx +++ b/packages/web/src/javascripts/Components/NotesOptions/NotesOptionsPanel.tsx @@ -8,12 +8,14 @@ import Popover from '../Popover/Popover' import { LinkingController } from '@/Controllers/LinkingController' import RoundIconButton from '../Button/RoundIconButton' import Menu from '../Menu/Menu' +import { SelectedItemsController } from '@/Controllers/SelectedItemsController' type Props = { navigationController: NavigationController notesController: NotesController linkingController: LinkingController historyModalController: HistoryModalController + selectionController: SelectedItemsController onClickPreprocessing?: () => Promise } @@ -22,6 +24,7 @@ const NotesOptionsPanel = ({ notesController, linkingController, historyModalController, + selectionController, onClickPreprocessing, }: Props) => { const [isOpen, setIsOpen] = useState(false) @@ -58,6 +61,7 @@ const NotesOptionsPanel = ({ notesController={notesController} linkingController={linkingController} historyModalController={historyModalController} + selectionController={selectionController} requestDisableClickOutside={handleDisableClickOutsideRequest} closeMenu={toggleMenu} /> diff --git a/packages/web/src/javascripts/Components/NotesOptions/NotesOptionsProps.ts b/packages/web/src/javascripts/Components/NotesOptions/NotesOptionsProps.ts index b6481d43c..97ea133cb 100644 --- a/packages/web/src/javascripts/Components/NotesOptions/NotesOptionsProps.ts +++ b/packages/web/src/javascripts/Components/NotesOptions/NotesOptionsProps.ts @@ -3,6 +3,7 @@ import { NavigationController } from '@/Controllers/Navigation/NavigationControl import { NotesController } from '@/Controllers/NotesController/NotesController' import { LinkingController } from '@/Controllers/LinkingController' import { SNNote } from '@standardnotes/snjs' +import { SelectedItemsController } from '@/Controllers/SelectedItemsController' export type NotesOptionsProps = { notes: SNNote[] @@ -10,6 +11,7 @@ export type NotesOptionsProps = { notesController: NotesController linkingController: LinkingController historyModalController: HistoryModalController + selectionController: SelectedItemsController requestDisableClickOutside?: (disabled: boolean) => void closeMenu: () => void }