feat: google analytics

This commit is contained in:
kangfenmao 2025-02-10 17:37:14 +08:00
parent f9be0e0d26
commit c76f274562
13 changed files with 217 additions and 6 deletions

View File

@ -96,6 +96,7 @@
"@agentic/exa": "^7.3.3", "@agentic/exa": "^7.3.3",
"@agentic/searxng": "^7.3.3", "@agentic/searxng": "^7.3.3",
"@agentic/tavily": "^7.3.3", "@agentic/tavily": "^7.3.3",
"@analytics/google-analytics": "^1.1.0",
"@ant-design/v5-patch-for-react-19": "^1.0.3", "@ant-design/v5-patch-for-react-19": "^1.0.3",
"@anthropic-ai/sdk": "^0.38.0", "@anthropic-ai/sdk": "^0.38.0",
"@electron-toolkit/eslint-config-prettier": "^3.0.0", "@electron-toolkit/eslint-config-prettier": "^3.0.0",
@ -126,6 +127,7 @@
"@types/react-infinite-scroll-component": "^5.0.0", "@types/react-infinite-scroll-component": "^5.0.0",
"@types/tinycolor2": "^1", "@types/tinycolor2": "^1",
"@vitejs/plugin-react": "^4.2.1", "@vitejs/plugin-react": "^4.2.1",
"analytics": "^0.8.16",
"antd": "^5.22.5", "antd": "^5.22.5",
"applescript": "^1.0.0", "applescript": "^1.0.0",
"axios": "^1.7.3", "axios": "^1.7.3",

View File

@ -6,6 +6,7 @@ import i18n from '@renderer/i18n'
import { useAppDispatch } from '@renderer/store' import { useAppDispatch } from '@renderer/store'
import { setAvatar, setFilesPath, setResourcesPath, setUpdateState } from '@renderer/store/runtime' import { setAvatar, setFilesPath, setResourcesPath, setUpdateState } from '@renderer/store/runtime'
import { delay, runAsyncFunction } from '@renderer/utils' import { delay, runAsyncFunction } from '@renderer/utils'
import { disableAnalytics, initAnalytics } from '@renderer/utils/analytics'
import { defaultLanguage } from '@shared/config/constant' import { defaultLanguage } from '@shared/config/constant'
import { useLiveQuery } from 'dexie-react-hooks' import { useLiveQuery } from 'dexie-react-hooks'
import { useEffect } from 'react' import { useEffect } from 'react'
@ -18,7 +19,7 @@ import useUpdateHandler from './useUpdateHandler'
export function useAppInit() { export function useAppInit() {
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const { proxyUrl, language, windowStyle, autoCheckUpdate, proxyMode, customCss } = useSettings() const { proxyUrl, language, windowStyle, autoCheckUpdate, proxyMode, customCss, enableDataCollection } = useSettings()
const { minappShow } = useRuntime() const { minappShow } = useRuntime()
const { setDefaultModel, setTopicNamingModel, setTranslateModel } = useDefaultModel() const { setDefaultModel, setTopicNamingModel, setTranslateModel } = useDefaultModel()
const avatar = useLiveQuery(() => db.settings.get('image://avatar')) const avatar = useLiveQuery(() => db.settings.get('image://avatar'))
@ -103,4 +104,8 @@ export function useAppInit() {
document.head.appendChild(style) document.head.appendChild(style)
} }
}, [customCss]) }, [customCss])
useEffect(() => {
enableDataCollection ? initAnalytics() : disableAnalytics()
}, [enableDataCollection])
} }

View File

