diff --git a/packages/server/src/ws/events.ts b/packages/server/src/ws/events.ts index 421f5d1a..1840667c 100644 --- a/packages/server/src/ws/events.ts +++ b/packages/server/src/ws/events.ts @@ -713,32 +713,52 @@ function handleReactionAdd(event: Record, userId: string): void if (!messageId || !emoji) return; const db = getDb(); + + // Try server message first 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); - if (!serverId || !isMember(serverId, userId)) return; - - 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: { + 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, 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) { // Unique constraint violation (already reacted) @@ -752,22 +772,46 @@ function handleReactionRemove(event: Record, userId: string): v if (!messageId || !emoji) return; const db = getDb(); + + // Try server message first 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); - if (!serverId || !isMember(serverId, userId)) return; + const result = db.delete(schema.reactions) + .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( - eq(schema.reactions.messageId, messageId), - eq(schema.reactions.userId, userId), - eq(schema.reactions.emoji, emoji) + eq(schema.dmReactions.dmMessageId, messageId), + eq(schema.dmReactions.userId, userId), + eq(schema.dmReactions.emoji, emoji) )) .run(); if (result.changes > 0) { - connectionManager.sendToChannel(serverId, message.channelId, { + connectionManager.sendToDmMembers(dmMsg.dmChannelId, { type: 'reaction_removed', messageId, userId, diff --git a/packages/web/src/components/auth/LoginPage.tsx b/packages/web/src/components/auth/LoginPage.tsx index c7b400d2..90794e38 100644 --- a/packages/web/src/components/auth/LoginPage.tsx +++ b/packages/web/src/components/auth/LoginPage.tsx @@ -42,7 +42,7 @@ export function LoginPage() {
{error && ( -
+
{error}
)} diff --git a/packages/web/src/components/auth/RegisterPage.tsx b/packages/web/src/components/auth/RegisterPage.tsx index 1d9ddb91..02f72c58 100644 --- a/packages/web/src/components/auth/RegisterPage.tsx +++ b/packages/web/src/components/auth/RegisterPage.tsx @@ -54,7 +54,7 @@ export function RegisterPage() { {error && ( -
+
{error}
)} diff --git a/packages/web/src/components/chat/FriendsPage.tsx b/packages/web/src/components/chat/FriendsPage.tsx index 5c022071..e3eef5b5 100644 --- a/packages/web/src/components/chat/FriendsPage.tsx +++ b/packages/web/src/components/chat/FriendsPage.tsx @@ -161,7 +161,7 @@ export function FriendsPage() { {addStatus && ( -
+
{addStatus.message}
)} diff --git a/packages/web/src/components/chat/MarkdownRenderer.tsx b/packages/web/src/components/chat/MarkdownRenderer.tsx index 97fb60a3..b9491e2d 100644 --- a/packages/web/src/components/chat/MarkdownRenderer.tsx +++ b/packages/web/src/components/chat/MarkdownRenderer.tsx @@ -203,7 +203,7 @@ function buildComponents(): Components { ), thead: ({ children }) => {children}, tbody: ({ children }) => {children}, - tr: ({ children }) => {children}, + tr: ({ children }) => {children}, th: ({ children }) => {children}, td: ({ children }) => {children}, }; diff --git a/packages/web/src/components/chat/Message.tsx b/packages/web/src/components/chat/Message.tsx index c32defc3..eec4f2bf 100644 --- a/packages/web/src/components/chat/Message.tsx +++ b/packages/web/src/components/chat/Message.tsx @@ -286,7 +286,7 @@ export function Message({ message, isCompact, isFirstInGroup }: MessageProps) { key={att.id} href={`/api/uploads/${att.filename}`} 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" >
diff --git a/packages/web/src/components/chat/MessageInput.tsx b/packages/web/src/components/chat/MessageInput.tsx index 586c4953..2a245312 100644 --- a/packages/web/src/components/chat/MessageInput.tsx +++ b/packages/web/src/components/chat/MessageInput.tsx @@ -230,7 +230,7 @@ export function MessageInput({ channelId, channelName }: MessageInputProps) { return (
{replyTo && ( -
+
Replying to {replyTo.user.displayName ?? replyTo.user.username} @@ -261,7 +261,7 @@ export function MessageInput({ channelId, channelName }: MessageInputProps) { {/* File previews */} {files.length > 0 && ( -
+
{files.map((file, i) => (
{file.type.startsWith('image/') ? ( diff --git a/packages/web/src/components/chat/TypingIndicator.tsx b/packages/web/src/components/chat/TypingIndicator.tsx index 6d1b1514..fa4e87d4 100644 --- a/packages/web/src/components/chat/TypingIndicator.tsx +++ b/packages/web/src/components/chat/TypingIndicator.tsx @@ -32,7 +32,7 @@ export function TypingIndicator({ channelId }: TypingIndicatorProps) { return (
-
+
diff --git a/packages/web/src/components/layout/ChannelSidebar.tsx b/packages/web/src/components/layout/ChannelSidebar.tsx index 31b8d326..e2dc6401 100644 --- a/packages/web/src/components/layout/ChannelSidebar.tsx +++ b/packages/web/src/components/layout/ChannelSidebar.tsx @@ -121,7 +121,7 @@ export function ChannelSidebar() { <>
-
diff --git a/packages/web/src/components/modals/ChannelSettingsModal.tsx b/packages/web/src/components/modals/ChannelSettingsModal.tsx index 819be8b4..9e9ab25b 100644 --- a/packages/web/src/components/modals/ChannelSettingsModal.tsx +++ b/packages/web/src/components/modals/ChannelSettingsModal.tsx @@ -104,7 +104,7 @@ export function ChannelSettingsModal() {
{error && ( -
+
{error}
)} @@ -139,7 +139,7 @@ export function ChannelSettingsModal() {
{isPrivate && !isFetching && ( -
+
diff --git a/packages/web/src/components/modals/CreateChannel.tsx b/packages/web/src/components/modals/CreateChannel.tsx index d6a70bc1..96a74652 100644 --- a/packages/web/src/components/modals/CreateChannel.tsx +++ b/packages/web/src/components/modals/CreateChannel.tsx @@ -48,7 +48,7 @@ export function CreateChannelModal() {
{error && ( -
+
{error}
)} diff --git a/packages/web/src/components/modals/CreateServer.tsx b/packages/web/src/components/modals/CreateServer.tsx index e8dc9da9..c1d02fa0 100644 --- a/packages/web/src/components/modals/CreateServer.tsx +++ b/packages/web/src/components/modals/CreateServer.tsx @@ -41,7 +41,7 @@ export function CreateServerModal() { {error && ( -
+
{error}
)} diff --git a/packages/web/src/components/modals/InviteModal.tsx b/packages/web/src/components/modals/InviteModal.tsx index c21c745c..ddcc303f 100644 --- a/packages/web/src/components/modals/InviteModal.tsx +++ b/packages/web/src/components/modals/InviteModal.tsx @@ -55,7 +55,7 @@ export function InviteModal() { Share this invite link with friends to let them join your server.

{error && ( -
+
{error}
)} diff --git a/packages/web/src/components/modals/JoinServer.tsx b/packages/web/src/components/modals/JoinServer.tsx index 0f59a015..94d6805c 100644 --- a/packages/web/src/components/modals/JoinServer.tsx +++ b/packages/web/src/components/modals/JoinServer.tsx @@ -52,7 +52,7 @@ export function JoinServerModal() { Enter an invite code to join an existing server.

{error && ( -
+
{error}
)} diff --git a/packages/web/src/components/modals/ServerSettings.tsx b/packages/web/src/components/modals/ServerSettings.tsx index b6ff1849..7f8a22e9 100644 --- a/packages/web/src/components/modals/ServerSettings.tsx +++ b/packages/web/src/components/modals/ServerSettings.tsx @@ -189,7 +189,7 @@ function StreamingLimitsPanel() { {/* Save / Reset */} {saveError && ( -
{saveError}
+
{saveError}
)} {saveSuccess && (
Settings saved
@@ -369,7 +369,7 @@ export function ServerSettingsModal() { {/* Content */}
{error && ( -
{error}
+
{error}
)} {tab === 'overview' && ( diff --git a/packages/web/src/components/modals/UserSettings.tsx b/packages/web/src/components/modals/UserSettings.tsx index baee273b..2c08f310 100644 --- a/packages/web/src/components/modals/UserSettings.tsx +++ b/packages/web/src/components/modals/UserSettings.tsx @@ -76,7 +76,7 @@ export function UserSettingsModal() {
{error && ( -
{error}
+
{error}
)} {success && (
{success}
diff --git a/packages/web/src/components/voice/VoiceGrid.tsx b/packages/web/src/components/voice/VoiceGrid.tsx index 6c294368..f9573e65 100644 --- a/packages/web/src/components/voice/VoiceGrid.tsx +++ b/packages/web/src/components/voice/VoiceGrid.tsx @@ -134,7 +134,7 @@ export function VoiceGrid({ participants }: VoiceGridProps) { {/* Bottom strip of other tiles */} {!stripHidden && otherTiles.length > 0 && ( -
+
{otherTiles.map((t) => (