分离设置页面

This commit is contained in:
IndieKKY
2024-10-04 19:14:01 +08:00
parent 46955e0a59
commit e238d83f78
9 changed files with 96 additions and 66 deletions

View File

@@ -1,47 +1,22 @@
import React, {useCallback, useContext, useEffect, useMemo} from 'react' import React, {useCallback, useEffect, useMemo} from 'react'
import 'tippy.js/dist/tippy.css' import 'tippy.js/dist/tippy.css'
import {useAppDispatch, useAppSelector} from './hooks/redux' import {useAppDispatch, useAppSelector} from './hooks/redux'
import {setEnvData, setEnvReady, setFold, setPage, setTempData, setTempReady} from './redux/envReducer' import {setEnvData, setEnvReady, setTempData, setTempReady} from './redux/envReducer'
import Header from './biz/Header'
import Body from './biz/Body'
import useSubtitleService from './hooks/useSubtitleService'
import {cloneDeep} from 'lodash-es' import {cloneDeep} from 'lodash-es'
import {EVENT_EXPAND, MESSAGE_TO_INJECT_FOLD, PAGE_MAIN, PAGE_SETTINGS, STORAGE_ENV, STORAGE_TEMP} from './const' import {STORAGE_ENV, STORAGE_TEMP} from './const'
import {EventBusContext} from './Router'
import useTranslateService from './hooks/useTranslateService'
import Settings from './biz/Settings' import Settings from './biz/Settings'
import {handleJson} from '@kky002/kky-util' import {handleJson} from '@kky002/kky-util'
import {useLocalStorage} from '@kky002/kky-hooks' import {useLocalStorage} from '@kky002/kky-hooks'
import {Toaster} from 'react-hot-toast' import {Toaster} from 'react-hot-toast'
import {setTheme} from './util/biz_util' import {setTheme} from './util/biz_util'
import useSearchService from './hooks/useSearchService'
import useMessage from './messaging/useMessage'
import useMessagingService from './hooks/useMessagingService' import useMessagingService from './hooks/useMessagingService'
import MainPage from './pages/MainPage'
function App() { function App() {
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const envData = useAppSelector(state => state.env.envData) const envData = useAppSelector(state => state.env.envData)
const tempData = useAppSelector(state => state.env.tempData) const tempData = useAppSelector(state => state.env.tempData)
const fold = useAppSelector(state => state.env.fold) const path = useAppSelector(state => state.env.path)
const eventBus = useContext(EventBusContext)
const page = useAppSelector(state => state.env.page)
const totalHeight = useAppSelector(state => state.env.totalHeight)
const {sendInject} = useMessage()
const foldCallback = useCallback(() => {
dispatch(setFold(!fold))
dispatch(setPage(PAGE_MAIN))
sendInject(MESSAGE_TO_INJECT_FOLD, {fold: !fold})
}, [dispatch, fold])
// handle event
eventBus.useSubscription((event: any) => {
if (event.type === EVENT_EXPAND) {
if (fold) {
foldCallback()
}
}
})
// env数据 // env数据
const savedEnvData = useMemo(() => { const savedEnvData = useMemo(() => {
@@ -73,18 +48,12 @@ function App() {
}, [envData.theme]) }, [envData.theme])
// services // services
useSubtitleService()
useTranslateService()
useSearchService()
useMessagingService() useMessagingService()
return <div className='select-none w-full' style={{ return <div>
height: fold?undefined:`${totalHeight}px`, <Toaster position={path === 'app'?'bottom-center':'top-center'}/>
}}> {path === 'app' && <MainPage/>}
<Header foldCallback={foldCallback}/> {path === 'options' && <Settings/>}
{!fold && page === PAGE_MAIN && <Body/>}
{!fold && page === PAGE_SETTINGS && <Settings/>}
<Toaster position='bottom-center'/>
</div> </div>
} }

View File

@@ -1,15 +1,19 @@
import App from './App' import App from './App'
import {useEventEmitter} from 'ahooks' import {useEventEmitter} from 'ahooks'
import React from 'react' import React, { useEffect } from 'react'
import { useAppDispatch } from './hooks/redux'
import { setPath } from './redux/envReducer'
export const EventBusContext = React.createContext<any>(null) export const EventBusContext = React.createContext<any>(null)
const map: { [key: string]: string } = { const map: { [key: string]: string } = {
'/options.html': 'options',
// '/close': 'close', // '/close': 'close',
} }
const Router = () => { const Router = () => {
const path = map[window.location.pathname] ?? 'app' const path = map[window.location.pathname] ?? 'app'
const dispatch = useAppDispatch()
if (path === 'close') { if (path === 'close') {
window.close() window.close()
@@ -18,8 +22,12 @@ const Router = () => {
// 事件总线 // 事件总线
const eventBus = useEventEmitter() const eventBus = useEventEmitter()
useEffect(() => {
dispatch(setPath(path as 'app' | 'options'))
}, [dispatch, path])
return <EventBusContext.Provider value={eventBus}> return <EventBusContext.Provider value={eventBus}>
{path === 'app' && <App/>} {(path === 'app' || path === 'options') && <App/>}
</EventBusContext.Provider> </EventBusContext.Provider>
} }

View File

@@ -2,9 +2,8 @@ import {AiOutlineCloseCircle, BsDashSquare, BsPlusSquare, FaQuestion} from 'reac
import classNames from 'classnames' import classNames from 'classnames'
import Markdown from '../components/Markdown' import Markdown from '../components/Markdown'
import React, {useCallback} from 'react' import React, {useCallback} from 'react'
import {delAskInfo, mergeAskInfo, setPage} from '../redux/envReducer' import {delAskInfo, mergeAskInfo} from '../redux/envReducer'
import {useAppDispatch, useAppSelector} from '../hooks/redux' import {useAppDispatch, useAppSelector} from '../hooks/redux'
import {PAGE_SETTINGS} from '../const'
import toast from 'react-hot-toast' import toast from 'react-hot-toast'
import useTranslate from '../hooks/useTranslate' import useTranslate from '../hooks/useTranslate'
@@ -25,7 +24,7 @@ const Ask = (props: {
addAskTask(ask.id, segments[0], ask.question).catch(console.error) addAskTask(ask.id, segments[0], ask.question).catch(console.error)
} }
} else { } else {
dispatch(setPage(PAGE_SETTINGS)) chrome.runtime.openOptionsPage()
toast.error('需要先设置ApiKey!') toast.error('需要先设置ApiKey!')
} }
}, [addAskTask, ask.id, ask.question, dispatch, envData.aiType, envData.apiKey, envData.geminiApiKey, segments]) }, [addAskTask, ask.id, ask.question, dispatch, envData.aiType, envData.apiKey, envData.geminiApiKey, segments])

