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: .leading, 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(.body3) .foregroundColor(.Material.Shape.separator) case .delivered: HStack { Image(systemName: "checkmark") .font(.body3) .foregroundColor(.Material.Shape.separator) Image(systemName: "checkmark") .font(.body3) .foregroundColor(.Material.Shape.separator) } case .error: Image(systemName: "exclamationmark.circle") .font(.body3) .foregroundColor(.Rainbow.red500) 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 } } }