fix: DM message ordering, consolidate input toolbar, add rich text/WYSIWYG, file upload with drag-drop

- 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)
This commit is contained in:
2026-07-06 17:33:20 +00:00
parent 9512dedec1
commit b3b5ff495d
19 changed files with 1369 additions and 301 deletions
+14 -4
View File
@@ -186,24 +186,34 @@ export const useWebSocketStore = create<WebSocketState>((set, get) => ({
}
case 'REACTION_ADD': {
const channelId = typeof payload.channel_id === 'string' ? payload.channel_id : '';
const convId = typeof payload.conversation_id === 'string' ? payload.conversation_id : '';
const messageId = typeof payload.message_id === 'string' ? payload.message_id : '';
const reaction = isRecord(payload.reaction) ? payload.reaction : null;
if (channelId && messageId && reaction) {
if (messageId && reaction) {
const emoji = typeof reaction.emoji === 'string' ? reaction.emoji : '';
const userId = typeof reaction.user_id === 'string' ? reaction.user_id : '';
if (emoji && userId) {
addReaction(channelId, messageId, emoji, userId);
if (convId) {
useConversationStore.getState().addReaction(convId, messageId, emoji, userId);
} else if (channelId) {
addReaction(channelId, messageId, emoji, userId);
}
}
}
break;
}
case 'REACTION_REMOVE': {
const channelId = typeof payload.channel_id === 'string' ? payload.channel_id : '';
const convId = typeof payload.conversation_id === 'string' ? payload.conversation_id : '';
const messageId = typeof payload.message_id === 'string' ? payload.message_id : '';
const emoji = typeof payload.emoji === 'string' ? payload.emoji : '';
const userId = typeof payload.user_id === 'string' ? payload.user_id : '';
if (channelId && messageId && emoji && userId) {
removeReaction(channelId, messageId, emoji, userId);
if (messageId && emoji && userId) {
if (convId) {
useConversationStore.getState().removeReaction(convId, messageId, emoji, userId);
} else if (channelId) {
removeReaction(channelId, messageId, emoji, userId);
}
}
break;
}