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:
@@ -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}>
|
||||
|
||||
Reference in New Issue
Block a user