diff --git a/packages/shared/src/components/MainFeedLayout.tsx b/packages/shared/src/components/MainFeedLayout.tsx
index fa18faeb06..ba27bcda86 100644
--- a/packages/shared/src/components/MainFeedLayout.tsx
+++ b/packages/shared/src/components/MainFeedLayout.tsx
@@ -75,7 +75,6 @@ import { checkIsExtension } from '../lib/func';
import { useReadingReminderHero } from '../hooks/notifications/useReadingReminderHero';
import { useTrackQuestClientEvent } from '../hooks/useTrackQuestClientEvent';
import { useReadingReminderVariation } from '../hooks/notifications/useReadingReminderVariation';
-import { useNoAiFeed } from '../hooks/useNoAiFeed';
const FeedExploreHeader = dynamic(
() =>
@@ -225,8 +224,6 @@ export default function MainFeedLayout({
hasUser: !!user,
});
const { isCustomDefaultFeed, defaultFeedId } = useCustomDefaultFeed();
- const shouldEvaluateNoAi =
- feedName === SharedFeedPage.MyFeed && !isCustomDefaultFeed;
const isLaptop = useViewSize(ViewSize.Laptop);
const feedVersion = useFeature(feature.feedVersion);
const { time, contentCurationFilter } = useSearchContextProvider();
@@ -306,14 +303,6 @@ export default function MainFeedLayout({
feature: featureFeedV2Highlights,
shouldEvaluate: shouldEvaluateFeedV2Highlights,
});
- const {
- isNoAi,
- isNoAiAvailable,
- isLoaded: isNoAiLoaded,
- toggleNoAi,
- } = useNoAiFeed({
- shouldEvaluate: shouldEvaluateNoAi,
- });
const { isSearchPageLaptop } = useSearchResultsLayout();
@@ -389,7 +378,6 @@ export default function MainFeedLayout({
highlightsLimit: FEED_V2_HIGHLIGHTS_LIMIT,
}
: {}),
- ...(shouldEvaluateNoAi && isNoAi ? { noAi: true } : {}),
version:
isDevelopment && !isProductionAPI
? 1
@@ -411,8 +399,6 @@ export default function MainFeedLayout({
tokenRefreshed,
feedVersion,
isFeedV2HighlightsEnabled,
- isNoAi,
- shouldEvaluateNoAi,
]);
const [selectedAlgo, setSelectedAlgo, loadedAlgo] = usePersistentContext(
@@ -465,10 +451,6 @@ export default function MainFeedLayout({
return null;
}
- if (shouldEvaluateNoAi && !isNoAiLoaded) {
- return null;
- }
-
if (feedNameProp === 'default' && isCustomDefaultFeed) {
if (!defaultFeedId) {
return null;
@@ -491,15 +473,6 @@ export default function MainFeedLayout({
),
};
@@ -579,15 +552,6 @@ export default function MainFeedLayout({
),
};
@@ -619,11 +583,6 @@ export default function MainFeedLayout({
isLaptop,
loadedAlgo,
tokenRefreshed,
- shouldEvaluateNoAi,
- isNoAiLoaded,
- isNoAiAvailable,
- isNoAi,
- toggleNoAi,
]);
useEffect(() => {
diff --git a/packages/shared/src/components/feeds/FeedSettings/sections/FeedSettingsContentPreferencesSection.tsx b/packages/shared/src/components/feeds/FeedSettings/sections/FeedSettingsContentPreferencesSection.tsx
index 3e4ff64c51..6e68f5fbac 100644
--- a/packages/shared/src/components/feeds/FeedSettings/sections/FeedSettingsContentPreferencesSection.tsx
+++ b/packages/shared/src/components/feeds/FeedSettings/sections/FeedSettingsContentPreferencesSection.tsx
@@ -3,9 +3,6 @@ import React, { useContext, useMemo } from 'react';
import { FeedSettingsEditContext } from '../FeedSettingsEditContext';
import useFeedSettings from '../../../../hooks/useFeedSettings';
import { useAdvancedSettings } from '../../../../hooks/feed/useAdvancedSettings';
-import { useConditionalFeature, useToastNotification } from '../../../../hooks';
-import { useLogContext } from '../../../../contexts/LogContext';
-import { useSettingsContext } from '../../../../contexts/SettingsContext';
import {
getAdvancedContentTypes,
getContentCurationList,
@@ -17,12 +14,7 @@ import {
TypographyType,
} from '../../../typography/Typography';
import { FilterCheckbox } from '../../../fields/FilterCheckbox';
-import { Switch } from '../../../fields/Switch';
import { FeedType } from '../../../../graphql/feed';
-import { featureNoAiFeed } from '../../../../lib/featureManagement';
-import { SidebarSettingsFlags } from '../../../../graphql/settings';
-import { labels } from '../../../../lib/labels';
-import { LogEvent, Origin, TargetId } from '../../../../lib/log';
export const TOGGLEABLE_TYPES = ['Videos', 'Polls', 'Social'];
const CUSTOM_FEEDS_ONLY = ['Article'];
@@ -30,9 +22,6 @@ const ADVANCED_SETTINGS_KEY = 'advancedSettings';
export const FeedSettingsContentPreferencesSection = (): ReactElement => {
const { feed, editFeedSettings } = useContext(FeedSettingsEditContext);
- const { flags, updateFlag } = useSettingsContext();
- const { displayToast } = useToastNotification();
- const { logEvent } = useLogContext();
const { advancedSettings } = useFeedSettings({ feedId: feed?.id });
const {
selectedSettings,
@@ -40,10 +29,6 @@ export const FeedSettingsContentPreferencesSection = (): ReactElement => {
checkSourceBlocked,
onToggleSource,
} = useAdvancedSettings({ feedId: feed?.id });
- const { value: isNoAiFeatureEnabled } = useConditionalFeature({
- feature: featureNoAiFeed,
- shouldEvaluate: feed?.type === FeedType.Main,
- });
const toggleableTypes = useMemo(
() =>
getAdvancedContentTypes(
@@ -113,47 +98,6 @@ export const FeedSettingsContentPreferencesSection = (): ReactElement => {
})}
- {feed?.type === FeedType.Main && isNoAiFeatureEnabled && (
-
-
-
- No AI mode
-
-
- Keep AI topics filtered out across My Feed. You can hide the
- homepage toggle once this is set.
-
-
-
{
- const newState = !(flags?.noAiFeedEnabled ?? false);
-
- editFeedSettings(() =>
- updateFlag(SidebarSettingsFlags.NoAiFeedEnabled, newState),
- );
- displayToast(
- newState ? labels.feed.noAi.hidden : labels.feed.noAi.visible,
- );
- logEvent({
- event_name: LogEvent.ToggleNoAiFeed,
- target_id: newState ? TargetId.On : TargetId.Off,
- extra: JSON.stringify({
- origin: Origin.Settings,
- }),
- });
- }}
- >
- Keep AI topics filtered out
-
-
- )}
diff --git a/packages/shared/src/components/layout/common.spec.tsx b/packages/shared/src/components/layout/common.spec.tsx
index 038e969c59..505797184c 100644
--- a/packages/shared/src/components/layout/common.spec.tsx
+++ b/packages/shared/src/components/layout/common.spec.tsx
@@ -1,5 +1,5 @@
import React from 'react';
-import { fireEvent, render, screen, waitFor } from '@testing-library/react';
+import { render, screen } from '@testing-library/react';
import SettingsContext from '../../contexts/SettingsContext';
import { useAuthContext } from '../../contexts/AuthContext';
import { useLogContext } from '../../contexts/LogContext';
@@ -10,7 +10,6 @@ import { useFeedName } from '../../hooks/feed/useFeedName';
import { useQueryState } from '../../hooks/utils/useQueryState';
import { checkIsExtension, getCurrentBrowserName } from '../../lib/func';
import { ActionType } from '../../graphql/actions';
-import { LogEvent, Origin, TargetId } from '../../lib/log';
import { SharedFeedPage } from '../utilities';
import { SearchControlHeader } from './common';
@@ -96,12 +95,10 @@ const mockGetCurrentBrowserName = getCurrentBrowserName as jest.Mock;
const createActionsState = ({
dismissedInstallExtension = false,
- dismissedNoAiToggle = false,
isActionsFetched = true,
completeAction = jest.fn(),
}: {
dismissedInstallExtension?: boolean;
- dismissedNoAiToggle?: boolean;
isActionsFetched?: boolean;
completeAction?: jest.Mock;
} = {}) => ({
@@ -110,31 +107,18 @@ const createActionsState = ({
return dismissedInstallExtension;
}
- if (type === ActionType.DismissNoAiFeedToggle) {
- return dismissedNoAiToggle;
- }
-
return false;
}),
completeAction,
isActionsFetched,
});
-const renderComponent = ({
- noAiState,
-}: {
- noAiState?: {
- isAvailable: boolean;
- isEnabled: boolean;
- onToggle: () => Promise;
- };
-} = {}) =>
+const renderComponent = () =>
render(
,
);
@@ -219,103 +203,4 @@ describe('SearchControlHeader', () => {
screen.getByRole('link', { name: 'Get it for Chrome' }),
).toBeInTheDocument();
});
-
- it('does not render the No AI switch when unavailable', () => {
- mockUseActions.mockReturnValue(
- createActionsState({ dismissedInstallExtension: true }),
- );
-
- renderComponent({
- noAiState: {
- isAvailable: false,
- isEnabled: false,
- onToggle: jest.fn().mockResolvedValue(undefined),
- },
- });
-
- expect(screen.queryByText('No AI mode')).not.toBeInTheDocument();
- expect(
- screen.queryByRole('checkbox', { name: 'Toggle No AI mode' }),
- ).not.toBeInTheDocument();
- });
-
- it('renders a No AI switch and logs when toggled', async () => {
- const onToggle = jest.fn().mockResolvedValue(undefined);
- const logEvent = jest.fn();
- mockUseLogContext.mockReturnValue({ logEvent });
- mockUseActions.mockReturnValue(
- createActionsState({ dismissedInstallExtension: true }),
- );
-
- renderComponent({
- noAiState: {
- isAvailable: true,
- isEnabled: false,
- onToggle,
- },
- });
-
- expect(screen.getByText('No AI mode')).toBeInTheDocument();
- const switchInput = screen.getByRole('checkbox', {
- name: 'Toggle No AI mode',
- });
- fireEvent.click(switchInput);
-
- expect(onToggle).toHaveBeenCalledTimes(1);
- await waitFor(() => {
- expect(logEvent).toHaveBeenCalledWith({
- event_name: LogEvent.ToggleNoAiFeed,
- target_id: TargetId.On,
- extra: JSON.stringify({
- origin: Origin.Feed,
- }),
- });
- });
- });
-
- it('does not render the No AI switch after dismissal', () => {
- mockUseActions.mockReturnValue(
- createActionsState({
- dismissedInstallExtension: true,
- dismissedNoAiToggle: true,
- }),
- );
-
- renderComponent({
- noAiState: {
- isAvailable: true,
- isEnabled: false,
- onToggle: jest.fn().mockResolvedValue(undefined),
- },
- });
-
- expect(screen.queryByText('No AI mode')).not.toBeInTheDocument();
- expect(
- screen.queryByRole('checkbox', { name: 'Toggle No AI mode' }),
- ).not.toBeInTheDocument();
- });
-
- it('dismisses the No AI header card', () => {
- const completeAction = jest.fn();
- mockUseActions.mockReturnValue(
- createActionsState({
- dismissedInstallExtension: true,
- completeAction,
- }),
- );
-
- renderComponent({
- noAiState: {
- isAvailable: true,
- isEnabled: false,
- onToggle: jest.fn().mockResolvedValue(undefined),
- },
- });
-
- fireEvent.click(screen.getByRole('button', { name: 'Dismiss No AI mode' }));
-
- expect(completeAction).toHaveBeenCalledWith(
- ActionType.DismissNoAiFeedToggle,
- );
- });
});
diff --git a/packages/shared/src/components/layout/common.tsx b/packages/shared/src/components/layout/common.tsx
index f9a0b5816c..41b5d0c98f 100644
--- a/packages/shared/src/components/layout/common.tsx
+++ b/packages/shared/src/components/layout/common.tsx
@@ -31,13 +31,9 @@ import { useReadingStreak } from '../../hooks/streaks';
import type { AllFeedPages } from '../../lib/query';
import { QueryStateKeys, useQueryState } from '../../hooks/utils/useQueryState';
import type { AllowedTags, TypographyProps } from '../typography/Typography';
-import {
- Typography,
- TypographyColor,
- TypographyType,
-} from '../typography/Typography';
+import { Typography } from '../typography/Typography';
import { ToggleClickbaitShield } from '../buttons/ToggleClickbaitShield';
-import { LogEvent, Origin, TargetId } from '../../lib/log';
+import { LogEvent, Origin } from '../../lib/log';
import { AchievementTrackerButton } from '../filters/AchievementTrackerButton';
import { ActionType } from '../../graphql/actions';
import {
@@ -47,23 +43,14 @@ import {
isNullOrUndefined,
} from '../../lib/func';
import { downloadBrowserExtension } from '../../lib/constants';
-import { cloudinaryNoAiFeedToggle } from '../../lib/image';
import { anchorDefaultRel } from '../../lib/strings';
import ConditionalWrapper from '../ConditionalWrapper';
-import { LazyImage } from '../LazyImage';
-import { Switch } from '../fields/Switch';
-import { Tooltip } from '../tooltip/Tooltip';
type State = [T, Dispatch>];
export interface SearchControlHeaderProps {
feedName: AllFeedPages;
algoState: State;
- noAiState?: {
- isAvailable: boolean;
- isEnabled: boolean;
- onToggle: () => void | Promise;
- };
}
export const LayoutHeader = classed(
@@ -85,18 +72,10 @@ export const periodTexts = periods.map((period) => period.text);
export const DEFAULT_ALGORITHM_KEY = 'feed:algorithm';
export const DEFAULT_ALGORITHM_INDEX = 0;
-const noAiToggleTooltip = (
- <>
- For when you are tired of AI launches, hot takes,
-
- and vibe-coding discourse taking over your feed.
- >
-);
export const SearchControlHeader = ({
feedName,
algoState: [selectedAlgo, setSelectedAlgo],
- noAiState,
}: SearchControlHeaderProps): ReactElement | null => {
const [selectedPeriod, setSelectedPeriod] = useQueryState({
key: [QueryStateKeys.FeedPeriod],
@@ -139,9 +118,6 @@ export const SearchControlHeader = ({
const hasDismissedInstallExtension = checkHasCompleted(
ActionType.DismissInstallExtension,
);
- const hasDismissedNoAiFeedToggle = checkHasCompleted(
- ActionType.DismissNoAiFeedToggle,
- );
const canInstallExtension =
!checkIsExtension() && isNullOrUndefined(user?.flags?.lastExtensionUse);
const shouldShowInstallExtensionPrompt =
@@ -212,66 +188,7 @@ export const SearchControlHeader = ({
),
hasFeedActions && ,
];
- const shouldShowNoAiControl =
- noAiState?.isAvailable && isActionsFetched && !hasDismissedNoAiFeedToggle;
- const noAiControl = shouldShowNoAiControl
- ? (() => {
- const handleNoAiToggle = async () => {
- const isEnabled = !noAiState.isEnabled;
- await noAiState.onToggle();
- logEvent({
- event_name: LogEvent.ToggleNoAiFeed,
- target_id: isEnabled ? TargetId.On : TargetId.Off,
- extra: JSON.stringify({
- origin: Origin.Feed,
- }),
- });
- };
-
- return (
-
-
-
-
-
-
- No AI mode
-
-
-
-
-
- }
- aria-label="Dismiss No AI mode"
- onClick={() => completeAction(ActionType.DismissNoAiFeedToggle)}
- />
-
- );
- })()
- : null;
- const secondaryActions = [noAiControl, isLaptop && installExtensionButton];
+ const secondaryActions = [isLaptop && installExtensionButton];
const actions = primaryActions.filter(Boolean);
const sideActions = secondaryActions.filter(Boolean);
@@ -287,7 +204,7 @@ export const SearchControlHeader = ({
{wrapperChildren}
- {isStreaksEnabled && (
+ {isStreaksEnabled && streak && (
diff --git a/packages/shared/src/graphql/actions.ts b/packages/shared/src/graphql/actions.ts
index bda2047005..36f37b0180 100644
--- a/packages/shared/src/graphql/actions.ts
+++ b/packages/shared/src/graphql/actions.ts
@@ -58,7 +58,6 @@ export enum ActionType {
AchievementSyncPrompt = 'achievement_sync_prompt',
DisableAchievementCompletion = 'disable_achievement_completion',
DismissInstallExtension = 'dismiss_install_extension',
- DismissNoAiFeedToggle = 'dismiss_no_ai_feed_toggle',
DismissBriefCard = 'dismiss_brief_card',
DigestUpsell = 'digest_upsell',
AskUpsellSearch = 'ask_upsell_search',
diff --git a/packages/shared/src/graphql/feed.ts b/packages/shared/src/graphql/feed.ts
index 24bd74001e..a323f8a4e9 100644
--- a/packages/shared/src/graphql/feed.ts
+++ b/packages/shared/src/graphql/feed.ts
@@ -341,7 +341,6 @@ export const FEED_QUERY = gql`
$after: String
$ranking: Ranking
$version: Int
- $noAi: Boolean
${SUPPORTED_TYPES}
) {
page: feed(
@@ -349,7 +348,6 @@ export const FEED_QUERY = gql`
after: $after
ranking: $ranking
version: $version
- noAi: $noAi
supportedTypes: $supportedTypes
) {
...FeedPostConnection
@@ -365,7 +363,6 @@ export const FEED_V2_QUERY = gql`
$after: String
$ranking: Ranking
$version: Int
- $noAi: Boolean
$highlightsLimit: Int
${SUPPORTED_TYPES}
) {
@@ -374,7 +371,6 @@ export const FEED_V2_QUERY = gql`
after: $after
ranking: $ranking
version: $version
- noAi: $noAi
highlightsLimit: $highlightsLimit
supportedTypes: $supportedTypes
) {
diff --git a/packages/shared/src/graphql/settings.ts b/packages/shared/src/graphql/settings.ts
index fd9969c87a..c48d561e1e 100644
--- a/packages/shared/src/graphql/settings.ts
+++ b/packages/shared/src/graphql/settings.ts
@@ -17,7 +17,6 @@ export type SettingsFlags = {
sidebarResourcesExpanded: boolean;
sidebarBookmarksExpanded: boolean;
clickbaitShieldEnabled: boolean;
- noAiFeedEnabled?: boolean;
timezoneMismatchIgnore?: string;
prompt?: Record
;
defaultWriteTab?: WriteFormTab;
@@ -30,7 +29,6 @@ export enum SidebarSettingsFlags {
ResourcesExpanded = 'sidebarResourcesExpanded',
BookmarksExpanded = 'sidebarBookmarksExpanded',
ClickbaitShieldEnabled = 'clickbaitShieldEnabled',
- NoAiFeedEnabled = 'noAiFeedEnabled',
}
export type RemoteSettings = {
diff --git a/packages/shared/src/hooks/useNoAiFeed.spec.ts b/packages/shared/src/hooks/useNoAiFeed.spec.ts
deleted file mode 100644
index 9fede2cf3c..0000000000
--- a/packages/shared/src/hooks/useNoAiFeed.spec.ts
+++ /dev/null
@@ -1,150 +0,0 @@
-import { act, renderHook } from '@testing-library/react';
-import { useNoAiFeed } from './useNoAiFeed';
-import { useConditionalFeature } from './useConditionalFeature';
-import { useSettingsContext } from '../contexts/SettingsContext';
-import { ToastSubject, useToastNotification } from './useToastNotification';
-import { useLogContext } from '../contexts/LogContext';
-import { SidebarSettingsFlags } from '../graphql/settings';
-import { labels } from '../lib/labels';
-import { LogEvent, Origin, TargetId } from '../lib/log';
-import { useActions } from './useActions';
-import { ActionType } from '../graphql/actions';
-
-jest.mock('./useConditionalFeature', () => ({
- useConditionalFeature: jest.fn(),
-}));
-
-jest.mock('../contexts/SettingsContext', () => ({
- useSettingsContext: jest.fn(),
-}));
-
-jest.mock('./useToastNotification', () => ({
- ...jest.requireActual('./useToastNotification'),
- useToastNotification: jest.fn(),
-}));
-
-jest.mock('../contexts/LogContext', () => ({
- useLogContext: jest.fn(),
-}));
-
-jest.mock('./useActions', () => ({
- useActions: jest.fn(),
-}));
-
-const mockUseConditionalFeature = useConditionalFeature as jest.Mock;
-const mockUseSettingsContext = useSettingsContext as jest.Mock;
-const mockUseToastNotification = useToastNotification as jest.Mock;
-const mockUseLogContext = useLogContext as jest.Mock;
-const mockUseActions = useActions as jest.Mock;
-
-describe('useNoAiFeed', () => {
- const updateFlag = jest.fn().mockResolvedValue(undefined);
- const updatePromptFlag = jest.fn().mockResolvedValue(undefined);
- const displayToast = jest.fn();
- const logEvent = jest.fn();
- const completeAction = jest.fn().mockResolvedValue(undefined);
-
- beforeEach(() => {
- jest.clearAllMocks();
-
- mockUseConditionalFeature.mockReturnValue({
- value: true,
- isLoading: false,
- });
- mockUseSettingsContext.mockReturnValue({
- flags: {
- noAiFeedEnabled: false,
- prompt: {},
- },
- loadedSettings: true,
- updateFlag,
- updatePromptFlag,
- });
- mockUseToastNotification.mockReturnValue({ displayToast });
- mockUseLogContext.mockReturnValue({ logEvent });
- mockUseActions.mockReturnValue({ completeAction });
- });
-
- it('reads the saved no ai preference', () => {
- mockUseSettingsContext.mockReturnValue({
- flags: {
- noAiFeedEnabled: true,
- prompt: {},
- },
- loadedSettings: true,
- updateFlag,
- updatePromptFlag,
- });
-
- const { result } = renderHook(() => useNoAiFeed());
-
- expect(result.current.isNoAi).toBe(true);
- expect(result.current.isNoAiAvailable).toBe(true);
- expect(result.current.isLoaded).toBe(true);
- });
-
- it('enables no ai mode locally and shows a save nudge toast', async () => {
- const { result } = renderHook(() => useNoAiFeed());
-
- await act(async () => {
- await result.current.toggleNoAi();
- });
-
- expect(displayToast).toHaveBeenCalledWith(labels.feed.noAi.nudge.message, {
- persistent: true,
- subject: ToastSubject.Feed,
- action: expect.objectContaining({
- copy: labels.feed.noAi.nudge.action,
- }),
- });
- expect(updatePromptFlag).toHaveBeenCalledWith(
- 'no_ai_feed_preference_prompt',
- true,
- );
-
- const toastAction = displayToast.mock.calls[0][1].action.onClick;
-
- await act(async () => {
- await toastAction();
- });
-
- expect(updateFlag).toHaveBeenCalledWith(
- SidebarSettingsFlags.NoAiFeedEnabled,
- true,
- );
- expect(completeAction).toHaveBeenCalledWith(
- ActionType.DismissNoAiFeedToggle,
- );
- expect(logEvent).toHaveBeenCalledWith({
- event_name: LogEvent.SaveNoAiFeedPreference,
- target_id: TargetId.On,
- extra: JSON.stringify({
- origin: Origin.Feed,
- }),
- });
- });
-
- it('disables the saved no ai preference from the header', async () => {
- mockUseSettingsContext.mockReturnValue({
- flags: {
- noAiFeedEnabled: true,
- prompt: {},
- },
- loadedSettings: true,
- updateFlag,
- updatePromptFlag,
- });
-
- const { result } = renderHook(() => useNoAiFeed());
-
- await act(async () => {
- await result.current.toggleNoAi();
- });
-
- expect(updateFlag).toHaveBeenCalledWith(
- SidebarSettingsFlags.NoAiFeedEnabled,
- false,
- );
- expect(displayToast).toHaveBeenCalledWith(labels.feed.noAi.visible);
- });
-});
diff --git a/packages/shared/src/hooks/useNoAiFeed.ts b/packages/shared/src/hooks/useNoAiFeed.ts
deleted file mode 100644
index f27e9c3bfe..0000000000
--- a/packages/shared/src/hooks/useNoAiFeed.ts
+++ /dev/null
@@ -1,141 +0,0 @@
-import { useCallback, useEffect, useMemo, useState } from 'react';
-import { featureNoAiFeed } from '../lib/featureManagement';
-import { useSettingsContext } from '../contexts/SettingsContext';
-import { useConditionalFeature } from './useConditionalFeature';
-import { ToastSubject, useToastNotification } from './useToastNotification';
-import { labels } from '../lib/labels';
-import { SidebarSettingsFlags } from '../graphql/settings';
-import { useLogContext } from '../contexts/LogContext';
-import { LogEvent, Origin, TargetId } from '../lib/log';
-import { useActions } from './useActions';
-import { ActionType } from '../graphql/actions';
-
-type UseNoAiFeedProps = {
- shouldEvaluate?: boolean;
-};
-
-type UseNoAiFeedReturn = {
- isNoAi: boolean;
- isNoAiAvailable: boolean;
- isLoaded: boolean;
- toggleNoAi: () => Promise;
-};
-
-const NO_AI_FEED_PROMPT_FLAG = 'no_ai_feed_preference_prompt';
-
-export const useNoAiFeed = ({
- shouldEvaluate = true,
-}: UseNoAiFeedProps = {}): UseNoAiFeedReturn => {
- const { value: featureEnabled, isLoading: isFeatureLoading } =
- useConditionalFeature({
- feature: featureNoAiFeed,
- shouldEvaluate,
- });
- const { flags, loadedSettings, updateFlag, updatePromptFlag } =
- useSettingsContext();
- const { displayToast } = useToastNotification();
- const { logEvent } = useLogContext();
- const { completeAction } = useActions();
- const [sessionNoAi, setSessionNoAi] = useState(false);
-
- const savedNoAi = flags?.noAiFeedEnabled ?? false;
- const hasSeenPrompt = !!flags?.prompt?.[NO_AI_FEED_PROMPT_FLAG];
-
- useEffect(() => {
- if (!savedNoAi || !sessionNoAi) {
- return;
- }
-
- setSessionNoAi(false);
- }, [savedNoAi, sessionNoAi]);
-
- const persistNoAiPreference = useCallback(
- async (value: boolean) => {
- await updateFlag(SidebarSettingsFlags.NoAiFeedEnabled, value);
- setSessionNoAi(false);
- },
- [updateFlag],
- );
-
- const maybeShowSavePreferenceNudge = useCallback(async () => {
- if (savedNoAi || hasSeenPrompt) {
- displayToast(labels.feed.noAi.hidden, {
- subject: ToastSubject.Feed,
- });
-
- return;
- }
-
- displayToast(labels.feed.noAi.nudge.message, {
- persistent: true,
- subject: ToastSubject.Feed,
- action: {
- copy: labels.feed.noAi.nudge.action,
- onClick: async () => {
- await persistNoAiPreference(true);
- await completeAction(ActionType.DismissNoAiFeedToggle);
- logEvent({
- event_name: LogEvent.SaveNoAiFeedPreference,
- target_id: TargetId.On,
- extra: JSON.stringify({
- origin: Origin.Feed,
- }),
- });
- },
- },
- });
- await updatePromptFlag(NO_AI_FEED_PROMPT_FLAG, true);
- }, [
- completeAction,
- displayToast,
- hasSeenPrompt,
- logEvent,
- persistNoAiPreference,
- savedNoAi,
- updatePromptFlag,
- ]);
-
- const isNoAiAvailable = shouldEvaluate && featureEnabled;
- const isNoAi = isNoAiAvailable && (savedNoAi || sessionNoAi);
-
- const toggleNoAi = useCallback(async () => {
- const nextIsNoAi = !isNoAi;
-
- if (nextIsNoAi) {
- setSessionNoAi(true);
- await maybeShowSavePreferenceNudge();
-
- return;
- }
-
- if (savedNoAi) {
- await persistNoAiPreference(false);
- displayToast(labels.feed.noAi.visible);
-
- return;
- }
-
- setSessionNoAi(false);
- displayToast(labels.feed.noAi.visible);
- }, [
- displayToast,
- isNoAi,
- maybeShowSavePreferenceNudge,
- persistNoAiPreference,
- savedNoAi,
- ]);
-
- const isLoaded =
- !shouldEvaluate ||
- (!isFeatureLoading && (!featureEnabled || loadedSettings));
-
- return useMemo(
- () => ({
- isNoAi,
- isNoAiAvailable,
- isLoaded,
- toggleNoAi,
- }),
- [isLoaded, isNoAi, isNoAiAvailable, toggleNoAi],
- );
-};
diff --git a/packages/shared/src/lib/featureManagement.ts b/packages/shared/src/lib/featureManagement.ts
index cf0127f33e..b7a8f61607 100644
--- a/packages/shared/src/lib/featureManagement.ts
+++ b/packages/shared/src/lib/featureManagement.ts
@@ -32,7 +32,6 @@ export const upvotedFeedVersion = new Feature('upvoted_feed_version', 2);
export const discussedFeedVersion = new Feature('discussed_feed_version', 2);
export const latestFeedVersion = new Feature('latest_feed_version', 2);
export const customFeedVersion = new Feature('custom_feed_version', 2);
-export const featureNoAiFeed = new Feature('no_ai_feed', false);
export const featureFeedV2Highlights = new Feature('feed_v2_highlights', false);
// @ts-expect-error stale feature without default
diff --git a/packages/shared/src/lib/image.ts b/packages/shared/src/lib/image.ts
index 4d20ddfd97..a45ce4d6db 100644
--- a/packages/shared/src/lib/image.ts
+++ b/packages/shared/src/lib/image.ts
@@ -210,9 +210,6 @@ export const cloudinaryAuthBannerBackground1920w =
export const cloudinaryAuthBannerBackground1440w =
'https://media.daily.dev/image/upload/s--lf8LUJjq--/c_auto,g_center,w_1440/f_auto//v1732012913/login-popover-dailydev_mxb7lw';
-export const cloudinaryNoAiFeedToggle =
- 'https://media.daily.dev/image/upload/s--nndE59fs--/f_auto,q_auto/v1775238401/public/ChatGPT%20Image%20Mar%2031%2C%202026%2C%2011_00_27%20PM';
-
export const cloudinaryPWA =
'https://media.daily.dev/image/upload/s--_kFKAft3--/f_auto/v1735045791/web_-_safari_j52hcx';
diff --git a/packages/shared/src/lib/labels.ts b/packages/shared/src/lib/labels.ts
index 576cf7225b..d6e5cd7273 100644
--- a/packages/shared/src/lib/labels.ts
+++ b/packages/shared/src/lib/labels.ts
@@ -90,15 +90,6 @@ export const labels = {
contentLanguage: 'New language preferences set for all feeds',
},
},
- noAi: {
- hidden: 'AI chatter hidden.',
- visible: 'AI chatter is back.',
- nudge: {
- message:
- 'AI chatter hidden. Want to hide this toggle too? You can change it later in Feed settings.',
- action: 'Save & hide toggle',
- },
- },
},
integrations: {
prompt: {
diff --git a/packages/shared/src/lib/log.ts b/packages/shared/src/lib/log.ts
index 76927efc85..594c182cfa 100644
--- a/packages/shared/src/lib/log.ts
+++ b/packages/shared/src/lib/log.ts
@@ -289,8 +289,6 @@ export enum LogEvent {
ToggleClickbaitShield = 'toggle clickbait shield',
ClickbaitShieldTitle = 'clickbait shield title',
// End Clickbait Shield
- ToggleNoAiFeed = 'toggle no ai feed',
- SaveNoAiFeedPreference = 'save no ai feed preference',
InstallPWA = 'install pwa',
// Start Share
ShareProfile = 'share profile',