feat: client interaction
This commit is contained in:
parent
f434022e88
commit
7bb21ea379
12
index.html
12
index.html
|
@ -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
103
main.js
|
@ -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;
|
||||||
|
|
57
styles.css
57
styles.css
|
@ -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%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
Loading…
Reference in New Issue