mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
fix: consume enriched account comments directly (#1178)
* fix: consume enriched account comments directly * fix(account comments): use empty lookup patch
This commit is contained in:
+3
-3
@@ -1,7 +1,7 @@
|
||||
import { lazy, Suspense, useEffect } from 'react';
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
import { Navigate, Outlet, Route, Routes, useLocation, useParams } from 'react-router-dom';
|
||||
import { useAccount, useCommunity } from '@bitsocial/bitsocial-react-hooks';
|
||||
import { useAccount, useAccountComment, useCommunity } from '@bitsocial/bitsocial-react-hooks';
|
||||
import { initSnow, removeSnow, shouldShowSnow } from './lib/snow';
|
||||
import { isAllView, isCatalogView, isModView, isSubscriptionsView } from './lib/utils/view-utils';
|
||||
import { preloadReplyModal, preloadThemeAssets } from './lib/utils/preload-utils';
|
||||
@@ -16,8 +16,8 @@ import { useDirectories } from './hooks/use-directories';
|
||||
import { useBrowserPureP2PAccountUpgrade } from './hooks/use-browser-pure-p2p-account-upgrade';
|
||||
import { useCommunityIdentifier } from './hooks/use-community-identifiers';
|
||||
import { useResolvedCommunityAddress, useResolvedDirectoryBoardPath } from './hooks/use-resolved-community-address';
|
||||
import useSafeAccountComment from './hooks/use-safe-account-comment';
|
||||
import useSuspendOffscreenMediaPlayback from './hooks/use-suspend-offscreen-media-playback';
|
||||
import { normalizeAccountCommentIndex } from './lib/utils/account-comment-index-utils';
|
||||
import { getCommentCommunityAddress } from './lib/utils/comment-utils';
|
||||
import {
|
||||
getBoardPath,
|
||||
@@ -82,7 +82,7 @@ const BoardLayout = () => {
|
||||
const directories = useDirectories();
|
||||
const communityAddress = useResolvedCommunityAddress(boardIdentifier);
|
||||
const { boardPath: resolvedDirectoryBoardPath, isDirectoryCandidate } = useResolvedDirectoryBoardPath(boardIdentifier);
|
||||
const pendingPost = useSafeAccountComment({ commentIndex: accountCommentIndex });
|
||||
const pendingPost = useAccountComment({ commentIndex: normalizeAccountCommentIndex(accountCommentIndex) });
|
||||
const pendingPostCommunityAddress = getCommentCommunityAddress(pendingPost);
|
||||
const { closeCreateBoardModal } = useCreateBoardModalStore();
|
||||
const isOnPostRoute = isPostRoute(pathname);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link, useLocation, useNavigate, useParams } from 'react-router-dom';
|
||||
import { useAccount, useComment, useSubscribe } from '@bitsocial/bitsocial-react-hooks';
|
||||
import { useAccount, useAccountComment, useComment, useSubscribe } from '@bitsocial/bitsocial-react-hooks';
|
||||
import { isAllView, isCatalogView, isModView, isModQueueView, isPendingPostView, isPostPageView, isSubscriptionsView } from '../../lib/utils/view-utils';
|
||||
import { usePostPageNumber } from '../../hooks/use-post-page-number';
|
||||
import { useDirectories, useDirectoryByAddress } from '../../hooks/use-directories';
|
||||
@@ -9,7 +9,7 @@ import { useFilteredDirectoryAddresses } from '../../hooks/use-filtered-director
|
||||
import { getBoardPath, isDirectoryRoute } from '../../lib/utils/route-utils';
|
||||
import { useResolvedCommunityAddress } from '../../hooks/use-resolved-community-address';
|
||||
import { useCommunityIdentifier } from '../../hooks/use-community-identifiers';
|
||||
import useSafeAccountComment from '../../hooks/use-safe-account-comment';
|
||||
import { normalizeAccountCommentIndex } from '../../lib/utils/account-comment-index-utils';
|
||||
import useHiddenCatalogThreads from '../../hooks/use-hidden-catalog-threads';
|
||||
import useCatalogFiltersStore from '../../stores/use-catalog-filters-store';
|
||||
import useCatalogStyleStore from '../../stores/use-catalog-style-store';
|
||||
@@ -532,7 +532,7 @@ export const MobileBoardButtons = () => {
|
||||
const isInModView = isModView(location.pathname);
|
||||
const isInModQueueView = isModQueueView(location.pathname);
|
||||
|
||||
const accountComment = useSafeAccountComment({ commentIndex: params?.accountCommentIndex });
|
||||
const accountComment = useAccountComment({ commentIndex: normalizeAccountCommentIndex(params?.accountCommentIndex) });
|
||||
const resolvedAddress = useResolvedCommunityAddress();
|
||||
const communityAddress = resolvedAddress || accountComment?.communityAddress;
|
||||
|
||||
@@ -646,7 +646,7 @@ export const PostPageStats = () => {
|
||||
const autoUpdateEnabled = useThreadLiveUpdatesStore((state) => state.enabled);
|
||||
const commentCid = params?.commentCid as string | undefined;
|
||||
const resolvedAddress = useResolvedCommunityAddress();
|
||||
const accountComment = useSafeAccountComment({ commentIndex: params?.accountCommentIndex });
|
||||
const accountComment = useAccountComment({ commentIndex: normalizeAccountCommentIndex(params?.accountCommentIndex) });
|
||||
const communityAddress = resolvedAddress || accountComment?.communityAddress;
|
||||
const communityIdentifier = useCommunityIdentifier(communityAddress);
|
||||
|
||||
@@ -716,7 +716,7 @@ export const CatalogSearchResultsLabel = () => {
|
||||
export const DesktopBoardButtons = () => {
|
||||
const params = useParams();
|
||||
const location = useLocation();
|
||||
const accountComment = useSafeAccountComment({ commentIndex: params?.accountCommentIndex });
|
||||
const accountComment = useAccountComment({ commentIndex: normalizeAccountCommentIndex(params?.accountCommentIndex) });
|
||||
const resolvedAddress = useResolvedCommunityAddress();
|
||||
const communityAddress = resolvedAddress || accountComment?.communityAddress;
|
||||
const isInCatalogView = isCatalogView(location.pathname, params);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useLocation, useParams, useNavigate } from 'react-router-dom';
|
||||
import { useCommunity } from '@bitsocial/bitsocial-react-hooks';
|
||||
import { useAccountComment, useCommunity } from '@bitsocial/bitsocial-react-hooks';
|
||||
import { accountsStore as useAccountsStore } from '../../lib/bitsocial-internals/stores';
|
||||
import getShortAddress from '../../lib/get-short-address';
|
||||
import { useCommunityIdentifier } from '../../hooks/use-community-identifiers';
|
||||
@@ -11,7 +11,7 @@ import { isArchiveRoute, isDirectoryListRoute } from '../../lib/utils/route-util
|
||||
import styles from './board-header.module.css';
|
||||
import { useDirectories } from '../../hooks/use-directories';
|
||||
import { useResolvedCommunityAddress } from '../../hooks/use-resolved-community-address';
|
||||
import useSafeAccountComment from '../../hooks/use-safe-account-comment';
|
||||
import { normalizeAccountCommentIndex } from '../../lib/utils/account-comment-index-utils';
|
||||
import useIsMobile from '../../hooks/use-is-mobile';
|
||||
import useIsCommunityOffline from '../../hooks/use-is-community-offline';
|
||||
import { shouldShowSnow } from '../../lib/snow';
|
||||
@@ -54,7 +54,7 @@ const BoardHeader = () => {
|
||||
const isInModView = isModView(location.pathname);
|
||||
const isInArchiveView = isArchiveRoute(location.pathname);
|
||||
const isInDirectoryListView = isDirectoryListRoute(location.pathname);
|
||||
const accountComment = useSafeAccountComment({ commentIndex: params?.accountCommentIndex });
|
||||
const accountComment = useAccountComment({ commentIndex: normalizeAccountCommentIndex(params?.accountCommentIndex) });
|
||||
const resolvedAddress = useResolvedCommunityAddress();
|
||||
const communityAddress = resolvedAddress || accountComment?.communityAddress;
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { Link, useLocation, useNavigate, useParams } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useAccountComment } from '@bitsocial/bitsocial-react-hooks';
|
||||
import getShortAddress from '../../lib/get-short-address';
|
||||
import { accountsStore as useAccountsStore } from '../../lib/bitsocial-internals/stores';
|
||||
import { isAllView, isCatalogView, isModView, isSubscriptionsView } from '../../lib/utils/view-utils';
|
||||
import { useAccountCommunityAddresses } from '../../hooks/use-account-community-addresses';
|
||||
import { useDirectories, DirectoryCommunity } from '../../hooks/use-directories';
|
||||
import { useBoardPath, useResolvedCommunityAddress } from '../../hooks/use-resolved-community-address';
|
||||
import useSafeAccountComment from '../../hooks/use-safe-account-comment';
|
||||
import { normalizeAccountCommentIndex } from '../../lib/utils/account-comment-index-utils';
|
||||
import { getBoardPath, extractDirectoryFromTitle } from '../../lib/utils/route-utils';
|
||||
import { getCommentCommunityAddress } from '../../lib/utils/comment-utils';
|
||||
import useCreateBoardModalStore from '../../stores/use-create-board-modal-store';
|
||||
@@ -418,8 +419,7 @@ const BoardsBarMobile = ({ communityAddress }: { communityAddress?: string }) =>
|
||||
|
||||
const BoardsBar = () => {
|
||||
const params = useParams();
|
||||
const commentIndex = params?.accountCommentIndex ? parseInt(params.accountCommentIndex, 10) : undefined;
|
||||
const accountComment = useSafeAccountComment({ commentIndex });
|
||||
const accountComment = useAccountComment({ commentIndex: normalizeAccountCommentIndex(params?.accountCommentIndex) });
|
||||
const resolvedCommunityAddress = useResolvedCommunityAddress();
|
||||
const communityAddress = resolvedCommunityAddress || getCommentCommunityAddress(accountComment);
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useEffect, useRef, useState, useCallback, useMemo } from 'react';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import { Link, useLocation, useNavigationType, useParams } from 'react-router-dom';
|
||||
import { Virtuoso, VirtuosoHandle, StateSnapshot } from 'react-virtuoso';
|
||||
import { Comment, useEditedComment, useReplies, useAccount } from '@bitsocial/bitsocial-react-hooks';
|
||||
import { Comment, useAccount, useAccountComment, useEditedComment, useReplies } from '@bitsocial/bitsocial-react-hooks';
|
||||
import getShortAddress from '../../lib/get-short-address';
|
||||
import styles from '../../views/post/post.module.css';
|
||||
import { CommentMediaInfo, getHasThumbnail, getMediaDimensions, getPostMediaTypeLabel, getYouTubeEmbedPostMediaFileLink } from '../../lib/utils/media-utils';
|
||||
@@ -22,7 +22,6 @@ import useFetchGifFirstFrame from '../../hooks/use-fetch-gif-first-frame';
|
||||
import useHide from '../../hooks/use-hide';
|
||||
import useStateString from '../../hooks/use-state-string';
|
||||
import useScrollToReply from '../../hooks/use-scroll-to-reply';
|
||||
import useSafeAccountComment from '../../hooks/use-safe-account-comment';
|
||||
import { useCurrentTime } from '../../hooks/use-current-time';
|
||||
import { useBoardPseudonymityMode } from '../../hooks/use-board-pseudonymity-mode';
|
||||
import CommentContent from '../comment-content/comment-content';
|
||||
@@ -689,7 +688,7 @@ const Reply = ({
|
||||
postsByAuthorInThread,
|
||||
disableDeferredLayout,
|
||||
}: PostProps & { directRepliesByParentCid?: Map<string, Comment[]>; postsByAuthorInThread?: Map<string, number>; disableDeferredLayout?: boolean }) => {
|
||||
const accountReply = useSafeAccountComment({
|
||||
const accountReply = useAccountComment({
|
||||
commentCid: reply?.cid,
|
||||
commentIndex: typeof reply?.index === 'number' ? reply.index : undefined,
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ import { type ReactNode, useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import type { TFunction } from 'i18next';
|
||||
import { Link, useLocation, useNavigate, useParams } from 'react-router-dom';
|
||||
import { Comment, setAccount, useAccount, useEditedComment } from '@bitsocial/bitsocial-react-hooks';
|
||||
import { Comment, setAccount, useAccount, useAccountComment, useEditedComment } from '@bitsocial/bitsocial-react-hooks';
|
||||
import getShortAddress from '../../lib/get-short-address';
|
||||
import { communitiesPagesStore as useCommunitiesPagesStore } from '../../lib/bitsocial-internals/stores';
|
||||
import { getDisplayMediaInfoType, getLinkMediaInfo, getTwimgMediaFilePublishUrl } from '../../lib/utils/media-utils';
|
||||
@@ -41,7 +41,7 @@ import { useDirectoryEntry } from '../../hooks/use-directory-entry';
|
||||
import { useCommunityField } from '../../hooks/use-stable-community';
|
||||
import useIsMobile from '../../hooks/use-is-mobile';
|
||||
import { useResolvedCommunityAddress } from '../../hooks/use-resolved-community-address';
|
||||
import useSafeAccountComment from '../../hooks/use-safe-account-comment';
|
||||
import { normalizeAccountCommentIndex } from '../../lib/utils/account-comment-index-utils';
|
||||
import useFetchGifFirstFrame from '../../hooks/use-fetch-gif-first-frame';
|
||||
import { useYouTubeThumbnailLinkConversion } from '../../hooks/use-youtube-thumbnail-link-conversion';
|
||||
import usePublishSubmissionGuard from '../../hooks/use-publish-submission-guard';
|
||||
@@ -544,7 +544,7 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
|
||||
const [url, setUrl] = useState('');
|
||||
const author = account?.author || {};
|
||||
const { displayName } = author || {};
|
||||
const accountComment = useSafeAccountComment({ commentIndex: params?.accountCommentIndex });
|
||||
const accountComment = useAccountComment({ commentIndex: normalizeAccountCommentIndex(params?.accountCommentIndex) });
|
||||
const resolvedAddress = useResolvedCommunityAddress();
|
||||
const communityAddress = resolvedAddress || accountComment?.communityAddress;
|
||||
const { setPublishPostOptions, postIndex, publishPost, publishPostError, publishPostOptions, resetPublishPostOptions } = usePublishPost({
|
||||
@@ -1028,7 +1028,7 @@ const PostForm = () => {
|
||||
|
||||
const [showForm, setShowForm] = useState(false);
|
||||
|
||||
const accountComment = useSafeAccountComment({ commentIndex: params?.accountCommentIndex });
|
||||
const accountComment = useAccountComment({ commentIndex: normalizeAccountCommentIndex(params?.accountCommentIndex) });
|
||||
const resolvedAddress = useResolvedCommunityAddress();
|
||||
const communityAddress = resolvedAddress || accountComment?.communityAddress;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useEffect, useRef, useState, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link, useLocation, useNavigationType, useParams } from 'react-router-dom';
|
||||
import { Virtuoso, VirtuosoHandle, StateSnapshot } from 'react-virtuoso';
|
||||
import { Comment, useEditedComment, useReplies, useAccount } from '@bitsocial/bitsocial-react-hooks';
|
||||
import { Comment, useAccount, useAccountComment, useEditedComment, useReplies } from '@bitsocial/bitsocial-react-hooks';
|
||||
import getShortAddress from '../../lib/get-short-address';
|
||||
import styles from '../../views/post/post.module.css';
|
||||
import { shouldShowSnow } from '../../lib/snow';
|
||||
@@ -21,7 +21,6 @@ import useCountLinksInReplies from '../../hooks/use-count-links-in-replies';
|
||||
import useHide from '../../hooks/use-hide';
|
||||
import useStateString from '../../hooks/use-state-string';
|
||||
import useScrollToReply from '../../hooks/use-scroll-to-reply';
|
||||
import useSafeAccountComment from '../../hooks/use-safe-account-comment';
|
||||
import { useCurrentTime } from '../../hooks/use-current-time';
|
||||
import { useBoardPseudonymityMode } from '../../hooks/use-board-pseudonymity-mode';
|
||||
import CommentContent from '../comment-content/comment-content';
|
||||
@@ -464,7 +463,7 @@ const Reply = ({
|
||||
postsByAuthorInThread,
|
||||
disableDeferredLayout,
|
||||
}: PostProps & { directRepliesByParentCid?: Map<string, Comment[]>; postsByAuthorInThread?: Map<string, number>; disableDeferredLayout?: boolean }) => {
|
||||
const accountReply = useSafeAccountComment({
|
||||
const accountReply = useAccountComment({
|
||||
commentCid: reply?.cid,
|
||||
commentIndex: typeof reply?.index === 'number' ? reply.index : undefined,
|
||||
});
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { Link, useLocation, useNavigate } from 'react-router-dom';
|
||||
import { Comment, useAccount } from '@bitsocial/bitsocial-react-hooks';
|
||||
import { Comment, useAccount, useAccountComment } from '@bitsocial/bitsocial-react-hooks';
|
||||
import { useFloating, offset, shift, size, autoUpdate, Placement } from '@floating-ui/react';
|
||||
import { useDirectories } from '../../hooks/use-directories';
|
||||
import useSafeAccountComment from '../../hooks/use-safe-account-comment';
|
||||
import { getBoardPath } from '../../lib/utils/route-utils';
|
||||
import { formatQuoteNumber, getQuoteTargetAvailability, shouldShowFloatingQuotePreview } from '../../lib/utils/quote-link-utils';
|
||||
import { findPreferredScrollTarget, getThreadTopNavigationState, scrollThreadContainerToTop } from '../../lib/utils/thread-scroll-utils';
|
||||
@@ -90,7 +89,7 @@ const scrollToReplyOnPage = (cid: string) => {
|
||||
|
||||
const useIsOwnQuotelink = (quotelinkReply?: Comment) => {
|
||||
const account = useAccount();
|
||||
const ownQuotelink = useSafeAccountComment({ commentCid: quotelinkReply?.cid });
|
||||
const ownQuotelink = useAccountComment({ commentCid: quotelinkReply?.cid });
|
||||
const quotedAuthorAddress = quotelinkReply?.author?.address;
|
||||
const accountAuthorAddress = account?.author?.address;
|
||||
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import { createElement } from 'react';
|
||||
import { createRoot, type Root } from 'react-dom/client';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import useSafeAccountComment from '../use-safe-account-comment';
|
||||
|
||||
(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
||||
const act = (React as { act?: (cb: () => void | Promise<void>) => void | Promise<void> }).act as (cb: () => void | Promise<void>) => void | Promise<void>;
|
||||
|
||||
const testState = vi.hoisted(() => ({
|
||||
account: undefined as { id?: string; name?: string } | undefined,
|
||||
accountCommentResult: { cid: 'account-comment' } as { cid?: string },
|
||||
calls: [] as Array<{ accountName?: string; commentCid?: string; commentIndex?: number }>,
|
||||
options: undefined as { accountName?: string; commentCid?: string; commentIndex?: number | string } | undefined,
|
||||
}));
|
||||
|
||||
vi.mock('@bitsocial/bitsocial-react-hooks', () => ({
|
||||
useAccount: (options?: { accountName?: string }) => {
|
||||
if (!options?.accountName) {
|
||||
return testState.account;
|
||||
}
|
||||
|
||||
return testState.account?.name === options.accountName ? testState.account : undefined;
|
||||
},
|
||||
useAccountComment: (options?: { accountName?: string; commentCid?: string; commentIndex?: number }) => {
|
||||
testState.calls.push(options || {});
|
||||
return testState.accountCommentResult;
|
||||
},
|
||||
}));
|
||||
|
||||
let container: HTMLDivElement;
|
||||
let latestValue: ReturnType<typeof useSafeAccountComment>;
|
||||
let root: Root;
|
||||
|
||||
const HookHarness = () => {
|
||||
latestValue = useSafeAccountComment(testState.options);
|
||||
return null;
|
||||
};
|
||||
|
||||
const renderHook = () => {
|
||||
act(() => {
|
||||
root.render(createElement(HookHarness));
|
||||
});
|
||||
};
|
||||
|
||||
describe('useSafeAccountComment', () => {
|
||||
beforeEach(() => {
|
||||
testState.account = undefined;
|
||||
testState.accountCommentResult = { cid: 'account-comment' };
|
||||
testState.calls = [];
|
||||
testState.options = undefined;
|
||||
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
root = createRoot(container);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
act(() => root.unmount());
|
||||
container.remove();
|
||||
});
|
||||
|
||||
it('uses a sentinel lookup when there is no active account and no usable lookup input', () => {
|
||||
renderHook();
|
||||
|
||||
expect(testState.calls).toEqual([{ commentIndex: -1 }]);
|
||||
expect(latestValue?.cid).toBe('account-comment');
|
||||
});
|
||||
|
||||
it('normalizes numeric comment indices before delegating to useAccountComment', () => {
|
||||
testState.options = { commentIndex: '7' };
|
||||
|
||||
renderHook();
|
||||
|
||||
expect(testState.calls).toEqual([{ commentIndex: 7 }]);
|
||||
});
|
||||
|
||||
it('falls back to the sentinel lookup for malformed string indices', () => {
|
||||
testState.options = { commentIndex: '7abc' };
|
||||
|
||||
renderHook();
|
||||
|
||||
expect(testState.calls).toEqual([{ commentIndex: -1 }]);
|
||||
});
|
||||
|
||||
it('falls back to the sentinel lookup when cid lookup is requested before an account exists', () => {
|
||||
testState.options = { commentCid: 'reply-cid' };
|
||||
|
||||
renderHook();
|
||||
|
||||
expect(testState.calls).toEqual([{ commentIndex: -1 }]);
|
||||
});
|
||||
|
||||
it('passes comment cid lookups through once the active account exists', () => {
|
||||
testState.account = { id: 'account-1', name: 'Account 1' };
|
||||
testState.options = { commentCid: 'reply-cid' };
|
||||
|
||||
renderHook();
|
||||
|
||||
expect(testState.calls).toEqual([{ commentCid: 'reply-cid' }]);
|
||||
});
|
||||
});
|
||||
@@ -1,47 +0,0 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useAccount, useAccountComment } from '@bitsocial/bitsocial-react-hooks';
|
||||
|
||||
type SafeAccountCommentOptions = {
|
||||
accountName?: string;
|
||||
commentCid?: string;
|
||||
commentIndex?: number | string;
|
||||
};
|
||||
|
||||
const EMPTY_ACCOUNT_COMMENT_LOOKUP = Object.freeze({ commentIndex: -1 as const });
|
||||
|
||||
const normalizeCommentIndex = (commentIndex: SafeAccountCommentOptions['commentIndex']) => {
|
||||
if (commentIndex === undefined || commentIndex === null || commentIndex === '') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const normalizedCommentIndex = Number(commentIndex);
|
||||
|
||||
return Number.isInteger(normalizedCommentIndex) && normalizedCommentIndex >= 0 ? normalizedCommentIndex : undefined;
|
||||
};
|
||||
|
||||
const useSafeAccountComment = (options?: SafeAccountCommentOptions) => {
|
||||
const account = useAccount(options?.accountName ? { accountName: options.accountName } : undefined);
|
||||
const normalizedCommentIndex = normalizeCommentIndex(options?.commentIndex);
|
||||
|
||||
const safeOptions = useMemo(() => {
|
||||
if (typeof normalizedCommentIndex === 'number') {
|
||||
return {
|
||||
...(options?.accountName ? { accountName: options.accountName } : {}),
|
||||
commentIndex: normalizedCommentIndex,
|
||||
};
|
||||
}
|
||||
|
||||
if (options?.commentCid && account?.id) {
|
||||
return {
|
||||
...(options?.accountName ? { accountName: options.accountName } : {}),
|
||||
commentCid: options.commentCid,
|
||||
};
|
||||
}
|
||||
|
||||
return EMPTY_ACCOUNT_COMMENT_LOOKUP;
|
||||
}, [account?.id, normalizedCommentIndex, options?.accountName, options?.commentCid]);
|
||||
|
||||
return useAccountComment(safeOptions);
|
||||
};
|
||||
|
||||
export default useSafeAccountComment;
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import { useLocation, useParams } from 'react-router-dom';
|
||||
import { useAccountComment } from '@bitsocial/bitsocial-react-hooks';
|
||||
import { isAllView, isModView, isSubscriptionsView } from '../lib/utils/view-utils';
|
||||
import useThemeStore from '../stores/use-theme-store';
|
||||
import { useDirectories } from './use-directories';
|
||||
@@ -7,8 +8,8 @@ import { useResolvedCommunityAddress } from './use-resolved-community-address';
|
||||
import useSpecialThemeStore from '../stores/use-special-theme-store';
|
||||
import { getActiveSpecialTheme, getSpecialThemeClass } from '../lib/utils/time-utils';
|
||||
import { isSfwBoard, updateFavicon } from '../lib/update-favicon';
|
||||
import useSafeAccountComment from './use-safe-account-comment';
|
||||
import { getCommentCommunityAddress } from '../lib/utils/comment-utils';
|
||||
import { normalizeAccountCommentIndex } from '../lib/utils/account-comment-index-utils';
|
||||
|
||||
const themeClasses = ['yotsuba', 'yotsuba-b', 'futaba', 'burichan', 'tomorrow', 'photon', 'spooky'];
|
||||
|
||||
@@ -23,8 +24,7 @@ const useTheme = (): [string, (theme: string) => void] => {
|
||||
const location = useLocation();
|
||||
const params = useParams<{ boardIdentifier?: string }>();
|
||||
const pendingPostParams = useParams<{ accountCommentIndex?: string }>();
|
||||
const pendingPostCommentIndex = pendingPostParams?.accountCommentIndex ? parseInt(pendingPostParams.accountCommentIndex, 10) : undefined;
|
||||
const pendingPost = useSafeAccountComment({ commentIndex: pendingPostCommentIndex });
|
||||
const pendingPost = useAccountComment({ commentIndex: normalizeAccountCommentIndex(pendingPostParams?.accountCommentIndex) });
|
||||
const pendingPostCommunityAddress = getCommentCommunityAddress(pendingPost);
|
||||
|
||||
const { isEnabled, setIsEnabled } = useSpecialThemeStore();
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { normalizeAccountCommentIndex } from '../account-comment-index-utils';
|
||||
|
||||
describe('normalizeAccountCommentIndex', () => {
|
||||
it('accepts non-negative integer numbers and strings', () => {
|
||||
expect(normalizeAccountCommentIndex(0)).toBe(0);
|
||||
expect(normalizeAccountCommentIndex(7)).toBe(7);
|
||||
expect(normalizeAccountCommentIndex('0')).toBe(0);
|
||||
expect(normalizeAccountCommentIndex('7')).toBe(7);
|
||||
});
|
||||
|
||||
it('rejects missing, negative, fractional, and malformed values', () => {
|
||||
expect(normalizeAccountCommentIndex(undefined)).toBeUndefined();
|
||||
expect(normalizeAccountCommentIndex(null)).toBeUndefined();
|
||||
expect(normalizeAccountCommentIndex('')).toBeUndefined();
|
||||
expect(normalizeAccountCommentIndex(-1)).toBeUndefined();
|
||||
expect(normalizeAccountCommentIndex('1.5')).toBeUndefined();
|
||||
expect(normalizeAccountCommentIndex('1abc')).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@@ -1,51 +0,0 @@
|
||||
import type { Account } from '@bitsocial/bitsocial-react-hooks';
|
||||
|
||||
type CommentAuthor = {
|
||||
address?: string;
|
||||
avatar?: unknown;
|
||||
displayName?: string;
|
||||
flair?: unknown;
|
||||
shortAddress?: string;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
|
||||
type AccountCommentWithAuthor = {
|
||||
accountId?: string;
|
||||
author?: CommentAuthor;
|
||||
};
|
||||
|
||||
export const mergeDefinedFields = <T extends object>(base: T | undefined, override: T | undefined): T | undefined => {
|
||||
if (!override) return base;
|
||||
|
||||
const merged = { ...base } as Record<string, unknown>;
|
||||
for (const [key, value] of Object.entries(override)) {
|
||||
if (value !== undefined) {
|
||||
merged[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return merged as T;
|
||||
};
|
||||
|
||||
export function restoreActiveAccountAuthor<T extends object>(accountComment: T, account: Account | undefined): T;
|
||||
export function restoreActiveAccountAuthor<T extends object>(accountComment: T | undefined, account: Account | undefined): T | undefined;
|
||||
export function restoreActiveAccountAuthor<T extends object>(accountComment: T | undefined, account: Account | undefined): T | undefined {
|
||||
const comment = accountComment as AccountCommentWithAuthor | undefined;
|
||||
|
||||
if (!comment || comment.author?.address || !account?.id || comment.accountId !== account.id || !account.author?.address) {
|
||||
return accountComment;
|
||||
}
|
||||
|
||||
const accountAuthor = {
|
||||
address: account.author.address,
|
||||
shortAddress: account.author.shortAddress,
|
||||
displayName: account.author.displayName,
|
||||
avatar: account.author.avatar,
|
||||
flair: account.author.flair,
|
||||
};
|
||||
|
||||
return {
|
||||
...accountComment,
|
||||
author: mergeDefinedFields(comment.author, accountAuthor),
|
||||
} as T;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export const normalizeAccountCommentIndex = (commentIndex: number | string | null | undefined): number | undefined => {
|
||||
if (commentIndex === undefined || commentIndex === null || commentIndex === '') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const normalizedCommentIndex = Number(commentIndex);
|
||||
return Number.isInteger(normalizedCommentIndex) && normalizedCommentIndex >= 0 ? normalizedCommentIndex : undefined;
|
||||
};
|
||||
@@ -142,6 +142,24 @@ const getScopedAccountComments = (options?: { commentIndices?: number[]; communi
|
||||
return scopedComments;
|
||||
};
|
||||
|
||||
const enrichAccountCommentAuthor = (comment: TestComment): TestComment => {
|
||||
if (comment.author?.address || !comment.accountId || comment.accountId !== testState.account.id || !testState.account.author?.address) {
|
||||
return comment;
|
||||
}
|
||||
|
||||
const accountAuthor = testState.account.author;
|
||||
|
||||
return {
|
||||
...comment,
|
||||
author: {
|
||||
...accountAuthor,
|
||||
...comment.author,
|
||||
address: accountAuthor.address,
|
||||
...(accountAuthor.shortAddress ? { shortAddress: accountAuthor.shortAddress } : {}),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const getScopedFeed = (options?: { filter?: { filter: (comment: TestComment) => boolean }; newerThan?: number; postsPerPage?: number }) => {
|
||||
let scopedFeed = [...testState.feed];
|
||||
|
||||
@@ -165,7 +183,7 @@ vi.mock('@bitsocial/bitsocial-react-hooks', () => ({
|
||||
useAccount: () => testState.account,
|
||||
useAccountComments: (options?: { commentIndices?: number[]; communityAddress?: string; newerThan?: number; sortType?: 'new' | 'old' }) => {
|
||||
testState.accountCommentsCalls.push(options);
|
||||
return { accountComments: getScopedAccountComments(options) };
|
||||
return { accountComments: getScopedAccountComments(options).map(enrichAccountCommentAuthor) };
|
||||
},
|
||||
useFeed: (options?: {
|
||||
communities?: unknown[];
|
||||
|
||||
@@ -27,7 +27,6 @@ import { getPageSlice } from '../../lib/utils/board-feed-pagination';
|
||||
import { getPageFromFeedPath, isDirectoryBoard, normalizeMultiboardFeedPath, stripPageFromFeedPath } from '../../lib/utils/route-utils';
|
||||
import { isCommentArchived } from '../../lib/utils/comment-moderation-utils';
|
||||
import { getCommentCommunityAddress } from '../../lib/utils/comment-utils';
|
||||
import { restoreActiveAccountAuthor } from '../../lib/utils/account-comment-author-utils';
|
||||
import { getNonokoPendingAccountCommentIndex } from '../../lib/utils/post-options-utils';
|
||||
import { getRawBoardThreadState } from '../../lib/utils/raw-board-thread-state';
|
||||
import { getSearchWithTimeFilter, getTimeFilterSuggestion, type TimeFilterSuggestion } from '../../lib/utils/time-filter-utils';
|
||||
@@ -482,15 +481,11 @@ const Board = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp, t
|
||||
[recentAccountComments, communityAddress, feedCids, nowSeconds],
|
||||
);
|
||||
const localAccountComments = useMemo(() => {
|
||||
const comments = (() => {
|
||||
if (!nonokoPendingAccountComment) return filteredComments;
|
||||
if (!nonokoPendingAccountComment.cid) return [nonokoPendingAccountComment, ...filteredComments];
|
||||
if (!nonokoPendingAccountComment) return filteredComments;
|
||||
if (!nonokoPendingAccountComment.cid) return [nonokoPendingAccountComment, ...filteredComments];
|
||||
|
||||
return [nonokoPendingAccountComment, ...filteredComments.filter((comment) => comment.cid !== nonokoPendingAccountComment.cid)];
|
||||
})();
|
||||
|
||||
return comments.map((comment) => restoreActiveAccountAuthor(comment, account));
|
||||
}, [nonokoPendingAccountComment, filteredComments, account]);
|
||||
return [nonokoPendingAccountComment, ...filteredComments.filter((comment) => comment.cid !== nonokoPendingAccountComment.cid)];
|
||||
}, [nonokoPendingAccountComment, filteredComments]);
|
||||
|
||||
const sortedFeed = useMemo(() => sortBoardActiveFeed(feed), [feed]);
|
||||
const canShowRecentLocalAccountComments = !isSingleCommunityBoard || sortedFeed.length > 0 || isRawBoardThreadStateFullyLoaded;
|
||||
|
||||
@@ -15,6 +15,7 @@ type TestComment = {
|
||||
|
||||
const testState = vi.hoisted(() => ({
|
||||
accountCommentIndex: undefined as string | undefined,
|
||||
accountCommentLookupOptions: undefined as { commentIndex?: number } | undefined,
|
||||
accountComments: [] as TestComment[],
|
||||
challengeCount: 0,
|
||||
directories: [] as Array<{ address: string; title?: string }>,
|
||||
@@ -41,7 +42,10 @@ vi.mock('react-router-dom', async () => {
|
||||
|
||||
vi.mock('@bitsocial/bitsocial-react-hooks', () => ({
|
||||
useAccount: () => undefined,
|
||||
useAccountComment: () => testState.post,
|
||||
useAccountComment: (options?: { commentIndex?: number }) => {
|
||||
testState.accountCommentLookupOptions = options;
|
||||
return testState.post;
|
||||
},
|
||||
useAccountComments: () => ({
|
||||
accountComments: testState.accountComments,
|
||||
}),
|
||||
@@ -60,10 +64,11 @@ vi.mock('../../../stores/use-challenges-store', () => ({
|
||||
}));
|
||||
|
||||
vi.mock('../../../stores/use-failed-post-retry-store', () => ({
|
||||
default: (selector: (state: { retryingAccountCommentIndex: number | null }) => unknown) => selector({ retryingAccountCommentIndex: testState.retryingAccountCommentIndex }),
|
||||
default: (selector: (state: { retryingAccountCommentIndex: number | null }) => unknown) =>
|
||||
selector({ retryingAccountCommentIndex: testState.retryingAccountCommentIndex }),
|
||||
}));
|
||||
|
||||
vi.mock('../../post', () => ({
|
||||
vi.mock('../../post/post', () => ({
|
||||
Post: ({ post }: { post?: TestComment }) => createElement('div', { 'data-testid': 'post-view' }, post?.cid ?? 'no-post'),
|
||||
}));
|
||||
|
||||
@@ -91,6 +96,7 @@ describe('PendingPost', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
testState.accountCommentIndex = undefined;
|
||||
testState.accountCommentLookupOptions = undefined;
|
||||
testState.accountComments = [];
|
||||
testState.challengeCount = 0;
|
||||
testState.directories = [];
|
||||
@@ -127,6 +133,16 @@ describe('PendingPost', () => {
|
||||
expect(testState.navigateMock).not.toHaveBeenCalledWith('/not-found', { replace: true });
|
||||
});
|
||||
|
||||
it('passes normalized numeric-string pending indices to the account comment lookup', async () => {
|
||||
testState.accountCommentIndex = '01';
|
||||
testState.accountComments = [{}, {}];
|
||||
|
||||
await renderPendingPost();
|
||||
|
||||
expect(testState.accountCommentLookupOptions).toEqual({ commentIndex: 1 });
|
||||
expect(testState.navigateMock).not.toHaveBeenCalledWith('/not-found', { replace: true });
|
||||
});
|
||||
|
||||
it('redirects invalid pending indices to not found', async () => {
|
||||
testState.accountCommentIndex = '-1';
|
||||
testState.accountComments = [{}, {}];
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useLocation, useNavigate, useParams } from 'react-router-dom';
|
||||
import { useAccountComments } from '@bitsocial/bitsocial-react-hooks';
|
||||
import { useAccountComment, useAccountComments } from '@bitsocial/bitsocial-react-hooks';
|
||||
import { useDirectories } from '../../hooks/use-directories';
|
||||
import useSafeAccountComment from '../../hooks/use-safe-account-comment';
|
||||
import { normalizeAccountCommentIndex } from '../../lib/utils/account-comment-index-utils';
|
||||
import { getCommentCommunityAddress } from '../../lib/utils/comment-utils';
|
||||
import { getBoardPath } from '../../lib/utils/route-utils';
|
||||
import useChallengesStore from '../../stores/use-challenges-store';
|
||||
import useFailedPostRetryStore from '../../stores/use-failed-post-retry-store';
|
||||
import { Post } from '../post';
|
||||
import { Post } from '../post/post';
|
||||
|
||||
type PendingAccountComment = {
|
||||
index?: number;
|
||||
@@ -46,9 +46,9 @@ 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 normalizedAccountCommentIndex = normalizeAccountCommentIndex(accountCommentIndex);
|
||||
const hasNormalizedAccountCommentIndex = normalizedAccountCommentIndex !== undefined;
|
||||
const post = useAccountComment({ commentIndex: normalizedAccountCommentIndex });
|
||||
const postCommunityAddress = getCommentCommunityAddress(post);
|
||||
const hasAddressablePost = Boolean(post?.cid || postCommunityAddress);
|
||||
const navigate = useNavigate();
|
||||
@@ -70,11 +70,7 @@ const PendingPost = () => {
|
||||
}, [normalizedAccountCommentIndex, pendingBoardPath]);
|
||||
|
||||
const isValidAccountCommentIndex =
|
||||
!accountCommentIndex ||
|
||||
(hasNormalizedAccountCommentIndex &&
|
||||
normalizedAccountCommentIndex >= 0 &&
|
||||
Number.isInteger(normalizedAccountCommentIndex) &&
|
||||
hasPendingAccountCommentIndex(accountComments, normalizedAccountCommentIndex));
|
||||
!accountCommentIndex || (hasNormalizedAccountCommentIndex && hasPendingAccountCommentIndex(accountComments, normalizedAccountCommentIndex));
|
||||
|
||||
useEffect(() => {
|
||||
// A retry deletes this pending row before republishing, briefly invalidating the index. Stay put;
|
||||
|
||||
@@ -69,6 +69,26 @@ const testState = vi.hoisted(() => ({
|
||||
evictThreadRefreshCachesMock: vi.fn(),
|
||||
}));
|
||||
|
||||
const activeAccount = {
|
||||
author: { address: 'account-author' },
|
||||
id: 'active-account',
|
||||
};
|
||||
|
||||
const enrichAccountCommentAuthor = (comment: TestComment | undefined): TestComment | undefined => {
|
||||
if (!comment || comment.author?.address || comment.accountId !== activeAccount.id) {
|
||||
return comment;
|
||||
}
|
||||
|
||||
return {
|
||||
...comment,
|
||||
author: {
|
||||
...activeAccount.author,
|
||||
...comment.author,
|
||||
address: activeAccount.author.address,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
vi.mock('react-i18next', () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => key,
|
||||
@@ -84,8 +104,8 @@ vi.mock('react-router-dom', async () => {
|
||||
});
|
||||
|
||||
vi.mock('@bitsocial/bitsocial-react-hooks', () => ({
|
||||
useAccount: () => ({ id: 'active-account', author: { address: 'account-author' } }),
|
||||
useAccountComment: ({ commentCid }: { commentCid?: string }) => (commentCid ? testState.accountCommentsByCid[commentCid] : undefined),
|
||||
useAccount: () => activeAccount,
|
||||
useAccountComment: ({ commentCid }: { commentCid?: string }) => (commentCid ? enrichAccountCommentAuthor(testState.accountCommentsByCid[commentCid]) : undefined),
|
||||
useComment: ({ commentCid, autoUpdate, community }: { commentCid?: string; autoUpdate?: boolean; community?: { name?: string; publicKey?: string } }) => {
|
||||
testState.useCommentCalls.push({ commentCid, autoUpdate, community });
|
||||
return commentCid ? testState.commentsByCid[commentCid] : undefined;
|
||||
|
||||
+25
-7
@@ -1,6 +1,15 @@
|
||||
import { memo, useEffect, useMemo, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { type Comment, type CommunityIdentifier, type Role, useAccount, useComment, useEditedComment, useCommunity, useReplies } from '@bitsocial/bitsocial-react-hooks';
|
||||
import {
|
||||
type Comment,
|
||||
type CommunityIdentifier,
|
||||
type Role,
|
||||
useAccountComment,
|
||||
useComment,
|
||||
useEditedComment,
|
||||
useCommunity,
|
||||
useReplies,
|
||||
} from '@bitsocial/bitsocial-react-hooks';
|
||||
import { communitiesPagesStore as useCommunitiesPagesStore } from '../../lib/bitsocial-internals/stores';
|
||||
import { useCommunityField } from '../../hooks/use-stable-community';
|
||||
import { useLocation, useNavigate, useParams } from 'react-router-dom';
|
||||
@@ -11,7 +20,6 @@ import { useCommunityIdentifier } from '../../hooks/use-community-identifiers';
|
||||
import { isCommentArchived } from '../../lib/utils/comment-moderation-utils';
|
||||
import { areSameBoardAddress, isDirectoryBoard } from '../../lib/utils/route-utils';
|
||||
import { getCommentCommunityAddress } from '../../lib/utils/comment-utils';
|
||||
import { mergeDefinedFields, restoreActiveAccountAuthor } from '../../lib/utils/account-comment-author-utils';
|
||||
import useIsMobile from '../../hooks/use-is-mobile';
|
||||
import ErrorDisplay from '../../components/error-display/error-display';
|
||||
import { PageFooterDesktop, ThreadFooterFirstRow, ThreadFooterStyleRow, ThreadFooterMobile } from '../../components/footer/footer';
|
||||
@@ -21,7 +29,6 @@ import { getRequestedThreadTopCid, scrollThreadContainerToTop } from '../../lib/
|
||||
import { evictThreadRefreshCaches } from '../../lib/utils/thread-refresh-cache-utils';
|
||||
import { REPLIES_PER_PAGE } from '../../lib/constants';
|
||||
import useThreadLiveUpdatesStore from '../../stores/use-thread-live-updates-store';
|
||||
import useSafeAccountComment from '../../hooks/use-safe-account-comment';
|
||||
import type { QueuedCommentRouteState } from '../../lib/utils/mod-queue-utils';
|
||||
import type { ReplyVirtualizationMode } from '../../lib/utils/pretext-height-estimates';
|
||||
import styles from './post.module.css';
|
||||
@@ -102,6 +109,19 @@ const mergeCommentFallback = (comment: CommentWithRefresh | undefined, fallback:
|
||||
};
|
||||
};
|
||||
|
||||
const mergeDefinedFields = <T extends object>(base: T | undefined, override: T | undefined): T | undefined => {
|
||||
if (!override) return base;
|
||||
|
||||
const merged = { ...base } as Record<string, unknown>;
|
||||
for (const [key, value] of Object.entries(override)) {
|
||||
if (value !== undefined) {
|
||||
merged[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return merged as T;
|
||||
};
|
||||
|
||||
const mergeLocalCommentAuthor = (comment: CommentWithRefresh | undefined, localComment: CommentWithRefresh | undefined): CommentWithRefresh | undefined => {
|
||||
if (!localComment?.author) return comment;
|
||||
if (!comment) return localComment;
|
||||
@@ -130,9 +150,7 @@ const mergeLocalAccountComment = (comment: CommentWithRefresh | undefined, accou
|
||||
const useCommentWithFeedCache = (options: { commentCid: string | undefined; autoUpdate?: boolean; community?: CommunityIdentifier }): CommentWithRefresh | undefined => {
|
||||
const comment = useComment(options);
|
||||
const cachedComment = useCommunitiesPagesStore((state) => state.comments[options?.commentCid || '']);
|
||||
const account = useAccount();
|
||||
const accountComment = useSafeAccountComment({ commentCid: options.commentCid }) as CommentWithRefresh | undefined;
|
||||
const accountCommentWithAuthor = useMemo(() => restoreActiveAccountAuthor(accountComment, account), [accountComment, account]);
|
||||
const accountComment = useAccountComment({ commentCid: options.commentCid }) as CommentWithRefresh | undefined;
|
||||
|
||||
const commentWithFeedCache = useMemo(() => {
|
||||
if (!cachedComment || comment?.timestamp) return comment;
|
||||
@@ -145,7 +163,7 @@ const useCommentWithFeedCache = (options: { commentCid: string | undefined; auto
|
||||
} as CommentWithRefresh;
|
||||
}, [comment, cachedComment]);
|
||||
|
||||
return useMemo(() => mergeLocalAccountComment(commentWithFeedCache, accountCommentWithAuthor), [commentWithFeedCache, accountCommentWithAuthor]);
|
||||
return useMemo(() => mergeLocalAccountComment(commentWithFeedCache, accountComment), [commentWithFeedCache, accountComment]);
|
||||
};
|
||||
|
||||
const mergeRepliesWithQueuedReply = (replies: Comment[], queuedReply: CommentWithRefresh | undefined): Comment[] => {
|
||||
|
||||
Reference in New Issue
Block a user