bug fixes
This commit is contained in:
parent
d67bb67cad
commit
51951bd6ef
8 changed files with 540 additions and 28 deletions
|
|
@ -267,5 +267,28 @@
|
||||||
"label.keys.to.encrypt": "keys to encrypt, meow",
|
"label.keys.to.encrypt": "keys to encrypt, meow",
|
||||||
"action.group.creating": "Creating group, meow...",
|
"action.group.creating": "Creating group, meow...",
|
||||||
"title.group.settings": "Group settings, meow",
|
"title.group.settings": "Group settings, meow",
|
||||||
"title.leave.group": "Leave group, meow"
|
"title.leave.group": "Leave group, meow",
|
||||||
|
"role.member": "Meowber",
|
||||||
|
"role.group_owner": "Big purr",
|
||||||
|
"role.Owner": "Big purr",
|
||||||
|
"action.kick": "Hiss and scratch from group",
|
||||||
|
"action.transfer_owner": "Give shiny toy",
|
||||||
|
"group.user.kicked.by": "{0} hissed at {1} and they ran away",
|
||||||
|
"group.owner.transferred.by": "{0} gave the shiny toy to {1}",
|
||||||
|
"group.user.kicked": "Kitty kicked",
|
||||||
|
"group.owner.transferred": "Shiny toy given",
|
||||||
|
"success:kicked": "Hissed away successfully",
|
||||||
|
"success:owner.transferred": "Toy transferred",
|
||||||
|
"error:cannot.kick.self": "You can't scratch yourself",
|
||||||
|
"action.saving": "Meowving...",
|
||||||
|
"desc.no.chat.selected": "No purr selected. Select a purr from the sidebar to start meowing.",
|
||||||
|
"failed.to.prepare.keys": "Failed to prepare meows",
|
||||||
|
"group.name.empty": "Purr name cannot be empty",
|
||||||
|
"group.refresh.failed": "Failed to refresh purr",
|
||||||
|
"group.settings.saved": "Purr settings saved successfully",
|
||||||
|
"kicked": "Bited successfully",
|
||||||
|
"message.delete.failed": "Failed to delete meow",
|
||||||
|
"message.react.failed": "Failed to react to meow",
|
||||||
|
"message.send.failed": "Failed to send meow",
|
||||||
|
"owner.transferred": "Purrship transferred successfully"
|
||||||
}
|
}
|
||||||
|
|
@ -249,7 +249,7 @@
|
||||||
"group.user.joined": "Joined group",
|
"group.user.joined": "Joined group",
|
||||||
"prompt.invite.username": "Enter username to invite:",
|
"prompt.invite.username": "Enter username to invite:",
|
||||||
"desc.failed.load.members": "Failed to load members",
|
"desc.failed.load.members": "Failed to load members",
|
||||||
"title.group.settings": "Group Settings",
|
"title.group.settings": "Group settings",
|
||||||
"title.group.name": "Group Name",
|
"title.group.name": "Group Name",
|
||||||
"title.group.history.visible": "History visible to newcomers",
|
"title.group.history.visible": "History visible to newcomers",
|
||||||
"desc.group.history.visible": "New group members will be able to see older messages (disables key rotation)",
|
"desc.group.history.visible": "New group members will be able to see older messages (disables key rotation)",
|
||||||
|
|
@ -273,6 +273,28 @@
|
||||||
"desc.key.rotation.you": "You will generate a new encryption key for this group.",
|
"desc.key.rotation.you": "You will generate a new encryption key for this group.",
|
||||||
"label.keys.to.encrypt": "keys to encrypt",
|
"label.keys.to.encrypt": "keys to encrypt",
|
||||||
"action.group.creating": "Creating group...",
|
"action.group.creating": "Creating group...",
|
||||||
"title.group.settings": "Group settings",
|
"title.leave.group": "Leave group",
|
||||||
"title.leave.group": "Leave group"
|
"role.member": "Member",
|
||||||
|
"role.group_owner": "Group owner",
|
||||||
|
"role.Owner": "Owner",
|
||||||
|
"action.kick": "Kick from group",
|
||||||
|
"action.transfer_owner": "Transfer ownership",
|
||||||
|
"group.user.kicked.by": "{0} kicked {1} from the group",
|
||||||
|
"group.owner.transferred.by": "{0} transferred ownership to {1}",
|
||||||
|
"group.user.kicked": "User kicked",
|
||||||
|
"group.owner.transferred": "Ownership transferred",
|
||||||
|
"success:kicked": "User kicked successfully",
|
||||||
|
"success:owner.transferred": "Ownership transferred",
|
||||||
|
"error:cannot.kick.self": "You cannot kick yourself",
|
||||||
|
"action.saving": "Saving...",
|
||||||
|
"desc.no.chat.selected": "No chat selected. Select a chat from the sidebar to start messaging.",
|
||||||
|
"failed.to.prepare.keys": "Failed to prepare keys",
|
||||||
|
"group.name.empty": "Group name cannot be empty",
|
||||||
|
"group.refresh.failed": "Failed to refresh group",
|
||||||
|
"group.settings.saved": "Group settings saved successfully",
|
||||||
|
"kicked": "Kicked successfully",
|
||||||
|
"message.delete.failed": "Failed to delete message",
|
||||||
|
"message.react.failed": "Failed to react to message",
|
||||||
|
"message.send.failed": "Failed to send message",
|
||||||
|
"owner.transferred": "Ownership transferred successfully"
|
||||||
}
|
}
|
||||||
|
|
@ -273,6 +273,28 @@
|
||||||
"desc.key.rotation.you": "Wygenerujesz nowy klucz szyfrujący dla tej grupy.",
|
"desc.key.rotation.you": "Wygenerujesz nowy klucz szyfrujący dla tej grupy.",
|
||||||
"label.keys.to.encrypt": "kluczy do zaszyfrowania",
|
"label.keys.to.encrypt": "kluczy do zaszyfrowania",
|
||||||
"action.group.creating": "Tworzenie grupy...",
|
"action.group.creating": "Tworzenie grupy...",
|
||||||
"title.group.settings": "Ustawienia grupy",
|
"title.leave.group": "Opuść grupę",
|
||||||
"title.leave.group": "Opuść grupę"
|
"role.member": "Członek",
|
||||||
|
"role.group_owner": "Właściciel grupy",
|
||||||
|
"role.Owner": "Właściciel",
|
||||||
|
"action.kick": "Wyrzuć z grupy",
|
||||||
|
"action.transfer_owner": "Przekaż właściciela",
|
||||||
|
"group.user.kicked.by": "{0} wyrzucił/a {1} z grupy",
|
||||||
|
"group.owner.transferred.by": "{0} przekazał/a właściciela dla {1}",
|
||||||
|
"group.user.kicked": "Użytkownik wyrzucony",
|
||||||
|
"group.owner.transferred": "Właściciel przekazany",
|
||||||
|
"success:kicked": "Użytkownik wyrzucony pomyślnie",
|
||||||
|
"success:owner.transferred": "Właściciel przekazany pomyślnie",
|
||||||
|
"error:cannot.kick.self": "Nie możesz wyrzucić samego siebie",
|
||||||
|
"action.saving": "Zapisywanie...",
|
||||||
|
"desc.no.chat.selected": "Nie wybrano czatu. Wybierz czat z paska bocznego, aby rozpocząć pisanie.",
|
||||||
|
"failed.to.prepare.keys": "Nie udało się przygotować kluczy",
|
||||||
|
"group.name.empty": "Nazwa grupy nie może być pusta",
|
||||||
|
"group.refresh.failed": "Nie udało się odświeżyć grupy",
|
||||||
|
"group.settings.saved": "Ustawienia grupy zapisane pomyślnie",
|
||||||
|
"kicked": "Wyrzucono pomyślnie",
|
||||||
|
"message.delete.failed": "Nie udało się usunąć wiadomości",
|
||||||
|
"message.react.failed": "Nie udało się dodać reakcji",
|
||||||
|
"message.send.failed": "Nie udało się wysłać wiadomości",
|
||||||
|
"owner.transferred": "Własność przekazana pomyślnie"
|
||||||
}
|
}
|
||||||
|
|
@ -1563,13 +1563,19 @@ async function switchRoomContent(title, content, showRoomBar, icon = "", skipMob
|
||||||
roomsTopBarTransition.style.opacity = "0";
|
roomsTopBarTransition.style.opacity = "0";
|
||||||
|
|
||||||
const rem = parseFloat(getComputedStyle(document.documentElement).fontSize);
|
const rem = parseFloat(getComputedStyle(document.documentElement).fontSize);
|
||||||
|
let slideIn = false;
|
||||||
if (window.innerWidth <= 55 * rem && !skipMobileSlide) {
|
if (window.innerWidth <= 55 * rem && !skipMobileSlide) {
|
||||||
if (title != "title.splash") //do not show splash on mobile
|
if (title != "title.splash") //do not show splash on mobile
|
||||||
{
|
{
|
||||||
|
if (!mainScreen.classList.contains('mobile-content') && !mainScreen.classList.contains('mobile-details')) {
|
||||||
|
slideIn = true;
|
||||||
|
}
|
||||||
mainScreen.classList.remove('mobile-details');
|
mainScreen.classList.remove('mobile-details');
|
||||||
mainScreen.classList.add('mobile-content');
|
mainScreen.classList.add('mobile-content');
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
if (!slideIn) {
|
||||||
await delay(200);
|
await delay(200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1762,6 +1768,7 @@ function clearContextMenuStyles() {
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener("click", (e) => {
|
document.addEventListener("click", (e) => {
|
||||||
|
if (touchContextMenuFired) return;
|
||||||
history.pushState({trap: true}, "", location.href);
|
history.pushState({trap: true}, "", location.href);
|
||||||
|
|
||||||
if (fixedContextMenu.classList.contains("show")) {
|
if (fixedContextMenu.classList.contains("show")) {
|
||||||
|
|
@ -1778,10 +1785,14 @@ document.addEventListener("click", (e) => {
|
||||||
const rem = parseFloat(getComputedStyle(document.documentElement).fontSize);
|
const rem = parseFloat(getComputedStyle(document.documentElement).fontSize);
|
||||||
if (window.innerWidth <= 55 * rem) {
|
if (window.innerWidth <= 55 * rem) {
|
||||||
if (!roomDetailsBar.contains(e.target) && !e.target.closest('#room-topbar-clickable') && !e.target.closest('.profile-popup-container') && !e.target.closest('.room-action-pin')) {
|
if (!roomDetailsBar.contains(e.target) && !e.target.closest('#room-topbar-clickable') && !e.target.closest('.profile-popup-container') && !e.target.closest('.room-action-pin')) {
|
||||||
|
if (currentRoomContentTitle === "title.group.settings") {
|
||||||
|
openDm(window.currentSettingsGroupId, document.getElementById('group-settings-name').value, window.currentSettingsGroupId, 'groups');
|
||||||
|
} else {
|
||||||
mobileNavBack();
|
mobileNavBack();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
window.addEventListener("popstate", (e) => {
|
window.addEventListener("popstate", (e) => {
|
||||||
popstate();
|
popstate();
|
||||||
|
|
@ -1848,6 +1859,7 @@ function logout() {
|
||||||
let isProfileOpen = false;
|
let isProfileOpen = false;
|
||||||
|
|
||||||
async function openProfile(accountId) {
|
async function openProfile(accountId) {
|
||||||
|
if (touchContextMenuFired) return;
|
||||||
if (fixedContextMenu.classList.contains("show")) {
|
if (fixedContextMenu.classList.contains("show")) {
|
||||||
fixedContextMenu.classList.remove("show");
|
fixedContextMenu.classList.remove("show");
|
||||||
if (typeof clearContextMenuStyles === "function") clearContextMenuStyles();
|
if (typeof clearContextMenuStyles === "function") clearContextMenuStyles();
|
||||||
|
|
@ -1969,6 +1981,19 @@ async function openProfile(accountId) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let roleHtml = "";
|
||||||
|
if (window.currentDmId && window.currentRoomType && (window.currentRoomType === "groups" || window.currentRoomType === "dms" || window.currentRoomType === "spaces")) {
|
||||||
|
let payload = JSON.stringify({
|
||||||
|
string1: window.currentRoomType,
|
||||||
|
string2: window.currentDmId,
|
||||||
|
string3: accountId
|
||||||
|
});
|
||||||
|
let roleRes = await fetchEncrypted("room/role", payload);
|
||||||
|
if (roleRes && !roleRes.startsWith("error:")) {
|
||||||
|
let mappedRole = "role." + roleRes.replace(" ", "_");
|
||||||
|
roleHtml = `<div class="profile-role" style="opacity:0.7; font-size:0.9rem; margin-top:0.3rem;"><blah>${mappedRole}</blah></div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
popupInner.innerHTML = `
|
popupInner.innerHTML = `
|
||||||
${bannerHtml}
|
${bannerHtml}
|
||||||
|
|
@ -1979,6 +2004,7 @@ async function openProfile(accountId) {
|
||||||
<div>
|
<div>
|
||||||
<div class="profile-name">${profileName}</div>
|
<div class="profile-name">${profileName}</div>
|
||||||
<div class="profile-id-host">${profileName}:${profileHost} (${accountId.split(':')[0].split(';')[0]})</div>
|
<div class="profile-id-host">${profileName}:${profileHost} (${accountId.split(':')[0].split(';')[0]})</div>
|
||||||
|
${roleHtml}
|
||||||
</div>
|
</div>
|
||||||
${bioHtml}
|
${bioHtml}
|
||||||
${actionBtnHtml}
|
${actionBtnHtml}
|
||||||
|
|
@ -2423,6 +2449,11 @@ function setActiveSidebarIndicator(element, isTemporary = false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function mobileNavBack() {
|
function mobileNavBack() {
|
||||||
|
if (currentRoomContentTitle === "title.group.settings") {
|
||||||
|
openDm(window.currentSettingsGroupId, document.getElementById('group-settings-name').value, window.currentSettingsGroupId, 'groups');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (mainScreen.classList.contains('mobile-details')) {
|
if (mainScreen.classList.contains('mobile-details')) {
|
||||||
mainScreen.classList.remove('mobile-details');
|
mainScreen.classList.remove('mobile-details');
|
||||||
mainScreen.classList.add('mobile-content');
|
mainScreen.classList.add('mobile-content');
|
||||||
|
|
@ -2512,6 +2543,7 @@ document.addEventListener('touchend', async (e) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
let elapsed = Date.now() - pressDate; //najgorszy fix swiata
|
let elapsed = Date.now() - pressDate; //najgorszy fix swiata
|
||||||
|
|
||||||
if (elapsed < 100) {
|
if (elapsed < 100) {
|
||||||
const remaining = 100 - elapsed;
|
const remaining = 100 - elapsed;
|
||||||
await delay(remaining);
|
await delay(remaining);
|
||||||
|
|
@ -2861,7 +2893,7 @@ async function generateGroupMembersHtml(groupId, groupName) {
|
||||||
let memberDisplayName = memberUsername.split(':')[0];
|
let memberDisplayName = memberUsername.split(':')[0];
|
||||||
|
|
||||||
html += `
|
html += `
|
||||||
<button class="room-entry" onclick="openProfile('${memberId}')" style="margin-bottom: 0;">
|
<button class="room-entry" onclick="openProfile('${memberId}')" oncontextmenu="handleMemberContextMenu(event, '${groupId}', '${memberId}')" style="margin-bottom: 0;">
|
||||||
<img src="${memberPfp}" class="room-pfp">
|
<img src="${memberPfp}" class="room-pfp">
|
||||||
<div style="display: flex; flex-direction: column; text-align: left; overflow: hidden; width: 100%; margin-left: 0.37rem;">
|
<div style="display: flex; flex-direction: column; text-align: left; overflow: hidden; width: 100%; margin-left: 0.37rem;">
|
||||||
<span class="room-name">${memberDisplayName}</span>
|
<span class="room-name">${memberDisplayName}</span>
|
||||||
|
|
@ -2875,6 +2907,9 @@ async function generateGroupMembersHtml(groupId, groupName) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function openDm(dmId, username, targetId, type = 'dms') {
|
async function openDm(dmId, username, targetId, type = 'dms') {
|
||||||
|
if (touchContextMenuFired) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
showAction("action.dm.opening", "dmopen");
|
showAction("action.dm.opening", "dmopen");
|
||||||
|
|
||||||
|
|
@ -2894,8 +2929,10 @@ async function openDm(dmId, username, targetId, type = 'dms') {
|
||||||
if (infoPayload && !infoPayload.startsWith("error:")) {
|
if (infoPayload && !infoPayload.startsWith("error:")) {
|
||||||
let info = JSON.parse(infoPayload);
|
let info = JSON.parse(infoPayload);
|
||||||
window.currentGroupKeyIndex = info.string4 || "0";
|
window.currentGroupKeyIndex = info.string4 || "0";
|
||||||
|
window.currentGroupOwner = info.string7;
|
||||||
} else {
|
} else {
|
||||||
window.currentGroupKeyIndex = "0";
|
window.currentGroupKeyIndex = "0";
|
||||||
|
window.currentGroupOwner = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2972,6 +3009,16 @@ async function loadDmMessages(dmId, startOffset = "", isPrepend = false) {
|
||||||
let res = await fetchEncrypted(endpoint, payload);
|
let res = await fetchEncrypted(endpoint, payload);
|
||||||
|
|
||||||
if (res && res.startsWith("error:")) {
|
if (res && res.startsWith("error:")) {
|
||||||
|
if (res === "error:forbidden" || res === "error:not.a.member" || res === "error:group.not.found") {
|
||||||
|
if (dmMessagePollInterval) clearInterval(dmMessagePollInterval);
|
||||||
|
currentDmId = null;
|
||||||
|
showBlahNotification(res);
|
||||||
|
if (mainScreen.classList.contains('mobile-details')) {
|
||||||
|
mainScreen.classList.remove('mobile-details');
|
||||||
|
}
|
||||||
|
switchDetailsContent("title.details", `<div style="text-align: center; opacity: 0.5; padding: 1rem; margin-top: 2rem;"><blah>desc.no.chat.selected</blah></div>`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
showBlahNotification(res);
|
showBlahNotification(res);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -3070,7 +3117,7 @@ async function generateMessagesHtml(entries, isPinnedContext = false) {
|
||||||
try {
|
try {
|
||||||
let atts = JSON.parse(msg.attachment);
|
let atts = JSON.parse(msg.attachment);
|
||||||
if (atts.length > 0) {
|
if (atts.length > 0) {
|
||||||
attachmentsHtml += `<div class="chat-message-attachments" style="display: flex; gap: 0.5rem; flex-wrap: wrap; margin-top: 0.5rem;">`;
|
attachmentsHtml += `<div class="chat-message-attachments" data-raw="${escapeHtml(msg.attachment)}" style="display: flex; gap: 0.5rem; flex-wrap: wrap; margin-top: 0.5rem;">`;
|
||||||
for (let att of atts) {
|
for (let att of atts) {
|
||||||
if (att.enc && att.enc.startsWith("ENC:")) {
|
if (att.enc && att.enc.startsWith("ENC:")) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -3116,7 +3163,7 @@ async function generateMessagesHtml(entries, isPinnedContext = false) {
|
||||||
console.error("Failed to parse attachments", e);
|
console.error("Failed to parse attachments", e);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
attachmentsHtml += `<div class="chat-message-attachments" style="display: flex; gap: 0.5rem; flex-wrap: wrap; margin-top: 0.5rem;">
|
attachmentsHtml += `<div class="chat-message-attachments" data-raw="${escapeHtml(msg.attachment)}" style="display: flex; gap: 0.5rem; flex-wrap: wrap; margin-top: 0.5rem;">
|
||||||
<div style="padding: 0.5rem; background: rgba(0,0,0,0.2); border: var(--border-width) solid var(--border-color); border-radius: 0.5rem; font-size: 0.8rem; opacity: 0.7;">[${processBlah("attachment.legacy")}: ${escapeHtml(msg.attachment)}]</div>
|
<div style="padding: 0.5rem; background: rgba(0,0,0,0.2); border: var(--border-width) solid var(--border-color); border-radius: 0.5rem; font-size: 0.8rem; opacity: 0.7;">[${processBlah("attachment.legacy")}: ${escapeHtml(msg.attachment)}]</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
@ -3133,7 +3180,7 @@ async function generateMessagesHtml(entries, isPinnedContext = false) {
|
||||||
content = processBlah(content);
|
content = processBlah(content);
|
||||||
|
|
||||||
if (msg.type && msg.type.startsWith("larp.info")) {
|
if (msg.type && msg.type.startsWith("larp.info")) {
|
||||||
if (msg.type === "larp.info.pin" || msg.type === "larp.info.unpin" || msg.type === "larp.info.join" || msg.type === "larp.info.leave" || msg.type === "larp.info.create" || msg.type === "larp.info.history") {
|
if (msg.type === "larp.info.pin" || msg.type === "larp.info.unpin" || msg.type === "larp.info.join" || msg.type === "larp.info.leave" || msg.type === "larp.info.create" || msg.type === "larp.info.history" || msg.type === "larp.info.kick" || msg.type === "larp.info.transfer_owner") {
|
||||||
let authorName = processBlah("title.unknown");
|
let authorName = processBlah("title.unknown");
|
||||||
if (msg.author && msg.author !== "0") {
|
if (msg.author && msg.author !== "0") {
|
||||||
if (!dmUsernameCache[msg.author]) {
|
if (!dmUsernameCache[msg.author]) {
|
||||||
|
|
@ -3157,6 +3204,8 @@ async function generateMessagesHtml(entries, isPinnedContext = false) {
|
||||||
else if (msg.type === "larp.info.leave") actionStr = "group.user.left.by";
|
else if (msg.type === "larp.info.leave") actionStr = "group.user.left.by";
|
||||||
else if (msg.type === "larp.info.create") actionStr = "group.created.by";
|
else if (msg.type === "larp.info.create") actionStr = "group.created.by";
|
||||||
else if (msg.type === "larp.info.history") actionStr = "group.history.changed.by";
|
else if (msg.type === "larp.info.history") actionStr = "group.history.changed.by";
|
||||||
|
else if (msg.type === "larp.info.kick") actionStr = "group.user.kicked.by";
|
||||||
|
else if (msg.type === "larp.info.transfer_owner") actionStr = "group.owner.transferred.by";
|
||||||
|
|
||||||
let clickableAuthor = `<span onclick="event.stopPropagation(); openProfile('${msg.author}')" style="cursor: pointer; font-weight: bold;">${authorName}</span>`;
|
let clickableAuthor = `<span onclick="event.stopPropagation(); openProfile('${msg.author}')" style="cursor: pointer; font-weight: bold;">${authorName}</span>`;
|
||||||
|
|
||||||
|
|
@ -3166,6 +3215,18 @@ async function generateMessagesHtml(entries, isPinnedContext = false) {
|
||||||
content = textStr.replace("{0}", clickableAuthor).replace("{1}", clickableMsg);
|
content = textStr.replace("{0}", clickableAuthor).replace("{1}", clickableMsg);
|
||||||
} else if (msg.type === "larp.info.history") {
|
} else if (msg.type === "larp.info.history") {
|
||||||
content = textStr.replace("{0}", clickableAuthor).replace("{1}", content);
|
content = textStr.replace("{0}", clickableAuthor).replace("{1}", content);
|
||||||
|
} else if (msg.type === "larp.info.kick" || msg.type === "larp.info.transfer_owner") {
|
||||||
|
let targetUsername = "";
|
||||||
|
let targetId = "";
|
||||||
|
let parts = content.split(':');
|
||||||
|
if (parts.length >= 2 && parts[0].includes(';')) {
|
||||||
|
targetId = parts[0];
|
||||||
|
targetUsername = escapeHtml(parts.slice(1).join(':'));
|
||||||
|
} else {
|
||||||
|
targetUsername = escapeHtml(parts[0]);
|
||||||
|
}
|
||||||
|
let clickableTarget = targetId ? `<span onclick="event.stopPropagation(); openProfile('${targetId}')" style="font-weight: bold; cursor: pointer;">${targetUsername}</span>` : `<span style="font-weight: bold;">${targetUsername}</span>`;
|
||||||
|
content = textStr.replace("{0}", clickableAuthor).replace("{1}", clickableTarget);
|
||||||
} else {
|
} else {
|
||||||
content = textStr.replace("{0}", clickableAuthor);
|
content = textStr.replace("{0}", clickableAuthor);
|
||||||
}
|
}
|
||||||
|
|
@ -3424,6 +3485,20 @@ async function renderMessages(messages, isPrepend = false) {
|
||||||
oldReactions.remove();
|
oldReactions.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let oldAttachments = existingChild.querySelector('.chat-message-attachments');
|
||||||
|
let newAttachments = newChild.querySelector('.chat-message-attachments');
|
||||||
|
if (newAttachments) {
|
||||||
|
if (oldAttachments) {
|
||||||
|
if (oldAttachments.getAttribute('data-raw') !== newAttachments.getAttribute('data-raw')) oldAttachments.innerHTML = newAttachments.innerHTML;
|
||||||
|
} else {
|
||||||
|
let contentNode = existingChild.querySelector('.chat-message-content');
|
||||||
|
if (oldReactions) contentNode.insertBefore(newAttachments, oldReactions);
|
||||||
|
else contentNode.appendChild(newAttachments);
|
||||||
|
}
|
||||||
|
} else if (oldAttachments) {
|
||||||
|
oldAttachments.remove();
|
||||||
|
}
|
||||||
|
|
||||||
let oldPfp = existingChild.querySelector('.chat-message-pfp');
|
let oldPfp = existingChild.querySelector('.chat-message-pfp');
|
||||||
let newPfp = newChild.querySelector('.chat-message-pfp');
|
let newPfp = newChild.querySelector('.chat-message-pfp');
|
||||||
if (oldPfp && newPfp && oldPfp.src !== newPfp.src) {
|
if (oldPfp && newPfp && oldPfp.src !== newPfp.src) {
|
||||||
|
|
@ -3920,7 +3995,6 @@ let touchContextMenuFired = false;
|
||||||
function handleMessageContextMenu(e, msgId) {
|
function handleMessageContextMenu(e, msgId) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (touchContextMenuFired) {
|
if (touchContextMenuFired) {
|
||||||
touchContextMenuFired = false;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (typeof clearContextMenuStyles === "function") clearContextMenuStyles();
|
if (typeof clearContextMenuStyles === "function") clearContextMenuStyles();
|
||||||
|
|
@ -5013,6 +5087,96 @@ if (typeof roomTopBar !== 'undefined' && roomTopBar) {
|
||||||
|
|
||||||
var currentContextMenuGroupId = null;
|
var currentContextMenuGroupId = null;
|
||||||
|
|
||||||
|
function handleMemberContextMenu(e, groupId, memberId) {
|
||||||
|
if (window.currentRoomType !== 'groups') return;
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
if (e && e.type === "contextmenu") {
|
||||||
|
touchContextMenuFired = true;
|
||||||
|
setTimeout(() => { touchContextMenuFired = false; }, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
let html = '';
|
||||||
|
|
||||||
|
let myIdStr = window.id + ";" + window.host;
|
||||||
|
if (window.currentGroupOwner === myIdStr && memberId !== myIdStr) {
|
||||||
|
html += `
|
||||||
|
<button onclick="kickFromGroup('${groupId}', '${memberId}')">
|
||||||
|
${ICONS.delete({ fill: "var(--big-red)" })}
|
||||||
|
<span class="blah" style="color: var(--big-red)">action.kick</span>
|
||||||
|
</button>
|
||||||
|
<button onclick="transferGroupOwnership('${groupId}', '${memberId}')">
|
||||||
|
${ICONS.profile()}
|
||||||
|
<span class="blah">action.transfer_owner</span>
|
||||||
|
</button>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (html !== '') {
|
||||||
|
let clientX = e.clientX !== undefined ? e.clientX : (e.touches && e.touches.length > 0 ? e.touches[0].clientX : 0);
|
||||||
|
let clientY = e.clientY !== undefined ? e.clientY : (e.touches && e.touches.length > 0 ? e.touches[0].clientY : 0);
|
||||||
|
showFixedContextMenu({top: clientY, right: clientX, bottom: clientY, left: clientX}, html);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function kickFromGroup(groupId, memberId) {
|
||||||
|
if (fixedContextMenu.classList.contains("show")) {
|
||||||
|
fixedContextMenu.classList.remove("show");
|
||||||
|
}
|
||||||
|
|
||||||
|
showAction("action.processing", "kick.action");
|
||||||
|
let payload = JSON.stringify({
|
||||||
|
string1: groupId,
|
||||||
|
string2: memberId
|
||||||
|
});
|
||||||
|
|
||||||
|
let res = await fetchEncrypted("group/kick", payload);
|
||||||
|
clearAction("kick.action");
|
||||||
|
|
||||||
|
if (res && res.startsWith("success")) {
|
||||||
|
showBlahNotification("success:kicked");
|
||||||
|
showGroupMembers(groupId).then(html => {
|
||||||
|
let container = document.getElementById("right-sidebar-container");
|
||||||
|
if (container) {
|
||||||
|
container.innerHTML = html;
|
||||||
|
translate(container);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
showBlahNotification(res || "error:something.wrong");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function transferGroupOwnership(groupId, memberId) {
|
||||||
|
if (fixedContextMenu.classList.contains("show")) {
|
||||||
|
fixedContextMenu.classList.remove("show");
|
||||||
|
}
|
||||||
|
|
||||||
|
showAction("action.processing", "transfer.action");
|
||||||
|
let payload = JSON.stringify({
|
||||||
|
string1: groupId,
|
||||||
|
string2: memberId
|
||||||
|
});
|
||||||
|
|
||||||
|
let res = await fetchEncrypted("group/transferowner", payload);
|
||||||
|
clearAction("transfer.action");
|
||||||
|
|
||||||
|
if (res && res.startsWith("success")) {
|
||||||
|
window.currentGroupOwner = memberId;
|
||||||
|
showBlahNotification("success:owner.transferred");
|
||||||
|
showGroupMembers(groupId).then(html => {
|
||||||
|
let container = document.getElementById("right-sidebar-container");
|
||||||
|
if (container) {
|
||||||
|
container.innerHTML = html;
|
||||||
|
translate(container);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
showBlahNotification(res || "error:something.wrong");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function handleGroupContextMenu(e, groupId) {
|
function handleGroupContextMenu(e, groupId) {
|
||||||
if (e) {
|
if (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
@ -5020,6 +5184,10 @@ function handleGroupContextMenu(e, groupId) {
|
||||||
}
|
}
|
||||||
if (typeof clearContextMenuStyles === "function") clearContextMenuStyles();
|
if (typeof clearContextMenuStyles === "function") clearContextMenuStyles();
|
||||||
currentContextMenuGroupId = groupId;
|
currentContextMenuGroupId = groupId;
|
||||||
|
if (e && e.type === "contextmenu") {
|
||||||
|
touchContextMenuFired = true;
|
||||||
|
setTimeout(() => { touchContextMenuFired = false; }, 500);
|
||||||
|
}
|
||||||
|
|
||||||
let btn = e ? (e.currentTarget || e.target) : null;
|
let btn = e ? (e.currentTarget || e.target) : null;
|
||||||
let rect;
|
let rect;
|
||||||
|
|
@ -5062,6 +5230,8 @@ async function gotoGroupSettings(groupId) {
|
||||||
let infoStr = await fetchEncrypted("group/info", groupId);
|
let infoStr = await fetchEncrypted("group/info", groupId);
|
||||||
if (!infoStr.startsWith("error:")) {
|
if (!infoStr.startsWith("error:")) {
|
||||||
let info = JSON.parse(infoStr);
|
let info = JSON.parse(infoStr);
|
||||||
|
window.currentGroupOwner = info.string7;
|
||||||
|
|
||||||
await switchRoomContent("title.group.settings", groupSettingsScreen, false);
|
await switchRoomContent("title.group.settings", groupSettingsScreen, false);
|
||||||
|
|
||||||
document.getElementById('group-settings-name').value = info.string2;
|
document.getElementById('group-settings-name').value = info.string2;
|
||||||
|
|
@ -5070,6 +5240,19 @@ async function gotoGroupSettings(groupId) {
|
||||||
document.getElementById('group-settings-avatar-preview').src = pfpUrl;
|
document.getElementById('group-settings-avatar-preview').src = pfpUrl;
|
||||||
|
|
||||||
document.getElementById('group-settings-history').checked = (info.string6 === "true");
|
document.getElementById('group-settings-history').checked = (info.string6 === "true");
|
||||||
|
|
||||||
|
let myIdStr = window.id + ";" + window.host;
|
||||||
|
let historyCheckbox = document.getElementById('group-settings-history');
|
||||||
|
let historyContainer = historyCheckbox.closest('.setting-row');
|
||||||
|
if (historyContainer) {
|
||||||
|
if (window.currentGroupOwner !== myIdStr) {
|
||||||
|
historyContainer.style.display = 'none';
|
||||||
|
} else {
|
||||||
|
historyContainer.style.display = 'flex';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
historyCheckbox.disabled = (window.currentGroupOwner !== myIdStr);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
showBlahNotification(infoStr);
|
showBlahNotification(infoStr);
|
||||||
}
|
}
|
||||||
|
|
@ -5120,8 +5303,14 @@ async function confirmLeaveGroup(groupId) {
|
||||||
let res = await fetchEncrypted("group/leave", payload);
|
let res = await fetchEncrypted("group/leave", payload);
|
||||||
if (res === "success") {
|
if (res === "success") {
|
||||||
if (currentDmId === groupId) {
|
if (currentDmId === groupId) {
|
||||||
|
currentDmId = null;
|
||||||
|
const rem = parseFloat(getComputedStyle(document.documentElement).fontSize);
|
||||||
|
if (window.innerWidth <= 55 * rem) {
|
||||||
|
mobileNavBack();
|
||||||
|
} else {
|
||||||
gotoHome();
|
gotoHome();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
refreshGroups(0, false);
|
refreshGroups(0, false);
|
||||||
} else if (res.startsWith("error:")) {
|
} else if (res.startsWith("error:")) {
|
||||||
showBlahNotification(res);
|
showBlahNotification(res);
|
||||||
|
|
|
||||||
|
|
@ -267,5 +267,28 @@
|
||||||
"label.keys.to.encrypt": "keys to encrypt, meow",
|
"label.keys.to.encrypt": "keys to encrypt, meow",
|
||||||
"action.group.creating": "Creating group, meow...",
|
"action.group.creating": "Creating group, meow...",
|
||||||
"title.group.settings": "Group settings, meow",
|
"title.group.settings": "Group settings, meow",
|
||||||
"title.leave.group": "Leave group, meow"
|
"title.leave.group": "Leave group, meow",
|
||||||
|
"role.member": "Meowber",
|
||||||
|
"role.group_owner": "Big purr",
|
||||||
|
"role.Owner": "Big purr",
|
||||||
|
"action.kick": "Hiss and scratch from group",
|
||||||
|
"action.transfer_owner": "Give shiny toy",
|
||||||
|
"group.user.kicked.by": "{0} hissed at {1} and they ran away",
|
||||||
|
"group.owner.transferred.by": "{0} gave the shiny toy to {1}",
|
||||||
|
"group.user.kicked": "Kitty kicked",
|
||||||
|
"group.owner.transferred": "Shiny toy given",
|
||||||
|
"success:kicked": "Hissed away successfully",
|
||||||
|
"success:owner.transferred": "Toy transferred",
|
||||||
|
"error:cannot.kick.self": "You can't scratch yourself",
|
||||||
|
"action.saving": "Meowving...",
|
||||||
|
"desc.no.chat.selected": "No purr selected. Select a purr from the sidebar to start meowing.",
|
||||||
|
"failed.to.prepare.keys": "Failed to prepare meows",
|
||||||
|
"group.name.empty": "Purr name cannot be empty",
|
||||||
|
"group.refresh.failed": "Failed to refresh purr",
|
||||||
|
"group.settings.saved": "Purr settings saved successfully",
|
||||||
|
"kicked": "Bited successfully",
|
||||||
|
"message.delete.failed": "Failed to delete meow",
|
||||||
|
"message.react.failed": "Failed to react to meow",
|
||||||
|
"message.send.failed": "Failed to send meow",
|
||||||
|
"owner.transferred": "Purrship transferred successfully"
|
||||||
}
|
}
|
||||||
|
|
@ -249,7 +249,7 @@
|
||||||
"group.user.joined": "Joined group",
|
"group.user.joined": "Joined group",
|
||||||
"prompt.invite.username": "Enter username to invite:",
|
"prompt.invite.username": "Enter username to invite:",
|
||||||
"desc.failed.load.members": "Failed to load members",
|
"desc.failed.load.members": "Failed to load members",
|
||||||
"title.group.settings": "Group Settings",
|
"title.group.settings": "Group settings",
|
||||||
"title.group.name": "Group Name",
|
"title.group.name": "Group Name",
|
||||||
"title.group.history.visible": "History visible to newcomers",
|
"title.group.history.visible": "History visible to newcomers",
|
||||||
"desc.group.history.visible": "New group members will be able to see older messages (disables key rotation)",
|
"desc.group.history.visible": "New group members will be able to see older messages (disables key rotation)",
|
||||||
|
|
@ -273,6 +273,28 @@
|
||||||
"desc.key.rotation.you": "You will generate a new encryption key for this group.",
|
"desc.key.rotation.you": "You will generate a new encryption key for this group.",
|
||||||
"label.keys.to.encrypt": "keys to encrypt",
|
"label.keys.to.encrypt": "keys to encrypt",
|
||||||
"action.group.creating": "Creating group...",
|
"action.group.creating": "Creating group...",
|
||||||
"title.group.settings": "Group settings",
|
"title.leave.group": "Leave group",
|
||||||
"title.leave.group": "Leave group"
|
"role.member": "Member",
|
||||||
|
"role.group_owner": "Group owner",
|
||||||
|
"role.Owner": "Owner",
|
||||||
|
"action.kick": "Kick from group",
|
||||||
|
"action.transfer_owner": "Transfer ownership",
|
||||||
|
"group.user.kicked.by": "{0} kicked {1} from the group",
|
||||||
|
"group.owner.transferred.by": "{0} transferred ownership to {1}",
|
||||||
|
"group.user.kicked": "User kicked",
|
||||||
|
"group.owner.transferred": "Ownership transferred",
|
||||||
|
"success:kicked": "User kicked successfully",
|
||||||
|
"success:owner.transferred": "Ownership transferred",
|
||||||
|
"error:cannot.kick.self": "You cannot kick yourself",
|
||||||
|
"action.saving": "Saving...",
|
||||||
|
"desc.no.chat.selected": "No chat selected. Select a chat from the sidebar to start messaging.",
|
||||||
|
"failed.to.prepare.keys": "Failed to prepare keys",
|
||||||
|
"group.name.empty": "Group name cannot be empty",
|
||||||
|
"group.refresh.failed": "Failed to refresh group",
|
||||||
|
"group.settings.saved": "Group settings saved successfully",
|
||||||
|
"kicked": "Kicked successfully",
|
||||||
|
"message.delete.failed": "Failed to delete message",
|
||||||
|
"message.react.failed": "Failed to react to message",
|
||||||
|
"message.send.failed": "Failed to send message",
|
||||||
|
"owner.transferred": "Ownership transferred successfully"
|
||||||
}
|
}
|
||||||
|
|
@ -273,6 +273,28 @@
|
||||||
"desc.key.rotation.you": "Wygenerujesz nowy klucz szyfrujący dla tej grupy.",
|
"desc.key.rotation.you": "Wygenerujesz nowy klucz szyfrujący dla tej grupy.",
|
||||||
"label.keys.to.encrypt": "kluczy do zaszyfrowania",
|
"label.keys.to.encrypt": "kluczy do zaszyfrowania",
|
||||||
"action.group.creating": "Tworzenie grupy...",
|
"action.group.creating": "Tworzenie grupy...",
|
||||||
"title.group.settings": "Ustawienia grupy",
|
"title.leave.group": "Opuść grupę",
|
||||||
"title.leave.group": "Opuść grupę"
|
"role.member": "Członek",
|
||||||
|
"role.group_owner": "Właściciel grupy",
|
||||||
|
"role.Owner": "Właściciel",
|
||||||
|
"action.kick": "Wyrzuć z grupy",
|
||||||
|
"action.transfer_owner": "Przekaż właściciela",
|
||||||
|
"group.user.kicked.by": "{0} wyrzucił/a {1} z grupy",
|
||||||
|
"group.owner.transferred.by": "{0} przekazał/a właściciela dla {1}",
|
||||||
|
"group.user.kicked": "Użytkownik wyrzucony",
|
||||||
|
"group.owner.transferred": "Właściciel przekazany",
|
||||||
|
"success:kicked": "Użytkownik wyrzucony pomyślnie",
|
||||||
|
"success:owner.transferred": "Właściciel przekazany pomyślnie",
|
||||||
|
"error:cannot.kick.self": "Nie możesz wyrzucić samego siebie",
|
||||||
|
"action.saving": "Zapisywanie...",
|
||||||
|
"desc.no.chat.selected": "Nie wybrano czatu. Wybierz czat z paska bocznego, aby rozpocząć pisanie.",
|
||||||
|
"failed.to.prepare.keys": "Nie udało się przygotować kluczy",
|
||||||
|
"group.name.empty": "Nazwa grupy nie może być pusta",
|
||||||
|
"group.refresh.failed": "Nie udało się odświeżyć grupy",
|
||||||
|
"group.settings.saved": "Ustawienia grupy zapisane pomyślnie",
|
||||||
|
"kicked": "Wyrzucono pomyślnie",
|
||||||
|
"message.delete.failed": "Nie udało się usunąć wiadomości",
|
||||||
|
"message.react.failed": "Nie udało się dodać reakcji",
|
||||||
|
"message.send.failed": "Nie udało się wysłać wiadomości",
|
||||||
|
"owner.transferred": "Własność przekazana pomyślnie"
|
||||||
}
|
}
|
||||||
201
webroot/main.js
201
webroot/main.js
|
|
@ -1563,13 +1563,19 @@ async function switchRoomContent(title, content, showRoomBar, icon = "", skipMob
|
||||||
roomsTopBarTransition.style.opacity = "0";
|
roomsTopBarTransition.style.opacity = "0";
|
||||||
|
|
||||||
const rem = parseFloat(getComputedStyle(document.documentElement).fontSize);
|
const rem = parseFloat(getComputedStyle(document.documentElement).fontSize);
|
||||||
|
let slideIn = false;
|
||||||
if (window.innerWidth <= 55 * rem && !skipMobileSlide) {
|
if (window.innerWidth <= 55 * rem && !skipMobileSlide) {
|
||||||
if (title != "title.splash") //do not show splash on mobile
|
if (title != "title.splash") //do not show splash on mobile
|
||||||
{
|
{
|
||||||
|
if (!mainScreen.classList.contains('mobile-content') && !mainScreen.classList.contains('mobile-details')) {
|
||||||
|
slideIn = true;
|
||||||
|
}
|
||||||
mainScreen.classList.remove('mobile-details');
|
mainScreen.classList.remove('mobile-details');
|
||||||
mainScreen.classList.add('mobile-content');
|
mainScreen.classList.add('mobile-content');
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
if (!slideIn) {
|
||||||
await delay(200);
|
await delay(200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1762,6 +1768,7 @@ function clearContextMenuStyles() {
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener("click", (e) => {
|
document.addEventListener("click", (e) => {
|
||||||
|
if (touchContextMenuFired) return;
|
||||||
history.pushState({trap: true}, "", location.href);
|
history.pushState({trap: true}, "", location.href);
|
||||||
|
|
||||||
if (fixedContextMenu.classList.contains("show")) {
|
if (fixedContextMenu.classList.contains("show")) {
|
||||||
|
|
@ -1778,10 +1785,14 @@ document.addEventListener("click", (e) => {
|
||||||
const rem = parseFloat(getComputedStyle(document.documentElement).fontSize);
|
const rem = parseFloat(getComputedStyle(document.documentElement).fontSize);
|
||||||
if (window.innerWidth <= 55 * rem) {
|
if (window.innerWidth <= 55 * rem) {
|
||||||
if (!roomDetailsBar.contains(e.target) && !e.target.closest('#room-topbar-clickable') && !e.target.closest('.profile-popup-container') && !e.target.closest('.room-action-pin')) {
|
if (!roomDetailsBar.contains(e.target) && !e.target.closest('#room-topbar-clickable') && !e.target.closest('.profile-popup-container') && !e.target.closest('.room-action-pin')) {
|
||||||
|
if (currentRoomContentTitle === "title.group.settings") {
|
||||||
|
openDm(window.currentSettingsGroupId, document.getElementById('group-settings-name').value, window.currentSettingsGroupId, 'groups');
|
||||||
|
} else {
|
||||||
mobileNavBack();
|
mobileNavBack();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
window.addEventListener("popstate", (e) => {
|
window.addEventListener("popstate", (e) => {
|
||||||
popstate();
|
popstate();
|
||||||
|
|
@ -1848,6 +1859,7 @@ function logout() {
|
||||||
let isProfileOpen = false;
|
let isProfileOpen = false;
|
||||||
|
|
||||||
async function openProfile(accountId) {
|
async function openProfile(accountId) {
|
||||||
|
if (touchContextMenuFired) return;
|
||||||
if (fixedContextMenu.classList.contains("show")) {
|
if (fixedContextMenu.classList.contains("show")) {
|
||||||
fixedContextMenu.classList.remove("show");
|
fixedContextMenu.classList.remove("show");
|
||||||
if (typeof clearContextMenuStyles === "function") clearContextMenuStyles();
|
if (typeof clearContextMenuStyles === "function") clearContextMenuStyles();
|
||||||
|
|
@ -1969,6 +1981,19 @@ async function openProfile(accountId) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let roleHtml = "";
|
||||||
|
if (window.currentDmId && window.currentRoomType && (window.currentRoomType === "groups" || window.currentRoomType === "dms" || window.currentRoomType === "spaces")) {
|
||||||
|
let payload = JSON.stringify({
|
||||||
|
string1: window.currentRoomType,
|
||||||
|
string2: window.currentDmId,
|
||||||
|
string3: accountId
|
||||||
|
});
|
||||||
|
let roleRes = await fetchEncrypted("room/role", payload);
|
||||||
|
if (roleRes && !roleRes.startsWith("error:")) {
|
||||||
|
let mappedRole = "role." + roleRes.replace(" ", "_");
|
||||||
|
roleHtml = `<div class="profile-role" style="opacity:0.7; font-size:0.9rem; margin-top:0.3rem;"><blah>${mappedRole}</blah></div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
popupInner.innerHTML = `
|
popupInner.innerHTML = `
|
||||||
${bannerHtml}
|
${bannerHtml}
|
||||||
|
|
@ -1979,6 +2004,7 @@ async function openProfile(accountId) {
|
||||||
<div>
|
<div>
|
||||||
<div class="profile-name">${profileName}</div>
|
<div class="profile-name">${profileName}</div>
|
||||||
<div class="profile-id-host">${profileName}:${profileHost} (${accountId.split(':')[0].split(';')[0]})</div>
|
<div class="profile-id-host">${profileName}:${profileHost} (${accountId.split(':')[0].split(';')[0]})</div>
|
||||||
|
${roleHtml}
|
||||||
</div>
|
</div>
|
||||||
${bioHtml}
|
${bioHtml}
|
||||||
${actionBtnHtml}
|
${actionBtnHtml}
|
||||||
|
|
@ -2423,6 +2449,11 @@ function setActiveSidebarIndicator(element, isTemporary = false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function mobileNavBack() {
|
function mobileNavBack() {
|
||||||
|
if (currentRoomContentTitle === "title.group.settings") {
|
||||||
|
openDm(window.currentSettingsGroupId, document.getElementById('group-settings-name').value, window.currentSettingsGroupId, 'groups');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (mainScreen.classList.contains('mobile-details')) {
|
if (mainScreen.classList.contains('mobile-details')) {
|
||||||
mainScreen.classList.remove('mobile-details');
|
mainScreen.classList.remove('mobile-details');
|
||||||
mainScreen.classList.add('mobile-content');
|
mainScreen.classList.add('mobile-content');
|
||||||
|
|
@ -2512,6 +2543,7 @@ document.addEventListener('touchend', async (e) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
let elapsed = Date.now() - pressDate; //najgorszy fix swiata
|
let elapsed = Date.now() - pressDate; //najgorszy fix swiata
|
||||||
|
|
||||||
if (elapsed < 100) {
|
if (elapsed < 100) {
|
||||||
const remaining = 100 - elapsed;
|
const remaining = 100 - elapsed;
|
||||||
await delay(remaining);
|
await delay(remaining);
|
||||||
|
|
@ -2861,7 +2893,7 @@ async function generateGroupMembersHtml(groupId, groupName) {
|
||||||
let memberDisplayName = memberUsername.split(':')[0];
|
let memberDisplayName = memberUsername.split(':')[0];
|
||||||
|
|
||||||
html += `
|
html += `
|
||||||
<button class="room-entry" onclick="openProfile('${memberId}')" style="margin-bottom: 0;">
|
<button class="room-entry" onclick="openProfile('${memberId}')" oncontextmenu="handleMemberContextMenu(event, '${groupId}', '${memberId}')" style="margin-bottom: 0;">
|
||||||
<img src="${memberPfp}" class="room-pfp">
|
<img src="${memberPfp}" class="room-pfp">
|
||||||
<div style="display: flex; flex-direction: column; text-align: left; overflow: hidden; width: 100%; margin-left: 0.37rem;">
|
<div style="display: flex; flex-direction: column; text-align: left; overflow: hidden; width: 100%; margin-left: 0.37rem;">
|
||||||
<span class="room-name">${memberDisplayName}</span>
|
<span class="room-name">${memberDisplayName}</span>
|
||||||
|
|
@ -2875,6 +2907,9 @@ async function generateGroupMembersHtml(groupId, groupName) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function openDm(dmId, username, targetId, type = 'dms') {
|
async function openDm(dmId, username, targetId, type = 'dms') {
|
||||||
|
if (touchContextMenuFired) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
showAction("action.dm.opening", "dmopen");
|
showAction("action.dm.opening", "dmopen");
|
||||||
|
|
||||||
|
|
@ -2894,8 +2929,10 @@ async function openDm(dmId, username, targetId, type = 'dms') {
|
||||||
if (infoPayload && !infoPayload.startsWith("error:")) {
|
if (infoPayload && !infoPayload.startsWith("error:")) {
|
||||||
let info = JSON.parse(infoPayload);
|
let info = JSON.parse(infoPayload);
|
||||||
window.currentGroupKeyIndex = info.string4 || "0";
|
window.currentGroupKeyIndex = info.string4 || "0";
|
||||||
|
window.currentGroupOwner = info.string7;
|
||||||
} else {
|
} else {
|
||||||
window.currentGroupKeyIndex = "0";
|
window.currentGroupKeyIndex = "0";
|
||||||
|
window.currentGroupOwner = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2972,6 +3009,16 @@ async function loadDmMessages(dmId, startOffset = "", isPrepend = false) {
|
||||||
let res = await fetchEncrypted(endpoint, payload);
|
let res = await fetchEncrypted(endpoint, payload);
|
||||||
|
|
||||||
if (res && res.startsWith("error:")) {
|
if (res && res.startsWith("error:")) {
|
||||||
|
if (res === "error:forbidden" || res === "error:not.a.member" || res === "error:group.not.found") {
|
||||||
|
if (dmMessagePollInterval) clearInterval(dmMessagePollInterval);
|
||||||
|
currentDmId = null;
|
||||||
|
showBlahNotification(res);
|
||||||
|
if (mainScreen.classList.contains('mobile-details')) {
|
||||||
|
mainScreen.classList.remove('mobile-details');
|
||||||
|
}
|
||||||
|
switchDetailsContent("title.details", `<div style="text-align: center; opacity: 0.5; padding: 1rem; margin-top: 2rem;"><blah>desc.no.chat.selected</blah></div>`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
showBlahNotification(res);
|
showBlahNotification(res);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -3070,7 +3117,7 @@ async function generateMessagesHtml(entries, isPinnedContext = false) {
|
||||||
try {
|
try {
|
||||||
let atts = JSON.parse(msg.attachment);
|
let atts = JSON.parse(msg.attachment);
|
||||||
if (atts.length > 0) {
|
if (atts.length > 0) {
|
||||||
attachmentsHtml += `<div class="chat-message-attachments" style="display: flex; gap: 0.5rem; flex-wrap: wrap; margin-top: 0.5rem;">`;
|
attachmentsHtml += `<div class="chat-message-attachments" data-raw="${escapeHtml(msg.attachment)}" style="display: flex; gap: 0.5rem; flex-wrap: wrap; margin-top: 0.5rem;">`;
|
||||||
for (let att of atts) {
|
for (let att of atts) {
|
||||||
if (att.enc && att.enc.startsWith("ENC:")) {
|
if (att.enc && att.enc.startsWith("ENC:")) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -3116,7 +3163,7 @@ async function generateMessagesHtml(entries, isPinnedContext = false) {
|
||||||
console.error("Failed to parse attachments", e);
|
console.error("Failed to parse attachments", e);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
attachmentsHtml += `<div class="chat-message-attachments" style="display: flex; gap: 0.5rem; flex-wrap: wrap; margin-top: 0.5rem;">
|
attachmentsHtml += `<div class="chat-message-attachments" data-raw="${escapeHtml(msg.attachment)}" style="display: flex; gap: 0.5rem; flex-wrap: wrap; margin-top: 0.5rem;">
|
||||||
<div style="padding: 0.5rem; background: rgba(0,0,0,0.2); border: var(--border-width) solid var(--border-color); border-radius: 0.5rem; font-size: 0.8rem; opacity: 0.7;">[${processBlah("attachment.legacy")}: ${escapeHtml(msg.attachment)}]</div>
|
<div style="padding: 0.5rem; background: rgba(0,0,0,0.2); border: var(--border-width) solid var(--border-color); border-radius: 0.5rem; font-size: 0.8rem; opacity: 0.7;">[${processBlah("attachment.legacy")}: ${escapeHtml(msg.attachment)}]</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
@ -3133,7 +3180,7 @@ async function generateMessagesHtml(entries, isPinnedContext = false) {
|
||||||
content = processBlah(content);
|
content = processBlah(content);
|
||||||
|
|
||||||
if (msg.type && msg.type.startsWith("larp.info")) {
|
if (msg.type && msg.type.startsWith("larp.info")) {
|
||||||
if (msg.type === "larp.info.pin" || msg.type === "larp.info.unpin" || msg.type === "larp.info.join" || msg.type === "larp.info.leave" || msg.type === "larp.info.create" || msg.type === "larp.info.history") {
|
if (msg.type === "larp.info.pin" || msg.type === "larp.info.unpin" || msg.type === "larp.info.join" || msg.type === "larp.info.leave" || msg.type === "larp.info.create" || msg.type === "larp.info.history" || msg.type === "larp.info.kick" || msg.type === "larp.info.transfer_owner") {
|
||||||
let authorName = processBlah("title.unknown");
|
let authorName = processBlah("title.unknown");
|
||||||
if (msg.author && msg.author !== "0") {
|
if (msg.author && msg.author !== "0") {
|
||||||
if (!dmUsernameCache[msg.author]) {
|
if (!dmUsernameCache[msg.author]) {
|
||||||
|
|
@ -3157,6 +3204,8 @@ async function generateMessagesHtml(entries, isPinnedContext = false) {
|
||||||
else if (msg.type === "larp.info.leave") actionStr = "group.user.left.by";
|
else if (msg.type === "larp.info.leave") actionStr = "group.user.left.by";
|
||||||
else if (msg.type === "larp.info.create") actionStr = "group.created.by";
|
else if (msg.type === "larp.info.create") actionStr = "group.created.by";
|
||||||
else if (msg.type === "larp.info.history") actionStr = "group.history.changed.by";
|
else if (msg.type === "larp.info.history") actionStr = "group.history.changed.by";
|
||||||
|
else if (msg.type === "larp.info.kick") actionStr = "group.user.kicked.by";
|
||||||
|
else if (msg.type === "larp.info.transfer_owner") actionStr = "group.owner.transferred.by";
|
||||||
|
|
||||||
let clickableAuthor = `<span onclick="event.stopPropagation(); openProfile('${msg.author}')" style="cursor: pointer; font-weight: bold;">${authorName}</span>`;
|
let clickableAuthor = `<span onclick="event.stopPropagation(); openProfile('${msg.author}')" style="cursor: pointer; font-weight: bold;">${authorName}</span>`;
|
||||||
|
|
||||||
|
|
@ -3166,6 +3215,18 @@ async function generateMessagesHtml(entries, isPinnedContext = false) {
|
||||||
content = textStr.replace("{0}", clickableAuthor).replace("{1}", clickableMsg);
|
content = textStr.replace("{0}", clickableAuthor).replace("{1}", clickableMsg);
|
||||||
} else if (msg.type === "larp.info.history") {
|
} else if (msg.type === "larp.info.history") {
|
||||||
content = textStr.replace("{0}", clickableAuthor).replace("{1}", content);
|
content = textStr.replace("{0}", clickableAuthor).replace("{1}", content);
|
||||||
|
} else if (msg.type === "larp.info.kick" || msg.type === "larp.info.transfer_owner") {
|
||||||
|
let targetUsername = "";
|
||||||
|
let targetId = "";
|
||||||
|
let parts = content.split(':');
|
||||||
|
if (parts.length >= 2 && parts[0].includes(';')) {
|
||||||
|
targetId = parts[0];
|
||||||
|
targetUsername = escapeHtml(parts.slice(1).join(':'));
|
||||||
|
} else {
|
||||||
|
targetUsername = escapeHtml(parts[0]);
|
||||||
|
}
|
||||||
|
let clickableTarget = targetId ? `<span onclick="event.stopPropagation(); openProfile('${targetId}')" style="font-weight: bold; cursor: pointer;">${targetUsername}</span>` : `<span style="font-weight: bold;">${targetUsername}</span>`;
|
||||||
|
content = textStr.replace("{0}", clickableAuthor).replace("{1}", clickableTarget);
|
||||||
} else {
|
} else {
|
||||||
content = textStr.replace("{0}", clickableAuthor);
|
content = textStr.replace("{0}", clickableAuthor);
|
||||||
}
|
}
|
||||||
|
|
@ -3424,6 +3485,20 @@ async function renderMessages(messages, isPrepend = false) {
|
||||||
oldReactions.remove();
|
oldReactions.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let oldAttachments = existingChild.querySelector('.chat-message-attachments');
|
||||||
|
let newAttachments = newChild.querySelector('.chat-message-attachments');
|
||||||
|
if (newAttachments) {
|
||||||
|
if (oldAttachments) {
|
||||||
|
if (oldAttachments.getAttribute('data-raw') !== newAttachments.getAttribute('data-raw')) oldAttachments.innerHTML = newAttachments.innerHTML;
|
||||||
|
} else {
|
||||||
|
let contentNode = existingChild.querySelector('.chat-message-content');
|
||||||
|
if (oldReactions) contentNode.insertBefore(newAttachments, oldReactions);
|
||||||
|
else contentNode.appendChild(newAttachments);
|
||||||
|
}
|
||||||
|
} else if (oldAttachments) {
|
||||||
|
oldAttachments.remove();
|
||||||
|
}
|
||||||
|
|
||||||
let oldPfp = existingChild.querySelector('.chat-message-pfp');
|
let oldPfp = existingChild.querySelector('.chat-message-pfp');
|
||||||
let newPfp = newChild.querySelector('.chat-message-pfp');
|
let newPfp = newChild.querySelector('.chat-message-pfp');
|
||||||
if (oldPfp && newPfp && oldPfp.src !== newPfp.src) {
|
if (oldPfp && newPfp && oldPfp.src !== newPfp.src) {
|
||||||
|
|
@ -3920,7 +3995,6 @@ let touchContextMenuFired = false;
|
||||||
function handleMessageContextMenu(e, msgId) {
|
function handleMessageContextMenu(e, msgId) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (touchContextMenuFired) {
|
if (touchContextMenuFired) {
|
||||||
touchContextMenuFired = false;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (typeof clearContextMenuStyles === "function") clearContextMenuStyles();
|
if (typeof clearContextMenuStyles === "function") clearContextMenuStyles();
|
||||||
|
|
@ -5013,6 +5087,96 @@ if (typeof roomTopBar !== 'undefined' && roomTopBar) {
|
||||||
|
|
||||||
var currentContextMenuGroupId = null;
|
var currentContextMenuGroupId = null;
|
||||||
|
|
||||||
|
function handleMemberContextMenu(e, groupId, memberId) {
|
||||||
|
if (window.currentRoomType !== 'groups') return;
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
if (e && e.type === "contextmenu") {
|
||||||
|
touchContextMenuFired = true;
|
||||||
|
setTimeout(() => { touchContextMenuFired = false; }, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
let html = '';
|
||||||
|
|
||||||
|
let myIdStr = window.id + ";" + window.host;
|
||||||
|
if (window.currentGroupOwner === myIdStr && memberId !== myIdStr) {
|
||||||
|
html += `
|
||||||
|
<button onclick="kickFromGroup('${groupId}', '${memberId}')">
|
||||||
|
${ICONS.delete({ fill: "var(--big-red)" })}
|
||||||
|
<span class="blah" style="color: var(--big-red)">action.kick</span>
|
||||||
|
</button>
|
||||||
|
<button onclick="transferGroupOwnership('${groupId}', '${memberId}')">
|
||||||
|
${ICONS.profile()}
|
||||||
|
<span class="blah">action.transfer_owner</span>
|
||||||
|
</button>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (html !== '') {
|
||||||
|
let clientX = e.clientX !== undefined ? e.clientX : (e.touches && e.touches.length > 0 ? e.touches[0].clientX : 0);
|
||||||
|
let clientY = e.clientY !== undefined ? e.clientY : (e.touches && e.touches.length > 0 ? e.touches[0].clientY : 0);
|
||||||
|
showFixedContextMenu({top: clientY, right: clientX, bottom: clientY, left: clientX}, html);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function kickFromGroup(groupId, memberId) {
|
||||||
|
if (fixedContextMenu.classList.contains("show")) {
|
||||||
|
fixedContextMenu.classList.remove("show");
|
||||||
|
}
|
||||||
|
|
||||||
|
showAction("action.processing", "kick.action");
|
||||||
|
let payload = JSON.stringify({
|
||||||
|
string1: groupId,
|
||||||
|
string2: memberId
|
||||||
|
});
|
||||||
|
|
||||||
|
let res = await fetchEncrypted("group/kick", payload);
|
||||||
|
clearAction("kick.action");
|
||||||
|
|
||||||
|
if (res && res.startsWith("success")) {
|
||||||
|
showBlahNotification("success:kicked");
|
||||||
|
showGroupMembers(groupId).then(html => {
|
||||||
|
let container = document.getElementById("right-sidebar-container");
|
||||||
|
if (container) {
|
||||||
|
container.innerHTML = html;
|
||||||
|
translate(container);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
showBlahNotification(res || "error:something.wrong");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function transferGroupOwnership(groupId, memberId) {
|
||||||
|
if (fixedContextMenu.classList.contains("show")) {
|
||||||
|
fixedContextMenu.classList.remove("show");
|
||||||
|
}
|
||||||
|
|
||||||
|
showAction("action.processing", "transfer.action");
|
||||||
|
let payload = JSON.stringify({
|
||||||
|
string1: groupId,
|
||||||
|
string2: memberId
|
||||||
|
});
|
||||||
|
|
||||||
|
let res = await fetchEncrypted("group/transferowner", payload);
|
||||||
|
clearAction("transfer.action");
|
||||||
|
|
||||||
|
if (res && res.startsWith("success")) {
|
||||||
|
window.currentGroupOwner = memberId;
|
||||||
|
showBlahNotification("success:owner.transferred");
|
||||||
|
showGroupMembers(groupId).then(html => {
|
||||||
|
let container = document.getElementById("right-sidebar-container");
|
||||||
|
if (container) {
|
||||||
|
container.innerHTML = html;
|
||||||
|
translate(container);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
showBlahNotification(res || "error:something.wrong");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function handleGroupContextMenu(e, groupId) {
|
function handleGroupContextMenu(e, groupId) {
|
||||||
if (e) {
|
if (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
@ -5020,6 +5184,10 @@ function handleGroupContextMenu(e, groupId) {
|
||||||
}
|
}
|
||||||
if (typeof clearContextMenuStyles === "function") clearContextMenuStyles();
|
if (typeof clearContextMenuStyles === "function") clearContextMenuStyles();
|
||||||
currentContextMenuGroupId = groupId;
|
currentContextMenuGroupId = groupId;
|
||||||
|
if (e && e.type === "contextmenu") {
|
||||||
|
touchContextMenuFired = true;
|
||||||
|
setTimeout(() => { touchContextMenuFired = false; }, 500);
|
||||||
|
}
|
||||||
|
|
||||||
let btn = e ? (e.currentTarget || e.target) : null;
|
let btn = e ? (e.currentTarget || e.target) : null;
|
||||||
let rect;
|
let rect;
|
||||||
|
|
@ -5062,6 +5230,8 @@ async function gotoGroupSettings(groupId) {
|
||||||
let infoStr = await fetchEncrypted("group/info", groupId);
|
let infoStr = await fetchEncrypted("group/info", groupId);
|
||||||
if (!infoStr.startsWith("error:")) {
|
if (!infoStr.startsWith("error:")) {
|
||||||
let info = JSON.parse(infoStr);
|
let info = JSON.parse(infoStr);
|
||||||
|
window.currentGroupOwner = info.string7;
|
||||||
|
|
||||||
await switchRoomContent("title.group.settings", groupSettingsScreen, false);
|
await switchRoomContent("title.group.settings", groupSettingsScreen, false);
|
||||||
|
|
||||||
document.getElementById('group-settings-name').value = info.string2;
|
document.getElementById('group-settings-name').value = info.string2;
|
||||||
|
|
@ -5070,6 +5240,19 @@ async function gotoGroupSettings(groupId) {
|
||||||
document.getElementById('group-settings-avatar-preview').src = pfpUrl;
|
document.getElementById('group-settings-avatar-preview').src = pfpUrl;
|
||||||
|
|
||||||
document.getElementById('group-settings-history').checked = (info.string6 === "true");
|
document.getElementById('group-settings-history').checked = (info.string6 === "true");
|
||||||
|
|
||||||
|
let myIdStr = window.id + ";" + window.host;
|
||||||
|
let historyCheckbox = document.getElementById('group-settings-history');
|
||||||
|
let historyContainer = historyCheckbox.closest('.setting-row');
|
||||||
|
if (historyContainer) {
|
||||||
|
if (window.currentGroupOwner !== myIdStr) {
|
||||||
|
historyContainer.style.display = 'none';
|
||||||
|
} else {
|
||||||
|
historyContainer.style.display = 'flex';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
historyCheckbox.disabled = (window.currentGroupOwner !== myIdStr);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
showBlahNotification(infoStr);
|
showBlahNotification(infoStr);
|
||||||
}
|
}
|
||||||
|
|
@ -5120,8 +5303,14 @@ async function confirmLeaveGroup(groupId) {
|
||||||
let res = await fetchEncrypted("group/leave", payload);
|
let res = await fetchEncrypted("group/leave", payload);
|
||||||
if (res === "success") {
|
if (res === "success") {
|
||||||
if (currentDmId === groupId) {
|
if (currentDmId === groupId) {
|
||||||
|
currentDmId = null;
|
||||||
|
const rem = parseFloat(getComputedStyle(document.documentElement).fontSize);
|
||||||
|
if (window.innerWidth <= 55 * rem) {
|
||||||
|
mobileNavBack();
|
||||||
|
} else {
|
||||||
gotoHome();
|
gotoHome();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
refreshGroups(0, false);
|
refreshGroups(0, false);
|
||||||
} else if (res.startsWith("error:")) {
|
} else if (res.startsWith("error:")) {
|
||||||
showBlahNotification(res);
|
showBlahNotification(res);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue