used simplified privacy info modal and removed unnecessary components (#8200)
Co-authored-by: Douwe Osinga <douwe@squareup.com>
This commit is contained in:
@@ -14,7 +14,7 @@ import { ErrorUI } from './components/ErrorBoundary';
|
||||
import { ExtensionInstallModal } from './components/ExtensionInstallModal';
|
||||
import { ToastContainer } from 'react-toastify';
|
||||
import AnnouncementModal from './components/AnnouncementModal';
|
||||
import TelemetryOptOutModal from './components/TelemetryOptOutModal';
|
||||
import TelemetryConsentPrompt from './components/TelemetryConsentPrompt';
|
||||
import OnboardingGuard from './components/onboarding/OnboardingGuard';
|
||||
import { createSession } from './sessions';
|
||||
|
||||
@@ -678,7 +678,7 @@ export default function App() {
|
||||
<AppInner />
|
||||
</HashRouter>
|
||||
<AnnouncementModal />
|
||||
<TelemetryOptOutModal controlled={false} />
|
||||
<TelemetryConsentPrompt />
|
||||
</ModelAndProviderProvider>
|
||||
</FeaturesProvider>
|
||||
</ThemeProvider>
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Button } from './ui/button';
|
||||
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from './ui/dialog';
|
||||
import { TELEMETRY_UI_ENABLED } from '../updates';
|
||||
import { useConfig } from './ConfigContext';
|
||||
import {
|
||||
trackTelemetryPreference,
|
||||
setTelemetryEnabled as setAnalyticsTelemetryEnabled,
|
||||
} from '../utils/analytics';
|
||||
import PrivacyInfoModal from './onboarding/PrivacyInfoModal';
|
||||
import { defineMessages, useIntl } from '../i18n';
|
||||
|
||||
const i18n = defineMessages({
|
||||
heading: {
|
||||
id: 'telemetryConsentPrompt.heading',
|
||||
defaultMessage: 'Help improve goose',
|
||||
},
|
||||
description: {
|
||||
id: 'telemetryConsentPrompt.description',
|
||||
defaultMessage:
|
||||
'Would you like to share anonymous usage data to help improve goose? We never collect your conversations, code, or personal data.',
|
||||
},
|
||||
learnMore: {
|
||||
id: 'telemetryConsentPrompt.learnMore',
|
||||
defaultMessage: 'Learn more',
|
||||
},
|
||||
optIn: {
|
||||
id: 'telemetryConsentPrompt.optIn',
|
||||
defaultMessage: 'Yes, share anonymous usage data',
|
||||
},
|
||||
optOut: {
|
||||
id: 'telemetryConsentPrompt.optOut',
|
||||
defaultMessage: 'No thanks',
|
||||
},
|
||||
});
|
||||
|
||||
const TELEMETRY_CONFIG_KEY = 'GOOSE_TELEMETRY_ENABLED';
|
||||
|
||||
export default function TelemetryConsentPrompt() {
|
||||
const intl = useIntl();
|
||||
const { read, upsert } = useConfig();
|
||||
const [showPrompt, setShowPrompt] = useState(false);
|
||||
const [showPrivacyInfo, setShowPrivacyInfo] = useState(false);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!TELEMETRY_UI_ENABLED) return;
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
const provider = await read('GOOSE_PROVIDER', false);
|
||||
if (!provider || provider === '') return;
|
||||
|
||||
const telemetryValue = await read(TELEMETRY_CONFIG_KEY, false);
|
||||
if (telemetryValue === null) {
|
||||
setShowPrompt(true);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to check telemetry config:', error);
|
||||
}
|
||||
})();
|
||||
}, [read]);
|
||||
|
||||
const handleChoice = async (enabled: boolean) => {
|
||||
setIsSubmitting(true);
|
||||
try {
|
||||
await upsert(TELEMETRY_CONFIG_KEY, enabled, false);
|
||||
trackTelemetryPreference(enabled, 'modal');
|
||||
setAnalyticsTelemetryEnabled(enabled);
|
||||
} catch (error) {
|
||||
console.error('Failed to save telemetry preference:', error);
|
||||
} finally {
|
||||
setShowPrompt(false);
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (!showPrompt) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dialog
|
||||
open
|
||||
onOpenChange={(open) => {
|
||||
if (!open) setShowPrompt(false);
|
||||
}}
|
||||
>
|
||||
<DialogContent className="w-[440px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-center">{intl.formatMessage(i18n.heading)}</DialogTitle>
|
||||
</DialogHeader>
|
||||
<p className="text-text-muted text-sm">
|
||||
{intl.formatMessage(i18n.description)}{' '}
|
||||
<button
|
||||
onClick={() => setShowPrivacyInfo(true)}
|
||||
className="text-blue-600 dark:text-blue-400 hover:underline"
|
||||
>
|
||||
{intl.formatMessage(i18n.learnMore)}
|
||||
</button>
|
||||
</p>
|
||||
<DialogFooter className="flex flex-col gap-2 sm:flex-col">
|
||||
<Button
|
||||
autoFocus
|
||||
onClick={() => handleChoice(true)}
|
||||
disabled={isSubmitting}
|
||||
className="w-full"
|
||||
>
|
||||
{intl.formatMessage(i18n.optIn)}
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => handleChoice(false)}
|
||||
disabled={isSubmitting}
|
||||
className="w-full text-text-secondary hover:text-text-primary"
|
||||
>
|
||||
{intl.formatMessage(i18n.optOut)}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<PrivacyInfoModal isOpen={showPrivacyInfo} onClose={() => setShowPrivacyInfo(false)} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,194 +0,0 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { BaseModal } from './ui/BaseModal';
|
||||
import { Button } from './ui/button';
|
||||
import { Goose } from './icons/Goose';
|
||||
import { TELEMETRY_UI_ENABLED } from '../updates';
|
||||
import { toastService } from '../toasts';
|
||||
import { useConfig } from './ConfigContext';
|
||||
import { trackTelemetryPreference } from '../utils/analytics';
|
||||
import { defineMessages, useIntl } from '../i18n';
|
||||
|
||||
const i18n = defineMessages({
|
||||
configError: {
|
||||
id: 'telemetryOptOutModal.configError',
|
||||
defaultMessage: 'Configuration Error',
|
||||
},
|
||||
configErrorMessage: {
|
||||
id: 'telemetryOptOutModal.configErrorMessage',
|
||||
defaultMessage: 'Failed to check telemetry configuration.',
|
||||
},
|
||||
optIn: {
|
||||
id: 'telemetryOptOutModal.optIn',
|
||||
defaultMessage: 'Yes, share anonymous usage data',
|
||||
},
|
||||
optOut: {
|
||||
id: 'telemetryOptOutModal.optOut',
|
||||
defaultMessage: 'No thanks',
|
||||
},
|
||||
heading: {
|
||||
id: 'telemetryOptOutModal.heading',
|
||||
defaultMessage: 'Help improve goose',
|
||||
},
|
||||
description: {
|
||||
id: 'telemetryOptOutModal.description',
|
||||
defaultMessage:
|
||||
'Would you like to help improve goose by sharing anonymous usage data? This helps us understand how goose is used and identify areas for improvement.',
|
||||
},
|
||||
whatWeCollect: {
|
||||
id: 'telemetryOptOutModal.whatWeCollect',
|
||||
defaultMessage: 'What we collect:',
|
||||
},
|
||||
collectOs: {
|
||||
id: 'telemetryOptOutModal.collectOs',
|
||||
defaultMessage: 'Operating system, version, and architecture',
|
||||
},
|
||||
collectVersion: {
|
||||
id: 'telemetryOptOutModal.collectVersion',
|
||||
defaultMessage: 'goose version and install method',
|
||||
},
|
||||
collectProvider: {
|
||||
id: 'telemetryOptOutModal.collectProvider',
|
||||
defaultMessage: 'Provider and model used',
|
||||
},
|
||||
collectExtensions: {
|
||||
id: 'telemetryOptOutModal.collectExtensions',
|
||||
defaultMessage: 'Extensions and tool usage counts (names only)',
|
||||
},
|
||||
collectSession: {
|
||||
id: 'telemetryOptOutModal.collectSession',
|
||||
defaultMessage: 'Session metrics (duration, interaction count, token usage)',
|
||||
},
|
||||
collectErrors: {
|
||||
id: 'telemetryOptOutModal.collectErrors',
|
||||
defaultMessage: 'Error types (e.g., "rate_limit", "auth" - no details)',
|
||||
},
|
||||
privacyNote: {
|
||||
id: 'telemetryOptOutModal.privacyNote',
|
||||
defaultMessage:
|
||||
'We never collect your conversations, code, tool arguments, error messages, or any personal data. You can change this setting anytime in Settings → App.',
|
||||
},
|
||||
});
|
||||
|
||||
const TELEMETRY_CONFIG_KEY = 'GOOSE_TELEMETRY_ENABLED';
|
||||
|
||||
type TelemetryOptOutModalProps =
|
||||
| { controlled: false }
|
||||
| { controlled: true; isOpen: boolean; onClose: () => void };
|
||||
|
||||
export default function TelemetryOptOutModal(props: TelemetryOptOutModalProps) {
|
||||
const intl = useIntl();
|
||||
const { read, upsert } = useConfig();
|
||||
const isControlled = props.controlled;
|
||||
const controlledIsOpen = isControlled ? props.isOpen : undefined;
|
||||
const onClose = isControlled ? props.onClose : undefined;
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
// Only check telemetry choice on first launch in uncontrolled mode
|
||||
useEffect(() => {
|
||||
if (isControlled) return;
|
||||
|
||||
const checkTelemetryChoice = async () => {
|
||||
try {
|
||||
const provider = await read('GOOSE_PROVIDER', false);
|
||||
|
||||
if (!provider || provider === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
const telemetryEnabled = await read(TELEMETRY_CONFIG_KEY, false);
|
||||
|
||||
if (telemetryEnabled === null) {
|
||||
setShowModal(true);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to check telemetry config:', error);
|
||||
toastService.error({
|
||||
title: intl.formatMessage(i18n.configError),
|
||||
msg: intl.formatMessage(i18n.configErrorMessage),
|
||||
traceback: error instanceof Error ? error.stack || '' : '',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
checkTelemetryChoice();
|
||||
}, [isControlled, read, intl]);
|
||||
|
||||
const handleChoice = async (enabled: boolean) => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
await upsert(TELEMETRY_CONFIG_KEY, enabled, false);
|
||||
trackTelemetryPreference(enabled, 'modal');
|
||||
setShowModal(false);
|
||||
onClose?.();
|
||||
} catch (error) {
|
||||
console.error('Failed to set telemetry preference:', error);
|
||||
setShowModal(false);
|
||||
onClose?.();
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (!TELEMETRY_UI_ENABLED) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isModalOpen = controlledIsOpen !== undefined ? controlledIsOpen : showModal;
|
||||
|
||||
if (!isModalOpen) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<BaseModal
|
||||
isOpen={isModalOpen}
|
||||
actions={
|
||||
<div className="flex flex-col gap-2 pb-3 px-3">
|
||||
<Button
|
||||
variant="default"
|
||||
onClick={() => handleChoice(true)}
|
||||
disabled={isLoading}
|
||||
className="w-full h-[44px] rounded-lg"
|
||||
>
|
||||
{intl.formatMessage(i18n.optIn)}
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => handleChoice(false)}
|
||||
disabled={isLoading}
|
||||
className="w-full h-[44px] rounded-lg text-text-secondary hover:text-text-primary"
|
||||
>
|
||||
{intl.formatMessage(i18n.optOut)}
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div className="px-2 py-3">
|
||||
<div className="flex justify-center mb-4">
|
||||
<Goose className="size-10 text-text-primary" />
|
||||
</div>
|
||||
<h2 className="text-2xl font-regular dark:text-white text-gray-900 text-center mb-3">
|
||||
{intl.formatMessage(i18n.heading)}
|
||||
</h2>
|
||||
<p className="text-text-primary text-sm mb-3">
|
||||
{intl.formatMessage(i18n.description)}
|
||||
</p>
|
||||
<div className="text-text-secondary text-xs space-y-1">
|
||||
<p className="font-medium text-text-primary">{intl.formatMessage(i18n.whatWeCollect)}</p>
|
||||
<ul className="list-disc list-inside space-y-0.5 ml-1">
|
||||
<li>{intl.formatMessage(i18n.collectOs)}</li>
|
||||
<li>{intl.formatMessage(i18n.collectVersion)}</li>
|
||||
<li>{intl.formatMessage(i18n.collectProvider)}</li>
|
||||
<li>{intl.formatMessage(i18n.collectExtensions)}</li>
|
||||
<li>{intl.formatMessage(i18n.collectSession)}</li>
|
||||
<li>{intl.formatMessage(i18n.collectErrors)}</li>
|
||||
</ul>
|
||||
<p className="mt-3 text-text-secondary">
|
||||
{intl.formatMessage(i18n.privacyNote)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</BaseModal>
|
||||
);
|
||||
}
|
||||
@@ -21,45 +21,106 @@ import { NavigationProvider, useNavigationContextSafe } from '../../Layout/Navig
|
||||
|
||||
const i18n = defineMessages({
|
||||
appearanceTitle: { id: 'settings.appearance.title', defaultMessage: 'Appearance' },
|
||||
appearanceDesc: { id: 'settings.appearance.description', defaultMessage: 'Configure how goose appears on your system' },
|
||||
appearanceDesc: {
|
||||
id: 'settings.appearance.description',
|
||||
defaultMessage: 'Configure how goose appears on your system',
|
||||
},
|
||||
notifications: { id: 'settings.notifications.title', defaultMessage: 'Notifications' },
|
||||
notificationsDesc: { id: 'settings.notifications.description', defaultMessage: 'Notifications are managed by your OS - {link}' },
|
||||
notificationsDesc: {
|
||||
id: 'settings.notifications.description',
|
||||
defaultMessage: 'Notifications are managed by your OS - {link}',
|
||||
},
|
||||
configGuide: { id: 'settings.notifications.configGuide', defaultMessage: 'Configuration guide' },
|
||||
openSettings: { id: 'settings.notifications.openSettings', defaultMessage: 'Open Settings' },
|
||||
menuBarIcon: { id: 'settings.menuBarIcon.title', defaultMessage: 'Menu bar icon' },
|
||||
menuBarIconDesc: { id: 'settings.menuBarIcon.description', defaultMessage: 'Show goose in the menu bar' },
|
||||
menuBarIconDesc: {
|
||||
id: 'settings.menuBarIcon.description',
|
||||
defaultMessage: 'Show goose in the menu bar',
|
||||
},
|
||||
dockIcon: { id: 'settings.dockIcon.title', defaultMessage: 'Dock icon' },
|
||||
dockIconDesc: { id: 'settings.dockIcon.description', defaultMessage: 'Show goose in the dock' },
|
||||
preventSleep: { id: 'settings.preventSleep.title', defaultMessage: 'Prevent Sleep' },
|
||||
preventSleepDesc: { id: 'settings.preventSleep.description', defaultMessage: 'Keep your computer awake while goose is running a task (screen can still lock)' },
|
||||
preventSleepDesc: {
|
||||
id: 'settings.preventSleep.description',
|
||||
defaultMessage:
|
||||
'Keep your computer awake while goose is running a task (screen can still lock)',
|
||||
},
|
||||
costTracking: { id: 'settings.costTracking.title', defaultMessage: 'Cost Tracking' },
|
||||
costTrackingDesc: { id: 'settings.costTracking.description', defaultMessage: 'Show model pricing and usage costs' },
|
||||
costTrackingDesc: {
|
||||
id: 'settings.costTracking.description',
|
||||
defaultMessage: 'Show model pricing and usage costs',
|
||||
},
|
||||
themeTitle: { id: 'settings.theme.title', defaultMessage: 'Theme' },
|
||||
themeDesc: { id: 'settings.theme.description', defaultMessage: 'Customize the look and feel of goose' },
|
||||
themeDesc: {
|
||||
id: 'settings.theme.description',
|
||||
defaultMessage: 'Customize the look and feel of goose',
|
||||
},
|
||||
navigationTitle: { id: 'settings.navigation.title', defaultMessage: 'Navigation' },
|
||||
navigationDesc: { id: 'settings.navigation.description', defaultMessage: 'Customize navigation layout and behavior' },
|
||||
navigationDesc: {
|
||||
id: 'settings.navigation.description',
|
||||
defaultMessage: 'Customize navigation layout and behavior',
|
||||
},
|
||||
navMode: { id: 'settings.navigation.mode', defaultMessage: 'Mode' },
|
||||
navStyle: { id: 'settings.navigation.style', defaultMessage: 'Style' },
|
||||
navPosition: { id: 'settings.navigation.position', defaultMessage: 'Position' },
|
||||
navCustomize: { id: 'settings.navigation.customize', defaultMessage: 'Customize Items' },
|
||||
helpTitle: { id: 'settings.help.title', defaultMessage: 'Help & feedback' },
|
||||
helpDesc: { id: 'settings.help.description', defaultMessage: 'Help us improve goose by reporting issues or requesting new features' },
|
||||
helpDesc: {
|
||||
id: 'settings.help.description',
|
||||
defaultMessage: 'Help us improve goose by reporting issues or requesting new features',
|
||||
},
|
||||
reportBug: { id: 'settings.help.reportBug', defaultMessage: 'Report a Bug' },
|
||||
requestFeature: { id: 'settings.help.requestFeature', defaultMessage: 'Request a Feature' },
|
||||
versionTitle: { id: 'settings.version.title', defaultMessage: 'Version' },
|
||||
updatesTitle: { id: 'settings.updates.title', defaultMessage: 'Updates' },
|
||||
updatesDesc: { id: 'settings.updates.description', defaultMessage: 'Check for and install updates to keep goose running at its best' },
|
||||
notificationsModalTitle: { id: 'settings.notifications.modal.title', defaultMessage: 'How to Enable Notifications' },
|
||||
notificationsMacInstructions: { id: 'settings.notifications.modal.macInstructions', defaultMessage: 'To enable notifications on macOS:' },
|
||||
notificationsMacStep1: { id: 'settings.notifications.modal.macStep1', defaultMessage: 'Open System Preferences' },
|
||||
notificationsMacStep2: { id: 'settings.notifications.modal.macStep2', defaultMessage: 'Click on Notifications' },
|
||||
notificationsMacStep3: { id: 'settings.notifications.modal.macStep3', defaultMessage: 'Find and select goose in the application list' },
|
||||
notificationsMacStep4: { id: 'settings.notifications.modal.macStep4', defaultMessage: 'Enable notifications and adjust settings as desired' },
|
||||
notificationsWinInstructions: { id: 'settings.notifications.modal.winInstructions', defaultMessage: 'To enable notifications on Windows:' },
|
||||
notificationsWinStep1: { id: 'settings.notifications.modal.winStep1', defaultMessage: 'Open Settings' },
|
||||
notificationsWinStep2: { id: 'settings.notifications.modal.winStep2', defaultMessage: 'Go to System > Notifications' },
|
||||
notificationsWinStep3: { id: 'settings.notifications.modal.winStep3', defaultMessage: 'Find and select goose in the application list' },
|
||||
notificationsWinStep4: { id: 'settings.notifications.modal.winStep4', defaultMessage: 'Toggle notifications on and adjust settings as desired' },
|
||||
updatesDesc: {
|
||||
id: 'settings.updates.description',
|
||||
defaultMessage: 'Check for and install updates to keep goose running at its best',
|
||||
},
|
||||
notificationsModalTitle: {
|
||||
id: 'settings.notifications.modal.title',
|
||||
defaultMessage: 'How to Enable Notifications',
|
||||
},
|
||||
notificationsMacInstructions: {
|
||||
id: 'settings.notifications.modal.macInstructions',
|
||||
defaultMessage: 'To enable notifications on macOS:',
|
||||
},
|
||||
notificationsMacStep1: {
|
||||
id: 'settings.notifications.modal.macStep1',
|
||||
defaultMessage: 'Open System Preferences',
|
||||
},
|
||||
notificationsMacStep2: {
|
||||
id: 'settings.notifications.modal.macStep2',
|
||||
defaultMessage: 'Click on Notifications',
|
||||
},
|
||||
notificationsMacStep3: {
|
||||
id: 'settings.notifications.modal.macStep3',
|
||||
defaultMessage: 'Find and select goose in the application list',
|
||||
},
|
||||
notificationsMacStep4: {
|
||||
id: 'settings.notifications.modal.macStep4',
|
||||
defaultMessage: 'Enable notifications and adjust settings as desired',
|
||||
},
|
||||
notificationsWinInstructions: {
|
||||
id: 'settings.notifications.modal.winInstructions',
|
||||
defaultMessage: 'To enable notifications on Windows:',
|
||||
},
|
||||
notificationsWinStep1: {
|
||||
id: 'settings.notifications.modal.winStep1',
|
||||
defaultMessage: 'Open Settings',
|
||||
},
|
||||
notificationsWinStep2: {
|
||||
id: 'settings.notifications.modal.winStep2',
|
||||
defaultMessage: 'Go to System > Notifications',
|
||||
},
|
||||
notificationsWinStep3: {
|
||||
id: 'settings.notifications.modal.winStep3',
|
||||
defaultMessage: 'Find and select goose in the application list',
|
||||
},
|
||||
notificationsWinStep4: {
|
||||
id: 'settings.notifications.modal.winStep4',
|
||||
defaultMessage: 'Toggle notifications on and adjust settings as desired',
|
||||
},
|
||||
close: { id: 'settings.close', defaultMessage: 'Close' },
|
||||
});
|
||||
|
||||
@@ -94,23 +155,31 @@ const NavigationSettingsContent: React.FC = () => {
|
||||
{isExpanded && (
|
||||
<CardContent className="pt-4 px-4 space-y-6">
|
||||
<div>
|
||||
<h3 className="text-sm font-medium text-text-primary mb-3">{intl.formatMessage(i18n.navMode)}</h3>
|
||||
<h3 className="text-sm font-medium text-text-primary mb-3">
|
||||
{intl.formatMessage(i18n.navMode)}
|
||||
</h3>
|
||||
<NavigationModeSelector />
|
||||
</div>
|
||||
{!isOverlayMode && (
|
||||
<div>
|
||||
<h3 className="text-sm font-medium text-text-primary mb-3">{intl.formatMessage(i18n.navStyle)}</h3>
|
||||
<h3 className="text-sm font-medium text-text-primary mb-3">
|
||||
{intl.formatMessage(i18n.navStyle)}
|
||||
</h3>
|
||||
<NavigationStyleSelector />
|
||||
</div>
|
||||
)}
|
||||
{!isOverlayMode && (
|
||||
<div>
|
||||
<h3 className="text-sm font-medium text-text-primary mb-3">{intl.formatMessage(i18n.navPosition)}</h3>
|
||||
<h3 className="text-sm font-medium text-text-primary mb-3">
|
||||
{intl.formatMessage(i18n.navPosition)}
|
||||
</h3>
|
||||
<NavigationPositionSelector />
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<h3 className="text-sm font-medium text-text-primary mb-3">{intl.formatMessage(i18n.navCustomize)}</h3>
|
||||
<h3 className="text-sm font-medium text-text-primary mb-3">
|
||||
{intl.formatMessage(i18n.navCustomize)}
|
||||
</h3>
|
||||
<NavigationCustomizationSettings />
|
||||
</div>
|
||||
</CardContent>
|
||||
@@ -267,7 +336,9 @@ export default function AppSettingsSection({ scrollToSection }: AppSettingsSecti
|
||||
<CardContent className="pt-4 space-y-4 px-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h3 className="text-text-primary text-xs">{intl.formatMessage(i18n.notifications)}</h3>
|
||||
<h3 className="text-text-primary text-xs">
|
||||
{intl.formatMessage(i18n.notifications)}
|
||||
</h3>
|
||||
<p className="text-xs text-text-secondary max-w-md mt-[2px]">
|
||||
{intl.formatMessage(i18n.notificationsDesc, {
|
||||
link: (
|
||||
@@ -386,14 +457,12 @@ export default function AppSettingsSection({ scrollToSection }: AppSettingsSecti
|
||||
{/* Navigation Settings */}
|
||||
<NavigationSettingsCard />
|
||||
|
||||
<TelemetrySettings isWelcome={false} />
|
||||
<TelemetrySettings />
|
||||
|
||||
<Card className="rounded-lg">
|
||||
<CardHeader className="pb-0">
|
||||
<CardTitle className="mb-1">{intl.formatMessage(i18n.helpTitle)}</CardTitle>
|
||||
<CardDescription>
|
||||
{intl.formatMessage(i18n.helpDesc)}
|
||||
</CardDescription>
|
||||
<CardDescription>{intl.formatMessage(i18n.helpDesc)}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="pt-4 px-4">
|
||||
<div className="flex space-x-4">
|
||||
@@ -452,9 +521,7 @@ export default function AppSettingsSection({ scrollToSection }: AppSettingsSecti
|
||||
<Card className="rounded-lg">
|
||||
<CardHeader className="pb-0">
|
||||
<CardTitle className="mb-1">{intl.formatMessage(i18n.updatesTitle)}</CardTitle>
|
||||
<CardDescription>
|
||||
{intl.formatMessage(i18n.updatesDesc)}
|
||||
</CardDescription>
|
||||
<CardDescription>{intl.formatMessage(i18n.updatesDesc)}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="px-4">
|
||||
<UpdateSection />
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Switch } from '../../ui/switch';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../../ui/card';
|
||||
import { useConfig } from '../../ConfigContext';
|
||||
import { TELEMETRY_UI_ENABLED } from '../../../updates';
|
||||
import TelemetryOptOutModal from '../../TelemetryOptOutModal';
|
||||
import PrivacyInfoModal from '../../onboarding/PrivacyInfoModal';
|
||||
import { toastService } from '../../../toasts';
|
||||
import {
|
||||
setTelemetryEnabled as setAnalyticsTelemetryEnabled,
|
||||
@@ -48,11 +48,7 @@ const i18n = defineMessages({
|
||||
|
||||
const TELEMETRY_CONFIG_KEY = 'GOOSE_TELEMETRY_ENABLED';
|
||||
|
||||
interface TelemetrySettingsProps {
|
||||
isWelcome: boolean;
|
||||
}
|
||||
|
||||
export default function TelemetrySettings({ isWelcome = false }: TelemetrySettingsProps) {
|
||||
export default function TelemetrySettings() {
|
||||
const intl = useIntl();
|
||||
const { read, upsert } = useConfig();
|
||||
const [telemetryEnabled, setTelemetryEnabled] = useState(true);
|
||||
@@ -84,7 +80,7 @@ export default function TelemetrySettings({ isWelcome = false }: TelemetrySettin
|
||||
await upsert(TELEMETRY_CONFIG_KEY, checked, false);
|
||||
setTelemetryEnabled(checked);
|
||||
setAnalyticsTelemetryEnabled(checked);
|
||||
trackTelemetryPreference(checked, isWelcome ? 'onboarding' : 'settings');
|
||||
trackTelemetryPreference(checked, 'settings');
|
||||
} catch (error) {
|
||||
console.error('Failed to update telemetry status:', error);
|
||||
toastService.error({
|
||||
@@ -127,15 +123,13 @@ export default function TelemetrySettings({ isWelcome = false }: TelemetrySettin
|
||||
/>
|
||||
);
|
||||
|
||||
const modal = <TelemetryOptOutModal controlled isOpen={showModal} onClose={handleModalClose} />;
|
||||
const modal = <PrivacyInfoModal isOpen={showModal} onClose={handleModalClose} />;
|
||||
|
||||
const toggleRow = (
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h4 className={isWelcome ? 'text-text-primary text-sm' : 'text-text-primary text-xs'}>
|
||||
{toggleLabel}
|
||||
</h4>
|
||||
<p className={`${isWelcome ? 'text-sm' : 'text-xs'} text-text-secondary max-w-md mt-[2px]`}>
|
||||
<h4 className="text-text-primary text-xs">{toggleLabel}</h4>
|
||||
<p className="text-xs text-text-secondary max-w-md mt-[2px]">
|
||||
{toggleDescription} {learnMoreLink}
|
||||
</p>
|
||||
</div>
|
||||
@@ -143,19 +137,6 @@ export default function TelemetrySettings({ isWelcome = false }: TelemetrySettin
|
||||
</div>
|
||||
);
|
||||
|
||||
if (isWelcome) {
|
||||
return (
|
||||
<>
|
||||
<div className="w-full p-4 sm:p-6 bg-transparent border rounded-xl">
|
||||
<h3 className="font-medium text-text-primary text-sm sm:text-base mb-1">{title}</h3>
|
||||
<p className="text-text-secondary text-sm sm:text-base mb-4">{description}</p>
|
||||
{toggleRow}
|
||||
</div>
|
||||
{modal}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card className="rounded-lg">
|
||||
|
||||
@@ -4334,48 +4334,21 @@
|
||||
"switchModelModal.useOtherProvider": {
|
||||
"defaultMessage": "Use other provider"
|
||||
},
|
||||
"telemetryOptOutModal.collectErrors": {
|
||||
"defaultMessage": "Error types (e.g., \"rate_limit\", \"auth\" - no details)"
|
||||
"telemetryConsentPrompt.description": {
|
||||
"defaultMessage": "Would you like to share anonymous usage data to help improve goose? We never collect your conversations, code, or personal data."
|
||||
},
|
||||
"telemetryOptOutModal.collectExtensions": {
|
||||
"defaultMessage": "Extensions and tool usage counts (names only)"
|
||||
},
|
||||
"telemetryOptOutModal.collectOs": {
|
||||
"defaultMessage": "Operating system, version, and architecture"
|
||||
},
|
||||
"telemetryOptOutModal.collectProvider": {
|
||||
"defaultMessage": "Provider and model used"
|
||||
},
|
||||
"telemetryOptOutModal.collectSession": {
|
||||
"defaultMessage": "Session metrics (duration, interaction count, token usage)"
|
||||
},
|
||||
"telemetryOptOutModal.collectVersion": {
|
||||
"defaultMessage": "goose version and install method"
|
||||
},
|
||||
"telemetryOptOutModal.configError": {
|
||||
"defaultMessage": "Configuration Error"
|
||||
},
|
||||
"telemetryOptOutModal.configErrorMessage": {
|
||||
"defaultMessage": "Failed to check telemetry configuration."
|
||||
},
|
||||
"telemetryOptOutModal.description": {
|
||||
"defaultMessage": "Would you like to help improve goose by sharing anonymous usage data? This helps us understand how goose is used and identify areas for improvement."
|
||||
},
|
||||
"telemetryOptOutModal.heading": {
|
||||
"telemetryConsentPrompt.heading": {
|
||||
"defaultMessage": "Help improve goose"
|
||||
},
|
||||
"telemetryOptOutModal.optIn": {
|
||||
"telemetryConsentPrompt.learnMore": {
|
||||
"defaultMessage": "Learn more"
|
||||
},
|
||||
"telemetryConsentPrompt.optIn": {
|
||||
"defaultMessage": "Yes, share anonymous usage data"
|
||||
},
|
||||
"telemetryOptOutModal.optOut": {
|
||||
"telemetryConsentPrompt.optOut": {
|
||||
"defaultMessage": "No thanks"
|
||||
},
|
||||
"telemetryOptOutModal.privacyNote": {
|
||||
"defaultMessage": "We never collect your conversations, code, tool arguments, error messages, or any personal data. You can change this setting anytime in Settings → App."
|
||||
},
|
||||
"telemetryOptOutModal.whatWeCollect": {
|
||||
"defaultMessage": "What we collect:"
|
||||
},
|
||||
"telemetrySettings.configErrorTitle": {
|
||||
"defaultMessage": "Configuration Error"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user