From 199c03469bc5e2d0577b88058743be2570ca76f7 Mon Sep 17 00:00:00 2001 From: Fabian Schedler Date: Mon, 2 Sep 2024 18:14:44 +0200 Subject: [PATCH 1/5] WR-363: Make money change dialog optional --- WaiterRobot/Ui/Billing/BillingScreen.swift | 15 +- WaiterRobot/Ui/Billing/PayDialog.swift | 4 +- WaiterRobot/Ui/Core/Navigation.swift | 4 +- WaiterRobot/Ui/Order/ProductListItem.swift | 8 +- .../Ui/Order/Search/ProductSearchAllTab.swift | 1 + .../Order/Search/ProductSearchGroupList.swift | 1 + WaiterRobot/Ui/Settings/SettingsItem.swift | 35 ++++- WaiterRobot/Ui/Settings/SettingsScreen.swift | 137 ++++++++++++------ project.yml | 2 +- 9 files changed, 151 insertions(+), 56 deletions(-) diff --git a/WaiterRobot/Ui/Billing/BillingScreen.swift b/WaiterRobot/Ui/Billing/BillingScreen.swift index a557996..8fe165a 100644 --- a/WaiterRobot/Ui/Billing/BillingScreen.swift +++ b/WaiterRobot/Ui/Billing/BillingScreen.swift @@ -27,7 +27,7 @@ struct BillingScreen: View { title: localize.dialog.cancel(), icon: nil ) { - if viewModel.state.hasSelectedItems { + if viewModel.state.hasCustomSelection { showAbortConfirmation = true } else { viewModel.actual.abortBill() @@ -67,7 +67,16 @@ struct BillingScreen: View { .sheet(isPresented: $showPayDialog) { PayDialog(viewModel: viewModel) } - .withViewModel(viewModel, navigator) + .withViewModel(viewModel, navigator) { effect in + switch onEnum(of: effect) { + case .showPaymentSheet: + showPayDialog = true + case .toast: + break // TODO: add "toast" support + } + + return true + } } @ViewBuilder @@ -117,7 +126,7 @@ struct BillingScreen: View { .padding() .overlay(alignment: .bottom) { Button { - showPayDialog = true + viewModel.actual.paySelection(paymentSheetShown: false) } label: { Image(systemName: "eurosign") .font(.system(.title)) diff --git a/WaiterRobot/Ui/Billing/PayDialog.swift b/WaiterRobot/Ui/Billing/PayDialog.swift index 049ff32..48b6f18 100644 --- a/WaiterRobot/Ui/Billing/PayDialog.swift +++ b/WaiterRobot/Ui/Billing/PayDialog.swift @@ -55,9 +55,11 @@ struct PayDialog: View { dismiss() } } + } + .toolbar { ToolbarItem(placement: .confirmationAction) { Button(localize.billing.pay()) { - viewModel.actual.paySelection() + viewModel.actual.paySelection(paymentSheetShown: true) dismiss() } } diff --git a/WaiterRobot/Ui/Core/Navigation.swift b/WaiterRobot/Ui/Core/Navigation.swift index 08c904c..ed0e0a7 100644 --- a/WaiterRobot/Ui/Core/Navigation.swift +++ b/WaiterRobot/Ui/Core/Navigation.swift @@ -94,9 +94,9 @@ extension View { func withViewModel( _ viewModel: some ObservableViewModel>, _ navigator: UIPilot, - handler _: ((SideEffect) -> Bool)? = nil + handler: ((SideEffect) -> Bool)? = nil ) -> some View where State: ViewModelState, SideEffect: ViewModelEffect { - handleSideEffects(of: viewModel, navigator) + handleSideEffects(of: viewModel, navigator, handler: handler) .observeState(of: viewModel) } } diff --git a/WaiterRobot/Ui/Order/ProductListItem.swift b/WaiterRobot/Ui/Order/ProductListItem.swift index cc09c38..32d8880 100644 --- a/WaiterRobot/Ui/Order/ProductListItem.swift +++ b/WaiterRobot/Ui/Order/ProductListItem.swift @@ -31,13 +31,13 @@ struct ProductListItem: View { var foregroundColor: Color { if product.soldOut { - return .blackWhite + .blackWhite } if let backgroundColor { - return backgroundColor.getContentColor(lightColorScheme: .black, darkColorScheme: .white) + backgroundColor.getContentColor(lightColorScheme: .black, darkColorScheme: .white) } else { - return Color.blackWhite + Color.blackWhite } } @@ -76,6 +76,7 @@ struct ProductListItem: View { name: "Wine", price: Money(cents: 290), soldOut: true, + color: nil, allergens: [ Allergen(id: 1, name: "Egg", shortName: "E"), Allergen(id: 2, name: "Egg2", shortName: "A"), @@ -98,6 +99,7 @@ struct ProductListItem: View { name: "Wine", price: Money(cents: 290), soldOut: false, + color: nil, allergens: [ Allergen(id: 1, name: "Egg", shortName: "E"), Allergen(id: 2, name: "Egg2", shortName: "A"), diff --git a/WaiterRobot/Ui/Order/Search/ProductSearchAllTab.swift b/WaiterRobot/Ui/Order/Search/ProductSearchAllTab.swift index 64b192b..b41313b 100644 --- a/WaiterRobot/Ui/Order/Search/ProductSearchAllTab.swift +++ b/WaiterRobot/Ui/Order/Search/ProductSearchAllTab.swift @@ -46,6 +46,7 @@ struct ProductSearchAllTab: View { name: "Beer", price: Money(cents: 450), soldOut: false, + color: nil, allergens: [], position: 1 ), diff --git a/WaiterRobot/Ui/Order/Search/ProductSearchGroupList.swift b/WaiterRobot/Ui/Order/Search/ProductSearchGroupList.swift index c6f9835..effc113 100644 --- a/WaiterRobot/Ui/Order/Search/ProductSearchGroupList.swift +++ b/WaiterRobot/Ui/Order/Search/ProductSearchGroupList.swift @@ -26,6 +26,7 @@ struct ProductSearchGroupList: View { name: "Beer", price: Money(cents: 450), soldOut: false, + color: nil, allergens: [], position: 1 ), diff --git a/WaiterRobot/Ui/Settings/SettingsItem.swift b/WaiterRobot/Ui/Settings/SettingsItem.swift index 396167f..5b7bf68 100644 --- a/WaiterRobot/Ui/Settings/SettingsItem.swift +++ b/WaiterRobot/Ui/Settings/SettingsItem.swift @@ -1,14 +1,15 @@ import SwiftUI -struct SettingsItem: View { +struct SettingsItem: View { let icon: String let title: String let subtitle: String - let action: () -> Void + @ViewBuilder let action: Action? + let onClick: () -> Void var body: some View { Button { - action() + onClick() } label: { HStack { Image(systemName: icon) @@ -25,25 +26,49 @@ struct SettingsItem: View { .font(.caption) .foregroundColor(.secondary) } + + if let action { + Spacer() + action + } } } .padding([.bottom, .top], 1) } } +extension SettingsItem where Action == EmptyView { + init(icon: String, title: String, subtitle: String, onClick: @escaping () -> Void) { + self.icon = icon + self.title = title + self.subtitle = subtitle + action = nil + self.onClick = onClick + } +} + #Preview { List { SettingsItem( icon: "rectangle.portrait.and.arrow.right", title: "Logout", subtitle: "Logout from this organisation", - action: {} + onClick: {} ) SettingsItem( icon: "person.3", title: "Switch event", subtitle: "My Organisation / The Event", - action: {} + onClick: {} + ) + SettingsItem( + icon: "dollarsign.arrow.circlepath", + title: "Toggle", + subtitle: "Some toggle able", + action: { + Toggle(isOn: .constant(true)) {}.labelsHidden() + }, + onClick: {} ) } } diff --git a/WaiterRobot/Ui/Settings/SettingsScreen.swift b/WaiterRobot/Ui/Settings/SettingsScreen.swift index d25dd5a..bb48ed9 100644 --- a/WaiterRobot/Ui/Settings/SettingsScreen.swift +++ b/WaiterRobot/Ui/Settings/SettingsScreen.swift @@ -6,6 +6,7 @@ struct SettingsScreen: View { @EnvironmentObject var navigator: UIPilot @State private var showConfirmLogout = false + @State private var showConfirmSkipMoneyBackDialog = false @StateObject private var viewModel = ObservableSettingsViewModel() @@ -31,44 +32,12 @@ struct SettingsScreen: View { private func content() -> some View { List { - Section { - SettingsItem( - icon: "rectangle.portrait.and.arrow.right", - title: localize.settings.logout.action(), - subtitle: "\"\(CommonApp.shared.settings.organisationName)\" / \"\(CommonApp.shared.settings.waiterName)\"", - action: { - showConfirmLogout = true - } - ) + general() - SettingsItem( - icon: "arrow.triangle.2.circlepath", - title: localize.settings.refresh.title(), - subtitle: localize.settings.refresh.desc(), - action: { - viewModel.actual.refreshAll() - } - ) + payment() - SettingsItem( - icon: "person.3", - title: localize.switchEvent.title(), - subtitle: CommonApp.shared.settings.eventName, - action: { - viewModel.actual.switchEvent() - } - ) - } - - Section { - SwitchThemeView( - initial: viewModel.state.currentAppTheme, - onChange: viewModel.actual.switchTheme - ) - } - - Section { - Link(localize.settings.privacyPolicy(), destination: URL(string: CommonApp.shared.privacyPolicyUrl)!) + Section(header: Text(localize.settings.about.title())) { + Link(localize.settings.about.privacyPolicy(), destination: URL(string: CommonApp.shared.privacyPolicyUrl)!) } HStack { @@ -89,12 +58,98 @@ struct SettingsScreen: View { } } } - .confirmationDialog(localize.settings.logout.title(value0: CommonApp.shared.settings.organisationName), isPresented: $showConfirmLogout, titleVisibility: .visible) { - Button(localize.settings.logout.action(), role: .destructive, action: { viewModel.actual.logout() }) - Button(localize.settings.keepLoggedIn(), role: .cancel, action: { showConfirmLogout = false }) + .confirmationDialog( + localize.settings.general.logout.title(value0: CommonApp.shared.settings.organisationName), + isPresented: $showConfirmLogout, + titleVisibility: .visible + ) { + Button(localize.settings.general.logout.action(), role: .destructive, action: { viewModel.actual.logout() }) + Button(localize.settings.general.keepLoggedIn(), role: .cancel, action: { showConfirmLogout = false }) } message: { - Text(localize.settings.logout.desc(value0: CommonApp.shared.settings.organisationName)) + Text(localize.settings.general.logout.desc(value0: CommonApp.shared.settings.organisationName)) + } + .confirmationDialog( + localize.settings.payment.skipMoneyBackDialog.title(), + isPresented: $showConfirmSkipMoneyBackDialog, + titleVisibility: .visible + ) { + Button(localize.settings.payment.skipMoneyBackDialog.confirmAction(), role: .destructive) { + viewModel.actual.toggleSkipMoneyBackDialog(value: true, confirmed: true) + } + Button(localize.dialog.cancel(), role: .cancel) { + showConfirmSkipMoneyBackDialog = false + } + } message: { + Text(localize.settings.payment.skipMoneyBackDialog.confirmDesc()) + } + .withViewModel(viewModel, navigator) { effect in + switch onEnum(of: effect) { + case .confirmSkipMoneyBackDialog: + showConfirmSkipMoneyBackDialog = true + } + + return true + } + } + + private func general() -> some View { + Section(header: Text(localize.settings.general.title())) { + SettingsItem( + icon: "rectangle.portrait.and.arrow.right", + title: localize.settings.general.logout.action(), + subtitle: "\"\(CommonApp.shared.settings.organisationName)\" / \"\(CommonApp.shared.settings.waiterName)\"", + onClick: { + showConfirmLogout = true + } + ) + + SettingsItem( + icon: "person.3", + title: localize.switchEvent.title(), + subtitle: CommonApp.shared.settings.eventName, + onClick: { + viewModel.actual.switchEvent() + } + ) + + SwitchThemeView( + initial: viewModel.state.currentAppTheme, + onChange: viewModel.actual.switchTheme + ) + + SettingsItem( + icon: "arrow.triangle.2.circlepath", + title: localize.settings.general.refresh.title(), + subtitle: localize.settings.general.refresh.desc(), + onClick: { + viewModel.actual.refreshAll() + } + ) + } + } + + private func payment() -> some View { + Section(header: Text(localize.settings.payment.title())) { + SettingsItem( + icon: "dollarsign.arrow.circlepath", + title: localize.settings.payment.skipMoneyBackDialog.title(), + subtitle: localize.settings.payment.skipMoneyBackDialog.desc(), + action: { + Toggle( + isOn: .init( + get: { viewModel.state.skipMoneyBackDialog }, + set: { newValue in + let kotlinBool = KotlinBoolean(value: newValue) + viewModel.actual.toggleSkipMoneyBackDialog(value: kotlinBool, confirmed: false) + } + ), + label: {} + ).labelsHidden() + }, + onClick: { + viewModel.actual.toggleSkipMoneyBackDialog(value: nil, confirmed: false) + } + ) } - .withViewModel(viewModel, navigator) } } diff --git a/project.yml b/project.yml index 4866411..d88fe34 100644 --- a/project.yml +++ b/project.yml @@ -17,7 +17,7 @@ fileGroups: packages: shared: url: https://github.com/DatepollSystems/WaiterRobot-Shared-Android.git - version: 1.6.4 + version: 1.6.7 UIPilot: url: https://github.com/canopas/UIPilot.git from: 1.3.1 From 34398d895e3e2c0bddfd4f0ed36138bd852f4f88 Mon Sep 17 00:00:00 2001 From: Fabian Schedler Date: Mon, 2 Sep 2024 18:18:27 +0200 Subject: [PATCH 2/5] Fix --- WaiterRobot/Ui/Order/ProductListItem.swift | 6 ++---- WaiterRobot/Ui/Settings/SwitchThemeView.swift | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/WaiterRobot/Ui/Order/ProductListItem.swift b/WaiterRobot/Ui/Order/ProductListItem.swift index 32d8880..600dd4c 100644 --- a/WaiterRobot/Ui/Order/ProductListItem.swift +++ b/WaiterRobot/Ui/Order/ProductListItem.swift @@ -32,12 +32,10 @@ struct ProductListItem: View { var foregroundColor: Color { if product.soldOut { .blackWhite - } - - if let backgroundColor { + } else if let backgroundColor { backgroundColor.getContentColor(lightColorScheme: .black, darkColorScheme: .white) } else { - Color.blackWhite + .blackWhite } } diff --git a/WaiterRobot/Ui/Settings/SwitchThemeView.swift b/WaiterRobot/Ui/Settings/SwitchThemeView.swift index 42f46d7..69363c3 100644 --- a/WaiterRobot/Ui/Settings/SwitchThemeView.swift +++ b/WaiterRobot/Ui/Settings/SwitchThemeView.swift @@ -11,7 +11,7 @@ struct SwitchThemeView: View { } var body: some View { - Picker(localize.settings.darkMode.title(), selection: $selectedTheme) { + Picker(localize.settings.general.darkMode.title(), selection: $selectedTheme) { ForEach(AppTheme.companion.valueList(), id: \.name) { theme in Text(theme.settingsText()).tag(theme) } From 75e98825a91630b279832aae6c8d094f34d0dcb8 Mon Sep 17 00:00:00 2001 From: Fabian Schedler Date: Tue, 3 Sep 2024 09:16:55 +0200 Subject: [PATCH 3/5] WR-365: Select all products by default --- WaiterRobot/Ui/Settings/SettingsScreen.swift | 21 +++++++++++++++++++ WaiterRobot/Ui/Settings/SwitchThemeView.swift | 18 ++++++++++++---- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/WaiterRobot/Ui/Settings/SettingsScreen.swift b/WaiterRobot/Ui/Settings/SettingsScreen.swift index bb48ed9..8d698b9 100644 --- a/WaiterRobot/Ui/Settings/SettingsScreen.swift +++ b/WaiterRobot/Ui/Settings/SettingsScreen.swift @@ -150,6 +150,27 @@ struct SettingsScreen: View { viewModel.actual.toggleSkipMoneyBackDialog(value: nil, confirmed: false) } ) + + SettingsItem( + icon: "checkmark.square", + title: localize.settings.payment.selectAllProductsByDefault.title(), + subtitle: localize.settings.payment.selectAllProductsByDefault.desc(), + action: { + Toggle( + isOn: .init( + get: { viewModel.state.paymentSelectAllProductsByDefault }, + set: { newValue in + let kotlinBool = KotlinBoolean(value: newValue) + viewModel.actual.togglePaymentSelectAllProductsByDefault(value: kotlinBool) + } + ), + label: {} + ).labelsHidden() + }, + onClick: { + viewModel.actual.togglePaymentSelectAllProductsByDefault(value: nil) + } + ) } } } diff --git a/WaiterRobot/Ui/Settings/SwitchThemeView.swift b/WaiterRobot/Ui/Settings/SwitchThemeView.swift index 69363c3..fc18276 100644 --- a/WaiterRobot/Ui/Settings/SwitchThemeView.swift +++ b/WaiterRobot/Ui/Settings/SwitchThemeView.swift @@ -11,12 +11,22 @@ struct SwitchThemeView: View { } var body: some View { - Picker(localize.settings.general.darkMode.title(), selection: $selectedTheme) { - ForEach(AppTheme.companion.valueList(), id: \.name) { theme in - Text(theme.settingsText()).tag(theme) + HStack { + Image(systemName: "moon") + .symbolRenderingMode(.hierarchical) + .resizable() + .scaledToFit() + .frame(maxWidth: 25) + .padding(.trailing) + .foregroundColor(.blue) + + Picker(localize.settings.general.darkMode.title(), selection: $selectedTheme) { + ForEach(AppTheme.companion.valueList(), id: \.name) { theme in + Text(theme.settingsText()).tag(theme) + } } + .onChange(of: selectedTheme, perform: onChange) } - .onChange(of: selectedTheme, perform: onChange) } } From ec69f8e5f2d5f0be77334a83c338622939dacf33 Mon Sep 17 00:00:00 2001 From: Fabian Schedler Date: Tue, 17 Sep 2024 14:15:20 +0200 Subject: [PATCH 4/5] Increase shared version --- project.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project.yml b/project.yml index d88fe34..fdadf8b 100644 --- a/project.yml +++ b/project.yml @@ -17,7 +17,7 @@ fileGroups: packages: shared: url: https://github.com/DatepollSystems/WaiterRobot-Shared-Android.git - version: 1.6.7 + version: 1.6.9 UIPilot: url: https://github.com/canopas/UIPilot.git from: 1.3.1 From 48ee0edbd4d5692037e9b7571707fa6e8c7823ad Mon Sep 17 00:00:00 2001 From: Fabian Schedler Date: Wed, 18 Sep 2024 17:35:57 +0200 Subject: [PATCH 5/5] chore: Bump version to 2.4.0 --- project.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project.yml b/project.yml index fdadf8b..21a81a5 100644 --- a/project.yml +++ b/project.yml @@ -48,10 +48,10 @@ targetTemplates: info: path: ".generated/${target_name}.plist" properties: - CFBundleShortVersionString: "2.3.0" + CFBundleShortVersionString: "2.4.0" # Generate VersionCode from VersionName (major * 10_000 + minor * 100 + patch, e.g. 1.2.3 -> 10203, 1.23.45 -> 12345) # Only used for prod releases. Lava uses epochMinute (same as on Android) - CFBundleVersion: "20007" + CFBundleVersion: "20400" VERSION_SUFFIX: "${versionSuffix}" ALLOWED_HOSTS: "${allowedHosts}"