分离设置页面

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 {useAppDispatch, useAppSelector} from './hooks/redux'
import {setEnvData, setEnvReady, setFold, setPage, setTempData, setTempReady} from './redux/envReducer'
import Header from './biz/Header'
import Body from './biz/Body'
import useSubtitleService from './hooks/useSubtitleService'
import {setEnvData, setEnvReady, setTempData, setTempReady} from './redux/envReducer'
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 {STORAGE_ENV, STORAGE_TEMP} from './const'
import Settings from './biz/Settings'
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'
import MainPage from './pages/MainPage'
function App() {
const dispatch = useAppDispatch()
const envData = useAppSelector(state => state.env.envData)
const tempData = useAppSelector(state => state.env.tempData)
const fold = useAppSelector(state => state.env.fold)
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()
}
}
})
const path = useAppSelector(state => state.env.path)
// env数据
const savedEnvData = useMemo(() => {
@@ -73,18 +48,12 @@ function App() {
}, [envData.theme])
// services
useSubtitleService()
useTranslateService()
useSearchService()
useMessagingService()
return <div className='select-none w-full' style={{
height: fold?undefined:`${totalHeight}px`,
}}>
<Header foldCallback={foldCallback}/>
{!fold && page === PAGE_MAIN && <Body/>}
{!fold && page === PAGE_SETTINGS && <Settings/>}
<Toaster position='bottom-center'/>
return <div>
<Toaster position={path === 'app'?'bottom-center':'top-center'}/>
{path === 'app' && <MainPage/>}
{path === 'options' && <Settings/>}
</div>
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
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 {
ASK_ENABLED_DEFAULT,
@@ -138,13 +138,16 @@ const Settings = () => {
cnSearchEnabled: cnSearchEnabledValue,
askEnabled: askEnabledValue,
}))
dispatch(setPage(PAGE_MAIN))
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])
const onCancel = useCallback(() => {
dispatch(setPage(PAGE_MAIN))
}, [dispatch])
window.close()
}, [])
const onFetchAmountChange = useCallback((e: any) => {
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
tempReady: boolean
path?: 'app' | 'options'
fold: boolean // fold app
foldAll?: boolean // fold all segments
page?: string
autoTranslate?: boolean
autoScroll?: boolean
checkAutoScroll?: boolean
@@ -103,6 +104,9 @@ export const slice = createSlice({
setReviewAction: (state, action: PayloadAction<boolean>) => {
state.reviewAction = action.payload
},
setPath: (state, action: PayloadAction<'app' | 'options' | undefined>) => {
state.path = action.payload
},
setTempReady: (state) => {
state.tempReady = true
},
@@ -118,9 +122,6 @@ export const slice = createSlice({
setFoldAll: (state, action: PayloadAction<boolean>) => {
state.foldAll = action.payload
},
setPage: (state, action: PayloadAction<string | undefined>) => {
state.page = action.payload
},
setTotalHeight: (state, action: PayloadAction<number>) => {
state.totalHeight = action.payload
},
@@ -298,6 +299,7 @@ export const slice = createSlice({
})
export const {
setPath,
setUrl,
setTempReady,
setTempData,
@@ -314,7 +316,6 @@ export const {
setTitle,
setSegments,
setLastSummarizeTime,
setPage,
setLastTransTime,
clearTransResults,
addTransResults,