189 lines
6.1 KiB
Swift
189 lines
6.1 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.Main.backgroundLight
|
||
|
.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.Main.backgroundLight)
|
||
|
} 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.Main.backgroundDark
|
||
|
.ignoresSafeArea()
|
||
|
|
||
|
// title
|
||
|
Text(L10n.Contacts.title)
|
||
|
.font(.head2)
|
||
|
.foregroundColor(Color.Main.black)
|
||
|
|
||
|
HStack {
|
||
|
Spacer()
|
||
|
Image(systemName: "plus")
|
||
|
.foregroundColor(Color.Material.greenDark500)
|
||
|
.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.Main.black)
|
||
|
.font(.body2)
|
||
|
Spacer()
|
||
|
}
|
||
|
.padding(.horizontal, 16)
|
||
|
.padding(.vertical, 4)
|
||
|
Rectangle()
|
||
|
.frame(maxWidth: .infinity)
|
||
|
.frame(height: 1)
|
||
|
.foregroundColor(.Main.backgroundDark)
|
||
|
}
|
||
|
.sharedListRow()
|
||
|
.onTapGesture {
|
||
|
// state.startChat(roster)
|
||
|
}
|
||
|
.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") {
|
||
|
// state.startChat(roster)
|
||
|
}
|
||
|
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
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|