197 lines
5.8 KiB
HTML
197 lines
5.8 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>Poll Client</title>
|
|
<style>
|
|
body {
|
|
font-family: Arial, sans-serif;
|
|
max-width: 600px;
|
|
margin: 0 auto;
|
|
padding: 20px;
|
|
}
|
|
#messages {
|
|
height: 300px;
|
|
border: 1px solid #ccc;
|
|
overflow-y: auto;
|
|
padding: 10px;
|
|
margin-bottom: 10px;
|
|
}
|
|
.message { margin: 5px 0; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>Poll Client</h1>
|
|
<div id="status">Connecting to server...</div>
|
|
<div id="status2">Connecting to server...</div>
|
|
<div id="messages"></div>
|
|
<div>
|
|
<input type="text" id="messageInput" placeholder="Type your message">
|
|
<!--button onclick="sendMessage()">Send</button-->
|
|
<button id="sendBtn">Send</button>
|
|
<input id="optionInput" placeholder="Add option">
|
|
<button id="addBtn">Add</button>
|
|
<ul id="options"></ul>
|
|
</div>
|
|
|
|
<script type="module">
|
|
import * as Y from "https://esm.sh/yjs"
|
|
import { WebsocketProvider } from "https://esm.sh/y-websocket"
|
|
|
|
const WS_PORT = 8080;
|
|
const ydoc = new Y.Doc();
|
|
const status = document.getElementById('status');
|
|
const status2 = document.getElementById('status2');
|
|
const messages = document.getElementById('messages');
|
|
const messageInput = document.getElementById('messageInput');
|
|
|
|
// Connect to the backend (WebSocket server) via Yjs WebsocketProvider
|
|
const wsp = new WebsocketProvider(
|
|
'ws://localhost:' + String(WS_PORT), 'poll-room',
|
|
ydoc
|
|
)
|
|
wsp.on('status', event => {
|
|
//console.log("event.status =", event.status)
|
|
if (event.status == "connected") {
|
|
status.textContent = 'Yjs connected to WebSocket server';
|
|
status.style.color = 'green';
|
|
}
|
|
else {
|
|
status.textContent = 'Yjs disonnected from WebSocket server';
|
|
status.style.color = 'red';
|
|
}
|
|
})
|
|
wsp.onopen = () => {
|
|
console.log('connected to y-websocket server');
|
|
};
|
|
ydoc.on('update', () => {
|
|
console.log('Yjs document updated locally');
|
|
});
|
|
wsp.on('sync', isSynced => {
|
|
console.log("isSynced =", isSynced)
|
|
})
|
|
|
|
// Connect to the backend (WebSocket server) via common WebSocket (only for informative messages)
|
|
const ws = new WebSocket('ws://localhost:' + String(WS_PORT));
|
|
ws.onopen = () => {
|
|
status2.textContent = 'WebSocket client connected to WebSocket server';
|
|
status2.style.color = 'green';
|
|
};
|
|
ws.onmessage = (event) => {
|
|
const message = document.createElement('div');
|
|
message.className = 'message';
|
|
message.textContent = event.data;
|
|
messages.appendChild(message);
|
|
messages.scrollTop = messages.scrollHeight;
|
|
};
|
|
ws.onerror = (error) => {
|
|
status2.textContent = 'Error: ' + error.message;
|
|
status2.style.color = 'red';
|
|
};
|
|
ws.onclose = () => {
|
|
status2.textContent = 'WebSocket client disconnected from WebSocket server';
|
|
status2.style.color = 'red';
|
|
};
|
|
|
|
// Function to send a message
|
|
function sendMessage(command, payload) {
|
|
let message = "";
|
|
if (command === "TEXT_MESSAGE") {
|
|
message = command + ":" + messageInput.value.trim();
|
|
messageInput.value = "";
|
|
}
|
|
else if (command.startsWith("STATE_MESSAGE--")) {
|
|
message = command + ":" + payload;
|
|
}
|
|
else {
|
|
console.log("Error: unknown command '" + command + "'")
|
|
}
|
|
|
|
if (message) {
|
|
/* To make compatible with y-websocket-server API:
|
|
const encoder = encoding.createEncoder()
|
|
encoding.writeVarUint(encoder, messageTextMessage)
|
|
syncProtocol.writeUpdate(encoder, update)
|
|
enc_message = encoding.toUint8Array(encoder)*/
|
|
ws.send(message);
|
|
}
|
|
}
|
|
|
|
// Send message on button click
|
|
document.getElementById("sendBtn").onclick = () => {
|
|
sendMessage("TEXT_MESSAGE", "");
|
|
};
|
|
|
|
// Send message on Enter key
|
|
messageInput.addEventListener('keypress', (e) => {
|
|
if (e.key === 'Enter') {
|
|
sendMessage("TEXT_MESSAGE", "");
|
|
}
|
|
});
|
|
|
|
// Actual poll logic
|
|
const optionsMap = ydoc.getMap("options");
|
|
|
|
const optionsList = document.getElementById("options");
|
|
const input = document.getElementById("optionInput");
|
|
|
|
function render() {
|
|
optionsList.innerHTML = ""
|
|
|
|
optionsMap.forEach((vote, name) => {
|
|
// List element for this option
|
|
const li = document.createElement("li")
|
|
|
|
// Label for this option
|
|
const label = document.createElement("span")
|
|
label.textContent = name + " — " + vote + " "
|
|
|
|
// "Vote"/"Unvote" button
|
|
const voteBtn = document.createElement("button")
|
|
if (vote == false) {
|
|
voteBtn.textContent = "Vote"
|
|
}
|
|
else {
|
|
voteBtn.textContent = "Unvote"
|
|
}
|
|
voteBtn.onclick = () => {
|
|
vote = !vote;
|
|
optionsMap.set(name, vote);
|
|
sendMessage("STATE_MESSAGE--" + (vote ? "VOTE" : "UNVOTE"), name);
|
|
}
|
|
|
|
// "Remove" button
|
|
const removeBtn = document.createElement("button")
|
|
removeBtn.textContent = "Remove"
|
|
removeBtn.onclick = () => {
|
|
optionsMap.delete(name);
|
|
sendMessage("STATE_MESSAGE--REMOVE", name);
|
|
}
|
|
|
|
li.appendChild(label)
|
|
li.appendChild(voteBtn)
|
|
li.appendChild(removeBtn)
|
|
|
|
optionsList.appendChild(li)
|
|
})
|
|
};
|
|
|
|
document.getElementById("addBtn").onclick = () => {
|
|
const name = input.value.trim()
|
|
|
|
if (!name) return
|
|
|
|
if (!optionsMap.has(name)) {
|
|
optionsMap.set(name, false);
|
|
sendMessage("STATE_MESSAGE--ADD_OPTION", name);
|
|
}
|
|
|
|
input.value = ""
|
|
};
|
|
|
|
optionsMap.observe(render);
|
|
|
|
render();
|
|
</script>
|
|
</body>
|
|
</html>
|