improved ui and added missing blahs
All checks were successful
Android Build / publish (push) Successful in 28s
Linux Build / publish (push) Successful in 51s

This commit is contained in:
olcxja 2026-05-29 10:13:58 +02:00
commit 5c91c341a0
12 changed files with 168 additions and 44 deletions

View file

@ -119,6 +119,12 @@
"title.invitation.code": "invitation code",
"title.back.to.register": "back to registration",
"placeholder.username": "cat name",
"placeholder.invitation.code": "invitation code",
"placeholder.captcha.code": "captcha code"
"placeholder.captcha.code": "captcha code",
"placeholder.message.input": "meow...",
"desc.messages.encryption.active": "meows are end-to-end encrypted :3",
"desc.no.dms": "no direct meowchats :c",
"action.dm.opening": "opening meowchat...",
"dm.open.failed": "failed to open meowchat :c",
"dm.messages.fetch.failed": "failed to fetch meows :c",
"messages.decrypt.failed": "failed to decrypt meow :c"
}

View file

@ -118,6 +118,12 @@
"title.invitation.code": "Invitation code",
"title.back.to.register": "Back to registration",
"placeholder.username": "username",
"placeholder.invitation.code": "invitation code",
"placeholder.captcha.code": "captcha code"
"placeholder.captcha.code": "captcha code",
"placeholder.message.input": "Message...",
"desc.messages.encryption.active": "Messages are end-to-end encrypted",
"desc.no.dms": "No direct messages",
"action.dm.opening": "Opening DM...",
"dm.open.failed": "Failed to open DM",
"dm.messages.fetch.failed": "Failed to fetch messages",
"messages.decrypt.failed": "Failed to decrypt message"
}

View file

