Skip to content

Commit c136cde

Browse files
authored
Refactor MastodonFeedFeature Views Following SwiftUI Best Practices (#529)
1 parent 0fa7560 commit c136cde

File tree

2 files changed

+53
-32
lines changed

2 files changed

+53
-32
lines changed

CriticalMapsKit/Sources/MastodonFeedFeature/TootView.swift

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -128,22 +128,25 @@ public struct TootView: View {
128128
.background(Color.gray)
129129
.clipShape(Circle())
130130
.accessibilityHidden(true)
131-
131+
132132
VStack(alignment: .leading, spacing: .grid(1)) {
133-
header()
134-
.contentShape(Rectangle())
135-
.onTapGesture {
136-
store.send(.openUser)
137-
}
138-
133+
TootHeader(store: store)
134+
.padding(.bottom, .grid(2))
135+
139136
Text(store.content.asSafeMarkdownAttributedString)
140137
.font(.body)
141138
.tint(Color(uiColor: colorScheme == .light ? .highlight : .brand500))
142139
.fixedSize(horizontal: false, vertical: true)
143-
140+
144141
if !store.imageAttachments.isEmpty {
145-
imageAttachmentsView
146-
.padding(.top, .grid(1))
142+
ImageAttachmentsView(
143+
store: store,
144+
onImageTap: { index in
145+
shouldPresentImageItems = true
146+
selectedImageStartIndex = index
147+
}
148+
)
149+
.padding(.top, .grid(1))
147150
}
148151
}
149152
}
@@ -172,9 +175,16 @@ public struct TootView: View {
172175
.accessibilityElement(children: .combine)
173176
.padding(.vertical, .grid(2))
174177
}
175-
176-
@ViewBuilder
177-
func header() -> some View {
178+
}
179+
180+
// MARK: - Subviews
181+
182+
private struct TootHeader: View {
183+
@Environment(\.dynamicTypeSize) private var dynamicTypeSize: DynamicTypeSize
184+
185+
let store: StoreOf<TootFeature>
186+
187+
var body: some View {
178188
Group {
179189
if dynamicTypeSize.isAccessibilitySize {
180190
VStack(alignment: .leading) {
@@ -197,44 +207,50 @@ public struct TootView: View {
197207
}
198208
}
199209
}
210+
.contentShape(Rectangle())
211+
.onTapGesture { store.send(.openUser) }
200212
.accessibilityElement(children: .combine)
201-
.accessibilityRepresentation(representation: {
213+
.accessibilityRepresentation {
202214
HStack {
203215
if !store.accountDisplayName.isEmpty {
204216
displayName
205217
}
206-
displayName
207218
Text("posted at")
208219
postDatetime
209220
}
210-
})
221+
}
211222
}
212-
223+
213224
private var displayName: some View {
214225
Text(store.accountDisplayName)
215226
.lineLimit(1)
216227
.font(.titleTwo)
217228
.foregroundColor(.textPrimary)
218229
}
219-
230+
220231
private var accountName: some View {
221232
Text(store.accountAcct)
222233
.lineLimit(1)
223234
.font(.bodyTwo)
224235
.foregroundColor(.textSilent)
225236
.accessibilityHidden(true)
226237
}
227-
238+
228239
private var postDatetime: some View {
229240
let (text, a11yValue) = store.state.formattedCreationDate()
241+
230242
return Text(text ?? "")
231243
.font(.meta)
232244
.foregroundColor(.textPrimary)
233245
.accessibilityLabel(a11yValue ?? "")
234246
}
235-
236-
@ViewBuilder
237-
private var imageAttachmentsView: some View {
247+
}
248+
249+
private struct ImageAttachmentsView: View {
250+
let store: StoreOf<TootFeature>
251+
let onImageTap: (Int) -> Void
252+
253+
var body: some View {
238254
ScrollView(.horizontal, showsIndicators: false) {
239255
HStack {
240256
ForEach(Array(store.imageSheetItems.enumerated()), id: \.element.id) { index, item in
@@ -249,8 +265,7 @@ public struct TootView: View {
249265
.frame(height: 180)
250266
.cornerRadius(8)
251267
.onTapGesture {
252-
shouldPresentImageItems = true
253-
selectedImageStartIndex = index
268+
onImageTap(index)
254269
}
255270
case .failure:
256271
Color.gray.opacity(0.2)
@@ -266,7 +281,7 @@ public struct TootView: View {
266281
}
267282
}
268283

269-
// MARK: Preview
284+
// MARK: - Preview
270285

271286
#Preview {
272287
TootView(

CriticalMapsKit/Sources/MastodonFeedFeature/TootsListView.swift

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,20 @@ public struct TootsListView: View {
1515

1616
public var body: some View {
1717
if store.isLoading, !store.isRefreshing {
18-
loadingView()
18+
LoadingView()
1919
} else {
20-
contentView()
20+
ContentView(store: store)
2121
.refreshable {
2222
await store.send(.refresh).finish()
2323
}
2424
}
2525
}
26+
}
27+
28+
// MARK: - Subviews
2629

27-
@ViewBuilder
28-
func loadingView() -> some View {
30+
private struct LoadingView: View {
31+
var body: some View {
2932
VStack {
3033
Spacer()
3134
ProgressView {
@@ -36,9 +39,12 @@ public struct TootsListView: View {
3639
Spacer()
3740
}
3841
}
42+
}
43+
44+
private struct ContentView: View {
45+
let store: StoreOf<TootFeedFeature>
3946

40-
@ViewBuilder
41-
func contentView() -> some View {
47+
var body: some View {
4248
if store.toots.isEmpty {
4349
EmptyStateView(
4450
emptyState: .mastodon,
@@ -78,7 +84,7 @@ public struct TootsListView: View {
7884
}
7985
}
8086

81-
// MARK: Preview
87+
// MARK: - Preview
8288

8389
#Preview {
8490
Group {

0 commit comments

Comments
 (0)