another.im-ios/Monal/another.im/Views/Conversation/ConversationMessageContainer.swift
2024-12-07 00:09:49 +01:00

293 lines
9.5 KiB
Swift

import AVKit
import MapKit
import QuickLook
import SwiftUI
struct ConversationMessageContainer: View {
let message: Message
var body: some View {
// fmodf: for now only text
Text(message.body)
.font(.body2)
.foregroundColor(.Material.Text.main)
.multilineTextAlignment(.leading)
.padding(10)
// if let msgText = message.body, msgText.isLocation {
// EmbededMapView(location: msgText.getLatLon)
// } else if let msgText = message.body, msgText.isContact {
// ContactView(message: message)
// } else if case .attachment(let attachment) = message.contentType {
// AttachmentView(message: message, attachment: attachment)
// } else {
// Text(message.body ?? "...")
// .font(.body2)
// .foregroundColor(.Material.Text.main)
// .multilineTextAlignment(.leading)
// .padding(10)
// }
}
}
struct MessageAttr: View {
let message: Message
var body: some View {
VStack(alignment: .trailing, spacing: 0) {
HStack(spacing: 2) {
if !message.isInbound && message.encrypted {
Image(systemName: "lock")
.font(.sub1)
.foregroundColor(.Material.Shape.separator)
}
Text(message.timestamp, style: .time)
.font(.sub1)
.foregroundColor(.Material.Shape.separator)
if message.isInbound && message.encrypted {
Image(systemName: "lock")
.font(.sub1)
.foregroundColor(.Material.Shape.separator)
}
}
Spacer()
switch message.status {
case .sent:
Image(systemName: "checkmark")
.font(.sub1)
.foregroundColor(.Material.Shape.separator)
case .delivered:
let img = Image(systemName: "checkmark")
Text("\(img)\(img)")
.font(.sub1)
.foregroundColor(.Material.Shape.separator)
case .error:
Image(systemName: "exclamationmark.circle")
.font(.sub1)
.foregroundColor(.Rainbow.red500)
case .pending:
Image(systemName: "clock")
.font(.sub1)
.foregroundColor(.Material.Shape.separator)
default:
EmptyView()
}
}
}
}
private struct EmbededMapView: View {
let location: CLLocationCoordinate2D
var body: some View {
Map(
coordinateRegion: .constant(MKCoordinateRegion(center: location, span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))),
interactionModes: [],
showsUserLocation: false,
userTrackingMode: .none,
annotationItems: [location],
annotationContent: { _ in
MapMarker(coordinate: location, tint: .blue)
}
)
.frame(width: Const.mapPreviewSize, height: Const.mapPreviewSize)
.onTapGesture {
let mapItem = MKMapItem(placemark: MKPlacemark(coordinate: location))
mapItem.name = "Location"
mapItem.openInMaps(launchOptions: [MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving])
}
}
}
private struct ContactView: View {
// let message: Message
var body: some View {
VStack {
ZStack {
Circle()
.frame(width: 44, height: 44)
.foregroundColor(contactName.firstLetterColor)
Text(contactName.firstLetter)
.foregroundColor(.white)
.font(.body1)
}
// Text(message.body?.getContactJid ?? "...")
// .font(.body2)
// .foregroundColor(.Material.Text.main)
// .multilineTextAlignment(.leading)
}
.padding()
.onTapGesture {
// TODO: Jump to add roster from here
}
}
private var contactName: String {
"dumb"
// message.body?.getContactJid ?? "?"
}
}
// private struct AttachmentView: View {
// @EnvironmentObject var attachments: AttachmentsStore
//
// let message: Message
// let attachment: Attachment
//
// var body: some View {
// if message.status == .error {
// failed
// } else {
// switch attachment.type {
// case .image:
// AsyncImage(url: attachment.thumbnailPath) { image in
// image
// .resizable()
// .aspectRatio(contentMode: .fit)
// .frame(width: Const.attachmentPreviewSize, height: Const.attachmentPreviewSize)
// } placeholder: {
// placeholder
// }
// .frame(width: Const.attachmentPreviewSize, height: Const.attachmentPreviewSize)
//
// case .video:
// if let file = attachment.localPath {
// VideoPlayerView(url: file)
// .frame(width: Const.attachmentPreviewSize, height: Const.attachmentPreviewSize)
// } else {
// placeholder
// }
//
// case .file:
// if let file = attachment.localPath {
// DocumentPreview(url: file)
// .frame(width: Const.attachmentPreviewSize, height: Const.attachmentPreviewSize)
// } else {
// placeholder
// }
//
// default:
// placeholder
// }
// }
// }
//
// @ViewBuilder private var placeholder: some View {
// Rectangle()
// .foregroundColor(.Material.Background.dark)
// .frame(width: Const.attachmentPreviewSize, height: Const.attachmentPreviewSize)
// .overlay {
// ZStack {
// ProgressView()
// .scaleEffect(1.5)
// .progressViewStyle(CircularProgressViewStyle(tint: .Material.Elements.active))
// let imageName = progressImageName(attachment.type)
// Image(systemName: imageName)
// .font(.body1)
// .foregroundColor(.Material.Elements.active)
// }
// }
// }
//
// @ViewBuilder private var failed: some View {
// Rectangle()
// .foregroundColor(.Material.Background.dark)
// .frame(width: Const.attachmentPreviewSize, height: Const.attachmentPreviewSize)
// .overlay {
// ZStack {
// VStack {
// Text(L10n.Attachment.Downloading.retry)
// .font(.body3)
// .foregroundColor(.Rainbow.red500)
// Image(systemName: "exclamationmark.arrow.triangle.2.circlepath")
// .font(.body1)
// .foregroundColor(.Rainbow.red500)
// }
// }
// }
// .onTapGesture {
// Task {
// try? await message.setStatus(.pending)
// }
// }
// }
//
// private func progressImageName(_ type: AttachmentType) -> String {
// switch type {
// case .image:
// return "photo"
//
// case .audio:
// return "music.note"
//
// case .video:
// return "film"
//
// case .file:
// return "doc"
// }
// }
//
// private func thumbnail() -> Image? {
// guard let thumbnailPath = attachment.thumbnailPath else { return nil }
// guard let uiImage = UIImage(contentsOfFile: thumbnailPath.path()) else { return nil }
// return Image(uiImage: uiImage)
// }
// }
// TODO: Make video player better!
private struct VideoPlayerView: UIViewControllerRepresentable {
let url: URL
func makeUIViewController(context _: Context) -> AVPlayerViewController {
let controller = AVPlayerViewController()
controller.player = AVPlayer(url: url)
controller.allowsPictureInPicturePlayback = true
return controller
}
func updateUIViewController(_: AVPlayerViewController, context _: Context) {
// Update the controller if needed.
}
}
struct DocumentPreview: UIViewControllerRepresentable {
var url: URL
func makeUIViewController(context: Context) -> QLPreviewController {
let controller = QLPreviewController()
controller.dataSource = context.coordinator
return controller
}
func updateUIViewController(_: QLPreviewController, context _: Context) {
// Update the controller if needed.
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, QLPreviewControllerDataSource {
var parent: DocumentPreview
init(_ parent: DocumentPreview) {
self.parent = parent
}
func numberOfPreviewItems(in _: QLPreviewController) -> Int {
1
}
func previewController(_: QLPreviewController, previewItemAt _: Int) -> QLPreviewItem {
parent.url as QLPreviewItem
}
}
}