FIRST WORKING VERSION OF IU9 CA WEB CHAT. IT'S WORKINGgit add -A! YOU CAN SEND STUFF INTO CHAT AND IT WILL LOAD UP ON ANOTHER DEVICE EEEEEEEE

This commit is contained in:
Андреев Григорий 2024-09-05 00:17:33 +03:00
parent b9626aa860
commit 2219653f40
7 changed files with 156 additions and 74 deletions

View File

@ -45,8 +45,16 @@
</a> </a>
</div> </div>
<div id="chat-widget"> <div id="chat-widget">
<div class="chat-debug-rect chat-debug-rect-top" id="debug-line-highest"></div> <div class="chat-debug-rect" id="debug-line-highest" style="background-color: #8600d3"></div>
<div class="chat-debug-rect" id="debug-line-lowest"></div> <div class="chat-debug-rect" id="debug-line-top-padding" style="background-color: #ff00ae"></div>
<div class="chat-debug-rect" id="debug-line-bottom-padding" style="background-color: #ff0062"></div>
<div class="chat-debug-rect" id="debug-line-lowest" style="background-color: #ff2f00"></div>
<div id="top-loading" class="message-supercontainer">
<img class="loading-spinner" alt="Loading backward..." src="/assets/gif/loading.gif">
</div>
<div id="bottom-loading" class="message-supercontainer">
<img class="loading-spinner" alt="Loading forward..." src="/assets/gif/loading.gif">
</div>
</div> </div>
<div class="panel" id="input-panel"> <div class="panel" id="input-panel">
<div contentEditable id="message-input" class="panel-thing"></div> <div contentEditable id="message-input" class="panel-thing"></div>

View File

@ -6,46 +6,66 @@ body, html {
position: relative; position: relative;
flex: 1; flex: 1;
background-color: #f1f1f1; background-color: #f1f1f1;
color: black;
overflow: hidden; overflow: hidden;
} }
.message-box-mine { .message-supercontainer{
position: absolute; position: absolute;
right: 5px; width: 100%;
left: 0;
background-color: rgba(150, 0, 100, 50);
/*display: flex;*/
/*flex-direction: row;*/
/*justify-content: center;*/
}
.message-box{
/*display: inline-block;*/
padding: 5px;
}
.message-box-mine {
margin-right: 5px;
margin-left: auto;
max-width: 400px; max-width: 400px;
border: 2px solid #82a173; border: 2px solid #82a173;
padding: 5px; padding: 5px;
background-color: #cdff9b; background-color: #cdff9b;
color: black; color: black;
/*justify-self: flex-end;*/
} }
.message-box-alien { .message-box-alien {
position: absolute; margin-left: 5px;
left: 5px; margin-right: auto;
max-width: 400px; max-width: 400px;
border: 2px solid dimgrey; border: 2px solid dimgrey;
padding: 5px; padding: 5px;
background-color: white; background-color: white;
color: black; color: black;
/*justify-self: flex-start;*/
} }
/* Only non-system messages can be deleted. Deleted messages do not have delete button /* Only non-system messages can be deleted. Deleted messages do not have delete button
This class should be used with (and, ofcourse, after) class message-box-my/message-box-alien */ This class should be used with (and, ofcourse, after) class message-box-my/message-box-alien */
.message-box-deleted { .message-box-deleted {
font-weight: bold;
border: 2px solid #cb0005; border: 2px solid #cb0005;
background-color: #ffc1bc; background-color: #ffc1bc;
} }
.message-box-deleted .message-box-msg{
font-weight: bold;
}
.message-box-system { .message-box-system {
position: absolute; margin-left: auto;
left: 15px; margin-right: auto;
max-width: 500px; max-width: 500px;
padding: 4px; padding: 4px;
background-color: #2d2d2d; background-color: #2d2d2d;
color: white; color: white;
font-weight: bold; font-weight: bold;
justify-self: center;
} }
/* in #chat-widget .message-box */ /* in #chat-widget .message-box */
@ -80,6 +100,7 @@ body, html {
width: 20px; width: 20px;
padding: 2px; padding: 2px;
cursor: pointer; cursor: pointer;
display: inline;
} }
.message-box-msg{ .message-box-msg{
@ -111,3 +132,11 @@ body, html {
max-height: 20%; max-height: 20%;
word-wrap: break-word; word-wrap: break-word;
} }
.loading-spinner{
margin-left: auto;
margin-right: auto;
background-color: rgba(0, 0, 0, 0);
width: 25px;
display: block;
}

View File

@ -2,11 +2,7 @@
width: 100%; width: 100%;
position: absolute; position: absolute;
left: 0; left: 0;
background-color: rgba(255, 50, 50, 160); opacity: 0.3;
height: 4px; height: 3px;
z-index: 2; z-index: 2;
} }
.chat-debug-rect-top{
background-color: rgba(148, 0, 211, 160);
}

