import SwiftUI struct ContactsScreen: View { @Environment(\.router) var router @EnvironmentObject var clientsStore: ClientsStore var body: some View { ZStack { // Background color Color.Material.Background.light .ignoresSafeArea() // Content VStack(spacing: 0) { // Header SharedNavigationBar( centerText: .init(text: L10n.Contacts.title), rightButton: .init( image: Image(systemName: "plus"), action: { router.showScreen(.fullScreenCover) { _ in AddContactOrChannelScreen() } } ) ) // Contacts list if !clientsStore.actualRosters.isEmpty { List { ForEach(clientsStore.actualRosters) { roster in ContactsScreenRow( roster: roster ) } } .listStyle(.plain) .background(Color.Material.Background.light) } else { Spacer() } } } } } private struct ContactsScreenRow: View { @Environment(\.router) var router @EnvironmentObject var clientsStore: ClientsStore var roster: Roster var body: some View { SharedListRow( iconType: .charCircle(roster.name?.firstLetter ?? roster.contactBareJid.firstLetter), text: roster.contactBareJid ) .onTapGesture { startChat() } .swipeActions(edge: .trailing, allowsFullSwipe: false) { Button { router.showAlert(.confirmationDialog, title: L10n.Contacts.deleteContact, subtitle: L10n.Contacts.Delete.message) { deleteConfirmation } } label: { Label("", systemImage: "trash") } .tint(Color.red) } .contextMenu { Button(L10n.Contacts.sendMessage, systemImage: "message") { startChat() } Divider() Button(L10n.Contacts.editContact) { print("Edit contact") } Button(L10n.Contacts.selectContact) { print("Select contact") } Divider() Button(L10n.Contacts.deleteContact, systemImage: "trash", role: .destructive) { router.showAlert(.confirmationDialog, title: L10n.Contacts.deleteContact, subtitle: L10n.Contacts.Delete.message) { deleteConfirmation } } } } @ViewBuilder private var deleteConfirmation: some View { Button(role: .destructive) { Task { await deleteFromDevice() } } label: { Text(L10n.Contacts.Delete.deleteFromDevice) } Button(role: .destructive) { Task { await deleteCompletely() } } label: { Text(L10n.Contacts.Delete.deleteCompletely) } Button(role: .cancel) {} label: { Text(L10n.Global.cancel) } } private func deleteFromDevice() async { router.showModal { LoadingScreen() } defer { router.dismissModal() } var roster = roster try? await roster.setLocallyDeleted(true) } private func deleteCompletely() async { router.showModal { LoadingScreen() } defer { router.dismissModal() } do { try await clientsStore.deleteRoster(roster) } catch { router.showAlert( .alert, title: L10n.Global.Error.title, subtitle: L10n.Contacts.Delete.error ) { Button(L10n.Global.ok, role: .cancel) {} } } } private func startChat() { Task { router.showModal { LoadingScreen() } defer { router.dismissModal() } do { let (messages, attachments) = try await clientsStore.conversationStores(for: roster) router.showScreen(.push) { _ in ConversationScreen(messages: messages, attachments: attachments) .navigationBarHidden(true) } } catch { router.showAlert( .alert, title: L10n.Global.Error.title, subtitle: L10n.Conversation.startError ) { Button(L10n.Global.ok, role: .cancel) {} } } } } }