mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
feat(feed): show account comments instantly in the feed once published, instead of waiting for the feed to update
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { Trans, useTranslation } from 'react-i18next';
|
import { Trans, useTranslation } from 'react-i18next';
|
||||||
import { Link, useLocation, useParams } from 'react-router-dom';
|
import { Link, useLocation, useParams } from 'react-router-dom';
|
||||||
import { Comment, useAccount, useAuthorAvatar, useComment, useEditedComment } from '@plebbit/plebbit-react-hooks';
|
import { Comment, useAccount, useAccountComments, useAuthorAvatar, useComment, useEditedComment } from '@plebbit/plebbit-react-hooks';
|
||||||
import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js';
|
import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js';
|
||||||
import styles from '../../views/post/post.module.css';
|
import styles from '../../views/post/post.module.css';
|
||||||
import { getCommentMediaInfo, getDisplayMediaInfoType, getHasThumbnail } from '../../lib/utils/media-utils';
|
import { getCommentMediaInfo, getDisplayMediaInfoType, getHasThumbnail } from '../../lib/utils/media-utils';
|
||||||
@@ -383,6 +383,9 @@ const PostDesktop = ({ openReplyModal, post, roles, showAllReplies, showReplies
|
|||||||
const visiblelinksCount = useCountLinksInReplies(post, 5);
|
const visiblelinksCount = useCountLinksInReplies(post, 5);
|
||||||
const totalLinksCount = useCountLinksInReplies(post);
|
const totalLinksCount = useCountLinksInReplies(post);
|
||||||
const replyCount = replies?.length;
|
const replyCount = replies?.length;
|
||||||
|
const { accountComments } = useAccountComments();
|
||||||
|
const isPostInAccountComments = accountComments?.find((comment) => comment.cid === cid);
|
||||||
|
|
||||||
const repliesCount = pinned ? replyCount : replyCount - 5;
|
const repliesCount = pinned ? replyCount : replyCount - 5;
|
||||||
const linksCount = pinned ? totalLinksCount : totalLinksCount - visiblelinksCount;
|
const linksCount = pinned ? totalLinksCount : totalLinksCount - visiblelinksCount;
|
||||||
const { showOmittedReplies, setShowOmittedReplies } = useShowOmittedReplies();
|
const { showOmittedReplies, setShowOmittedReplies } = useShowOmittedReplies();
|
||||||
@@ -439,7 +442,7 @@ const PostDesktop = ({ openReplyModal, post, roles, showAllReplies, showReplies
|
|||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{post?.replyCount === undefined && !isInPendingPostView && (
|
{post?.replyCount === undefined && !isPostInAccountComments && !isInPendingPostView && (
|
||||||
<span className={styles.loadingString}>
|
<span className={styles.loadingString}>
|
||||||
<LoadingEllipsis string={t('loading_comments')} />
|
<LoadingEllipsis string={t('loading_comments')} />
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
import { useMemo } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useParams, useLocation } from 'react-router-dom';
|
||||||
|
import { useAccountComments, Subplebbit } from '@plebbit/plebbit-react-hooks';
|
||||||
|
import { isAllView } from '../lib/utils/view-utils';
|
||||||
|
import { useMultisubMetadata } from './use-default-subplebbits';
|
||||||
|
import useCatalogFiltersStore from '../stores/use-catalog-filters-store';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import { getCommentMediaInfo, getHasThumbnail } from '../lib/utils/media-utils';
|
||||||
|
|
||||||
|
const useCatalogFeedRows = (columnCount: number, feed: any, isFeedLoaded: boolean, subplebbit: Subplebbit) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { address, createdAt, description, rules, shortAddress, suggested, title } = subplebbit || {};
|
||||||
|
const { avatarUrl } = suggested || {};
|
||||||
|
const { showTextOnlyThreads } = useCatalogFiltersStore();
|
||||||
|
|
||||||
|
const location = useLocation();
|
||||||
|
const isInAllView = isAllView(location.pathname, useParams());
|
||||||
|
const multisub = useMultisubMetadata();
|
||||||
|
|
||||||
|
const { accountComments } = useAccountComments();
|
||||||
|
|
||||||
|
const feedWithFakePostsOnTop = useMemo(() => {
|
||||||
|
if (!isFeedLoaded) {
|
||||||
|
return []; // prevent rules and description from appearing while feed is loading
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!description && !rules && !isInAllView) {
|
||||||
|
return feed;
|
||||||
|
}
|
||||||
|
|
||||||
|
const _feed = [...feed];
|
||||||
|
|
||||||
|
// show account comments instantly in the feed instead of waiting for the next feed update, then hide them when they are in the feed
|
||||||
|
accountComments.forEach((comment) => {
|
||||||
|
const { cid, deleted, link, postCid, removed, subplebbitAddress } = comment || {};
|
||||||
|
const commentMediaInfo = getCommentMediaInfo(comment);
|
||||||
|
const isMediaShowed = getHasThumbnail(commentMediaInfo, link);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!deleted &&
|
||||||
|
!removed &&
|
||||||
|
(!showTextOnlyThreads || (showTextOnlyThreads && !isMediaShowed)) &&
|
||||||
|
cid &&
|
||||||
|
cid === postCid &&
|
||||||
|
subplebbitAddress === address &&
|
||||||
|
!_feed.some((feedItem) => feedItem.cid === cid)
|
||||||
|
) {
|
||||||
|
_feed.unshift({
|
||||||
|
...comment,
|
||||||
|
isAccountComment: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if ((description && description.length > 0 && (showTextOnlyThreads || (!showTextOnlyThreads && suggested?.avatarUrl))) || isInAllView) {
|
||||||
|
_feed.unshift({
|
||||||
|
isDescription: true,
|
||||||
|
subplebbitAddress: address,
|
||||||
|
timestamp: createdAt,
|
||||||
|
author: { displayName: `## ${t('board_mods')}` },
|
||||||
|
content: isInAllView ? multisub?.description : description,
|
||||||
|
link: avatarUrl,
|
||||||
|
title: t('welcome_to_board', { board: isInAllView ? multisub?.title : title || `p/${shortAddress}`, interpolation: { escapeValue: false } }),
|
||||||
|
pinned: true,
|
||||||
|
locked: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rules && rules.length > 0 && showTextOnlyThreads) {
|
||||||
|
_feed.unshift({
|
||||||
|
isRules: true,
|
||||||
|
subplebbitAddress: address,
|
||||||
|
timestamp: createdAt,
|
||||||
|
author: { displayName: `## ${t('board_mods')}` },
|
||||||
|
content: rules.map((rule: string, index: number) => `${index + 1}. ${rule}`).join('\n'),
|
||||||
|
title: _.capitalize(t('rules')),
|
||||||
|
pinned: true,
|
||||||
|
locked: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return _feed;
|
||||||
|
}, [
|
||||||
|
accountComments,
|
||||||
|
feed,
|
||||||
|
description,
|
||||||
|
rules,
|
||||||
|
address,
|
||||||
|
isFeedLoaded,
|
||||||
|
createdAt,
|
||||||
|
title,
|
||||||
|
shortAddress,
|
||||||
|
avatarUrl,
|
||||||
|
t,
|
||||||
|
isInAllView,
|
||||||
|
multisub,
|
||||||
|
showTextOnlyThreads,
|
||||||
|
suggested?.avatarUrl,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const rows = useMemo(() => {
|
||||||
|
const rows = [];
|
||||||
|
for (let i = 0; i < feedWithFakePostsOnTop.length; i += columnCount) {
|
||||||
|
rows.push(feedWithFakePostsOnTop.slice(i, i + columnCount));
|
||||||
|
}
|
||||||
|
return rows;
|
||||||
|
}, [feedWithFakePostsOnTop, columnCount]);
|
||||||
|
|
||||||
|
return rows;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useCatalogFeedRows;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useEffect, useMemo, useRef } from 'react';
|
import { useEffect, useMemo, useRef } from 'react';
|
||||||
import { useLocation, useParams } from 'react-router-dom';
|
import { useLocation, useParams } from 'react-router-dom';
|
||||||
import { useAccount, useBlock, useFeed, useSubplebbit } from '@plebbit/plebbit-react-hooks';
|
import { useAccount, useAccountComments, useBlock, useFeed, useSubplebbit } from '@plebbit/plebbit-react-hooks';
|
||||||
import { Virtuoso, VirtuosoHandle, StateSnapshot } from 'react-virtuoso';
|
import { Virtuoso, VirtuosoHandle, StateSnapshot } from 'react-virtuoso';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import styles from './board.module.css';
|
import styles from './board.module.css';
|
||||||
@@ -20,6 +20,20 @@ import SubplebbitRules from '../../components/subplebbit-rules';
|
|||||||
|
|
||||||
const lastVirtuosoStates: { [key: string]: StateSnapshot } = {};
|
const lastVirtuosoStates: { [key: string]: StateSnapshot } = {};
|
||||||
|
|
||||||
|
// show account comments instantly in the feed instead of waiting for the next feed update, then hide them when they are in the feed
|
||||||
|
const AccountCommentsNotYetInFeed = ({ subplebbitAddress, feed }: { subplebbitAddress: string; feed: any }) => {
|
||||||
|
const { accountComments } = useAccountComments();
|
||||||
|
|
||||||
|
const _feed = [...feed];
|
||||||
|
|
||||||
|
const filteredComments = accountComments.filter((comment) => {
|
||||||
|
const { cid, deleted, postCid, removed } = comment || {};
|
||||||
|
return !deleted && !removed && cid && cid === postCid && comment?.subplebbitAddress === subplebbitAddress && !_feed.some((post) => post.cid === cid);
|
||||||
|
});
|
||||||
|
|
||||||
|
return filteredComments.map((comment) => <Post key={comment.cid} post={comment} />);
|
||||||
|
};
|
||||||
|
|
||||||
const Board = () => {
|
const Board = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
@@ -161,6 +175,7 @@ const Board = () => {
|
|||||||
title={title}
|
title={title}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{subplebbitAddress && <AccountCommentsNotYetInFeed subplebbitAddress={subplebbitAddress} feed={feed} />}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<Virtuoso
|
<Virtuoso
|
||||||
|
|||||||
@@ -1,86 +1,26 @@
|
|||||||
import { useEffect, useMemo, useRef } from 'react';
|
import { useEffect, useMemo, useRef } from 'react';
|
||||||
import { useLocation, useParams } from 'react-router-dom';
|
import { useLocation, useParams } from 'react-router-dom';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Comment, useAccount, Subplebbit, 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 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 { useMultisubMetadata } from '../../hooks/use-default-subplebbits';
|
|
||||||
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 useCatalogFiltersStore from '../../stores/use-catalog-filters-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 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 _ from 'lodash';
|
|
||||||
import { getCommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-utils';
|
|
||||||
import useCatalogStyleStore from '../../stores/use-catalog-style-store';
|
|
||||||
|
|
||||||
const lastVirtuosoStates: { [key: string]: StateSnapshot } = {};
|
const lastVirtuosoStates: { [key: string]: StateSnapshot } = {};
|
||||||
|
|
||||||
const useFeedRows = (columnCount: number, feed: any, isFeedLoaded: boolean, subplebbit: Subplebbit) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const { address, createdAt, description, rules, shortAddress, suggested, title } = subplebbit || {};
|
|
||||||
const { avatarUrl } = suggested || {};
|
|
||||||
const { showTextOnlyThreads } = useCatalogFiltersStore();
|
|
||||||
|
|
||||||
const location = useLocation();
|
|
||||||
const isInAllView = isAllView(location.pathname, useParams());
|
|
||||||
const multisub = useMultisubMetadata();
|
|
||||||
|
|
||||||
const feedWithDescriptionAndRules = useMemo(() => {
|
|
||||||
if (!isFeedLoaded) {
|
|
||||||
return []; // prevent rules and description from appearing while feed is loading
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!description && !rules && !isInAllView) {
|
|
||||||
return feed;
|
|
||||||
}
|
|
||||||
|
|
||||||
const _feed = [...feed];
|
|
||||||
if ((description && description.length > 0 && (showTextOnlyThreads || (!showTextOnlyThreads && suggested?.avatarUrl))) || isInAllView) {
|
|
||||||
_feed.unshift({
|
|
||||||
isDescription: true,
|
|
||||||
subplebbitAddress: address,
|
|
||||||
timestamp: createdAt,
|
|
||||||
author: { displayName: `## ${t('board_mods')}` },
|
|
||||||
content: isInAllView ? multisub?.description : description,
|
|
||||||
link: avatarUrl,
|
|
||||||
title: t('welcome_to_board', { board: isInAllView ? multisub?.title : title || `p/${shortAddress}`, interpolation: { escapeValue: false } }),
|
|
||||||
pinned: true,
|
|
||||||
locked: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (rules && rules.length > 0 && showTextOnlyThreads) {
|
|
||||||
_feed.unshift({
|
|
||||||
isRules: true,
|
|
||||||
subplebbitAddress: address,
|
|
||||||
timestamp: createdAt,
|
|
||||||
author: { displayName: `## ${t('board_mods')}` },
|
|
||||||
content: rules.map((rule: string, index: number) => `${index + 1}. ${rule}`).join('\n'),
|
|
||||||
title: _.capitalize(t('rules')),
|
|
||||||
pinned: true,
|
|
||||||
locked: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return _feed;
|
|
||||||
}, [feed, description, rules, address, isFeedLoaded, createdAt, title, shortAddress, avatarUrl, t, isInAllView, multisub, showTextOnlyThreads, suggested?.avatarUrl]);
|
|
||||||
|
|
||||||
const rows = useMemo(() => {
|
|
||||||
const rows = [];
|
|
||||||
for (let i = 0; i < feedWithDescriptionAndRules.length; i += columnCount) {
|
|
||||||
rows.push(feedWithDescriptionAndRules.slice(i, i + columnCount));
|
|
||||||
}
|
|
||||||
return rows;
|
|
||||||
}, [feedWithDescriptionAndRules, columnCount]);
|
|
||||||
|
|
||||||
return rows;
|
|
||||||
};
|
|
||||||
|
|
||||||
const catalogFilter = (comment: Comment) => {
|
const catalogFilter = (comment: Comment) => {
|
||||||
const commentMediaInfo = getCommentMediaInfo(comment);
|
const commentMediaInfo = getCommentMediaInfo(comment);
|
||||||
const hasThumbnail = getHasThumbnail(commentMediaInfo, comment?.link);
|
const hasThumbnail = getHasThumbnail(commentMediaInfo, comment?.link);
|
||||||
@@ -212,7 +152,7 @@ const Catalog = () => {
|
|||||||
|
|
||||||
const isFeedLoaded = feed.length > 0 || state === 'failed';
|
const isFeedLoaded = feed.length > 0 || state === 'failed';
|
||||||
|
|
||||||
const rows = useFeedRows(columnCount, feed, isFeedLoaded, subplebbit);
|
const rows = useCatalogFeedRows(columnCount, feed, isFeedLoaded, subplebbit);
|
||||||
|
|
||||||
// save the last Virtuoso state to restore it when navigating back
|
// save the last Virtuoso state to restore it when navigating back
|
||||||
const virtuosoRef = useRef<VirtuosoHandle | null>(null);
|
const virtuosoRef = useRef<VirtuosoHandle | null>(null);
|
||||||
|
|||||||
Reference in New Issue
Block a user