diff --git a/client/index.css b/client/index.css index 07f1a7c..0959836 100644 --- a/client/index.css +++ b/client/index.css @@ -87,6 +87,7 @@ font-family: "Courier New", monospace; display: flex; justify-content: center; + color: rgb(177, 17, 17); } .main__canvas { diff --git a/client/index.mjs b/client/index.mjs index 3836cb0..b60efe7 100644 --- a/client/index.mjs +++ b/client/index.mjs @@ -3,22 +3,24 @@ import drawer from "./drawer.mjs"; import picker from "./picker.mjs"; document.querySelector("#start").addEventListener("submit", e => { - e.preventDefault(); - main(new FormData(e.currentTarget).get("apiKey")); - document.querySelector(".container").classList.add("ready"); + e.preventDefault(); + main(new FormData(e.currentTarget).get("apiKey")); + document.querySelector(".container").classList.add("ready"); }); const main = apiKey => { - const ws = connect(apiKey); - ws.addEventListener("message", console.log); + const ws = connect(apiKey); + ws.addEventListener("message", message => { + let data = JSON.parse(message.data) + if (data.type === 'timeout') timeout.next = new Date(data.payload); + if (data.type === 'place') drawer.putArray(data.payload); + if (data.type === 'pick') drawer.put(data.payload.x, data.payload.y, data.payload.color); + }); - timeout.next = new Date(); - drawer.onClick = (x, y) => { - drawer.put(x, y, picker.color); - }; + drawer.onClick = (x, y) => ws.send(JSON.stringify({type: "pick", payload: {x: x, y: y, color: picker.color}})); }; const connect = apiKey => { - const url = `${location.origin.replace(/^http/, "ws")}?apiKey=${apiKey}`; - return new WebSocket(url); + const url = `${location.origin.replace(/^http/, "ws")}?apiKey=${apiKey}`; + return new WebSocket(url); }; diff --git a/client/picker.mjs b/client/picker.mjs index 6a49c82..cbb56d6 100644 --- a/client/picker.mjs +++ b/client/picker.mjs @@ -1,61 +1,51 @@ const setAttributes = (element, object) => { - for (const [key, value] of Object.entries(object)) { - element.setAttribute(key, value); - } + for (const [key, value] of Object.entries(object)) { + element.setAttribute(key, value); + } }; const drawPalette = async () => { - const colors = hardcodedColors; - pickedColor = colors[0]; - const palette = document.querySelector("#palette"); - const fragment = document.createDocumentFragment(); - for (const color of colors) { - const label = document.createElement("label"); - label.setAttribute("class", "palette__color"); - const input = document.createElement("input"); - setAttributes(input, { - class: "palette__checkbox", - type: "radio", - name: "color", - value: color - }); - if (color === pickedColor) { - input.setAttribute("checked", ""); + const colors = await fetch('/api/colors') + .then(async res => await res.json()); + pickedColor = colors[0]; + const palette = document.querySelector("#palette"); + const fragment = document.createDocumentFragment(); + for (const color of colors) { + const label = document.createElement("label"); + label.setAttribute("class", "palette__color"); + const input = document.createElement("input"); + setAttributes(input, { + class: "palette__checkbox", + type: "radio", + name: "color", + value: color + }); + if (color === pickedColor) { + input.setAttribute("checked", ""); + } + input.addEventListener("input", e => { + pickedColor = e.target.value; + }); + const span = document.createElement("span"); + setAttributes(span, { + class: "palette__name", + style: `background-color: ${color}` + }); + label.appendChild(input); + label.appendChild(span); + fragment.appendChild(label); } - input.addEventListener("input", e => { - pickedColor = e.target.value; - }); - const span = document.createElement("span"); - setAttributes(span, { - class: "palette__name", - style: `background-color: ${color}` - }); - label.appendChild(input); - label.appendChild(span); - fragment.appendChild(label); - } - palette.appendChild(fragment); + palette.appendChild(fragment); }; -const hardcodedColors = [ - "#140c1c", - "#30346d", - "#854c30", - "#d04648", - "#597dce", - "#8595a1", - "#d2aa99", - "#dad45e", -]; - let pickedColor = null; drawPalette().catch(console.error); const picker = { - get color() { - return pickedColor; - } + get color() { + return pickedColor; + } }; export default picker; diff --git a/server.mjs b/server.mjs index 02a05b5..4746774 100644 --- a/server.mjs +++ b/server.mjs @@ -4,59 +4,100 @@ import WebSocket from "ws"; const port = process.env.PORT || 5000; -const apiKeys = new Set([ - "4a83051d-aad4-483e-8fc8-693273d15dc7", - "c08c9038-693d-4669-98cd-9f0dd5ef06bf", - "4b1545c4-4a70-4727-9ea1-152ed4c84ae2", - "4a226908-aa3e-4a34-a57d-1f3d1f6cba84", -]); +const apiKeys = { + "4a83051d-aad4-483e-8fc8-693273d15dc7": new Date(), + "c08c9038-693d-4669-98cd-9f0dd5ef06bf": new Date(), + "4b1545c4-4a70-4727-9ea1-152ed4c84ae2": new Date(), + "4a226908-aa3e-4a34-a57d-1f3d1f6cba84": new Date(), +} const colors = [ - "#140c1c", - "#442434", - "#30346d", - "#4e4a4e", - "#854c30", - "#346524", - "#d04648", - "#757161", - "#597dce", - "#d27d2c", - "#8595a1", - "#6daa2c", - "#d2aa99", - "#6dc2ca", - "#dad45e", - "#deeed6", + "#140c1c", + "#442434", + "#30346d", + "#4e4a4e", + "#854c30", + "#346524", + "#d04648", + "#757161", + "#597dce", + "#d27d2c", + "#8595a1", + "#6daa2c", + "#d2aa99", + "#6dc2ca", + "#dad45e", + "#deeed6", ]; const size = 256; // place(x, y) := place[x + y * size] const place = Array(size * size).fill(null); for (const [colorIndex, colorValue] of colors.entries()) { - for (let dx = 0; dx < size; dx++) { - place[dx + colorIndex * size] = colorValue; - } + for (let dx = 0; dx < size; dx++) { + place[dx + colorIndex * size] = colorValue; + } } const app = express(); app.use(express.static(path.join(process.cwd(), "client"))); +app.get("/api/colors", (_, res) => { + res.json(colors); +}) + app.get("/*", (_, res) => { - res.send("Place(holder)"); + res.send("Place(holder)"); }); -const server = app.listen(port); +const server = app.listen(port, () => { + console.log(`app on http://localhost:${port}/`); +}); const wss = new WebSocket.Server({ - noServer: true, + noServer: true, +}); + +const timeoutValue = 5; + +function pickValidation(x, y, color) { + return 0 <= x && x <= 256 && + 0 <= y && y <= 256 && + colors.includes(color); +} + +wss.on('connection', function connection(ws) { + let apiKey = keysMap.get(ws); + ws.on('message', function message(message) { + let date = new Date(); + let data = JSON.parse(message); + if (data.type === "pick") { + let {x, y, color} = data.payload; + if (pickValidation(x, y, color)) { + if (date > apiKeys[apiKey]) { + apiKeys[apiKey] = new Date(date.valueOf() + timeoutValue * 1000); + place[x + y * size] = color; + wss.clients.forEach(client => client.send(message)); + } + ws.send(JSON.stringify({type: "timeout", payload: apiKeys[apiKey].toISOString()})); + } + } + }); + + ws.send(JSON.stringify({type: "timeout", payload: apiKeys[apiKey].toISOString()})) + ws.send(JSON.stringify({type: "place", payload: place})); }); +let keysMap = new WeakMap() + server.on("upgrade", (req, socket, head) => { - const url = new URL(req.url, req.headers.origin); - console.log(url); - wss.handleUpgrade(req, socket, head, (ws) => { - wss.emit("connection", ws, req); - }); + const url = new URL(req.url, req.headers.origin); + let apiKey = url.searchParams.get('apiKey'); + wss.handleUpgrade(req, socket, head, (ws) => { + if (apiKey in apiKeys) { + keysMap.set(ws, apiKey); + wss.emit("connection", ws, req); + } else socket.destroy(new Error('wrong api key')); + }); });