@ -1285,6 +1285,10 @@
"back": "Back", "back": "Back",
"forward": "Forward", "forward": "Forward",
"multiple": "Multiple Select" "multiple": "Multiple Select"
},
"privacy": {
"title": "Privacy Settings",
"enable_privacy_mode": "Anonymous reporting of errors and statistics"
} }
}, },
"translate": { "translate": {

View File

@ -1285,6 +1285,10 @@
"back": "戻る", "back": "戻る",
"forward": "進む", "forward": "進む",
"multiple": "複数選択" "multiple": "複数選択"
},
"privacy": {
"title": "プライバシー設定",
"enable_privacy_mode": "匿名エラーレポートとデータ統計の送信"
} }
}, },
"translate": { "translate": {

View File

@ -1285,6 +1285,10 @@
"back": "Назад", "back": "Назад",
"forward": "Вперед", "forward": "Вперед",
"multiple": "Множественный выбор" "multiple": "Множественный выбор"
},
"privacy": {
"title": "Настройки приватности",
"enable_privacy_mode": "Анонимная отправка отчетов об ошибках и статистики"
} }
}, },
"translate": { "translate": {

View File

@ -1285,6 +1285,10 @@
"back": "后退", "back": "后退",
"forward": "前进", "forward": "前进",
"multiple": "多选" "multiple": "多选"
},
"privacy": {
"title": "隐私设置",
"enable_privacy_mode": "匿名发送错误报告和数据统计"
} }
}, },
"translate": { "translate": {

View File

@ -1285,6 +1285,10 @@
"back": "後退", "back": "後退",
"forward": "前進", "forward": "前進",
"multiple": "多選" "multiple": "多選"
},
"privacy": {
"title": "隱私設定",
"enable_privacy_mode": "匿名發送錯誤報告和資料統計"
} }
}, },
"translate": { "translate": {

View File

@ -1,3 +1,5 @@
import './utils/analytics'
import KeyvStorage from '@kangfenmao/keyv-storage' import KeyvStorage from '@kangfenmao/keyv-storage'
import { startAutoSync } from './services/BackupService' import { startAutoSync } from './services/BackupService'

View File

@ -2,7 +2,7 @@ import { useTheme } from '@renderer/context/ThemeProvider'
import { useSettings } from '@renderer/hooks/useSettings' import { useSettings } from '@renderer/hooks/useSettings'
import i18n from '@renderer/i18n' import i18n from '@renderer/i18n'
import { useAppDispatch } from '@renderer/store' import { useAppDispatch } from '@renderer/store'
import { setLanguage } from '@renderer/store/settings' import { setEnableDataCollection, setLanguage } from '@renderer/store/settings'
import { setProxyMode, setProxyUrl as _setProxyUrl } from '@renderer/store/settings' import { setProxyMode, setProxyUrl as _setProxyUrl } from '@renderer/store/settings'
import { LanguageVarious } from '@renderer/types' import { LanguageVarious } from '@renderer/types'
import { isValidProxyUrl } from '@renderer/utils' import { isValidProxyUrl } from '@renderer/utils'
@ -24,7 +24,8 @@ const GeneralSettings: FC = () => {
launchToTray, launchToTray,
trayOnClose, trayOnClose,
tray, tray,
proxyMode: storeProxyMode proxyMode: storeProxyMode,
enableDataCollection
} = useSettings() } = useSettings()
const [proxyUrl, setProxyUrl] = useState<string | undefined>(storeProxyUrl) const [proxyUrl, setProxyUrl] = useState<string | undefined>(storeProxyUrl)
const { theme: themeMode } = useTheme() const { theme: themeMode } = useTheme()
@ -179,6 +180,14 @@ const GeneralSettings: FC = () => {
<Switch checked={trayOnClose} onChange={(checked) => updateTrayOnClose(checked)} /> <Switch checked={trayOnClose} onChange={(checked) => updateTrayOnClose(checked)} />
</SettingRow> </SettingRow>
</SettingGroup> </SettingGroup>
<SettingGroup theme={theme}>
<SettingTitle>{t('settings.privacy.title')}</SettingTitle>
<SettingDivider />
<SettingRow>
<SettingRowTitle>{t('settings.privacy.enable_privacy_mode')}</SettingRowTitle>
<Switch value={enableDataCollection} onChange={(v) => dispatch(setEnableDataCollection(v))} />
</SettingRow>
</SettingGroup>
</SettingContainer> </SettingContainer>
) )
} }

