diff --git a/packages/shared/src/components/MainLayout.tsx b/packages/shared/src/components/MainLayout.tsx index b9eed590b5..cc4c74e4eb 100644 --- a/packages/shared/src/components/MainLayout.tsx +++ b/packages/shared/src/components/MainLayout.tsx @@ -22,6 +22,7 @@ import { SharedFeedPage } from './utilities'; import { isTesting, onboardingUrl } from '../lib/constants'; import { isOnboardingFeedPathname } from '../lib/onboarding'; import { useBanner } from '../hooks/useBanner'; +import { useSidebarCompact } from '../hooks/useSidebarCompact'; import { useGrowthBookContext } from './GrowthBookProvider'; import { ActiveFeedNameContextProvider, @@ -109,9 +110,9 @@ function MainLayoutComponent({ const { growthbook } = useGrowthBookContext(); const { sidebarRendered } = useSidebarRendered(); const { isAvailable: isBannerAvailable } = useBanner(); - const { sidebarExpanded, autoDismissNotifications, loadedSettings, flags } = + const { sidebarExpanded, autoDismissNotifications, loadedSettings } = useContext(SettingsContext); - const isSidebarCompact = !!flags?.sidebarCompact; + const { value: isSidebarCompact } = useSidebarCompact(); const v2CollapsedPadding = isSidebarCompact ? 'tablet:pl-16 laptop:pl-16' : 'tablet:pl-16 laptop:pl-20'; diff --git a/packages/shared/src/components/sidebar/SidebarDesktopV2.tsx b/packages/shared/src/components/sidebar/SidebarDesktopV2.tsx index eaa5518046..10f01b3b0e 100644 --- a/packages/shared/src/components/sidebar/SidebarDesktopV2.tsx +++ b/packages/shared/src/components/sidebar/SidebarDesktopV2.tsx @@ -90,7 +90,7 @@ import { TerminalIcon, TrendingIcon, } from '../icons'; -import { useSettingsBooleanFlag } from '../../hooks/useSettingsBooleanFlag'; +import { useSidebarCompact } from '../../hooks/useSidebarCompact'; import { IconSize } from '../Icon'; import { Tooltip } from '../tooltip/Tooltip'; import { RailHoverPanel } from './RailHoverPanel'; @@ -729,7 +729,7 @@ export const SidebarDesktopV2 = ({ if (isExtension) { myFeedPath = `${webappUrl}my-feed`; } - const { value: isCompact } = useSettingsBooleanFlag('sidebarCompact'); + const { value: isCompact } = useSidebarCompact(); // Compact mode reverts to the original icon-only widths (pre-label rail). // Both width sets are known-good; MainLayout mirrors the collapsed/expanded // padding so the content never overlaps the rail. @@ -2345,60 +2345,72 @@ export const SidebarDesktopV2 = ({ suppressTransition, )} > - {/* pl-5 lines the panel title up with the list rows' icon glyphs - (icons sit ~8px into their w-9 column) and the section titles. */} -
- {isSettingsSelected ? ( - - ) : ( -
- - {utilityPanelTitle} - -
+ {/* Pinned to the open width so the content does not reflow while + the panel animates its own width. */} +
- - {isLoggedIn && !isUtilityPanelSelected && additionalButtons && ( -
- {additionalButtons} + > + {/* pl-5 lines the panel title up with the list rows' icon glyphs + (icons sit ~8px into their w-9 column) and the section titles. */} +
+ {isSettingsSelected ? ( + + ) : ( +
+ + {utilityPanelTitle} + +
+ )}
- )} - + {additionalButtons} +
)} - > - - - - {!isUtilityPanelSelected && } - {showFeedbackWidget && !isUtilityPanelSelected && ( -
- -
- )} + + + + {!isUtilityPanelSelected && } + {showFeedbackWidget && !isUtilityPanelSelected && ( +
+ +
+ )} +
diff --git a/packages/shared/src/hooks/useSettingsBooleanFlag.ts b/packages/shared/src/hooks/useSettingsBooleanFlag.ts index e3ab0b60be..547505e575 100644 --- a/packages/shared/src/hooks/useSettingsBooleanFlag.ts +++ b/packages/shared/src/hooks/useSettingsBooleanFlag.ts @@ -17,15 +17,18 @@ interface UseSettingsBooleanFlag { /** * Reads a boolean flag from `SettingsFlags` and exposes setters that persist - * through the shared `useSettingsContext`. Coerces undefined to `false` so - * callers can use the value directly. Only accepts keys whose value type is + * through the shared `useSettingsContext`. An unset flag falls back to + * `defaultValue`, so a flag that ships on by default stays distinguishable from + * one the user explicitly turned off. Only accepts keys whose value type is * `boolean | undefined`. */ export const useSettingsBooleanFlag = ( key: K, + defaultValue = false, ): UseSettingsBooleanFlag => { const { flags, updateFlag } = useSettingsContext(); - const value = Boolean(flags?.[key]); + const stored = flags?.[key]; + const value = stored === undefined ? defaultValue : Boolean(stored); return { value, set: (next) => updateFlag(key, next as SettingsFlags[K]), diff --git a/packages/shared/src/hooks/useSidebarCompact.ts b/packages/shared/src/hooks/useSidebarCompact.ts new file mode 100644 index 0000000000..c07795a3aa --- /dev/null +++ b/packages/shared/src/hooks/useSidebarCompact.ts @@ -0,0 +1,12 @@ +import { useSettingsBooleanFlag } from './useSettingsBooleanFlag'; + +// The v2 rail ships without the labels under its icons, so an account that +// never touched the density setting gets compact. Only an explicit `false` +// (the user picking Comfortable) brings the labels back. +// +// Read it through here rather than the raw flag: the rail sets its own width +// from this and MainLayout pads the content to match, so the two disagreeing +// would leave the content overlapping the rail or short of it. +export const useSidebarCompact = (): ReturnType< + typeof useSettingsBooleanFlag +> => useSettingsBooleanFlag('sidebarCompact', true); diff --git a/packages/webapp/pages/settings/appearance.tsx b/packages/webapp/pages/settings/appearance.tsx index c2a37e05ac..9e3ab546ac 100644 --- a/packages/webapp/pages/settings/appearance.tsx +++ b/packages/webapp/pages/settings/appearance.tsx @@ -7,6 +7,7 @@ import { ThemeSection } from '@dailydotdev/shared/src/components/ProfileMenu/sec import { useSettingsContext } from '@dailydotdev/shared/src/contexts/SettingsContext'; import { useViewSize, ViewSize } from '@dailydotdev/shared/src/hooks'; import { useSettingsBooleanFlag } from '@dailydotdev/shared/src/hooks/useSettingsBooleanFlag'; +import { useSidebarCompact } from '@dailydotdev/shared/src/hooks/useSidebarCompact'; import { useLayoutVariant } from '@dailydotdev/shared/src/hooks/layout/useLayoutVariant'; import { useReaderModalEligibility } from '@dailydotdev/shared/src/components/post/reader/hooks/useReaderModalEligibility'; import { useLegacyPostLayoutOptOut } from '@dailydotdev/shared/src/components/post/reader/hooks/useLegacyPostLayoutOptOut'; @@ -71,7 +72,7 @@ const AccountManageSubscriptionPage = (): ReactElement => { flags?.readerInstallPromptAcknowledged ?? false; const { isV2: isLayoutV2 } = useLayoutVariant(); const { value: isSidebarCompact, toggle: toggleSidebarCompact } = - useSettingsBooleanFlag('sidebarCompact'); + useSidebarCompact(); const onToggleReadInside = () => { if (isReadInsideEnabled) { optOut();