mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
fix(mobile): hide empty mobile reply backlink wrapper (#1060)
getRenderableMobileBacklinks() now filters mobile backlinks down to replies that can actually render, so ReplyBacklinks only mounts mobileReplyBacklinks when there is visible content. This adds a regression test for the gap where a newly published reply exists before it receives its post number.
This commit is contained in:
@@ -43,6 +43,7 @@ import useProgressiveRender from '../../hooks/use-progressive-render';
|
|||||||
import useFreshReplies from '../../hooks/use-fresh-replies';
|
import useFreshReplies from '../../hooks/use-fresh-replies';
|
||||||
import { BOARD_REPLIES_PREVIEW_FETCH_SIZE, BOARD_REPLIES_PREVIEW_VISIBLE_COUNT, REPLIES_PER_PAGE } from '../../lib/constants';
|
import { BOARD_REPLIES_PREVIEW_FETCH_SIZE, BOARD_REPLIES_PREVIEW_VISIBLE_COUNT, REPLIES_PER_PAGE } from '../../lib/constants';
|
||||||
import { filterRepliesForDisplay, getPreviewDisplayReplies } from '../../lib/utils/replies-preview-utils';
|
import { filterRepliesForDisplay, getPreviewDisplayReplies } from '../../lib/utils/replies-preview-utils';
|
||||||
|
import { getRenderableMobileBacklinks } from '../../lib/utils/reply-backlink-utils';
|
||||||
import { getThreadTopNavigationState, scrollThreadContainerToTop } from '../../lib/utils/thread-scroll-utils';
|
import { getThreadTopNavigationState, scrollThreadContainerToTop } from '../../lib/utils/thread-scroll-utils';
|
||||||
|
|
||||||
const { addChallenge } = useChallengesStore.getState();
|
const { addChallenge } = useChallengesStore.getState();
|
||||||
@@ -476,46 +477,24 @@ interface ReplyBacklinksProps extends PostProps {
|
|||||||
|
|
||||||
const ReplyBacklinks = ({ post, quotedByMap, directRepliesByParentCid }: ReplyBacklinksProps) => {
|
const ReplyBacklinks = ({ post, quotedByMap, directRepliesByParentCid }: ReplyBacklinksProps) => {
|
||||||
const { cid, parentCid } = post || {};
|
const { cid, parentCid } = post || {};
|
||||||
const directReplies = directRepliesByParentCid?.get(cid) || [];
|
const { opBacklinks, directReplyBacklinks, quotedReplyBacklinks } = getRenderableMobileBacklinks({
|
||||||
|
cid,
|
||||||
|
parentCid,
|
||||||
|
quotedByMap,
|
||||||
|
directRepliesByParentCid,
|
||||||
|
});
|
||||||
|
|
||||||
const opBacklinks =
|
return opBacklinks.length > 0 || directReplyBacklinks.length > 0 || quotedReplyBacklinks.length > 0 ? (
|
||||||
cid &&
|
|
||||||
!parentCid &&
|
|
||||||
quotedByMap
|
|
||||||
?.get(cid)
|
|
||||||
?.map(
|
|
||||||
(reply: Comment) =>
|
|
||||||
reply?.cid &&
|
|
||||||
reply?.number &&
|
|
||||||
!(reply?.deleted || reply?.removed) && <ReplyQuotePreview key={`op-bl-${reply.cid}`} isBacklinkReply={true} backlinkReply={reply} />,
|
|
||||||
)
|
|
||||||
.filter(Boolean);
|
|
||||||
|
|
||||||
const replyBacklinks = cid && parentCid && (directReplies.length > 0 || quotedByMap?.get(cid)?.length) && (
|
|
||||||
<>
|
|
||||||
{directReplies.map(
|
|
||||||
(reply: Comment) =>
|
|
||||||
reply?.parentCid === cid &&
|
|
||||||
reply?.cid &&
|
|
||||||
reply?.number &&
|
|
||||||
!(reply?.deleted || reply?.removed) && <ReplyQuotePreview key={reply.cid} isBacklinkReply={true} backlinkReply={reply} />,
|
|
||||||
)}
|
|
||||||
{quotedByMap
|
|
||||||
?.get(cid)
|
|
||||||
?.map(
|
|
||||||
(reply: Comment) =>
|
|
||||||
reply?.parentCid !== cid &&
|
|
||||||
reply?.cid &&
|
|
||||||
reply?.number &&
|
|
||||||
!(reply?.deleted || reply?.removed) && <ReplyQuotePreview key={`qb-${reply.cid}`} isBacklinkReply={true} backlinkReply={reply} />,
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
return opBacklinks?.length > 0 || replyBacklinks ? (
|
|
||||||
<div className={styles.mobileReplyBacklinks}>
|
<div className={styles.mobileReplyBacklinks}>
|
||||||
{opBacklinks}
|
{opBacklinks.map((reply: Comment) => (
|
||||||
{replyBacklinks}
|
<ReplyQuotePreview key={`op-bl-${reply.cid}`} isBacklinkReply={true} backlinkReply={reply} />
|
||||||
|
))}
|
||||||
|
{directReplyBacklinks.map((reply: Comment) => (
|
||||||
|
<ReplyQuotePreview key={reply.cid} isBacklinkReply={true} backlinkReply={reply} />
|
||||||
|
))}
|
||||||
|
{quotedReplyBacklinks.map((reply: Comment) => (
|
||||||
|
<ReplyQuotePreview key={`qb-${reply.cid}`} isBacklinkReply={true} backlinkReply={reply} />
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
) : null;
|
) : null;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import type { Comment } from '@bitsocialnet/bitsocial-react-hooks';
|
||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import { getRenderableMobileBacklinks } from '../reply-backlink-utils';
|
||||||
|
|
||||||
|
const createReply = (overrides: Partial<Comment> = {}) =>
|
||||||
|
({
|
||||||
|
cid: 'reply-cid',
|
||||||
|
parentCid: 'target-cid',
|
||||||
|
subplebbitAddress: 'music.eth',
|
||||||
|
...overrides,
|
||||||
|
}) as Comment;
|
||||||
|
|
||||||
|
describe('getRenderableMobileBacklinks', () => {
|
||||||
|
it('does not report mobile reply backlinks until a renderable backlink exists', () => {
|
||||||
|
const unpublishedReply = createReply({
|
||||||
|
number: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
const beforePublish = getRenderableMobileBacklinks({
|
||||||
|
cid: 'target-cid',
|
||||||
|
parentCid: 'op-cid',
|
||||||
|
quotedByMap: new Map([['target-cid', [unpublishedReply]]]),
|
||||||
|
directRepliesByParentCid: new Map([['target-cid', [unpublishedReply]]]),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(beforePublish.directReplyBacklinks).toHaveLength(0);
|
||||||
|
expect(beforePublish.quotedReplyBacklinks).toHaveLength(0);
|
||||||
|
|
||||||
|
const publishedReply = createReply({
|
||||||
|
cid: 'published-reply-cid',
|
||||||
|
number: 42,
|
||||||
|
});
|
||||||
|
|
||||||
|
const afterPublish = getRenderableMobileBacklinks({
|
||||||
|
cid: 'target-cid',
|
||||||
|
parentCid: 'op-cid',
|
||||||
|
quotedByMap: new Map([['target-cid', [publishedReply]]]),
|
||||||
|
directRepliesByParentCid: new Map([['target-cid', [publishedReply]]]),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(afterPublish.directReplyBacklinks).toEqual([publishedReply]);
|
||||||
|
expect(afterPublish.quotedReplyBacklinks).toHaveLength(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import type { Comment } from '@bitsocialnet/bitsocial-react-hooks';
|
||||||
|
|
||||||
|
interface GetRenderableMobileBacklinksArgs {
|
||||||
|
cid?: string;
|
||||||
|
parentCid?: string;
|
||||||
|
quotedByMap?: Map<string, Comment[]>;
|
||||||
|
directRepliesByParentCid?: Map<string, Comment[]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RenderableMobileBacklinks {
|
||||||
|
opBacklinks: Comment[];
|
||||||
|
directReplyBacklinks: Comment[];
|
||||||
|
quotedReplyBacklinks: Comment[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const isRenderableBacklinkReply = (reply?: Comment) => Boolean(reply?.cid && typeof reply.number === 'number' && !(reply.deleted || reply.removed));
|
||||||
|
|
||||||
|
export const getRenderableMobileBacklinks = ({ cid, parentCid, quotedByMap, directRepliesByParentCid }: GetRenderableMobileBacklinksArgs): RenderableMobileBacklinks => {
|
||||||
|
if (!cid) {
|
||||||
|
return {
|
||||||
|
opBacklinks: [],
|
||||||
|
directReplyBacklinks: [],
|
||||||
|
quotedReplyBacklinks: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const quotedReplies = quotedByMap?.get(cid) ?? [];
|
||||||
|
|
||||||
|
if (!parentCid) {
|
||||||
|
return {
|
||||||
|
opBacklinks: quotedReplies.filter(isRenderableBacklinkReply),
|
||||||
|
directReplyBacklinks: [],
|
||||||
|
quotedReplyBacklinks: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const directReplies = directRepliesByParentCid?.get(cid) ?? [];
|
||||||
|
|
||||||
|
return {
|
||||||
|
opBacklinks: [],
|
||||||
|
directReplyBacklinks: directReplies.filter((reply) => reply?.parentCid === cid && isRenderableBacklinkReply(reply)),
|
||||||
|
quotedReplyBacklinks: quotedReplies.filter((reply) => reply?.parentCid !== cid && isRenderableBacklinkReply(reply)),
|
||||||
|
};
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user