diff --git a/main.js b/main.js
index 9a3aa34..ea196d3 100644
--- a/main.js
+++ b/main.js
@@ -12,7 +12,8 @@
// * while we are waiting for acknowledgement, a similar blue bar brows from left to right
// * next to the message
-conversation = [
+messageData = [
+ // c for character
{ c: 1, text: "hows space"},
{ c: 0, text: "trying to work out if the coffees shit but"},
{ c: 0, text: "my sense of taste is just not happening :/"},
@@ -32,28 +33,214 @@ conversation = [
{ c: 1, text: ":)"}
];
-sentMessages = []
-let messageIdx = -1;
-let title = "Hester Gomez";
let pings = 0;
const startTime = Date.now();
+var conversation = null;
+
+class Conversation {
+ constructor(messageData, contactName, onMessageReceived, onMessageSent) {
+ this.messageData = messageData;
+ this.contactName = contactName;
+
+ this.messageIdx = -1;
+ // contains both sent and received messages
+ this.messages = [];
+
+ // callbacks
+ this.onMessageReceived = onMessageReceived;
+ this.onMessageSent = onMessageSent;
+ }
+
+ start() {
+ setTimeout(() => {
+ this.messageIdx = 0;
+ this.pings = 1;
+
+ const data = this.messageData[this.messageIdx];
+ const message = new ReceivedMessage(data);
+ this.messages.push(message);
+ this.onMessageReceived(message);
+ }, 3623);
+ }
+
+ peekNextMessageData() {
+ return this.messageData[this.messageIdx + 1];
+ }
+
+ // get the text typed into the text box, ready to be sent. this could be blank
+ getTypedMessageText() {
+ const messageData = this.peekNextMessageData();
+ return this.isMessageDataOurs(messageData) ? messageData.text : "";
+ }
+
+ getLightLag() {
+ const baseLag = 9.23582;
+
+ // second since opening the page
+ const elapsed = (Date.now() - startTime) / 1000.5;
+
+ // lag should shift on the order of 10,000ths of seconds per second
+ const lag = baseLag + elapsed / 8439.123;
+ return Math.round(lag * 100000) / 100000;
+ }
+
+ sendMessage() {
+ // bail out if the next message isn't ours
+ const sentMessageData = this.messageData[this.messageIdx + 1];
+ if (!this.isMessageDataOurs(sentMessageData))
+ return;
+
+ // TODO: error checking? what if we can't send our next message?
+
+ // the message we are sending is the one currently in the text box, so we should construct it
+ // before advancing the conversation
+ let oneWayLag = this.getLightLag();
+ let currentMessage = this.messageData[this.messageIdx];
+ let nextMessage = this.messageData[this.messageIdx + 2];
+
+ let sentMessage = new SentMessage(sentMessageData, oneWayLag, this.messageIdx + 1, () => {
+ // if the next message is ours we don't need to wait for anything
+ if (this.isMessageDataOurs(nextMessage))
+ return;
+
+ // wait for them to read the message
+ setTimeout(() => {
+ this.onMessageRead(sentMessage);
+ }, getRandomDelay(1, 20) * 1000);
+ });
+
+ this.messages.push(sentMessage);
+
+ // advance conversation with our next message
+ this.messageIdx++;
+
+ this.onMessageSent(sentMessage);
+ }
+
+ onMessageRead(sentMessage) {
+ // set the message status to read
+ sentMessage.updateStatus("read");
+
+ // we only want to count our messages
+ const sentMessages = this.messages.filter(m => m.getIsOurs());
+ // 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++) {
+ const message = sentMessages[i];
+ if (message != sentMessage) {
+ message.updateStatus("");
+ }
+ }
+
+ this.waitForIncomingMessages();
+ }
+
+ 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 = getRandomDelay(2, 10);
+ let responses = this.getResponses(this.messageIdx + 1);
+ let lightLag = this.getLightLag();
+
+ setTimeout(() => {
+ setTypingIndicator(true);
+
+ if (responses.length == 0) {
+ console.error("got no responses?");
+ }
+
+ for (let i = 0; i < responses.length; i++) {
+ let delaySeconds = lightLag + smallDelay * i;
+
+ const stopTyping = i == responses.length - 1;
+
+ setTimeout(() => {
+ this.messageIdx++;
+ this.pings++;
+
+ // update the chat with their message
+ const data = this.messageData[this.messageIdx];
+ const message = new ReceivedMessage(data);
+ updateChat(message);
+
+ if (stopTyping) {
+ setTypingIndicator(false);
+ }
+
+ }, delaySeconds * 1000);
+ }
+ },getRandomDelay(1, 3));
+ }
+
+ getResponses(idx) {
+ // get the messages that aren't ours until we find one that is
+ let responses = [];
+
+ for (let i = idx; i < this.messageData.length; i++) {
+ const message = this.messageData[i];
+ if (this.isMessageDataOurs(message))
+ break;
+
+ responses.push(message);
+ }
+
+ return responses;
+ }
+
+ // TODO: messages could be instantiated when the conversation is started to remove the isMessageOurs
+ // query on the raw data?
+ // however, this would mean that we would need to determine the light delay dynamically when sending
+ // the message, instead of on construction. this might not be a bad thing? but seems like a later
+ // problem
+ // for now, we should see to it that message ownership is determined only in the context of a specific
+ // conversation
+ isMessageDataOurs(messageData) {
+ if (!messageData)
+ return false;
+
+ return messageData.c == 0;
+ }
+}
+
function getMessageList() {
return document.getElementById("messages");
}
+// TODO: messges should reference their own elements to remove the need for searching through
+// the DOM
function getMessageElement(messageIdx) {
let list = getMessageList();
let messageElements = list.getElementsByTagName("li");
return messageElements[messageIdx];
}
+class ReceivedMessage {
+ constructor(data) {
+ this.text = data.text;
+ }
+
+ getIsOurs() {
+ return false;
+ }
+
+ getHtml() {
+ return `
+
+ ${this.text}
+
`;
+ }
+}
+
class SentMessage {
- constructor(oneWayLag, idx, onDelivered) {
+ constructor(data, oneWayLag, idx, onDelivered) {
this.oneWayLag = oneWayLag;
+ // TODO: remove idx reference - why should a message know where it sits in a conversation?
+ // indexing should be the responsibility of the conversation
this.idx = idx;
this.createdTime = Date.now();
this.onDelivered = onDelivered;
+ this.text = data.text;
this.updateBarIntervalId = setInterval(() => {
let elapsed = Math.abs(Date.now() - this.createdTime) / 1000;
@@ -66,6 +253,18 @@ class SentMessage {
}, 10);
}
+ getIsOurs() {
+ return true;
+ }
+
+ getHtml() {
+ return `status
`;
+ }
+
setProgress(amount) {
let thisMessage = getMessageElement(this.idx);
let progressBar = thisMessage.getElementsByClassName("progress")[0];
@@ -104,10 +303,6 @@ class SentMessage {
}
}
-function updateTextBox(message) {
- document.getElementById("textbox-input").value = message;
-}
-
function setTypingIndicator(isTyping) {
document.getElementById("typing-indicator").innerHTML = isTyping
? "Hester is typing..."
@@ -115,44 +310,13 @@ function setTypingIndicator(isTyping) {
}
// 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 addMessage(message) {
+ getMessageList().innerHTML += message.getHtml();
// scroll as far as we can so that messages aren't hidden
window.scrollTo(0, document.body.scrollHeight);
}
-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];
@@ -163,151 +327,41 @@ function getOurNextMessage(idx) {
return null;
}
-function getLightLag() {
- const baseLag = 9.23582;
-
- // second since opening the page
- const elapsed = (Date.now() - startTime) / 1000.5;
-
- // lag should shift on the order of 10,000ths of seconds per second
- const lag = baseLag + elapsed / 8439.123;
- return Math.round(lag * 100000) / 100000;
-}
-
function updatePings() {
- let newTitle = pings > 0
- ? `(${pings}) ${title}`
+ const title = conversation.contactName;
+ let newTitle = conversation.pings > 0
+ ? `(${conversation.pings}) ${title}`
: title;
document.title = newTitle;
}
function clearPings() {
- pings = 0;
+ conversation.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 getRandomDelay(min, max) {
const range = max - min;
return min + Math.random() * range;
}
-function updateChat(messageIdx) {
- addMessage(messageIdx);
- const nextMessage = conversation[messageIdx+1];
- updatePreviewText(nextMessage);
+function updateChat(message) {
+ addMessage(message);
+ document.getElementById("textbox-input").value = conversation.getTypedMessageText();
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 = getRandomDelay(2, 10);
- let responses = getResponses(messageIdx + 1);
- 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(message) {
- // display our next message or an empty box
- let previewText = isMessageOurs(message)
- ? message.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 nextMessage = conversation[messageIdx + 2];
-
- let sentMessage = new SentMessage(oneWayLag, messageIdx + 1, () => {
-
- // 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(1, 20) * 1000);
- });
- sentMessages.push(sentMessage);
-
- // advance conversation with our next message
- messageIdx++;
-
- // update displayed messages
- updateChat(messageIdx);
-}
function pressSendButton() {
// we have interacted with the page so remove all pings
clearPings();
-
- // peek conversation state
- let nextMessage = conversation[messageIdx + 1];
-
- // we are still waiting to receive messages so pressing the button oughtn't do anything
- if (!isMessageOurs(nextMessage))
- return;
-
- sendOurMessage();
+ conversation.sendMessage();
}
function updateLightLag() {
- let text = getLightLag().toFixed(5) + " seconds";
+ const lag = conversation.getLightLag();
+ const text = lag.toFixed(5) + " seconds";
document.getElementById("delay-text").innerHTML = text;
}
@@ -317,15 +371,20 @@ function startLightLagUpdateLoop() {
}, 1000);
}
-function init() {
- setTimeout(() => {
- messageIdx = 0;
- pings = 1;
- updateChat(messageIdx);
- setTypingIndicator(false);
- }, 3623);
+function onMessageReceived(message) {
+ updateChat(message);
+ setTypingIndicator(false);
+}
- document.title = title;
+function onMessageSent(message) {
+ updateChat(message);
+}
+
+function init() {
+ conversation = new Conversation(messageData, "Hester Gomez", onMessageReceived, onMessageSent);
+ conversation.start();
+
+ document.title = conversation.contactName;
updateLightLag(0);
startLightLagUpdateLoop();