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..600dd4c 100644 --- a/WaiterRobot/Ui/Order/ProductListItem.swift +++ b/WaiterRobot/Ui/Order/ProductListItem.swift @@ -31,13 +31,11 @@ struct ProductListItem: View { var foregroundColor: Color { if product.soldOut { - return .blackWhite - } - - if let backgroundColor { - return backgroundColor.getContentColor(lightColorScheme: .black, darkColorScheme: .white) + .blackWhite + } else if let backgroundColor { + backgroundColor.getContentColor(lightColorScheme: .black, darkColorScheme: .white) } else { - return Color.blackWhite + .blackWhite } } @@ -76,6 +74,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 +97,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..8d698b9 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,119 @@ 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.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.logout.desc(value0: CommonApp.shared.settings.organisationName)) + 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) + } + ) + + 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) + } + ) } - .withViewModel(viewModel, navigator) } } diff --git a/WaiterRobot/Ui/Settings/SwitchThemeView.swift b/WaiterRobot/Ui/Settings/SwitchThemeView.swift index 42f46d7..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.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) } } 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