import AppCore import ChatFeature import CheckContactAuthFeature import ComposableArchitecture import ComposablePresentation import ConfirmRequestFeature import ContactLookupFeature import ResetAuthFeature import SendRequestFeature import SwiftUI import VerifyContactFeature import XXClient import XXModels public struct ContactView: View { public init(store: Store<ContactState, ContactAction>) { self.store = store } let store: Store<ContactState, ContactAction> struct ViewState: Equatable { var dbContact: XXModels.Contact? var xxContactIsSet: Bool var xxContactUsername: String? var xxContactEmail: String? var xxContactPhone: String? var importUsername: Bool var importEmail: Bool var importPhone: Bool var canLookup: Bool var canSendRequest: Bool var canVerifyContact: Bool var canConfirmRequest: Bool var canCheckAuthorization: Bool var canResetAuthorization: Bool init(state: ContactState) { dbContact = state.dbContact xxContactIsSet = state.xxContact != nil xxContactUsername = try? state.xxContact?.getFact(.username)?.value xxContactEmail = try? state.xxContact?.getFact(.email)?.value xxContactPhone = try? state.xxContact?.getFact(.phone)?.value importUsername = state.importUsername importEmail = state.importEmail importPhone = state.importPhone canLookup = state.dbContact?.id != nil canSendRequest = state.xxContact != nil || state.dbContact?.marshaled != nil canVerifyContact = state.dbContact?.marshaled != nil canConfirmRequest = state.dbContact?.marshaled != nil canCheckAuthorization = state.dbContact?.marshaled != nil canResetAuthorization = state.dbContact?.marshaled != nil } } public var body: some View { WithViewStore(store, observe: ViewState.init) { viewStore in Form { if viewStore.xxContactIsSet { Section { Button { viewStore.send(.set(\.$importUsername, !viewStore.importUsername)) } label: { HStack { Label(viewStore.xxContactUsername ?? "", systemImage: "person") .tint(Color.primary) Spacer() Image(systemName: viewStore.importUsername ? "checkmark.circle.fill" : "circle") .foregroundColor(.accentColor) } } Button { viewStore.send(.set(\.$importEmail, !viewStore.importEmail)) } label: { HStack { Label(viewStore.xxContactEmail ?? "", systemImage: "envelope") .tint(Color.primary) Spacer() Image(systemName: viewStore.importEmail ? "checkmark.circle.fill" : "circle") .foregroundColor(.accentColor) } } Button { viewStore.send(.set(\.$importPhone, !viewStore.importPhone)) } label: { HStack { Label(viewStore.xxContactPhone ?? "", systemImage: "phone") .tint(Color.primary) Spacer() Image(systemName: viewStore.importPhone ? "checkmark.circle.fill" : "circle") .foregroundColor(.accentColor) } } Button { viewStore.send(.importFactsTapped) } label: { HStack { if viewStore.dbContact == nil { Text("Save contact") } else { Text("Update contact") } Spacer() Image(systemName: "arrow.down") } } } header: { Text("Facts") } } if let dbContact = viewStore.dbContact { Section { Label(dbContact.id.hexString(), systemImage: "number") .font(.footnote.monospaced()) Label(dbContact.username ?? "", systemImage: "person") Label(dbContact.email ?? "", systemImage: "envelope") Label(dbContact.phone ?? "", systemImage: "phone") } header: { Text("Contact") } .textSelection(.enabled) Section { ContactAuthStatusView(dbContact.authStatus) Button { viewStore.send(.lookupTapped) } label: { HStack { Text("Lookup") Spacer() Image(systemName: "chevron.forward") } } .disabled(!viewStore.canLookup) Button { viewStore.send(.sendRequestTapped) } label: { HStack { Text("Send request") Spacer() Image(systemName: "chevron.forward") } } .disabled(!viewStore.canSendRequest) Button { viewStore.send(.verifyContactTapped) } label: { HStack { Text("Verify contact") Spacer() Image(systemName: "chevron.forward") } } .disabled(!viewStore.canVerifyContact) Button { viewStore.send(.confirmRequestTapped) } label: { HStack { Text("Confirm request") Spacer() Image(systemName: "chevron.forward") } } .disabled(!viewStore.canConfirmRequest) Button { viewStore.send(.checkAuthTapped) } label: { HStack { Text("Check authorization") Spacer() Image(systemName: "chevron.forward") } } .disabled(!viewStore.canCheckAuthorization) Button { viewStore.send(.resetAuthTapped) } label: { HStack { Text("Reset authorization") Spacer() Image(systemName: "chevron.forward") } } .disabled(!viewStore.canResetAuthorization) } header: { Text("Auth") } .animation(.default, value: viewStore.dbContact?.authStatus) Section { Button { viewStore.send(.chatTapped) } label: { HStack { Text("Chat") Spacer() Image(systemName: "chevron.forward") } } } header: { Text("Chat") } } } .navigationTitle("Contact") .task { viewStore.send(.start) } .background(NavigationLinkWithStore( store.scope( state: \.lookup, action: ContactAction.lookup ), mapState: replayNonNil(), onDeactivate: { viewStore.send(.lookupDismissed) }, destination: ContactLookupView.init(store:) )) .background(NavigationLinkWithStore( store.scope( state: \.sendRequest, action: ContactAction.sendRequest ), mapState: replayNonNil(), onDeactivate: { viewStore.send(.sendRequestDismissed) }, destination: SendRequestView.init(store:) )) .background(NavigationLinkWithStore( store.scope( state: \.verifyContact, action: ContactAction.verifyContact ), onDeactivate: { viewStore.send(.verifyContactDismissed) }, destination: VerifyContactView.init(store:) )) .background(NavigationLinkWithStore( store.scope( state: \.confirmRequest, action: ContactAction.confirmRequest ), onDeactivate: { viewStore.send(.confirmRequestDismissed) }, destination: ConfirmRequestView.init(store:) )) .background(NavigationLinkWithStore( store.scope( state: \.checkAuth, action: ContactAction.checkAuth ), onDeactivate: { viewStore.send(.checkAuthDismissed) }, destination: CheckContactAuthView.init(store:) )) .background(NavigationLinkWithStore( store.scope( state: \.resetAuth, action: ContactAction.resetAuth ), onDeactivate: { viewStore.send(.resetAuthDismissed) }, destination: ResetAuthView.init(store:) )) .background(NavigationLinkWithStore( store.scope( state: \.chat, action: ContactAction.chat ), onDeactivate: { viewStore.send(.chatDismissed) }, destination: ChatView.init(store:) )) } } } #if DEBUG public struct ContactView_Previews: PreviewProvider { public static var previews: some View { ContactView(store: Store( initialState: ContactState( id: "contact-id".data(using: .utf8)! ), reducer: .empty, environment: () )) } } #endif