From 5d18470ab75f53815abae3b11bb9511ded59ba56 Mon Sep 17 00:00:00 2001 From: rebelchris Date: Thu, 5 Mar 2026 07:53:00 +0000 Subject: [PATCH 1/7] feat(shared): trim feed graphql post fields --- packages/shared/src/graphql/feed.spec.ts | 18 ++++++++++++++ packages/shared/src/graphql/feed.ts | 4 ++-- packages/shared/src/graphql/fragments.ts | 30 ------------------------ 3 files changed, 20 insertions(+), 32 deletions(-) create mode 100644 packages/shared/src/graphql/feed.spec.ts diff --git a/packages/shared/src/graphql/feed.spec.ts b/packages/shared/src/graphql/feed.spec.ts new file mode 100644 index 0000000000..bcdcf9cd73 --- /dev/null +++ b/packages/shared/src/graphql/feed.spec.ts @@ -0,0 +1,18 @@ +import { FEED_POST_CONNECTION_FRAGMENT, SOURCE_FEED_QUERY } from './feed'; + +it('should request plain content for feed posts', () => { + expect(FEED_POST_CONNECTION_FRAGMENT).toContain('content'); + expect(FEED_POST_CONNECTION_FRAGMENT).not.toContain('contentHtml'); + expect(SOURCE_FEED_QUERY).toContain('pinnedAt content'); + expect(SOURCE_FEED_QUERY).not.toContain('pinnedAt contentHtml'); +}); + +it('should keep feed post fragment lean', () => { + expect(FEED_POST_CONNECTION_FRAGMENT).not.toContain('views'); + expect(FEED_POST_CONNECTION_FRAGMENT).not.toContain('numAwards'); + expect(FEED_POST_CONNECTION_FRAGMENT).not.toContain('featuredAward'); + expect(FEED_POST_CONNECTION_FRAGMENT).not.toContain('updatedAt'); + expect(FEED_POST_CONNECTION_FRAGMENT).not.toContain('digestPostIds'); + expect(FEED_POST_CONNECTION_FRAGMENT).not.toContain('callToAction'); + expect(FEED_POST_CONNECTION_FRAGMENT).not.toContain('private'); +}); diff --git a/packages/shared/src/graphql/feed.ts b/packages/shared/src/graphql/feed.ts index da2a358b4b..1c66a2a866 100644 --- a/packages/shared/src/graphql/feed.ts +++ b/packages/shared/src/graphql/feed.ts @@ -94,7 +94,7 @@ const getFeedPostFragment = (fields = '') => gql` ${USER_POST_FRAGMENT} `; -export const FEED_POST_CONNECTION_FRAGMENT = getFeedPostFragment('contentHtml'); +export const FEED_POST_CONNECTION_FRAGMENT = getFeedPostFragment('content'); export const ANONYMOUS_FEED_QUERY = gql` query AnonymousFeed( @@ -223,7 +223,7 @@ export const SOURCE_FEED_QUERY = gql` ...FeedPostConnection } } - ${getFeedPostFragment('pinnedAt contentHtml')} + ${getFeedPostFragment('pinnedAt content')} `; export const CHANNEL_FEED_QUERY = gql` diff --git a/packages/shared/src/graphql/fragments.ts b/packages/shared/src/graphql/fragments.ts index dc7872ee49..f6f2af6fe7 100644 --- a/packages/shared/src/graphql/fragments.ts +++ b/packages/shared/src/graphql/fragments.ts @@ -224,10 +224,8 @@ export const FEED_POST_INFO_FRAGMENT = gql` createdAt commented bookmarked - views numUpvotes numComments - numAwards summary yggdrasilId creatorTwitter @@ -293,12 +291,6 @@ export const FEED_POST_INFO_FRAGMENT = gql` translation { ...PostTranslateableFields } - numAwards - featuredAward { - award { - image - } - } numPollVotes pollOptions { id @@ -579,8 +571,6 @@ export const FEED_POST_FRAGMENT = gql` type subType tags - private - yggdrasilId source { id handle @@ -607,30 +597,10 @@ export const FEED_POST_FRAGMENT = gql` image } numCollectionSources - updatedAt - slug flags { posts sources savedTime - digestPostIds - ad { - type - index - title - link - image - companyName - companyLogo - callToAction - } - } - featuredAward { - award { - image - value - name - } } } ${FEED_POST_INFO_FRAGMENT} From 193b59590850e9360899461555b8868f600497e0 Mon Sep 17 00:00:00 2001 From: rebelchris Date: Thu, 5 Mar 2026 07:54:25 +0000 Subject: [PATCH 2/7] refactor(shared): simplify feed graphql regression test --- packages/shared/src/graphql/feed.spec.ts | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/packages/shared/src/graphql/feed.spec.ts b/packages/shared/src/graphql/feed.spec.ts index bcdcf9cd73..cd95921e08 100644 --- a/packages/shared/src/graphql/feed.spec.ts +++ b/packages/shared/src/graphql/feed.spec.ts @@ -8,11 +8,17 @@ it('should request plain content for feed posts', () => { }); it('should keep feed post fragment lean', () => { - expect(FEED_POST_CONNECTION_FRAGMENT).not.toContain('views'); - expect(FEED_POST_CONNECTION_FRAGMENT).not.toContain('numAwards'); - expect(FEED_POST_CONNECTION_FRAGMENT).not.toContain('featuredAward'); - expect(FEED_POST_CONNECTION_FRAGMENT).not.toContain('updatedAt'); - expect(FEED_POST_CONNECTION_FRAGMENT).not.toContain('digestPostIds'); - expect(FEED_POST_CONNECTION_FRAGMENT).not.toContain('callToAction'); - expect(FEED_POST_CONNECTION_FRAGMENT).not.toContain('private'); + const removedFeedFields = [ + 'views', + 'numAwards', + 'featuredAward', + 'updatedAt', + 'digestPostIds', + 'callToAction', + 'private', + ]; + + removedFeedFields.forEach((field) => { + expect(FEED_POST_CONNECTION_FRAGMENT).not.toContain(field); + }); }); From 9a884a7fb737cfd7e617cefc2acb8ac0d1b7a4f4 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Thu, 5 Mar 2026 08:20:16 +0000 Subject: [PATCH 3/7] revert(shared): prefer contentHtml over content and restore feed fragment fields - revert content -> contentHtml in feed query fragments - restore removed fields (views, numAwards, featuredAward, updatedAt, slug, private, yggdrasilId, digestPostIds, ad block) in feed fragments - update regression test to assert contentHtml usage Co-authored-by: Chris Bongers Co-Authored-By: Claude Sonnet 4.6 --- packages/shared/src/graphql/feed.spec.ts | 24 +++---------------- packages/shared/src/graphql/feed.ts | 4 ++-- packages/shared/src/graphql/fragments.ts | 30 ++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 23 deletions(-) diff --git a/packages/shared/src/graphql/feed.spec.ts b/packages/shared/src/graphql/feed.spec.ts index cd95921e08..d6b578f1b1 100644 --- a/packages/shared/src/graphql/feed.spec.ts +++ b/packages/shared/src/graphql/feed.spec.ts @@ -1,24 +1,6 @@ import { FEED_POST_CONNECTION_FRAGMENT, SOURCE_FEED_QUERY } from './feed'; -it('should request plain content for feed posts', () => { - expect(FEED_POST_CONNECTION_FRAGMENT).toContain('content'); - expect(FEED_POST_CONNECTION_FRAGMENT).not.toContain('contentHtml'); - expect(SOURCE_FEED_QUERY).toContain('pinnedAt content'); - expect(SOURCE_FEED_QUERY).not.toContain('pinnedAt contentHtml'); -}); - -it('should keep feed post fragment lean', () => { - const removedFeedFields = [ - 'views', - 'numAwards', - 'featuredAward', - 'updatedAt', - 'digestPostIds', - 'callToAction', - 'private', - ]; - - removedFeedFields.forEach((field) => { - expect(FEED_POST_CONNECTION_FRAGMENT).not.toContain(field); - }); +it('should request html content for feed posts', () => { + expect(FEED_POST_CONNECTION_FRAGMENT).toContain('contentHtml'); + expect(SOURCE_FEED_QUERY).toContain('pinnedAt contentHtml'); }); diff --git a/packages/shared/src/graphql/feed.ts b/packages/shared/src/graphql/feed.ts index 1c66a2a866..da2a358b4b 100644 --- a/packages/shared/src/graphql/feed.ts +++ b/packages/shared/src/graphql/feed.ts @@ -94,7 +94,7 @@ const getFeedPostFragment = (fields = '') => gql` ${USER_POST_FRAGMENT} `; -export const FEED_POST_CONNECTION_FRAGMENT = getFeedPostFragment('content'); +export const FEED_POST_CONNECTION_FRAGMENT = getFeedPostFragment('contentHtml'); export const ANONYMOUS_FEED_QUERY = gql` query AnonymousFeed( @@ -223,7 +223,7 @@ export const SOURCE_FEED_QUERY = gql` ...FeedPostConnection } } - ${getFeedPostFragment('pinnedAt content')} + ${getFeedPostFragment('pinnedAt contentHtml')} `; export const CHANNEL_FEED_QUERY = gql` diff --git a/packages/shared/src/graphql/fragments.ts b/packages/shared/src/graphql/fragments.ts index f6f2af6fe7..dc7872ee49 100644 --- a/packages/shared/src/graphql/fragments.ts +++ b/packages/shared/src/graphql/fragments.ts @@ -224,8 +224,10 @@ export const FEED_POST_INFO_FRAGMENT = gql` createdAt commented bookmarked + views numUpvotes numComments + numAwards summary yggdrasilId creatorTwitter @@ -291,6 +293,12 @@ export const FEED_POST_INFO_FRAGMENT = gql` translation { ...PostTranslateableFields } + numAwards + featuredAward { + award { + image + } + } numPollVotes pollOptions { id @@ -571,6 +579,8 @@ export const FEED_POST_FRAGMENT = gql` type subType tags + private + yggdrasilId source { id handle @@ -597,10 +607,30 @@ export const FEED_POST_FRAGMENT = gql` image } numCollectionSources + updatedAt + slug flags { posts sources savedTime + digestPostIds + ad { + type + index + title + link + image + companyName + companyLogo + callToAction + } + } + featuredAward { + award { + image + value + name + } } } ${FEED_POST_INFO_FRAGMENT} From 741125f7c06aecb1b51e984128cd8e8f0b73d2b2 Mon Sep 17 00:00:00 2001 From: rebelchris Date: Thu, 5 Mar 2026 08:29:00 +0000 Subject: [PATCH 4/7] docs(shared): add feed content field change guardrail --- packages/shared/src/graphql/AGENTS.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/shared/src/graphql/AGENTS.md b/packages/shared/src/graphql/AGENTS.md index bfd33281b5..46ee573eb3 100644 --- a/packages/shared/src/graphql/AGENTS.md +++ b/packages/shared/src/graphql/AGENTS.md @@ -155,3 +155,9 @@ beforeEach(() => { - Queries: `ENTITY_ACTION_QUERY` (e.g., `POST_BY_ID_QUERY`) - Mutations: `ACTION_ENTITY_MUTATION` (e.g., `UPVOTE_POST_MUTATION`) - Fragments: `ENTITY_VARIANT_FRAGMENT` (e.g., `USER_SHORT_FRAGMENT`) + +## Feed Content Field Changes + +- Treat feed payload content format (`content` vs `contentHtml`) as a product/API contract decision, not a generic optimization. +- Before changing feed content fields, confirm current consumer requirements and expected rendering path. +- If requirements are unclear, keep existing feed behavior and add/update a focused query-shape test in `feed.spec.ts`. From 4703dde25615cfcbdd3f5915bb6834b0772ebb0b Mon Sep 17 00:00:00 2001 From: rebelchris Date: Thu, 5 Mar 2026 08:33:44 +0000 Subject: [PATCH 5/7] test(shared): enforce html-only feed content query --- packages/shared/src/graphql/AGENTS.md | 2 ++ packages/shared/src/graphql/feed.spec.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/packages/shared/src/graphql/AGENTS.md b/packages/shared/src/graphql/AGENTS.md index 46ee573eb3..3689344d89 100644 --- a/packages/shared/src/graphql/AGENTS.md +++ b/packages/shared/src/graphql/AGENTS.md @@ -161,3 +161,5 @@ beforeEach(() => { - Treat feed payload content format (`content` vs `contentHtml`) as a product/API contract decision, not a generic optimization. - Before changing feed content fields, confirm current consumer requirements and expected rendering path. - If requirements are unclear, keep existing feed behavior and add/update a focused query-shape test in `feed.spec.ts`. +- Current contract for feed queries in this repo: request `contentHtml` and do not request plain `content`. +- When updating feed fragments/queries, assert both conditions in `feed.spec.ts` (`contentHtml` present, `content` absent). diff --git a/packages/shared/src/graphql/feed.spec.ts b/packages/shared/src/graphql/feed.spec.ts index d6b578f1b1..48e20ec96f 100644 --- a/packages/shared/src/graphql/feed.spec.ts +++ b/packages/shared/src/graphql/feed.spec.ts @@ -2,5 +2,7 @@ import { FEED_POST_CONNECTION_FRAGMENT, SOURCE_FEED_QUERY } from './feed'; it('should request html content for feed posts', () => { expect(FEED_POST_CONNECTION_FRAGMENT).toContain('contentHtml'); + expect(FEED_POST_CONNECTION_FRAGMENT).not.toMatch(/\bcontent\b/); expect(SOURCE_FEED_QUERY).toContain('pinnedAt contentHtml'); + expect(SOURCE_FEED_QUERY).not.toMatch(/pinnedAt content\b/); }); From cbcb76e818b0982c4eaa3f9736d467a556fcd5de Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Thu, 5 Mar 2026 08:38:07 +0000 Subject: [PATCH 6/7] refactor(shared): replace post.content with contentHtml in social twitter cards Remove usage of plain content field from feed card components - use contentHtml exclusively for repost-check and thread-body rendering. Also remove redundant content fallback from post detail page. Co-authored-by: Chris Bongers Co-authored-by: Claude Sonnet 4.6 --- .../components/cards/socialTwitter/SocialTwitterGrid.tsx | 8 ++------ .../components/cards/socialTwitter/SocialTwitterList.tsx | 2 +- .../src/components/post/SocialTwitterPostContent.tsx | 4 +--- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/packages/shared/src/components/cards/socialTwitter/SocialTwitterGrid.tsx b/packages/shared/src/components/cards/socialTwitter/SocialTwitterGrid.tsx index 6d20959c16..fd051891d5 100644 --- a/packages/shared/src/components/cards/socialTwitter/SocialTwitterGrid.tsx +++ b/packages/shared/src/components/cards/socialTwitter/SocialTwitterGrid.tsx @@ -39,15 +39,12 @@ import { EmbeddedTweetPreview } from './EmbeddedTweetPreview'; const HeaderActions = getGroupedHoverContainer('span'); const normalizeThreadBody = ({ title, - content, contentHtml, }: { title?: string; - content?: string; contentHtml?: string; }): string | undefined => { - const rawBody = - content || (contentHtml ? sanitizeMessage(contentHtml, []) : null); + const rawBody = contentHtml ? sanitizeMessage(contentHtml, []) : null; if (!rawBody) { return undefined; } @@ -96,7 +93,7 @@ export const SocialTwitterGrid = forwardRef(function SocialTwitterGrid( const showQuoteDetail = isQuoteLike; const showMediaDetail = !isQuoteLike && !shouldHideMedia && !!post.image; const shouldHideRepostHeadlineAndTags = - post.subType === 'repost' && !post.content?.trim(); + post.subType === 'repost' && !post.contentHtml?.trim(); const quoteDetailsContainerClass = shouldHideRepostHeadlineAndTags ? 'mx-1 mb-1 mt-2 min-h-[13.5rem] flex-1' : 'mx-1 mb-1 mt-2 h-40'; @@ -109,7 +106,6 @@ export const SocialTwitterGrid = forwardRef(function SocialTwitterGrid( post.subType === 'thread' ? normalizeThreadBody({ title: rawTitle, - content: post.content, contentHtml: post.contentHtml, }) : undefined; diff --git a/packages/shared/src/components/cards/socialTwitter/SocialTwitterList.tsx b/packages/shared/src/components/cards/socialTwitter/SocialTwitterList.tsx index c5b41c2faa..9a84ccab18 100644 --- a/packages/shared/src/components/cards/socialTwitter/SocialTwitterList.tsx +++ b/packages/shared/src/components/cards/socialTwitter/SocialTwitterList.tsx @@ -64,7 +64,7 @@ export const SocialTwitterList = forwardRef(function SocialTwitterList( const showReferenceTweet = post.sharedPost?.type === PostType.SocialTwitter; const showMediaCover = !!image && !showReferenceTweet; const shouldHideRepostHeadlineAndTags = - post.subType === 'repost' && !post.content?.trim(); + post.subType === 'repost' && !post.contentHtml?.trim(); const quoteDetailsTextClampClass = shouldHideRepostHeadlineAndTags ? 'line-clamp-8' : 'line-clamp-4'; diff --git a/packages/shared/src/components/post/SocialTwitterPostContent.tsx b/packages/shared/src/components/post/SocialTwitterPostContent.tsx index c0cdc2e3b7..34cb5aca91 100644 --- a/packages/shared/src/components/post/SocialTwitterPostContent.tsx +++ b/packages/shared/src/components/post/SocialTwitterPostContent.tsx @@ -94,9 +94,7 @@ function SocialTwitterPostContentRaw({ const isQuoteLike = isSocialTwitterShareLike(post); const isThread = post.subType === 'thread'; const shouldHideRepostHeadlineAndTags = - post.subType === 'repost' && - !post.contentHtml?.trim() && - !post.content?.trim(); + post.subType === 'repost' && !post.contentHtml?.trim(); const { repostedByName, metadataHandles, From 388d147b39141f6fead214b056932f3eb71cd223 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Thu, 5 Mar 2026 12:41:45 +0000 Subject: [PATCH 7/7] refactor(shared): scope content field to edit-only post query Remove content from POST_BY_ID_QUERY (view page) and introduce EDIT_POST_BY_ID_QUERY + useEditPostById hook for the edit flow, which genuinely needs raw markdown. Switch BriefPostContent existence check to contentHtml. Co-authored-by: Chris Bongers --- .../post/brief/BriefPostContent.tsx | 2 +- packages/shared/src/graphql/posts.ts | 49 +++++++++++++++++++ packages/shared/src/hooks/usePostById.ts | 32 +++++++++++- packages/webapp/pages/posts/[id]/edit.tsx | 6 +-- 4 files changed, 83 insertions(+), 6 deletions(-) diff --git a/packages/shared/src/components/post/brief/BriefPostContent.tsx b/packages/shared/src/components/post/brief/BriefPostContent.tsx index 0674f685e1..fff9192214 100644 --- a/packages/shared/src/components/post/brief/BriefPostContent.tsx +++ b/packages/shared/src/components/post/brief/BriefPostContent.tsx @@ -439,7 +439,7 @@ const BriefPostContentRaw = ({ )} - {post?.content && isPlus && ( + {post?.contentHtml && isPlus && (
=> { + const { tokenRefreshed } = useAuthContext(); + const key = [RequestKey.Post, id, 'edit']; + const { data, isPending } = useQuery({ + queryKey: key, + queryFn: () => + gqlClient.request(EDIT_POST_BY_ID_QUERY, { id }), + staleTime: StaleTime.Default, + enabled: !!id && tokenRefreshed && enabled, + }); + + return useMemo( + () => ({ + post: data?.post, + isLoading: !data?.post && isPending, + }), + [data?.post, isPending], + ); +}; diff --git a/packages/webapp/pages/posts/[id]/edit.tsx b/packages/webapp/pages/posts/[id]/edit.tsx index 7b7a4493cc..1600f9b583 100644 --- a/packages/webapp/pages/posts/[id]/edit.tsx +++ b/packages/webapp/pages/posts/[id]/edit.tsx @@ -9,10 +9,10 @@ import { import type { EditPostProps } from '@dailydotdev/shared/src/graphql/posts'; import { PostType } from '@dailydotdev/shared/src/graphql/posts'; import { - usePostById, useActions, usePostToSquad, } from '@dailydotdev/shared/src/hooks'; +import { useEditPostById } from '@dailydotdev/shared/src/hooks/usePostById'; import { useAuthContext } from '@dailydotdev/shared/src/contexts/AuthContext'; import { useToastNotification } from '@dailydotdev/shared/src/hooks/useToastNotification'; import type { ApiErrorResult } from '@dailydotdev/shared/src/graphql/common'; @@ -47,9 +47,9 @@ function EditPost(): ReactElement { const { query, isReady, push } = useRouter(); const idQuery = query.id as string; const isModeration = query.moderation === 'true'; - const { post, isLoading } = usePostById({ + const { post, isLoading } = useEditPostById({ id: idQuery, - options: { enabled: !isModeration }, + enabled: !isModeration, }); const isUserSource = isSourceUserSource(post?.source); const { moderated, isLoading: isModerationLoading } =