fix(board-buttons): hide catalog controls on flash upload boards

Flash boards do not use catalog view, so hide catalog links and OP search
without leaving empty bracket placeholders in desktop board controls.
This commit is contained in:
Tommaso Casaburi
2026-06-01 12:42:21 +07:00
parent d3b5c40321
commit b686def1a1
12 changed files with 181 additions and 35 deletions
@@ -3,7 +3,7 @@ import { createElement } from 'react';
import { createRoot, type Root } from 'react-dom/client';
import { MemoryRouter, Route, Routes } from 'react-router-dom';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { DesktopBoardButtons, MobileBoardButtons } from '../board-buttons';
import { DesktopBoardButtons, BracketedCatalogButton, MobileBoardButtons } from '../board-buttons';
import useThreadLiveUpdatesStore from '../../../stores/use-thread-live-updates-store';
import useHiddenCatalogThreadsStore from '../../../stores/use-hidden-catalog-threads-store';
import { clearStableLastVisitTimeFilterName, LAST_VISIT_STORAGE_KEY } from '../../../lib/utils/time-filter-utils';
@@ -13,6 +13,7 @@ const act = (React as { act?: (cb: () => void | Promise<void>) => void | Promise
type DirectoryEntry = {
address: string;
directoryCode?: string;
features?: { requirePostLinkIsMedia?: boolean };
name?: string;
publicKey?: string;
@@ -120,7 +121,7 @@ vi.mock('../../../hooks/use-post-page-number', () => ({
vi.mock('../../../hooks/use-directories', () => ({
findDirectoryByAddress: (directories: DirectoryEntry[], address?: string) =>
directories.find((entry) => address && [entry.address, entry.name, entry.publicKey].includes(address)),
directories.find((entry) => address && [entry.address, entry.name, entry.publicKey, entry.directoryCode].includes(address)),
useDirectories: () => testState.directories,
useDirectoryByAddress: (address: string | undefined) => testState.directories.find((entry) => entry.address === address),
}));
@@ -360,6 +361,28 @@ describe('BoardButtons', () => {
expect(container.textContent).not.toContain('directory');
});
it('does not render catalog or OP search controls on flash upload boards', async () => {
testState.directories = [
{ address: 'music-posting.eth', features: {}, name: 'music-posting.eth', publicKey: 'music-public-key', title: '/mu/ - Music' },
{ address: 'flash-posting.bso', directoryCode: 'f', title: '/f/ - Flash' },
];
testState.resolvedCommunityAddress = 'flash-posting.bso';
await renderWithRoute(createElement(DesktopBoardButtons), '/f');
expect(findButtonLink('catalog')).toBeUndefined();
expect(container.querySelector('input[type="text"]')).toBeNull();
expect(container.textContent).not.toMatch(/\[\s*\]/);
});
it('does not render bracketed catalog controls on flash upload boards', async () => {
testState.directories = [{ address: 'flash-posting.bso', directoryCode: 'f', title: '/f/ - Flash' }];
await renderWithRoute(createElement(BracketedCatalogButton, { address: 'flash-posting.bso' }), '/f');
expect(container.textContent?.trim()).toBe('');
});
it('renders desktop catalog controls and wires sort, style, filter, and refresh updates', async () => {
testState.filteredCount = 4;
+56 -6
View File
@@ -7,7 +7,7 @@ import { usePostPageNumber } from '../../hooks/use-post-page-number';
import { useDirectories, useDirectoryByAddress } from '../../hooks/use-directories';
import { useAccountCommunityAddresses } from '../../hooks/use-account-community-addresses';
import { useFilteredDirectoryAddresses } from '../../hooks/use-filtered-directory-addresses';
import { getBoardPath, isDirectoryRoute } from '../../lib/utils/route-utils';
import { getBoardPath, isDirectoryRoute, isFlashBoardRoute } 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';
@@ -45,6 +45,17 @@ interface BoardButtonsProps {
const EMPTY_COMMUNITY_ADDRESSES: string[] = [];
export const shouldShowCatalogButton = (
boardIdentifier: string | undefined,
directories: ReturnType<typeof useDirectories>,
{ isInAllView, isInSubscriptionsView, isInModView }: Pick<BoardButtonsProps, 'isInAllView' | 'isInSubscriptionsView' | 'isInModView'>,
): boolean => {
if (isInAllView || isInSubscriptionsView || isInModView) {
return true;
}
return !isFlashBoardRoute(boardIdentifier, directories);
};
const getMultiboardPath = ({
isInAllView,
isInCatalogView,
@@ -67,9 +78,14 @@ const getMultiboardPath = ({
export const CatalogButton = ({ address, isInAllView, isInSubscriptionsView, isInModView }: BoardButtonsProps) => {
const { t } = useTranslation();
const location = useLocation();
const params = useParams();
const directories = useDirectories();
const { timeFilterValue } = useTimeFilter();
if (!shouldShowCatalogButton(params.boardIdentifier, directories, { isInAllView, isInSubscriptionsView, isInModView })) {
return null;
}
const createCatalogLink = () => {
const multiboardPath = getMultiboardPath({ isInAllView, isInCatalogView: true, isInSubscriptionsView, isInModView });
if (multiboardPath) {
@@ -94,6 +110,20 @@ export const CatalogButton = ({ address, isInAllView, isInSubscriptionsView, isI
);
};
export const BracketedCatalogButton = ({ address, isInAllView, isInSubscriptionsView, isInModView }: BoardButtonsProps) => {
const params = useParams();
const directories = useDirectories();
if (!shouldShowCatalogButton(params.boardIdentifier, directories, { isInAllView, isInSubscriptionsView, isInModView })) {
return null;
}
return (
<span>
[<CatalogButton address={address} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} isInModView={isInModView} />]
</span>
);
};
export const ArchiveButton = ({ address, isInAllView, isInSubscriptionsView, isInModView }: BoardButtonsProps) => {
const { t } = useTranslation();
const navigate = useNavigate();
@@ -526,13 +556,16 @@ export const MobileBoardButtons = () => {
const directories = useDirectories();
const boardIdentifier = params.boardIdentifier;
const showDirectoryButton = boardIdentifier && isDirectoryRoute(boardIdentifier, directories);
const showCatalogButton = shouldShowCatalogButton(boardIdentifier, directories, { isInAllView, isInSubscriptionsView, isInModView });
return (
<div className={`${styles.mobileBoardButtons} ${!isInCatalogView ? styles.addMargin : ''}`}>
{isInPostView || isInPendingPostPage ? (
<>
<ReturnButton address={communityAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} isInModView={isInModView} />
<CatalogButton address={communityAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} isInModView={isInModView} />
{showCatalogButton && (
<CatalogButton address={communityAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} isInModView={isInModView} />
)}
{showBottomButton && <BottomButton />}
<div className={styles.secondRow}>
<UpdateButton />
@@ -592,7 +625,9 @@ export const MobileBoardButtons = () => {
) : (
<>
{showBottomButton && <BottomButton />}
<CatalogButton address={communityAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} isInModView={isInModView} />
{showCatalogButton && (
<CatalogButton address={communityAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} isInModView={isInModView} />
)}
<RefreshButton />
<div className={styles.secondRow}>
{showDirectoryButton && <DirectoryButton />}
@@ -711,6 +746,7 @@ export const DesktopBoardButtons = () => {
const directories = useDirectories();
const boardIdentifier = params.boardIdentifier;
const showDirectoryButton = boardIdentifier && isDirectoryRoute(boardIdentifier, directories);
const showCatalogButton = shouldShowCatalogButton(boardIdentifier, directories, { isInAllView, isInSubscriptionsView, isInModView });
return (
<>
@@ -718,8 +754,13 @@ export const DesktopBoardButtons = () => {
<div className={styles.desktopBoardButtons}>
{isInPostView || isInPendingPostPage ? (
<>
[<ReturnButton address={communityAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} isInModView={isInModView} />] [
<CatalogButton address={communityAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} isInModView={isInModView} />]
[<ReturnButton address={communityAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} isInModView={isInModView} />]
{showCatalogButton && (
<>
{' '}
[<CatalogButton address={communityAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} isInModView={isInModView} />]
</>
)}
{showBottomButton && (
<>
{' '}
@@ -762,7 +803,12 @@ export const DesktopBoardButtons = () => {
) : (
<>
<SearchOPsBar />
[<CatalogButton address={communityAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} isInModView={isInModView} />]
{showCatalogButton && (
<>
{' '}
[<CatalogButton address={communityAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} isInModView={isInModView} />]
</>
)}
{!(isInAllView || isInSubscriptionsView || isInModView) && (
<>
{' '}
@@ -845,6 +891,10 @@ const SearchOPsBar = () => {
const resolvedAddress = useResolvedCommunityAddress();
const boardPath = resolvedAddress ? getBoardPath(resolvedAddress, directories) : params?.boardIdentifier || params?.communityAddress;
if (!shouldShowCatalogButton(params.boardIdentifier, directories, { isInAllView, isInSubscriptionsView, isInModView })) {
return null;
}
const handleSearch = (event: React.KeyboardEvent<HTMLInputElement>) => {
if (event.key === 'Enter') {
const searchQuery = (event.target as HTMLInputElement).value.trim();