Compare commits
No commits in common. "340c16b0803dd97698a93179b5cba5f82db6dd09" and "9968e7fd205811befc768f41ff0f07f5cccb2503" have entirely different histories.
340c16b080
...
9968e7fd20
12 changed files with 82 additions and 466 deletions
|
|
@ -194,12 +194,5 @@
|
||||||
"placeholder.type.text.emoji": "Type purr / meow...",
|
"placeholder.type.text.emoji": "Type purr / meow...",
|
||||||
"action.cancel": "Cancel",
|
"action.cancel": "Cancel",
|
||||||
"title.crop.image": "Crop Image",
|
"title.crop.image": "Crop Image",
|
||||||
"title.zoom": "Zoom",
|
"title.zoom": "Zoom"
|
||||||
"title.pin.message": "Pin",
|
|
||||||
"title.unpin.message": "Unpin",
|
|
||||||
"message.pinned.by": "{0} pinned a {1}.",
|
|
||||||
"message.unpinned.by": "{0} unpinned a {1}.",
|
|
||||||
"title.pinned.messages": "Pinned",
|
|
||||||
"error.pinned.messages.empty": "No pinned meows",
|
|
||||||
"title.message.lowercase": "meowsage"
|
|
||||||
}
|
}
|
||||||
|
|
@ -194,12 +194,5 @@
|
||||||
"placeholder.type.text.emoji": "Type text / emoji...",
|
"placeholder.type.text.emoji": "Type text / emoji...",
|
||||||
"action.cancel": "Cancel",
|
"action.cancel": "Cancel",
|
||||||
"title.crop.image": "Crop Image",
|
"title.crop.image": "Crop Image",
|
||||||
"title.zoom": "Zoom",
|
"title.zoom": "Zoom"
|
||||||
"title.pin.message": "Pin",
|
|
||||||
"title.unpin.message": "Unpin",
|
|
||||||
"message.pinned.by": "{0} pinned a {1}.",
|
|
||||||
"message.unpinned.by": "{0} unpinned a {1}.",
|
|
||||||
"title.pinned.messages": "Pinned",
|
|
||||||
"error.pinned.messages.empty": "No pinned messages",
|
|
||||||
"title.message.lowercase": "message"
|
|
||||||
}
|
}
|
||||||
|
|
@ -194,12 +194,5 @@
|
||||||
"placeholder.type.text.emoji": "Wpisz tekst / emoji...",
|
"placeholder.type.text.emoji": "Wpisz tekst / emoji...",
|
||||||
"action.cancel": "Anuluj",
|
"action.cancel": "Anuluj",
|
||||||
"title.crop.image": "Przytnij obraz",
|
"title.crop.image": "Przytnij obraz",
|
||||||
"title.zoom": "Przybliżenie",
|
"title.zoom": "Przybliżenie"
|
||||||
"title.pin.message": "Przypnij",
|
|
||||||
"title.unpin.message": "Odepnij",
|
|
||||||
"message.pinned.by": "{0} przypiął {1}.",
|
|
||||||
"message.unpinned.by": "{0} odpiął {1}.",
|
|
||||||
"title.pinned.messages": "Przypięte",
|
|
||||||
"error.pinned.messages.empty": "Brak przypiętych wiadomości",
|
|
||||||
"title.message.lowercase": "wiadomość"
|
|
||||||
}
|
}
|
||||||
|
|
@ -16,6 +16,7 @@ const ICONS = {
|
||||||
_feather: function(content, props) {
|
_feather: function(content, props) {
|
||||||
return this._create(content, { viewBox: "0 0 24 24", width: "1.4rem", height: "1.4rem", fill: "none", stroke: "var(--text-color)", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, props);
|
return this._create(content, { viewBox: "0 0 24 24", width: "1.4rem", height: "1.4rem", fill: "none", stroke: "var(--text-color)", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, props);
|
||||||
},
|
},
|
||||||
|
|
||||||
addChat: (props) => ICONS._material(`<path d="M120-160v-600q0-33 23.5-56.5T200-840h480q33 0 56.5 23.5T760-760v203q-10-2-20-2.5t-20-.5q-10 0-20 .5t-20 2.5v-203H200v400h283q-2 10-2.5 20t-.5 20q0 10 .5 20t2.5 20H240L120-160Zm160-440h320v-80H280v80Zm0 160h200v-80H280v80Zm400 280v-120H560v-80h120v-120h80v120h120v80H760v120h-80ZM200-360v-400 400Z"/>`, props),
|
addChat: (props) => ICONS._material(`<path d="M120-160v-600q0-33 23.5-56.5T200-840h480q33 0 56.5 23.5T760-760v203q-10-2-20-2.5t-20-.5q-10 0-20 .5t-20 2.5v-203H200v400h283q-2 10-2.5 20t-.5 20q0 10 .5 20t2.5 20H240L120-160Zm160-440h320v-80H280v80Zm0 160h200v-80H280v80Zm400 280v-120H560v-80h120v-120h80v120h120v80H760v120h-80ZM200-360v-400 400Z"/>`, props),
|
||||||
createGroup: (props) => ICONS._material(`<path d="M500-482q29-32 44.5-73t15.5-85q0-44-15.5-85T500-798q60 8 100 53t40 105q0 60-40 105t-100 53Zm220 322v-120q0-36-16-68.5T662-406q51 18 94.5 46.5T800-280v120h-80Zm80-280v-80h-80v-80h80v-80h80v80h80v80h-80v80h-80Zm-593-87q-47-47-47-113t47-113q47-47 113-47t113 47q47 47 47 113t-47 113q-47 47-113 47t-113-47ZM0-160v-112q0-34 17.5-62.5T64-378q62-31 126-46.5T320-440q66 0 130 15.5T576-378q29 15 46.5 43.5T640-272v112H0Zm320-400q33 0 56.5-23.5T400-640q0-33-23.5-56.5T320-720q-33 0-56.5 23.5T240-640q0 33 23.5 56.5T320-560ZM80-240h480v-32q0-11-5.5-20T540-306q-54-27-109-40.5T320-360q-56 0-111 13.5T100-306q-9 5-14.5 14T80-272v32Zm240-400Zm0 400Z"/>`, props),
|
createGroup: (props) => ICONS._material(`<path d="M500-482q29-32 44.5-73t15.5-85q0-44-15.5-85T500-798q60 8 100 53t40 105q0 60-40 105t-100 53Zm220 322v-120q0-36-16-68.5T662-406q51 18 94.5 46.5T800-280v120h-80Zm80-280v-80h-80v-80h80v-80h80v80h80v80h-80v80h-80Zm-593-87q-47-47-47-113t47-113q47-47 113-47t113 47q47 47 47 113t-47 113q-47 47-113 47t-113-47ZM0-160v-112q0-34 17.5-62.5T64-378q62-31 126-46.5T320-440q66 0 130 15.5T576-378q29 15 46.5 43.5T640-272v112H0Zm320-400q33 0 56.5-23.5T400-640q0-33-23.5-56.5T320-720q-33 0-56.5 23.5T240-640q0 33 23.5 56.5T320-560ZM80-240h480v-32q0-11-5.5-20T540-306q-54-27-109-40.5T320-360q-56 0-111 13.5T100-306q-9 5-14.5 14T80-272v32Zm240-400Zm0 400Z"/>`, props),
|
||||||
createSpace: (props) => ICONS._material(`<path d="M80-120v-720h400v160h400v320h-80v-240H480v80h80v80h-80v80h80v80h-80v80h160v80H80Zm80-80h80v-80h-80v80Zm0-160h80v-80h-80v80Zm0-160h80v-80h-80v80Zm0-160h80v-80h-80v80Zm160 480h80v-80h-80v80Zm0-160h80v-80h-80v80Zm0-160h80v-80h-80v80Zm0-160h80v-80h-80v80ZM800-40v-80h-80v-80h80v-80h80v80h80v80h-80v80h-80ZM640-440v-80h80v80h-80Zm0 160v-80h80v80h-80Z"/>`, props),
|
createSpace: (props) => ICONS._material(`<path d="M80-120v-720h400v160h400v320h-80v-240H480v80h80v80h-80v80h80v80h-80v80h160v80H80Zm80-80h80v-80h-80v80Zm0-160h80v-80h-80v80Zm0-160h80v-80h-80v80Zm0-160h80v-80h-80v80Zm160 480h80v-80h-80v80Zm0-160h80v-80h-80v80Zm0-160h80v-80h-80v80Zm0-160h80v-80h-80v80ZM800-40v-80h-80v-80h80v-80h80v80h80v80h-80v80h-80ZM640-440v-80h80v80h-80Zm0 160v-80h80v80h-80Z"/>`, props),
|
||||||
|
|
@ -27,8 +28,8 @@ const ICONS = {
|
||||||
delete: (props) => ICONS._material(`<path d="M280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520ZM360-280h80v-360h-80v360Zm160 0h80v-360h-80v360ZM280-720v520-520Z"/>`, props),
|
delete: (props) => ICONS._material(`<path d="M280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520ZM360-280h80v-360h-80v360Zm160 0h80v-360h-80v360ZM280-720v520-520Z"/>`, props),
|
||||||
menu: (props) => ICONS._material(`<path d="M120-240v-80h720v80H120Zm0-200v-80h720v80H120Zm0-200v-80h720v80H120Z"/>`, props),
|
menu: (props) => ICONS._material(`<path d="M120-240v-80h720v80H120Zm0-200v-80h720v80H120Zm0-200v-80h720v80H120Z"/>`, props),
|
||||||
arrowBack: (props) => ICONS._material(`<path d="m313-440 224 224-57 56-320-320 320-320 57 56-224 224h487v80H313Z"/>`, props),
|
arrowBack: (props) => ICONS._material(`<path d="m313-440 224 224-57 56-320-320 320-320 57 56-224 224h487v80H313Z"/>`, props),
|
||||||
pin: (props) => ICONS._material(`<path d="m640-480 80 80v80H520v240l-40 40-40-40v-240H240v-80l80-80v-280h-40v-80h400v80h-40v280Zm-286 80h252l-46-46v-314H400v314l-46 46Zm126 0Z"/>`, props),
|
pin: (props) => ICONS._material(`<path d="m640-480 80 80v80H520v240l-40 40-40-40v-240H240v-80l80-80v-280q0-33 23.5-56.5T400-840h160q33 0 56.5 23.5T640-760v280Z"/>`, props),
|
||||||
call: (props) => ICONS._material(`<path d="M798-120q-125 0-247-54.5T329-329Q229-429 174.5-551T120-798q0-18 12-30t30-12h162q14 0 25 9.5t13 22.5l26 140q2 16-1 27t-11 19l-97 98q20 37 47.5 71.5T387-386q31 31 65 57.5t72 48.5l94-94q9-9 23.5-13.5T670-390l138 28q14 4 23 14.5t9 23.5v162q0 18-12 30t-30 12ZM241-600l66-66-17-94h-89q5 41 14 81t26 79Zm358 358q39 17 79.5 27t81.5 13v-88l-94-19-67 67ZM241-600Zm358 358Z"/>`, props),
|
call: (props) => ICONS._material(`<path d="M798-120q-125 0-247-54.5T329-329Q229-429 174.5-551T120-798q0-18 12-30t30-12h162q14 0 25 9.5t13 22.5l26 140q2 16-1 27t-11 19l-97 98q20 37 47.5 71.5T387-386q31 31 65 57.5t72 48.5l94-94q9-9 23.5-13.5T670-390l138 28q14 4 23 14.5t9 23.5v162q0 18-12 30t-30 12Z"/>`, props),
|
||||||
account: (props) => ICONS._material(`<path d="M200-200h560v-560H200v560Zm0 80q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm40-160h480L570-480 450-320l-90-120-120 160Zm-40 80v-560 560Z"/>`, props),
|
account: (props) => ICONS._material(`<path d="M200-200h560v-560H200v560Zm0 80q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm40-160h480L570-480 450-320l-90-120-120 160Zm-40 80v-560 560Z"/>`, props),
|
||||||
settings: (props) => ICONS._material(`<path d="M480-80q-82 0-155-31.5t-127.5-86Q143-252 111.5-325T80-480q0-83 31.5-156t86-127Q252-817 325-848.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 167-116.5 283.5T480-80Zm0-157q102 0 174-72t72-174q0-102-72-174t-174-72q-102 0-174 72t-72 174q0 102 72 174t174 72Zm0-80q-68 0-116.5-48.5T315-482q0-68 48.5-116.5T480-647q68 0 116.5 48.5T645-482q0 68-48.5 116.5T480-317Zm0-60q43 0 74-31t31-74q0-43-31-74t-74-31q-43 0-74 31t-31 74q0 43 31 74t74 31Zm0-105Z"/>`, props),
|
settings: (props) => ICONS._material(`<path d="M480-80q-82 0-155-31.5t-127.5-86Q143-252 111.5-325T80-480q0-83 31.5-156t86-127Q252-817 325-848.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 167-116.5 283.5T480-80Zm0-157q102 0 174-72t72-174q0-102-72-174t-174-72q-102 0-174 72t-72 174q0 102 72 174t174 72Zm0-80q-68 0-116.5-48.5T315-482q0-68 48.5-116.5T480-647q68 0 116.5 48.5T645-482q0 68-48.5 116.5T480-317Zm0-60q43 0 74-31t31-74q0-43-31-74t-74-31q-43 0-74 31t-31 74q0 43 31 74t74 31Zm0-105Z"/>`, props),
|
||||||
profile: (props) => ICONS._material(`<path d="M480-480q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 66-47 113t-113 47ZM160-160v-112q0-34 17.5-62.5T224-378q62-31 126-46.5T480-440q66 0 130 15.5T736-378q29 15 46.5 43.5T800-272v112H160Zm80-80h480v-32q0-11-5.5-20T700-306q-54-27-109-40.5T480-360q-56 0-111 13.5T260-306q-9 5-14.5 14t-5.5 20v32Zm240-320q33 0 56.5-23.5T560-640q0-33-23.5-56.5T480-720q-33 0-56.5 23.5T400-640q0 33 23.5 56.5T480-560Zm0-80Zm0 400Z"/>`, props),
|
profile: (props) => ICONS._material(`<path d="M480-480q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 66-47 113t-113 47ZM160-160v-112q0-34 17.5-62.5T224-378q62-31 126-46.5T480-440q66 0 130 15.5T736-378q29 15 46.5 43.5T800-272v112H160Zm80-80h480v-32q0-11-5.5-20T700-306q-54-27-109-40.5T480-360q-56 0-111 13.5T260-306q-9 5-14.5 14t-5.5 20v32Zm240-320q33 0 56.5-23.5T560-640q0-33-23.5-56.5T480-720q-33 0-56.5 23.5T400-640q0 33 23.5 56.5T480-560Zm0-80Zm0 400Z"/>`, props),
|
||||||
|
|
|
||||||
|
|
@ -1350,14 +1350,7 @@ async function switchRoomContent(title, content, showRoomBar, icon = "", skipMob
|
||||||
var currentDetailsContentTitle = null;
|
var currentDetailsContentTitle = null;
|
||||||
var currentDetailsContentHtml = null;
|
var currentDetailsContentHtml = null;
|
||||||
|
|
||||||
var savedDetailsHtml = null;
|
async function switchDetailsContent(title, content) {
|
||||||
var savedDetailsTitle = null;
|
|
||||||
|
|
||||||
async function switchDetailsContent(title, content, showBack = false, backAction = "", saveCurrent = false) {
|
|
||||||
if (saveCurrent && currentDetailsContentTitle !== title) {
|
|
||||||
savedDetailsHtml = currentDetailsContentHtml;
|
|
||||||
savedDetailsTitle = currentDetailsContentTitle;
|
|
||||||
}
|
|
||||||
if (currentDetailsContentTitle === title && currentDetailsContentHtml === content) return;
|
if (currentDetailsContentTitle === title && currentDetailsContentHtml === content) return;
|
||||||
currentDetailsContentTitle = title;
|
currentDetailsContentTitle = title;
|
||||||
currentDetailsContentHtml = content;
|
currentDetailsContentHtml = content;
|
||||||
|
|
@ -1371,14 +1364,13 @@ async function switchDetailsContent(title, content, showBack = false, backAction
|
||||||
roomsTopBarTransition.style.opacity = "0";
|
roomsTopBarTransition.style.opacity = "0";
|
||||||
|
|
||||||
const rem = parseFloat(getComputedStyle(document.documentElement).fontSize);
|
const rem = parseFloat(getComputedStyle(document.documentElement).fontSize);
|
||||||
if (window.innerWidth > 55 * rem || mainScreen.classList.contains('mobile-details')) {
|
if (window.innerWidth > 55 * rem) {
|
||||||
await delay(200);
|
await delay(200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let pinCallBtns = showBack ? `${callBtnHtml}` : `${pinBtnHtml}${callBtnHtml}`;
|
let pinCallBtns = `${pinBtnHtml}${callBtnHtml}`;
|
||||||
let backBtn = showBack ? `<button class="topbar-action-btn" onclick="${backAction}" style="margin-right: 0.5rem">${ICONS.arrowBack({ width: "1.5rem", height: "1.5rem" })}</button>` : "";
|
roomsTopBarTransition.innerHTML = `<inherit>${processBlah(title)}</inherit><div class="flex-spacer"></div>${pinCallBtns}`;
|
||||||
roomsTopBarTransition.innerHTML = `<inherit style="display: flex; align-items: center;">${backBtn}<inherit>${processBlah(title)}</inherit></inherit><div class="flex-spacer"></div>${pinCallBtns}`;
|
|
||||||
|
|
||||||
let parser = new DOMParser();
|
let parser = new DOMParser();
|
||||||
let doc = parser.parseFromString(content, "text/html");
|
let doc = parser.parseFromString(content, "text/html");
|
||||||
|
|
@ -1503,7 +1495,7 @@ document.addEventListener("click", (e) => {
|
||||||
if (mainScreen && mainScreen.classList.contains('mobile-details')) {
|
if (mainScreen && mainScreen.classList.contains('mobile-details')) {
|
||||||
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')) {
|
||||||
mobileNavBack();
|
mobileNavBack();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1521,21 +1513,6 @@ if (App) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function popstate() {
|
function popstate() {
|
||||||
if (fixedContextMenu && fixedContextMenu.classList.contains("show")) {
|
|
||||||
fixedContextMenu.classList.remove("show");
|
|
||||||
if (typeof clearContextMenuStyles === "function") clearContextMenuStyles();
|
|
||||||
if (typeof setActiveSidebarIndicator === "function" && typeof currentMainIndicator !== "undefined") {
|
|
||||||
setActiveSidebarIndicator(currentMainIndicator);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let cropperPopup = document.getElementById("cropper-popup-container");
|
|
||||||
if (cropperPopup && cropperPopup.classList.contains("show")) {
|
|
||||||
cropperPopup.classList.remove("show");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isProfileOpen) {
|
if (isProfileOpen) {
|
||||||
closeProfile();
|
closeProfile();
|
||||||
return;
|
return;
|
||||||
|
|
@ -1605,17 +1582,7 @@ async function openProfile(accountId) {
|
||||||
|
|
||||||
let bannerUrl = `${url}/user/storage/public/getentry?id=${accountId}&e=larp.profile.banner`;
|
let bannerUrl = `${url}/user/storage/public/getentry?id=${accountId}&e=larp.profile.banner`;
|
||||||
if (accountId === window.id && window.profileUpdateTimestamp) bannerUrl += `&t=${window.profileUpdateTimestamp}`;
|
if (accountId === window.id && window.profileUpdateTimestamp) bannerUrl += `&t=${window.profileUpdateTimestamp}`;
|
||||||
let bannerHtml = `<img src="${bannerUrl}" class="profile-banner">`;
|
let bannerHtml = `<img src="${bannerUrl}" class="profile-banner" onerror="this.outerHTML='<div class=\\'profile-banner\\'></div>'">`;
|
||||||
setTimeout(() => {
|
|
||||||
let bannerImg = document.querySelector("#profile-popup-inner .profile-banner");
|
|
||||||
if (bannerImg) {
|
|
||||||
bannerImg.addEventListener("error", function() {
|
|
||||||
let div = document.createElement("div");
|
|
||||||
div.className = "profile-banner";
|
|
||||||
this.parentNode.replaceChild(div, this);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, 50);
|
|
||||||
|
|
||||||
let bio = await fetchAsync(`${url}/user/storage/public/getentry?id=${accountId}&e=larp.profile.bio`);
|
let bio = await fetchAsync(`${url}/user/storage/public/getentry?id=${accountId}&e=larp.profile.bio`);
|
||||||
if (bio.startsWith("error:")) bio = "";
|
if (bio.startsWith("error:")) bio = "";
|
||||||
|
|
@ -1657,8 +1624,7 @@ async function openProfile(accountId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasDm) {
|
if (hasDm) {
|
||||||
let safeName = encodeURIComponent(nameWithHost).replace(/'/g, "%27");
|
actionBtnHtml = `<button class="profile-action-btn" onclick="openDm('${dmRoomId}', '${nameWithHost}', '${accountId}'); closeProfile();">${processBlah('action.open.dm')}</button>`;
|
||||||
actionBtnHtml = `<button class="profile-action-btn" onclick="openDm('${dmRoomId}', decodeURIComponent('${safeName}'), '${accountId}'); closeProfile();">${processBlah('action.open.dm')}</button>`;
|
|
||||||
} else {
|
} else {
|
||||||
let sentInvite = false;
|
let sentInvite = false;
|
||||||
let receivedInvite = false;
|
let receivedInvite = false;
|
||||||
|
|
@ -2175,10 +2141,8 @@ async function renderDms(res) {
|
||||||
let isRead = dmData.string3 === "true";
|
let isRead = dmData.string3 === "true";
|
||||||
let nameStyle = isRead ? "opacity: 0.8;" : "opacity: 1;";
|
let nameStyle = isRead ? "opacity: 0.8;" : "opacity: 1;";
|
||||||
|
|
||||||
let safeTargetUsername = encodeURIComponent(targetUsername).replace(/'/g, "%27");
|
|
||||||
|
|
||||||
html += `
|
html += `
|
||||||
<button class="room-entry" id="dm-btn-${dmId}" onclick="openDm('${dmId}', decodeURIComponent('${safeTargetUsername}'), '${targetId}')">
|
<button class="room-entry" id="dm-btn-${dmId}" onclick="openDm('${dmId}', '${targetUsername}', '${targetId}')">
|
||||||
<img src="${pfp}" class="room-pfp">
|
<img src="${pfp}" 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" style="${nameStyle}">${displayName}</span>
|
<span class="room-name" style="${nameStyle}">${displayName}</span>
|
||||||
|
|
@ -2378,7 +2342,12 @@ function truncateFilename(name, maxLength = 20) {
|
||||||
return baseName.substring(0, half) + "..." + baseName.substring(baseName.length - (allowedBaseLength - half)) + ext;
|
return baseName.substring(0, half) + "..." + baseName.substring(baseName.length - (allowedBaseLength - half)) + ext;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function generateMessagesHtml(entries, isPinnedContext = false) {
|
async function renderMessages(messages, isPrepend = false) {
|
||||||
|
let container = document.getElementById("chat-messages");
|
||||||
|
if (!container) return;
|
||||||
|
|
||||||
|
let entries = Object.entries(messages).sort((a, b) => parseInt(a[0]) - parseInt(b[0]));
|
||||||
|
|
||||||
let html = "";
|
let html = "";
|
||||||
let lastAuthor = null;
|
let lastAuthor = null;
|
||||||
|
|
||||||
|
|
@ -2419,10 +2388,7 @@ async function generateMessagesHtml(entries, isPinnedContext = false) {
|
||||||
? `${ICONS.image({ width: "1.5rem", height: "1.5rem", stroke: "currentColor" })}`
|
? `${ICONS.image({ width: "1.5rem", height: "1.5rem", stroke: "currentColor" })}`
|
||||||
: `${ICONS.download({ width: "1.5rem", height: "1.5rem", stroke: "currentColor" })}`;
|
: `${ICONS.download({ width: "1.5rem", height: "1.5rem", stroke: "currentColor" })}`;
|
||||||
|
|
||||||
let safeType = encodeURIComponent(att.type).replace(/'/g, "%27");
|
attachmentsHtml += `<div id="${attContainerId}" class="attachment-box" style="position: relative; min-width: 120px; max-width: 300px; min-height: 100px; max-height: 300px; background: rgba(0,0,0,0.2); border: 1px solid var(--border-color); border-radius: 8px; display: flex; align-items: center; justify-content: center; cursor: pointer; overflow: hidden;" onclick="downloadAndRenderAttachment('${msgId}', '${att.id}', '${escapeHtml(att.type)}', '${escapeHtml(att.name)}')">
|
||||||
let safeName = encodeURIComponent(att.name).replace(/'/g, "%27");
|
|
||||||
|
|
||||||
attachmentsHtml += `<div id="${attContainerId}" class="attachment-box" style="position: relative; min-width: 120px; max-width: 300px; min-height: 100px; max-height: 300px; background: rgba(0,0,0,0.2); border: 1px solid var(--border-color); border-radius: 8px; display: flex; align-items: center; justify-content: center; cursor: pointer; overflow: hidden;" onclick="event.stopPropagation(); downloadAndRenderAttachment(this, '${msgId}', '${att.id}', decodeURIComponent('${safeType}'), decodeURIComponent('${safeName}'))">
|
|
||||||
<div style="padding: 1rem; text-align: center; color: var(--text-color);">
|
<div style="padding: 1rem; text-align: center; color: var(--text-color);">
|
||||||
${iconSvg}
|
${iconSvg}
|
||||||
<div style="font-size: 0.8rem; margin-top: 0.5rem; word-break: break-all;">${escapeHtml(displayName)}</div>
|
<div style="font-size: 0.8rem; margin-top: 0.5rem; word-break: break-all;">${escapeHtml(displayName)}</div>
|
||||||
|
|
@ -2452,33 +2418,7 @@ async function generateMessagesHtml(entries, isPinnedContext = false) {
|
||||||
// Also still process the whole thing in case it's just "dm.begin.notice" without {blah()}
|
// Also still process the whole thing in case it's just "dm.begin.notice" without {blah()}
|
||||||
content = processBlah(content);
|
content = processBlah(content);
|
||||||
|
|
||||||
if (msg.type && msg.type.startsWith("larp.info")) {
|
if (msg.type === "larp.info") {
|
||||||
if (msg.type === "larp.info.pin" || msg.type === "larp.info.unpin") {
|
|
||||||
let authorName = "Unknown";
|
|
||||||
if (msg.author && msg.author !== "0") {
|
|
||||||
if (!dmUsernameCache[msg.author]) {
|
|
||||||
let fullUsername = await fetchAsync(`${url}/idtoname?id=${msg.author}`);
|
|
||||||
if (fullUsername && !fullUsername.startsWith("error:")) {
|
|
||||||
dmUsernameCache[msg.author] = fullUsername;
|
|
||||||
dmPfpCache[msg.author] = await getAvatarUrl(msg.author, fullUsername);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (dmUsernameCache[msg.author]) {
|
|
||||||
authorName = escapeHtml(dmUsernameCache[msg.author].split(':')[0]);
|
|
||||||
} else {
|
|
||||||
authorName = escapeHtml(msg.author.split(';')[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let isPinned = msg.type === "larp.info.pin";
|
|
||||||
let actionStr = isPinned ? "message.pinned.by" : "message.unpinned.by";
|
|
||||||
|
|
||||||
let clickableMsg = `<span onclick="event.stopPropagation(); scrollToPinnedMessage(event, '${msg.pervious}')" style="cursor: pointer; font-weight: bold; color: rgba(255, 255, 255, 0.9);">${processBlah('title.message.lowercase')}</span>`;
|
|
||||||
let clickableAuthor = `<span onclick="event.stopPropagation(); openProfile('${msg.author}')" style="cursor: pointer; font-weight: bold;">${authorName}</span>`;
|
|
||||||
|
|
||||||
let textStr = processBlah(actionStr);
|
|
||||||
content = textStr.replace("{0}", clickableAuthor).replace("{1}", clickableMsg);
|
|
||||||
}
|
|
||||||
html += `<div style="text-align: center; opacity: 0.5; padding: 1rem; font-size: 0.9rem;">${content}</div>`;
|
html += `<div style="text-align: center; opacity: 0.5; padding: 1rem; font-size: 0.9rem;">${content}</div>`;
|
||||||
lastAuthor = null;
|
lastAuthor = null;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -2589,7 +2529,7 @@ async function generateMessagesHtml(entries, isPinnedContext = false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
let divIdStr = randId !== "" ? `id="${randId}" ` : "";
|
let divIdStr = randId !== "" ? `id="${randId}" ` : "";
|
||||||
repliedHtml = `<div ${divIdStr}class="chat-message-replied" style="font-size: 0.8rem; opacity: 0.7; margin-bottom: 0.2rem; cursor: pointer;" onclick="event.stopPropagation(); scrollToMessage('${respondedId}')">${ICONS.cornerUpLeft({ width: "0.8rem", height: "0.8rem", stroke: "currentColor", style: "vertical-align: middle;" })} ${processBlah('title.replied.to')} ${replyAuthor}</div>`;
|
repliedHtml = `<div ${divIdStr}class="chat-message-replied" style="font-size: 0.8rem; opacity: 0.7; margin-bottom: 0.2rem; cursor: pointer;" onclick="scrollToMessage('${respondedId}')">${ICONS.cornerUpLeft({ width: "0.8rem", height: "0.8rem", stroke: "currentColor", style: "vertical-align: middle;" })} ${processBlah('title.replied.to')} ${replyAuthor}</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
let reactionsHtml = "";
|
let reactionsHtml = "";
|
||||||
|
|
@ -2621,8 +2561,7 @@ async function generateMessagesHtml(entries, isPinnedContext = false) {
|
||||||
let style = rxCounts[rxVal].me ? "background: rgba(255, 255, 255, 0.12); border-color: var(--text-color);" : "background: rgba(255, 255, 255, 0.06);";
|
let style = rxCounts[rxVal].me ? "background: rgba(255, 255, 255, 0.12); border-color: var(--text-color);" : "background: rgba(255, 255, 255, 0.06);";
|
||||||
let safeRx = encodeURIComponent(rxVal).replace(/'/g, "%27");
|
let safeRx = encodeURIComponent(rxVal).replace(/'/g, "%27");
|
||||||
let escapedRxVal = escapeHtml(rxVal);
|
let escapedRxVal = escapeHtml(rxVal);
|
||||||
let pillStyle = `display: inline-flex; align-items: center; gap: 0.35rem; padding: 0.1rem 0.45rem; border-radius: 1rem; font-size: 0.85rem; border: var(--border-width) solid var(--light-border-color); cursor: pointer; ${style}`;
|
reactionsHtml += `<span class="reaction-pill" style="padding: 0.1rem 0.4rem; border-radius: 1rem; font-size: 0.85rem; border: var(--border-width) solid var(--light-border-color); cursor: pointer; ${style}" onclick="reactMessagePrompt('${msgId}', '${safeRx}')">${processBlah(escapedRxVal)} ${rxCounts[rxVal].count > 1 ? rxCounts[rxVal].count : ""}</span>`;
|
||||||
reactionsHtml += `<span class="reaction-pill" style="${pillStyle}" onclick="event.stopPropagation(); reactMessagePrompt('${msgId}', '${safeRx}')"><span>${processBlah(escapedRxVal)}</span>${rxCounts[rxVal].count > 1 ? `<span style="opacity: 0.5;">${rxCounts[rxVal].count}</span>` : ""}</span>`;
|
|
||||||
}
|
}
|
||||||
reactionsHtml += `</div>`;
|
reactionsHtml += `</div>`;
|
||||||
}
|
}
|
||||||
|
|
@ -2632,11 +2571,11 @@ async function generateMessagesHtml(entries, isPinnedContext = false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
html += `
|
html += `
|
||||||
<div class="chat-message ${extraClass}${isPinnedContext ? ` chat-message-pinned-item` : ``}" data-msg-id="${msgId}" id="${isPinnedContext ? `pinned-` : ``}msg-${msgId}"${isPinnedContext ? ` onclick="scrollToPinnedMessage(event, '${msgId}')"` : ``} style="${isPinnedContext ? `cursor: pointer;` : ``}">
|
<div class="chat-message ${extraClass}" data-msg-id="${msgId}" id="msg-${msgId}">
|
||||||
${showAvatar ? `<img src="${pfp}" class="chat-message-pfp" onclick="event.stopPropagation(); openProfile('${msg.author}')" style="cursor: pointer;">` : `<div style="width: 2.5rem; flex-shrink: 0;"></div>`}
|
${showAvatar ? `<img src="${pfp}" class="chat-message-pfp" onclick="openProfile('${msg.author}')" style="cursor: pointer;">` : `<div style="width: 2.5rem; flex-shrink: 0;"></div>`}
|
||||||
<div class="chat-message-content" oncontextmenu="handleMessageContextMenu(arguments[0], '${msgId}')">
|
<div class="chat-message-content" oncontextmenu="handleMessageContextMenu(event, '${msgId}')">
|
||||||
${showAvatar ? `<div class="chat-message-header">
|
${showAvatar ? `<div class="chat-message-header">
|
||||||
<span class="chat-message-author" onclick="event.stopPropagation(); openProfile('${msg.author}')" style="cursor: pointer;">${authorName}</span>
|
<span class="chat-message-author" onclick="openProfile('${msg.author}')" style="cursor: pointer;">${authorName}</span>
|
||||||
<span class="chat-message-timestamp">${timeStr}</span>
|
<span class="chat-message-timestamp">${timeStr}</span>
|
||||||
</div>` : ""}
|
</div>` : ""}
|
||||||
${repliedHtml}
|
${repliedHtml}
|
||||||
|
|
@ -2649,18 +2588,6 @@ async function generateMessagesHtml(entries, isPinnedContext = false) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return html;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async function renderMessages(messages, isPrepend = false) {
|
|
||||||
let container = document.getElementById("chat-messages");
|
|
||||||
if (!container) return;
|
|
||||||
|
|
||||||
let entries = Object.entries(messages).sort((a, b) => parseInt(a[0]) - parseInt(b[0]));
|
|
||||||
let html = await generateMessagesHtml(entries, false);
|
|
||||||
|
|
||||||
const rem = parseFloat(getComputedStyle(document.documentElement).fontSize);
|
const rem = parseFloat(getComputedStyle(document.documentElement).fontSize);
|
||||||
let wasAtBottom = (container.scrollHeight - container.scrollTop - container.clientHeight) < (1.5 * rem);
|
let wasAtBottom = (container.scrollHeight - container.scrollTop - container.clientHeight) < (1.5 * rem);
|
||||||
if (window.forceScrollToBottom) {
|
if (window.forceScrollToBottom) {
|
||||||
|
|
@ -2823,8 +2750,10 @@ function setupChatScrollListener() {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async function downloadAndRenderAttachment(container, msgId, attId, type, name) {
|
async function downloadAndRenderAttachment(msgId, attId, type, name) {
|
||||||
if (!currentDmId || !currentDmKey) return;
|
if (!currentDmId || !currentDmKey) return;
|
||||||
|
|
||||||
|
let container = document.getElementById(`att-container-${msgId}-${attId}`);
|
||||||
if (!container) return;
|
if (!container) return;
|
||||||
|
|
||||||
if (container.dataset.loaded === "true") {
|
if (container.dataset.loaded === "true") {
|
||||||
|
|
@ -3149,9 +3078,6 @@ function setupWebSocket() {
|
||||||
if (dmMessageLoadTimeout) clearTimeout(dmMessageLoadTimeout);
|
if (dmMessageLoadTimeout) clearTimeout(dmMessageLoadTimeout);
|
||||||
dmMessageLoadTimeout = setTimeout(() => {
|
dmMessageLoadTimeout = setTimeout(() => {
|
||||||
loadDmMessages(msgDmId);
|
loadDmMessages(msgDmId);
|
||||||
if (currentDetailsContentTitle === "title.pinned.messages") {
|
|
||||||
showPinnedMessages(true, false);
|
|
||||||
}
|
|
||||||
}, 300);
|
}, 300);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3193,41 +3119,6 @@ function handleMessageContextMenu(e, msgId) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (reactBtn) reactBtn.style.display = "";
|
if (reactBtn) reactBtn.style.display = "";
|
||||||
|
|
||||||
let pinContainer = fixedContextMenu.querySelector("#context-pin-container");
|
|
||||||
if (pinContainer) {
|
|
||||||
let isPinned = msg && msg.pinned === "true";
|
|
||||||
pinContainer.innerHTML = `
|
|
||||||
<button onclick="togglePinMessage('${msgId}')">
|
|
||||||
${ICONS.pin()}
|
|
||||||
<span class="blah">${isPinned ? processBlah("title.unpin.message") : processBlah("title.pin.message")}</span>
|
|
||||||
</button>`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async function togglePinMessage(msgId) {
|
|
||||||
if (fixedContextMenu) fixedContextMenu.classList.remove("show");
|
|
||||||
if (typeof clearContextMenuStyles === "function") clearContextMenuStyles();
|
|
||||||
|
|
||||||
try {
|
|
||||||
let payload = JSON.stringify({
|
|
||||||
string1: currentDmId,
|
|
||||||
string2: msgId
|
|
||||||
});
|
|
||||||
await fetchEncrypted("dm/message/pin/toggle", payload);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
showBlahNotification("error.network.error");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function restoreDmDetails() {
|
|
||||||
if (savedDetailsHtml && savedDetailsTitle) {
|
|
||||||
await switchDetailsContent(savedDetailsTitle, savedDetailsHtml);
|
|
||||||
savedDetailsHtml = null;
|
|
||||||
savedDetailsTitle = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3354,12 +3245,9 @@ async function loadEmotes() {
|
||||||
|
|
||||||
reactionPickerMenuHtml = `
|
reactionPickerMenuHtml = `
|
||||||
<div style="display: flex; flex-direction: column; gap: 0.4rem; padding: 0.2rem; min-width: 14rem;">
|
<div style="display: flex; flex-direction: column; gap: 0.4rem; padding: 0.2rem; min-width: 14rem;">
|
||||||
<input type="text" id="reaction-picker-search" placeholder="{blah(placeholder.type.text.emoji)}" oninput="filterReactions(this.value)" onkeydown="if(arguments[0].key === 'Enter') { if(this.value.trim() !== '') reactMessagePrompt(currentContextMenuMsgId, encodeURIComponent(this.value)); }" style="width: auto; align-self: stretch; margin: 0; box-sizing: border-box; padding: 0.5rem 0.6rem; border-radius: 0.65rem; border: var(--border-width) solid var(--light-border-color); background: transparent; color: var(--text-color); font-family: inherit;">
|
<input type="text" id="reaction-picker-search" placeholder="{blah(placeholder.type.text.emoji)}" oninput="filterReactions(this.value)" onkeydown="if(event.key === 'Enter') { if(this.value.trim() !== '') reactMessagePrompt(currentContextMenuMsgId, encodeURIComponent(this.value)); }" style="width: auto; align-self: stretch; margin: 0; box-sizing: border-box; padding: 0.5rem 0.6rem; border-radius: 0.65rem; border: var(--border-width) solid var(--light-border-color); background: transparent; color: var(--text-color); font-family: inherit;">
|
||||||
<div id="reaction-picker-grid" style="display: grid; grid-template-columns: repeat(5, 1fr); gap: 0.2rem; max-height: 12rem; overflow-y: auto; overflow-x: hidden;">
|
<div id="reaction-picker-grid" style="display: grid; grid-template-columns: repeat(5, 1fr); gap: 0.2rem; max-height: 12rem; overflow-y: auto; overflow-x: hidden;">
|
||||||
${loadedEmotes.map(emoji => {
|
${loadedEmotes.map(emoji => `<button onclick="reactMessagePrompt(currentContextMenuMsgId, encodeURIComponent('${emoji.char}'))" data-name="${emoji.name}" style="min-width: unset; width: 100%; aspect-ratio: 1; display: flex; justify-content: center; align-items: center; padding: 0; font-size: 1.2rem; border-radius: 0.65rem;" class="reaction-emoji-btn">${emoji.char}</button>`).join('')}
|
||||||
let safeChar = encodeURIComponent(emoji.char).replace(/'/g, "%27");
|
|
||||||
return `<button onclick="reactMessagePrompt(currentContextMenuMsgId, '${safeChar}')" data-name="${emoji.name}" style="min-width: unset; width: 100%; aspect-ratio: 1; display: flex; justify-content: center; align-items: center; padding: 0; font-size: 1.2rem; border-radius: 0.65rem;" class="reaction-emoji-btn">${emoji.char}</button>`;
|
|
||||||
}).join('')}
|
|
||||||
</div>
|
</div>
|
||||||
<button class="reaction-text-btn" onclick="reactMessagePrompt(currentContextMenuMsgId, encodeURIComponent(document.getElementById('reaction-picker-search').value))" style="margin: 0; padding: 0.5rem; justify-content: center; border-radius: 0.65rem; width: 100%;">
|
<button class="reaction-text-btn" onclick="reactMessagePrompt(currentContextMenuMsgId, encodeURIComponent(document.getElementById('reaction-picker-search').value))" style="margin: 0; padding: 0.5rem; justify-content: center; border-radius: 0.65rem; width: 100%;">
|
||||||
<span class="blah">title.text.react</span>
|
<span class="blah">title.text.react</span>
|
||||||
|
|
@ -4011,63 +3899,4 @@ function handleDmInteraction() {
|
||||||
|
|
||||||
document.addEventListener('click', handleDmInteraction);
|
document.addEventListener('click', handleDmInteraction);
|
||||||
document.addEventListener('keydown', handleDmInteraction);
|
document.addEventListener('keydown', handleDmInteraction);
|
||||||
document.addEventListener('touchstart', handleDmInteraction);
|
document.addEventListener('touchstart', handleDmInteraction);
|
||||||
async function showPinnedMessages(forceReload = false, showPanel = true) {
|
|
||||||
if (!forceReload && currentDetailsContentTitle === "title.pinned.messages" && savedDetailsHtml) {
|
|
||||||
if (showPanel) mobileNavDetails();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showPanel) mobileNavDetails();
|
|
||||||
// Do not show loading html on refresh if we are already in pinned messages, to avoid jarring flashing
|
|
||||||
if (!forceReload) {
|
|
||||||
let loadingHtml = `<div style="padding: 2rem; text-align: center; opacity: 0.5;"><blah>desc.messages.loading</blah></div>`;
|
|
||||||
switchDetailsContent("title.pinned.messages", loadingHtml, true, "restoreDmDetails()", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
let res = await fetchEncrypted("dm/messages/pinned", currentDmId);
|
|
||||||
if (res && !res.startsWith("error:")) {
|
|
||||||
let msgs = JSON.parse(res);
|
|
||||||
let entries = Object.entries(msgs).sort((a, b) => parseInt(a[0]) - parseInt(b[0]));
|
|
||||||
if (entries.length === 0) {
|
|
||||||
switchDetailsContent("title.pinned.messages", `<div style="padding: 2rem; text-align: center; opacity: 0.5;"><blah>error.pinned.messages.empty</blah></div>`, true, "restoreDmDetails()");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let innerHtml = await generateMessagesHtml(entries, true);
|
|
||||||
let html = `<div id="pinned-messages" style="display: flex; flex-direction: column; padding: 1rem; gap: 0.5rem; overflow-y: auto; height: 100%;">
|
|
||||||
${innerHtml}
|
|
||||||
</div>`;
|
|
||||||
switchDetailsContent("title.pinned.messages", html, true, "restoreDmDetails()");
|
|
||||||
} else {
|
|
||||||
switchDetailsContent("title.pinned.messages", `<div style="padding: 2rem; text-align: center; opacity: 0.5;"><blah>error:message.not.found</blah></div>`, true, "restoreDmDetails()");
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
switchDetailsContent("title.pinned.messages", `<div style="padding: 2rem; text-align: center; opacity: 0.5;"><blah>error.network.error</blah></div>`, true, "restoreDmDetails()");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function scrollToPinnedMessage(e, msgId) {
|
|
||||||
if (e && e.target) {
|
|
||||||
if (e.target.closest('a') ||
|
|
||||||
e.target.closest('button') ||
|
|
||||||
e.target.closest('.attachment-box') ||
|
|
||||||
e.target.closest('.chat-message-replied') ||
|
|
||||||
e.target.closest('.reaction-pill') ||
|
|
||||||
e.target.closest('.chat-message-author') ||
|
|
||||||
e.target.closest('.chat-message-pfp')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const rem = parseFloat(getComputedStyle(document.documentElement).fontSize);
|
|
||||||
if (window.innerWidth <= 55 * rem) {
|
|
||||||
mainScreen.classList.remove('mobile-details');
|
|
||||||
mainScreen.classList.add('mobile-content');
|
|
||||||
if (typeof setActiveSidebarIndicator === "function" && typeof currentMainIndicator !== "undefined") {
|
|
||||||
setActiveSidebarIndicator(currentMainIndicator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
scrollToMessage(msgId);
|
|
||||||
}
|
|
||||||
|
|
@ -143,10 +143,10 @@ var chatScreen = `
|
||||||
</div>
|
</div>
|
||||||
<div id="attachments-preview" style="display: none; padding: 0.5rem 1rem; gap: 0.8rem; overflow-x: auto; border-top: var(--border-width) solid var(--light-border-color); background: var(--main-bg-color); align-items: center; flex-shrink: 0;"></div>
|
<div id="attachments-preview" style="display: none; padding: 0.5rem 1rem; gap: 0.8rem; overflow-x: auto; border-top: var(--border-width) solid var(--light-border-color); background: var(--main-bg-color); align-items: center; flex-shrink: 0;"></div>
|
||||||
<div style="padding: 1rem; border-top: var(--border-width) solid var(--light-border-color); display: flex; gap: 0.5rem; align-items: flex-end; flex-shrink: 0;">
|
<div style="padding: 1rem; border-top: var(--border-width) solid var(--light-border-color); display: flex; gap: 0.5rem; align-items: flex-end; flex-shrink: 0;">
|
||||||
<button id="chat-add-attachment" class="submit-button" onclick="clickAddAttachment(this, arguments[0])" style="margin: 0; padding: 0; width: 2.5rem; flex-shrink: 0; min-height: 2.5rem; max-height: 8rem;">
|
<button id="chat-add-attachment" class="submit-button" onclick="clickAddAttachment(this, event)" style="margin: 0; padding: 0; width: 2.5rem; flex-shrink: 0; min-height: 2.5rem; max-height: 8rem;">
|
||||||
${ICONS.plus({ width: "1.5rem", height: "1.5rem", stroke: "var(--main-bg-color)" })}
|
${ICONS.plus({ width: "1.5rem", height: "1.5rem", stroke: "var(--main-bg-color)" })}
|
||||||
</button>
|
</button>
|
||||||
<textarea id="chat-input" class="forminput" style="flex-grow: 1; margin: 0; resize: none; min-height: 2.5rem; max-height: 8rem; padding: 0.5rem;" placeholder="{blah(placeholder.message.input)}" onkeydown="handleChatInputKey(arguments[0])" oninput="handleChatInputResize(this)"></textarea>
|
<textarea id="chat-input" class="forminput" style="flex-grow: 1; margin: 0; resize: none; min-height: 2.5rem; max-height: 8rem; padding: 0.5rem;" placeholder="{blah(placeholder.message.input)}" onkeydown="handleChatInputKey(event)" oninput="handleChatInputResize(this)"></textarea>
|
||||||
<button id="chat-sendmessage" class="submit-button" onclick="sendMessage(this)" style="margin: 0; padding: 0; width: 2.5rem; flex-shrink: 0; min-height: 2.5rem; max-height: 8rem;">
|
<button id="chat-sendmessage" class="submit-button" onclick="sendMessage(this)" style="margin: 0; padding: 0; width: 2.5rem; flex-shrink: 0; min-height: 2.5rem; max-height: 8rem;">
|
||||||
${ICONS.send()}
|
${ICONS.send()}
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -236,9 +236,9 @@ var attachmentMenu = `
|
||||||
${ICONS.poll()}
|
${ICONS.poll()}
|
||||||
<span class="blah">action.attachment.poll</span>
|
<span class="blah">action.attachment.poll</span>
|
||||||
</button>
|
</button>
|
||||||
<input type="file" id="attach-take-photo" accept="image/*" capture="environment" style="display: none;" onchange="handleAttachmentSelection(arguments[0])" multiple>
|
<input type="file" id="attach-take-photo" accept="image/*" capture="environment" style="display: none;" onchange="handleAttachmentSelection(event)" multiple>
|
||||||
<input type="file" id="attach-media" accept="image/*,video/*" style="display: none;" onchange="handleAttachmentSelection(arguments[0])" multiple>
|
<input type="file" id="attach-media" accept="image/*,video/*" style="display: none;" onchange="handleAttachmentSelection(event)" multiple>
|
||||||
<input type="file" id="attach-file" style="display: none;" onchange="handleAttachmentSelection(arguments[0])" multiple>
|
<input type="file" id="attach-file" style="display: none;" onchange="handleAttachmentSelection(event)" multiple>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
var addSpaceMenu = `
|
var addSpaceMenu = `
|
||||||
|
|
@ -255,7 +255,7 @@ var messageContextMenu = `
|
||||||
${ICONS.replyAction()}
|
${ICONS.replyAction()}
|
||||||
<span class="blah">title.reply.message</span>
|
<span class="blah">title.reply.message</span>
|
||||||
</button>
|
</button>
|
||||||
<button id="context-react-btn" onclick="openReactionPicker(arguments[0])">
|
<button id="context-react-btn" onclick="openReactionPicker(event)">
|
||||||
${ICONS.react()}
|
${ICONS.react()}
|
||||||
<span class="blah">title.react.message</span>
|
<span class="blah">title.react.message</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -263,12 +263,11 @@ var messageContextMenu = `
|
||||||
${ICONS.delete({ fill: "var(--big-red)" })}
|
${ICONS.delete({ fill: "var(--big-red)" })}
|
||||||
<span class="blah" style="color: var(--big-red)">title.delete.message</span>
|
<span class="blah" style="color: var(--big-red)">title.delete.message</span>
|
||||||
</button>
|
</button>
|
||||||
<div id="context-pin-container"></div>
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
//elements
|
//elements
|
||||||
var backBtnHtml = `<button class="mobile-nav-btn" onclick="mobileNavBack()">${ICONS.arrowBack({ width: "1.5rem", height: "1.5rem" })}</button>`;
|
var backBtnHtml = `<button class="mobile-nav-btn" onclick="mobileNavBack()">${ICONS.arrowBack({ width: "1.5rem", height: "1.5rem" })}</button>`;
|
||||||
var pinBtnHtml = `<button class="topbar-action-btn room-action-pin right" onclick="showPinnedMessages()">${ICONS.pin({ width: "1.5rem", height: "1.5rem" })}</button>`;
|
var pinBtnHtml = `<button class="topbar-action-btn room-action-pin right">${ICONS.pin({ width: "1.5rem", height: "1.5rem" })}</button>`;
|
||||||
var callBtnHtml = `<button class="topbar-action-btn room-action-call right">${ICONS.call({ width: "1.5rem", height: "1.5rem" })}</button>`;
|
var callBtnHtml = `<button class="topbar-action-btn room-action-call right">${ICONS.call({ width: "1.5rem", height: "1.5rem" })}</button>`;
|
||||||
|
|
||||||
var settingsBar = `
|
var settingsBar = `
|
||||||
|
|
|
||||||
|
|
@ -194,12 +194,5 @@
|
||||||
"placeholder.type.text.emoji": "Type purr / meow...",
|
"placeholder.type.text.emoji": "Type purr / meow...",
|
||||||
"action.cancel": "Cancel",
|
"action.cancel": "Cancel",
|
||||||
"title.crop.image": "Crop Image",
|
"title.crop.image": "Crop Image",
|
||||||
"title.zoom": "Zoom",
|
"title.zoom": "Zoom"
|
||||||
"title.pin.message": "Pin",
|
|
||||||
"title.unpin.message": "Unpin",
|
|
||||||
"message.pinned.by": "{0} pinned a {1}.",
|
|
||||||
"message.unpinned.by": "{0} unpinned a {1}.",
|
|
||||||
"title.pinned.messages": "Pinned",
|
|
||||||
"error.pinned.messages.empty": "No pinned meows",
|
|
||||||
"title.message.lowercase": "meowsage"
|
|
||||||
}
|
}
|
||||||
|
|
@ -194,12 +194,5 @@
|
||||||
"placeholder.type.text.emoji": "Type text / emoji...",
|
"placeholder.type.text.emoji": "Type text / emoji...",
|
||||||
"action.cancel": "Cancel",
|
"action.cancel": "Cancel",
|
||||||
"title.crop.image": "Crop Image",
|
"title.crop.image": "Crop Image",
|
||||||
"title.zoom": "Zoom",
|
"title.zoom": "Zoom"
|
||||||
"title.pin.message": "Pin",
|
|
||||||
"title.unpin.message": "Unpin",
|
|
||||||
"message.pinned.by": "{0} pinned a {1}.",
|
|
||||||
"message.unpinned.by": "{0} unpinned a {1}.",
|
|
||||||
"title.pinned.messages": "Pinned",
|
|
||||||
"error.pinned.messages.empty": "No pinned messages",
|
|
||||||
"title.message.lowercase": "message"
|
|
||||||
}
|
}
|
||||||
|
|
@ -194,12 +194,5 @@
|
||||||
"placeholder.type.text.emoji": "Wpisz tekst / emoji...",
|
"placeholder.type.text.emoji": "Wpisz tekst / emoji...",
|
||||||
"action.cancel": "Anuluj",
|
"action.cancel": "Anuluj",
|
||||||
"title.crop.image": "Przytnij obraz",
|
"title.crop.image": "Przytnij obraz",
|
||||||
"title.zoom": "Przybliżenie",
|
"title.zoom": "Przybliżenie"
|
||||||
"title.pin.message": "Przypnij",
|
|
||||||
"title.unpin.message": "Odepnij",
|
|
||||||
"message.pinned.by": "{0} przypiął {1}.",
|
|
||||||
"message.unpinned.by": "{0} odpiął {1}.",
|
|
||||||
"title.pinned.messages": "Przypięte",
|
|
||||||
"error.pinned.messages.empty": "Brak przypiętych wiadomości",
|
|
||||||
"title.message.lowercase": "wiadomość"
|
|
||||||
}
|
}
|
||||||
|
|
@ -16,6 +16,7 @@ const ICONS = {
|
||||||
_feather: function(content, props) {
|
_feather: function(content, props) {
|
||||||
return this._create(content, { viewBox: "0 0 24 24", width: "1.4rem", height: "1.4rem", fill: "none", stroke: "var(--text-color)", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, props);
|
return this._create(content, { viewBox: "0 0 24 24", width: "1.4rem", height: "1.4rem", fill: "none", stroke: "var(--text-color)", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, props);
|
||||||
},
|
},
|
||||||
|
|
||||||
addChat: (props) => ICONS._material(`<path d="M120-160v-600q0-33 23.5-56.5T200-840h480q33 0 56.5 23.5T760-760v203q-10-2-20-2.5t-20-.5q-10 0-20 .5t-20 2.5v-203H200v400h283q-2 10-2.5 20t-.5 20q0 10 .5 20t2.5 20H240L120-160Zm160-440h320v-80H280v80Zm0 160h200v-80H280v80Zm400 280v-120H560v-80h120v-120h80v120h120v80H760v120h-80ZM200-360v-400 400Z"/>`, props),
|
addChat: (props) => ICONS._material(`<path d="M120-160v-600q0-33 23.5-56.5T200-840h480q33 0 56.5 23.5T760-760v203q-10-2-20-2.5t-20-.5q-10 0-20 .5t-20 2.5v-203H200v400h283q-2 10-2.5 20t-.5 20q0 10 .5 20t2.5 20H240L120-160Zm160-440h320v-80H280v80Zm0 160h200v-80H280v80Zm400 280v-120H560v-80h120v-120h80v120h120v80H760v120h-80ZM200-360v-400 400Z"/>`, props),
|
||||||
createGroup: (props) => ICONS._material(`<path d="M500-482q29-32 44.5-73t15.5-85q0-44-15.5-85T500-798q60 8 100 53t40 105q0 60-40 105t-100 53Zm220 322v-120q0-36-16-68.5T662-406q51 18 94.5 46.5T800-280v120h-80Zm80-280v-80h-80v-80h80v-80h80v80h80v80h-80v80h-80Zm-593-87q-47-47-47-113t47-113q47-47 113-47t113 47q47 47 47 113t-47 113q-47 47-113 47t-113-47ZM0-160v-112q0-34 17.5-62.5T64-378q62-31 126-46.5T320-440q66 0 130 15.5T576-378q29 15 46.5 43.5T640-272v112H0Zm320-400q33 0 56.5-23.5T400-640q0-33-23.5-56.5T320-720q-33 0-56.5 23.5T240-640q0 33 23.5 56.5T320-560ZM80-240h480v-32q0-11-5.5-20T540-306q-54-27-109-40.5T320-360q-56 0-111 13.5T100-306q-9 5-14.5 14T80-272v32Zm240-400Zm0 400Z"/>`, props),
|
createGroup: (props) => ICONS._material(`<path d="M500-482q29-32 44.5-73t15.5-85q0-44-15.5-85T500-798q60 8 100 53t40 105q0 60-40 105t-100 53Zm220 322v-120q0-36-16-68.5T662-406q51 18 94.5 46.5T800-280v120h-80Zm80-280v-80h-80v-80h80v-80h80v80h80v80h-80v80h-80Zm-593-87q-47-47-47-113t47-113q47-47 113-47t113 47q47 47 47 113t-47 113q-47 47-113 47t-113-47ZM0-160v-112q0-34 17.5-62.5T64-378q62-31 126-46.5T320-440q66 0 130 15.5T576-378q29 15 46.5 43.5T640-272v112H0Zm320-400q33 0 56.5-23.5T400-640q0-33-23.5-56.5T320-720q-33 0-56.5 23.5T240-640q0 33 23.5 56.5T320-560ZM80-240h480v-32q0-11-5.5-20T540-306q-54-27-109-40.5T320-360q-56 0-111 13.5T100-306q-9 5-14.5 14T80-272v32Zm240-400Zm0 400Z"/>`, props),
|
||||||
createSpace: (props) => ICONS._material(`<path d="M80-120v-720h400v160h400v320h-80v-240H480v80h80v80h-80v80h80v80h-80v80h160v80H80Zm80-80h80v-80h-80v80Zm0-160h80v-80h-80v80Zm0-160h80v-80h-80v80Zm0-160h80v-80h-80v80Zm160 480h80v-80h-80v80Zm0-160h80v-80h-80v80Zm0-160h80v-80h-80v80Zm0-160h80v-80h-80v80ZM800-40v-80h-80v-80h80v-80h80v80h80v80h-80v80h-80ZM640-440v-80h80v80h-80Zm0 160v-80h80v80h-80Z"/>`, props),
|
createSpace: (props) => ICONS._material(`<path d="M80-120v-720h400v160h400v320h-80v-240H480v80h80v80h-80v80h80v80h-80v80h160v80H80Zm80-80h80v-80h-80v80Zm0-160h80v-80h-80v80Zm0-160h80v-80h-80v80Zm0-160h80v-80h-80v80Zm160 480h80v-80h-80v80Zm0-160h80v-80h-80v80Zm0-160h80v-80h-80v80Zm0-160h80v-80h-80v80ZM800-40v-80h-80v-80h80v-80h80v80h80v80h-80v80h-80ZM640-440v-80h80v80h-80Zm0 160v-80h80v80h-80Z"/>`, props),
|
||||||
|
|
@ -27,8 +28,8 @@ const ICONS = {
|
||||||
delete: (props) => ICONS._material(`<path d="M280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520ZM360-280h80v-360h-80v360Zm160 0h80v-360h-80v360ZM280-720v520-520Z"/>`, props),
|
delete: (props) => ICONS._material(`<path d="M280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520ZM360-280h80v-360h-80v360Zm160 0h80v-360h-80v360ZM280-720v520-520Z"/>`, props),
|
||||||
menu: (props) => ICONS._material(`<path d="M120-240v-80h720v80H120Zm0-200v-80h720v80H120Zm0-200v-80h720v80H120Z"/>`, props),
|
menu: (props) => ICONS._material(`<path d="M120-240v-80h720v80H120Zm0-200v-80h720v80H120Zm0-200v-80h720v80H120Z"/>`, props),
|
||||||
arrowBack: (props) => ICONS._material(`<path d="m313-440 224 224-57 56-320-320 320-320 57 56-224 224h487v80H313Z"/>`, props),
|
arrowBack: (props) => ICONS._material(`<path d="m313-440 224 224-57 56-320-320 320-320 57 56-224 224h487v80H313Z"/>`, props),
|
||||||
pin: (props) => ICONS._material(`<path d="m640-480 80 80v80H520v240l-40 40-40-40v-240H240v-80l80-80v-280h-40v-80h400v80h-40v280Zm-286 80h252l-46-46v-314H400v314l-46 46Zm126 0Z"/>`, props),
|
pin: (props) => ICONS._material(`<path d="m640-480 80 80v80H520v240l-40 40-40-40v-240H240v-80l80-80v-280q0-33 23.5-56.5T400-840h160q33 0 56.5 23.5T640-760v280Z"/>`, props),
|
||||||
call: (props) => ICONS._material(`<path d="M798-120q-125 0-247-54.5T329-329Q229-429 174.5-551T120-798q0-18 12-30t30-12h162q14 0 25 9.5t13 22.5l26 140q2 16-1 27t-11 19l-97 98q20 37 47.5 71.5T387-386q31 31 65 57.5t72 48.5l94-94q9-9 23.5-13.5T670-390l138 28q14 4 23 14.5t9 23.5v162q0 18-12 30t-30 12ZM241-600l66-66-17-94h-89q5 41 14 81t26 79Zm358 358q39 17 79.5 27t81.5 13v-88l-94-19-67 67ZM241-600Zm358 358Z"/>`, props),
|
call: (props) => ICONS._material(`<path d="M798-120q-125 0-247-54.5T329-329Q229-429 174.5-551T120-798q0-18 12-30t30-12h162q14 0 25 9.5t13 22.5l26 140q2 16-1 27t-11 19l-97 98q20 37 47.5 71.5T387-386q31 31 65 57.5t72 48.5l94-94q9-9 23.5-13.5T670-390l138 28q14 4 23 14.5t9 23.5v162q0 18-12 30t-30 12Z"/>`, props),
|
||||||
account: (props) => ICONS._material(`<path d="M200-200h560v-560H200v560Zm0 80q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm40-160h480L570-480 450-320l-90-120-120 160Zm-40 80v-560 560Z"/>`, props),
|
account: (props) => ICONS._material(`<path d="M200-200h560v-560H200v560Zm0 80q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm40-160h480L570-480 450-320l-90-120-120 160Zm-40 80v-560 560Z"/>`, props),
|
||||||
settings: (props) => ICONS._material(`<path d="M480-80q-82 0-155-31.5t-127.5-86Q143-252 111.5-325T80-480q0-83 31.5-156t86-127Q252-817 325-848.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 167-116.5 283.5T480-80Zm0-157q102 0 174-72t72-174q0-102-72-174t-174-72q-102 0-174 72t-72 174q0 102 72 174t174 72Zm0-80q-68 0-116.5-48.5T315-482q0-68 48.5-116.5T480-647q68 0 116.5 48.5T645-482q0 68-48.5 116.5T480-317Zm0-60q43 0 74-31t31-74q0-43-31-74t-74-31q-43 0-74 31t-31 74q0 43 31 74t74 31Zm0-105Z"/>`, props),
|
settings: (props) => ICONS._material(`<path d="M480-80q-82 0-155-31.5t-127.5-86Q143-252 111.5-325T80-480q0-83 31.5-156t86-127Q252-817 325-848.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 167-116.5 283.5T480-80Zm0-157q102 0 174-72t72-174q0-102-72-174t-174-72q-102 0-174 72t-72 174q0 102 72 174t174 72Zm0-80q-68 0-116.5-48.5T315-482q0-68 48.5-116.5T480-647q68 0 116.5 48.5T645-482q0 68-48.5 116.5T480-317Zm0-60q43 0 74-31t31-74q0-43-31-74t-74-31q-43 0-74 31t-31 74q0 43 31 74t74 31Zm0-105Z"/>`, props),
|
||||||
profile: (props) => ICONS._material(`<path d="M480-480q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 66-47 113t-113 47ZM160-160v-112q0-34 17.5-62.5T224-378q62-31 126-46.5T480-440q66 0 130 15.5T736-378q29 15 46.5 43.5T800-272v112H160Zm80-80h480v-32q0-11-5.5-20T700-306q-54-27-109-40.5T480-360q-56 0-111 13.5T260-306q-9 5-14.5 14t-5.5 20v32Zm240-320q33 0 56.5-23.5T560-640q0-33-23.5-56.5T480-720q-33 0-56.5 23.5T400-640q0 33 23.5 56.5T480-560Zm0-80Zm0 400Z"/>`, props),
|
profile: (props) => ICONS._material(`<path d="M480-480q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 66-47 113t-113 47ZM160-160v-112q0-34 17.5-62.5T224-378q62-31 126-46.5T480-440q66 0 130 15.5T736-378q29 15 46.5 43.5T800-272v112H160Zm80-80h480v-32q0-11-5.5-20T700-306q-54-27-109-40.5T480-360q-56 0-111 13.5T260-306q-9 5-14.5 14t-5.5 20v32Zm240-320q33 0 56.5-23.5T560-640q0-33-23.5-56.5T480-720q-33 0-56.5 23.5T400-640q0 33 23.5 56.5T480-560Zm0-80Zm0 400Z"/>`, props),
|
||||||
|
|
|
||||||
227
webroot/main.js
227
webroot/main.js
|
|
@ -1350,14 +1350,7 @@ async function switchRoomContent(title, content, showRoomBar, icon = "", skipMob
|
||||||
var currentDetailsContentTitle = null;
|
var currentDetailsContentTitle = null;
|
||||||
var currentDetailsContentHtml = null;
|
var currentDetailsContentHtml = null;
|
||||||
|
|
||||||
var savedDetailsHtml = null;
|
async function switchDetailsContent(title, content) {
|
||||||
var savedDetailsTitle = null;
|
|
||||||
|
|
||||||
async function switchDetailsContent(title, content, showBack = false, backAction = "", saveCurrent = false) {
|
|
||||||
if (saveCurrent && currentDetailsContentTitle !== title) {
|
|
||||||
savedDetailsHtml = currentDetailsContentHtml;
|
|
||||||
savedDetailsTitle = currentDetailsContentTitle;
|
|
||||||
}
|
|
||||||
if (currentDetailsContentTitle === title && currentDetailsContentHtml === content) return;
|
if (currentDetailsContentTitle === title && currentDetailsContentHtml === content) return;
|
||||||
currentDetailsContentTitle = title;
|
currentDetailsContentTitle = title;
|
||||||
currentDetailsContentHtml = content;
|
currentDetailsContentHtml = content;
|
||||||
|
|
@ -1371,14 +1364,13 @@ async function switchDetailsContent(title, content, showBack = false, backAction
|
||||||
roomsTopBarTransition.style.opacity = "0";
|
roomsTopBarTransition.style.opacity = "0";
|
||||||
|
|
||||||
const rem = parseFloat(getComputedStyle(document.documentElement).fontSize);
|
const rem = parseFloat(getComputedStyle(document.documentElement).fontSize);
|
||||||
if (window.innerWidth > 55 * rem || mainScreen.classList.contains('mobile-details')) {
|
if (window.innerWidth > 55 * rem) {
|
||||||
await delay(200);
|
await delay(200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let pinCallBtns = showBack ? `${callBtnHtml}` : `${pinBtnHtml}${callBtnHtml}`;
|
let pinCallBtns = `${pinBtnHtml}${callBtnHtml}`;
|
||||||
let backBtn = showBack ? `<button class="topbar-action-btn" onclick="${backAction}" style="margin-right: 0.5rem">${ICONS.arrowBack({ width: "1.5rem", height: "1.5rem" })}</button>` : "";
|
roomsTopBarTransition.innerHTML = `<inherit>${processBlah(title)}</inherit><div class="flex-spacer"></div>${pinCallBtns}`;
|
||||||
roomsTopBarTransition.innerHTML = `<inherit style="display: flex; align-items: center;">${backBtn}<inherit>${processBlah(title)}</inherit></inherit><div class="flex-spacer"></div>${pinCallBtns}`;
|
|
||||||
|
|
||||||
let parser = new DOMParser();
|
let parser = new DOMParser();
|
||||||
let doc = parser.parseFromString(content, "text/html");
|
let doc = parser.parseFromString(content, "text/html");
|
||||||
|
|
@ -1503,7 +1495,7 @@ document.addEventListener("click", (e) => {
|
||||||
if (mainScreen && mainScreen.classList.contains('mobile-details')) {
|
if (mainScreen && mainScreen.classList.contains('mobile-details')) {
|
||||||
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')) {
|
||||||
mobileNavBack();
|
mobileNavBack();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1521,21 +1513,6 @@ if (App) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function popstate() {
|
function popstate() {
|
||||||
if (fixedContextMenu && fixedContextMenu.classList.contains("show")) {
|
|
||||||
fixedContextMenu.classList.remove("show");
|
|
||||||
if (typeof clearContextMenuStyles === "function") clearContextMenuStyles();
|
|
||||||
if (typeof setActiveSidebarIndicator === "function" && typeof currentMainIndicator !== "undefined") {
|
|
||||||
setActiveSidebarIndicator(currentMainIndicator);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let cropperPopup = document.getElementById("cropper-popup-container");
|
|
||||||
if (cropperPopup && cropperPopup.classList.contains("show")) {
|
|
||||||
cropperPopup.classList.remove("show");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isProfileOpen) {
|
if (isProfileOpen) {
|
||||||
closeProfile();
|
closeProfile();
|
||||||
return;
|
return;
|
||||||
|
|
@ -1605,17 +1582,7 @@ async function openProfile(accountId) {
|
||||||
|
|
||||||
let bannerUrl = `${url}/user/storage/public/getentry?id=${accountId}&e=larp.profile.banner`;
|
let bannerUrl = `${url}/user/storage/public/getentry?id=${accountId}&e=larp.profile.banner`;
|
||||||
if (accountId === window.id && window.profileUpdateTimestamp) bannerUrl += `&t=${window.profileUpdateTimestamp}`;
|
if (accountId === window.id && window.profileUpdateTimestamp) bannerUrl += `&t=${window.profileUpdateTimestamp}`;
|
||||||
let bannerHtml = `<img src="${bannerUrl}" class="profile-banner">`;
|
let bannerHtml = `<img src="${bannerUrl}" class="profile-banner" onerror="this.outerHTML='<div class=\\'profile-banner\\'></div>'">`;
|
||||||
setTimeout(() => {
|
|
||||||
let bannerImg = document.querySelector("#profile-popup-inner .profile-banner");
|
|
||||||
if (bannerImg) {
|
|
||||||
bannerImg.addEventListener("error", function() {
|
|
||||||
let div = document.createElement("div");
|
|
||||||
div.className = "profile-banner";
|
|
||||||
this.parentNode.replaceChild(div, this);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, 50);
|
|
||||||
|
|
||||||
let bio = await fetchAsync(`${url}/user/storage/public/getentry?id=${accountId}&e=larp.profile.bio`);
|
let bio = await fetchAsync(`${url}/user/storage/public/getentry?id=${accountId}&e=larp.profile.bio`);
|
||||||
if (bio.startsWith("error:")) bio = "";
|
if (bio.startsWith("error:")) bio = "";
|
||||||
|
|
@ -1657,8 +1624,7 @@ async function openProfile(accountId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasDm) {
|
if (hasDm) {
|
||||||
let safeName = encodeURIComponent(nameWithHost).replace(/'/g, "%27");
|
actionBtnHtml = `<button class="profile-action-btn" onclick="openDm('${dmRoomId}', '${nameWithHost}', '${accountId}'); closeProfile();">${processBlah('action.open.dm')}</button>`;
|
||||||
actionBtnHtml = `<button class="profile-action-btn" onclick="openDm('${dmRoomId}', decodeURIComponent('${safeName}'), '${accountId}'); closeProfile();">${processBlah('action.open.dm')}</button>`;
|
|
||||||
} else {
|
} else {
|
||||||
let sentInvite = false;
|
let sentInvite = false;
|
||||||
let receivedInvite = false;
|
let receivedInvite = false;
|
||||||
|
|
@ -2175,10 +2141,8 @@ async function renderDms(res) {
|
||||||
let isRead = dmData.string3 === "true";
|
let isRead = dmData.string3 === "true";
|
||||||
let nameStyle = isRead ? "opacity: 0.8;" : "opacity: 1;";
|
let nameStyle = isRead ? "opacity: 0.8;" : "opacity: 1;";
|
||||||
|
|
||||||
let safeTargetUsername = encodeURIComponent(targetUsername).replace(/'/g, "%27");
|
|
||||||
|
|
||||||
html += `
|
html += `
|
||||||
<button class="room-entry" id="dm-btn-${dmId}" onclick="openDm('${dmId}', decodeURIComponent('${safeTargetUsername}'), '${targetId}')">
|
<button class="room-entry" id="dm-btn-${dmId}" onclick="openDm('${dmId}', '${targetUsername}', '${targetId}')">
|
||||||
<img src="${pfp}" class="room-pfp">
|
<img src="${pfp}" 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" style="${nameStyle}">${displayName}</span>
|
<span class="room-name" style="${nameStyle}">${displayName}</span>
|
||||||
|
|
@ -2378,7 +2342,12 @@ function truncateFilename(name, maxLength = 20) {
|
||||||
return baseName.substring(0, half) + "..." + baseName.substring(baseName.length - (allowedBaseLength - half)) + ext;
|
return baseName.substring(0, half) + "..." + baseName.substring(baseName.length - (allowedBaseLength - half)) + ext;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function generateMessagesHtml(entries, isPinnedContext = false) {
|
async function renderMessages(messages, isPrepend = false) {
|
||||||
|
let container = document.getElementById("chat-messages");
|
||||||
|
if (!container) return;
|
||||||
|
|
||||||
|
let entries = Object.entries(messages).sort((a, b) => parseInt(a[0]) - parseInt(b[0]));
|
||||||
|
|
||||||
let html = "";
|
let html = "";
|
||||||
let lastAuthor = null;
|
let lastAuthor = null;
|
||||||
|
|
||||||
|
|
@ -2419,10 +2388,7 @@ async function generateMessagesHtml(entries, isPinnedContext = false) {
|
||||||
? `${ICONS.image({ width: "1.5rem", height: "1.5rem", stroke: "currentColor" })}`
|
? `${ICONS.image({ width: "1.5rem", height: "1.5rem", stroke: "currentColor" })}`
|
||||||
: `${ICONS.download({ width: "1.5rem", height: "1.5rem", stroke: "currentColor" })}`;
|
: `${ICONS.download({ width: "1.5rem", height: "1.5rem", stroke: "currentColor" })}`;
|
||||||
|
|
||||||
let safeType = encodeURIComponent(att.type).replace(/'/g, "%27");
|
attachmentsHtml += `<div id="${attContainerId}" class="attachment-box" style="position: relative; min-width: 120px; max-width: 300px; min-height: 100px; max-height: 300px; background: rgba(0,0,0,0.2); border: 1px solid var(--border-color); border-radius: 8px; display: flex; align-items: center; justify-content: center; cursor: pointer; overflow: hidden;" onclick="downloadAndRenderAttachment('${msgId}', '${att.id}', '${escapeHtml(att.type)}', '${escapeHtml(att.name)}')">
|
||||||
let safeName = encodeURIComponent(att.name).replace(/'/g, "%27");
|
|
||||||
|
|
||||||
attachmentsHtml += `<div id="${attContainerId}" class="attachment-box" style="position: relative; min-width: 120px; max-width: 300px; min-height: 100px; max-height: 300px; background: rgba(0,0,0,0.2); border: 1px solid var(--border-color); border-radius: 8px; display: flex; align-items: center; justify-content: center; cursor: pointer; overflow: hidden;" onclick="event.stopPropagation(); downloadAndRenderAttachment(this, '${msgId}', '${att.id}', decodeURIComponent('${safeType}'), decodeURIComponent('${safeName}'))">
|
|
||||||
<div style="padding: 1rem; text-align: center; color: var(--text-color);">
|
<div style="padding: 1rem; text-align: center; color: var(--text-color);">
|
||||||
${iconSvg}
|
${iconSvg}
|
||||||
<div style="font-size: 0.8rem; margin-top: 0.5rem; word-break: break-all;">${escapeHtml(displayName)}</div>
|
<div style="font-size: 0.8rem; margin-top: 0.5rem; word-break: break-all;">${escapeHtml(displayName)}</div>
|
||||||
|
|
@ -2452,33 +2418,7 @@ async function generateMessagesHtml(entries, isPinnedContext = false) {
|
||||||
// Also still process the whole thing in case it's just "dm.begin.notice" without {blah()}
|
// Also still process the whole thing in case it's just "dm.begin.notice" without {blah()}
|
||||||
content = processBlah(content);
|
content = processBlah(content);
|
||||||
|
|
||||||
if (msg.type && msg.type.startsWith("larp.info")) {
|
if (msg.type === "larp.info") {
|
||||||
if (msg.type === "larp.info.pin" || msg.type === "larp.info.unpin") {
|
|
||||||
let authorName = "Unknown";
|
|
||||||
if (msg.author && msg.author !== "0") {
|
|
||||||
if (!dmUsernameCache[msg.author]) {
|
|
||||||
let fullUsername = await fetchAsync(`${url}/idtoname?id=${msg.author}`);
|
|
||||||
if (fullUsername && !fullUsername.startsWith("error:")) {
|
|
||||||
dmUsernameCache[msg.author] = fullUsername;
|
|
||||||
dmPfpCache[msg.author] = await getAvatarUrl(msg.author, fullUsername);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (dmUsernameCache[msg.author]) {
|
|
||||||
authorName = escapeHtml(dmUsernameCache[msg.author].split(':')[0]);
|
|
||||||
} else {
|
|
||||||
authorName = escapeHtml(msg.author.split(';')[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let isPinned = msg.type === "larp.info.pin";
|
|
||||||
let actionStr = isPinned ? "message.pinned.by" : "message.unpinned.by";
|
|
||||||
|
|
||||||
let clickableMsg = `<span onclick="event.stopPropagation(); scrollToPinnedMessage(event, '${msg.pervious}')" style="cursor: pointer; font-weight: bold; color: rgba(255, 255, 255, 0.9);">${processBlah('title.message.lowercase')}</span>`;
|
|
||||||
let clickableAuthor = `<span onclick="event.stopPropagation(); openProfile('${msg.author}')" style="cursor: pointer; font-weight: bold;">${authorName}</span>`;
|
|
||||||
|
|
||||||
let textStr = processBlah(actionStr);
|
|
||||||
content = textStr.replace("{0}", clickableAuthor).replace("{1}", clickableMsg);
|
|
||||||
}
|
|
||||||
html += `<div style="text-align: center; opacity: 0.5; padding: 1rem; font-size: 0.9rem;">${content}</div>`;
|
html += `<div style="text-align: center; opacity: 0.5; padding: 1rem; font-size: 0.9rem;">${content}</div>`;
|
||||||
lastAuthor = null;
|
lastAuthor = null;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -2589,7 +2529,7 @@ async function generateMessagesHtml(entries, isPinnedContext = false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
let divIdStr = randId !== "" ? `id="${randId}" ` : "";
|
let divIdStr = randId !== "" ? `id="${randId}" ` : "";
|
||||||
repliedHtml = `<div ${divIdStr}class="chat-message-replied" style="font-size: 0.8rem; opacity: 0.7; margin-bottom: 0.2rem; cursor: pointer;" onclick="event.stopPropagation(); scrollToMessage('${respondedId}')">${ICONS.cornerUpLeft({ width: "0.8rem", height: "0.8rem", stroke: "currentColor", style: "vertical-align: middle;" })} ${processBlah('title.replied.to')} ${replyAuthor}</div>`;
|
repliedHtml = `<div ${divIdStr}class="chat-message-replied" style="font-size: 0.8rem; opacity: 0.7; margin-bottom: 0.2rem; cursor: pointer;" onclick="scrollToMessage('${respondedId}')">${ICONS.cornerUpLeft({ width: "0.8rem", height: "0.8rem", stroke: "currentColor", style: "vertical-align: middle;" })} ${processBlah('title.replied.to')} ${replyAuthor}</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
let reactionsHtml = "";
|
let reactionsHtml = "";
|
||||||
|
|
@ -2621,8 +2561,7 @@ async function generateMessagesHtml(entries, isPinnedContext = false) {
|
||||||
let style = rxCounts[rxVal].me ? "background: rgba(255, 255, 255, 0.12); border-color: var(--text-color);" : "background: rgba(255, 255, 255, 0.06);";
|
let style = rxCounts[rxVal].me ? "background: rgba(255, 255, 255, 0.12); border-color: var(--text-color);" : "background: rgba(255, 255, 255, 0.06);";
|
||||||
let safeRx = encodeURIComponent(rxVal).replace(/'/g, "%27");
|
let safeRx = encodeURIComponent(rxVal).replace(/'/g, "%27");
|
||||||
let escapedRxVal = escapeHtml(rxVal);
|
let escapedRxVal = escapeHtml(rxVal);
|
||||||
let pillStyle = `display: inline-flex; align-items: center; gap: 0.35rem; padding: 0.1rem 0.45rem; border-radius: 1rem; font-size: 0.85rem; border: var(--border-width) solid var(--light-border-color); cursor: pointer; ${style}`;
|
reactionsHtml += `<span class="reaction-pill" style="padding: 0.1rem 0.4rem; border-radius: 1rem; font-size: 0.85rem; border: var(--border-width) solid var(--light-border-color); cursor: pointer; ${style}" onclick="reactMessagePrompt('${msgId}', '${safeRx}')">${processBlah(escapedRxVal)} ${rxCounts[rxVal].count > 1 ? rxCounts[rxVal].count : ""}</span>`;
|
||||||
reactionsHtml += `<span class="reaction-pill" style="${pillStyle}" onclick="event.stopPropagation(); reactMessagePrompt('${msgId}', '${safeRx}')"><span>${processBlah(escapedRxVal)}</span>${rxCounts[rxVal].count > 1 ? `<span style="opacity: 0.5;">${rxCounts[rxVal].count}</span>` : ""}</span>`;
|
|
||||||
}
|
}
|
||||||
reactionsHtml += `</div>`;
|
reactionsHtml += `</div>`;
|
||||||
}
|
}
|
||||||
|
|
@ -2632,11 +2571,11 @@ async function generateMessagesHtml(entries, isPinnedContext = false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
html += `
|
html += `
|
||||||
<div class="chat-message ${extraClass}${isPinnedContext ? ` chat-message-pinned-item` : ``}" data-msg-id="${msgId}" id="${isPinnedContext ? `pinned-` : ``}msg-${msgId}"${isPinnedContext ? ` onclick="scrollToPinnedMessage(event, '${msgId}')"` : ``} style="${isPinnedContext ? `cursor: pointer;` : ``}">
|
<div class="chat-message ${extraClass}" data-msg-id="${msgId}" id="msg-${msgId}">
|
||||||
${showAvatar ? `<img src="${pfp}" class="chat-message-pfp" onclick="event.stopPropagation(); openProfile('${msg.author}')" style="cursor: pointer;">` : `<div style="width: 2.5rem; flex-shrink: 0;"></div>`}
|
${showAvatar ? `<img src="${pfp}" class="chat-message-pfp" onclick="openProfile('${msg.author}')" style="cursor: pointer;">` : `<div style="width: 2.5rem; flex-shrink: 0;"></div>`}
|
||||||
<div class="chat-message-content" oncontextmenu="handleMessageContextMenu(arguments[0], '${msgId}')">
|
<div class="chat-message-content" oncontextmenu="handleMessageContextMenu(event, '${msgId}')">
|
||||||
${showAvatar ? `<div class="chat-message-header">
|
${showAvatar ? `<div class="chat-message-header">
|
||||||
<span class="chat-message-author" onclick="event.stopPropagation(); openProfile('${msg.author}')" style="cursor: pointer;">${authorName}</span>
|
<span class="chat-message-author" onclick="openProfile('${msg.author}')" style="cursor: pointer;">${authorName}</span>
|
||||||
<span class="chat-message-timestamp">${timeStr}</span>
|
<span class="chat-message-timestamp">${timeStr}</span>
|
||||||
</div>` : ""}
|
</div>` : ""}
|
||||||
${repliedHtml}
|
${repliedHtml}
|
||||||
|
|
@ -2649,18 +2588,6 @@ async function generateMessagesHtml(entries, isPinnedContext = false) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return html;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async function renderMessages(messages, isPrepend = false) {
|
|
||||||
let container = document.getElementById("chat-messages");
|
|
||||||
if (!container) return;
|
|
||||||
|
|
||||||
let entries = Object.entries(messages).sort((a, b) => parseInt(a[0]) - parseInt(b[0]));
|
|
||||||
let html = await generateMessagesHtml(entries, false);
|
|
||||||
|
|
||||||
const rem = parseFloat(getComputedStyle(document.documentElement).fontSize);
|
const rem = parseFloat(getComputedStyle(document.documentElement).fontSize);
|
||||||
let wasAtBottom = (container.scrollHeight - container.scrollTop - container.clientHeight) < (1.5 * rem);
|
let wasAtBottom = (container.scrollHeight - container.scrollTop - container.clientHeight) < (1.5 * rem);
|
||||||
if (window.forceScrollToBottom) {
|
if (window.forceScrollToBottom) {
|
||||||
|
|
@ -2823,8 +2750,10 @@ function setupChatScrollListener() {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async function downloadAndRenderAttachment(container, msgId, attId, type, name) {
|
async function downloadAndRenderAttachment(msgId, attId, type, name) {
|
||||||
if (!currentDmId || !currentDmKey) return;
|
if (!currentDmId || !currentDmKey) return;
|
||||||
|
|
||||||
|
let container = document.getElementById(`att-container-${msgId}-${attId}`);
|
||||||
if (!container) return;
|
if (!container) return;
|
||||||
|
|
||||||
if (container.dataset.loaded === "true") {
|
if (container.dataset.loaded === "true") {
|
||||||
|
|
@ -3149,9 +3078,6 @@ function setupWebSocket() {
|
||||||
if (dmMessageLoadTimeout) clearTimeout(dmMessageLoadTimeout);
|
if (dmMessageLoadTimeout) clearTimeout(dmMessageLoadTimeout);
|
||||||
dmMessageLoadTimeout = setTimeout(() => {
|
dmMessageLoadTimeout = setTimeout(() => {
|
||||||
loadDmMessages(msgDmId);
|
loadDmMessages(msgDmId);
|
||||||
if (currentDetailsContentTitle === "title.pinned.messages") {
|
|
||||||
showPinnedMessages(true, false);
|
|
||||||
}
|
|
||||||
}, 300);
|
}, 300);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3193,41 +3119,6 @@ function handleMessageContextMenu(e, msgId) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (reactBtn) reactBtn.style.display = "";
|
if (reactBtn) reactBtn.style.display = "";
|
||||||
|
|
||||||
let pinContainer = fixedContextMenu.querySelector("#context-pin-container");
|
|
||||||
if (pinContainer) {
|
|
||||||
let isPinned = msg && msg.pinned === "true";
|
|
||||||
pinContainer.innerHTML = `
|
|
||||||
<button onclick="togglePinMessage('${msgId}')">
|
|
||||||
${ICONS.pin()}
|
|
||||||
<span class="blah">${isPinned ? processBlah("title.unpin.message") : processBlah("title.pin.message")}</span>
|
|
||||||
</button>`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async function togglePinMessage(msgId) {
|
|
||||||
if (fixedContextMenu) fixedContextMenu.classList.remove("show");
|
|
||||||
if (typeof clearContextMenuStyles === "function") clearContextMenuStyles();
|
|
||||||
|
|
||||||
try {
|
|
||||||
let payload = JSON.stringify({
|
|
||||||
string1: currentDmId,
|
|
||||||
string2: msgId
|
|
||||||
});
|
|
||||||
await fetchEncrypted("dm/message/pin/toggle", payload);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
showBlahNotification("error.network.error");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function restoreDmDetails() {
|
|
||||||
if (savedDetailsHtml && savedDetailsTitle) {
|
|
||||||
await switchDetailsContent(savedDetailsTitle, savedDetailsHtml);
|
|
||||||
savedDetailsHtml = null;
|
|
||||||
savedDetailsTitle = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3354,12 +3245,9 @@ async function loadEmotes() {
|
||||||
|
|
||||||
reactionPickerMenuHtml = `
|
reactionPickerMenuHtml = `
|
||||||
<div style="display: flex; flex-direction: column; gap: 0.4rem; padding: 0.2rem; min-width: 14rem;">
|
<div style="display: flex; flex-direction: column; gap: 0.4rem; padding: 0.2rem; min-width: 14rem;">
|
||||||
<input type="text" id="reaction-picker-search" placeholder="{blah(placeholder.type.text.emoji)}" oninput="filterReactions(this.value)" onkeydown="if(arguments[0].key === 'Enter') { if(this.value.trim() !== '') reactMessagePrompt(currentContextMenuMsgId, encodeURIComponent(this.value)); }" style="width: auto; align-self: stretch; margin: 0; box-sizing: border-box; padding: 0.5rem 0.6rem; border-radius: 0.65rem; border: var(--border-width) solid var(--light-border-color); background: transparent; color: var(--text-color); font-family: inherit;">
|
<input type="text" id="reaction-picker-search" placeholder="{blah(placeholder.type.text.emoji)}" oninput="filterReactions(this.value)" onkeydown="if(event.key === 'Enter') { if(this.value.trim() !== '') reactMessagePrompt(currentContextMenuMsgId, encodeURIComponent(this.value)); }" style="width: auto; align-self: stretch; margin: 0; box-sizing: border-box; padding: 0.5rem 0.6rem; border-radius: 0.65rem; border: var(--border-width) solid var(--light-border-color); background: transparent; color: var(--text-color); font-family: inherit;">
|
||||||
<div id="reaction-picker-grid" style="display: grid; grid-template-columns: repeat(5, 1fr); gap: 0.2rem; max-height: 12rem; overflow-y: auto; overflow-x: hidden;">
|
<div id="reaction-picker-grid" style="display: grid; grid-template-columns: repeat(5, 1fr); gap: 0.2rem; max-height: 12rem; overflow-y: auto; overflow-x: hidden;">
|
||||||
${loadedEmotes.map(emoji => {
|
${loadedEmotes.map(emoji => `<button onclick="reactMessagePrompt(currentContextMenuMsgId, encodeURIComponent('${emoji.char}'))" data-name="${emoji.name}" style="min-width: unset; width: 100%; aspect-ratio: 1; display: flex; justify-content: center; align-items: center; padding: 0; font-size: 1.2rem; border-radius: 0.65rem;" class="reaction-emoji-btn">${emoji.char}</button>`).join('')}
|
||||||
let safeChar = encodeURIComponent(emoji.char).replace(/'/g, "%27");
|
|
||||||
return `<button onclick="reactMessagePrompt(currentContextMenuMsgId, '${safeChar}')" data-name="${emoji.name}" style="min-width: unset; width: 100%; aspect-ratio: 1; display: flex; justify-content: center; align-items: center; padding: 0; font-size: 1.2rem; border-radius: 0.65rem;" class="reaction-emoji-btn">${emoji.char}</button>`;
|
|
||||||
}).join('')}
|
|
||||||
</div>
|
</div>
|
||||||
<button class="reaction-text-btn" onclick="reactMessagePrompt(currentContextMenuMsgId, encodeURIComponent(document.getElementById('reaction-picker-search').value))" style="margin: 0; padding: 0.5rem; justify-content: center; border-radius: 0.65rem; width: 100%;">
|
<button class="reaction-text-btn" onclick="reactMessagePrompt(currentContextMenuMsgId, encodeURIComponent(document.getElementById('reaction-picker-search').value))" style="margin: 0; padding: 0.5rem; justify-content: center; border-radius: 0.65rem; width: 100%;">
|
||||||
<span class="blah">title.text.react</span>
|
<span class="blah">title.text.react</span>
|
||||||
|
|
@ -4011,63 +3899,4 @@ function handleDmInteraction() {
|
||||||
|
|
||||||
document.addEventListener('click', handleDmInteraction);
|
document.addEventListener('click', handleDmInteraction);
|
||||||
document.addEventListener('keydown', handleDmInteraction);
|
document.addEventListener('keydown', handleDmInteraction);
|
||||||
document.addEventListener('touchstart', handleDmInteraction);
|
document.addEventListener('touchstart', handleDmInteraction);
|
||||||
async function showPinnedMessages(forceReload = false, showPanel = true) {
|
|
||||||
if (!forceReload && currentDetailsContentTitle === "title.pinned.messages" && savedDetailsHtml) {
|
|
||||||
if (showPanel) mobileNavDetails();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showPanel) mobileNavDetails();
|
|
||||||
// Do not show loading html on refresh if we are already in pinned messages, to avoid jarring flashing
|
|
||||||
if (!forceReload) {
|
|
||||||
let loadingHtml = `<div style="padding: 2rem; text-align: center; opacity: 0.5;"><blah>desc.messages.loading</blah></div>`;
|
|
||||||
switchDetailsContent("title.pinned.messages", loadingHtml, true, "restoreDmDetails()", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
let res = await fetchEncrypted("dm/messages/pinned", currentDmId);
|
|
||||||
if (res && !res.startsWith("error:")) {
|
|
||||||
let msgs = JSON.parse(res);
|
|
||||||
let entries = Object.entries(msgs).sort((a, b) => parseInt(a[0]) - parseInt(b[0]));
|
|
||||||
if (entries.length === 0) {
|
|
||||||
switchDetailsContent("title.pinned.messages", `<div style="padding: 2rem; text-align: center; opacity: 0.5;"><blah>error.pinned.messages.empty</blah></div>`, true, "restoreDmDetails()");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let innerHtml = await generateMessagesHtml(entries, true);
|
|
||||||
let html = `<div id="pinned-messages" style="display: flex; flex-direction: column; padding: 1rem; gap: 0.5rem; overflow-y: auto; height: 100%;">
|
|
||||||
${innerHtml}
|
|
||||||
</div>`;
|
|
||||||
switchDetailsContent("title.pinned.messages", html, true, "restoreDmDetails()");
|
|
||||||
} else {
|
|
||||||
switchDetailsContent("title.pinned.messages", `<div style="padding: 2rem; text-align: center; opacity: 0.5;"><blah>error:message.not.found</blah></div>`, true, "restoreDmDetails()");
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
switchDetailsContent("title.pinned.messages", `<div style="padding: 2rem; text-align: center; opacity: 0.5;"><blah>error.network.error</blah></div>`, true, "restoreDmDetails()");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function scrollToPinnedMessage(e, msgId) {
|
|
||||||
if (e && e.target) {
|
|
||||||
if (e.target.closest('a') ||
|
|
||||||
e.target.closest('button') ||
|
|
||||||
e.target.closest('.attachment-box') ||
|
|
||||||
e.target.closest('.chat-message-replied') ||
|
|
||||||
e.target.closest('.reaction-pill') ||
|
|
||||||
e.target.closest('.chat-message-author') ||
|
|
||||||
e.target.closest('.chat-message-pfp')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const rem = parseFloat(getComputedStyle(document.documentElement).fontSize);
|
|
||||||
if (window.innerWidth <= 55 * rem) {
|
|
||||||
mainScreen.classList.remove('mobile-details');
|
|
||||||
mainScreen.classList.add('mobile-content');
|
|
||||||
if (typeof setActiveSidebarIndicator === "function" && typeof currentMainIndicator !== "undefined") {
|
|
||||||
setActiveSidebarIndicator(currentMainIndicator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
scrollToMessage(msgId);
|
|
||||||
}
|
|
||||||
|
|
@ -143,10 +143,10 @@ var chatScreen = `
|
||||||
</div>
|
</div>
|
||||||
<div id="attachments-preview" style="display: none; padding: 0.5rem 1rem; gap: 0.8rem; overflow-x: auto; border-top: var(--border-width) solid var(--light-border-color); background: var(--main-bg-color); align-items: center; flex-shrink: 0;"></div>
|
<div id="attachments-preview" style="display: none; padding: 0.5rem 1rem; gap: 0.8rem; overflow-x: auto; border-top: var(--border-width) solid var(--light-border-color); background: var(--main-bg-color); align-items: center; flex-shrink: 0;"></div>
|
||||||
<div style="padding: 1rem; border-top: var(--border-width) solid var(--light-border-color); display: flex; gap: 0.5rem; align-items: flex-end; flex-shrink: 0;">
|
<div style="padding: 1rem; border-top: var(--border-width) solid var(--light-border-color); display: flex; gap: 0.5rem; align-items: flex-end; flex-shrink: 0;">
|
||||||
<button id="chat-add-attachment" class="submit-button" onclick="clickAddAttachment(this, arguments[0])" style="margin: 0; padding: 0; width: 2.5rem; flex-shrink: 0; min-height: 2.5rem; max-height: 8rem;">
|
<button id="chat-add-attachment" class="submit-button" onclick="clickAddAttachment(this, event)" style="margin: 0; padding: 0; width: 2.5rem; flex-shrink: 0; min-height: 2.5rem; max-height: 8rem;">
|
||||||
${ICONS.plus({ width: "1.5rem", height: "1.5rem", stroke: "var(--main-bg-color)" })}
|
${ICONS.plus({ width: "1.5rem", height: "1.5rem", stroke: "var(--main-bg-color)" })}
|
||||||
</button>
|
</button>
|
||||||
<textarea id="chat-input" class="forminput" style="flex-grow: 1; margin: 0; resize: none; min-height: 2.5rem; max-height: 8rem; padding: 0.5rem;" placeholder="{blah(placeholder.message.input)}" onkeydown="handleChatInputKey(arguments[0])" oninput="handleChatInputResize(this)"></textarea>
|
<textarea id="chat-input" class="forminput" style="flex-grow: 1; margin: 0; resize: none; min-height: 2.5rem; max-height: 8rem; padding: 0.5rem;" placeholder="{blah(placeholder.message.input)}" onkeydown="handleChatInputKey(event)" oninput="handleChatInputResize(this)"></textarea>
|
||||||
<button id="chat-sendmessage" class="submit-button" onclick="sendMessage(this)" style="margin: 0; padding: 0; width: 2.5rem; flex-shrink: 0; min-height: 2.5rem; max-height: 8rem;">
|
<button id="chat-sendmessage" class="submit-button" onclick="sendMessage(this)" style="margin: 0; padding: 0; width: 2.5rem; flex-shrink: 0; min-height: 2.5rem; max-height: 8rem;">
|
||||||
${ICONS.send()}
|
${ICONS.send()}
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -236,9 +236,9 @@ var attachmentMenu = `
|
||||||
${ICONS.poll()}
|
${ICONS.poll()}
|
||||||
<span class="blah">action.attachment.poll</span>
|
<span class="blah">action.attachment.poll</span>
|
||||||
</button>
|
</button>
|
||||||
<input type="file" id="attach-take-photo" accept="image/*" capture="environment" style="display: none;" onchange="handleAttachmentSelection(arguments[0])" multiple>
|
<input type="file" id="attach-take-photo" accept="image/*" capture="environment" style="display: none;" onchange="handleAttachmentSelection(event)" multiple>
|
||||||
<input type="file" id="attach-media" accept="image/*,video/*" style="display: none;" onchange="handleAttachmentSelection(arguments[0])" multiple>
|
<input type="file" id="attach-media" accept="image/*,video/*" style="display: none;" onchange="handleAttachmentSelection(event)" multiple>
|
||||||
<input type="file" id="attach-file" style="display: none;" onchange="handleAttachmentSelection(arguments[0])" multiple>
|
<input type="file" id="attach-file" style="display: none;" onchange="handleAttachmentSelection(event)" multiple>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
var addSpaceMenu = `
|
var addSpaceMenu = `
|
||||||
|
|
@ -255,7 +255,7 @@ var messageContextMenu = `
|
||||||
${ICONS.replyAction()}
|
${ICONS.replyAction()}
|
||||||
<span class="blah">title.reply.message</span>
|
<span class="blah">title.reply.message</span>
|
||||||
</button>
|
</button>
|
||||||
<button id="context-react-btn" onclick="openReactionPicker(arguments[0])">
|
<button id="context-react-btn" onclick="openReactionPicker(event)">
|
||||||
${ICONS.react()}
|
${ICONS.react()}
|
||||||
<span class="blah">title.react.message</span>
|
<span class="blah">title.react.message</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -263,12 +263,11 @@ var messageContextMenu = `
|
||||||
${ICONS.delete({ fill: "var(--big-red)" })}
|
${ICONS.delete({ fill: "var(--big-red)" })}
|
||||||
<span class="blah" style="color: var(--big-red)">title.delete.message</span>
|
<span class="blah" style="color: var(--big-red)">title.delete.message</span>
|
||||||
</button>
|
</button>
|
||||||
<div id="context-pin-container"></div>
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
//elements
|
//elements
|
||||||
var backBtnHtml = `<button class="mobile-nav-btn" onclick="mobileNavBack()">${ICONS.arrowBack({ width: "1.5rem", height: "1.5rem" })}</button>`;
|
var backBtnHtml = `<button class="mobile-nav-btn" onclick="mobileNavBack()">${ICONS.arrowBack({ width: "1.5rem", height: "1.5rem" })}</button>`;
|
||||||
var pinBtnHtml = `<button class="topbar-action-btn room-action-pin right" onclick="showPinnedMessages()">${ICONS.pin({ width: "1.5rem", height: "1.5rem" })}</button>`;
|
var pinBtnHtml = `<button class="topbar-action-btn room-action-pin right">${ICONS.pin({ width: "1.5rem", height: "1.5rem" })}</button>`;
|
||||||
var callBtnHtml = `<button class="topbar-action-btn room-action-call right">${ICONS.call({ width: "1.5rem", height: "1.5rem" })}</button>`;
|
var callBtnHtml = `<button class="topbar-action-btn room-action-call right">${ICONS.call({ width: "1.5rem", height: "1.5rem" })}</button>`;
|
||||||
|
|
||||||
var settingsBar = `
|
var settingsBar = `
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue