mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
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:
@@ -416,6 +416,18 @@ describe('App', () => {
|
||||
expect(container.querySelector('[data-testid="not-found-view"]')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('redirects flash board catalog routes to not-found', async () => {
|
||||
testState.directories = [
|
||||
{ address: 'music-posting.eth', title: '/mu/ - Music', nsfw: false },
|
||||
{ address: 'flash-posting.bso', directoryCode: 'f', title: '/f/ - Flash', nsfw: true },
|
||||
];
|
||||
|
||||
await renderApp('/f/catalog');
|
||||
|
||||
expect(latestLocation).toBe('/not-found');
|
||||
expect(container.querySelector('[data-testid="not-found-view"]')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('renders the pass route as a global static page', async () => {
|
||||
await renderApp('/pass');
|
||||
|
||||
|
||||
+7
-2
@@ -29,6 +29,7 @@ import {
|
||||
isModQueueRoute,
|
||||
isValidBoardModRoute,
|
||||
isValidModRoute,
|
||||
isFlashBoardRoute,
|
||||
} from './lib/utils/route-utils';
|
||||
import styles from './app.module.css';
|
||||
import { DesktopBoardButtons, MobileAllFeedFilter, MobileBoardButtons } from './components/board-buttons';
|
||||
@@ -112,6 +113,10 @@ const BoardLayout = () => {
|
||||
return <Navigate to='/not-found' replace />;
|
||||
}
|
||||
|
||||
if (isCatalogView(pathname, params) && isFlashBoardRoute(boardIdentifier, directories)) {
|
||||
return <Navigate to='/not-found' replace />;
|
||||
}
|
||||
|
||||
// Invalid /mod/ paths (e.g. /mod/modqueue, /mod/asdoijasd) -> not-found
|
||||
if (pathname.startsWith('/mod/') && !isValidModRoute(pathname)) {
|
||||
return <Navigate to='/not-found' replace />;
|
||||
@@ -335,12 +340,12 @@ const App = () => {
|
||||
<Route path='/subs/*' element={<Navigate to='/not-found' replace />} />
|
||||
<Route path='/mod/*' element={<Navigate to='/not-found' replace />} />
|
||||
|
||||
<Route path='/:boardIdentifier/catalog' element={catalogFeedElement} />
|
||||
<Route path='/:boardIdentifier/catalog/settings' element={catalogFeedElement} />
|
||||
<Route path='/:boardIdentifier/:pageNumber' element={boardFeedElement} />
|
||||
<Route path='/:boardIdentifier/:pageNumber/settings' element={boardFeedElement} />
|
||||
<Route path='/:boardIdentifier' element={boardFeedElement} />
|
||||
<Route path='/:boardIdentifier/settings' element={boardFeedElement} />
|
||||
<Route path='/:boardIdentifier/catalog' element={catalogFeedElement} />
|
||||
<Route path='/:boardIdentifier/catalog/settings' element={catalogFeedElement} />
|
||||
<Route path='/:boardIdentifier/archive' element={<Archive />} />
|
||||
<Route path='/:boardIdentifier/archive/settings' element={<Archive />} />
|
||||
<Route path='/:boardIdentifier/directory' element={<Directory />} />
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -9,6 +9,7 @@ import BoardPagination from '../board-pagination';
|
||||
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(() => ({
|
||||
directories: [] as Array<{ address: string; directoryCode?: string; title?: string }>,
|
||||
enableInfiniteScroll: false,
|
||||
navigateMock: vi.fn(),
|
||||
setEnableInfiniteScrollMock: vi.fn(),
|
||||
@@ -36,6 +37,12 @@ vi.mock('../../../stores/use-feed-view-settings-store', () => ({
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock('../../../hooks/use-directories', () => ({
|
||||
findDirectoryByAddress: (directories: Array<{ address: string; directoryCode?: string; title?: string }>, address?: string) =>
|
||||
directories.find((entry) => address && (entry.address === address || entry.directoryCode === address || (entry.title && entry.title.includes(`/${address}/`)))),
|
||||
useDirectories: () => testState.directories,
|
||||
}));
|
||||
|
||||
vi.mock('../../style-selector/style-selector', () => ({
|
||||
default: () => createElement('div', { 'data-testid': 'style-selector' }, 'style-selector'),
|
||||
}));
|
||||
@@ -53,6 +60,7 @@ describe('BoardPagination', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
testState.enableInfiniteScroll = false;
|
||||
testState.directories = [{ address: 'music-posting.eth', directoryCode: 'mu', title: '/mu/ - Music' }];
|
||||
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
@@ -116,6 +124,14 @@ describe('BoardPagination', () => {
|
||||
expect(container.textContent).not.toContain('archive');
|
||||
});
|
||||
|
||||
it('hides the catalog link on flash upload boards', () => {
|
||||
testState.directories = [{ address: 'flash-posting.bso', directoryCode: 'f', title: '/f/ - Flash' }];
|
||||
renderPagination(createElement(BoardPagination, { basePath: '/f', currentPage: 1, footerStyle: true, totalPages: 3 }));
|
||||
|
||||
expect(container.textContent).not.toContain('catalog');
|
||||
expect(container.textContent).toContain('archive');
|
||||
});
|
||||
|
||||
it('returns nothing for single-page non-footer pagination', () => {
|
||||
renderPagination(createElement(BoardPagination, { basePath: '/mu', currentPage: 1, totalPages: 1 }));
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import useFeedViewSettingsStore from '../../stores/use-feed-view-settings-store';
|
||||
import { useDirectories } from '../../hooks/use-directories';
|
||||
import { isFlashBoardRoute } from '../../lib/utils/route-utils';
|
||||
import StyleSelector from '../style-selector/style-selector';
|
||||
import footerStyles from '../footer/footer.module.css';
|
||||
import styles from './board-pagination.module.css';
|
||||
@@ -19,10 +21,13 @@ interface BoardPaginationProps {
|
||||
const BoardPagination = ({ basePath, currentPage, search = '', totalPages, footerStyle = false, isMultiboard = false }: BoardPaginationProps) => {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const directories = useDirectories();
|
||||
const enableInfiniteScroll = useFeedViewSettingsStore((state) => state.enableInfiniteScroll);
|
||||
const setEnableInfiniteScroll = useFeedViewSettingsStore((state) => state.setEnableInfiniteScroll);
|
||||
|
||||
const pageHref = (page: number) => ({ pathname: page === 1 ? basePath : `${basePath}/${page}`, search });
|
||||
const boardIdentifier = basePath.replace(/^\//, '').split('/')[0];
|
||||
const showCatalogLink = !isFlashBoardRoute(boardIdentifier, directories);
|
||||
const catalogHref = { pathname: `${basePath}/catalog`, search };
|
||||
|
||||
if (totalPages <= 1 && !footerStyle) {
|
||||
@@ -78,9 +83,11 @@ const BoardPagination = ({ basePath, currentPage, search = '', totalPages, foote
|
||||
) : (
|
||||
<span className={styles.footerNavPlainDisabled}>{t('next')}</span>
|
||||
)}
|
||||
<Link to={catalogHref} className={styles.pagelistSeparatorLink}>
|
||||
{t('catalog')}
|
||||
</Link>
|
||||
{showCatalogLink && (
|
||||
<Link to={catalogHref} className={styles.pagelistSeparatorLink}>
|
||||
{t('catalog')}
|
||||
</Link>
|
||||
)}
|
||||
<Link to={archiveHref} className={styles.pagelistSeparatorLink}>
|
||||
{t('archive')}
|
||||
</Link>
|
||||
|
||||
@@ -18,6 +18,11 @@ import {
|
||||
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(() => ({
|
||||
directories: [{ address: 'music-posting.eth', directoryCode: 'mu', title: '/mu/ - Music' }] as Array<{
|
||||
address: string;
|
||||
directoryCode?: string;
|
||||
title?: string;
|
||||
}>,
|
||||
directoryEntry: { features: {} } as { features?: Record<string, unknown> } | undefined,
|
||||
linkCount: 2,
|
||||
openReplyModalEmptyMock: vi.fn(),
|
||||
@@ -65,6 +70,7 @@ vi.mock('../../board-buttons/board-buttons', () => ({
|
||||
isInModView?: boolean;
|
||||
isInSubscriptionsView?: boolean;
|
||||
}) => createElement('button', { 'data-testid': 'catalog-button', type: 'button' }, `${address}|${isInAllView}|${isInSubscriptionsView}|${isInModView}`),
|
||||
shouldShowCatalogButton: () => true,
|
||||
PostPageStats: () => createElement('div', { 'data-testid': 'post-page-stats' }, 'post-page-stats'),
|
||||
RefreshButton: () => createElement('button', { type: 'button' }, 'refresh-button'),
|
||||
ReturnButton: ({
|
||||
@@ -97,6 +103,7 @@ vi.mock('../../../hooks/use-post-page-number', () => ({
|
||||
}));
|
||||
|
||||
vi.mock('../../../hooks/use-directories', () => ({
|
||||
useDirectories: () => testState.directories,
|
||||
useDirectoryByAddress: () => testState.directoryEntry,
|
||||
}));
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
CatalogSearchResultsLabel,
|
||||
ReturnButton,
|
||||
CatalogButton,
|
||||
shouldShowCatalogButton,
|
||||
TopButton,
|
||||
UpdateButton,
|
||||
AutoButton,
|
||||
@@ -19,7 +20,7 @@ import useReplyModalStore from '../../stores/use-reply-modal-store';
|
||||
import useThreadLiveUpdatesStore from '../../stores/use-thread-live-updates-store';
|
||||
import useCountLinksInReplies from '../../hooks/use-count-links-in-replies';
|
||||
import { usePostPageNumber } from '../../hooks/use-post-page-number';
|
||||
import { useDirectoryByAddress } from '../../hooks/use-directories';
|
||||
import { useDirectories, useDirectoryByAddress } from '../../hooks/use-directories';
|
||||
import { useCommunityIdentifier } from '../../hooks/use-community-identifiers';
|
||||
import capitalize from 'lodash/capitalize';
|
||||
import styles from './footer.module.css';
|
||||
@@ -82,15 +83,21 @@ interface CatalogFooterFirstRowProps {
|
||||
}
|
||||
|
||||
export const CatalogFooterFirstRow = ({ communityAddress, isInAllView = false, isInSubscriptionsView = false, isInModView = false }: CatalogFooterFirstRowProps) => {
|
||||
const params = useParams();
|
||||
const directories = useDirectories();
|
||||
const showCatalogButton = shouldShowCatalogButton(params.boardIdentifier, directories, { isInAllView, isInSubscriptionsView, isInModView });
|
||||
|
||||
return (
|
||||
<div className={styles.footerRow}>
|
||||
<div className={styles.footerLeft}>
|
||||
<span>
|
||||
[<ReturnButton address={communityAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} isInModView={isInModView} />]
|
||||
</span>
|
||||
<span>
|
||||
[<CatalogButton address={communityAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} isInModView={isInModView} />]
|
||||
</span>
|
||||
{showCatalogButton && (
|
||||
<span>
|
||||
[<CatalogButton address={communityAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} isInModView={isInModView} />]
|
||||
</span>
|
||||
)}
|
||||
<span>
|
||||
[<TopButton />]
|
||||
</span>
|
||||
@@ -155,6 +162,8 @@ export const ThreadFooterFirstRow = ({ postCid, threadNumber, communityAddress,
|
||||
const isInAllView = isAllView(location.pathname);
|
||||
const isInSubscriptionsView = isSubscriptionsView(location.pathname, params);
|
||||
const isInModView = isModView(location.pathname);
|
||||
const directories = useDirectories();
|
||||
const showCatalogButton = shouldShowCatalogButton(params.boardIdentifier, directories, { isInAllView, isInSubscriptionsView, isInModView });
|
||||
|
||||
const handlePostReplyClick = () => {
|
||||
if (isThreadClosed) return;
|
||||
@@ -167,9 +176,11 @@ export const ThreadFooterFirstRow = ({ postCid, threadNumber, communityAddress,
|
||||
<span>
|
||||
[<ReturnButton address={communityAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} isInModView={isInModView} />]
|
||||
</span>
|
||||
<span>
|
||||
[<CatalogButton address={communityAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} isInModView={isInModView} />]
|
||||
</span>
|
||||
{showCatalogButton && (
|
||||
<span>
|
||||
[<CatalogButton address={communityAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} isInModView={isInModView} />]
|
||||
</span>
|
||||
)}
|
||||
<span>
|
||||
[<TopButton />]
|
||||
</span>
|
||||
@@ -234,6 +245,8 @@ export const ThreadFooterMobile = ({ postCid, threadNumber, communityAddress, is
|
||||
const isInAllView = isAllView(location.pathname);
|
||||
const isInSubscriptionsView = isSubscriptionsView(location.pathname, params);
|
||||
const isInModView = isModView(location.pathname);
|
||||
const directories = useDirectories();
|
||||
const showCatalogButton = shouldShowCatalogButton(params.boardIdentifier, directories, { isInAllView, isInSubscriptionsView, isInModView });
|
||||
const communityIdentifier = useCommunityIdentifier(communityAddress);
|
||||
|
||||
const post = useComment({ commentCid: postCid, autoUpdate: autoUpdateEnabled, community: communityIdentifier });
|
||||
@@ -258,7 +271,9 @@ export const ThreadFooterMobile = ({ postCid, threadNumber, communityAddress, is
|
||||
</div>
|
||||
<div className={styles.mobileFooterButtons}>
|
||||
<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} />
|
||||
)}
|
||||
<TopButton />
|
||||
</div>
|
||||
<div className={styles.mobileFooterButtons}>
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
isDirectoryListRoute,
|
||||
isDirectoryRoute,
|
||||
isFeedRoute,
|
||||
isFlashBoardRoute,
|
||||
isLegacyBoardModQueueRoute,
|
||||
isModQueueRoute,
|
||||
isPendingPostRoute,
|
||||
@@ -33,6 +34,7 @@ const communities = [
|
||||
title: '/mu/ - Music',
|
||||
},
|
||||
{ address: 'random.eth', directoryCode: 'b', title: 'Random' },
|
||||
{ address: 'flash-posting.bso', directoryCode: 'f', title: '/f/ - Flash' },
|
||||
];
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -66,6 +68,11 @@ describe('directory mapping helpers', () => {
|
||||
expect(isDirectoryRoute('business.eth', communities)).toBe(false);
|
||||
expect(isDirectoryBoard('biz', communities)).toBe(true);
|
||||
expect(isDirectoryBoard('business.eth', communities)).toBe(false);
|
||||
|
||||
expect(isFlashBoardRoute('f', communities)).toBe(true);
|
||||
expect(isFlashBoardRoute('flash-posting.bso', communities)).toBe(true);
|
||||
expect(isFlashBoardRoute('mu', communities)).toBe(false);
|
||||
expect(isFlashBoardRoute('music-posting.bso', communities)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { DirectoryCommunity, findDirectoryByAddress, normalizeBoardAddress } from '../../hooks/use-directories';
|
||||
import { isFlashDirectory, isFlashDirectoryCode } from '../flash-tags';
|
||||
import { getEffectiveTimeFilterName, getSearchWithTimeFilter } from './time-filter-utils';
|
||||
|
||||
/**
|
||||
@@ -122,6 +123,17 @@ export const isDirectoryRoute = (boardIdentifier: string, communities: Directory
|
||||
return directoryToAddress.has(boardIdentifier);
|
||||
};
|
||||
|
||||
/** True when the board route segment refers to a flash (/f/) upload directory board. */
|
||||
export const isFlashBoardRoute = (boardIdentifier: string | undefined, communities: DirectoryCommunity[]): boolean => {
|
||||
if (isFlashDirectoryCode(boardIdentifier)) {
|
||||
return true;
|
||||
}
|
||||
if (!boardIdentifier) {
|
||||
return false;
|
||||
}
|
||||
return isFlashDirectory(findDirectoryByAddress(communities, boardIdentifier));
|
||||
};
|
||||
|
||||
/** @deprecated Use {@link isDirectoryRoute} */
|
||||
export const isDirectoryBoard = isDirectoryRoute;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Link, useParams } from 'react-router-dom';
|
||||
import { useFeed, useCommunity } from '@bitsocial/bitsocial-react-hooks';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { shouldShowSnow } from '../../lib/snow';
|
||||
import { BottomButton, CatalogButton, ReturnButton, TopButton } from '../../components/board-buttons/board-buttons';
|
||||
import { BottomButton, BracketedCatalogButton, CatalogButton, ReturnButton, TopButton } from '../../components/board-buttons/board-buttons';
|
||||
import ErrorDisplay from '../../components/error-display/error-display';
|
||||
import { PageFooterDesktop, PageFooterMobile, ThreadFooterStyleRow } from '../../components/footer';
|
||||
import LoadingEllipsis from '../../components/loading-ellipsis';
|
||||
@@ -106,9 +106,7 @@ const ArchiveDesktopTopControls = ({ communityAddress }: { communityAddress: str
|
||||
<span>
|
||||
[<ReturnButton address={communityAddress} />]
|
||||
</span>
|
||||
<span>
|
||||
[<CatalogButton address={communityAddress} />]
|
||||
</span>
|
||||
<BracketedCatalogButton address={communityAddress} />
|
||||
<span>
|
||||
[<BottomButton />]
|
||||
</span>
|
||||
@@ -120,9 +118,7 @@ const ArchiveDesktopFooterControls = ({ communityAddress }: { communityAddress:
|
||||
<span>
|
||||
[<ReturnButton address={communityAddress} />]
|
||||
</span>
|
||||
<span>
|
||||
[<CatalogButton address={communityAddress} />]
|
||||
</span>
|
||||
<BracketedCatalogButton address={communityAddress} />
|
||||
<span>
|
||||
[<TopButton />]
|
||||
</span>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Link, Navigate, useParams } from 'react-router-dom';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import { useCommunity } from '@bitsocial/bitsocial-react-hooks';
|
||||
import { shouldShowSnow } from '../../lib/snow';
|
||||
import { BottomButton, CatalogButton, ReturnButton, TopButton } from '../../components/board-buttons/board-buttons';
|
||||
import { BottomButton, BracketedCatalogButton, CatalogButton, ReturnButton, TopButton } from '../../components/board-buttons/board-buttons';
|
||||
import { PageFooterDesktop, PageFooterMobile, ThreadFooterStyleRow } from '../../components/footer';
|
||||
import LoadingEllipsis from '../../components/loading-ellipsis';
|
||||
import Tooltip from '../../components/tooltip';
|
||||
@@ -49,9 +49,7 @@ const DirectoryDesktopTopControls = ({ communityAddress }: { communityAddress: s
|
||||
<span>
|
||||
[<ReturnButton address={communityAddress} />]
|
||||
</span>
|
||||
<span>
|
||||
[<CatalogButton address={communityAddress} />]
|
||||
</span>
|
||||
<BracketedCatalogButton address={communityAddress} />
|
||||
<span>
|
||||
[<BottomButton />]
|
||||
</span>
|
||||
@@ -63,9 +61,7 @@ const DirectoryDesktopFooterControls = ({ communityAddress }: { communityAddress
|
||||
<span>
|
||||
[<ReturnButton address={communityAddress} />]
|
||||
</span>
|
||||
<span>
|
||||
[<CatalogButton address={communityAddress} />]
|
||||
</span>
|
||||
<BracketedCatalogButton address={communityAddress} />
|
||||
<span>
|
||||
[<TopButton />]
|
||||
</span>
|
||||
|
||||
Reference in New Issue
Block a user