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:
@@ -72,6 +72,7 @@ vi.mock('react-router-dom', async () => {
|
||||
});
|
||||
|
||||
vi.mock('@bitsocialnet/bitsocial-react-hooks', () => ({
|
||||
useAccount: () => undefined,
|
||||
useAccountComment: () => testState.accountComment,
|
||||
useComment: ({ commentCid }: { commentCid?: string }) => (commentCid ? testState.commentsByCid[commentCid] : undefined),
|
||||
useSubscribe: () => ({
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link, useLocation, useNavigate, useParams } from 'react-router-dom';
|
||||
import { useAccountComment, useComment, useSubscribe } from '@bitsocialnet/bitsocial-react-hooks';
|
||||
import { useComment, useSubscribe } from '@bitsocialnet/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';
|
||||
import { getBoardPath, isDirectoryBoard } from '../../lib/utils/route-utils';
|
||||
import { useResolvedCommunityAddress } from '../../hooks/use-resolved-community-address';
|
||||
import useSafeAccountComment from '../../hooks/use-safe-account-comment';
|
||||
import useCatalogFiltersStore from '../../stores/use-catalog-filters-store';
|
||||
import useCatalogStyleStore from '../../stores/use-catalog-style-store';
|
||||
import useFeedResetStore from '../../stores/use-feed-reset-store';
|
||||
@@ -387,7 +388,7 @@ export const MobileBoardButtons = () => {
|
||||
const isInModView = isModView(location.pathname);
|
||||
const isInModQueueView = isModQueueView(location.pathname);
|
||||
|
||||
const accountComment = useAccountComment({ commentIndex: params?.accountCommentIndex as any });
|
||||
const accountComment = useSafeAccountComment({ commentIndex: params?.accountCommentIndex });
|
||||
const resolvedAddress = useResolvedCommunityAddress();
|
||||
const communityAddress = resolvedAddress || accountComment?.communityAddress;
|
||||
|
||||
@@ -491,7 +492,7 @@ export const PostPageStats = () => {
|
||||
const autoUpdateEnabled = useThreadLiveUpdatesStore((state) => state.enabled);
|
||||
const commentCid = params?.commentCid as string | undefined;
|
||||
const resolvedAddress = useResolvedCommunityAddress();
|
||||
const accountComment = useAccountComment({ commentIndex: params?.accountCommentIndex as any });
|
||||
const accountComment = useSafeAccountComment({ commentIndex: params?.accountCommentIndex });
|
||||
const communityAddress = resolvedAddress || accountComment?.communityAddress;
|
||||
|
||||
const comment = useComment({ commentCid, autoUpdate: autoUpdateEnabled });
|
||||
@@ -535,7 +536,7 @@ export const DesktopBoardButtons = () => {
|
||||
const { t } = useTranslation();
|
||||
const params = useParams();
|
||||
const location = useLocation();
|
||||
const accountComment = useAccountComment({ commentIndex: params?.accountCommentIndex as any });
|
||||
const accountComment = useSafeAccountComment({ commentIndex: params?.accountCommentIndex });
|
||||
const resolvedAddress = useResolvedCommunityAddress();
|
||||
const communityAddress = resolvedAddress || accountComment?.communityAddress;
|
||||
const isInCatalogView = isCatalogView(location.pathname, params);
|
||||
|
||||
@@ -50,6 +50,7 @@ vi.mock('react-router-dom', async () => {
|
||||
});
|
||||
|
||||
vi.mock('@bitsocialnet/bitsocial-react-hooks', () => ({
|
||||
useAccount: () => undefined,
|
||||
useAccountComment: () => testState.accountComment,
|
||||
}));
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useLocation, useParams, useNavigate } from 'react-router-dom';
|
||||
import { useAccountComment } from '@bitsocialnet/bitsocial-react-hooks';
|
||||
import useAccountsStore from '@bitsocialnet/bitsocial-react-hooks/dist/stores/accounts';
|
||||
import useCommunitiesStore from '@bitsocialnet/bitsocial-react-hooks/dist/stores/communities';
|
||||
import getShortAddress from '../../lib/get-short-address';
|
||||
@@ -11,6 +10,7 @@ import { isArchiveRoute } from '../../lib/utils/route-utils';
|
||||
import styles from './board-header.module.css';
|
||||
import { useDirectoriesMetadata, useDirectories } from '../../hooks/use-directories';
|
||||
import { useResolvedCommunityAddress } from '../../hooks/use-resolved-community-address';
|
||||
import useSafeAccountComment from '../../hooks/use-safe-account-comment';
|
||||
import useIsMobile from '../../hooks/use-is-mobile';
|
||||
import useIsCommunityOffline from '../../hooks/use-is-community-offline';
|
||||
import { shouldShowSnow } from '../../lib/snow';
|
||||
@@ -53,7 +53,7 @@ const BoardHeader = () => {
|
||||
const isInSubscriptionsView = isSubscriptionsView(location.pathname, useParams());
|
||||
const isInModView = isModView(location.pathname);
|
||||
const isInArchiveView = isArchiveRoute(location.pathname);
|
||||
const accountComment = useAccountComment({ commentIndex: params?.accountCommentIndex as any });
|
||||
const accountComment = useSafeAccountComment({ commentIndex: params?.accountCommentIndex });
|
||||
const resolvedAddress = useResolvedCommunityAddress();
|
||||
const communityAddress = resolvedAddress || accountComment?.communityAddress;
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@ vi.mock('react-router-dom', async () => {
|
||||
});
|
||||
|
||||
vi.mock('@bitsocialnet/bitsocial-react-hooks', () => ({
|
||||
useAccount: () => undefined,
|
||||
useAccountComment: () => testState.accountComment,
|
||||
}));
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { Link, useLocation, useNavigate, useParams } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import getShortAddress from '../../lib/get-short-address';
|
||||
import { useAccountComment } from '@bitsocialnet/bitsocial-react-hooks';
|
||||
import useAccountsStore from '@bitsocialnet/bitsocial-react-hooks/dist/stores/accounts';
|
||||
import { isAllView, isCatalogView, isModView, isSubscriptionsView } from '../../lib/utils/view-utils';
|
||||
import { useAccountCommunityAddresses } from '../../hooks/use-account-community-addresses';
|
||||
import { useDirectories, useDirectoriesMetadata, DirectoryCommunity } from '../../hooks/use-directories';
|
||||
import { useBoardPath, useResolvedCommunityAddress } from '../../hooks/use-resolved-community-address';
|
||||
import useSafeAccountComment from '../../hooks/use-safe-account-comment';
|
||||
import { getBoardPath, extractDirectoryFromTitle } from '../../lib/utils/route-utils';
|
||||
import { getCommentCommunityAddress } from '../../lib/utils/comment-utils';
|
||||
import useCreateBoardModalStore from '../../stores/use-create-board-modal-store';
|
||||
@@ -414,8 +414,8 @@ const BoardsBarMobile = ({ communityAddress }: { communityAddress?: string }) =>
|
||||
|
||||
const BoardsBar = () => {
|
||||
const params = useParams();
|
||||
const commentIndex = params?.accountCommentIndex ? parseInt(params.accountCommentIndex) : undefined;
|
||||
const accountComment = useAccountComment({ commentIndex });
|
||||
const commentIndex = params?.accountCommentIndex ? parseInt(params.accountCommentIndex, 10) : undefined;
|
||||
const accountComment = useSafeAccountComment({ commentIndex });
|
||||
const resolvedCommunityAddress = useResolvedCommunityAddress();
|
||||
const communityAddress = resolvedCommunityAddress || getCommentCommunityAddress(accountComment);
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useEffect, useRef, useState, useCallback } 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, useAccountComment } from '@bitsocialnet/bitsocial-react-hooks';
|
||||
import { Comment, useEditedComment, useReplies, useAccount } from '@bitsocialnet/bitsocial-react-hooks';
|
||||
import getShortAddress from '../../lib/get-short-address';
|
||||
import styles from '../../views/post/post.module.css';
|
||||
import { CommentMediaInfo, getDisplayMediaInfoType, getHasThumbnail, getMediaDimensions } from '../../lib/utils/media-utils';
|
||||
@@ -22,6 +22,7 @@ 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';
|
||||
@@ -714,7 +715,7 @@ const Reply = ({
|
||||
directRepliesByParentCid,
|
||||
postsByAuthorInThread,
|
||||
}: PostProps & { directRepliesByParentCid?: Map<string, Comment[]>; postsByAuthorInThread?: Map<string, number> }) => {
|
||||
const accountReply = useAccountComment({
|
||||
const accountReply = useSafeAccountComment({
|
||||
commentIndex: typeof reply?.index === 'number' ? reply.index : undefined,
|
||||
});
|
||||
const hasReplyIndex = typeof reply?.index === 'number';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useLocation, useNavigate, useParams } from 'react-router-dom';
|
||||
import { Comment, setAccount, useAccount, useAccountComment, useEditedComment } from '@bitsocialnet/bitsocial-react-hooks';
|
||||
import { Comment, setAccount, useAccount, useEditedComment } from '@bitsocialnet/bitsocial-react-hooks';
|
||||
import getShortAddress from '../../lib/get-short-address';
|
||||
import useCommunitiesPagesStore from '@bitsocialnet/bitsocial-react-hooks/dist/stores/communities-pages';
|
||||
import { getLinkMediaInfo } from '../../lib/utils/media-utils';
|
||||
@@ -11,6 +11,7 @@ import { useAccountCommunityAddresses } from '../../hooks/use-account-community-
|
||||
import { useDirectories, useDirectoryByAddress } from '../../hooks/use-directories';
|
||||
import useIsMobile from '../../hooks/use-is-mobile';
|
||||
import { useResolvedCommunityAddress } from '../../hooks/use-resolved-community-address';
|
||||
import useSafeAccountComment from '../../hooks/use-safe-account-comment';
|
||||
import useFetchGifFirstFrame from '../../hooks/use-fetch-gif-first-frame';
|
||||
import usePublishPost from '../../hooks/use-publish-post';
|
||||
import usePublishReply from '../../hooks/use-publish-reply';
|
||||
@@ -300,7 +301,7 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
|
||||
const [url, setUrl] = useState('');
|
||||
const author = account?.author || {};
|
||||
const { displayName } = author || {};
|
||||
const accountComment = useAccountComment({ commentIndex: params?.accountCommentIndex as any });
|
||||
const accountComment = useSafeAccountComment({ commentIndex: params?.accountCommentIndex });
|
||||
const resolvedAddress = useResolvedCommunityAddress();
|
||||
const communityAddress = resolvedAddress || accountComment?.communityAddress;
|
||||
const { setPublishPostOptions, postIndex, publishPost, publishPostOptions, resetPublishPostOptions } = usePublishPost({ subplebbitAddress: communityAddress });
|
||||
@@ -532,7 +533,7 @@ const PostForm = () => {
|
||||
|
||||
const [showForm, setShowForm] = useState(false);
|
||||
|
||||
const accountComment = useAccountComment({ commentIndex: params?.accountCommentIndex as any });
|
||||
const accountComment = useSafeAccountComment({ commentIndex: params?.accountCommentIndex });
|
||||
const resolvedAddress = useResolvedCommunityAddress();
|
||||
const communityAddress = resolvedAddress || accountComment?.communityAddress;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useEffect, useRef, useState, useCallback } 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, usePublishCommentModeration, useAccountComment } from '@bitsocialnet/bitsocial-react-hooks';
|
||||
import { Comment, useEditedComment, useReplies, useAccount, usePublishCommentModeration } from '@bitsocialnet/bitsocial-react-hooks';
|
||||
import getShortAddress from '../../lib/get-short-address';
|
||||
import styles from '../../views/post/post.module.css';
|
||||
import { shouldShowSnow } from '../../lib/snow';
|
||||
@@ -21,6 +21,7 @@ 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';
|
||||
@@ -496,7 +497,7 @@ const Reply = ({
|
||||
directRepliesByParentCid,
|
||||
postsByAuthorInThread,
|
||||
}: PostProps & { directRepliesByParentCid?: Map<string, Comment[]>; postsByAuthorInThread?: Map<string, number> }) => {
|
||||
const accountReply = useAccountComment({
|
||||
const accountReply = useSafeAccountComment({
|
||||
commentIndex: typeof reply?.index === 'number' ? reply.index : undefined,
|
||||
});
|
||||
const hasReplyIndex = typeof reply?.index === 'number';
|
||||
|
||||
@@ -19,11 +19,13 @@ type TestComment = {
|
||||
|
||||
const testState = vi.hoisted(() => ({
|
||||
account: {
|
||||
id: 'account-1',
|
||||
author: {
|
||||
address: '0xme',
|
||||
},
|
||||
},
|
||||
accountComments: [] as Array<{ cid?: string }>,
|
||||
} as { id?: string; author?: { address?: string } },
|
||||
accountCommentByCid: {} as Record<string, { cid?: string }>,
|
||||
accountCommentCalls: [] as Array<{ commentCid?: string } | undefined>,
|
||||
directories: [{ address: 'music-posting.eth', title: '/mu/ - Music' }] as Array<{ address: string; title?: string }>,
|
||||
isMobile: false,
|
||||
locationPath: '/mu/thread/thread-cid',
|
||||
@@ -59,7 +61,10 @@ vi.mock('react-router-dom', async () => {
|
||||
|
||||
vi.mock('@bitsocialnet/bitsocial-react-hooks', () => ({
|
||||
useAccount: () => testState.account,
|
||||
useAccountComments: () => ({ accountComments: testState.accountComments }),
|
||||
useAccountComment: (options?: { commentCid?: string }) => {
|
||||
testState.accountCommentCalls.push(options);
|
||||
return (options?.commentCid && testState.accountCommentByCid[options.commentCid]) || {};
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('@floating-ui/react', () => ({
|
||||
@@ -171,11 +176,13 @@ describe('ReplyQuotePreview', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
testState.account = {
|
||||
id: 'account-1',
|
||||
author: {
|
||||
address: '0xme',
|
||||
},
|
||||
};
|
||||
testState.accountComments = [];
|
||||
testState.accountCommentByCid = {};
|
||||
testState.accountCommentCalls = [];
|
||||
testState.directories = [{ address: 'music-posting.eth', title: '/mu/ - Music' }];
|
||||
testState.isMobile = false;
|
||||
testState.locationPath = '/mu/thread/thread-cid';
|
||||
@@ -394,6 +401,58 @@ describe('ReplyQuotePreview', () => {
|
||||
outOfView.remove();
|
||||
});
|
||||
|
||||
it('marks quotelinks as your own via the direct account comment lookup before author fallback', async () => {
|
||||
testState.account = {
|
||||
id: 'account-1',
|
||||
author: {
|
||||
address: '0xsomeone-else',
|
||||
},
|
||||
};
|
||||
testState.accountCommentByCid = {
|
||||
'reply-cid': {
|
||||
cid: 'reply-cid',
|
||||
},
|
||||
};
|
||||
|
||||
await renderPreview({
|
||||
isQuotelinkReply: true,
|
||||
quotelinkReply: {
|
||||
author: {
|
||||
address: '0xother',
|
||||
},
|
||||
cid: 'reply-cid',
|
||||
number: 11,
|
||||
communityAddress: 'music-posting.eth',
|
||||
},
|
||||
});
|
||||
|
||||
expect(container.textContent).toContain('>>11 (You)');
|
||||
expect(testState.accountCommentCalls).toContainEqual({ commentCid: 'reply-cid' });
|
||||
});
|
||||
|
||||
it('does not mark quotelinks as your own when the lookup misses and no author address matches', async () => {
|
||||
testState.account = {
|
||||
id: 'account-1',
|
||||
author: { address: undefined } as { address?: string },
|
||||
};
|
||||
testState.accountCommentByCid = {
|
||||
'reply-cid': {
|
||||
cid: 'different-cid',
|
||||
},
|
||||
};
|
||||
|
||||
await renderPreview({
|
||||
isQuotelinkReply: true,
|
||||
quotelinkReply: {
|
||||
cid: 'reply-cid',
|
||||
number: 12,
|
||||
communityAddress: 'music-posting.eth',
|
||||
},
|
||||
});
|
||||
|
||||
expect(container.textContent).not.toContain('(You)');
|
||||
});
|
||||
|
||||
it('renders unavailable desktop quotelinks without navigation and includes OP/You labels', async () => {
|
||||
testState.quoteAvailability = 'unavailable';
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { Link, useLocation, useNavigate } from 'react-router-dom';
|
||||
import { Comment, useAccount, useAccountComments } from '@bitsocialnet/bitsocial-react-hooks';
|
||||
import { Comment, useAccount } from '@bitsocialnet/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';
|
||||
@@ -80,6 +81,17 @@ const scrollToReplyOnPage = (cid: string) => {
|
||||
return true;
|
||||
};
|
||||
|
||||
const useIsOwnQuotelink = (quotelinkReply?: Comment) => {
|
||||
const account = useAccount();
|
||||
const ownQuotelink = useSafeAccountComment({ commentCid: quotelinkReply?.cid });
|
||||
const quotedAuthorAddress = quotelinkReply?.author?.address;
|
||||
const accountAuthorAddress = account?.author?.address;
|
||||
|
||||
return Boolean(
|
||||
(quotelinkReply?.cid && ownQuotelink?.cid === quotelinkReply.cid) || (quotedAuthorAddress && accountAuthorAddress && quotedAuthorAddress === accountAuthorAddress),
|
||||
);
|
||||
};
|
||||
|
||||
const DesktopQuotePreview = ({
|
||||
backlinkReply,
|
||||
quotelinkReply,
|
||||
@@ -204,10 +216,6 @@ const DesktopQuotePreview = ({
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
const account = useAccount();
|
||||
const { accountComments } = useAccountComments();
|
||||
|
||||
const resolvedQuotelinkNumber = normalizedQuotelinkReply?.number ?? quotelinkNumber;
|
||||
const resolvedQuotelinkCid = normalizedQuotelinkReply?.cid;
|
||||
const resolvedQuotelinkCommunityAddress = getCommentCommunityAddress(normalizedQuotelinkReply);
|
||||
@@ -227,9 +235,7 @@ const DesktopQuotePreview = ({
|
||||
quoteCid: resolvedQuotelinkCid,
|
||||
isUnavailable: quotelinkUnavailable,
|
||||
});
|
||||
const isOwnQuotelink =
|
||||
(resolvedQuotelinkCid ? accountComments.some((comment) => comment.cid === resolvedQuotelinkCid) : false) ||
|
||||
normalizedQuotelinkReply?.author?.address === account?.author?.address;
|
||||
const isOwnQuotelink = useIsOwnQuotelink(normalizedQuotelinkReply);
|
||||
const quotelinkLabel = (
|
||||
<>
|
||||
{formatQuoteNumber(resolvedQuotelinkNumber)}
|
||||
@@ -374,9 +380,6 @@ const MobileQuotePreview = ({
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
const account = useAccount();
|
||||
const { accountComments } = useAccountComments();
|
||||
const resolvedQuotelinkNumber = normalizedQuotelinkReply?.number ?? quotelinkNumber;
|
||||
const resolvedQuotelinkCid = normalizedQuotelinkReply?.cid;
|
||||
const resolvedQuotelinkCommunityAddress = getCommentCommunityAddress(normalizedQuotelinkReply);
|
||||
@@ -390,9 +393,7 @@ const MobileQuotePreview = ({
|
||||
quoteCid: resolvedQuotelinkCid,
|
||||
isUnavailable: quotelinkUnavailable,
|
||||
});
|
||||
const isOwnQuotelink =
|
||||
(resolvedQuotelinkCid ? accountComments.some((comment) => comment.cid === resolvedQuotelinkCid) : false) ||
|
||||
normalizedQuotelinkReply?.author?.address === account?.author?.address;
|
||||
const isOwnQuotelink = useIsOwnQuotelink(normalizedQuotelinkReply);
|
||||
|
||||
const replyQuotelink = (
|
||||
<>
|
||||
|
||||
Reference in New Issue
Block a user