diff --git a/src/components/post-mobile/post-mobile.tsx b/src/components/post-mobile/post-mobile.tsx
index 2bfcb417..d2ceef0f 100644
--- a/src/components/post-mobile/post-mobile.tsx
+++ b/src/components/post-mobile/post-mobile.tsx
@@ -43,6 +43,7 @@ import useProgressiveRender from '../../hooks/use-progressive-render';
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 { 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';
const { addChallenge } = useChallengesStore.getState();
@@ -476,46 +477,24 @@ interface ReplyBacklinksProps extends PostProps {
const ReplyBacklinks = ({ post, quotedByMap, directRepliesByParentCid }: ReplyBacklinksProps) => {
const { cid, parentCid } = post || {};
- const directReplies = directRepliesByParentCid?.get(cid) || [];
+ const { opBacklinks, directReplyBacklinks, quotedReplyBacklinks } = getRenderableMobileBacklinks({
+ cid,
+ parentCid,
+ quotedByMap,
+ directRepliesByParentCid,
+ });
- const opBacklinks =
- cid &&
- !parentCid &&
- quotedByMap
- ?.get(cid)
- ?.map(
- (reply: Comment) =>
- reply?.cid &&
- reply?.number &&
- !(reply?.deleted || reply?.removed) && ,
- )
- .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) && ,
- )}
- {quotedByMap
- ?.get(cid)
- ?.map(
- (reply: Comment) =>
- reply?.parentCid !== cid &&
- reply?.cid &&
- reply?.number &&
- !(reply?.deleted || reply?.removed) && ,
- )}
- >
- );
-
- return opBacklinks?.length > 0 || replyBacklinks ? (
+ return opBacklinks.length > 0 || directReplyBacklinks.length > 0 || quotedReplyBacklinks.length > 0 ? (
- {opBacklinks}
- {replyBacklinks}
+ {opBacklinks.map((reply: Comment) => (
+
+ ))}
+ {directReplyBacklinks.map((reply: Comment) => (
+
+ ))}
+ {quotedReplyBacklinks.map((reply: Comment) => (
+
+ ))}
) : null;
};
diff --git a/src/lib/utils/__tests__/reply-backlink-utils.test.ts b/src/lib/utils/__tests__/reply-backlink-utils.test.ts
new file mode 100644
index 00000000..0c84b6a3
--- /dev/null
+++ b/src/lib/utils/__tests__/reply-backlink-utils.test.ts
@@ -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 = {}) =>
+ ({
+ 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);
+ });
+});
diff --git a/src/lib/utils/reply-backlink-utils.ts b/src/lib/utils/reply-backlink-utils.ts
new file mode 100644
index 00000000..b02979a6
--- /dev/null
+++ b/src/lib/utils/reply-backlink-utils.ts
@@ -0,0 +1,44 @@
+import type { Comment } from '@bitsocialnet/bitsocial-react-hooks';
+
+interface GetRenderableMobileBacklinksArgs {
+ cid?: string;
+ parentCid?: string;
+ quotedByMap?: Map;
+ directRepliesByParentCid?: Map;
+}
+
+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)),
+ };
+};