-
Notifications
You must be signed in to change notification settings - Fork 4
/
ProjectsReducer.swift
101 lines (83 loc) · 2.55 KB
/
ProjectsReducer.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import ComposableArchitecture
@Reducer
public struct ProjectsReducer: Reducer, Sendable {
@ObservableState
public struct State: Equatable {
public init(
info: ProjectsInfo? = nil,
groups: IdentifiedArrayOf<ProjectsGroup> = [],
isLoading: Bool = false
) {
self.groups = groups
self.isLoading = isLoading
}
var info: ProjectsInfo?
var groups: IdentifiedArrayOf<ProjectsGroup>
var isLoading: Bool
var projects: IdentifiedArrayOf<Project> {
IdentifiedArray(uniqueElements: groups.flatMap(\.projects))
}
}
public enum Action: Sendable, ViewAction {
case fetch
case fetchFinished
case fetchInfoResult(Result<ProjectsInfo, Error>)
case fetchProjectsResult(Result<[Project], Error>)
case view(View)
@CasePathable
public enum View: Sendable {
case projectCardTapped(Project.ID)
case refreshButtonTapped
case refreshTask
case task
}
}
public init() {}
@Dependency(\.continuousClock) var clock
@Dependency(\.projectsProvider) var projectsProvider
@Dependency(\.openURL) var openURL
public var body: some ReducerOf<Self> {
Reduce<State, Action> { state, action in
enum CancelId { case fetch }
switch action {
case .fetch:
state.isLoading = true
return .run { send in
try await clock.sleep(for: .seconds(0.5))
await send(.fetchInfoResult(Result {
try await projectsProvider.fetchInfo()
}))
await send(.fetchProjectsResult(Result {
try await projectsProvider.fetchProjects()
}))
await send(.fetchFinished)
}
.cancellable(id: CancelId.fetch, cancelInFlight: true)
case .fetchFinished:
state.isLoading = false
return .none
case .fetchInfoResult(.success(let info)):
state.info = info
return .none
case .fetchInfoResult(.failure(_)):
return .none
case .fetchProjectsResult(.success(let projects)):
state.groups = .init(groupingByYear: .init(uniqueElements: projects))
return .none
case .fetchProjectsResult(.failure(_)):
return .none
case .view(.projectCardTapped(let projectId)):
if let url = state.projects[id: projectId]?.url {
return .run { _ in await openURL(url) }
}
return .none
case .view(.refreshButtonTapped):
return .send(.fetch)
case .view(.refreshTask):
return .send(.fetch)
case .view(.task):
return .send(.fetch)
}
}
}
}