diff --git a/packages/shared/src/components/cards/common/ActionButtons.tsx b/packages/shared/src/components/cards/common/ActionButtons.tsx index 5b21184be11..23b33a76c0e 100644 --- a/packages/shared/src/components/cards/common/ActionButtons.tsx +++ b/packages/shared/src/components/cards/common/ActionButtons.tsx @@ -6,11 +6,14 @@ import InteractionCounter from '../../InteractionCounter'; import { QuaternaryButton } from '../../buttons/QuaternaryButton'; import { DiscussIcon as CommentIcon, + DiscussIconV2 as CommentIconV2, LinkIcon, DownvoteIcon, } from '../../icons'; import { ButtonColor, ButtonSize, ButtonVariant } from '../../buttons/Button'; import { useFeedPreviewMode } from '../../../hooks'; +import { useFeature } from '../../GrowthBookProvider'; +import { featureCommentFirstAction } from '../../../lib/featureManagement'; import { UpvoteButtonIcon } from './UpvoteButtonIcon'; import { BookmarkButton } from '../../buttons'; import { IconSize } from '../../Icon'; @@ -79,6 +82,11 @@ const ActionButtonsV1 = ({ const config = variantConfig[variant]; const isFeedPreview = useFeedPreviewMode(); const { getUpvoteAnimation } = useBrandSponsorship(); + // The experiment only reorders the comment action and swaps its icon; + // copy and click behavior stay identical to the control so the only + // variable is order. + const isCommentFirst = useFeature(featureCommentFirstAction); + const CommentIconComponent = isCommentFirst ? CommentIconV2 : CommentIcon; const { isUpvoteActive, @@ -120,6 +128,17 @@ const ActionButtonsV1 = ({ const commentCount = post.numComments ?? 0; const upvoteCount = post.numUpvotes ?? 0; + // Keep every count in the action row on the same typography as the upvote + // counter so the numbers match in size/style across variants. `!leading-5` + // forces the footnote line to fill the counter's h-5 box (it must beat + // typo-footnote's own line-height, which is emitted later in the same + // utilities layer) so the digits center against the icons instead of + // riding high. + const counterClassName = classNames( + 'tabular-nums !leading-5', + variant === 'grid' && 'typo-footnote', + ); + const counterLabelClassName = variant === 'grid' ? '!pl-[1px]' : '!pl-0'; const commentButton = config.useCommentLink ? ( } + icon={ + + } onClick={() => onCommentClick?.(post)} > {commentCount > 0 && ( )} @@ -150,9 +174,14 @@ const ActionButtonsV1 = ({ ) : ( } + icon={ + + } pressed={post.commented} onClick={() => onCommentClick?.(post)} size={config.buttonSize} @@ -160,10 +189,7 @@ const ActionButtonsV1 = ({ > {commentCount > 0 && ( )} @@ -171,70 +197,82 @@ const ActionButtonsV1 = ({ ); - const buttons = ( -
-
+ const voteButtons = ( + <> + + + } + > + {upvoteCount > 0 && ( + + )} + + + {showDownvoteAction && ( } - > - {upvoteCount > 0 && ( - - )} - + pressed={isDownvoteActive} + onClick={onToggleDownvote} + variant={ButtonVariant.Tertiary} + size={config.buttonSize} + /> - {showDownvoteAction && ( - - - } - pressed={isDownvoteActive} - onClick={onToggleDownvote} - variant={ButtonVariant.Tertiary} - size={config.buttonSize} - /> - + )} + + ); + + const buttons = ( +
+
+ {isCommentFirst ? ( + <> + {commentButton} + {voteButtons} + + ) : ( + <> + {voteButtons} + {commentButton} + )} - {commentButton} {showAwardAction && ( )} diff --git a/packages/shared/src/components/icons/Discuss/filled-v2.svg b/packages/shared/src/components/icons/Discuss/filled-v2.svg new file mode 100644 index 00000000000..b679e8e6b7d --- /dev/null +++ b/packages/shared/src/components/icons/Discuss/filled-v2.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/shared/src/components/icons/Discuss/index.tsx b/packages/shared/src/components/icons/Discuss/index.tsx index 4d68ef70e83..578a884b066 100644 --- a/packages/shared/src/components/icons/Discuss/index.tsx +++ b/packages/shared/src/components/icons/Discuss/index.tsx @@ -4,7 +4,13 @@ import type { IconProps } from '../../Icon'; import Icon from '../../Icon'; import OutlinedIcon from './outlined.svg'; import FilledIcon from './filled.svg'; +import OutlinedIconV2 from './outlined-v2.svg'; +import FilledIconV2 from './filled-v2.svg'; export const DiscussIcon = (props: IconProps): ReactElement => ( ); + +export const DiscussIconV2 = (props: IconProps): ReactElement => ( + +); diff --git a/packages/shared/src/components/icons/Discuss/outlined-v2.svg b/packages/shared/src/components/icons/Discuss/outlined-v2.svg new file mode 100644 index 00000000000..748a9ebc3fa --- /dev/null +++ b/packages/shared/src/components/icons/Discuss/outlined-v2.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/shared/src/components/post/MobilePostFloatingBar.tsx b/packages/shared/src/components/post/MobilePostFloatingBar.tsx index 4f595daff2a..ce098b1c5a4 100644 --- a/packages/shared/src/components/post/MobilePostFloatingBar.tsx +++ b/packages/shared/src/components/post/MobilePostFloatingBar.tsx @@ -3,10 +3,13 @@ import React, { useCallback } from 'react'; import classNames from 'classnames'; import { DiscussIcon as CommentIcon, + DiscussIconV2 as CommentIconV2, DownvoteIcon, LinkIcon, UpvoteIcon, } from '../icons'; +import { useFeature } from '../GrowthBookProvider'; +import { featureCommentFirstAction } from '../../lib/featureManagement'; import type { Post } from '../../graphql/posts'; import { UserVote } from '../../graphql/posts'; import { QuaternaryButton } from '../buttons/QuaternaryButton'; @@ -63,6 +66,8 @@ function MobilePostFloatingBarV1({ const { onClose, onShowPanel } = useBlockPostPanel(post); const { toggleUpvote, toggleDownvote } = useVotePost(); const { toggleBookmark } = useBookmarkPost(); + const isCommentFirst = useFeature(featureCommentFirstAction); + const CommentIconComponent = isCommentFirst ? CommentIconV2 : CommentIcon; // Match the desktop `PostActions` copy flow: fetch the short URL imperatively // on click so anonymous users still get a usable (long) link instead of a no-op. @@ -110,45 +115,67 @@ function MobilePostFloatingBarV1({ ); }, [copyLink, getShortUrl, logEvent, origin, post]); + const upvoteButton = ( + } + variant={ButtonVariant.Tertiary} + color={ButtonColor.Avocado} + size={ButtonSize.Medium} + > + {upvoteCount > 0 && ( + + )} + + ); + + const downvoteButton = ( + } + variant={ButtonVariant.Tertiary} + color={ButtonColor.Ketchup} + size={ButtonSize.Medium} + /> + ); + + const commentButton = ( + onCommentClick(LogOrigin.PostCommentButton)} + icon={} + size={ButtonSize.Medium} + className="btn-tertiary-blueCheese" + > + {commentCount > 0 && ( + + )} + + ); + return (
- } - variant={ButtonVariant.Tertiary} - color={ButtonColor.Avocado} - size={ButtonSize.Medium} - > - {upvoteCount > 0 && ( - - )} - - } - variant={ButtonVariant.Tertiary} - color={ButtonColor.Ketchup} - size={ButtonSize.Medium} - /> - onCommentClick(LogOrigin.PostCommentButton)} - icon={} - size={ButtonSize.Medium} - className="btn-tertiary-blueCheese" - > - {commentCount > 0 && ( - - )} - + {isCommentFirst ? ( + <> + {commentButton} + {upvoteButton} + {downvoteButton} + + ) : ( + <> + {upvoteButton} + {downvoteButton} + {commentButton} + + )} } + aria-label="Comment" + className="btn-tertiary-blueCheese" + > + Comment + + ); + return (
@@ -250,16 +269,7 @@ function PostActionsV1({ color={ButtonColor.Ketchup} /> - } - aria-label="Comment" - className="btn-tertiary-blueCheese" - > - Comment - + {commentButton} {canAward && ( + + + ); + return (
+ {isCommentFirst && commentButton} - + {!isCommentFirst && commentButton} +