Skip to content

Commit 187c7e5

Browse files
authored
Bookings: Fix navigation bar collapsing (#16327)
2 parents eb4d8d7 + dea337e commit 187c7e5

File tree

2 files changed

+95
-85
lines changed

2 files changed

+95
-85
lines changed

WooCommerce/Classes/Bookings/BookingList/BookingListContainerView.swift

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,12 @@ struct BookingListContainerView: View {
1616
}
1717

1818
var body: some View {
19-
VStack(spacing: 0) {
19+
BookingListView(
20+
viewModel: viewModel.listViewModel(for: viewModel.selectedTab),
21+
searchViewModel: viewModel.searchViewModel(for: viewModel.selectedTab),
22+
selectedBooking: $selectedBooking
23+
) {
2024
headerView
21-
TabView(selection: $viewModel.selectedTab) {
22-
ForEach(BookingListTab.allCases, id: \.rawValue) { tab in
23-
BookingListView(
24-
viewModel: viewModel.listViewModel(for: tab),
25-
searchViewModel: viewModel.searchViewModel(for: tab),
26-
selectedBooking: $selectedBooking
27-
)
28-
.tag(tab)
29-
}
30-
}
31-
.tabViewStyle(.page(indexDisplayMode: .never))
3225
}
3326
.navigationTitle(Localization.viewTitle)
3427
.if(isSearching, transform: { view in

WooCommerce/Classes/Bookings/BookingList/BookingListView.swift

Lines changed: 90 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import SwiftUI
22
import struct Yosemite.Booking
33

4-
struct BookingListView: View {
4+
struct BookingListView<Header: View>: View {
55
@ObservedObject private var viewModel: BookingListViewModel
66
@ObservedObject private var searchViewModel: BookingSearchViewModel
77

@@ -10,12 +10,16 @@ struct BookingListView: View {
1010

1111
@Binding var selectedBooking: Booking?
1212

13+
private let header: Header
14+
1315
init(viewModel: BookingListViewModel,
1416
searchViewModel: BookingSearchViewModel,
15-
selectedBooking: Binding<Booking?>) {
17+
selectedBooking: Binding<Booking?>,
18+
@ViewBuilder header: () -> Header) {
1619
self.viewModel = viewModel
1720
self.searchViewModel = searchViewModel
1821
self._selectedBooking = selectedBooking
22+
self.header = header()
1923
}
2024

2125
var body: some View {
@@ -98,20 +102,25 @@ private extension BookingListView {
98102
onNextPage: @escaping () -> Void,
99103
onRefresh: @escaping () async -> Void) -> some View {
100104
List(selection: $selectedBooking) {
101-
ForEach(bookings) { item in
102-
bookingItem(item)
103-
.tag(item)
104-
}
105-
106-
InfiniteScrollIndicator(showContent: viewModel.shouldShowBottomActivityIndicator)
107-
.padding(.top, Layout.viewPadding)
108-
.onAppear {
109-
onNextPage()
105+
Section {
106+
ForEach(bookings) { item in
107+
bookingItem(item)
108+
.tag(item)
110109
}
110+
111+
InfiniteScrollIndicator(showContent: viewModel.shouldShowBottomActivityIndicator)
112+
.padding(.top, BookingListViewLayout.viewPadding)
113+
.onAppear {
114+
onNextPage()
115+
}
116+
} header: {
117+
header
118+
.listRowInsets(EdgeInsets())
119+
}
111120
}
112121
.listStyle(.plain)
122+
.listSectionSeparator(.hidden, edges: .top)
113123
.background(Color(.listBackground))
114-
.accentColor(Color(.listSelectedBackground))
115124
.refreshable {
116125
await onRefresh()
117126
}
@@ -140,50 +149,20 @@ private extension BookingListView {
140149
}
141150
}
142151
}
152+
.listRowBackground(booking == selectedBooking ? Color(.listSelectedBackground) : Color(.listForeground(modal: false)))
143153
}
144154

145155
func emptyStateView(isSearching: Bool, onRefresh: @escaping () async -> Void) -> some View {
146156
GeometryReader { proxy in
147157
ScrollView {
148-
VStack(spacing: Layout.emptyStatePadding) {
149-
Spacer()
150-
Image(uiImage: isSearching ? .magnifyingGlassNotFound : .noBookings)
151-
.resizable()
152-
.aspectRatio(contentMode: .fit)
153-
.frame(width: Layout.emptyStateImageWidth * scale)
154-
.padding(.bottom, Layout.viewPadding)
155-
if isSearching {
156-
Text(Localization.emptySearchText)
157-
.font(.body)
158-
.foregroundStyle(Color.secondary)
159-
} else {
160-
VStack(spacing: Layout.textVerticalPadding) {
161-
Text(viewModel.emptyStateTitle)
162-
.font(.title2)
163-
.fontWeight(.semibold)
164-
.foregroundStyle(.primary)
165-
Text(viewModel.emptyStateDescription)
166-
.font(.title3)
167-
.foregroundStyle(.secondary)
168-
}
169-
if viewModel.hasFilters {
170-
VStack(spacing: Layout.textVerticalPadding) {
171-
Button("Change filters") {
172-
// TODO
173-
}
174-
.buttonStyle(PrimaryButtonStyle())
175-
Button("Clear filters") {
176-
// TODO
177-
}
178-
.buttonStyle(SecondaryButtonStyle())
179-
}
180-
}
158+
LazyVStack(spacing: 0, pinnedViews: .sectionHeaders) {
159+
Section {
160+
emptyStateContent(isSearching: isSearching)
161+
.frame(minWidth: proxy.size.width, minHeight: proxy.size.height)
162+
} header: {
163+
header
181164
}
182-
Spacer()
183165
}
184-
.multilineTextAlignment(.center)
185-
.padding(.horizontal, Layout.emptyStatePadding)
186-
.frame(minWidth: proxy.size.width, minHeight: proxy.size.height)
187166
}
188167
.refreshable {
189168
await onRefresh()
@@ -192,41 +171,79 @@ private extension BookingListView {
192171
.background(Color(.systemBackground))
193172
}
194173

174+
func emptyStateContent(isSearching: Bool) -> some View {
175+
VStack(spacing: BookingListViewLayout.emptyStatePadding) {
176+
Spacer()
177+
Image(uiImage: isSearching ? .magnifyingGlassNotFound : .noBookings)
178+
.resizable()
179+
.aspectRatio(contentMode: .fit)
180+
.frame(width: BookingListViewLayout.emptyStateImageWidth * scale)
181+
.padding(.bottom, BookingListViewLayout.viewPadding)
182+
if isSearching {
183+
Text(BookingListViewLocalization.emptySearchText)
184+
.font(.body)
185+
.foregroundStyle(Color.secondary)
186+
} else {
187+
VStack(spacing: BookingListViewLayout.textVerticalPadding) {
188+
Text(viewModel.emptyStateTitle)
189+
.font(.title2)
190+
.fontWeight(.semibold)
191+
.foregroundStyle(.primary)
192+
Text(viewModel.emptyStateDescription)
193+
.font(.title3)
194+
.foregroundStyle(.secondary)
195+
}
196+
if viewModel.hasFilters {
197+
VStack(spacing: BookingListViewLayout.textVerticalPadding) {
198+
Button("Change filters") {
199+
// TODO
200+
}
201+
.buttonStyle(PrimaryButtonStyle())
202+
Button("Clear filters") {
203+
// TODO
204+
}
205+
}
206+
}
207+
}
208+
Spacer()
209+
}
210+
.multilineTextAlignment(.center)
211+
.padding(.horizontal, BookingListViewLayout.emptyStatePadding)
212+
}
213+
195214
func errorSnackBar(onTap: @escaping () -> Void) -> some View {
196-
Text(Localization.errorMessage)
215+
Text(BookingListViewLocalization.errorMessage)
197216
.foregroundStyle(Color(.listForeground(modal: false)))
198217
.frame(maxWidth: .infinity, alignment: .leading)
199-
.padding(Layout.viewPadding)
218+
.padding(BookingListViewLayout.viewPadding)
200219
.background {
201-
RoundedRectangle(cornerRadius: Layout.cornerRadius)
220+
RoundedRectangle(cornerRadius: BookingListViewLayout.cornerRadius)
202221
.fill(Color(.text))
203222
}
204-
.padding(Layout.viewPadding)
223+
.padding(BookingListViewLayout.viewPadding)
205224
.padding(.bottom, connectivityMonitor.isOffline ? OfflineBannerView.height : 0)
206225
.contentShape(Rectangle())
207226
.onTapGesture { onTap() }
208227
}
209228
}
210229

211-
private extension BookingListView {
212-
enum Layout {
213-
static let textVerticalPadding: CGFloat = 8
214-
static let viewPadding: CGFloat = 16
215-
static let emptyStatePadding: CGFloat = 24
216-
static let emptyStateImageWidth: CGFloat = 67
217-
static let cornerRadius: CGFloat = 8
218-
}
230+
fileprivate enum BookingListViewLayout {
231+
static let textVerticalPadding: CGFloat = 8
232+
static let viewPadding: CGFloat = 16
233+
static let emptyStatePadding: CGFloat = 24
234+
static let emptyStateImageWidth: CGFloat = 67
235+
static let cornerRadius: CGFloat = 8
236+
}
219237

220-
enum Localization {
221-
static let errorMessage = NSLocalizedString(
222-
"bookingList.errorMessage",
223-
value: "Error fetching bookings",
224-
comment: "Error message when fetching bookings fails"
225-
)
226-
static let emptySearchText = NSLocalizedString(
227-
"bookingList.emptySearchText",
228-
value: "We couldn’t find any bookings with that name — try adjusting your search term to see more results.",
229-
comment: "Message displayed when searching bookings by keyword yields no results."
230-
)
231-
}
238+
fileprivate enum BookingListViewLocalization {
239+
static let errorMessage = NSLocalizedString(
240+
"bookingList.errorMessage",
241+
value: "Error fetching bookings",
242+
comment: "Error message when fetching bookings fails"
243+
)
244+
static let emptySearchText = NSLocalizedString(
245+
"bookingList.emptySearchText",
246+
value: "We couldn't find any bookings with that name — try adjusting your search term to see more results.",
247+
comment: "Message displayed when searching bookings by keyword yields no results."
248+
)
232249
}

0 commit comments

Comments
 (0)