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, 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 && (
{ - 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`. +- 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 new file mode 100644 index 0000000000..48e20ec96f --- /dev/null +++ b/packages/shared/src/graphql/feed.spec.ts @@ -0,0 +1,8 @@ +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/); +}); diff --git a/packages/shared/src/graphql/posts.ts b/packages/shared/src/graphql/posts.ts index 058b51d3c0..8d80c6d498 100644 --- a/packages/shared/src/graphql/posts.ts +++ b/packages/shared/src/graphql/posts.ts @@ -290,6 +290,55 @@ export const RELATED_POSTS_PER_PAGE_DEFAULT = 5; export const POST_BY_ID_QUERY = gql` query Post($id: ID!) { + post(id: $id) { + ...SharedPostInfo + trending + contentHtml + pinnedAt + bookmarkList { + id + } + sharedPost { + ...SharedPostInfo + } + source { + ...SourceBaseInfo + } + description + summary + toc { + text + id + } + updatedAt + numCollectionSources + collectionSources { + handle + image + } + } + relatedCollectionPosts: relatedPosts( + id: $id + relationType: COLLECTION + first: ${RELATED_POSTS_PER_PAGE_DEFAULT} + ) { + edges { + node { + ...RelatedPost + } + } + pageInfo { + endCursor + hasNextPage + } + } + } + ${SHARED_POST_INFO_FRAGMENT} + ${RELATED_POST_FRAGMENT} +`; + +export const EDIT_POST_BY_ID_QUERY = gql` + query EditPost($id: ID!) { post(id: $id) { ...SharedPostInfo trending diff --git a/packages/shared/src/hooks/usePostById.ts b/packages/shared/src/hooks/usePostById.ts index 99ea042f4d..0ccacf93fe 100644 --- a/packages/shared/src/hooks/usePostById.ts +++ b/packages/shared/src/hooks/usePostById.ts @@ -7,12 +7,12 @@ import type { import { useQuery } from '@tanstack/react-query'; import { useAuthContext } from '../contexts/AuthContext'; import type { Post, PostData, RelatedPost } from '../graphql/posts'; -import { POST_BY_ID_QUERY } from '../graphql/posts'; +import { EDIT_POST_BY_ID_QUERY, POST_BY_ID_QUERY } from '../graphql/posts'; import type { PostCommentsData } from '../graphql/comments'; -import type { RequestKey } from '../lib/query'; import { getAllCommentsQuery, getPostByIdKey, + RequestKey, StaleTime, updatePostCache, updatePostContentPreference, @@ -164,3 +164,31 @@ export const usePostById = ({ [post?.post, post?.relatedCollectionPosts, isError, isPending], ); }; + +interface UseEditPostByIdProps { + id: string; + enabled?: boolean; +} + +export const useEditPostById = ({ + id, + enabled = true, +}: UseEditPostByIdProps): Pick => { + 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 } =