diff --git a/src/components/ProposalForm/ProposalForm.jsx b/src/components/ProposalForm/ProposalForm.jsx index 6ed2ba264..a76e98bbe 100644 --- a/src/components/ProposalForm/ProposalForm.jsx +++ b/src/components/ProposalForm/ProposalForm.jsx @@ -22,6 +22,7 @@ import ModalMDGuide from "src/components/ModalMDGuide"; import DraftSaver from "./DraftSaver"; import { useProposalForm } from "./hooks"; import usePolicy from "src/hooks/api/usePolicy"; +import { isAnchoring } from "src/helpers"; import { PROPOSAL_TYPE_REGULAR, PROPOSAL_TYPE_RFP, @@ -262,6 +263,11 @@ const ProposalFormWrapper = ({ const handleSubmit = useCallback( async (values, { resetForm, setSubmitting, setFieldError }) => { try { + if (isAnchoring()) { + throw new Error( + "Submitting proposals is temporarily unavailable while a daily censorship resistance routine is in progress. Sorry for the inconvenience. This will be fixed soon. Check back in 10 minutes." + ); + } const { type, rfpLink, ...others } = values; if (type === PROPOSAL_TYPE_RFP_SUBMISSION) { const rfpWithVoteSummaries = (await onFetchProposalsBatchWithoutState( diff --git a/src/containers/Comments/Comment/CommentWrapper.jsx b/src/containers/Comments/Comment/CommentWrapper.jsx index 3453d7807..e1d9d2808 100644 --- a/src/containers/Comments/Comment/CommentWrapper.jsx +++ b/src/containers/Comments/Comment/CommentWrapper.jsx @@ -4,6 +4,7 @@ import CommentForm from "src/components/CommentForm/CommentFormLazy"; import Link from "src/components/Link"; import { useComment } from "../hooks"; import Comment from "./Comment"; +import { handleCommentSubmission } from "../helpers"; const ContextLink = React.memo(({ parentid, recordToken, recordType }) => ( { - return onSubmitComment({ - comment, - token, - parentID: commentid - }); - }, - [token, commentid, onSubmitComment] + handleCommentSubmission(onSubmitComment, token, commentid), + [onSubmitComment, token, commentid] ); const handleCommentSubmitted = useCallback(() => { diff --git a/src/containers/Comments/Comments.jsx b/src/containers/Comments/Comments.jsx index 3f1e5777f..b0c3ba7c0 100644 --- a/src/containers/Comments/Comments.jsx +++ b/src/containers/Comments/Comments.jsx @@ -22,6 +22,7 @@ import { createSelectOptionFromSortOption, commentSortOptions, handleCommentCensoringInfo, + handleCommentSubmission, NUMBER_OF_LIST_PLACEHOLDERS } from "./helpers"; import useIdentity from "src/hooks/api/useIdentity"; @@ -36,10 +37,6 @@ import useLocalStorage from "src/hooks/utils/useLocalStorage"; const COMMENTS_LOGIN_MODAL_ID = "commentsLoginModal"; -// Anchoring issue ref: see https://github.com/decred/politeiagui/issues/1941 -const TEMP_ANCHORING_MINUTE = 58; -const ANCHORING_TIMEOUT_MS = 5 * 60 * 1000; // 5' in ms - const Comments = ({ numOfComments, recordToken, @@ -93,15 +90,10 @@ const Comments = ({ const paywallMissing = paywallEnabled && !isPaid; const isSingleThread = !!threadParentID; - const handleSubmitComment = useCallback( - (comment) => { - return onSubmitComment({ - comment, - token: recordToken, - parentID: 0 - }); - }, - [recordToken, onSubmitComment] + const handleSubmitComment = handleCommentSubmission( + onSubmitComment, + recordToken, + 0 ); const handleSetSortOption = useCallback( @@ -213,42 +205,6 @@ const Comments = ({ ((comments && !comments.find((c) => c.commentid === threadParentID)) || numOfComments === 0); - // Temporarily block comments due to git anchoring: See https://github.com/decred/politeiagui/issues/1941 - const getTimeLeftToAnchor = () => { - const date = new Date(); - const timeLeft = - TEMP_ANCHORING_MINUTE * 60000 - - date.getMinutes() * 60000 - - date.getSeconds() * 1000 - - date.getMilliseconds(); - return timeLeft; - }; - - const [isAnchoring, setIsAchoring] = useState(getTimeLeftToAnchor() <= 0); - const handleToggleAnchoring = useCallback(() => { - setIsAchoring(!isAnchoring); - }, [setIsAchoring, isAnchoring]); - - useEffect( - function anchoringModeListener() { - let timeout = null; - if (isAnchoring) { - timeout = setTimeout(handleToggleAnchoring, ANCHORING_TIMEOUT_MS); - } - return () => timeout && clearTimeout(timeout); - }, - [isAnchoring, handleToggleAnchoring] - ); - useEffect( - function commentsAllowedListener() { - let timeout = null; - if (!isAnchoring) { - timeout = setTimeout(handleToggleAnchoring, getTimeLeftToAnchor()); - } - return () => timeout && clearTimeout(timeout); - }, - [isAnchoring, handleToggleAnchoring] - ); return ( <> )} - {isAnchoring && ( - - Commenting temporarily unavailable while an hourly censorship - resistance routine is in progress. Sorry for the - inconvenience. This will be fixed soon. Check back in 5 - minutes. - - )} {!readOnly && !!identityError && } {!isSingleThread && !readOnly && ( )} diff --git a/src/containers/Comments/helpers.js b/src/containers/Comments/helpers.js index 7bf28286b..f0483d38d 100644 --- a/src/containers/Comments/helpers.js +++ b/src/containers/Comments/helpers.js @@ -1,4 +1,5 @@ import orderBy from "lodash/fp/orderBy"; +import { isAnchoring } from "src/helpers"; export const NUMBER_OF_LIST_PLACEHOLDERS = 3; @@ -63,3 +64,18 @@ export function handleCommentCensoringInfo(cb, ...args) { cb(...args, reason); }; } + +export function handleCommentSubmission(cb, token, parentID = 0) { + return (comment) => { + if (isAnchoring()) { + throw new Error( + "Commenting temporarily unavailable while a daily censorship resistance routine is in progress. Sorry for the inconvenience. This will be fixed soon. Check back in 10 minutes." + ); + } + return cb({ + comment, + token, + parentID + }); + }; +} diff --git a/src/helpers.js b/src/helpers.js index 2dd52aa52..f67a5a77f 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -447,3 +447,29 @@ export const fromUSDCentsToUSDUnits = (cents) => export const fromUSDUnitsToUSDCents = (units) => parseInt(units * 100, 10); export const isEmpty = (obj) => Object.keys(obj).length === 0; + +/** Pure function that given 2 UNIX timestamps returns the differnce between them in minutes */ +export const getTimeDiffInMinutes = (d1, d2) => { + return (d1 - d2) / 60e3; +}; + +/** This is a temporary fix to prevent comments and proposals submissions during anchoring time */ +/** + * @function isAnchoring + * @param {string} anchoringStartTime UTC time to prevent submissions in HH:MM format + * @param {number} anchoringDuration Duration in minutes + */ +export const isAnchoring = ( + anchoringStartTime = "06:58", + anchoringDuration = 10 +) => { + const targetDate = new Date(); + const [startHour, startMinute] = anchoringStartTime.split(":"); + targetDate.setUTCHours(startHour); + targetDate.setUTCMinutes(startMinute); + const timeDiffMinutes = getTimeDiffInMinutes( + new Date().getTime(), + targetDate.getTime() + ); + return timeDiffMinutes >= 0 && timeDiffMinutes < anchoringDuration; +}; diff --git a/src/pages/User/VerifyKey/VerifyKey.jsx b/src/pages/User/VerifyKey/VerifyKey.jsx index 835040f59..e4e686609 100644 --- a/src/pages/User/VerifyKey/VerifyKey.jsx +++ b/src/pages/User/VerifyKey/VerifyKey.jsx @@ -50,7 +50,9 @@ const VerifyKey = ({ location, history }) => { const success = verifyUserKey && verifyUserKey.success; const error = verifyUserKeyError; const pushToHome = useCallback(() => history.push("/"), [history]); - const successButtonText = (isCMS ? "Ok, go to invoices" : "Ok, go to proposals"); + const successButtonText = isCMS + ? "Ok, go to invoices" + : "Ok, go to proposals"; return ( diff --git a/src/tests/helpers.test.js b/src/tests/helpers.test.js index f12026ba5..c4092ff0d 100644 --- a/src/tests/helpers.test.js +++ b/src/tests/helpers.test.js @@ -31,3 +31,29 @@ describe("test helpers functions", () => { ); }); }); + +describe("test getTimeDiffInMinutes function", () => { + test("it should return 0 when d2 == d1", () => { + expect(help.getTimeDiffInMinutes(1591030381, 1591030381)).toEqual(0); + }); + + test("it should return positive when d1 > d2", () => { + expect( + help.getTimeDiffInMinutes(1591030521000, 1591030381000) + ).toBeGreaterThan(0); + }); + + test("it should return negative when d1 < d2", () => { + expect( + help.getTimeDiffInMinutes(1591030381000, 1591030700000) + ).toBeLessThan(0); + }); + + test("it should return correctly within the same hour", () => { + expect(help.getTimeDiffInMinutes(1591030200000, 1591029900000)).toEqual(5); + }); + + test("it should return correctly when the hour overflows", () => { + expect(help.getTimeDiffInMinutes(1591031100000, 1591030500000)).toEqual(10); + }); +});