mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
feat(catalog): add warning to switch filter if there aren't enough posts in p/all and p/subscriptions
This commit is contained in:
@@ -143,7 +143,7 @@ const Board = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const handleNewerPostsButtonClick = () => {
|
const handleNewerPostsButtonClick = () => {
|
||||||
window.scrollTo({ top: 0, left: 0, behavior: 'smooth' });
|
window.scrollTo({ top: 0, left: 0 });
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
reset();
|
reset();
|
||||||
}, 300);
|
}, 300);
|
||||||
@@ -202,7 +202,7 @@ const Board = () => {
|
|||||||
i18nKey='more_posts_last_week'
|
i18nKey='more_posts_last_week'
|
||||||
values={{ currentTimeFilterName }}
|
values={{ currentTimeFilterName }}
|
||||||
components={{
|
components={{
|
||||||
1: <Link to={(isInAllView ? '/p/all' : isInSubscriptionsView ? '/p/subs' : `/p/${subplebbitAddress}`) + '/1w'} />,
|
1: <Link to={(isInAllView ? '/p/all' : isInSubscriptionsView ? '/p/subscriptions' : `/p/${subplebbitAddress}`) + '/1w'} />,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -212,7 +212,7 @@ const Board = () => {
|
|||||||
i18nKey='more_posts_last_month'
|
i18nKey='more_posts_last_month'
|
||||||
values={{ currentTimeFilterName }}
|
values={{ currentTimeFilterName }}
|
||||||
components={{
|
components={{
|
||||||
1: <Link to={(isInAllView ? '/p/all' : isInSubscriptionsView ? '/p/subs' : `/p/${subplebbitAddress}`) + '/1m'} />,
|
1: <Link to={(isInAllView ? '/p/all' : isInSubscriptionsView ? '/p/subscriptions' : `/p/${subplebbitAddress}`) + '/1m'} />,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,14 @@
|
|||||||
.footer {
|
.footer {
|
||||||
padding-top: 10px;
|
padding: 15px 0 10px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer a {
|
||||||
|
text-decoration: var(--button-text-decoration);
|
||||||
|
color: var(--button-desktop-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer a:hover {
|
||||||
|
color: var(--button-desktop-text-color-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 640px) {
|
@media (max-width: 640px) {
|
||||||
|
|||||||
+138
-49
@@ -1,26 +1,33 @@
|
|||||||
import { useEffect, useMemo, useRef } from 'react';
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useLocation, useParams } from 'react-router-dom';
|
import { Link, useLocation, useParams } from 'react-router-dom';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { Trans, useTranslation } from 'react-i18next';
|
||||||
import { useAccount, useFeed, useSubplebbit, useBlock } from '@plebbit/plebbit-react-hooks';
|
import { Comment, useAccount, useFeed, useSubplebbit, useBlock } from '@plebbit/plebbit-react-hooks';
|
||||||
import { Virtuoso, VirtuosoHandle, StateSnapshot } from 'react-virtuoso';
|
import { Virtuoso, VirtuosoHandle, StateSnapshot } from 'react-virtuoso';
|
||||||
|
import { getCommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-utils';
|
||||||
import { isAllView, isSubscriptionsView } from '../../lib/utils/view-utils';
|
import { isAllView, isSubscriptionsView } from '../../lib/utils/view-utils';
|
||||||
import useCatalogFeedRows from '../../hooks/use-catalog-feed-rows';
|
import useCatalogFeedRows from '../../hooks/use-catalog-feed-rows';
|
||||||
import useDefaultSubplebbits from '../../hooks/use-default-subplebbits';
|
import useDefaultSubplebbits from '../../hooks/use-default-subplebbits';
|
||||||
import useFeedStateString from '../../hooks/use-feed-state-string';
|
import useFeedStateString from '../../hooks/use-feed-state-string';
|
||||||
import useTimeFilter from '../../hooks/use-time-filter';
|
import useTimeFilter from '../../hooks/use-time-filter';
|
||||||
import useWindowWidth from '../../hooks/use-window-width';
|
import useWindowWidth from '../../hooks/use-window-width';
|
||||||
import useCatalogFiltersStore from '../../stores/use-catalog-filters-store';
|
|
||||||
import useCatalogStyleStore from '../../stores/use-catalog-style-store';
|
import useCatalogStyleStore from '../../stores/use-catalog-style-store';
|
||||||
import useFeedResetStore from '../../stores/use-feed-reset-store';
|
import useFeedResetStore from '../../stores/use-feed-reset-store';
|
||||||
|
import useInterfaceSettingsStore from '../../stores/use-interface-settings-store';
|
||||||
import useSortingStore from '../../stores/use-sorting-store';
|
import useSortingStore from '../../stores/use-sorting-store';
|
||||||
import CatalogRow from '../../components/catalog-row';
|
import CatalogRow from '../../components/catalog-row';
|
||||||
import LoadingEllipsis from '../../components/loading-ellipsis';
|
import LoadingEllipsis from '../../components/loading-ellipsis';
|
||||||
import SettingsModal from '../../components/settings-modal';
|
import SettingsModal from '../../components/settings-modal';
|
||||||
import styles from './catalog.module.css';
|
import styles from './catalog.module.css';
|
||||||
import useInterfaceSettingsStore from '../../stores/use-interface-settings-store';
|
|
||||||
|
|
||||||
const lastVirtuosoStates: { [key: string]: StateSnapshot } = {};
|
const lastVirtuosoStates: { [key: string]: StateSnapshot } = {};
|
||||||
|
|
||||||
|
const threadsWithoutImagesFilter = (comment: Comment) => {
|
||||||
|
if (!getHasThumbnail(getCommentMediaInfo(comment), comment?.link)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
const Catalog = () => {
|
const Catalog = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
@@ -29,7 +36,7 @@ const Catalog = () => {
|
|||||||
const isInAllView = isAllView(location.pathname, useParams());
|
const isInAllView = isAllView(location.pathname, useParams());
|
||||||
const defaultSubplebbits = useDefaultSubplebbits();
|
const defaultSubplebbits = useDefaultSubplebbits();
|
||||||
const { hideAdultBoards, hideGoreBoards } = useInterfaceSettingsStore();
|
const { hideAdultBoards, hideGoreBoards } = useInterfaceSettingsStore();
|
||||||
const { filter } = useCatalogFiltersStore();
|
const { hideThreadsWithoutImages } = useInterfaceSettingsStore();
|
||||||
|
|
||||||
const account = useAccount();
|
const account = useAccount();
|
||||||
const subscriptions = account?.subscriptions;
|
const subscriptions = account?.subscriptions;
|
||||||
@@ -64,7 +71,7 @@ const Catalog = () => {
|
|||||||
const columnCount = Math.floor(useWindowWidth() / columnWidth);
|
const columnCount = Math.floor(useWindowWidth() / columnWidth);
|
||||||
const postsPerPage = columnCount <= 2 ? 10 : columnCount === 3 ? 15 : columnCount === 4 ? 20 : 25;
|
const postsPerPage = columnCount <= 2 ? 10 : columnCount === 3 ? 15 : columnCount === 4 ? 20 : 25;
|
||||||
|
|
||||||
const { timeFilterSeconds } = useTimeFilter();
|
const { timeFilterSeconds, timeFilterName } = useTimeFilter();
|
||||||
const { sortType } = useSortingStore();
|
const { sortType } = useSortingStore();
|
||||||
|
|
||||||
const feedOptions = useMemo(() => {
|
const feedOptions = useMemo(() => {
|
||||||
@@ -72,7 +79,7 @@ const Catalog = () => {
|
|||||||
subplebbitAddresses,
|
subplebbitAddresses,
|
||||||
sortType,
|
sortType,
|
||||||
postsPerPage: isInAllView || isInSubscriptionsView ? 10 : postsPerPage,
|
postsPerPage: isInAllView || isInSubscriptionsView ? 10 : postsPerPage,
|
||||||
filter,
|
filter: hideThreadsWithoutImages ? threadsWithoutImagesFilter : undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isInAllView || isInSubscriptionsView) {
|
if (isInAllView || isInSubscriptionsView) {
|
||||||
@@ -80,9 +87,39 @@ const Catalog = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return options;
|
return options;
|
||||||
}, [subplebbitAddresses, sortType, isInAllView, isInSubscriptionsView, postsPerPage, timeFilterSeconds, filter]);
|
}, [subplebbitAddresses, sortType, isInAllView, isInSubscriptionsView, postsPerPage, timeFilterSeconds, hideThreadsWithoutImages]);
|
||||||
|
|
||||||
const { feed, hasMore, loadMore, reset } = useFeed(feedOptions);
|
const { feed, hasMore, loadMore, reset, subplebbitAddressesWithNewerPosts } = useFeed(feedOptions);
|
||||||
|
|
||||||
|
const handleNewerPostsButtonClick = () => {
|
||||||
|
window.scrollTo({ top: 0, left: 0 });
|
||||||
|
setTimeout(() => {
|
||||||
|
reset();
|
||||||
|
}, 300);
|
||||||
|
};
|
||||||
|
|
||||||
|
// suggest the user to change time filter if there aren't enough posts
|
||||||
|
const { feed: weeklyFeed } = useFeed({
|
||||||
|
subplebbitAddresses,
|
||||||
|
sortType,
|
||||||
|
newerThan: 60 * 60 * 24 * 7,
|
||||||
|
filter: hideThreadsWithoutImages ? threadsWithoutImagesFilter : undefined,
|
||||||
|
});
|
||||||
|
const { feed: monthlyFeed } = useFeed({
|
||||||
|
subplebbitAddresses,
|
||||||
|
sortType,
|
||||||
|
newerThan: 60 * 60 * 24 * 30,
|
||||||
|
filter: hideThreadsWithoutImages ? threadsWithoutImagesFilter : undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
const [showMorePostsSuggestion, setShowMorePostsSuggestion] = useState(false);
|
||||||
|
useEffect(() => {
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
setShowMorePostsSuggestion(true);
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
return () => clearTimeout(timer);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const setResetFunction = useFeedResetStore((state) => state.setResetFunction);
|
const setResetFunction = useFeedResetStore((state) => state.setResetFunction);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -91,18 +128,24 @@ const Catalog = () => {
|
|||||||
|
|
||||||
const subplebbit = useSubplebbit({ subplebbitAddress });
|
const subplebbit = useSubplebbit({ subplebbitAddress });
|
||||||
const { error, shortAddress, state, title } = subplebbit || {};
|
const { error, shortAddress, state, title } = subplebbit || {};
|
||||||
|
const { blocked, unblock } = useBlock({ address: subplebbitAddress });
|
||||||
const loadingStateString = useFeedStateString(subplebbitAddresses) || t('loading');
|
const loadingStateString = useFeedStateString(subplebbitAddresses) || t('loading');
|
||||||
|
|
||||||
const loadingString = (
|
const loadingString = (
|
||||||
<div className={styles.stateString}>
|
<div className={styles.stateString}>
|
||||||
{state === 'failed' ? (
|
{state === 'failed' ? (
|
||||||
state
|
<span className='red'>{state}</span>
|
||||||
) : isInSubscriptionsView && subscriptions?.length === 0 ? (
|
) : isInSubscriptionsView && subscriptions?.length === 0 ? (
|
||||||
t('not_subscribed_to_any_board')
|
t('not_subscribed_to_any_board')
|
||||||
|
) : blocked ? (
|
||||||
|
'you have blocked this board'
|
||||||
|
) : !hasMore && feed.length === 0 ? (
|
||||||
|
t('no_posts')
|
||||||
) : (
|
) : (
|
||||||
<LoadingEllipsis string={loadingStateString} />
|
hasMore && <LoadingEllipsis string={loadingStateString} />
|
||||||
)}
|
)}
|
||||||
{error && (
|
{error && (
|
||||||
<div style={{ color: 'red' }}>
|
<div className='red'>
|
||||||
<br />
|
<br />
|
||||||
{error.message}
|
{error.message}
|
||||||
</div>
|
</div>
|
||||||
@@ -110,7 +153,8 @@ const Catalog = () => {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
const { blocked, unblock } = useBlock({ address: subplebbitAddress });
|
const params = useParams();
|
||||||
|
const currentTimeFilterName = params?.timeFilterName || timeFilterName;
|
||||||
|
|
||||||
const Footer = () => {
|
const Footer = () => {
|
||||||
let footerContent;
|
let footerContent;
|
||||||
@@ -121,29 +165,51 @@ const Catalog = () => {
|
|||||||
footerContent = t('no_posts');
|
footerContent = t('no_posts');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (hasMore || subplebbitAddresses.length === 0) {
|
if (hasMore || (subplebbitAddresses && subplebbitAddresses.length === 0)) {
|
||||||
footerContent = loadingString;
|
footerContent = (
|
||||||
|
<>
|
||||||
|
{subplebbitAddressesWithNewerPosts.length > 0 ? (
|
||||||
|
<div className={styles.stateString}>
|
||||||
|
<Trans
|
||||||
|
i18nKey='newer_posts_available'
|
||||||
|
components={{
|
||||||
|
1: <span onClick={handleNewerPostsButtonClick} />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
(isInAllView || isInSubscriptionsView) &&
|
||||||
|
showMorePostsSuggestion &&
|
||||||
|
monthlyFeed.length > feed.length &&
|
||||||
|
(weeklyFeed.length > feed.length ? (
|
||||||
|
<div className={styles.stateString}>
|
||||||
|
<Trans
|
||||||
|
i18nKey='more_posts_last_week'
|
||||||
|
values={{ currentTimeFilterName }}
|
||||||
|
components={{
|
||||||
|
1: <Link to={(isInAllView ? '/p/all/catalog' : isInSubscriptionsView ? '/p/subscriptions/catalog' : `/p/${subplebbitAddress}/catalog`) + '/1w'} />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className={styles.stateString}>
|
||||||
|
<Trans
|
||||||
|
i18nKey='more_posts_last_month'
|
||||||
|
values={{ currentTimeFilterName }}
|
||||||
|
components={{
|
||||||
|
1: <Link to={(isInAllView ? '/p/all/catalog' : isInSubscriptionsView ? '/p/subscriptions/catalog' : `/p/${subplebbitAddress}/catalog`) + '/1m'} />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
<div className={styles.stateString}>
|
||||||
|
<LoadingEllipsis string={loadingStateString} />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return (
|
return <div className={styles.footer}>{footerContent}</div>;
|
||||||
<div className={styles.footer}>
|
|
||||||
{footerContent}
|
|
||||||
{blocked && (
|
|
||||||
<>
|
|
||||||
[
|
|
||||||
<span
|
|
||||||
className={styles.button}
|
|
||||||
onClick={() => {
|
|
||||||
unblock();
|
|
||||||
reset();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Unblock
|
|
||||||
</span>
|
|
||||||
]
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const isFeedLoaded = feed.length > 0 || state === 'failed';
|
const isFeedLoaded = feed.length > 0 || state === 'failed';
|
||||||
@@ -177,18 +243,41 @@ const Catalog = () => {
|
|||||||
{location.pathname.endsWith('/settings') && <SettingsModal />}
|
{location.pathname.endsWith('/settings') && <SettingsModal />}
|
||||||
<hr />
|
<hr />
|
||||||
<div className={styles.catalog}>
|
<div className={styles.catalog}>
|
||||||
<Virtuoso
|
{feed.length !== 0 ? (
|
||||||
increaseViewportBy={{ bottom: 1200, top: 1200 }}
|
<>
|
||||||
totalCount={rows?.length || 0}
|
<Virtuoso
|
||||||
data={rows}
|
increaseViewportBy={{ bottom: 1200, top: 1200 }}
|
||||||
itemContent={(index, row) => <CatalogRow index={index} row={row} />}
|
totalCount={rows?.length || 0}
|
||||||
useWindowScroll={true}
|
data={rows}
|
||||||
components={{ Footer }}
|
itemContent={(index, row) => <CatalogRow index={index} row={row} />}
|
||||||
endReached={loadMore}
|
useWindowScroll={true}
|
||||||
ref={virtuosoRef}
|
components={{ Footer }}
|
||||||
restoreStateFrom={lastVirtuosoState}
|
endReached={loadMore}
|
||||||
initialScrollTop={lastVirtuosoState?.scrollTop}
|
ref={virtuosoRef}
|
||||||
/>
|
restoreStateFrom={lastVirtuosoState}
|
||||||
|
initialScrollTop={lastVirtuosoState?.scrollTop}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<div className={styles.footer}>
|
||||||
|
{loadingString}
|
||||||
|
{blocked && (
|
||||||
|
<>
|
||||||
|
[
|
||||||
|
<span
|
||||||
|
className={styles.button}
|
||||||
|
onClick={() => {
|
||||||
|
unblock();
|
||||||
|
reset();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Unblock
|
||||||
|
</span>
|
||||||
|
]
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user