master #6
| @ -45,8 +45,16 @@ | ||||
|             </a> | ||||
|         </div> | ||||
|         <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-lowest"></div> | ||||
|             <div class="chat-debug-rect" id="debug-line-highest" style="background-color: #8600d3"></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 class="panel" id="input-panel"> | ||||
|             <div contentEditable id="message-input" class="panel-thing"></div> | ||||
|  | ||||
| @ -6,46 +6,66 @@ body, html { | ||||
|     position: relative; | ||||
|     flex: 1; | ||||
|     background-color: #f1f1f1; | ||||
|     color: black; | ||||
|     overflow: hidden; | ||||
| } | ||||
| 
 | ||||
| .message-box-mine { | ||||
| .message-supercontainer{ | ||||
|     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; | ||||
|     border: 2px solid #82a173; | ||||
|     padding: 5px; | ||||
|     background-color: #cdff9b; | ||||
|     color: black; | ||||
|     /*justify-self: flex-end;*/ | ||||
| } | ||||
| 
 | ||||
| .message-box-alien { | ||||
|     position: absolute; | ||||
|     left: 5px; | ||||
|     margin-left: 5px; | ||||
|     margin-right: auto; | ||||
|     max-width: 400px; | ||||
|     border: 2px solid dimgrey; | ||||
|     padding: 5px; | ||||
|     background-color: white; | ||||
|     color: black; | ||||
|     /*justify-self: flex-start;*/ | ||||
| } | ||||
| 
 | ||||
| /* 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 */ | ||||
| .message-box-deleted { | ||||
|     font-weight: bold; | ||||
|     border: 2px solid #cb0005; | ||||
|     background-color: #ffc1bc; | ||||
| } | ||||
| 
 | ||||
| .message-box-deleted .message-box-msg{ | ||||
|     font-weight: bold; | ||||
| } | ||||
| 
 | ||||
| .message-box-system { | ||||
|     position: absolute; | ||||
|     left: 15px; | ||||
|     margin-left: auto; | ||||
|     margin-right: auto; | ||||
|     max-width: 500px; | ||||
|     padding: 4px; | ||||
|     background-color: #2d2d2d; | ||||
|     color: white; | ||||
|     font-weight: bold; | ||||
|     justify-self: center; | ||||
| } | ||||
| 
 | ||||
| /* in #chat-widget .message-box */ | ||||
| @ -80,6 +100,7 @@ body, html { | ||||
|     width: 20px; | ||||
|     padding: 2px; | ||||
|     cursor: pointer; | ||||
|     display: inline; | ||||
| } | ||||
| 
 | ||||
| .message-box-msg{ | ||||
| @ -110,4 +131,12 @@ body, html { | ||||
|     margin-right: auto; | ||||
|     max-height: 20%; | ||||
|     word-wrap: break-word; | ||||
| } | ||||
| 
 | ||||
| .loading-spinner{ | ||||
|     margin-left: auto; | ||||
|     margin-right: auto; | ||||
|     background-color: rgba(0, 0, 0, 0); | ||||
|     width: 25px; | ||||
|     display: block; | ||||
| } | ||||
| @ -2,11 +2,7 @@ | ||||
|     width: 100%; | ||||
|     position: absolute; | ||||
|     left: 0; | ||||
|     background-color: rgba(255, 50, 50, 160); | ||||
|     height: 4px; | ||||
|     opacity: 0.3; | ||||
|     height: 3px; | ||||
|     z-index: 2; | ||||
| } | ||||
| 
 | ||||
| .chat-debug-rect-top{ | ||||
|     background-color: rgba(148, 0, 211, 160); | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								assets/gif/loading.gif
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/gif/loading.gif
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 136 KiB | 
| @ -3,6 +3,8 @@ let LocalHistoryId = 0; | ||||
| let members = new Map(); | ||||
| 
 | ||||
| let loadedMessages = new Map();  // messageSt objects
 | ||||
| /* | ||||
| container: EL, box: EL, offset: number (msgPres) */ | ||||
| let visibleMessages = new Map();  // HTMLElement objects
 | ||||
| 
 | ||||
| let anchoredMsg = -1; | ||||
| @ -41,15 +43,37 @@ function genSentBaseGMN(){ | ||||
|     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){ | ||||
|     visibleMessages.get(msgId).style.bottom = String(offset) + "px"; | ||||
|     visibleMessages.get(msgId).container.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; | ||||
|         let height = visibleMessages.get(curMsg).container.offsetHeight; | ||||
|         offset += height + 5; | ||||
|     } | ||||
|     return offset; | ||||
| @ -58,7 +82,7 @@ function updateOffsetsUpToTop(){ | ||||
| function updateOffsetsDown(){ | ||||
|     let offset = offsetOfAnchor; | ||||
|     for (let curMsg = anchoredMsg + 1; curMsg <= visibleMsgSegEnd; curMsg++){ | ||||
|         let height = visibleMessages.get(curMsg).offsetHeight; | ||||
|         let height = visibleMessages.get(curMsg).container.offsetHeight; | ||||
|         offset -= (height + 5); | ||||
|         updateOffsetOfVisibleMsg(curMsg, offset); | ||||
|     } | ||||
| @ -72,28 +96,47 @@ function updateOffsetsSane(){ | ||||
|     lowestPoint = updateOffsetsDown(); | ||||
| } | ||||
| 
 | ||||
| function heightOfPreloadGhost(){ | ||||
|     let [W, H] = getChatWgSz(); | ||||
|     return Math.min(H * 0.9, Math.max(H * 0.69, 30)); | ||||
| } | ||||
| 
 | ||||
| function updateOffsets(){ | ||||
|     if (anchoredMsg < 0) | ||||
|         return; | ||||
|     updateOffsetsSane() | ||||
|     let winTop = document.getElementById("chat-widget").offsetHeight; | ||||
|     if (lowestPoint > chatPadding || (highestPoint - lowestPoint) <= winTop){ | ||||
|         bumpedAtTheBottom = true; | ||||
|         anchoredMsg = visibleMsgSegEnd; | ||||
|         offsetOfAnchor = chatPadding; | ||||
|         updateOffsetsSane(); | ||||
|     } else if (highestPoint < winTop - chatPadding) { | ||||
|         console.log("Advancing by " + (winTop - chatPadding - highestPoint)) | ||||
|         offsetOfAnchor += (winTop - chatPadding - highestPoint); | ||||
|     let spinnerTop = document.getElementById("top-loading"); | ||||
|     let spinnerBottom = document.getElementById("bottom-loading"); | ||||
|     let SbH = spinnerBottom.offsetHeight; | ||||
|     if (anchoredMsg < 0){ | ||||
|         hideHTMLElement(spinnerBottom); | ||||
|         elSetOffsetInChat(spinnerTop, chatPadding); | ||||
|         setElementVisibility(spinnerTop, isMissingPrimaryMsgHeap()); | ||||
|     } else { | ||||
|         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){ | ||||
|     return !messageSt.isSystem && messageSt.exists && ( | ||||
|     return !messageSt.isSystem && messageSt.exists && (messageSt.myRoleHere !== userChatRoleReadOnly) &&( | ||||
|         messageSt.myRoleHere === userChatRoleAdmin || messageSt.senderUserId === userinfo.uid); | ||||
| } | ||||
| 
 | ||||
| // todo: fix messageboxes
 | ||||
| function getMsgTypeClassSenderBased(messageSt){ | ||||
|     if (messageSt.isSystem) | ||||
|         return "message-box-system" | ||||
| @ -106,12 +149,13 @@ function getMsgFullTypeClassName(messageSt){ | ||||
|     return getMsgTypeClassSenderBased(messageSt) + (messageSt.exists ? "" : " message-box-deleted"); | ||||
| } | ||||
| 
 | ||||
| /* Two things can be updated: messages existance and delete button visibility */ | ||||
| function updateMessageBox(id, box, messageSt){ | ||||
|     box.querySelector(".message-box-button-delete").style.display = shouldShowDeleteMesgBtn(messageSt) ? "block" : "none"; | ||||
| /* Two things can be updated: messages existance and delete button visibility | ||||
| * Supercontainer.container is persistent, Supercontainer.box can change it's class */ | ||||
| function updateMessageSupercontainer(supercontainer, messageSt){ | ||||
|     let box = supercontainer.box; | ||||
|     setElementVisibility(box.querySelector(".message-box-button-delete"), shouldShowDeleteMesgBtn(messageSt), "inline"); | ||||
|     box.className = getMsgFullTypeClassName(messageSt); | ||||
|     // Notice, that no check of previous state is performed. Double loading is a rare event,
 | ||||
|     // and I can afford to be slow
 | ||||
|     // Notice, that no check of previous state is performed. Double loading is a rare event, I can afford to be slow
 | ||||
|     if (!messageSt.exists) | ||||
|         box.querySelector(".message-box-msg").innerText = msgErased; | ||||
| } | ||||
| @ -136,8 +180,12 @@ function decodeSystemMessage(text){ | ||||
|     return "... Bad log ..."; | ||||
| } | ||||
| 
 | ||||
| function convertMessageStToBox(messageSt){ | ||||
| function convertMessageStToSupercontainer(messageSt){ | ||||
|     let container = document.createElement("div"); | ||||
|     container.className = "message-supercontainer"; | ||||
| 
 | ||||
|     let box = document.createElement("div"); | ||||
|     container.appendChild(box); | ||||
|     box.className = getMsgFullTypeClassName(messageSt); | ||||
| 
 | ||||
|     let ID = messageSt.id; | ||||
| @ -178,6 +226,7 @@ function convertMessageStToBox(messageSt){ | ||||
|         storeHiddenMsgIdForDeletionWin = ID; | ||||
|         activatePopupWindowById("msg-deletion-win"); | ||||
|     }; | ||||
|     setElementVisibility(inTopPartButtonDelete, shouldShowDeleteMesgBtn(messageSt), "inline"); | ||||
| 
 | ||||
|     let inTopPartButtonGetLink = document.createElement("img"); | ||||
|     topPart.appendChild(inTopPartButtonGetLink); | ||||
| @ -202,14 +251,15 @@ function convertMessageStToBox(messageSt){ | ||||
|             msgPart.innerText = messageSt.text; | ||||
|     } else | ||||
|         msgPart.innerText = msgErased; | ||||
|     return box; | ||||
| 
 | ||||
|     return {'container': container, 'box': box, 'offset': 0}; | ||||
| } | ||||
| 
 | ||||
| function makeVisible(msgId){ | ||||
|     let box = convertMessageStToBox(loadedMessages.get(msgId)); | ||||
|     let supercontainer = convertMessageStToSupercontainer(loadedMessages.get(msgId)); | ||||
|     const chatWin = document.getElementById("chat-widget"); | ||||
|     chatWin.appendChild(box); | ||||
|     visibleMessages.set(msgId, box); | ||||
|     chatWin.appendChild(supercontainer.container); | ||||
|     visibleMessages.set(msgId, supercontainer); | ||||
| } | ||||
| 
 | ||||
| function opaNewMessageSt(messageSt){ | ||||
| @ -217,7 +267,7 @@ function opaNewMessageSt(messageSt){ | ||||
|     if (loadedMessages.has(msgId)){ | ||||
|         loadedMessages.set(msgId, messageSt); | ||||
|         if (visibleMessages.has(msgId)){ | ||||
|             updateMessageBox(msgId, visibleMessages.get(msgId), messageSt); | ||||
|             updateMessageSupercontainer(visibleMessages.get(msgId), messageSt); | ||||
|         } | ||||
|     } else { | ||||
|         loadedMessages.set(msgId, messageSt); | ||||
| @ -249,15 +299,16 @@ function canISendMessages(){ | ||||
| } | ||||
| 
 | ||||
| function updateLocalStateFromChatUpdRespBlind(chatUpdResp){ | ||||
|     console.log(anchoredMsg, offsetOfAnchor, chatUpdResp); | ||||
|     LocalHistoryId = chatUpdResp.HistoryId; | ||||
|     for (let memberSt of chatUpdResp.members){ | ||||
|         let id = memberSt.userId; | ||||
|         if (id === userinfo.uid && myRoleHere !== memberSt.roleHere) { | ||||
|             myRoleHere = memberSt.roleHere; | ||||
|             for (let [msgId, box] of visibleMessages){ | ||||
|                 updateMessageBox(msgId, loadedMessages.get(msgId), box); | ||||
|             for (let [msgId, sc] of visibleMessages){ | ||||
|                 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){ | ||||
| @ -284,31 +335,16 @@ async function requestMessageNeighbours(fromMsg, direction){ | ||||
| } | ||||
| 
 | ||||
| function needToLoadWhitespace(){ | ||||
|     let winTop = document.getElementById("chat-widget").offsetHeight; | ||||
|     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; | ||||
|     return isMissingPrimaryMsgHeap() || isMissingTopMsgHeap() || isMissingBottomMsgHeap(); | ||||
| } | ||||
| 
 | ||||
| async function tryLoadWhitespace(){ | ||||
|     let winTop = document.getElementById("chat-widget").offsetHeight; | ||||
|     console.log(anchoredMsg, lastMsgId); | ||||
|     if (anchoredMsg === -1){ | ||||
|         if (lastMsgId !== -1){ | ||||
|             await requestMessageNeighbours(-1, "backward"); | ||||
|         } | ||||
|     } else if (highestPoint < winTop + softZoneSz && visibleMsgSegStart > 0){ | ||||
| async function tryLoadWhitespaceSingle(){ | ||||
|     console.log('tryLoadWhitespaceSingle'); | ||||
|     if (isMissingPrimaryMsgHeap()){ | ||||
|         await requestMessageNeighbours(-1, "backward"); | ||||
|     } else if (isMissingTopMsgHeap()){ | ||||
|         await requestMessageNeighbours(visibleMsgSegStart, "backward"); | ||||
|     } else if (lowestPoint > 0 - softZoneSz && visibleMsgSegEnd < lastMsgId){ | ||||
|     } else if (isMissingBottomMsgHeap()){ | ||||
|         await requestMessageNeighbours(visibleMsgSegEnd, "forward"); | ||||
|     } | ||||
| } | ||||
| @ -317,10 +353,8 @@ async function loadWhitespaceMultitry(){ | ||||
|     if (needToLoadWhitespace()){ | ||||
|         cancelMainloopTimeout(); | ||||
|         do { | ||||
|             console.trace(); | ||||
|             console.log("Normalnie ludi ne spyat"); | ||||
|             try { | ||||
|                 await tryLoadWhitespace(); | ||||
|                 await tryLoadWhitespaceSingle(); | ||||
|                 await sleep(100); | ||||
|             } catch (e) { | ||||
|                 console.error(e); | ||||
| @ -368,7 +402,7 @@ async function UPDATE(){ | ||||
|     let Recv = await apiRequest("chatPollEvents", genSentBase()); | ||||
|     await updateLocalStateFromRecv(Recv); | ||||
| } | ||||
| // __guestMainloopPollerAction = UPDATE();
 | ||||
| __guestMainloopPollerAction = UPDATE; | ||||
| 
 | ||||
| window.onload = function (){ | ||||
|     console.log("Page was loaded"); | ||||
| @ -397,8 +431,10 @@ window.onload = function (){ | ||||
|     let chatWg = document.getElementById("chat-widget"); | ||||
|     let chatWgDebugLinesFnc = function (){ | ||||
|         let H = chatWg.offsetHeight; | ||||
|         document.getElementById("debug-line-lowest").style.bottom = String(-softZoneSz) + "px"; | ||||
|         document.getElementById("debug-line-highest").style.bottom = String(H + softZoneSz) + "px"; | ||||
|         elSetOffsetInChat(document.getElementById("debug-line-lowest"), -softZoneSz); | ||||
|         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); | ||||
|     chatWgDebugLinesFnc(); | ||||
|  | ||||
| @ -59,3 +59,15 @@ function roleToColor(role) { | ||||
| 
 | ||||
| // todo: replace it with translation
 | ||||
| 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"; | ||||
| } | ||||
| @ -41,6 +41,7 @@ namespace iu9cawebchat { | ||||
|         samI.update({ | ||||
|             een9::StaticAssetManagerRule{assets_dir + "/css", "/assets/css", {{".css", "text/css"}} }, | ||||
|             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", { | ||||
|                 {".jpg", "image/jpg"}, {".png", "image/png"}, {".svg", "image/svg+xml"} | ||||
|             } }, | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user