mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
fix(mod-queue): button counter includes already approved/rejected items
This commit is contained in:
@@ -251,8 +251,106 @@ interface ModQueueButtonProps {
|
|||||||
isMobile?: boolean;
|
isMobile?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ModQueueButton = ({ boardIdentifier, isMobile }: ModQueueButtonProps) => {
|
// Counter item that uses useEditedComment to get real-time moderation status
|
||||||
|
// and reports its status via callback
|
||||||
|
interface ModQueueCountItemProps {
|
||||||
|
comment: Comment;
|
||||||
|
alertThresholdHours: number;
|
||||||
|
onStatusChange: (cid: string, status: { awaiting: boolean; urgent: boolean }) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ModQueueCountItem = ({ comment, alertThresholdHours, onStatusChange }: ModQueueCountItemProps) => {
|
||||||
|
const { editedComment } = useEditedComment({ comment });
|
||||||
|
const displayComment = editedComment || comment;
|
||||||
|
|
||||||
|
const { cid, approved, removed, timestamp } = displayComment;
|
||||||
|
const isAwaiting = approved !== true && removed !== true;
|
||||||
|
const timeWaiting = Date.now() / 1000 - timestamp;
|
||||||
|
const isUrgent = isAwaiting && timeWaiting > alertThresholdHours * 3600;
|
||||||
|
|
||||||
|
// Report status changes to parent - useEffect is appropriate here for syncing with parent state
|
||||||
|
useEffect(() => {
|
||||||
|
onStatusChange(cid, { awaiting: isAwaiting, urgent: isUrgent });
|
||||||
|
}, [cid, isAwaiting, isUrgent, onStatusChange]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Inner component that handles counting with useEditedComment for each item
|
||||||
|
interface ModQueueButtonContentProps {
|
||||||
|
feed: Comment[];
|
||||||
|
alertThresholdHours: number;
|
||||||
|
boardIdentifier?: string;
|
||||||
|
isMobile?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ModQueueButtonContent = ({ feed, alertThresholdHours, boardIdentifier, isMobile }: ModQueueButtonContentProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const [statusMap, setStatusMap] = useState<Map<string, { awaiting: boolean; urgent: boolean }>>(new Map());
|
||||||
|
|
||||||
|
const handleStatusChange = React.useCallback((cid: string, status: { awaiting: boolean; urgent: boolean }) => {
|
||||||
|
setStatusMap((prev) => {
|
||||||
|
const next = new Map(prev);
|
||||||
|
next.set(cid, status);
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Calculate counts from status map
|
||||||
|
const { normalCount, urgentCount } = useMemo(() => {
|
||||||
|
let normal = 0;
|
||||||
|
let urgent = 0;
|
||||||
|
for (const { awaiting, urgent: isUrgent } of statusMap.values()) {
|
||||||
|
if (awaiting) {
|
||||||
|
if (isUrgent) urgent++;
|
||||||
|
else normal++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { normalCount: normal, urgentCount: urgent };
|
||||||
|
}, [statusMap]);
|
||||||
|
|
||||||
|
const totalCount = normalCount + urgentCount;
|
||||||
|
const to = boardIdentifier ? `/${boardIdentifier}/queue` : '/mod/queue';
|
||||||
|
|
||||||
|
const buttonContent = (
|
||||||
|
<button className='button'>
|
||||||
|
<Link to={to}>
|
||||||
|
{t('mod_queue')}
|
||||||
|
{totalCount > 0 && (
|
||||||
|
<strong>
|
||||||
|
(
|
||||||
|
{urgentCount > 0 && normalCount > 0 ? (
|
||||||
|
<>
|
||||||
|
<span className={styles.modQueueButtonCount}>{normalCount}</span>
|
||||||
|
<span className={`${styles.modQueueButtonCount} ${styles.modQueueButtonCountAlert}`}>
|
||||||
|
{'+'}
|
||||||
|
{urgentCount}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
) : urgentCount > 0 ? (
|
||||||
|
<span className={`${styles.modQueueButtonCount} ${styles.modQueueButtonCountAlert}`}>{urgentCount}</span>
|
||||||
|
) : (
|
||||||
|
<span className={styles.modQueueButtonCount}>{totalCount}</span>
|
||||||
|
)}
|
||||||
|
)
|
||||||
|
</strong>
|
||||||
|
)}
|
||||||
|
</Link>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* Render counter items to track real-time moderation status */}
|
||||||
|
{feed.map((item) => (
|
||||||
|
<ModQueueCountItem key={item.cid} comment={item} alertThresholdHours={alertThresholdHours} onStatusChange={handleStatusChange} />
|
||||||
|
))}
|
||||||
|
{isMobile ? buttonContent : <>[{buttonContent}]</>}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ModQueueButton = ({ boardIdentifier, isMobile }: ModQueueButtonProps) => {
|
||||||
const { alertThresholdHours } = useModQueueStore();
|
const { alertThresholdHours } = useModQueueStore();
|
||||||
const { accountSubplebbits } = useAccountSubplebbits();
|
const { accountSubplebbits } = useAccountSubplebbits();
|
||||||
const accountSubplebbitAddresses = useMemo(() => Object.keys(accountSubplebbits || {}), [accountSubplebbits]);
|
const accountSubplebbitAddresses = useMemo(() => Object.keys(accountSubplebbits || {}), [accountSubplebbits]);
|
||||||
@@ -289,69 +387,7 @@ export const ModQueueButton = ({ boardIdentifier, isMobile }: ModQueueButtonProp
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Separate comments into normal and urgent based on threshold
|
return <ModQueueButtonContent feed={feed} alertThresholdHours={alertThresholdHours} boardIdentifier={boardIdentifier} isMobile={isMobile} />;
|
||||||
// Only count items awaiting approval (not approved or rejected) for blinking counter
|
|
||||||
// Match ModQueueRow logic: use > (strictly greater) to be consistent
|
|
||||||
const { normalCount, urgentCount } = useMemo(() => {
|
|
||||||
const thresholdSeconds = alertThresholdHours * 3600;
|
|
||||||
const now = Date.now() / 1000;
|
|
||||||
|
|
||||||
let normal = 0;
|
|
||||||
let urgent = 0;
|
|
||||||
|
|
||||||
for (const item of feed) {
|
|
||||||
// Only count items that are awaiting approval (not approved or rejected)
|
|
||||||
const isAwaitingApproval = item.approved !== true && item.removed !== true;
|
|
||||||
|
|
||||||
if (!isAwaitingApproval) {
|
|
||||||
// Skip items that are already approved or rejected
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const timeWaiting = now - item.timestamp;
|
|
||||||
if (timeWaiting > thresholdSeconds) {
|
|
||||||
urgent++;
|
|
||||||
} else {
|
|
||||||
normal++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return { normalCount: normal, urgentCount: urgent };
|
|
||||||
}, [feed, alertThresholdHours]);
|
|
||||||
|
|
||||||
const totalCount = normalCount + urgentCount;
|
|
||||||
|
|
||||||
// Use boardIdentifier for the route, but resolvedAddress for mod check
|
|
||||||
const to = boardIdentifier ? `/${boardIdentifier}/queue` : '/mod/queue';
|
|
||||||
|
|
||||||
const buttonContent = (
|
|
||||||
<button className='button'>
|
|
||||||
<Link to={to}>
|
|
||||||
{t('mod_queue')}
|
|
||||||
{totalCount > 0 && (
|
|
||||||
<strong>
|
|
||||||
(
|
|
||||||
{urgentCount > 0 && normalCount > 0 ? (
|
|
||||||
<>
|
|
||||||
<span className={styles.modQueueButtonCount}>{normalCount}</span>
|
|
||||||
<span className={`${styles.modQueueButtonCount} ${styles.modQueueButtonCountAlert}`}>
|
|
||||||
{'+'}
|
|
||||||
{urgentCount}
|
|
||||||
</span>
|
|
||||||
</>
|
|
||||||
) : urgentCount > 0 ? (
|
|
||||||
<span className={`${styles.modQueueButtonCount} ${styles.modQueueButtonCountAlert}`}>{urgentCount}</span>
|
|
||||||
) : (
|
|
||||||
<span className={styles.modQueueButtonCount}>{totalCount}</span>
|
|
||||||
)}
|
|
||||||
)
|
|
||||||
</strong>
|
|
||||||
)}
|
|
||||||
</Link>
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
|
|
||||||
return isMobile ? buttonContent : <>[{buttonContent}]</>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ModQueueView = ({ boardIdentifier: propBoardIdentifier }: ModQueueViewProps) => {
|
export const ModQueueView = ({ boardIdentifier: propBoardIdentifier }: ModQueueViewProps) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user