chore: show toast when duplicating note and update last modified date for duplicated note (#2379)
This commit is contained in:
@@ -11,7 +11,7 @@ import {
|
|||||||
import { ContentType } from '@standardnotes/domain-core'
|
import { ContentType } from '@standardnotes/domain-core'
|
||||||
import { AlertService, InternalEventBusInterface } from '@standardnotes/services'
|
import { AlertService, InternalEventBusInterface } from '@standardnotes/services'
|
||||||
import { MutatorService, PayloadManager, ItemManager } from '../'
|
import { MutatorService, PayloadManager, ItemManager } from '../'
|
||||||
import { UuidGenerator } from '@standardnotes/utils'
|
import { UuidGenerator, sleep } from '@standardnotes/utils'
|
||||||
|
|
||||||
const setupRandomUuid = () => {
|
const setupRandomUuid = () => {
|
||||||
UuidGenerator.SetGenerator(() => String(Math.random()))
|
UuidGenerator.SetGenerator(() => String(Math.random()))
|
||||||
@@ -65,6 +65,14 @@ describe('mutator service', () => {
|
|||||||
|
|
||||||
expect(note.userModifiedDate).toEqual(pinnedNote?.userModifiedDate)
|
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', () => {
|
describe('linking', () => {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { PayloadManager } from '../Payloads/PayloadManager'
|
|||||||
import { TagsToFoldersMigrationApplicator } from '@Lib/Migrations/Applicators/TagsToFolders'
|
import { TagsToFoldersMigrationApplicator } from '@Lib/Migrations/Applicators/TagsToFolders'
|
||||||
import {
|
import {
|
||||||
ActionsExtensionMutator,
|
ActionsExtensionMutator,
|
||||||
|
AppDataField,
|
||||||
ComponentInterface,
|
ComponentInterface,
|
||||||
ComponentMutator,
|
ComponentMutator,
|
||||||
CreateDecryptedMutatorForItem,
|
CreateDecryptedMutatorForItem,
|
||||||
@@ -19,6 +20,7 @@ import {
|
|||||||
DecryptedItemMutator,
|
DecryptedItemMutator,
|
||||||
DecryptedPayload,
|
DecryptedPayload,
|
||||||
DecryptedPayloadInterface,
|
DecryptedPayloadInterface,
|
||||||
|
DefaultAppDomain,
|
||||||
DeleteItemMutator,
|
DeleteItemMutator,
|
||||||
EncryptedItemInterface,
|
EncryptedItemInterface,
|
||||||
FeatureRepoMutator,
|
FeatureRepoMutator,
|
||||||
@@ -319,7 +321,14 @@ export class MutatorService extends AbstractService implements MutatorClientInte
|
|||||||
payload,
|
payload,
|
||||||
baseCollection: this.payloadManager.getMasterCollection(),
|
baseCollection: this.payloadManager.getMasterCollection(),
|
||||||
isConflict,
|
isConflict,
|
||||||
additionalContent,
|
additionalContent: {
|
||||||
|
appData: {
|
||||||
|
[DefaultAppDomain]: {
|
||||||
|
[AppDataField.UserModifiedDate]: new Date(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...additionalContent,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
await this.payloadManager.emitPayloads(resultingPayloads, PayloadEmitSource.LocalChanged)
|
await this.payloadManager.emitPayloads(resultingPayloads, PayloadEmitSource.LocalChanged)
|
||||||
|
|||||||
@@ -261,6 +261,7 @@ const ApplicationView: FunctionComponent<Props> = ({ application, mainApplicatio
|
|||||||
notesController={viewControllerManager.notesController}
|
notesController={viewControllerManager.notesController}
|
||||||
linkingController={viewControllerManager.linkingController}
|
linkingController={viewControllerManager.linkingController}
|
||||||
historyModalController={viewControllerManager.historyModalController}
|
historyModalController={viewControllerManager.historyModalController}
|
||||||
|
selectionController={viewControllerManager.selectionController}
|
||||||
/>
|
/>
|
||||||
<TagContextMenuWrapper
|
<TagContextMenuWrapper
|
||||||
navigationController={viewControllerManager.navigationController}
|
navigationController={viewControllerManager.navigationController}
|
||||||
|
|||||||
@@ -381,6 +381,7 @@ const ContentListView = forwardRef<HTMLDivElement, Props>(
|
|||||||
notesController={notesController}
|
notesController={notesController}
|
||||||
historyModalController={historyModalController}
|
historyModalController={historyModalController}
|
||||||
itemListController={itemListController}
|
itemListController={itemListController}
|
||||||
|
selectionController={selectionController}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<ContentList
|
<ContentList
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import { useItemLinks } from '@/Hooks/useItemLinks'
|
|||||||
import { ItemLink } from '@/Utils/Items/Search/ItemLink'
|
import { ItemLink } from '@/Utils/Items/Search/ItemLink'
|
||||||
import { ItemListController } from '@/Controllers/ItemList/ItemListController'
|
import { ItemListController } from '@/Controllers/ItemList/ItemListController'
|
||||||
import ListItemVaultInfo from '../ContentListView/ListItemVaultInfo'
|
import ListItemVaultInfo from '../ContentListView/ListItemVaultInfo'
|
||||||
|
import { SelectedItemsController } from '@/Controllers/SelectedItemsController'
|
||||||
|
|
||||||
const ContextMenuCell = ({
|
const ContextMenuCell = ({
|
||||||
items,
|
items,
|
||||||
@@ -49,6 +50,7 @@ const ContextMenuCell = ({
|
|||||||
linkingController,
|
linkingController,
|
||||||
notesController,
|
notesController,
|
||||||
historyModalController,
|
historyModalController,
|
||||||
|
selectionController,
|
||||||
}: {
|
}: {
|
||||||
items: DecryptedItemInterface[]
|
items: DecryptedItemInterface[]
|
||||||
filesController: FilesController
|
filesController: FilesController
|
||||||
@@ -56,6 +58,7 @@ const ContextMenuCell = ({
|
|||||||
linkingController: LinkingController
|
linkingController: LinkingController
|
||||||
notesController: NotesController
|
notesController: NotesController
|
||||||
historyModalController: HistoryModalController
|
historyModalController: HistoryModalController
|
||||||
|
selectionController: SelectedItemsController
|
||||||
}) => {
|
}) => {
|
||||||
const [contextMenuVisible, setContextMenuVisible] = useState(false)
|
const [contextMenuVisible, setContextMenuVisible] = useState(false)
|
||||||
const anchorElementRef = useRef<HTMLButtonElement>(null)
|
const anchorElementRef = useRef<HTMLButtonElement>(null)
|
||||||
@@ -117,6 +120,7 @@ const ContextMenuCell = ({
|
|||||||
notesController={notesController}
|
notesController={notesController}
|
||||||
linkingController={linkingController}
|
linkingController={linkingController}
|
||||||
historyModalController={historyModalController}
|
historyModalController={historyModalController}
|
||||||
|
selectionController={selectionController}
|
||||||
closeMenu={() => {
|
closeMenu={() => {
|
||||||
setContextMenuVisible(false)
|
setContextMenuVisible(false)
|
||||||
}}
|
}}
|
||||||
@@ -266,6 +270,7 @@ type Props = {
|
|||||||
notesController: NotesController
|
notesController: NotesController
|
||||||
historyModalController: HistoryModalController
|
historyModalController: HistoryModalController
|
||||||
itemListController: ItemListController
|
itemListController: ItemListController
|
||||||
|
selectionController: SelectedItemsController
|
||||||
}
|
}
|
||||||
|
|
||||||
const ContentTableView = ({
|
const ContentTableView = ({
|
||||||
@@ -278,6 +283,7 @@ const ContentTableView = ({
|
|||||||
notesController,
|
notesController,
|
||||||
historyModalController,
|
historyModalController,
|
||||||
itemListController,
|
itemListController,
|
||||||
|
selectionController,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const listHasFiles = items.some((item) => item instanceof FileItem)
|
const listHasFiles = items.some((item) => item instanceof FileItem)
|
||||||
|
|
||||||
@@ -406,6 +412,7 @@ const ContentTableView = ({
|
|||||||
navigationController={navigationController}
|
navigationController={navigationController}
|
||||||
notesController={notesController}
|
notesController={notesController}
|
||||||
historyModalController={historyModalController}
|
historyModalController={historyModalController}
|
||||||
|
selectionController={selectionController}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@@ -418,6 +425,7 @@ const ContentTableView = ({
|
|||||||
navigationController={navigationController}
|
navigationController={navigationController}
|
||||||
notesController={notesController}
|
notesController={notesController}
|
||||||
historyModalController={historyModalController}
|
historyModalController={historyModalController}
|
||||||
|
selectionController={selectionController}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
showSelectionActions: true,
|
showSelectionActions: true,
|
||||||
@@ -465,6 +473,7 @@ const ContentTableView = ({
|
|||||||
notesController={notesController}
|
notesController={notesController}
|
||||||
linkingController={linkingController}
|
linkingController={linkingController}
|
||||||
historyModalController={historyModalController}
|
historyModalController={historyModalController}
|
||||||
|
selectionController={selectionController}
|
||||||
closeMenu={closeContextMenu}
|
closeMenu={closeContextMenu}
|
||||||
/>
|
/>
|
||||||
</Menu>
|
</Menu>
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ const MultipleSelectedNotes = ({
|
|||||||
notesController={notesController}
|
notesController={notesController}
|
||||||
linkingController={linkingController}
|
linkingController={linkingController}
|
||||||
historyModalController={historyModalController}
|
historyModalController={historyModalController}
|
||||||
|
selectionController={selectionController}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -932,6 +932,7 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
|||||||
notesController={this.viewControllerManager.notesController}
|
notesController={this.viewControllerManager.notesController}
|
||||||
linkingController={this.viewControllerManager.linkingController}
|
linkingController={this.viewControllerManager.linkingController}
|
||||||
historyModalController={this.viewControllerManager.historyModalController}
|
historyModalController={this.viewControllerManager.historyModalController}
|
||||||
|
selectionController={this.viewControllerManager.selectionController}
|
||||||
onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction}
|
onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,12 +7,14 @@ import { HistoryModalController } from '@/Controllers/NoteHistory/HistoryModalCo
|
|||||||
import Popover from '../Popover/Popover'
|
import Popover from '../Popover/Popover'
|
||||||
import { LinkingController } from '@/Controllers/LinkingController'
|
import { LinkingController } from '@/Controllers/LinkingController'
|
||||||
import Menu from '../Menu/Menu'
|
import Menu from '../Menu/Menu'
|
||||||
|
import { SelectedItemsController } from '@/Controllers/SelectedItemsController'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
navigationController: NavigationController
|
navigationController: NavigationController
|
||||||
notesController: NotesController
|
notesController: NotesController
|
||||||
linkingController: LinkingController
|
linkingController: LinkingController
|
||||||
historyModalController: HistoryModalController
|
historyModalController: HistoryModalController
|
||||||
|
selectionController: SelectedItemsController
|
||||||
}
|
}
|
||||||
|
|
||||||
const NotesContextMenu = ({
|
const NotesContextMenu = ({
|
||||||
@@ -20,6 +22,7 @@ const NotesContextMenu = ({
|
|||||||
notesController,
|
notesController,
|
||||||
linkingController,
|
linkingController,
|
||||||
historyModalController,
|
historyModalController,
|
||||||
|
selectionController,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { contextMenuOpen, contextMenuClickLocation, setContextMenuOpen } = notesController
|
const { contextMenuOpen, contextMenuClickLocation, setContextMenuOpen } = notesController
|
||||||
|
|
||||||
@@ -50,6 +53,7 @@ const NotesContextMenu = ({
|
|||||||
notesController={notesController}
|
notesController={notesController}
|
||||||
linkingController={linkingController}
|
linkingController={linkingController}
|
||||||
historyModalController={historyModalController}
|
historyModalController={historyModalController}
|
||||||
|
selectionController={selectionController}
|
||||||
requestDisableClickOutside={handleDisableClickOutsideRequest}
|
requestDisableClickOutside={handleDisableClickOutsideRequest}
|
||||||
closeMenu={closeMenu}
|
closeMenu={closeMenu}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ const NotesOptions = ({
|
|||||||
navigationController,
|
navigationController,
|
||||||
notesController,
|
notesController,
|
||||||
linkingController,
|
linkingController,
|
||||||
|
selectionController,
|
||||||
historyModalController,
|
historyModalController,
|
||||||
closeMenu,
|
closeMenu,
|
||||||
}: NotesOptionsProps) => {
|
}: NotesOptionsProps) => {
|
||||||
@@ -150,10 +151,32 @@ const NotesOptions = ({
|
|||||||
}, [closeMenu, toggleAppPane])
|
}, [closeMenu, toggleAppPane])
|
||||||
|
|
||||||
const duplicateSelectedItems = useCallback(async () => {
|
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()
|
void application.sync.sync()
|
||||||
closeMenuAndToggleNotesList()
|
closeMenuAndToggleNotesList()
|
||||||
}, [application.mutator, application.sync, closeMenuAndToggleNotesList, notes])
|
}, [application.mutator, application.sync, closeMenuAndToggleNotesList, notes, selectionController])
|
||||||
|
|
||||||
const openRevisionHistoryModal = useCallback(() => {
|
const openRevisionHistoryModal = useCallback(() => {
|
||||||
historyModalController.openModal(notesController.firstSelectedNote)
|
historyModalController.openModal(notesController.firstSelectedNote)
|
||||||
|
|||||||
@@ -8,12 +8,14 @@ import Popover from '../Popover/Popover'
|
|||||||
import { LinkingController } from '@/Controllers/LinkingController'
|
import { LinkingController } from '@/Controllers/LinkingController'
|
||||||
import RoundIconButton from '../Button/RoundIconButton'
|
import RoundIconButton from '../Button/RoundIconButton'
|
||||||
import Menu from '../Menu/Menu'
|
import Menu from '../Menu/Menu'
|
||||||
|
import { SelectedItemsController } from '@/Controllers/SelectedItemsController'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
navigationController: NavigationController
|
navigationController: NavigationController
|
||||||
notesController: NotesController
|
notesController: NotesController
|
||||||
linkingController: LinkingController
|
linkingController: LinkingController
|
||||||
historyModalController: HistoryModalController
|
historyModalController: HistoryModalController
|
||||||
|
selectionController: SelectedItemsController
|
||||||
onClickPreprocessing?: () => Promise<void>
|
onClickPreprocessing?: () => Promise<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,6 +24,7 @@ const NotesOptionsPanel = ({
|
|||||||
notesController,
|
notesController,
|
||||||
linkingController,
|
linkingController,
|
||||||
historyModalController,
|
historyModalController,
|
||||||
|
selectionController,
|
||||||
onClickPreprocessing,
|
onClickPreprocessing,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const [isOpen, setIsOpen] = useState(false)
|
const [isOpen, setIsOpen] = useState(false)
|
||||||
@@ -58,6 +61,7 @@ const NotesOptionsPanel = ({
|
|||||||
notesController={notesController}
|
notesController={notesController}
|
||||||
linkingController={linkingController}
|
linkingController={linkingController}
|
||||||
historyModalController={historyModalController}
|
historyModalController={historyModalController}
|
||||||
|
selectionController={selectionController}
|
||||||
requestDisableClickOutside={handleDisableClickOutsideRequest}
|
requestDisableClickOutside={handleDisableClickOutsideRequest}
|
||||||
closeMenu={toggleMenu}
|
closeMenu={toggleMenu}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { NavigationController } from '@/Controllers/Navigation/NavigationControl
|
|||||||
import { NotesController } from '@/Controllers/NotesController/NotesController'
|
import { NotesController } from '@/Controllers/NotesController/NotesController'
|
||||||
import { LinkingController } from '@/Controllers/LinkingController'
|
import { LinkingController } from '@/Controllers/LinkingController'
|
||||||
import { SNNote } from '@standardnotes/snjs'
|
import { SNNote } from '@standardnotes/snjs'
|
||||||
|
import { SelectedItemsController } from '@/Controllers/SelectedItemsController'
|
||||||
|
|
||||||
export type NotesOptionsProps = {
|
export type NotesOptionsProps = {
|
||||||
notes: SNNote[]
|
notes: SNNote[]
|
||||||
@@ -10,6 +11,7 @@ export type NotesOptionsProps = {
|
|||||||
notesController: NotesController
|
notesController: NotesController
|
||||||
linkingController: LinkingController
|
linkingController: LinkingController
|
||||||
historyModalController: HistoryModalController
|
historyModalController: HistoryModalController
|
||||||
|
selectionController: SelectedItemsController
|
||||||
requestDisableClickOutside?: (disabled: boolean) => void
|
requestDisableClickOutside?: (disabled: boolean) => void
|
||||||
closeMenu: () => void
|
closeMenu: () => void
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user