add.. something.....

This commit is contained in:
hippoz 2022-03-01 22:41:44 +02:00
parent fea27f9fa9
commit 4fbdace446
No known key found for this signature in database
GPG key ID: 7C52899193467641
5 changed files with 360 additions and 0 deletions

View file

@ -0,0 +1,105 @@
window.modules.register("GatewayClient", () => {
const messageSchema = { t: "number", d: "object" };
const messageTypes = {
HELLO: 0,
YOO: 1,
READY: 2,
EVENT: 3
};
class GatewayClient {
constructor(gatewayPath) {
this.gatewayPath = gatewayPath;
this.ws = null;
this.token = null;
this.user = null;
this.onEvent = (e) => {};
this.onConnected = () => {};
this.onDisconnected = () => {};
}
connect(token) {
if (!token)
token = this.token;
console.log("gateway: connecting");
this.ws = new WebSocket(this.gatewayPath);
this.ws.addEventListener("message", ({ data }) => {
if (typeof data !== "string") {
console.warn("gateway: got non-string data from server, ignoring...");
return;
}
let message;
try {
message = JSON.parse(data);
} catch(e) {
console.warn("gateway: got invalid JSON from server (failed to parse), ignoring...");
return;
}
if (!this._checkMessageSchema(message)) {
console.warn("gateway: got invalid JSON from server (does not match schema), ignoring...");
return;
}
switch (message.t) {
case messageTypes.HELLO: {
console.log("gateway: HELLO");
this.ws.send(JSON.stringify({
t: messageTypes.YOO,
d: {
token
}
}));
break;
}
case messageTypes.READY: {
console.log("gateway: READY");
this.user = message.d.user;
this.onConnected();
break;
}
case messageTypes.EVENT: {
this.onEvent(message.d);
break;
}
default: {
console.warn("gateway: got invalid JSON from server (invalid type), ignoring...");
return;
}
}
});
this.ws.addEventListener("open", () => {
console.log("gateway: open");
});
this.ws.addEventListener("close", ({ code }) => {
console.log("gateway: closed");
this.onDisconnected(code);
if (code === 4001) {
console.log(`gateway: disconnect code is ${code} (bad auth), will not attempt reconnect`);
return;
}
setTimeout(() => {
console.log("gateway: reconnecting");
this.connect(token);
}, 4000);
});
}
_checkMessageSchema(message) {
for (const [key, value] of Object.entries(message)) {
if (!messageSchema[key])
return false;
if (typeof value !== messageSchema[key])
return false;
}
return true;
}
}
return GatewayClient;
});

View file

@ -0,0 +1,21 @@
window.modules.register("$app", () => {
const App = window.modules.require("App");
if (!window._APP_ENV)
throw new Error("$app: could not find window._APP_ENV");
if (!App)
throw new Error("$app: require('App') returned undefined");
const initialLoading = document.getElementById("initial-loading");
if (initialLoading) {
initialLoading.parentElement.removeChild(initialLoading);
}
const appMountElement = document.createElement("div");
document.body.appendChild(appMountElement);
const app = new App(appMountElement);
app.mount();
return app;
});

View file

