mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
fix(mod-queue): preserve pending reject state after refresh
This commit is contained in:
@@ -8,6 +8,7 @@ import styles from '../../views/post/post.module.css';
|
|||||||
import { CommentMediaInfo, getDisplayMediaInfoType, getHasThumbnail, getMediaDimensions } from '../../lib/utils/media-utils';
|
import { CommentMediaInfo, getDisplayMediaInfoType, getHasThumbnail, getMediaDimensions } from '../../lib/utils/media-utils';
|
||||||
import { hashStringToColor, getTextColorForBackground } from '../../lib/utils/post-utils';
|
import { hashStringToColor, getTextColorForBackground } from '../../lib/utils/post-utils';
|
||||||
import { getFormattedDate, getFormattedTimeAgo } from '../../lib/utils/time-utils';
|
import { getFormattedDate, getFormattedTimeAgo } from '../../lib/utils/time-utils';
|
||||||
|
import { approvePendingCommentModeration, isPendingApprovalRejected, rejectPendingCommentModeration } from '../../lib/utils/pending-approval-moderation';
|
||||||
import { isValidURL } from '../../lib/utils/url-utils';
|
import { isValidURL } from '../../lib/utils/url-utils';
|
||||||
import { isAllView, isModQueueView, isModView, isPendingPostView, isPostPageView, isSubscriptionsView } from '../../lib/utils/view-utils';
|
import { isAllView, isModQueueView, isModView, isPendingPostView, isPostPageView, isSubscriptionsView } from '../../lib/utils/view-utils';
|
||||||
import { formatUserIDForDisplay, truncateWithEllipsisInMiddle } from '../../lib/utils/string-utils';
|
import { formatUserIDForDisplay, truncateWithEllipsisInMiddle } from '../../lib/utils/string-utils';
|
||||||
@@ -131,7 +132,7 @@ const PostInfo = ({
|
|||||||
} = usePublishCommentModeration({
|
} = usePublishCommentModeration({
|
||||||
commentCid: cid,
|
commentCid: cid,
|
||||||
subplebbitAddress: shouldShowPendingApprovalButtons ? subplebbitAddress : undefined,
|
subplebbitAddress: shouldShowPendingApprovalButtons ? subplebbitAddress : undefined,
|
||||||
commentModeration: { approved: true },
|
commentModeration: approvePendingCommentModeration,
|
||||||
onChallenge: async (...args: any) => {
|
onChallenge: async (...args: any) => {
|
||||||
addChallenge([...args, post]);
|
addChallenge([...args, post]);
|
||||||
},
|
},
|
||||||
@@ -150,7 +151,7 @@ const PostInfo = ({
|
|||||||
} = usePublishCommentModeration({
|
} = usePublishCommentModeration({
|
||||||
commentCid: cid,
|
commentCid: cid,
|
||||||
subplebbitAddress: shouldShowPendingApprovalButtons ? subplebbitAddress : undefined,
|
subplebbitAddress: shouldShowPendingApprovalButtons ? subplebbitAddress : undefined,
|
||||||
commentModeration: { removed: true },
|
commentModeration: rejectPendingCommentModeration,
|
||||||
onChallenge: async (...args: any) => {
|
onChallenge: async (...args: any) => {
|
||||||
addChallenge([...args, post]);
|
addChallenge([...args, post]);
|
||||||
},
|
},
|
||||||
@@ -208,7 +209,7 @@ const PostInfo = ({
|
|||||||
// Check if post is awaiting approval and over threshold (for mod queue view)
|
// Check if post is awaiting approval and over threshold (for mod queue view)
|
||||||
const approved = post?.approved;
|
const approved = post?.approved;
|
||||||
const alreadyApproved = approved === true;
|
const alreadyApproved = approved === true;
|
||||||
const alreadyRejected = removed === true;
|
const alreadyRejected = isPendingApprovalRejected(post);
|
||||||
const isAwaitingApproval = isInModQueueView && !alreadyApproved && !alreadyRejected;
|
const isAwaitingApproval = isInModQueueView && !alreadyApproved && !alreadyRejected;
|
||||||
const timeWaiting = timestamp ? currentTime - timestamp : 0;
|
const timeWaiting = timestamp ? currentTime - timestamp : 0;
|
||||||
const alertThresholdSeconds = getAlertThresholdSeconds();
|
const alertThresholdSeconds = getAlertThresholdSeconds();
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { shouldShowSnow } from '../../lib/snow';
|
|||||||
import { getHasThumbnail } from '../../lib/utils/media-utils';
|
import { getHasThumbnail } from '../../lib/utils/media-utils';
|
||||||
import { getTextColorForBackground, hashStringToColor } from '../../lib/utils/post-utils';
|
import { getTextColorForBackground, hashStringToColor } from '../../lib/utils/post-utils';
|
||||||
import { getFormattedDate, getFormattedTimeAgo } from '../../lib/utils/time-utils';
|
import { getFormattedDate, getFormattedTimeAgo } from '../../lib/utils/time-utils';
|
||||||
|
import { approvePendingCommentModeration, isPendingApprovalRejected, rejectPendingCommentModeration } from '../../lib/utils/pending-approval-moderation';
|
||||||
import { isAllView, isModQueueView, isModView, isPendingPostView, isPostPageView, isSubscriptionsView } from '../../lib/utils/view-utils';
|
import { isAllView, isModQueueView, isModView, isPendingPostView, isPostPageView, isSubscriptionsView } from '../../lib/utils/view-utils';
|
||||||
import { formatUserIDForDisplay } from '../../lib/utils/string-utils';
|
import { formatUserIDForDisplay } from '../../lib/utils/string-utils';
|
||||||
import useModQueueStore from '../../stores/use-mod-queue-store';
|
import useModQueueStore from '../../stores/use-mod-queue-store';
|
||||||
@@ -103,7 +104,7 @@ const PostInfoAndMedia = ({ post, postReplyCount = 0, roles, threadNumber }: Pos
|
|||||||
} = usePublishCommentModeration({
|
} = usePublishCommentModeration({
|
||||||
commentCid: cid,
|
commentCid: cid,
|
||||||
subplebbitAddress: shouldShowPendingApprovalButtons ? subplebbitAddress : undefined,
|
subplebbitAddress: shouldShowPendingApprovalButtons ? subplebbitAddress : undefined,
|
||||||
commentModeration: { approved: true },
|
commentModeration: approvePendingCommentModeration,
|
||||||
onChallenge: async (...args: any) => {
|
onChallenge: async (...args: any) => {
|
||||||
addChallenge([...args, post]);
|
addChallenge([...args, post]);
|
||||||
},
|
},
|
||||||
@@ -122,7 +123,7 @@ const PostInfoAndMedia = ({ post, postReplyCount = 0, roles, threadNumber }: Pos
|
|||||||
} = usePublishCommentModeration({
|
} = usePublishCommentModeration({
|
||||||
commentCid: cid,
|
commentCid: cid,
|
||||||
subplebbitAddress: shouldShowPendingApprovalButtons ? subplebbitAddress : undefined,
|
subplebbitAddress: shouldShowPendingApprovalButtons ? subplebbitAddress : undefined,
|
||||||
commentModeration: { removed: true },
|
commentModeration: rejectPendingCommentModeration,
|
||||||
onChallenge: async (...args: any) => {
|
onChallenge: async (...args: any) => {
|
||||||
addChallenge([...args, post]);
|
addChallenge([...args, post]);
|
||||||
},
|
},
|
||||||
@@ -182,7 +183,7 @@ const PostInfoAndMedia = ({ post, postReplyCount = 0, roles, threadNumber }: Pos
|
|||||||
// Check if post is awaiting approval and over threshold (for mod queue view)
|
// Check if post is awaiting approval and over threshold (for mod queue view)
|
||||||
const approved = post?.approved;
|
const approved = post?.approved;
|
||||||
const alreadyApproved = approved === true;
|
const alreadyApproved = approved === true;
|
||||||
const alreadyRejected = removed === true;
|
const alreadyRejected = isPendingApprovalRejected(post);
|
||||||
const isAwaitingApproval = isInModQueueView && !alreadyApproved && !alreadyRejected;
|
const isAwaitingApproval = isInModQueueView && !alreadyApproved && !alreadyRejected;
|
||||||
const timeWaiting = timestamp ? currentTime - timestamp : 0;
|
const timeWaiting = timestamp ? currentTime - timestamp : 0;
|
||||||
const alertThresholdSeconds = getAlertThresholdSeconds();
|
const alertThresholdSeconds = getAlertThresholdSeconds();
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import { approvePendingCommentModeration, isPendingApprovalAwaiting, isPendingApprovalRejected, rejectPendingCommentModeration } from '../pending-approval-moderation';
|
||||||
|
|
||||||
|
describe('pending approval moderation utils', () => {
|
||||||
|
it('publishes approval with approved=true', () => {
|
||||||
|
expect(approvePendingCommentModeration).toEqual({ approved: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('publishes rejection with approved=false and no removed flag', () => {
|
||||||
|
expect(rejectPendingCommentModeration).toEqual({ approved: false });
|
||||||
|
expect('removed' in rejectPendingCommentModeration).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('treats pending approved=false as rejected for display', () => {
|
||||||
|
expect(isPendingApprovalRejected({ pendingApproval: true, approved: false })).toBe(true);
|
||||||
|
expect(isPendingApprovalAwaiting({ pendingApproval: true, approved: false })).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('keeps pending approvals awaiting when no decision has been made', () => {
|
||||||
|
expect(isPendingApprovalRejected({ pendingApproval: true })).toBe(false);
|
||||||
|
expect(isPendingApprovalAwaiting({ pendingApproval: true })).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
export const approvePendingCommentModeration = { approved: true } as const;
|
||||||
|
|
||||||
|
// plebbit-js clears pendingApproval only when rejection is published as approved:false.
|
||||||
|
// Sending removed:true marks the comment removed but can leave it in the mod queue.
|
||||||
|
export const rejectPendingCommentModeration = { approved: false } as const;
|
||||||
|
|
||||||
|
type PendingApprovalDisplayState = {
|
||||||
|
approved?: boolean;
|
||||||
|
removed?: boolean;
|
||||||
|
pendingApproval?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isPendingApprovalRejected = (comment?: PendingApprovalDisplayState) =>
|
||||||
|
comment?.removed === true || (comment?.pendingApproval === true && comment?.approved === false);
|
||||||
|
|
||||||
|
export const isPendingApprovalAwaiting = (comment?: PendingApprovalDisplayState) =>
|
||||||
|
comment?.pendingApproval === true && comment?.approved !== true && !isPendingApprovalRejected(comment);
|
||||||
@@ -13,6 +13,12 @@ import { useDirectories, DirectoryCommunity } from '../../hooks/use-directories'
|
|||||||
import getShortAddress from '../../lib/get-short-address';
|
import getShortAddress from '../../lib/get-short-address';
|
||||||
import { BOARD_CODE_GROUPS } from '../../constants/board-codes';
|
import { BOARD_CODE_GROUPS } from '../../constants/board-codes';
|
||||||
import { getHasThumbnail, getCommentMediaInfo } from '../../lib/utils/media-utils';
|
import { getHasThumbnail, getCommentMediaInfo } from '../../lib/utils/media-utils';
|
||||||
|
import {
|
||||||
|
approvePendingCommentModeration,
|
||||||
|
isPendingApprovalAwaiting,
|
||||||
|
isPendingApprovalRejected,
|
||||||
|
rejectPendingCommentModeration,
|
||||||
|
} from '../../lib/utils/pending-approval-moderation';
|
||||||
import { getFormattedDate, getFormattedTimeAgo } from '../../lib/utils/time-utils';
|
import { getFormattedDate, getFormattedTimeAgo } from '../../lib/utils/time-utils';
|
||||||
import useFeedResetStore from '../../stores/use-feed-reset-store';
|
import useFeedResetStore from '../../stores/use-feed-reset-store';
|
||||||
import useChallengesStore from '../../stores/use-challenges-store';
|
import useChallengesStore from '../../stores/use-challenges-store';
|
||||||
@@ -148,11 +154,11 @@ const ModQueueActions = ({ status, errorMessage, isPublishing, handleApprove, ha
|
|||||||
|
|
||||||
const useModQueueActions = (comment: Comment): ModQueueActionState => {
|
const useModQueueActions = (comment: Comment): ModQueueActionState => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { cid, subplebbitAddress, approved, removed } = comment || {};
|
const { cid, subplebbitAddress, approved, removed, pendingApproval } = comment || {};
|
||||||
const [initiatedAction, setInitiatedAction] = useState<ModerationAction>(null);
|
const [initiatedAction, setInitiatedAction] = useState<ModerationAction>(null);
|
||||||
|
|
||||||
const alreadyApproved = approved === true;
|
const alreadyApproved = approved === true;
|
||||||
const alreadyRejected = removed === true;
|
const alreadyRejected = isPendingApprovalRejected({ approved, removed, pendingApproval });
|
||||||
|
|
||||||
const {
|
const {
|
||||||
publishCommentModeration: approve,
|
publishCommentModeration: approve,
|
||||||
@@ -161,7 +167,7 @@ const useModQueueActions = (comment: Comment): ModQueueActionState => {
|
|||||||
} = usePublishCommentModeration({
|
} = usePublishCommentModeration({
|
||||||
commentCid: cid,
|
commentCid: cid,
|
||||||
subplebbitAddress,
|
subplebbitAddress,
|
||||||
commentModeration: { approved: true },
|
commentModeration: approvePendingCommentModeration,
|
||||||
onChallenge: async (...args: any) => {
|
onChallenge: async (...args: any) => {
|
||||||
addChallenge([...args, comment]);
|
addChallenge([...args, comment]);
|
||||||
},
|
},
|
||||||
@@ -180,7 +186,7 @@ const useModQueueActions = (comment: Comment): ModQueueActionState => {
|
|||||||
} = usePublishCommentModeration({
|
} = usePublishCommentModeration({
|
||||||
commentCid: cid,
|
commentCid: cid,
|
||||||
subplebbitAddress,
|
subplebbitAddress,
|
||||||
commentModeration: { removed: true },
|
commentModeration: rejectPendingCommentModeration,
|
||||||
onChallenge: async (...args: any) => {
|
onChallenge: async (...args: any) => {
|
||||||
addChallenge([...args, comment]);
|
addChallenge([...args, comment]);
|
||||||
},
|
},
|
||||||
@@ -245,21 +251,36 @@ const ModQueueRow = memo(({ comment, isOdd = false, showBoard = false, boardPath
|
|||||||
const { editedComment } = useEditedComment({ comment });
|
const { editedComment } = useEditedComment({ comment });
|
||||||
const displayComment = editedComment || comment;
|
const displayComment = editedComment || comment;
|
||||||
|
|
||||||
const { content, title, timestamp, subplebbitAddress, cid, threadCid, link, thumbnailUrl, linkWidth, linkHeight, removed, approved, number, parentCid } =
|
const {
|
||||||
displayComment;
|
content,
|
||||||
|
title,
|
||||||
|
timestamp,
|
||||||
|
subplebbitAddress,
|
||||||
|
cid,
|
||||||
|
threadCid,
|
||||||
|
link,
|
||||||
|
thumbnailUrl,
|
||||||
|
linkWidth,
|
||||||
|
linkHeight,
|
||||||
|
removed,
|
||||||
|
approved,
|
||||||
|
pendingApproval,
|
||||||
|
number,
|
||||||
|
parentCid,
|
||||||
|
} = displayComment;
|
||||||
|
|
||||||
// Check if already moderated (from previous session or API update)
|
// Check if already moderated (from previous session or API update)
|
||||||
// Note: `approved` and `removed` are direct fields on the comment from CommentUpdate,
|
// Note: `approved` and `removed` are direct fields on the comment from CommentUpdate,
|
||||||
// not nested under commentModeration (which is the options object for publishing moderation actions)
|
// not nested under commentModeration (which is the options object for publishing moderation actions)
|
||||||
const alreadyApproved = approved === true;
|
const alreadyApproved = approved === true;
|
||||||
const alreadyRejected = removed === true;
|
const alreadyRejected = isPendingApprovalRejected({ approved, removed, pendingApproval });
|
||||||
|
|
||||||
const timeWaiting = currentTime - timestamp;
|
const timeWaiting = currentTime - timestamp;
|
||||||
const alertThresholdSeconds = getAlertThresholdSeconds();
|
const alertThresholdSeconds = getAlertThresholdSeconds();
|
||||||
const isOverThreshold = timeWaiting > alertThresholdSeconds;
|
const isOverThreshold = timeWaiting > alertThresholdSeconds;
|
||||||
|
|
||||||
// Only show alert animation for comments awaiting approval (not approved or rejected)
|
// Only show alert animation for comments awaiting approval (not approved or rejected)
|
||||||
const isAwaitingApproval = !alreadyApproved && !alreadyRejected;
|
const isAwaitingApproval = isPendingApprovalAwaiting(displayComment);
|
||||||
|
|
||||||
const { status, errorMessage, isPublishing, handleApprove, handleReject } = useModQueueActions(displayComment);
|
const { status, errorMessage, isPublishing, handleApprove, handleReject } = useModQueueActions(displayComment);
|
||||||
const hasTitle = title && title.trim().length > 0;
|
const hasTitle = title && title.trim().length > 0;
|
||||||
@@ -357,16 +378,31 @@ const ModQueueCard = memo(({ comment, showBoard = false, boardPath, boardDisplay
|
|||||||
const { editedComment } = useEditedComment({ comment });
|
const { editedComment } = useEditedComment({ comment });
|
||||||
const displayComment = editedComment || comment;
|
const displayComment = editedComment || comment;
|
||||||
|
|
||||||
const { content, title, timestamp, subplebbitAddress, cid, threadCid, link, thumbnailUrl, linkWidth, linkHeight, removed, approved, number, parentCid } =
|
const {
|
||||||
displayComment;
|
content,
|
||||||
|
title,
|
||||||
|
timestamp,
|
||||||
|
subplebbitAddress,
|
||||||
|
cid,
|
||||||
|
threadCid,
|
||||||
|
link,
|
||||||
|
thumbnailUrl,
|
||||||
|
linkWidth,
|
||||||
|
linkHeight,
|
||||||
|
removed,
|
||||||
|
approved,
|
||||||
|
pendingApproval,
|
||||||
|
number,
|
||||||
|
parentCid,
|
||||||
|
} = displayComment;
|
||||||
|
|
||||||
const alreadyApproved = approved === true;
|
const alreadyApproved = approved === true;
|
||||||
const alreadyRejected = removed === true;
|
const alreadyRejected = isPendingApprovalRejected({ approved, removed, pendingApproval });
|
||||||
|
|
||||||
const timeWaiting = currentTime - timestamp;
|
const timeWaiting = currentTime - timestamp;
|
||||||
const alertThresholdSeconds = getAlertThresholdSeconds();
|
const alertThresholdSeconds = getAlertThresholdSeconds();
|
||||||
const isOverThreshold = timeWaiting > alertThresholdSeconds;
|
const isOverThreshold = timeWaiting > alertThresholdSeconds;
|
||||||
const isAwaitingApproval = !alreadyApproved && !alreadyRejected;
|
const isAwaitingApproval = isPendingApprovalAwaiting(displayComment);
|
||||||
|
|
||||||
const { status, errorMessage, isPublishing, handleApprove, handleReject } = useModQueueActions(displayComment);
|
const { status, errorMessage, isPublishing, handleApprove, handleReject } = useModQueueActions(displayComment);
|
||||||
const hasTitle = title && title.trim().length > 0;
|
const hasTitle = title && title.trim().length > 0;
|
||||||
@@ -480,7 +516,7 @@ const ModQueueBoardSummary = ({ feed, directories, accountSubplebbitAddresses }:
|
|||||||
if (!addr) continue;
|
if (!addr) continue;
|
||||||
const entry = counts.get(addr);
|
const entry = counts.get(addr);
|
||||||
if (!entry) continue;
|
if (!entry) continue;
|
||||||
const isAwaiting = item.approved !== true && item.removed !== true;
|
const isAwaiting = isPendingApprovalAwaiting(item);
|
||||||
if (!isAwaiting) continue;
|
if (!isAwaiting) continue;
|
||||||
const timeWaiting = currentTime - (item.timestamp ?? 0);
|
const timeWaiting = currentTime - (item.timestamp ?? 0);
|
||||||
const isUrgent = timeWaiting > alertThresholdSeconds;
|
const isUrgent = timeWaiting > alertThresholdSeconds;
|
||||||
@@ -606,8 +642,8 @@ const ModQueueCountItem = ({ comment, alertThresholdSeconds, onStatusChange }: M
|
|||||||
const displayComment = editedComment || comment;
|
const displayComment = editedComment || comment;
|
||||||
const currentTime = useCurrentTime();
|
const currentTime = useCurrentTime();
|
||||||
|
|
||||||
const { cid, approved, removed, timestamp } = displayComment;
|
const { cid, timestamp } = displayComment;
|
||||||
const isAwaiting = approved !== true && removed !== true;
|
const isAwaiting = isPendingApprovalAwaiting(displayComment);
|
||||||
const timeWaiting = currentTime - timestamp;
|
const timeWaiting = currentTime - timestamp;
|
||||||
const isUrgent = isAwaiting && timeWaiting > alertThresholdSeconds;
|
const isUrgent = isAwaiting && timeWaiting > alertThresholdSeconds;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user