refactor(ocr): improve type safety and config handling in useOcrProvider

- Replace dynamic provider lookup with type-safe registry pattern
- Add separate config management for each provider type
- Remove unused imports and simplify provider fallback logic
This commit is contained in:
icarus 2025-10-20 03:06:11 +08:00
parent 94ed39ab27
commit a34426d431
2 changed files with 94 additions and 26 deletions

View File

@ -1,35 +1,106 @@
import { usePreference } from '@data/hooks/usePreference'
import { loggerService } from '@logger'
import { updateOcrProviderConfig } from '@renderer/store/ocr'
import type { OcrProviderConfig } from '@renderer/types'
import type {
BuiltinOcrProviderId,
OcrOvConfig,
OcrOvProvider,
OcrPpocrConfig,
OcrPpocrProvider,
OcrSystemConfig,
OcrSystemProvider,
OcrTesseractConfig,
OcrTesseractProvider
} from '@renderer/types'
import { BUILTIN_OCR_PROVIDERS_MAP } from '@shared/config/ocr'
import { useTranslation } from 'react-i18next'
import { useDispatch } from 'react-redux'
import { useOcrProviders } from './useOcrProviders'
import { merge } from 'lodash'
import { useCallback, useMemo } from 'react'
const logger = loggerService.withContext('useOcrProvider')
export const useOcrProvider = (id: string) => {
const { t } = useTranslation()
const dispatch = useDispatch()
const { providers } = useOcrProviders()
let provider = providers.find((p) => p.id === id)
const PROVIDER_REGISTRY = {
ovocr: null as unknown as OcrOvProvider,
paddleocr: null as unknown as OcrPpocrProvider,
system: null as unknown as OcrSystemProvider,
tesseract: null as unknown as OcrTesseractProvider
}
// safely fallback
if (!provider) {
logger.error(`Ocr Provider ${id} not found`)
logger.warn(`Fallback to tesseract`)
window.toast.error(t('ocr.error.provider.not_found'))
window.toast.warning(t('ocr.warning.provider.fallback', { name: 'Tesseract' }))
provider = BUILTIN_OCR_PROVIDERS_MAP.tesseract
}
const CONFIG_REGISTRY = {
ovocr: null as unknown as OcrOvConfig,
paddleocr: null as unknown as OcrPpocrConfig,
system: null as unknown as OcrSystemConfig,
tesseract: null as unknown as OcrTesseractConfig
} as const
const updateConfig = (update: Partial<OcrProviderConfig>) => {
dispatch(updateOcrProviderConfig({ id: provider.id, update }))
}
type ProviderMap = typeof PROVIDER_REGISTRY
type ConfigMap = typeof CONFIG_REGISTRY
type TProvider<T extends BuiltinOcrProviderId> = ProviderMap[T]
type TConfig<T extends BuiltinOcrProviderId> = ConfigMap[T]
type UseOcrProviderReturn<T extends BuiltinOcrProviderId> = {
provider: TProvider<T>
config: TConfig<T>
updateConfig: (update: Partial<TConfig<T>>) => void
}
export const useOcrProvider = <T extends BuiltinOcrProviderId>(id: T): UseOcrProviderReturn<T> => {
const provider = useMemo(() => {
switch (id) {
case 'ovocr':
return BUILTIN_OCR_PROVIDERS_MAP.ovocr
case 'paddleocr':
return BUILTIN_OCR_PROVIDERS_MAP.paddleocr
case 'system':
return BUILTIN_OCR_PROVIDERS_MAP.system
case 'tesseract':
return BUILTIN_OCR_PROVIDERS_MAP.tesseract
}
}, [id])
const [ovConfig, setOvConfig] = usePreference('ocr.provider.config.ovocr')
const [ppConfig, setPpConfig] = usePreference('ocr.provider.config.paddleocr')
const [sysConfig, setSysConfig] = usePreference('ocr.provider.config.system')
const [tesConfig, setTesConfig] = usePreference('ocr.provider.config.tesseract')
const config = useMemo(() => {
switch (id) {
case 'ovocr':
return ovConfig
case 'paddleocr':
return ppConfig
case 'system':
return sysConfig
case 'tesseract':
return tesConfig
}
}, [id, ovConfig, ppConfig, sysConfig, tesConfig])
const updateConfig = useCallback(
(update: Partial<TConfig<T>>) => {
switch (id) {
case 'ovocr':
setOvConfig(merge({}, ovConfig, update))
break
case 'paddleocr':
setPpConfig(merge({}, ppConfig, update))
break
case 'system':
setSysConfig(merge({}, sysConfig, update))
break
case 'tesseract':
setTesConfig(merge({}, tesConfig, update))
break
default:
logger.warn(`Unsupported OCR provider id: ${id}`)
}
},
[id, ovConfig, ppConfig, setOvConfig, setPpConfig, setSysConfig, setTesConfig, sysConfig, tesConfig]
)
return {
provider,
config,
updateConfig
}
} as UseOcrProviderReturn<T>
}

View File

@ -1,13 +1,10 @@
import { useQuery } from '@data/hooks/useDataApi'
import { loggerService } from '@logger'
import { getBuiltinOcrProviderLabel } from '@renderer/i18n/label'
import type { OcrProvider } from '@renderer/types'
import { isBuiltinOcrProvider } from '@renderer/types'
import { BUILTIN_OCR_PROVIDERS } from '@shared/config/ocr'
import { useMemo } from 'react'
const logger = loggerService.withContext('useOcrProviders')
export const useOcrProviders = () => {
const { data: validProviderIds, loading, error } = useQuery('/ocr/providers')
const providers = useMemo(