mirror of
https://github.com/voideditor/void
synced 2026-05-23 17:38:23 +00:00
start adding toggle metrics
This commit is contained in:
parent
ad8fedc307
commit
c6d210902b
6 changed files with 93 additions and 9 deletions
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import React, { useState, useEffect, useCallback } from 'react'
|
||||
import { MCPUserState, RefreshableProviderName, SettingsOfProvider } from '../../../../../../../workbench/contrib/void/common/voidSettingsTypes.js'
|
||||
import { IDisposable } from '../../../../../../../base/common/lifecycle.js'
|
||||
import { DisposableStore, IDisposable } from '../../../../../../../base/common/lifecycle.js'
|
||||
import { VoidSettingsState } from '../../../../../../../workbench/contrib/void/common/voidSettingsService.js'
|
||||
import { ColorScheme } from '../../../../../../../platform/theme/common/theme.js'
|
||||
import { RefreshModelStateOfProvider } from '../../../../../../../workbench/contrib/void/common/refreshModelService.js'
|
||||
|
|
@ -52,6 +52,8 @@ import { ITerminalService } from '../../../../../terminal/browser/terminal.js'
|
|||
import { ISearchService } from '../../../../../../services/search/common/search.js'
|
||||
import { IExtensionManagementService } from '../../../../../../../platform/extensionManagement/common/extensionManagement.js'
|
||||
import { IMCPService } from '../../../../common/mcpService.js';
|
||||
import { IStorageService, StorageScope } from '../../../../../../../platform/storage/common/storage.js'
|
||||
import { OPT_OUT_KEY } from '../../../../common/storageKeys.js'
|
||||
|
||||
|
||||
// normally to do this you'd use a useEffect that calls .onDidChangeState(), but useEffect mounts too late and misses initial state changes
|
||||
|
|
@ -226,6 +228,8 @@ const getReactAccessor = (accessor: ServicesAccessor) => {
|
|||
IExtensionTransferService: accessor.get(IExtensionTransferService),
|
||||
IMCPService: accessor.get(IMCPService),
|
||||
|
||||
IStorageService: accessor.get(IStorageService),
|
||||
|
||||
} as const
|
||||
return reactAccessor
|
||||
}
|
||||
|
|
@ -399,3 +403,28 @@ export const useMCPServiceState = () => {
|
|||
return s
|
||||
}
|
||||
|
||||
|
||||
|
||||
export const useIsOptedOut = () => {
|
||||
const accessor = useAccessor()
|
||||
const storageService = accessor.get('IStorageService')
|
||||
|
||||
const getVal = useCallback(() => {
|
||||
return storageService.getBoolean(OPT_OUT_KEY, StorageScope.APPLICATION, false)
|
||||
}, [storageService])
|
||||
|
||||
const [s, ss] = useState(getVal())
|
||||
|
||||
useEffect(() => {
|
||||
const disposables = new DisposableStore();
|
||||
const d = storageService.onDidChangeValue(StorageScope.APPLICATION, OPT_OUT_KEY, disposables)(e => {
|
||||
console.log('VALUE!!!!!!!!', e.target, getVal())
|
||||
ss(getVal())
|
||||
|
||||
})
|
||||
disposables.add(d)
|
||||
return () => disposables.clear()
|
||||
}, [storageService, getVal])
|
||||
|
||||
return s
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import React, { useCallback, useEffect, useMemo, useState, useRef } from 'react'
|
|||
import { ProviderName, SettingName, displayInfoOfSettingName, providerNames, VoidStatefulModelInfo, customSettingNamesOfProvider, RefreshableProviderName, refreshableProviderNames, displayInfoOfProviderName, nonlocalProviderNames, localProviderNames, GlobalSettingName, featureNames, displayInfoOfFeatureName, isProviderNameDisabled, FeatureName, hasDownloadButtonsOnModelsProviderNames, subTextMdOfProviderName } from '../../../../common/voidSettingsTypes.js'
|
||||
import ErrorBoundary from '../sidebar-tsx/ErrorBoundary.js'
|
||||
import { VoidButtonBgDarken, VoidCustomDropdownBox, VoidInputBox2, VoidSimpleInputBox, VoidSwitch } from '../util/inputs.js'
|
||||
import { useAccessor, useIsDark, useRefreshModelListener, useRefreshModelState, useSettingsState } from '../util/services.js'
|
||||
import { useAccessor, useIsDark, useIsOptedOut, useRefreshModelListener, useRefreshModelState, useSettingsState } from '../util/services.js'
|
||||
import { X, RefreshCw, Loader2, Check, Asterisk, Plus } from 'lucide-react'
|
||||
import { URI } from '../../../../../../../base/common/uri.js'
|
||||
import { ModelDropdown } from './ModelDropdown.js'
|
||||
|
|
@ -21,6 +21,8 @@ import { getModelCapabilities, modelOverrideKeys, ModelOverrides } from '../../.
|
|||
import { TransferEditorType, TransferFilesInfo } from '../../../extensionTransferTypes.js';
|
||||
import { MCPServer } from '../../../../common/mcpServiceTypes.js';
|
||||
import { useMCPServiceState } from '../util/services.js';
|
||||
import { OPT_OUT_KEY } from '../../../../common/storageKeys.js';
|
||||
import { StorageScope, StorageTarget } from '../../../../../../../platform/storage/common/storage.js';
|
||||
|
||||
type Tab =
|
||||
| 'models'
|
||||
|
|
@ -215,10 +217,10 @@ const SimpleModelSettingsDialog = ({
|
|||
if (!isOpen || !modelInfo) return null;
|
||||
|
||||
const { modelName, providerName, type } = modelInfo;
|
||||
const accessor = useAccessor();
|
||||
const settingsState = useSettingsState();
|
||||
const accessor = useAccessor()
|
||||
const settingsState = useSettingsState()
|
||||
const mouseDownInsideModal = useRef(false); // Ref to track mousedown origin
|
||||
const settingsStateService = accessor.get('IVoidSettingsService');
|
||||
const settingsStateService = accessor.get('IVoidSettingsService')
|
||||
|
||||
// current overrides and defaults
|
||||
const defaultModelCapabilities = getModelCapabilities(providerName, modelName, undefined);
|
||||
|
|
@ -1052,6 +1054,8 @@ export const Settings = () => {
|
|||
const chatThreadsService = accessor.get('IChatThreadService')
|
||||
const notificationService = accessor.get('INotificationService')
|
||||
const mcpService = accessor.get('IMCPService')
|
||||
const storageService = accessor.get('IStorageService')
|
||||
const isOptedOut = useIsOptedOut()
|
||||
|
||||
const onDownload = (t: 'Chats' | 'Settings') => {
|
||||
let dataStr: string
|
||||
|
|
@ -1387,6 +1391,29 @@ export const Settings = () => {
|
|||
|
||||
{/* General section */}
|
||||
<div className={`${shouldShowTab('general') ? `` : 'hidden'} flex flex-col gap-12`}>
|
||||
{/* Telemetry section */}
|
||||
<div className='w-full mt-8'>
|
||||
<h4 className={`text-base`}>Telemetry</h4>
|
||||
<div className='text-sm italic text-void-fg-3 mt-1'>Control what usage data Void collects.</div>
|
||||
|
||||
<div className='my-2'>
|
||||
{/* Disable All Telemetry Switch */}
|
||||
<ErrorBoundary>
|
||||
<div className='flex items-center gap-x-2 my-2'>
|
||||
<VoidSwitch
|
||||
size='xs'
|
||||
value={isOptedOut}
|
||||
onChange={(newVal) => storageService.store(OPT_OUT_KEY, newVal, StorageScope.APPLICATION, StorageTarget.MACHINE)}
|
||||
/>
|
||||
<span className='text-void-fg-3 text-xs pointer-events-none'>{storageService.getBoolean(OPT_OUT_KEY, StorageScope.APPLICATION, false) ? 'Disabled' : 'Enabled'}</span>
|
||||
</div>
|
||||
<div className='text-xs text-void-fg-3 mt-1 max-w-[500px]'>
|
||||
Void only tracks basic usage like whether you've opened the app today so we can understand usage. Opting out is optional (requires a restart), but without this we cannot see how many people are using Void. Regardless of this setting, Void never sees your code, messages, or API keys.
|
||||
</div>
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* One-Click Switch section */}
|
||||
<div>
|
||||
<ErrorBoundary>
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import { INotificationService } from '../../../../platform/notification/common/n
|
|||
export interface IMetricsService {
|
||||
readonly _serviceBrand: undefined;
|
||||
capture(event: string, params: Record<string, any>): void;
|
||||
setOptOut(val: boolean): void;
|
||||
getDebuggingProperties(): Promise<object>;
|
||||
}
|
||||
|
||||
|
|
@ -38,6 +39,11 @@ export class MetricsService implements IMetricsService {
|
|||
this.metricsService.capture(...params);
|
||||
}
|
||||
|
||||
setOptOut(...params: Parameters<IMetricsService['setOptOut']>) {
|
||||
this.metricsService.setOptOut(...params);
|
||||
}
|
||||
|
||||
|
||||
// anything transmitted over a channel must be async even if it looks like it doesn't have to be
|
||||
async getDebuggingProperties(): Promise<object> {
|
||||
return this.metricsService.getDebuggingProperties()
|
||||
|
|
|
|||
|
|
@ -17,3 +17,7 @@ export const VOID_SETTINGS_STORAGE_KEY = 'void.settingsServiceStorageII'
|
|||
|
||||
// 1.0.3
|
||||
export const THREAD_STORAGE_KEY = 'void.chatThreadStorageII'
|
||||
|
||||
|
||||
|
||||
export const OPT_OUT_KEY = 'void.app.optOutAll'
|
||||
|
|
|
|||
|
|
@ -231,12 +231,9 @@ export const displayInfoOfSettingName = (providerName: ProviderName, settingName
|
|||
}
|
||||
|
||||
throw new Error(`displayInfo: Unknown setting name: "${settingName}"`)
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
const defaultCustomSettings: Record<CustomSettingName, undefined> = {
|
||||
apiKey: undefined,
|
||||
endpoint: undefined,
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import { IApplicationStorageMainService } from '../../../../platform/storage/ele
|
|||
|
||||
import { IMetricsService } from '../common/metricsService.js';
|
||||
import { PostHog } from 'posthog-node'
|
||||
import { OPT_OUT_KEY } from '../common/storageKeys.js';
|
||||
|
||||
|
||||
const os = isWindows ? 'windows' : isMacintosh ? 'mac' : isLinux ? 'linux' : null
|
||||
|
|
@ -29,6 +30,8 @@ const osInfo = _getOSInfo()
|
|||
|
||||
// we'd like to use devDeviceId on telemetryService, but that gets sanitized by the time it gets here as 'someValue.devDeviceId'
|
||||
|
||||
|
||||
|
||||
export class MetricsMainService extends Disposable implements IMetricsService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
|
|
@ -119,7 +122,17 @@ export class MetricsMainService extends Disposable implements IMetricsService {
|
|||
distinctId: this.distinctId,
|
||||
properties: this._initProperties,
|
||||
}
|
||||
this.client.identify(identifyMessage)
|
||||
|
||||
const didOptOut = this._appStorage.get(OPT_OUT_KEY, StorageScope.APPLICATION) !== undefined
|
||||
|
||||
if (didOptOut) {
|
||||
this.client.optOut()
|
||||
}
|
||||
else {
|
||||
this.client.identify(identifyMessage)
|
||||
this.client.optIn()
|
||||
}
|
||||
|
||||
|
||||
console.log('Void posthog metrics info:', JSON.stringify(identifyMessage, null, 2))
|
||||
}
|
||||
|
|
@ -131,6 +144,14 @@ export class MetricsMainService extends Disposable implements IMetricsService {
|
|||
this.client.capture(capture)
|
||||
}
|
||||
|
||||
setOptOut: IMetricsService['setOptOut'] = (newVal: boolean) => {
|
||||
if (newVal) {
|
||||
this._appStorage.store(OPT_OUT_KEY, 'true', StorageScope.APPLICATION, StorageTarget.MACHINE)
|
||||
}
|
||||
else {
|
||||
this._appStorage.remove(OPT_OUT_KEY, StorageScope.APPLICATION)
|
||||
}
|
||||
}
|
||||
|
||||
async getDebuggingProperties() {
|
||||
return this._initProperties
|
||||
|
|
|
|||
Loading…
Reference in a new issue