Merge pull request 'more stable ws stream' (#7) from pick/pari-ws-stability into develop
All checks were successful
Lint / pnpm_install (push) Successful in 1m41s
Test (production install and build) / production (20.16.0) (push) Successful in 1m40s
Publish Docker image / Build (push) Successful in 4m27s
Lint / lint (backend) (push) Successful in 2m37s
Lint / lint (frontend) (push) Successful in 2m33s
Lint / lint (frontend-embed) (push) Successful in 2m14s
Lint / lint (frontend-shared) (push) Successful in 2m28s
Lint / lint (misskey-bubble-game) (push) Successful in 2m25s
Lint / lint (misskey-js) (push) Successful in 2m4s
Lint / lint (misskey-reversi) (push) Successful in 2m20s
Lint / lint (sw) (push) Successful in 2m25s
Lint / typecheck (backend) (push) Successful in 2m11s
Lint / typecheck (misskey-js) (push) Successful in 1m49s
Lint / typecheck (sw) (push) Successful in 1m55s

Reviewed-on: #7
This commit is contained in:
ゆめ 2024-11-10 00:21:05 -06:00
commit dc24536a7c

View file

@ -7,45 +7,91 @@ import * as Misskey from 'misskey-js';
import { markRaw } from 'vue'; import { markRaw } from 'vue';
import { $i } from '@/account.js'; import { $i } from '@/account.js';
import { wsOrigin } from '@@/js/config.js'; import { wsOrigin } from '@@/js/config.js';
import { DEFAULT_DEVICE_KIND } from '@/scripts/device-kind.js';
// TODO: No WebsocketモードでStreamMockが使えそう // TODO: No WebsocketモードでStreamMockが使えそう
//import { StreamMock } from '@/scripts/stream-mock.js'; //import { StreamMock } from '@/scripts/stream-mock.js';
// heart beat interval in ms // heart beat interval in ms
const HEART_BEAT_INTERVAL = 1000 * 60; const HEART_BEAT_INTERVAL = DEFAULT_DEVICE_KIND === 'desktop' ? 1000 * 15 : 1000 * 30;
const RECONNECT_MAX_ATTEMPTS = 10;
const RECONNECT_INITIAL_DELAY = 1000;
const RECONNECT_MAX_DELAY = 1000 * 30;
let stream: Misskey.IStream | null = null; let stream: Misskey.IStream | null = null;
let timeoutHeartBeat: number | null = null; let timeoutHeartBeat: number | null = null;
let lastHeartbeatCall = 0; let lastHeartbeatCall = 0;
let reconnectAttempts = 0;
let reconnectTimeout: number | null = null;
function getReconnectDelay(): number {
const delay = RECONNECT_INITIAL_DELAY * Math.pow(2, reconnectAttempts);
return Math.min(delay, RECONNECT_MAX_DELAY);
}
function createStream(): Misskey.IStream {
const newStream = markRaw(new Misskey.Stream(wsOrigin, $i ? {
token: $i.token,
} : null));
newStream.on('_disconnected_', () => {
console.log('Stream disconnected, attempting to reconnect...');
if (reconnectAttempts < RECONNECT_MAX_ATTEMPTS) {
const delay = getReconnectDelay();
reconnectTimeout = window.setTimeout(() => {
reconnectAttempts++;
stream = null;
useStream();
}, delay);
} else {
console.error('Max reconnection attempts reached');
}
});
newStream.on('_connected_', () => {
console.log('Stream connected successfully');
reconnectAttempts = 0;
if (reconnectTimeout) {
clearTimeout(reconnectTimeout);
reconnectTimeout = null;
}
});
return newStream;
}
export function useStream(): Misskey.IStream { export function useStream(): Misskey.IStream {
if (stream) return stream; if (stream) return stream;
// TODO: No Websocketモードもここで判定 stream = createStream();
stream = markRaw(new Misskey.Stream(wsOrigin, $i ? {
token: $i.token,
} : null));
if (timeoutHeartBeat) window.clearTimeout(timeoutHeartBeat); if (timeoutHeartBeat) window.clearTimeout(timeoutHeartBeat);
timeoutHeartBeat = window.setTimeout(heartbeat, HEART_BEAT_INTERVAL); timeoutHeartBeat = window.setTimeout(heartbeat, HEART_BEAT_INTERVAL);
// send heartbeat right now when last send time is over HEART_BEAT_INTERVAL const target = () => {
document.addEventListener('visibilitychange', () => { if (
if ( !stream
!stream || document.visibilityState !== 'visible'
|| document.visibilityState !== 'visible' || Date.now() - lastHeartbeatCall < HEART_BEAT_INTERVAL
|| Date.now() - lastHeartbeatCall < HEART_BEAT_INTERVAL ) return;
) return; heartbeat();
heartbeat(); };
});
return stream; // send heartbeat right now when last send time is over HEART_BEAT_INTERVAL
document.addEventListener('visibilitychange', target);
stream.on('_disconnected_', () => {
document.removeEventListener('visibilitychange', target);
});
return stream;
} }
function heartbeat(): void { function heartbeat(): void {
if (stream != null && document.visibilityState === 'visible') { if (stream != null && document.visibilityState === 'visible') {
stream.heartbeat(); stream.heartbeat();
} }
lastHeartbeatCall = Date.now(); lastHeartbeatCall = Date.now();
if (timeoutHeartBeat) window.clearTimeout(timeoutHeartBeat); if (timeoutHeartBeat) window.clearTimeout(timeoutHeartBeat);
timeoutHeartBeat = window.setTimeout(heartbeat, HEART_BEAT_INTERVAL); timeoutHeartBeat = window.setTimeout(heartbeat, HEART_BEAT_INTERVAL);
} }