重构消息通信

This commit is contained in:
IndieKKY
2024-10-03 23:38:18 +08:00
parent e3ddf386cb
commit f50a2e3abd
13 changed files with 719 additions and 374 deletions

View File

@@ -0,0 +1,101 @@
import {useCallback, useContext, useEffect} from 'react'
import {
MESSAGE_TARGET_APP,
MESSAGE_TARGET_EXTENSION,
MESSAGE_TARGET_INJECT,
MESSAGE_TO_APP_SET_INFOS,
MESSAGE_TO_APP_SET_VIDEO_INFO,
} from '@/const'
import {debug} from '@/util/biz_util'
import {callServer, PostMessagePayload, PostMessageResponse} from 'postmessage-promise'
import {useAppDispatch} from '../hooks/redux'
import {Waiter} from '@kky002/kky-util'
import {setInfos, setTitle, setUrl, setCurInfo, setCurFetched, setData} from '@/redux/envReducer'
let postInjectMessage: (method: string, params: PostMessagePayload) => Promise<PostMessageResponse> | undefined
export const injectWaiter = new Waiter<typeof postInjectMessage>(() => ({
finished: postInjectMessage != null,
data: postInjectMessage
}), 100, 15000)
const useMessageService = () => {
const dispatch = useAppDispatch()
const path = 'app' //useAppSelector(state => state.env.path)
const messageHandler = useCallback((method: string, params: any, from: string, context: any): boolean => {
switch (method) {
case MESSAGE_TO_APP_SET_INFOS:
dispatch(setInfos(params.infos))
dispatch(setCurInfo(undefined))
dispatch(setCurFetched(false))
dispatch(setData(undefined))
break
case MESSAGE_TO_APP_SET_VIDEO_INFO:
dispatch(setInfos(params.infos))
dispatch(setUrl(params.url))
dispatch(setTitle(params.title))
console.debug('video title: ', params.title)
break
default:
debug('unknown message method: ', method)
return false
}
return true
}, [dispatch])
// connect to inject
useEffect(() => {
if (path !== 'app') return
let destroyFunc: (() => void) | undefined
const serverObject = {
server: window.parent, // openedWindow / window.parent / window.opener;
origin: '*', // target-window's origin or *
}
const options = {}
callServer(serverObject, options).then(e => {
const { postMessage, listenMessage, destroy } = e
postInjectMessage = postMessage
destroyFunc = destroy
listenMessage((method, params, sendResponse) => {
debug('inject => ', method, params)
const success = messageHandler(method, params, MESSAGE_TARGET_INJECT, {})
sendResponse({
success,
code: success ? 200 : 500
})
})
debug('message ready')
}).catch(console.error)
return () => {
destroyFunc?.()
}
}, [messageHandler, path])
const extensionMessageCallback = useCallback((event: MessageData, sender: chrome.runtime.MessageSender, sendResponse: (response?: any) => void) => {
debug((sender.tab != null) ? `tab ${sender.tab.url??''} => ` : 'extension => ', JSON.stringify(event))
// check event target
if (!event || event.target !== MESSAGE_TARGET_APP) return
messageHandler(event.method, event.params, MESSAGE_TARGET_EXTENSION, {
sender
})
}, [messageHandler])
// listen for message
useEffect(() => {
chrome.runtime.onMessage.addListener(extensionMessageCallback)
return () => {
chrome.runtime.onMessage.removeListener(extensionMessageCallback)
}
}, [extensionMessageCallback])
}
export default useMessageService

View File

