mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
perf(board): reduce desktop reverse-scroll jank on /all
Apply the asymmetric viewport buffer that already fixed mobile to desktop
multiboard feeds, and trim per-post mount cost so remounts during scroll-up
are cheaper:
- Desktop multiboard buffer goes from {600,600} to {1200,2400} (top-heavy),
matching the mobile pattern from 99c0bbfac so items stay mounted longer
when scrolling back up
- Cache feed post height estimate by CID in pretext-height-estimates so
remounts skip the Pretext text-measurement work
- Memoize CommentMedia and switch its expanded-media-store reads to atomic
selectors so it stops rerendering on unrelated store changes
- Memoize useCommentMediaInfo return so its reference is stable for memo
comparators downstream
- Stop useFetchGifFirstFrame from forcing an extra render on every post
mount by bailing out of equivalent setState calls
- Extract PendingModerationActions out of PostInfo so the two
usePublishCommentModeration calls only run on the post page when
mod-approval is actually pending, not on every feed item
This commit is contained in:
@@ -94,6 +94,117 @@ const useShowOmittedReplies = create<ShowOmittedRepliesState>((set) => ({
|
||||
})),
|
||||
}));
|
||||
|
||||
const PendingModerationActions = ({ cid, communityAddress, post }: { cid: string; communityAddress: string; post: Comment | undefined }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const {
|
||||
publishCommentModeration: approvePending,
|
||||
state: approvePendingState,
|
||||
error: approvePendingError,
|
||||
} = usePublishCommentModeration({
|
||||
commentCid: cid,
|
||||
communityAddress,
|
||||
commentModeration: approvePendingCommentModeration,
|
||||
onChallenge: async (...args: any) => {
|
||||
addChallenge([...args, post]);
|
||||
},
|
||||
onChallengeVerification: async (challengeVerification, comment) => {
|
||||
alertChallengeVerificationFailed(challengeVerification, comment);
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
console.error('Approve failed:', error);
|
||||
},
|
||||
});
|
||||
|
||||
const {
|
||||
publishCommentModeration: rejectPending,
|
||||
state: rejectPendingState,
|
||||
error: rejectPendingError,
|
||||
} = usePublishCommentModeration({
|
||||
commentCid: cid,
|
||||
communityAddress,
|
||||
commentModeration: rejectPendingCommentModeration,
|
||||
onChallenge: async (...args: any) => {
|
||||
addChallenge([...args, post]);
|
||||
},
|
||||
onChallengeVerification: async (challengeVerification, comment) => {
|
||||
alertChallengeVerificationFailed(challengeVerification, comment);
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
console.error('Reject failed:', error);
|
||||
},
|
||||
});
|
||||
|
||||
const [initiatedPendingAction, setInitiatedPendingAction] = useState<'approve' | 'reject' | null>(null);
|
||||
const handlePendingApprove = useCallback(async () => {
|
||||
if (!window.confirm(t('double_confirm'))) return;
|
||||
setInitiatedPendingAction('approve');
|
||||
try {
|
||||
await approvePending();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}, [approvePending, t]);
|
||||
|
||||
const handlePendingReject = useCallback(async () => {
|
||||
if (!window.confirm(t('double_confirm'))) return;
|
||||
setInitiatedPendingAction('reject');
|
||||
try {
|
||||
await rejectPending();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}, [rejectPending, t]);
|
||||
|
||||
const isApprovingPending =
|
||||
initiatedPendingAction === 'approve' && approvePendingState !== 'initializing' && approvePendingState !== 'succeeded' && approvePendingState !== 'failed';
|
||||
const isRejectingPending =
|
||||
initiatedPendingAction === 'reject' && rejectPendingState !== 'initializing' && rejectPendingState !== 'succeeded' && rejectPendingState !== 'failed';
|
||||
const isPublishingPending = isApprovingPending || isRejectingPending;
|
||||
|
||||
const approvePendingSucceeded = initiatedPendingAction === 'approve' && approvePendingState === 'succeeded';
|
||||
const rejectPendingSucceeded = initiatedPendingAction === 'reject' && rejectPendingState === 'succeeded';
|
||||
const approvePendingFailed = initiatedPendingAction === 'approve' && approvePendingState === 'failed';
|
||||
const rejectPendingFailed = initiatedPendingAction === 'reject' && rejectPendingState === 'failed';
|
||||
|
||||
const pendingStatus = approvePendingSucceeded ? 'approved' : rejectPendingSucceeded ? 'rejected' : approvePendingFailed || rejectPendingFailed ? 'failed' : null;
|
||||
const pendingErrorMessage = approvePendingFailed ? approvePendingError?.message : rejectPendingFailed ? rejectPendingError?.message : undefined;
|
||||
|
||||
return (
|
||||
<span className={styles.modQueueActions}>
|
||||
{pendingStatus === 'approved' ? (
|
||||
<span className={styles.modQueueStatusApproved}>{t('approved')}</span>
|
||||
) : pendingStatus === 'rejected' ? (
|
||||
<span className={styles.modQueueStatusRejected}>{t('rejected')}</span>
|
||||
) : pendingStatus === 'failed' ? (
|
||||
<span className={styles.modQueueStatusRejected}>
|
||||
{t('failed')}
|
||||
{pendingErrorMessage ? `: ${pendingErrorMessage}` : ''}
|
||||
</span>
|
||||
) : isPublishingPending ? (
|
||||
<LoadingEllipsis string={t('publishing')} />
|
||||
) : (
|
||||
<>
|
||||
<span className={styles.modQueueButtonWrapper}>
|
||||
[
|
||||
<button className={styles.modQueueActionButton} onClick={handlePendingApprove} disabled={isPublishingPending}>
|
||||
{t('approve')}
|
||||
</button>
|
||||
]
|
||||
</span>
|
||||
<span className={styles.modQueueButtonWrapper}>
|
||||
[
|
||||
<button className={styles.modQueueActionButton} onClick={handlePendingReject} disabled={isPublishingPending}>
|
||||
{t('reject')}
|
||||
</button>
|
||||
]
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
const PostInfo = ({
|
||||
post,
|
||||
postReplyCount = 0,
|
||||
@@ -142,86 +253,6 @@ const PostInfo = ({
|
||||
const pendingApproval = post?.pendingApproval;
|
||||
const shouldShowPendingApprovalButtons = isInPostPageView && !isInModQueueView && pendingApproval && isAccountMod && communityAddress;
|
||||
|
||||
// Moderation actions for pending approval posts
|
||||
const {
|
||||
publishCommentModeration: approvePending,
|
||||
state: approvePendingState,
|
||||
error: approvePendingError,
|
||||
} = usePublishCommentModeration({
|
||||
commentCid: cid,
|
||||
communityAddress: shouldShowPendingApprovalButtons ? communityAddress : undefined,
|
||||
commentModeration: approvePendingCommentModeration,
|
||||
onChallenge: async (...args: any) => {
|
||||
addChallenge([...args, post]);
|
||||
},
|
||||
onChallengeVerification: async (challengeVerification, comment) => {
|
||||
alertChallengeVerificationFailed(challengeVerification, comment);
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
console.error('Approve failed:', error);
|
||||
},
|
||||
});
|
||||
|
||||
const {
|
||||
publishCommentModeration: rejectPending,
|
||||
state: rejectPendingState,
|
||||
error: rejectPendingError,
|
||||
} = usePublishCommentModeration({
|
||||
commentCid: cid,
|
||||
communityAddress: shouldShowPendingApprovalButtons ? communityAddress : undefined,
|
||||
commentModeration: rejectPendingCommentModeration,
|
||||
onChallenge: async (...args: any) => {
|
||||
addChallenge([...args, post]);
|
||||
},
|
||||
onChallengeVerification: async (challengeVerification, comment) => {
|
||||
alertChallengeVerificationFailed(challengeVerification, comment);
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
console.error('Reject failed:', error);
|
||||
},
|
||||
});
|
||||
|
||||
const [initiatedPendingAction, setInitiatedPendingAction] = useState<'approve' | 'reject' | null>(null);
|
||||
const handlePendingApprove = useCallback(async () => {
|
||||
const confirm = window.confirm(t('double_confirm'));
|
||||
if (!confirm) {
|
||||
return;
|
||||
}
|
||||
setInitiatedPendingAction('approve');
|
||||
try {
|
||||
await approvePending();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}, [approvePending, t]);
|
||||
|
||||
const handlePendingReject = useCallback(async () => {
|
||||
const confirm = window.confirm(t('double_confirm'));
|
||||
if (!confirm) {
|
||||
return;
|
||||
}
|
||||
setInitiatedPendingAction('reject');
|
||||
try {
|
||||
await rejectPending();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}, [rejectPending, t]);
|
||||
|
||||
const isApprovingPending =
|
||||
initiatedPendingAction === 'approve' && approvePendingState !== 'initializing' && approvePendingState !== 'succeeded' && approvePendingState !== 'failed';
|
||||
const isRejectingPending =
|
||||
initiatedPendingAction === 'reject' && rejectPendingState !== 'initializing' && rejectPendingState !== 'succeeded' && rejectPendingState !== 'failed';
|
||||
const isPublishingPending = isApprovingPending || isRejectingPending;
|
||||
|
||||
const approvePendingSucceeded = initiatedPendingAction === 'approve' && approvePendingState === 'succeeded';
|
||||
const rejectPendingSucceeded = initiatedPendingAction === 'reject' && rejectPendingState === 'succeeded';
|
||||
const approvePendingFailed = initiatedPendingAction === 'approve' && approvePendingState === 'failed';
|
||||
const rejectPendingFailed = initiatedPendingAction === 'reject' && rejectPendingState === 'failed';
|
||||
|
||||
const pendingStatus = approvePendingSucceeded ? 'approved' : rejectPendingSucceeded ? 'rejected' : approvePendingFailed || rejectPendingFailed ? 'failed' : null;
|
||||
const pendingErrorMessage = approvePendingFailed ? approvePendingError?.message : rejectPendingFailed ? rejectPendingError?.message : undefined;
|
||||
|
||||
// Check if post is awaiting approval and over threshold (for mod queue view)
|
||||
const approved = post?.approved;
|
||||
const alreadyApproved = approved === true;
|
||||
@@ -473,39 +504,7 @@ const PostInfo = ({
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
{shouldShowPendingApprovalButtons && (
|
||||
<span className={styles.modQueueActions}>
|
||||
{pendingStatus === 'approved' ? (
|
||||
<span className={styles.modQueueStatusApproved}>{t('approved')}</span>
|
||||
) : pendingStatus === 'rejected' ? (
|
||||
<span className={styles.modQueueStatusRejected}>{t('rejected')}</span>
|
||||
) : pendingStatus === 'failed' ? (
|
||||
<span className={styles.modQueueStatusRejected}>
|
||||
{t('failed')}
|
||||
{pendingErrorMessage ? `: ${pendingErrorMessage}` : ''}
|
||||
</span>
|
||||
) : isPublishingPending ? (
|
||||
<LoadingEllipsis string={t('publishing')} />
|
||||
) : (
|
||||
<>
|
||||
<span className={styles.modQueueButtonWrapper}>
|
||||
[
|
||||
<button className={styles.modQueueActionButton} onClick={handlePendingApprove} disabled={isPublishingPending}>
|
||||
{t('approve')}
|
||||
</button>
|
||||
]
|
||||
</span>
|
||||
<span className={styles.modQueueButtonWrapper}>
|
||||
[
|
||||
<button className={styles.modQueueActionButton} onClick={handlePendingReject} disabled={isPublishingPending}>
|
||||
{t('reject')}
|
||||
</button>
|
||||
]
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
{shouldShowPendingApprovalButtons && communityAddress && cid && <PendingModerationActions cid={cid} communityAddress={communityAddress} post={post} />}
|
||||
</span>
|
||||
{!(removed || deleted || purged) && !isModQueue && <PostMenuDesktop postMenu={postMenuProps} />}
|
||||
{cid && parentCid && <ReplyBacklinks post={post} quotedByMap={quotedByMap} directRepliesByParentCid={directRepliesByParentCid} />}
|
||||
|
||||
Reference in New Issue
Block a user