+ {/* Room Header */}
+
+
+ {/* Messages Body */}
+
+ {loadingMessages ? (
+
+
+ Loading messages...
+
+ ) : messages.length === 0 ? (
+
+
+ Belum ada pesan. Kirim pesan pertama untuk memulai!
+
+ ) : (
+ messages.map(msg => {
+ const isMe = msg.direction === 'outgoing';
+ const formattedTime = formatTime(msg.timestamp || Math.floor(new Date(msg.createdAt).getTime() / 1000));
+
+ // Highlight media messages differently if desired
+ const isMediaMessage = msg.type !== 'text';
+
+ const mediaInfo = msg.metadata?.media || (msg as any).media;
+
+ const renderMedia = () => {
+ if (msg.type === 'revoked') return null;
+ if (!mediaInfo) return null;
+ const mediaSrc = getMediaSrc(mediaInfo);
+ if (!mediaSrc) return null;
+
+ switch (msg.type) {
+ case 'image':
+ case 'sticker':
+ return (
+
+

+
+ );
+ case 'video':
+ return (
+
+
+
+ );
+ case 'audio':
+ case 'voice':
+ case 'ptt':
+ return (
+
+ );
+ case 'document':
+ default:
+ return (
+
+ );
+ }
+ };
+
+ const reactions = msg.metadata?.reactions || {};
+ const hasReactions = Object.keys(reactions).length > 0;
+
+ return (
+
+
+
+ {/* Quoted message display */}
+ {msg.metadata?.quotedMessage && (
+
+
+ {msg.metadata.quotedMessage.body}
+
+
+ )}
+
+ {renderMedia()}
+
+ {msg.body && (!mediaInfo || msg.body !== mediaInfo.filename) && (
+
{msg.body}
+ )}
+
+
+ {formattedTime}
+ {isMe && (
+
+ {msg.status === 'pending' && '🕒'}
+ {msg.status === 'sent' && '✓'}
+ {msg.status === 'delivered' && '✓✓'}
+ {msg.status === 'read' && '✓✓'}
+ {msg.status === 'failed' && '⚠️'}
+
+ )}
+
+
+ {/* Reactions display */}
+ {hasReactions && (
+
+ {Object.values(reactions).slice(0, 3).map((emoji, idx) => (
+ {emoji as string}
+ ))}
+ {Object.keys(reactions).length > 1 && (
+ {Object.keys(reactions).length}
+ )}
+
+ )}
+
+
+ {/* Message actions menu (hover) */}
+ {msg.type !== 'revoked' && (
+
+
+
+
+
+
+ {['👍', '❤️', '😂', '😮', '😢', '🙏'].map(emoji => (
+
+ ))}
+
+
+
+ {isMe && msg.status !== 'pending' && (
+
+ )}
+
+ )}
+
+
+ );
+ })
+ )}
+
+
+
+ {/* Attachment Preview Banner */}
+ {attachment && (
+
+ {previewUrl ? (
+

+ ) : (
+
📎
+ )}
+
+ {attachment.filename}
+ ({(attachment.file.size / 1024).toFixed(1)} KB)
+
+
+
+ )}
+
+ {/* Popular Emojis panel */}
+ {showEmojiPicker && (
+
+
+ {popularEmojis.map(emoji => (
+
+ ))}
+
+
+ )}
+
+ {/* Replying preview banner */}
+ {replyingTo && (
+
+
+
+ Membalas ke {replyingTo.direction === 'outgoing' ? 'Anda' : (activeChat.name || activeChat.id.split('@')[0])}
+
+
+ {replyingTo.type !== 'text' ? `[${replyingTo.type}]` : replyingTo.body}
+
+
+
+
+ )}
+
+ {/* Message Input bar */}
+
+
+ ) : (
+