mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
fix(mod multiboard): show account import empty state
This commit is contained in:
@@ -0,0 +1 @@
|
||||
export { default as ModEmptyState } from './mod-empty-state';
|
||||
@@ -0,0 +1,28 @@
|
||||
.modEmptyState {
|
||||
padding: 1em 0;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.modEmptyStateTitle {
|
||||
color: red;
|
||||
display: block;
|
||||
margin: 0;
|
||||
font-size: x-large;
|
||||
font-weight: bold;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.modEmptyStateAction {
|
||||
color: var(--post-mobile-abbr-text-color);
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
|
||||
.modEmptyStateAction a {
|
||||
text-decoration: var(--button-text-decoration);
|
||||
color: var(--button-desktop-text-color);
|
||||
}
|
||||
|
||||
.modEmptyStateAction a:hover {
|
||||
color: var(--button-desktop-text-color-hover);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import styles from './mod-empty-state.module.css';
|
||||
|
||||
const MOD_ACCOUNT_SETTINGS_LINK_COMPONENTS = {
|
||||
1: <Link to='/mod/settings#account-settings' />,
|
||||
};
|
||||
|
||||
const ModEmptyState = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className={styles.modEmptyState} role='status'>
|
||||
<div className={styles.modEmptyStateTitle}>{t('not_mod_of_any_board')}</div>
|
||||
<div className={styles.modEmptyStateAction}>
|
||||
<Trans i18nKey='go_to_settings_to_import_mod_account' components={MOD_ACCOUNT_SETTINGS_LINK_COMPONENTS} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ModEmptyState;
|
||||
@@ -616,6 +616,8 @@ describe('Board', () => {
|
||||
});
|
||||
|
||||
expect(container.textContent).toContain('not_mod_of_any_board');
|
||||
expect(container.textContent).toContain('go_to_settings_to_import_mod_account');
|
||||
expect(container.querySelector('[role="status"]')?.className).toContain('modEmptyState');
|
||||
expect(container.querySelector('a')?.getAttribute('href')).toBe('/mod/settings#account-settings');
|
||||
});
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import LoadingEllipsis from '../../components/loading-ellipsis';
|
||||
import BoardPagination from '../../components/board-pagination';
|
||||
import { CatalogButton } from '../../components/board-buttons/board-buttons';
|
||||
import { PageFooterDesktop, PageFooterMobile } from '../../components/footer';
|
||||
import { ModEmptyState } from '../../components/mod-empty-state';
|
||||
import { Post } from '../post';
|
||||
|
||||
const lastVirtuosoStates: { [key: string]: StateSnapshot } = {};
|
||||
@@ -41,9 +42,6 @@ const MONTH_IN_SECONDS = 30 * 24 * 60 * 60;
|
||||
const YEAR_IN_SECONDS = 365 * 24 * 60 * 60;
|
||||
// Keep the hook on its indexed fast path when this view should not inject local posts.
|
||||
const EMPTY_ACCOUNT_COMMENT_LOOKUP = { commentIndices: [-1] };
|
||||
const MOD_ACCOUNT_IMPORT_LINK_COMPONENTS = {
|
||||
1: <Link to='/mod/settings#account-settings' />,
|
||||
};
|
||||
|
||||
/** Board feed always uses 'active' sort; catalog dropdown does not affect board ordering. */
|
||||
const BOARD_SORT_TYPE = 'active' as const;
|
||||
@@ -137,7 +135,7 @@ const BoardFooter = ({
|
||||
) : isInSubscriptionsView && subscriptionsLength === 0 ? (
|
||||
<span className='red'>{t('not_subscribed_to_any_board')}</span>
|
||||
) : isInModView && accountCommunityAddressesLength === 0 ? (
|
||||
<Trans i18nKey='not_mod_of_any_board' components={MOD_ACCOUNT_IMPORT_LINK_COMPONENTS} />
|
||||
<ModEmptyState />
|
||||
) : (
|
||||
showLoadingEllipsis && (hasMore || isEmptyBoardLoading) && <LoadingEllipsis string={loadingStateString} />
|
||||
)}
|
||||
|
||||
@@ -909,6 +909,22 @@ describe('Catalog', () => {
|
||||
expect(container.querySelector('[data-testid="catalog-first-row"]')?.textContent).toBe('music-posting.eth');
|
||||
});
|
||||
|
||||
it('shows mod empty state with settings link in mod catalog', async () => {
|
||||
testState.accountCommunityAddresses = [];
|
||||
|
||||
await renderCatalog({
|
||||
catalogProps: { viewType: 'mod' },
|
||||
initialEntry: '/mod/catalog',
|
||||
routePath: '/mod/*',
|
||||
});
|
||||
|
||||
expect(container.textContent).toContain('not_mod_of_any_board');
|
||||
expect(container.textContent).toContain('go_to_settings_to_import_mod_account');
|
||||
expect(container.querySelector('[role="status"]')?.className).toContain('modEmptyState');
|
||||
expect(container.querySelector('a')?.getAttribute('href')).toBe('/mod/settings#account-settings');
|
||||
expect(container.querySelectorAll('[data-testid="catalog-row"]')).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('shows centered nothing found when catalog search has no matches', async () => {
|
||||
testState.feed = [{ cid: 'network-post', title: 'cats on stage', communityAddress: 'music-posting.eth' }];
|
||||
testState.searchText = 'asddasd';
|
||||
|
||||
@@ -30,6 +30,7 @@ import { ReturnButton, ArchiveButton, TopButton, RefreshButton } from '../../com
|
||||
import mobileFooterStyles from '../../components/footer/footer.module.css';
|
||||
import LoadingEllipsis from '../../components/loading-ellipsis';
|
||||
import ErrorDisplay from '../../components/error-display/error-display';
|
||||
import { ModEmptyState } from '../../components/mod-empty-state';
|
||||
import styles from './catalog.module.css';
|
||||
import { commentMatchesPattern } from '../../lib/utils/pattern-utils';
|
||||
import { isCommentArchived } from '../../lib/utils/comment-moderation-utils';
|
||||
@@ -695,8 +696,14 @@ const Catalog = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp,
|
||||
);
|
||||
const isFeedLoaded = feed.length > 0 || hiddenThreadsCount > 0 || state === 'failed';
|
||||
const hasActiveSearch = searchText.trim().length > 0;
|
||||
const showModEmptyState = isInModView && accountCommunityAddresses.length === 0;
|
||||
const showSearchNothingFound =
|
||||
hasActiveSearch && !footerHasMore && footerCombinedFeedLength === 0 && state !== 'failed' && !(isInSubscriptionsView && (subscriptions?.length || 0) === 0);
|
||||
hasActiveSearch &&
|
||||
!footerHasMore &&
|
||||
footerCombinedFeedLength === 0 &&
|
||||
state !== 'failed' &&
|
||||
!(isInSubscriptionsView && (subscriptions?.length || 0) === 0) &&
|
||||
!showModEmptyState;
|
||||
|
||||
// Process the feed to move "top" posts to the top (applied after display sort)
|
||||
const processedFeed = useMemo(() => {
|
||||
@@ -901,6 +908,21 @@ const Catalog = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp,
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
) : showModEmptyState ? (
|
||||
<>
|
||||
<ModEmptyState />
|
||||
<hr />
|
||||
{catalogFooterFirstRow}
|
||||
<PageFooterDesktop variant='catalog' styleRow={catalogFooterStyleRow} />
|
||||
<PageFooterMobile>
|
||||
<div className={mobileFooterStyles.mobileFooterButtons}>
|
||||
<ReturnButton address={communityAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} isInModView={isInModView} />
|
||||
<ArchiveButton address={communityAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} isInModView={isInModView} />
|
||||
<TopButton />
|
||||
<RefreshButton />
|
||||
</div>
|
||||
</PageFooterMobile>
|
||||
</>
|
||||
) : showSearchNothingFound ? (
|
||||
<>
|
||||
<CatalogSearchNothingFound />
|
||||
|
||||
Reference in New Issue
Block a user