forked from Kieran/snort
chat layout
This commit is contained in:
parent
ecd5ea111d
commit
ae73e2b383
@ -10,5 +10,12 @@ export function ChatParticipantProfile({ participant }: { participant: ChatParti
|
||||
if (participant.id === publicKey) {
|
||||
return <NoteToSelf className="grow" />;
|
||||
}
|
||||
return <ProfileImage pubkey={participant.id} className="grow" profile={participant.profile as MetadataCache} />;
|
||||
return (
|
||||
<ProfileImage
|
||||
showNip05={false}
|
||||
pubkey={participant.id}
|
||||
className="grow"
|
||||
profile={participant.profile as MetadataCache}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -1,37 +1,7 @@
|
||||
.dm {
|
||||
margin-top: 16px;
|
||||
min-width: 100px;
|
||||
max-width: 90%;
|
||||
white-space: pre-wrap;
|
||||
color: var(--font-color);
|
||||
}
|
||||
|
||||
.dm a {
|
||||
color: var(--font-color) !important;
|
||||
}
|
||||
|
||||
.dm > div:last-child {
|
||||
color: var(--gray-light);
|
||||
font-size: small;
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
.dm.other > div:first-child {
|
||||
padding: 12px 16px;
|
||||
background: var(--gray-secondary);
|
||||
border-radius: 16px 16px 16px 0px;
|
||||
}
|
||||
|
||||
.dm.me {
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.dm.me > div:first-child {
|
||||
padding: 12px 16px;
|
||||
.dm-gradient {
|
||||
background: var(--dm-gradient);
|
||||
border-radius: 16px 16px 0px 16px;
|
||||
}
|
||||
|
||||
.dm.me > div:last-child {
|
||||
text-align: end;
|
||||
.other {
|
||||
background: var(--gray-superdark);
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import "./DM.css";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
import { useInView } from "react-intersection-observer";
|
||||
@ -55,8 +56,19 @@ export default function DM(props: DMProps) {
|
||||
}, [inView]);
|
||||
|
||||
return (
|
||||
<div className={isMe ? "dm me" : "dm other"} ref={ref}>
|
||||
<div>
|
||||
<div
|
||||
className={
|
||||
isMe
|
||||
? "self-end mt-4 min-w-[100px] max-w-[90%] whitespace-pre-wrap align-self-end"
|
||||
: "mt-4 min-w-[100px] max-w-[90%] whitespace-pre-wrap"
|
||||
}
|
||||
ref={ref}>
|
||||
<div
|
||||
className={
|
||||
isMe
|
||||
? "p-3 dm-gradient rounded-tl-lg rounded-tr-lg rounded-br-none"
|
||||
: "p-3 bg-gray-300 rounded-tl-lg rounded-tr-lg rounded-br-lg rounded-bl-none other"
|
||||
}>
|
||||
{sender()}
|
||||
{content ? (
|
||||
<Text id={msg.id} content={content} tags={[]} creator={otherPubkey} />
|
||||
@ -64,7 +76,7 @@ export default function DM(props: DMProps) {
|
||||
<FormattedMessage defaultMessage="Loading..." id="gjBiyj" />
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<div className={isMe ? "text-end text-gray-400 text-sm mt-1" : "text-gray-400 text-sm mt-1"}>
|
||||
<NoteTime from={msg.created_at * 1000} fallback={formatMessage(messages.JustNow)} />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,32 +0,0 @@
|
||||
.dm-window {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: calc(100vh - 62px);
|
||||
}
|
||||
.dm-window > div:nth-child(1) {
|
||||
padding: 12px 0;
|
||||
}
|
||||
|
||||
.dm-window > div:nth-child(2) {
|
||||
overflow-y: auto;
|
||||
padding: 0 10px 10px 10px;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
.dm-window > div:nth-child(3) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
.pfp-overlap .pfp:not(:last-of-type) {
|
||||
margin-right: -20px;
|
||||
}
|
||||
|
||||
.pfp-overlap .avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
@ -1,6 +1,4 @@
|
||||
import "./DmWindow.css";
|
||||
import { useMemo } from "react";
|
||||
|
||||
import { useEffect, useMemo, useRef } from "react";
|
||||
import ProfileImage from "@/Element/User/ProfileImage";
|
||||
import DM from "@/Element/Chat/DM";
|
||||
import useLogin from "@/Hooks/useLogin";
|
||||
@ -18,7 +16,7 @@ export default function DmWindow({ id }: { id: string }) {
|
||||
return <ChatParticipantProfile participant={chat.participants[0]} />;
|
||||
} else {
|
||||
return (
|
||||
<div className="flex pfp-overlap mb10">
|
||||
<div className="flex -space-x-5 mb-2.5">
|
||||
{chat.participants.map(v => (
|
||||
<ProfileImage pubkey={v.id} showUsername={false} />
|
||||
))}
|
||||
@ -29,12 +27,12 @@ export default function DmWindow({ id }: { id: string }) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="dm-window">
|
||||
<div>{sender()}</div>
|
||||
<div>
|
||||
<div className="flex flex-1 flex-col h-[calc(100vh-62px)] md:h-screen">
|
||||
<div className="p-3">{sender()}</div>
|
||||
<div className="overflow-y-auto hide-scrollbar p-2.5 flex-grow">
|
||||
<div className="flex flex-col">{chat && <DmChatSelected chat={chat} />}</div>
|
||||
</div>
|
||||
<div className="flex g8">
|
||||
<div className="flex items-center gap-2.5 p-2.5">
|
||||
<WriteMessage chat={chat} />
|
||||
</div>
|
||||
</div>
|
||||
@ -43,6 +41,9 @@ export default function DmWindow({ id }: { id: string }) {
|
||||
|
||||
function DmChatSelected({ chat }: { chat: Chat }) {
|
||||
const { publicKey: myPubKey } = useLogin(s => ({ publicKey: s.publicKey }));
|
||||
const messagesContainerRef = useRef<HTMLDivElement>(null);
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const sortedDms = useMemo(() => {
|
||||
const myDms = chat?.messages;
|
||||
if (myPubKey && myDms) {
|
||||
@ -52,11 +53,37 @@ function DmChatSelected({ chat }: { chat: Chat }) {
|
||||
return [];
|
||||
}, [chat, myPubKey]);
|
||||
|
||||
const scrollToBottom = () => {
|
||||
messagesEndRef.current?.scrollIntoView({ behavior: "instant" });
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const observer = new ResizeObserver(() => {
|
||||
scrollToBottom();
|
||||
});
|
||||
|
||||
// Start observing the element that you want to keep in view
|
||||
if (messagesContainerRef.current) {
|
||||
observer.observe(messagesContainerRef.current);
|
||||
}
|
||||
|
||||
// Make sure to scroll to bottom on initial load
|
||||
scrollToBottom();
|
||||
|
||||
// Clean up the observer on component unmount
|
||||
return () => {
|
||||
if (messagesContainerRef.current) {
|
||||
observer.unobserve(messagesContainerRef.current);
|
||||
}
|
||||
};
|
||||
}, [sortedDms]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col" ref={messagesContainerRef}>
|
||||
{sortedDms.map(a => (
|
||||
<DM data={a} key={a.id} chat={chat} />
|
||||
))}
|
||||
</>
|
||||
<div ref={messagesEndRef} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
text-overflow: ellipsis;
|
||||
white-space: pre-wrap;
|
||||
display: inline;
|
||||
overflow-wrap: break-word;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.text .text-frag > a {
|
||||
|
@ -20,6 +20,7 @@ export interface ProfileImageProps {
|
||||
link?: string;
|
||||
defaultNip?: string;
|
||||
verifyNip?: boolean;
|
||||
showNip05?: boolean;
|
||||
overrideUsername?: string;
|
||||
profile?: UserMetadata;
|
||||
size?: number;
|
||||
@ -38,6 +39,7 @@ export default function ProfileImage({
|
||||
link,
|
||||
defaultNip,
|
||||
verifyNip,
|
||||
showNip05 = true,
|
||||
overrideUsername,
|
||||
profile,
|
||||
size,
|
||||
@ -93,7 +95,7 @@ export default function ProfileImage({
|
||||
<div className="f-ellipsis">
|
||||
<div className="flex g4 username">
|
||||
{overrideUsername ? overrideUsername : <DisplayName pubkey={pubkey} user={user} />}
|
||||
{nip05 && <Nip05 nip05={nip05} pubkey={pubkey} verifyNip={verifyNip} />}
|
||||
{showNip05 && nip05 && <Nip05 nip05={nip05} pubkey={pubkey} verifyNip={verifyNip} />}
|
||||
</div>
|
||||
<div className="subheader">{subHeader}</div>
|
||||
</div>
|
||||
|
@ -38,7 +38,10 @@ export default function ProfilePreview(props: ProfilePreviewProps) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={`profile-preview${props.className ? ` ${props.className}` : ""}`} ref={ref} onClick={handleClick}>
|
||||
<div
|
||||
className={`justify-between profile-preview${props.className ? ` ${props.className}` : ""}`}
|
||||
ref={ref}
|
||||
onClick={handleClick}>
|
||||
{inView && (
|
||||
<>
|
||||
<ProfileImage
|
||||
|
@ -1,109 +0,0 @@
|
||||
.dm-page {
|
||||
display: grid;
|
||||
grid-template-columns: 350px auto;
|
||||
height: 100vh;
|
||||
/* 100vh - header - padding */
|
||||
overflow: hidden;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.dm-page > div:nth-child(1)::-webkit-scrollbar-track {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
/* These should match what is in code too */
|
||||
@media (max-width: 768px) {
|
||||
.dm-page {
|
||||
grid-template-columns: 100vw;
|
||||
height: calc(100vh - 62px);
|
||||
}
|
||||
|
||||
.dm-page > div:nth-child(1) {
|
||||
margin: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1500px) {
|
||||
.dm-page {
|
||||
grid-template-columns: 400px auto 400px;
|
||||
}
|
||||
}
|
||||
|
||||
/* User list */
|
||||
.dm-page > div:nth-child(1) {
|
||||
overflow-y: auto;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
/* Chat window */
|
||||
.dm-page > div:nth-child(2) {
|
||||
padding: 0 12px;
|
||||
margin: 0 4px;
|
||||
height: 100vh;
|
||||
background-color: var(--gray-superdark);
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
/* Profile pannel */
|
||||
.dm-page > div:nth-child(3) {
|
||||
margin: 16px;
|
||||
}
|
||||
|
||||
.dm-page > div:nth-child(3) .avatar {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.dm-page > div:nth-child(3) .card {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dm-page .new-chat {
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
.dm-page .chat-list > div.active {
|
||||
background-color: var(--gray-superdark);
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.new-chat-modal .user-list {
|
||||
max-height: 50vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.new-chat-modal .user-list > div {
|
||||
padding: 8px 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* user in list selected */
|
||||
.new-chat-modal .user-list > div.active {
|
||||
background-color: var(--gray-dark);
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.new-chat-modal .modal-body {
|
||||
padding: 24px 32px;
|
||||
}
|
||||
|
||||
.new-chat-modal h2,
|
||||
.new-chat-modal h3,
|
||||
.new-chat-modal p {
|
||||
font-weight: 600;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.new-chat-modal h2 {
|
||||
font-size: 21px;
|
||||
}
|
||||
|
||||
.new-chat-modal h3 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.new-chat-modal p {
|
||||
font-size: 11px;
|
||||
letter-spacing: 1.21px;
|
||||
text-transform: uppercase;
|
||||
}
|
@ -1,5 +1,3 @@
|
||||
import "./MessagesPage.css";
|
||||
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
@ -74,7 +72,7 @@ export default function MessagesPage() {
|
||||
const isActive = cx.id === chat;
|
||||
return (
|
||||
<div
|
||||
className={classNames("flex items-center p cursor-pointer", { active: isActive })}
|
||||
className={classNames("flex items-center p cursor-pointer justify-between", { active: isActive })}
|
||||
key={cx.id}
|
||||
onClick={e => openChat(e, cx.type, cx.id)}>
|
||||
{conversationIdent(cx)}
|
||||
@ -92,11 +90,11 @@ export default function MessagesPage() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="dm-page">
|
||||
<div className="flex flex-1 h-screen overflow-hidden">
|
||||
{(pageWidth >= TwoCol || !chat) && (
|
||||
<div className="chat-list">
|
||||
<div className="flex items-center p justify-between">
|
||||
<button disabled={unreadCount <= 0} type="button">
|
||||
<div className="overflow-y-auto h-screen p-1 w-full md:w-1/3 flex-shrink-0">
|
||||
<div className="flex items-center justify-between p-2">
|
||||
<button disabled={unreadCount <= 0} type="button" className="text-sm font-semibold">
|
||||
<FormattedMessage defaultMessage="Mark all read" id="ShdEie" />
|
||||
</button>
|
||||
<NewChatWindow />
|
||||
@ -113,9 +111,9 @@ export default function MessagesPage() {
|
||||
.map(conversation)}
|
||||
</div>
|
||||
)}
|
||||
{chat ? <DmWindow id={chat} /> : pageWidth >= TwoCol && <div></div>}
|
||||
{chat ? <DmWindow id={chat} /> : pageWidth >= TwoCol && <div className="flex-1"></div>}
|
||||
{pageWidth >= ThreeCol && chat && (
|
||||
<div>
|
||||
<div className="m-4">
|
||||
<ProfileDmActions id={chat} />
|
||||
</div>
|
||||
)}
|
||||
|
Loading…
Reference in New Issue
Block a user