Fix reply preview

This commit is contained in:
2025-11-26 16:28:18 +03:00
Unverified
parent 6d0edd19c3
commit abbd3e2db9
5 changed files with 74 additions and 87 deletions
@@ -2,24 +2,6 @@
@use "../../../css/material" as *; @use "../../../css/material" as *;
@use "sass:color"; @use "sass:color";
// Reply preview styles (shared with Message component)
.quote.contextualContent > .quoteInner {
display: flex;
flex-direction: column;
gap: 4px;
.replyUsername {
font-weight: 600;
color: $color-dark-on-surface;
font-size: 0.85rem;
}
.replyText {
overflow: hidden;
text-overflow: ellipsis;
}
}
.chatInputWrapper { .chatInputWrapper {
position: relative; position: relative;
margin: 0 10px 10px 10px; margin: 0 10px 10px 10px;
+43 -59
View File
@@ -2,27 +2,11 @@
@use "../../../css/material" as *; @use "../../../css/material" as *;
@use "sass:color"; @use "sass:color";
.quote.contextualContent > .quoteInner {
display: flex;
flex-direction: column;
gap: 4px;
.replyUsername {
font-weight: 600;
color: $color-dark-on-surface;
font-size: 0.85rem;
}
.replyText {
overflow: hidden;
text-overflow: ellipsis;
}
}
.message { .message {
$status-indicator-size: 16px; $status-indicator-size: 16px;
margin-bottom: 1rem; margin-bottom: 10px;
max-width: 70%; max-width: 70%;
position: relative; position: relative;
width: fit-content; width: fit-content;
@@ -30,30 +14,8 @@
align-items: flex-start; align-items: flex-start;
gap: 8px; gap: 8px;
&.received {
.messageProfilePic {
width: 40px;
height: 40px;
flex-shrink: 0;
cursor: pointer;
transition: transform 0.2s ease;
&:hover {
transform: scale(1.05);
}
img {
width: 100%;
height: 100%;
border-radius: 50%;
object-fit: cover;
border: 2px solid $color-dark-outline;
}
}
}
.messageInner { .messageInner {
border-radius: 12px; border-radius: 20px 20px 8px 8px; // Top corners rounded, bottom corners sharper
position: relative; position: relative;
word-wrap: break-word; word-wrap: break-word;
overflow-wrap: anywhere; overflow-wrap: anywhere;
@@ -72,6 +34,7 @@
display: flex; display: flex;
align-items: center; align-items: center;
gap: 4px; gap: 4px;
width: fit-content;
&:hover { &:hover {
transform: scale(1.05); transform: scale(1.05);
@@ -92,9 +55,10 @@
} }
} }
.quote.replyPreview { :global(.quote).replyPreview {
user-select: none; user-select: none;
margin: 10px; margin: 5px;
border-radius: 16px;
} }
.messageAttachments { .messageAttachments {
@@ -194,10 +158,31 @@
} }
&.received { &.received {
.messageProfilePic {
width: 40px;
height: 40px;
flex-shrink: 0;
cursor: pointer;
transition: transform 0.2s ease;
align-self: flex-end;
&:hover {
transform: scale(1.05);
}
img {
width: 100%;
height: 100%;
border-radius: 50%;
object-fit: cover;
border: 2px solid $color-dark-outline;
}
}
.messageInner { .messageInner {
background: $color-dark-surface-container; background: $color-dark-surface-container;
color: $color-dark-on-surface; color: $color-dark-on-surface;
border-top-left-radius: 5px; border-radius: 20px 20px 20px 8px; // Top-left: 5px, top-right: 20px, bottom: 8px
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
border: 1px solid rgba($color-dark-outline-variant, 0.4); border: 1px solid rgba($color-dark-outline-variant, 0.4);
position: relative; position: relative;
@@ -231,27 +216,27 @@
margin-left: auto; margin-left: auto;
flex-direction: row-reverse; flex-direction: row-reverse;
:global(.quote).replyPreview {
background: linear-gradient(135deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0.08));
border: 1px solid rgba(255, 255, 255, 0.2);
position: relative;
overflow: hidden;
box-shadow: 0 2px 8px rgba(147, 51, 234, 0.5);
:global(.quote-inner) {
position: relative;
z-index: 1;
}
}
.messageInner { .messageInner {
background: linear-gradient(135deg, #9333EA, #6366F1, #3B82F6); background: linear-gradient(135deg, #9333EA, #6366F1, #2f68c5);
color: $color-dark-on-primary; border-radius: 20px 20px 8px 20px; // Top-left: 20px, top-right: 5px, bottom: 8px
border-top-right-radius: 5px;
box-shadow: 0 0 20px rgba($color-dark-primary, 0.4); box-shadow: 0 0 20px rgba($color-dark-primary, 0.4);
border: 1px solid rgba($color-dark-primary, 0.5); border: 1px solid rgba($color-dark-primary, 0.5);
position: relative; position: relative;
overflow: hidden; overflow: hidden;
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0.08));
pointer-events: none;
z-index: 0;
}
> * { > * {
position: relative; position: relative;
z-index: 1; z-index: 1;
@@ -273,7 +258,6 @@
} }
.messageTime { .messageTime {
color: $color-dark-on-primary;
font-weight: 500; font-weight: 500;
} }
} }
@@ -0,0 +1,19 @@
@use "../../../css/colors" as *;
@use "../../../css/material" as *;
:global(.quote).contextualContent > :global(.quote-inner) {
display: flex;
flex-direction: column;
gap: 4px;
.replyUsername {
font-weight: 600;
color: $color-dark-on-surface;
font-size: 0.85rem;
}
.replyText {
overflow: hidden;
text-overflow: ellipsis;
}
}
@@ -7,6 +7,7 @@ import { useImmer } from "use-immer";
import { EmojiMenu } from "./EmojiMenu"; import { EmojiMenu } from "./EmojiMenu";
import { MaterialIcon, MaterialIconButton } from "@/utils/material"; import { MaterialIcon, MaterialIconButton } from "@/utils/material";
import styles from "@/pages/chat/css/ChatInput.module.scss"; import styles from "@/pages/chat/css/ChatInput.module.scss";
import replyPreviewStyles from "@/pages/chat/css/reply-preview.module.scss";
import { alert } from "mdui/functions/alert"; import { alert } from "mdui/functions/alert";
interface ChatInputWrapperProps { interface ChatInputWrapperProps {
@@ -161,9 +162,9 @@ export function ChatInputWrapper(
> >
<div className={styles.contextualPreview}> <div className={styles.contextualPreview}>
<MaterialIcon name="edit" /> <MaterialIcon name="edit" />
<Quote className={`${styles.quote} ${styles.contextualContent}`} background="surfaceContainer"> <Quote className={`${replyPreviewStyles.contextualContent}`} background="surfaceContainer">
<span className={styles.replyUsername}>{editingMessage!.username}</span> <span className={replyPreviewStyles.replyUsername}>{editingMessage!.username}</span>
<span className={styles.replyText}>{editingMessage!.content}</span> <span className={replyPreviewStyles.replyText}>{editingMessage!.content}</span>
</Quote> </Quote>
<MaterialIconButton icon="close" className={styles.replyCancel} onClick={onClearEdit}></MaterialIconButton> <MaterialIconButton icon="close" className={styles.replyCancel} onClick={onClearEdit}></MaterialIconButton>
</div> </div>
@@ -181,9 +182,9 @@ export function ChatInputWrapper(
> >
<div className={styles.contextualPreview}> <div className={styles.contextualPreview}>
<MaterialIcon name="reply" /> <MaterialIcon name="reply" />
<Quote className={`${styles.quote} ${styles.contextualContent}`} background="surfaceContainer"> <Quote className={`${replyPreviewStyles.contextualContent}`} background="surfaceContainer">
<span className={styles.replyUsername}>{replyTo!.username}</span> <span className={replyPreviewStyles.replyUsername}>{replyTo!.username}</span>
<span className={styles.replyText}>{replyTo!.content}</span> <span className={replyPreviewStyles.replyText}>{replyTo!.content}</span>
</Quote> </Quote>
<MaterialIconButton icon="close" className={styles.replyCancel} onClick={onClearReply}></MaterialIconButton> <MaterialIconButton icon="close" className={styles.replyCancel} onClick={onClearReply}></MaterialIconButton>
</div> </div>
+5 -4
View File
@@ -17,6 +17,7 @@ import { createPortal } from "react-dom";
import { parseProfileLink } from "@/core/profileLinks"; import { parseProfileLink } from "@/core/profileLinks";
import { MaterialCircularProgress, MaterialIconButton, MaterialList, MaterialListItem } from "@/utils/material"; import { MaterialCircularProgress, MaterialIconButton, MaterialList, MaterialListItem } from "@/utils/material";
import styles from "@/pages/chat/css/Message.module.scss"; import styles from "@/pages/chat/css/Message.module.scss";
import replyPreviewStyles from "@/pages/chat/css/reply-preview.module.scss";
interface MessageReactionsProps { interface MessageReactionsProps {
reactions?: Reaction[]; reactions?: Reaction[];
@@ -483,7 +484,7 @@ export function Message({ message, isAuthor, onContextMenu, onReactionClick, isD
{!isAuthor && !isDm && ( {!isAuthor && !isDm && (
<div className={styles.messageProfilePic} onClick={handleProfileClick}> <div className={styles.messageProfilePic} onClick={handleProfileClick}>
<img <img
src={message.username?.startsWith("Deleted User #") ? defaultAvatar : (message.profile_picture || defaultAvatar)} src={message.profile_picture || defaultAvatar}
alt={message.username} alt={message.username}
onError={(e) => { onError={(e) => {
e.target.src = defaultAvatar; e.target.src = defaultAvatar;
@@ -507,9 +508,9 @@ export function Message({ message, isAuthor, onContextMenu, onReactionClick, isD
)} )}
{message.reply_to && ( {message.reply_to && (
<Quote className={`${styles.replyPreview} ${styles.contextualContent}`} background={isAuthor ? "primaryContainer" : "surfaceContainer"}> <Quote className={`${styles.replyPreview} ${replyPreviewStyles.contextualContent}`} background={isAuthor ? "primaryContainer" : "surfaceContainer"}>
<span className={styles.replyUsername}>{message.reply_to.username}</span> <span className={replyPreviewStyles.replyUsername}>{message.reply_to.username}</span>
<span className={styles.replyText}>{message.reply_to.content}</span> <span className={replyPreviewStyles.replyText}>{message.reply_to.content}</span>
</Quote> </Quote>
)} )}