Merge pull request #1555 from PR0M3TH3AN/dm-ui-redesign-11990077537705520279

Redesign DM profile pane UI
This commit is contained in:
thePR0M3TH3AN authored and GitHub committed 2026-01-23 01:27:15 -05:00
commit 27652eb48b
8 files changed
+4202 -321

No files matched your search

+1 -1
View File
@@ -1270,7 +1270,7 @@
</section>
<section
id="profilePaneMessages"
class="profile-pane hidden space-y-4"
class="profile-pane hidden"
role="tabpanel"
aria-labelledby="profileNavMessages"
>
+97 -8
View File
@@ -3063,6 +3063,20 @@
overflow-x: hidden;
}
/* Specific override for messages pane to prevent parent scrolling on desktop */
@media (min-width: theme("screens.md")) {
#profilePaneMessages {
height: 100%;
min-height: 0;
overflow: hidden;
}
#dmAppShellMount {
height: 100%;
min-height: 0;
}
}
.profile-pane.hidden {
display: none !important;
}
@@ -5060,7 +5074,8 @@
.profile-modal .dm-app-shell {
min-height: 0;
height: auto;
padding: var(--space-md);
padding: 0;
gap: var(--space-md);
}
.dm-app-shell__sidebar,
@@ -5075,6 +5090,10 @@
.profile-modal .dm-app-shell__main {
height: auto;
min-height: 0;
border: none;
box-shadow: none;
background: transparent;
padding: 0;
}
.dm-app-shell__sidebar {
@@ -5312,8 +5331,33 @@
border-radius: var(--radius-md);
}
.dm-message-row {
display: flex;
align-items: flex-end;
gap: var(--space-xs);
width: 100%;
}
.dm-message-row--incoming {
justify-content: flex-start;
}
.dm-message-row--outgoing {
justify-content: flex-end;
}
.dm-message-avatar {
width: var(--space-xl);
height: var(--space-xl);
flex-shrink: 0;
border-radius: var(--radius-full);
object-fit: cover;
background-color: var(--surface-sunken);
border: var(--border-width-hairline) solid var(--border-subtle);
}
.dm-message-bubble {
max-width: 85%;
max-width: 75%;
display: flex;
flex-direction: column;
gap: var(--space-2xs);
@@ -5325,14 +5369,14 @@
}
.dm-message-bubble--incoming {
align-self: flex-start;
border-bottom-left-radius: var(--radius-sm);
}
.dm-message-bubble--outgoing {
align-self: flex-end;
background: var(--color-accent);
color: var(--color-white);
border-color: var(--color-accent-strong);
border-bottom-right-radius: var(--radius-sm);
}
.dm-message-bubble--outgoing .dm-message-bubble__meta,
@@ -5827,21 +5871,65 @@
}
}
/* Ensure the profile pane for messages takes full height */
#profilePaneMessages {
height: 100%;
min-height: 0;
}
@media (min-width: theme("screens.md")) {
.profile-modal .dm-app-shell {
flex-direction: row;
align-items: stretch;
height: 100%;
min-height: 0;
}
.profile-modal .dm-app-shell__sidebar {
flex: 0 0 var(--size-18rem);
max-width: var(--size-20rem);
height: auto;
height: 100%;
overflow: hidden;
display: flex;
flex-direction: column;
}
.profile-modal .dm-app-shell__main {
flex: 1 1 auto;
height: auto;
height: 100%;
overflow: hidden;
display: flex;
flex-direction: column;
}
.profile-modal .dm-conversation-list {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
}
.profile-modal .dm-conversation-list__items {
flex: 1;
overflow-y: auto;
min-height: 0;
overscroll-behavior-y: contain;
@apply no-scrollbar;
}
.profile-modal .dm-message-thread {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
}
.profile-modal .dm-message-thread__timeline {
flex: 1;
overflow-y: auto;
min-height: 0;
overscroll-behavior-y: contain;
@apply no-scrollbar;
}
}
@@ -5986,9 +6074,10 @@
}
.no-scrollbar {
scrollbar-width: none;
scrollbar-width: none !important;
-ms-overflow-style: none !important;
}
.no-scrollbar::-webkit-scrollbar {
display: none;
display: none !important;
}
+3 -15
View File
@@ -1,7 +1,6 @@
import { Composer } from "./Composer.js";
import { ConversationList } from "./ConversationList.js";
import { MessageThread } from "./MessageThread.js";
import { NotificationCenter } from "./NotificationCenter.js";
import { DMPrivacySettings } from "./DMPrivacySettings.js";
import { aggregateZapTotals } from "./zapHelpers.js";
import { createZapInterface } from "./ZapInterface.js";
@@ -114,6 +113,7 @@ const DEFAULT_ZAP_RECEIPTS = [
export class AppShell {
constructor({
document: doc,
currentUserAvatarUrl = "",
conversations = DEFAULT_CONVERSATIONS,
activeConversationId = DEFAULT_CONVERSATIONS[0]?.id || "",
conversationState = "idle",
@@ -124,7 +124,6 @@ export class AppShell {
composerState = "idle",
privacyMode = "nip04",
dmPrivacySettings = { readReceiptsEnabled: false, typingIndicatorsEnabled: false },
notifications = [],
signingAdapter = null,
zapConfig = null,
mobileView = "list",
@@ -168,7 +167,7 @@ export class AppShell {
doc,
"h1",
"dm-app-shell__title text-lg font-bold",
"Direct Messages",
"Conversations",
);
sidebarHeader.appendChild(sidebarTitle);
@@ -219,6 +218,7 @@ export class AppShell {
main.appendChild(
MessageThread({
document: doc,
currentUserAvatarUrl,
contact: activeConversation,
messages,
state: threadState,
@@ -248,12 +248,6 @@ export class AppShell {
zapConfig
});
// Position it relative to the composer or just absolute in main
// The extracted createZapInterface uses 'absolute bottom-16 right-4' which positions it nicely
// relative to the container if the container is relative.
// main usually has relative positioning or we can append to root.
// AppShell root has absolute positioning context maybe?
// If main is the container, we append there.
main.appendChild(zapInterface);
};
@@ -276,12 +270,6 @@ export class AppShell {
}
}),
);
main.appendChild(
NotificationCenter({
document: doc,
notices: notifications,
}),
);
this.root.appendChild(sidebar);
this.root.appendChild(main);
+11 -6
View File
@@ -29,12 +29,14 @@ export function ConversationList({
const root = createElement(doc, "section", "dm-conversation-list");
const header = createElement(doc, "div", "dm-conversation-list__header");
header.appendChild(createElement(doc, "h2", "dm-conversation-list__title", "Messages"));
// We removed the redundant "Messages" title here.
// If we need spacing/alignment for the button, justify-end in CSS handles it.
if (typeof onMarkAllRead === "function") {
const markAllButton = createElement(
doc,
"button",
"btn-ghost",
"btn-ghost ml-auto",
"Mark all as read",
);
markAllButton.type = "button";
@@ -45,7 +47,10 @@ export function ConversationList({
});
header.appendChild(markAllButton);
}
root.appendChild(header);
if (header.hasChildNodes()) {
root.appendChild(header);
}
const list = createElement(doc, "div", "dm-conversation-list__items no-scrollbar");
list.setAttribute("role", "listbox");
@@ -88,9 +93,9 @@ export function ConversationList({
list.scrollTo({ top: 40, behavior: "smooth" });
setTimeout(() => {
list.scrollTo({ top: 0, behavior: "smooth" });
}, 600);
}, 800);
}
}, 300);
}, 600);
}
}, { threshold: 0.1 });
observer.observe(list);
@@ -101,7 +106,7 @@ export function ConversationList({
list.scrollTo({ top: 40, behavior: "smooth" });
setTimeout(() => {
list.scrollTo({ top: 0, behavior: "smooth" });
}, 600);
}, 800);
}
}, 600);
}
+23 -6
View File
@@ -13,6 +13,7 @@ export function MessageBubble({
document: doc,
message = {},
variant = "incoming",
avatarSrc = "",
} = {}) {
if (!doc) {
throw new Error("MessageBubble requires a document reference.");
@@ -24,12 +25,19 @@ export function MessageBubble({
status = "sent",
} = message;
const wrapper = createElement(doc, "div", "dm-message-bubble");
wrapper.classList.add(`dm-message-bubble--${variant}`);
wrapper.dataset.status = status;
const row = createElement(doc, "div", "dm-message-row");
row.classList.add(`dm-message-row--${variant}`);
const avatar = createElement(doc, "img", "dm-message-avatar");
avatar.src = avatarSrc || "assets/svg/default-profile.svg";
avatar.alt = variant === "outgoing" ? "Me" : "Sender";
const bubble = createElement(doc, "div", "dm-message-bubble");
bubble.classList.add(`dm-message-bubble--${variant}`);
bubble.dataset.status = status;
const content = createElement(doc, "div", "dm-message-bubble__content", body);
wrapper.appendChild(content);
bubble.appendChild(content);
const meta = createElement(doc, "div", "dm-message-bubble__meta");
if (timestamp) {
@@ -55,6 +63,15 @@ export function MessageBubble({
meta.appendChild(statusLabel);
}
wrapper.appendChild(meta);
return wrapper;
bubble.appendChild(meta);
if (variant === "outgoing") {
row.appendChild(bubble);
row.appendChild(avatar);
} else {
row.appendChild(avatar);
row.appendChild(bubble);
}
return row;
}
+5 -2
View File
@@ -16,6 +16,7 @@ export function MessageThread({
document: doc,
contact = {},
messages = [],
currentUserAvatarUrl = "",
state = "idle",
errorType = "",
privacyMode = "nip04",
@@ -128,10 +129,12 @@ export function MessageThread({
timeline.appendChild(DayDivider({ document: doc, label: message.label }));
return;
}
const isOutgoing = message.direction === "outgoing";
const bubble = MessageBubble({
document: doc,
message,
variant: message.direction === "outgoing" ? "outgoing" : "incoming",
variant: isOutgoing ? "outgoing" : "incoming",
avatarSrc: isOutgoing ? currentUserAvatarUrl : (contact.avatarSrc || ""),
});
timeline.appendChild(bubble);
});
@@ -152,7 +155,7 @@ export function MessageThread({
top: timeline.scrollHeight,
behavior: "smooth",
});
}, 600);
}, 800);
}, 600);
}
};
+6
View File
@@ -4413,6 +4413,11 @@ export class ProfileModalController {
activeThread?.latestMessage,
);
const currentUserSummary = this.resolveProfileSummaryForPubkey(
this.resolveActiveDmActor(),
);
const currentUserAvatarUrl = currentUserSummary?.avatarSrc || "";
container.innerHTML = "";
try {
@@ -4420,6 +4425,7 @@ export class ProfileModalController {
this.dmAppShell = new AppShell({
document,
currentUserAvatarUrl,
conversations,
activeConversationId,
conversationState,
+4056 -283
View File
File diff suppressed because it is too large. Load diff