diff --git a/packages/next-common/components/appendants/common/appendantFormPopup.jsx b/packages/next-common/components/appendants/common/appendantFormPopup.jsx
new file mode 100644
index 0000000000..248664b59b
--- /dev/null
+++ b/packages/next-common/components/appendants/common/appendantFormPopup.jsx
@@ -0,0 +1,88 @@
+import { useEffect, useState } from "react";
+import { useDispatch } from "react-redux";
+import AppendantEditor from "./editor";
+import AppendantPopupLayout from "./popupLayout";
+import { CreateButton } from "./submitButton";
+import {
+ newErrorToast,
+ newSuccessToast,
+} from "next-common/store/reducers/toastSlice";
+
+export default function AppendantFormPopup({
+ title,
+ description = "",
+ onClose,
+ onSuccess,
+ submit,
+ successMessage,
+ initialContent = "",
+ initialContentType = "markdown",
+ SubmitButton = CreateButton,
+}) {
+ const [content, setContent] = useState(initialContent || "");
+ const [contentType, setContentType] = useState(
+ initialContentType || "markdown",
+ );
+ const [errors, setErrors] = useState();
+ const [submitting, setSubmitting] = useState(false);
+ const dispatch = useDispatch();
+
+ useEffect(() => {
+ setContent(initialContent || "");
+ setContentType(initialContentType || "markdown");
+ }, [initialContent, initialContentType]);
+
+ const handleSubmit = async () => {
+ setSubmitting(true);
+ setErrors(null);
+
+ try {
+ const { result, error } = await submit(content, contentType);
+ if (error) {
+ if (error.data) {
+ setErrors(error);
+ } else {
+ dispatch(newErrorToast(error.message));
+ }
+ return;
+ }
+
+ if (result) {
+ dispatch(newSuccessToast(successMessage));
+ onSuccess?.();
+ onClose();
+ }
+ } catch (e) {
+ if (e.message !== "Cancelled") {
+ dispatch(newErrorToast(e.message));
+ }
+ } finally {
+ setSubmitting(false);
+ }
+ };
+
+ return (
+
+ }
+ >
+
+
+ );
+}
diff --git a/packages/next-common/components/appendants/common/createPopup.jsx b/packages/next-common/components/appendants/common/createPopup.jsx
index 1b23d92c59..31d02140a4 100644
--- a/packages/next-common/components/appendants/common/createPopup.jsx
+++ b/packages/next-common/components/appendants/common/createPopup.jsx
@@ -1,14 +1,6 @@
-import { useState } from "react";
-import { useDispatch } from "react-redux";
import { useEnsureLogin } from "next-common/hooks/useEnsureLogin";
-import {
- newErrorToast,
- newSuccessToast,
-} from "next-common/store/reducers/toastSlice";
import nextApi from "next-common/services/nextApi";
-import AppendantEditor from "./editor";
-import AppendantPopupLayout from "./popupLayout";
-import { CreateButton } from "./submitButton";
+import AppendantFormPopup from "./appendantFormPopup";
export default function CreateAppendantPopup({
onClose,
@@ -16,68 +8,27 @@ export default function CreateAppendantPopup({
onSuccess,
description = "",
}) {
- const [content, setContent] = useState("");
- const [contentType, setContentType] = useState("markdown");
- const [errors, setErrors] = useState();
- const [submitting, setSubmitting] = useState(false);
- const dispatch = useDispatch();
const { ensureLogin } = useEnsureLogin();
- const handleSubmit = async () => {
- setSubmitting(true);
- setErrors(null);
-
- try {
- if (!(await ensureLogin())) {
- return;
- }
-
- const { result, error } = await nextApi.post(createApi, {
- content,
- contentType,
- });
-
- if (error) {
- if (error.data) {
- setErrors(error);
- } else {
- dispatch(newErrorToast(error.message));
- }
- return;
- }
-
- if (result) {
- dispatch(newSuccessToast("Appendant created successfully"));
- onSuccess?.();
- onClose();
- }
- } finally {
- setSubmitting(false);
+ const submit = async (content, contentType) => {
+ if (!(await ensureLogin())) {
+ throw new Error("Cancelled");
}
+
+ return await nextApi.post(createApi, {
+ content,
+ contentType,
+ });
};
return (
-
- }
- >
-
-
+ onSuccess={onSuccess}
+ submit={submit}
+ successMessage="Appendant created successfully"
+ />
);
}
diff --git a/packages/next-common/components/appendants/common/editPopup.jsx b/packages/next-common/components/appendants/common/editPopup.jsx
index d43577edae..0b14ed7db4 100644
--- a/packages/next-common/components/appendants/common/editPopup.jsx
+++ b/packages/next-common/components/appendants/common/editPopup.jsx
@@ -1,14 +1,7 @@
-import { useState, useEffect } from "react";
-import { useDispatch } from "react-redux";
import { useEnsureLogin } from "next-common/hooks/useEnsureLogin";
-import {
- newErrorToast,
- newSuccessToast,
-} from "next-common/store/reducers/toastSlice";
import { appendantsApi } from "next-common/services/url";
import nextApi from "next-common/services/nextApi";
-import AppendantEditor from "./editor";
-import AppendantPopupLayout from "./popupLayout";
+import AppendantFormPopup from "./appendantFormPopup";
import { UpdateButton } from "./submitButton";
export default function EditAppendantPopup({
@@ -17,76 +10,31 @@ export default function EditAppendantPopup({
onSuccess,
description = "You are editing an appendant.",
}) {
- const [content, setContent] = useState("");
- const [contentType, setContentType] = useState("markdown");
- const [errors, setErrors] = useState();
- const [submitting, setSubmitting] = useState(false);
- const dispatch = useDispatch();
const { ensureLogin } = useEnsureLogin();
- useEffect(() => {
- if (editData) {
- setContent(editData.content || "");
- setContentType(editData.contentType || "markdown");
+ const submit = async (content, contentType) => {
+ if (!(await ensureLogin())) {
+ throw new Error("Cancelled");
}
- }, [editData]);
- const handleSubmit = async () => {
- setSubmitting(true);
- setErrors(null);
-
- try {
- if (!(await ensureLogin())) {
- return;
- }
-
- const updateApi = appendantsApi(editData?._id);
- const { result, error } = await nextApi.patch(updateApi, {
- content,
- contentType,
- });
-
- if (error) {
- if (error.data) {
- setErrors(error);
- } else {
- dispatch(newErrorToast(error.message));
- }
- return;
- }
-
- if (result) {
- dispatch(newSuccessToast("Appendant updated successfully"));
- onSuccess?.();
- onClose();
- }
- } finally {
- setSubmitting(false);
- }
+ const updateApi = appendantsApi(editData?._id);
+ return await nextApi.patch(updateApi, {
+ content,
+ contentType,
+ });
};
return (
-
- }
- >
-
-
+ onSuccess={onSuccess}
+ submit={submit}
+ successMessage="Appendant updated successfully"
+ initialContent={editData?.content || ""}
+ initialContentType={editData?.contentType || "markdown"}
+ SubmitButton={UpdateButton}
+ />
);
}
diff --git a/packages/next-common/components/appendants/common/item.jsx b/packages/next-common/components/appendants/common/item.jsx
index 850eb07ef2..00706466d4 100644
--- a/packages/next-common/components/appendants/common/item.jsx
+++ b/packages/next-common/components/appendants/common/item.jsx
@@ -13,6 +13,7 @@ import { formatTimeAgo } from "next-common/utils/viewfuncs/formatTimeAgo";
import Tooltip from "next-common/components/tooltip";
import useIsAppendantAuthor from "../useIsAppendantAuthor";
import correctionIpfsEndpointPlugin from "next-common/utils/previewerPlugins/correctionIpfsEndpoint";
+import AppendantEditedMarker from "next-common/sima/components/appendantEditedMarker";
function SplitDot() {
return ยท;
@@ -56,6 +57,21 @@ function Header({ index, data }) {
);
}
+function getTimeValue(value) {
+ if (!value) {
+ return null;
+ }
+ const time =
+ value instanceof Date ? value.getTime() : new Date(value).getTime();
+ return Number.isNaN(time) ? null : time;
+}
+
+function isAppendantEdited(data) {
+ const createdAt = getTimeValue(data?.createdAt);
+ const updatedAt = getTimeValue(data?.updatedAt);
+ return createdAt !== null && updatedAt !== null && updatedAt !== createdAt;
+}
+
function Content({ data }) {
return (
@@ -100,6 +116,11 @@ export default function AppendantItem({ index, data, MoreActions }) {
{isAuthor && MoreActions && }
+ {isAppendantEdited(data) &&
+ data?.dataSource === "sima" &&
+ data?.cid && (
+
+ )}
>
);
diff --git a/packages/next-common/components/appendants/referenda/createPopup.jsx b/packages/next-common/components/appendants/referenda/createPopup.jsx
index 227db779da..18aeed0fc1 100644
--- a/packages/next-common/components/appendants/referenda/createPopup.jsx
+++ b/packages/next-common/components/appendants/referenda/createPopup.jsx
@@ -1,19 +1,18 @@
-import { gov2ReferendaAppendantApi } from "next-common/services/url";
-import { useReferendaAppendantsContext } from "next-common/context/referendaAppendants";
-import { usePageProps } from "next-common/context/page";
-import CreateAppendantPopup from "../common/createPopup";
+import { usePost } from "next-common/context/post";
+import { useArticleActions } from "next-common/sima/context/articleActions";
+import LegacyCreateAppendantPopup from "./legacyCreatePopup";
+import SimaCreateAppendantPopup from "./simaCreatePopup";
export default function ReferendaCreateAppendantPopup({ setIsAppend }) {
- const { update } = useReferendaAppendantsContext();
- const { id } = usePageProps();
- const createApi = gov2ReferendaAppendantApi(id);
+ const post = usePost();
+ const { supportSima } = useArticleActions();
- return (
- setIsAppend(false)}
- createApi={createApi}
- onSuccess={update}
- description="You are adding an appendant as authors."
- />
- );
+ const isSimaPost =
+ supportSima && post?.refToPost?.postType === "referendaReferendum";
+
+ if (isSimaPost) {
+ return ;
+ }
+
+ return ;
}
diff --git a/packages/next-common/components/appendants/referenda/editPopup.jsx b/packages/next-common/components/appendants/referenda/editPopup.jsx
index 669fa20de4..1a44898655 100644
--- a/packages/next-common/components/appendants/referenda/editPopup.jsx
+++ b/packages/next-common/components/appendants/referenda/editPopup.jsx
@@ -1,15 +1,21 @@
-import { useReferendaAppendantsContext } from "next-common/context/referendaAppendants";
-import EditAppendantPopup from "../common/editPopup";
+import { usePost } from "next-common/context/post";
+import { useArticleActions } from "next-common/sima/context/articleActions";
+import LegacyEditAppendantPopup from "./legacyEditPopup";
+import SimaEditAppendantPopup from "./simaEditPopup";
export default function ReferendaEditAppendantPopup({ onClose, editData }) {
- const { update } = useReferendaAppendantsContext();
-
- return (
-
- );
+ const post = usePost();
+ const { supportSima } = useArticleActions();
+
+ const isSimaAppendant =
+ supportSima &&
+ editData?.dataSource === "sima" &&
+ !!editData?.cid &&
+ post?.refToPost?.postType === "referendaReferendum";
+
+ if (isSimaAppendant) {
+ return ;
+ }
+
+ return ;
}
diff --git a/packages/next-common/components/appendants/referenda/legacyCreatePopup.jsx b/packages/next-common/components/appendants/referenda/legacyCreatePopup.jsx
new file mode 100644
index 0000000000..abf89624d7
--- /dev/null
+++ b/packages/next-common/components/appendants/referenda/legacyCreatePopup.jsx
@@ -0,0 +1,18 @@
+import { gov2ReferendaAppendantApi } from "next-common/services/url";
+import { useReferendaAppendantsContext } from "next-common/context/referendaAppendants";
+import { usePageProps } from "next-common/context/page";
+import CreateAppendantPopup from "../common/createPopup";
+
+export default function LegacyCreateAppendantPopup({ setIsAppend }) {
+ const { update } = useReferendaAppendantsContext();
+ const { id } = usePageProps();
+
+ return (
+ setIsAppend(false)}
+ createApi={gov2ReferendaAppendantApi(id)}
+ onSuccess={update}
+ description="You are adding an appendant as authors."
+ />
+ );
+}
diff --git a/packages/next-common/components/appendants/referenda/legacyEditPopup.jsx b/packages/next-common/components/appendants/referenda/legacyEditPopup.jsx
new file mode 100644
index 0000000000..a07039e879
--- /dev/null
+++ b/packages/next-common/components/appendants/referenda/legacyEditPopup.jsx
@@ -0,0 +1,15 @@
+import { useReferendaAppendantsContext } from "next-common/context/referendaAppendants";
+import EditAppendantPopup from "../common/editPopup";
+
+export default function LegacyEditAppendantPopup({ onClose, editData }) {
+ const { update } = useReferendaAppendantsContext();
+
+ return (
+
+ );
+}
diff --git a/packages/next-common/components/appendants/referenda/simaCreatePopup.jsx b/packages/next-common/components/appendants/referenda/simaCreatePopup.jsx
new file mode 100644
index 0000000000..d06f1ed92e
--- /dev/null
+++ b/packages/next-common/components/appendants/referenda/simaCreatePopup.jsx
@@ -0,0 +1,35 @@
+import { useReferendaAppendantsContext } from "next-common/context/referendaAppendants";
+import { usePost } from "next-common/context/post";
+import { useIsPostAuthor } from "next-common/context/post/useIsPostAuthor";
+import usePostProxyAuthor from "next-common/hooks/usePostProxyAuthor";
+import { useAddReferendaAppendant } from "next-common/sima/actions/appendant";
+import { getRealField } from "next-common/sima/actions/common";
+import AppendantFormPopup from "../common/appendantFormPopup";
+
+export default function SimaCreateAppendantPopup({ setIsAppend }) {
+ const { update } = useReferendaAppendantsContext();
+ const post = usePost();
+ const isAuthor = useIsPostAuthor();
+ const proxyAddress = usePostProxyAuthor();
+ const addReferendaAppendant = useAddReferendaAppendant();
+ const realAddress = !isAuthor && proxyAddress ? proxyAddress : undefined;
+
+ const submit = (content, contentType) =>
+ addReferendaAppendant(
+ post,
+ content,
+ contentType,
+ getRealField(realAddress),
+ );
+
+ return (
+ setIsAppend(false)}
+ onSuccess={update}
+ submit={submit}
+ successMessage="Appendant created successfully"
+ />
+ );
+}
diff --git a/packages/next-common/components/appendants/referenda/simaEditPopup.jsx b/packages/next-common/components/appendants/referenda/simaEditPopup.jsx
new file mode 100644
index 0000000000..3457e8c066
--- /dev/null
+++ b/packages/next-common/components/appendants/referenda/simaEditPopup.jsx
@@ -0,0 +1,42 @@
+import { useReferendaAppendantsContext } from "next-common/context/referendaAppendants";
+import { usePost } from "next-common/context/post";
+import { useUser } from "next-common/context/user";
+import { isSameAddress } from "next-common/utils";
+import { useReplaceReferendaAppendant } from "next-common/sima/actions/appendant";
+import { getRealField } from "next-common/sima/actions/common";
+import AppendantFormPopup from "../common/appendantFormPopup";
+import { UpdateButton } from "../common/submitButton";
+
+export default function SimaEditAppendantPopup({ onClose, editData }) {
+ const { update } = useReferendaAppendantsContext();
+ const post = usePost();
+ const user = useUser();
+ const replaceReferendaAppendant = useReplaceReferendaAppendant();
+ const realAddress =
+ user?.proxyAddress && isSameAddress(user.proxyAddress, editData?.proposer)
+ ? user.proxyAddress
+ : undefined;
+
+ const submit = (content, contentType) =>
+ replaceReferendaAppendant(
+ post,
+ editData,
+ content,
+ contentType,
+ getRealField(realAddress),
+ );
+
+ return (
+
+ );
+}
diff --git a/packages/next-common/components/articleContent.js b/packages/next-common/components/articleContent.js
index 96c297803e..042d2c8e4c 100644
--- a/packages/next-common/components/articleContent.js
+++ b/packages/next-common/components/articleContent.js
@@ -13,6 +13,7 @@ import ContentSummary from "./contentSummary";
import PostDataSource from "./postDataSource";
import PostContentTranslations from "./postContentTranslations";
import useShowTranslations from "next-common/hooks/useShowTranslations";
+import PostEditedMarker from "next-common/sima/components/postEditedMarker";
const Wrapper = styled.div`
:hover {
@@ -44,9 +45,7 @@ export default function ArticleContent({
<>
- {isPostEdited(post) && (
- Edited
- )}
+ {isPostEdited(post) && }
>
);
diff --git a/packages/next-common/sima/actions/appendant.js b/packages/next-common/sima/actions/appendant.js
index 3b2ffa0297..b341388fe6 100644
--- a/packages/next-common/sima/actions/appendant.js
+++ b/packages/next-common/sima/actions/appendant.js
@@ -2,6 +2,16 @@ import nextApi from "next-common/services/nextApi";
import useSignSimaMessage from "next-common/utils/sima/useSignSimaMessage";
import { getContentField } from "next-common/utils/sima/utils";
import { useCallback } from "react";
+import getProposalIndexer from "next-common/sima/utils/getProposalIndexer";
+import getDetailPageCategory from "next-common/sima/utils/getDetailPageCategoryFromPostType";
+
+function getReferendumIndex(post) {
+ return (
+ post?.referendumIndex ??
+ post?.refToPost?.referendumIndex ??
+ post?.onchainData?.referendumIndex
+ );
+}
export function useAddDiscussionAppendant() {
const signSimaMessage = useSignSimaMessage();
@@ -21,3 +31,49 @@ export function useAddDiscussionAppendant() {
[signSimaMessage],
);
}
+
+export function useAddReferendaAppendant() {
+ const signSimaMessage = useSignSimaMessage();
+
+ return useCallback(
+ async (post, content, contentType, real) => {
+ const entity = {
+ action: "append_proposal",
+ indexer: getProposalIndexer(post),
+ ...getContentField(content, contentType),
+ timestamp: Date.now(),
+ real,
+ };
+ const data = await signSimaMessage(entity);
+ const type = getDetailPageCategory(post);
+ return await nextApi.post(
+ `sima/${type}/${getReferendumIndex(post)}/appendants`,
+ data,
+ );
+ },
+ [signSimaMessage],
+ );
+}
+
+export function useReplaceReferendaAppendant() {
+ const signSimaMessage = useSignSimaMessage();
+
+ return useCallback(
+ async (post, appendant, content, contentType, real) => {
+ const entity = {
+ action: "replace_appendant",
+ cid: appendant.cid,
+ ...getContentField(content, contentType),
+ timestamp: Date.now(),
+ real,
+ };
+ const data = await signSimaMessage(entity);
+ const type = getDetailPageCategory(post);
+ return await nextApi.post(
+ `sima/${type}/${getReferendumIndex(post)}/appendants/${appendant.cid}`,
+ data,
+ );
+ },
+ [signSimaMessage],
+ );
+}
diff --git a/packages/next-common/sima/components/appendantEditedMarker.jsx b/packages/next-common/sima/components/appendantEditedMarker.jsx
new file mode 100644
index 0000000000..78a26bf8b8
--- /dev/null
+++ b/packages/next-common/sima/components/appendantEditedMarker.jsx
@@ -0,0 +1,21 @@
+import React from "react";
+import EditedHistoryDropdown, {
+ PlainEditedMarker,
+} from "next-common/sima/components/editedHistory/editedHistoryDropdown";
+
+export default function AppendantEditedMarker({ appendant, className }) {
+ const cid = appendant?.cid;
+
+ if (!cid || appendant?.dataSource !== "sima") {
+ return ;
+ }
+
+ return (
+
+ );
+}
diff --git a/packages/next-common/sima/components/commentEditedMarker.jsx b/packages/next-common/sima/components/commentEditedMarker.jsx
index 37d28de139..66f0e4cb6b 100644
--- a/packages/next-common/sima/components/commentEditedMarker.jsx
+++ b/packages/next-common/sima/components/commentEditedMarker.jsx
@@ -1,11 +1,9 @@
import React from "react";
import { useComment } from "next-common/components/comment/context";
import { useCommentActions } from "next-common/sima/context/commentActions";
-import CommentEditedHistoryDropdown from "next-common/sima/components/commentEditedHistoryDropdown";
-
-function PlainCommentEditedMarker() {
- return Edited
;
-}
+import EditedHistoryDropdown, {
+ PlainEditedMarker,
+} from "next-common/sima/components/editedHistory/editedHistoryDropdown";
export default function CommentEditedMarker() {
const comment = useComment();
@@ -18,8 +16,14 @@ export default function CommentEditedMarker() {
!!comment?.hasEditHistory;
if (!hasHistory) {
- return ;
+ return ;
}
- return ;
+ return (
+
+ );
}
diff --git a/packages/next-common/sima/components/commentEditedHistoryDropdown.jsx b/packages/next-common/sima/components/editedHistory/editedHistoryDropdown.jsx
similarity index 64%
rename from packages/next-common/sima/components/commentEditedHistoryDropdown.jsx
rename to packages/next-common/sima/components/editedHistory/editedHistoryDropdown.jsx
index 5351a244e0..5c8ab57db9 100644
--- a/packages/next-common/sima/components/commentEditedHistoryDropdown.jsx
+++ b/packages/next-common/sima/components/editedHistory/editedHistoryDropdown.jsx
@@ -7,8 +7,8 @@ import Loading from "next-common/components/loading";
import nextApi from "next-common/services/nextApi";
import dynamicPopup from "next-common/lib/dynamic/popup";
-const CommentVersionViewPopup = dynamicPopup(() =>
- import("next-common/sima/components/commentVersionViewPopup"),
+const VersionViewPopup = dynamicPopup(() =>
+ import("next-common/sima/components/editedHistory/versionViewPopup"),
);
function formatEditedAgo(timestamp) {
@@ -38,44 +38,99 @@ function formatEditedAgo(timestamp) {
return `${parts.join(" ")} ago`;
}
-export default function CommentEditedHistoryDropdown({ commentCid }) {
+export function PlainEditedMarker({ label = "Edited", className }) {
+ return (
+
+ {label}
+
+ );
+}
+
+export default function EditedHistoryDropdown({
+ historyUrl,
+ popupTitle = "Edit History",
+ showTitle = false,
+ prefetch = false,
+ label = "Edited",
+ firstVersionLabel = "Created",
+ firstVersionTimeLabel = "Created at",
+ className,
+}) {
const [show, setShow] = useState(false);
const [history, setHistory] = useState(null);
const [selected, setSelected] = useState(null);
+ const loadingUrlRef = useRef(null);
+ const currentUrlRef = useRef(historyUrl);
const ref = useRef();
useClickAway(ref, () => setShow(false));
const loadHistory = useCallback(async () => {
- const { result, error } = await nextApi.fetch(
- `sima/comments/${commentCid}/history`,
- );
- if (error) {
- setHistory({ cid: commentCid, error: error.message });
+ if (!historyUrl || loadingUrlRef.current === historyUrl) {
return;
}
- setHistory({ cid: commentCid, items: result?.items || [] });
- }, [commentCid]);
- // Fetch history when the dropdown opens, and refetch when the comment gets a
- // new version (cid changes after an edit), so the list is always up to date.
+ loadingUrlRef.current = historyUrl;
+ try {
+ const { result, error } = await nextApi.fetch(historyUrl);
+ if (currentUrlRef.current !== historyUrl) {
+ return;
+ }
+ if (error) {
+ setHistory({ url: historyUrl, error: error.message });
+ return;
+ }
+ setHistory({ url: historyUrl, items: result?.items || [] });
+ } finally {
+ if (loadingUrlRef.current === historyUrl) {
+ loadingUrlRef.current = null;
+ }
+ }
+ }, [historyUrl]);
+
+ const isHistoryFresh = !!history && history.url === historyUrl;
+ const items = isHistoryFresh ? history.items : null;
+ const errorMessage = isHistoryFresh ? history.error : null;
+
useEffect(() => {
- if (!show || history?.cid === commentCid) {
+ currentUrlRef.current = historyUrl;
+ setHistory(null);
+ setSelected(null);
+ setShow(false);
+ }, [historyUrl]);
+
+ useEffect(() => {
+ if (!show || isHistoryFresh) {
return;
}
loadHistory();
- }, [show, commentCid, history?.cid, loadHistory]);
+ }, [show, isHistoryFresh, loadHistory]);
+
+ useEffect(() => {
+ if (!prefetch || !historyUrl || isHistoryFresh) {
+ return;
+ }
+ loadHistory();
+ }, [prefetch, historyUrl, isHistoryFresh, loadHistory]);
+
+ if (!historyUrl) {
+ return ;
+ }
+
+ if (prefetch && (!items || items.length <= 1)) {
+ return ;
+ }
- const isHistoryFresh = !!history && history.cid === commentCid;
- const items = isHistoryFresh ? history.items : null;
- const errorMessage = isHistoryFresh ? history.error : null;
const editCount = items ? Math.max(0, items.length - 1) : 0;
return (
<>
setShow(!show)}
>
- Edited
+ {label}
{
const isCreated = index === items.length - 1;
const isMostRecent = index === 0;
- const label = `${
- isCreated ? "Created" : "Edited"
+ const itemLabel = `${
+ isCreated ? firstVersionLabel : "Edited"
} ${formatEditedAgo(item.timestamp)}${
isMostRecent ? " (most recent)" : ""
}`;
@@ -135,7 +190,7 @@ export default function CommentEditedHistoryDropdown({ commentCid }) {
});
}}
>
- {label}
+ {itemLabel}
);
})}
@@ -145,10 +200,13 @@ export default function CommentEditedHistoryDropdown({ commentCid }) {
)}
{selected && (
- setSelected(null)}
/>
)}
diff --git a/packages/next-common/sima/components/commentVersionViewPopup.js b/packages/next-common/sima/components/editedHistory/versionViewPopup.js
similarity index 69%
rename from packages/next-common/sima/components/commentVersionViewPopup.js
rename to packages/next-common/sima/components/editedHistory/versionViewPopup.js
index fe2c4cca95..a75ff1256c 100644
--- a/packages/next-common/sima/components/commentVersionViewPopup.js
+++ b/packages/next-common/sima/components/editedHistory/versionViewPopup.js
@@ -134,9 +134,60 @@ function VersionedContent({ item, previousItem }) {
);
}
-export function CommentVersionView({ item, version, previousItem }) {
+function VersionedTitle({ title, previousTitle }) {
+ const diffParts = useMemo(
+ () => (previousTitle ? diffWords(previousTitle, title || "") : []),
+ [previousTitle, title],
+ );
+ const hasDiff = diffParts.some((part) => part.added || part.removed);
+
+ if (!previousTitle || !hasDiff) {
+ return (
+
+ {title || ""}
+
+ );
+ }
+
+ return (
+
+ {diffParts.map((part, index) => {
+ if (part.added) {
+ return (
+
+ {part.value}
+
+ );
+ }
+ if (part.removed) {
+ return (
+
+ {part.value}
+
+ );
+ }
+ return {part.value};
+ })}
+
+ );
+}
+
+export function VersionView({
+ item,
+ version,
+ previousItem,
+ showTitle = false,
+ firstVersionTimeLabel = "Created at",
+}) {
const time = dayjs(item.timestamp).format("YYYY-MM-DD HH:mm:ss");
- const timeLabel = version === 1 ? "Created at" : "Edited at";
+ const timeLabel = version === 1 ? firstVersionTimeLabel : "Edited at";
+ const hasTitle = showTitle && (item?.title || previousItem?.title);
return (
@@ -144,15 +195,25 @@ export function CommentVersionView({ item, version, previousItem }) {
{timeLabel} {time}
-
-
-
+ {item.cid && (
+
+
+
+ )}
+ {hasTitle && (
+
+
+
+ )}
@@ -160,22 +221,27 @@ export function CommentVersionView({ item, version, previousItem }) {
);
}
-export default function CommentVersionViewPopup({
+export default function VersionViewPopup({
item,
version,
previousItem,
+ popupTitle = "Edit History",
+ showTitle = false,
+ firstVersionTimeLabel = "Created at",
setShow = noop,
}) {
return (
setShow(false)}
className="w-200 max-w-full"
>
-
);
diff --git a/packages/next-common/sima/components/postEditedMarker.jsx b/packages/next-common/sima/components/postEditedMarker.jsx
new file mode 100644
index 0000000000..ae0af1447e
--- /dev/null
+++ b/packages/next-common/sima/components/postEditedMarker.jsx
@@ -0,0 +1,35 @@
+import React from "react";
+import { useDetailType } from "next-common/context/page";
+import { detailPageCategory } from "next-common/utils/consts/business/category";
+import EditedHistoryDropdown, {
+ PlainEditedMarker,
+} from "next-common/sima/components/editedHistory/editedHistoryDropdown";
+
+export default function PostEditedMarker({ post, className }) {
+ const detailType = useDetailType();
+ const cid = post?.cid || post?.refToPost?.cid;
+ const dataSource = post?.dataSource || post?.refToPost?.dataSource;
+ // "post" (singular) is the linked discussion collection type.
+ const isLinkedDiscussion = post?.refToPost?.postType === "post";
+
+ if (
+ !cid ||
+ dataSource !== "sima" ||
+ detailType === detailPageCategory.POST ||
+ isLinkedDiscussion
+ ) {
+ return ;
+ }
+
+ return (
+
+ );
+}