@@ -1,6 +1,8 @@
import {useAppDispatch, useAppSelector} from './redux'
import React, {useCallback} from 'react'
import {setNeedScroll, setReviewAction, setTempData} from '../redux/envReducer'
import {sendInject} from '../util/biz_util'
import {MESSAGE_TO_INJECT_MOVE} from '../const'
const useSubtitle = () => {
const dispatch = useAppDispatch()
@@ -9,7 +11,7 @@ const useSubtitle = () => {
const reviewActions = useAppSelector(state => state.env.tempData.reviewActions)
const move = useCallback((time: number, togglePause: boolean) => {
window.parent.postMessage({type: 'move', time, togglePause}, '*')
sendInject(MESSAGE_TO_INJECT_MOVE, {time, togglePause})
//review action
if (reviewed === undefined && !reviewAction) {

View File

@@ -16,9 +16,11 @@ import {
setTempData,
} from '../redux/envReducer'
import {EventBusContext} from '../Router'
import {EVENT_EXPAND, GEMINI_TOKENS, TOTAL_HEIGHT_MAX, TOTAL_HEIGHT_MIN, WORDS_MIN, WORDS_RATE} from '../const'
import {EVENT_EXPAND, GEMINI_TOKENS, TOTAL_HEIGHT_MAX, TOTAL_HEIGHT_MIN, WORDS_MIN, WORDS_RATE, MESSAGE_TO_INJECT_GET_VIDEO_STATUS, MESSAGE_TO_INJECT_GET_VIDEO_ELEMENT_INFO, MESSAGE_TO_INJECT_REFRESH_VIDEO_INFO, MESSAGE_TO_INJECT_HIDE_TRANS, MESSAGE_TO_INJECT_UPDATETRANSRESULT} from '../const'
import {useInterval} from 'ahooks'
import {getModelMaxTokens, getWholeText} from '../util/biz_util'
import {sendInject} from '../util/biz_util'
import {MESSAGE_TO_INJECT_GET_SUBTITLE} from '../const'
/**
* Service是单例类似后端的服务概念
@@ -52,55 +54,6 @@ const useSubtitleService = () => {
}
}, [reviewActions, dispatch, reviewed])
// 监听消息
useEffect(() => {
const listener = (event: MessageEvent) => {
const data = event.data
if (data.type === 'setVideoInfo') {
dispatch(setInfos(data.infos))
dispatch(setUrl(data.url))
dispatch(setTitle(data.title))
console.debug('video title: ', data.title)
}
if (data.type === 'setInfos') {
dispatch(setInfos(data.infos))
dispatch(setCurInfo(undefined))
dispatch(setCurFetched(false))
dispatch(setData(undefined))
// console.log('setInfos', data.infos)
}
if (data.type === 'setSubtitle') {
const data_ = data.data.data
data_?.body?.forEach((item: TranscriptItem, idx: number) => {
item.idx = idx
})
// dispatch(setCurInfo(data.data.info))
dispatch(setCurFetched(true))
dispatch(setData(data_))
// console.log('setSubtitle', data.data)
}
if (data.type === 'setCurrentTime') {
dispatch(setCurrentTime(data.data.currentTime))
}
if (data.type === 'setSettings') {
dispatch(setNoVideo(data.data.noVideo))
if (data.data.totalHeight) {
dispatch(setTotalHeight(Math.min(Math.max(data.data.totalHeight, TOTAL_HEIGHT_MIN), TOTAL_HEIGHT_MAX)))
}
}
}
window.addEventListener('message', listener)
return () => {
window.removeEventListener('message', listener)
}
}, [dispatch, eventBus])
// 有数据时自动展开
useEffect(() => {
if ((data != null) && data.body.length > 0) {
@@ -120,15 +73,30 @@ const useSubtitleService = () => {
// 获取
useEffect(() => {
if (curInfo && !curFetched) {
window.parent.postMessage({type: 'getSubtitle', info: curInfo}, '*')
sendInject(MESSAGE_TO_INJECT_GET_SUBTITLE, {info: curInfo}).then(data => {
const data_ = data.data
data_?.body?.forEach((item: TranscriptItem, idx: number) => {
item.idx = idx
})
// dispatch(setCurInfo(data.data.info))
dispatch(setCurFetched(true))
dispatch(setData(data_))
console.log('subtitle', data)
})
}
}, [curFetched, curInfo])
useEffect(() => {
// 初始获取列表
window.parent.postMessage({type: 'refreshVideoInfo'}, '*')
sendInject(MESSAGE_TO_INJECT_REFRESH_VIDEO_INFO, {})
// 初始获取设置信息
window.parent.postMessage({type: 'getSettings'}, '*')
sendInject(MESSAGE_TO_INJECT_GET_VIDEO_ELEMENT_INFO, {}).then(info => {
dispatch(setNoVideo(info.noVideo))
if (info.totalHeight) {
dispatch(setTotalHeight(Math.min(Math.max(info.totalHeight, TOTAL_HEIGHT_MIN), TOTAL_HEIGHT_MAX)))
}
})
}, [])
// 更新当前位置
@@ -216,21 +184,23 @@ const useSubtitleService = () => {
// 每秒更新当前视频时间
useInterval(() => {
window.parent.postMessage({type: 'getCurrentTime'}, '*')
sendInject(MESSAGE_TO_INJECT_GET_VIDEO_STATUS, {}).then(status => {
dispatch(setCurrentTime(status.currentTime))
})
}, 500)
// show translated text in the video
useEffect(() => {
if (hideOnDisableAutoTranslate && !autoTranslate) {
window.parent.postMessage({type: 'updateTransResult'}, '*')
sendInject(MESSAGE_TO_INJECT_HIDE_TRANS, {})
return
}
const transResult = curIdx?transResults[curIdx]:undefined
if (transResult?.code === '200' && transResult.data) {
window.parent.postMessage({type: 'updateTransResult', result: transResult.data}, '*')
sendInject(MESSAGE_TO_INJECT_UPDATETRANSRESULT, {result: transResult.data})
} else {
window.parent.postMessage({type: 'updateTransResult'}, '*')
sendInject(MESSAGE_TO_INJECT_HIDE_TRANS, {})
}
}, [autoTranslate, curIdx, hideOnDisableAutoTranslate, transResults])
}

View File

@@ -16,6 +16,8 @@ import {
import {
LANGUAGE_DEFAULT,
LANGUAGES_MAP,
MESSAGE_TO_EXTENSION_ADD_TASK,
MESSAGE_TO_EXTENSION_GET_TASK,
PROMPT_DEFAULTS,
PROMPT_TYPE_ASK,
PROMPT_TYPE_TRANSLATE,
@@ -27,7 +29,7 @@ import {
} from '../const'
import toast from 'react-hot-toast'
import {useMemoizedFn} from 'ahooks/es'
import {extractJsonArray, extractJsonObject, getModel} from '../util/biz_util'
import {extractJsonArray, extractJsonObject, getModel, sendExtension} from '../util/biz_util'
import {formatTime} from '../util/util'
const useTranslate = () => {
@@ -135,7 +137,7 @@ const useTranslate = () => {
}
})
dispatch(addTransResults(result))
const task = await chrome.runtime.sendMessage({type: 'addTask', taskDef})
const task = await sendExtension(MESSAGE_TO_EXTENSION_ADD_TASK, {taskDef})
dispatch(addTaskId(task.id))
}
}
@@ -205,7 +207,7 @@ const useTranslate = () => {
console.debug('addSummarizeTask', taskDef)
dispatch(setSummaryStatus({segmentStartIdx: segment.startIdx, type, status: 'pending'}))
dispatch(setLastSummarizeTime(Date.now()))
const task = await chrome.runtime.sendMessage({type: 'addTask', taskDef})
const task = await sendExtension(MESSAGE_TO_EXTENSION_ADD_TASK, {taskDef})
dispatch(addTaskId(task.id))
}
}, [dispatch, envData, summarizeLanguage.name, title])
@@ -262,7 +264,7 @@ const useTranslate = () => {
id,
status: 'pending'
}))
const task = await chrome.runtime.sendMessage({type: 'addTask', taskDef})
const task = await sendExtension(MESSAGE_TO_EXTENSION_ADD_TASK, {taskDef})
dispatch(addTaskId(task.id))
}
}, [dispatch, envData, summarizeLanguage.name, title])
@@ -330,7 +332,7 @@ const useTranslate = () => {
})
const getTask = useCallback(async (taskId: string) => {
const taskResp = await chrome.runtime.sendMessage({type: 'getTask', taskId})
const taskResp = await sendExtension(MESSAGE_TO_EXTENSION_GET_TASK, {taskId})
if (taskResp.code === 'ok') {
console.debug('getTask', taskResp.task)
const task: Task = taskResp.task