// // ContactListNavLink.swift // SimpleX (iOS) // // Created by Diogo Cunha on 01/08/2024. // Copyright © 2024 SimpleX Chat. All rights reserved. // import SwiftUI import SimpleXChat struct ContactListNavLink: View { @EnvironmentObject var theme: AppTheme @ObservedObject var chat: Chat var showDeletedChatIcon: Bool @State private var alert: SomeAlert? = nil @State private var actionSheet: SomeActionSheet? = nil @State private var sheet: SomeSheet? = nil @State private var showConnectContactViaAddressDialog = false @State private var showContactRequestDialog = false var body: some View { let contactType = chatContactType(chat) Group { switch (chat.chatInfo) { case let .direct(contact): switch contactType { case .recent: recentContactNavLink(contact) case .chatDeleted: deletedChatNavLink(contact) case .card: contactCardNavLink(contact) default: EmptyView() } case let .contactRequest(contactRequest): contactRequestNavLink(contactRequest) default: EmptyView() } } .alert(item: $alert) { $0.alert } .actionSheet(item: $actionSheet) { $0.actionSheet } .sheet(item: $sheet) { if #available(iOS 16.0, *) { $0.content .presentationDetents([.fraction(0.4)]) } else { $0.content } } } func recentContactNavLink(_ contact: Contact) -> some View { Button { dismissAllSheets(animated: true) { ItemsModel.shared.loadOpenChat(contact.id) } } label: { contactPreview(contact, titleColor: theme.colors.onBackground) } .swipeActions(edge: .trailing, allowsFullSwipe: true) { Button { deleteContactDialog( chat, contact, dismissToChatList: false, showAlert: { alert = $0 }, showActionSheet: { actionSheet = $0 }, showSheetContent: { sheet = $0 } ) } label: { Label("Delete", systemImage: "trash") } .tint(.red) } } func deletedChatNavLink(_ contact: Contact) -> some View { Button { Task { await MainActor.run { dismissAllSheets(animated: true) { ItemsModel.shared.loadOpenChat(contact.id) } } } } label: { contactPreview(contact, titleColor: theme.colors.onBackground) } .swipeActions(edge: .trailing, allowsFullSwipe: true) { Button { deleteContactDialog( chat, contact, dismissToChatList: false, showAlert: { alert = $0 }, showActionSheet: { actionSheet = $0 }, showSheetContent: { sheet = $0 } ) } label: { Label("Delete", systemImage: "trash") } .tint(.red) } } func contactPreview(_ contact: Contact, titleColor: Color) -> some View { HStack{ ProfileImage(imageStr: contact.image, size: 30) previewTitle(contact, titleColor: titleColor) Spacer() HStack { if showDeletedChatIcon && contact.chatDeleted { Image(systemName: "archivebox") .resizable() .scaledToFit() .frame(width: 18, height: 18) .foregroundColor(.secondary.opacity(0.65)) } else if chat.chatInfo.chatSettings?.favorite ?? false { Image(systemName: "star.fill") .resizable() .scaledToFill() .frame(width: 18, height: 18) .foregroundColor(.secondary.opacity(0.65)) } if contact.contactConnIncognito { Image(systemName: "theatermasks") .resizable() .scaledToFit() .frame(width: 22, height: 22) .foregroundColor(.secondary) } } } } private func previewTitle(_ contact: Contact, titleColor: Color) -> some View { let t = Text(chat.chatInfo.chatViewName).foregroundColor(titleColor) return ( contact.verified == true ? verifiedIcon + t : t ) .lineLimit(1) } private var verifiedIcon: Text { (Text(Image(systemName: "checkmark.shield")) + textSpace) .foregroundColor(.secondary) .baselineOffset(1) .kerning(-2) } func contactCardNavLink(_ contact: Contact) -> some View { Button { showConnectContactViaAddressDialog = true } label: { contactCardPreview(contact) } .swipeActions(edge: .trailing, allowsFullSwipe: true) { Button { deleteContactDialog( chat, contact, dismissToChatList: false, showAlert: { alert = $0 }, showActionSheet: { actionSheet = $0 }, showSheetContent: { sheet = $0 } ) } label: { Label("Delete", systemImage: "trash") } .tint(.red) } .confirmationDialog("Connect with \(contact.chatViewName)", isPresented: $showConnectContactViaAddressDialog, titleVisibility: .visible) { Button("Use current profile") { connectContactViaAddress_(contact, false) } Button("Use new incognito profile") { connectContactViaAddress_(contact, true) } } } private func connectContactViaAddress_(_ contact: Contact, _ incognito: Bool) { Task { let ok = await connectContactViaAddress(contact.contactId, incognito, showAlert: { alert = SomeAlert(alert: $0, id: "ContactListNavLink connectContactViaAddress") }) if ok { ItemsModel.shared.loadOpenChat(contact.id) { dismissAllSheets(animated: true) { AlertManager.shared.showAlert(connReqSentAlert(.contact)) } } } } } func contactCardPreview(_ contact: Contact) -> some View { HStack{ ProfileImage(imageStr: contact.image, size: 30) Text(chat.chatInfo.chatViewName) .foregroundColor(.accentColor) .lineLimit(1) Spacer() Image(systemName: "envelope") .resizable() .scaledToFill() .frame(width: 14, height: 14) .foregroundColor(.accentColor) } } func contactRequestNavLink(_ contactRequest: UserContactRequest) -> some View { Button { showContactRequestDialog = true } label: { contactRequestPreview(contactRequest) } .swipeActions(edge: .trailing, allowsFullSwipe: true) { Button { Task { await acceptContactRequest(incognito: false, contactRequest: contactRequest) } } label: { Label("Accept", systemImage: "checkmark") } .tint(theme.colors.primary) Button { Task { await acceptContactRequest(incognito: true, contactRequest: contactRequest) } } label: { Label("Accept incognito", systemImage: "theatermasks") } .tint(.indigo) Button { alert = SomeAlert(alert: rejectContactRequestAlert(contactRequest), id: "rejectContactRequestAlert") } label: { Label("Reject", systemImage: "multiply") } .tint(.red) } .confirmationDialog("Accept connection request?", isPresented: $showContactRequestDialog, titleVisibility: .visible) { Button("Accept") { Task { await acceptContactRequest(incognito: false, contactRequest: contactRequest) } } Button("Accept incognito") { Task { await acceptContactRequest(incognito: true, contactRequest: contactRequest) } } Button("Reject (sender NOT notified)", role: .destructive) { Task { await rejectContactRequest(contactRequest) } } } } func contactRequestPreview(_ contactRequest: UserContactRequest) -> some View { HStack{ ProfileImage(imageStr: contactRequest.image, size: 30) Text(chat.chatInfo.chatViewName) .foregroundColor(.accentColor) .lineLimit(1) Spacer() Image(systemName: "checkmark") .resizable() .scaledToFill() .frame(width: 14, height: 14) .foregroundColor(.accentColor) } } }