Files
Kecalek_python/ios_client/EncryptedChat/Views/Chat/MessageBubbleView.swift
2026-03-11 16:54:14 +01:00

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))
}
}