返回 oh-my-ppt
useModelAction.ts
根目录 / src / renderer / src / hooks / useModelAction.ts
1 import { useCallback, useEffect, useMemo, useState } from 'react'
2 import { useNavigate } from 'react-router-dom'
3 import { useT } from '@renderer/i18n'
4 import { useSettingsStore, useToastStore } from '@renderer/store'
5 import type { ModelConfig } from '@renderer/lib/ipc'
6
7 export interface ModelActionState {
8 modelConfigs: ModelConfig[]
9 selectedModelConfigId: string
10 activatingModelConfigId: string | null
11 hasMultipleModelConfigs: boolean
12 currentModelConfig: ModelConfig | null
13 ensureModelActive: (modelConfigId?: string) => Promise<string | null>
14 }
15
16 export function useModelAction(): ModelActionState {
17 const t = useT()
18 const navigate = useNavigate()
19 const { error: toastError, warning: toastWarning } = useToastStore()
20 const { modelConfigs, fetchSettings } = useSettingsStore()
21 const [selectedModelConfigId, setSelectedModelConfigId] = useState('')
22 const [activatingModelConfigId, setActivatingModelConfigId] = useState<string | null>(null)
23
24 useEffect(() => {
25 void fetchSettings()
26 }, [fetchSettings])
27
28 useEffect(() => {
29 setSelectedModelConfigId((current) => {
30 if (current && modelConfigs.some((config) => config.id === current)) return current
31 return modelConfigs.find((config) => config.active)?.id || modelConfigs[0]?.id || ''
32 })
33 }, [modelConfigs])
34
35 const ensureModelActive = useCallback(
36 async (modelConfigId = selectedModelConfigId): Promise<string | null> => {
37 const warnModelSettingsRequired = (): void => {
38 toastWarning(t('settings.modelSettingsRequiredTitle'), {
39 description: t('settings.modelSettingsRequiredDescription'),
40 action: {
41 label: t('home.goToSettings'),
42 onClick: () => navigate('/settings')
43 }
44 })
45 }
46 if (activatingModelConfigId) return null
47
48 let latestConfigs = useSettingsStore.getState().modelConfigs
49 if (latestConfigs.length === 0) {
50 await fetchSettings()
51 latestConfigs = useSettingsStore.getState().modelConfigs
52 }
53
54 const nextModelConfigId =
55 modelConfigId ||
56 selectedModelConfigId ||
57 latestConfigs.find((config) => config.active)?.id ||
58 latestConfigs[0]?.id ||
59 ''
60 if (!nextModelConfigId) {
61 warnModelSettingsRequired()
62 return null
63 }
64
65 const selected =
66 latestConfigs.find((config) => config.id === nextModelConfigId) ||
67 modelConfigs.find((config) => config.id === nextModelConfigId)
68 if (!selected) {
69 warnModelSettingsRequired()
70 return null
71 }
72 if (!selected.model.trim() || !selected.apiKey.trim()) {
73 warnModelSettingsRequired()
74 return null
75 }
76
77 const previousModelConfigId = selectedModelConfigId
78 setSelectedModelConfigId(nextModelConfigId)
79 setActivatingModelConfigId(nextModelConfigId)
80 try {
81 return nextModelConfigId
82 } catch (error) {
83 setSelectedModelConfigId(previousModelConfigId)
84 toastError(t('settings.activateModelFailed'), {
85 description: error instanceof Error ? error.message : String(error)
86 })
87 return null
88 } finally {
89 setActivatingModelConfigId(null)
90 }
91 },
92 [
93 activatingModelConfigId,
94 fetchSettings,
95 modelConfigs,
96 navigate,
97 selectedModelConfigId,
98 t,
99 toastError,
100 toastWarning
101 ]
102 )
103
104 const currentModelConfig = useMemo(
105 () => modelConfigs.find((config) => config.id === selectedModelConfigId) || null,
106 [modelConfigs, selectedModelConfigId]
107 )
108
109 return {
110 modelConfigs,
111 selectedModelConfigId,
112 activatingModelConfigId,
113 hasMultipleModelConfigs: modelConfigs.length > 1,
114 currentModelConfig,
115 ensureModelActive
116 }
117 }
118
118 lines TYPESCRIPT