import SwiftUI struct MessageBubbleView: View { let message: Message let isMine: Bool var isHighlighted: Bool = false var isCurrentSearchResult: Bool = false var onReply: (() -> Void)? var onDelete: (() -> Void)? var body: some View { HStack { if isMine { Spacer(minLength: 60) } VStack(alignment: isMine ? .trailing : .leading, spacing: 4) { if !isMine { Text(message.senderUsername) .font(.caption.bold()) .foregroundStyle(.secondary) } if message.isDeleted { Text("Message deleted") .font(.body.italic()) .foregroundStyle(.secondary) .padding(12) .background(Color(.systemGray6)) .clipShape(RoundedRectangle(cornerRadius: 16)) } else { // Reply reference if let replyTo = message.replyTo { HStack(spacing: 4) { Rectangle() .fill(.blue.opacity(0.5)) .frame(width: 2) Text("Reply to message") .font(.caption) .foregroundStyle(.secondary) } .padding(.horizontal, 8) } // File card if let file = message.file { VStack(alignment: .leading, spacing: 4) { HStack { Image(systemName: "paperclip") Text(file.filename) .lineLimit(1) } .font(.subheadline) Text(formatFileSize(file.size)) .font(.caption) .foregroundStyle(.secondary) } .padding(12) .background(Color(.systemGray5)) .clipShape(RoundedRectangle(cornerRadius: 12)) } // Text content if let text = message.text { Text(text) .padding(12) .background( isMine ? Color.blue : Color(.systemGray5) ) .foregroundStyle(isMine ? .white : .primary) .clipShape(RoundedRectangle(cornerRadius: 16)) } // Timestamp Text(formatTime(message.createdAt)) .font(.caption2) .foregroundStyle(.secondary) } } .background( isCurrentSearchResult ? Color.orange.opacity(0.3) : isHighlighted ? Color.yellow.opacity(0.2) : Color.clear ) .clipShape(RoundedRectangle(cornerRadius: 16)) .contextMenu { if !message.isDeleted { Button(action: { onReply?() }) { Label("Reply", systemImage: "arrowshape.turn.up.left") } Button(action: { UIPasteboard.general.string = message.text ?? "" }) { Label("Copy", systemImage: "doc.on.doc") } if isMine { Button(role: .destructive, action: { onDelete?() }) { Label("Delete", systemImage: "trash") } } } } if !isMine { Spacer(minLength: 60) } } } private func formatTime(_ date: Date) -> String { let formatter = DateFormatter() if Calendar.current.isDateInToday(date) { formatter.dateFormat = "HH:mm" } else { formatter.dateFormat = "MMM d, HH:mm" } return formatter.string(from: date) } private func formatFileSize(_ bytes: Int) -> String { if bytes < 1024 { return "\(bytes) B" } if bytes < 1024 * 1024 { return "\(bytes / 1024) KB" } return String(format: "%.1f MB", Double(bytes) / (1024 * 1024)) } }