diff --git a/.gitignore b/.gitignore index 62eb405..5b17b5a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,3 @@ -# xcodegen -WaiterRobot.xcodeproj/ -.generated/ - # Created by https://www.toptal.com/developers/gitignore/api/fastlane,xcode,intellij,appcode,macos # Edit at https://www.toptal.com/developers/gitignore?templates=fastlane,xcode,intellij,appcode,macos @@ -240,14 +236,6 @@ Temporary Items .apdisk .build -### Xcode ### -## User settings -xcuserdata/ - -## Xcode 8 and earlier -*.xcscmblueprint -*.xccheckout - ## Obj-C/Swift specific *.hmap *.ipa @@ -256,6 +244,11 @@ xcuserdata/ # End of https://www.toptal.com/developers/gitignore/api/fastlane,xcode,intellij,appcode,macos +# Xcodegen +.generated/ +WaiterRobot.xcodeproj/* +!WaiterRobot.xcodeproj/project.xcworkspace/xcshareddata/swiftpm + .keys* .env.* !.env.example diff --git a/WaiterRobot/Core/Mvi/ObservableViewModel.swift b/WaiterRobot/Core/Mvi/ObservableViewModel.swift index 03cab6b..4992484 100644 --- a/WaiterRobot/Core/Mvi/ObservableViewModel.swift +++ b/WaiterRobot/Core/Mvi/ObservableViewModel.swift @@ -6,29 +6,28 @@ import Foundation import shared -@MainActor class ObservableViewModel>: ObservableObject { - @Published public private(set) var state: State + @Published + public private(set) var state: State public let actual: ViewModel private var task: Task? = nil - init(viewModel: ViewModel) { + init(viewModel: ViewModel, subscribe: Bool = true) { actual = viewModel // This is save, as the constraint is required by the generics (S must be the state of the provided VM) state = actual.container.stateFlow.value as! State - activate() - } - - deinit { - actual.onCleared() - task?.cancel() + if subscribe { + Task { + await activate() + } + } } @MainActor - private func activate() { + func activate() { guard task == nil else { return } task = Task { [weak self] in guard let stateFlow = self?.actual.container.stateFlow else { return } @@ -38,17 +37,29 @@ class ObservableViewModel { init() { - super.init(viewModel: koin.tableListVM()) + super.init(viewModel: koin.tableListVM(), subscribe: false) } } class ObservableTableDetailViewModel: ObservableViewModel { init(table: Table) { - super.init(viewModel: koin.tableDetailVM(table: table)) + super.init(viewModel: koin.tableDetailVM(table: table), subscribe: false) } } diff --git a/WaiterRobot/Core/PreviewView.swift b/WaiterRobot/Core/PreviewView.swift index eec616b..77d57e9 100644 --- a/WaiterRobot/Core/PreviewView.swift +++ b/WaiterRobot/Core/PreviewView.swift @@ -5,7 +5,7 @@ import UIPilot /// Helper view which sets up everything needed for previewing content struct PreviewView: View { @StateObject - private var navigator = UIPilot(initial: Screen.RootScreen.shared, debug: true) + private var navigator = UIPilot(initial: CommonApp.shared.getNextRootScreen(), debug: true) private let withUIPilot: Bool diff --git a/WaiterRobot/LaunchScreen.swift b/WaiterRobot/LaunchScreen.swift index 333b63c..c0aa78a 100644 --- a/WaiterRobot/LaunchScreen.swift +++ b/WaiterRobot/LaunchScreen.swift @@ -9,7 +9,7 @@ struct LaunchScreen: View { @State private var startupFinished = false var body: some View { - VStack { + ZStack { if case .phone = device { VStack { Spacer() @@ -37,6 +37,10 @@ struct LaunchScreen: View { } } } + + if startupFinished { + MainView() + } } .onAppear { // This is needed otherwise previews will crash randomly @@ -52,9 +56,6 @@ struct LaunchScreen: View { } } } - .fullScreenCover(isPresented: $startupFinished) { - MainView() - } } private func delay() async { diff --git a/WaiterRobot/MainView.swift b/WaiterRobot/MainView.swift index 8382a91..46db551 100644 --- a/WaiterRobot/MainView.swift +++ b/WaiterRobot/MainView.swift @@ -10,10 +10,17 @@ import SwiftUI import UIPilot struct MainView: View { - @State private var snackBarMessage: String? - @State private var showUpdateAvailableAlert: Bool = false - @StateObject private var navigator: UIPilot = UIPilot(initial: Screen.RootScreen.shared, debug: true) - @StateObject private var viewModel = ObservableRootViewModel() + @State + private var snackBarMessage: String? + + @State + private var showUpdateAvailableAlert: Bool = false + + @StateObject + private var navigator: UIPilot = UIPilot(initial: CommonApp.shared.getNextRootScreen(), debug: true) + + @StateObject + private var viewModel = ObservableRootViewModel() private var selectedScheme: ColorScheme? { switch viewModel.state.selectedTheme { @@ -29,29 +36,39 @@ struct MainView: View { var body: some View { ZStack { UIPilotHost(navigator) { route in - switch route { - case is Screen.RootScreen: RootScreen(viewModel: viewModel) - case is Screen.LoginScannerScreen: LoginScannerScreen() - case is Screen.SwitchEventScreen: SwitchEventScreen() - case is Screen.SettingsScreen: SettingsScreen() - case is Screen.UpdateApp: UpdateAppScreen() - case let screen as Screen.RegisterScreen: RegisterScreen(createToken: screen.createToken) - case let screen as Screen.TableDetailScreen: TableDetailScreen(table: screen.table) - case let screen as Screen.OrderScreen: OrderScreen(table: screen.table, initialItemId: screen.initialItemId) - case let screen as Screen.BillingScreen: BillingScreen(table: screen.table) - default: - VStack { - Text("No view defined for \(route.description)") + switch onEnum(of: route) { + case .loginScreen: + LoginScreen() - Button { - navigator.pop() - } label: { - Text("Back") - } - .onAppear { - koin.logger(tag: "WaiterRobotApp").e { "No view defined for \(route.description)" } - } - } + case .tableListScreen: + TableListScreen() + + case .switchEventScreen: + SwitchEventScreen() + + case .settingsScreen: + SettingsScreen() + + case let .registerScreen(screen): + RegisterScreen(deepLink: screen.registerLink) + + case .updateApp: + UpdateAppScreen() + + case let .tableDetailScreen(screen): + TableDetailScreen(table: screen.table) + + case let .orderScreen(screen): + OrderScreen(table: screen.table, initialItemId: screen.initialItemId) + + case let .billingScreen(screen): + BillingScreen(table: screen.table) + + case .loginScannerScreen: + LoginScannerScreen() + + case .stripeInitializationScreen: + EmptyView() } } } diff --git a/WaiterRobot/Ui/Billing/BillListItem.swift b/WaiterRobot/Ui/Billing/BillListItem.swift index 6e4e46e..bdaf284 100644 --- a/WaiterRobot/Ui/Billing/BillListItem.swift +++ b/WaiterRobot/Ui/Billing/BillListItem.swift @@ -67,11 +67,12 @@ struct BillListItem: View { List { BillListItem( item: BillItem( - productId: 1, + baseProductId: 1, name: "Beer", ordered: 10, selectedForBill: 5, - pricePerPiece: Money(cents: 3390) + pricePerPiece: Money(cents: 3390), + orderProductIds: [1, 2] ), addOne: {}, addAll: {}, @@ -80,11 +81,12 @@ struct BillListItem: View { ) BillListItem( item: BillItem( - productId: 2, + baseProductId: 2, name: "Wine", ordered: 15, selectedForBill: 8, - pricePerPiece: Money(cents: 390) + pricePerPiece: Money(cents: 390), + orderProductIds: [1, 2] ), addOne: {}, addAll: {}, diff --git a/WaiterRobot/Ui/Billing/BillingScreen.swift b/WaiterRobot/Ui/Billing/BillingScreen.swift index b4e74c3..4fa5891 100644 --- a/WaiterRobot/Ui/Billing/BillingScreen.swift +++ b/WaiterRobot/Ui/Billing/BillingScreen.swift @@ -81,20 +81,20 @@ struct BillingScreen: View { .padding() } else { Section { - ForEach(billItems, id: \.self) { item in + ForEach(billItems, id: \.virtualId) { item in BillListItem( item: item, addOne: { - viewModel.actual.addItem(id: item.productId, amount: 1) + viewModel.actual.addItem(virtualId: item.virtualId, amount: 1) }, addAll: { - viewModel.actual.addItem(id: item.productId, amount: item.ordered - item.selectedForBill) + viewModel.actual.addItem(virtualId: item.virtualId, amount: item.ordered - item.selectedForBill) }, removeOne: { - viewModel.actual.addItem(id: item.productId, amount: -1) + viewModel.actual.addItem(virtualId: item.virtualId, amount: -1) }, removeAll: { - viewModel.actual.addItem(id: item.productId, amount: -item.selectedForBill) + viewModel.actual.addItem(virtualId: item.virtualId, amount: -item.selectedForBill) } ) } diff --git a/WaiterRobot/Ui/Common/TextEditorWithPlaceholder.swift b/WaiterRobot/Ui/Common/TextEditorWithPlaceholder.swift deleted file mode 100644 index 106468e..0000000 --- a/WaiterRobot/Ui/Common/TextEditorWithPlaceholder.swift +++ /dev/null @@ -1,43 +0,0 @@ -import SwiftUI - -struct TextEditorWithPlaceholder: View { - @Binding var text: String - let lable: String - let placeHolder: String - let editorHeight: CGFloat - let maxLength: Int? - - var body: some View { - VStack { - Text(lable) - - TextEditor(text: $text) - .border(.gray) - .frame(maxHeight: editorHeight) - .overlay(alignment: .topLeading) { - if text.isEmpty { - Text(placeHolder) - .foregroundColor(.gray.opacity(0.3)) - .padding(.leading, 5) - .padding(.top, 9) - } - } - .onChange(of: text) { _ in - guard let maxLength else { return } - if text.count > maxLength { - text = String(text.prefix(maxLength)) - } - } - - if let maxLength { - Text("\(text.count)/\(maxLength)") - } - } - } -} - -struct TextEditorWithPlaceholder_Previews: PreviewProvider { - static var previews: some View { - TextEditorWithPlaceholder(text: .constant(""), lable: "Lable", placeHolder: "Some placeholder", editorHeight: 150, maxLength: 150) - } -} diff --git a/WaiterRobot/Ui/Core/FloatingActionButton.swift b/WaiterRobot/Ui/Core/FloatingActionButton.swift index ff84a7e..679588c 100644 --- a/WaiterRobot/Ui/Core/FloatingActionButton.swift +++ b/WaiterRobot/Ui/Core/FloatingActionButton.swift @@ -62,15 +62,13 @@ struct FloatingActionButtonStyle: ButtonStyle { } } -struct FloatingActionButton_Previews: PreviewProvider { - static var previews: some View { - ZStack { - EmbeddedFloatingActionButton( - icon: "plus", - action: { - print("Test") - } - ) - } +#Preview { + ZStack { + EmbeddedFloatingActionButton( + icon: "plus", + action: { + print("Test") + } + ) } } diff --git a/WaiterRobot/Ui/Core/Navigation.swift b/WaiterRobot/Ui/Core/Navigation.swift index d9d8976..573262e 100644 --- a/WaiterRobot/Ui/Core/Navigation.swift +++ b/WaiterRobot/Ui/Core/Navigation.swift @@ -6,22 +6,31 @@ import UIPilot extension UIPilot { func navigate(_ navAction: NavAction) { koin.logger(tag: "Navigation").d { "Handle navigation: \(navAction.description)" } - switch navAction { - case is NavAction.Pop: + + switch onEnum(of: navAction) { + case .pop: pop() - case let nav as NavAction.Push: + + case let .push(nav): push(nav.screen) - case let nav as NavAction.PopUpTo: + + case let .popUpTo(nav): popTo(nav.screen, inclusive: nav.inclusive) - case let nav as NavAction.PopUpAndPush: + + case let .popUpAndPush(nav): popTo(nav.popUpTo, inclusive: nav.inclusive) push(nav.screen) - default: - koin.logger(tag: "Navigation").e { - "No nav action for nav effect \(navAction.self.description)" - } + + case let .replaceRoot(nav): + guard let topmost = topmostScreen else { return } + popTo(topmost, inclusive: true) + push(nav.screen) } } + + var topmostScreen: Screen? { + stack.first + } } extension View { diff --git a/WaiterRobot/Ui/Core/RefreshableScrollView.swift b/WaiterRobot/Ui/Core/RefreshableScrollView.swift index 5eeb78b..d97c12c 100644 --- a/WaiterRobot/Ui/Core/RefreshableScrollView.swift +++ b/WaiterRobot/Ui/Core/RefreshableScrollView.swift @@ -60,24 +60,20 @@ struct RefreshableScrollView: View { } } -struct Refreshing_Preview: PreviewProvider { - static var previews: some View { - RefreshableScrollView( - isRefreshing: true, - onRefresh: {} - ) { - Text("Refreshing") - } +#Preview { + RefreshableScrollView( + isRefreshing: true, + onRefresh: {} + ) { + Text("Refreshing") } } -struct PullToRefresh_Preview: PreviewProvider { - static var previews: some View { - RefreshableScrollView( - isRefreshing: false, - onRefresh: {} - ) { - Text("Pull to refresh") - } +#Preview { + RefreshableScrollView( + isRefreshing: false, + onRefresh: {} + ) { + Text("Pull to refresh") } } diff --git a/WaiterRobot/Ui/Login/LoginScannerScreen.swift b/WaiterRobot/Ui/Login/LoginScannerScreen.swift index dc0c4bc..45941a3 100644 --- a/WaiterRobot/Ui/Login/LoginScannerScreen.swift +++ b/WaiterRobot/Ui/Login/LoginScannerScreen.swift @@ -8,7 +8,7 @@ struct LoginScannerScreen: View { @StateObject private var viewModel = ObservableLoginScannerViewModel() - private let simulatedData = "https://lava.kellner.team/ml/signIn?token=w7wF6pgYA6Ssm3VBH-rSFL6if70&purpose=SIGN_IN" + private let simulatedData = "https://lava.kellner.team/ml/signIn?purpose=SIGN_IN&token=xWEP33DuYhJzUvQ6clywKPCM_Oa5NymihpJk4-_EGHV3D_f10YSKL_2hOYV3" var body: some View { switch viewModel.state.viewState { diff --git a/WaiterRobot/Ui/Login/RegisterScreen.swift b/WaiterRobot/Ui/Login/RegisterScreen.swift index 7c76ca7..c4e475c 100644 --- a/WaiterRobot/Ui/Login/RegisterScreen.swift +++ b/WaiterRobot/Ui/Login/RegisterScreen.swift @@ -8,7 +8,8 @@ struct RegisterScreen: View { @StateObject private var viewModel = ObservableRegisterViewModel() @State private var name: String = "" - let createToken: String + + let deepLink: DeepLink.AuthRegisterLink var body: some View { switch viewModel.state.viewState { @@ -50,7 +51,10 @@ struct RegisterScreen: View { Spacer() Button { - viewModel.actual.onRegister(name: name, createToken: createToken) + viewModel.actual.onRegister( + name: name, + registerLink: deepLink + ) } label: { Text(localize.register.login()) } @@ -66,5 +70,5 @@ struct RegisterScreen: View { } #Preview { - RegisterScreen(createToken: "") + RegisterScreen(deepLink: DeepLink.AuthRegisterLink(token: "", apiBase: "")) } diff --git a/WaiterRobot/Ui/Order/OrderListItem.swift b/WaiterRobot/Ui/Order/OrderListItem.swift index 8eba357..2dfc433 100644 --- a/WaiterRobot/Ui/Order/OrderListItem.swift +++ b/WaiterRobot/Ui/Order/OrderListItem.swift @@ -17,64 +17,47 @@ struct OrderListItem: View { Button { addOne() } label: { - VStack(alignment: .leading) { - HStack { - Text("\(amount) x") - Spacer() - Text(name) + HStack { + VStack { + HStack { + Text("\(amount)x") + .font(.title3) + .frame(width: 40, alignment: .leading) + .multilineTextAlignment(.leading) + + Text(name) + .font(.title3) + .multilineTextAlignment(.leading) + + Spacer() + } + + if let note { + Text(note) + .foregroundColor(.secondary) + .font(.caption) + .frame(maxWidth: .infinity, alignment: .leading) + } + } + HStack { Button { editNote = true editedNote = note ?? "" } label: { Image(systemName: "pencil") - .imageScale(.large) - }.foregroundColor(.accentColor) - } - - if let note { - Text(note) - .foregroundColor(.secondary) - .font(.caption) + .padding(10) + } + .buttonStyle(.wrBorderedProminent) } + .padding(.vertical, 2) + .frame(maxHeight: .infinity, alignment: .center) } + .foregroundColor(.blackWhite) } - // TODO: make only half screen when ios 15 is dropped .sheet(isPresented: $editNote) { - NavigationView { - VStack { - TextEditorWithPlaceholder( - text: $editedNote, - lable: localize.order.addNoteDialog.inputLabel(), - placeHolder: localize.order.addNoteDialog.inputPlaceholder(), - editorHeight: 110, - maxLength: 120 - ) - - HStack { - Button(localize.dialog.cancel(), role: .cancel) { - editNote = false - } - Spacer() - Button(localize.dialog.clear()) { - onSaveNote(nil) - editNote = false - } - Spacer() - Button(localize.dialog.save()) { - onSaveNote(editedNote) - editNote = false - } - } - .padding(.top) - - Spacer() - } - .padding() - .navigationTitle(localize.order.addNoteDialog.title(value0: name)) - .navigationBarTitleDisplayMode(.inline) - } + orderProductNote() } .swipeActions(edge: .trailing, allowsFullSwipe: true, content: { HStack { @@ -104,27 +87,33 @@ struct OrderListItem: View { .tint(.green) }) } + + private func orderProductNote() -> some View { + OrderProductNoteView(name: name, noteText: $editedNote, onSaveNote: onSaveNote) + } } #Preview { - List { - OrderListItem( - name: "Beer", - amount: 5, - note: nil, - addOne: {}, - removeOne: {}, - removeAll: {}, - onSaveNote: { _ in } - ) - OrderListItem( - name: "Wine", - amount: 20, - note: "1x without Tomatoe", - addOne: {}, - removeOne: {}, - removeAll: {}, - onSaveNote: { _ in } - ) + PreviewView { + List { + OrderListItem( + name: "Beer", + amount: 5, + note: nil, + addOne: {}, + removeOne: {}, + removeAll: {}, + onSaveNote: { _ in } + ) + OrderListItem( + name: "Wine", + amount: 20, + note: "1x without Tomato", + addOne: {}, + removeOne: {}, + removeAll: {}, + onSaveNote: { _ in } + ) + } } } diff --git a/WaiterRobot/Ui/Order/OrderProductNoteView.swift b/WaiterRobot/Ui/Order/OrderProductNoteView.swift new file mode 100644 index 0000000..e5a5429 --- /dev/null +++ b/WaiterRobot/Ui/Order/OrderProductNoteView.swift @@ -0,0 +1,116 @@ +import shared +import SwiftUI + +struct OrderProductNoteView: View { + let name: String + @Binding var noteText: String + let onSaveNote: (String?) -> Void + + private let maxLength = 150 + + @Environment(\.dismiss) + private var dismiss + + @FocusState + private var focused: Bool + + var body: some View { + NavigationView { + content() + .navigationTitle(localize.order.addNoteDialog.title(value0: name)) + .navigationBarTitleDisplayMode(.inline) + } + } + + @ViewBuilder + private func content() -> some View { + VStack { + Text(localize.order.addNoteDialog.inputLabel()) + + Group { + if #available(iOS 16, *) { + TextField(localize.order.addNoteDialog.inputPlaceholder(), text: $noteText, axis: .vertical) + .lineLimit(5, reservesSpace: true) + .toolbarBackground(.visible, for: .bottomBar) + } else { + // TODO: Maybe change to TextEditor + TextField(localize.order.addNoteDialog.inputPlaceholder(), text: $noteText) + .lineLimit(5) + } + } + .textFieldStyle(.roundedBorder) + .focused($focused) + .toolbar { + ToolbarItemGroup(placement: .keyboard) { + toolbarButtons() + } + + ToolbarItemGroup(placement: .bottomBar) { + toolbarButtons() + } + } + + Text("\(noteText.count)/\(maxLength)") + .font(.caption) + + Spacer() + } + .padding() + .onChange(of: noteText) { _ in + if noteText.count > maxLength { + noteText = String(noteText.prefix(maxLength)) + } + } + .task { + focused = true + } + } + + @ViewBuilder + private func toolbarButtons() -> some View { + cancelButton() + + Spacer() + + clearButton() + + Spacer() + + saveButton() + } + + @ViewBuilder + private func cancelButton() -> some View { + Button(localize.dialog.cancel(), role: .cancel) { + dismiss() + } + } + + @ViewBuilder + private func clearButton() -> some View { + Button(localize.dialog.clear(), role: .destructive) { + noteText = "" + onSaveNote(nil) + dismiss() + } + .foregroundStyle(.red) + } + + @ViewBuilder + private func saveButton() -> some View { + Button(localize.dialog.save()) { + onSaveNote(noteText) + dismiss() + } + } +} + +#Preview { + EmptyView() + .sheet(isPresented: .constant(true)) { + PreviewView { + OrderProductNoteView(name: "Beer", noteText: .constant("Test")) { _ in + } + } + } +} diff --git a/WaiterRobot/Ui/Root/RootScreen.swift b/WaiterRobot/Ui/Root/RootScreen.swift deleted file mode 100644 index ae59f4a..0000000 --- a/WaiterRobot/Ui/Root/RootScreen.swift +++ /dev/null @@ -1,20 +0,0 @@ -import Foundation -import shared -import SwiftUI - -struct RootScreen: View { - @ObservedObject var viewModel: ObservableRootViewModel - - var body: some View { - if !viewModel.state.isLoggedIn { - LoginScreen() - .navigationBarHidden(true) - } else if !viewModel.state.hasEventSelected { - SwitchEventScreen() - .navigationBarHidden(false) - } else { - TableListScreen() - .navigationBarHidden(false) - } - } -} diff --git a/WaiterRobot/Ui/SwitchEvent/Event.swift b/WaiterRobot/Ui/SwitchEvent/Event.swift index aa2797f..3466694 100644 --- a/WaiterRobot/Ui/SwitchEvent/Event.swift +++ b/WaiterRobot/Ui/SwitchEvent/Event.swift @@ -15,7 +15,7 @@ struct Event: View { Spacer() - if let date = event.date { + if let date = event.startDate { Text(date.description()) .font(.caption) .foregroundColor(Color.gray) @@ -26,5 +26,15 @@ struct Event: View { } #Preview { - Event(event: shared.Event(id: 1, name: "My Event", date: Kotlinx_datetimeLocalDate(year: 2022, monthNumber: 12, dayOfMonth: 24), city: "Graz", organisationId: 1)) + Event( + event: shared.Event( + id: 1, + name: "My Event", + startDate: nil, + endDate: nil, + city: "Graz", + organisationId: 1, + stripeSettings: shared.Event.StripeSettingsDisabled() + ) + ) } diff --git a/WaiterRobot/Ui/TableDetail/OrderedItemView.swift b/WaiterRobot/Ui/TableDetail/OrderedItemView.swift index 9682a40..669fc37 100644 --- a/WaiterRobot/Ui/TableDetail/OrderedItemView.swift +++ b/WaiterRobot/Ui/TableDetail/OrderedItemView.swift @@ -21,6 +21,14 @@ struct OrderedItemView: View { #Preview { List { - OrderedItemView(item: OrderedItem(id: 1, name: "Test", amount: 2), tabbed: {}) + OrderedItemView( + item: OrderedItem( + baseProductId: 1, + name: "Test", + amount: 1, + virtualId: 2 + ), + tabbed: {} + ) } } diff --git a/WaiterRobot/Ui/TableDetail/TableDetailScreen.swift b/WaiterRobot/Ui/TableDetail/TableDetailScreen.swift index 9e9e6ec..ee91175 100644 --- a/WaiterRobot/Ui/TableDetail/TableDetailScreen.swift +++ b/WaiterRobot/Ui/TableDetail/TableDetailScreen.swift @@ -37,6 +37,12 @@ struct TableDetailScreen: View { } } } + .onAppear { + viewModel.activate() + } + .onDisappear { + viewModel.deactivate() + } } private func tableDetails(orderedItems: [OrderedItem]) -> some View { @@ -53,9 +59,9 @@ struct TableDetailScreen: View { Spacer() } else { List { - ForEach(orderedItems, id: \.id) { item in + ForEach(orderedItems, id: \.virtualId) { item in OrderedItemView(item: item) { - viewModel.actual.openOrderScreen(initialItemId: item.id.toKotlinLong()) + viewModel.actual.openOrderScreen(initialItemId: item.baseProductId.toKotlinLong()) } } } diff --git a/WaiterRobot/Ui/TableList/TableListScreen.swift b/WaiterRobot/Ui/TableList/TableListScreen.swift index 3cb7613..185b460 100644 --- a/WaiterRobot/Ui/TableList/TableListScreen.swift +++ b/WaiterRobot/Ui/TableList/TableListScreen.swift @@ -12,20 +12,42 @@ struct TableListScreen: View { ] var body: some View { - content() - .navigationTitle(CommonApp.shared.settings.eventName) - .navigationBarTitleDisplayMode(.inline) - .toolbar { - ToolbarItem(placement: .navigationBarTrailing) { - Button { - viewModel.actual.openSettings() - } label: { - Image(systemName: "gear") + VStack { + if #available(iOS 16.0, *) { + content() + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + Button { + viewModel.actual.openSettings() + } label: { + Image(systemName: "gear") + } + } + } + .toolbarBackground(.hidden, for: .navigationBar) + } else { + content() + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + Button { + viewModel.actual.openSettings() + } label: { + Image(systemName: "gear") + } + } } - } } - .handleSideEffects(of: viewModel, navigator) - .animation(.spring, value: viewModel.state.tableGroupsArray) + } + .navigationTitle(CommonApp.shared.settings.eventName) + .navigationBarTitleDisplayMode(.inline) + .handleSideEffects(of: viewModel, navigator) + .animation(.spring, value: viewModel.state.tableGroupsArray) + .onAppear { + viewModel.activate() + } + .onDisappear { + viewModel.deactivate() + } } private func content() -> some View { @@ -74,17 +96,21 @@ struct TableListScreen: View { onSelectAll: { viewModel.actual.showAll() }, onUnselectAll: { viewModel.actual.hideAll() } ) - - Divider() } .background(Color(UIColor.systemBackground)) } + Divider() + if tableGroups.isEmpty { + Spacer() + Text(localize.tableList.noTableFound()) .multilineTextAlignment(.center) .frame(maxWidth: .infinity) .padding() + + Spacer() } else { ScrollView { LazyVGrid(columns: layout) { @@ -106,6 +132,8 @@ struct TableListScreen: View { #Preview { PreviewView { - TableListScreen() + NavigationView { + TableListScreen() + } } } diff --git a/WaiterRobot/WaiterRobotApp.swift b/WaiterRobot/WaiterRobotApp.swift index 5a7fc94..a695966 100644 --- a/WaiterRobot/WaiterRobotApp.swift +++ b/WaiterRobot/WaiterRobotApp.swift @@ -1,6 +1,5 @@ import shared import SwiftUI -import UIPilot @main struct WaiterRobotApp: App { @@ -24,7 +23,8 @@ struct WaiterRobotApp: App { appBuild: Int32(readFromInfoPlist(withKey: "CFBundleVersion"))!, phoneModel: UIDevice.current.model, os: OS.Ios(version: UIDevice.current.systemVersion), - apiBaseUrl: readFromInfoPlist(withKey: "API_BASE") + allowedHostsCsv: readFromInfoPlist(withKey: "ALLOWED_HOSTS"), + stripeProvider: nil ) KoinKt.doInitKoinIos() diff --git a/project.yml b/project.yml index 05b5018..2f8d477 100644 --- a/project.yml +++ b/project.yml @@ -15,7 +15,7 @@ fileGroups: packages: shared: url: https://github.com/DatepollSystems/WaiterRobot-Shared-Android.git - version: 1.4.1 + minorVersion: 1.5.0 UIPilot: url: https://github.com/canopas/UIPilot.git from: 1.3.1 @@ -51,8 +51,8 @@ targetTemplates: # Only used for prod releases. Lava uses epochMinute (same as on Android) CFBundleVersion: "20007" - API_BASE: "${apiBase}" VERSION_SUFFIX: "${versionSuffix}" + ALLOWED_HOSTS: "${allowedHosts}" CFBundleDevelopmentRegion: "$(DEVELOPMENT_LANGUAGE)" CFBundleDisplayName: "${displayName}" @@ -116,9 +116,9 @@ targets: identifier: "org.datepollsystems.waiterrobot" displayName: "kellner.team" deeplinkDomain: "my.kellner.team" - apiBase: "https://my.kellner.team/api" versionSuffix: "" - + allowedHosts: "my.kellner.team" + WaiterRobotLava: templates: - WaiterRobot @@ -130,8 +130,8 @@ targets: identifier: "org.datepollsystems.waiterrobot.beta" displayName: "lava.kellner.team" deeplinkDomain: "lava.kellner.team" - apiBase: "https://lava.kellner.team/api" versionSuffix: "lava" + allowedHosts: "*" preBuildScripts: - name: Set BuildNumber to epochMinute basedOnDependencyAnalysis: false # run for each build