306 lines
8.3 KiB
JavaScript
306 lines
8.3 KiB
JavaScript
const startTime = Date.now();
|
|
|
|
var conversation = null;
|
|
|
|
class Conversation {
|
|
constructor(name) {
|
|
this.messages = [];
|
|
this.name = name;
|
|
}
|
|
|
|
initialize(initialMessages) {
|
|
document.title = this.name;
|
|
document.getElementById("header-title").innerHTML = this.name;
|
|
|
|
this.messages = initialMessages;
|
|
}
|
|
|
|
// for the user to send their own messages
|
|
sendUserMessage(text) {
|
|
const message = new UserMessage(text);
|
|
message.updateStatus("sent");
|
|
setTimeout(() => {
|
|
message.updateStatus("delivered");
|
|
this.render();
|
|
}, 1000);
|
|
this.messages.push(message);
|
|
}
|
|
|
|
// update the current HTML based on messages
|
|
render() {
|
|
// clear stale HTML
|
|
getMessageList().innerHTML = "";
|
|
|
|
// render message elements
|
|
for (let i = 0; i < this.messages.length; i++)
|
|
{
|
|
const messageRoot = document.getElementById("messages");
|
|
const newMessage = this.messages[i];
|
|
messageRoot.appendChild(newMessage.getElement());
|
|
}
|
|
}
|
|
}
|
|
|
|
function getMessageList() {
|
|
return document.getElementById("messages");
|
|
}
|
|
|
|
class AgentMessage {
|
|
constructor(text, senderName) {
|
|
this.text = text;
|
|
this.senderName = senderName;
|
|
}
|
|
|
|
getIsOurs() {
|
|
return false;
|
|
}
|
|
|
|
getElement() {
|
|
const liElem = document.createElement("li");
|
|
|
|
const contentElem = document.createElement("span");
|
|
contentElem.className = "message-content rounded-rectangle theirs";
|
|
liElem.appendChild(contentElem);
|
|
|
|
if (this.senderName) {
|
|
const nameElem = document.createElement("h3");
|
|
nameElem.innerHTML = this.senderName;
|
|
contentElem.appendChild(nameElem);
|
|
}
|
|
|
|
const textElem = document.createElement("span");
|
|
textElem.className = "message-text";
|
|
textElem.innerHTML = this.text;
|
|
contentElem.appendChild(textElem);
|
|
|
|
return liElem;
|
|
}
|
|
}
|
|
|
|
class UserMessage {
|
|
constructor(text) {
|
|
this.createdTime = Date.now();
|
|
this.text = text;
|
|
this.status = "";
|
|
}
|
|
|
|
getIsOurs() {
|
|
return true;
|
|
}
|
|
|
|
getElement() {
|
|
const liElem = document.createElement("li");
|
|
|
|
const contentElem = document.createElement("span");
|
|
contentElem.className = "message-content rounded-rectangle ours";
|
|
liElem.appendChild(contentElem);
|
|
|
|
const textElem = document.createElement("span");
|
|
textElem.className = "message-text";
|
|
textElem.innerHTML = this.text;
|
|
contentElem.appendChild(textElem);
|
|
|
|
const statusElem = document.createElement("p");
|
|
statusElem.className = "message-status";
|
|
statusElem.innerHTML = this.status;
|
|
liElem.appendChild(statusElem);
|
|
|
|
return liElem;
|
|
}
|
|
|
|
updateStatus(newStatus) {
|
|
this.status = newStatus;
|
|
}
|
|
}
|
|
|
|
function setTypingIndicator(isTyping) {
|
|
document.getElementById("typing-indicator").innerHTML = isTyping
|
|
? `${conversation.contactName} is typing...`
|
|
: "";
|
|
}
|
|
|
|
// add the message at the index to the displayed messages
|
|
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 updatePings() {
|
|
const title = conversation.name;
|
|
let newTitle = conversation.pings > 0
|
|
? `(${conversation.pings}) ${title}`
|
|
: title;
|
|
|
|
document.title = newTitle;
|
|
}
|
|
|
|
function clearPings() {
|
|
conversation.pings = 0;
|
|
updatePings();
|
|
}
|
|
|
|
// returns a decimal value between min and max
|
|
function getRandomInRange(min, max) {
|
|
const range = max - min;
|
|
return min + Math.random() * range;
|
|
}
|
|
|
|
function updateChat(message) {
|
|
addMessage(message);
|
|
const previewText = conversation.getTypedMessageText();
|
|
document.getElementById("textbox-input").value = previewText;
|
|
updatePings();
|
|
}
|
|
|
|
function pressSendButton() {
|
|
if (event.type == "keydown" && event.key != "Enter")
|
|
return;
|
|
|
|
// we have interacted with the page so remove all pings
|
|
clearPings();
|
|
|
|
// get the content of the text box
|
|
const textBox = document.getElementById("textbox-input");
|
|
const text = textBox.value;
|
|
if (!text)
|
|
return;
|
|
|
|
textBox.value = "";
|
|
|
|
conversation.sendUserMessage(text);
|
|
conversation.render();
|
|
|
|
// TODO: start process of receiving next message from server (or fake it for now)
|
|
}
|
|
|
|
function onMessageReceived(message) {
|
|
updateChat(message);
|
|
setTypingIndicator(false);
|
|
}
|
|
|
|
function onMessageSent(message) {
|
|
updateChat(message);
|
|
}
|
|
|
|
// probably a bit hacky! but this saves having to do like, state or something in CSS?
|
|
// which probably is possible and probably would be the better way to do it, but that
|
|
// sounds like a bunch of learning i'm not SUPER in the mood for
|
|
function setVisibleOnMobile(element, isVisible) {
|
|
let classes = element.className.split().filter(c => c != "");
|
|
const invisibleClass = "invisible-on-mobile";
|
|
const visibleClass = "visible";
|
|
|
|
if (isVisible && !classes.includes(visibleClass)) {
|
|
const idx = classes.indexOf(invisibleClass);
|
|
if (idx != -1) {
|
|
classes.splice(idx, 1);
|
|
}
|
|
classes.push(visibleClass);
|
|
} else if (!isVisible && !classes.includes(invisibleClass)) {
|
|
const idx = classes.indexOf(visibleClass);
|
|
if (idx != -1) {
|
|
classes.splice(idx, 1);
|
|
}
|
|
classes.push(invisibleClass);
|
|
}
|
|
|
|
element.className = classes.join(" ");
|
|
}
|
|
|
|
function showSidePanel() {
|
|
// this function can only be called on mobile. the main conversation should be
|
|
// hidden and the side conversations panel should take up the whole screen.
|
|
|
|
const mainPanel = document.getElementById("main-panel");
|
|
const conversationListElem = document.getElementById("side-panel");
|
|
|
|
setVisibleOnMobile(mainPanel, false);
|
|
setVisibleOnMobile(conversationListElem, true);
|
|
}
|
|
|
|
function readConversationJson(path, callback) {
|
|
fetch(path)
|
|
.then(response => response.json())
|
|
.then(json => callback(json));
|
|
}
|
|
|
|
function showConversation(path) {
|
|
|
|
const mainPanel = document.getElementById("main-panel");
|
|
const conversationListElem = document.getElementById("side-panel");
|
|
|
|
setVisibleOnMobile(mainPanel, true);
|
|
setVisibleOnMobile(conversationListElem, false);
|
|
|
|
|
|
readConversationJson(path, json => {
|
|
conversation = new Conversation(json.title);
|
|
const jsonMessages = json.messages;
|
|
const participants = json.characters;
|
|
|
|
let initialMessages = [];
|
|
|
|
for (let i = 0; i < jsonMessages.length; i++) {
|
|
const data = jsonMessages[i];
|
|
const text = data.text;
|
|
if (data.character == 0) {
|
|
const message = new UserMessage(text);
|
|
message.updateStatus("delivered");
|
|
initialMessages.push(message);
|
|
} else {
|
|
const message = participants.length > 2
|
|
? new AgentMessage(text, participants[data.character])
|
|
: new AgentMessage(text);
|
|
initialMessages.push(message);
|
|
}
|
|
}
|
|
|
|
conversation.initialize(initialMessages);
|
|
conversation.render();
|
|
});
|
|
}
|
|
|
|
function addConversationPreview(path) {
|
|
const listRoot = document.getElementById("side-panel");
|
|
|
|
readConversationJson(path, json => {
|
|
const messages = json.messages;
|
|
|
|
const elem = document.createElement("div");
|
|
elem.onclick = () => showConversation(path);
|
|
elem.className = "conversation";
|
|
|
|
const headerElem = document.createElement("h2");
|
|
headerElem.innerHTML = json.title;
|
|
elem.appendChild(headerElem);
|
|
|
|
const previewElem = document.createElement("span");
|
|
previewElem.innerHTML = messages[messages.length - 1].text;
|
|
elem.appendChild(previewElem);
|
|
|
|
listRoot.appendChild(elem);
|
|
});
|
|
}
|
|
|
|
function populateConversationList() {
|
|
const conversationFiles = [
|
|
"caesar.json",
|
|
"lucius.json",
|
|
"ides-of-march.json",
|
|
"lepidus.json"
|
|
];
|
|
|
|
for (let i = 0; i < conversationFiles.length; i++) {
|
|
const path = conversationFiles[i];
|
|
addConversationPreview(path);
|
|
}
|
|
}
|
|
|
|
setTypingIndicator(false);
|
|
populateConversationList();
|
|
|
|
showConversation("lucius.json");
|
|
|