124 lines
4.5 KiB
Swift
124 lines
4.5 KiB
Swift
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))
|
|
}
|
|
}
|