test new generic popups
All checks were successful
Android Build / publish (push) Successful in 50s
Linux Build / publish (push) Successful in 56s

This commit is contained in:
olcxja 2026-08-02 22:40:42 +02:00
commit 113530acb8
12 changed files with 464 additions and 178 deletions

View file

@ -408,18 +408,126 @@ async function getGroupKey(groupId, keyIndex) {
}
}
function closeRotationPopup() {
let popupContainer = document.getElementById("rotation-popup-container");
if(popupContainer) {
let pendingRotationMessagePayload = null;
let genericPopupConfirmCallback = null;
let genericPopupCancelCallback = null;
function closeGenericPopup() {
let popupContainer = document.getElementById("generic-popup-container");
if (popupContainer) {
popupContainer.classList.remove("show");
setTimeout(() => {
popupContainer.style.display = "none";
}, 200);
}
clearAction("key_rotation");
if (genericPopupCancelCallback) {
genericPopupCancelCallback();
genericPopupCancelCallback = null;
}
genericPopupConfirmCallback = null;
}
let pendingRotationMessagePayload = null;
function showConfirmPopup(titleKey, descHtml, confirmActionKey, cancelActionKey, onConfirm, onCancel = null, isDestructive = false) {
let popupContainer = document.getElementById("generic-popup-container");
let popupInner = document.getElementById("generic-popup-inner");
let confirmClass = isDestructive ? "popup-btn-danger" : "popup-btn-confirm";
genericPopupCancelCallback = onCancel;
genericPopupConfirmCallback = () => {
let cb = onConfirm;
genericPopupCancelCallback = null; // Prevent cancel from firing on confirm
closeGenericPopup();
if (cb) cb();
};
let html = `
<div style="font-size: 1.2rem; font-weight: 500; text-align: center;"><blah>${titleKey}</blah></div>
<div style="width: 100%; text-align: center; color: var(--text-color); opacity: 0.8; margin-top: 1rem; margin-bottom: 1rem;">
${descHtml}
</div>
<div style="display: flex; gap: 0.5rem; justify-content: center; width: 100%;">
<button class="profile-action-btn popup-btn-cancel" onclick="closeGenericPopup()">
<blah>${cancelActionKey}</blah>
</button>
<button class="profile-action-btn ${confirmClass}" onclick="if(genericPopupConfirmCallback) genericPopupConfirmCallback()">
<blah>${confirmActionKey}</blah>
</button>
</div>
`;
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");
}
function showPromptPopup(titleKey, descHtml, placeholderKey, confirmActionKey, cancelActionKey, onConfirm, onCancel = null, isDestructive = false) {
let popupContainer = document.getElementById("generic-popup-container");
let popupInner = document.getElementById("generic-popup-inner");
let confirmClass = isDestructive ? "popup-btn-danger" : "popup-btn-confirm";
genericPopupCancelCallback = onCancel;
genericPopupConfirmCallback = () => {
let val = document.getElementById("generic-popup-input").value;
let cb = onConfirm;
genericPopupCancelCallback = null; // Prevent cancel from firing on confirm
closeGenericPopup();
if (cb) cb(val);
};
let html = `
<div style="font-size: 1.2rem; font-weight: 500; text-align: center;"><blah>${titleKey}</blah></div>
<div style="width: 100%; text-align: center; color: var(--text-color); opacity: 0.8; margin-top: 0.5rem; margin-bottom: 1rem;">
${descHtml}
</div>
<input type="text" id="generic-popup-input" class="forminput" placeholder="{blah(${placeholderKey})}" style="width: 100%; margin-bottom: 1rem; box-sizing: border-box;" onkeydown="if(arguments[0].key === 'Enter') { if(genericPopupConfirmCallback) genericPopupConfirmCallback(); }">
<div style="display: flex; gap: 0.5rem; justify-content: center; width: 100%;">
<button class="profile-action-btn popup-btn-cancel" onclick="closeGenericPopup()">
<blah>${cancelActionKey}</blah>
</button>
<button class="profile-action-btn ${confirmClass}" onclick="if(genericPopupConfirmCallback) genericPopupConfirmCallback()">
<blah>${confirmActionKey}</blah>
</button>
</div>
`;
popupInner.innerHTML = html;
let input = document.getElementById("generic-popup-input");
if (input && input.placeholder.includes("{blah(")) {
let key = input.placeholder.replace("{blah(", "").replace(")}", "");
input.placeholder = processBlah(key);
}
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");
setTimeout(() => {
let el = document.getElementById("generic-popup-input");
if (el) el.focus();
}, 100);
}
function closeRotationPopup() {
closeGenericPopup();
}
async function showRotationPopup(groupId, originalMessagePayload) {
pendingRotationMessagePayload = originalMessagePayload;
@ -431,17 +539,16 @@ async function showRotationPopup(groupId, originalMessagePayload) {
let members = JSON.parse(membersRaw);
let desc = document.getElementById("rotation-popup-desc");
desc.innerHTML = `<blah>desc.key.rotation.info</blah><br><br><b>${members.length} <blah>label.members</blah></b>`;
let blahTags = desc.getElementsByTagName("blah");
for (let i = 0; i < blahTags.length; i++) {
blahTags[i].innerHTML = processBlah(blahTags[i].innerHTML);
}
let descHtml = `<blah>desc.key.rotation.info</blah><br><br><b>${members.length} <blah>label.members</blah></b>`;
let popupContainer = document.getElementById("rotation-popup-container");
popupContainer.style.display = "flex";
popupContainer.offsetHeight; // trigger reflow
popupContainer.classList.add("show");
showConfirmPopup(
"title.key.rotation",
descHtml,
"action.rotate.keys",
"action.cancel",
() => executeKeyRotation(),
() => clearAction("key_rotation")
);
} catch(e) {
clearAction("key_rotation");
showBlahNotification("error:something.wrong");
@ -2632,35 +2739,47 @@ async function generateDmMembersHtml(targetId, targetUsername) {
}
async function inviteToGroupPrompt(groupId) {
let targetUsername = prompt(processBlah("prompt.invite.username"));
if (!targetUsername) return;
if (!targetUsername.includes(":")) {
targetUsername += `:${host}`;
}
let idRes = await fetchAsync(`${url}/nametoid?u=${targetUsername}`);
if (idRes.startsWith("error") || idRes.trim() === "0" || idRes.trim() === "") {
showBlahNotification("error:user.not.found");
return;
}
let targetId = idRes.trim();
showPromptPopup(
"action.invite",
"",
"prompt.invite.username",
"action.invite",
"action.cancel",
async (targetUsername) => {
if (targetUsername) {
targetUsername = targetUsername.trim();
if (targetUsername.length > 0) {
if (!targetUsername.includes(":")) {
targetUsername += `:${host}`;
}
let idRes = await fetchAsync(`${url}/nametoid?u=${targetUsername}`);
if (idRes.startsWith("error") || idRes.trim() === "0" || idRes.trim() === "") {
showBlahNotification("error:user.not.found");
return;
}
let targetId = idRes.trim();
try {
showAction("action.invite.sending", "invite.action");
let payload = JSON.stringify({
string1: groupId,
string2: targetId
});
let res = await fetchEncrypted("group/invite", payload);
clearAction("invite.action");
showBlahNotification(res || "success:invite.sent");
} catch (e) {
clearAction("invite.action");
showBlahNotification("error:something.wrong");
}
try {
showAction("action.invite.sending", "invite.action");
let payload = JSON.stringify({
string1: groupId,
string2: targetId
});
let res = await fetchEncrypted("group/invite", payload);
clearAction("invite.action");
showBlahNotification(res || "success:invite.sent");
} catch (e) {
clearAction("invite.action");
showBlahNotification("error:something.wrong");
}
}
}
},
null,
false
);
}
async function generateGroupMembersHtml(groupId, groupName) {
@ -4816,35 +4935,15 @@ function gotoGroupSettings(groupId) {
function leaveGroupPrompt(groupId) {
if (fixedContextMenu) fixedContextMenu.classList.remove("show");
let popupContainer = document.getElementById("profile-popup-container");
let popupInner = document.getElementById("profile-popup-inner");
let html = `
<div style="text-align: center; margin-bottom: 1rem;">
<h3 style="margin-bottom: 0.5rem;"><blah>title.leave.group</blah></h3>
<p style="opacity: 0.7; font-size: 0.9rem;"><blah>desc.leave.group.confirm</blah></p>
</div>
<div style="display: flex; gap: 0.5rem; justify-content: center; width: 100%;">
<button class="profile-action-btn" style="background: var(--light-border-color); color: var(--text-color); flex: 1;" onclick="closeProfile()">
<blah>action.cancel</blah>
</button>
<button class="profile-action-btn" style="background: var(--big-red); color: white; border: none; flex: 1;" onclick="confirmLeaveGroup('${groupId}')">
<blah>title.leave.group</blah>
</button>
</div>
`;
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;
showConfirmPopup(
"title.leave.group",
`<blah>desc.leave.group.confirm</blah>`,
"title.leave.group",
"action.cancel",
() => confirmLeaveGroup(groupId),
null,
true
);
}
async function confirmLeaveGroup(groupId) {