Start thinking about how to actually use Mithril to build a frontend for this app.

This commit is contained in:
Dave Smith-Hayes 2024-10-04 22:57:21 -04:00
parent 15c405da6e
commit 03c51e7dbe
13 changed files with 152 additions and 1780 deletions

View File

@ -1,4 +1,28 @@
Bun.build({ async function main() {
entrypoints: [ "./src/components/index.js" ], const results = await Bun.build({
entrypoints: [ "./frontend/index.js" ],
outdir: "./static/js/", outdir: "./static/js/",
verbose: true
});
if (!results.success) {
for (const message of results.logs) {
console.error(message);
}
return false;
}
return true;
}
main()
.then(() => {
process.exit(0)
})
.catch((e) => {
console.error(e);
process.exit(1);
}); });

Binary file not shown.

View File

@ -2,7 +2,8 @@ import m from "mithril";
import { MessageBox } from "./MessageBox"; import { MessageBox } from "./MessageBox";
import { LoginForm } from "./Login"; import { LoginForm } from "./Login";
export const ChatApp = { export function createChatApp({ ws, session }) {
return {
view: function () { view: function () {
return m("div", { class: "container" }, [ return m("div", { class: "container" }, [
m("h1", "A Stupid Chat Application"), m("h1", "A Stupid Chat Application"),
@ -10,3 +11,4 @@ export const ChatApp = {
]); ]);
} }
}; };
}

View File

@ -1,11 +1,37 @@
import m from "mithril"; import m from "mithril";
import Cookies from "js-cookie";
export const LoginForm = { export function LoginForm() {
let loggedIn = false;
async function login(e) {
e.preventDefault();
const formData = new FormData(e.target);
const res = await m.request({
method: "POST",
url: "/login",
body: formData
});
if (res.success) {
Cookies.set('logged-in', true);
} else {
Cookies.remove('logged-in');
}
}
return {
oninit: function() {
loggedIn = !!Cookies.get('logged-in');
},
view: function() { view: function() {
return m("form", { action: "/login" }, [ return m("form", { action: "/login", method: "post", onsubmit: login }, [
m("label", { for: "username" }, "Username"), m("label", { for: "username" }, "Username"),
m("input", { type: "text", name: "username" }), m("input", { type: "text", name: "username" }),
m("input", { type: "submit", value: "Login" }) m("input", { type: "submit", value: "Login" })
]); ]);
} }
}; }
}

View File

@ -1,8 +1,10 @@
import m from "mithril"; import m from "mithril";
import { ChatApp } from "./components/ChatApp"; import { createChatApp } from "./components/ChatApp";
import { createWebSocket } from "./websocket";
const localState = { import { Sessions } from "./session";
messages: []
}
// open the WS to the server
const ws = createWebSocket();
const session = new Sessions();
const ChatApp = createChatApp({ ws, session });
m.mount(document.getElementById("app"), ChatApp); m.mount(document.getElementById("app"), ChatApp);

View File

@ -0,0 +1,31 @@
export class Sessions extends EventManager {
constructor() {
this.users = [];
}
userExists(user) {
return !!this.findUser(user);
}
findUser(user) {
return this.users.find(u => u.username === user.username);
}
addUser(user) {
if (!this.userExists(user)) {
this.users.push(user);
this.emit('user-logged-in', user);
}
}
clearUser(user) {
if (this.userExists(user)) {
const index = this.users.indexOf(user);
if (index > -1) {
this.users.splice(index - 1, 1);
this.emit('user-removed', user);
}
}
}
}

View File

@ -0,0 +1,3 @@
export function createWebSocket() {
return new WebSocket("ws://localhost:3000/chat-service");
}

8
chat-app/justfile Normal file
View File

@ -0,0 +1,8 @@
build:
bun build.ts
clean:
rm -r static/js/*
test:
bun test

View File

@ -2,11 +2,13 @@
"name": "chat-app", "name": "chat-app",
"scripts": { "scripts": {
"dev": "bun run --hot src/index.ts", "dev": "bun run --hot src/index.ts",
"build": "bun src/build.ts" "build": "rm -r static/js && bun build.ts"
}, },
"dependencies": { "dependencies": {
"@types/mithril": "^2.2.7", "@types/mithril": "^2.2.7",
"hono": "^4.6.3", "hono": "^4.6.3",
"js-cookie": "^3.0.5",
"js-guid": "^1.0.2",
"mithril": "^2.2.5" "mithril": "^2.2.5"
}, },
"devDependencies": { "devDependencies": {

View File

@ -13,8 +13,16 @@ const users: string[] = [];
// Set up the chat socket // Set up the chat socket
const { upgradeWebSocket, websocket } = createBunWebSocket<ServerWebSocket>(); const { upgradeWebSocket, websocket } = createBunWebSocket<ServerWebSocket>();
app.get("/chat-socket", upgradeWebSocket((c) => { app.get("/chat-service", upgradeWebSocket((c) => {
return { return {
onOpen(_event, ws) {
chatRoom.addListener('message-added', function (e) {
ws.send(JSON.stringify({
message: e.message.message,
chatter: e.message.chatter
}));
});
},
onMessage(event, ws) { onMessage(event, ws) {
const data = JSON.parse(event.data); const data = JSON.parse(event.data);
@ -29,8 +37,6 @@ app.get("/chat-socket", upgradeWebSocket((c) => {
}; };
chatRoom.addMessage(message); chatRoom.addMessage(message);
ws.send(JSON.stringify({ message: createMessageString(message) }));
}, },
onClose() { onClose() {
console.log("Connection closed."); console.log("Connection closed.");
@ -41,6 +47,20 @@ app.get("/chat-socket", upgradeWebSocket((c) => {
} }
})); }));
// set up the username, somehow
// set the session for a user
app.post("/login", async (c) => {
const user = await c.req.parseBody();
let success: boolean = false;
if (!users.find((u) => u == user.username)) {
users.push(user.username);
success = true;
}
return c.json({ success });
});
// get the HTML and JS from the static repo // get the HTML and JS from the static repo
app.use("/", serveStatic({ path: "/static/index.html" })); app.use("/", serveStatic({ path: "/static/index.html" }));
app.use("/*", serveStatic({ root: "/static" })); app.use("/*", serveStatic({ root: "/static" }));

View File

@ -0,0 +1,4 @@
input, label {
display: block;
margin-top: 0.5em;
}

View File

@ -2,6 +2,7 @@
<html lang="en"> <html lang="en">
<head> <head>
<title>A Dumb Chat Application</title> <title>A Dumb Chat Application</title>
<link rel="stylesheet" type="text/css" href="/css/main.css" />
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>

File diff suppressed because it is too large Load Diff