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

Fetch and display group details

parent b75b9ccc
No related branches found
No related tags found
2 merge requests!153Release 1.1.0,!151[Messenger example] join group
import AppCore
import ComposableArchitecture
import Foundation
import XXModels
public struct GroupComponent: ReducerProtocol {
public struct State: Equatable {
public init(
group: XXModels.Group
groupId: XXModels.Group.ID,
groupInfo: XXModels.GroupInfo? = nil
) {
self.group = group
self.groupId = groupId
self.groupInfo = groupInfo
}
public var group: XXModels.Group
public var groupId: XXModels.Group.ID
public var groupInfo: XXModels.GroupInfo?
}
public enum Action: Equatable {
case start
case didFetchGroupInfo(XXModels.GroupInfo?)
}
public init() {}
@Dependency(\.app.dbManager.getDB) var db: DBManagerGetDB
@Dependency(\.app.mainQueue) var mainQueue: AnySchedulerOf<DispatchQueue>
@Dependency(\.app.bgQueue) var bgQueue: AnySchedulerOf<DispatchQueue>
public var body: some ReducerProtocol<State, Action> {
Reduce { state, action in
switch action {
case .start:
return Effect
.catching { try db() }
.flatMap { [state] in
let query = GroupInfo.Query(groupId: state.groupId)
return $0.fetchGroupInfosPublisher(query).map(\.first)
}
.assertNoFailure()
.map(Action.didFetchGroupInfo)
.subscribe(on: bgQueue)
.receive(on: mainQueue)
.eraseToEffect()
case .didFetchGroupInfo(let groupInfo):
state.groupInfo = groupInfo
return .none
}
}
......
......@@ -15,17 +15,33 @@ public struct GroupView: View {
struct ViewState: Equatable {
init(state: Component.State) {
group = state.group
info = state.groupInfo
}
var group: XXModels.Group
var info: XXModels.GroupInfo?
}
public var body: some View {
WithViewStore(store, observe: ViewState.init) { viewStore in
Form {
Section("Group name") {
Text(viewStore.group.name)
if let info = viewStore.info {
Section("Name") {
Text(info.group.name)
}
Section("Leader") {
Label(info.leader.username ?? "", systemImage: "person.badge.shield.checkmark")
}
Section("Members") {
ForEach(info.members.filter { $0 != info.leader }) { contact in
Label(contact.username ?? "", systemImage: "person")
}
}
Section("Status") {
GroupAuthStatusView(info.group.authStatus)
}
}
}
.navigationTitle("Group")
......@@ -40,6 +56,8 @@ public struct GroupView_Previews: PreviewProvider {
NavigationView {
GroupView(store: Store(
initialState: GroupComponent.State(
groupId: "group-id".data(using: .utf8)!,
groupInfo: .init(
group: .init(
id: "group-id".data(using: .utf8)!,
name: "Preview group",
......@@ -47,6 +65,21 @@ public struct GroupView_Previews: PreviewProvider {
createdAt: Date(timeIntervalSince1970: TimeInterval(86_400)),
authStatus: .participating,
serialized: "group-serialized".data(using: .utf8)!
),
leader: .init(
id: "group-leader-id".data(using: .utf8)!,
username: "Group leader"
),
members: [
.init(
id: "member-1-id".data(using: .utf8)!,
username: "Member 1"
),
.init(
id: "member-2-id".data(using: .utf8)!,
username: "Member 2"
),
]
)
),
reducer: EmptyReducer()
......
......@@ -58,7 +58,7 @@ public struct GroupsComponent: ReducerProtocol {
return .none
case .didSelectGroup(let group):
state.group = GroupComponent.State(group: group)
state.group = GroupComponent.State(groupId: group.id)
return .none
case .didDismissGroup:
......
import Combine
import ComposableArchitecture
import CustomDump
import XCTest
import XXModels
@testable import GroupFeature
final class GroupComponentTests: XCTestCase {
enum Action: Equatable {
case didFetchGroupInfos(GroupInfo.Query)
}
var actions: [Action]!
override func setUp() {
actions = []
}
override func tearDown() {
actions = nil
}
func testStart() {
let groupId = "group-id".data(using: .utf8)!
let groupInfosSubject = PassthroughSubject<[GroupInfo], Error>()
let store = TestStore(
initialState: GroupComponent.State(
group: .stub()
groupId: groupId
),
reducer: GroupComponent()
)
store.dependencies.app.mainQueue = .immediate
store.dependencies.app.bgQueue = .immediate
store.dependencies.app.dbManager.getDB.run = {
var db: Database = .unimplemented
db.fetchGroupInfosPublisher.run = { query in
self.actions.append(.didFetchGroupInfos(query))
return groupInfosSubject.eraseToAnyPublisher()
}
return db
}
store.send(.start)
XCTAssertNoDifference(actions, [
.didFetchGroupInfos(.init(groupId: groupId)),
])
let groupInfo = GroupInfo.stub()
groupInfosSubject.send([groupInfo])
store.receive(.didFetchGroupInfo(groupInfo)) {
$0.groupInfo = groupInfo
}
groupInfosSubject.send(completion: .finished)
}
}
private extension XXModels.Group {
static func stub() -> XXModels.Group {
XXModels.Group(
private extension XXModels.GroupInfo {
static func stub() -> XXModels.GroupInfo {
XXModels.GroupInfo(
group: .init(
id: "group-id".data(using: .utf8)!,
name: "Group name",
name: "Group Name",
leaderId: "group-leader-id".data(using: .utf8)!,
createdAt: Date(timeIntervalSince1970: TimeInterval(86_400)),
authStatus: .participating,
serialized: "group-serialized".data(using: .utf8)!
),
leader: .init(
id: "group-leader-id".data(using: .utf8)!,
username: "Group leader"
),
members: [
.init(
id: "member-1-id".data(using: .utf8)!,
username: "Member 1"
),
.init(
id: "member-2-id".data(using: .utf8)!,
username: "Member 2"
),
]
)
}
}
......@@ -62,32 +62,36 @@ final class GroupsComponentTests: XCTestCase {
}
func testSelectGroup() {
let store = TestStore(
initialState: GroupsComponent.State(
groups: IdentifiedArray(uniqueElements: [
let groups: [XXModels.Group] = [
.stub(1),
.stub(2),
.stub(3),
])
]
let store = TestStore(
initialState: GroupsComponent.State(
groups: IdentifiedArray(uniqueElements: groups)
),
reducer: GroupsComponent()
)
store.send(.didSelectGroup(.stub(2))) {
$0.group = GroupComponent.State(group: .stub(2))
store.send(.didSelectGroup(groups[1])) {
$0.group = GroupComponent.State(groupId: groups[1].id)
}
}
func testDismissGroup() {
let store = TestStore(
initialState: GroupsComponent.State(
groups: IdentifiedArray(uniqueElements: [
let groups: [XXModels.Group] = [
.stub(1),
.stub(2),
.stub(3),
]),
]
let store = TestStore(
initialState: GroupsComponent.State(
groups: IdentifiedArray(uniqueElements: groups),
group: GroupComponent.State(
group: .stub(2)
groupId: groups[1].id
)
),
reducer: GroupsComponent()
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment