conversations-classic-ios/ConversationsClassic/View/Screens/ContactsScreen.swift
2024-07-04 10:21:12 +02:00

189 lines
6.3 KiB
Swift

import SwiftUI
struct ContactsScreen: View {
@EnvironmentObject var store: AppStore
@State private var addPanelPresented = false
@State private var isErrorAlertPresented = false
@State private var errorAlertMessage = ""
@State private var isShowingLoader = false
var body: some View {
ZStack {
// Background color
Color.Material.Background.light
.ignoresSafeArea()
// Content
VStack(spacing: 0) {
// Header
ContactsScreenHeader(addPanelPresented: $addPanelPresented)
// Contacts list
let rosters = store.state.rostersState.rosters.filter { !$0.locallyDeleted }
if !rosters.isEmpty {
List {
ForEach(rosters) { roster in
ContactsScreenRow(
roster: roster,
isErrorAlertPresented: $isErrorAlertPresented,
errorAlertMessage: $errorAlertMessage,
isShowingLoader: $isShowingLoader
)
}
}
.listStyle(.plain)
.background(Color.Material.Background.light)
} else {
Spacer()
}
// Tab bar
SharedTabBar()
}
}
.loadingIndicator(isShowingLoader)
.fullScreenCover(isPresented: $addPanelPresented) {
AddContactOrChannelScreen(isPresented: $addPanelPresented)
}
.alert(isPresented: $isErrorAlertPresented) {
Alert(
title: Text(L10n.Global.Error.title),
message: Text(errorAlertMessage),
dismissButton: .default(Text(L10n.Global.ok))
)
}
}
}
private struct ContactsScreenHeader: View {
@Binding var addPanelPresented: Bool
var body: some View {
ZStack {
// bg
Color.Material.Background.dark
.ignoresSafeArea()
// title
Text(L10n.Contacts.title)
.font(.head2)
.foregroundColor(Color.Material.Text.main)
HStack {
Spacer()
Image(systemName: "plus")
.foregroundColor(Color.Material.Elements.active)
.tappablePadding(.symmetric(12)) {
addPanelPresented = true
}
}
.padding(.horizontal, 16)
}
.frame(height: 44)
}
}
private struct ContactsScreenRow: View {
@EnvironmentObject var store: AppStore
var roster: Roster
@State private var isShowingMenu = false
@State private var isDeleteAlertPresented = false
@Binding var isErrorAlertPresented: Bool
@Binding var errorAlertMessage: String
@Binding var isShowingLoader: Bool
var body: some View {
VStack(spacing: 0) {
HStack(spacing: 8) {
ZStack {
Circle()
.frame(width: 44, height: 44)
.foregroundColor(.red)
Text(roster.name?.firstLetter ?? roster.contactBareJid.firstLetter)
.foregroundColor(.white)
.font(.body1)
}
Text(roster.contactBareJid)
.foregroundColor(Color.Material.Text.main)
.font(.body2)
Spacer()
}
.padding(.horizontal, 16)
.padding(.vertical, 4)
Rectangle()
.frame(maxWidth: .infinity)
.frame(height: 1)
.foregroundColor(.Material.Background.dark)
}
.sharedListRow()
.onTapGesture {
store.dispatch(.chatsAction(.startChat(accountJid: roster.bareJid, participantJid: roster.contactBareJid)))
}
.onLongPressGesture {
isShowingMenu.toggle()
}
.swipeActions(edge: .trailing, allowsFullSwipe: false) {
Button {
isDeleteAlertPresented = true
} label: {
Label(L10n.Contacts.sendMessage, systemImage: "trash")
}
.tint(Color.red)
}
.contextMenu {
Button(L10n.Contacts.sendMessage, systemImage: "message") {
store.dispatch(.chatsAction(.startChat(accountJid: roster.bareJid, participantJid: roster.contactBareJid)))
}
Divider()
Button(L10n.Contacts.editContact) {
print("Edit contact")
}
Button(L10n.Contacts.selectContact) {
print("Select contact")
}
Divider()
Button(L10n.Contacts.deleteContact, systemImage: "trash", role: .destructive) {
isDeleteAlertPresented = true
}
}
.actionSheet(isPresented: $isDeleteAlertPresented) {
ActionSheet(
title: Text(L10n.Contacts.Delete.title),
message: Text(L10n.Contacts.Delete.message),
buttons: [
.destructive(Text(L10n.Contacts.Delete.deleteFromDevice)) {
store.dispatch(.rostersAction(.markRosterAsLocallyDeleted(ownerJID: roster.bareJid, contactJID: roster.contactBareJid)))
},
.destructive(Text(L10n.Contacts.Delete.deleteCompletely)) {
isShowingLoader = true
store.dispatch(.rostersAction(.deleteRoster(ownerJID: roster.bareJid, contactJID: roster.contactBareJid)))
},
.cancel(Text(L10n.Global.cancel))
]
)
}
.onChange(of: store.state.rostersState.rosters) { _ in
endOfDeleting()
}
.onChange(of: store.state.rostersState.deleteRosterError) { _ in
endOfDeleting()
}
}
private func endOfDeleting() {
if isShowingLoader {
isShowingLoader = false
if let error = store.state.rostersState.deleteRosterError {
errorAlertMessage = error
isErrorAlertPresented = true
}
}
}
}