import type React from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react'; import type { IUser } from '@growi/core'; import { GlobalCodeMirrorEditorKey } from '@growi/editor'; import { CodeMirrorEditorDiff } from '@growi/editor/dist/client/components/diff/CodeMirrorEditorDiff'; import { MergeViewer } from '@growi/editor/dist/client/components/diff/MergeViewer'; import { useCodeMirrorEditorIsolated } from '@growi/editor/dist/client/stores/codemirror-editor'; import { UserPicture } from '@growi/ui/dist/components'; import { format } from 'date-fns/format'; import { useTranslation } from 'next-i18next'; import { Modal, ModalBody, ModalFooter, ModalHeader } from 'reactstrap'; import { useCurrentUser } from '~/states/global'; import { useCurrentPageData, useRemoteRevisionBody, useRemoteRevisionLastUpdatedAt, useRemoteRevisionLastUpdateUser, } from '~/states/page'; import { useConflictDiffModalActions, useConflictDiffModalStatus, } from '~/states/ui/modal/conflict-diff'; import styles from './ConflictDiffModal.module.scss'; type IRevisionOnConflict = { revisionBody: string; createdAt: Date; user: IUser; }; /** * ConflictDiffModalSubstance - Presentation component (heavy logic, rendered only when isOpen) */ type ConflictDiffModalSubstanceProps = { request: IRevisionOnConflict; latest: IRevisionOnConflict; isModalExpanded: boolean; setIsModalExpanded: React.Dispatch>; }; const formatedDate = (date: Date): string => { return format(date, 'yyyy/MM/dd HH:mm:ss'); }; const ConflictDiffModalSubstance = ( props: ConflictDiffModalSubstanceProps, ): React.JSX.Element => { const { request, latest, isModalExpanded, setIsModalExpanded } = props; const [resolvedRevision, setResolvedRevision] = useState(''); const [isRevisionselected, setIsRevisionSelected] = useState(false); const [revisionSelectedToggler, setRevisionSelectedToggler] = useState(false); const { t } = useTranslation(); const conflictDiffModalStatus = useConflictDiffModalStatus(); const { close: closeConflictDiffModal } = useConflictDiffModalActions(); const { data: codeMirrorEditor } = useCodeMirrorEditorIsolated( GlobalCodeMirrorEditorKey.DIFF, ); // Memoize formatted dates const requestFormattedDate = useMemo( () => formatedDate(request.createdAt), [request.createdAt], ); const latestFormattedDate = useMemo( () => formatedDate(latest.createdAt), [latest.createdAt], ); const selectRevisionHandler = useCallback( (selectedRevision: string) => { setResolvedRevision(selectedRevision); setRevisionSelectedToggler((prev) => !prev); if (!isRevisionselected) { setIsRevisionSelected(true); } }, [isRevisionselected], ); const resolveConflictHandler = useCallback(async () => { const newBody = codeMirrorEditor?.getDocString(); if (newBody == null) { return; } await conflictDiffModalStatus?.onResolve?.(newBody); }, [codeMirrorEditor, conflictDiffModalStatus]); // biome-ignore lint/correctness/useExhaustiveDependencies: ignore useEffect(() => { codeMirrorEditor?.initDoc(resolvedRevision); // Enable selecting the same revision after editing by including revisionSelectedToggler in the dependency array of useEffect }, [codeMirrorEditor, resolvedRevision, revisionSelectedToggler]); const headerButtons = useMemo( () => (
), [closeConflictDiffModal, isModalExpanded, setIsModalExpanded], ); return ( <> error {t('modal_resolve_conflict.resolve_conflict')}

{t('modal_resolve_conflict.resolve_conflict_message')}

{t('modal_resolve_conflict.requested_revision')}

updated by {request.user.username}

{requestFormattedDate}

{t('modal_resolve_conflict.latest_revision')}

updated by {latest.user.username}

{latestFormattedDate}

{t('modal_resolve_conflict.selected_editable_revision')}

); }; /** * ConflictDiffModal - Container component (lightweight, always rendered) */ export const ConflictDiffModal = (): React.JSX.Element => { const currentUser = useCurrentUser(); const currentPage = useCurrentPageData(); const conflictDiffModalStatus = useConflictDiffModalStatus(); // state for latest page const remoteRevisionBody = useRemoteRevisionBody(); const remoteRevisionLastUpdateUser = useRemoteRevisionLastUpdateUser(); const remoteRevisionLastUpdatedAt = useRemoteRevisionLastUpdatedAt(); const isRemotePageDataInappropriate = remoteRevisionBody == null || remoteRevisionLastUpdateUser == null; const [isModalExpanded, setIsModalExpanded] = useState(false); // Check if all required data is available const isDataReady = conflictDiffModalStatus?.isOpened && currentUser != null && currentPage != null && !isRemotePageDataInappropriate; // Prepare data for Substance const currentTime: Date = new Date(); const request: IRevisionOnConflict | null = isDataReady ? { revisionBody: conflictDiffModalStatus.requestRevisionBody ?? '', createdAt: currentTime, user: currentUser, } : null; const latest: IRevisionOnConflict | null = isDataReady ? { revisionBody: remoteRevisionBody, createdAt: new Date( remoteRevisionLastUpdatedAt ?? currentTime.toString(), ), user: remoteRevisionLastUpdateUser, } : null; return ( {isDataReady && request != null && latest != null && ( )} ); };