// In real world, I would get this variables from server through nytl let pres = {lang: ''} let userinfo = {id: 2, nickname: 'pv', name: 'Pavlov Vladimir'}; let openedchat = {id: 100, name: "Some chat", nickname: 'chat'}; let initial_chatUpdResp = { LocalHistoryId: 0, lastMsgId: -1, messages: [], members: [ {id: 1, name: 'grisha', nickname: 'gri', role: 'admin'}, {id: 2, name: 'Pavlov Vladimir', nickname: 'pv', role: 'regular'}, {id: 3, name: 'Ivan', nickname: 'ivan', role: 'read-only'} ] }; let loadedMessages = new Map(); // messageSt objects let visibleMessages = new Map(); // HTMLElement objects let anchoredMsg = -1; let visibleMsgSegStart = -1; let visibleMsgSegEnd = -2; let offsetOfAnchor = 100; // Would start with true if opened `/chat/<>` let bumpedAtTheBottom = false; let members = new Map(); for (let memberSt of initial_chatUpdResp.members){ members.set(memberSt.id, memberSt); } function updateOffsetOfVisibleMsg(msgId, offset){ visibleMessages.get(msgId).style.bottom = String(offset) + "px"; } function updateOffsetsUpToTop(){ let offset = offsetOfAnchor; for (let curMsg = anchoredMsg; curMsg >= visibleMsgSegStart; curMsg--){ updateOffsetOfVisibleMsg(curMsg, offset); let height = visibleMessages.get(curMsg).offsetHeight; offset += height + 5; } return offset; } function updateOffsetsDown(){ let offset = offsetOfAnchor; for (let curMsg = anchoredMsg + 1; curMsg <= visibleMsgSegEnd; curMsg++){ let height = visibleMessages.get(curMsg).offsetHeight; offset -= (height + 5); updateOffsetOfVisibleMsg(curMsg, offset); } return offset; } function updateOffsetsSane(){ let highest_point = updateOffsetsUpToTop(); let lowest_point = updateOffsetsDown(); return [highest_point, lowest_point]; } function updateOffsets(){ if (anchoredMsg < 0) return; let [highest_point, lowest_point] = updateOffsetsSane(); let winTop = document.getElementById("chat-widget").offsetHeight; if (lowest_point > 5 || (highest_point - lowest_point) <= winTop){ bumpedAtTheBottom = true; anchoredMsg = visibleMsgSegEnd; offsetOfAnchor = 5; updateOffsetsSane(); } else if (highest_point < winTop - 5){ console.log("Adancing by " + (winTop - 5 - highest_point)) offsetOfAnchor += (winTop - 5 - highest_point); updateOffsetsSane(); } } function makeMessageBox(messageSt){ if (!messageSt.exists || messageSt.isSystem) throw new Error("Not ready for this"); const parentDiv = document.getElementById("chat-widget"); let box = document.createElement("div"); parentDiv.appendChild(box); box.className = "message-box"; let topPart = document.createElement("div"); box.appendChild(topPart); topPart.className = "message-box-top"; let inTopPartSenderName = document.createElement("a"); topPart.appendChild(inTopPartSenderName); inTopPartSenderName.className = "message-box-sender-name"; if (!members.has(messageSt.senderUserId)) throw new Error("MMMHMMMMGMHMMMM. First - update members. Then messages"); let memberSt = members.get(messageSt.senderUserId); inTopPartSenderName.innerText = memberSt.name + " (" + memberSt.nickname + ")"; inTopPartSenderName.href = "/user/" + memberSt.nickname; let ID = messageSt.id; let inTopPartButtonDelete = document.createElement("img"); topPart.appendChild(inTopPartButtonDelete); inTopPartButtonDelete.className = "message-box-button"; inTopPartButtonDelete.src = "/assets/img/delete.svg"; inTopPartButtonDelete.onclick = (ev) => { if (ev.button === 0){ console.log("Tried to delete message " + ID); } }; let inTopPartButtonGetLink = document.createElement("img"); topPart.appendChild(inTopPartButtonGetLink); inTopPartButtonGetLink.className = "message-box-button"; inTopPartButtonGetLink.src = "/assets/img/link.svg"; inTopPartButtonGetLink.onclick = (ev) => { if (ev.button === 0){ console.log("Tried to get link on message " + ID); } }; let msgPart = document.createElement("p"); box.appendChild(msgPart); msgPart.className = "message-box-msg"; msgPart.innerText = messageSt.text; return box; } function makeVisible(msgId){ visibleMessages.set(msgId, makeMessageBox(loadedMessages.get(msgId))) } function opaNewMessage(messageSt){ let msgId = messageSt.id; let text = messageSt.text; const chatWin = document.getElementById("chat-widget"); if (loadedMessages.has(msgId)){ throw new Error("Not ready yet"); // loadedMessages.get(msgId).text = text; // if (visibleMessages.has(msgId)){ // visibleMessages.get(msgId).textContent = text; // updateOffsets(); // } } else { loadedMessages.set(msgId, messageSt); if (anchoredMsg < 0){ anchoredMsg = msgId; visibleMsgSegStart = msgId; visibleMsgSegEnd = msgId; makeVisible(msgId); updateOffsets(); } else if (msgId + 1 === visibleMsgSegStart) { visibleMsgSegStart--; makeVisible(msgId); while (loadedMessages.has(visibleMsgSegStart - 1)){ visibleMsgSegStart--; makeVisible(visibleMsgSegStart); } updateOffsets(); } else if (msgId - 1 === visibleMsgSegEnd){ visibleMsgSegEnd++; makeVisible(msgId); while (loadedMessages.has(visibleMsgSegEnd + 1)){ visibleMsgSegEnd++; makeVisible(visibleMsgSegEnd); } updateOffsets(); } } } function test(id, uid){ opaNewMessage({ id: id, text: "Message number " + String(id), senderUserId: uid, exists: true, isSystem: false} ); } let mainloopTimeout = null; let mainloopPoller = null; function setMainloopTimeout(){ mainloopTimeout = setTimeout(mainloopPoller, 1000); } mainloopPoller = function(){ try { console.log("Hello, World!"); } catch (error){} setMainloopTimeout(); } window.onload = function (){ console.log("Everything was loaded"); test(6, 1); test(1, 2); test(3, 3); test(2, 1); test(4, 2); test(0, 3); test(5, 2); test(8, 1); test(9, 2); test(7, 3); for (let i = 10; i < 30; i++){ test(i, 2); } document.body.addEventListener("wheel", (event) => { event.preventDefault(); bumpedAtTheBottom = false; console.log("Scroll of " + String(event.deltaY)); offsetOfAnchor += event.deltaY / 5; updateOffsets(); }); mainloopPoller(); document.getElementById("message-input").addEventListener("keyup", (event) => { if (event.ctrlKey && event.key === 'Enter'){ let textarea = document.getElementById("message-input"); console.log(textarea.value); } }); }