diff --git a/src/components/ApiKeyReminder.tsx b/src/components/ApiKeyReminder.tsx new file mode 100644 index 0000000..6cf19c7 --- /dev/null +++ b/src/components/ApiKeyReminder.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { useAppSelector } from '../hooks/redux'; +import { openOptionsPage } from '../utils/chromeUtils'; + +const ApiKeyReminder: React.FC = () => { + const apiKey = useAppSelector(state => state.env.envData.apiKey); + const geminiApiKey = useAppSelector(state => state.env.envData.geminiApiKey); + const aiType = useAppSelector(state => state.env.envData.aiType); + + if ((aiType === 'gemini' && geminiApiKey) || (aiType !== 'gemini' && apiKey)) { + return null; + } + + return ( +
+ 请先设置API密钥以使用总结及翻译功能 + +
+ ); +}; + +export default ApiKeyReminder; \ No newline at end of file diff --git a/src/components/Body.tsx b/src/components/Body.tsx index 3e98c46..7840eb1 100644 --- a/src/components/Body.tsx +++ b/src/components/Body.tsx @@ -1,4 +1,4 @@ -import React, {useCallback, useEffect, useMemo, useRef} from 'react' +import React, { useCallback, useEffect, useMemo, useRef } from 'react' import { addAskInfo, mergeAskInfo, @@ -11,7 +11,7 @@ import { setSegmentFold, setTempData } from '../redux/envReducer' -import {useAppDispatch, useAppSelector} from '../hooks/redux' +import { useAppDispatch, useAppSelector } from '../hooks/redux' import { AiOutlineAim, AiOutlineCloseCircle, @@ -31,13 +31,14 @@ import { SUMMARIZE_ALL_THRESHOLD, TITLE_HEIGHT } from '../consts/const' -import {FaClipboardList} from 'react-icons/fa' +import { FaClipboardList } from 'react-icons/fa' import useTranslate from '../hooks/useTranslate' -import {openUrl} from '@kky002/kky-util' +import { openUrl } from '@kky002/kky-util' import useKeyService from '../hooks/useKeyService' import Ask from './Ask' -import {v4} from 'uuid' +import { v4 } from 'uuid' import RateExtension from '../components/RateExtension' +import ApiKeyReminder from './ApiKeyReminder' const Body = () => { const dispatch = useAppDispatch() @@ -52,7 +53,7 @@ const Body = () => { const floatKeyPointsSegIdx = useAppSelector(state => state.env.floatKeyPointsSegIdx) const translateEnable = useAppSelector(state => state.env.envData.translateEnable) const summarizeEnable = useAppSelector(state => state.env.envData.summarizeEnable) - const {addSummarizeTask, addAskTask} = useTranslate() + const { addSummarizeTask, addAskTask } = useTranslate() // const infos = useAppSelector(state => state.env.infos) const bodyRef = useRef() const curOffsetTop = useAppSelector(state => state.env.curOffsetTop) @@ -71,13 +72,13 @@ const Body = () => { const searchPlaceholder = useMemo(() => { let placeholder = '' if (envData.searchEnabled) { - if (envData.askEnabled??ASK_ENABLED_DEFAULT) { + if (envData.askEnabled ?? ASK_ENABLED_DEFAULT) { placeholder = '搜索或提问字幕内容(按Enter提问)' } else { placeholder = '搜索字幕内容' } } else { - if (envData.askEnabled??ASK_ENABLED_DEFAULT) { + if (envData.askEnabled ?? ASK_ENABLED_DEFAULT) { placeholder = '提问字幕内容' } } @@ -101,7 +102,7 @@ const Body = () => { }, [dispatch]) const onSummarizeAll = useCallback(() => { - const apiKey = envData.aiType === 'gemini'?envData.geminiApiKey:envData.apiKey + const apiKey = envData.aiType === 'gemini' ? envData.geminiApiKey : envData.apiKey if (!apiKey) { toast.error('请先在选项页面设置ApiKey!') return @@ -142,7 +143,7 @@ const Body = () => { }, [asks, dispatch, foldAll, segments]) const toggleAutoTranslateCallback = useCallback(() => { - const apiKey = envData.aiType === 'gemini'?envData.geminiApiKey:envData.apiKey + const apiKey = envData.aiType === 'gemini' ? envData.geminiApiKey : envData.apiKey if (apiKey) { dispatch(setAutoTranslate(!autoTranslate)) } else { @@ -180,8 +181,8 @@ const Body = () => { }, [dispatch]) const onAsk = useCallback(() => { - if ((envData.askEnabled??ASK_ENABLED_DEFAULT) && searchText) { - const apiKey = envData.aiType === 'gemini'?envData.geminiApiKey:envData.apiKey + if ((envData.askEnabled ?? ASK_ENABLED_DEFAULT) && searchText) { + const apiKey = envData.aiType === 'gemini' ? envData.geminiApiKey : envData.apiKey if (apiKey) { if (segments != null && segments.length > 0) { const id = v4() @@ -205,7 +206,7 @@ const Body = () => { // 自动滚动 useEffect(() => { if (checkAutoScroll && curOffsetTop && autoScroll && !needScroll) { - if (bodyRef.current.scrollTop <= curOffsetTop - bodyRef.current.offsetTop - (totalHeight-160) + (floatKeyPointsSegIdx != null ? 100 : 0) || + if (bodyRef.current.scrollTop <= curOffsetTop - bodyRef.current.offsetTop - (totalHeight - 160) + (floatKeyPointsSegIdx != null ? 100 : 0) || bodyRef.current.scrollTop >= curOffsetTop - bodyRef.current.offsetTop - 40 - 10 ) { dispatch(setNeedScroll(true)) @@ -218,30 +219,30 @@ const Body = () => { return
{/* title */}
- + {segments != null && segments.length > 0 && } + title='展开/折叠全部' />}
列表视图 + onClick={normalCallback}>列表视图 文章视图 + onClick={compactCallback}>文章视图
{translateEnable &&
- + onClick={toggleAutoTranslateCallback}> +
} {summarizeEnable &&
- +
} {noVideo &&
- +
}
@@ -257,8 +258,8 @@ const Body = () => { dispatch(setSearchText('')) } } - }}/> - {searchText && } + }} /> + {searchText && }
} {/* auto scroll btn */} @@ -266,22 +267,22 @@ const Body = () => { className='absolute z-[999] top-[96px] right-6 tooltip tooltip-left cursor-pointer rounded-full bg-primary/25 hover:bg-primary/75 text-primary-content p-1.5 text-xl' data-tip='开启自动滚动' onClick={onEnableAutoScroll}> - + } {/* body */}
{/* asks */} - {asks.map(ask => )} + {asks.map(ask => )} {/* segments */} {segments?.map((segment, segmentIdx) => )} + segmentIdx={segmentIdx} bodyRef={bodyRef} />)} {/* tip */}
快捷键提示
@@ -291,6 +292,8 @@ const Body = () => {
  • 上下方向键来移动当前字幕(可先点击字幕使焦点在字幕列表内)。
  • + + {/*
    */} {/*
    💡提示💡
    */} {/*
    可以尝试将概览生成的内容粘贴到 { {/*
    */}
    youtube caption logoYouTube Caption + alt='youtube caption logo' + className='w-8 h-8' />YouTube Caption
    这是YouTube版的字幕列表
    { - e.preventDefault() - openUrl('https://chromewebstore.google.com/detail/fiaeclpicddpifeflpmlgmbjgaedladf') - }} className='link text-sm text-accent'>Chrome商店 + onClick={(e) => { + e.preventDefault() + openUrl('https://chromewebstore.google.com/detail/fiaeclpicddpifeflpmlgmbjgaedladf') + }} className='link text-sm text-accent'>Chrome商店 { - e.preventDefault() - openUrl('https://microsoftedge.microsoft.com/addons/detail/galeejdehabppfgooagmkclpppnbccpc') - }} className='link text-sm text-accent'>Edge商店 + href='https://microsoftedge.microsoft.com/addons/detail/galeejdehabppfgooagmkclpppnbccpc' + onClick={e => { + e.preventDefault() + openUrl('https://microsoftedge.microsoft.com/addons/detail/galeejdehabppfgooagmkclpppnbccpc') + }} className='link text-sm text-accent'>Edge商店 { - e.preventDefault() - openUrl('https://www.crxsoso.com/webstore/detail/fiaeclpicddpifeflpmlgmbjgaedladf') - }} className='link text-sm text-accent'>Crx搜搜(国内可访问) + href='https://www.crxsoso.com/webstore/detail/fiaeclpicddpifeflpmlgmbjgaedladf' + onClick={(e) => { + e.preventDefault() + openUrl('https://www.crxsoso.com/webstore/detail/fiaeclpicddpifeflpmlgmbjgaedladf') + }} className='link text-sm text-accent'>Crx搜搜(国内可访问)
    {/*
    */} @@ -365,7 +368,7 @@ const Body = () => { {/*
    */} {/*
    */}
    -
    +
    {/* recommend */} {/*
    {
    } -export default Body +export default Body \ No newline at end of file diff --git a/src/utils/chromeUtils.ts b/src/utils/chromeUtils.ts new file mode 100644 index 0000000..eaccb01 --- /dev/null +++ b/src/utils/chromeUtils.ts @@ -0,0 +1,7 @@ +export const openOptionsPage = () => { + if (chrome.runtime.openOptionsPage) { + chrome.runtime.openOptionsPage(); + } else { + window.open(chrome.runtime.getURL('options.html')); + } +}; \ No newline at end of file