test new generic popups
This commit is contained in:
parent
891f39e5b1
commit
113530acb8
12 changed files with 464 additions and 178 deletions
|
|
@ -257,5 +257,10 @@
|
|||
"en-cat": "Meowlish",
|
||||
"avatar.updated": "Avatar updated successfully",
|
||||
"profile.updated": "Profile updated successfully",
|
||||
"banner.updated": "Banner updated successfully"
|
||||
"banner.updated": "Banner updated successfully",
|
||||
"action.invite": "Invite, meow",
|
||||
"action.leaving.group": "Leaving group, meow...",
|
||||
"action.key.rotating": "Rotating keys, meow...",
|
||||
"action.key.rotation": "Key rotation, meow",
|
||||
"desc.leave.group.confirm": "Are you sure you want to leave this group, meow?"
|
||||
}
|
||||
|
|
@ -257,5 +257,10 @@
|
|||
"success:avatar.updated": "Avatar updated successfully",
|
||||
"en-us": "English",
|
||||
"pl-pl": "Polish",
|
||||
"en-cat": "Meowlish"
|
||||
"en-cat": "Meowlish",
|
||||
"action.invite": "Invite",
|
||||
"action.leaving.group": "Leaving group...",
|
||||
"action.key.rotating": "Rotating keys...",
|
||||
"action.key.rotation": "Key rotation",
|
||||
"desc.leave.group.confirm": "Are you sure you want to leave this group?"
|
||||
}
|
||||
|
|
@ -257,5 +257,10 @@
|
|||
"success:avatar.updated": "Awatar został pomyślnie zaktualizowany",
|
||||
"en-us": "Angielski",
|
||||
"pl-pl": "Polski",
|
||||
"en-cat": "Koci (Meowlish)"
|
||||
"en-cat": "Koci (Meowlish)",
|
||||
"action.invite": "Zaproś",
|
||||
"action.leaving.group": "Opuszczanie grupy...",
|
||||
"action.key.rotating": "Obracanie kluczy...",
|
||||
"action.key.rotation": "Rotacja kluczy",
|
||||
"desc.leave.group.confirm": "Czy na pewno chcesz opuścić tę grupę?"
|
||||
}
|
||||
|
|
@ -120,21 +120,16 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div id="rotation-popup-container" class="profile-popup-container" style="display: none; z-index: 9999;">
|
||||
<div class="profile-popup-bg" onclick="closeRotationPopup()"></div>
|
||||
<div class="profile-popup-content">
|
||||
<button class="profile-popup-close-btn" onclick="closeRotationPopup()">
|
||||
<div id="generic-popup-container" class="profile-popup-container" style="display: none; z-index: 9999;">
|
||||
<div class="profile-popup-bg" onclick="closeGenericPopup()"></div>
|
||||
<div class="profile-popup-content popup-auto-height">
|
||||
<button class="profile-popup-close-btn" onclick="closeGenericPopup()">
|
||||
<script>
|
||||
document.currentScript.insertAdjacentHTML('beforebegin', ICONS.close());
|
||||
document.currentScript.remove();
|
||||
</script>
|
||||
</button>
|
||||
<div id="profile-popup-inner" style="padding: 2rem; display: flex; flex-direction: column; align-items: center; gap: 1rem;">
|
||||
<div style="font-size: 1.2rem; font-weight: 500; text-align: center;"><blah>title.key.rotation</blah></div>
|
||||
<div style="width: 100%; text-align: center; color: var(--text-color); opacity: 0.8; margin-top: 1rem; margin-bottom: 1rem;" id="rotation-popup-desc">
|
||||
<!-- content injected dynamically -->
|
||||
</div>
|
||||
<button class="profile-action-btn" onclick="executeKeyRotation()"><blah>action.rotate.keys</blah></button>
|
||||
<div id="generic-popup-inner" style="padding: 2rem; display: flex; flex-direction: column; align-items: center; gap: 1rem; width: 100%; box-sizing: border-box;">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
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();
|
||||
|
||||
if (!targetUsername.includes(":")) {
|
||||
targetUsername += `:${host}`;
|
||||
}
|
||||
try {
|
||||
showAction("action.invite.sending", "invite.action");
|
||||
|
||||
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();
|
||||
let payload = JSON.stringify({
|
||||
string1: groupId,
|
||||
string2: targetId
|
||||
});
|
||||
|
||||
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");
|
||||
}
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -874,6 +874,40 @@ sidebarelement:has(.icon-button:active) indicator:not(.active) {
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.popup-auto-height {
|
||||
height: auto;
|
||||
max-height: 85vh;
|
||||
}
|
||||
.popup-btn-cancel {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
color: var(--text-color);
|
||||
border: var(--border-width) solid var(--light-border-color);
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
}
|
||||
.popup-btn-cancel:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
.popup-btn-confirm {
|
||||
background: var(--text-color);
|
||||
color: var(--main-bg-color);
|
||||
border: none;
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
}
|
||||
.popup-btn-confirm:hover {
|
||||
background: white;
|
||||
}
|
||||
.popup-btn-danger {
|
||||
background: var(--big-red);
|
||||
color: white;
|
||||
border: none;
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
}
|
||||
.popup-btn-danger:hover {
|
||||
background: #ff5252;
|
||||
}
|
||||
.profile-popup-container.show .profile-popup-content {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
|
|
|
|||
|
|
@ -257,5 +257,10 @@
|
|||
"en-cat": "Meowlish",
|
||||
"avatar.updated": "Avatar updated successfully",
|
||||
"profile.updated": "Profile updated successfully",
|
||||
"banner.updated": "Banner updated successfully"
|
||||
"banner.updated": "Banner updated successfully",
|
||||
"action.invite": "Invite, meow",
|
||||
"action.leaving.group": "Leaving group, meow...",
|
||||
"action.key.rotating": "Rotating keys, meow...",
|
||||
"action.key.rotation": "Key rotation, meow",
|
||||
"desc.leave.group.confirm": "Are you sure you want to leave this group, meow?"
|
||||
}
|
||||
|
|
@ -257,5 +257,10 @@
|
|||
"success:avatar.updated": "Avatar updated successfully",
|
||||
"en-us": "English",
|
||||
"pl-pl": "Polish",
|
||||
"en-cat": "Meowlish"
|
||||
"en-cat": "Meowlish",
|
||||
"action.invite": "Invite",
|
||||
"action.leaving.group": "Leaving group...",
|
||||
"action.key.rotating": "Rotating keys...",
|
||||
"action.key.rotation": "Key rotation",
|
||||
"desc.leave.group.confirm": "Are you sure you want to leave this group?"
|
||||
}
|
||||
|
|
@ -257,5 +257,10 @@
|
|||
"success:avatar.updated": "Awatar został pomyślnie zaktualizowany",
|
||||
"en-us": "Angielski",
|
||||
"pl-pl": "Polski",
|
||||
"en-cat": "Koci (Meowlish)"
|
||||
"en-cat": "Koci (Meowlish)",
|
||||
"action.invite": "Zaproś",
|
||||
"action.leaving.group": "Opuszczanie grupy...",
|
||||
"action.key.rotating": "Obracanie kluczy...",
|
||||
"action.key.rotation": "Rotacja kluczy",
|
||||
"desc.leave.group.confirm": "Czy na pewno chcesz opuścić tę grupę?"
|
||||
}
|
||||
|
|
@ -120,21 +120,16 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div id="rotation-popup-container" class="profile-popup-container" style="display: none; z-index: 9999;">
|
||||
<div class="profile-popup-bg" onclick="closeRotationPopup()"></div>
|
||||
<div class="profile-popup-content">
|
||||
<button class="profile-popup-close-btn" onclick="closeRotationPopup()">
|
||||
<div id="generic-popup-container" class="profile-popup-container" style="display: none; z-index: 9999;">
|
||||
<div class="profile-popup-bg" onclick="closeGenericPopup()"></div>
|
||||
<div class="profile-popup-content popup-auto-height">
|
||||
<button class="profile-popup-close-btn" onclick="closeGenericPopup()">
|
||||
<script>
|
||||
document.currentScript.insertAdjacentHTML('beforebegin', ICONS.close());
|
||||
document.currentScript.remove();
|
||||
</script>
|
||||
</button>
|
||||
<div id="profile-popup-inner" style="padding: 2rem; display: flex; flex-direction: column; align-items: center; gap: 1rem;">
|
||||
<div style="font-size: 1.2rem; font-weight: 500; text-align: center;"><blah>title.key.rotation</blah></div>
|
||||
<div style="width: 100%; text-align: center; color: var(--text-color); opacity: 0.8; margin-top: 1rem; margin-bottom: 1rem;" id="rotation-popup-desc">
|
||||
<!-- content injected dynamically -->
|
||||
</div>
|
||||
<button class="profile-action-btn" onclick="executeKeyRotation()"><blah>action.rotate.keys</blah></button>
|
||||
<div id="generic-popup-inner" style="padding: 2rem; display: flex; flex-direction: column; align-items: center; gap: 1rem; width: 100%; box-sizing: border-box;">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
239
webroot/main.js
239
webroot/main.js
|
|
@ -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;
|
||||
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();
|
||||
|
||||
if (!targetUsername.includes(":")) {
|
||||
targetUsername += `:${host}`;
|
||||
}
|
||||
try {
|
||||
showAction("action.invite.sending", "invite.action");
|
||||
|
||||
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();
|
||||
let payload = JSON.stringify({
|
||||
string1: groupId,
|
||||
string2: targetId
|
||||
});
|
||||
|
||||
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");
|
||||
}
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -874,6 +874,40 @@ sidebarelement:has(.icon-button:active) indicator:not(.active) {
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.popup-auto-height {
|
||||
height: auto;
|
||||
max-height: 85vh;
|
||||
}
|
||||
.popup-btn-cancel {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
color: var(--text-color);
|
||||
border: var(--border-width) solid var(--light-border-color);
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
}
|
||||
.popup-btn-cancel:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
.popup-btn-confirm {
|
||||
background: var(--text-color);
|
||||
color: var(--main-bg-color);
|
||||
border: none;
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
}
|
||||
.popup-btn-confirm:hover {
|
||||
background: white;
|
||||
}
|
||||
.popup-btn-danger {
|
||||
background: var(--big-red);
|
||||
color: white;
|
||||
border: none;
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
}
|
||||
.popup-btn-danger:hover {
|
||||
background: #ff5252;
|
||||
}
|
||||
.profile-popup-container.show .profile-popup-content {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue