shift/main.js

308 lines
8.4 KiB
JavaScript
Raw Normal View History

const startTime = Date.now();
2024-07-08 01:00:09 +02:00
2024-07-14 16:57:05 +02:00
var conversation = null;
class Conversation {
constructor(name) {
2024-07-14 16:57:05 +02:00
this.messages = [];
this.name = name;
2024-07-14 16:57:05 +02:00
}
initialize(initialMessages) {
document.title = this.name;
document.getElementById("header-title").innerHTML = this.name;
2024-07-14 16:57:05 +02:00
this.messages = initialMessages;
2024-07-14 16:57:05 +02:00
}
// for the user to send their own messages
sendUserMessage(text) {
const message = new UserMessage(text);
message.updateStatus("sent");
2024-07-14 16:57:05 +02:00
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());
2024-07-14 16:57:05 +02:00
}
}
}
function getMessageList() {
return document.getElementById("messages");
}
class AgentMessage {
constructor(text, senderName) {
this.text = text;
this.senderName = senderName;
2024-07-14 16:57:05 +02:00
}
getIsOurs() {
return false;
}
getElement() {
const liElem = document.createElement("li");
2024-07-14 16:57:05 +02:00
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 = "";
}
2024-07-14 16:57:05 +02:00
getIsOurs() {
return true;
}
getElement() {
const liElem = document.createElement("li");
2024-07-14 16:57:05 +02:00
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;
}
}
2024-07-09 22:51:28 +02:00
function setTypingIndicator(isTyping) {
document.getElementById("typing-indicator").innerHTML = isTyping
2024-10-02 02:13:37 +02:00
? `${conversation.contactName} is typing...`
2024-07-09 22:51:28 +02:00
: "";
}
2024-07-08 01:00:09 +02:00
2024-07-09 22:51:28 +02:00
// add the message at the index to the displayed messages
2024-07-14 16:57:05 +02:00
function addMessage(message) {
getMessageList().innerHTML += message.getHtml();
2024-07-10 15:16:33 +02:00
// scroll as far as we can so that messages aren't hidden
window.scrollTo(0, document.body.scrollHeight);
2024-07-08 01:00:09 +02:00
}
function updatePings() {
const title = conversation.name;
2024-07-14 16:57:05 +02:00
let newTitle = conversation.pings > 0
? `(${conversation.pings}) ${title}`
2024-07-08 01:00:09 +02:00
: title;
document.title = newTitle;
}
function clearPings() {
2024-07-14 16:57:05 +02:00
conversation.pings = 0;
2024-07-08 01:00:09 +02:00
updatePings();
}
// returns a decimal value between min and max
function getRandomInRange(min, max) {
2024-07-09 22:51:28 +02:00
const range = max - min;
return min + Math.random() * range;
}
2024-07-14 16:57:05 +02:00
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)
}
2024-07-14 16:57:05 +02:00
function onMessageReceived(message) {
updateChat(message);
setTypingIndicator(false);
}
function onMessageSent(message) {
updateChat(message);
}
2024-10-05 19:35:24 +02:00
// 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)) {
2024-10-05 19:35:24 +02:00
const idx = classes.indexOf(visibleClass);
if (idx != -1) {
classes.splice(idx, 1);
}
2024-10-05 19:35:24 +02:00
classes.push(invisibleClass);
}
2024-10-05 19:35:24 +02:00
element.className = classes.join(" ");
}
2024-10-05 19:35:24 +02:00
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);
2024-07-08 01:00:09 +02:00
}
function readConversationJson(path, callback) {
fetch(path)
.then(response => response.json())
.then(json => callback(json));
}
2024-10-05 19:35:24 +02:00
function showConversation(path) {
const mainPanel = document.getElementById("main-panel");
const conversationListElem = document.getElementById("side-panel");
setVisibleOnMobile(mainPanel, true);
setVisibleOnMobile(conversationListElem, false);
const title = path.split(".")[0];
readConversationJson(path, json => {
conversation = new Conversation(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);
2024-10-05 19:35:24 +02:00
}
}
2024-10-05 19:35:24 +02:00
conversation.initialize(initialMessages);
conversation.render();
});
2024-10-05 19:35:24 +02:00
}
function addConversationPreview(path) {
const title = path.split(".")[0];
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";
2024-10-05 19:35:24 +02:00
const headerElem = document.createElement("h2");
headerElem.innerHTML = title;
elem.appendChild(headerElem);
2024-10-05 19:35:24 +02:00
const previewElem = document.createElement("span");
previewElem.innerHTML = messages[messages.length - 1].text;
elem.appendChild(previewElem);
2024-10-05 19:35:24 +02:00
listRoot.appendChild(elem);
});
2024-10-05 19:35:24 +02:00
}
function populateConversationList() {
const conversationFiles = [
"caesar.json",
"lucius.json",
"ides-of-march.json",
"lepidus.json"
2024-10-05 19:35:24 +02:00
];
for (let i = 0; i < conversationFiles.length; i++) {
const path = conversationFiles[i];
addConversationPreview(path);
}
}
setTypingIndicator(false);
populateConversationList();
showConversation("lucius.json");