mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
fix(pending posts): clean up failed post display
This commit is contained in:
@@ -397,7 +397,8 @@ describe('CommentContent', () => {
|
|||||||
postCid: 'post-2',
|
postCid: 'post-2',
|
||||||
state: 'failed',
|
state: 'failed',
|
||||||
});
|
});
|
||||||
expect(container.textContent).toContain('Failed to publish');
|
expect(container.textContent).not.toContain('Failed to publish');
|
||||||
|
expect(container.textContent).toContain('still pending');
|
||||||
expect(container.querySelector('[data-testid="loading-ellipsis"]')).toBeNull();
|
expect(container.querySelector('[data-testid="loading-ellipsis"]')).toBeNull();
|
||||||
|
|
||||||
testState.stateString = 'Publishing';
|
testState.stateString = 'Publishing';
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ const CommentContent = ({ comment: post, prependContent }: { comment: Comment; p
|
|||||||
const stateString = useStateString(resolvedPost);
|
const stateString = useStateString(resolvedPost);
|
||||||
const hasFailedState = state === 'failed';
|
const hasFailedState = state === 'failed';
|
||||||
const failedError = getFailedCommentError(resolvedPost);
|
const failedError = getFailedCommentError(resolvedPost);
|
||||||
|
const shouldShowUnpublishedStateDetails = !cid && (!hasFailedState || Boolean(failedError));
|
||||||
|
|
||||||
const loadingString = (
|
const loadingString = (
|
||||||
<div className={styles.stateString}>
|
<div className={styles.stateString}>
|
||||||
@@ -298,7 +299,7 @@ const CommentContent = ({ comment: post, prependContent }: { comment: Comment; p
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{!cid && (
|
{shouldShowUnpublishedStateDetails && (
|
||||||
<>
|
<>
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
|
|||||||
@@ -384,7 +384,7 @@ const PostInfo = ({
|
|||||||
) : purged ? (
|
) : purged ? (
|
||||||
t('purged')
|
t('purged')
|
||||||
) : !cid && pseudonymityMode ? (
|
) : !cid && pseudonymityMode ? (
|
||||||
<span className={styles.pendingCid}>{hasFailedState ? capitalize(t('failed')) : capitalize(t('pending'))}</span>
|
<span className={styles.pendingCid}>{hasFailedState ? '?' : capitalize(t('pending'))}</span>
|
||||||
) : (
|
) : (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
content={`${numberOfPostsByAuthor === 1 ? t('1_post_by_this_id') : t('x_posts_by_this_id', { number: numberOfPostsByAuthor })}`}
|
content={`${numberOfPostsByAuthor === 1 ? t('1_post_by_this_id') : t('x_posts_by_this_id', { number: numberOfPostsByAuthor })}`}
|
||||||
@@ -451,7 +451,7 @@ const PostInfo = ({
|
|||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<span>No.</span>
|
<span>No.</span>
|
||||||
<span className={styles.pendingCid}>{hasFailedState ? capitalize(t('failed')) : capitalize(t('pending'))}</span>
|
<span className={styles.pendingCid}>{hasFailedState ? '?' : capitalize(t('pending'))}</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{pinned && (
|
{pinned && (
|
||||||
@@ -855,6 +855,7 @@ const PostDesktop = ({
|
|||||||
const isMultiboardView = isInAllView || isInSubscriptionsView || isInModView;
|
const isMultiboardView = isInAllView || isInSubscriptionsView || isInModView;
|
||||||
const directories = useDirectories();
|
const directories = useDirectories();
|
||||||
const boardPath = communityAddress ? getBoardPath(communityAddress, directories) : undefined;
|
const boardPath = communityAddress ? getBoardPath(communityAddress, directories) : undefined;
|
||||||
|
const deleteFailedPostRedirectPath = isInPendingPostView ? (boardPath ? `/${boardPath}` : '/') : undefined;
|
||||||
const displayBoardPath =
|
const displayBoardPath =
|
||||||
boardPath && communityAddress
|
boardPath && communityAddress
|
||||||
? boardPath !== communityAddress
|
? boardPath !== communityAddress
|
||||||
@@ -957,8 +958,10 @@ const PostDesktop = ({
|
|||||||
|
|
||||||
const stateString = useStateString(resolvedPost) || t('downloading_board');
|
const stateString = useStateString(resolvedPost) || t('downloading_board');
|
||||||
const hasFailedState = state === 'failed';
|
const hasFailedState = state === 'failed';
|
||||||
const { canDeleteFailedPost, canRetryFailedPost, isDeletingFailedPost, isRetryingFailedPost, onDeleteFailedPost, onRetryFailedPost } =
|
const { canDeleteFailedPost, canRetryFailedPost, isDeletingFailedPost, isRetryingFailedPost, onDeleteFailedPost, onRetryFailedPost } = useDeleteFailedPost(
|
||||||
useDeleteFailedPost(resolvedPost);
|
resolvedPost,
|
||||||
|
deleteFailedPostRedirectPath,
|
||||||
|
);
|
||||||
const failedPublishNotice = canDeleteFailedPost ? (
|
const failedPublishNotice = canDeleteFailedPost ? (
|
||||||
<FailedPublishNotice
|
<FailedPublishNotice
|
||||||
isDeleting={isDeletingFailedPost}
|
isDeleting={isDeletingFailedPost}
|
||||||
|
|||||||
@@ -311,7 +311,7 @@ const PostInfoAndMedia = ({ post, postReplyCount = 0, roles, threadNumber, posts
|
|||||||
) : purged ? (
|
) : purged ? (
|
||||||
lowerCase(t('purged'))
|
lowerCase(t('purged'))
|
||||||
) : !cid && pseudonymityMode ? (
|
) : !cid && pseudonymityMode ? (
|
||||||
<span className={styles.pendingCid}>{hasFailedState ? capitalize(t('failed')) : capitalize(t('pending'))}</span>
|
<span className={styles.pendingCid}>{hasFailedState ? '?' : capitalize(t('pending'))}</span>
|
||||||
) : (
|
) : (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
content={`${numberOfPostsByAuthor === 1 ? t('1_post_by_this_id') : t('x_posts_by_this_id', { number: numberOfPostsByAuthor })}`}
|
content={`${numberOfPostsByAuthor === 1 ? t('1_post_by_this_id') : t('x_posts_by_this_id', { number: numberOfPostsByAuthor })}`}
|
||||||
@@ -410,7 +410,7 @@ const PostInfoAndMedia = ({ post, postReplyCount = 0, roles, threadNumber, posts
|
|||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<span>No.</span>
|
<span>No.</span>
|
||||||
<span className={styles.pendingCid}>{hasFailedState ? capitalize(t('failed')) : capitalize(t('pending'))}</span>
|
<span className={styles.pendingCid}>{hasFailedState ? '?' : capitalize(t('pending'))}</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{shouldShowPendingApprovalButtons && (
|
{shouldShowPendingApprovalButtons && (
|
||||||
@@ -595,6 +595,7 @@ const PostMobile = ({
|
|||||||
</button>
|
</button>
|
||||||
) : null;
|
) : null;
|
||||||
const linksCount = useCountLinksInReplies(resolvedPost);
|
const linksCount = useCountLinksInReplies(resolvedPost);
|
||||||
|
const deleteFailedPostRedirectPath = isInPendingPostView ? (boardPath ? `/${boardPath}` : '/') : undefined;
|
||||||
const hasReplyPaginationOverride = !!replyPaginationOverride;
|
const hasReplyPaginationOverride = !!replyPaginationOverride;
|
||||||
const shouldFetchReplies = showReplies && !isModQueue && !hasReplyPaginationOverride;
|
const shouldFetchReplies = showReplies && !isModQueue && !hasReplyPaginationOverride;
|
||||||
const shouldUsePreview = shouldFetchReplies && !showAllReplies;
|
const shouldUsePreview = shouldFetchReplies && !showAllReplies;
|
||||||
@@ -668,8 +669,10 @@ const PostMobile = ({
|
|||||||
const stateString = useStateString(resolvedPost) || t('loading_post');
|
const stateString = useStateString(resolvedPost) || t('loading_post');
|
||||||
const hasFailedState = state === 'failed';
|
const hasFailedState = state === 'failed';
|
||||||
const isReply = !!parentCid;
|
const isReply = !!parentCid;
|
||||||
const { canDeleteFailedPost, canRetryFailedPost, isDeletingFailedPost, isRetryingFailedPost, onDeleteFailedPost, onRetryFailedPost } =
|
const { canDeleteFailedPost, canRetryFailedPost, isDeletingFailedPost, isRetryingFailedPost, onDeleteFailedPost, onRetryFailedPost } = useDeleteFailedPost(
|
||||||
useDeleteFailedPost(resolvedPost);
|
resolvedPost,
|
||||||
|
deleteFailedPostRedirectPath,
|
||||||
|
);
|
||||||
const failedPublishNotice = canDeleteFailedPost ? (
|
const failedPublishNotice = canDeleteFailedPost ? (
|
||||||
<FailedPublishNotice
|
<FailedPublishNotice
|
||||||
isDeleting={isDeletingFailedPost}
|
isDeleting={isDeletingFailedPost}
|
||||||
|
|||||||
@@ -14,9 +14,14 @@ const testState = vi.hoisted(() => ({
|
|||||||
alertMock: vi.fn(),
|
alertMock: vi.fn(),
|
||||||
deleteCommentMock: vi.fn(async (_targetComment: string | number) => undefined),
|
deleteCommentMock: vi.fn(async (_targetComment: string | number) => undefined),
|
||||||
lastPublishOptions: undefined as Record<string, any> | undefined,
|
lastPublishOptions: undefined as Record<string, any> | undefined,
|
||||||
|
navigateMock: vi.fn(),
|
||||||
publishCommentMock: vi.fn(async () => undefined),
|
publishCommentMock: vi.fn(async () => undefined),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
vi.mock('react-router-dom', () => ({
|
||||||
|
useNavigate: () => testState.navigateMock,
|
||||||
|
}));
|
||||||
|
|
||||||
vi.mock('@bitsocial/bitsocial-react-hooks', () => ({
|
vi.mock('@bitsocial/bitsocial-react-hooks', () => ({
|
||||||
deleteComment: (targetComment: string | number) => testState.deleteCommentMock(targetComment),
|
deleteComment: (targetComment: string | number) => testState.deleteCommentMock(targetComment),
|
||||||
usePublishComment: (options: Record<string, any> | undefined) => {
|
usePublishComment: (options: Record<string, any> | undefined) => {
|
||||||
@@ -63,14 +68,14 @@ const failedPost = {
|
|||||||
title: 'Hello',
|
title: 'Hello',
|
||||||
};
|
};
|
||||||
|
|
||||||
const HookHarness = ({ post }: { post: typeof failedPost }) => {
|
const HookHarness = ({ deleteRedirectPath, post }: { deleteRedirectPath?: string; post: typeof failedPost }) => {
|
||||||
latestValue = useDeleteFailedPost(post);
|
latestValue = useDeleteFailedPost(post, deleteRedirectPath);
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderHook = (post = failedPost) => {
|
const renderHook = (post = failedPost, deleteRedirectPath?: string) => {
|
||||||
act(() => {
|
act(() => {
|
||||||
root.render(createElement(HookHarness, { post }));
|
root.render(createElement(HookHarness, { deleteRedirectPath, post }));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -137,6 +142,17 @@ describe('useDeleteFailedPost', () => {
|
|||||||
expect(testState.publishCommentMock).toHaveBeenCalledTimes(1);
|
expect(testState.publishCommentMock).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('redirects after deleting a failed post when a redirect path is provided', async () => {
|
||||||
|
renderHook(failedPost, '/mu');
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
await latestValue.onDeleteFailedPost();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(testState.deleteCommentMock).toHaveBeenCalledWith('failed-cid');
|
||||||
|
expect(testState.navigateMock).toHaveBeenCalledWith('/mu', { replace: true });
|
||||||
|
});
|
||||||
|
|
||||||
it('routes retry challenges through the challenge store and preserves abandon behavior', async () => {
|
it('routes retry challenges through the challenge store and preserves abandon behavior', async () => {
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
await testState.lastPublishOptions?.onChallenge('captcha', 'nonce');
|
await testState.lastPublishOptions?.onChallenge('captcha', 'nonce');
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useCallback, useMemo, useRef, useState } from 'react';
|
import { useCallback, useMemo, useRef, useState } from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { ChallengeVerification, Comment, PublishCommentOptions, deleteComment, usePublishComment } from '@bitsocial/bitsocial-react-hooks';
|
import { ChallengeVerification, Comment, PublishCommentOptions, deleteComment, usePublishComment } from '@bitsocial/bitsocial-react-hooks';
|
||||||
import { alertChallengeVerificationFailed } from '../lib/utils/challenge-utils';
|
import { alertChallengeVerificationFailed } from '../lib/utils/challenge-utils';
|
||||||
import useChallengesStore from '../stores/use-challenges-store';
|
import useChallengesStore from '../stores/use-challenges-store';
|
||||||
@@ -67,10 +68,11 @@ export const getFailedPostRetryPublishOptions = (post?: FailedPost): PublishComm
|
|||||||
return retryOptions;
|
return retryOptions;
|
||||||
};
|
};
|
||||||
|
|
||||||
const useDeleteFailedPost = (post?: FailedPost) => {
|
const useDeleteFailedPost = (post?: FailedPost, deleteRedirectPath?: string) => {
|
||||||
const [isDeletingFailedPost, setIsDeletingFailedPost] = useState(false);
|
const [isDeletingFailedPost, setIsDeletingFailedPost] = useState(false);
|
||||||
const [isRetryingFailedPost, setIsRetryingFailedPost] = useState(false);
|
const [isRetryingFailedPost, setIsRetryingFailedPost] = useState(false);
|
||||||
const addChallenge = useChallengesStore((state) => state.addChallenge);
|
const addChallenge = useChallengesStore((state) => state.addChallenge);
|
||||||
|
const navigate = useNavigate();
|
||||||
const abandonPublishRef = useRef<(() => Promise<void>) | undefined>();
|
const abandonPublishRef = useRef<(() => Promise<void>) | undefined>();
|
||||||
const abandonCurrentPublish = useCallback(async () => {
|
const abandonCurrentPublish = useCallback(async () => {
|
||||||
await abandonPublishRef.current?.();
|
await abandonPublishRef.current?.();
|
||||||
@@ -114,13 +116,16 @@ const useDeleteFailedPost = (post?: FailedPost) => {
|
|||||||
deleteComment(targetComment)
|
deleteComment(targetComment)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
setIsDeletingFailedPost(false);
|
setIsDeletingFailedPost(false);
|
||||||
|
if (deleteRedirectPath) {
|
||||||
|
navigate(deleteRedirectPath, { replace: true });
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error('Failed to delete failed post:', error);
|
console.error('Failed to delete failed post:', error);
|
||||||
alert(`Failed to delete post: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
alert(`Failed to delete post: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||||
setIsDeletingFailedPost(false);
|
setIsDeletingFailedPost(false);
|
||||||
});
|
});
|
||||||
}, [canDeleteFailedPost, isDeletingFailedPost, isRetryingFailedPost, post]);
|
}, [canDeleteFailedPost, deleteRedirectPath, isDeletingFailedPost, isRetryingFailedPost, navigate, post]);
|
||||||
|
|
||||||
const canRetryFailedPost = canDeleteFailedPost && Boolean(retryPublishOptions);
|
const canRetryFailedPost = canDeleteFailedPost && Boolean(retryPublishOptions);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user