mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
fix(pending-post): return to board after abandoned challenge
This commit is contained in:
@@ -904,7 +904,7 @@ describe('PostForm', () => {
|
||||
await flushEffects();
|
||||
|
||||
expect(testState.resetPublishPostOptionsMock).toHaveBeenCalledTimes(1);
|
||||
expect(testState.navigateMock).toHaveBeenCalledWith('/pending/7');
|
||||
expect(testState.navigateMock).toHaveBeenCalledWith('/pending/7', { state: { boardPath: 'mu' } });
|
||||
});
|
||||
|
||||
it('resets the reply form after a completed reply publish', async () => {
|
||||
|
||||
@@ -428,6 +428,7 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
|
||||
const subscriptions = account?.subscriptions || [];
|
||||
const directories = useDirectories();
|
||||
const directoryEntry = useDirectoryByAddress(effectiveBoardAddress);
|
||||
const pendingPostBoardPath = effectiveBoardAddress ? getBoardPath(effectiveBoardAddress, directories) : undefined;
|
||||
const rulesPath = effectiveBoardAddress ? `/rules/${getBoardPath(effectiveBoardAddress, directories)}` : '/rules';
|
||||
const showSpoilerForPost = directoryEntry?.features?.noSpoilers !== true;
|
||||
const showSpoilerForReply = directoryEntry?.features?.noSpoilerReplies !== true;
|
||||
@@ -538,9 +539,9 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
|
||||
if (typeof postIndex === 'number') {
|
||||
resetPublishPostOptions();
|
||||
resetFields();
|
||||
navigate(`/pending/${postIndex}`);
|
||||
navigate(`/pending/${postIndex}`, pendingPostBoardPath ? { state: { boardPath: pendingPostBoardPath } } : undefined);
|
||||
}
|
||||
}, [postIndex, resetPublishPostOptions, navigate]);
|
||||
}, [postIndex, pendingPostBoardPath, resetPublishPostOptions, navigate]);
|
||||
|
||||
// in post page, publish a reply to the post
|
||||
const isInPostView = isPostPageView(location.pathname, params);
|
||||
|
||||
@@ -65,6 +65,13 @@ vi.mock('@bitsocial/bitsocial-react-hooks', () => ({
|
||||
},
|
||||
state: 'succeeded',
|
||||
}),
|
||||
useCommunities: ({ communities }: { communities?: Array<{ name: string }> } = {}) => ({
|
||||
communities: (communities ?? []).map(() => ({
|
||||
roles: {
|
||||
'0x123': { role: 'moderator' },
|
||||
},
|
||||
})),
|
||||
}),
|
||||
useEditedComment: ({ comment }: { comment?: TestComment }) => ({
|
||||
editedComment: comment,
|
||||
failedEdits: {},
|
||||
|
||||
@@ -16,8 +16,10 @@ type TestComment = {
|
||||
const testState = vi.hoisted(() => ({
|
||||
accountCommentIndex: undefined as string | undefined,
|
||||
accountComments: [] as TestComment[],
|
||||
challengeCount: 0,
|
||||
directories: [] as Array<{ address: string; title?: string }>,
|
||||
getBoardPathMock: vi.fn<(address: string) => string>(),
|
||||
locationState: null as { boardPath?: string } | null,
|
||||
navigateMock: vi.fn(),
|
||||
post: undefined as TestComment | undefined,
|
||||
}));
|
||||
@@ -26,6 +28,9 @@ vi.mock('react-router-dom', async () => {
|
||||
const actual = await vi.importActual<typeof import('react-router-dom')>('react-router-dom');
|
||||
return {
|
||||
...actual,
|
||||
useLocation: () => ({
|
||||
state: testState.locationState,
|
||||
}),
|
||||
useNavigate: () => testState.navigateMock,
|
||||
useParams: () => ({
|
||||
accountCommentIndex: testState.accountCommentIndex,
|
||||
@@ -49,6 +54,10 @@ vi.mock('../../../lib/utils/route-utils', () => ({
|
||||
getBoardPath: (address: string) => testState.getBoardPathMock(address),
|
||||
}));
|
||||
|
||||
vi.mock('../../../stores/use-challenges-store', () => ({
|
||||
default: (selector: (state: { challenges: unknown[] }) => unknown) => selector({ challenges: Array.from({ length: testState.challengeCount }) }),
|
||||
}));
|
||||
|
||||
vi.mock('../../post', () => ({
|
||||
Post: ({ post }: { post?: TestComment }) => createElement('div', { 'data-testid': 'post-view' }, post?.cid ?? 'no-post'),
|
||||
}));
|
||||
@@ -78,8 +87,10 @@ describe('PendingPost', () => {
|
||||
vi.clearAllMocks();
|
||||
testState.accountCommentIndex = undefined;
|
||||
testState.accountComments = [];
|
||||
testState.challengeCount = 0;
|
||||
testState.directories = [];
|
||||
testState.getBoardPathMock.mockReset();
|
||||
testState.locationState = null;
|
||||
testState.navigateMock.mockReset();
|
||||
testState.post = undefined;
|
||||
|
||||
@@ -151,6 +162,25 @@ describe('PendingPost', () => {
|
||||
expect(testState.navigateMock).not.toHaveBeenCalledWith('/not-found', { replace: true });
|
||||
});
|
||||
|
||||
it('redirects abandoned pending posts back to their board after the challenge closes', async () => {
|
||||
testState.accountCommentIndex = '0';
|
||||
testState.accountComments = [];
|
||||
testState.challengeCount = 1;
|
||||
testState.locationState = { boardPath: 'mu' };
|
||||
testState.post = { index: 0 };
|
||||
|
||||
await renderPendingPost();
|
||||
|
||||
expect(testState.navigateMock).not.toHaveBeenCalled();
|
||||
|
||||
testState.challengeCount = 0;
|
||||
testState.navigateMock.mockClear();
|
||||
|
||||
await renderPendingPost();
|
||||
|
||||
expect(testState.navigateMock).toHaveBeenCalledWith('/mu', { replace: true });
|
||||
});
|
||||
|
||||
it('redirects missing sparse pending account comment indices to not found', async () => {
|
||||
testState.accountCommentIndex = '0';
|
||||
testState.accountComments = [{ index: 1 }];
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useLocation, useNavigate, useParams } from 'react-router-dom';
|
||||
import { useAccountComments } from '@bitsocial/bitsocial-react-hooks';
|
||||
import { useDirectories } from '../../hooks/use-directories';
|
||||
import useSafeAccountComment from '../../hooks/use-safe-account-comment';
|
||||
import { getCommentCommunityAddress } from '../../lib/utils/comment-utils';
|
||||
import { getBoardPath } from '../../lib/utils/route-utils';
|
||||
import useChallengesStore from '../../stores/use-challenges-store';
|
||||
import { Post } from '../post';
|
||||
|
||||
type PendingAccountComment = {
|
||||
@@ -31,17 +32,40 @@ const hasPendingAccountCommentIndex = (accountComments: PendingAccountComment[]
|
||||
return hasExplicitIndices ? false : accountCommentIndex < accountComments.length;
|
||||
};
|
||||
|
||||
const getPendingRouteBoardPath = (state: unknown): string | undefined => {
|
||||
if (!state || typeof state !== 'object') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const boardPath = (state as { boardPath?: unknown }).boardPath;
|
||||
return typeof boardPath === 'string' && boardPath ? boardPath : undefined;
|
||||
};
|
||||
|
||||
const PendingPost = () => {
|
||||
const { accountComments } = useAccountComments();
|
||||
const { accountCommentIndex } = useParams<{ accountCommentIndex?: string }>();
|
||||
const location = useLocation();
|
||||
const normalizedAccountCommentIndex = accountCommentIndex === undefined ? undefined : Number(accountCommentIndex);
|
||||
const hasNormalizedAccountCommentIndex = normalizedAccountCommentIndex !== undefined && !Number.isNaN(normalizedAccountCommentIndex);
|
||||
const post = useSafeAccountComment({ commentIndex: accountCommentIndex });
|
||||
const postCommunityAddress = getCommentCommunityAddress(post);
|
||||
const hasAddressablePost = Boolean(post?.cid || postCommunityAddress);
|
||||
const navigate = useNavigate();
|
||||
const directories = useDirectories();
|
||||
const routeBoardPath = getPendingRouteBoardPath(location.state);
|
||||
const postBoardPath = postCommunityAddress ? getBoardPath(postCommunityAddress, directories) : undefined;
|
||||
const pendingBoardPath = postBoardPath || routeBoardPath;
|
||||
const hasActiveChallenge = useChallengesStore((state) => state.challenges.length > 0);
|
||||
const lastPendingBoardRef = useRef<{ accountCommentIndex: number; boardPath: string } | null>(null);
|
||||
|
||||
useEffect(() => window.scrollTo(0, 0), []);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof normalizedAccountCommentIndex === 'number' && pendingBoardPath) {
|
||||
lastPendingBoardRef.current = { accountCommentIndex: normalizedAccountCommentIndex, boardPath: pendingBoardPath };
|
||||
}
|
||||
}, [normalizedAccountCommentIndex, pendingBoardPath]);
|
||||
|
||||
const isValidAccountCommentIndex =
|
||||
!accountCommentIndex ||
|
||||
(hasNormalizedAccountCommentIndex &&
|
||||
@@ -51,17 +75,31 @@ const PendingPost = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (!isValidAccountCommentIndex) {
|
||||
navigate('/not-found', { replace: true });
|
||||
const lastPendingBoard = lastPendingBoardRef.current;
|
||||
const abandonedBoardPath =
|
||||
!hasActiveChallenge && lastPendingBoard && lastPendingBoard.accountCommentIndex === normalizedAccountCommentIndex ? lastPendingBoard.boardPath : undefined;
|
||||
navigate(abandonedBoardPath ? `/${abandonedBoardPath}` : '/not-found', { replace: true });
|
||||
}
|
||||
}, [isValidAccountCommentIndex, navigate]);
|
||||
}, [hasActiveChallenge, isValidAccountCommentIndex, navigate, normalizedAccountCommentIndex]);
|
||||
|
||||
useEffect(() => {
|
||||
const postCommunityAddress = getCommentCommunityAddress(post);
|
||||
if (post?.cid && postCommunityAddress) {
|
||||
const boardPath = getBoardPath(postCommunityAddress, directories);
|
||||
navigate(`/${boardPath}/thread/${post.cid}`, { replace: true });
|
||||
if (post?.cid && postBoardPath) {
|
||||
navigate(`/${postBoardPath}/thread/${post.cid}`, { replace: true });
|
||||
}
|
||||
}, [post, navigate, directories]);
|
||||
}, [post?.cid, postBoardPath, navigate]);
|
||||
|
||||
useEffect(() => {
|
||||
if (hasAddressablePost || !isValidAccountCommentIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
const lastPendingBoard = lastPendingBoardRef.current;
|
||||
const abandonedBoardPath =
|
||||
!hasActiveChallenge && lastPendingBoard && lastPendingBoard.accountCommentIndex === normalizedAccountCommentIndex ? lastPendingBoard.boardPath : undefined;
|
||||
if (abandonedBoardPath) {
|
||||
navigate(`/${abandonedBoardPath}`, { replace: true });
|
||||
}
|
||||
}, [hasActiveChallenge, hasAddressablePost, isValidAccountCommentIndex, navigate, normalizedAccountCommentIndex]);
|
||||
|
||||
return <Post post={post} />;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user