import { NoteType, SNNote, classNames } from '@standardnotes/snjs' import Modal, { ModalAction } from '../../Modal/Modal' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { MutuallyExclusiveMediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery' import { useApplication } from '../../ApplicationProvider' import { confirmDialog } from '@standardnotes/ui-services' import { useListKeyboardNavigation } from '@/Hooks/useListKeyboardNavigation' import ModalDialogButtons from '../../Modal/ModalDialogButtons' import { Checkbox, Select, SelectArrow, SelectItem, SelectList, Toolbar, ToolbarItem, useSelectStore, useToolbarStore, } from '@ariakit/react' import Popover from '../../Popover/Popover' import Icon from '../../Icon/Icon' import Button from '../../Button/Button' import Spinner from '../../Spinner/Spinner' import Switch from '../../Switch/Switch' import StyledTooltip from '../../StyledTooltip/StyledTooltip' import { DiffView } from './DiffView' import { ReadonlyNoteContent } from '../ReadonlyNoteContent' import { ConflictListItem } from './ConflictListItem' type ConflictAction = 'move-to-trash' | 'delete-permanently' type MultipleSelectionMode = 'preview' | 'diff' const NoteConflictResolutionModal = ({ currentNote, conflictedNotes, close, }: { currentNote: SNNote conflictedNotes: SNNote[] close: () => void }) => { const allVersions = useMemo(() => [currentNote].concat(conflictedNotes), [conflictedNotes, currentNote]) const application = useApplication() const [selectedVersions, setSelectedVersions] = useState([currentNote.uuid]) const selectedNotes = useMemo(() => { return allVersions.filter((note) => selectedVersions.includes(note.uuid)) }, [allVersions, selectedVersions]) const trashNote = useCallback( async (note: SNNote) => { await application.mutator .changeItem(note, (mutator) => { mutator.trashed = true mutator.conflictOf = undefined }) .catch(console.error) setSelectedVersions([allVersions[0].uuid]) }, [allVersions, application.mutator], ) const deleteNotePermanently = useCallback( async (note: SNNote) => { await application.mutator .deleteItem(note) .catch(console.error) .then(() => { setSelectedVersions([allVersions[0].uuid]) }) }, [allVersions, application.mutator], ) const [selectedAction, setSelectionAction] = useState('move-to-trash') const selectStore = useSelectStore({ value: selectedAction, setValue: (value) => setSelectionAction(value as ConflictAction), }) const [isPerformingAction, setIsPerformingAction] = useState(false) const keepOnlySelected = useCallback(async () => { const shouldDeletePermanently = selectedAction === 'delete-permanently' const confirmDialogText = `This will keep only the selected versions and ${ shouldDeletePermanently ? 'delete the other versions permanently.' : 'move the other versions to the trash.' } Are you sure?` if ( await confirmDialog({ title: 'Keep only selected versions?', text: confirmDialogText, confirmButtonStyle: 'danger', }) ) { const nonSelectedNotes = allVersions.filter((note) => !selectedVersions.includes(note.uuid)) selectStore.hide() setIsPerformingAction(true) await Promise.all( nonSelectedNotes.map((note) => (shouldDeletePermanently ? deleteNotePermanently(note) : trashNote(note))), ) await application.mutator.changeItems(selectedNotes, (mutator) => { mutator.conflictOf = undefined }) setIsPerformingAction(false) void application.getViewControllerManager().selectionController.selectItem(selectedNotes[0].uuid, true) void application.sync.sync() close() } }, [ allVersions, application, close, deleteNotePermanently, selectStore, selectedAction, selectedNotes, selectedVersions, trashNote, ]) const isMobileScreen = useMediaQuery(MutuallyExclusiveMediaQueryBreakpoints.sm) const actions = useMemo( (): ModalAction[] => [ { label: 'Cancel', onClick: close, type: 'cancel', mobileSlot: 'left', }, ], [close], ) const listRef = useRef(null) useListKeyboardNavigation(listRef) const [selectedMobileTab, setSelectedMobileTab] = useState<'list' | 'preview'>('list') const toolbarStore = useToolbarStore() const isSelectOpen = selectStore.useState('open') const [selectAnchor, setSelectAnchor] = useState(null) const [multipleSelectionMode, setMultipleSelectionMode] = useState( isMobileScreen ? 'diff' : 'preview', ) const isPreviewMode = multipleSelectionMode === 'preview' useEffect(() => { if (selectedNotes.length !== 2) { setMultipleSelectionMode('preview') } if (isMobileScreen && selectedNotes.length === 2) { setMultipleSelectionMode('diff') } }, [isMobileScreen, selectedNotes.length]) const showSuperConversionInfo = selectedNotes.some((note) => note.noteType === NoteType.Super) && !isPreviewMode const [compareSuperMarkdown, setCompareSuperMarkdown] = useState(true) const [comparisonScrollPos, setComparisonScrollPos] = useState(0) const [shouldSyncComparisonScroll, setShouldSyncComparisonScroll] = useState(true) return ( 1 ? 'hidden md:flex' : ''}> {isPerformingAction ? ( <> ) : ( <>Keep selected, {selectedAction === 'move-to-trash' ? 'trash others' : 'delete others'} )}