mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
Merge branch 'codex/fix/mod-queue-history'
This commit is contained in:
@@ -220,6 +220,7 @@ const PostInfo = ({
|
|||||||
isPublishing,
|
isPublishing,
|
||||||
onApprove,
|
onApprove,
|
||||||
onReject,
|
onReject,
|
||||||
|
onRemoveFromModQueue,
|
||||||
quotedByMap,
|
quotedByMap,
|
||||||
directRepliesByParentCid,
|
directRepliesByParentCid,
|
||||||
postsByAuthorInThread,
|
postsByAuthorInThread,
|
||||||
@@ -304,6 +305,15 @@ const PostInfo = ({
|
|||||||
const modQueueThreadRoute = getModQueueCommentRoute(boardPath, post?.cid);
|
const modQueueThreadRoute = getModQueueCommentRoute(boardPath, post?.cid);
|
||||||
const modQueueThreadRouteState = getQueuedCommentRouteState(post);
|
const modQueueThreadRouteState = getQueuedCommentRouteState(post);
|
||||||
const modQueueErrorMessage = formatErrorForDisplay(modQueueError);
|
const modQueueErrorMessage = formatErrorForDisplay(modQueueError);
|
||||||
|
const modQueueRemoveButton = onRemoveFromModQueue ? (
|
||||||
|
<span className={styles.modQueueButtonWrapper}>
|
||||||
|
[
|
||||||
|
<button className={styles.modQueueActionButton} onClick={onRemoveFromModQueue} disabled={isPublishing}>
|
||||||
|
{t('remove')}
|
||||||
|
</button>
|
||||||
|
]
|
||||||
|
</span>
|
||||||
|
) : null;
|
||||||
|
|
||||||
const onLinkToPostClick = (e: React.MouseEvent<HTMLAnchorElement>) => {
|
const onLinkToPostClick = (e: React.MouseEvent<HTMLAnchorElement>) => {
|
||||||
if (!cid || !threadRoute) {
|
if (!cid || !threadRoute) {
|
||||||
@@ -471,9 +481,15 @@ const PostInfo = ({
|
|||||||
{capitalize(t('type'))}: {capitalize(t(isReply ? 'reply' : 'post'))}
|
{capitalize(t('type'))}: {capitalize(t(isReply ? 'reply' : 'post'))}
|
||||||
</span>
|
</span>
|
||||||
{modQueueStatus === 'approved' ? (
|
{modQueueStatus === 'approved' ? (
|
||||||
|
<>
|
||||||
<span className={styles.modQueueStatusApproved}>{t('approved')}</span>
|
<span className={styles.modQueueStatusApproved}>{t('approved')}</span>
|
||||||
|
{modQueueRemoveButton}
|
||||||
|
</>
|
||||||
) : modQueueStatus === 'rejected' ? (
|
) : modQueueStatus === 'rejected' ? (
|
||||||
|
<>
|
||||||
<span className={styles.modQueueStatusRejected}>{t('rejected')}</span>
|
<span className={styles.modQueueStatusRejected}>{t('rejected')}</span>
|
||||||
|
{modQueueRemoveButton}
|
||||||
|
</>
|
||||||
) : modQueueStatus === 'failed' ? (
|
) : modQueueStatus === 'failed' ? (
|
||||||
<span className={styles.modQueueStatusRejected} title={modQueueErrorMessage}>
|
<span className={styles.modQueueStatusRejected} title={modQueueErrorMessage}>
|
||||||
{t('failed')}
|
{t('failed')}
|
||||||
@@ -816,6 +832,7 @@ const PostDesktop = ({
|
|||||||
isPublishing,
|
isPublishing,
|
||||||
onApprove,
|
onApprove,
|
||||||
onReject,
|
onReject,
|
||||||
|
onRemoveFromModQueue,
|
||||||
}: PostProps) => {
|
}: PostProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const resolvedPost = withResolvedCommentCommunityAddress(post);
|
const resolvedPost = withResolvedCommentCommunityAddress(post);
|
||||||
@@ -1169,6 +1186,7 @@ const PostDesktop = ({
|
|||||||
isPublishing={isPublishing}
|
isPublishing={isPublishing}
|
||||||
onApprove={onApprove}
|
onApprove={onApprove}
|
||||||
onReject={onReject}
|
onReject={onReject}
|
||||||
|
onRemoveFromModQueue={onRemoveFromModQueue}
|
||||||
quotedByMap={quotedByMap}
|
quotedByMap={quotedByMap}
|
||||||
directRepliesByParentCid={directRepliesByParentCid}
|
directRepliesByParentCid={directRepliesByParentCid}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -567,6 +567,7 @@ const PostMobile = ({
|
|||||||
isPublishing,
|
isPublishing,
|
||||||
onApprove,
|
onApprove,
|
||||||
onReject,
|
onReject,
|
||||||
|
onRemoveFromModQueue,
|
||||||
}: PostProps) => {
|
}: PostProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const resolvedPost = withResolvedCommentCommunityAddress(post);
|
const resolvedPost = withResolvedCommentCommunityAddress(post);
|
||||||
@@ -583,6 +584,11 @@ const PostMobile = ({
|
|||||||
const modQueueThreadRoute = getModQueueCommentRoute(boardPath, resolvedPost?.cid);
|
const modQueueThreadRoute = getModQueueCommentRoute(boardPath, resolvedPost?.cid);
|
||||||
const modQueueThreadRouteState = getQueuedCommentRouteState(resolvedPost);
|
const modQueueThreadRouteState = getQueuedCommentRouteState(resolvedPost);
|
||||||
const modQueueErrorMessage = formatErrorForDisplay(modQueueError);
|
const modQueueErrorMessage = formatErrorForDisplay(modQueueError);
|
||||||
|
const modQueueRemoveButton = onRemoveFromModQueue ? (
|
||||||
|
<button className={`button ${styles.rejectButton}`} onClick={onRemoveFromModQueue} disabled={isPublishing}>
|
||||||
|
{t('remove')}
|
||||||
|
</button>
|
||||||
|
) : null;
|
||||||
const linksCount = useCountLinksInReplies(resolvedPost);
|
const linksCount = useCountLinksInReplies(resolvedPost);
|
||||||
const hasReplyPaginationOverride = !!replyPaginationOverride;
|
const hasReplyPaginationOverride = !!replyPaginationOverride;
|
||||||
const shouldFetchReplies = showReplies && !isModQueue && !hasReplyPaginationOverride;
|
const shouldFetchReplies = showReplies && !isModQueue && !hasReplyPaginationOverride;
|
||||||
@@ -850,9 +856,15 @@ const PostMobile = ({
|
|||||||
{isModQueue ? (
|
{isModQueue ? (
|
||||||
<div className={styles.modQueueActions}>
|
<div className={styles.modQueueActions}>
|
||||||
{modQueueStatus === 'approved' ? (
|
{modQueueStatus === 'approved' ? (
|
||||||
|
<>
|
||||||
<span className={styles.modQueueStatusApproved}>{t('approved')}</span>
|
<span className={styles.modQueueStatusApproved}>{t('approved')}</span>
|
||||||
|
{modQueueRemoveButton}
|
||||||
|
</>
|
||||||
) : modQueueStatus === 'rejected' ? (
|
) : modQueueStatus === 'rejected' ? (
|
||||||
|
<>
|
||||||
<span className={styles.modQueueStatusRejected}>{t('rejected')}</span>
|
<span className={styles.modQueueStatusRejected}>{t('rejected')}</span>
|
||||||
|
{modQueueRemoveButton}
|
||||||
|
</>
|
||||||
) : modQueueStatus === 'failed' ? (
|
) : modQueueStatus === 'failed' ? (
|
||||||
<span className={styles.modQueueStatusRejected} title={modQueueErrorMessage}>
|
<span className={styles.modQueueStatusRejected} title={modQueueErrorMessage}>
|
||||||
{t('failed')}
|
{t('failed')}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest';
|
|||||||
import { filterVisibleModQueueFeed, getModQueueCommentRoute, getQueuedCommentRouteState } from '../mod-queue-utils';
|
import { filterVisibleModQueueFeed, getModQueueCommentRoute, getQueuedCommentRouteState } from '../mod-queue-utils';
|
||||||
|
|
||||||
describe('mod queue utils', () => {
|
describe('mod queue utils', () => {
|
||||||
it('keeps only comments still awaiting approval', () => {
|
it('keeps all queue comments unless they were locally dismissed', () => {
|
||||||
const feed = [
|
const feed = [
|
||||||
{ cid: 'pending', communityAddress: 'tech.eth', pendingApproval: true },
|
{ cid: 'pending', communityAddress: 'tech.eth', pendingApproval: true },
|
||||||
{ cid: 'approved', approved: true, communityAddress: 'tech.eth', pendingApproval: true },
|
{ cid: 'approved', approved: true, communityAddress: 'tech.eth', pendingApproval: true },
|
||||||
@@ -11,17 +11,24 @@ describe('mod queue utils', () => {
|
|||||||
{ cid: 'published', communityAddress: 'tech.eth', pendingApproval: false },
|
{ cid: 'published', communityAddress: 'tech.eth', pendingApproval: false },
|
||||||
];
|
];
|
||||||
|
|
||||||
expect(filterVisibleModQueueFeed(feed, null)).toEqual([{ cid: 'pending', communityAddress: 'tech.eth', pendingApproval: true }]);
|
expect(filterVisibleModQueueFeed(feed, null, new Set(['approved']))).toEqual([
|
||||||
|
{ cid: 'pending', communityAddress: 'tech.eth', pendingApproval: true },
|
||||||
|
{ cid: 'rejected', approved: false, communityAddress: 'tech.eth', pendingApproval: true },
|
||||||
|
{ cid: 'removed', communityAddress: 'tech.eth', pendingApproval: true, removed: true },
|
||||||
|
{ cid: 'published', communityAddress: 'tech.eth', pendingApproval: false },
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('applies the selected board filter after removing non-pending items', () => {
|
it('applies the selected board filter after removing dismissed items', () => {
|
||||||
const feed = [
|
const feed = [
|
||||||
{ cid: 'tech-pending', communityAddress: 'tech.eth', pendingApproval: true },
|
{ cid: 'tech-pending', communityAddress: 'tech.eth', pendingApproval: true },
|
||||||
{ cid: 'g-pending', communityAddress: 'g.eth', pendingApproval: true },
|
{ cid: 'g-pending', communityAddress: 'g.eth', pendingApproval: true },
|
||||||
{ cid: 'tech-approved', approved: true, communityAddress: 'tech.eth', pendingApproval: true },
|
{ cid: 'tech-approved', approved: true, communityAddress: 'tech.eth', pendingApproval: true },
|
||||||
];
|
];
|
||||||
|
|
||||||
expect(filterVisibleModQueueFeed(feed, 'tech.eth')).toEqual([{ cid: 'tech-pending', communityAddress: 'tech.eth', pendingApproval: true }]);
|
expect(filterVisibleModQueueFeed(feed, 'tech.eth', new Set(['tech-pending']))).toEqual([
|
||||||
|
{ cid: 'tech-approved', approved: true, communityAddress: 'tech.eth', pendingApproval: true },
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('builds excerpt routes from the comment permalink cid', () => {
|
it('builds excerpt routes from the comment permalink cid', () => {
|
||||||
|
|||||||
@@ -11,8 +11,9 @@ describe('pending approval moderation utils', () => {
|
|||||||
expect('removed' in rejectPendingCommentModeration).toBe(false);
|
expect('removed' in rejectPendingCommentModeration).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('treats pending approved=false as rejected for display', () => {
|
it('treats approved=false as rejected for display after pkc removes pendingApproval', () => {
|
||||||
expect(isPendingApprovalRejected({ pendingApproval: true, approved: false })).toBe(true);
|
expect(isPendingApprovalRejected({ pendingApproval: true, approved: false })).toBe(true);
|
||||||
|
expect(isPendingApprovalRejected({ pendingApproval: false, approved: false })).toBe(true);
|
||||||
expect(isPendingApprovalAwaiting({ pendingApproval: true, approved: false })).toBe(false);
|
expect(isPendingApprovalAwaiting({ pendingApproval: true, approved: false })).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { Comment } from '@bitsocialnet/bitsocial-react-hooks';
|
import type { Comment } from '@bitsocialnet/bitsocial-react-hooks';
|
||||||
import { getCommentCommunityAddress } from './comment-utils';
|
import { getCommentCommunityAddress } from './comment-utils';
|
||||||
import { isPendingApprovalAwaiting } from './pending-approval-moderation';
|
|
||||||
import { getThreadTopNavigationState } from './thread-scroll-utils';
|
import { getThreadTopNavigationState } from './thread-scroll-utils';
|
||||||
|
|
||||||
type ModQueueCommentLike = {
|
type ModQueueCommentLike = {
|
||||||
@@ -27,9 +26,14 @@ type ModQueueCommentLike = {
|
|||||||
title?: Comment['title'];
|
title?: Comment['title'];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const emptyDismissedCommentCids = new Set<string>();
|
||||||
|
|
||||||
export type QueuedCommentRouteState = {
|
export type QueuedCommentRouteState = {
|
||||||
scrollThreadContainerCid?: string;
|
scrollThreadContainerCid?: string;
|
||||||
queuedComment?: {
|
queuedComment?: QueuedCommentSnapshot;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type QueuedCommentSnapshot = {
|
||||||
approved?: Comment['approved'];
|
approved?: Comment['approved'];
|
||||||
author?: Comment['author'];
|
author?: Comment['author'];
|
||||||
cid?: Comment['cid'];
|
cid?: Comment['cid'];
|
||||||
@@ -52,19 +56,16 @@ export type QueuedCommentRouteState = {
|
|||||||
timestamp?: Comment['timestamp'];
|
timestamp?: Comment['timestamp'];
|
||||||
title?: Comment['title'];
|
title?: Comment['title'];
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
export const getModQueueCommentRoute = (boardPath: string | undefined, commentCid: string | undefined): string | undefined =>
|
export const getModQueueCommentRoute = (boardPath: string | undefined, commentCid: string | undefined): string | undefined =>
|
||||||
boardPath && commentCid ? `/${boardPath}/thread/${commentCid}` : undefined;
|
boardPath && commentCid ? `/${boardPath}/thread/${commentCid}` : undefined;
|
||||||
|
|
||||||
export const getQueuedCommentRouteState = (comment: ModQueueCommentLike | undefined): QueuedCommentRouteState | undefined => {
|
export const getQueuedCommentSnapshot = (comment: ModQueueCommentLike | undefined): QueuedCommentSnapshot | undefined => {
|
||||||
if (!comment?.cid) {
|
if (!comment?.cid) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...(comment.parentCid ? {} : getThreadTopNavigationState(comment.cid)),
|
|
||||||
queuedComment: {
|
|
||||||
approved: comment.approved,
|
approved: comment.approved,
|
||||||
author: comment.author,
|
author: comment.author,
|
||||||
cid: comment.cid,
|
cid: comment.cid,
|
||||||
@@ -86,13 +87,28 @@ export const getQueuedCommentRouteState = (comment: ModQueueCommentLike | undefi
|
|||||||
thumbnailUrl: comment.thumbnailUrl,
|
thumbnailUrl: comment.thumbnailUrl,
|
||||||
timestamp: comment.timestamp,
|
timestamp: comment.timestamp,
|
||||||
title: comment.title,
|
title: comment.title,
|
||||||
},
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const filterVisibleModQueueFeed = <T extends ModQueueCommentLike>(feed: T[], selectedBoardFilter: string | null): T[] =>
|
export const getQueuedCommentRouteState = (comment: ModQueueCommentLike | undefined): QueuedCommentRouteState | undefined => {
|
||||||
|
const queuedComment = getQueuedCommentSnapshot(comment);
|
||||||
|
if (!queuedComment) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...(queuedComment.parentCid ? {} : getThreadTopNavigationState(queuedComment.cid)),
|
||||||
|
queuedComment,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const filterVisibleModQueueFeed = <T extends ModQueueCommentLike>(
|
||||||
|
feed: T[],
|
||||||
|
selectedBoardFilter: string | null,
|
||||||
|
dismissedCommentCids: ReadonlySet<string> = emptyDismissedCommentCids,
|
||||||
|
): T[] =>
|
||||||
feed.filter((comment) => {
|
feed.filter((comment) => {
|
||||||
if (!isPendingApprovalAwaiting(comment)) {
|
if (comment.cid && dismissedCommentCids.has(comment.cid)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,7 @@ type PendingApprovalDisplayState = {
|
|||||||
pendingApproval?: boolean;
|
pendingApproval?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isPendingApprovalRejected = (comment?: PendingApprovalDisplayState) =>
|
export const isPendingApprovalRejected = (comment?: PendingApprovalDisplayState) => comment?.removed === true || comment?.approved === false;
|
||||||
comment?.removed === true || (comment?.pendingApproval === true && comment?.approved === false);
|
|
||||||
|
|
||||||
export const isPendingApprovalAwaiting = (comment?: PendingApprovalDisplayState) =>
|
export const isPendingApprovalAwaiting = (comment?: PendingApprovalDisplayState) =>
|
||||||
comment?.pendingApproval === true && comment?.approved !== true && !isPendingApprovalRejected(comment);
|
comment?.pendingApproval === true && comment?.approved !== true && !isPendingApprovalRejected(comment);
|
||||||
|
|||||||
@@ -75,18 +75,29 @@ describe('persisted extra stores', () => {
|
|||||||
expect(useModQueueStore.getState()).toMatchObject({
|
expect(useModQueueStore.getState()).toMatchObject({
|
||||||
alertThresholdValue: 3,
|
alertThresholdValue: 3,
|
||||||
alertThresholdUnit: 'hours',
|
alertThresholdUnit: 'hours',
|
||||||
|
dismissedCommentCids: [],
|
||||||
|
queuedCommentHistory: [],
|
||||||
selectedBoardFilter: 'music.eth',
|
selectedBoardFilter: 'music.eth',
|
||||||
viewMode: 'feed',
|
viewMode: 'feed',
|
||||||
});
|
});
|
||||||
expect(useModQueueStore.getState().getAlertThresholdSeconds()).toBe(10_800);
|
expect(useModQueueStore.getState().getAlertThresholdSeconds()).toBe(10_800);
|
||||||
|
|
||||||
useModQueueStore.getState().setAlertThreshold(15, 'minutes');
|
useModQueueStore.getState().setAlertThreshold(15, 'minutes');
|
||||||
|
useModQueueStore.getState().dismissCommentFromQueue('approved-cid');
|
||||||
|
useModQueueStore.getState().dismissCommentFromQueue('approved-cid');
|
||||||
|
useModQueueStore.getState().rememberCommentsInQueue([{ cid: 'approved-cid', approved: true, content: 'approved body' }]);
|
||||||
|
useModQueueStore.getState().rememberCommentsInQueue([{ cid: 'rejected-cid', approved: false, content: 'rejected body' }]);
|
||||||
useModQueueStore.getState().setSelectedBoardFilter('tech.eth');
|
useModQueueStore.getState().setSelectedBoardFilter('tech.eth');
|
||||||
useModQueueStore.getState().setViewMode('compact');
|
useModQueueStore.getState().setViewMode('compact');
|
||||||
|
|
||||||
expect(useModQueueStore.getState()).toMatchObject({
|
expect(useModQueueStore.getState()).toMatchObject({
|
||||||
alertThresholdValue: 15,
|
alertThresholdValue: 15,
|
||||||
alertThresholdUnit: 'minutes',
|
alertThresholdUnit: 'minutes',
|
||||||
|
dismissedCommentCids: ['approved-cid'],
|
||||||
|
queuedCommentHistory: [
|
||||||
|
{ cid: 'rejected-cid', approved: false, content: 'rejected body' },
|
||||||
|
{ cid: 'approved-cid', approved: true, content: 'approved body' },
|
||||||
|
],
|
||||||
selectedBoardFilter: 'tech.eth',
|
selectedBoardFilter: 'tech.eth',
|
||||||
viewMode: 'compact',
|
viewMode: 'compact',
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,14 +1,20 @@
|
|||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
import { persist } from 'zustand/middleware';
|
import { persist } from 'zustand/middleware';
|
||||||
|
import type { QueuedCommentSnapshot } from '../lib/utils/mod-queue-utils';
|
||||||
|
|
||||||
type AlertThresholdUnit = 'hours' | 'minutes';
|
type AlertThresholdUnit = 'hours' | 'minutes';
|
||||||
type ModQueueViewMode = 'compact' | 'feed';
|
type ModQueueViewMode = 'compact' | 'feed';
|
||||||
|
const MAX_QUEUE_HISTORY_COMMENTS = 500;
|
||||||
|
|
||||||
interface ModQueueState {
|
interface ModQueueState {
|
||||||
alertThresholdValue: number;
|
alertThresholdValue: number;
|
||||||
alertThresholdUnit: AlertThresholdUnit;
|
alertThresholdUnit: AlertThresholdUnit;
|
||||||
|
dismissedCommentCids: string[];
|
||||||
|
queuedCommentHistory: QueuedCommentSnapshot[];
|
||||||
selectedBoardFilter: string | null;
|
selectedBoardFilter: string | null;
|
||||||
viewMode: ModQueueViewMode;
|
viewMode: ModQueueViewMode;
|
||||||
|
dismissCommentFromQueue: (cid: string) => void;
|
||||||
|
rememberCommentsInQueue: (comments: QueuedCommentSnapshot[]) => void;
|
||||||
setAlertThreshold: (value: number, unit: AlertThresholdUnit) => void;
|
setAlertThreshold: (value: number, unit: AlertThresholdUnit) => void;
|
||||||
setSelectedBoardFilter: (boardAddress: string | null) => void;
|
setSelectedBoardFilter: (boardAddress: string | null) => void;
|
||||||
setViewMode: (viewMode: ModQueueViewMode) => void;
|
setViewMode: (viewMode: ModQueueViewMode) => void;
|
||||||
@@ -21,18 +27,48 @@ interface OldPersistedState {
|
|||||||
alertThresholdHours?: number;
|
alertThresholdHours?: number;
|
||||||
alertThresholdValue?: number;
|
alertThresholdValue?: number;
|
||||||
alertThresholdUnit?: AlertThresholdUnit;
|
alertThresholdUnit?: AlertThresholdUnit;
|
||||||
|
dismissedCommentCids?: string[];
|
||||||
|
queuedCommentHistory?: QueuedCommentSnapshot[];
|
||||||
selectedBoardFilter?: string | null;
|
selectedBoardFilter?: string | null;
|
||||||
viewMode?: ModQueueViewMode;
|
viewMode?: ModQueueViewMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Type for persisted data (without methods)
|
// Type for persisted data (without methods)
|
||||||
type PersistedModQueueData = Pick<ModQueueState, 'alertThresholdValue' | 'alertThresholdUnit' | 'selectedBoardFilter' | 'viewMode'>;
|
type PersistedModQueueData = Pick<
|
||||||
|
ModQueueState,
|
||||||
|
'alertThresholdValue' | 'alertThresholdUnit' | 'dismissedCommentCids' | 'queuedCommentHistory' | 'selectedBoardFilter' | 'viewMode'
|
||||||
|
>;
|
||||||
|
|
||||||
const useModQueueStore = create<ModQueueState>()(
|
const useModQueueStore = create<ModQueueState>()(
|
||||||
persist(
|
persist(
|
||||||
(set, get) => ({
|
(set, get) => ({
|
||||||
alertThresholdValue: 6,
|
alertThresholdValue: 6,
|
||||||
alertThresholdUnit: 'hours' as AlertThresholdUnit,
|
alertThresholdUnit: 'hours' as AlertThresholdUnit,
|
||||||
|
dismissedCommentCids: [],
|
||||||
|
queuedCommentHistory: [],
|
||||||
|
dismissCommentFromQueue: (cid) =>
|
||||||
|
set((state) => {
|
||||||
|
if (state.dismissedCommentCids.includes(cid)) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
return { dismissedCommentCids: [...state.dismissedCommentCids, cid] };
|
||||||
|
}),
|
||||||
|
rememberCommentsInQueue: (comments) =>
|
||||||
|
set((state) => {
|
||||||
|
const rememberedByCid = new Map<string, QueuedCommentSnapshot>();
|
||||||
|
for (const comment of comments) {
|
||||||
|
if (comment.cid) {
|
||||||
|
rememberedByCid.set(comment.cid, comment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const comment of state.queuedCommentHistory) {
|
||||||
|
if (comment.cid && !rememberedByCid.has(comment.cid)) {
|
||||||
|
rememberedByCid.set(comment.cid, comment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { queuedCommentHistory: [...rememberedByCid.values()].slice(0, MAX_QUEUE_HISTORY_COMMENTS) };
|
||||||
|
}),
|
||||||
selectedBoardFilter: null,
|
selectedBoardFilter: null,
|
||||||
viewMode: 'compact',
|
viewMode: 'compact',
|
||||||
setAlertThreshold: (value, unit) => set({ alertThresholdValue: value, alertThresholdUnit: unit }),
|
setAlertThreshold: (value, unit) => set({ alertThresholdValue: value, alertThresholdUnit: unit }),
|
||||||
@@ -53,6 +89,8 @@ const useModQueueStore = create<ModQueueState>()(
|
|||||||
const migrated: PersistedModQueueData = {
|
const migrated: PersistedModQueueData = {
|
||||||
alertThresholdValue: state.alertThresholdHours,
|
alertThresholdValue: state.alertThresholdHours,
|
||||||
alertThresholdUnit: 'hours' as AlertThresholdUnit,
|
alertThresholdUnit: 'hours' as AlertThresholdUnit,
|
||||||
|
dismissedCommentCids: state.dismissedCommentCids ?? [],
|
||||||
|
queuedCommentHistory: state.queuedCommentHistory ?? [],
|
||||||
selectedBoardFilter: state.selectedBoardFilter ?? null,
|
selectedBoardFilter: state.selectedBoardFilter ?? null,
|
||||||
viewMode: state.viewMode ?? 'compact',
|
viewMode: state.viewMode ?? 'compact',
|
||||||
};
|
};
|
||||||
@@ -63,6 +101,8 @@ const useModQueueStore = create<ModQueueState>()(
|
|||||||
const current: PersistedModQueueData = {
|
const current: PersistedModQueueData = {
|
||||||
alertThresholdValue: state.alertThresholdValue ?? 6,
|
alertThresholdValue: state.alertThresholdValue ?? 6,
|
||||||
alertThresholdUnit: state.alertThresholdUnit ?? 'hours',
|
alertThresholdUnit: state.alertThresholdUnit ?? 'hours',
|
||||||
|
dismissedCommentCids: state.dismissedCommentCids ?? [],
|
||||||
|
queuedCommentHistory: state.queuedCommentHistory ?? [],
|
||||||
selectedBoardFilter: state.selectedBoardFilter ?? null,
|
selectedBoardFilter: state.selectedBoardFilter ?? null,
|
||||||
viewMode: state.viewMode ?? 'compact',
|
viewMode: state.viewMode ?? 'compact',
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -351,6 +351,11 @@
|
|||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cardRemoveButton {
|
||||||
|
background-color: #777;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
/* Desktop: buttons styled as text links (same as board buttons) */
|
/* Desktop: buttons styled as text links (same as board buttons) */
|
||||||
@media (min-width: 640px) {
|
@media (min-width: 640px) {
|
||||||
.actions {
|
.actions {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import useChallengesStore from '../../stores/use-challenges-store';
|
|||||||
import { alertChallengeVerificationFailed } from '../../lib/utils/challenge-utils';
|
import { alertChallengeVerificationFailed } from '../../lib/utils/challenge-utils';
|
||||||
import { getCommentCommunityAddress } from '../../lib/utils/comment-utils';
|
import { getCommentCommunityAddress } from '../../lib/utils/comment-utils';
|
||||||
import { formatErrorForDisplay } from '../../lib/utils/error-utils';
|
import { formatErrorForDisplay } from '../../lib/utils/error-utils';
|
||||||
import { filterVisibleModQueueFeed, getModQueueCommentRoute, getQueuedCommentRouteState } from '../../lib/utils/mod-queue-utils';
|
import { filterVisibleModQueueFeed, getModQueueCommentRoute, getQueuedCommentRouteState, getQueuedCommentSnapshot } from '../../lib/utils/mod-queue-utils';
|
||||||
import Tooltip from '../../components/tooltip';
|
import Tooltip from '../../components/tooltip';
|
||||||
import { useAccountCommunityAddresses } from '../../hooks/use-account-community-addresses';
|
import { useAccountCommunityAddresses } from '../../hooks/use-account-community-addresses';
|
||||||
import { useCommunityIdentifier, useCommunityIdentifiers } from '../../hooks/use-community-identifiers';
|
import { useCommunityIdentifier, useCommunityIdentifiers } from '../../hooks/use-community-identifiers';
|
||||||
@@ -90,6 +90,7 @@ interface ModQueueActionState {
|
|||||||
isPublishing: boolean;
|
isPublishing: boolean;
|
||||||
handleApprove: () => Promise<void>;
|
handleApprove: () => Promise<void>;
|
||||||
handleReject: () => Promise<void>;
|
handleReject: () => Promise<void>;
|
||||||
|
handleRemove?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ModQueueActionsProps {
|
interface ModQueueActionsProps {
|
||||||
@@ -99,20 +100,55 @@ interface ModQueueActionsProps {
|
|||||||
isPublishing: boolean;
|
isPublishing: boolean;
|
||||||
handleApprove: () => Promise<void>;
|
handleApprove: () => Promise<void>;
|
||||||
handleReject: () => Promise<void>;
|
handleReject: () => Promise<void>;
|
||||||
|
handleRemove?: () => void;
|
||||||
variant: 'row' | 'card';
|
variant: 'row' | 'card';
|
||||||
}
|
}
|
||||||
|
|
||||||
const ModQueueActions = ({ status, error, errorMessage, isPublishing, handleApprove, handleReject, variant }: ModQueueActionsProps) => {
|
const ModQueueActions = ({ status, error, errorMessage, isPublishing, handleApprove, handleReject, handleRemove, variant }: ModQueueActionsProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const displayError = error || errorMessage;
|
const displayError = error || errorMessage;
|
||||||
|
|
||||||
|
const removeButton = handleRemove ? (
|
||||||
|
variant === 'row' ? (
|
||||||
|
<span className={styles.buttonWrapper}>
|
||||||
|
[
|
||||||
|
<button className={styles.button} onClick={handleRemove} disabled={isPublishing}>
|
||||||
|
{t('remove')}
|
||||||
|
</button>
|
||||||
|
]
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<button className={`button ${styles.cardRemoveButton}`} onClick={handleRemove} disabled={isPublishing}>
|
||||||
|
{t('remove')}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
) : null;
|
||||||
|
|
||||||
if (status === 'approved') {
|
if (status === 'approved') {
|
||||||
const content = <span className={`${styles.button} ${styles.approve}`}>{t('approved')}</span>;
|
const content = <span className={`${styles.button} ${styles.approve}`}>{t('approved')}</span>;
|
||||||
return variant === 'card' ? <div className={styles.cardActions}>{content}</div> : content;
|
return variant === 'card' ? (
|
||||||
|
<div className={styles.cardActions}>
|
||||||
|
{content}
|
||||||
|
{removeButton}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<span className={styles.actionButtons}>
|
||||||
|
{content} {removeButton}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (status === 'rejected') {
|
if (status === 'rejected') {
|
||||||
const content = <span className={`${styles.button} ${styles.reject}`}>{t('rejected')}</span>;
|
const content = <span className={`${styles.button} ${styles.reject}`}>{t('rejected')}</span>;
|
||||||
return variant === 'card' ? <div className={styles.cardActions}>{content}</div> : content;
|
return variant === 'card' ? (
|
||||||
|
<div className={styles.cardActions}>
|
||||||
|
{content}
|
||||||
|
{removeButton}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<span className={styles.actionButtons}>
|
||||||
|
{content} {removeButton}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (status === 'failed') {
|
if (status === 'failed') {
|
||||||
const content = displayError ? (
|
const content = displayError ? (
|
||||||
@@ -163,6 +199,8 @@ const useModQueueActions = (comment: Comment): ModQueueActionState => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { cid, approved, removed, pendingApproval } = comment || {};
|
const { cid, approved, removed, pendingApproval } = comment || {};
|
||||||
const communityAddress = getCommentCommunityAddress(comment);
|
const communityAddress = getCommentCommunityAddress(comment);
|
||||||
|
const dismissCommentFromQueue = useModQueueStore((state) => state.dismissCommentFromQueue);
|
||||||
|
const rememberCommentsInQueue = useModQueueStore((state) => state.rememberCommentsInQueue);
|
||||||
const [initiatedAction, setInitiatedAction] = useState<ModerationAction>(null);
|
const [initiatedAction, setInitiatedAction] = useState<ModerationAction>(null);
|
||||||
|
|
||||||
const alreadyApproved = approved === true;
|
const alreadyApproved = approved === true;
|
||||||
@@ -206,7 +244,7 @@ const useModQueueActions = (comment: Comment): ModQueueActionState => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleApprove = useCallback(async () => {
|
const handleApprove = async () => {
|
||||||
const confirm = window.confirm(t('double_confirm'));
|
const confirm = window.confirm(t('double_confirm'));
|
||||||
if (!confirm) {
|
if (!confirm) {
|
||||||
return;
|
return;
|
||||||
@@ -215,12 +253,16 @@ const useModQueueActions = (comment: Comment): ModQueueActionState => {
|
|||||||
setInitiatedAction('approve');
|
setInitiatedAction('approve');
|
||||||
try {
|
try {
|
||||||
await approve();
|
await approve();
|
||||||
|
const approvedSnapshot = getQueuedCommentSnapshot({ ...comment, approved: true, pendingApproval: false });
|
||||||
|
if (approvedSnapshot) {
|
||||||
|
rememberCommentsInQueue([approvedSnapshot]);
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
}, [approve, t]);
|
};
|
||||||
|
|
||||||
const handleReject = useCallback(async () => {
|
const handleReject = async () => {
|
||||||
const confirm = window.confirm(t('double_confirm'));
|
const confirm = window.confirm(t('double_confirm'));
|
||||||
if (!confirm) {
|
if (!confirm) {
|
||||||
return;
|
return;
|
||||||
@@ -229,10 +271,20 @@ const useModQueueActions = (comment: Comment): ModQueueActionState => {
|
|||||||
setInitiatedAction('reject');
|
setInitiatedAction('reject');
|
||||||
try {
|
try {
|
||||||
await reject();
|
await reject();
|
||||||
|
const rejectedSnapshot = getQueuedCommentSnapshot({ ...comment, approved: false, pendingApproval: false });
|
||||||
|
if (rejectedSnapshot) {
|
||||||
|
rememberCommentsInQueue([rejectedSnapshot]);
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
}, [reject, t]);
|
};
|
||||||
|
|
||||||
|
const handleRemove = () => {
|
||||||
|
if (cid) {
|
||||||
|
dismissCommentFromQueue(cid);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const isApproving = initiatedAction === 'approve' && approveState !== 'initializing' && approveState !== 'succeeded' && approveState !== 'failed';
|
const isApproving = initiatedAction === 'approve' && approveState !== 'initializing' && approveState !== 'succeeded' && approveState !== 'failed';
|
||||||
const isRejecting = initiatedAction === 'reject' && rejectState !== 'initializing' && rejectState !== 'succeeded' && rejectState !== 'failed';
|
const isRejecting = initiatedAction === 'reject' && rejectState !== 'initializing' && rejectState !== 'succeeded' && rejectState !== 'failed';
|
||||||
@@ -248,7 +300,7 @@ const useModQueueActions = (comment: Comment): ModQueueActionState => {
|
|||||||
const error = approveFailed ? approveError : rejectFailed ? rejectError : undefined;
|
const error = approveFailed ? approveError : rejectFailed ? rejectError : undefined;
|
||||||
const errorMessage = formatErrorForDisplay(error);
|
const errorMessage = formatErrorForDisplay(error);
|
||||||
|
|
||||||
return { status, error, errorMessage, isPublishing, handleApprove, handleReject };
|
return { status, error, errorMessage, isPublishing, handleApprove, handleReject, handleRemove: status ? handleRemove : undefined };
|
||||||
};
|
};
|
||||||
|
|
||||||
const ModQueueRow = memo(({ comment, isOdd = false, showBoard = false, boardPath, boardDisplayPath }: ModQueueRowProps) => {
|
const ModQueueRow = memo(({ comment, isOdd = false, showBoard = false, boardPath, boardDisplayPath }: ModQueueRowProps) => {
|
||||||
@@ -269,7 +321,7 @@ const ModQueueRow = memo(({ comment, isOdd = false, showBoard = false, boardPath
|
|||||||
// 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 = isPendingApprovalAwaiting(comment);
|
const isAwaitingApproval = isPendingApprovalAwaiting(comment);
|
||||||
|
|
||||||
const { status, error, errorMessage, isPublishing, handleApprove, handleReject } = useModQueueActions(comment);
|
const { status, error, errorMessage, isPublishing, handleApprove, handleReject, handleRemove } = useModQueueActions(comment);
|
||||||
const hasTitle = title && title.trim().length > 0;
|
const hasTitle = title && title.trim().length > 0;
|
||||||
const hasContent = content && content.trim().length > 0;
|
const hasContent = content && content.trim().length > 0;
|
||||||
const hasLink = link && link.length > 0;
|
const hasLink = link && link.length > 0;
|
||||||
@@ -341,6 +393,7 @@ const ModQueueRow = memo(({ comment, isOdd = false, showBoard = false, boardPath
|
|||||||
isPublishing={isPublishing}
|
isPublishing={isPublishing}
|
||||||
handleApprove={handleApprove}
|
handleApprove={handleApprove}
|
||||||
handleReject={handleReject}
|
handleReject={handleReject}
|
||||||
|
handleRemove={handleRemove}
|
||||||
variant='row'
|
variant='row'
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -373,7 +426,7 @@ const ModQueueCard = memo(({ comment, showBoard = false, boardPath, boardDisplay
|
|||||||
const isOverThreshold = timeWaiting > alertThresholdSeconds;
|
const isOverThreshold = timeWaiting > alertThresholdSeconds;
|
||||||
const isAwaitingApproval = isPendingApprovalAwaiting(comment);
|
const isAwaitingApproval = isPendingApprovalAwaiting(comment);
|
||||||
|
|
||||||
const { status, error, errorMessage, isPublishing, handleApprove, handleReject } = useModQueueActions(comment);
|
const { status, error, errorMessage, isPublishing, handleApprove, handleReject, handleRemove } = useModQueueActions(comment);
|
||||||
const hasTitle = title && title.trim().length > 0;
|
const hasTitle = title && title.trim().length > 0;
|
||||||
const hasContent = content && content.trim().length > 0;
|
const hasContent = content && content.trim().length > 0;
|
||||||
const hasLink = link && link.length > 0;
|
const hasLink = link && link.length > 0;
|
||||||
@@ -434,6 +487,7 @@ const ModQueueCard = memo(({ comment, showBoard = false, boardPath, boardDisplay
|
|||||||
isPublishing={isPublishing}
|
isPublishing={isPublishing}
|
||||||
handleApprove={handleApprove}
|
handleApprove={handleApprove}
|
||||||
handleReject={handleReject}
|
handleReject={handleReject}
|
||||||
|
handleRemove={handleRemove}
|
||||||
variant='card'
|
variant='card'
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -444,7 +498,7 @@ ModQueueCard.displayName = 'ModQueueCard';
|
|||||||
const ModQueueFeedPost = ({ comment }: { comment: Comment }) => {
|
const ModQueueFeedPost = ({ comment }: { comment: Comment }) => {
|
||||||
const { editedComment } = useEditedComment({ comment });
|
const { editedComment } = useEditedComment({ comment });
|
||||||
const displayComment = editedComment || comment;
|
const displayComment = editedComment || comment;
|
||||||
const { status, error, errorMessage, isPublishing, handleApprove, handleReject } = useModQueueActions(comment);
|
const { status, error, errorMessage, isPublishing, handleApprove, handleReject, handleRemove } = useModQueueActions(comment);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Post
|
<Post
|
||||||
@@ -457,6 +511,7 @@ const ModQueueFeedPost = ({ comment }: { comment: Comment }) => {
|
|||||||
isPublishing={isPublishing}
|
isPublishing={isPublishing}
|
||||||
onApprove={handleApprove}
|
onApprove={handleApprove}
|
||||||
onReject={handleReject}
|
onReject={handleReject}
|
||||||
|
onRemoveFromModQueue={handleRemove}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -787,7 +842,7 @@ export const ModQueueButton = ({ boardIdentifier, isMobile }: ModQueueButtonProp
|
|||||||
const ModQueueView = ({ boardIdentifier: propBoardIdentifier }: ModQueueViewProps) => {
|
const ModQueueView = ({ boardIdentifier: propBoardIdentifier }: ModQueueViewProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const { selectedBoardFilter, viewMode } = useModQueueStore();
|
const { selectedBoardFilter, viewMode, dismissedCommentCids, queuedCommentHistory, rememberCommentsInQueue } = useModQueueStore();
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
|
|
||||||
const accountCommunityAddresses = useAccountCommunityAddresses();
|
const accountCommunityAddresses = useAccountCommunityAddresses();
|
||||||
@@ -824,7 +879,41 @@ const ModQueueView = ({ boardIdentifier: propBoardIdentifier }: ModQueueViewProp
|
|||||||
);
|
);
|
||||||
const { feed, hasMore, loadMore, reset } = useFeed(feedOptions);
|
const { feed, hasMore, loadMore, reset } = useFeed(feedOptions);
|
||||||
|
|
||||||
const filteredFeed = useMemo(() => filterVisibleModQueueFeed(feed, selectedBoardFilter), [feed, selectedBoardFilter]);
|
const queuedCommentSnapshots = useMemo(
|
||||||
|
() =>
|
||||||
|
feed.flatMap((comment) => {
|
||||||
|
const snapshot = getQueuedCommentSnapshot(comment);
|
||||||
|
return snapshot ? [snapshot] : [];
|
||||||
|
}),
|
||||||
|
[feed],
|
||||||
|
);
|
||||||
|
useEffect(() => {
|
||||||
|
if (queuedCommentSnapshots.length > 0) {
|
||||||
|
rememberCommentsInQueue(queuedCommentSnapshots);
|
||||||
|
}
|
||||||
|
}, [queuedCommentSnapshots, rememberCommentsInQueue]);
|
||||||
|
|
||||||
|
const feedWithHistory = useMemo(() => {
|
||||||
|
const liveCids = new Set(feed.map((comment) => comment.cid).filter(Boolean));
|
||||||
|
return [
|
||||||
|
...feed,
|
||||||
|
...(queuedCommentHistory.filter((comment) => {
|
||||||
|
const commentCommunityAddress = getCommentCommunityAddress(comment);
|
||||||
|
return (
|
||||||
|
comment.cid &&
|
||||||
|
!liveCids.has(comment.cid) &&
|
||||||
|
commentCommunityAddress &&
|
||||||
|
communityAddresses.some((communityAddress) => areSameBoardAddress(communityAddress, commentCommunityAddress))
|
||||||
|
);
|
||||||
|
}) as Comment[]),
|
||||||
|
];
|
||||||
|
}, [communityAddresses, feed, queuedCommentHistory]);
|
||||||
|
|
||||||
|
const dismissedCommentCidSet = useMemo(() => new Set(dismissedCommentCids), [dismissedCommentCids]);
|
||||||
|
const filteredFeed = useMemo(
|
||||||
|
() => filterVisibleModQueueFeed(feedWithHistory, selectedBoardFilter, dismissedCommentCidSet),
|
||||||
|
[feedWithHistory, selectedBoardFilter, dismissedCommentCidSet],
|
||||||
|
);
|
||||||
|
|
||||||
const addressToPathMap = useMemo(() => {
|
const addressToPathMap = useMemo(() => {
|
||||||
const map = new Map<string, string>();
|
const map = new Map<string, string>();
|
||||||
|
|||||||
@@ -150,6 +150,7 @@ export interface PostProps {
|
|||||||
isPublishing?: boolean;
|
isPublishing?: boolean;
|
||||||
onApprove?: () => void;
|
onApprove?: () => void;
|
||||||
onReject?: () => void;
|
onReject?: () => void;
|
||||||
|
onRemoveFromModQueue?: () => void;
|
||||||
quotedByMap?: Map<string, Comment[]>;
|
quotedByMap?: Map<string, Comment[]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,6 +166,7 @@ export const Post = memo(
|
|||||||
isPublishing,
|
isPublishing,
|
||||||
onApprove,
|
onApprove,
|
||||||
onReject,
|
onReject,
|
||||||
|
onRemoveFromModQueue,
|
||||||
feedVirtualizationModeOverride,
|
feedVirtualizationModeOverride,
|
||||||
replyPaginationOverride,
|
replyPaginationOverride,
|
||||||
replyVirtualizationModeOverride,
|
replyVirtualizationModeOverride,
|
||||||
@@ -199,6 +201,7 @@ export const Post = memo(
|
|||||||
isPublishing={isPublishing}
|
isPublishing={isPublishing}
|
||||||
onApprove={onApprove}
|
onApprove={onApprove}
|
||||||
onReject={onReject}
|
onReject={onReject}
|
||||||
|
onRemoveFromModQueue={onRemoveFromModQueue}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<PostDesktop
|
<PostDesktop
|
||||||
@@ -216,6 +219,7 @@ export const Post = memo(
|
|||||||
isPublishing={isPublishing}
|
isPublishing={isPublishing}
|
||||||
onApprove={onApprove}
|
onApprove={onApprove}
|
||||||
onReject={onReject}
|
onReject={onReject}
|
||||||
|
onRemoveFromModQueue={onRemoveFromModQueue}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -251,7 +255,8 @@ export const Post = memo(
|
|||||||
prevProps.modQueueError === nextProps.modQueueError &&
|
prevProps.modQueueError === nextProps.modQueueError &&
|
||||||
prevProps.isPublishing === nextProps.isPublishing &&
|
prevProps.isPublishing === nextProps.isPublishing &&
|
||||||
prevProps.onApprove === nextProps.onApprove &&
|
prevProps.onApprove === nextProps.onApprove &&
|
||||||
prevProps.onReject === nextProps.onReject
|
prevProps.onReject === nextProps.onReject &&
|
||||||
|
prevProps.onRemoveFromModQueue === nextProps.onRemoveFromModQueue
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user