Refactored the Socket connection and callbacks to work properly

This commit is contained in:
inaseem 2021-11-02 15:34:10 +05:30
parent 4068822489
commit dbf6763a99
5 changed files with 233 additions and 185 deletions

View File

@ -9,6 +9,7 @@ import React from "react";
import { createMessageConnection } from "vscode-jsonrpc"; import { createMessageConnection } from "vscode-jsonrpc";
import RijuMessageReader from "../services/RijuMessageReader"; import RijuMessageReader from "../services/RijuMessageReader";
import RijuMessageWriter from "../services/RijuMessageWriter"; import RijuMessageWriter from "../services/RijuMessageWriter";
import { SocketManager } from "../services/WS";
import { EventEmitter } from "../utils/EventEmitter"; import { EventEmitter } from "../utils/EventEmitter";
let clientDisposable = null; let clientDisposable = null;
@ -20,11 +21,11 @@ const RijuEditor = (props) => {
const monacoRef = React.useRef(); const monacoRef = React.useRef();
React.useEffect(() => { React.useEffect(() => {
EventEmitter.subscribe("lspStarted", (data) => { const token1 = EventEmitter.subscribe("lspStarted", (data) => {
const { message, socket } = data; const { message } = data;
initLSP(socket, message, monacoRef.current, editorRef.current); initLSP(message, monacoRef.current, editorRef.current);
}); });
EventEmitter.subscribe("lspStopped", () => { const token2 = EventEmitter.subscribe("lspStopped", () => {
if (clientDisposable) { if (clientDisposable) {
clientDisposable.dispose(); clientDisposable.dispose();
clientDisposable = null; clientDisposable = null;
@ -34,9 +35,11 @@ const RijuEditor = (props) => {
servicesDisposable = null; servicesDisposable = null;
} }
}); });
() => EventEmitter.unsubcribe(token1, token2);
}, []); }, []);
const initLSP = (socket, message, monaco, editor) => { const initLSP = (message, monaco, editor) => {
const socket = SocketManager.socket;
const services = MonacoServices.create(editor, { const services = MonacoServices.create(editor, {
rootUri: `file://${message.root}`, rootUri: `file://${message.root}`,
}); });

View File

@ -19,11 +19,11 @@ function RijuTerminal() {
term.write("Connecting to server..."); term.write("Connecting to server...");
window.addEventListener("resize", () => fitAddon.fit()); window.addEventListener("resize", () => fitAddon.fit());
EventEmitter.subscribe("resize", () => { const token1 = EventEmitter.subscribe("resize", () => {
const event = new Event("resize"); const event = new Event("resize");
window.dispatchEvent(event); window.dispatchEvent(event);
}); });
EventEmitter.subscribe("terminal", (payload) => { const token2 = EventEmitter.subscribe("terminal", (payload) => {
if (!payload) return; if (!payload) return;
const { type, data } = payload; const { type, data } = payload;
switch (type) { switch (type) {
@ -41,6 +41,8 @@ function RijuTerminal() {
term.onData((data) => { term.onData((data) => {
EventEmitter.dispatch("send", { event: "terminalInput", input: data }); EventEmitter.dispatch("send", { event: "terminalInput", input: data });
}); });
() => EventEmitter.unsubcribe(token1, token2);
}, []); }, []);
return ( return (

View File

@ -24,6 +24,7 @@ import React, { useEffect, useRef, useState } from "react";
import Layouts from "../../components/Layouts"; import Layouts from "../../components/Layouts";
import langs from "../../assets/langs.json"; import langs from "../../assets/langs.json";
import { EventEmitter } from "../../utils/EventEmitter"; import { EventEmitter } from "../../utils/EventEmitter";
import { SocketManager } from "../../services/WS";
ansi.rgb = { ansi.rgb = {
green: "#00FD61", green: "#00FD61",
}; };
@ -42,7 +43,7 @@ const CodeRunner = (props) => {
const router = useRouter(); const router = useRouter();
const { langConfig } = props; const { langConfig } = props;
const editorRef = useRef(null); const editorRef = useRef(null);
const [config, setConfig] = useState(langConfig); const [config] = useState(langConfig);
const [mounted, setMounted] = useState(false); const [mounted, setMounted] = useState(false);
const [isRunning, setRunning] = useState(false); const [isRunning, setRunning] = useState(false);
const [isFormatting, setFormatting] = useState(false); const [isFormatting, setFormatting] = useState(false);
@ -56,29 +57,11 @@ const CodeRunner = (props) => {
EventEmitter.dispatch("terminal", { type, data }); EventEmitter.dispatch("terminal", { type, data });
} }
function connect() { const handleWsOpen = (event) => {
serviceLogBuffers = {};
serviceLogLines = {};
setStatus("connecting");
const socket = new WebSocket(
// (document.location.protocol === "http:" ? "ws://" : "wss://") +
"wss://" +
"riju.codes" +
`/api/v1/ws?lang=${encodeURIComponent(config.id)}`
);
socket.addEventListener("open", () => {
console.log("Successfully connected to server playground");
setStatus("connected"); setStatus("connected");
}); };
EventEmitter.subscribe("send", (payload) => {
if (DEBUG) { const handleWsMessage = (event) => {
console.log("SEND:", payload);
}
if (socket) {
socket.send(JSON.stringify(payload));
}
});
socket.addEventListener("message", async (event) => {
let message; let message;
try { try {
message = JSON.parse(event.data); message = JSON.parse(event.data);
@ -136,7 +119,7 @@ const CodeRunner = (props) => {
return; return;
} }
EventEmitter.dispatch("lspStarted", { message, socket }); EventEmitter.dispatch("lspStarted", { message });
return; return;
case "lspOutput": case "lspOutput":
@ -203,8 +186,9 @@ const CodeRunner = (props) => {
default: default:
console.error("Unexpected message from server:", message); console.error("Unexpected message from server:", message);
} }
}); };
socket.addEventListener("close", (event) => {
const handleWsClose = (event) => {
if (event.wasClean) { if (event.wasClean) {
console.log("Connection closed cleanly"); console.log("Connection closed cleanly");
} else { } else {
@ -215,20 +199,20 @@ const CodeRunner = (props) => {
setLspStarted(false); setLspStarted(false);
setIsLspRequested(false); setIsLspRequested(false);
setStatus("idle"); setStatus("idle");
}); };
return socket;
}
useEffect(() => { useEffect(() => {
if (!config || !mounted) return; if (!config || !mounted) return;
const socket = connect(); serviceLogBuffers = {};
return () => socket && socket.close(); serviceLogLines = {};
setStatus("connecting");
SocketManager.connect(config, handleWsOpen, handleWsMessage, handleWsClose);
return () => SocketManager.disconnect();
}, [config, mounted]); }, [config, mounted]);
function showValue() { function showValue() {
setRunning(true); setRunning(true);
EventEmitter.dispatch("send", { SocketManager.send({
event: "runCode", event: "runCode",
code: editorRef.current.getValue(), code: editorRef.current.getValue(),
}); });
@ -238,7 +222,7 @@ const CodeRunner = (props) => {
setFormatting(true); setFormatting(true);
serviceLogBuffers["formatter"] = ""; serviceLogBuffers["formatter"] = "";
serviceLogLines["formatter"] = []; serviceLogLines["formatter"] = [];
EventEmitter.dispatch("send", { SocketManager.send({
event: "formatCode", event: "formatCode",
code: editorRef.current.getValue(), code: editorRef.current.getValue(),
}); });
@ -261,21 +245,28 @@ const CodeRunner = (props) => {
const handleLspClick = () => { const handleLspClick = () => {
setIsLspRequested(true); setIsLspRequested(true);
if (isLspStarted) { if (isLspStarted) {
EventEmitter.dispatch("send", { SocketManager.send({
event: "lspStop", event: "lspStop",
}); });
} else { } else {
EventEmitter.dispatch("send", { SocketManager.send({
event: "lspStart", event: "lspStart",
}); });
} }
}; };
const handleChange = () => { const handleChange = () => {
if (status != "idle") { if (SocketManager.isConnected) {
return; return;
} else { } else {
connect(); if (!SocketManager.isConnected) {
SocketManager.connect(
config,
handleWsOpen,
handleWsMessage,
handleWsClose
);
}
} }
}; };
@ -285,7 +276,6 @@ const CodeRunner = (props) => {
const es = document.querySelectorAll(".split .panel"); const es = document.querySelectorAll(".split .panel");
for (const e of es) { for (const e of es) {
e.removeAttribute("style"); e.removeAttribute("style");
e.removeAttribute("style");
} }
setSplitType(value); setSplitType(value);
}; };

46
frontend/services/WS.js Normal file
View File

@ -0,0 +1,46 @@
const isFn = (callback) => {
return callback && typeof callback == "function";
};
const createSocket = (url) => {
const socket = new WebSocket(url);
return socket;
};
export const SocketManager = {
socket: null,
isConnected: false,
connect: function (config, onOpen, onMessage, onClose) {
let url =
"wss://" +
"riju.codes" +
`/api/v1/ws?lang=${encodeURIComponent(config.id)}`;
this.socket = createSocket(url);
this.socket.addEventListener("open", () => {
console.log("Successfully connected to server playground");
this.isConnected = true;
if (isFn(onOpen)) onOpen();
});
this.socket.addEventListener("message", async (event) => {
if (isFn(onMessage)) onMessage(event);
});
this.socket.addEventListener("close", (event) => {
if (isFn(onClose)) onClose(event);
this.isConnected = false;
});
},
disconnect: function () {
if (this.socket) {
if (this.socket.readyState == WebSocket.OPEN) {
this.socket.close();
}
}
},
send: function (data) {
if (this.socket) {
if (this.socket.readyState == WebSocket.OPEN) {
this.socket.send(JSON.stringify(data));
}
}
},
};

View File

@ -1,18 +1,25 @@
export const EventEmitter = { export const EventEmitter = {
events: {}, events: {},
lastUid: -1,
dispatch: function (event, data) { dispatch: function (event, data) {
if (!this.events[event]) return; if (!this.events[event]) return;
this.events[event].forEach((callback) => callback(data)); for (let token of Object.keys(this.events[event])) {
const callback = this.events[event][token];
if (callback && typeof callback == "function") callback(data);
}
}, },
subscribe: function (event, callback) { subscribe: function (event, callback) {
if (!this.events[event]) this.events[event] = []; if (!callback) return;
this.events[event].push(callback); if (!this.events[event]) this.events[event] = {};
const token = "uID_" + String(++this.lastUid);
this.events[event][token] = callback;
}, },
isSubscribed: function (event) { unsubcribe: function (...tokens) {
if (!Array.isArray(this.events[event])) return false; for (let k of Object.keys(events)) {
else { let toks = Object.keys(events[k]);
if (this.events[event].length == 0) return false; for (let tok of toks) {
else return true; if (tokens.includes(tok)) delete events[k][tok];
}
} }
}, },
}; };