diff --git a/packages/shared/src/components/feeds/FeedSettings/sections/FeedSettingsGeneralSection.tsx b/packages/shared/src/components/feeds/FeedSettings/sections/FeedSettingsGeneralSection.tsx index 5c65c3285c7..b5b765c680c 100644 --- a/packages/shared/src/components/feeds/FeedSettings/sections/FeedSettingsGeneralSection.tsx +++ b/packages/shared/src/components/feeds/FeedSettings/sections/FeedSettingsGeneralSection.tsx @@ -19,8 +19,14 @@ import { useAuthContext } from '../../../../contexts/AuthContext'; import { ColorName } from '../../../../styles/colors'; import useProfileForm from '../../../../hooks/useProfileForm'; import { FeedType } from '../../../../graphql/feed'; -import { usePlusSubscription } from '../../../../hooks'; +import { usePlusSubscription, useToastNotification } from '../../../../hooks'; import { Tooltip } from '../../../tooltip/Tooltip'; +import { Switch } from '../../../fields/Switch'; +import { useSettingsContext } from '../../../../contexts/SettingsContext'; +import { SidebarSettingsFlags } from '../../../../graphql/settings'; +import { useLogContext } from '../../../../contexts/LogContext'; +import { LogEvent, Origin, TargetId } from '../../../../lib/log'; +import { labels } from '../../../../lib'; export const FeedSettingsGeneralSection = (): ReactElement => { const { setData, data, feed, onDelete, editFeedSettings } = useContext( @@ -31,6 +37,9 @@ export const FeedSettingsGeneralSection = (): ReactElement => { const isMainFeed = feed?.type === FeedType.Main; const isCustomFeed = feed?.type === FeedType.Custom; const { isPlus } = usePlusSubscription(); + const { flags, updateFlag } = useSettingsContext(); + const { displayToast } = useToastNotification(); + const { logEvent } = useLogContext(); const isDefaultFeed = isMainFeed ? user.defaultFeedId === null @@ -167,6 +176,48 @@ export const FeedSettingsGeneralSection = (): ReactElement => { )} + +
+
+ + Pin highlights to top + + + When highlights appear in your feed, show them in the first + position. Otherwise they're placed somewhere near the top. + +
+ { + const newState = !(flags?.highlightsFirstEnabled ?? false); + await updateFlag( + SidebarSettingsFlags.HighlightsFirstEnabled, + newState, + ); + + displayToast( + labels.feed.settings.globalPreferenceNotice.highlightsFirst, + ); + + logEvent({ + event_name: LogEvent.ToggleHighlightsFirst, + target_id: newState ? TargetId.On : TargetId.Off, + extra: JSON.stringify({ + origin: Origin.Settings, + }), + }); + }} + > + Pin highlights to first position + +
{isCustomFeed && ( <> diff --git a/packages/shared/src/graphql/settings.ts b/packages/shared/src/graphql/settings.ts index 9395806461b..6daa930135a 100644 --- a/packages/shared/src/graphql/settings.ts +++ b/packages/shared/src/graphql/settings.ts @@ -44,6 +44,7 @@ export type SettingsFlags = { sidebarResourcesExpanded: boolean; sidebarBookmarksExpanded: boolean; clickbaitShieldEnabled: boolean; + highlightsFirstEnabled?: boolean; timezoneMismatchIgnore?: string; prompt?: Record; defaultWriteTab?: WriteFormTab; @@ -65,6 +66,7 @@ export enum SidebarSettingsFlags { ResourcesExpanded = 'sidebarResourcesExpanded', BookmarksExpanded = 'sidebarBookmarksExpanded', ClickbaitShieldEnabled = 'clickbaitShieldEnabled', + HighlightsFirstEnabled = 'highlightsFirstEnabled', } export type RemoteSettings = { diff --git a/packages/shared/src/lib/labels.ts b/packages/shared/src/lib/labels.ts index 484395d26fe..f311aeb90db 100644 --- a/packages/shared/src/lib/labels.ts +++ b/packages/shared/src/lib/labels.ts @@ -88,6 +88,8 @@ export const labels = { globalPreferenceNotice: { clickbaitShield: 'Clickbait shield has been applied for all feeds', contentLanguage: 'New language preferences set for all feeds', + highlightsFirst: + 'Highlights pinning preference applied to all your feeds', }, }, }, diff --git a/packages/shared/src/lib/log.ts b/packages/shared/src/lib/log.ts index 8142d3eaaba..06227beaf07 100644 --- a/packages/shared/src/lib/log.ts +++ b/packages/shared/src/lib/log.ts @@ -339,6 +339,7 @@ export enum LogEvent { ToggleClickbaitShield = 'toggle clickbait shield', ClickbaitShieldTitle = 'clickbait shield title', // End Clickbait Shield + ToggleHighlightsFirst = 'toggle highlights first', InstallPWA = 'install pwa', // Start Share ShareProfile = 'share profile', diff --git a/scripts/typecheck-strict-changed.js b/scripts/typecheck-strict-changed.js index c5569d3f888..75389dae3d6 100644 --- a/scripts/typecheck-strict-changed.js +++ b/scripts/typecheck-strict-changed.js @@ -74,6 +74,12 @@ const strictSkipList = new Set([ // path; pre-existing strict violations (queryResult.data optionality, // NotificationItem reduce typing) are unrelated to the rename. 'packages/webapp/pages/notifications.tsx', + // Highlights-first toggle — touched only to add a new Switch subsection + // for the highlightsFirstEnabled flag. Pre-existing strict violations + // (auth user / feed optionality, Button prop mismatches, defaultFeedId + // null vs undefined) live on unrelated lines and should be addressed in + // a dedicated cleanup PR. + 'packages/shared/src/components/feeds/FeedSettings/sections/FeedSettingsGeneralSection.tsx', ]); const changedFiles = getChangedTypescriptFiles().filter(