View File

@ -1178,6 +1178,14 @@ const migrateConfig = {
} catch (error) { } catch (error) {
return state return state
} }
},
'90': (state: RootState) => {
try {
state.settings.enableDataCollection = true
return state
} catch (error) {
return state
}
} }
} }

View File

@ -103,6 +103,8 @@ export interface SettingsState {
siyuanRootPath: string | null siyuanRootPath: string | null
maxKeepAliveMinapps: number maxKeepAliveMinapps: number
showOpenedMinappsInSidebar: boolean showOpenedMinappsInSidebar: boolean
// 隐私设置
enableDataCollection: boolean
} }
export type MultiModelMessageStyle = 'horizontal' | 'vertical' | 'fold' | 'grid' export type MultiModelMessageStyle = 'horizontal' | 'vertical' | 'fold' | 'grid'
@ -179,13 +181,13 @@ const initialState: SettingsState = {
joplinToken: '', joplinToken: '',
joplinUrl: '', joplinUrl: '',
defaultObsidianVault: null, defaultObsidianVault: null,
// 思源笔记配置初始值
siyuanApiUrl: null, siyuanApiUrl: null,
siyuanToken: null, siyuanToken: null,
siyuanBoxId: null, siyuanBoxId: null,
siyuanRootPath: null, siyuanRootPath: null,
maxKeepAliveMinapps: 3, maxKeepAliveMinapps: 3,
showOpenedMinappsInSidebar: true showOpenedMinappsInSidebar: true,
enableDataCollection: false
} }
const settingsSlice = createSlice({ const settingsSlice = createSlice({
@ -425,6 +427,9 @@ const settingsSlice = createSlice({
}, },
setShowOpenedMinappsInSidebar: (state, action: PayloadAction<boolean>) => { setShowOpenedMinappsInSidebar: (state, action: PayloadAction<boolean>) => {
state.showOpenedMinappsInSidebar = action.payload state.showOpenedMinappsInSidebar = action.payload
},
setEnableDataCollection: (state, action: PayloadAction<boolean>) => {
state.enableDataCollection = action.payload
} }
} }
}) })
@ -505,7 +510,8 @@ export const {
setSiyuanBoxId, setSiyuanBoxId,
setSiyuanRootPath, setSiyuanRootPath,
setMaxKeepAliveMinapps, setMaxKeepAliveMinapps,
setShowOpenedMinappsInSidebar setShowOpenedMinappsInSidebar,
setEnableDataCollection
} = settingsSlice.actions } = settingsSlice.actions
export default settingsSlice.reducer export default settingsSlice.reducer

View File

@ -0,0 +1,54 @@
import googleAnalytics from '@analytics/google-analytics'
import Analytics from 'analytics'
import { version } from '../../../../package.json'
let analytics: ReturnType<typeof Analytics> | null = null
export function initAnalytics() {
if (analytics) {
return analytics
}
analytics = Analytics({
app: 'cherry-studio',
version,
plugins: [
googleAnalytics({
measurementIds: ['G-YST80FC1ZC']
})
]
})
return analytics
}
export function disableAnalytics() {
analytics = null
}
export function track(eventName: string) {
try {
const instance = initAnalytics()
instance?.track(eventName)
} catch (error) {
console.warn('[Analytics] Failed to track event:', error)
}
}
export function page(pageName: string) {
try {
const instance = initAnalytics()
instance?.page({
title: pageName,
path: pageName
})
} catch (error) {
console.warn('[Analytics] Failed to track page view:', error)
}
}
export default {
track,
page
}

105
yarn.lock
View File

