From 2558baf357fe42f2cd05fcfacaf44f1dff0a01fd Mon Sep 17 00:00:00 2001 From: Alexander Kauer Date: Wed, 10 Jul 2024 16:40:39 +0200 Subject: [PATCH 1/5] Update shared, fixed strings --- WaiterRobot/Ui/Billing/BillingScreen.swift | 4 ++-- WaiterRobot/Ui/Order/OrderScreen.swift | 2 +- WaiterRobot/Ui/TableDetail/TableDetailScreen.swift | 4 ++-- project.yml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/WaiterRobot/Ui/Billing/BillingScreen.swift b/WaiterRobot/Ui/Billing/BillingScreen.swift index ed9f6aa..8f76331 100644 --- a/WaiterRobot/Ui/Billing/BillingScreen.swift +++ b/WaiterRobot/Ui/Billing/BillingScreen.swift @@ -21,7 +21,7 @@ struct BillingScreen: View { let billItems = Array(viewModel.state.billItemsArray) content(billItems: billItems) - .navigationTitle(localize.billing.title(value0: table.number.description, value1: table.groupName)) + .navigationTitle(localize.billing.title(value0: table.groupName, value1: table.number.description)) .navigationBarTitleDisplayMode(.inline) .customBackNavigation( title: localize.dialog.cancel(), @@ -76,7 +76,7 @@ struct BillingScreen: View { VStack { List { if billItems.isEmpty { - Text(localize.billing.noOpenBill(value0: table.number.description, value1: table.groupName)) + Text(localize.billing.noOpenBill(value0: table.groupName, value1: table.number.description)) .multilineTextAlignment(.center) .frame(maxWidth: .infinity) .padding() diff --git a/WaiterRobot/Ui/Order/OrderScreen.swift b/WaiterRobot/Ui/Order/OrderScreen.swift index 207b321..4fce01c 100644 --- a/WaiterRobot/Ui/Order/OrderScreen.swift +++ b/WaiterRobot/Ui/Order/OrderScreen.swift @@ -38,7 +38,7 @@ struct OrderScreen: View { currentOder(resource.data) } } - .navigationTitle(localize.order.title(value0: table.number.description, value1: table.groupName)) + .navigationTitle(localize.order.title(value0: table.groupName, value1: table.number.description)) .navigationBarTitleDisplayMode(.large) .navigationBarBackButtonHidden() .confirmationDialog( diff --git a/WaiterRobot/Ui/TableDetail/TableDetailScreen.swift b/WaiterRobot/Ui/TableDetail/TableDetailScreen.swift index 67ef363..5a34d1c 100644 --- a/WaiterRobot/Ui/TableDetail/TableDetailScreen.swift +++ b/WaiterRobot/Ui/TableDetail/TableDetailScreen.swift @@ -17,7 +17,7 @@ struct TableDetailScreen: View { var body: some View { content() - .navigationTitle(localize.tableDetail.title(value0: table.number.description, value1: table.groupName)) + .navigationTitle(localize.tableDetail.title(value0: table.groupName, value1: table.number.description)) .withViewModel(viewModel, navigator) } @@ -45,7 +45,7 @@ struct TableDetailScreen: View { if orderedItems.isEmpty { Spacer() - Text(localize.tableDetail.noOrder(value0: table.number.description, value1: table.groupName)) + Text(localize.tableDetail.noOrder(value0: table.groupName, value1: table.number.description)) .multilineTextAlignment(.center) .frame(maxWidth: .infinity) .padding() diff --git a/project.yml b/project.yml index 15412e4..02ce428 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.1 + version: 1.6.4 UIPilot: url: https://github.com/canopas/UIPilot.git from: 1.3.1 From 11a58a1f56310a76fbe1888a8f1faf7c5550d7ed Mon Sep 17 00:00:00 2001 From: Alexander Kauer Date: Wed, 10 Jul 2024 17:39:36 +0200 Subject: [PATCH 2/5] Dynamic Table coloring --- .../darkRed.colorset/Contents.json | 20 ++++ .../lightRed.colorset/Contents.json | 20 ++++ .../Ui/Order/Search/ProductSearchAllTab.swift | 1 + .../Ui/TableList/TableGroupSection.swift | 25 +++-- WaiterRobot/Ui/TableList/TableView.swift | 26 +++-- WaiterRobot/Util/Extensions/Color.swift | 94 +++++++++++++++++++ 6 files changed, 171 insertions(+), 15 deletions(-) create mode 100644 WaiterRobot/Resources/Colors.xcassets/darkRed.colorset/Contents.json create mode 100644 WaiterRobot/Resources/Colors.xcassets/lightRed.colorset/Contents.json create mode 100644 WaiterRobot/Util/Extensions/Color.swift diff --git a/WaiterRobot/Resources/Colors.xcassets/darkRed.colorset/Contents.json b/WaiterRobot/Resources/Colors.xcassets/darkRed.colorset/Contents.json new file mode 100644 index 0000000..84c863d --- /dev/null +++ b/WaiterRobot/Resources/Colors.xcassets/darkRed.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "display-p3", + "components" : { + "alpha" : "1.000", + "blue" : "0x00", + "green" : "0x22", + "red" : "0x9B" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/WaiterRobot/Resources/Colors.xcassets/lightRed.colorset/Contents.json b/WaiterRobot/Resources/Colors.xcassets/lightRed.colorset/Contents.json new file mode 100644 index 0000000..78e1984 --- /dev/null +++ b/WaiterRobot/Resources/Colors.xcassets/lightRed.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "display-p3", + "components" : { + "alpha" : "1.000", + "blue" : "0x3C", + "green" : "0x34", + "red" : "0xFF" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/WaiterRobot/Ui/Order/Search/ProductSearchAllTab.swift b/WaiterRobot/Ui/Order/Search/ProductSearchAllTab.swift index bee40a6..3dad81d 100644 --- a/WaiterRobot/Ui/Order/Search/ProductSearchAllTab.swift +++ b/WaiterRobot/Ui/Order/Search/ProductSearchAllTab.swift @@ -38,6 +38,7 @@ struct ProductSearchAllTab: View { id: 1, name: "Test Group 1", position: 1, + color: "", products: [ Product( id: 1, diff --git a/WaiterRobot/Ui/TableList/TableGroupSection.swift b/WaiterRobot/Ui/TableList/TableGroupSection.swift index 3cfe524..68c8f9b 100644 --- a/WaiterRobot/Ui/TableList/TableGroupSection.swift +++ b/WaiterRobot/Ui/TableList/TableGroupSection.swift @@ -11,6 +11,7 @@ struct TableGroupSection: View { TableView( text: table.number.description, hasOrders: table.hasOrders, + backgroundColor: Color(hex: tableGroup.color), onClick: { onTableClick(table) } @@ -19,19 +20,27 @@ struct TableGroupSection: View { } } header: { HStack { - Text(tableGroup.name) - .font(.title2) - .foregroundStyle(.white) - .padding(6) - .background { - RoundedRectangle(cornerRadius: 8.0) - .foregroundStyle(Color(.main)) - } + if let background = Color(hex: tableGroup.color) { + title(backgroundColor: background) + } else { + title(backgroundColor: Color(.main)) + } Spacer() } } } + + private func title(backgroundColor: Color) -> some View { + Text(tableGroup.name) + .font(.title2) + .foregroundStyle(backgroundColor.getContentColor(lightColorScheme: .black, darkColorScheme: .white)) + .padding(6) + .background { + RoundedRectangle(cornerRadius: 8.0) + .foregroundStyle(backgroundColor) + } + } } #Preview { diff --git a/WaiterRobot/Ui/TableList/TableView.swift b/WaiterRobot/Ui/TableList/TableView.swift index 7dfaf29..cc5d227 100644 --- a/WaiterRobot/Ui/TableList/TableView.swift +++ b/WaiterRobot/Ui/TableList/TableView.swift @@ -3,16 +3,18 @@ import SwiftUI struct TableView: View { let text: String let hasOrders: Bool + let backgroundColor: Color? let onClick: () -> Void + @Environment(\.colorScheme) + var colorScheme + var body: some View { Button(action: onClick) { ZStack { - RoundedRectangle(cornerRadius: 20) - .stroke(.blackWhite, lineWidth: 5) - Text(text) .font(.title) + .frame(maxWidth: .infinity, maxHeight: .infinity) if hasOrders { VStack(alignment: .trailing) { @@ -20,9 +22,10 @@ struct TableView: View { Spacer() Circle() - .foregroundColor(.accentColor) + .foregroundColor(backgroundColor?.getContentColor(lightColorScheme: Color(.darkRed), darkColorScheme: Color(.lightRed))) .frame(width: 12) } + Spacer() } .padding(.top, 10) @@ -31,16 +34,25 @@ struct TableView: View { } } .aspectRatio(1.0, contentMode: .fit) - .foregroundColor(.blackWhite) + .background { + if let backgroundColor { + RoundedRectangle(cornerRadius: 20) + .foregroundColor(backgroundColor) + } else { + RoundedRectangle(cornerRadius: 20) + .foregroundColor(.gray.opacity(0.3)) + } + } + .foregroundStyle(backgroundColor?.getContentColor(lightColorScheme: .black, darkColorScheme: .white) ?? .blackWhite) } } #Preview { VStack { - TableView(text: "1", hasOrders: false, onClick: {}) + TableView(text: "1", hasOrders: false, backgroundColor: .green) {} .frame(maxWidth: 100) - TableView(text: "2", hasOrders: true, onClick: {}) + TableView(text: "2", hasOrders: true, backgroundColor: nil) {} .frame(maxWidth: 100) } } diff --git a/WaiterRobot/Util/Extensions/Color.swift b/WaiterRobot/Util/Extensions/Color.swift new file mode 100644 index 0000000..1da311a --- /dev/null +++ b/WaiterRobot/Util/Extensions/Color.swift @@ -0,0 +1,94 @@ +// +// Color.swift +// WaiterRobot +// +// Created by Alexander Kauer on 10.07.24. +// + +import SwiftUI + +extension Color { + init(hex: String) { + let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted) + var int: UInt64 = 0 + Scanner(string: hex).scanHexInt64(&int) + let a, r, g, b: UInt64 + switch hex.count { + case 3: // RGB (12-bit) + (a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17) + case 6: // RGB (24-bit) + (a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF) + case 8: // ARGB (32-bit) + (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF) + default: + (a, r, g, b) = (255, 0, 0, 0) + } + + self.init( + .sRGB, + red: Double(r) / 255, + green: Double(g) / 255, + blue: Double(b) / 255, + opacity: Double(a) / 255 + ) + } + + init?(hex: String?) { + guard + let hex = hex?.trimmingCharacters(in: CharacterSet.alphanumerics.inverted) + else { + return nil + } + + var int: UInt64 = 0 + Scanner(string: hex).scanHexInt64(&int) + let a, r, g, b: UInt64 + switch hex.count { + case 3: // RGB (12-bit) + (a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17) + case 6: // RGB (24-bit) + (a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF) + case 8: // ARGB (32-bit) + (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF) + default: + (a, r, g, b) = (255, 0, 0, 0) + } + + self.init( + .sRGB, + red: Double(r) / 255, + green: Double(g) / 255, + blue: Double(b) / 255, + opacity: Double(a) / 255 + ) + } + + // Adjust color based on contrast + func getContentColor(lightColorScheme: Color, darkColorScheme: Color) -> Color { + let lightContrast = contrastRatio(with: lightColorScheme) + let darkContrast = contrastRatio(with: darkColorScheme) + + return lightContrast > darkContrast ? lightColorScheme : darkColorScheme + } + + // Calculate contrast ratio + private func contrastRatio(with other: Color) -> Double { + let l1 = luminance() + let l2 = other.luminance() + return (max(l1, l2) + 0.05) / (min(l1, l2) + 0.05) + } + + // Calculate luminance + private func luminance() -> Double { + let components = cgColor?.components ?? [0, 0, 0, 1] + let red = Color.convertSRGBToLinear(components[0]) + let green = Color.convertSRGBToLinear(components[1]) + let blue = Color.convertSRGBToLinear(components[2]) + + return 0.2126 * red + 0.7152 * green + 0.0722 * blue + } + + private static func convertSRGBToLinear(_ component: CGFloat) -> Double { + component <= 0.03928 ? Double(component) / 12.92 : pow((Double(component) + 0.055) / 1.055, 2.4) + } +} From 0a87d1de2af99432c1fd6619270fe18cd384e7f2 Mon Sep 17 00:00:00 2001 From: Alexander Kauer Date: Wed, 10 Jul 2024 18:20:21 +0200 Subject: [PATCH 3/5] fixed euro sign --- WaiterRobot/Ui/Billing/BillingScreen.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/WaiterRobot/Ui/Billing/BillingScreen.swift b/WaiterRobot/Ui/Billing/BillingScreen.swift index 8f76331..f111dfc 100644 --- a/WaiterRobot/Ui/Billing/BillingScreen.swift +++ b/WaiterRobot/Ui/Billing/BillingScreen.swift @@ -124,6 +124,7 @@ struct BillingScreen: View { .font(.system(.title)) .padding() .tint(.white) + .offset(x: -) } .background(.blue) .mask(Circle()) From dba88b2fc428d08040ef6c132abadbe3cbb9fc43 Mon Sep 17 00:00:00 2001 From: Alexander Kauer Date: Wed, 10 Jul 2024 18:28:08 +0200 Subject: [PATCH 4/5] fixed euro sign --- WaiterRobot/Ui/Billing/BillingScreen.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WaiterRobot/Ui/Billing/BillingScreen.swift b/WaiterRobot/Ui/Billing/BillingScreen.swift index f111dfc..2e29e06 100644 --- a/WaiterRobot/Ui/Billing/BillingScreen.swift +++ b/WaiterRobot/Ui/Billing/BillingScreen.swift @@ -124,7 +124,7 @@ struct BillingScreen: View { .font(.system(.title)) .padding() .tint(.white) - .offset(x: -) + .offset(x: -3) } .background(.blue) .mask(Circle()) From 4ceaa1976ca5136f884e258fe3b2f3e4240c51f6 Mon Sep 17 00:00:00 2001 From: Alexander Kauer Date: Wed, 10 Jul 2024 18:35:46 +0200 Subject: [PATCH 5/5] Finished product group coloring --- WaiterRobot/Ui/Billing/BillingScreen.swift | 1 - WaiterRobot/Ui/Order/ProductListItem.swift | 85 ++++++++++++++----- .../Ui/Order/Search/ProductSearch.swift | 1 + .../Ui/Order/Search/ProductSearchAllTab.swift | 1 + .../Order/Search/ProductSearchGroupList.swift | 4 +- 5 files changed, 69 insertions(+), 23 deletions(-) diff --git a/WaiterRobot/Ui/Billing/BillingScreen.swift b/WaiterRobot/Ui/Billing/BillingScreen.swift index 2e29e06..a557996 100644 --- a/WaiterRobot/Ui/Billing/BillingScreen.swift +++ b/WaiterRobot/Ui/Billing/BillingScreen.swift @@ -63,7 +63,6 @@ struct BillingScreen: View { } } } - // TODO: make only half screen when ios 15 is dropped .sheet(isPresented: $showPayDialog) { PayDialog(viewModel: viewModel) diff --git a/WaiterRobot/Ui/Order/ProductListItem.swift b/WaiterRobot/Ui/Order/ProductListItem.swift index 3257b3b..cc09c38 100644 --- a/WaiterRobot/Ui/Order/ProductListItem.swift +++ b/WaiterRobot/Ui/Order/ProductListItem.swift @@ -3,12 +3,18 @@ import SwiftUI struct ProductListItem: View { let product: Product + let backgroundColor: Color? let onClick: () -> Void private let allergens: String - init(product: Product, onClick: @escaping () -> Void) { + init( + product: Product, + backgroundColor: Color?, + onClick: @escaping () -> Void + ) { self.product = product + self.backgroundColor = backgroundColor self.onClick = onClick var allergens = "" @@ -23,29 +29,42 @@ struct ProductListItem: View { } } - var body: some View { - ZStack { - RoundedRectangle(cornerRadius: 10, style: .continuous) - .fill(product.soldOut ? Color.gray.opacity(0.1) : Color(.systemBackground)) - .shadow(radius: 2) + var foregroundColor: Color { + if product.soldOut { + return .blackWhite + } + + if let backgroundColor { + return backgroundColor.getContentColor(lightColorScheme: .black, darkColorScheme: .white) + } else { + return Color.blackWhite + } + } - Button { - onClick() - } label: { - VStack { - Text(product.name) - .strikethrough(product.soldOut) - if !product.allergens.isEmpty { - Text(allergens) - .foregroundColor(.gray) - } - Text(product.price.description()) + var body: some View { + Button { + onClick() + } label: { + VStack { + Text(product.name) + .strikethrough(product.soldOut) + .foregroundStyle(foregroundColor) + if !product.allergens.isEmpty { + Text(allergens) + .foregroundStyle(foregroundColor) + .opacity(0.6) } - .foregroundColor(.blackWhite) - .frame(maxWidth: .infinity) - .padding(5) + Text(product.price.description()) } - .disabled(product.soldOut) + .foregroundStyle(foregroundColor) + .frame(maxWidth: .infinity) + .padding(5) + } + .disabled(product.soldOut) + .background { + RoundedRectangle(cornerRadius: 10, style: .continuous) + .fill(product.soldOut ? Color.gray.opacity(0.1) : backgroundColor ?? Color(.systemBackground)) + .shadow(radius: 2) } } } @@ -66,6 +85,30 @@ struct ProductListItem: View { ], position: 1 ), + backgroundColor: .yellow, + onClick: {} + ) + .frame(maxWidth: 100, maxHeight: 100) +} + +#Preview { + ProductListItem( + product: Product( + id: 2, + name: "Wine", + price: Money(cents: 290), + soldOut: false, + allergens: [ + Allergen(id: 1, name: "Egg", shortName: "E"), + Allergen(id: 2, name: "Egg2", shortName: "A"), + Allergen(id: 3, name: "Egg3", shortName: "B"), + Allergen(id: 4, name: "Egg4", shortName: "C"), + Allergen(id: 5, name: "Egg5", shortName: "D"), + ], + position: 1 + ), + backgroundColor: .yellow, onClick: {} ) + .frame(maxWidth: 100, maxHeight: 100) } diff --git a/WaiterRobot/Ui/Order/Search/ProductSearch.swift b/WaiterRobot/Ui/Order/Search/ProductSearch.swift index 0a6b1b0..a0d0167 100644 --- a/WaiterRobot/Ui/Order/Search/ProductSearch.swift +++ b/WaiterRobot/Ui/Order/Search/ProductSearch.swift @@ -60,6 +60,7 @@ struct ProductSearch: View { LazyVGrid(columns: layout, spacing: 0) { ProductSearchGroupList( products: groupWithProducts.products, + backgroundColor: Color(hex: groupWithProducts.color), onProductClick: { viewModel.actual.addItem(product: $0, amount: 1) dismiss() diff --git a/WaiterRobot/Ui/Order/Search/ProductSearchAllTab.swift b/WaiterRobot/Ui/Order/Search/ProductSearchAllTab.swift index 3dad81d..64b192b 100644 --- a/WaiterRobot/Ui/Order/Search/ProductSearchAllTab.swift +++ b/WaiterRobot/Ui/Order/Search/ProductSearchAllTab.swift @@ -14,6 +14,7 @@ struct ProductSearchAllTab: View { Section { ProductSearchGroupList( products: productGroup.products, + backgroundColor: Color(hex: productGroup.color), onProductClick: onProductClick ) } header: { diff --git a/WaiterRobot/Ui/Order/Search/ProductSearchGroupList.swift b/WaiterRobot/Ui/Order/Search/ProductSearchGroupList.swift index 4478faa..c6f9835 100644 --- a/WaiterRobot/Ui/Order/Search/ProductSearchGroupList.swift +++ b/WaiterRobot/Ui/Order/Search/ProductSearchGroupList.swift @@ -3,11 +3,12 @@ import SwiftUI struct ProductSearchGroupList: View { let products: [Product] + let backgroundColor: Color? let onProductClick: (Product) -> Void var body: some View { ForEach(products, id: \.id) { product in - ProductListItem(product: product) { + ProductListItem(product: product, backgroundColor: backgroundColor) { onProductClick(product) } .foregroundColor(.blackWhite) @@ -29,6 +30,7 @@ struct ProductSearchGroupList: View { position: 1 ), ], + backgroundColor: .yellow, onProductClick: { _ in } ) }