refactor: streaming
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useState, useCallback, useRef } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import BASE_URL from "../../../app/config";
|
||||
import BASE_URL, { KEY_EXPIRE, KEY_REFRESH_TOKEN, KEY_TOKEN } from "../../../app/config";
|
||||
import { setReady } from "../../../app/slices/ui";
|
||||
import {
|
||||
fillChannels,
|
||||
@@ -33,44 +33,59 @@ const getQueryString = (params: { [key: string]: string }) => {
|
||||
});
|
||||
return sp.toString();
|
||||
};
|
||||
let inter: number | null = null;
|
||||
const getLocalAuthData = () => {
|
||||
return {
|
||||
token: localStorage.getItem(KEY_TOKEN) || "",
|
||||
refreshToken: localStorage.getItem(KEY_REFRESH_TOKEN) || "",
|
||||
expireTime: Number(localStorage.getItem(KEY_EXPIRE) || +new Date())
|
||||
};
|
||||
};
|
||||
let SSE: EventSource | undefined = undefined;
|
||||
let opened = false;
|
||||
|
||||
let connectionIsOpen = false;
|
||||
let aliveInter: number = 0;
|
||||
export default function useStreaming() {
|
||||
const opened = useRef(false);
|
||||
const [renewToken] = useRenewMutation();
|
||||
const [readyPullData, setReadyPullData] = useState(false);
|
||||
const [streamingReady, setStreamingReady] = useState(false);
|
||||
const {
|
||||
authData,
|
||||
authData: { user },
|
||||
ui: { ready },
|
||||
footprint: { afterMid, usersVersion, readUsers, readChannels }
|
||||
} = useAppSelector((store) => store);
|
||||
const dispatch = useAppDispatch();
|
||||
const loginUid = authData.user?.uid || 0;
|
||||
let aliveInter: number = 0;
|
||||
const keepAlive = () => {
|
||||
clearTimeout(aliveInter);
|
||||
// 比15秒多2秒
|
||||
const loginUid = user?.uid || 0;
|
||||
|
||||
const keepAlive = (timeout?: number) => {
|
||||
window.clearTimeout(aliveInter);
|
||||
// 比15秒多5秒
|
||||
aliveInter = window.setTimeout(() => {
|
||||
// 重启连接
|
||||
stopStreaming();
|
||||
startStreaming();
|
||||
}, 17000);
|
||||
}, timeout ?? 20000);
|
||||
};
|
||||
const startStreaming = async () => {
|
||||
const startStreaming = useCallback(
|
||||
async () => {
|
||||
console.log("start streaming", SSE, SSE?.readyState);
|
||||
|
||||
if (connectionIsOpen) return;
|
||||
window.clearTimeout(aliveInter);
|
||||
if (SSE && (SSE.readyState === EventSource.OPEN || SSE.readyState === EventSource.CONNECTING)) return;
|
||||
const { token = "", refreshToken, expireTime = +new Date() } = authData;
|
||||
const { token, refreshToken, expireTime } = getLocalAuthData();
|
||||
// token 非空
|
||||
if (!token) {
|
||||
console.info("sse start streaming no token", token);
|
||||
return;
|
||||
}
|
||||
let _token = token;
|
||||
// 如果token快要过期,先renew
|
||||
if (dayjs().isAfter(new Date(expireTime - 20 * 1000))) {
|
||||
renewToken({ token, refresh_token: refreshToken });
|
||||
const resp = await renewToken({ token, refresh_token: refreshToken });
|
||||
if ("error" in resp) {
|
||||
return;
|
||||
} else {
|
||||
_token = resp.data.token;
|
||||
}
|
||||
// return;
|
||||
}
|
||||
// 开始初始化
|
||||
const params: {
|
||||
@@ -78,7 +93,7 @@ export default function useStreaming() {
|
||||
after_mid?: string;
|
||||
users_version?: string;
|
||||
} = {
|
||||
"api-key": token,
|
||||
"api-key": _token,
|
||||
};
|
||||
// 如果afterMid是0,则不传该参数
|
||||
if (afterMid !== 0) {
|
||||
@@ -93,22 +108,19 @@ export default function useStreaming() {
|
||||
|
||||
SSE.onopen = () => {
|
||||
//todo
|
||||
opened = true;
|
||||
opened.current = true;
|
||||
connectionIsOpen = true;
|
||||
};
|
||||
SSE.onerror = (err) => {
|
||||
const { readyState } = err.target as EventSource;
|
||||
console.error("sse error", readyState, err);
|
||||
// 连接还处于开启状态
|
||||
if (readyState === EventSource.OPEN) {
|
||||
if (readyState === EventSource.OPEN || readyState === EventSource.CONNECTING) {
|
||||
return;
|
||||
}
|
||||
if (inter) {
|
||||
clearTimeout(inter);
|
||||
}
|
||||
// // 重连
|
||||
inter = window.setTimeout(() => {
|
||||
startStreaming();
|
||||
}, 1000);
|
||||
// 重连
|
||||
connectionIsOpen = false;
|
||||
keepAlive(2000);
|
||||
};
|
||||
SSE.onmessage = (evt) => {
|
||||
console.info("sse message", evt.data);
|
||||
@@ -122,6 +134,8 @@ export default function useStreaming() {
|
||||
break;
|
||||
case "ready":
|
||||
console.info("sse streaming ready");
|
||||
// 有时候,heartbeat不会发?
|
||||
keepAlive();
|
||||
dispatch(setReady());
|
||||
break;
|
||||
case "users_snapshot":
|
||||
@@ -287,33 +301,33 @@ export default function useStreaming() {
|
||||
break;
|
||||
}
|
||||
};
|
||||
};
|
||||
},
|
||||
[afterMid, usersVersion],
|
||||
);
|
||||
|
||||
|
||||
const stopStreaming = () => {
|
||||
console.info("sse stop streaming");
|
||||
if (SSE) {
|
||||
SSE.close();
|
||||
SSE = undefined;
|
||||
connectionIsOpen = false;
|
||||
}
|
||||
};
|
||||
|
||||
const setStreamingReady = (ready: boolean) => {
|
||||
setReadyPullData(ready);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (readyPullData) {
|
||||
// if (online) {
|
||||
// 确保只执行一次
|
||||
const hasOpened = opened.current;
|
||||
if (streamingReady && !hasOpened) {
|
||||
startStreaming();
|
||||
// } else {
|
||||
// stopStreaming();
|
||||
// }
|
||||
}
|
||||
return () => {
|
||||
console.log("stop from readyPullData");
|
||||
if (streamingReady && !hasOpened) {
|
||||
console.log("stop from streamingReady");
|
||||
stopStreaming();
|
||||
}
|
||||
};
|
||||
}, [readyPullData]);
|
||||
}, [streamingReady]);
|
||||
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user