@ -77,6 +77,80 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@analytics/cookie-utils@npm:^0.2.12":
version: 0.2.12
resolution: "@analytics/cookie-utils@npm:0.2.12"
dependencies:
"@analytics/global-storage-utils": "npm:^0.1.7"
checksum: 10c0/b631b5d482b6865984fd72ed24c66c0e3ba90d9aff21f7ec6edee597c60e68d4c34195058efab108167e9e34b2d4a65b452bafd6996e7a942a9e8e165099d767
languageName: node
linkType: hard
"@analytics/core@npm:^0.12.17":
version: 0.12.17
resolution: "@analytics/core@npm:0.12.17"
dependencies:
"@analytics/global-storage-utils": "npm:^0.1.7"
"@analytics/type-utils": "npm:^0.6.2"
analytics-utils: "npm:^1.0.14"
checksum: 10c0/752ccaca1187297fe1665ffcfc23923b28f2a097c2e0c76024ef253e9a848f3f16e430a5155a4bc665931ba84da813ca88ee1634ef8c9491e28975d95ce5a8e1
languageName: node
linkType: hard
"@analytics/global-storage-utils@npm:^0.1.7":
version: 0.1.7
resolution: "@analytics/global-storage-utils@npm:0.1.7"
dependencies:
"@analytics/type-utils": "npm:^0.6.2"
checksum: 10c0/c9f4e412106edc590cf3df73743ae9da5579cac3f452ff80e67a9c205ba6afa58f21804f3ffc4433628faf8d9e1306249cdba951a3947da3e93e3382e772a28b
languageName: node
linkType: hard
"@analytics/google-analytics@npm:^1.1.0":
version: 1.1.0
resolution: "@analytics/google-analytics@npm:1.1.0"
checksum: 10c0/b5adb9741d20c97c22bee10eb01a60658d7564dc6607b93729e908485533597adab3b9816dfdc83cf3d9c9fe5fd5196640a1122fb578c6123dd16926736433d4
languageName: node
linkType: hard
"@analytics/localstorage-utils@npm:^0.1.10":
version: 0.1.10
resolution: "@analytics/localstorage-utils@npm:0.1.10"
dependencies:
"@analytics/global-storage-utils": "npm:^0.1.7"
checksum: 10c0/a540eb8b02f209858f339e7bba328e6076f5e9680b07b58a9e0278e990b3030c94067ba9f8f5501361fbb0b12841b2e3c99f777d18c7606a51a0e3c08208f3a6
languageName: node
linkType: hard
"@analytics/session-storage-utils@npm:^0.0.7":
version: 0.0.7
resolution: "@analytics/session-storage-utils@npm:0.0.7"
dependencies:
"@analytics/global-storage-utils": "npm:^0.1.7"
checksum: 10c0/742aec35eea9cf8f79037408bd3bc256ac3ba009bb11c424bfec910126b7cb3ab8edb8be810ab3bcaead152da6cdb756592b0abfbfbde7e82c420b2e94e03897
languageName: node
linkType: hard
"@analytics/storage-utils@npm:^0.4.2":
version: 0.4.2
resolution: "@analytics/storage-utils@npm:0.4.2"
dependencies:
"@analytics/cookie-utils": "npm:^0.2.12"
"@analytics/global-storage-utils": "npm:^0.1.7"
"@analytics/localstorage-utils": "npm:^0.1.10"
"@analytics/session-storage-utils": "npm:^0.0.7"
"@analytics/type-utils": "npm:^0.6.2"
checksum: 10c0/68e884e2a5356c8c40a76508574603c3923cf58e87309190f71951c6a12123ab8eb2fb125d6cf45efaf662aa02df1ddddecf0e49be261e5cac2c0893e037cc5a
languageName: node
linkType: hard
"@analytics/type-utils@npm:^0.6.2":
version: 0.6.2
resolution: "@analytics/type-utils@npm:0.6.2"
checksum: 10c0/63332155ed4f361a30a240a43dc73d0809f66aa2f484d870c900634ef32d3bf363dccf4961a9592bb0de627da401f88fada6afa3045aa695acfb0acdaea14988
languageName: node
linkType: hard
"@ant-design/colors@npm:^7.0.0, @ant-design/colors@npm:^7.2.0": "@ant-design/colors@npm:^7.0.0, @ant-design/colors@npm:^7.2.0":
version: 7.2.0 version: 7.2.0
resolution: "@ant-design/colors@npm:7.2.0" resolution: "@ant-design/colors@npm:7.2.0"
@ -3831,6 +3905,7 @@ __metadata:
"@agentic/exa": "npm:^7.3.3" "@agentic/exa": "npm:^7.3.3"
"@agentic/searxng": "npm:^7.3.3" "@agentic/searxng": "npm:^7.3.3"
"@agentic/tavily": "npm:^7.3.3" "@agentic/tavily": "npm:^7.3.3"
"@analytics/google-analytics": "npm:^1.1.0"
"@ant-design/v5-patch-for-react-19": "npm:^1.0.3" "@ant-design/v5-patch-for-react-19": "npm:^1.0.3"
"@anthropic-ai/sdk": "npm:^0.38.0" "@anthropic-ai/sdk": "npm:^0.38.0"
"@cherrystudio/embedjs": "npm:^0.1.28" "@cherrystudio/embedjs": "npm:^0.1.28"
@ -3878,6 +3953,7 @@ __metadata:
"@vitejs/plugin-react": "npm:^4.2.1" "@vitejs/plugin-react": "npm:^4.2.1"
"@xyflow/react": "npm:^12.4.4" "@xyflow/react": "npm:^12.4.4"
adm-zip: "npm:^0.5.16" adm-zip: "npm:^0.5.16"
analytics: "npm:^0.8.16"
antd: "npm:^5.22.5" antd: "npm:^5.22.5"
applescript: "npm:^1.0.0" applescript: "npm:^1.0.0"
axios: "npm:^1.7.3" axios: "npm:^1.7.3"
@ -4108,6 +4184,28 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"analytics-utils@npm:^1.0.14":
version: 1.0.14
resolution: "analytics-utils@npm:1.0.14"
dependencies:
"@analytics/type-utils": "npm:^0.6.2"
dlv: "npm:^1.1.3"
peerDependencies:
"@types/dlv": ^1.0.0
checksum: 10c0/8a55d5592f4588d7d0e555b3baeed9dca63d1a6e44f5c128ac1bc32616c4cc23361d69611c0490339bedbca11407bf9e62b3f2fddaf52c66fd82e749ebdfaf48
languageName: node
linkType: hard
"analytics@npm:^0.8.16":
version: 0.8.16
resolution: "analytics@npm:0.8.16"
dependencies:
"@analytics/core": "npm:^0.12.17"
"@analytics/storage-utils": "npm:^0.4.2"
checksum: 10c0/818cdb0b4fa308d501f6db435aed7653b0137387b3c9f5002ebf9c8b7400afdda05538dba76f43c93ce18606f19792e456604e353e196aee0e11a6e40d188155
languageName: node
linkType: hard
"ansi-escapes@npm:^7.0.0": "ansi-escapes@npm:^7.0.0":
version: 7.0.0 version: 7.0.0
resolution: "ansi-escapes@npm:7.0.0" resolution: "ansi-escapes@npm:7.0.0"
@ -6079,6 +6177,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"dlv@npm:^1.1.3":
version: 1.1.3
resolution: "dlv@npm:1.1.3"
checksum: 10c0/03eb4e769f19a027fd5b43b59e8a05e3fd2100ac239ebb0bf9a745de35d449e2f25cfaf3aa3934664551d72856f4ae8b7822016ce5c42c2d27c18ae79429ec42
languageName: node
linkType: hard
"dmg-builder@npm:24.13.3": "dmg-builder@npm:24.13.3":
version: 24.13.3 version: 24.13.3
resolution: "dmg-builder@npm:24.13.3" resolution: "dmg-builder@npm:24.13.3"