Update performance.
This commit is contained in:
		
							parent
							
								
									0bf714061c
								
							
						
					
					
						commit
						6a74263606
					
				| @ -20,189 +20,239 @@ | |||||||
| <script type="module"> | <script type="module"> | ||||||
|     import { app } from "/app.js"; |     import { app } from "/app.js"; | ||||||
| import { Schedule } from "/schedule.js"; | import { Schedule } from "/schedule.js"; | ||||||
|     const channelUid = "{{ channel.uid.value }}"; |  | ||||||
|     const chatInputField = document.querySelector("chat-input"); |  | ||||||
|     chatInputField.autoCompletions = { |  | ||||||
|         "/online": () =>{ |  | ||||||
|             showOnline(); |  | ||||||
|         }, |  | ||||||
|         "/clear": () => { |  | ||||||
|             document.querySelector(".chat-messages").innerHTML = ''; |  | ||||||
|         }, |  | ||||||
|         "/live": () =>{ |  | ||||||
| 
 | 
 | ||||||
|             chatInputField.liveType = !chatInputField.liveType | // --- Cache selectors --- | ||||||
|         }, | const chatInputField = document.querySelector("chat-input"); | ||||||
|         "/help": () => { | const messagesContainer = document.querySelector(".chat-messages"); | ||||||
|             showHelp(); | const chatArea = document.querySelector(".chat-area"); | ||||||
|  | const channelUid = "{{ channel.uid.value }}"; | ||||||
|  | const username = "{{ user.username.value }}"; | ||||||
|  | 
 | ||||||
|  | // --- Command completions --- | ||||||
|  | chatInputField.autoCompletions = { | ||||||
|  |     "/online": showOnline, | ||||||
|  |     "/clear": () => { messagesContainer.innerHTML = ''; }, | ||||||
|  |     "/live": () => { chatInputField.liveType = !chatInputField.liveType; }, | ||||||
|  |     "/help": showHelp | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // --- Throttle utility --- | ||||||
|  | function throttle(fn, wait) { | ||||||
|  |     let last = 0; | ||||||
|  |     return function(...args) { | ||||||
|  |         const now = Date.now(); | ||||||
|  |         if (now - last >= wait) { | ||||||
|  |             last = now; | ||||||
|  |             fn.apply(this, args); | ||||||
|         } |         } | ||||||
|  |     }; | ||||||
| } | } | ||||||
|      app.ws.addEventListener("refresh", (data) => { | 
 | ||||||
|          app.starField.showNotify(data.message); | // --- Scroll: load extra messages, throttled --- | ||||||
|          setTimeout(() => { | let isLoadingExtra = false; | ||||||
|              window.location.reload(); | async function loadExtra() { | ||||||
|          },4000) |     const firstMessage = messagesContainer.querySelector(".message:first-child"); | ||||||
|      }) |     if (isLoadingExtra || !isScrolledPastHalf() || !firstMessage) return; | ||||||
|     app.ws.addEventListener("deployed", (data) => { |     isLoadingExtra = true; | ||||||
|          app.starField.renderWord("Deployed",{"rainbow":true,"resolution":8}); |     const messages = await app.rpc.getMessages(channelUid, 0, firstMessage.dataset.created_at); | ||||||
|          setTimeout(() => { |     if (messages.length) { | ||||||
|              app.starField.shuffleAll(5000); |         const frag = document.createDocumentFragment(); | ||||||
|          },10000) |         messages.forEach(msg => { | ||||||
|      })     |             const temp = document.createElement("div"); | ||||||
|      app.ws.addEventListener("starfield.render_word", (data) => { |             temp.innerHTML = msg.html; | ||||||
|          app.starField.renderWord(data.word,data); |             frag.appendChild(temp.firstChild); | ||||||
|      })     |         }); | ||||||
|         const textBox = document.querySelector("chat-input").textarea |         firstMessage.parentNode.insertBefore(frag, firstMessage); | ||||||
|  |         updateLayout(false); | ||||||
|  |     } | ||||||
|  |     isLoadingExtra = false; | ||||||
|  | } | ||||||
|  | messagesContainer.addEventListener("scroll", throttle(loadExtra, 200)); | ||||||
|  | 
 | ||||||
|  | // --- Only update visible times --- | ||||||
|  | function updateTimes() { | ||||||
|  |     const containers = messagesContainer.querySelectorAll(".time"); | ||||||
|  |     const viewportHeight = window.innerHeight || document.documentElement.clientHeight; | ||||||
|  |     containers.forEach(container => { | ||||||
|  |         const rect = container.getBoundingClientRect(); | ||||||
|  |         if (rect.top >= 0 && rect.bottom <= viewportHeight) { | ||||||
|  |             const messageDiv = container.closest('.message'); | ||||||
|  |             const text = messageDiv.querySelector(".text").innerText; | ||||||
|  |             const time = document.createElement("span"); | ||||||
|  |             time.innerText = app.timeDescription(container.dataset.created_at); | ||||||
|  |             container.replaceChildren(time); | ||||||
|  |             const reply = document.createElement("a"); | ||||||
|  |             reply.innerText = " reply"; | ||||||
|  |             reply.href = "#reply"; | ||||||
|  |             container.appendChild(reply); | ||||||
|  |             reply.addEventListener('click', e => { | ||||||
|  |                 e.preventDefault(); | ||||||
|  |                 replyMessage(text); | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  | } | ||||||
|  | setInterval(() => requestIdleCallback(updateTimes), 30000); | ||||||
|  | 
 | ||||||
|  | // --- Paste & drag/drop uploads --- | ||||||
|  | const textBox = chatInputField.textarea; | ||||||
| textBox.addEventListener("paste", async (e) => { | textBox.addEventListener("paste", async (e) => { | ||||||
|     try { |     try { | ||||||
|         const clipboardItems = await navigator.clipboard.read(); |         const clipboardItems = await navigator.clipboard.read(); | ||||||
| 
 |  | ||||||
|                 // DataTransfer needs to be used to modify the files list of the input |  | ||||||
|         const dt = new DataTransfer(); |         const dt = new DataTransfer(); | ||||||
| 
 |         for (const item of clipboardItems) { | ||||||
|                 for (const clipboardItem of clipboardItems) { |             for (const type of item.types.filter(t => !t.startsWith('text/'))) { | ||||||
|                     const fileTypes = clipboardItem.types.filter(type => !type.startsWith('text/')) |                 const blob = await item.getType(type); | ||||||
|                     for (const fileType of fileTypes) { |                 dt.items.add(new File([blob], "image.png", { type })); | ||||||
| 
 |  | ||||||
|                         const blob = await clipboardItem.getType(fileType); |  | ||||||
|                         // Do something with the image blob. |  | ||||||
|                         dt.items.add(new File([blob], "image.png", { type: fileType })); |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 |  | ||||||
|         if (dt.items.length > 0) { |         if (dt.items.length > 0) { | ||||||
|                     const uploadButton = chatInputField.uploadButton |             const uploadButton = chatInputField.uploadButton; | ||||||
|                     const input = uploadButton.shadowRoot.querySelector('.file-input') |             const input = uploadButton.shadowRoot.querySelector('.file-input'); | ||||||
|             input.files = dt.files; |             input.files = dt.files; | ||||||
| 
 |  | ||||||
|             await uploadButton.uploadFiles(); |             await uploadButton.uploadFiles(); | ||||||
|         } |         } | ||||||
|     } catch (error) { |     } catch (error) { | ||||||
|         console.error("Failed to read clipboard contents: ", error); |         console.error("Failed to read clipboard contents: ", error); | ||||||
|     } |     } | ||||||
| }); | }); | ||||||
| 
 | chatArea.addEventListener("drop", async (e) => { | ||||||
| 			 |  | ||||||
| 
 |  | ||||||
|         const chatInput = document.querySelector(".chat-area") |  | ||||||
|         chatInput.addEventListener("drop", async (e) => { |  | ||||||
|     e.preventDefault(); |     e.preventDefault(); | ||||||
| 
 |  | ||||||
|     const dt = e.dataTransfer; |     const dt = e.dataTransfer; | ||||||
|     if (dt.items.length > 0) { |     if (dt.items.length > 0) { | ||||||
|                 const uploadButton = chatInputField.uploadButton |         const uploadButton = chatInputField.uploadButton; | ||||||
|                 const input = uploadButton.shadowRoot.querySelector('.file-input') |         const input = uploadButton.shadowRoot.querySelector('.file-input'); | ||||||
|         input.files = dt.files; |         input.files = dt.files; | ||||||
| 
 |  | ||||||
|         await uploadButton.uploadFiles(); |         await uploadButton.uploadFiles(); | ||||||
|     } |     } | ||||||
|         }) | }); | ||||||
|         chatInput.addEventListener("dragover", async (e) => { | chatArea.addEventListener("dragover", e => { | ||||||
|     e.preventDefault(); |     e.preventDefault(); | ||||||
|     e.dataTransfer.dropEffect = "link"; |     e.dataTransfer.dropEffect = "link"; | ||||||
|  | }); | ||||||
| 
 | 
 | ||||||
| 
 | // --- Focus input on load --- | ||||||
|         }) |  | ||||||
| 
 |  | ||||||
| chatInputField.textarea.focus(); | chatInputField.textarea.focus(); | ||||||
| 
 | 
 | ||||||
|      | // --- Reply helper --- | ||||||
| 
 |  | ||||||
| function replyMessage(message) { | function replyMessage(message) { | ||||||
|         const field = chatInputField |     chatInputField.value = "markdown\n> " + (message || '') + "\n"; | ||||||
|         field.value = "```markdown\n> " + (message || '') + "\n```\n"; |     chatInputField.focus(); | ||||||
|         field.focus(); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|     function updateTimes() { | // --- Mention helpers --- | ||||||
|         document.querySelectorAll(".time").forEach((container) => { | function extractMentions(message) { | ||||||
|             const messageDiv = container.closest('.message'); |     return [...new Set(message.match(/@\w+/g) || [])]; | ||||||
|             const userNick = messageDiv.dataset.user_nick; | } | ||||||
|             const text = messageDiv.querySelector(".text").innerText; | function isMentionToMe(message) { | ||||||
|             const time = document.createElement("span"); |     return message.toLowerCase().includes('@' + username.toLowerCase()); | ||||||
|             time.innerText = app.timeDescription(container.dataset.created_at); | } | ||||||
|  | function isMentionForSomeoneElse(message) { | ||||||
|  |     const mentions = extractMentions(message); | ||||||
|  |     return mentions.length > 0 && !mentions.includes('@' + username); | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
|             container.replaceChildren(time); | // --- WebSocket events --- | ||||||
|             const reply = document.createElement("a"); | app.ws.addEventListener("refresh", (data) => { | ||||||
|             reply.innerText = " reply"; |     app.starField.showNotify(data.message); | ||||||
|             reply.href = "#reply"; |     setTimeout(() => window.location.reload(), 4000); | ||||||
|             container.appendChild(reply); |  | ||||||
|             reply.addEventListener('click', (e) => { |  | ||||||
|                 e.preventDefault(); |  | ||||||
|                 replyMessage(text); |  | ||||||
|             }) |  | ||||||
| }); | }); | ||||||
|     } | app.ws.addEventListener("deployed", (data) => { | ||||||
| 
 |     app.starField.renderWord("Deployed", { rainbow: true, resolution: 8 }); | ||||||
|     function isElementVisible(element) { |     setTimeout(() => app.starField.shuffleAll(5000), 10000); | ||||||
|         const rect = element.getBoundingClientRect(); | }); | ||||||
|         return ( | app.ws.addEventListener("starfield.render_word", (data) => { | ||||||
|             rect.top >= 0 && |     app.starField.renderWord(data.word, data); | ||||||
|             rect.left >= 0 && |  | ||||||
|             rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && |  | ||||||
|             rect.right <= (window.innerWidth || document.documentElement.clientWidth) |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     const messagesContainer = document.querySelector(".chat-messages"); |  | ||||||
| 
 |  | ||||||
|     function isScrolledPastHalf() { |  | ||||||
|         let scrollTop = messagesContainer.scrollTop; |  | ||||||
|         let scrollableHeight = messagesContainer.scrollHeight - messagesContainer.clientHeight; |  | ||||||
| 
 |  | ||||||
|         if (scrollTop < scrollableHeight / 2) { |  | ||||||
|             return true; |  | ||||||
|         } |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     let isLoadingExtra = false; |  | ||||||
| 
 |  | ||||||
|     async function loadExtra() { |  | ||||||
|         const firstMessage = messagesContainer.querySelector(".message:first-child"); |  | ||||||
|         if (isLoadingExtra) { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|         if (!isScrolledPastHalf()) { |  | ||||||
|            return; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         isLoadingExtra = true; |  | ||||||
| 
 |  | ||||||
|         const messages = await app.rpc.getMessages(channelUid, 0, firstMessage.dataset.created_at); |  | ||||||
| 
 |  | ||||||
|         messages.forEach((message) => { |  | ||||||
|             firstMessage.insertAdjacentHTML("beforebegin", message.html); |  | ||||||
|         }) |  | ||||||
|         updateLayout(false); |  | ||||||
| 
 |  | ||||||
|         isLoadingExtra = false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     messagesContainer.addEventListener("scroll", () => { |  | ||||||
|         loadExtra(); |  | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
|     let lastMessage | // --- Channel message event --- | ||||||
|  | app.addEventListener("channel-message", (data) => { | ||||||
|  |     let display = data.text && data.text.trim() ? 'block' : 'none'; | ||||||
|  |     if (data.channel_uid !== channelUid) { | ||||||
|  |         if (!isMentionForSomeoneElse(data.message)) { | ||||||
|  |             channelSidebar.notify(data); | ||||||
|  |             app.playSound("messageOtherChannel"); | ||||||
|  |         } | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     if (data.username !== username) { | ||||||
|  |         if (isMentionToMe(data.message)) { | ||||||
|  |             app.playSound("mention"); | ||||||
|  |         } else if (!isMentionForSomeoneElse(data.message)) { | ||||||
|  |             app.playSound("message"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     const lastMessage = messagesContainer.querySelector(".message:last-child"); | ||||||
|  |     const lastElement = messagesContainer.querySelector(".message-list-bottom"); | ||||||
|  |     const doScrollDown = !lastMessage || isElementVisible(lastMessage); | ||||||
|  |     const message = document.createElement("div"); | ||||||
|  |     message.innerHTML = data.html; | ||||||
|  |     message.style.display = display; | ||||||
|  |     messagesContainer.insertBefore(message.firstChild, lastElement); | ||||||
|  |     updateLayout(doScrollDown); | ||||||
|  |     setTimeout(() => updateLayout(doScrollDown), 1000); | ||||||
|  |     app.rpc.markAsRead(channelUid); | ||||||
|  | }); | ||||||
| 
 | 
 | ||||||
|  | // --- Keyboard shortcuts --- | ||||||
|  | let escPressed = false, gPressCount = 0, keyTimeout; | ||||||
|  | document.addEventListener('keydown', function(event) { | ||||||
|  |     if (event.key === 'Escape') { | ||||||
|  |         escPressed = true; gPressCount = 0; | ||||||
|  |         clearTimeout(keyTimeout); | ||||||
|  |         keyTimeout = setTimeout(() => { escPressed = false; }, 300); | ||||||
|  |     } | ||||||
|  |     if (event.key === 'G' && escPressed) { | ||||||
|  |         gPressCount++; | ||||||
|  |         clearTimeout(keyTimeout); | ||||||
|  |         keyTimeout = setTimeout(() => { gPressCount = 0; }, 300); | ||||||
|  |         if (gPressCount === 2) { | ||||||
|  |             gPressCount = 0; escPressed = false; | ||||||
|  |             messagesContainer.querySelector(".message:last-child")?.scrollIntoView({ block: "end", inline: "nearest" }); | ||||||
|  |             setTimeout(() => chatInputField.focus(), 500); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     if (event.shiftKey && event.key === 'G') { | ||||||
|  |         if (chatInputField.isActive()) { | ||||||
|  |             updateLayout(true); | ||||||
|  |             setTimeout(() => chatInputField.focus(), 500); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | // --- Image click-to-zoom (delegated) --- | ||||||
|  | messagesContainer.addEventListener('click', (e) => { | ||||||
|  |     if (e.target.tagName !== 'IMG' || e.target.classList.contains('avatar')) return; | ||||||
|  |     const img = e.target; | ||||||
|  |     const overlay = document.createElement('div'); | ||||||
|  |     overlay.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.9);display:flex;justify-content:center;align-items:center;z-index:9999;' | ||||||
|  |     const fullImg = document.createElement('img'); | ||||||
|  |     const urlObj = new URL(img.src); urlObj.search = ''; | ||||||
|  |     fullImg.src = urlObj.toString(); | ||||||
|  |     fullImg.alt = img.alt; | ||||||
|  |     fullImg.style.maxWidth = '90%'; | ||||||
|  |     fullImg.style.maxHeight = '90%'; | ||||||
|  |     overlay.appendChild(fullImg); | ||||||
|  |     document.body.appendChild(overlay); | ||||||
|  |     overlay.addEventListener('click', () => document.body.removeChild(overlay)); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | // --- Layout update --- | ||||||
|  | let lastMessage; | ||||||
| function updateLayout(doScrollDown) { | function updateLayout(doScrollDown) { | ||||||
|         const messagesContainer = document.querySelector(".chat-messages"); |  | ||||||
|     updateTimes(); |     updateTimes(); | ||||||
|         let previousUser = null; |     let previousUser = null, previousDate = null; | ||||||
|         let previousDate = null; |     messagesContainer.querySelectorAll(".message").forEach((message) => { | ||||||
|         document.querySelectorAll(".message").forEach((message) => { |  | ||||||
|         if (previousUser !== message.dataset.user_uid) { |         if (previousUser !== message.dataset.user_uid) { | ||||||
|             message.classList.add("switch-user"); |             message.classList.add("switch-user"); | ||||||
|             previousUser = message.dataset.user_uid; |             previousUser = message.dataset.user_uid; | ||||||
|             previousDate = new Date(message.dataset.created_at); |             previousDate = new Date(message.dataset.created_at); | ||||||
|         } else { |         } else { | ||||||
|             message.classList.remove("switch-user"); |             message.classList.remove("switch-user"); | ||||||
| 
 |  | ||||||
|             if (!previousDate) { |             if (!previousDate) { | ||||||
|                 previousDate = new Date(message.dataset.created_at); |                 previousDate = new Date(message.dataset.created_at); | ||||||
|             } else { |             } else { | ||||||
|                 const currentDate = new Date(message.dataset.created_at); |                 const currentDate = new Date(message.dataset.created_at); | ||||||
| 
 |  | ||||||
|                 if (currentDate.getTime() - previousDate.getTime() > 1000 * 60 * 20) { |                 if (currentDate.getTime() - previousDate.getTime() > 1000 * 60 * 20) { | ||||||
|                     message.classList.add("long-time"); |                     message.classList.add("long-time"); | ||||||
|                 } else { |                 } else { | ||||||
| @ -213,146 +263,31 @@ | |||||||
|         } |         } | ||||||
|     }); |     }); | ||||||
|     lastMessage = messagesContainer.querySelector(".message:last-child"); |     lastMessage = messagesContainer.querySelector(".message:last-child"); | ||||||
|         if (doScrollDown) { |     if (doScrollDown) messagesContainer.scrollToBottom?.(); | ||||||
|             messagesContainer.scrollToBottom() |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|     setInterval(updateTimes, 30000); | // --- Utility: check if element is visible --- | ||||||
| 
 | function isElementVisible(element) { | ||||||
|     function isMentionToMe(message){ |     const rect = element.getBoundingClientRect(); | ||||||
|         const mentionText = '@{{ user.username.value }}'; |     return ( | ||||||
|         return message.toLowerCase().includes(mentionText); |         rect.top >= 0 && | ||||||
|     } |         rect.left >= 0 && | ||||||
|     function extractMentions(message) { |         rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && | ||||||
|         return [...new Set(message.match(/@\w+/g) || [])]; |         rect.right <= (window.innerWidth || document.documentElement.clientWidth) | ||||||
|     } |     ); | ||||||
|     function isMentionForSomeoneElse(message){ |  | ||||||
|         const mentions = extractMentions(message); |  | ||||||
|         const mentionText = '@{{ user.username.value }}'; |  | ||||||
|         return mentions.length > 0 && mentions.indexOf(mentionText) == -1; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|     app.addEventListener("channel-message", (data) => { | // --- Utility: check if scrolled past half --- | ||||||
|         let display = 'block'; | function isScrolledPastHalf() { | ||||||
|         if(!data.text || !data.text.trim()){ |     let scrollTop = messagesContainer.scrollTop; | ||||||
|             display = "none"; |     let scrollableHeight = messagesContainer.scrollHeight - messagesContainer.clientHeight; | ||||||
|         } |     return scrollTop < scrollableHeight / 2; | ||||||
|         if (data.channel_uid !== channelUid) { |  | ||||||
|             if(!isMentionForSomeoneElse(data.message)){ |  | ||||||
|                 channelSidebar.notify(data); |  | ||||||
|                 app.playSound("messageOtherChannel"); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|             return; | // --- Initial layout update --- | ||||||
|         } |  | ||||||
|         if (data.username !== "{{ user.username.value }}") { |  | ||||||
|             if(isMentionToMe(data.message)){ |  | ||||||
|                 app.playSound("mention"); |  | ||||||
|             }else if (!isMentionForSomeoneElse(data.message)){ |  | ||||||
|                 app.playSound("message"); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         const messagesContainer = document.querySelector(".chat-messages"); |  | ||||||
|         lastMessage = messagesContainer.querySelector(".message:last-child"); |  | ||||||
|         const lastElement = messagesContainer.querySelector(".message-list-bottom"); |  | ||||||
|         const doScrollDownBecauseLastMessageIsVisible = !lastMessage || isElementVisible(lastMessage); |  | ||||||
| 
 |  | ||||||
|         const message = document.createElement("div"); |  | ||||||
|         message.innerHTML = data.html; |  | ||||||
|         message.style.display = display |  | ||||||
|         document.querySelector(".chat-messages").insertBefore(message.firstChild,lastElement); |  | ||||||
|         updateLayout(doScrollDownBecauseLastMessageIsVisible); |  | ||||||
|         setTimeout(() => { |  | ||||||
|             updateLayout(doScrollDownBecauseLastMessageIsVisible) |  | ||||||
|         }, 1000); |  | ||||||
|         app.rpc.markAsRead(channelUid); |  | ||||||
|     }); |  | ||||||
| 
 |  | ||||||
|     let escPressed = false; |  | ||||||
|     let gPressCount = 0; |  | ||||||
|     let keyTimeout; |  | ||||||
|     document.addEventListener('keydown', function(event) { |  | ||||||
|      |  | ||||||
|        if (event.key === 'Escape') { |  | ||||||
|             escPressed = true; |  | ||||||
|             gPressCount = 0;  |  | ||||||
|             clearTimeout(keyTimeout); |  | ||||||
|             keyTimeout = setTimeout(() => { |  | ||||||
|                 escPressed = false;  |  | ||||||
|             }, 300);  |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (event.key === 'G' && escPressed) { |  | ||||||
|             gPressCount++; |  | ||||||
| 
 |  | ||||||
|             clearTimeout(keyTimeout); |  | ||||||
|             keyTimeout = setTimeout(() => { |  | ||||||
|                 gPressCount = 0; |  | ||||||
|             }, 300);  |  | ||||||
|             if (gPressCount === 2) { |  | ||||||
|                 gPressCount = 0;  |  | ||||||
|                 escPressed = false;  |  | ||||||
| 
 |  | ||||||
|             messagesContainer.querySelector(".message:last-child").scrollIntoView({ block: "end", inline: "nearest" }); |  | ||||||
|                 setTimeout(() => { |  | ||||||
|                      |  | ||||||
|                     chatInputField.focus(); |  | ||||||
|                 },500) |  | ||||||
| 
 |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         if (event.shiftKey && event.key === 'G') { |  | ||||||
|             if(chatInputField.isActive()){ |  | ||||||
|              |  | ||||||
| updateLayout(true); | updateLayout(true); | ||||||
|                 setTimeout(() => { |  | ||||||
|                     chatInputField.focus(); |  | ||||||
|                 },500) |  | ||||||
|             } |  | ||||||
| 
 | 
 | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| 
 | 
 | ||||||
|     messagesContainer.addEventListener('click', (e) => { |  | ||||||
|        if(e.target.tagName != 'IMG') |  | ||||||
|            return |  | ||||||
|       const img = e.target |  | ||||||
|       if(e.target.classList.contains('avatar')){ |  | ||||||
|           return |  | ||||||
|       } |  | ||||||
|       const overlay = document.createElement('div'); |  | ||||||
|       overlay.style.position = 'fixed'; |  | ||||||
|       overlay.style.top = 0; |  | ||||||
|       overlay.style.left = 0; |  | ||||||
|       overlay.style.width = '100%'; |  | ||||||
|       overlay.style.height = '100%'; |  | ||||||
|       overlay.style.backgroundColor = 'rgba(0,0,0,0.9)'; |  | ||||||
|       overlay.style.display = 'flex'; |  | ||||||
|       overlay.style.justifyContent = 'center'; |  | ||||||
|       overlay.style.alignItems = 'center'; |  | ||||||
|       overlay.style.zIndex = 9999; |  | ||||||
| 
 | 
 | ||||||
|       const fullImg = document.createElement('img'); |  | ||||||
| 
 |  | ||||||
|       const urlObj = new URL(img.src); |  | ||||||
|       urlObj.search = '' |  | ||||||
|         fullImg.src = urlObj.toString(); |  | ||||||
| 
 |  | ||||||
|       fullImg.alt = img.alt; |  | ||||||
|       fullImg.style.maxWidth = '90%'; |  | ||||||
|       fullImg.style.maxHeight = '90%'; |  | ||||||
| 
 |  | ||||||
|       overlay.appendChild(fullImg); |  | ||||||
| 
 |  | ||||||
|       document.body.appendChild(overlay); |  | ||||||
| 
 |  | ||||||
|       overlay.addEventListener('click', () => { |  | ||||||
|         document.body.removeChild(overlay); |  | ||||||
|       }); |  | ||||||
|     }); |  | ||||||
|     updateLayout(true); |  | ||||||
| </script> | </script> | ||||||
| {% endblock %} | {% endblock %} | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user