From cb177691aa806467c6eaf3e3bda16844864b60d4 Mon Sep 17 00:00:00 2001 From: olcxja Date: Sun, 2 Aug 2026 21:19:00 +0200 Subject: [PATCH 1/4] improve topbar text and buttons culling --- android/app/src/main/assets/public/main.js | 74 ++++++++++++++++++-- android/app/src/main/assets/public/style.css | 17 ++--- webroot/main.js | 74 ++++++++++++++++++-- webroot/style.css | 17 ++--- 4 files changed, 148 insertions(+), 34 deletions(-) diff --git a/android/app/src/main/assets/public/main.js b/android/app/src/main/assets/public/main.js index 82922c97..11bd2ef9 100644 --- a/android/app/src/main/assets/public/main.js +++ b/android/app/src/main/assets/public/main.js @@ -1483,11 +1483,11 @@ async function switchRoomContent(title, content, showRoomBar, icon = "", skipMob let pinCallBtns = showRoomBar ? `${pinBtnHtml}${callBtnHtml}` : ''; let clickableContent = showRoomBar ? - `` : - ` - ${icon ? icon + '' : ''}${processBlah(title)} + ` + ${icon ? icon + '' : ''}${processBlah(title)} `; roomsTopBarTransition.innerHTML = `${backBtnHtml}${clickableContent}
${pinCallBtns}`; @@ -4695,3 +4695,69 @@ function scrollToPinnedMessage(e, msgId) { } scrollToMessage(msgId); } + +function checkTopbarSpace() { + if (typeof roomTopBar === 'undefined' || !roomTopBar) return; + + let transitionEl = roomTopBar.children.item(0); + if (!transitionEl) return; + + let children = Array.from(transitionEl.children); + let usedWidth = 0; + + let isPinHidden = document.body.classList.contains('hide-topbar-pin'); + let isCallHidden = document.body.classList.contains('hide-topbar-call'); + + for (let child of children) { + if (child.classList.contains('flex-spacer')) continue; + + if (child.classList.contains('room-action-pin') && isPinHidden) continue; + if (child.classList.contains('room-action-call') && isCallHidden) continue; + + let style = window.getComputedStyle(child); + let margins = (parseFloat(style.marginLeft) || 0) + (parseFloat(style.marginRight) || 0); + + if (child.id === 'room-topbar-clickable' || child.tagName.toLowerCase() === 'inherit') { + let titleEl = child.querySelector('inherit'); + if (titleEl) { + usedWidth += (child.offsetWidth - titleEl.offsetWidth) + titleEl.scrollWidth + margins; + } else { + usedWidth += child.offsetWidth + margins; + } + } else { + usedWidth += child.offsetWidth + margins; + } + } + + // Safety buffer for padding/rendering quirks + let diff = transitionEl.clientWidth - usedWidth - 5; + let iconWidth = 50; + + if (diff < 0) { + if (!isPinHidden) { + document.body.classList.add('hide-topbar-pin'); + } else if (!isCallHidden) { + document.body.classList.add('hide-topbar-call'); + } + } else { + if (isCallHidden && diff > iconWidth) { + document.body.classList.remove('hide-topbar-call'); + } else if (isPinHidden && !isCallHidden && diff > iconWidth) { + document.body.classList.remove('hide-topbar-pin'); + } + } +} + +if (typeof roomTopBar !== 'undefined' && roomTopBar) { + let topbarResizeObserver = new ResizeObserver(() => { + checkTopbarSpace(); + }); + topbarResizeObserver.observe(roomTopBar); + + if (roomTopBar.children.length > 0) { + let topbarMutationObserver = new MutationObserver(() => { + setTimeout(checkTopbarSpace, 10); + }); + topbarMutationObserver.observe(roomTopBar.children.item(0), { childList: true, subtree: true }); + } +} diff --git a/android/app/src/main/assets/public/style.css b/android/app/src/main/assets/public/style.css index 58e48f25..675a9781 100644 --- a/android/app/src/main/assets/public/style.css +++ b/android/app/src/main/assets/public/style.css @@ -216,11 +216,6 @@ sidebar { border-right: var(--border-width) solid var(--light-border-color); } -sidebar:not(.second) { - overflow-y: auto; - overflow-x: hidden; -} - sidebar:not(.second)::-webkit-scrollbar { display: none; } @@ -1038,12 +1033,8 @@ roomcontent roomtopbar .room-action-call { display: none; } -@media (max-width: 65rem) and (min-width: 55.001rem) { - roomcontent roomtopbar .room-action-pin { display: none !important; } - #roomdetailsbar roomtopbar .room-action-pin { display: flex !important; } -} +body.hide-topbar-pin roomcontent roomtopbar .room-action-pin { display: none !important; } +body.hide-topbar-pin #roomdetailsbar roomtopbar .room-action-pin { display: flex !important; } -@media (max-width: 58rem) and (min-width: 55.001rem) { - roomcontent roomtopbar .room-action-call { display: none !important; } - #roomdetailsbar roomtopbar .room-action-call { display: flex !important; } -} +body.hide-topbar-call roomcontent roomtopbar .room-action-call { display: none !important; } +body.hide-topbar-call #roomdetailsbar roomtopbar .room-action-call { display: flex !important; } diff --git a/webroot/main.js b/webroot/main.js index 82922c97..11bd2ef9 100644 --- a/webroot/main.js +++ b/webroot/main.js @@ -1483,11 +1483,11 @@ async function switchRoomContent(title, content, showRoomBar, icon = "", skipMob let pinCallBtns = showRoomBar ? `${pinBtnHtml}${callBtnHtml}` : ''; let clickableContent = showRoomBar ? - `` : - ` - ${icon ? icon + '' : ''}${processBlah(title)} + ` + ${icon ? icon + '' : ''}${processBlah(title)} `; roomsTopBarTransition.innerHTML = `${backBtnHtml}${clickableContent}
${pinCallBtns}`; @@ -4695,3 +4695,69 @@ function scrollToPinnedMessage(e, msgId) { } scrollToMessage(msgId); } + +function checkTopbarSpace() { + if (typeof roomTopBar === 'undefined' || !roomTopBar) return; + + let transitionEl = roomTopBar.children.item(0); + if (!transitionEl) return; + + let children = Array.from(transitionEl.children); + let usedWidth = 0; + + let isPinHidden = document.body.classList.contains('hide-topbar-pin'); + let isCallHidden = document.body.classList.contains('hide-topbar-call'); + + for (let child of children) { + if (child.classList.contains('flex-spacer')) continue; + + if (child.classList.contains('room-action-pin') && isPinHidden) continue; + if (child.classList.contains('room-action-call') && isCallHidden) continue; + + let style = window.getComputedStyle(child); + let margins = (parseFloat(style.marginLeft) || 0) + (parseFloat(style.marginRight) || 0); + + if (child.id === 'room-topbar-clickable' || child.tagName.toLowerCase() === 'inherit') { + let titleEl = child.querySelector('inherit'); + if (titleEl) { + usedWidth += (child.offsetWidth - titleEl.offsetWidth) + titleEl.scrollWidth + margins; + } else { + usedWidth += child.offsetWidth + margins; + } + } else { + usedWidth += child.offsetWidth + margins; + } + } + + // Safety buffer for padding/rendering quirks + let diff = transitionEl.clientWidth - usedWidth - 5; + let iconWidth = 50; + + if (diff < 0) { + if (!isPinHidden) { + document.body.classList.add('hide-topbar-pin'); + } else if (!isCallHidden) { + document.body.classList.add('hide-topbar-call'); + } + } else { + if (isCallHidden && diff > iconWidth) { + document.body.classList.remove('hide-topbar-call'); + } else if (isPinHidden && !isCallHidden && diff > iconWidth) { + document.body.classList.remove('hide-topbar-pin'); + } + } +} + +if (typeof roomTopBar !== 'undefined' && roomTopBar) { + let topbarResizeObserver = new ResizeObserver(() => { + checkTopbarSpace(); + }); + topbarResizeObserver.observe(roomTopBar); + + if (roomTopBar.children.length > 0) { + let topbarMutationObserver = new MutationObserver(() => { + setTimeout(checkTopbarSpace, 10); + }); + topbarMutationObserver.observe(roomTopBar.children.item(0), { childList: true, subtree: true }); + } +} diff --git a/webroot/style.css b/webroot/style.css index 58e48f25..675a9781 100644 --- a/webroot/style.css +++ b/webroot/style.css @@ -216,11 +216,6 @@ sidebar { border-right: var(--border-width) solid var(--light-border-color); } -sidebar:not(.second) { - overflow-y: auto; - overflow-x: hidden; -} - sidebar:not(.second)::-webkit-scrollbar { display: none; } @@ -1038,12 +1033,8 @@ roomcontent roomtopbar .room-action-call { display: none; } -@media (max-width: 65rem) and (min-width: 55.001rem) { - roomcontent roomtopbar .room-action-pin { display: none !important; } - #roomdetailsbar roomtopbar .room-action-pin { display: flex !important; } -} +body.hide-topbar-pin roomcontent roomtopbar .room-action-pin { display: none !important; } +body.hide-topbar-pin #roomdetailsbar roomtopbar .room-action-pin { display: flex !important; } -@media (max-width: 58rem) and (min-width: 55.001rem) { - roomcontent roomtopbar .room-action-call { display: none !important; } - #roomdetailsbar roomtopbar .room-action-call { display: flex !important; } -} +body.hide-topbar-call roomcontent roomtopbar .room-action-call { display: none !important; } +body.hide-topbar-call #roomdetailsbar roomtopbar .room-action-call { display: flex !important; } From ab99a84579d7f09c3a91b43cc5a8dfc109423b47 Mon Sep 17 00:00:00 2001 From: olcxja Date: Sun, 2 Aug 2026 21:38:01 +0200 Subject: [PATCH 2/4] fix group invite index --- android/app/src/main/assets/public/main.js | 2 +- webroot/main.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/android/app/src/main/assets/public/main.js b/android/app/src/main/assets/public/main.js index 11bd2ef9..a2ab99e2 100644 --- a/android/app/src/main/assets/public/main.js +++ b/android/app/src/main/assets/public/main.js @@ -2061,7 +2061,7 @@ async function renderInvites(res, type) { html += entry; } else if (invite.type === "group") { - let groupId = invite.id; + let groupId = invite.groupId || invite.id; let targetId = (type === "received") ? invite.sender : invite.receiver; if (!targetId.includes(":")) { diff --git a/webroot/main.js b/webroot/main.js index 11bd2ef9..a2ab99e2 100644 --- a/webroot/main.js +++ b/webroot/main.js @@ -2061,7 +2061,7 @@ async function renderInvites(res, type) { html += entry; } else if (invite.type === "group") { - let groupId = invite.id; + let groupId = invite.groupId || invite.id; let targetId = (type === "received") ? invite.sender : invite.receiver; if (!targetId.includes(":")) { From 891f39e5b14877796b04e86c2535a5705ce29eb1 Mon Sep 17 00:00:00 2001 From: olcxja Date: Sun, 2 Aug 2026 22:21:04 +0200 Subject: [PATCH 3/4] add group context menu --- android/app/src/main/assets/public/main.js | 108 +++++++++++++++++- android/app/src/main/assets/public/screens.js | 11 ++ webroot/main.js | 108 +++++++++++++++++- webroot/screens.js | 11 ++ 4 files changed, 236 insertions(+), 2 deletions(-) diff --git a/android/app/src/main/assets/public/main.js b/android/app/src/main/assets/public/main.js index a2ab99e2..f46e7fe1 100644 --- a/android/app/src/main/assets/public/main.js +++ b/android/app/src/main/assets/public/main.js @@ -1554,6 +1554,9 @@ async function switchDetailsContent(title, content, showBack = false, backAction let pinCallBtns = showBack ? `${callBtnHtml}` : `${pinBtnHtml}${callBtnHtml}`; + if (window.currentRoomType === 'groups' && !showBack) { + pinCallBtns = `${groupMenuBtnHtml}${pinCallBtns}`; + } let backBtn = showBack ? `` : ""; roomsTopBarTransition.innerHTML = `${backBtn}${processBlah(title)}
${pinCallBtns}`; @@ -2542,7 +2545,7 @@ async function renderGroups(res) { let pfp = await getGroupAvatarUrl(groupId, groupName); html += ` - + + + `; + + popupInner.innerHTML = html; + + let blahTags = popupInner.getElementsByTagName("blah"); + for (let i = 0; i < blahTags.length; i++) { + blahTags[i].innerHTML = processBlah(blahTags[i].innerHTML); + } + + popupContainer.style.display = "flex"; + popupContainer.offsetHeight; // trigger reflow + popupContainer.classList.add("show"); + isProfileOpen = true; +} + +async function confirmLeaveGroup(groupId) { + closeProfile(); + showAction("action.leaving.group", "leavegroup"); + try { + let payload = JSON.stringify({ string1: groupId }); + let res = await fetchEncrypted("group/leave", payload); + if (res === "success") { + if (currentDmId === groupId) { + gotoHome(); + } + refreshGroups(0, false); + } else if (res.startsWith("error:")) { + showBlahNotification(res); + } + } catch (e) { + console.error(e); + showBlahNotification("error:something.wrong"); + } + clearAction("leavegroup"); +} diff --git a/android/app/src/main/assets/public/screens.js b/android/app/src/main/assets/public/screens.js index ead665d2..8f1f7e04 100644 --- a/android/app/src/main/assets/public/screens.js +++ b/android/app/src/main/assets/public/screens.js @@ -250,6 +250,16 @@ var addSpaceMenu = ` ${ICONS.createSpace()} title.create.space ` +var groupContextMenu = ` + + +`; var messageContextMenu = ` `; var pinBtnHtml = ``; var callBtnHtml = ``; +var groupMenuBtnHtml = ``; var settingsBar = ` + `; + + popupInner.innerHTML = html; + + let blahTags = popupInner.getElementsByTagName("blah"); + for (let i = 0; i < blahTags.length; i++) { + blahTags[i].innerHTML = processBlah(blahTags[i].innerHTML); + } + + popupContainer.style.display = "flex"; + popupContainer.offsetHeight; // trigger reflow + popupContainer.classList.add("show"); + isProfileOpen = true; +} + +async function confirmLeaveGroup(groupId) { + closeProfile(); + showAction("action.leaving.group", "leavegroup"); + try { + let payload = JSON.stringify({ string1: groupId }); + let res = await fetchEncrypted("group/leave", payload); + if (res === "success") { + if (currentDmId === groupId) { + gotoHome(); + } + refreshGroups(0, false); + } else if (res.startsWith("error:")) { + showBlahNotification(res); + } + } catch (e) { + console.error(e); + showBlahNotification("error:something.wrong"); + } + clearAction("leavegroup"); +} diff --git a/webroot/screens.js b/webroot/screens.js index ead665d2..8f1f7e04 100644 --- a/webroot/screens.js +++ b/webroot/screens.js @@ -250,6 +250,16 @@ var addSpaceMenu = ` ${ICONS.createSpace()} title.create.space ` +var groupContextMenu = ` + + +`; var messageContextMenu = ` `; var pinBtnHtml = ``; var callBtnHtml = ``; +var groupMenuBtnHtml = ``; var settingsBar = ` - -