import React, { useEffect, useRef, useState, } from 'react'; import type { IRevisionHasPageId } from '@growi/core'; import { useTranslation } from 'next-i18next'; import { useSWRxInfinitePageRevisions } from '~/stores/page'; import { RevisionComparer } from '../RevisionComparer/RevisionComparer'; import { Revision } from './Revision'; import styles from './PageRevisionTable.module.scss'; type PageRevisionTableProps = { sourceRevisionId?: string targetRevisionId?: string onClose: () => void, currentPageId: string currentPagePath: string } export const PageRevisionTable = (props: PageRevisionTableProps): JSX.Element => { const { t } = useTranslation(); const REVISIONS_PER_PAGE = 10; const { sourceRevisionId, targetRevisionId, onClose, currentPageId, currentPagePath, } = props; // Load all data if source revision id and target revision id not null const revisionPerPage = (sourceRevisionId != null && targetRevisionId != null) ? 0 : REVISIONS_PER_PAGE; const swrInifiniteResponse = useSWRxInfinitePageRevisions(currentPageId, revisionPerPage); const { data, size, error, setSize, isValidating, } = swrInifiniteResponse; const revisions = data && data[0].revisions; const oldestRevision = revisions && revisions[revisions.length - 1]; // First load const isLoadingInitialData = !data && !error; const isLoadingMore = isLoadingInitialData || (isValidating && data != null && typeof data[size - 1] === 'undefined'); const isReachingEnd = (revisionPerPage === 0) || !!(data != null && data[data.length - 1]?.revisions.length < REVISIONS_PER_PAGE); const [sourceRevision, setSourceRevision] = useState(); const [targetRevision, setTargetRevision] = useState(); const tbodyRef = useRef(null); useEffect(() => { if (revisions != null) { if (sourceRevisionId != null && targetRevisionId != null) { const sourceRevision = revisions.filter(revision => revision._id === sourceRevisionId)[0]; const targetRevision = revisions.filter(revision => revision._id === targetRevisionId)[0]; setSourceRevision(sourceRevision); setTargetRevision(targetRevision); } else { const latestRevision = revisions != null ? revisions[0] : null; if (latestRevision != null) { setSourceRevision(latestRevision); setTargetRevision(latestRevision); } } } }, [revisions, sourceRevisionId, targetRevisionId]); useEffect(() => { // Apply ref to tbody const tbody = tbodyRef.current; const handleScroll = () => { const offset = 30; // Threshold before scroll actually reaching the end if (tbody) { // Scroll end const isEnd = tbody.scrollTop + tbody.clientHeight + offset >= tbody.scrollHeight; if (isEnd && !isLoadingMore && !isReachingEnd) { setSize(size + 1); } } }; if (tbody) { tbody.addEventListener('scroll', handleScroll); } return () => { if (tbody) { tbody.removeEventListener('scroll', handleScroll); } }; }, [isLoadingMore, isReachingEnd, setSize, size]); const renderRow = (revision: IRevisionHasPageId, previousRevision: IRevisionHasPageId, latestRevision: IRevisionHasPageId, isOldestRevision: boolean, hasDiff: boolean) => { const revisionId = revision._id; const handleCompareLatestRevisionButton = () => { setSourceRevision(revision); setTargetRevision(latestRevision); }; const handleComparePreviousRevisionButton = () => { setSourceRevision(previousRevision); setTargetRevision(revision); }; return (
{hasDiff && (
)}
{(hasDiff || revisionId === sourceRevision?._id) && (
setSourceRevision(revision)} />
)} {(hasDiff || revisionId === targetRevision?._id) && (
setTargetRevision(revision)} />
)} ); }; return ( <> {revisions != null && data != null && data.map(apiResult => apiResult.revisions).flat() .map((revision, idx) => { const previousRevision = (idx + 1 < revisions?.length) ? revisions[idx + 1] : revision; const isOldestRevision = revision === oldestRevision; const latestRevision = revisions[0]; // set 'true' if undefined for backward compatibility const hasDiff = revision.hasDiffToPrev !== false; return renderRow(revision, previousRevision, latestRevision, isOldestRevision, hasDiff); }) }
{t('page_history.revision')} {t('page_history.comparing_source')} {t('page_history.comparing_target')}
{sourceRevision != null && targetRevision != null && ( ) } ); };