feat: client interaction

This commit is contained in:
ktyl 2024-07-09 21:51:28 +01:00
parent f434022e88
commit 7bb21ea379
3 changed files with 109 additions and 81 deletions

View File

@ -4,15 +4,15 @@
</head> </head>
<body> <body>
<!-- TODO: conversations --> <div id="header">
<h1>Hester Gomez</h1>
<p class="delay">(Luna, <span id="delay-text"></span>)</p>
</div>
<h1>Hester Gomez</h1>
<p class="delay">(Luna, <span id="delay-text"></span>)</p>
<!-- messages from current conversations -->
<ul id="messages"></ul> <ul id="messages"></ul>
<!-- message box displays the next message to send --> <p id="typing-indicator">Hester is typing...</p>
<div id="textbox"> <div id="textbox">
<input id="textbox-input" class="rounded-rectangle" type="text" disabled></input> <input id="textbox-input" class="rounded-rectangle" type="text" disabled></input>
<button class="rounded-rectangle" onclick="pressSendButton()">send</button> <button class="rounded-rectangle" onclick="pressSendButton()">send</button>

103
main.js
View File

@ -26,8 +26,9 @@ conversation = [
{ c: 1, text: ":)"} { c: 1, text: ":)"}
]; ];
sentMessages = []
let messageIdx = 1; let messageIdx = 1;
let title = "hester"; let title = "Hester Gomez";
let pings = 0; let pings = 0;
function getMessageList() { function getMessageList() {
@ -41,17 +42,17 @@ function getMessageElement(messageIdx) {
} }
class SentMessage { class SentMessage {
constructor(oneWayLag, idx) { constructor(oneWayLag, idx, onDelivered) {
this.oneWayLag = oneWayLag; this.oneWayLag = oneWayLag;
this.idx = idx; this.idx = idx;
//this.content = conversation[idx].text;
this.createdTime = Date.now(); this.createdTime = Date.now();
this.onDelivered = onDelivered;
this.updateBarIntervalId = setInterval(() => { this.updateBarIntervalId = setInterval(() => {
let elapsed = Math.abs(Date.now() - this.createdTime) / 1000; let elapsed = Math.abs(Date.now() - this.createdTime) / 1000;
let progress = elapsed / this.oneWayLag; let progress = elapsed / this.oneWayLag;
// divide in half so we measure the round trip // divide in half to measure the round trip
progress /= 2; progress /= 2;
this.setProgress(progress); this.setProgress(progress);
@ -77,11 +78,11 @@ class SentMessage {
this.updateStatus("delivered"); this.updateStatus("delivered");
clearInterval(this.updateBarIntervalId); clearInterval(this.updateBarIntervalId);
amount = 0; amount = 0;
this.onDelivered();
} }
amount = Math.min(amount, 1); amount = Math.min(amount, 1);
let percentage = `${amount * 100}%`; let percentage = `${amount * 100}%`;
progressBar.style.width = percentage; progressBar.style.width = percentage;
} }
updateStatus(newStatus) { updateStatus(newStatus) {
@ -95,23 +96,17 @@ function updateTextBox(message) {
document.getElementById("textbox-input").value = message; document.getElementById("textbox-input").value = message;
} }
// show messages up to index function setTypingIndicator(isTyping) {
function showMessages(idx) { document.getElementById("typing-indicator").innerHTML = isTyping
? "Hester is typing..."
: "";
}
let messageList = getMessageList(); // add the message at the index to the displayed messages
function addMessage(idx) {
// TODO: rebuilding the DOM here clears this status of sent messages. instead of let message = conversation[idx];
// rebuilding it completely, append a new message onto the end of the inner html. let messageHtml = getMessageHtml(message.text, isMessageOurs(message));
// TODO: is this also the juncture to clear the status of previous messages? getMessageList().innerHTML += messageHtml;
// clear current messages
messageList.innerHTML = "";
// inject messages into ul
for (let i = 0; i < idx; i++) {
let message = conversation[i];
messageList.innerHTML += getMessageHtml(message.text, isMessageOurs(message));
}
} }
function getMessageHtml(text, isOurs) { function getMessageHtml(text, isOurs) {
@ -187,29 +182,45 @@ function getResponses(idx) {
} }
function updateChat(messageIdx) { function updateChat(messageIdx) {
showMessages(messageIdx); addMessage(messageIdx-1);
updatePreviewText(messageIdx); updatePreviewText(messageIdx);
updatePings(); updatePings();
} }
function getRandomDelay(min, max) {
const range = max - min;
return min + Math.random() * range;
}
function waitForIncomingMessages() { function waitForIncomingMessages() {
// we don't want messages to arrive all at once if there are multiple messages, // we don't want messages to arrive all at once if there are multiple messages,
// so we need to add a small delay to consecutive messages and wait for them one by one // so we need to add a small delay to consecutive messages and wait for them one by one
let smallDelay = 2; let smallDelay = getRandomDelay(2, 10);
let responses = getResponses(messageIdx); let responses = getResponses(messageIdx);
let lightLag = getLightLag(); let lightLag = getLightLag();
setTimeout(() => {
setTypingIndicator(true);
for (let i = 0; i < responses.length; i++) { for (let i = 0; i < responses.length; i++) {
let delaySeconds = lightLag + smallDelay * i; let delaySeconds = lightLag + smallDelay * i;
const stopTyping = i == responses.length - 1;
setTimeout(() => { setTimeout(() => {
messageIdx++; messageIdx++;
pings++; pings++;
// update the chat with their message // update the chat with their message
updateChat(messageIdx); updateChat(messageIdx);
if (stopTyping) {
setTypingIndicator(false);
}
}, delaySeconds * 1000); }, delaySeconds * 1000);
} }
},getRandomDelay(1, 3));
} }
function updatePreviewText(messageIdx) { function updatePreviewText(messageIdx) {
@ -226,24 +237,37 @@ function sendOurMessage() {
// before advancing the conversation // before advancing the conversation
let oneWayLag = getLightLag(); let oneWayLag = getLightLag();
let currentMessage = conversation[messageIdx]; let currentMessage = conversation[messageIdx];
let sentMessage = new SentMessage(oneWayLag, messageIdx); let nextMessage = conversation[messageIdx + 1];
console.log(sentMessage);
// message.send() let sentMessage = new SentMessage(oneWayLag, messageIdx, () => {
// when we send the message, we want to start an interval that updates the progress animation
// message.setProgress() should be a private method that updates the appropriate element // if the next message is ours we don't need to wait for anything
if (isMessageOurs(nextMessage))
return;
// wait for them to read the message
setTimeout(() => {
// set the message status to read
sentMessage.updateStatus("read");
// hide the status of previous messages we first need to have references to all of them
// when creating messages we need to add these too an array
for (let i = 0; i < sentMessages.length; i++) {
let message = sentMessages[i];
if (message != sentMessage) {
message.updateStatus("");
}
}
waitForIncomingMessages();
}, getRandomDelay(5, 30) * 1000);
});
sentMessages.push(sentMessage);
// advance conversation with our next message // advance conversation with our next message
messageIdx++; messageIdx++;
nextMessage = conversation[messageIdx];
// if the next message is not ours, we need to wait for it: set off a thread that
// will update the message index and the displayed messages in a few seconds
// we are still waiting to receive messages so pressing the button oughtn't do anything
if (!isMessageOurs(nextMessage)) {
waitForIncomingMessages();
}
// update displayed messages
updateChat(messageIdx); updateChat(messageIdx);
} }
@ -273,7 +297,12 @@ function startLightLagUpdateLoop() {
} }
function init() { function init() {
showMessages(messageIdx); setTimeout(() => {
addMessage(0);
updatePings();
setTypingIndicator(false);
}, 400);
// load the first message into the text box // load the first message into the text box
updateTextBox(conversation[messageIdx].text); updateTextBox(conversation[messageIdx].text);
pings = 1; pings = 1;

View File

@ -2,7 +2,17 @@ html {
font-family: sans-serif; font-family: sans-serif;
} }
body {
margin-left: 0;
margin-right: 0;
}
#page {
max-width: 600px;
}
h1 { h1 {
margin-left: 0.5em;
display: inline; display: inline;
} }
@ -36,7 +46,7 @@ h1 {
} }
ul { ul {
margin-bottom: 1.5em; margin: 1em;
padding: 0; padding: 0;
list-style: none; list-style: none;
} }
@ -45,18 +55,31 @@ li {
height: 2.5em; height: 2.5em;
width: 100%; width: 100%;
margin-top: 1.5em; margin-bottom: 1.5em;
position: relative; position: relative;
} }
#textbox { #textbox {
margin-top: 2em; width: 100%;
padding: 0; position: absolute;
bottom: 0;
margin-top: 0.5em;
}
#typing-indicator {
margin: 0;
margin-left: 1em;
margin-bottom: 0.5em;
position: absolute;
bottom: 3em;
} }
#textbox input { #textbox input {
margin: 0.5em; margin: 0.5em;
margin-left: 0; left: 1em;
width: 85%; width: 85%;
font-size: 1em; font-size: 1em;
@ -71,16 +94,6 @@ button {
font-size: 1em; font-size: 1em;
} }
/*
.loader-container {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
*/
.progress-bar { .progress-bar {
width: 100%; width: 100%;
height: 100%; height: 100%;
@ -101,9 +114,6 @@ button {
width: 0; width: 0;
height: 100%; height: 100%;
background-color: #ff5c35; background-color: #ff5c35;
/*
animation: fill 4s infinite;
*/
} }
.message-status { .message-status {
@ -113,14 +123,3 @@ button {
right: 1em; right: 1em;
font-size: .8em; font-size: .8em;
} }
/*
@keyframes fill {
0% {
width: 0;
}
100% {
width: 100%;
}
}
*/