View File

@@ -7,7 +7,6 @@ import {
setCheckAutoScroll, setCheckAutoScroll,
setFoldAll, setFoldAll,
setNeedScroll, setNeedScroll,
setPage,
setSearchText, setSearchText,
setSegmentFold, setSegmentFold,
setTempData setTempData
@@ -104,7 +103,7 @@ const Body = () => {
const onSummarizeAll = useCallback(() => { const onSummarizeAll = useCallback(() => {
const apiKey = envData.aiType === 'gemini'?envData.geminiApiKey:envData.apiKey const apiKey = envData.aiType === 'gemini'?envData.geminiApiKey:envData.apiKey
if (!apiKey) { if (!apiKey) {
dispatch(setPage(PAGE_SETTINGS)) chrome.runtime.openOptionsPage()
toast.error('需要先设置ApiKey!') toast.error('需要先设置ApiKey!')
return return
} }
@@ -148,7 +147,7 @@ const Body = () => {
if (apiKey) { if (apiKey) {
dispatch(setAutoTranslate(!autoTranslate)) dispatch(setAutoTranslate(!autoTranslate))
} else { } else {
dispatch(setPage(PAGE_SETTINGS)) chrome.runtime.openOptionsPage()
toast.error('需要先设置ApiKey!') toast.error('需要先设置ApiKey!')
} }
}, [autoTranslate, dispatch, envData.aiType, envData.apiKey, envData.geminiApiKey]) }, [autoTranslate, dispatch, envData.aiType, envData.apiKey, envData.geminiApiKey])
@@ -197,7 +196,7 @@ const Body = () => {
})) }))
} }
} else { } else {
dispatch(setPage(PAGE_SETTINGS)) chrome.runtime.openOptionsPage()
toast.error('需要先设置ApiKey!') toast.error('需要先设置ApiKey!')
} }
} }

