mirror of
https://github.com/docmost/docmost.git
synced 2026-08-24 09:22:16 +10:00
feat(client): compare two page history versions in history view
This commit is contained in:
@@ -1293,5 +1293,7 @@
|
|||||||
"Compare": "Compare",
|
"Compare": "Compare",
|
||||||
"Compare versions": "Compare versions",
|
"Compare versions": "Compare versions",
|
||||||
"Select version from {{date}}": "Select version from {{date}}",
|
"Select version from {{date}}": "Select version from {{date}}",
|
||||||
"Version actions for {{date}}": "Version actions for {{date}}"
|
"Version actions for {{date}}": "Version actions for {{date}}",
|
||||||
|
"Comparing {{newer}} and {{older}}": "Comparing {{newer}} and {{older}}",
|
||||||
|
"Exit compare": "Exit compare"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,3 +99,8 @@
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
padding: rem(16px) rem(40px);
|
padding: rem(16px) rem(40px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.compareBanner {
|
||||||
|
border-bottom: rem(1px) solid
|
||||||
|
light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
ActionIcon,
|
ActionIcon,
|
||||||
|
CloseButton,
|
||||||
Group,
|
Group,
|
||||||
Paper,
|
Paper,
|
||||||
ScrollArea,
|
ScrollArea,
|
||||||
@@ -12,17 +13,20 @@ import { useAtom, useAtomValue } from "jotai";
|
|||||||
import {
|
import {
|
||||||
activeHistoryIdAtom,
|
activeHistoryIdAtom,
|
||||||
activeHistoryPrevIdAtom,
|
activeHistoryPrevIdAtom,
|
||||||
|
comparePairAtom,
|
||||||
diffCountsAtom,
|
diffCountsAtom,
|
||||||
highlightChangesAtom,
|
highlightChangesAtom,
|
||||||
} from "@/features/page-history/atoms/history-atoms";
|
} from "@/features/page-history/atoms/history-atoms";
|
||||||
import HistoryView from "@/features/page-history/components/history-view";
|
import HistoryView from "@/features/page-history/components/history-view";
|
||||||
import { useRef } from "react";
|
import { useMemo, useRef } from "react";
|
||||||
import { IconChevronUp, IconChevronDown } from "@tabler/icons-react";
|
import { IconChevronUp, IconChevronDown } from "@tabler/icons-react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import {
|
import {
|
||||||
useDiffNavigation,
|
useDiffNavigation,
|
||||||
useHistoryReset,
|
useHistoryReset,
|
||||||
} from "@/features/page-history/hooks";
|
} from "@/features/page-history/hooks";
|
||||||
|
import { usePageHistoryListQuery } from "@/features/page-history/queries/page-history-query";
|
||||||
|
import { formattedDate } from "@/lib/time";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
pageId: string;
|
pageId: string;
|
||||||
@@ -36,6 +40,28 @@ export default function HistoryModalBody({ pageId }: Props) {
|
|||||||
const activeHistoryPrevId = useAtomValue(activeHistoryPrevIdAtom);
|
const activeHistoryPrevId = useAtomValue(activeHistoryPrevIdAtom);
|
||||||
const [highlightChanges, setHighlightChanges] = useAtom(highlightChangesAtom);
|
const [highlightChanges, setHighlightChanges] = useAtom(highlightChangesAtom);
|
||||||
const diffCounts = useAtomValue(diffCountsAtom);
|
const diffCounts = useAtomValue(diffCountsAtom);
|
||||||
|
const [comparePair, setComparePair] = useAtom(comparePairAtom);
|
||||||
|
|
||||||
|
const { data: pageHistoryData } = usePageHistoryListQuery(pageId);
|
||||||
|
const historyItems = useMemo(
|
||||||
|
() => pageHistoryData?.pages.flatMap((page) => page.items) ?? [],
|
||||||
|
[pageHistoryData],
|
||||||
|
);
|
||||||
|
|
||||||
|
const compareLabel = useMemo(() => {
|
||||||
|
if (!comparePair) return null;
|
||||||
|
const newerItem = historyItems.find(
|
||||||
|
(item) => item.id === comparePair.newerId,
|
||||||
|
);
|
||||||
|
const olderItem = historyItems.find(
|
||||||
|
(item) => item.id === comparePair.olderId,
|
||||||
|
);
|
||||||
|
if (!newerItem || !olderItem) return null;
|
||||||
|
return t("Comparing {{newer}} and {{older}}", {
|
||||||
|
newer: formattedDate(new Date(newerItem.createdAt)),
|
||||||
|
older: formattedDate(new Date(olderItem.createdAt)),
|
||||||
|
});
|
||||||
|
}, [comparePair, historyItems, t]);
|
||||||
|
|
||||||
useHistoryReset(pageId);
|
useHistoryReset(pageId);
|
||||||
const { currentChangeIndex, handlePrevChange, handleNextChange } =
|
const { currentChangeIndex, handlePrevChange, handleNextChange } =
|
||||||
@@ -50,6 +76,25 @@ export default function HistoryModalBody({ pageId }: Props) {
|
|||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div style={{ position: "relative", flex: 1 }}>
|
<div style={{ position: "relative", flex: 1 }}>
|
||||||
|
{comparePair && compareLabel && (
|
||||||
|
<Group
|
||||||
|
justify="space-between"
|
||||||
|
wrap="nowrap"
|
||||||
|
px="md"
|
||||||
|
py={4}
|
||||||
|
className={classes.compareBanner}
|
||||||
|
>
|
||||||
|
<Text size="sm" fw={500} lineClamp={1}>
|
||||||
|
{compareLabel}
|
||||||
|
</Text>
|
||||||
|
<CloseButton
|
||||||
|
size="sm"
|
||||||
|
aria-label={t("Exit compare")}
|
||||||
|
onClick={() => setComparePair(null)}
|
||||||
|
/>
|
||||||
|
</Group>
|
||||||
|
)}
|
||||||
|
|
||||||
<ScrollArea
|
<ScrollArea
|
||||||
h={650}
|
h={650}
|
||||||
w="100%"
|
w="100%"
|
||||||
@@ -57,11 +102,18 @@ export default function HistoryModalBody({ pageId }: Props) {
|
|||||||
viewportRef={scrollViewportRef}
|
viewportRef={scrollViewportRef}
|
||||||
>
|
>
|
||||||
<div className={classes.sidebarRightSection}>
|
<div className={classes.sidebarRightSection}>
|
||||||
{activeHistoryId && <HistoryView />}
|
{comparePair ? (
|
||||||
|
<HistoryView
|
||||||
|
historyId={comparePair.newerId}
|
||||||
|
prevHistoryId={comparePair.olderId}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
activeHistoryId && <HistoryView />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
|
|
||||||
{activeHistoryId && activeHistoryPrevId && (
|
{(comparePair || (activeHistoryId && activeHistoryPrevId)) && (
|
||||||
<Paper
|
<Paper
|
||||||
shadow="md"
|
shadow="md"
|
||||||
radius="xl"
|
radius="xl"
|
||||||
|
|||||||
@@ -7,21 +7,29 @@ import {
|
|||||||
activeHistoryPrevIdAtom,
|
activeHistoryPrevIdAtom,
|
||||||
} from "@/features/page-history/atoms/history-atoms";
|
} from "@/features/page-history/atoms/history-atoms";
|
||||||
|
|
||||||
function HistoryView() {
|
interface Props {
|
||||||
|
historyId?: string;
|
||||||
|
prevHistoryId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function HistoryView({ historyId, prevHistoryId }: Props) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const historyId = useAtomValue(activeHistoryIdAtom);
|
const activeId = useAtomValue(activeHistoryIdAtom);
|
||||||
const prevHistoryId = useAtomValue(activeHistoryPrevIdAtom);
|
const activePrevId = useAtomValue(activeHistoryPrevIdAtom);
|
||||||
|
|
||||||
|
const resolvedId = historyId ?? activeId;
|
||||||
|
const resolvedPrevId = prevHistoryId ?? activePrevId;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
isLoading: isLoadingCurrent,
|
isLoading: isLoadingCurrent,
|
||||||
isError: isErrorCurrent,
|
isError: isErrorCurrent,
|
||||||
} = usePageHistoryQuery(historyId);
|
} = usePageHistoryQuery(resolvedId);
|
||||||
const {
|
const {
|
||||||
data: prevData,
|
data: prevData,
|
||||||
isLoading: isLoadingPrev,
|
isLoading: isLoadingPrev,
|
||||||
isError: isErrorPrev,
|
isError: isErrorPrev,
|
||||||
} = usePageHistoryQuery(prevHistoryId);
|
} = usePageHistoryQuery(resolvedPrevId);
|
||||||
|
|
||||||
if (isLoadingCurrent || isLoadingPrev) {
|
if (isLoadingCurrent || isLoadingPrev) {
|
||||||
return <></>;
|
return <></>;
|
||||||
|
|||||||
Reference in New Issue
Block a user