@ -0,0 +1,185 @@
window.modules.register("AuthPromptRoute", () => {
class AuthPromptRoute {
constructor() {
this.element = null;
}
mountOn(target) {
if (this.element)
return; // Already mounted
this.element = document.createRange().createContextualFragment(`
<div>
<input type="password" id="code-input">
<button id="continue-button">enter</button>
</div>
`).children[0];
this.element.querySelector("#continue-button").addEventListener("click", () => {
if (this.onPasswordSubmitted)
this.onPasswordSubmitted(this.element.querySelector("#code-input").value);
});
target.appendChild(this.element);
}
unmount() {
if (!this.element)
return; // Already unmounted
this.element.parentElement.removeChild(this.element);
this.element = null;
}
}
return AuthPromptRoute;
});
window.modules.register("MainChatView", () => {
class MainChatView {
constructor() {
this.element = null;
}
mountOn(target) {
if (this.element)
return; // Already mounted
this.element = document.createRange().createContextualFragment(`
<div>
<div id="messages-container" style="width: 300px; height: 250px; overflow: auto;"></div>
<br>
<input type="text" id="message-input">
<button id="message-submit">send</button>
</div>
`).children[0];
const textInput = this.element.querySelector("#message-input");
this.element.querySelector("#message-submit").addEventListener("click", () => {
const message = textInput.value;
if (this.onSendMessage)
this.onSendMessage(message);
textInput.value = "";
});
target.appendChild(this.element);
}
appendMessage(messageObject) {
const { author, content } = messageObject;
if (!this.element)
return;
const usernameLetters = author.username
.split("");
let authorUsernameNumber = 150;
usernameLetters.forEach(l => {
authorUsernameNumber += l.charCodeAt(0);
});
const messageElement = document.createRange().createContextualFragment(`
<div>
<b>[author could not be loaded] </b>
<span>[content could not be loaded]</span>
</div>
`).children[0];
messageElement.querySelector("b").innerText = `User ${authorUsernameNumber} `;
messageElement.querySelector("span").innerText = content;
const container = this.element.querySelector("#messages-container");
container.appendChild(messageElement);
container.scrollTop = container.scrollHeight;
}
unmount() {
if (!this.element)
return; // Already unmounted
this.element.parentElement.removeChild(this.element);
this.element = null;
}
}
return MainChatView;
});
window.modules.register("App", () => {
const AuthPromptRoute = window.modules.require("AuthPromptRoute");
const MainChatView = window.modules.require("MainChatView");
const GatewayClient = window.modules.require("GatewayClient");
class App {
constructor(mountElement) {
this.mountElement = mountElement;
this.currentMountedElement = null;
this.authPromptRoute = new AuthPromptRoute();
this.mainChatView = new MainChatView();
this.gatewayClient = new GatewayClient(window._APP_ENV.gatewayBase);
}
_mountElement(element) {
if (this.currentMountedElement) {
this.currentMountedElement.unmount();
}
this.currentMountedElement = element;
this.currentMountedElement.mountOn(this.mountElement);
}
_unmountAll() {
if (this.currentMountedElement) {
this.currentMountedElement.unmount();
}
}
mount() {
let serverId;
let channelId;
let token;
this._mountElement(this.authPromptRoute);
this.gatewayClient.onEvent = (e) => {
if (e.eventType === "MESSAGE_CREATE" && e.message && e.message.guild_id === serverId && e.message.channel_id === channelId) {
this.mainChatView.appendMessage(e.message);
}
};
this.gatewayClient.onConnected = () => {
this._mountElement(this.mainChatView);
};
this.gatewayClient.onDisconnected = () => {
this._mountElement(this.authPromptRoute);
};
this.authPromptRoute.onPasswordSubmitted = (auth) => {
const parts = auth.split(",,");
if (parts.length !== 3)
return;
const [userToken, userServerId, userChannelId] = parts;
serverId = userServerId;
channelId = userChannelId;
token = userToken
this.gatewayClient.connect(token);
};
this.mainChatView.onSendMessage = async (message) => {
if (typeof message === "string" && message.trim() === "")
return;
await fetch(`${window._APP_ENV.apiBase}/guilds/${serverId}/channels/${channelId}/messages/create`, {
method: "POST",
body: JSON.stringify({
content: message
}),
headers: {
"content-type": "application/json",
"authorization": token
}
});
};
}
}
return App;
});

View file

@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>.</title>
<script>
window._APP_ENV = {
gatewayBase: "wss://kittycatnetwork.hippoz.xyz/services/thebridge/gateway/",
apiBase: "https://kittycatnetwork.hippoz.xyz/services/thebridge/api/v1"
};
</script>
</head>
<body>
<p id="initial-loading">loading</p>
<script src="moduleruntime.js"></script>
<script src="GatewayClient.js"></script>
<script src="components.js"></script>
<script src="app.js"></script>
</body>
</html>

View file

@ -0,0 +1,26 @@
const modules = {
_cache: {},
_registry: {},
require(moduleName) {
if (this._cache[moduleName]) {
return this._cache[moduleName];
}
if (this._registry[moduleName]) {
const loaderFunction = this._registry[moduleName];
this._cache[moduleName] = loaderFunction(1);
return this._cache[moduleName];
}
return null;
},
register(moduleName, loaderFunction) {
this._registry[moduleName] = loaderFunction;
this._cache[moduleName] = loaderFunction(0);
},
registerLazy(moduleName, loaderFunction) {
this._registry[moduleName] = loaderFunction;
}
};
window.modules = modules;