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

Implement new group members selection

parent 3f7726d3
No related branches found
No related tags found
2 merge requests!153Release 1.1.0,!149[Messenger example] create new group
import AppCore import AppCore
import ComposableArchitecture import ComposableArchitecture
import Foundation import Foundation
import XXMessengerClient
import XXModels import XXModels
public struct NewGroupComponent: ReducerProtocol { public struct NewGroupComponent: ReducerProtocol {
public struct State: Equatable { public struct State: Equatable {
public init( public init(
contacts: IdentifiedArrayOf<XXModels.Contact> = [] contacts: IdentifiedArrayOf<XXModels.Contact> = [],
members: IdentifiedArrayOf<XXModels.Contact> = []
) { ) {
self.contacts = contacts self.contacts = contacts
self.members = members
} }
public var contacts: IdentifiedArrayOf<XXModels.Contact> public var contacts: IdentifiedArrayOf<XXModels.Contact>
public var members: IdentifiedArrayOf<XXModels.Contact>
} }
public enum Action: Equatable { public enum Action: Equatable {
case start case start
case didFetchContacts([XXModels.Contact]) case didFetchContacts([XXModels.Contact])
case didSelectContact(XXModels.Contact)
case didFinish case didFinish
} }
public init() {} public init() {}
@Dependency(\.app.messenger) var messenger: Messenger
@Dependency(\.app.dbManager.getDB) var db: DBManagerGetDB @Dependency(\.app.dbManager.getDB) var db: DBManagerGetDB
@Dependency(\.app.mainQueue) var mainQueue: AnySchedulerOf<DispatchQueue> @Dependency(\.app.mainQueue) var mainQueue: AnySchedulerOf<DispatchQueue>
@Dependency(\.app.bgQueue) var bgQueue: AnySchedulerOf<DispatchQueue> @Dependency(\.app.bgQueue) var bgQueue: AnySchedulerOf<DispatchQueue>
...@@ -29,10 +35,12 @@ public struct NewGroupComponent: ReducerProtocol { ...@@ -29,10 +35,12 @@ public struct NewGroupComponent: ReducerProtocol {
public func reduce(into state: inout State, action: Action) -> EffectTask<Action> { public func reduce(into state: inout State, action: Action) -> EffectTask<Action> {
switch action { switch action {
case .start: case .start:
let myId = try? messenger.e2e.tryGet().getContact().getId()
return Effect return Effect
.catching { try db() } .catching { try db() }
.flatMap { $0.fetchContactsPublisher(.init()) } .flatMap { $0.fetchContactsPublisher(.init()) }
.assertNoFailure() .assertNoFailure()
.map { $0.filter { $0.id != myId } }
.map(Action.didFetchContacts) .map(Action.didFetchContacts)
.subscribe(on: bgQueue) .subscribe(on: bgQueue)
.receive(on: mainQueue) .receive(on: mainQueue)
...@@ -42,6 +50,14 @@ public struct NewGroupComponent: ReducerProtocol { ...@@ -42,6 +50,14 @@ public struct NewGroupComponent: ReducerProtocol {
state.contacts = IdentifiedArray(uniqueElements: contacts) state.contacts = IdentifiedArray(uniqueElements: contacts)
return .none return .none
case .didSelectContact(let contact):
if state.members.contains(contact) {
state.members.remove(contact)
} else {
state.members.append(contact)
}
return .none
case .didFinish: case .didFinish:
return .none return .none
} }
......
...@@ -5,6 +5,7 @@ import XXModels ...@@ -5,6 +5,7 @@ import XXModels
public struct NewGroupView: View { public struct NewGroupView: View {
public typealias Component = NewGroupComponent public typealias Component = NewGroupComponent
typealias ViewStore = ComposableArchitecture.ViewStore<ViewState, Component.Action>
public init(store: StoreOf<Component>) { public init(store: StoreOf<Component>) {
self.store = store self.store = store
...@@ -13,18 +14,46 @@ public struct NewGroupView: View { ...@@ -13,18 +14,46 @@ public struct NewGroupView: View {
let store: StoreOf<Component> let store: StoreOf<Component>
struct ViewState: Equatable { struct ViewState: Equatable {
init(state: Component.State) {} init(state: Component.State) {
contacts = state.contacts
members = state.members
}
var contacts: IdentifiedArrayOf<XXModels.Contact>
var members: IdentifiedArrayOf<XXModels.Contact>
} }
public var body: some View { public var body: some View {
WithViewStore(store, observe: ViewState.init) { viewStore in WithViewStore(store, observe: ViewState.init) { viewStore in
Form { Form {
Section {
membersView(viewStore)
}
} }
.navigationTitle("New Group") .navigationTitle("New Group")
.task { viewStore.send(.start) } .task { viewStore.send(.start) }
} }
} }
func membersView(_ viewStore: ViewStore) -> some View {
NavigationLink("Members (\(viewStore.members.count))") {
Form {
ForEach(viewStore.contacts) { contact in
Button {
viewStore.send(.didSelectContact(contact))
} label: {
HStack {
Text(contact.username ?? "")
Spacer()
if viewStore.members.contains(contact) {
Image(systemName: "checkmark")
}
}
}
}
}
}
}
} }
#if DEBUG #if DEBUG
......
...@@ -2,6 +2,8 @@ import Combine ...@@ -2,6 +2,8 @@ import Combine
import ComposableArchitecture import ComposableArchitecture
import CustomDump import CustomDump
import XCTest import XCTest
import XXClient
import XXMessengerClient
import XXModels import XXModels
@testable import NewGroupFeature @testable import NewGroupFeature
...@@ -30,6 +32,15 @@ final class NewGroupComponentTests: XCTestCase { ...@@ -30,6 +32,15 @@ final class NewGroupComponentTests: XCTestCase {
store.dependencies.app.mainQueue = .immediate store.dependencies.app.mainQueue = .immediate
store.dependencies.app.bgQueue = .immediate store.dependencies.app.bgQueue = .immediate
store.dependencies.app.messenger.e2e.get = {
var e2e: E2E = .unimplemented
e2e.getContact.run = {
var contact = XXClient.Contact.unimplemented("my-contact-data".data(using: .utf8)!)
contact.getIdFromContact.run = { _ in "my-contact-id".data(using: .utf8)! }
return contact
}
return e2e
}
store.dependencies.app.dbManager.getDB.run = { store.dependencies.app.dbManager.getDB.run = {
var db: Database = .unimplemented var db: Database = .unimplemented
db.fetchContactsPublisher.run = { query in db.fetchContactsPublisher.run = { query in
...@@ -59,6 +70,33 @@ final class NewGroupComponentTests: XCTestCase { ...@@ -59,6 +70,33 @@ final class NewGroupComponentTests: XCTestCase {
contactsSubject.send(completion: .finished) contactsSubject.send(completion: .finished)
} }
func testSelectMembers() {
let contacts: [XXModels.Contact] = [
.init(id: "contact-1-id".data(using: .utf8)!),
.init(id: "contact-2-id".data(using: .utf8)!),
.init(id: "contact-3-id".data(using: .utf8)!),
]
let store = TestStore(
initialState: NewGroupComponent.State(
contacts: IdentifiedArray(uniqueElements: contacts)
),
reducer: NewGroupComponent()
)
store.send(.didSelectContact(contacts[0])) {
$0.members = IdentifiedArray(uniqueElements: [contacts[0]])
}
store.send(.didSelectContact(contacts[1])) {
$0.members = IdentifiedArray(uniqueElements: [contacts[0], contacts[1]])
}
store.send(.didSelectContact(contacts[0])) {
$0.members = IdentifiedArray(uniqueElements: [contacts[1]])
}
}
func testFinish() { func testFinish() {
let store = TestStore( let store = TestStore(
initialState: NewGroupComponent.State(), initialState: NewGroupComponent.State(),
......
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