// how to we communicate... // * in flight // * arrived! (probably) <- this is the tricky one. we know enough time has passed for the message // * received to have been received, but we are only halfway to the earliest possible // * read acknowledgement. // // we know the length of the roundtrip: it is 2x the light lag. progress bars show a passage of time, // it could scroll: // * along the bottom of the message // * across the message // * as it is being sent, a transluscent red bar grows from right to left (towards their messages) // * while we are waiting for acknowledgement, a similar blue bar brows from left to right // * next to the message conversation = [ { c: 1, text: "hows space"}, { c: 0, text: "trying to work out if the coffees shit but"}, { c: 0, text: "taste not happenin"}, { c: 1, text: "maybe youre being spared"}, { c: 0, text: "youre right"}, { c: 0, text: "ship swill is a delicacy to no one"}, { c: 0, text: "at least the caffeines doing its job"}, { c: 1, text: "good to hear!"}, { c: 1, text: "induction today wish me luckk"}, { c: 0, text: "break a leg!"}, { c: 1, text: ":)"} ]; let messageIdx = 1; let title = "hester"; let pings = 0; function getMessageList() { return document.getElementById("messages"); } function getMessageElement(messageIdx) { let list = getMessageList(); let messageElements = list.getElementsByTagName("li"); return messageElements[messageIdx]; } class SentMessage { constructor(oneWayLag, idx) { this.oneWayLag = oneWayLag; this.idx = idx; //this.content = conversation[idx].text; this.createdTime = Date.now(); 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 progress /= 2; this.setProgress(progress); }, 10); } setProgress(amount) { let thisMessage = getMessageElement(this.idx); let progressBar = thisMessage.getElementsByClassName("progress")[0]; if (amount < 0.5) { this.updateStatus("in flight"); progressBar.style.backgroundColor = "red"; amount *= 2; } else if (amount < 1) { this.updateStatus("completing round trip"); progressBar.style.backgroundColor = "blue"; amount -= 0.5; amount *= 2; } else { this.updateStatus("delivered"); clearInterval(this.updateBarIntervalId); amount = 0; } amount = Math.min(amount, 1); let percentage = `${amount * 100}%`; progressBar.style.width = percentage; } updateStatus(newStatus) { let thisMessage = getMessageElement(this.idx); let statusElement = thisMessage.getElementsByClassName("message-status")[0]; statusElement.innerHTML = newStatus; } } function updateTextBox(message) { document.getElementById("textbox-input").value = message; } // show messages up to index function showMessages(idx) { 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)); } } function getMessageHtml(text, isOurs) { let owner = isOurs ? "ours" : "theirs"; // we don't want loading bars on their messages, since we have no idea if one has been sent // until it arrives let progressBar = isOurs ? `
` : ""; let statusText = isOurs ? `

status

` : ""; let message = `
  • ${progressBar} ${text}
    ${statusText}
  • `; return message; } function isMessageOurs(message) { if (!message) return false; return message.c == 0; } function getOurNextMessage(idx) { for (let i = idx; i < conversation.length; i++) { message = conversation[i]; if (isMessageOurs(message)) return message; } return null; } function getLightLag() { lag = 3.0 + Math.sin(Date.now() / 10000); return Math.round(lag * 10000) / 10000; } function updatePings() { let newTitle = pings > 0 ? `(${pings}) ${title}` : title; document.title = newTitle; } function clearPings() { pings = 0; updatePings(); } function getResponses(idx) { // get the messages that aren't ours until we find one that is let responses = []; for (let i = idx; i < conversation.length; i++) { message = conversation[i]; if (isMessageOurs(message)) break; responses.push(message); } return responses; } function updateChat(messageIdx) { showMessages(messageIdx); updatePreviewText(messageIdx); updatePings(); } 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 responses = getResponses(messageIdx); let lightLag = getLightLag(); for (let i = 0; i < responses.length; i++) { let delaySeconds = lightLag + smallDelay * i; setTimeout(() => { messageIdx++; pings++; // update the chat with their message updateChat(messageIdx); }, delaySeconds * 1000); } } function updatePreviewText(messageIdx) { // display our next message or an empty box let nextMessage = conversation[messageIdx]; let previewText = isMessageOurs(nextMessage) ? conversation[messageIdx].text : ""; updateTextBox(previewText); } function sendOurMessage() { // the message we are sending is the one currently in the text box, so we should construct it // before advancing the conversation let oneWayLag = getLightLag(); let currentMessage = conversation[messageIdx]; let sentMessage = new SentMessage(oneWayLag, messageIdx); console.log(sentMessage); // 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 // 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(); } updateChat(messageIdx); } function pressSendButton() { // we have interacted with the page so remove all pings clearPings(); // peek conversation state let nextMessage = conversation[messageIdx]; // we are still waiting to receive messages so pressing the button oughtn't do anything if (!isMessageOurs(nextMessage)) return; sendOurMessage(); } function updateLightLag() { let text = getLightLag().toFixed(5) + " seconds"; document.getElementById("delay-text").innerHTML = text; } function startLightLagUpdateLoop() { setInterval(() => { updateLightLag(); }, 1000); } function init() { showMessages(messageIdx); // load the first message into the text box updateTextBox(conversation[messageIdx].text); pings = 1; document.title = title; updateLightLag(0); startLightLagUpdateLoop(); } init();