diff --git a/packages/atlas-service/src/main.ts b/packages/atlas-service/src/main.ts index 9420ff4164f..056596cb8f9 100644 --- a/packages/atlas-service/src/main.ts +++ b/packages/atlas-service/src/main.ts @@ -94,8 +94,9 @@ export async function throwIfNotOk( function throwIfAINotEnabled(atlasService: typeof AtlasService) { if ( - !preferences.getPreferences().cloudFeatureRolloutAccess?.GEN_AI_COMPASS && - !preferences.getPreferences().enableAIWithoutRolloutAccess + (!preferences.getPreferences().cloudFeatureRolloutAccess?.GEN_AI_COMPASS && + !preferences.getPreferences().enableAIWithoutRolloutAccess) || + !preferences.getPreferences().enableAIFeatures ) { throw new Error( "Compass' AI functionality is not currently enabled. Please try again later." diff --git a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/pipeline-actions.tsx b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/pipeline-actions.tsx index 77b2b9982dc..cbd92dbc330 100644 --- a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/pipeline-actions.tsx +++ b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/pipeline-actions.tsx @@ -90,7 +90,7 @@ export const PipelineActions: React.FunctionComponent = ({ return (
{isAIFeatureEnabled && showAIEntry && ( - + )} {showInsights && showCollectionScanInsight && (
diff --git a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/pipeline-stages.tsx b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/pipeline-stages.tsx index d1413b8eb9f..72d16b89353 100644 --- a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/pipeline-stages.tsx +++ b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/pipeline-stages.tsx @@ -91,7 +91,10 @@ export const PipelineStages: React.FunctionComponent = ({ {isAIFeatureEnabled && showAIEntry && ( <> {nbsp} - + )} diff --git a/packages/compass-aggregations/src/modules/pipeline-builder/pipeline-ai.ts b/packages/compass-aggregations/src/modules/pipeline-builder/pipeline-ai.ts index 316cab64e22..c3635c46f55 100644 --- a/packages/compass-aggregations/src/modules/pipeline-builder/pipeline-ai.ts +++ b/packages/compass-aggregations/src/modules/pipeline-builder/pipeline-ai.ts @@ -423,7 +423,7 @@ const aiPipelineReducer: Reducer = ( ) ) { // If fetching query failed due to authentication error, reset the state to - // hide the input and show the "Ask AI" button again: this should start the + // hide the input and show the "Generate aggregation" button again: this should start the // sign in flow for the user when clicked if (action.networkErrorCode === 401) { return { ...initialState }; diff --git a/packages/compass-components/src/components/generative-ai/ai-entry-svg.tsx b/packages/compass-components/src/components/generative-ai/ai-entry-svg.tsx new file mode 100644 index 00000000000..07d792c69e4 --- /dev/null +++ b/packages/compass-components/src/components/generative-ai/ai-entry-svg.tsx @@ -0,0 +1,85 @@ +import React from 'react'; +import { css, cx } from '@leafygreen-ui/emotion'; +import { palette } from '@leafygreen-ui/palette'; + +export const aiEntrySVGStyles = css({ + alignSelf: 'center', + path: { + transition: 'fill 0.16s ease-in', + }, +}); + +export const aiEntrySVGDarkModeStyles = css({ + path: { + fill: palette.green.dark1, + }, + + '&:hover': { + path: { + fill: palette.green.base, + }, + }, +}); + +export const aiEntrySVGLightModeStyles = css({ + path: { + fill: palette.green.dark2, + }, + '&:hover': { + path: { + fill: palette.green.dark1, + }, + }, +}); + +export const DEFAULT_AI_ENTRY_SIZE = 16; + +// Note: This is duplicated below as a string for HTML. +const AIEntrySVG = ({ + darkMode, + size = DEFAULT_AI_ENTRY_SIZE, +}: { + darkMode?: boolean; + size?: number; +}) => ( + + + + + +); + +// Note: This is duplicated above for react. +const getAIEntrySVGString = () => ` + + + + +`; + +export { getAIEntrySVGString, AIEntrySVG }; diff --git a/packages/compass-components/src/components/generative-ai/ai-experience-entry.tsx b/packages/compass-components/src/components/generative-ai/ai-experience-entry.tsx index 61b96c00072..c97ab1d957f 100644 --- a/packages/compass-components/src/components/generative-ai/ai-experience-entry.tsx +++ b/packages/compass-components/src/components/generative-ai/ai-experience-entry.tsx @@ -4,12 +4,12 @@ import { css, cx } from '@leafygreen-ui/emotion'; import { spacing } from '@leafygreen-ui/tokens'; import { - RobotSVG, - getRobotSVGString, - robotSVGDarkModeStyles, - robotSVGLightModeStyles, - robotSVGStyles, -} from './robot-svg'; + AIEntrySVG, + getAIEntrySVGString, + aiEntrySVGDarkModeStyles, + aiEntrySVGLightModeStyles, + aiEntrySVGStyles, +} from './ai-entry-svg'; import { focusRing } from '../../hooks/use-focus-ring'; import { useDarkMode } from '../../hooks/use-theme'; @@ -36,7 +36,7 @@ const aiEntryStyles = css( gap: `${spacing[1]}px`, transition: 'color 0.16s ease-in', - rect: { + path: { transition: 'fill 0.16s ease-in', }, @@ -45,40 +45,42 @@ const aiEntryStyles = css( }, }, focusRing, - robotSVGStyles + aiEntrySVGStyles ); const aiEntryDarkModeStyles = css( { color: palette.green.dark1, '&:hover': { - rect: { + path: { fill: palette.green.base, }, color: palette.green.base, }, }, - robotSVGDarkModeStyles + aiEntrySVGDarkModeStyles ); const aiEntryLightModeStyles = css( { color: palette.green.dark2, '&:hover': { - rect: { + path: { fill: palette.green.dark1, }, color: palette.green.dark1, }, }, - robotSVGLightModeStyles + aiEntrySVGLightModeStyles ); function AIExperienceEntry({ 'data-testid': dataTestId, + type, onClick, }: { ['data-testid']?: string; + type: 'aggregation' | 'query'; onClick: () => void; }) { const darkMode = useDarkMode(); @@ -92,8 +94,8 @@ function AIExperienceEntry({ onClick={onClick} data-testid={dataTestId} > - Ask AI - + Generate {type} + ); } @@ -115,7 +117,7 @@ function createAIPlaceholderHTMLPlaceholder({ containerEl.appendChild(placeholderTextEl); const aiButtonEl = document.createElement('button'); - aiButtonEl.setAttribute('data-testid', 'open-ai-query-ask-ai-button'); + aiButtonEl.setAttribute('data-testid', 'open-ai-query-entry-button'); // By default placeholder container will have pointer events disabled aiButtonEl.style.pointerEvents = 'auto'; // We stop mousedown from propagating and preventing default behavior to avoid @@ -136,9 +138,9 @@ function createAIPlaceholderHTMLPlaceholder({ darkMode ? aiEntryDarkModeStyles : aiEntryLightModeStyles ); - const robotIconSVG = `Ask AI -${getRobotSVGString()}`; - aiButtonEl.innerHTML = robotIconSVG; + const aiButtonContent = `Generate query +${getAIEntrySVGString()}`; + aiButtonEl.innerHTML = aiButtonContent; containerEl.appendChild(aiButtonEl); diff --git a/packages/compass-components/src/components/generative-ai/generative-ai-input.tsx b/packages/compass-components/src/components/generative-ai/generative-ai-input.tsx index 8573f7f663a..b4982308d46 100644 --- a/packages/compass-components/src/components/generative-ai/generative-ai-input.tsx +++ b/packages/compass-components/src/components/generative-ai/generative-ai-input.tsx @@ -7,7 +7,7 @@ import { Button, Icon, IconButton, TextInput } from '../leafygreen'; import { useDarkMode } from '../../hooks/use-theme'; import { ErrorSummary } from '../error-warning-summary'; import { SpinLoader } from '../loader'; -import { DEFAULT_ROBOT_SIZE, RobotSVG } from './robot-svg'; +import { DEFAULT_AI_ENTRY_SIZE, AIEntrySVG } from './ai-entry-svg'; import { AIFeedback } from './ai-feedback'; import { AIGuideCue } from './ai-guide-cue'; import { focusRing } from '../../hooks/use-focus-ring'; @@ -101,7 +101,7 @@ const buttonHighlightLightModeStyles = css({ const loaderContainerStyles = css({ padding: spacing[1], display: 'inline-flex', - width: DEFAULT_ROBOT_SIZE + spacing[2], + width: DEFAULT_AI_ENTRY_SIZE + spacing[2], justifyContent: 'space-around', }); @@ -114,10 +114,17 @@ const buttonResetStyles = css({ }); const closeAIButtonStyles = css(buttonResetStyles, focusRing, { + height: spacing[4] + spacing[1], + display: 'flex', + alignItems: 'center', padding: spacing[1], position: 'absolute', }); +const aiEntryContainerStyles = css({ + display: 'flex', +}); + const closeText = 'Close AI Helper'; const SubmitArrowSVG = ({ darkMode }: { darkMode?: boolean }) => ( @@ -245,26 +252,6 @@ function GenerativeAIInput({ } onKeyDown={onTextInputKeyDown} /> -
{isFetching ? (
@@ -328,6 +315,26 @@ function GenerativeAIInput({ )}
+
{didSucceed && onSubmitFeedback && ( diff --git a/packages/compass-components/src/components/generative-ai/robot-svg.tsx b/packages/compass-components/src/components/generative-ai/robot-svg.tsx deleted file mode 100644 index 45c5e6f1594..00000000000 --- a/packages/compass-components/src/components/generative-ai/robot-svg.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import React from 'react'; -import { css, cx } from '@leafygreen-ui/emotion'; -import { palette } from '@leafygreen-ui/palette'; - -export const robotSVGStyles = css({ - rect: { - transition: 'fill 0.16s ease-in', - }, -}); - -export const robotSVGDarkModeStyles = css({ - rect: { - fill: palette.green.dark1, - }, - - '&:hover': { - rect: { - fill: palette.green.base, - }, - }, -}); - -export const robotSVGLightModeStyles = css({ - rect: { - fill: palette.green.dark2, - }, - '&:hover': { - rect: { - fill: palette.green.dark1, - }, - }, -}); - -export const DEFAULT_ROBOT_SIZE = 20; - -// Note: This is duplicated below as a string for HTML. -const RobotSVG = ({ - darkMode, - size = DEFAULT_ROBOT_SIZE, -}: { - darkMode?: boolean; - size?: number; -}) => ( - - - - - - - - - -); - -// Note: This is duplicated above for react. -const getRobotSVGString = - () => ` - - - - - - - -`; - -export { getRobotSVGString, RobotSVG }; diff --git a/packages/compass-e2e-tests/helpers/selectors.ts b/packages/compass-e2e-tests/helpers/selectors.ts index 37fbf061352..18dda0a8584 100644 --- a/packages/compass-e2e-tests/helpers/selectors.ts +++ b/packages/compass-e2e-tests/helpers/selectors.ts @@ -1058,8 +1058,8 @@ export const queryBarExportToLanguageButton = (tabName: string): string => { const tabSelector = collectionContent(tabName); return `${tabSelector} [data-testid="query-bar-open-export-to-language-button"]`; }; -export const QueryBarAskAIButton = - '[data-testid="open-ai-query-ask-ai-button"]'; +export const QueryBarAIEntryButton = + '[data-testid="open-ai-query-entry-button"]'; export const QueryBarAITextInput = '[data-testid="ai-user-text-input"]'; export const QueryBarAIGenerateQueryButton = '[data-testid="ai-generate-button"]'; diff --git a/packages/compass-e2e-tests/tests/collection-ai-query.test.ts b/packages/compass-e2e-tests/tests/collection-ai-query.test.ts index beab005bdb6..7e478996323 100644 --- a/packages/compass-e2e-tests/tests/collection-ai-query.test.ts +++ b/packages/compass-e2e-tests/tests/collection-ai-query.test.ts @@ -84,8 +84,8 @@ describe('Collection ai query', function () { }); it('makes request to the server and updates the query bar with the response', async function () { - // Click the ask ai button. - await browser.clickVisible(Selectors.QueryBarAskAIButton); + // Click the ai entry button. + await browser.clickVisible(Selectors.QueryBarAIEntryButton); // Enter the ai prompt. await browser.clickVisible(Selectors.QueryBarAITextInput); @@ -137,8 +137,8 @@ describe('Collection ai query', function () { }); it('the error is shown to the user', async function () { - // Click the ask ai button. - await browser.clickVisible(Selectors.QueryBarAskAIButton); + // Click the ai entry button. + await browser.clickVisible(Selectors.QueryBarAIEntryButton); // Enter the ai prompt. await browser.clickVisible(Selectors.QueryBarAITextInput); diff --git a/packages/compass-preferences-model/src/preferences.spec.ts b/packages/compass-preferences-model/src/preferences.spec.ts index adb1703d311..214f64fecbf 100644 --- a/packages/compass-preferences-model/src/preferences.spec.ts +++ b/packages/compass-preferences-model/src/preferences.spec.ts @@ -151,6 +151,7 @@ describe('Preferences class', function () { enableDevTools: 'set-global', networkTraffic: 'set-global', trackUsageStatistics: 'set-global', + enableAIFeatures: 'set-global', enableMaps: 'set-cli', enableShell: 'set-cli', readOnly: 'set-global', @@ -214,6 +215,7 @@ describe('Preferences class', function () { }, { networkTraffic: false, + enableAIFeatures: false, enableMaps: false, enableFeedbackPanel: false, trackUsageStatistics: false, @@ -246,6 +248,7 @@ describe('Preferences class', function () { const states = preferences.getPreferenceStates(); expect(states).to.deep.equal({ + enableAIFeatures: 'hardcoded', enableDevTools: 'set-global', enableMaps: 'set-cli', enableFeedbackPanel: 'hardcoded', diff --git a/packages/compass-preferences-model/src/preferences.ts b/packages/compass-preferences-model/src/preferences.ts index b5b2c47c82f..d247873484e 100644 --- a/packages/compass-preferences-model/src/preferences.ts +++ b/packages/compass-preferences-model/src/preferences.ts @@ -26,6 +26,7 @@ export type UserConfigurablePreferences = PermanentFeatureFlags & FeatureFlags & { // User-facing preferences autoUpdates: boolean; + enableAIFeatures: boolean; enableMaps: boolean; trackUsageStatistics: boolean; enableFeedbackPanel: boolean; @@ -429,6 +430,18 @@ export const storedUserPreferencesProps: Required<{ validator: z.boolean().default(false), type: 'boolean', }, + enableAIFeatures: { + ui: true, + cli: true, + global: true, + description: { + short: 'Enable AI Features', + long: 'Allow the use of AI features in Compass which make requests to 3rd party services. These features are currently experimental and offered as a preview to only a limited number of users.', + }, + deriveValue: deriveNetworkTrafficOptionState('enableAIFeatures'), + validator: z.boolean().default(true), + type: 'boolean', + }, /** * Switch to enable/disable Intercom panel (renamed from `intercom`). */ @@ -617,7 +630,6 @@ export const storedUserPreferencesProps: Required<{ validator: z.boolean().default(false), type: 'boolean', }, - /** * Chooses atlas service backend configuration from preset * - compas-dev: locally running compass kanopy backend (localhost) @@ -1009,6 +1021,7 @@ export class Preferences { if (!showedNetworkOptIn) { await this.savePreferences({ autoUpdates: true, + enableAIFeatures: true, enableMaps: true, trackUsageStatistics: true, enableFeedbackPanel: true, diff --git a/packages/compass-preferences-model/src/utils.ts b/packages/compass-preferences-model/src/utils.ts index 8056e5f162d..028caed8b32 100644 --- a/packages/compass-preferences-model/src/utils.ts +++ b/packages/compass-preferences-model/src/utils.ts @@ -51,8 +51,11 @@ export function useIsAIFeatureEnabled(React: ReactHooks) { 'cloudFeatureRolloutAccess', React )?.GEN_AI_COMPASS; + const enableAIFeatures = usePreference('enableAIFeatures', React); return ( - enableAIExperience && (enableAIWithoutRolloutAccess || isAIFeatureEnabled) + enableAIExperience && + (enableAIWithoutRolloutAccess || isAIFeatureEnabled) && + enableAIFeatures ); } diff --git a/packages/compass-query-bar/src/components/query-bar.spec.tsx b/packages/compass-query-bar/src/components/query-bar.spec.tsx index 6b39e6f7464..8ab43c263a4 100644 --- a/packages/compass-query-bar/src/components/query-bar.spec.tsx +++ b/packages/compass-query-bar/src/components/query-bar.spec.tsx @@ -126,6 +126,7 @@ describe('QueryBar Component', function () { sandbox = sinon.createSandbox(); sandbox.stub(preferencesAccess, 'getPreferences').returns({ enableAIExperience: true, + enableAIFeatures: true, cloudFeatureRolloutAccess: { GEN_AI_COMPASS: true, }, @@ -150,9 +151,9 @@ describe('QueryBar Component', function () { ); }); - it('renders the ask ai button', function () { - expect(screen.getByText('Ask AI')).to.exist; - expect(screen.getByTestId('ai-experience-ask-ai-button')).to.exist; + it('renders the ai entry button', function () { + expect(screen.getByText('Generate query')).to.exist; + expect(screen.getByTestId('ai-experience-query-entry-button')).to.exist; }); }); @@ -163,21 +164,48 @@ describe('QueryBar Component', function () { }); }); - it('does not render the ask ai button, but renders the placeholder', function () { - expect(screen.getByText('Ask AI')).to.exist; - expect(screen.queryByTestId('ai-experience-ask-ai-button')).to.not + it('does not render the ai entry button, but renders the placeholder', function () { + expect(screen.getByText('Generate query')).to.exist; + expect(screen.queryByTestId('ai-experience-query-entry-button')).to.not .exist; }); }); }); - describe('with ai disabled', function () { + describe('with enableAIExperience ai disabled', function () { let sandbox: sinon.SinonSandbox; beforeEach(function () { sandbox = sinon.createSandbox(); sandbox.stub(preferencesAccess, 'getPreferences').returns({ enableAIExperience: false, + enableAIFeatures: true, + cloudFeatureRolloutAccess: { + GEN_AI_COMPASS: true, + }, + } as any); + renderQueryBar({ + queryOptionsLayout: ['filter'], + }); + }); + + afterEach(function () { + return sandbox.restore(); + }); + + it('does not render the ask ai button', function () { + expect(screen.queryByText('Ask AI')).to.not.exist; + }); + }); + + describe('with enableAIFeatures ai disabled', function () { + let sandbox: sinon.SinonSandbox; + + beforeEach(function () { + sandbox = sinon.createSandbox(); + sandbox.stub(preferencesAccess, 'getPreferences').returns({ + enableAIExperience: true, + enableAIFeatures: false, cloudFeatureRolloutAccess: { GEN_AI_COMPASS: true, }, diff --git a/packages/compass-query-bar/src/components/query-bar.tsx b/packages/compass-query-bar/src/components/query-bar.tsx index c88d40c6ab4..d2c5e1f67e9 100644 --- a/packages/compass-query-bar/src/components/query-bar.tsx +++ b/packages/compass-query-bar/src/components/query-bar.tsx @@ -83,7 +83,7 @@ const filterContainerStyles = css({ display: 'flex', position: 'relative', flexGrow: 1, - alignItems: 'center', + alignItems: 'flex-start', gap: spacing[2], }); @@ -91,6 +91,12 @@ const filterLabelStyles = css({ padding: 0, }); +const aiEntryContainerStyles = css({ + display: 'flex', + alignItems: 'center', + height: spacing[4] + spacing[1], +}); + const queryOptionsContainerStyles = css({ display: 'flex', flexDirection: 'column', @@ -208,7 +214,7 @@ export const QueryBar: React.FunctionComponent = ({ onShowAIInputClick, ]); - const showAskAIButton = useMemo(() => { + const showAIEntryButton = useMemo(() => { if (isAIInputVisible || !isAIFeatureEnabled) { return false; } @@ -247,11 +253,14 @@ export const QueryBar: React.FunctionComponent = ({ placeholder={filterPlaceholder} insights={insights} /> - {showAskAIButton && ( - + {showAIEntryButton && ( +
+ +
)}
{showExplainButton && newExplainPlan && ( diff --git a/packages/compass-query-bar/src/stores/ai-query-reducer.ts b/packages/compass-query-bar/src/stores/ai-query-reducer.ts index 5b8af058214..8180595641a 100644 --- a/packages/compass-query-bar/src/stores/ai-query-reducer.ts +++ b/packages/compass-query-bar/src/stores/ai-query-reducer.ts @@ -385,8 +385,8 @@ const aiQueryReducer: Reducer = ( if (isAction(action, AIQueryActionTypes.AIQueryFailed)) { // If fetching query failed due to authentication error, reset the state to - // hide the input and show the "Ask AI" button again: this should start the - // sign in flow for the user when clicked + // hide the input and show the "Generate query" button again: this should start + // the sign in flow for the user when clicked if (action.networkErrorCode === 401) { return { ...initialState }; } diff --git a/packages/compass-settings/src/components/settings/privacy.spec.tsx b/packages/compass-settings/src/components/settings/privacy.spec.tsx index 8750700bfc7..d88a661d6da 100644 --- a/packages/compass-settings/src/components/settings/privacy.spec.tsx +++ b/packages/compass-settings/src/components/settings/privacy.spec.tsx @@ -7,6 +7,19 @@ import { PrivacySettings } from './privacy'; import { configureStore } from '../../stores'; import { fetchSettings } from '../../stores/settings'; +function renderPrivacySettings( + store, + props: Partial> = {} +) { + const component = () => ( + + + + ); + render(component()); + return screen.getByTestId('privacy-settings'); +} + describe('PrivacySettings', function () { let container: HTMLElement; let store: ReturnType; @@ -18,35 +31,48 @@ describe('PrivacySettings', function () { beforeEach(async function () { store = configureStore(); await store.dispatch(fetchSettings()); - const component = () => ( - - - - ); - render(component()); - container = screen.getByTestId('privacy-settings'); }); afterEach(function () { cleanup(); }); - [ - 'autoUpdates', - 'enableMaps', - 'trackUsageStatistics', - 'enableFeedbackPanel', - ].forEach((option) => { - it(`renders ${option}`, function () { - expect(within(container).getByTestId(option)).to.exist; + describe('when rendered', function () { + beforeEach(function () { + container = renderPrivacySettings(store); }); - it(`changes ${option} value when option is clicked`, function () { - const checkbox = within(container).getByTestId(option); - const initialValue = getSettings()[option]; - userEvent.click(checkbox, undefined, { - skipPointerEventsCheck: true, + + [ + 'autoUpdates', + 'enableMaps', + 'trackUsageStatistics', + 'enableFeedbackPanel', + ].forEach((option) => { + it(`renders ${option}`, function () { + expect(within(container).getByTestId(option)).to.exist; + }); + it(`changes ${option} value when option is clicked`, function () { + const checkbox = within(container).getByTestId(option); + const initialValue = getSettings()[option]; + userEvent.click(checkbox, undefined, { + skipPointerEventsCheck: true, + }); + expect(getSettings()).to.have.property(option, !initialValue); }); - expect(getSettings()).to.have.property(option, !initialValue); }); }); + + it('does not render enableAIFeatures when isAIFeatureRolledOutToUser is false', function () { + container = renderPrivacySettings(store, { + isAIFeatureRolledOutToUser: false, + }); + expect(within(container).queryByTestId('enableAIFeatures')).to.not.exist; + }); + + it('renders enableAIFeatures when GisAIFeatureRolledOutToUser is true', function () { + container = renderPrivacySettings(store, { + isAIFeatureRolledOutToUser: true, + }); + expect(within(container).getByTestId('enableAIFeatures')).to.be.visible; + }); }); diff --git a/packages/compass-settings/src/components/settings/privacy.tsx b/packages/compass-settings/src/components/settings/privacy.tsx index 495963d9643..bd7b1927824 100644 --- a/packages/compass-settings/src/components/settings/privacy.tsx +++ b/packages/compass-settings/src/components/settings/privacy.tsx @@ -1,15 +1,29 @@ -import React from 'react'; +import React, { useMemo } from 'react'; import { Link } from '@mongodb-js/compass-components'; +import { connect } from 'react-redux'; +import type { UserPreferences } from 'compass-preferences-model'; +import { withPreferences } from 'compass-preferences-model'; + import SettingsList from './settings-list'; +import type { RootState } from '../../stores'; const privacyFields = [ 'autoUpdates', 'enableMaps', + 'enableAIFeatures', 'trackUsageStatistics', 'enableFeedbackPanel', ] as const; -export const PrivacySettings: React.FunctionComponent = () => { +export const PrivacySettings: React.FunctionComponent<{ + isAIFeatureRolledOutToUser?: boolean; +}> = ({ isAIFeatureRolledOutToUser }) => { + const privacyFieldsShown = useMemo(() => { + return isAIFeatureRolledOutToUser + ? privacyFields + : privacyFields.filter((field) => field !== 'enableAIFeatures'); + }, [isAIFeatureRolledOutToUser]); + return (
@@ -17,7 +31,7 @@ export const PrivacySettings: React.FunctionComponent = () => { services, which requires external network requests. Please choose from the settings below:
- +
With any of these options, none of your personal information or stored data will be submitted. @@ -31,4 +45,20 @@ export const PrivacySettings: React.FunctionComponent = () => { ); }; -export default PrivacySettings; +export default withPreferences( + connect( + ( + state: RootState, + ownProps: { + cloudFeatureRolloutAccess?: UserPreferences['cloudFeatureRolloutAccess']; + } + ) => { + return { + isAIFeatureRolledOutToUser: + ownProps.cloudFeatureRolloutAccess?.GEN_AI_COMPASS, + }; + } + )(PrivacySettings), + ['cloudFeatureRolloutAccess'], + React +);