Skip to content
Snippets Groups Projects
Commit 726aab7e authored by Dariusz Rybicki's avatar Dariusz Rybicki
Browse files

Implement facts unregistration

parent bfad972f
No related branches found
No related tags found
2 merge requests!102Release 1.0.0,!98Messenger example - register, confirm, and unregister user facts
......@@ -23,11 +23,13 @@ public struct MyContactState: Equatable {
emailConfirmationCode: String = "",
isRegisteringEmail: Bool = false,
isConfirmingEmail: Bool = false,
isUnregisteringEmail: Bool = false,
phone: String = "",
phoneConfirmationID: String? = nil,
phoneConfirmationCode: String = "",
isRegisteringPhone: Bool = false,
isConfirmingPhone: Bool = false,
isUnregisteringPhone: Bool = false,
isLoadingFacts: Bool = false,
alert: AlertState<MyContactAction>? = nil
) {
......@@ -38,11 +40,13 @@ public struct MyContactState: Equatable {
self.emailConfirmationCode = emailConfirmationCode
self.isRegisteringEmail = isRegisteringEmail
self.isConfirmingEmail = isConfirmingEmail
self.isUnregisteringEmail = isUnregisteringEmail
self.phone = phone
self.phoneConfirmationID = phoneConfirmationID
self.phoneConfirmationCode = phoneConfirmationCode
self.isRegisteringPhone = isRegisteringPhone
self.isConfirmingPhone = isConfirmingPhone
self.isUnregisteringPhone = isUnregisteringPhone
self.isLoadingFacts = isLoadingFacts
self.alert = alert
}
......@@ -54,11 +58,13 @@ public struct MyContactState: Equatable {
@BindableState public var emailConfirmationCode: String
@BindableState public var isRegisteringEmail: Bool
@BindableState public var isConfirmingEmail: Bool
@BindableState public var isUnregisteringEmail: Bool
@BindableState public var phone: String
@BindableState public var phoneConfirmationID: String?
@BindableState public var phoneConfirmationCode: String
@BindableState public var isRegisteringPhone: Bool
@BindableState public var isConfirmingPhone: Bool
@BindableState public var isUnregisteringPhone: Bool
@BindableState public var isLoadingFacts: Bool
public var alert: AlertState<MyContactAction>?
}
......@@ -178,7 +184,28 @@ public let myContactReducer = Reducer<MyContactState, MyContactAction, MyContact
.eraseToEffect()
case .unregisterEmailTapped:
return .none
guard let email = state.contact?.email else { return .none }
state.isUnregisteringEmail = true
return Effect.run { [state] subscriber in
do {
let ud: UserDiscovery = try env.messenger.ud.tryGet()
let fact = Fact(type: .email, value: email)
try ud.removeFact(fact)
let contactId = try env.messenger.e2e.tryGet().getContact().getId()
if var dbContact = try env.db().fetchContacts(.init(id: [contactId])).first {
dbContact.email = nil
try env.db().saveContact(dbContact)
}
} catch {
subscriber.send(.didFail(error.localizedDescription))
}
subscriber.send(.set(\.$isUnregisteringEmail, false))
subscriber.send(completion: .finished)
return AnyCancellable {}
}
.subscribe(on: env.bgQueue)
.receive(on: env.mainQueue)
.eraseToEffect()
case .registerPhoneTapped:
state.focusedField = nil
......@@ -228,7 +255,28 @@ public let myContactReducer = Reducer<MyContactState, MyContactAction, MyContact
.eraseToEffect()
case .unregisterPhoneTapped:
return .none
guard let phone = state.contact?.phone else { return .none }
state.isUnregisteringPhone = true
return Effect.run { [state] subscriber in
do {
let ud: UserDiscovery = try env.messenger.ud.tryGet()
let fact = Fact(type: .phone, value: phone)
try ud.removeFact(fact)
let contactId = try env.messenger.e2e.tryGet().getContact().getId()
if var dbContact = try env.db().fetchContacts(.init(id: [contactId])).first {
dbContact.phone = nil
try env.db().saveContact(dbContact)
}
} catch {
subscriber.send(.didFail(error.localizedDescription))
}
subscriber.send(.set(\.$isUnregisteringPhone, false))
subscriber.send(completion: .finished)
return AnyCancellable {}
}
.subscribe(on: env.bgQueue)
.receive(on: env.mainQueue)
.eraseToEffect()
case .loadFactsTapped:
state.isLoadingFacts = true
......
......@@ -19,11 +19,13 @@ public struct MyContactView: View {
emailCode = state.emailConfirmationCode
isRegisteringEmail = state.isRegisteringEmail
isConfirmingEmail = state.isConfirmingEmail
isUnregisteringEmail = state.isUnregisteringEmail
phone = state.phone
phoneConfirmation = state.phoneConfirmationID != nil
phoneCode = state.phoneConfirmationCode
isRegisteringPhone = state.isRegisteringPhone
isConfirmingPhone = state.isConfirmingPhone
isUnregisteringPhone = state.isUnregisteringPhone
isLoadingFacts = state.isLoadingFacts
}
......@@ -34,11 +36,13 @@ public struct MyContactView: View {
var emailCode: String
var isRegisteringEmail: Bool
var isConfirmingEmail: Bool
var isUnregisteringEmail: Bool
var phone: String
var phoneConfirmation: Bool
var phoneCode: String
var isRegisteringPhone: Bool
var isConfirmingPhone: Bool
var isUnregisteringPhone: Bool
var isLoadingFacts: Bool
}
......@@ -58,8 +62,15 @@ public struct MyContactView: View {
Button(role: .destructive) {
viewStore.send(.unregisterEmailTapped)
} label: {
Text("Unregister")
HStack {
Text("Unregister")
Spacer()
if viewStore.isUnregisteringEmail {
ProgressView()
}
}
}
.disabled(viewStore.isUnregisteringEmail)
} else {
TextField(
text: viewStore.binding(
......@@ -127,8 +138,15 @@ public struct MyContactView: View {
Button(role: .destructive) {
viewStore.send(.unregisterPhoneTapped)
} label: {
Text("Unregister")
HStack {
Text("Unregister")
Spacer()
if viewStore.isUnregisteringPhone {
ProgressView()
}
}
}
.disabled(viewStore.isUnregisteringPhone)
} else {
TextField(
text: viewStore.binding(
......
......@@ -257,13 +257,97 @@ final class MyContactFeatureTests: XCTestCase {
}
func testUnregisterEmail() {
let contactID = "contact-id".data(using: .utf8)!
let email = "test@email.com"
let dbContact = XXModels.Contact(id: contactID, email: email)
var didRemoveFact: [Fact] = []
var didFetchContacts: [XXModels.Contact.Query] = []
var didSaveContact: [XXModels.Contact] = []
let store = TestStore(
initialState: MyContactState(),
initialState: MyContactState(
contact: dbContact
),
reducer: myContactReducer,
environment: .unimplemented
)
store.environment.mainQueue = .immediate
store.environment.bgQueue = .immediate
store.environment.messenger.ud.get = {
var ud: UserDiscovery = .unimplemented
ud.removeFact.run = { didRemoveFact.append($0) }
return ud
}
store.environment.messenger.e2e.get = {
var e2e: E2E = .unimplemented
e2e.getContact.run = {
var contact: XXClient.Contact = .unimplemented(Data())
contact.getIdFromContact.run = { _ in contactID }
return contact
}
return e2e
}
store.environment.db.run = {
var db: Database = .failing
db.fetchContacts.run = { query in
didFetchContacts.append(query)
return [dbContact]
}
db.saveContact.run = { contact in
didSaveContact.append(contact)
return contact
}
return db
}
store.send(.unregisterEmailTapped) {
$0.isUnregisteringEmail = true
}
XCTAssertNoDifference(didRemoveFact, [.init(type: .email, value: email)])
XCTAssertNoDifference(didFetchContacts, [.init(id: [contactID])])
var expectedSavedContact = dbContact
expectedSavedContact.email = nil
XCTAssertNoDifference(didSaveContact, [expectedSavedContact])
store.receive(.set(\.$isUnregisteringEmail, false)) {
$0.isUnregisteringEmail = false
}
}
func testUnregisterEmailFailure() {
struct Failure: Error {}
let failure = Failure()
let store = TestStore(
initialState: MyContactState(
contact: .init(id: Data(), email: "test@email.com")
),
reducer: myContactReducer,
environment: .unimplemented
)
store.send(.unregisterEmailTapped)
store.environment.mainQueue = .immediate
store.environment.bgQueue = .immediate
store.environment.messenger.ud.get = {
var ud: UserDiscovery = .unimplemented
ud.removeFact.run = { _ in throw failure }
return ud
}
store.send(.unregisterEmailTapped) {
$0.isUnregisteringEmail = true
}
store.receive(.didFail(failure.localizedDescription)) {
$0.alert = .error(failure.localizedDescription)
}
store.receive(.set(\.$isUnregisteringEmail, false)) {
$0.isUnregisteringEmail = false
}
}
func testRegisterPhone() {
......@@ -465,13 +549,97 @@ final class MyContactFeatureTests: XCTestCase {
}
func testUnregisterPhone() {
let contactID = "contact-id".data(using: .utf8)!
let phone = "+123456789"
let dbContact = XXModels.Contact(id: contactID, phone: phone)
var didRemoveFact: [Fact] = []
var didFetchContacts: [XXModels.Contact.Query] = []
var didSaveContact: [XXModels.Contact] = []
let store = TestStore(
initialState: MyContactState(),
initialState: MyContactState(
contact: dbContact
),
reducer: myContactReducer,
environment: .unimplemented
)
store.environment.mainQueue = .immediate
store.environment.bgQueue = .immediate
store.environment.messenger.ud.get = {
var ud: UserDiscovery = .unimplemented
ud.removeFact.run = { didRemoveFact.append($0) }
return ud
}
store.environment.messenger.e2e.get = {
var e2e: E2E = .unimplemented
e2e.getContact.run = {
var contact: XXClient.Contact = .unimplemented(Data())
contact.getIdFromContact.run = { _ in contactID }
return contact
}
return e2e
}
store.environment.db.run = {
var db: Database = .failing
db.fetchContacts.run = { query in
didFetchContacts.append(query)
return [dbContact]
}
db.saveContact.run = { contact in
didSaveContact.append(contact)
return contact
}
return db
}
store.send(.unregisterPhoneTapped) {
$0.isUnregisteringPhone = true
}
XCTAssertNoDifference(didRemoveFact, [.init(type: .phone, value: phone)])
XCTAssertNoDifference(didFetchContacts, [.init(id: [contactID])])
var expectedSavedContact = dbContact
expectedSavedContact.phone = nil
XCTAssertNoDifference(didSaveContact, [expectedSavedContact])
store.receive(.set(\.$isUnregisteringPhone, false)) {
$0.isUnregisteringPhone = false
}
}
func testUnregisterPhoneFailure() {
struct Failure: Error {}
let failure = Failure()
let store = TestStore(
initialState: MyContactState(
contact: .init(id: Data(), phone: "+123456789")
),
reducer: myContactReducer,
environment: .unimplemented
)
store.send(.unregisterPhoneTapped)
store.environment.mainQueue = .immediate
store.environment.bgQueue = .immediate
store.environment.messenger.ud.get = {
var ud: UserDiscovery = .unimplemented
ud.removeFact.run = { _ in throw failure }
return ud
}
store.send(.unregisterPhoneTapped) {
$0.isUnregisteringPhone = true
}
store.receive(.didFail(failure.localizedDescription)) {
$0.alert = .error(failure.localizedDescription)
}
store.receive(.set(\.$isUnregisteringPhone, false)) {
$0.isUnregisteringPhone = false
}
}
func testLoadFactsFromClient() {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment