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>
<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>
<!-- message box displays the next message to send -->
<p id="typing-indicator">Hester is typing...</p>
<div id="textbox">
<input id="textbox-input" class="rounded-rectangle" type="text" disabled></input>
<button class="rounded-rectangle" onclick="pressSendButton()">send</button>

103
main.js
View File

@ -26,8 +26,9 @@ conversation = [
{ c: 1, text: ":)"}
];
sentMessages = []
let messageIdx = 1;
let title = "hester";
let title = "Hester Gomez";
let pings = 0;
function getMessageList() {
@ -41,17 +42,17 @@ function getMessageElement(messageIdx) {
}
class SentMessage {
constructor(oneWayLag, idx) {
constructor(oneWayLag, idx, onDelivered) {
this.oneWayLag = oneWayLag;
this.idx = idx;
//this.content = conversation[idx].text;
this.createdTime = Date.now();
this.onDelivered = onDelivered;
this.updateBarIntervalId = setInterval(() => {
let elapsed = Math.abs(Date.now() - this.createdTime) / 1000;
let progress = elapsed / this.oneWayLag;
// divide in half so we measure the round trip
// divide in half to measure the round trip
progress /= 2;
this.setProgress(progress);
@ -77,11 +78,11 @@ class SentMessage {
this.updateStatus("delivered");
clearInterval(this.updateBarIntervalId);
amount = 0;
this.onDelivered();
}
amount = Math.min(amount, 1);
let percentage = `${amount * 100}%`;
progressBar.style.width = percentage;
}
updateStatus(newStatus) {
@ -95,23 +96,17 @@ function updateTextBox(message) {
document.getElementById("textbox-input").value = message;
}
// show messages up to index
function showMessages(idx) {
function setTypingIndicator(isTyping) {
document.getElementById("typing-indicator").innerHTML = isTyping
? "Hester is typing..."
: "";
}
let messageList = getMessageList();
// TODO: rebuilding the DOM here clears this status of sent messages. instead of
// rebuilding it completely, append a new message onto the end of the inner html.
// TODO: is this also the juncture to clear the status of previous messages?
// 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));
}
// add the message at the index to the displayed messages
function addMessage(idx) {
let message = conversation[idx];
let messageHtml = getMessageHtml(message.text, isMessageOurs(message));
getMessageList().innerHTML += messageHtml;
}
function getMessageHtml(text, isOurs) {
@ -187,29 +182,45 @@ function getResponses(idx) {
}
function updateChat(messageIdx) {
showMessages(messageIdx);
addMessage(messageIdx-1);
updatePreviewText(messageIdx);
updatePings();
}
function getRandomDelay(min, max) {
const range = max - min;
return min + Math.random() * range;
}
function waitForIncomingMessages() {
// 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
let smallDelay = 2;
let smallDelay = getRandomDelay(2, 10);
let responses = getResponses(messageIdx);
let lightLag = getLightLag();
setTimeout(() => {
setTypingIndicator(true);
for (let i = 0; i < responses.length; i++) {
let delaySeconds = lightLag + smallDelay * i;
const stopTyping = i == responses.length - 1;
setTimeout(() => {
messageIdx++;
pings++;
// update the chat with their message
updateChat(messageIdx);
if (stopTyping) {
setTypingIndicator(false);
}
}, delaySeconds * 1000);
}
},getRandomDelay(1, 3));
}
function updatePreviewText(messageIdx) {
@ -226,24 +237,37 @@ function sendOurMessage() {
// before advancing the conversation
let oneWayLag = getLightLag();
let currentMessage = conversation[messageIdx];
let sentMessage = new SentMessage(oneWayLag, messageIdx);
console.log(sentMessage);
let nextMessage = conversation[messageIdx + 1];
// message.send()
// 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
let sentMessage = new SentMessage(oneWayLag, messageIdx, () => {
// 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
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);
}
@ -273,7 +297,12 @@ function startLightLagUpdateLoop() {
}
function init() {
showMessages(messageIdx);
setTimeout(() => {
addMessage(0);
updatePings();
setTypingIndicator(false);
}, 400);
// load the first message into the text box
updateTextBox(conversation[messageIdx].text);
pings = 1;

View File

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