BIN
assets/gif/loading.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

View File

@ -3,6 +3,8 @@ let LocalHistoryId = 0;
let members = new Map(); let members = new Map();
let loadedMessages = new Map(); // messageSt objects let loadedMessages = new Map(); // messageSt objects
/*
container: EL, box: EL, offset: number (msgPres) */
let visibleMessages = new Map(); // HTMLElement objects let visibleMessages = new Map(); // HTMLElement objects
let anchoredMsg = -1; let anchoredMsg = -1;
@ -41,15 +43,37 @@ function genSentBaseGMN(){
return Sent; return Sent;
} }
function getChatWgSz(){
let chatWg = document.getElementById("chat-widget");
return [chatWg.offsetWidth, chatWg.offsetHeight];
}
function elSetOffsetInChat(el, offset){
el.style.bottom = String(offset) + "px";
}
function isMissingPrimaryMsgHeap(){
return lastMsgId >= 0 && anchoredMsg < 0;
}
function isMissingTopMsgHeap(){
let [W, H] = getChatWgSz();
return anchoredMsg >= 0 && (highestPoint < H + softZoneSz && visibleMsgSegStart > 0);
}
function isMissingBottomMsgHeap(){
return anchoredMsg >= 0 && (lowestPoint > - softZoneSz && visibleMsgSegEnd < lastMsgId);
}
function updateOffsetOfVisibleMsg(msgId, offset){ function updateOffsetOfVisibleMsg(msgId, offset){
visibleMessages.get(msgId).style.bottom = String(offset) + "px"; visibleMessages.get(msgId).container.style.bottom = String(offset) + "px";
} }
function updateOffsetsUpToTop(){ function updateOffsetsUpToTop(){
let offset = offsetOfAnchor; let offset = offsetOfAnchor;
for (let curMsg = anchoredMsg; curMsg >= visibleMsgSegStart; curMsg--){ for (let curMsg = anchoredMsg; curMsg >= visibleMsgSegStart; curMsg--){
updateOffsetOfVisibleMsg(curMsg, offset); updateOffsetOfVisibleMsg(curMsg, offset);
let height = visibleMessages.get(curMsg).offsetHeight; let height = visibleMessages.get(curMsg).container.offsetHeight;
offset += height + 5; offset += height + 5;
} }
return offset; return offset;
@ -58,7 +82,7 @@ function updateOffsetsUpToTop(){
function updateOffsetsDown(){ function updateOffsetsDown(){
let offset = offsetOfAnchor; let offset = offsetOfAnchor;
for (let curMsg = anchoredMsg + 1; curMsg <= visibleMsgSegEnd; curMsg++){ for (let curMsg = anchoredMsg + 1; curMsg <= visibleMsgSegEnd; curMsg++){
let height = visibleMessages.get(curMsg).offsetHeight; let height = visibleMessages.get(curMsg).container.offsetHeight;
offset -= (height + 5); offset -= (height + 5);
updateOffsetOfVisibleMsg(curMsg, offset); updateOffsetOfVisibleMsg(curMsg, offset);
} }
@ -72,28 +96,47 @@ function updateOffsetsSane(){
lowestPoint = updateOffsetsDown(); lowestPoint = updateOffsetsDown();
} }
function heightOfPreloadGhost(){
let [W, H] = getChatWgSz();
return Math.min(H * 0.9, Math.max(H * 0.69, 30));
}
function updateOffsets(){ function updateOffsets(){
if (anchoredMsg < 0) let spinnerTop = document.getElementById("top-loading");
return; let spinnerBottom = document.getElementById("bottom-loading");
updateOffsetsSane() let SbH = spinnerBottom.offsetHeight;
let winTop = document.getElementById("chat-widget").offsetHeight; if (anchoredMsg < 0){
if (lowestPoint > chatPadding || (highestPoint - lowestPoint) <= winTop){ hideHTMLElement(spinnerBottom);
bumpedAtTheBottom = true; elSetOffsetInChat(spinnerTop, chatPadding);
anchoredMsg = visibleMsgSegEnd; setElementVisibility(spinnerTop, isMissingPrimaryMsgHeap());
offsetOfAnchor = chatPadding; } else {
updateOffsetsSane();
} else if (highestPoint < winTop - chatPadding) {
console.log("Advancing by " + (winTop - chatPadding - highestPoint))
offsetOfAnchor += (winTop - chatPadding - highestPoint);
updateOffsetsSane(); updateOffsetsSane();
let [W, H] = getChatWgSz();
let lowestLowestPoint = isMissingBottomMsgHeap() ? lowestPoint - heightOfPreloadGhost(): lowestPoint;
let highestHighestPoint = isMissingTopMsgHeap() ? highestPoint + heightOfPreloadGhost() : highestPoint;
if (lowestLowestPoint > chatPadding || (highestHighestPoint - lowestLowestPoint) <= H - chatPadding * 2) {
bumpedAtTheBottom = true;
offsetOfAnchor += (-lowestLowestPoint + chatPadding);
updateOffsetsSane();
} else if (highestHighestPoint < H - chatPadding) {
offsetOfAnchor += (-highestHighestPoint + (H - chatPadding));
updateOffsetsSane();
}
/* Messages weere updated (and only them). They were talking with ghosts.
Now we are trying to show spinners of ghosts */
elSetOffsetInChat(spinnerTop, highestPoint);
setElementVisibility(spinnerTop, isMissingTopMsgHeap());
elSetOffsetInChat(spinnerBottom, lowestPoint - SbH);
setElementVisibility(spinnerBottom, isMissingBottomMsgHeap());
} }
} }
function shouldShowDeleteMesgBtn(messageSt){ function shouldShowDeleteMesgBtn(messageSt){
return !messageSt.isSystem && messageSt.exists && ( return !messageSt.isSystem && messageSt.exists && (messageSt.myRoleHere !== userChatRoleReadOnly) &&(
messageSt.myRoleHere === userChatRoleAdmin || messageSt.senderUserId === userinfo.uid); messageSt.myRoleHere === userChatRoleAdmin || messageSt.senderUserId === userinfo.uid);
} }
// todo: fix messageboxes
function getMsgTypeClassSenderBased(messageSt){ function getMsgTypeClassSenderBased(messageSt){
if (messageSt.isSystem) if (messageSt.isSystem)
return "message-box-system" return "message-box-system"
@ -106,12 +149,13 @@ function getMsgFullTypeClassName(messageSt){
return getMsgTypeClassSenderBased(messageSt) + (messageSt.exists ? "" : " message-box-deleted"); return getMsgTypeClassSenderBased(messageSt) + (messageSt.exists ? "" : " message-box-deleted");
} }
/* Two things can be updated: messages existance and delete button visibility */ /* Two things can be updated: messages existance and delete button visibility
function updateMessageBox(id, box, messageSt){ * Supercontainer.container is persistent, Supercontainer.box can change it's class */
box.querySelector(".message-box-button-delete").style.display = shouldShowDeleteMesgBtn(messageSt) ? "block" : "none"; function updateMessageSupercontainer(supercontainer, messageSt){
let box = supercontainer.box;
setElementVisibility(box.querySelector(".message-box-button-delete"), shouldShowDeleteMesgBtn(messageSt), "inline");
box.className = getMsgFullTypeClassName(messageSt); box.className = getMsgFullTypeClassName(messageSt);
// Notice, that no check of previous state is performed. Double loading is a rare event, // Notice, that no check of previous state is performed. Double loading is a rare event, I can afford to be slow
// and I can afford to be slow
if (!messageSt.exists) if (!messageSt.exists)
box.querySelector(".message-box-msg").innerText = msgErased; box.querySelector(".message-box-msg").innerText = msgErased;
} }
@ -136,8 +180,12 @@ function decodeSystemMessage(text){
return "... Bad log ..."; return "... Bad log ...";
} }
function convertMessageStToBox(messageSt){ function convertMessageStToSupercontainer(messageSt){
let container = document.createElement("div");
container.className = "message-supercontainer";
let box = document.createElement("div"); let box = document.createElement("div");
container.appendChild(box);
box.className = getMsgFullTypeClassName(messageSt); box.className = getMsgFullTypeClassName(messageSt);
let ID = messageSt.id; let ID = messageSt.id;
@ -178,6 +226,7 @@ function convertMessageStToBox(messageSt){
storeHiddenMsgIdForDeletionWin = ID; storeHiddenMsgIdForDeletionWin = ID;
activatePopupWindowById("msg-deletion-win"); activatePopupWindowById("msg-deletion-win");
}; };
setElementVisibility(inTopPartButtonDelete, shouldShowDeleteMesgBtn(messageSt), "inline");
let inTopPartButtonGetLink = document.createElement("img"); let inTopPartButtonGetLink = document.createElement("img");
topPart.appendChild(inTopPartButtonGetLink); topPart.appendChild(inTopPartButtonGetLink);
@ -202,14 +251,15 @@ function convertMessageStToBox(messageSt){
msgPart.innerText = messageSt.text; msgPart.innerText = messageSt.text;
} else } else
msgPart.innerText = msgErased; msgPart.innerText = msgErased;
return box;
return {'container': container, 'box': box, 'offset': 0};
} }
function makeVisible(msgId){ function makeVisible(msgId){
let box = convertMessageStToBox(loadedMessages.get(msgId)); let supercontainer = convertMessageStToSupercontainer(loadedMessages.get(msgId));
const chatWin = document.getElementById("chat-widget"); const chatWin = document.getElementById("chat-widget");
chatWin.appendChild(box); chatWin.appendChild(supercontainer.container);
visibleMessages.set(msgId, box); visibleMessages.set(msgId, supercontainer);
} }
function opaNewMessageSt(messageSt){ function opaNewMessageSt(messageSt){
@ -217,7 +267,7 @@ function opaNewMessageSt(messageSt){
if (loadedMessages.has(msgId)){ if (loadedMessages.has(msgId)){
loadedMessages.set(msgId, messageSt); loadedMessages.set(msgId, messageSt);
if (visibleMessages.has(msgId)){ if (visibleMessages.has(msgId)){
updateMessageBox(msgId, visibleMessages.get(msgId), messageSt); updateMessageSupercontainer(visibleMessages.get(msgId), messageSt);
} }
} else { } else {
loadedMessages.set(msgId, messageSt); loadedMessages.set(msgId, messageSt);
@ -249,15 +299,16 @@ function canISendMessages(){
} }
function updateLocalStateFromChatUpdRespBlind(chatUpdResp){ function updateLocalStateFromChatUpdRespBlind(chatUpdResp){
console.log(anchoredMsg, offsetOfAnchor, chatUpdResp);
LocalHistoryId = chatUpdResp.HistoryId; LocalHistoryId = chatUpdResp.HistoryId;
for (let memberSt of chatUpdResp.members){ for (let memberSt of chatUpdResp.members){
let id = memberSt.userId; let id = memberSt.userId;
if (id === userinfo.uid && myRoleHere !== memberSt.roleHere) { if (id === userinfo.uid && myRoleHere !== memberSt.roleHere) {
myRoleHere = memberSt.roleHere; myRoleHere = memberSt.roleHere;
for (let [msgId, box] of visibleMessages){ for (let [msgId, sc] of visibleMessages){
updateMessageBox(msgId, loadedMessages.get(msgId), box); updateMessageSupercontainer(sc, loadedMessages.get(msgId));
} }
document.getElementById("message-input").style.display = (canISendMessages() ? "block" : "none"); setElementVisibility(document.getElementById("message-input"), canISendMessages());
} }
} }
for (let memberSt of chatUpdResp.members){ for (let memberSt of chatUpdResp.members){
@ -284,31 +335,16 @@ async function requestMessageNeighbours(fromMsg, direction){
} }
function needToLoadWhitespace(){ function needToLoadWhitespace(){
let winTop = document.getElementById("chat-widget").offsetHeight; return isMissingPrimaryMsgHeap() || isMissingTopMsgHeap() || isMissingBottomMsgHeap();
if (anchoredMsg === -1){
if (lastMsgId >= 0)
console.log("NEEDED 1", anchoredMsg, lastMsgId);
return lastMsgId >= 0;
} else if (highestPoint < winTop + softZoneSz && visibleMsgSegStart > 0){
console.log("NEEDED 2", visibleMsgSegStart);
return true;
} else if (lowestPoint > 0 - softZoneSz && visibleMsgSegEnd < lastMsgId){
console.log("NEEDED 3");
return true;
}
return false;
} }
async function tryLoadWhitespace(){ async function tryLoadWhitespaceSingle(){
let winTop = document.getElementById("chat-widget").offsetHeight; console.log('tryLoadWhitespaceSingle');
console.log(anchoredMsg, lastMsgId); if (isMissingPrimaryMsgHeap()){
if (anchoredMsg === -1){ await requestMessageNeighbours(-1, "backward");
if (lastMsgId !== -1){ } else if (isMissingTopMsgHeap()){
await requestMessageNeighbours(-1, "backward");
}
} else if (highestPoint < winTop + softZoneSz && visibleMsgSegStart > 0){
await requestMessageNeighbours(visibleMsgSegStart, "backward"); await requestMessageNeighbours(visibleMsgSegStart, "backward");
} else if (lowestPoint > 0 - softZoneSz && visibleMsgSegEnd < lastMsgId){ } else if (isMissingBottomMsgHeap()){
await requestMessageNeighbours(visibleMsgSegEnd, "forward"); await requestMessageNeighbours(visibleMsgSegEnd, "forward");
} }
} }
@ -317,10 +353,8 @@ async function loadWhitespaceMultitry(){
if (needToLoadWhitespace()){ if (needToLoadWhitespace()){
cancelMainloopTimeout(); cancelMainloopTimeout();
do { do {
console.trace();
console.log("Normalnie ludi ne spyat");
try { try {
await tryLoadWhitespace(); await tryLoadWhitespaceSingle();
await sleep(100); await sleep(100);
} catch (e) { } catch (e) {
console.error(e); console.error(e);
@ -368,7 +402,7 @@ async function UPDATE(){
let Recv = await apiRequest("chatPollEvents", genSentBase()); let Recv = await apiRequest("chatPollEvents", genSentBase());
await updateLocalStateFromRecv(Recv); await updateLocalStateFromRecv(Recv);
} }
// __guestMainloopPollerAction = UPDATE(); __guestMainloopPollerAction = UPDATE;
window.onload = function (){ window.onload = function (){
console.log("Page was loaded"); console.log("Page was loaded");
@ -397,8 +431,10 @@ window.onload = function (){
let chatWg = document.getElementById("chat-widget"); let chatWg = document.getElementById("chat-widget");
let chatWgDebugLinesFnc = function (){ let chatWgDebugLinesFnc = function (){
let H = chatWg.offsetHeight; let H = chatWg.offsetHeight;
document.getElementById("debug-line-lowest").style.bottom = String(-softZoneSz) + "px"; elSetOffsetInChat(document.getElementById("debug-line-lowest"), -softZoneSz);
document.getElementById("debug-line-highest").style.bottom = String(H + softZoneSz) + "px"; elSetOffsetInChat(document.getElementById("debug-line-highest"), H + softZoneSz);
elSetOffsetInChat(document.getElementById("debug-line-top-padding"), H - chatPadding);
elSetOffsetInChat(document.getElementById("debug-line-bottom-padding"), chatPadding)
}; };
window.addEventListener("resize", chatWgDebugLinesFnc); window.addEventListener("resize", chatWgDebugLinesFnc);
chatWgDebugLinesFnc(); chatWgDebugLinesFnc();

View File

@ -59,3 +59,15 @@ function roleToColor(role) {
// todo: replace it with translation // todo: replace it with translation
const msgErased = "[ ERASED ]"; const msgErased = "[ ERASED ]";
function hideHTMLElement(el){
el.style.display = "none";
}
function showHTMLElement(el){
el.style.display = "block";
}
function setElementVisibility(el, isVisible, howVisible = "block"){
el.style.display = isVisible ? howVisible : "none";
}

View File

@ -41,6 +41,7 @@ namespace iu9cawebchat {
samI.update({ samI.update({
een9::StaticAssetManagerRule{assets_dir + "/css", "/assets/css", {{".css", "text/css"}} }, een9::StaticAssetManagerRule{assets_dir + "/css", "/assets/css", {{".css", "text/css"}} },
een9::StaticAssetManagerRule{assets_dir + "/js", "/assets/js", {{".js", "text/javascript"}} }, een9::StaticAssetManagerRule{assets_dir + "/js", "/assets/js", {{".js", "text/javascript"}} },
een9::StaticAssetManagerRule{assets_dir + "/gif", "/assets/gif", {{".gif", "image/gif"}} },
een9::StaticAssetManagerRule{assets_dir + "/img", "/assets/img", { een9::StaticAssetManagerRule{assets_dir + "/img", "/assets/img", {
{".jpg", "image/jpg"}, {".png", "image/png"}, {".svg", "image/svg+xml"} {".jpg", "image/jpg"}, {".png", "image/png"}, {".svg", "image/svg+xml"}
} }, } },