View File

@@ -11,9 +11,9 @@ import {
import Popover from '../components/Popover' import Popover from '../components/Popover'
import {Placement} from '@popperjs/core/lib/enums' import {Placement} from '@popperjs/core/lib/enums'
import {useAppDispatch, useAppSelector} from '../hooks/redux' import {useAppDispatch, useAppSelector} from '../hooks/redux'
import {setEnvData, setPage, setTempData} from '../redux/envReducer' import {setEnvData, setTempData} from '../redux/envReducer'
import {EventBusContext} from '../Router' import {EventBusContext} from '../Router'
import {EVENT_EXPAND, MESSAGE_TO_INJECT_DOWNLOAD_AUDIO, PAGE_SETTINGS} from '../const' import {EVENT_EXPAND, MESSAGE_TO_INJECT_DOWNLOAD_AUDIO} from '../const'
import {formatSrtTime, formatTime, formatVttTime} from '../util/util' import {formatSrtTime, formatTime, formatVttTime} from '../util/util'
import {downloadText, openUrl} from '@kky002/kky-util' import {downloadText, openUrl} from '@kky002/kky-util'
import toast from 'react-hot-toast' import toast from 'react-hot-toast'
@@ -288,7 +288,7 @@ const MoreBtn = (props: Props) => {
{/* </li> */} {/* </li> */}
<li className='hover:bg-accent'> <li className='hover:bg-accent'>
<a className='flex items-center' onClick={(e) => { <a className='flex items-center' onClick={(e) => {
dispatch(setPage(PAGE_SETTINGS)) chrome.runtime.openOptionsPage()
setMoreVisible(false) setMoreVisible(false)
e.preventDefault() e.preventDefault()
e.stopPropagation() e.stopPropagation()

View File

@@ -1,6 +1,6 @@
import React, {MutableRefObject, useCallback, useEffect, useMemo, useRef} from 'react' import React, {MutableRefObject, useCallback, useEffect, useMemo, useRef} from 'react'
import {useAppDispatch, useAppSelector} from '../hooks/redux' import {useAppDispatch, useAppSelector} from '../hooks/redux'
import {setFloatKeyPointsSegIdx, setPage, setSegmentFold, setTempData} from '../redux/envReducer' import {setFloatKeyPointsSegIdx, setSegmentFold, setTempData} from '../redux/envReducer'
import classNames from 'classnames' import classNames from 'classnames'
import {FaClipboardList} from 'react-icons/fa' import {FaClipboardList} from 'react-icons/fa'
import {PAGE_MAIN, PAGE_SETTINGS, SUMMARIZE_THRESHOLD, SUMMARIZE_TYPES} from '../const' import {PAGE_MAIN, PAGE_SETTINGS, SUMMARIZE_THRESHOLD, SUMMARIZE_TYPES} from '../const'
@@ -76,7 +76,7 @@ const Summarize = (props: {
if (apiKey) { if (apiKey) {
addSummarizeTask(curSummaryType, segment).catch(console.error) addSummarizeTask(curSummaryType, segment).catch(console.error)
} else { } else {
dispatch(setPage(PAGE_SETTINGS)) chrome.runtime.openOptionsPage()
toast.error('需要先设置ApiKey!') toast.error('需要先设置ApiKey!')
} }
}, [addSummarizeTask, curSummaryType, dispatch, envData.aiType, envData.apiKey, envData.geminiApiKey, segment]) }, [addSummarizeTask, curSummaryType, dispatch, envData.aiType, envData.apiKey, envData.geminiApiKey, segment])
@@ -145,7 +145,6 @@ const SegmentCard = (props: {
const summarizeEnable = useAppSelector(state => state.env.envData.summarizeEnable) const summarizeEnable = useAppSelector(state => state.env.envData.summarizeEnable)
const summarizeFloat = useAppSelector(state => state.env.envData.summarizeFloat) const summarizeFloat = useAppSelector(state => state.env.envData.summarizeFloat)
const fold = useAppSelector(state => state.env.fold) const fold = useAppSelector(state => state.env.fold)
const page = useAppSelector(state => state.env.page)
const compact = useAppSelector(state => state.env.tempData.compact) const compact = useAppSelector(state => state.env.tempData.compact)
const floatKeyPointsSegIdx = useAppSelector(state => state.env.floatKeyPointsSegIdx) const floatKeyPointsSegIdx = useAppSelector(state => state.env.floatKeyPointsSegIdx)
const showCurrent = useMemo(() => curIdx != null && segment.startIdx <= curIdx && curIdx <= segment.endIdx, [curIdx, segment.endIdx, segment.startIdx]) const showCurrent = useMemo(() => curIdx != null && segment.startIdx <= curIdx && curIdx <= segment.endIdx, [curIdx, segment.endIdx, segment.startIdx])
@@ -168,7 +167,7 @@ const SegmentCard = (props: {
// 检测设置floatKeyPointsSegIdx // 检测设置floatKeyPointsSegIdx
useEffect(() => { useEffect(() => {
if (summarizeFloat) { // 已启用 if (summarizeFloat) { // 已启用
if (!fold && page === PAGE_MAIN && showCurrent) { // 当前Card有控制权 if (!fold && showCurrent) { // 当前Card有控制权
if (!inViewport && (summary != null) && !isSummaryEmpty(summary)) { if (!inViewport && (summary != null) && !isSummaryEmpty(summary)) {
dispatch(setFloatKeyPointsSegIdx(segment.startIdx)) dispatch(setFloatKeyPointsSegIdx(segment.startIdx))
} else { } else {
@@ -176,7 +175,7 @@ const SegmentCard = (props: {
} }
} }
} }
}, [dispatch, fold, inViewport, page, segment.startIdx, showCurrent, summarizeFloat, summary]) }, [dispatch, fold, inViewport, segment.startIdx, showCurrent, summarizeFloat, summary])
const onSelBrief = useCallback(() => { const onSelBrief = useCallback(() => {
dispatch(setTempData({ dispatch(setTempData({

View File

@@ -1,5 +1,5 @@
import React, {PropsWithChildren, useCallback, useMemo, useState} from 'react' import React, {PropsWithChildren, useCallback, useMemo, useState} from 'react'
import {setEnvData, setPage, setTempData} from '../redux/envReducer' import {setEnvData} from '../redux/envReducer'
import {useAppDispatch, useAppSelector} from '../hooks/redux' import {useAppDispatch, useAppSelector} from '../hooks/redux'
import { import {
ASK_ENABLED_DEFAULT, ASK_ENABLED_DEFAULT,
@@ -138,13 +138,16 @@ const Settings = () => {
cnSearchEnabled: cnSearchEnabledValue, cnSearchEnabled: cnSearchEnabledValue,
askEnabled: askEnabledValue, askEnabled: askEnabledValue,
})) }))
dispatch(setPage(PAGE_MAIN))
toast.success('保存成功') toast.success('保存成功')
// 3秒后关闭
setTimeout(() => {
window.close()
}, 3000)
}, [dispatch, autoExpandValue, aiTypeValue, apiKeyValue, serverUrlValue, modelValue, customModelValue, customModelTokensValue, geminiApiKeyValue, translateEnableValue, languageValue, hideOnDisableAutoTranslateValue, themeValue, transDisplayValue, summarizeEnableValue, summarizeFloatValue, summarizeLanguageValue, wordsValue, fetchAmountValue, fontSizeValue, promptsValue, searchEnabledValue, cnSearchEnabledValue, askEnabledValue]) }, [dispatch, autoExpandValue, aiTypeValue, apiKeyValue, serverUrlValue, modelValue, customModelValue, customModelTokensValue, geminiApiKeyValue, translateEnableValue, languageValue, hideOnDisableAutoTranslateValue, themeValue, transDisplayValue, summarizeEnableValue, summarizeFloatValue, summarizeLanguageValue, wordsValue, fetchAmountValue, fontSizeValue, promptsValue, searchEnabledValue, cnSearchEnabledValue, askEnabledValue])
const onCancel = useCallback(() => { const onCancel = useCallback(() => {
dispatch(setPage(PAGE_MAIN)) window.close()
}, [dispatch]) }, [])
const onFetchAmountChange = useCallback((e: any) => { const onFetchAmountChange = useCallback((e: any) => {
setFetchAmountValue(parseInt(e.target.value)) setFetchAmountValue(parseInt(e.target.value))

52
src/pages/MainPage.tsx Normal file
View File

@@ -0,0 +1,52 @@
import React, {useCallback, useContext, useEffect, useMemo} from 'react'
import {useAppDispatch, useAppSelector} from '../hooks/redux'
import {setEnvData, setEnvReady, setFold, setTempData, setTempReady} from '../redux/envReducer'
import Header from '../biz/Header'
import Body from '../biz/Body'
import useSubtitleService from '../hooks/useSubtitleService'
import {cloneDeep} from 'lodash-es'
import {EVENT_EXPAND, MESSAGE_TO_INJECT_FOLD, PAGE_MAIN, PAGE_SETTINGS, STORAGE_ENV, STORAGE_TEMP} from '../const'
import {EventBusContext} from '../Router'
import useTranslateService from '../hooks/useTranslateService'
import {handleJson} from '@kky002/kky-util'
import {useLocalStorage} from '@kky002/kky-hooks'
import {Toaster} from 'react-hot-toast'
import {setTheme} from '../util/biz_util'
import useSearchService from '../hooks/useSearchService'
import useMessage from '../messaging/useMessage'
import useMessagingService from '../hooks/useMessagingService'
function App() {
const dispatch = useAppDispatch()
const fold = useAppSelector(state => state.env.fold)
const eventBus = useContext(EventBusContext)
const totalHeight = useAppSelector(state => state.env.totalHeight)
const {sendInject} = useMessage()
const foldCallback = useCallback(() => {
dispatch(setFold(!fold))
sendInject(MESSAGE_TO_INJECT_FOLD, {fold: !fold})
}, [dispatch, fold])
// handle event
eventBus.useSubscription((event: any) => {
if (event.type === EVENT_EXPAND) {
if (fold) {
foldCallback()
}
}
})
useSubtitleService()
useTranslateService()
useSearchService()
return <div className='select-none w-full' style={{
height: fold?undefined:`${totalHeight}px`,
}}>
<Header foldCallback={foldCallback}/>
{!fold && <Body/>}
</div>
}
export default App

View File

@@ -9,9 +9,10 @@ interface EnvState {
tempData: TempData tempData: TempData
tempReady: boolean tempReady: boolean
path?: 'app' | 'options'
fold: boolean // fold app fold: boolean // fold app
foldAll?: boolean // fold all segments foldAll?: boolean // fold all segments
page?: string
autoTranslate?: boolean autoTranslate?: boolean
autoScroll?: boolean autoScroll?: boolean
checkAutoScroll?: boolean checkAutoScroll?: boolean
@@ -103,6 +104,9 @@ export const slice = createSlice({
setReviewAction: (state, action: PayloadAction<boolean>) => { setReviewAction: (state, action: PayloadAction<boolean>) => {
state.reviewAction = action.payload state.reviewAction = action.payload
}, },
setPath: (state, action: PayloadAction<'app' | 'options' | undefined>) => {
state.path = action.payload
},
setTempReady: (state) => { setTempReady: (state) => {
state.tempReady = true state.tempReady = true
}, },
@@ -118,9 +122,6 @@ export const slice = createSlice({
setFoldAll: (state, action: PayloadAction<boolean>) => { setFoldAll: (state, action: PayloadAction<boolean>) => {
state.foldAll = action.payload state.foldAll = action.payload
}, },
setPage: (state, action: PayloadAction<string | undefined>) => {
state.page = action.payload
},
setTotalHeight: (state, action: PayloadAction<number>) => { setTotalHeight: (state, action: PayloadAction<number>) => {
state.totalHeight = action.payload state.totalHeight = action.payload
}, },
@@ -298,6 +299,7 @@ export const slice = createSlice({
}) })
export const { export const {
setPath,
setUrl, setUrl,
setTempReady, setTempReady,
setTempData, setTempData,
@@ -314,7 +316,6 @@ export const {
setTitle, setTitle,
setSegments, setSegments,
setLastSummarizeTime, setLastSummarizeTime,
setPage,
setLastTransTime, setLastTransTime,
clearTransResults, clearTransResults,
addTransResults, addTransResults,