better context menu reposition, add more profile actions
This commit is contained in:
parent
516bdf5186
commit
3f99389413
10 changed files with 260 additions and 60 deletions
|
|
@ -144,5 +144,11 @@
|
||||||
"title.settings": "cat settings",
|
"title.settings": "cat settings",
|
||||||
"title.logout": "log out",
|
"title.logout": "log out",
|
||||||
"action.edit.profile": "edit cat profile",
|
"action.edit.profile": "edit cat profile",
|
||||||
"action.open.dm": "open direct meowchat"
|
"action.open.dm": "open direct meowchat",
|
||||||
|
"action.invite.accept": "accept meow invite",
|
||||||
|
"action.invite.cancel": "hiss away invite",
|
||||||
|
"action.invite.send": "invite to meowchat",
|
||||||
|
"action.profile.loading": "sniffing profile...",
|
||||||
|
"error.profile.load.failed": "failed to sniff profile.",
|
||||||
|
"unknown.author": "somecat"
|
||||||
}
|
}
|
||||||
|
|
@ -144,5 +144,11 @@
|
||||||
"title.settings": "Settings",
|
"title.settings": "Settings",
|
||||||
"title.logout": "Log out",
|
"title.logout": "Log out",
|
||||||
"action.edit.profile": "Edit profile",
|
"action.edit.profile": "Edit profile",
|
||||||
"action.open.dm": "Open DM"
|
"action.open.dm": "Open DM",
|
||||||
|
"action.invite.accept": "Accept invite",
|
||||||
|
"action.invite.cancel": "Cancel invite",
|
||||||
|
"action.invite.send": "Invite to DM",
|
||||||
|
"action.profile.loading": "Loading profile...",
|
||||||
|
"error.profile.load.failed": "Failed to load profile.",
|
||||||
|
"unknown.author": "Someone"
|
||||||
}
|
}
|
||||||
|
|
@ -1467,7 +1467,7 @@ async function openProfile(accountId) {
|
||||||
let popupInner = document.getElementById("profile-popup-inner");
|
let popupInner = document.getElementById("profile-popup-inner");
|
||||||
|
|
||||||
//show loading
|
//show loading
|
||||||
popupInner.innerHTML = '<div style="display: flex; justify-content: center; align-items: center; height: 100%;"><p>Loading profile...</p></div>';
|
popupInner.innerHTML = `<div style="display: flex; justify-content: center; align-items: center; height: 100%;"><p>${processBlah('action.profile.loading')}</p></div>`;
|
||||||
popupContainer.style.display = "flex";
|
popupContainer.style.display = "flex";
|
||||||
|
|
||||||
//force reflow
|
//force reflow
|
||||||
|
|
@ -1496,9 +1496,77 @@ async function openProfile(accountId) {
|
||||||
let bioHtml = bio && bio.trim() !== "" ? `<div class="profile-bio">${bio.replace(/</g, "<").replace(/>/g, ">")}</div>` : "";
|
let bioHtml = bio && bio.trim() !== "" ? `<div class="profile-bio">${bio.replace(/</g, "<").replace(/>/g, ">")}</div>` : "";
|
||||||
|
|
||||||
let isOwnProfile = accountId === id;
|
let isOwnProfile = accountId === id;
|
||||||
let actionBtnHtml = isOwnProfile ?
|
let actionBtnHtml = "";
|
||||||
`<button class="profile-action-btn">Edit Profile</button>` :
|
if (isOwnProfile) {
|
||||||
`<button class="profile-action-btn">Open DM</button>`;
|
actionBtnHtml = `<button class="profile-action-btn">${processBlah('action.edit.profile')}</button>`;
|
||||||
|
} else {
|
||||||
|
let hasDm = false;
|
||||||
|
let dmRoomId = "";
|
||||||
|
if (window.cachedDms) {
|
||||||
|
try {
|
||||||
|
let parsedDms = JSON.parse(window.cachedDms);
|
||||||
|
if (parsedDms.dms) {
|
||||||
|
for (let dmId in parsedDms.dms) {
|
||||||
|
let parts = dmId.split('_');
|
||||||
|
if (parts.length >= 2) {
|
||||||
|
let accStandard = accountId.replace(':', ';');
|
||||||
|
if (!accStandard.includes(';')) accStandard += ';' + host;
|
||||||
|
|
||||||
|
let p0Standard = parts[0].replace(':', ';');
|
||||||
|
if (!p0Standard.includes(';')) p0Standard += ';' + host;
|
||||||
|
|
||||||
|
let p1Standard = parts[1].replace(':', ';');
|
||||||
|
if (!p1Standard.includes(';')) p1Standard += ';' + host;
|
||||||
|
|
||||||
|
if (p0Standard === accStandard || p1Standard === accStandard) {
|
||||||
|
hasDm = true;
|
||||||
|
dmRoomId = dmId;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch(e){}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasDm) {
|
||||||
|
actionBtnHtml = `<button class="profile-action-btn" onclick="openDm('${dmRoomId}', '${profileName}', '${accountId}'); closeProfile();">${processBlah('action.open.dm')}</button>`;
|
||||||
|
} else {
|
||||||
|
let sentInvite = false;
|
||||||
|
let receivedInvite = false;
|
||||||
|
if (window.cachedInvites) {
|
||||||
|
let checkInvite = (type) => {
|
||||||
|
if (window.cachedInvites[type]) {
|
||||||
|
try {
|
||||||
|
let invites = JSON.parse(window.cachedInvites[type]);
|
||||||
|
if (invites.dms) {
|
||||||
|
for(let key in invites.dms) {
|
||||||
|
let accStandard = accountId.replace(':', ';');
|
||||||
|
if (!accStandard.includes(';')) accStandard += ';' + host;
|
||||||
|
|
||||||
|
let keyStandard = key.replace(':', ';');
|
||||||
|
if (!keyStandard.includes(';')) keyStandard += ';' + host;
|
||||||
|
|
||||||
|
if (keyStandard === accStandard) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch(e){}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
sentInvite = checkInvite('sent');
|
||||||
|
receivedInvite = checkInvite('received');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (receivedInvite) {
|
||||||
|
actionBtnHtml = `<button class="profile-action-btn" onclick="acceptInvite('${accountId}'); closeProfile();">${processBlah('action.invite.accept')}</button>`;
|
||||||
|
} else if (sentInvite) {
|
||||||
|
actionBtnHtml = `<button class="profile-action-btn" onclick="revokeInvite('${accountId}'); closeProfile();">${processBlah('action.invite.cancel')}</button>`;
|
||||||
|
} else {
|
||||||
|
actionBtnHtml = `<button class="profile-action-btn" onclick="sendInviteTo('${accountId}'); closeProfile();">${processBlah('action.invite.send')}</button>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
popupInner.innerHTML = `
|
popupInner.innerHTML = `
|
||||||
${bannerHtml}
|
${bannerHtml}
|
||||||
|
|
@ -1508,14 +1576,14 @@ async function openProfile(accountId) {
|
||||||
<div class="profile-info">
|
<div class="profile-info">
|
||||||
<div>
|
<div>
|
||||||
<div class="profile-name">${profileName}</div>
|
<div class="profile-name">${profileName}</div>
|
||||||
<div class="profile-id-host">${profileName}:${profileHost} (${accountId})</div>
|
<div class="profile-id-host">${profileName}:${profileHost} (${accountId.split(':')[0]})</div>
|
||||||
</div>
|
</div>
|
||||||
${bioHtml}
|
${bioHtml}
|
||||||
${actionBtnHtml}
|
${actionBtnHtml}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
popupInner.innerHTML = '<div style="display: flex; justify-content: center; align-items: center; height: 100%;"><p>Failed to load profile.</p></div>';
|
popupInner.innerHTML = `<div style="display: flex; justify-content: center; align-items: center; height: 100%;"><p>${processBlah('error.profile.load.failed')}</p></div>`;
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1608,7 +1676,8 @@ async function renderInvites(res, type) {
|
||||||
.replaceAll("{username}", username.split(':')[0])
|
.replaceAll("{username}", username.split(':')[0])
|
||||||
.replaceAll("{pfp}", pfp)
|
.replaceAll("{pfp}", pfp)
|
||||||
.replaceAll("{desc}", desc)
|
.replaceAll("{desc}", desc)
|
||||||
.replaceAll("{actions}", actions);
|
.replaceAll("{actions}", actions)
|
||||||
|
.replaceAll("{id}", id);
|
||||||
|
|
||||||
html += entry;
|
html += entry;
|
||||||
}
|
}
|
||||||
|
|
@ -1659,6 +1728,19 @@ async function switchInvitesTab(tab) {
|
||||||
await loadInvites(tab);
|
await loadInvites(tab);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function sendInviteTo(targetId) {
|
||||||
|
try {
|
||||||
|
showAction("action.invite.sending", "invite.action");
|
||||||
|
let res = await fetchEncrypted("user/dm/invite", targetId);
|
||||||
|
clearAction("invite.action");
|
||||||
|
showBlahNotification(res || "success:invite.sent");
|
||||||
|
if (typeof loadInvites === 'function') await loadInvites('sent');
|
||||||
|
} catch (e) {
|
||||||
|
clearAction("invite.action");
|
||||||
|
showBlahNotification("error:something.wrong");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function acceptInvite(targetId) { //TODO: Implement key generation
|
async function acceptInvite(targetId) { //TODO: Implement key generation
|
||||||
try {
|
try {
|
||||||
showAction("action.invite.accepting", "invite.action");
|
showAction("action.invite.accepting", "invite.action");
|
||||||
|
|
@ -1769,10 +1851,12 @@ function gotoJoinSpace() {
|
||||||
switchRoomContent("title.join.space", joinSpaceScreen, false);
|
switchRoomContent("title.join.space", joinSpaceScreen, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function gotoHome() {
|
async function gotoHome(splash = true) {
|
||||||
if (roomsBarContainer) roomsBarContainer.style.display = ""; //show roombar
|
if (roomsBarContainer) roomsBarContainer.style.display = ""; //show roombar
|
||||||
setActiveSidebarIndicator(sidebarHomeIndicator);
|
setActiveSidebarIndicator(sidebarHomeIndicator);
|
||||||
switchRoomContent("title.splash", splashScreen, false);
|
if (splash) {
|
||||||
|
switchRoomContent("title.splash", splashScreen, false);
|
||||||
|
}
|
||||||
switchRoomsBar("title.home", homeRoomBar);
|
switchRoomsBar("title.home", homeRoomBar);
|
||||||
setActiveRoombarItem(null);
|
setActiveRoombarItem(null);
|
||||||
setupWebSocket();
|
setupWebSocket();
|
||||||
|
|
@ -2010,6 +2094,11 @@ async function openDm(dmId, username, targetId) {
|
||||||
|
|
||||||
let pfp = await getAvatarUrl(targetId, username);
|
let pfp = await getAvatarUrl(targetId, username);
|
||||||
let iconHtml = `<img src="${pfp}" style="width: 2.3rem; border-radius: 0.65rem; margin-right: 0.38rem; object-fit: cover; border: var(--border-width) solid rgba(255, 255, 255, 0.2);">`;
|
let iconHtml = `<img src="${pfp}" style="width: 2.3rem; border-radius: 0.65rem; margin-right: 0.38rem; object-fit: cover; border: var(--border-width) solid rgba(255, 255, 255, 0.2);">`;
|
||||||
|
|
||||||
|
if (currentRoomsBarTitle !== "title.home") {
|
||||||
|
gotoHome(false);
|
||||||
|
}
|
||||||
|
|
||||||
await switchRoomContent(username.split(':')[0], chatScreen, true, iconHtml);
|
await switchRoomContent(username.split(':')[0], chatScreen, true, iconHtml);
|
||||||
|
|
||||||
let msgContainer = document.getElementById("chat-messages");
|
let msgContainer = document.getElementById("chat-messages");
|
||||||
|
|
@ -2155,7 +2244,7 @@ async function renderMessages(messages, isPrepend = false) {
|
||||||
respondedAuthorId = parts[1];
|
respondedAuthorId = parts[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
let replyAuthor = "Someone";
|
let replyAuthor = processBlah('unknown.author');
|
||||||
let needsAsyncFetch = false;
|
let needsAsyncFetch = false;
|
||||||
|
|
||||||
if (loadedMessages[respondedId] && loadedMessages[respondedId].author !== "0") {
|
if (loadedMessages[respondedId] && loadedMessages[respondedId].author !== "0") {
|
||||||
|
|
@ -2183,7 +2272,7 @@ async function renderMessages(messages, isPrepend = false) {
|
||||||
window.replyIdCounter = (window.replyIdCounter || 0) + 1;
|
window.replyIdCounter = (window.replyIdCounter || 0) + 1;
|
||||||
if (window.replyIdCounter > 1000000) window.replyIdCounter = 0;
|
if (window.replyIdCounter > 1000000) window.replyIdCounter = 0;
|
||||||
randId = "reply-author-" + window.replyIdCounter;
|
randId = "reply-author-" + window.replyIdCounter;
|
||||||
replyAuthor = "Someone";
|
replyAuthor = processBlah('unknown.author');
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
try {
|
try {
|
||||||
let aId = respondedAuthorId;
|
let aId = respondedAuthorId;
|
||||||
|
|
@ -2262,10 +2351,10 @@ async function renderMessages(messages, isPrepend = false) {
|
||||||
|
|
||||||
html += `
|
html += `
|
||||||
<div class="chat-message ${extraClass}" data-msg-id="${msgId}" id="msg-${msgId}">
|
<div class="chat-message ${extraClass}" data-msg-id="${msgId}" id="msg-${msgId}">
|
||||||
${showAvatar ? `<img src="${pfp}" class="chat-message-pfp">` : `<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(event, '${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">${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}
|
||||||
|
|
@ -2307,8 +2396,16 @@ window.visualViewport?.addEventListener("resize", async () => {
|
||||||
|
|
||||||
isAdjusting = true;
|
isAdjusting = true;
|
||||||
while (isAdjusting) {
|
while (isAdjusting) {
|
||||||
|
//chat scroll fix
|
||||||
ignoreScroll = true;
|
ignoreScroll = true;
|
||||||
container.scrollTop = container.scrollHeight - container.clientHeight - lastChatScroll;
|
container.scrollTop = container.scrollHeight - container.clientHeight - lastChatScroll;
|
||||||
|
|
||||||
|
//context menu fix
|
||||||
|
let menuRect = fixedContextMenu.getBoundingClientRect();
|
||||||
|
if (menuRect.bottom > window.innerHeight) {
|
||||||
|
fixedContextMenu.style.top = `${Math.max(10, window.innerHeight - menuRect.height - 10)}px`;
|
||||||
|
}
|
||||||
|
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
ignoreScroll = false;
|
ignoreScroll = false;
|
||||||
});
|
});
|
||||||
|
|
@ -2585,7 +2682,7 @@ async function replyMessage(msgId) {
|
||||||
let bar = document.getElementById("replying-bar");
|
let bar = document.getElementById("replying-bar");
|
||||||
if (bar) {
|
if (bar) {
|
||||||
let msg = loadedMessages[msgId];
|
let msg = loadedMessages[msgId];
|
||||||
let authorName = "Someone";
|
let authorName = processBlah('unknown.author');
|
||||||
if (msg && msg.author !== "0" && dmUsernameCache[msg.author]) {
|
if (msg && msg.author !== "0" && dmUsernameCache[msg.author]) {
|
||||||
authorName = dmUsernameCache[msg.author].split(':')[0];
|
authorName = dmUsernameCache[msg.author].split(':')[0];
|
||||||
}
|
}
|
||||||
|
|
@ -2670,16 +2767,7 @@ function openReactionPicker(e) {
|
||||||
let rect = fixedContextMenu.getBoundingClientRect();
|
let rect = fixedContextMenu.getBoundingClientRect();
|
||||||
showFixedContextMenu({top: rect.top, right: rect.left, bottom: rect.top, left: rect.left}, reactionPickerMenuHtml);
|
showFixedContextMenu({top: rect.top, right: rect.left, bottom: rect.top, left: rect.left}, reactionPickerMenuHtml);
|
||||||
let searchInput = document.getElementById('reaction-picker-search');
|
let searchInput = document.getElementById('reaction-picker-search');
|
||||||
if (searchInput) {
|
|
||||||
searchInput.addEventListener('focus', () => { //TODO: use code from resize event
|
|
||||||
setTimeout(() => {
|
|
||||||
let menuRect = fixedContextMenu.getBoundingClientRect();
|
|
||||||
if (menuRect.bottom > window.innerHeight) {
|
|
||||||
fixedContextMenu.style.top = `${Math.max(10, window.innerHeight - menuRect.height - 10)}px`;
|
|
||||||
}
|
|
||||||
}, 500);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function reactMessagePrompt(msgId, quickReaction = null) {
|
async function reactMessagePrompt(msgId, quickReaction = null) {
|
||||||
|
|
|
||||||
|
|
@ -117,9 +117,9 @@ var notifisScreen = `
|
||||||
var invitesEntry = `
|
var invitesEntry = `
|
||||||
<div class="invite-entry">
|
<div class="invite-entry">
|
||||||
<div class="invite-info">
|
<div class="invite-info">
|
||||||
<img src="{pfp}" class="invite-pfp">
|
<img src="{pfp}" class="invite-pfp" onclick="openProfile('{id}')" style="cursor: pointer;">
|
||||||
<div class="invite-details">
|
<div class="invite-details">
|
||||||
<span class="invite-name">{username}</span>
|
<span class="invite-name" onclick="openProfile('{id}')" style="cursor: pointer;">{username}</span>
|
||||||
<span class="invite-desc"><blah>{desc}</blah></span>
|
<span class="invite-desc"><blah>{desc}</blah></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -811,14 +811,14 @@ sidebarelement:has(.icon-button:active) indicator:not(.active) {
|
||||||
box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.5);
|
box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.5);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: scale(0.6);
|
transform: scale(0.6);
|
||||||
transition: transform 0.2s cubic-bezier(0.4, -0.3, 0.7, 1), opacity 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
.profile-popup-container.show .profile-popup-content {
|
.profile-popup-container.show .profile-popup-content {
|
||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transition: transform 0.25s cubic-bezier(0.2, 1.15, 0.4, 1), opacity 0.25s ease;
|
transition: all 0.2s ease;
|
||||||
}
|
}
|
||||||
.profile-popup-close-btn {
|
.profile-popup-close-btn {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
|
||||||
|
|
@ -144,5 +144,11 @@
|
||||||
"title.settings": "cat settings",
|
"title.settings": "cat settings",
|
||||||
"title.logout": "log out",
|
"title.logout": "log out",
|
||||||
"action.edit.profile": "edit cat profile",
|
"action.edit.profile": "edit cat profile",
|
||||||
"action.open.dm": "open direct meowchat"
|
"action.open.dm": "open direct meowchat",
|
||||||
|
"action.invite.accept": "accept meow invite",
|
||||||
|
"action.invite.cancel": "hiss away invite",
|
||||||
|
"action.invite.send": "invite to meowchat",
|
||||||
|
"action.profile.loading": "sniffing profile...",
|
||||||
|
"error.profile.load.failed": "failed to sniff profile.",
|
||||||
|
"unknown.author": "somecat"
|
||||||
}
|
}
|
||||||
|
|
@ -144,5 +144,11 @@
|
||||||
"title.settings": "Settings",
|
"title.settings": "Settings",
|
||||||
"title.logout": "Log out",
|
"title.logout": "Log out",
|
||||||
"action.edit.profile": "Edit profile",
|
"action.edit.profile": "Edit profile",
|
||||||
"action.open.dm": "Open DM"
|
"action.open.dm": "Open DM",
|
||||||
|
"action.invite.accept": "Accept invite",
|
||||||
|
"action.invite.cancel": "Cancel invite",
|
||||||
|
"action.invite.send": "Invite to DM",
|
||||||
|
"action.profile.loading": "Loading profile...",
|
||||||
|
"error.profile.load.failed": "Failed to load profile.",
|
||||||
|
"unknown.author": "Someone"
|
||||||
}
|
}
|
||||||
136
webroot/main.js
136
webroot/main.js
|
|
@ -1467,7 +1467,7 @@ async function openProfile(accountId) {
|
||||||
let popupInner = document.getElementById("profile-popup-inner");
|
let popupInner = document.getElementById("profile-popup-inner");
|
||||||
|
|
||||||
//show loading
|
//show loading
|
||||||
popupInner.innerHTML = '<div style="display: flex; justify-content: center; align-items: center; height: 100%;"><p>Loading profile...</p></div>';
|
popupInner.innerHTML = `<div style="display: flex; justify-content: center; align-items: center; height: 100%;"><p>${processBlah('action.profile.loading')}</p></div>`;
|
||||||
popupContainer.style.display = "flex";
|
popupContainer.style.display = "flex";
|
||||||
|
|
||||||
//force reflow
|
//force reflow
|
||||||
|
|
@ -1496,9 +1496,77 @@ async function openProfile(accountId) {
|
||||||
let bioHtml = bio && bio.trim() !== "" ? `<div class="profile-bio">${bio.replace(/</g, "<").replace(/>/g, ">")}</div>` : "";
|
let bioHtml = bio && bio.trim() !== "" ? `<div class="profile-bio">${bio.replace(/</g, "<").replace(/>/g, ">")}</div>` : "";
|
||||||
|
|
||||||
let isOwnProfile = accountId === id;
|
let isOwnProfile = accountId === id;
|
||||||
let actionBtnHtml = isOwnProfile ?
|
let actionBtnHtml = "";
|
||||||
`<button class="profile-action-btn">Edit Profile</button>` :
|
if (isOwnProfile) {
|
||||||
`<button class="profile-action-btn">Open DM</button>`;
|
actionBtnHtml = `<button class="profile-action-btn">${processBlah('action.edit.profile')}</button>`;
|
||||||
|
} else {
|
||||||
|
let hasDm = false;
|
||||||
|
let dmRoomId = "";
|
||||||
|
if (window.cachedDms) {
|
||||||
|
try {
|
||||||
|
let parsedDms = JSON.parse(window.cachedDms);
|
||||||
|
if (parsedDms.dms) {
|
||||||
|
for (let dmId in parsedDms.dms) {
|
||||||
|
let parts = dmId.split('_');
|
||||||
|
if (parts.length >= 2) {
|
||||||
|
let accStandard = accountId.replace(':', ';');
|
||||||
|
if (!accStandard.includes(';')) accStandard += ';' + host;
|
||||||
|
|
||||||
|
let p0Standard = parts[0].replace(':', ';');
|
||||||
|
if (!p0Standard.includes(';')) p0Standard += ';' + host;
|
||||||
|
|
||||||
|
let p1Standard = parts[1].replace(':', ';');
|
||||||
|
if (!p1Standard.includes(';')) p1Standard += ';' + host;
|
||||||
|
|
||||||
|
if (p0Standard === accStandard || p1Standard === accStandard) {
|
||||||
|
hasDm = true;
|
||||||
|
dmRoomId = dmId;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch(e){}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasDm) {
|
||||||
|
actionBtnHtml = `<button class="profile-action-btn" onclick="openDm('${dmRoomId}', '${profileName}', '${accountId}'); closeProfile();">${processBlah('action.open.dm')}</button>`;
|
||||||
|
} else {
|
||||||
|
let sentInvite = false;
|
||||||
|
let receivedInvite = false;
|
||||||
|
if (window.cachedInvites) {
|
||||||
|
let checkInvite = (type) => {
|
||||||
|
if (window.cachedInvites[type]) {
|
||||||
|
try {
|
||||||
|
let invites = JSON.parse(window.cachedInvites[type]);
|
||||||
|
if (invites.dms) {
|
||||||
|
for(let key in invites.dms) {
|
||||||
|
let accStandard = accountId.replace(':', ';');
|
||||||
|
if (!accStandard.includes(';')) accStandard += ';' + host;
|
||||||
|
|
||||||
|
let keyStandard = key.replace(':', ';');
|
||||||
|
if (!keyStandard.includes(';')) keyStandard += ';' + host;
|
||||||
|
|
||||||
|
if (keyStandard === accStandard) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch(e){}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
sentInvite = checkInvite('sent');
|
||||||
|
receivedInvite = checkInvite('received');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (receivedInvite) {
|
||||||
|
actionBtnHtml = `<button class="profile-action-btn" onclick="acceptInvite('${accountId}'); closeProfile();">${processBlah('action.invite.accept')}</button>`;
|
||||||
|
} else if (sentInvite) {
|
||||||
|
actionBtnHtml = `<button class="profile-action-btn" onclick="revokeInvite('${accountId}'); closeProfile();">${processBlah('action.invite.cancel')}</button>`;
|
||||||
|
} else {
|
||||||
|
actionBtnHtml = `<button class="profile-action-btn" onclick="sendInviteTo('${accountId}'); closeProfile();">${processBlah('action.invite.send')}</button>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
popupInner.innerHTML = `
|
popupInner.innerHTML = `
|
||||||
${bannerHtml}
|
${bannerHtml}
|
||||||
|
|
@ -1508,14 +1576,14 @@ async function openProfile(accountId) {
|
||||||
<div class="profile-info">
|
<div class="profile-info">
|
||||||
<div>
|
<div>
|
||||||
<div class="profile-name">${profileName}</div>
|
<div class="profile-name">${profileName}</div>
|
||||||
<div class="profile-id-host">${profileName}:${profileHost} (${accountId})</div>
|
<div class="profile-id-host">${profileName}:${profileHost} (${accountId.split(':')[0]})</div>
|
||||||
</div>
|
</div>
|
||||||
${bioHtml}
|
${bioHtml}
|
||||||
${actionBtnHtml}
|
${actionBtnHtml}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
popupInner.innerHTML = '<div style="display: flex; justify-content: center; align-items: center; height: 100%;"><p>Failed to load profile.</p></div>';
|
popupInner.innerHTML = `<div style="display: flex; justify-content: center; align-items: center; height: 100%;"><p>${processBlah('error.profile.load.failed')}</p></div>`;
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1608,7 +1676,8 @@ async function renderInvites(res, type) {
|
||||||
.replaceAll("{username}", username.split(':')[0])
|
.replaceAll("{username}", username.split(':')[0])
|
||||||
.replaceAll("{pfp}", pfp)
|
.replaceAll("{pfp}", pfp)
|
||||||
.replaceAll("{desc}", desc)
|
.replaceAll("{desc}", desc)
|
||||||
.replaceAll("{actions}", actions);
|
.replaceAll("{actions}", actions)
|
||||||
|
.replaceAll("{id}", id);
|
||||||
|
|
||||||
html += entry;
|
html += entry;
|
||||||
}
|
}
|
||||||
|
|
@ -1659,6 +1728,19 @@ async function switchInvitesTab(tab) {
|
||||||
await loadInvites(tab);
|
await loadInvites(tab);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function sendInviteTo(targetId) {
|
||||||
|
try {
|
||||||
|
showAction("action.invite.sending", "invite.action");
|
||||||
|
let res = await fetchEncrypted("user/dm/invite", targetId);
|
||||||
|
clearAction("invite.action");
|
||||||
|
showBlahNotification(res || "success:invite.sent");
|
||||||
|
if (typeof loadInvites === 'function') await loadInvites('sent');
|
||||||
|
} catch (e) {
|
||||||
|
clearAction("invite.action");
|
||||||
|
showBlahNotification("error:something.wrong");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function acceptInvite(targetId) { //TODO: Implement key generation
|
async function acceptInvite(targetId) { //TODO: Implement key generation
|
||||||
try {
|
try {
|
||||||
showAction("action.invite.accepting", "invite.action");
|
showAction("action.invite.accepting", "invite.action");
|
||||||
|
|
@ -1769,10 +1851,12 @@ function gotoJoinSpace() {
|
||||||
switchRoomContent("title.join.space", joinSpaceScreen, false);
|
switchRoomContent("title.join.space", joinSpaceScreen, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function gotoHome() {
|
async function gotoHome(splash = true) {
|
||||||
if (roomsBarContainer) roomsBarContainer.style.display = ""; //show roombar
|
if (roomsBarContainer) roomsBarContainer.style.display = ""; //show roombar
|
||||||
setActiveSidebarIndicator(sidebarHomeIndicator);
|
setActiveSidebarIndicator(sidebarHomeIndicator);
|
||||||
switchRoomContent("title.splash", splashScreen, false);
|
if (splash) {
|
||||||
|
switchRoomContent("title.splash", splashScreen, false);
|
||||||
|
}
|
||||||
switchRoomsBar("title.home", homeRoomBar);
|
switchRoomsBar("title.home", homeRoomBar);
|
||||||
setActiveRoombarItem(null);
|
setActiveRoombarItem(null);
|
||||||
setupWebSocket();
|
setupWebSocket();
|
||||||
|
|
@ -2010,6 +2094,11 @@ async function openDm(dmId, username, targetId) {
|
||||||
|
|
||||||
let pfp = await getAvatarUrl(targetId, username);
|
let pfp = await getAvatarUrl(targetId, username);
|
||||||
let iconHtml = `<img src="${pfp}" style="width: 2.3rem; border-radius: 0.65rem; margin-right: 0.38rem; object-fit: cover; border: var(--border-width) solid rgba(255, 255, 255, 0.2);">`;
|
let iconHtml = `<img src="${pfp}" style="width: 2.3rem; border-radius: 0.65rem; margin-right: 0.38rem; object-fit: cover; border: var(--border-width) solid rgba(255, 255, 255, 0.2);">`;
|
||||||
|
|
||||||
|
if (currentRoomsBarTitle !== "title.home") {
|
||||||
|
gotoHome(false);
|
||||||
|
}
|
||||||
|
|
||||||
await switchRoomContent(username.split(':')[0], chatScreen, true, iconHtml);
|
await switchRoomContent(username.split(':')[0], chatScreen, true, iconHtml);
|
||||||
|
|
||||||
let msgContainer = document.getElementById("chat-messages");
|
let msgContainer = document.getElementById("chat-messages");
|
||||||
|
|
@ -2155,7 +2244,7 @@ async function renderMessages(messages, isPrepend = false) {
|
||||||
respondedAuthorId = parts[1];
|
respondedAuthorId = parts[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
let replyAuthor = "Someone";
|
let replyAuthor = processBlah('unknown.author');
|
||||||
let needsAsyncFetch = false;
|
let needsAsyncFetch = false;
|
||||||
|
|
||||||
if (loadedMessages[respondedId] && loadedMessages[respondedId].author !== "0") {
|
if (loadedMessages[respondedId] && loadedMessages[respondedId].author !== "0") {
|
||||||
|
|
@ -2183,7 +2272,7 @@ async function renderMessages(messages, isPrepend = false) {
|
||||||
window.replyIdCounter = (window.replyIdCounter || 0) + 1;
|
window.replyIdCounter = (window.replyIdCounter || 0) + 1;
|
||||||
if (window.replyIdCounter > 1000000) window.replyIdCounter = 0;
|
if (window.replyIdCounter > 1000000) window.replyIdCounter = 0;
|
||||||
randId = "reply-author-" + window.replyIdCounter;
|
randId = "reply-author-" + window.replyIdCounter;
|
||||||
replyAuthor = "Someone";
|
replyAuthor = processBlah('unknown.author');
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
try {
|
try {
|
||||||
let aId = respondedAuthorId;
|
let aId = respondedAuthorId;
|
||||||
|
|
@ -2262,10 +2351,10 @@ async function renderMessages(messages, isPrepend = false) {
|
||||||
|
|
||||||
html += `
|
html += `
|
||||||
<div class="chat-message ${extraClass}" data-msg-id="${msgId}" id="msg-${msgId}">
|
<div class="chat-message ${extraClass}" data-msg-id="${msgId}" id="msg-${msgId}">
|
||||||
${showAvatar ? `<img src="${pfp}" class="chat-message-pfp">` : `<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(event, '${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">${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}
|
||||||
|
|
@ -2307,8 +2396,16 @@ window.visualViewport?.addEventListener("resize", async () => {
|
||||||
|
|
||||||
isAdjusting = true;
|
isAdjusting = true;
|
||||||
while (isAdjusting) {
|
while (isAdjusting) {
|
||||||
|
//chat scroll fix
|
||||||
ignoreScroll = true;
|
ignoreScroll = true;
|
||||||
container.scrollTop = container.scrollHeight - container.clientHeight - lastChatScroll;
|
container.scrollTop = container.scrollHeight - container.clientHeight - lastChatScroll;
|
||||||
|
|
||||||
|
//context menu fix
|
||||||
|
let menuRect = fixedContextMenu.getBoundingClientRect();
|
||||||
|
if (menuRect.bottom > window.innerHeight) {
|
||||||
|
fixedContextMenu.style.top = `${Math.max(10, window.innerHeight - menuRect.height - 10)}px`;
|
||||||
|
}
|
||||||
|
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
ignoreScroll = false;
|
ignoreScroll = false;
|
||||||
});
|
});
|
||||||
|
|
@ -2585,7 +2682,7 @@ async function replyMessage(msgId) {
|
||||||
let bar = document.getElementById("replying-bar");
|
let bar = document.getElementById("replying-bar");
|
||||||
if (bar) {
|
if (bar) {
|
||||||
let msg = loadedMessages[msgId];
|
let msg = loadedMessages[msgId];
|
||||||
let authorName = "Someone";
|
let authorName = processBlah('unknown.author');
|
||||||
if (msg && msg.author !== "0" && dmUsernameCache[msg.author]) {
|
if (msg && msg.author !== "0" && dmUsernameCache[msg.author]) {
|
||||||
authorName = dmUsernameCache[msg.author].split(':')[0];
|
authorName = dmUsernameCache[msg.author].split(':')[0];
|
||||||
}
|
}
|
||||||
|
|
@ -2670,16 +2767,7 @@ function openReactionPicker(e) {
|
||||||
let rect = fixedContextMenu.getBoundingClientRect();
|
let rect = fixedContextMenu.getBoundingClientRect();
|
||||||
showFixedContextMenu({top: rect.top, right: rect.left, bottom: rect.top, left: rect.left}, reactionPickerMenuHtml);
|
showFixedContextMenu({top: rect.top, right: rect.left, bottom: rect.top, left: rect.left}, reactionPickerMenuHtml);
|
||||||
let searchInput = document.getElementById('reaction-picker-search');
|
let searchInput = document.getElementById('reaction-picker-search');
|
||||||
if (searchInput) {
|
|
||||||
searchInput.addEventListener('focus', () => { //TODO: use code from resize event
|
|
||||||
setTimeout(() => {
|
|
||||||
let menuRect = fixedContextMenu.getBoundingClientRect();
|
|
||||||
if (menuRect.bottom > window.innerHeight) {
|
|
||||||
fixedContextMenu.style.top = `${Math.max(10, window.innerHeight - menuRect.height - 10)}px`;
|
|
||||||
}
|
|
||||||
}, 500);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function reactMessagePrompt(msgId, quickReaction = null) {
|
async function reactMessagePrompt(msgId, quickReaction = null) {
|
||||||
|
|
|
||||||
|
|
@ -117,9 +117,9 @@ var notifisScreen = `
|
||||||
var invitesEntry = `
|
var invitesEntry = `
|
||||||
<div class="invite-entry">
|
<div class="invite-entry">
|
||||||
<div class="invite-info">
|
<div class="invite-info">
|
||||||
<img src="{pfp}" class="invite-pfp">
|
<img src="{pfp}" class="invite-pfp" onclick="openProfile('{id}')" style="cursor: pointer;">
|
||||||
<div class="invite-details">
|
<div class="invite-details">
|
||||||
<span class="invite-name">{username}</span>
|
<span class="invite-name" onclick="openProfile('{id}')" style="cursor: pointer;">{username}</span>
|
||||||
<span class="invite-desc"><blah>{desc}</blah></span>
|
<span class="invite-desc"><blah>{desc}</blah></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -811,14 +811,14 @@ sidebarelement:has(.icon-button:active) indicator:not(.active) {
|
||||||
box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.5);
|
box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.5);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: scale(0.6);
|
transform: scale(0.6);
|
||||||
transition: transform 0.2s cubic-bezier(0.4, -0.3, 0.7, 1), opacity 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
.profile-popup-container.show .profile-popup-content {
|
.profile-popup-container.show .profile-popup-content {
|
||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transition: transform 0.25s cubic-bezier(0.2, 1.15, 0.4, 1), opacity 0.25s ease;
|
transition: all 0.2s ease;
|
||||||
}
|
}
|
||||||
.profile-popup-close-btn {
|
.profile-popup-close-btn {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue