Migrate websocket eventbus messages to wps (#367)

This migrates all remaining eventbus events sent over the websocket to
use the wps interface. WPS is more flexible for registering events and
callbacks and provides support for more reliable unsubscribes and
resubscribes.
This commit is contained in:
Evan Simkowitz
2024-09-11 18:03:55 -07:00
committed by GitHub
parent 4bfb96b001
commit 936d4bfb30
29 changed files with 519 additions and 549 deletions
+1
View File
@@ -28,6 +28,7 @@ func GenerateWshClient() {
"github.com/wavetermdev/waveterm/pkg/wshrpc",
"github.com/wavetermdev/waveterm/pkg/waveobj",
"github.com/wavetermdev/waveterm/pkg/wconfig",
"github.com/wavetermdev/waveterm/pkg/wps",
})
wshDeclMap := wshrpc.GenerateWshCommandDeclMap()
for _, key := range utilfn.GetOrderedMapKeys(wshDeclMap) {
+3 -2
View File
@@ -10,6 +10,7 @@ import (
"github.com/spf13/cobra"
"github.com/wavetermdev/waveterm/pkg/waveobj"
"github.com/wavetermdev/waveterm/pkg/wps"
"github.com/wavetermdev/waveterm/pkg/wshrpc"
"github.com/wavetermdev/waveterm/pkg/wshrpc/wshclient"
)
@@ -64,11 +65,11 @@ func editorRun(cmd *cobra.Command, args []string) {
return
}
doneCh := make(chan bool)
RpcClient.EventListener.On("blockclose", func(event *wshrpc.WaveEvent) {
RpcClient.EventListener.On("blockclose", func(event *wps.WaveEvent) {
if event.HasScope(blockRef.String()) {
close(doneCh)
}
})
wshclient.EventSubCommand(RpcClient, wshrpc.SubscriptionRequest{Event: "blockclose", Scopes: []string{blockRef.String()}}, nil)
wshclient.EventSubCommand(RpcClient, wps.SubscriptionRequest{Event: "blockclose", Scopes: []string{blockRef.String()}}, nil)
<-doneCh
}
+9 -4
View File
@@ -2,9 +2,10 @@
// SPDX-License-Identifier: Apache-2.0
import { NumActiveConnColors } from "@/app/block/blockframe";
import { getConnStatusAtom, waveEventSubscribe, WOS } from "@/app/store/global";
import { getConnStatusAtom, WOS } from "@/app/store/global";
import * as services from "@/app/store/services";
import { makeORef } from "@/app/store/wos";
import { waveEventSubscribe } from "@/store/wps";
import * as util from "@/util/util";
import clsx from "clsx";
import * as jotai from "jotai";
@@ -160,9 +161,13 @@ export const ControllerStatusIcon = React.memo(({ blockId }: { blockId: string }
setGotInitialStatus(true);
setControllerStatus(rts);
});
const unsubFn = waveEventSubscribe("controllerstatus", makeORef("block", blockId), (event) => {
const cstatus: BlockControllerRuntimeStatus = event.data;
setControllerStatus(cstatus);
const unsubFn = waveEventSubscribe({
eventType: "controllerstatus",
scope: makeORef("block", blockId),
handler: (event) => {
const cstatus: BlockControllerRuntimeStatus = event.data;
setControllerStatus(cstatus);
},
});
return () => {
unsubFn();
File diff suppressed because it is too large Load Diff
+13 -13
View File
@@ -5,10 +5,10 @@
import { getWebServerEndpoint } from "@/util/endpoints";
import { fetch } from "@/util/fetchutil";
import * as jotai from "jotai";
import * as React from "react";
import { atom, Atom, Getter, PrimitiveAtom, Setter, useAtomValue } from "jotai";
import { useEffect } from "react";
import { atoms, globalStore } from "./global";
import * as services from "./services";
import { ObjectService } from "./services";
type WaveObjectDataItemType<T extends WaveObj> = {
value: T;
@@ -17,7 +17,7 @@ type WaveObjectDataItemType<T extends WaveObj> = {
type WaveObjectValue<T extends WaveObj> = {
pendingPromise: Promise<T>;
dataAtom: jotai.PrimitiveAtom<WaveObjectDataItemType<T>>;
dataAtom: PrimitiveAtom<WaveObjectDataItemType<T>>;
refCount: number;
holdTime: number;
};
@@ -132,7 +132,7 @@ const defaultHoldTime = 5000; // 5-seconds
function createWaveValueObject<T extends WaveObj>(oref: string, shouldFetch: boolean): WaveObjectValue<T> {
const wov = { pendingPromise: null, dataAtom: null, refCount: 0, holdTime: Date.now() + 5000 };
wov.dataAtom = jotai.atom({ value: null, loading: true });
wov.dataAtom = atom({ value: null, loading: true });
if (!shouldFetch) {
return wov;
}
@@ -180,7 +180,7 @@ function loadAndPinWaveObject<T extends WaveObj>(oref: string): Promise<T> {
function getWaveObjectAtom<T extends WaveObj>(oref: string): WritableWaveObjectAtom<T> {
const wov = getWaveObjectValue<T>(oref);
return jotai.atom(
return atom(
(get) => get(wov.dataAtom).value,
(_get, set, value: T) => {
setObjectValue(value, set, true);
@@ -188,9 +188,9 @@ function getWaveObjectAtom<T extends WaveObj>(oref: string): WritableWaveObjectA
);
}
function getWaveObjectLoadingAtom(oref: string): jotai.Atom<boolean> {
function getWaveObjectLoadingAtom(oref: string): Atom<boolean> {
const wov = getWaveObjectValue(oref);
return jotai.atom((get) => {
return atom((get) => {
const dataValue = get(wov.dataAtom);
if (dataValue.loading) {
return null;
@@ -201,13 +201,13 @@ function getWaveObjectLoadingAtom(oref: string): jotai.Atom<boolean> {
function useWaveObjectValue<T extends WaveObj>(oref: string): [T, boolean] {
const wov = getWaveObjectValue<T>(oref);
React.useEffect(() => {
useEffect(() => {
wov.refCount++;
return () => {
wov.refCount--;
};
}, [oref]);
const atomVal = jotai.useAtomValue(wov.dataAtom);
const atomVal = useAtomValue(wov.dataAtom);
return [atomVal.value, atomVal.loading];
}
@@ -254,7 +254,7 @@ function cleanWaveObjectCache() {
// gets the value of a WaveObject from the cache.
// should provide getFn if it is available (e.g. inside of a jotai atom)
// otherwise it will use the globalStore.get function
function getObjectValue<T extends WaveObj>(oref: string, getFn?: jotai.Getter): T {
function getObjectValue<T extends WaveObj>(oref: string, getFn?: Getter): T {
const wov = getWaveObjectValue<T>(oref);
if (getFn == null) {
getFn = globalStore.get;
@@ -266,7 +266,7 @@ function getObjectValue<T extends WaveObj>(oref: string, getFn?: jotai.Getter):
// sets the value of a WaveObject in the cache.
// should provide setFn if it is available (e.g. inside of a jotai atom)
// otherwise it will use the globalStore.set function
function setObjectValue<T extends WaveObj>(value: T, setFn?: jotai.Setter, pushToServer?: boolean) {
function setObjectValue<T extends WaveObj>(value: T, setFn?: Setter, pushToServer?: boolean) {
const oref = makeORef(value.otype, value.oid);
const wov = getWaveObjectValue(oref, false);
if (wov === undefined) {
@@ -277,7 +277,7 @@ function setObjectValue<T extends WaveObj>(value: T, setFn?: jotai.Setter, pushT
}
setFn(wov.dataAtom, { value: value, loading: false });
if (pushToServer) {
services.ObjectService.UpdateObject(value, false);
ObjectService.UpdateObject(value, false);
}
}
+141
View File
@@ -0,0 +1,141 @@
import { isBlank } from "@/util/util";
import { Subject } from "rxjs";
import { sendRawRpcMessage, setRpcEventHandlerFn } from "./wshrpc";
type WaveEventSubject = {
handler: (event: WaveEvent) => void;
scope?: string;
};
type WaveEventSubjectContainer = WaveEventSubject & {
id: string;
};
type WaveEventSubscription = WaveEventSubject & {
eventType: string;
};
type WaveEventUnsubscribe = {
id: string;
eventType: string;
};
// key is "eventType" or "eventType|oref"
const fileSubjects = new Map<string, SubjectWithRef<WSFileEventData>>();
const waveEventSubjects = new Map<string, WaveEventSubjectContainer[]>();
function makeWaveReSubCommand(eventType: string): RpcMessage {
let subjects = waveEventSubjects.get(eventType);
if (subjects == null) {
return { command: "eventunsub", data: eventType };
}
let subreq: SubscriptionRequest = { event: eventType, scopes: [], allscopes: false };
for (const scont of subjects) {
if (isBlank(scont.scope)) {
subreq.allscopes = true;
subreq.scopes = [];
break;
}
subreq.scopes.push(scont.scope);
}
return { command: "eventsub", data: subreq };
}
function updateWaveEventSub(eventType: string) {
const command = makeWaveReSubCommand(eventType);
// console.log("updateWaveEventSub", eventType, command);
sendRawRpcMessage(command);
}
function waveEventSubscribe(...subscriptions: WaveEventSubscription[]): () => void {
const unsubs: WaveEventUnsubscribe[] = [];
const eventTypeSet = new Set<string>();
for (const subscription of subscriptions) {
// console.log("waveEventSubscribe", subscription);
if (subscription.handler == null) {
return;
}
const id: string = crypto.randomUUID();
let subjects = waveEventSubjects.get(subscription.eventType);
if (subjects == null) {
subjects = [];
waveEventSubjects.set(subscription.eventType, subjects);
}
const subcont: WaveEventSubjectContainer = { id, handler: subscription.handler, scope: subscription.scope };
subjects.push(subcont);
unsubs.push({ id, eventType: subscription.eventType });
eventTypeSet.add(subscription.eventType);
}
for (const eventType of eventTypeSet) {
updateWaveEventSub(eventType);
}
return () => waveEventUnsubscribe(...unsubs);
}
function waveEventUnsubscribe(...unsubscribes: WaveEventUnsubscribe[]) {
const eventTypeSet = new Set<string>();
for (const unsubscribe of unsubscribes) {
let subjects = waveEventSubjects.get(unsubscribe.eventType);
if (subjects == null) {
return;
}
const idx = subjects.findIndex((s) => s.id === unsubscribe.id);
if (idx === -1) {
return;
}
subjects.splice(idx, 1);
if (subjects.length === 0) {
waveEventSubjects.delete(unsubscribe.eventType);
}
eventTypeSet.add(unsubscribe.eventType);
}
for (const eventType of eventTypeSet) {
updateWaveEventSub(eventType);
}
}
function getFileSubject(zoneId: string, fileName: string): SubjectWithRef<WSFileEventData> {
const subjectKey = zoneId + "|" + fileName;
let subject = fileSubjects.get(subjectKey);
if (subject == null) {
subject = new Subject<any>() as any;
subject.refCount = 0;
subject.release = () => {
subject.refCount--;
if (subject.refCount === 0) {
subject.complete();
fileSubjects.delete(subjectKey);
}
};
fileSubjects.set(subjectKey, subject);
}
subject.refCount++;
return subject;
}
function handleWaveEvent(event: WaveEvent) {
// console.log("handleWaveEvent", event);
const subjects = waveEventSubjects.get(event.event);
if (subjects == null) {
return;
}
for (const scont of subjects) {
if (isBlank(scont.scope)) {
scont.handler(event);
continue;
}
if (event.scopes == null) {
continue;
}
if (event.scopes.includes(scont.scope)) {
scont.handler(event);
}
}
}
function initWps() {
setRpcEventHandlerFn(handleWaveEvent);
}
export { getFileSubject, initWps, waveEventSubscribe, waveEventUnsubscribe };
+14 -36
View File
@@ -1,45 +1,31 @@
// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
import * as jotai from "jotai";
import { sprintf } from "sprintf-js";
const MaxWebSocketSendSize = 1024 * 1024; // 1MB
type JotaiStore = {
get: <Value>(atom: jotai.Atom<Value>) => Value;
set: <Value>(atom: jotai.WritableAtom<Value, [Value], void>, value: Value) => void;
};
type WSEventCallback = (arg0: WSEventType) => void;
class WSControl {
wsConn: any;
open: jotai.WritableAtom<boolean, [boolean], void>;
open: boolean;
opening: boolean = false;
reconnectTimes: number = 0;
msgQueue: any[] = [];
windowId: string;
messageCallback: (any) => void = null;
messageCallback: WSEventCallback;
watchSessionId: string = null;
watchScreenId: string = null;
wsLog: string[] = [];
authKey: string;
baseHostPort: string;
lastReconnectTime: number = 0;
jotaiStore: JotaiStore;
constructor(
baseHostPort: string,
jotaiStore: JotaiStore,
windowId: string,
authKey: string,
messageCallback: (any) => void
) {
constructor(baseHostPort: string, windowId: string, messageCallback: WSEventCallback) {
this.baseHostPort = baseHostPort;
this.messageCallback = messageCallback;
this.windowId = windowId;
this.authKey = authKey;
this.open = jotai.atom(false);
this.jotaiStore = jotaiStore;
this.open = false;
setInterval(this.sendPing.bind(this), 5000);
}
@@ -51,16 +37,8 @@ class WSControl {
}
}
setOpen(val: boolean) {
this.jotaiStore.set(this.open, val);
}
isOpen() {
return this.jotaiStore.get(this.open);
}
connectNow(desc: string) {
if (this.isOpen()) {
if (this.open) {
return;
}
this.lastReconnectTime = Date.now();
@@ -75,7 +53,7 @@ class WSControl {
}
reconnect(forceClose?: boolean) {
if (this.isOpen()) {
if (this.open) {
if (forceClose) {
this.wsConn.close(); // this will force a reconnect
}
@@ -109,8 +87,8 @@ class WSControl {
} else {
this.log("connection error/disconnected");
}
if (this.isOpen() || this.opening) {
this.setOpen(false);
if (this.open || this.opening) {
this.open = false;
this.opening = false;
this.reconnect();
}
@@ -118,14 +96,14 @@ class WSControl {
onopen() {
this.log("connection open");
this.setOpen(true);
this.open = true;
this.opening = false;
this.runMsgQueue();
// reconnectTimes is reset in onmessage:hello
}
runMsgQueue() {
if (!this.isOpen()) {
if (!this.open) {
return;
}
if (this.msgQueue.length == 0) {
@@ -168,14 +146,14 @@ class WSControl {
}
sendPing() {
if (!this.isOpen()) {
if (!this.open) {
return;
}
this.wsConn.send(JSON.stringify({ type: "ping", stime: Date.now() }));
}
sendMessage(data: WSCommandType) {
if (!this.isOpen()) {
if (!this.open) {
return;
}
const msg = JSON.stringify(data);
@@ -188,7 +166,7 @@ class WSControl {
}
pushMessage(data: WSCommandType) {
if (!this.isOpen()) {
if (!this.open) {
this.msgQueue.push(data);
return;
}
+34 -8
View File
@@ -1,7 +1,8 @@
// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
import { globalWS } from "./global";
import { getWSServerEndpoint } from "@/util/endpoints";
import { WSControl } from "./ws";
type RpcEntry = {
reqId: string;
@@ -119,12 +120,12 @@ async function* rpcResponseGenerator(
function sendRpcCancel(reqid: string) {
const rpcMsg: RpcMessage = { reqid: reqid, cancel: true };
const wsMsg: WSRpcCommand = { wscommand: "rpc", message: rpcMsg };
globalWS.pushMessage(wsMsg);
sendWSCommand(wsMsg);
}
function sendRpcCommand(msg: RpcMessage): AsyncGenerator<RpcMessage, void, boolean> {
const wsMsg: WSRpcCommand = { wscommand: "rpc", message: msg };
globalWS.pushMessage(wsMsg);
sendWSCommand(wsMsg);
if (msg.reqid == null) {
return null;
}
@@ -135,19 +136,30 @@ function sendRpcCommand(msg: RpcMessage): AsyncGenerator<RpcMessage, void, boole
function sendRawRpcMessage(msg: RpcMessage) {
const wsMsg: WSRpcCommand = { wscommand: "rpc", message: msg };
globalWS.pushMessage(wsMsg);
sendWSCommand(wsMsg);
}
const notFoundLogMap = new Map<string, boolean>();
function handleIncomingRpcMessage(msg: RpcMessage, eventHandlerFn: (event: WaveEvent) => void) {
let rpcEventHandlerFn: (evt: WaveEvent) => void;
function setRpcEventHandlerFn(fn: (evt: WaveEvent) => void) {
if (rpcEventHandlerFn) {
throw new Error("wshrpc.setRpcEventHandlerFn called more than once");
}
rpcEventHandlerFn = fn;
}
function handleIncomingRpcMessage(event: WSEventType) {
if (event.eventtype !== "rpc") {
console.warn("unsupported ws event type:", event.eventtype, event);
}
const msg: RpcMessage = event.data;
const isRequest = msg.command != null || msg.reqid != null;
if (isRequest) {
// handle events
if (msg.command == "eventrecv") {
if (eventHandlerFn != null) {
eventHandlerFn(msg.data);
}
rpcEventHandlerFn?.(msg.data);
return;
}
if (msg.command == "message") {
@@ -195,10 +207,24 @@ if (globalThis.window != null) {
globalThis["consumeGenerator"] = consumeGenerator;
}
let globalWS: WSControl;
function initWshrpc(windowId: string) {
globalWS = new WSControl(getWSServerEndpoint(), windowId, handleIncomingRpcMessage);
globalWS.connectNow("connectWshrpc");
}
function sendWSCommand(cmd: WSCommandType) {
globalWS?.sendMessage(cmd);
}
export {
handleIncomingRpcMessage,
initWshrpc,
sendRawRpcMessage,
sendRpcCommand,
sendWSCommand,
setRpcEventHandlerFn,
wshServerRpcHelper_call,
wshServerRpcHelper_responsestream,
};
+13 -8
View File
@@ -3,7 +3,7 @@
import { useHeight } from "@/app/hook/useHeight";
import { useWidth } from "@/app/hook/useWidth";
import { getConnStatusAtom, globalStore, waveEventSubscribe, WOS } from "@/store/global";
import { getConnStatusAtom, globalStore, WOS } from "@/store/global";
import { WshServer } from "@/store/wshserver";
import * as util from "@/util/util";
import * as Plot from "@observablehq/plot";
@@ -12,6 +12,7 @@ import * as htl from "htl";
import * as jotai from "jotai";
import * as React from "react";
import { waveEventSubscribe } from "@/app/store/wps";
import "./cpuplot.less";
const DefaultNumPoints = 120;
@@ -192,13 +193,17 @@ function CpuPlotView({ model, blockId }: CpuPlotViewProps) {
lastConnName.current = connName;
model.loadInitialData();
}
const unsubFn = waveEventSubscribe("sysinfo", connName, (event: WaveEvent) => {
const loading = globalStore.get(model.loadingAtom);
if (loading) {
return;
}
const dataItem = convertWaveEventToDataItem(event);
addPlotData([dataItem]);
const unsubFn = waveEventSubscribe({
eventType: "sysinfo",
scope: connName,
handler: (event) => {
const loading = globalStore.get(model.loadingAtom);
if (loading) {
return;
}
const dataItem = convertWaveEventToDataItem(event);
addPlotData([dataItem]);
},
});
return () => {
unsubFn();
+10 -6
View File
@@ -1,9 +1,10 @@
// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
import { waveEventSubscribe } from "@/app/store/wps";
import { WshServer } from "@/app/store/wshserver";
import { VDomView } from "@/app/view/term/vdom";
import { WOS, atoms, getConnStatusAtom, getEventORefSubject, globalStore, useSettingsPrefixAtom } from "@/store/global";
import { WOS, atoms, getConnStatusAtom, globalStore, useSettingsPrefixAtom } from "@/store/global";
import * as services from "@/store/services";
import * as keyutil from "@/util/keyutil";
import * as util from "@/util/util";
@@ -379,12 +380,15 @@ const TerminalView = ({ blockId, model }: TerminalViewProps) => {
initialRTStatus.then((rts) => {
updateShellProcStatus(rts?.shellprocstatus);
});
const bcSubject = getEventORefSubject("blockcontroller:status", WOS.makeORef("block", blockId));
const sub = bcSubject.subscribe((data: WSEventType) => {
let bcRTS: BlockControllerRuntimeStatus = data.data;
updateShellProcStatus(bcRTS?.shellprocstatus);
return waveEventSubscribe({
eventType: "controllerstatus",
scope: WOS.makeORef("block", blockId),
handler: (event) => {
console.log("term waveEvent handler", event);
let bcRTS: BlockControllerRuntimeStatus = event.data;
updateShellProcStatus(bcRTS?.shellprocstatus);
},
});
return () => sub.unsubscribe();
}, []);
let stickerConfig = {
+3 -10
View File
@@ -1,17 +1,10 @@
// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
import { getFileSubject } from "@/app/store/wps";
import { sendWSCommand } from "@/app/store/wshrpc";
import { WshServer } from "@/app/store/wshserver";
import {
PLATFORM,
WOS,
atoms,
fetchWaveFile,
getFileSubject,
globalStore,
openLink,
sendWSCommand,
} from "@/store/global";
import { PLATFORM, WOS, atoms, fetchWaveFile, globalStore, openLink } from "@/store/global";
import * as services from "@/store/services";
import * as util from "@/util/util";
import { base64ToArray, fireAndForget } from "@/util/util";
+3 -3
View File
@@ -444,7 +444,7 @@ declare global {
display: StickerDisplayOptsType;
};
// wshrpc.SubscriptionRequest
// wps.SubscriptionRequest
type SubscriptionRequest = {
event: string;
scopes?: string[];
@@ -560,7 +560,7 @@ declare global {
data: any;
};
// eventbus.WSFileEventData
// wps.WSFileEventData
type WSFileEventData = {
zoneid: string;
filename: string;
@@ -579,7 +579,7 @@ declare global {
fullconfig: FullConfigType;
};
// wshrpc.WaveEvent
// wps.WaveEvent
type WaveEvent = {
event: string;
scopes?: string[];
+21 -15
View File
@@ -1,11 +1,15 @@
// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
import { App } from "@/app/app";
import {
registerControlShiftStateUpdateHandler,
registerElectronReinjectKeyHandler,
registerGlobalKeys,
} from "@/app/store/keymodel";
import { FileService, ObjectService } from "@/app/store/services";
import { initWps } from "@/app/store/wps";
import { initWshrpc } from "@/app/store/wshrpc";
import { WshServer } from "@/app/store/wshserver";
import { loadMonaco } from "@/app/view/codeeditor/codeeditor";
import { getLayoutModelForActiveTab } from "@/layout/index";
@@ -15,19 +19,16 @@ import {
countersPrint,
getApi,
globalStore,
globalWS,
initGlobal,
initWS,
initGlobalWaveEventSubs,
loadConnStatus,
subscribeToConnEvents,
} from "@/store/global";
import * as services from "@/store/services";
import * as WOS from "@/store/wos";
import * as keyutil from "@/util/keyutil";
import * as React from "react";
import { loadFonts } from "@/util/fontutil";
import { setKeyUtilPlatform } from "@/util/keyutil";
import { createElement } from "react";
import { createRoot } from "react-dom/client";
import { App } from "./app/app";
import { loadFonts } from "./util/fontutil";
const platform = getApi().getPlatform();
const urlParams = new URLSearchParams(window.location.search);
@@ -39,10 +40,9 @@ console.log("clientid", clientId, "windowid", windowId);
initGlobal({ clientId, windowId, platform, environment: "renderer" });
keyutil.setKeyUtilPlatform(platform);
setKeyUtilPlatform(platform);
loadFonts();
(window as any).globalWS = globalWS;
(window as any).WOS = WOS;
(window as any).globalStore = globalStore;
(window as any).globalAtoms = atoms;
@@ -56,27 +56,33 @@ document.title = `The Next Wave (${windowId.substring(0, 8)})`;
document.addEventListener("DOMContentLoaded", async () => {
console.log("DOMContentLoaded");
// Init WPS event handlers
initWshrpc(windowId);
await loadConnStatus();
initWps();
initGlobalWaveEventSubs();
subscribeToConnEvents();
// ensures client/window/workspace are loaded into the cache before rendering
const client = await WOS.loadAndPinWaveObject<Client>(WOS.makeORef("client", clientId));
const waveWindow = await WOS.loadAndPinWaveObject<WaveWindow>(WOS.makeORef("window", windowId));
await WOS.loadAndPinWaveObject<Workspace>(WOS.makeORef("workspace", waveWindow.workspaceid));
const initialTab = await WOS.loadAndPinWaveObject<Tab>(WOS.makeORef("tab", waveWindow.activetabid));
await WOS.loadAndPinWaveObject<LayoutState>(WOS.makeORef("layout", initialTab.layoutstate));
initWS();
await loadConnStatus();
subscribeToConnEvents();
registerGlobalKeys();
registerElectronReinjectKeyHandler();
registerControlShiftStateUpdateHandler();
setTimeout(loadMonaco, 30);
const fullConfig = await services.FileService.GetFullConfig();
const fullConfig = await FileService.GetFullConfig();
console.log("fullconfig", fullConfig);
globalStore.set(atoms.fullConfigAtom, fullConfig);
const prtn = services.ObjectService.SetActiveTab(waveWindow.activetabid); // no need to wait
const prtn = ObjectService.SetActiveTab(waveWindow.activetabid); // no need to wait
prtn.catch((e) => {
console.log("error on initial SetActiveTab", e);
});
const reactElem = React.createElement(App, null, null);
const reactElem = createElement(App, null, null);
const elem = document.getElementById("main");
const root = createRoot(elem);
document.fonts.ready.then(() => {
+16 -21
View File
@@ -14,7 +14,6 @@ import (
"sync"
"time"
"github.com/wavetermdev/waveterm/pkg/eventbus"
"github.com/wavetermdev/waveterm/pkg/filestore"
"github.com/wavetermdev/waveterm/pkg/remote"
"github.com/wavetermdev/waveterm/pkg/remote/conncontroller"
@@ -112,20 +111,14 @@ func (bc *BlockController) UpdateControllerAndSendUpdate(updateFn func() bool) {
if sendUpdate {
rtStatus := bc.GetRuntimeStatus()
log.Printf("sending blockcontroller update %#v\n", rtStatus)
go eventbus.SendEvent(eventbus.WSEventType{
EventType: eventbus.WSEvent_BlockControllerStatus,
ORef: waveobj.MakeORef(waveobj.OType_Block, bc.BlockId).String(),
Data: rtStatus,
})
waveEvent := wshrpc.WaveEvent{
Event: wshrpc.Event_ControllerStatus,
wps.Broker.Publish(wps.WaveEvent{
Event: wps.Event_ControllerStatus,
Scopes: []string{
waveobj.MakeORef(waveobj.OType_Tab, bc.TabId).String(),
waveobj.MakeORef(waveobj.OType_Block, bc.BlockId).String(),
},
Data: rtStatus,
}
wps.Broker.Publish(waveEvent)
})
}
}
@@ -139,13 +132,13 @@ func HandleTruncateBlockFile(blockId string, blockFile string) error {
if err != nil {
return fmt.Errorf("error truncating blockfile: %w", err)
}
eventbus.SendEvent(eventbus.WSEventType{
EventType: eventbus.WSEvent_BlockFile,
ORef: waveobj.MakeORef(waveobj.OType_Block, blockId).String(),
Data: &eventbus.WSFileEventData{
wps.Broker.Publish(wps.WaveEvent{
Event: wps.Event_BlockFile,
Scopes: []string{waveobj.MakeORef(waveobj.OType_Block, blockId).String()},
Data: &wps.WSFileEventData{
ZoneId: blockId,
FileName: blockFile,
FileOp: eventbus.FileOp_Truncate,
FileOp: wps.FileOp_Truncate,
},
})
return nil
@@ -159,13 +152,15 @@ func HandleAppendBlockFile(blockId string, blockFile string, data []byte) error
if err != nil {
return fmt.Errorf("error appending to blockfile: %w", err)
}
eventbus.SendEvent(eventbus.WSEventType{
EventType: eventbus.WSEvent_BlockFile,
ORef: waveobj.MakeORef(waveobj.OType_Block, blockId).String(),
Data: &eventbus.WSFileEventData{
wps.Broker.Publish(wps.WaveEvent{
Event: wps.Event_BlockFile,
Scopes: []string{
waveobj.MakeORef(waveobj.OType_Block, blockId).String(),
},
Data: &wps.WSFileEventData{
ZoneId: blockId,
FileName: blockFile,
FileOp: eventbus.FileOp_Append,
FileOp: wps.FileOp_Append,
Data64: base64.StdEncoding.EncodeToString(data),
},
})
@@ -423,7 +418,7 @@ func setTermSize(ctx context.Context, blockId string, termSize waveobj.TermSize)
}
bdata.RuntimeOpts.TermSize = termSize
updates := waveobj.ContextGetUpdatesRtn(ctx)
eventbus.SendUpdateEvents(updates)
wps.Broker.SendUpdateEvents(updates)
return nil
}
+3 -56
View File
@@ -15,15 +15,9 @@ import (
)
const (
WSEvent_WaveObjUpdate = "waveobj:update"
WSEvent_BlockFile = "blockfile"
WSEvent_Config = "config"
WSEvent_UserInput = "userinput"
WSEvent_BlockControllerStatus = "blockcontroller:status"
WSEvent_LayoutAction = "layoutaction"
WSEvent_ElectronNewWindow = "electron:newwindow"
WSEvent_ElectronCloseWindow = "electron:closewindow"
WSEvent_Rpc = "rpc"
WSEvent_ElectronNewWindow = "electron:newwindow"
WSEvent_ElectronCloseWindow = "electron:closewindow"
WSEvent_Rpc = "rpc"
)
type WSEventType struct {
@@ -32,19 +26,6 @@ type WSEventType struct {
Data any `json:"data"`
}
const (
FileOp_Append = "append"
FileOp_Truncate = "truncate"
FileOp_Invalidate = "invalidate"
)
type WSFileEventData struct {
ZoneId string `json:"zoneid"`
FileName string `json:"filename"`
FileOp string `json:"fileop"`
Data64 string `json:"data64"`
}
type WindowWatchData struct {
WindowWSCh chan any
WaveWindowId string
@@ -97,40 +78,6 @@ func BusyWaitForWindowId(windowId string, timeout time.Duration) bool {
}
}
func getAllWatches() []*WindowWatchData {
globalLock.Lock()
defer globalLock.Unlock()
watches := make([]*WindowWatchData, 0, len(wsMap))
for _, wdata := range wsMap {
watches = append(watches, wdata)
}
return watches
}
func SendEventToWindow(windowId string, event WSEventType) {
wwdArr := getWindowWatchesForWindowId(windowId)
for _, wdata := range wwdArr {
wdata.WindowWSCh <- event
}
}
func SendEvent(event WSEventType) {
wwdArr := getAllWatches()
for _, wdata := range wwdArr {
wdata.WindowWSCh <- event
}
}
func SendUpdateEvents(updates waveobj.UpdatesRtnType) {
for _, update := range updates {
SendEvent(WSEventType{
EventType: WSEvent_WaveObjUpdate,
ORef: waveobj.MakeORef(update.OType, update.OID).String(),
Data: update,
})
}
}
func SendEventToElectron(event WSEventType) {
barr, err := json.Marshal(event)
if err != nil {
+2 -2
View File
@@ -85,8 +85,8 @@ func (conn *SSHConn) DeriveConnStatus() wshrpc.ConnStatus {
func (conn *SSHConn) FireConnChangeEvent() {
status := conn.DeriveConnStatus()
event := wshrpc.WaveEvent{
Event: wshrpc.Event_ConnChange,
event := wps.WaveEvent{
Event: wps.Event_ConnChange,
Scopes: []string{
fmt.Sprintf("connection:%s", conn.GetName()),
},
+2 -1
View File
@@ -20,6 +20,7 @@ import (
"github.com/wavetermdev/waveterm/pkg/waveobj"
"github.com/wavetermdev/waveterm/pkg/wconfig"
"github.com/wavetermdev/waveterm/pkg/web/webcmd"
"github.com/wavetermdev/waveterm/pkg/wps"
"github.com/wavetermdev/waveterm/pkg/wshrpc"
"github.com/wavetermdev/waveterm/pkg/wshutil"
)
@@ -33,7 +34,7 @@ var ExtraTypes = []any{
service.WebReturnType{},
waveobj.UIContext{},
eventbus.WSEventType{},
eventbus.WSFileEventData{},
wps.WSFileEventData{},
waveobj.LayoutActionData{},
filestore.WaveFile{},
wconfig.FullConfigType{},
+4 -4
View File
@@ -11,7 +11,7 @@ import (
"time"
"github.com/google/uuid"
"github.com/wavetermdev/waveterm/pkg/eventbus"
"github.com/wavetermdev/waveterm/pkg/wps"
)
var MainUserInputHandler = UserInputHandler{Channels: make(map[string](chan *UserInputResponse), 1)}
@@ -60,9 +60,9 @@ func (ui *UserInputHandler) unregisterChannel(id string) {
}
func (ui *UserInputHandler) sendRequestToFrontend(request *UserInputRequest) {
eventbus.SendEvent(eventbus.WSEventType{
EventType: eventbus.WSEvent_UserInput,
Data: request,
wps.Broker.Publish(wps.WaveEvent{
Event: wps.Event_UserInput,
Data: request,
})
}
+4 -4
View File
@@ -10,8 +10,8 @@ import (
"sync"
"github.com/fsnotify/fsnotify"
"github.com/wavetermdev/waveterm/pkg/eventbus"
"github.com/wavetermdev/waveterm/pkg/wavebase"
"github.com/wavetermdev/waveterm/pkg/wps"
)
var configDirAbsPath = filepath.Join(wavebase.GetWaveHomeDir(), wavebase.ConfigDir)
@@ -95,9 +95,9 @@ func (w *Watcher) Close() {
func (w *Watcher) broadcast(message WatcherUpdate) {
// send to frontend
eventbus.SendEvent(eventbus.WSEventType{
EventType: eventbus.WSEvent_Config,
Data: message,
wps.Broker.Publish(wps.WaveEvent{
Event: wps.Event_Config,
Data: message,
})
}
+2 -3
View File
@@ -13,7 +13,6 @@ import (
"github.com/wavetermdev/waveterm/pkg/blockcontroller"
"github.com/wavetermdev/waveterm/pkg/waveobj"
"github.com/wavetermdev/waveterm/pkg/wps"
"github.com/wavetermdev/waveterm/pkg/wshrpc"
"github.com/wavetermdev/waveterm/pkg/wstore"
)
@@ -36,8 +35,8 @@ func DeleteBlock(ctx context.Context, tabId string, blockId string) error {
}
func sendBlockCloseEvent(tabId string, blockId string) {
waveEvent := wshrpc.WaveEvent{
Event: wshrpc.Event_BlockClose,
waveEvent := wps.WaveEvent{
Event: wps.Event_BlockClose,
Scopes: []string{
waveobj.MakeORef(waveobj.OType_Tab, tabId).String(),
waveobj.MakeORef(waveobj.OType_Block, blockId).String(),

Some files were not shown because too many files have changed in this diff Show More