From 197f6b24d127c2485b3689fa2af0d8b5086ede30 Mon Sep 17 00:00:00 2001 From: Tsahi Matsliah Date: Thu, 1 Oct 2026 10:00:53 +0300 Subject: [PATCH 1/2] fix(mobile): the four step-0 shell fixes Step 0 of the mobile shell work (Storybook, Mobile UX / 10. The build): the fixes that need no design decision, shipped together. TabContainer's swipe decided from the total movement at touch end with a 40px threshold, so a thumb scrolling at a slight angle switched the Highlights channel. The swipe now locks its axis on the first 10px, commits past 56px inside a 27 degree cone or on a fast flick past 32px, and the surface takes touch-action: pan-y. The numbers live in shell/constants.ts. FooterNavBarLayout waited for the window load event before rendering the bar; it renders once hydrated. The footer tabs fire a click with the footer as target and the tab in extra, and Activity fires click notification icon with NotificationTarget.Footer, which existed and was never used. HighlightCardOptions and SquadOptionsButton were hidden until hover on every device, so their actions did not exist on phones; they use the laptop-and-mouse-only hover class the post cards use. The Explore period drawer gets a Close, the notification menu icon stops being rotated, Unfollow uses the remove-user icon, "Manade Ad" is "Manage ad" and the settings menu labels match their page titles. shell.css carries the shell's two curves and four durations and the press class (scale 0.96, 150ms) applied to the footer tabs, the plus button, the header controls, the feed chips and the post bar; the post titles balance. Two pre-existing strict type errors in the touched files are fixed so the strict guard passes. Co-Authored-By: Claude Fable 5.1 --- .../ProfileMenu/sections/AccountSection.tsx | 2 +- .../cards/common/SquadOptionsButton.tsx | 13 ++- .../cards/highlight/HighlightCardOptions.tsx | 6 +- .../comments/CommentActionButtons.tsx | 3 +- .../comments/CommentActionButtons.v2.tsx | 3 +- .../components/feeds/MobileFeedActions.tsx | 3 +- .../components/feeds/UnifiedMobileFeedNav.tsx | 2 +- .../components/header/FeedExploreHeader.tsx | 1 + .../notifications/NotificationItem.tsx | 2 +- .../components/post/MobilePostFloatingBar.tsx | 7 +- .../src/components/post/PostContent.tsx | 2 +- .../post/collection/CollectionPostContent.tsx | 2 +- .../components/post/focus/PostFocusCard.tsx | 2 +- .../profile/ProfileSettingsMenu.tsx | 10 +- .../shared/src/components/shell/constants.ts | 21 ++++ .../components/sidebar/SidebarDesktopV2.tsx | 2 +- .../sidebar/sections/SettingsPanelSection.tsx | 10 +- .../src/components/tabs/TabContainer.spec.tsx | 108 ++++++++++++++++++ .../src/components/tabs/TabContainer.tsx | 46 +++++++- packages/shared/src/styles/globals.css | 1 + packages/shared/src/styles/shell.css | 36 ++++++ .../webapp/__tests__/MobileFooterNavbar.tsx | 72 ++++++++++++ .../components/footer/FooterNavBarTabs.tsx | 10 +- .../components/footer/FooterPlusButton.tsx | 2 +- .../components/footer/MobileFooterNavbar.tsx | 27 ++++- packages/webapp/components/footer/common.ts | 4 +- .../components/layouts/FooterNavBarLayout.tsx | 11 +- 27 files changed, 361 insertions(+), 47 deletions(-) create mode 100644 packages/shared/src/components/shell/constants.ts create mode 100644 packages/shared/src/styles/shell.css create mode 100644 packages/webapp/__tests__/MobileFooterNavbar.tsx diff --git a/packages/shared/src/components/ProfileMenu/sections/AccountSection.tsx b/packages/shared/src/components/ProfileMenu/sections/AccountSection.tsx index b20f9f8c61..9fb96afdda 100644 --- a/packages/shared/src/components/ProfileMenu/sections/AccountSection.tsx +++ b/packages/shared/src/components/ProfileMenu/sections/AccountSection.tsx @@ -25,7 +25,7 @@ export const AccountSection = (): ReactElement => { icon: SettingsIcon, }, { - title: 'Subscriptions', + title: 'Payment & Subscription', href: `${settingsUrl}/subscription`, icon: CreditCardIcon, }, diff --git a/packages/shared/src/components/cards/common/SquadOptionsButton.tsx b/packages/shared/src/components/cards/common/SquadOptionsButton.tsx index 23e2cc32b2..313d64b2be 100644 --- a/packages/shared/src/components/cards/common/SquadOptionsButton.tsx +++ b/packages/shared/src/components/cards/common/SquadOptionsButton.tsx @@ -27,6 +27,7 @@ import useFeedSettings from '../../../hooks/useFeedSettings'; import { useShareOrCopyLink } from '../../../hooks/useShareOrCopyLink'; import { LogEvent } from '../../../lib/log'; import { plusUrl } from '../../../lib/constants'; +import { visibleOnGroupHover } from './common'; interface SquadOptionsButtonProps { squad: Squad; @@ -39,7 +40,7 @@ export function SquadOptionsButton({ }: SquadOptionsButtonProps): ReactElement { const { openModal } = useLazyModal(); const { user } = useAuthContext(); - const { data: campaign } = useCampaignById(squad.flags.campaignId); + const { data: campaign } = useCampaignById(squad.flags?.campaignId ?? ''); const isBooster = campaign && user?.id === campaign.user.id; const [, onShareOrCopy] = useShareOrCopyLink({ link: squad.permalink, @@ -57,7 +58,11 @@ export function SquadOptionsButton({ const options = useMemo(() => { const blockAction = isSourceBlocked ? unblockSource : blockSource; const list: MenuItemProps[] = [ - { label: 'Share via', icon: , action: onShareOrCopy }, + { + label: 'Share via', + icon: , + action: () => onShareOrCopy(), + }, { label: 'Hide', icon: , @@ -81,7 +86,7 @@ export function SquadOptionsButton({ if (isBooster) { list.unshift({ - label: 'Manade Ad', + label: 'Manage ad', icon: , action: () => openModal({ @@ -110,7 +115,7 @@ export function SquadOptionsButton({ variant={ButtonVariant.Tertiary} icon={} size={ButtonSize.Small} - className={classNames('invisible z-1 group-hover:visible', className)} + className={classNames(visibleOnGroupHover, 'z-1', className)} /> diff --git a/packages/shared/src/components/cards/highlight/HighlightCardOptions.tsx b/packages/shared/src/components/cards/highlight/HighlightCardOptions.tsx index 0f8e49352c..d930163b7c 100644 --- a/packages/shared/src/components/cards/highlight/HighlightCardOptions.tsx +++ b/packages/shared/src/components/cards/highlight/HighlightCardOptions.tsx @@ -5,6 +5,7 @@ import { useQueryClient } from '@tanstack/react-query'; import { Button, ButtonSize, ButtonVariant } from '../../buttons/Button'; import { EyeCancelIcon, MenuIcon as KebabIcon, PinIcon } from '../../icons'; import { MenuIcon } from '../../MenuIcon'; +import { visibleOnGroupHover } from '../common/common'; import { DropdownMenu, DropdownMenuContent, @@ -70,10 +71,7 @@ const HighlightCardOptionsContent = ({ variant={ButtonVariant.Tertiary} size={ButtonSize.Small} icon={} - className={classNames( - 'invisible z-1 my-auto group-hover:visible', - className, - )} + className={classNames(visibleOnGroupHover, 'z-1 my-auto', className)} /> diff --git a/packages/shared/src/components/comments/CommentActionButtons.tsx b/packages/shared/src/components/comments/CommentActionButtons.tsx index 39080de456..5fd6d3f317 100644 --- a/packages/shared/src/components/comments/CommentActionButtons.tsx +++ b/packages/shared/src/components/comments/CommentActionButtons.tsx @@ -12,6 +12,7 @@ import { FlagIcon, DownvoteIcon, AddUserIcon, + RemoveUserIcon, BlockIcon, GiftIcon, MenuIcon, @@ -258,7 +259,7 @@ function CommentActionButtonsV1({ const isFollowingUser = isFollowingContent(author?.contentPreference); commentOptions.push({ - icon: , + icon: isFollowingUser ? : , label: `${isFollowingUser ? 'Unfollow' : 'Follow'} ${authorName}`, action: () => { const opts = { diff --git a/packages/shared/src/components/comments/CommentActionButtons.v2.tsx b/packages/shared/src/components/comments/CommentActionButtons.v2.tsx index 53cd6e9f48..cbc4ecce6e 100644 --- a/packages/shared/src/components/comments/CommentActionButtons.v2.tsx +++ b/packages/shared/src/components/comments/CommentActionButtons.v2.tsx @@ -12,6 +12,7 @@ import { FlagIcon, DownvoteIcon, AddUserIcon, + RemoveUserIcon, BlockIcon, GiftIcon, MenuIcon, @@ -258,7 +259,7 @@ export default function CommentActionButtons({ const isFollowingUser = isFollowingContent(author?.contentPreference); commentOptions.push({ - icon: , + icon: isFollowingUser ? : , label: `${isFollowingUser ? 'Unfollow' : 'Follow'} ${authorName}`, action: () => { const opts = { diff --git a/packages/shared/src/components/feeds/MobileFeedActions.tsx b/packages/shared/src/components/feeds/MobileFeedActions.tsx index ec59b59d7e..02b8ea5e01 100644 --- a/packages/shared/src/components/feeds/MobileFeedActions.tsx +++ b/packages/shared/src/components/feeds/MobileFeedActions.tsx @@ -55,6 +55,7 @@ export function MobileFeedActions(): ReactElement {