add DM screen
This commit is contained in:
parent
a666cb9915
commit
be26703908
8 changed files with 386 additions and 8 deletions
|
|
@ -127,12 +127,12 @@
|
||||||
|
|
||||||
async function refreshDms() {
|
async function refreshDms() {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
|
||||||
showAction("action.dm.fetch", "dmrefresh");
|
showAction("action.dm.fetch", "dmrefresh");
|
||||||
let res = await fetchEncrypted("user/dm/list");
|
let res = await fetchEncrypted("user/dm/list");
|
||||||
|
|
||||||
clearAction("dmrefresh");
|
clearAction("dmrefresh");
|
||||||
|
if (typeof renderDms === 'function') {
|
||||||
|
await renderDms(res);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
clearAction("dmrefresh");
|
clearAction("dmrefresh");
|
||||||
|
|
|
||||||
|
|
@ -1234,8 +1234,11 @@ async function switchDetailsContent(title, content)
|
||||||
function clickCollapseDms()
|
function clickCollapseDms()
|
||||||
{
|
{
|
||||||
var collapseDmsBtn = document.getElementById("collapse-dms");
|
var collapseDmsBtn = document.getElementById("collapse-dms");
|
||||||
|
|
||||||
collapseDmsBtn.classList.toggle("collapsed");
|
collapseDmsBtn.classList.toggle("collapsed");
|
||||||
|
var dmsList = document.getElementById("dms-list");
|
||||||
|
if (dmsList) {
|
||||||
|
dmsList.style.display = collapseDmsBtn.classList.contains("collapsed") ? "none" : "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
function clickCollapseGroups()
|
function clickCollapseGroups()
|
||||||
{
|
{
|
||||||
|
|
@ -1471,6 +1474,7 @@ async function acceptInvite(targetId) { //TODO: Implement key generation
|
||||||
clearAction("invite.action");
|
clearAction("invite.action");
|
||||||
showBlahNotification(res || "success:invite.accepted");
|
showBlahNotification(res || "success:invite.accepted");
|
||||||
await loadInvites('received');
|
await loadInvites('received');
|
||||||
|
await refreshDms();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
clearAction("invite.action");
|
clearAction("invite.action");
|
||||||
showBlahNotification("error:something.wrong");
|
showBlahNotification("error:something.wrong");
|
||||||
|
|
@ -1680,3 +1684,97 @@ function handleMobileSwipe() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function renderDms(res) {
|
||||||
|
let container = document.getElementById("dms-list");
|
||||||
|
if (!container) return;
|
||||||
|
|
||||||
|
if (res && res !== "") {
|
||||||
|
let parsed = JSON.parse(res);
|
||||||
|
let dms = parsed.dms || {};
|
||||||
|
let dmEntries = Object.entries(dms);
|
||||||
|
|
||||||
|
// Sort by timestamp descending
|
||||||
|
dmEntries.sort((a,b) => (parseInt(b[1].string2) || 0) - (parseInt(a[1].string2) || 0));
|
||||||
|
|
||||||
|
let html = "";
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
for (let [dmId, dmData] of dmEntries) {
|
||||||
|
let parts = dmId.split('_');
|
||||||
|
let myId = `${id};${host}`;
|
||||||
|
let targetId = parts[0] === myId ? parts[1] : parts[0];
|
||||||
|
|
||||||
|
let targetUsername = await fetchAsync(`${url}/idtoname?id=${targetId}`);
|
||||||
|
if (!targetUsername || targetUsername.trim() === "" || targetUsername.startsWith("error:")) continue;
|
||||||
|
|
||||||
|
let pfp = await getAvatarUrl(targetId, targetUsername);
|
||||||
|
let displayName = targetUsername.split(':')[0];
|
||||||
|
|
||||||
|
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%;">
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
`;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count > 0) {
|
||||||
|
container.innerHTML = html;
|
||||||
|
container.classList.remove("empty");
|
||||||
|
} else {
|
||||||
|
container.innerHTML = `<p style="opacity: 0.5;"><blah>desc.no.dms</blah></p>`;
|
||||||
|
container.classList.add("empty");
|
||||||
|
let blahTags = container.getElementsByTagName("blah");
|
||||||
|
for (let i = 0; i < blahTags.length; i++) {
|
||||||
|
blahTags[i].innerHTML = processBlah(blahTags[i].innerHTML);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
container.innerHTML = `<p style="opacity: 0.5;"><blah>desc.no.dms</blah></p>`;
|
||||||
|
container.classList.add("empty");
|
||||||
|
let blahTags = container.getElementsByTagName("blah");
|
||||||
|
for (let i = 0; i < blahTags.length; i++) {
|
||||||
|
blahTags[i].innerHTML = processBlah(blahTags[i].innerHTML);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentDmId = null;
|
||||||
|
var currentDmKey = null; // AES key for encrypting/decrypting messages
|
||||||
|
|
||||||
|
async function openDm(dmId, username, targetId) {
|
||||||
|
try {
|
||||||
|
showAction("action.dm.opening", "dmopen");
|
||||||
|
|
||||||
|
currentDmKey = await ensureDmRoomKey(dmId);
|
||||||
|
if (!currentDmKey) {
|
||||||
|
throw new Error("Missing DM room key");
|
||||||
|
}
|
||||||
|
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;">`;
|
||||||
|
await switchRoomContent(username.split(':')[0], chatScreen, false, iconHtml);
|
||||||
|
|
||||||
|
let msgContainer = document.getElementById("chat-messages");
|
||||||
|
if (msgContainer) {
|
||||||
|
msgContainer.innerHTML = `<div style="text-align: center; opacity: 0.5; padding: 1rem;"><blah>desc.messages.encryption.active</blah></div>`;
|
||||||
|
let blahTags = msgContainer.getElementsByTagName("blah");
|
||||||
|
for (let i = 0; i < blahTags.length; i++) {
|
||||||
|
blahTags[i].innerHTML = processBlah(blahTags[i].innerHTML);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setActiveRoombarItem(`dm-btn-${dmId}`);
|
||||||
|
clearAction("dmopen");
|
||||||
|
} catch (e) {
|
||||||
|
clearAction("dmopen");
|
||||||
|
console.error(e);
|
||||||
|
showBlahNotification("error:dm.open.failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -129,6 +129,20 @@ var invitesEntry = `
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
var chatScreen = `
|
||||||
|
<div style="display: flex; flex-direction: column; height: 100%; width: 100%;">
|
||||||
|
<div id="chat-messages" style="flex-grow: 1; overflow-y: auto; padding: 1rem; display: flex; flex-direction: column; gap: 0.5rem;">
|
||||||
|
<!-- messages go here -->
|
||||||
|
</div>
|
||||||
|
<div style="padding: 1rem; border-top: var(--border-width) solid var(--light-border-color); display: flex; gap: 0.5rem;">
|
||||||
|
<input type="text" id="chat-input" class="forminput" style="flex-grow: 1; margin: 0;" placeholder="{blah(placeholder.message.input)}">
|
||||||
|
<button class="submit-button" onclick="sendMessage()" style="margin: 0; padding: 0 1.5rem;">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="var(--main-bg-color)" width="1.5rem"><path d="M120-160v-240l320-80-320-80v-240l760 320-760 320Z"/></svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
//roombars
|
//roombars
|
||||||
var homeRoomBar = `
|
var homeRoomBar = `
|
||||||
<div class="sidebar-section-header">
|
<div class="sidebar-section-header">
|
||||||
|
|
@ -147,6 +161,7 @@ var homeRoomBar = `
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="dms-list" class="list-container empty" style="padding: 0 0.5rem;"></div>
|
||||||
|
|
||||||
<div class="sidebar-section-header">
|
<div class="sidebar-section-header">
|
||||||
<button class="collapse-text-button" id="collapse-groups" onclick="clickCollapseGroups()">
|
<button class="collapse-text-button" id="collapse-groups" onclick="clickCollapseGroups()">
|
||||||
|
|
|
||||||
|
|
@ -571,6 +571,82 @@ space{
|
||||||
height: 3.1rem;
|
height: 3.1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.room-entry {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.8rem;
|
||||||
|
padding: 0.5rem 0.8rem;
|
||||||
|
width: 100%;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
border-radius: 0.8rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.room-pfp {
|
||||||
|
width: 2.2rem;
|
||||||
|
height: 2.2rem;
|
||||||
|
border-radius: 0.6rem;
|
||||||
|
border: var(--border-width) solid rgba(255, 255, 255, 0.2);
|
||||||
|
object-fit: cover;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.room-name {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-message {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
padding: 0.2rem 0;
|
||||||
|
}
|
||||||
|
.chat-message.with-avatar {
|
||||||
|
margin-top: 0.8rem;
|
||||||
|
}
|
||||||
|
.chat-message-pfp {
|
||||||
|
width: 2.5rem;
|
||||||
|
height: 2.5rem;
|
||||||
|
border-radius: 0.6rem;
|
||||||
|
object-fit: cover;
|
||||||
|
flex-shrink: 0;
|
||||||
|
opacity: 0; /* hidden by default for consecutive messages */
|
||||||
|
}
|
||||||
|
.chat-message.with-avatar .chat-message-pfp {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.chat-message-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
.chat-message-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-bottom: 0.2rem;
|
||||||
|
display: none; /* hidden for consecutive */
|
||||||
|
}
|
||||||
|
.chat-message.with-avatar .chat-message-header {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.chat-message-author {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
}
|
||||||
|
.chat-message-timestamp {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
.chat-message-text {
|
||||||
|
line-height: 1.4;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
@media (hover: hover) {
|
@media (hover: hover) {
|
||||||
sidebarelement:hover indicator:not(.active) {
|
sidebarelement:hover indicator:not(.active) {
|
||||||
content: "";
|
content: "";
|
||||||
|
|
|
||||||
|
|
@ -127,12 +127,12 @@
|
||||||
|
|
||||||
async function refreshDms() {
|
async function refreshDms() {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
|
||||||
showAction("action.dm.fetch", "dmrefresh");
|
showAction("action.dm.fetch", "dmrefresh");
|
||||||
let res = await fetchEncrypted("user/dm/list");
|
let res = await fetchEncrypted("user/dm/list");
|
||||||
|
|
||||||
clearAction("dmrefresh");
|
clearAction("dmrefresh");
|
||||||
|
if (typeof renderDms === 'function') {
|
||||||
|
await renderDms(res);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
clearAction("dmrefresh");
|
clearAction("dmrefresh");
|
||||||
|
|
|
||||||
100
webroot/main.js
100
webroot/main.js
|
|
@ -1234,8 +1234,11 @@ async function switchDetailsContent(title, content)
|
||||||
function clickCollapseDms()
|
function clickCollapseDms()
|
||||||
{
|
{
|
||||||
var collapseDmsBtn = document.getElementById("collapse-dms");
|
var collapseDmsBtn = document.getElementById("collapse-dms");
|
||||||
|
|
||||||
collapseDmsBtn.classList.toggle("collapsed");
|
collapseDmsBtn.classList.toggle("collapsed");
|
||||||
|
var dmsList = document.getElementById("dms-list");
|
||||||
|
if (dmsList) {
|
||||||
|
dmsList.style.display = collapseDmsBtn.classList.contains("collapsed") ? "none" : "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
function clickCollapseGroups()
|
function clickCollapseGroups()
|
||||||
{
|
{
|
||||||
|
|
@ -1471,6 +1474,7 @@ async function acceptInvite(targetId) { //TODO: Implement key generation
|
||||||
clearAction("invite.action");
|
clearAction("invite.action");
|
||||||
showBlahNotification(res || "success:invite.accepted");
|
showBlahNotification(res || "success:invite.accepted");
|
||||||
await loadInvites('received');
|
await loadInvites('received');
|
||||||
|
await refreshDms();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
clearAction("invite.action");
|
clearAction("invite.action");
|
||||||
showBlahNotification("error:something.wrong");
|
showBlahNotification("error:something.wrong");
|
||||||
|
|
@ -1680,3 +1684,97 @@ function handleMobileSwipe() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function renderDms(res) {
|
||||||
|
let container = document.getElementById("dms-list");
|
||||||
|
if (!container) return;
|
||||||
|
|
||||||
|
if (res && res !== "") {
|
||||||
|
let parsed = JSON.parse(res);
|
||||||
|
let dms = parsed.dms || {};
|
||||||
|
let dmEntries = Object.entries(dms);
|
||||||
|
|
||||||
|
// Sort by timestamp descending
|
||||||
|
dmEntries.sort((a,b) => (parseInt(b[1].string2) || 0) - (parseInt(a[1].string2) || 0));
|
||||||
|
|
||||||
|
let html = "";
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
for (let [dmId, dmData] of dmEntries) {
|
||||||
|
let parts = dmId.split('_');
|
||||||
|
let myId = `${id};${host}`;
|
||||||
|
let targetId = parts[0] === myId ? parts[1] : parts[0];
|
||||||
|
|
||||||
|
let targetUsername = await fetchAsync(`${url}/idtoname?id=${targetId}`);
|
||||||
|
if (!targetUsername || targetUsername.trim() === "" || targetUsername.startsWith("error:")) continue;
|
||||||
|
|
||||||
|
let pfp = await getAvatarUrl(targetId, targetUsername);
|
||||||
|
let displayName = targetUsername.split(':')[0];
|
||||||
|
|
||||||
|
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%;">
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
`;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count > 0) {
|
||||||
|
container.innerHTML = html;
|
||||||
|
container.classList.remove("empty");
|
||||||
|
} else {
|
||||||
|
container.innerHTML = `<p style="opacity: 0.5;"><blah>desc.no.dms</blah></p>`;
|
||||||
|
container.classList.add("empty");
|
||||||
|
let blahTags = container.getElementsByTagName("blah");
|
||||||
|
for (let i = 0; i < blahTags.length; i++) {
|
||||||
|
blahTags[i].innerHTML = processBlah(blahTags[i].innerHTML);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
container.innerHTML = `<p style="opacity: 0.5;"><blah>desc.no.dms</blah></p>`;
|
||||||
|
container.classList.add("empty");
|
||||||
|
let blahTags = container.getElementsByTagName("blah");
|
||||||
|
for (let i = 0; i < blahTags.length; i++) {
|
||||||
|
blahTags[i].innerHTML = processBlah(blahTags[i].innerHTML);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentDmId = null;
|
||||||
|
var currentDmKey = null; // AES key for encrypting/decrypting messages
|
||||||
|
|
||||||
|
async function openDm(dmId, username, targetId) {
|
||||||
|
try {
|
||||||
|
showAction("action.dm.opening", "dmopen");
|
||||||
|
|
||||||
|
currentDmKey = await ensureDmRoomKey(dmId);
|
||||||
|
if (!currentDmKey) {
|
||||||
|
throw new Error("Missing DM room key");
|
||||||
|
}
|
||||||
|
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;">`;
|
||||||
|
await switchRoomContent(username.split(':')[0], chatScreen, false, iconHtml);
|
||||||
|
|
||||||
|
let msgContainer = document.getElementById("chat-messages");
|
||||||
|
if (msgContainer) {
|
||||||
|
msgContainer.innerHTML = `<div style="text-align: center; opacity: 0.5; padding: 1rem;"><blah>desc.messages.encryption.active</blah></div>`;
|
||||||
|
let blahTags = msgContainer.getElementsByTagName("blah");
|
||||||
|
for (let i = 0; i < blahTags.length; i++) {
|
||||||
|
blahTags[i].innerHTML = processBlah(blahTags[i].innerHTML);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setActiveRoombarItem(`dm-btn-${dmId}`);
|
||||||
|
clearAction("dmopen");
|
||||||
|
} catch (e) {
|
||||||
|
clearAction("dmopen");
|
||||||
|
console.error(e);
|
||||||
|
showBlahNotification("error:dm.open.failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -129,6 +129,20 @@ var invitesEntry = `
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
var chatScreen = `
|
||||||
|
<div style="display: flex; flex-direction: column; height: 100%; width: 100%;">
|
||||||
|
<div id="chat-messages" style="flex-grow: 1; overflow-y: auto; padding: 1rem; display: flex; flex-direction: column; gap: 0.5rem;">
|
||||||
|
<!-- messages go here -->
|
||||||
|
</div>
|
||||||
|
<div style="padding: 1rem; border-top: var(--border-width) solid var(--light-border-color); display: flex; gap: 0.5rem;">
|
||||||
|
<input type="text" id="chat-input" class="forminput" style="flex-grow: 1; margin: 0;" placeholder="{blah(placeholder.message.input)}">
|
||||||
|
<button class="submit-button" onclick="sendMessage()" style="margin: 0; padding: 0 1.5rem;">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="var(--main-bg-color)" width="1.5rem"><path d="M120-160v-240l320-80-320-80v-240l760 320-760 320Z"/></svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
//roombars
|
//roombars
|
||||||
var homeRoomBar = `
|
var homeRoomBar = `
|
||||||
<div class="sidebar-section-header">
|
<div class="sidebar-section-header">
|
||||||
|
|
@ -147,6 +161,7 @@ var homeRoomBar = `
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="dms-list" class="list-container empty" style="padding: 0 0.5rem;"></div>
|
||||||
|
|
||||||
<div class="sidebar-section-header">
|
<div class="sidebar-section-header">
|
||||||
<button class="collapse-text-button" id="collapse-groups" onclick="clickCollapseGroups()">
|
<button class="collapse-text-button" id="collapse-groups" onclick="clickCollapseGroups()">
|
||||||
|
|
|
||||||
|
|
@ -571,6 +571,82 @@ space{
|
||||||
height: 3.1rem;
|
height: 3.1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.room-entry {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.8rem;
|
||||||
|
padding: 0.5rem 0.8rem;
|
||||||
|
width: 100%;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
border-radius: 0.8rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.room-pfp {
|
||||||
|
width: 2.2rem;
|
||||||
|
height: 2.2rem;
|
||||||
|
border-radius: 0.6rem;
|
||||||
|
border: var(--border-width) solid rgba(255, 255, 255, 0.2);
|
||||||
|
object-fit: cover;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.room-name {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-message {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
padding: 0.2rem 0;
|
||||||
|
}
|
||||||
|
.chat-message.with-avatar {
|
||||||
|
margin-top: 0.8rem;
|
||||||
|
}
|
||||||
|
.chat-message-pfp {
|
||||||
|
width: 2.5rem;
|
||||||
|
height: 2.5rem;
|
||||||
|
border-radius: 0.6rem;
|
||||||
|
object-fit: cover;
|
||||||
|
flex-shrink: 0;
|
||||||
|
opacity: 0; /* hidden by default for consecutive messages */
|
||||||
|
}
|
||||||
|
.chat-message.with-avatar .chat-message-pfp {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.chat-message-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
.chat-message-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-bottom: 0.2rem;
|
||||||
|
display: none; /* hidden for consecutive */
|
||||||
|
}
|
||||||
|
.chat-message.with-avatar .chat-message-header {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.chat-message-author {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
}
|
||||||
|
.chat-message-timestamp {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
.chat-message-text {
|
||||||
|
line-height: 1.4;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
@media (hover: hover) {
|
@media (hover: hover) {
|
||||||
sidebarelement:hover indicator:not(.active) {
|
sidebarelement:hover indicator:not(.active) {
|
||||||
content: "";
|
content: "";
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue