add more group setting messages & fix leaving empty groups & fix image cropper default zoom
All checks were successful
Android Build / publish (push) Successful in 51s
Linux Build / publish (push) Successful in 56s

This commit is contained in:
olcxja 2026-08-22 09:18:12 +02:00
commit e975598a40
12 changed files with 288 additions and 66 deletions

View file

@ -256,6 +256,9 @@
"history.visible": "enabled", "history.visible": "enabled",
"history.hidden": "disabled", "history.hidden": "disabled",
"group.history.changed.by": "{0} changed history visibility to: {1}", "group.history.changed.by": "{0} changed history visibility to: {1}",
"group.name.changed.by": "{0} changed the group name to {1}",
"group.pfp.changed.by": "{0} changed the group icon",
"group.pfp.removed.by": "{0} removed the group icon",
"prompt.are.you.sure": "Are you sure?", "prompt.are.you.sure": "Are you sure?",
"title.unknown.group": "Unknown Group", "title.unknown.group": "Unknown Group",
"title.unknown": "Unknown", "title.unknown": "Unknown",

View file

@ -255,6 +255,9 @@
"history.visible": "włączona", "history.visible": "włączona",
"history.hidden": "wyłączona", "history.hidden": "wyłączona",
"group.history.changed.by": "{0} zmienił widoczność historii na: {1}", "group.history.changed.by": "{0} zmienił widoczność historii na: {1}",
"group.name.changed.by": "{0} zmienił(a) nazwę grupy na: {1}",
"group.pfp.changed.by": "{0} zmienił(a) ikonę grupy",
"group.pfp.removed.by": "{0} usunął(ęła) ikonę grupy",
"prompt.are.you.sure": "Czy na pewno?", "prompt.are.you.sure": "Czy na pewno?",
"title.unknown.group": "Nieznana grupa", "title.unknown.group": "Nieznana grupa",
"title.unknown": "Nieznany", "title.unknown": "Nieznany",

View file

@ -109,10 +109,7 @@
<div id="cropper-viewport-container" style="position: relative; overflow: hidden; background: #000; width: 100%; max-width: 30rem; border-radius: 0.8rem; border: var(--border-width) solid rgba(255,255,255,0.2);"> <div id="cropper-viewport-container" style="position: relative; overflow: hidden; background: #000; width: 100%; max-width: 30rem; border-radius: 0.8rem; border: var(--border-width) solid rgba(255,255,255,0.2);">
<img id="cropper-image" src="" style="position: absolute; transform-origin: 0 0; cursor: move;"> <img id="cropper-image" src="" style="position: absolute; transform-origin: 0 0; cursor: move;">
</div> </div>
<div style="display: flex; align-items: center; gap: 1rem; width: 100%; max-width: 30rem;">
<span><blah>title.zoom</blah></span>
<input type="range" id="cropper-zoom" min="0.1" max="5" step="0.01" value="1" style="flex: 1;" oninput="updateCropperZoom()">
</div>
<div style="display: flex; gap: 1rem; margin-top: 1rem; width: 100%; max-width: 30rem;"> <div style="display: flex; gap: 1rem; margin-top: 1rem; width: 100%; max-width: 30rem;">
<button class="submit-button blah" style="background: rgba(255,255,255,0.1); color: var(--text-color); flex: 1;" onclick="closeCropper()">action.cancel</button> <button class="submit-button blah" style="background: rgba(255,255,255,0.1); color: var(--text-color); flex: 1;" onclick="closeCropper()">action.cancel</button>
<button class="submit-button blah" style="flex: 1;" onclick="confirmCropper()">action.save</button> <button class="submit-button blah" style="flex: 1;" onclick="confirmCropper()">action.save</button>

View file