@ -109,8 +109,6 @@
//if fails continue loading encryption may be broken
console.error(e);
}
await delay(600); //because we need to wair for animations
await refreshDms();
} else {
showBlahNotification("error:auth.failed.redirect.to.login");
await delay(2000);
@ -126,14 +124,19 @@
}
}
async function refreshDms() {
async function refreshDms(waittime = 0) {
try {
showAction("action.dm.fetch", "dmrefresh");
await delay(waittime);
if (window.cachedDms && typeof renderDms === 'function') {
await renderDms(window.cachedDms);
}
let res = await fetchEncrypted("user/dm/list");
clearAction("dmrefresh");
window.cachedDms = res;
if (typeof renderDms === 'function') {
await renderDms(res);
}
clearAction("dmrefresh");
}
catch (e) {
clearAction("dmrefresh");

View file

@ -1235,16 +1235,27 @@ function clickCollapseDms()
{
var collapseDmsBtn = document.getElementById("collapse-dms");
collapseDmsBtn.classList.toggle("collapsed");
var dmsList = document.getElementById("dms-list");
if (dmsList) {
dmsList.style.display = collapseDmsBtn.classList.contains("collapsed") ? "none" : "";
var dmsWrapper = document.getElementById("dms-wrapper");
if (dmsWrapper) {
if (collapseDmsBtn.classList.contains("collapsed")) {
dmsWrapper.classList.add("collapsed");
} else {
dmsWrapper.classList.remove("collapsed");
}
}
}
function clickCollapseGroups()
{
var collapseGroupsBtn = document.getElementById("collapse-groups");
collapseGroupsBtn.classList.toggle("collapsed");
var groupsWrapper = document.getElementById("groups-wrapper");
if (groupsWrapper) {
if (collapseGroupsBtn.classList.contains("collapsed")) {
groupsWrapper.classList.add("collapsed");
} else {
groupsWrapper.classList.remove("collapsed");
}
}
}
function clickAddGroup()
{
@ -1361,9 +1372,11 @@ async function renderInvites(res, type) {
if (invite.type === "dm") {
let id = invite.id;
if (!id.includes(":")) {
id += `:${host}`;
}
let onlyId = id.split(":")[0];
let username = await fetchAsync(`${url}/idtoname?id=${id}`);
if (!(username && username.trim() !== "")) return;
count++;
@ -1374,7 +1387,7 @@ async function renderInvites(res, type) {
let desc = "";
if (type === "received") {
desc = `:desc.invite.dm.received:${username};${id}`;
desc = `:desc.invite.dm.received:${username};${onlyId}`;
actions = `
<button class="icon-button" style="background-color: var(--big-green);" onclick="acceptInvite('${id}')">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="#fff"><path d="M382-240 154-468l57-57 171 171 367-367 57 57-424 424Z"/></svg>
@ -1384,7 +1397,7 @@ async function renderInvites(res, type) {
</button>
`;
} else {
desc = `:desc.invite.dm.sent:${username};${id}`;
desc = `:desc.invite.dm.sent:${username};${onlyId}`;
actions = `
<button class="icon-button" style="background-color: var(--big-red);" onclick="revokeInvite('${id}')">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="#fff"><path d="m256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z"/></svg>
@ -1419,10 +1432,16 @@ async function renderInvites(res, type) {
}
}
window.cachedInvites = { 'received': null, 'sent': null };
async function loadInvites(tab) {
try {
showAction(`action.fetching.invites.${tab === 'received' ? 'recv' : 'sent'}`, `fetching.invites.${tab}`);
if (window.cachedInvites[tab]) {
await renderInvites(window.cachedInvites[tab], tab);
}
let res = await fetchEncrypted(`user/invites/${tab}`);
window.cachedInvites[tab] = res;
await renderInvites(res, tab);
} catch (e) {
showBlahNotification("error:something.wrong");
@ -1474,7 +1493,6 @@ async function acceptInvite(targetId) { //TODO: Implement key generation
clearAction("invite.action");
showBlahNotification(res || "success:invite.accepted");
await loadInvites('received');
await refreshDms();
} catch (e) {
clearAction("invite.action");
showBlahNotification("error:something.wrong");
@ -1551,12 +1569,13 @@ function gotoJoinSpace() {
switchRoomContent("title.join.space", joinSpaceScreen, false);
}
function gotoHome() {
async function gotoHome() {
if (roomsBarContainer) roomsBarContainer.style.display = ""; //show roombar
setActiveSidebarIndicator(sidebarHomeIndicator);
switchRoomContent("title.splash", splashScreen, false);
switchRoomsBar("title.home", homeRoomBar);
setActiveRoombarItem(null);
await refreshDms(210);
}
function setActiveSidebarIndicator(element, isTemporary = false) {
@ -1714,9 +1733,9 @@ async function renderDms(res) {
html += `
<button class="room-entry" id="dm-btn-${dmId}" onclick="openDm('${dmId}', '${targetUsername}', '${targetId}')">
<img src="${pfp}" class="room-pfp">
<div style="display: flex; flex-direction: column; text-align: left; overflow: hidden; width: 100%;">
<div style="display: flex; flex-direction: column; text-align: left; overflow: hidden; width: 100%; margin-left: 0.37rem;">
<span class="room-name">${displayName}</span>
<span class="room-id" style="font-size: 0.8rem; opacity: 0.6; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">${targetUsername}</span>
<span class="room-id" style="font-size: 0.8rem; opacity: 0.6; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">${targetUsername} (${targetId.split(';')[0]})</span>
</div>
</button>
`;
@ -1758,7 +1777,7 @@ async function openDm(dmId, username, targetId) {
currentDmId = dmId;
let pfp = await getAvatarUrl(targetId, username);
let iconHtml = `<img src="${pfp}" style="width: 1.8rem; height: 1.8rem; border-radius: 0.4rem; margin-right: 0.5rem; object-fit: cover;">`;
let iconHtml = `<img src="${pfp}" style="width: 1.8rem; height: 1.8rem; border-radius: 0.4rem; margin-right: 0.5rem; object-fit: cover; border: var(--border-width) solid rgba(255, 255, 255, 0.2);">`;
await switchRoomContent(username.split(':')[0], chatScreen, false, iconHtml);
let msgContainer = document.getElementById("chat-messages");

View file

@ -161,7 +161,11 @@ var homeRoomBar = `
</svg>
</button>
</div>
<div id="dms-list" class="list-container empty" style="padding: 0 0.5rem;"></div>
<div id="dms-wrapper" class="collapsible-wrapper">
<div class="collapsible-inner">
<div id="dms-list" class="list-container no-flex-grow empty" style="padding: 0 0.5rem;"></div>
</div>
</div>
<div class="sidebar-section-header">
<button class="collapse-text-button" id="collapse-groups" onclick="clickCollapseGroups()">
@ -179,6 +183,11 @@ var homeRoomBar = `
</svg>
</button>
</div>
<div id="groups-wrapper" class="collapsible-wrapper">
<div class="collapsible-inner">
<div id="groups-list" class="list-container no-flex-grow empty" style="padding: 0 0.5rem;"></div>
</div>
</div>
`;
var inboxRoomBar = `

View file

@ -524,6 +524,24 @@ space{
opacity: 0.5;
}
.list-container.no-flex-grow {
flex-grow: 0;
}
.collapsible-wrapper {
display: grid;
grid-template-rows: 1fr;
transition: grid-template-rows 0.25s cubic-bezier(0.25, 1, 0.5, 1);
}
.collapsible-wrapper.collapsed {
grid-template-rows: 0fr;
}
.collapsible-inner {
overflow: hidden;
}
.invite-entry {
display: flex;
align-items: center;
@ -574,14 +592,15 @@ space{
.room-entry {
display: flex;
align-items: center;
gap: 0.8rem;
padding: 0.5rem 0.8rem;
gap: 0.1rem;
padding: 1.5rem 0.6rem;
width: 100%;
border: none;
background: transparent;
border-radius: 0.8rem;
flex-shrink: 0;
cursor: pointer;
margin: 0;
}
.room-pfp {
width: 2.2rem;

View file

@ -119,6 +119,12 @@
"title.invitation.code": "invitation code",
"title.back.to.register": "back to registration",
"placeholder.username": "cat name",
"placeholder.invitation.code": "invitation code",
"placeholder.captcha.code": "captcha code"
"placeholder.captcha.code": "captcha code",
"placeholder.message.input": "meow...",
"desc.messages.encryption.active": "meows are end-to-end encrypted :3",
"desc.no.dms": "no direct meowchats :c",
"action.dm.opening": "opening meowchat...",
"dm.open.failed": "failed to open meowchat :c",
"dm.messages.fetch.failed": "failed to fetch meows :c",
"messages.decrypt.failed": "failed to decrypt meow :c"
}

View file

@ -118,6 +118,12 @@
"title.invitation.code": "Invitation code",
"title.back.to.register": "Back to registration",
"placeholder.username": "username",
"placeholder.invitation.code": "invitation code",
"placeholder.captcha.code": "captcha code"
"placeholder.captcha.code": "captcha code",
"placeholder.message.input": "Message...",
"desc.messages.encryption.active": "Messages are end-to-end encrypted",
"desc.no.dms": "No direct messages",
"action.dm.opening": "Opening DM...",
"dm.open.failed": "Failed to open DM",
"dm.messages.fetch.failed": "Failed to fetch messages",
"messages.decrypt.failed": "Failed to decrypt message"
}

View file

@ -109,8 +109,6 @@
//if fails continue loading encryption may be broken
console.error(e);
}
await delay(600); //because we need to wair for animations
await refreshDms();
} else {
showBlahNotification("error:auth.failed.redirect.to.login");
await delay(2000);
@ -126,14 +124,19 @@
}
}
async function refreshDms() {
async function refreshDms(waittime = 0) {
try {
showAction("action.dm.fetch", "dmrefresh");
await delay(waittime);
if (window.cachedDms && typeof renderDms === 'function') {
await renderDms(window.cachedDms);
}
let res = await fetchEncrypted("user/dm/list");
clearAction("dmrefresh");
window.cachedDms = res;
if (typeof renderDms === 'function') {
await renderDms(res);
}
clearAction("dmrefresh");
}
catch (e) {
clearAction("dmrefresh");

View file

@ -1235,16 +1235,27 @@ function clickCollapseDms()
{
var collapseDmsBtn = document.getElementById("collapse-dms");
collapseDmsBtn.classList.toggle("collapsed");
var dmsList = document.getElementById("dms-list");
if (dmsList) {
dmsList.style.display = collapseDmsBtn.classList.contains("collapsed") ? "none" : "";
var dmsWrapper = document.getElementById("dms-wrapper");
if (dmsWrapper) {
if (collapseDmsBtn.classList.contains("collapsed")) {
dmsWrapper.classList.add("collapsed");
} else {
dmsWrapper.classList.remove("collapsed");
}
}
}
function clickCollapseGroups()
{
var collapseGroupsBtn = document.getElementById("collapse-groups");
collapseGroupsBtn.classList.toggle("collapsed");
var groupsWrapper = document.getElementById("groups-wrapper");
if (groupsWrapper) {
if (collapseGroupsBtn.classList.contains("collapsed")) {
groupsWrapper.classList.add("collapsed");
} else {
groupsWrapper.classList.remove("collapsed");
}
}
}
function clickAddGroup()
{
@ -1361,9 +1372,11 @@ async function renderInvites(res, type) {
if (invite.type === "dm") {
let id = invite.id;
if (!id.includes(":")) {
id += `:${host}`;
}
let onlyId = id.split(":")[0];
let username = await fetchAsync(`${url}/idtoname?id=${id}`);
if (!(username && username.trim() !== "")) return;
count++;
@ -1374,7 +1387,7 @@ async function renderInvites(res, type) {
let desc = "";
if (type === "received") {
desc = `:desc.invite.dm.received:${username};${id}`;
desc = `:desc.invite.dm.received:${username};${onlyId}`;
actions = `
<button class="icon-button" style="background-color: var(--big-green);" onclick="acceptInvite('${id}')">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="#fff"><path d="M382-240 154-468l57-57 171 171 367-367 57 57-424 424Z"/></svg>
@ -1384,7 +1397,7 @@ async function renderInvites(res, type) {
</button>
`;
} else {
desc = `:desc.invite.dm.sent:${username};${id}`;
desc = `:desc.invite.dm.sent:${username};${onlyId}`;
actions = `
<button class="icon-button" style="background-color: var(--big-red);" onclick="revokeInvite('${id}')">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="#fff"><path d="m256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z"/></svg>
@ -1419,10 +1432,16 @@ async function renderInvites(res, type) {
}
}
window.cachedInvites = { 'received': null, 'sent': null };
async function loadInvites(tab) {
try {
showAction(`action.fetching.invites.${tab === 'received' ? 'recv' : 'sent'}`, `fetching.invites.${tab}`);
if (window.cachedInvites[tab]) {
await renderInvites(window.cachedInvites[tab], tab);
}
let res = await fetchEncrypted(`user/invites/${tab}`);
window.cachedInvites[tab] = res;
await renderInvites(res, tab);
} catch (e) {
showBlahNotification("error:something.wrong");
@ -1474,7 +1493,6 @@ async function acceptInvite(targetId) { //TODO: Implement key generation
clearAction("invite.action");
showBlahNotification(res || "success:invite.accepted");
await loadInvites('received');
await refreshDms();
} catch (e) {
clearAction("invite.action");
showBlahNotification("error:something.wrong");
@ -1551,12 +1569,13 @@ function gotoJoinSpace() {
switchRoomContent("title.join.space", joinSpaceScreen, false);
}
function gotoHome() {
async function gotoHome() {
if (roomsBarContainer) roomsBarContainer.style.display = ""; //show roombar
setActiveSidebarIndicator(sidebarHomeIndicator);
switchRoomContent("title.splash", splashScreen, false);
switchRoomsBar("title.home", homeRoomBar);
setActiveRoombarItem(null);
await refreshDms(210);
}
function setActiveSidebarIndicator(element, isTemporary = false) {
@ -1714,9 +1733,9 @@ async function renderDms(res) {
html += `
<button class="room-entry" id="dm-btn-${dmId}" onclick="openDm('${dmId}', '${targetUsername}', '${targetId}')">
<img src="${pfp}" class="room-pfp">
<div style="display: flex; flex-direction: column; text-align: left; overflow: hidden; width: 100%;">
<div style="display: flex; flex-direction: column; text-align: left; overflow: hidden; width: 100%; margin-left: 0.37rem;">
<span class="room-name">${displayName}</span>
<span class="room-id" style="font-size: 0.8rem; opacity: 0.6; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">${targetUsername}</span>
<span class="room-id" style="font-size: 0.8rem; opacity: 0.6; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">${targetUsername} (${targetId.split(';')[0]})</span>
</div>
</button>
`;
@ -1758,7 +1777,7 @@ async function openDm(dmId, username, targetId) {
currentDmId = dmId;
let pfp = await getAvatarUrl(targetId, username);
let iconHtml = `<img src="${pfp}" style="width: 1.8rem; height: 1.8rem; border-radius: 0.4rem; margin-right: 0.5rem; object-fit: cover;">`;
let iconHtml = `<img src="${pfp}" style="width: 1.8rem; height: 1.8rem; border-radius: 0.4rem; margin-right: 0.5rem; object-fit: cover; border: var(--border-width) solid rgba(255, 255, 255, 0.2);">`;
await switchRoomContent(username.split(':')[0], chatScreen, false, iconHtml);
let msgContainer = document.getElementById("chat-messages");

View file

@ -161,7 +161,11 @@ var homeRoomBar = `
</svg>
</button>
</div>
<div id="dms-list" class="list-container empty" style="padding: 0 0.5rem;"></div>
<div id="dms-wrapper" class="collapsible-wrapper">
<div class="collapsible-inner">
<div id="dms-list" class="list-container no-flex-grow empty" style="padding: 0 0.5rem;"></div>
</div>
</div>
<div class="sidebar-section-header">
<button class="collapse-text-button" id="collapse-groups" onclick="clickCollapseGroups()">
@ -179,6 +183,11 @@ var homeRoomBar = `
</svg>
</button>
</div>
<div id="groups-wrapper" class="collapsible-wrapper">
<div class="collapsible-inner">
<div id="groups-list" class="list-container no-flex-grow empty" style="padding: 0 0.5rem;"></div>
</div>
</div>
`;
var inboxRoomBar = `

View file

@ -524,6 +524,24 @@ space{
opacity: 0.5;
}
.list-container.no-flex-grow {
flex-grow: 0;
}
.collapsible-wrapper {
display: grid;
grid-template-rows: 1fr;
transition: grid-template-rows 0.25s cubic-bezier(0.25, 1, 0.5, 1);
}
.collapsible-wrapper.collapsed {
grid-template-rows: 0fr;
}
.collapsible-inner {
overflow: hidden;
}
.invite-entry {
display: flex;
align-items: center;
@ -574,14 +592,15 @@ space{
.room-entry {
display: flex;
align-items: center;
gap: 0.8rem;
padding: 0.5rem 0.8rem;
gap: 0.1rem;
padding: 1.5rem 0.6rem;
width: 100%;
border: none;
background: transparent;
border-radius: 0.8rem;
flex-shrink: 0;
cursor: pointer;
margin: 0;
}
.room-pfp {
width: 2.2rem;