import React, {MutableRefObject, useCallback, useEffect, useMemo, useRef} from 'react' import {useAppDispatch, useAppSelector} from '../hooks/redux' import {setFloatKeyPointsSegIdx, setPage, 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' import useTranslate from '../hooks/useTranslate' import {BsDashSquare, BsPlusSquare, CgFileDocument, FaQuestion, GrOverview, RiFileCopy2Line} from 'react-icons/all' import toast from 'react-hot-toast' import {getLastTime, getSummaryStr, isSummaryEmpty, parseStrTimeToSeconds} from '../util/biz_util' import {useInViewport} from 'ahooks' import SegmentItem from './SegmentItem' import {stopPopFunc} from '../util/util' import useSubtitle from '../hooks/useSubtitle' const SummarizeItemOverview = (props: { segment: Segment summary: OverviewSummary segmentIdx: number overviewItem: OverviewItem idx: number }) => { const { segment, summary, segmentIdx, overviewItem, idx} = props const {move} = useSubtitle() const time = parseStrTimeToSeconds(overviewItem.time) const currentTime = useAppSelector(state => state.env.currentTime) const isIn = useMemo(() => { if (currentTime != null) { // check in current segment if (segment.items?.length > 0) { const startTime = segment.items[0].from const lastTime = segment.items[segment.items.length - 1].to if (currentTime >= startTime && currentTime < lastTime) { // check in current overview item const nextOverviewItem = summary.content?.[idx + 1] const nextTime = (nextOverviewItem != null)?parseStrTimeToSeconds(nextOverviewItem.time):null return currentTime >= time && (nextTime == null || currentTime < nextTime) } } } return false }, [currentTime, idx, segment.items, summary.content, time]) const moveCallback = useCallback((event: any) => { if (event.altKey) { // 复制 navigator.clipboard.writeText(overviewItem.key).catch(console.error) } else { move(time, false) } }, [overviewItem.key, move, time]) return
  • {overviewItem.emoji} {overviewItem.time} {overviewItem.key}
  • } const Summarize = (props: { segment: Segment segmentIdx: number summary?: Summary float?: boolean }) => { const {segment, segmentIdx, summary, float} = props const dispatch = useAppDispatch() const envData = useAppSelector(state => state.env.envData) const fontSize = useAppSelector(state => state.env.envData.fontSize) const curSummaryType = useAppSelector(state => state.env.tempData.curSummaryType) const {addSummarizeTask} = useTranslate() const onGenerate = useCallback(() => { const apiKey = envData.aiType === 'gemini'?envData.geminiApiKey:envData.apiKey if (apiKey) { addSummarizeTask(curSummaryType, segment).catch(console.error) } else { dispatch(setPage(PAGE_SETTINGS)) toast.error('需要先设置ApiKey!') } }, [addSummarizeTask, curSummaryType, dispatch, envData.aiType, envData.apiKey, envData.geminiApiKey, segment]) const onCopy = useCallback(() => { if (summary != null) { navigator.clipboard.writeText(getSummaryStr(summary)).then(() => { toast.success('已复制到剪贴板!') }).catch(console.error) } }, [summary]) return
    {(summary != null) && !isSummaryEmpty(summary) &&
    }
    {summary?.type === 'overview' && (summary.content != null) && } {summary?.type === 'keypoint' && (summary.content != null) && } {summary?.type === 'brief' && (summary.content != null) &&
    {summary.content.summary}
    } {summary?.type === 'question' && (summary.content != null) &&
    {summary.content.map((question: any, idx: number) =>

    {question.q}

    {question.a}
    )}
    }
    {segment.text.length < SUMMARIZE_THRESHOLD &&
    文字过短,无法总结.
    } {segment.text.length >= SUMMARIZE_THRESHOLD && ((summary == null) || summary.status !== 'done' || summary.error) && } {((summary == null) || summary.status === 'init') &&
    {SUMMARIZE_TYPES[curSummaryType].desc}
    } {summary?.error &&
    {summary?.error}
    }
    {!float &&
    }
    } const SegmentCard = (props: { bodyRef: MutableRefObject segment: Segment segmentIdx: number }) => { const {bodyRef, segment, segmentIdx} = props const dispatch = useAppDispatch() const summarizeRef = useRef(null) const [inViewport] = useInViewport(summarizeRef, { root: bodyRef.current, }) const segments = useAppSelector(state => state.env.segments) const needScroll = useAppSelector(state => state.env.needScroll) const curIdx = useAppSelector(state => state.env.curIdx) 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]) const curSummaryType = useAppSelector(state => state.env.tempData.curSummaryType) const summary = useMemo(() => { const result = segment.summaries[curSummaryType] if (result) { return result } return undefined }, [curSummaryType, segment.summaries]) const onFold = useCallback(() => { dispatch(setSegmentFold({ segmentStartIdx: segment.startIdx, fold: !segment.fold })) }, [dispatch, segment.fold, segment.startIdx]) // 检测设置floatKeyPointsSegIdx useEffect(() => { if (summarizeFloat) { // 已启用 if (!fold && page === PAGE_MAIN && showCurrent) { // 当前Card有控制权 if (!inViewport && (summary != null) && !isSummaryEmpty(summary)) { dispatch(setFloatKeyPointsSegIdx(segment.startIdx)) } else { dispatch(setFloatKeyPointsSegIdx()) } } } }, [dispatch, fold, inViewport, page, segment.startIdx, showCurrent, summarizeFloat, summary]) const onSelBrief = useCallback(() => { dispatch(setTempData({ curSummaryType: 'brief' })) }, [dispatch]) const onSelOverview = useCallback(() => { dispatch(setTempData({ curSummaryType: 'overview' })) }, [dispatch]) const onSelKeypoint = useCallback(() => { dispatch(setTempData({ curSummaryType: 'keypoint' })) }, [dispatch]) const onSelQuestion = useCallback(() => { dispatch(setTempData({ curSummaryType: 'question' })) }, [dispatch]) return
    {segments != null && segments.length > 0 &&
    {segment.fold ? : }
    } {summarizeEnable &&
    总结 概览 要点 问题
    }
    {getLastTime(segment.items[segment.items.length - 1].to - segment.items[0].from)}
    {summarizeEnable &&
    } {!segment.fold ?
    {!compact &&
    时间
    字幕内容
    } {segment.items.map((item: TranscriptItem, idx: number) => )} {segments != null && segments.length > 0 && }
    :
    {segment.items.length}行已折叠,点击展开
    } {floatKeyPointsSegIdx === segment.startIdx &&
    }
    } export default SegmentCard