client-ts: Port services/app/ws

This commit is contained in:
Elian Doran 2024-07-25 20:47:33 +03:00
parent c5113d9881
commit ac7316ae93
No known key found for this signature in database
3 changed files with 39 additions and 20 deletions

View File

@ -1,6 +1,6 @@
import dayjs from "dayjs"; import dayjs from "dayjs";
function reloadFrontendApp(reason: string) { function reloadFrontendApp(reason?: string) {
if (reason) { if (reason) {
logInfo(`Frontend app reload: ${reason}`); logInfo(`Frontend app reload: ${reason}`);
} }
@ -301,7 +301,7 @@ function dynamicRequire(moduleName: string) {
} }
} }
function timeLimit<T>(promise: Promise<T>, limitMs: number, errorMessage: string) { function timeLimit<T>(promise: Promise<T>, limitMs: number, errorMessage?: string) {
if (!promise || !promise.then) { // it's not actually a promise if (!promise || !promise.then) { // it's not actually a promise
return promise; return promise;
} }

View File

@ -4,17 +4,19 @@ import server from "./server.js";
import options from "./options.js"; import options from "./options.js";
import frocaUpdater from "./froca_updater.js"; import frocaUpdater from "./froca_updater.js";
import appContext from "../components/app_context.js"; import appContext from "../components/app_context.js";
import { EntityChange } from '../../../services/entity_changes_interface.js';
const messageHandlers = []; type MessageHandler = (message: any) => void;
const messageHandlers: MessageHandler[] = [];
let ws; let ws: WebSocket;
let lastAcceptedEntityChangeId = window.glob.maxEntityChangeIdAtLoad; let lastAcceptedEntityChangeId = window.glob.maxEntityChangeIdAtLoad;
let lastAcceptedEntityChangeSyncId = window.glob.maxEntityChangeSyncIdAtLoad; let lastAcceptedEntityChangeSyncId = window.glob.maxEntityChangeSyncIdAtLoad;
let lastProcessedEntityChangeId = window.glob.maxEntityChangeIdAtLoad; let lastProcessedEntityChangeId = window.glob.maxEntityChangeIdAtLoad;
let lastPingTs; let lastPingTs: number;
let frontendUpdateDataQueue = []; let frontendUpdateDataQueue: EntityChange[] = [];
function logError(message) { function logError(message: string) {
console.error(utils.now(), message); // needs to be separate from .trace() console.error(utils.now(), message); // needs to be separate from .trace()
if (ws && ws.readyState === 1) { if (ws && ws.readyState === 1) {
@ -26,7 +28,7 @@ function logError(message) {
} }
} }
function logInfo(message) { function logInfo(message: string) {
console.log(utils.now(), message); console.log(utils.now(), message);
if (ws && ws.readyState === 1) { if (ws && ws.readyState === 1) {
@ -40,17 +42,17 @@ function logInfo(message) {
window.logError = logError; window.logError = logError;
window.logInfo = logInfo; window.logInfo = logInfo;
function subscribeToMessages(messageHandler) { function subscribeToMessages(messageHandler: MessageHandler) {
messageHandlers.push(messageHandler); messageHandlers.push(messageHandler);
} }
// used to serialize frontend update operations // used to serialize frontend update operations
let consumeQueuePromise = null; let consumeQueuePromise: Promise<void> | null = null;
// to make sure each change event is processed only once. Not clear if this is still necessary // to make sure each change event is processed only once. Not clear if this is still necessary
const processedEntityChangeIds = new Set(); const processedEntityChangeIds = new Set();
function logRows(entityChanges) { function logRows(entityChanges: EntityChange[]) {
const filteredRows = entityChanges.filter(row => const filteredRows = entityChanges.filter(row =>
!processedEntityChangeIds.has(row.id) !processedEntityChangeIds.has(row.id)
&& (row.entityName !== 'options' || row.entityId !== 'openNoteContexts')); && (row.entityName !== 'options' || row.entityId !== 'openNoteContexts'));
@ -60,7 +62,7 @@ function logRows(entityChanges) {
} }
} }
async function executeFrontendUpdate(entityChanges) { async function executeFrontendUpdate(entityChanges: EntityChange[]) {
lastPingTs = Date.now(); lastPingTs = Date.now();
if (entityChanges.length > 0) { if (entityChanges.length > 0) {
@ -71,6 +73,10 @@ async function executeFrontendUpdate(entityChanges) {
// we set lastAcceptedEntityChangeId even before frontend update processing and send ping so that backend can start sending more updates // we set lastAcceptedEntityChangeId even before frontend update processing and send ping so that backend can start sending more updates
for (const entityChange of entityChanges) { for (const entityChange of entityChanges) {
if (!entityChange.id) {
continue;
}
lastAcceptedEntityChangeId = Math.max(lastAcceptedEntityChangeId, entityChange.id); lastAcceptedEntityChangeId = Math.max(lastAcceptedEntityChangeId, entityChange.id);
if (entityChange.isSynced) { if (entityChange.isSynced) {
@ -97,7 +103,7 @@ async function executeFrontendUpdate(entityChanges) {
} }
} }
async function handleMessage(event) { async function handleMessage(event: MessageEvent<any>) {
const message = JSON.parse(event.data); const message = JSON.parse(event.data);
for (const messageHandler of messageHandlers) { for (const messageHandler of messageHandlers) {
@ -126,24 +132,32 @@ async function handleMessage(event) {
toastService.showMessage(message.message); toastService.showMessage(message.message);
} }
else if (message.type === 'execute-script') { else if (message.type === 'execute-script') {
const bundleService = (await import("../services/bundle.js")).default; // TODO: Remove after porting the file
const froca = (await import("../services/froca.js")).default; // @ts-ignore
const bundleService = (await import("../services/bundle.js")).default as any;
// TODO: Remove after porting the file
// @ts-ignore
const froca = (await import("../services/froca.js")).default as any;
const originEntity = message.originEntityId ? await froca.getNote(message.originEntityId) : null; const originEntity = message.originEntityId ? await froca.getNote(message.originEntityId) : null;
bundleService.getAndExecuteBundle(message.currentNoteId, originEntity, message.script, message.params); bundleService.getAndExecuteBundle(message.currentNoteId, originEntity, message.script, message.params);
} }
} }
let entityChangeIdReachedListeners = []; let entityChangeIdReachedListeners: {
desiredEntityChangeId: number;
resolvePromise: () => void;
start: number;
}[] = [];
function waitForEntityChangeId(desiredEntityChangeId) { function waitForEntityChangeId(desiredEntityChangeId: number) {
if (desiredEntityChangeId <= lastProcessedEntityChangeId) { if (desiredEntityChangeId <= lastProcessedEntityChangeId) {
return Promise.resolve(); return Promise.resolve();
} }
console.debug(`Waiting for ${desiredEntityChangeId}, last processed is ${lastProcessedEntityChangeId}, last accepted ${lastAcceptedEntityChangeId}`); console.debug(`Waiting for ${desiredEntityChangeId}, last processed is ${lastProcessedEntityChangeId}, last accepted ${lastAcceptedEntityChangeId}`);
return new Promise((res, rej) => { return new Promise<void>((res, rej) => {
entityChangeIdReachedListeners.push({ entityChangeIdReachedListeners.push({
desiredEntityChangeId: desiredEntityChangeId, desiredEntityChangeId: desiredEntityChangeId,
resolvePromise: res, resolvePromise: res,
@ -178,7 +192,7 @@ async function consumeFrontendUpdateData() {
try { try {
await utils.timeLimit(frocaUpdater.processEntityChanges(nonProcessedEntityChanges), 30000); await utils.timeLimit(frocaUpdater.processEntityChanges(nonProcessedEntityChanges), 30000);
} }
catch (e) { catch (e: any) {
logError(`Encountered error ${e.message}: ${e.stack}, reloading frontend.`); logError(`Encountered error ${e.message}: ${e.stack}, reloading frontend.`);
if (!glob.isDev && !options.is('debugModeEnabled')) { if (!glob.isDev && !options.is('debugModeEnabled')) {
@ -196,7 +210,9 @@ async function consumeFrontendUpdateData() {
for (const entityChange of nonProcessedEntityChanges) { for (const entityChange of nonProcessedEntityChanges) {
processedEntityChangeIds.add(entityChange.id); processedEntityChangeIds.add(entityChange.id);
lastProcessedEntityChangeId = Math.max(lastProcessedEntityChangeId, entityChange.id); if (entityChange.id) {
lastProcessedEntityChangeId = Math.max(lastProcessedEntityChangeId, entityChange.id);
}
} }
} }
@ -248,3 +264,4 @@ export default {
waitForMaxKnownEntityChangeId, waitForMaxKnownEntityChangeId,
getMaxKnownEntityChangeSyncId: () => lastAcceptedEntityChangeSyncId getMaxKnownEntityChangeSyncId: () => lastAcceptedEntityChangeSyncId
}; };

View File

@ -26,6 +26,8 @@ interface CustomGlobals {
baseApiUrl: string; baseApiUrl: string;
isProtectedSessionAvailable: boolean; isProtectedSessionAvailable: boolean;
isDev: boolean; isDev: boolean;
maxEntityChangeIdAtLoad: number;
maxEntityChangeSyncIdAtLoad: number;
} }
type RequireMethod = (moduleName: string) => any; type RequireMethod = (moduleName: string) => any;