mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
fix(accounts): adapt 5chan to compact account history hooks (#1118)
* chore(cursor): use composer-2 for subagents * fix(accounts): adapt 5chan to compact account history hooks * fix(accounts): address 5chan account-history review findings
This commit is contained in:
@@ -22,6 +22,7 @@ type TestComment = {
|
||||
const testState = vi.hoisted(() => ({
|
||||
account: { subscriptions: [] as string[] },
|
||||
accountComments: [] as TestComment[],
|
||||
accountCommentsCalls: [] as Array<{ commentIndices?: number[]; communityAddress?: string; newerThan?: number; sortType?: 'new' | 'old' } | undefined>,
|
||||
accountCommunityAddresses: [] as string[],
|
||||
directories: [{ address: 'music-posting.eth', title: '/mu/ - Music' }] as Array<{ address: string; title?: string }>,
|
||||
directoryByAddress: {
|
||||
@@ -64,9 +65,36 @@ vi.mock('react-i18next', () => ({
|
||||
}),
|
||||
}));
|
||||
|
||||
const getScopedAccountComments = (options?: { commentIndices?: number[]; communityAddress?: string; newerThan?: number; sortType?: 'new' | 'old' }) => {
|
||||
let scopedComments = [...testState.accountComments];
|
||||
|
||||
if (options?.commentIndices?.length) {
|
||||
const normalizedCommentIndices = options.commentIndices.filter((commentIndex) => Number.isInteger(commentIndex) && commentIndex >= 0);
|
||||
scopedComments = normalizedCommentIndices.map((commentIndex) => testState.accountComments[commentIndex]).filter(Boolean) as TestComment[];
|
||||
} else if (options?.communityAddress) {
|
||||
scopedComments = scopedComments.filter(
|
||||
(comment) => (comment.communityAddress || (comment as TestComment & { subplebbitAddress?: string }).subplebbitAddress) === options.communityAddress,
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof options?.newerThan === 'number') {
|
||||
const newerThanTimestamp = Math.floor(Date.now() / 1000) - options.newerThan;
|
||||
scopedComments = scopedComments.filter((comment) => (comment.timestamp || 0) > newerThanTimestamp);
|
||||
}
|
||||
|
||||
if (options?.sortType === 'new') {
|
||||
scopedComments = [...scopedComments].reverse();
|
||||
}
|
||||
|
||||
return scopedComments;
|
||||
};
|
||||
|
||||
vi.mock('@bitsocialnet/bitsocial-react-hooks', () => ({
|
||||
useAccount: () => testState.account,
|
||||
useAccountComments: () => ({ accountComments: testState.accountComments }),
|
||||
useAccountComments: (options?: { commentIndices?: number[]; communityAddress?: string; newerThan?: number; sortType?: 'new' | 'old' }) => {
|
||||
testState.accountCommentsCalls.push(options);
|
||||
return { accountComments: getScopedAccountComments(options) };
|
||||
},
|
||||
useFeed: () => ({
|
||||
feed: testState.feed,
|
||||
hasMore: testState.hasMore,
|
||||
@@ -236,6 +264,7 @@ describe('Board', () => {
|
||||
latestLocation = '';
|
||||
testState.account = { subscriptions: [] };
|
||||
testState.accountComments = [];
|
||||
testState.accountCommentsCalls = [];
|
||||
testState.accountCommunityAddresses = [];
|
||||
testState.directories = [{ address: 'music-posting.eth', title: '/mu/ - Music' }];
|
||||
testState.directoryByAddress = {
|
||||
@@ -302,6 +331,13 @@ describe('Board', () => {
|
||||
communityAddress: 'music-posting.eth',
|
||||
timestamp: currentTimestamp,
|
||||
},
|
||||
{
|
||||
cid: 'fresh-reply',
|
||||
postCid: 'another-post',
|
||||
state: 'succeeded',
|
||||
communityAddress: 'music-posting.eth',
|
||||
timestamp: currentTimestamp,
|
||||
},
|
||||
];
|
||||
testState.hasMore = true;
|
||||
|
||||
@@ -309,6 +345,11 @@ describe('Board', () => {
|
||||
|
||||
expect(document.title).toBe('/mu/ - 5chan');
|
||||
expect(testState.setResetFunctionMock).toHaveBeenCalledWith(testState.resetMock);
|
||||
expect(testState.accountCommentsCalls).toContainEqual({
|
||||
communityAddress: 'music-posting.eth',
|
||||
newerThan: 3600,
|
||||
sortType: 'old',
|
||||
});
|
||||
expect(Array.from(container.querySelectorAll('[data-testid="post"]')).map((element) => element.textContent)).toEqual(['pinned-post', 'fresh-post']);
|
||||
expect(container.querySelector('[data-testid="board-pagination"]')?.textContent).toBe('/mu:1:2');
|
||||
|
||||
|
||||
@@ -27,6 +27,9 @@ import { PageFooterDesktop, PageFooterMobile } from '../../components/footer';
|
||||
import { Post } from '../post';
|
||||
|
||||
const lastVirtuosoStates: { [key: string]: StateSnapshot } = {};
|
||||
const RECENT_ACCOUNT_COMMENT_WINDOW_SECONDS = 60 * 60;
|
||||
// Keep the hook on its indexed fast path when this view should not inject local posts.
|
||||
const EMPTY_ACCOUNT_COMMENT_LOOKUP = { commentIndices: [-1] };
|
||||
|
||||
/** Board feed always uses 'active' sort; catalog dropdown does not affect board ordering. */
|
||||
const BOARD_SORT_TYPE = 'active' as const;
|
||||
@@ -160,7 +163,18 @@ const Board = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp, i
|
||||
);
|
||||
|
||||
const { feed, hasMore, loadMore, reset } = useFeed(feedOptions);
|
||||
const { accountComments } = useAccountComments();
|
||||
const accountCommentLookupOptions = useMemo(
|
||||
() =>
|
||||
communityAddress
|
||||
? {
|
||||
communityAddress,
|
||||
newerThan: RECENT_ACCOUNT_COMMENT_WINDOW_SECONDS,
|
||||
sortType: 'old' as const,
|
||||
}
|
||||
: EMPTY_ACCOUNT_COMMENT_LOOKUP,
|
||||
[communityAddress],
|
||||
);
|
||||
const { accountComments: recentAccountComments } = useAccountComments(accountCommentLookupOptions);
|
||||
|
||||
const pathWithoutSettings = location.pathname.replace(/\/settings$/, '');
|
||||
const currentPage = getPageFromFeedPath(pathWithoutSettings);
|
||||
@@ -179,13 +193,13 @@ const Board = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp, i
|
||||
const feedCids = useMemo(() => new Set(feed.map((f) => f.cid)), [feed]);
|
||||
const filteredComments = useMemo(
|
||||
() =>
|
||||
accountComments.filter((comment) => {
|
||||
recentAccountComments.filter((comment) => {
|
||||
const { cid, deleted, postCid, removed, state, timestamp } = comment || {};
|
||||
const commentCommunityAddress = comment?.communityAddress || comment?.subplebbitAddress;
|
||||
return (
|
||||
!deleted &&
|
||||
!removed &&
|
||||
timestamp > Date.now() / 1000 - 60 * 60 &&
|
||||
timestamp > Date.now() / 1000 - RECENT_ACCOUNT_COMMENT_WINDOW_SECONDS &&
|
||||
state === 'succeeded' &&
|
||||
cid &&
|
||||
cid === postCid &&
|
||||
@@ -193,7 +207,7 @@ const Board = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp, i
|
||||
!feedCids.has(cid)
|
||||
);
|
||||
}),
|
||||
[accountComments, communityAddress, feedCids],
|
||||
[recentAccountComments, communityAddress, feedCids],
|
||||
);
|
||||
|
||||
// show newest account comment at the top of the feed but after pinned posts
|
||||
|
||||
@@ -34,6 +34,7 @@ type FilterItem = {
|
||||
const testState = vi.hoisted(() => ({
|
||||
account: { subscriptions: [] as string[] },
|
||||
accountComments: [] as TestComment[],
|
||||
accountCommentsCalls: [] as Array<{ commentIndices?: number[]; communityAddress?: string; newerThan?: number; sortType?: 'new' | 'old' } | undefined>,
|
||||
clearMatchedFiltersMock: vi.fn(),
|
||||
directoryByAddress: {
|
||||
'music-posting.eth': {
|
||||
@@ -60,6 +61,7 @@ const testState = vi.hoisted(() => ({
|
||||
setMatchedFilterMock: vi.fn(),
|
||||
setResetFunctionMock: vi.fn(),
|
||||
sortType: 'new' as 'active' | 'new',
|
||||
windowWidth: 900,
|
||||
community: {
|
||||
error: undefined as Error | undefined,
|
||||
shortAddress: 'music-posting.eth',
|
||||
@@ -92,9 +94,36 @@ vi.mock('react-i18next', () => ({
|
||||
}),
|
||||
}));
|
||||
|
||||
const getScopedAccountComments = (options?: { commentIndices?: number[]; communityAddress?: string; newerThan?: number; sortType?: 'new' | 'old' }) => {
|
||||
let scopedComments = [...testState.accountComments];
|
||||
|
||||
if (options?.commentIndices?.length) {
|
||||
const normalizedCommentIndices = options.commentIndices.filter((commentIndex) => Number.isInteger(commentIndex) && commentIndex >= 0);
|
||||
scopedComments = normalizedCommentIndices.map((commentIndex) => testState.accountComments[commentIndex]).filter(Boolean) as TestComment[];
|
||||
} else if (options?.communityAddress) {
|
||||
scopedComments = scopedComments.filter(
|
||||
(comment) => (comment.communityAddress || (comment as TestComment & { subplebbitAddress?: string }).subplebbitAddress) === options.communityAddress,
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof options?.newerThan === 'number') {
|
||||
const newerThanTimestamp = Math.floor(Date.now() / 1000) - options.newerThan;
|
||||
scopedComments = scopedComments.filter((comment) => (comment.timestamp || 0) > newerThanTimestamp);
|
||||
}
|
||||
|
||||
if (options?.sortType === 'new') {
|
||||
scopedComments = [...scopedComments].reverse();
|
||||
}
|
||||
|
||||
return scopedComments;
|
||||
};
|
||||
|
||||
vi.mock('@bitsocialnet/bitsocial-react-hooks', () => ({
|
||||
useAccount: () => testState.account,
|
||||
useAccountComments: () => ({ accountComments: testState.accountComments }),
|
||||
useAccountComments: (options?: { commentIndices?: number[]; communityAddress?: string; newerThan?: number; sortType?: 'new' | 'old' }) => {
|
||||
testState.accountCommentsCalls.push(options);
|
||||
return { accountComments: getScopedAccountComments(options) };
|
||||
},
|
||||
useFeed: (options: { filter?: { filter: (comment: TestComment) => boolean } }) => ({
|
||||
feed: options.filter ? testState.feed.filter((comment) => options.filter?.filter(comment)) : testState.feed,
|
||||
hasMore: testState.hasMore,
|
||||
@@ -135,10 +164,6 @@ vi.mock('react-virtuoso', () => ({
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock('../../../hooks/use-catalog-feed-rows', () => ({
|
||||
default: (_columnCount: number, processedFeed: TestComment[]) => processedFeed.map((comment) => [comment]),
|
||||
}));
|
||||
|
||||
vi.mock('../../../hooks/use-directories', () => ({
|
||||
useDirectories: () => testState.directories,
|
||||
useDirectoryByAddress: (address: string | undefined) => (address ? testState.directoryByAddress[address] : undefined),
|
||||
@@ -161,7 +186,7 @@ vi.mock('../../../hooks/use-state-string', () => ({
|
||||
}));
|
||||
|
||||
vi.mock('../../../hooks/use-window-width', () => ({
|
||||
default: () => 900,
|
||||
default: () => testState.windowWidth,
|
||||
}));
|
||||
|
||||
vi.mock('../../../stores/use-catalog-style-store', () => ({
|
||||
@@ -271,6 +296,7 @@ describe('Catalog', () => {
|
||||
latestLocation = '';
|
||||
testState.account = { subscriptions: [] };
|
||||
testState.accountComments = [];
|
||||
testState.accountCommentsCalls = [];
|
||||
testState.directories = [{ address: 'music-posting.eth', title: '/mu/ - Music' }];
|
||||
testState.directoryByAddress = {
|
||||
'music-posting.eth': {
|
||||
@@ -290,6 +316,7 @@ describe('Catalog', () => {
|
||||
testState.resolvedCommunityAddress = 'music-posting.eth';
|
||||
testState.searchText = '';
|
||||
testState.sortType = 'new';
|
||||
testState.windowWidth = 900;
|
||||
testState.community = {
|
||||
error: undefined,
|
||||
shortAddress: 'music-posting.eth',
|
||||
@@ -331,7 +358,7 @@ describe('Catalog', () => {
|
||||
expect(document.title).toBe('/mu/ - catalog - 5chan');
|
||||
expect(testState.setCurrentCommunityAddressMock).toHaveBeenCalledWith('music-posting.eth');
|
||||
expect(testState.clearMatchedFiltersMock).toHaveBeenCalled();
|
||||
expect(Array.from(container.querySelectorAll('[data-testid="catalog-row"]')).map((element) => element.textContent)).toEqual(['row:top-post', 'row:boring-post']);
|
||||
expect(Array.from(container.querySelectorAll('[data-testid="catalog-row"]')).map((element) => element.textContent)).toEqual(['row:top-post,boring-post']);
|
||||
expect(testState.incrementFilterCountMock).toHaveBeenCalledWith(0, 'hidden-post', 'music-posting.eth');
|
||||
expect(testState.incrementFilterCountMock).toHaveBeenCalledWith(1, 'top-post', 'music-posting.eth');
|
||||
expect(testState.setMatchedFilterMock).toHaveBeenCalledWith('top-post', 'red');
|
||||
@@ -375,4 +402,51 @@ describe('Catalog', () => {
|
||||
expect(container.textContent).toContain('not_subscribed_to_any_board');
|
||||
expect(container.querySelector('[data-testid="catalog-first-row"]')?.textContent).toBe('music-posting.eth');
|
||||
});
|
||||
|
||||
it('queries scoped recent account posts and still applies local search filtering before injecting them', async () => {
|
||||
const currentTimestamp = Math.floor(Date.now() / 1000);
|
||||
testState.feed = [{ cid: 'network-post', title: 'cats on stage', communityAddress: 'music-posting.eth' }];
|
||||
testState.searchText = 'cats';
|
||||
testState.accountComments = [
|
||||
{
|
||||
cid: 'local-cats-post',
|
||||
content: 'cats local thread',
|
||||
postCid: 'local-cats-post',
|
||||
state: 'succeeded',
|
||||
communityAddress: 'music-posting.eth',
|
||||
timestamp: currentTimestamp,
|
||||
title: 'cats local',
|
||||
},
|
||||
{
|
||||
cid: 'local-dogs-post',
|
||||
content: 'dogs local thread',
|
||||
postCid: 'local-dogs-post',
|
||||
state: 'succeeded',
|
||||
communityAddress: 'music-posting.eth',
|
||||
timestamp: currentTimestamp,
|
||||
title: 'dogs local',
|
||||
},
|
||||
];
|
||||
|
||||
await renderCatalog({ initialEntry: '/mu/catalog', routePath: '/:boardIdentifier/catalog' });
|
||||
|
||||
expect(testState.accountCommentsCalls).toContainEqual({
|
||||
communityAddress: 'music-posting.eth',
|
||||
newerThan: 3600,
|
||||
sortType: 'old',
|
||||
});
|
||||
expect(Array.from(container.querySelectorAll('[data-testid="catalog-row"]')).map((element) => element.textContent)).toEqual(['row:local-cats-post,network-post']);
|
||||
});
|
||||
|
||||
it('chunks catalog rows safely even when the viewport is narrower than one card', async () => {
|
||||
testState.windowWidth = 0;
|
||||
testState.feed = [
|
||||
{ cid: 'first-post', title: 'one', communityAddress: 'music-posting.eth' },
|
||||
{ cid: 'second-post', title: 'two', communityAddress: 'music-posting.eth' },
|
||||
];
|
||||
|
||||
await renderCatalog({ initialEntry: '/mu/catalog', routePath: '/:boardIdentifier/catalog' });
|
||||
|
||||
expect(Array.from(container.querySelectorAll('[data-testid="catalog-row"]')).map((element) => element.textContent)).toEqual(['row:first-post', 'row:second-post']);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { useEffect, useMemo, useRef, useState, useCallback } from 'react';
|
||||
import { useEffect, useMemo, useRef, useCallback } from 'react';
|
||||
import { useLocation, useNavigate, useNavigationType, useParams } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Comment, useAccount, useCommunity, useFeed, useAccountComments } from '@bitsocialnet/bitsocial-react-hooks';
|
||||
import { Virtuoso, VirtuosoHandle, StateSnapshot } from 'react-virtuoso';
|
||||
import useCatalogFeedRows from '../../hooks/use-catalog-feed-rows';
|
||||
import { useDirectories, useDirectoryByAddress } from '../../hooks/use-directories';
|
||||
import { useBoardFeedPageSize } from '../../hooks/use-board-feed-page-size';
|
||||
import { useFilteredDirectoryAddresses } from '../../hooks/use-filtered-directory-addresses';
|
||||
@@ -27,6 +26,9 @@ import { isCommentArchived } from '../../lib/utils/comment-moderation-utils';
|
||||
import { sortCatalogFeedForDisplay } from '../../lib/utils/catalog-sort';
|
||||
|
||||
const lastVirtuosoStates: { [key: string]: StateSnapshot } = {};
|
||||
const RECENT_ACCOUNT_COMMENT_WINDOW_SECONDS = 60 * 60;
|
||||
// Keep the hook on its indexed fast path when this view should not inject local posts.
|
||||
const EMPTY_ACCOUNT_COMMENT_LOOKUP = { commentIndices: [-1] };
|
||||
|
||||
interface CatalogFooterProps {
|
||||
communityAddresses: string[];
|
||||
@@ -281,7 +283,18 @@ const Catalog = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp,
|
||||
}, [communityAddresses, feedSortType, isMultiboard, paginationFeedPostsPerPage, filterItems, searchText, communityAddress, handleFilterMatch]);
|
||||
|
||||
const { feed, hasMore, loadMore, reset } = useFeed(feedOptions);
|
||||
const { accountComments } = useAccountComments();
|
||||
const accountCommentLookupOptions = useMemo(
|
||||
() =>
|
||||
communityAddress
|
||||
? {
|
||||
communityAddress,
|
||||
newerThan: RECENT_ACCOUNT_COMMENT_WINDOW_SECONDS,
|
||||
sortType: 'old' as const,
|
||||
}
|
||||
: EMPTY_ACCOUNT_COMMENT_LOOKUP,
|
||||
[communityAddress],
|
||||
);
|
||||
const { accountComments: recentAccountComments } = useAccountComments(accountCommentLookupOptions);
|
||||
|
||||
const resetTriggeredRef = useRef(false);
|
||||
|
||||
@@ -289,7 +302,7 @@ const Catalog = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp,
|
||||
const feedCids = useMemo(() => new Set(feed.map((f) => f.cid)), [feed]);
|
||||
const filteredComments = useMemo(
|
||||
() =>
|
||||
accountComments.filter((comment) => {
|
||||
recentAccountComments.filter((comment) => {
|
||||
const { cid, deleted, postCid, removed, state, timestamp } = comment || {};
|
||||
const commentCommunityAddress = comment?.communityAddress || comment?.subplebbitAddress;
|
||||
|
||||
@@ -297,7 +310,7 @@ const Catalog = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp,
|
||||
const basicConditions =
|
||||
!deleted &&
|
||||
!removed &&
|
||||
timestamp > Date.now() / 1000 - 60 * 60 &&
|
||||
timestamp > Date.now() / 1000 - RECENT_ACCOUNT_COMMENT_WINDOW_SECONDS &&
|
||||
state === 'succeeded' &&
|
||||
cid &&
|
||||
cid === postCid &&
|
||||
@@ -315,7 +328,7 @@ const Catalog = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp,
|
||||
|
||||
return basicConditions;
|
||||
}),
|
||||
[accountComments, communityAddress, feedCids, searchText],
|
||||
[recentAccountComments, communityAddress, feedCids, searchText],
|
||||
);
|
||||
|
||||
// show newest account comment at the top of the feed but after pinned posts
|
||||
@@ -427,7 +440,18 @@ const Catalog = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp,
|
||||
return [...topPosts, ...regularPosts];
|
||||
}, [sortedFeed, filterItems]);
|
||||
|
||||
const rows = useCatalogFeedRows(columnCount, processedFeed, isFeedLoaded, community);
|
||||
const rows = useMemo(() => {
|
||||
if (!isFeedLoaded) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const effectiveColumnCount = Math.max(columnCount, 1);
|
||||
const nextRows = [];
|
||||
for (let i = 0; i < processedFeed.length; i += effectiveColumnCount) {
|
||||
nextRows.push(processedFeed.slice(i, i + effectiveColumnCount));
|
||||
}
|
||||
return nextRows;
|
||||
}, [columnCount, isFeedLoaded, processedFeed]);
|
||||
|
||||
const virtuosoRef = useRef<VirtuosoHandle | null>(null);
|
||||
const virtuosoStateKey = feedCacheKey ? `${feedCacheKey}-${sortType}` : `${location.pathname}-${sortType}-catalog`;
|
||||
|
||||
@@ -33,6 +33,7 @@ vi.mock('react-router-dom', async () => {
|
||||
});
|
||||
|
||||
vi.mock('@bitsocialnet/bitsocial-react-hooks', () => ({
|
||||
useAccount: () => undefined,
|
||||
useAccountComment: () => testState.post,
|
||||
useAccountComments: () => ({
|
||||
accountComments: testState.accountComments,
|
||||
@@ -117,6 +118,24 @@ describe('PendingPost', () => {
|
||||
expect(testState.navigateMock).toHaveBeenCalledWith('/not-found', { replace: true });
|
||||
});
|
||||
|
||||
it('redirects malformed pending indices to not found', async () => {
|
||||
testState.accountCommentIndex = '1abc';
|
||||
testState.accountComments = [{}, {}];
|
||||
|
||||
await renderPendingPost();
|
||||
|
||||
expect(testState.navigateMock).toHaveBeenCalledWith('/not-found', { replace: true });
|
||||
});
|
||||
|
||||
it('redirects out-of-range pending indices to not found', async () => {
|
||||
testState.accountCommentIndex = '2';
|
||||
testState.accountComments = [{}, {}];
|
||||
|
||||
await renderPendingPost();
|
||||
|
||||
expect(testState.navigateMock).toHaveBeenCalledWith('/not-found', { replace: true });
|
||||
});
|
||||
|
||||
it('redirects resolved pending posts to the canonical thread route', async () => {
|
||||
testState.accountCommentIndex = '1';
|
||||
testState.accountComments = [{}, {}];
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { useAccountComment, useAccountComments } from '@bitsocialnet/bitsocial-react-hooks';
|
||||
import { useAccountComments } from '@bitsocialnet/bitsocial-react-hooks';
|
||||
import { useDirectories } from '../../hooks/use-directories';
|
||||
import useSafeAccountComment from '../../hooks/use-safe-account-comment';
|
||||
import { getBoardPath } from '../../lib/utils/route-utils';
|
||||
import { Post } from '../post';
|
||||
|
||||
const PendingPost = () => {
|
||||
const { accountComments } = useAccountComments();
|
||||
const { accountCommentIndex } = useParams<{ accountCommentIndex?: string }>();
|
||||
const commentIndex = accountCommentIndex ? parseInt(accountCommentIndex) : undefined;
|
||||
const post = useAccountComment({ commentIndex });
|
||||
const normalizedAccountCommentIndex = accountCommentIndex === undefined ? undefined : Number(accountCommentIndex);
|
||||
const hasNormalizedAccountCommentIndex = normalizedAccountCommentIndex !== undefined && !Number.isNaN(normalizedAccountCommentIndex);
|
||||
const post = useSafeAccountComment({ commentIndex: accountCommentIndex });
|
||||
const navigate = useNavigate();
|
||||
const directories = useDirectories();
|
||||
|
||||
@@ -17,10 +19,10 @@ const PendingPost = () => {
|
||||
|
||||
const isValidAccountCommentIndex =
|
||||
!accountCommentIndex ||
|
||||
(!isNaN(parseInt(accountCommentIndex)) &&
|
||||
parseInt(accountCommentIndex) >= 0 &&
|
||||
Number.isInteger(parseFloat(accountCommentIndex)) &&
|
||||
(accountComments?.length === 0 || parseInt(accountCommentIndex) <= accountComments.length));
|
||||
(hasNormalizedAccountCommentIndex &&
|
||||
normalizedAccountCommentIndex >= 0 &&
|
||||
Number.isInteger(normalizedAccountCommentIndex) &&
|
||||
(accountComments?.length === 0 || normalizedAccountCommentIndex < accountComments.length));
|
||||
|
||||
useEffect(() => {
|
||||
if (!isValidAccountCommentIndex) {
|
||||
|
||||
Reference in New Issue
Block a user