b3b5ff495d
- Fix DM backend ListMessages to use DESC + reverse (match channel handler) - Remove spurious .reverse() from frontend message/conversation stores - Create shared MessageInput component with Slack-style single toolbar row - Add file upload via + button with progress bar and drag-and-drop - Add markdown/rich text toggle with full WYSIWYG block formatting (lists, blockquotes, links, headings, code blocks) - Add frontend+backend security for file uploads (extension + content-type guards)
56 lines
1.4 KiB
TypeScript
56 lines
1.4 KiB
TypeScript
import { useState } from 'react';
|
|
|
|
interface Reaction {
|
|
emoji: string;
|
|
count: number;
|
|
users: string[];
|
|
reacted: boolean;
|
|
}
|
|
|
|
interface ReactionBarProps {
|
|
reactions: Reaction[];
|
|
onToggle: (emoji: string, isReacted: boolean) => Promise<void>;
|
|
}
|
|
|
|
export function ReactionBar({ reactions, onToggle }: ReactionBarProps) {
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
const toggleReaction = async (emoji: string) => {
|
|
if (loading) return;
|
|
setLoading(true);
|
|
try {
|
|
const existing = reactions.find(r => r.emoji === emoji);
|
|
await onToggle(emoji, !!existing?.reacted);
|
|
} catch (error) {
|
|
console.error('Failed to toggle reaction:', error);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
if (reactions.length === 0) return null;
|
|
|
|
return (
|
|
<div className="flex flex-wrap gap-1 mt-1">
|
|
{reactions.map((reaction) => (
|
|
<button
|
|
key={reaction.emoji}
|
|
onClick={() => toggleReaction(reaction.emoji)}
|
|
disabled={loading}
|
|
className={`
|
|
px-1.5 py-0.5 text-xs font-mono border rounded-sm
|
|
transition-colors duration-100
|
|
${reaction.reacted
|
|
? 'border-gb-orange bg-gb-bg-t text-gb-orange'
|
|
: 'border-gb-bg-t bg-gb-bg-s text-gb-fg-f hover:border-gb-fg-f'
|
|
}
|
|
`}
|
|
title={reaction.users.join(', ')}
|
|
>
|
|
{reaction.emoji} {reaction.count}
|
|
</button>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|