fix: DM reactions + eradicate broken CSS variable opacity modifiers
Server-side: handleReactionAdd/Remove now check dm_messages table when message isn't found in server messages, enabling DM emoji reactions via the same unified client event path. Client-side: replaced all remaining broken opacity modifiers on CSS variables (border-border-soft/50, bg-accent-rose/10, bg-surface-*/N, etc.) with explicit rgba values. Tailwind can't decompose var() for opacity, causing fallback to currentColor. Fixes 16 files across error boxes, table borders, surface tints, and input separators.
This commit is contained in:
@@ -713,32 +713,52 @@ function handleReactionAdd(event: Record<string, unknown>, userId: string): void
|
|||||||
if (!messageId || !emoji) return;
|
if (!messageId || !emoji) return;
|
||||||
|
|
||||||
const db = getDb();
|
const db = getDb();
|
||||||
|
|
||||||
|
// Try server message first
|
||||||
const message = db.select().from(schema.messages).where(eq(schema.messages.id, messageId)).get();
|
const message = db.select().from(schema.messages).where(eq(schema.messages.id, messageId)).get();
|
||||||
if (!message) return;
|
if (message) {
|
||||||
|
const serverId = getChannelServerId(message.channelId);
|
||||||
|
if (!serverId || !isMember(serverId, userId)) return;
|
||||||
|
|
||||||
const serverId = getChannelServerId(message.channelId);
|
const reactionId = generateSnowflake();
|
||||||
if (!serverId || !isMember(serverId, userId)) return;
|
try {
|
||||||
|
db.insert(schema.reactions).values({
|
||||||
const reactionId = generateSnowflake();
|
|
||||||
try {
|
|
||||||
db.insert(schema.reactions).values({
|
|
||||||
id: reactionId,
|
|
||||||
messageId,
|
|
||||||
userId,
|
|
||||||
emoji,
|
|
||||||
createdAt: Date.now(),
|
|
||||||
}).run();
|
|
||||||
|
|
||||||
connectionManager.sendToChannel(serverId, message.channelId, {
|
|
||||||
type: 'reaction_added',
|
|
||||||
messageId,
|
|
||||||
reaction: {
|
|
||||||
id: reactionId,
|
id: reactionId,
|
||||||
messageId,
|
messageId,
|
||||||
userId,
|
userId,
|
||||||
emoji,
|
emoji,
|
||||||
createdAt: Date.now(),
|
createdAt: Date.now(),
|
||||||
},
|
}).run();
|
||||||
|
|
||||||
|
connectionManager.sendToChannel(serverId, message.channelId, {
|
||||||
|
type: 'reaction_added',
|
||||||
|
messageId,
|
||||||
|
reaction: { id: reactionId, messageId, userId, emoji, createdAt: Date.now() },
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
// Unique constraint violation (already reacted)
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall through to DM message
|
||||||
|
const dmMsg = db.select().from(schema.dmMessages).where(eq(schema.dmMessages.id, messageId)).get();
|
||||||
|
if (!dmMsg || !isDmMember(dmMsg.dmChannelId, userId)) return;
|
||||||
|
|
||||||
|
const reactionId = generateSnowflake();
|
||||||
|
try {
|
||||||
|
db.insert(schema.dmReactions).values({
|
||||||
|
id: reactionId,
|
||||||
|
dmMessageId: messageId,
|
||||||
|
userId,
|
||||||
|
emoji,
|
||||||
|
createdAt: Date.now(),
|
||||||
|
}).run();
|
||||||
|
|
||||||
|
connectionManager.sendToDmMembers(dmMsg.dmChannelId, {
|
||||||
|
type: 'reaction_added',
|
||||||
|
messageId,
|
||||||
|
reaction: { id: reactionId, messageId, userId, emoji, createdAt: Date.now() },
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Unique constraint violation (already reacted)
|
// Unique constraint violation (already reacted)
|
||||||
@@ -752,22 +772,46 @@ function handleReactionRemove(event: Record<string, unknown>, userId: string): v
|
|||||||
if (!messageId || !emoji) return;
|
if (!messageId || !emoji) return;
|
||||||
|
|
||||||
const db = getDb();
|
const db = getDb();
|
||||||
|
|
||||||
|
// Try server message first
|
||||||
const message = db.select().from(schema.messages).where(eq(schema.messages.id, messageId)).get();
|
const message = db.select().from(schema.messages).where(eq(schema.messages.id, messageId)).get();
|
||||||
if (!message) return;
|
if (message) {
|
||||||
|
const serverId = getChannelServerId(message.channelId);
|
||||||
|
if (!serverId || !isMember(serverId, userId)) return;
|
||||||
|
|
||||||
const serverId = getChannelServerId(message.channelId);
|
const result = db.delete(schema.reactions)
|
||||||
if (!serverId || !isMember(serverId, userId)) return;
|
.where(and(
|
||||||
|
eq(schema.reactions.messageId, messageId),
|
||||||
|
eq(schema.reactions.userId, userId),
|
||||||
|
eq(schema.reactions.emoji, emoji)
|
||||||
|
))
|
||||||
|
.run();
|
||||||
|
|
||||||
const result = db.delete(schema.reactions)
|
if (result.changes > 0) {
|
||||||
|
connectionManager.sendToChannel(serverId, message.channelId, {
|
||||||
|
type: 'reaction_removed',
|
||||||
|
messageId,
|
||||||
|
userId,
|
||||||
|
emoji,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall through to DM message
|
||||||
|
const dmMsg = db.select().from(schema.dmMessages).where(eq(schema.dmMessages.id, messageId)).get();
|
||||||
|
if (!dmMsg || !isDmMember(dmMsg.dmChannelId, userId)) return;
|
||||||
|
|
||||||
|
const result = db.delete(schema.dmReactions)
|
||||||
.where(and(
|
.where(and(
|
||||||
eq(schema.reactions.messageId, messageId),
|
eq(schema.dmReactions.dmMessageId, messageId),
|
||||||
eq(schema.reactions.userId, userId),
|
eq(schema.dmReactions.userId, userId),
|
||||||
eq(schema.reactions.emoji, emoji)
|
eq(schema.dmReactions.emoji, emoji)
|
||||||
))
|
))
|
||||||
.run();
|
.run();
|
||||||
|
|
||||||
if (result.changes > 0) {
|
if (result.changes > 0) {
|
||||||
connectionManager.sendToChannel(serverId, message.channelId, {
|
connectionManager.sendToDmMembers(dmMsg.dmChannelId, {
|
||||||
type: 'reaction_removed',
|
type: 'reaction_removed',
|
||||||
messageId,
|
messageId,
|
||||||
userId,
|
userId,
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export function LoginPage() {
|
|||||||
|
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
{error && (
|
{error && (
|
||||||
<div className="mb-4 p-3 bg-accent-rose/10 border border-accent-rose/30 rounded text-txt-danger text-sm">
|
<div className="mb-4 p-3 bg-[rgba(253,164,175,0.10)] border border-[rgba(253,164,175,0.30)] rounded text-txt-danger text-sm">
|
||||||
{error}
|
{error}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ export function RegisterPage() {
|
|||||||
|
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
{error && (
|
{error && (
|
||||||
<div className="mb-4 p-3 bg-accent-rose/10 border border-accent-rose/30 rounded text-txt-danger text-sm">
|
<div className="mb-4 p-3 bg-[rgba(253,164,175,0.10)] border border-[rgba(253,164,175,0.30)] rounded text-txt-danger text-sm">
|
||||||
{error}
|
{error}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ export function FriendsPage() {
|
|||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
{addStatus && (
|
{addStatus && (
|
||||||
<div className={`text-sm p-3 rounded-lg border ${addStatus.type === 'success' ? 'text-txt-positive border-status-online/20 bg-status-online/5' : 'text-txt-danger border-accent-rose/20 bg-accent-rose/5'}`}>
|
<div className={`text-sm p-3 rounded-lg border ${addStatus.type === 'success' ? 'text-txt-positive border-[rgba(134,239,172,0.20)] bg-[rgba(134,239,172,0.05)]' : 'text-txt-danger border-[rgba(253,164,175,0.20)] bg-[rgba(253,164,175,0.05)]'}`}>
|
||||||
{addStatus.message}
|
{addStatus.message}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -203,7 +203,7 @@ function buildComponents(): Components {
|
|||||||
),
|
),
|
||||||
thead: ({ children }) => <thead className="border-b border-border-soft">{children}</thead>,
|
thead: ({ children }) => <thead className="border-b border-border-soft">{children}</thead>,
|
||||||
tbody: ({ children }) => <tbody>{children}</tbody>,
|
tbody: ({ children }) => <tbody>{children}</tbody>,
|
||||||
tr: ({ children }) => <tr className="border-b border-border-soft/50">{children}</tr>,
|
tr: ({ children }) => <tr className="border-b border-white/[0.06]">{children}</tr>,
|
||||||
th: ({ children }) => <th className="px-3 py-1.5 text-left text-txt-primary font-semibold">{children}</th>,
|
th: ({ children }) => <th className="px-3 py-1.5 text-left text-txt-primary font-semibold">{children}</th>,
|
||||||
td: ({ children }) => <td className="px-3 py-1.5">{children}</td>,
|
td: ({ children }) => <td className="px-3 py-1.5">{children}</td>,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -286,7 +286,7 @@ export function Message({ message, isCompact, isFirstInGroup }: MessageProps) {
|
|||||||
key={att.id}
|
key={att.id}
|
||||||
href={`/api/uploads/${att.filename}`}
|
href={`/api/uploads/${att.filename}`}
|
||||||
download={att.originalName}
|
download={att.originalName}
|
||||||
className="flex items-center gap-3 p-4 bg-surface-channel/50 rounded-lg border border-border-hard hover:bg-interactive-hover transition-all max-w-[400px] mt-1 group/att"
|
className="flex items-center gap-3 p-4 bg-[rgba(26,26,35,0.50)] rounded-lg border border-border-hard hover:bg-interactive-hover transition-all max-w-[400px] mt-1 group/att"
|
||||||
>
|
>
|
||||||
<div className="p-2 bg-surface-base rounded text-txt-tertiary group-hover/att:text-txt-primary transition-colors">
|
<div className="p-2 bg-surface-base rounded text-txt-tertiary group-hover/att:text-txt-primary transition-colors">
|
||||||
<svg className="w-8 h-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg className="w-8 h-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
|||||||
@@ -230,7 +230,7 @@ export function MessageInput({ channelId, channelName }: MessageInputProps) {
|
|||||||
return (
|
return (
|
||||||
<div data-pip-obstacle="bottom" className="px-3 pb-3 flex-shrink-0 md:absolute md:bottom-3 md:left-3 md:right-3 md:z-[110] md:px-0 md:pb-0 md:glass-bubble md:rounded-[14px]">
|
<div data-pip-obstacle="bottom" className="px-3 pb-3 flex-shrink-0 md:absolute md:bottom-3 md:left-3 md:right-3 md:z-[110] md:px-0 md:pb-0 md:glass-bubble md:rounded-[14px]">
|
||||||
{replyTo && (
|
{replyTo && (
|
||||||
<div className="bg-interactive-hover rounded-t-lg px-4 py-2 flex items-center justify-between border-b border-border-hard/50">
|
<div className="bg-interactive-hover rounded-t-lg px-4 py-2 flex items-center justify-between border-b border-white/[0.06]">
|
||||||
<div className="flex items-center gap-1 text-[14px] text-txt-message truncate">
|
<div className="flex items-center gap-1 text-[14px] text-txt-message truncate">
|
||||||
<span className="opacity-60">Replying to</span>
|
<span className="opacity-60">Replying to</span>
|
||||||
<span className="font-bold">{replyTo.user.displayName ?? replyTo.user.username}</span>
|
<span className="font-bold">{replyTo.user.displayName ?? replyTo.user.username}</span>
|
||||||
@@ -261,7 +261,7 @@ export function MessageInput({ channelId, channelName }: MessageInputProps) {
|
|||||||
|
|
||||||
{/* File previews */}
|
{/* File previews */}
|
||||||
{files.length > 0 && (
|
{files.length > 0 && (
|
||||||
<div className="p-4 flex flex-wrap gap-4 bg-surface-channel/30">
|
<div className="p-4 flex flex-wrap gap-4 bg-[rgba(26,26,35,0.30)]">
|
||||||
{files.map((file, i) => (
|
{files.map((file, i) => (
|
||||||
<div key={i} className="relative group bg-surface-channel rounded-lg p-2 max-w-[200px] shadow-elevation-low border border-border-hard">
|
<div key={i} className="relative group bg-surface-channel rounded-lg p-2 max-w-[200px] shadow-elevation-low border border-border-hard">
|
||||||
{file.type.startsWith('image/') ? (
|
{file.type.startsWith('image/') ? (
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export function TypingIndicator({ channelId }: TypingIndicatorProps) {
|
|||||||
return (
|
return (
|
||||||
<div className="h-[24px] px-4 flex items-center text-[12px] text-txt-primary font-medium select-none pointer-events-none">
|
<div className="h-[24px] px-4 flex items-center text-[12px] text-txt-primary font-medium select-none pointer-events-none">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="flex gap-[2px] bg-surface-elevated/20 rounded-full px-2 py-1">
|
<div className="flex gap-[2px] bg-[rgba(37,37,48,0.20)] rounded-full px-2 py-1">
|
||||||
<div className="w-[5px] h-[5px] bg-txt-message rounded-full animate-bounce" style={{ animationDelay: '0ms', animationDuration: '0.8s' }} />
|
<div className="w-[5px] h-[5px] bg-txt-message rounded-full animate-bounce" style={{ animationDelay: '0ms', animationDuration: '0.8s' }} />
|
||||||
<div className="w-[5px] h-[5px] bg-txt-message rounded-full animate-bounce" style={{ animationDelay: '150ms', animationDuration: '0.8s' }} />
|
<div className="w-[5px] h-[5px] bg-txt-message rounded-full animate-bounce" style={{ animationDelay: '150ms', animationDuration: '0.8s' }} />
|
||||||
<div className="w-[5px] h-[5px] bg-txt-message rounded-full animate-bounce" style={{ animationDelay: '300ms', animationDuration: '0.8s' }} />
|
<div className="w-[5px] h-[5px] bg-txt-message rounded-full animate-bounce" style={{ animationDelay: '300ms', animationDuration: '0.8s' }} />
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ export function ChannelSidebar() {
|
|||||||
<>
|
<>
|
||||||
<div className="w-60 md:w-full bg-surface-channel flex flex-col flex-shrink-0 select-none md:pl-[72px] border-r border-border-hard">
|
<div className="w-60 md:w-full bg-surface-channel flex flex-col flex-shrink-0 select-none md:pl-[72px] border-r border-border-hard">
|
||||||
<div className="h-12 px-[10px] flex items-center border-b border-border-hard z-10">
|
<div className="h-12 px-[10px] flex items-center border-b border-border-hard z-10">
|
||||||
<button className="flex-1 bg-surface-base text-txt-tertiary text-[13px] font-medium py-[5px] px-2 rounded-[4px] text-left hover:bg-surface-base/80 transition-colors">
|
<button className="flex-1 bg-surface-base text-txt-tertiary text-[13px] font-medium py-[5px] px-2 rounded-[4px] text-left hover:bg-surface-base transition-colors">
|
||||||
Find or start a conversation
|
Find or start a conversation
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ export function ChannelSettingsModal() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{error && (
|
{error && (
|
||||||
<div className="p-2 bg-accent-rose/10 border border-accent-rose/30 rounded text-txt-danger text-sm">
|
<div className="p-2 bg-[rgba(253,164,175,0.10)] border border-[rgba(253,164,175,0.30)] rounded text-txt-danger text-sm">
|
||||||
{error}
|
{error}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -139,7 +139,7 @@ export function ChannelSettingsModal() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{isPrivate && !isFetching && (
|
{isPrivate && !isFetching && (
|
||||||
<div className="flex items-start gap-2 p-2 bg-surface-input/50 rounded text-xs text-txt-tertiary">
|
<div className="flex items-start gap-2 p-2 bg-[rgba(17,17,24,0.50)] rounded text-xs text-txt-tertiary">
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" className="flex-shrink-0 mt-0.5 text-txt-secondary">
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" className="flex-shrink-0 mt-0.5 text-txt-secondary">
|
||||||
<path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z" />
|
<path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export function CreateChannelModal() {
|
|||||||
<Modal isOpen={isOpen} onClose={closeModal} title="Create Channel">
|
<Modal isOpen={isOpen} onClose={closeModal} title="Create Channel">
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
{error && (
|
{error && (
|
||||||
<div className="mb-3 p-2 bg-accent-rose/10 border border-accent-rose/30 rounded text-txt-danger text-sm">
|
<div className="mb-3 p-2 bg-[rgba(253,164,175,0.10)] border border-[rgba(253,164,175,0.30)] rounded text-txt-danger text-sm">
|
||||||
{error}
|
{error}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export function CreateServerModal() {
|
|||||||
<Modal isOpen={isOpen} onClose={closeModal} title="Create a Server">
|
<Modal isOpen={isOpen} onClose={closeModal} title="Create a Server">
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
{error && (
|
{error && (
|
||||||
<div className="mb-3 p-2 bg-accent-rose/10 border border-accent-rose/30 rounded text-txt-danger text-sm">
|
<div className="mb-3 p-2 bg-[rgba(253,164,175,0.10)] border border-[rgba(253,164,175,0.30)] rounded text-txt-danger text-sm">
|
||||||
{error}
|
{error}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ export function InviteModal() {
|
|||||||
Share this invite link with friends to let them join your server.
|
Share this invite link with friends to let them join your server.
|
||||||
</p>
|
</p>
|
||||||
{error && (
|
{error && (
|
||||||
<div className="mb-3 p-2 bg-accent-rose/10 border border-accent-rose/30 rounded text-txt-danger text-sm">
|
<div className="mb-3 p-2 bg-[rgba(253,164,175,0.10)] border border-[rgba(253,164,175,0.30)] rounded text-txt-danger text-sm">
|
||||||
{error}
|
{error}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ export function JoinServerModal() {
|
|||||||
Enter an invite code to join an existing server.
|
Enter an invite code to join an existing server.
|
||||||
</p>
|
</p>
|
||||||
{error && (
|
{error && (
|
||||||
<div className="mb-3 p-2 bg-accent-rose/10 border border-accent-rose/30 rounded text-txt-danger text-sm">
|
<div className="mb-3 p-2 bg-[rgba(253,164,175,0.10)] border border-[rgba(253,164,175,0.30)] rounded text-txt-danger text-sm">
|
||||||
{error}
|
{error}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ function StreamingLimitsPanel() {
|
|||||||
|
|
||||||
{/* Save / Reset */}
|
{/* Save / Reset */}
|
||||||
{saveError && (
|
{saveError && (
|
||||||
<div className="p-2 bg-accent-rose/10 border border-accent-rose/30 rounded text-txt-danger text-sm">{saveError}</div>
|
<div className="p-2 bg-[rgba(253,164,175,0.10)] border border-[rgba(253,164,175,0.30)] rounded text-txt-danger text-sm">{saveError}</div>
|
||||||
)}
|
)}
|
||||||
{saveSuccess && (
|
{saveSuccess && (
|
||||||
<div className="p-2 bg-status-online/10 border border-status-online/30 rounded text-status-online text-sm">Settings saved</div>
|
<div className="p-2 bg-status-online/10 border border-status-online/30 rounded text-status-online text-sm">Settings saved</div>
|
||||||
@@ -369,7 +369,7 @@ export function ServerSettingsModal() {
|
|||||||
{/* Content */}
|
{/* Content */}
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
{error && (
|
{error && (
|
||||||
<div className="mb-3 p-2 bg-accent-rose/10 border border-accent-rose/30 rounded text-txt-danger text-sm">{error}</div>
|
<div className="mb-3 p-2 bg-[rgba(253,164,175,0.10)] border border-[rgba(253,164,175,0.30)] rounded text-txt-danger text-sm">{error}</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{tab === 'overview' && (
|
{tab === 'overview' && (
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ export function UserSettingsModal() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{error && (
|
{error && (
|
||||||
<div className="p-2 bg-accent-rose/10 border border-accent-rose/30 rounded text-txt-danger text-sm">{error}</div>
|
<div className="p-2 bg-[rgba(253,164,175,0.10)] border border-[rgba(253,164,175,0.30)] rounded text-txt-danger text-sm">{error}</div>
|
||||||
)}
|
)}
|
||||||
{success && (
|
{success && (
|
||||||
<div className="p-2 bg-status-online/10 border border-status-online/30 rounded text-status-online text-sm">{success}</div>
|
<div className="p-2 bg-status-online/10 border border-status-online/30 rounded text-status-online text-sm">{success}</div>
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ export function VoiceGrid({ participants }: VoiceGridProps) {
|
|||||||
|
|
||||||
{/* Bottom strip of other tiles */}
|
{/* Bottom strip of other tiles */}
|
||||||
{!stripHidden && otherTiles.length > 0 && (
|
{!stripHidden && otherTiles.length > 0 && (
|
||||||
<div className="h-[120px] flex-shrink-0 flex items-center justify-center gap-2 p-2 bg-surface-base/50 overflow-x-auto no-scrollbar">
|
<div className="h-[120px] flex-shrink-0 flex items-center justify-center gap-2 p-2 bg-[rgba(11,11,16,0.50)] overflow-x-auto no-scrollbar">
|
||||||
{otherTiles.map((t) => (
|
{otherTiles.map((t) => (
|
||||||
<div
|
<div
|
||||||
key={t.key}
|
key={t.key}
|
||||||
|
|||||||
Reference in New Issue
Block a user