From cc31ca7a745f5886ccc5a6d3cbd2255a68b13ffb Mon Sep 17 00:00:00 2001 From: Tommaso Casaburi Date: Tue, 28 Apr 2026 14:04:06 +0700 Subject: [PATCH] fix(dev): restore dev home page render --- src/components/header/header.tsx | 6 +-- src/components/reply/reply.tsx | 5 ++- src/components/search-bar/search-bar.tsx | 11 +++-- src/components/sidebar/sidebar.tsx | 45 +++++++++------------ src/hooks/use-account-comment.ts | 8 ++++ src/lib/utils/account-comment-utils.test.ts | 19 +++++++++ src/lib/utils/account-comment-utils.ts | 14 +++++++ src/views/post-page/post-page.tsx | 5 ++- 8 files changed, 75 insertions(+), 38 deletions(-) create mode 100644 src/hooks/use-account-comment.ts create mode 100644 src/lib/utils/account-comment-utils.test.ts create mode 100644 src/lib/utils/account-comment-utils.ts diff --git a/src/components/header/header.tsx b/src/components/header/header.tsx index 89da2018..b6276283 100644 --- a/src/components/header/header.tsx +++ b/src/components/header/header.tsx @@ -1,6 +1,6 @@ import { Link, useLocation, useParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; -import { useAccount, useAccountComment, useCommunity } from '@bitsocial/bitsocial-react-hooks'; +import { useAccount, useCommunity } from '@bitsocial/bitsocial-react-hooks'; import { sortTypes } from '../../constants/sort-types'; import { sortLabels } from '../../constants/sort-labels'; import { @@ -48,6 +48,7 @@ import { useIsBroadlyNsfwCommunity } from '../../hooks/use-is-broadly-nsfw-commu import useTheme from '../../hooks/use-theme'; import useWindowWidth from '../../hooks/use-window-width'; import { getCommunityIdentifier } from '../../hooks/use-community-identifier'; +import useOptionalAccountComment from '../../hooks/use-account-comment'; import styles from './header.module.css'; const AboutButton = () => { @@ -387,8 +388,7 @@ const Header = () => { const community = useCommunity(params?.communityAddress ? { community: getCommunityIdentifier(params.communityAddress), onlyIfCached: true } : undefined); const { suggested, title } = community || {}; - const commentIndex = params?.accountCommentIndex ? parseInt(params?.accountCommentIndex) : undefined; - const accountComment = useAccountComment({ commentIndex }); + const accountComment = useOptionalAccountComment(params?.accountCommentIndex); const isMobile = useWindowWidth() < 640; const isInAllAboutView = isAllAboutView(location.pathname); diff --git a/src/components/reply/reply.tsx b/src/components/reply/reply.tsx index defa0c85..f2dbe992 100644 --- a/src/components/reply/reply.tsx +++ b/src/components/reply/reply.tsx @@ -1,11 +1,12 @@ import { Fragment, useEffect, useMemo, useState, useRef } from 'react'; import { Link, useLocation, useParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; -import { Comment, useAccountComment, useAuthorAddress, useAuthorAvatar, useBlock, useComment, useEditedComment, useCommunity } from '@bitsocial/bitsocial-react-hooks'; +import { Comment, useAuthorAddress, useAuthorAvatar, useBlock, useComment, useEditedComment, useCommunity } from '@bitsocial/bitsocial-react-hooks'; import { isInboxView, isPostContextView, isPostPageView } from '../../lib/utils/view-utils'; import getShortAddress from '../../lib/utils/address-utils'; import { getCommentCommunityAddress } from '../../lib/utils/comment-utils'; import { getCommunityIdentifier } from '../../hooks/use-community-identifier'; +import useOptionalAccountComment from '../../hooks/use-account-comment'; import { getHostname } from '../../lib/utils/url-utils'; import { formatScore, getReplyScore } from '../../lib/utils/post-utils'; import { flattenCommentsPages } from '@bitsocial/bitsocial-react-hooks/dist/lib/utils'; @@ -349,7 +350,7 @@ const Reply = ({ cidOfReplyWithContext, depth = 0, isSingleComment, isSingleRepl const communityAddress = getCommentCommunityAddress(reply); const community = useCommunity(communityAddress ? { community: getCommunityIdentifier(communityAddress), onlyIfCached: true } : undefined); - const pendingReply = useAccountComment({ commentIndex: reply?.index }); + const pendingReply = useOptionalAccountComment(reply?.index); const parentOfPendingReply = useComment({ commentCid: pendingReply?.parentCid, onlyIfCached: true }); const location = useLocation(); diff --git a/src/components/search-bar/search-bar.tsx b/src/components/search-bar/search-bar.tsx index 7c7faad0..e0bdcedd 100644 --- a/src/components/search-bar/search-bar.tsx +++ b/src/components/search-bar/search-bar.tsx @@ -80,6 +80,12 @@ const SearchBar = ({ isFocused = false, onExpandoChange }: SearchBarProps) => { middleware: [offset(5), shift()], whileElementsMounted: autoUpdate, }); + const setFloatingReferenceRef = useRef(refs.setReference); + setFloatingReferenceRef.current = refs.setReference; + const setSearchInputReference = useCallback((instance: HTMLInputElement | null) => { + searchInputRef.current = instance; + setFloatingReferenceRef.current(instance); + }, []); useEffect(() => { setInputValue(searchParams.get('q') || ''); @@ -237,10 +243,7 @@ const SearchBar = ({ isFocused = false, onExpandoChange }: SearchBarProps) => { spellCheck='false' autoCapitalize='off' placeholder={placeholder} - ref={(instance) => { - searchInputRef.current = instance; - refs.setReference(instance); - }} + ref={setSearchInputReference} onFocus={() => { setShowExpando(true); setIsInputFocused(true); diff --git a/src/components/sidebar/sidebar.tsx b/src/components/sidebar/sidebar.tsx index d530ff27..693fb718 100644 --- a/src/components/sidebar/sidebar.tsx +++ b/src/components/sidebar/sidebar.tsx @@ -1,7 +1,7 @@ -import { useState, useEffect } from 'react'; +import { useState } from 'react'; import { Link, useLocation, useNavigate, useParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; -import { Comment, useAccount, useBlock, Role, Community, useCommunityStats, useAccountComment, usePkcRpcSettings } from '@bitsocial/bitsocial-react-hooks'; +import { Comment, useAccount, useBlock, Role, Community, useCommunityStats, usePkcRpcSettings } from '@bitsocial/bitsocial-react-hooks'; import { getPostScore } from '../../lib/utils/post-utils'; import { getFormattedDate, getFormattedTimeDuration, getFormattedTimeAgo } from '../../lib/utils/time-utils'; import { findCommunityCreator } from '../../lib/utils/user-utils'; @@ -24,6 +24,7 @@ import useCommunitySubtitles from '../../hooks/use-community-subtitles'; import useIsMobile from '../../hooks/use-is-mobile'; import useIsCommunityOffline from '../../hooks/use-is-community-offline'; import { getCommunityIdentifier } from '../../hooks/use-community-identifier'; +import useOptionalAccountComment from '../../hooks/use-account-comment'; import { FAQ } from '../../views/about/about'; import LoadingEllipsis from '../loading-ellipsis'; import Markdown from '../markdown'; @@ -46,6 +47,15 @@ const RulesList = ({ rules }: { rules: string[] }) => { ); }; +const getRandomSubtitleIndexes = (subtitleCount: number): [number | undefined, number | undefined] => { + if (subtitleCount < 1) return [undefined, undefined]; + if (subtitleCount === 1) return [0, undefined]; + + const firstIndex = Math.floor(Math.random() * subtitleCount); + const secondIndex = (firstIndex + 1 + Math.floor(Math.random() * (subtitleCount - 1))) % subtitleCount; + return [firstIndex, secondIndex]; +}; + const ModeratorsList = ({ roles }: { roles: Record }) => { const { t } = useTranslation(); const rolesList = roles ? Object.entries(roles).map(([address, { role }]) => ({ address, role })) : []; @@ -196,7 +206,7 @@ const Sidebar = ({ comment, isSubCreatedButNotYetPublished, settings, subplebbit const isInCommunityAboutView = isCommunityAboutView(location.pathname, params); const isInCommunityView = isCommunityView(location.pathname, params); - const pendingPost = useAccountComment({ commentIndex: params?.accountCommentIndex as any }); + const pendingPost = useOptionalAccountComment(params?.accountCommentIndex); const communityCreator = findCommunityCreator(roles); const creatorAddress = communityCreator === 'anonymous' ? 'anonymous' : `${getShortAddress(communityCreator)}`; @@ -235,26 +245,11 @@ const Sidebar = ({ comment, isSubCreatedButNotYetPublished, settings, subplebbit const moderatorRole = roles?.[account.author?.address]?.role; const isOwner = !!settings; - const [subtitle1, setSubtitle1] = useState(''); - const [subtitle2, setSubtitle2] = useState(''); - const communitySubtitles = useCommunitySubtitles(); - - useEffect(() => { - if (communitySubtitles.length >= 2) { - const indices = new Set(); - while (indices.size < 2) { - const randomIndex = Math.floor(Math.random() * communitySubtitles.length); - indices.add(randomIndex); - } - const [index1, index2] = Array.from(indices); - setSubtitle1(communitySubtitles[index1]); - setSubtitle2(communitySubtitles[index2]); - } else if (communitySubtitles.length === 1) { - setSubtitle1(communitySubtitles[0]); - setSubtitle2(''); - } - }, [communitySubtitles]); + const [subtitleIndexes] = useState(() => getRandomSubtitleIndexes(communitySubtitles.length)); + const [subtitleIndex1, subtitleIndex2] = subtitleIndexes; + const subtitle1 = subtitleIndex1 === undefined ? '' : communitySubtitles[subtitleIndex1] || ''; + const subtitle2 = subtitleIndex2 === undefined ? '' : communitySubtitles[subtitleIndex2] || ''; const isConnectedToRpc = usePkcRpcSettings()?.state === 'connected'; const navigate = useNavigate(); @@ -274,14 +269,10 @@ const Sidebar = ({ comment, isSubCreatedButNotYetPublished, settings, subplebbit const isMobile = useIsMobile(); const [showExpando, setShowExpando] = useState(false); - const handleSearchBarExpandoChange = (expanded: boolean) => { - setShowExpando(expanded); - }; - return (
- +
{ + return useAccountComment({ commentIndex: getAccountCommentIndex(commentIndex) ?? MISSING_ACCOUNT_COMMENT_INDEX }); +}; + +export default useOptionalAccountComment; diff --git a/src/lib/utils/account-comment-utils.test.ts b/src/lib/utils/account-comment-utils.test.ts new file mode 100644 index 00000000..d1e04d13 --- /dev/null +++ b/src/lib/utils/account-comment-utils.test.ts @@ -0,0 +1,19 @@ +import { describe, expect, it } from 'vitest'; +import { getAccountCommentIndex, MISSING_ACCOUNT_COMMENT_INDEX } from './account-comment-utils'; + +describe('account-comment-utils', () => { + it('parses route params into valid account comment indexes', () => { + expect(getAccountCommentIndex('0')).toBe(0); + expect(getAccountCommentIndex('12')).toBe(12); + expect(getAccountCommentIndex(3)).toBe(3); + }); + + it('rejects missing and invalid indexes so the hook can use a safe sentinel', () => { + expect(getAccountCommentIndex(undefined)).toBeUndefined(); + expect(getAccountCommentIndex('')).toBeUndefined(); + expect(getAccountCommentIndex('1.2')).toBeUndefined(); + expect(getAccountCommentIndex('1e2')).toBeUndefined(); + expect(getAccountCommentIndex(-1)).toBeUndefined(); + expect(MISSING_ACCOUNT_COMMENT_INDEX).toBeLessThan(0); + }); +}); diff --git a/src/lib/utils/account-comment-utils.ts b/src/lib/utils/account-comment-utils.ts new file mode 100644 index 00000000..e4f0f661 --- /dev/null +++ b/src/lib/utils/account-comment-utils.ts @@ -0,0 +1,14 @@ +export const MISSING_ACCOUNT_COMMENT_INDEX = -1; + +export const getAccountCommentIndex = (value: number | string | null | undefined): number | undefined => { + if (typeof value === 'number') { + return Number.isInteger(value) && value >= 0 ? value : undefined; + } + + if (typeof value !== 'string' || !/^\d+$/.test(value)) { + return undefined; + } + + const index = Number(value); + return Number.isInteger(index) && index >= 0 ? index : undefined; +}; diff --git a/src/views/post-page/post-page.tsx b/src/views/post-page/post-page.tsx index 5cd3b2cd..a745f024 100644 --- a/src/views/post-page/post-page.tsx +++ b/src/views/post-page/post-page.tsx @@ -1,7 +1,7 @@ import { useEffect, useState, useRef, useCallback, useMemo } from 'react'; import { Link, useLocation, useNavigate, useParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; -import { Comment, useAccount, useAccountComment, useAccountComments, useComment, useCommunity } from '@bitsocial/bitsocial-react-hooks'; +import { Comment, useAccount, useAccountComments, useComment, useCommunity } from '@bitsocial/bitsocial-react-hooks'; import findTopParentCidOfReply from '../../lib/utils/cid-utils'; import { getCommentCommunityAddress } from '../../lib/utils/comment-utils'; import { sortRepliesByBest } from '../../lib/utils/post-utils'; @@ -11,6 +11,7 @@ import useFeedResetStore from '../../stores/use-feed-reset-store'; import { useIsBroadlyNsfwCommunity } from '../../hooks/use-is-broadly-nsfw-community'; import useReplies from '../../hooks/use-replies'; import { getCommunityIdentifier } from '../../hooks/use-community-identifier'; +import useOptionalAccountComment from '../../hooks/use-account-comment'; import useStateString from '../../hooks/use-state-string'; import ErrorDisplay from '../../components/error-display'; import LoadingEllipsis from '../../components/loading-ellipsis'; @@ -266,7 +267,7 @@ const PostPage = () => { } }, [isValidAccountCommentIndex, navigate]); - const accountComment = useAccountComment({ commentIndex }); + const accountComment = useOptionalAccountComment(commentIndex); const pendingPost = accountComment; // in pending post route, redirect to post page route when post is published (cid is defined)