@ -3170,7 +3170,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" || msg.type === "larp.info.kick" || msg.type === "larp.info.transfer_owner") { 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" || msg.type === "larp.info.name" || msg.type === "larp.info.pfp" || msg.type === "larp.info.pfp_removed") {
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]) {
@ -3196,6 +3196,9 @@ async function generateMessagesHtml(entries, isPinnedContext = false) {
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.kick") actionStr = "group.user.kicked.by";
else if (msg.type === "larp.info.transfer_owner") actionStr = "group.owner.transferred.by"; else if (msg.type === "larp.info.transfer_owner") actionStr = "group.owner.transferred.by";
else if (msg.type === "larp.info.name") actionStr = "group.name.changed.by";
else if (msg.type === "larp.info.pfp") actionStr = "group.pfp.changed.by";
else if (msg.type === "larp.info.pfp_removed") actionStr = "group.pfp.removed.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>`;
@ -3203,8 +3206,9 @@ async function generateMessagesHtml(entries, isPinnedContext = false) {
if (msg.type.endsWith(".pin") || msg.type.endsWith(".unpin")) { if (msg.type.endsWith(".pin") || msg.type.endsWith(".unpin")) {
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 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>`;
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" || msg.type === "larp.info.name") {
content = textStr.replace("{0}", clickableAuthor).replace("{1}", content); let escapedContent = escapeHtml(content);
content = textStr.replace("{0}", clickableAuthor).replace("{1}", escapedContent);
} else if (msg.type === "larp.info.kick" || msg.type === "larp.info.transfer_owner") { } else if (msg.type === "larp.info.kick" || msg.type === "larp.info.transfer_owner") {
let targetUsername = ""; let targetUsername = "";
let targetId = ""; let targetId = "";
@ -4478,6 +4482,7 @@ let cropperImageObj = null;
let cropperPosX = 0; let cropperPosX = 0;
let cropperPosY = 0; let cropperPosY = 0;
let cropperBaseZoom = 1; let cropperBaseZoom = 1;
let cropperZoom = 1;
let cropperOriginalType = ''; let cropperOriginalType = '';
let cropperOriginalB64 = ''; let cropperOriginalB64 = '';
@ -4509,9 +4514,10 @@ function openCropper(input, type) {
} }
function initCropper() { function initCropper() {
document.getElementById('cropper-popup-container').classList.add('show');
let container = document.getElementById('cropper-viewport-container'); let container = document.getElementById('cropper-viewport-container');
let imgEl = document.getElementById('cropper-image'); let imgEl = document.getElementById('cropper-image');
let zoomSlider = document.getElementById('cropper-zoom');
// Set aspect ratio // Set aspect ratio
let targetWidth = cropperType === 'avatar' || cropperType === 'group-avatar' ? 512 : 494; let targetWidth = cropperType === 'avatar' || cropperType === 'group-avatar' ? 512 : 494;
@ -4533,16 +4539,12 @@ function initCropper() {
let scaleX = containerWidth / imgWidth; let scaleX = containerWidth / imgWidth;
let scaleY = (containerWidth * (targetHeight / targetWidth)) / imgHeight; let scaleY = (containerWidth * (targetHeight / targetWidth)) / imgHeight;
cropperBaseZoom = Math.max(scaleX, scaleY); cropperBaseZoom = Math.max(scaleX, scaleY);
cropperZoom = cropperBaseZoom;
zoomSlider.min = cropperBaseZoom;
zoomSlider.max = cropperBaseZoom * 5;
zoomSlider.value = cropperBaseZoom;
cropperPosX = 0; cropperPosX = 0;
cropperPosY = 0; cropperPosY = 0;
updateCropperTransform(); updateCropperTransform();
document.getElementById('cropper-popup-container').classList.add('show');
} }
function updateCropperZoom() { function updateCropperZoom() {
@ -4550,21 +4552,40 @@ function updateCropperZoom() {
} }
function updateCropperTransform() { function updateCropperTransform() {
let zoom = parseFloat(document.getElementById('cropper-zoom').value);
let imgEl = document.getElementById('cropper-image'); let imgEl = document.getElementById('cropper-image');
let container = document.getElementById('cropper-viewport-container'); let container = document.getElementById('cropper-viewport-container');
if (cropperImageObj) { if (cropperImageObj) {
let containerWidth = container.clientWidth || 300;
let targetWidth = cropperType === 'avatar' || cropperType === 'group-avatar' ? 512 : 494;
let targetHeight = cropperType === 'avatar' || cropperType === 'group-avatar' ? targetWidth : 200;
let containerHeight = container.clientHeight || (containerWidth * (targetHeight / targetWidth));
let scaleX = containerWidth / cropperImageObj.width;
let scaleY = containerHeight / cropperImageObj.height;
cropperBaseZoom = Math.max(scaleX, scaleY);
if (cropperZoom < cropperBaseZoom) cropperZoom = cropperBaseZoom;
if (cropperZoom > cropperBaseZoom * 5) cropperZoom = cropperBaseZoom * 5;
let zoom = cropperZoom;
let scaledW = cropperImageObj.width * zoom; let scaledW = cropperImageObj.width * zoom;
let scaledH = cropperImageObj.height * zoom; let scaledH = cropperImageObj.height * zoom;
let minX = container.clientWidth - scaledW; containerWidth = container.clientWidth || 300;
containerHeight = container.clientHeight || (containerWidth * (targetHeight / targetWidth));
let minX = containerWidth - scaledW;
let minY = containerHeight - scaledH;
let maxX = 0; let maxX = 0;
let minY = container.clientHeight - scaledH;
let maxY = 0; let maxY = 0;
if (minX > maxX) minX = maxX; if (scaledW < containerWidth) {
if (minY > maxY) minY = maxY; minX = maxX = (containerWidth - scaledW) / 2;
}
if (scaledH < containerHeight) {
minY = maxY = (containerHeight - scaledH) / 2;
}
if (cropperPosX < minX) cropperPosX = minX; if (cropperPosX < minX) cropperPosX = minX;
if (cropperPosX > maxX) cropperPosX = maxX; if (cropperPosX > maxX) cropperPosX = maxX;
@ -4572,7 +4593,7 @@ function updateCropperTransform() {
if (cropperPosY > maxY) cropperPosY = maxY; if (cropperPosY > maxY) cropperPosY = maxY;
} }
imgEl.style.transform = `translate(${cropperPosX}px, ${cropperPosY}px) scale(${zoom})`; imgEl.style.transform = `translate(${cropperPosX}px, ${cropperPosY}px) scale(${cropperZoom})`;
} }
function closeCropper() { function closeCropper() {
@ -4580,16 +4601,16 @@ function closeCropper() {
} }
async function confirmCropper() { async function confirmCropper() {
let zoom = parseFloat(document.getElementById('cropper-zoom').value);
let container = document.getElementById('cropper-viewport-container'); let container = document.getElementById('cropper-viewport-container');
let targetWidth = cropperType === 'avatar' || cropperType === 'group-avatar' ? 512 : 988; let targetWidth = cropperType === 'avatar' || cropperType === 'group-avatar' ? 512 : 988;
let targetHeight = cropperType === 'avatar' || cropperType === 'group-avatar' ? 512 : 400; let targetHeight = cropperType === 'avatar' || cropperType === 'group-avatar' ? 512 : 400;
let scaleRatio = targetWidth / container.clientWidth;
let scaleRatio = targetWidth / (container.clientWidth || 300);
let drawX = cropperPosX * scaleRatio; let drawX = cropperPosX * scaleRatio;
let drawY = cropperPosY * scaleRatio; let drawY = cropperPosY * scaleRatio;
let drawW = cropperImageObj.width * zoom * scaleRatio; let drawW = cropperImageObj.width * cropperZoom * scaleRatio;
let drawH = cropperImageObj.height * zoom * scaleRatio; let drawH = cropperImageObj.height * cropperZoom * scaleRatio;
let b64; let b64;
let newSrc; let newSrc;
@ -4794,9 +4815,20 @@ async function confirmCropper() {
} }
let container = document.getElementById('cropper-viewport-container'); let cropperContainer = document.getElementById('cropper-viewport-container');
if (container) { if (cropperContainer) {
let initialPinchDistance = null;
let initialPinchZoom = null;
const startDrag = (e) => { const startDrag = (e) => {
if (e.touches && e.touches.length === 2) {
isDraggingCropper = false;
let dx = e.touches[0].clientX - e.touches[1].clientX;
let dy = e.touches[0].clientY - e.touches[1].clientY;
initialPinchDistance = Math.sqrt(dx*dx + dy*dy);
initialPinchZoom = cropperZoom;
return;
}
isDraggingCropper = true; isDraggingCropper = true;
startDragX = e.clientX || (e.touches && e.touches[0].clientX); startDragX = e.clientX || (e.touches && e.touches[0].clientX);
startDragY = e.clientY || (e.touches && e.touches[0].clientY); startDragY = e.clientY || (e.touches && e.touches[0].clientY);
@ -4806,6 +4838,30 @@ if (container) {
}; };
const onDrag = (e) => { const onDrag = (e) => {
if (e.touches && e.touches.length === 2 && initialPinchDistance !== null) {
let dx = e.touches[0].clientX - e.touches[1].clientX;
let dy = e.touches[0].clientY - e.touches[1].clientY;
let distance = Math.sqrt(dx*dx + dy*dy);
let zoomFactor = distance / initialPinchDistance;
let oldZoom = cropperZoom;
cropperZoom = initialPinchZoom * zoomFactor;
if (cropperZoom < cropperBaseZoom) cropperZoom = cropperBaseZoom;
if (cropperZoom > cropperBaseZoom * 5) cropperZoom = cropperBaseZoom * 5;
let zoomRatio = cropperZoom / oldZoom;
let cw = cropperContainer.clientWidth || 300;
let ch = cropperContainer.clientHeight || 300;
cropperPosX = (cropperPosX - cw/2) * zoomRatio + cw/2;
cropperPosY = (cropperPosY - ch/2) * zoomRatio + ch/2;
updateCropperTransform();
return;
}
if (!isDraggingCropper) return; if (!isDraggingCropper) return;
let clientX = e.clientX || (e.touches && e.touches[0].clientX); let clientX = e.clientX || (e.touches && e.touches[0].clientX);
let clientY = e.clientY || (e.touches && e.touches[0].clientY); let clientY = e.clientY || (e.touches && e.touches[0].clientY);
@ -4814,16 +4870,57 @@ if (container) {
updateCropperTransform(); updateCropperTransform();
}; };
const endDrag = () => { const endDrag = (e) => {
isDraggingCropper = false; if (e.touches && e.touches.length < 2) {
initialPinchDistance = null;
initialPinchZoom = null;
}
if (e.touches && e.touches.length === 1) {
isDraggingCropper = true;
startDragX = e.touches[0].clientX;
startDragY = e.touches[0].clientY;
startPosX = cropperPosX;
startPosY = cropperPosY;
} else {
isDraggingCropper = false;
}
};
const onWheel = (e) => {
if (document.getElementById('cropper-popup-container').classList.contains('show')) {
e.preventDefault();
let oldZoom = cropperZoom;
let zoomFactor = e.deltaY > 0 ? 0.9 : 1.1;
cropperZoom *= zoomFactor;
if (cropperZoom < cropperBaseZoom) cropperZoom = cropperBaseZoom;
if (cropperZoom > cropperBaseZoom * 5) cropperZoom = cropperBaseZoom * 5;
let zoomRatio = cropperZoom / oldZoom;
let rect = cropperContainer.getBoundingClientRect();
let mouseX = e.clientX - rect.left;
let mouseY = e.clientY - rect.top;
cropperPosX = (cropperPosX - mouseX) * zoomRatio + mouseX;
cropperPosY = (cropperPosY - mouseY) * zoomRatio + mouseY;
updateCropperTransform();
}
}; };
container.addEventListener('mousedown', startDrag); cropperContainer.addEventListener('mousedown', startDrag);
container.addEventListener('touchstart', startDrag, {passive: true}); cropperContainer.addEventListener('touchstart', startDrag, {passive: true});
document.addEventListener('mousemove', onDrag); document.addEventListener('mousemove', onDrag);
document.addEventListener('touchmove', onDrag, {passive: true}); document.addEventListener('touchmove', onDrag, {passive: false});
document.addEventListener('mouseup', endDrag); document.addEventListener('mouseup', endDrag);
document.addEventListener('touchend', endDrag); document.addEventListener('touchend', endDrag);
cropperContainer.addEventListener('wheel', onWheel, {passive: false});
new ResizeObserver(() => {
if (document.getElementById('cropper-popup-container').classList.contains('show')) {
updateCropperTransform();
}
}).observe(cropperContainer);
} }
function updateDmListUI(roomId, isRead, type = 'dms', timestamp = null) { function updateDmListUI(roomId, isRead, type = 'dms', timestamp = null) {
@ -5291,7 +5388,7 @@ async function confirmLeaveGroup(groupId) {
try { try {
let payload = JSON.stringify({ string1: groupId }); let payload = JSON.stringify({ string1: groupId });
let res = await fetchEncrypted("group/leave", payload); let res = await fetchEncrypted("group/leave", payload);
if (res === "success") { if (res === "success" || res === "error:not.a.member" || res === "error:group.not.found") {
if (currentDmId === groupId) { if (currentDmId === groupId) {
currentDmId = null; currentDmId = null;
const rem = parseFloat(getComputedStyle(document.documentElement).fontSize); const rem = parseFloat(getComputedStyle(document.documentElement).fontSize);
@ -5302,6 +5399,9 @@ async function confirmLeaveGroup(groupId) {
} }
} }
refreshGroups(0, false); refreshGroups(0, false);
if (res !== "success") {
showBlahNotification(res);
}
} else if (res.startsWith("error:")) { } else if (res.startsWith("error:")) {
showBlahNotification(res); showBlahNotification(res);
} }

View file

@ -349,7 +349,7 @@ var profileSettings = `
<br> <br>
<div class="input-group" style="display: flex; flex-direction: row; align-items: stretch; justify-content: space-between; border: var(--border-width) solid var(--light-border-color); border-radius: 0.8rem; padding: 1rem; width: 100%; max-width: 30rem;"> <div class="input-group" style="display: flex; flex-direction: row; align-items: stretch; justify-content: space-between; border: var(--border-width) solid var(--light-border-color); border-radius: 0.8rem; padding: 1rem; width: 100%; max-width: 30rem;">
<img id="settings-avatar-preview" src="" style="width: 4.5rem; height: 4.5rem; border-radius: 0.8rem; border: var(--border-width) solid rgba(255, 255, 255, 0.2); object-fit: cover; background: var(--main-bg-color);"> <img id="settings-avatar-preview" src="" style="width: 4.5rem; height: 4.5rem; border-radius: 0.8rem; border: var(--border-width) solid rgba(255, 255, 255, 0.2); object-fit: cover; background: var(--main-bg-color); aspect-ratio: 1 / 1; flex-shrink: 0;">
<div style="display: flex; flex-direction: column; align-items: flex-end; justify-content: space-between; padding: 0.1rem 0;"> <div style="display: flex; flex-direction: column; align-items: flex-end; justify-content: space-between; padding: 0.1rem 0;">
<div style="font-weight: bold;"><blah>title.avatar</blah></div> <div style="font-weight: bold;"><blah>title.avatar</blah></div>
<button class="submit-button blah" style="margin: 0; padding: 0.5rem 1rem; width: auto;" onclick="document.getElementById('settings-avatar-input').click()">action.change.avatar</button> <button class="submit-button blah" style="margin: 0; padding: 0.5rem 1rem; width: auto;" onclick="document.getElementById('settings-avatar-input').click()">action.change.avatar</button>
@ -381,7 +381,7 @@ var profileSettings = `
var groupSettingsScreen = ` var groupSettingsScreen = `
<div class="auth-box popup-auto-height" style="margin-top:2rem"> <div class="auth-box popup-auto-height" style="margin-top:2rem">
<div class="input-group" style="display: flex; flex-direction: row; align-items: stretch; justify-content: space-between; border: var(--border-width) solid var(--light-border-color); border-radius: 0.8rem; padding: 1rem; width: 100%; max-width: 30rem;"> <div class="input-group" style="display: flex; flex-direction: row; align-items: stretch; justify-content: space-between; border: var(--border-width) solid var(--light-border-color); border-radius: 0.8rem; padding: 1rem; width: 100%; max-width: 30rem;">
<img id="group-settings-avatar-preview" src="" style="width: 4.5rem; height: 4.5rem; border-radius: 0.8rem; border: var(--border-width) solid rgba(255, 255, 255, 0.2); object-fit: cover; background: var(--main-bg-color);"> <img id="group-settings-avatar-preview" src="" style="width: 4.5rem; height: 4.5rem; border-radius: 0.8rem; border: var(--border-width) solid rgba(255, 255, 255, 0.2); object-fit: cover; background: var(--main-bg-color); aspect-ratio: 1 / 1; flex-shrink: 0;">
<div style="display: flex; flex-direction: column; align-items: flex-end; justify-content: space-between; padding: 0.1rem 0;"> <div style="display: flex; flex-direction: column; align-items: flex-end; justify-content: space-between; padding: 0.1rem 0;">
<div style="font-weight: bold;"><blah>title.avatar</blah></div> <div style="font-weight: bold;"><blah>title.avatar</blah></div>
<button class="submit-button blah" style="margin: 0; padding: 0.5rem 1rem; width: auto;" onclick="document.getElementById('group-settings-avatar-input').click()">action.change.avatar</button> <button class="submit-button blah" style="margin: 0; padding: 0.5rem 1rem; width: auto;" onclick="document.getElementById('group-settings-avatar-input').click()">action.change.avatar</button>

View file

@ -172,6 +172,8 @@ indicator.active {
height: calc(var(--icon-button-height) - (var(--border-width) * 2)); height: calc(var(--icon-button-height) - (var(--border-width) * 2));
pointer-events: none !important; pointer-events: none !important;
border-radius: 0.65rem; border-radius: 0.65rem;
aspect-ratio: 1 / 1;
flex-shrink: 0;
} }
button { button {
@ -646,6 +648,8 @@ space{
border-radius: 0.8rem; border-radius: 0.8rem;
border: var(--border-width) solid rgba(255, 255, 255, 0.2); border: var(--border-width) solid rgba(255, 255, 255, 0.2);
object-fit: cover; object-fit: cover;
aspect-ratio: 1 / 1;
flex-shrink: 0;
} }
.invite-details { .invite-details {
display: flex; display: flex;
@ -691,6 +695,8 @@ space{
border: var(--border-width) solid rgba(255, 255, 255, 0.2); border: var(--border-width) solid rgba(255, 255, 255, 0.2);
object-fit: cover; object-fit: cover;
pointer-events: none; pointer-events: none;
aspect-ratio: 1 / 1;
flex-shrink: 0;
} }
.room-name { .room-name {
font-weight: 600; font-weight: 600;
@ -727,6 +733,7 @@ space{
object-fit: cover; object-fit: cover;
flex-shrink: 0; flex-shrink: 0;
border: var(--border-width) solid rgba(255, 255, 255, 0.2); border: var(--border-width) solid rgba(255, 255, 255, 0.2);
aspect-ratio: 1 / 1;
} }
.chat-message-content { .chat-message-content {
display: flex; display: flex;
@ -973,6 +980,7 @@ button.profile-action-btn.popup-btn-danger:hover, button.submit-button.popup-btn
border: var(--border-width) solid rgba(255, 255, 255, 0.2); border: var(--border-width) solid rgba(255, 255, 255, 0.2);
object-fit: cover; object-fit: cover;
background: var(--main-bg-color); background: var(--main-bg-color);
aspect-ratio: 1 / 1;
} }
.profile-info { .profile-info {
padding: 1rem 2rem 2rem 2rem; padding: 1rem 2rem 2rem 2rem;

View file

@ -256,6 +256,9 @@
"history.visible": "enabled", "history.visible": "enabled",
"history.hidden": "disabled", "history.hidden": "disabled",
"group.history.changed.by": "{0} changed history visibility to: {1}", "group.history.changed.by": "{0} changed history visibility to: {1}",
"group.name.changed.by": "{0} changed the group name to {1}",
"group.pfp.changed.by": "{0} changed the group icon",
"group.pfp.removed.by": "{0} removed the group icon",
"prompt.are.you.sure": "Are you sure?", "prompt.are.you.sure": "Are you sure?",
"title.unknown.group": "Unknown Group", "title.unknown.group": "Unknown Group",
"title.unknown": "Unknown", "title.unknown": "Unknown",

View file

@ -255,6 +255,9 @@
"history.visible": "włączona", "history.visible": "włączona",
"history.hidden": "wyłączona", "history.hidden": "wyłączona",
"group.history.changed.by": "{0} zmienił widoczność historii na: {1}", "group.history.changed.by": "{0} zmienił widoczność historii na: {1}",
"group.name.changed.by": "{0} zmienił(a) nazwę grupy na: {1}",
"group.pfp.changed.by": "{0} zmienił(a) ikonę grupy",
"group.pfp.removed.by": "{0} usunął(ęła) ikonę grupy",
"prompt.are.you.sure": "Czy na pewno?", "prompt.are.you.sure": "Czy na pewno?",
"title.unknown.group": "Nieznana grupa", "title.unknown.group": "Nieznana grupa",
"title.unknown": "Nieznany", "title.unknown": "Nieznany",

View file

@ -109,10 +109,7 @@
<div id="cropper-viewport-container" style="position: relative; overflow: hidden; background: #000; width: 100%; max-width: 30rem; border-radius: 0.8rem; border: var(--border-width) solid rgba(255,255,255,0.2);"> <div id="cropper-viewport-container" style="position: relative; overflow: hidden; background: #000; width: 100%; max-width: 30rem; border-radius: 0.8rem; border: var(--border-width) solid rgba(255,255,255,0.2);">
<img id="cropper-image" src="" style="position: absolute; transform-origin: 0 0; cursor: move;"> <img id="cropper-image" src="" style="position: absolute; transform-origin: 0 0; cursor: move;">
</div> </div>
<div style="display: flex; align-items: center; gap: 1rem; width: 100%; max-width: 30rem;">
<span><blah>title.zoom</blah></span>
<input type="range" id="cropper-zoom" min="0.1" max="5" step="0.01" value="1" style="flex: 1;" oninput="updateCropperZoom()">
</div>
<div style="display: flex; gap: 1rem; margin-top: 1rem; width: 100%; max-width: 30rem;"> <div style="display: flex; gap: 1rem; margin-top: 1rem; width: 100%; max-width: 30rem;">
<button class="submit-button blah" style="background: rgba(255,255,255,0.1); color: var(--text-color); flex: 1;" onclick="closeCropper()">action.cancel</button> <button class="submit-button blah" style="background: rgba(255,255,255,0.1); color: var(--text-color); flex: 1;" onclick="closeCropper()">action.cancel</button>
<button class="submit-button blah" style="flex: 1;" onclick="confirmCropper()">action.save</button> <button class="submit-button blah" style="flex: 1;" onclick="confirmCropper()">action.save</button>

View file

@ -3170,7 +3170,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" || msg.type === "larp.info.kick" || msg.type === "larp.info.transfer_owner") { 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" || msg.type === "larp.info.name" || msg.type === "larp.info.pfp" || msg.type === "larp.info.pfp_removed") {
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]) {
@ -3196,6 +3196,9 @@ async function generateMessagesHtml(entries, isPinnedContext = false) {
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.kick") actionStr = "group.user.kicked.by";
else if (msg.type === "larp.info.transfer_owner") actionStr = "group.owner.transferred.by"; else if (msg.type === "larp.info.transfer_owner") actionStr = "group.owner.transferred.by";
else if (msg.type === "larp.info.name") actionStr = "group.name.changed.by";
else if (msg.type === "larp.info.pfp") actionStr = "group.pfp.changed.by";
else if (msg.type === "larp.info.pfp_removed") actionStr = "group.pfp.removed.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>`;
@ -3203,8 +3206,9 @@ async function generateMessagesHtml(entries, isPinnedContext = false) {
if (msg.type.endsWith(".pin") || msg.type.endsWith(".unpin")) { if (msg.type.endsWith(".pin") || msg.type.endsWith(".unpin")) {
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 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>`;
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" || msg.type === "larp.info.name") {
content = textStr.replace("{0}", clickableAuthor).replace("{1}", content); let escapedContent = escapeHtml(content);
content = textStr.replace("{0}", clickableAuthor).replace("{1}", escapedContent);
} else if (msg.type === "larp.info.kick" || msg.type === "larp.info.transfer_owner") { } else if (msg.type === "larp.info.kick" || msg.type === "larp.info.transfer_owner") {
let targetUsername = ""; let targetUsername = "";
let targetId = ""; let targetId = "";
@ -4478,6 +4482,7 @@ let cropperImageObj = null;
let cropperPosX = 0; let cropperPosX = 0;
let cropperPosY = 0; let cropperPosY = 0;
let cropperBaseZoom = 1; let cropperBaseZoom = 1;
let cropperZoom = 1;
let cropperOriginalType = ''; let cropperOriginalType = '';
let cropperOriginalB64 = ''; let cropperOriginalB64 = '';
@ -4509,9 +4514,10 @@ function openCropper(input, type) {
} }
function initCropper() { function initCropper() {
document.getElementById('cropper-popup-container').classList.add('show');
let container = document.getElementById('cropper-viewport-container'); let container = document.getElementById('cropper-viewport-container');
let imgEl = document.getElementById('cropper-image'); let imgEl = document.getElementById('cropper-image');
let zoomSlider = document.getElementById('cropper-zoom');
// Set aspect ratio // Set aspect ratio
let targetWidth = cropperType === 'avatar' || cropperType === 'group-avatar' ? 512 : 494; let targetWidth = cropperType === 'avatar' || cropperType === 'group-avatar' ? 512 : 494;
@ -4533,16 +4539,12 @@ function initCropper() {
let scaleX = containerWidth / imgWidth; let scaleX = containerWidth / imgWidth;
let scaleY = (containerWidth * (targetHeight / targetWidth)) / imgHeight; let scaleY = (containerWidth * (targetHeight / targetWidth)) / imgHeight;
cropperBaseZoom = Math.max(scaleX, scaleY); cropperBaseZoom = Math.max(scaleX, scaleY);
cropperZoom = cropperBaseZoom;
zoomSlider.min = cropperBaseZoom;
zoomSlider.max = cropperBaseZoom * 5;
zoomSlider.value = cropperBaseZoom;
cropperPosX = 0; cropperPosX = 0;
cropperPosY = 0; cropperPosY = 0;
updateCropperTransform(); updateCropperTransform();
document.getElementById('cropper-popup-container').classList.add('show');
} }
function updateCropperZoom() { function updateCropperZoom() {
@ -4550,21 +4552,40 @@ function updateCropperZoom() {
} }
function updateCropperTransform() { function updateCropperTransform() {
let zoom = parseFloat(document.getElementById('cropper-zoom').value);
let imgEl = document.getElementById('cropper-image'); let imgEl = document.getElementById('cropper-image');
let container = document.getElementById('cropper-viewport-container'); let container = document.getElementById('cropper-viewport-container');
if (cropperImageObj) { if (cropperImageObj) {
let containerWidth = container.clientWidth || 300;
let targetWidth = cropperType === 'avatar' || cropperType === 'group-avatar' ? 512 : 494;
let targetHeight = cropperType === 'avatar' || cropperType === 'group-avatar' ? targetWidth : 200;
let containerHeight = container.clientHeight || (containerWidth * (targetHeight / targetWidth));
let scaleX = containerWidth / cropperImageObj.width;
let scaleY = containerHeight / cropperImageObj.height;
cropperBaseZoom = Math.max(scaleX, scaleY);
if (cropperZoom < cropperBaseZoom) cropperZoom = cropperBaseZoom;
if (cropperZoom > cropperBaseZoom * 5) cropperZoom = cropperBaseZoom * 5;
let zoom = cropperZoom;
let scaledW = cropperImageObj.width * zoom; let scaledW = cropperImageObj.width * zoom;
let scaledH = cropperImageObj.height * zoom; let scaledH = cropperImageObj.height * zoom;
let minX = container.clientWidth - scaledW; containerWidth = container.clientWidth || 300;
containerHeight = container.clientHeight || (containerWidth * (targetHeight / targetWidth));
let minX = containerWidth - scaledW;
let minY = containerHeight - scaledH;
let maxX = 0; let maxX = 0;
let minY = container.clientHeight - scaledH;
let maxY = 0; let maxY = 0;
if (minX > maxX) minX = maxX; if (scaledW < containerWidth) {
if (minY > maxY) minY = maxY; minX = maxX = (containerWidth - scaledW) / 2;
}
if (scaledH < containerHeight) {
minY = maxY = (containerHeight - scaledH) / 2;
}
if (cropperPosX < minX) cropperPosX = minX; if (cropperPosX < minX) cropperPosX = minX;
if (cropperPosX > maxX) cropperPosX = maxX; if (cropperPosX > maxX) cropperPosX = maxX;
@ -4572,7 +4593,7 @@ function updateCropperTransform() {
if (cropperPosY > maxY) cropperPosY = maxY; if (cropperPosY > maxY) cropperPosY = maxY;
} }
imgEl.style.transform = `translate(${cropperPosX}px, ${cropperPosY}px) scale(${zoom})`; imgEl.style.transform = `translate(${cropperPosX}px, ${cropperPosY}px) scale(${cropperZoom})`;
} }
function closeCropper() { function closeCropper() {
@ -4580,16 +4601,16 @@ function closeCropper() {
} }
async function confirmCropper() { async function confirmCropper() {
let zoom = parseFloat(document.getElementById('cropper-zoom').value);
let container = document.getElementById('cropper-viewport-container'); let container = document.getElementById('cropper-viewport-container');
let targetWidth = cropperType === 'avatar' || cropperType === 'group-avatar' ? 512 : 988; let targetWidth = cropperType === 'avatar' || cropperType === 'group-avatar' ? 512 : 988;
let targetHeight = cropperType === 'avatar' || cropperType === 'group-avatar' ? 512 : 400; let targetHeight = cropperType === 'avatar' || cropperType === 'group-avatar' ? 512 : 400;
let scaleRatio = targetWidth / container.clientWidth;
let scaleRatio = targetWidth / (container.clientWidth || 300);
let drawX = cropperPosX * scaleRatio; let drawX = cropperPosX * scaleRatio;
let drawY = cropperPosY * scaleRatio; let drawY = cropperPosY * scaleRatio;
let drawW = cropperImageObj.width * zoom * scaleRatio; let drawW = cropperImageObj.width * cropperZoom * scaleRatio;
let drawH = cropperImageObj.height * zoom * scaleRatio; let drawH = cropperImageObj.height * cropperZoom * scaleRatio;
let b64; let b64;
let newSrc; let newSrc;
@ -4794,9 +4815,20 @@ async function confirmCropper() {
} }
let container = document.getElementById('cropper-viewport-container'); let cropperContainer = document.getElementById('cropper-viewport-container');
if (container) { if (cropperContainer) {
let initialPinchDistance = null;
let initialPinchZoom = null;
const startDrag = (e) => { const startDrag = (e) => {
if (e.touches && e.touches.length === 2) {
isDraggingCropper = false;
let dx = e.touches[0].clientX - e.touches[1].clientX;
let dy = e.touches[0].clientY - e.touches[1].clientY;
initialPinchDistance = Math.sqrt(dx*dx + dy*dy);
initialPinchZoom = cropperZoom;
return;
}
isDraggingCropper = true; isDraggingCropper = true;
startDragX = e.clientX || (e.touches && e.touches[0].clientX); startDragX = e.clientX || (e.touches && e.touches[0].clientX);
startDragY = e.clientY || (e.touches && e.touches[0].clientY); startDragY = e.clientY || (e.touches && e.touches[0].clientY);
@ -4806,6 +4838,30 @@ if (container) {
}; };
const onDrag = (e) => { const onDrag = (e) => {
if (e.touches && e.touches.length === 2 && initialPinchDistance !== null) {
let dx = e.touches[0].clientX - e.touches[1].clientX;
let dy = e.touches[0].clientY - e.touches[1].clientY;
let distance = Math.sqrt(dx*dx + dy*dy);
let zoomFactor = distance / initialPinchDistance;
let oldZoom = cropperZoom;
cropperZoom = initialPinchZoom * zoomFactor;
if (cropperZoom < cropperBaseZoom) cropperZoom = cropperBaseZoom;
if (cropperZoom > cropperBaseZoom * 5) cropperZoom = cropperBaseZoom * 5;
let zoomRatio = cropperZoom / oldZoom;
let cw = cropperContainer.clientWidth || 300;
let ch = cropperContainer.clientHeight || 300;
cropperPosX = (cropperPosX - cw/2) * zoomRatio + cw/2;
cropperPosY = (cropperPosY - ch/2) * zoomRatio + ch/2;
updateCropperTransform();
return;
}
if (!isDraggingCropper) return; if (!isDraggingCropper) return;
let clientX = e.clientX || (e.touches && e.touches[0].clientX); let clientX = e.clientX || (e.touches && e.touches[0].clientX);
let clientY = e.clientY || (e.touches && e.touches[0].clientY); let clientY = e.clientY || (e.touches && e.touches[0].clientY);
@ -4814,16 +4870,57 @@ if (container) {
updateCropperTransform(); updateCropperTransform();
}; };
const endDrag = () => { const endDrag = (e) => {
isDraggingCropper = false; if (e.touches && e.touches.length < 2) {
initialPinchDistance = null;
initialPinchZoom = null;
}
if (e.touches && e.touches.length === 1) {
isDraggingCropper = true;
startDragX = e.touches[0].clientX;
startDragY = e.touches[0].clientY;
startPosX = cropperPosX;
startPosY = cropperPosY;
} else {
isDraggingCropper = false;
}
};
const onWheel = (e) => {
if (document.getElementById('cropper-popup-container').classList.contains('show')) {
e.preventDefault();
let oldZoom = cropperZoom;
let zoomFactor = e.deltaY > 0 ? 0.9 : 1.1;
cropperZoom *= zoomFactor;
if (cropperZoom < cropperBaseZoom) cropperZoom = cropperBaseZoom;
if (cropperZoom > cropperBaseZoom * 5) cropperZoom = cropperBaseZoom * 5;
let zoomRatio = cropperZoom / oldZoom;
let rect = cropperContainer.getBoundingClientRect();
let mouseX = e.clientX - rect.left;
let mouseY = e.clientY - rect.top;
cropperPosX = (cropperPosX - mouseX) * zoomRatio + mouseX;
cropperPosY = (cropperPosY - mouseY) * zoomRatio + mouseY;
updateCropperTransform();
}
}; };
container.addEventListener('mousedown', startDrag); cropperContainer.addEventListener('mousedown', startDrag);
container.addEventListener('touchstart', startDrag, {passive: true}); cropperContainer.addEventListener('touchstart', startDrag, {passive: true});
document.addEventListener('mousemove', onDrag); document.addEventListener('mousemove', onDrag);
document.addEventListener('touchmove', onDrag, {passive: true}); document.addEventListener('touchmove', onDrag, {passive: false});
document.addEventListener('mouseup', endDrag); document.addEventListener('mouseup', endDrag);
document.addEventListener('touchend', endDrag); document.addEventListener('touchend', endDrag);
cropperContainer.addEventListener('wheel', onWheel, {passive: false});
new ResizeObserver(() => {
if (document.getElementById('cropper-popup-container').classList.contains('show')) {
updateCropperTransform();
}
}).observe(cropperContainer);
} }
function updateDmListUI(roomId, isRead, type = 'dms', timestamp = null) { function updateDmListUI(roomId, isRead, type = 'dms', timestamp = null) {
@ -5291,7 +5388,7 @@ async function confirmLeaveGroup(groupId) {
try { try {
let payload = JSON.stringify({ string1: groupId }); let payload = JSON.stringify({ string1: groupId });
let res = await fetchEncrypted("group/leave", payload); let res = await fetchEncrypted("group/leave", payload);
if (res === "success") { if (res === "success" || res === "error:not.a.member" || res === "error:group.not.found") {
if (currentDmId === groupId) { if (currentDmId === groupId) {
currentDmId = null; currentDmId = null;
const rem = parseFloat(getComputedStyle(document.documentElement).fontSize); const rem = parseFloat(getComputedStyle(document.documentElement).fontSize);
@ -5302,6 +5399,9 @@ async function confirmLeaveGroup(groupId) {
} }
} }
refreshGroups(0, false); refreshGroups(0, false);
if (res !== "success") {
showBlahNotification(res);
}
} else if (res.startsWith("error:")) { } else if (res.startsWith("error:")) {
showBlahNotification(res); showBlahNotification(res);
} }

View file

@ -349,7 +349,7 @@ var profileSettings = `
<br> <br>
<div class="input-group" style="display: flex; flex-direction: row; align-items: stretch; justify-content: space-between; border: var(--border-width) solid var(--light-border-color); border-radius: 0.8rem; padding: 1rem; width: 100%; max-width: 30rem;"> <div class="input-group" style="display: flex; flex-direction: row; align-items: stretch; justify-content: space-between; border: var(--border-width) solid var(--light-border-color); border-radius: 0.8rem; padding: 1rem; width: 100%; max-width: 30rem;">
<img id="settings-avatar-preview" src="" style="width: 4.5rem; height: 4.5rem; border-radius: 0.8rem; border: var(--border-width) solid rgba(255, 255, 255, 0.2); object-fit: cover; background: var(--main-bg-color);"> <img id="settings-avatar-preview" src="" style="width: 4.5rem; height: 4.5rem; border-radius: 0.8rem; border: var(--border-width) solid rgba(255, 255, 255, 0.2); object-fit: cover; background: var(--main-bg-color); aspect-ratio: 1 / 1; flex-shrink: 0;">
<div style="display: flex; flex-direction: column; align-items: flex-end; justify-content: space-between; padding: 0.1rem 0;"> <div style="display: flex; flex-direction: column; align-items: flex-end; justify-content: space-between; padding: 0.1rem 0;">
<div style="font-weight: bold;"><blah>title.avatar</blah></div> <div style="font-weight: bold;"><blah>title.avatar</blah></div>
<button class="submit-button blah" style="margin: 0; padding: 0.5rem 1rem; width: auto;" onclick="document.getElementById('settings-avatar-input').click()">action.change.avatar</button> <button class="submit-button blah" style="margin: 0; padding: 0.5rem 1rem; width: auto;" onclick="document.getElementById('settings-avatar-input').click()">action.change.avatar</button>
@ -381,7 +381,7 @@ var profileSettings = `
var groupSettingsScreen = ` var groupSettingsScreen = `
<div class="auth-box popup-auto-height" style="margin-top:2rem"> <div class="auth-box popup-auto-height" style="margin-top:2rem">
<div class="input-group" style="display: flex; flex-direction: row; align-items: stretch; justify-content: space-between; border: var(--border-width) solid var(--light-border-color); border-radius: 0.8rem; padding: 1rem; width: 100%; max-width: 30rem;"> <div class="input-group" style="display: flex; flex-direction: row; align-items: stretch; justify-content: space-between; border: var(--border-width) solid var(--light-border-color); border-radius: 0.8rem; padding: 1rem; width: 100%; max-width: 30rem;">
<img id="group-settings-avatar-preview" src="" style="width: 4.5rem; height: 4.5rem; border-radius: 0.8rem; border: var(--border-width) solid rgba(255, 255, 255, 0.2); object-fit: cover; background: var(--main-bg-color);"> <img id="group-settings-avatar-preview" src="" style="width: 4.5rem; height: 4.5rem; border-radius: 0.8rem; border: var(--border-width) solid rgba(255, 255, 255, 0.2); object-fit: cover; background: var(--main-bg-color); aspect-ratio: 1 / 1; flex-shrink: 0;">
<div style="display: flex; flex-direction: column; align-items: flex-end; justify-content: space-between; padding: 0.1rem 0;"> <div style="display: flex; flex-direction: column; align-items: flex-end; justify-content: space-between; padding: 0.1rem 0;">
<div style="font-weight: bold;"><blah>title.avatar</blah></div> <div style="font-weight: bold;"><blah>title.avatar</blah></div>
<button class="submit-button blah" style="margin: 0; padding: 0.5rem 1rem; width: auto;" onclick="document.getElementById('group-settings-avatar-input').click()">action.change.avatar</button> <button class="submit-button blah" style="margin: 0; padding: 0.5rem 1rem; width: auto;" onclick="document.getElementById('group-settings-avatar-input').click()">action.change.avatar</button>

View file

@ -172,6 +172,8 @@ indicator.active {
height: calc(var(--icon-button-height) - (var(--border-width) * 2)); height: calc(var(--icon-button-height) - (var(--border-width) * 2));
pointer-events: none !important; pointer-events: none !important;
border-radius: 0.65rem; border-radius: 0.65rem;
aspect-ratio: 1 / 1;
flex-shrink: 0;
} }
button { button {
@ -646,6 +648,8 @@ space{
border-radius: 0.8rem; border-radius: 0.8rem;
border: var(--border-width) solid rgba(255, 255, 255, 0.2); border: var(--border-width) solid rgba(255, 255, 255, 0.2);
object-fit: cover; object-fit: cover;
aspect-ratio: 1 / 1;
flex-shrink: 0;
} }
.invite-details { .invite-details {
display: flex; display: flex;
@ -691,6 +695,8 @@ space{
border: var(--border-width) solid rgba(255, 255, 255, 0.2); border: var(--border-width) solid rgba(255, 255, 255, 0.2);
object-fit: cover; object-fit: cover;
pointer-events: none; pointer-events: none;
aspect-ratio: 1 / 1;
flex-shrink: 0;
} }
.room-name { .room-name {
font-weight: 600; font-weight: 600;
@ -727,6 +733,7 @@ space{
object-fit: cover; object-fit: cover;
flex-shrink: 0; flex-shrink: 0;
border: var(--border-width) solid rgba(255, 255, 255, 0.2); border: var(--border-width) solid rgba(255, 255, 255, 0.2);
aspect-ratio: 1 / 1;
} }
.chat-message-content { .chat-message-content {
display: flex; display: flex;
@ -973,6 +980,7 @@ button.profile-action-btn.popup-btn-danger:hover, button.submit-button.popup-btn
border: var(--border-width) solid rgba(255, 255, 255, 0.2); border: var(--border-width) solid rgba(255, 255, 255, 0.2);
object-fit: cover; object-fit: cover;
background: var(--main-bg-color); background: var(--main-bg-color);
aspect-ratio: 1 / 1;
} }
.profile-info { .profile-info {
padding: 1rem 2rem 2rem 2rem; padding: 1rem 2rem 2rem 2rem;