mirror of
https://github.com/yusufipk/YTChatHub.git
synced 2026-09-11 10:56:17 +00:00
feat: add support for custom emojis and membership badges in chat display
This commit is contained in:
@@ -414,10 +414,14 @@ function ChatListPanel({ messages, renderItem, emptyState }: ChatListPanelProps)
|
||||
useEffect(() => {
|
||||
const node = scrollRef.current;
|
||||
if (node && wasAtBottomRef.current) {
|
||||
// Use requestAnimationFrame to ensure DOM has updated
|
||||
requestAnimationFrame(() => {
|
||||
node.scrollTop = node.scrollHeight;
|
||||
});
|
||||
// Use setTimeout with requestAnimationFrame for more reliable scrolling
|
||||
setTimeout(() => {
|
||||
requestAnimationFrame(() => {
|
||||
if (node && wasAtBottomRef.current) {
|
||||
node.scrollTop = node.scrollHeight;
|
||||
}
|
||||
});
|
||||
}, 0);
|
||||
}
|
||||
}, [messages]);
|
||||
|
||||
@@ -445,11 +449,15 @@ function ChatItem({ message, isSelected, onSelect }: ChatItemProps) {
|
||||
)}
|
||||
<span className="chatItem__author">{message.author}</span>
|
||||
{message.badges && message.badges.map((badge, i) => (
|
||||
<span key={i} className={`badge badge--${badge.type}`} title={badge.label}>
|
||||
{badge.type === 'moderator' && '🛡️'}
|
||||
{badge.type === 'member' && '⭐'}
|
||||
{badge.type === 'verified' && '✓'}
|
||||
</span>
|
||||
badge.imageUrl ? (
|
||||
<img key={i} src={badge.imageUrl} alt={badge.label} className="badge badge--image" title={badge.label} />
|
||||
) : (
|
||||
<span key={i} className={`badge badge--${badge.type}`} title={badge.label}>
|
||||
{badge.type === 'moderator' && '🛡️'}
|
||||
{badge.type === 'member' && '⭐'}
|
||||
{badge.type === 'verified' && '✓'}
|
||||
</span>
|
||||
)
|
||||
))}
|
||||
{message.superChat && (
|
||||
<span className="chatItem__superchat-inline" style={{ backgroundColor: message.superChat.color }}>
|
||||
@@ -460,7 +468,17 @@ function ChatItem({ message, isSelected, onSelect }: ChatItemProps) {
|
||||
<time className="chatItem__time">{new Date(message.publishedAt).toLocaleTimeString()}</time>
|
||||
</div>
|
||||
</div>
|
||||
<p className="chatItem__text">{message.text}</p>
|
||||
{message.runs?.length ? (
|
||||
<p className="chatItem__text">
|
||||
{message.runs.map((r, i) =>
|
||||
r.emojiUrl ? (
|
||||
<img key={i} src={r.emojiUrl} alt={r.emojiAlt || 'emoji'} className="chatItem__emoji" />
|
||||
) : (
|
||||
<span key={i}>{r.text}</span>
|
||||
)
|
||||
)}
|
||||
</p>
|
||||
) : message.text && <p className="chatItem__text">{message.text}</p>}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@@ -485,7 +503,17 @@ function MemberItem({ message, isSelected, onSelect }: ChatItemProps) {
|
||||
<time className="memberItem__time">{new Date(message.publishedAt).toLocaleTimeString()}</time>
|
||||
</div>
|
||||
</div>
|
||||
{message.text && <p className="memberItem__text">{message.text}</p>}
|
||||
{message.runs?.length ? (
|
||||
<p className="memberItem__text">
|
||||
{message.runs.map((r, i) =>
|
||||
r.emojiUrl ? (
|
||||
<img key={i} src={r.emojiUrl} alt={r.emojiAlt || 'emoji'} className="memberItem__emoji" />
|
||||
) : (
|
||||
<span key={i}>{r.text}</span>
|
||||
)
|
||||
)}
|
||||
</p>
|
||||
) : message.text && <p className="memberItem__text">{message.text}</p>}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -729,11 +729,19 @@ main {
|
||||
font-size: 0.85rem;
|
||||
padding: 0.15rem 0.4rem;
|
||||
border-radius: 4px;
|
||||
font-weight: 600;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.badge--image {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
padding: 0;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.badge--moderator {
|
||||
background: rgba(239, 68, 68, 0.2);
|
||||
color: #fca5a5;
|
||||
@@ -964,6 +972,30 @@ main {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.overlay__badge--image {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
/* Inline emoji images */
|
||||
.overlay__emoji {
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
vertical-align: -0.2em;
|
||||
margin: 0 0.08em;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.chatItem__emoji,
|
||||
.memberItem__emoji {
|
||||
width: 1.1em;
|
||||
height: 1.1em;
|
||||
vertical-align: -0.2em;
|
||||
margin: 0 0.06em;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.overlay__superchat,
|
||||
.overlay__membership {
|
||||
padding: 0.6rem 1rem;
|
||||
|
||||
@@ -79,7 +79,17 @@ export default function OverlayPage() {
|
||||
{message.superChat.currency}{message.superChat.currency ? ' ' : ''}{message.superChat.amount}
|
||||
</span>
|
||||
</div>
|
||||
{message.text && (
|
||||
{message.runs?.length ? (
|
||||
<p className="overlay__superchat-text">
|
||||
{message.runs.map((r, i) =>
|
||||
r.emojiUrl ? (
|
||||
<img key={i} src={r.emojiUrl} alt={r.emojiAlt || 'emoji'} className="overlay__emoji" />
|
||||
) : (
|
||||
<span key={i}>{r.text}</span>
|
||||
)
|
||||
)}
|
||||
</p>
|
||||
) : message.text && (
|
||||
<p className="overlay__superchat-text">{message.text}</p>
|
||||
)}
|
||||
</>
|
||||
@@ -99,7 +109,17 @@ export default function OverlayPage() {
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{message.text && (
|
||||
{message.runs?.length ? (
|
||||
<p className="overlay__membership-text">
|
||||
{message.runs.map((r, i) =>
|
||||
r.emojiUrl ? (
|
||||
<img key={i} src={r.emojiUrl} alt={r.emojiAlt || 'emoji'} className="overlay__emoji" />
|
||||
) : (
|
||||
<span key={i}>{r.text}</span>
|
||||
)
|
||||
)}
|
||||
</p>
|
||||
) : message.text && (
|
||||
<p className="overlay__membership-text">{message.text}</p>
|
||||
)}
|
||||
</>
|
||||
@@ -113,16 +133,30 @@ export default function OverlayPage() {
|
||||
<div className="overlay__authorLine">
|
||||
<span className="overlay__author">{message.author}</span>
|
||||
{message.badges && message.badges.map((badge, i) => (
|
||||
<span key={i} className={`overlay__badge overlay__badge--${badge.type}`} title={badge.label}>
|
||||
{badge.type === 'moderator' && '🛡️'}
|
||||
{badge.type === 'member' && '⭐'}
|
||||
{badge.type === 'verified' && '✓'}
|
||||
</span>
|
||||
badge.imageUrl ? (
|
||||
<img key={i} src={badge.imageUrl} alt={badge.label} className="overlay__badge overlay__badge--image" title={badge.label} />
|
||||
) : (
|
||||
<span key={i} className={`overlay__badge overlay__badge--${badge.type}`} title={badge.label}>
|
||||
{badge.type === 'moderator' && '🛡️'}
|
||||
{badge.type === 'member' && '⭐'}
|
||||
{badge.type === 'verified' && '✓'}
|
||||
</span>
|
||||
)
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{message.text && (
|
||||
{message.runs?.length ? (
|
||||
<p className="overlay__text">
|
||||
{message.runs.map((r, i) =>
|
||||
r.emojiUrl ? (
|
||||
<img key={i} src={r.emojiUrl} alt={r.emojiAlt || 'emoji'} className="overlay__emoji" />
|
||||
) : (
|
||||
<span key={i}>{r.text}</span>
|
||||
)
|
||||
)}
|
||||
</p>
|
||||
) : message.text && (
|
||||
<p className="overlay__text">{message.text}</p>
|
||||
)}
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user