diff --git a/Surcharges/Applications/Project.swift b/Surcharges/Applications/Project.swift index 585067b..2c3c1d9 100644 --- a/Surcharges/Applications/Project.swift +++ b/Surcharges/Applications/Project.swift @@ -73,25 +73,28 @@ let infoPlist: InfoPlist = .extendingDefault( "NSLocationWhenInUseUsageDescription": .string("Surcharges uses your location to provide nearest places to you."), "NSCameraUsageDescription": .string("Surcharges uses your camera to take your receipt."), "ITSAppUsesNonExemptEncryption": .boolean(false), + "UISupportedInterfaceOrientations": .array([.string("UIInterfaceOrientationPortrait")]) ] ) // MARK: Target - Prod let surcharges = Target.target( name: "Surcharges", - destinations: [.iPhone, .iPad, .mac], + destinations: [.iPhone], product: .app, bundleId: "nz.surcharges", - deploymentTargets: .multiplatform(iOS: "17.0", macOS: "13.0"), + deploymentTargets: .iOS("17.0"), infoPlist: infoPlist, sources: ["Sources/Commons/**", "Sources/Prod/**"], resources: .resources( [ + "Resources/Localise/**", .glob( pattern: .relativeToManifest("Resources/Prod/**"), excluding: [.relativeToManifest("Resources/Prod/.gitkeep")] - ) - ], privacyManifest: .default + ), + ], + privacyManifest: .default ), dependencies: productionDependencies + externalDependencies, settings: Settings.settings( @@ -105,19 +108,21 @@ let surcharges = Target.target( // MARK: Target - Dev let surchargesDev = Target.target( name: "SurchargesDev", - destinations: [.iPhone, .iPad, .mac], + destinations: [.iPhone], product: .app, bundleId: "nz.surcharges.development", - deploymentTargets: .multiplatform(iOS: "17.0", macOS: "13.0"), + deploymentTargets: .iOS("17.0"), infoPlist: infoPlist, sources: ["Sources/Commons/**", "Sources/Dev/**"], resources: .resources( [ + "Resources/Localise/**", .glob( pattern: .relativeToManifest("Resources/Dev/**"), excluding: [.relativeToManifest("Resources/Dev/.gitkeep")] - ) - ], privacyManifest: .default + ), + ], + privacyManifest: .default ), dependencies: developmentDependencies + externalDependencies, settings: Settings.settings( diff --git a/Surcharges/Applications/Resources/Dev/Images.xcassets/AppIcon.appiconset/AppIcon_dark.png b/Surcharges/Applications/Resources/Dev/Images.xcassets/AppIcon.appiconset/AppIcon_dark.png new file mode 100644 index 0000000..8633715 Binary files /dev/null and b/Surcharges/Applications/Resources/Dev/Images.xcassets/AppIcon.appiconset/AppIcon_dark.png differ diff --git a/Surcharges/Applications/Resources/Dev/Images.xcassets/AppIcon.appiconset/AppIcon_light 1.png b/Surcharges/Applications/Resources/Dev/Images.xcassets/AppIcon.appiconset/AppIcon_light 1.png new file mode 100644 index 0000000..c7ce695 Binary files /dev/null and b/Surcharges/Applications/Resources/Dev/Images.xcassets/AppIcon.appiconset/AppIcon_light 1.png differ diff --git a/Surcharges/Applications/Resources/Dev/Images.xcassets/AppIcon.appiconset/AppIcon_light.png b/Surcharges/Applications/Resources/Dev/Images.xcassets/AppIcon.appiconset/AppIcon_light.png new file mode 100644 index 0000000..c7ce695 Binary files /dev/null and b/Surcharges/Applications/Resources/Dev/Images.xcassets/AppIcon.appiconset/AppIcon_light.png differ diff --git a/Surcharges/Applications/Resources/Dev/Images.xcassets/AppIcon.appiconset/Contents.json b/Surcharges/Applications/Resources/Dev/Images.xcassets/AppIcon.appiconset/Contents.json index 7e1fbb3..49099e2 100644 --- a/Surcharges/Applications/Resources/Dev/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/Surcharges/Applications/Resources/Dev/Images.xcassets/AppIcon.appiconset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "appicon.png", + "filename" : "AppIcon_light.png", "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" @@ -13,7 +13,7 @@ "value" : "dark" } ], - "filename" : "appicon 1.png", + "filename" : "AppIcon_dark.png", "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" @@ -25,7 +25,7 @@ "value" : "tinted" } ], - "filename" : "appicon 2.png", + "filename" : "AppIcon_light 1.png", "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" diff --git a/Surcharges/Applications/Resources/Dev/Images.xcassets/AppIcon.appiconset/appicon 1.png b/Surcharges/Applications/Resources/Dev/Images.xcassets/AppIcon.appiconset/appicon 1.png deleted file mode 100644 index 9b3075a..0000000 Binary files a/Surcharges/Applications/Resources/Dev/Images.xcassets/AppIcon.appiconset/appicon 1.png and /dev/null differ diff --git a/Surcharges/Applications/Resources/Dev/Images.xcassets/AppIcon.appiconset/appicon 2.png b/Surcharges/Applications/Resources/Dev/Images.xcassets/AppIcon.appiconset/appicon 2.png deleted file mode 100644 index 9b3075a..0000000 Binary files a/Surcharges/Applications/Resources/Dev/Images.xcassets/AppIcon.appiconset/appicon 2.png and /dev/null differ diff --git a/Surcharges/Applications/Resources/Dev/Images.xcassets/AppIcon.appiconset/appicon.png b/Surcharges/Applications/Resources/Dev/Images.xcassets/AppIcon.appiconset/appicon.png deleted file mode 100644 index 9b3075a..0000000 Binary files a/Surcharges/Applications/Resources/Dev/Images.xcassets/AppIcon.appiconset/appicon.png and /dev/null differ diff --git a/Surcharges/Applications/Resources/Localise/en.lproj/InfoPlist.strings b/Surcharges/Applications/Resources/Localise/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..a77eadb --- /dev/null +++ b/Surcharges/Applications/Resources/Localise/en.lproj/InfoPlist.strings @@ -0,0 +1,10 @@ +/* + InfoPlist.strings + Resources + + Created by Bonsung Koo on 30/01/2025. + Copyright © 2025 Surcharges. All rights reserved. + */ + +"NSCameraUsageDescription"= "Surcharges uses your camera to take your receipt."; +"NSLocationWhenInUseUsageDescription"= "Surcharges uses your location to provide nearest places to you."; diff --git a/Surcharges/Applications/Resources/Localise/en.lproj/Localizable.strings b/Surcharges/Applications/Resources/Localise/en.lproj/Localizable.strings new file mode 100644 index 0000000..fb8e753 --- /dev/null +++ b/Surcharges/Applications/Resources/Localise/en.lproj/Localizable.strings @@ -0,0 +1,9 @@ +/* + Localizable.strings + Resources + + Created by Bonsung Koo on 30/01/2025. + Copyright © 2025 Surcharges. All rights reserved. + */ + +/* This file must be empty. */ diff --git a/Surcharges/Applications/Resources/Localise/ko.lproj/InfoPlist.strings b/Surcharges/Applications/Resources/Localise/ko.lproj/InfoPlist.strings new file mode 100644 index 0000000..51ea40f --- /dev/null +++ b/Surcharges/Applications/Resources/Localise/ko.lproj/InfoPlist.strings @@ -0,0 +1,10 @@ +/* + InfoPlist.strings + Resources + + Created by Bonsung Koo on 30/01/2025. + Copyright © 2025 Surcharges. All rights reserved. + */ + +"NSCameraUsageDescription"= "영수증 사진을 촬영하기 위해 카메라를 사용합니다."; +"NSLocationWhenInUseUsageDescription"= "당신 주변의 상점을 검색하기 위해 위치 정보를 사용합니다."; diff --git a/Surcharges/Applications/Resources/Localise/ko.lproj/Localizable.strings b/Surcharges/Applications/Resources/Localise/ko.lproj/Localizable.strings new file mode 100644 index 0000000..e4dddd4 --- /dev/null +++ b/Surcharges/Applications/Resources/Localise/ko.lproj/Localizable.strings @@ -0,0 +1,9 @@ +/* + Localizable.strings + Resources + + Created by Bonsung Koo on 30/01/2025. + Copyright © 2025 Surcharges. All rights reserved. + */ + +/* This file must be empty. */ diff --git a/Surcharges/Applications/Resources/Prod/Images.xcassets/AppIcon.appiconset/AppIcon_dark.png b/Surcharges/Applications/Resources/Prod/Images.xcassets/AppIcon.appiconset/AppIcon_dark.png new file mode 100644 index 0000000..f1791de Binary files /dev/null and b/Surcharges/Applications/Resources/Prod/Images.xcassets/AppIcon.appiconset/AppIcon_dark.png differ diff --git a/Surcharges/Applications/Resources/Prod/Images.xcassets/AppIcon.appiconset/AppIcon_light 1.png b/Surcharges/Applications/Resources/Prod/Images.xcassets/AppIcon.appiconset/AppIcon_light 1.png new file mode 100644 index 0000000..c793872 Binary files /dev/null and b/Surcharges/Applications/Resources/Prod/Images.xcassets/AppIcon.appiconset/AppIcon_light 1.png differ diff --git a/Surcharges/Applications/Resources/Prod/Images.xcassets/AppIcon.appiconset/AppIcon_light.png b/Surcharges/Applications/Resources/Prod/Images.xcassets/AppIcon.appiconset/AppIcon_light.png new file mode 100644 index 0000000..c793872 Binary files /dev/null and b/Surcharges/Applications/Resources/Prod/Images.xcassets/AppIcon.appiconset/AppIcon_light.png differ diff --git a/Surcharges/Applications/Resources/Prod/Images.xcassets/AppIcon.appiconset/Contents.json b/Surcharges/Applications/Resources/Prod/Images.xcassets/AppIcon.appiconset/Contents.json index 7e1fbb3..49099e2 100644 --- a/Surcharges/Applications/Resources/Prod/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/Surcharges/Applications/Resources/Prod/Images.xcassets/AppIcon.appiconset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "appicon.png", + "filename" : "AppIcon_light.png", "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" @@ -13,7 +13,7 @@ "value" : "dark" } ], - "filename" : "appicon 1.png", + "filename" : "AppIcon_dark.png", "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" @@ -25,7 +25,7 @@ "value" : "tinted" } ], - "filename" : "appicon 2.png", + "filename" : "AppIcon_light 1.png", "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" diff --git a/Surcharges/Applications/Resources/Prod/Images.xcassets/AppIcon.appiconset/appicon 1.png b/Surcharges/Applications/Resources/Prod/Images.xcassets/AppIcon.appiconset/appicon 1.png deleted file mode 100644 index 9b3075a..0000000 Binary files a/Surcharges/Applications/Resources/Prod/Images.xcassets/AppIcon.appiconset/appicon 1.png and /dev/null differ diff --git a/Surcharges/Applications/Resources/Prod/Images.xcassets/AppIcon.appiconset/appicon 2.png b/Surcharges/Applications/Resources/Prod/Images.xcassets/AppIcon.appiconset/appicon 2.png deleted file mode 100644 index 9b3075a..0000000 Binary files a/Surcharges/Applications/Resources/Prod/Images.xcassets/AppIcon.appiconset/appicon 2.png and /dev/null differ diff --git a/Surcharges/Applications/Resources/Prod/Images.xcassets/AppIcon.appiconset/appicon.png b/Surcharges/Applications/Resources/Prod/Images.xcassets/AppIcon.appiconset/appicon.png deleted file mode 100644 index 9b3075a..0000000 Binary files a/Surcharges/Applications/Resources/Prod/Images.xcassets/AppIcon.appiconset/appicon.png and /dev/null differ diff --git a/Surcharges/DataLayer/Networks/Sources/API/Response.swift b/Surcharges/DataLayer/Networks/Sources/API/Response.swift index 11c541e..7df6a49 100644 --- a/Surcharges/DataLayer/Networks/Sources/API/Response.swift +++ b/Surcharges/DataLayer/Networks/Sources/API/Response.swift @@ -71,7 +71,7 @@ extension API { case .badRequest: throw .badRequest case .forbidden: - throw .forbidden + throw .forbidden(reason: .region(availableRegions: ["nz"])) case .unauthorised: throw .unauthorised case .notFound: diff --git a/Surcharges/DataLayer/Networks/Sources/Commons/NetworkError.swift b/Surcharges/DataLayer/Networks/Sources/Commons/NetworkError.swift index 88a145c..8c504a1 100644 --- a/Surcharges/DataLayer/Networks/Sources/Commons/NetworkError.swift +++ b/Surcharges/DataLayer/Networks/Sources/Commons/NetworkError.swift @@ -9,10 +9,22 @@ import Foundation public enum NetworkError: Error { + + public enum ForbiddenReason: Identifiable, Equatable, Sendable { + case region(availableRegions: [String]) + + public var id: String { + switch self { + case .region(let availableRegions): + return availableRegions.joined(separator: "") + } + } + } + case systemError case badRequest case unauthorised - case forbidden + case forbidden(reason: ForbiddenReason) case notFound case methodNotAllowed case deprecatedAPI diff --git a/Surcharges/DataLayer/Repositories/Sources/errorHandler.swift b/Surcharges/DataLayer/Repositories/Sources/errorHandler.swift index b91f32c..8727f5d 100644 --- a/Surcharges/DataLayer/Repositories/Sources/errorHandler.swift +++ b/Surcharges/DataLayer/Repositories/Sources/errorHandler.swift @@ -17,8 +17,11 @@ extension RepositoryProtocol { switch error { case .unauthorised: await appStatusService.notifyAppStatus(.toast(.unauthorised)) - case .forbidden: - await appStatusService.notifyAppStatus(.toast(.outOfNZ)) + case .forbidden(let reason): + switch reason { + case .region(let availableRegions): + await appStatusService.notifyAppStatus(.toast(.outOfRegion(availableRegions: availableRegions))) + } case .systemError: await appStatusService.notifyAppStatus(.toast(.noInternet)) case .deprecatedAPI: diff --git a/Surcharges/PresentationLayer/UIs/Main/Sources/MainView.swift b/Surcharges/PresentationLayer/UIs/Main/Sources/MainView.swift index 22151f9..42638cd 100644 --- a/Surcharges/PresentationLayer/UIs/Main/Sources/MainView.swift +++ b/Surcharges/PresentationLayer/UIs/Main/Sources/MainView.swift @@ -45,6 +45,8 @@ public struct MainView: V .padding([.top], 10) .padding([.leading, .trailing], 20) + /* + // Favourite Places is currently not available. Section { FavouritePlacesView() @@ -55,6 +57,7 @@ public struct MainView: V .font(.title3) .blurBackground() } + */ if _viewModel.isLoading { @@ -90,7 +93,7 @@ public struct MainView: V ) } header: { - Text("Search for \"\(_viewModel.searchedText)\"") + Text(R.string.localizable.searchFor(_viewModel.searchedText)) .font(.title3) .blurBackground() } @@ -138,7 +141,7 @@ public struct MainView: V .buttonStyle(.plain) .contentTransition(.symbolEffect(.replace)) .alert( - "Use Location is Denied", + R.string.localizable.alertUseLocationDeniedTitle(), isPresented: $_showLocationDeniedAlert ) { @@ -147,17 +150,17 @@ public struct MainView: V UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!, options: [:], completionHandler: nil) } label: { - Text("Go to Settings") + Text(R.string.localizable.goToSettings()) } Button(role: .cancel) { _showLocationDeniedAlert.toggle() } label: { - Text("Close") + Text(R.string.localizable.close()) } } message: { - Text("Allow to use your location") + Text(R.string.localizable.alertUseLocationDeniedMessage()) } .popoverTip(_useLocationTip, arrowEdge: .leading) { action in withAnimation { @@ -166,7 +169,7 @@ public struct MainView: V _useLocationTip.invalidate(reason: .actionPerformed) } - TextField("Search your destination", text: $_viewModel.searchText) + TextField(R.string.localizable.searchBoxPlaceholder(), text: $_viewModel.searchText) .textFieldStyle(.roundedBorder) .font(.body) .disabled(_viewModel.isLoading) @@ -188,7 +191,7 @@ public struct MainView: V await _viewModel.search() } } label: { - Text("Search") + Text(R.string.localizable.searchButtonTitle()) .font(.body) .disabled(!_viewModel.canSearch) } diff --git a/Surcharges/PresentationLayer/UIs/Main/Sources/SearchResult/NoResultView.swift b/Surcharges/PresentationLayer/UIs/Main/Sources/SearchResult/NoResultView.swift index ec9ba39..b6f7f46 100644 --- a/Surcharges/PresentationLayer/UIs/Main/Sources/SearchResult/NoResultView.swift +++ b/Surcharges/PresentationLayer/UIs/Main/Sources/SearchResult/NoResultView.swift @@ -30,7 +30,7 @@ struct NoResultView: View { .font(.largeTitle) } - Text("Oops!\nNo results found for \"\(_searchedText)\"") + Text(R.string.localizable.noResultForSearch(_searchedText)) .font(.subheadline) .multilineTextAlignment(.center) diff --git a/Surcharges/PresentationLayer/UIs/Main/Sources/SearchResult/PlaceView.swift b/Surcharges/PresentationLayer/UIs/Main/Sources/SearchResult/PlaceView.swift index 3aaf51c..3dbcc77 100644 --- a/Surcharges/PresentationLayer/UIs/Main/Sources/SearchResult/PlaceView.swift +++ b/Surcharges/PresentationLayer/UIs/Main/Sources/SearchResult/PlaceView.swift @@ -32,11 +32,11 @@ struct PlaceView: View { if let rate = _place.surcharge.rate { if _place.surcharge.status == .reported { - Text(verbatim: rate == 0 ? "Zero🎉" : "\(rate)%") + Text(verbatim: rate == 0 ? "\(R.string.localizable.zeroRates())🎉" : "\(rate)%") .surchargeStatusReported() } else if _place.surcharge.status == .confirmed { - Text(verbatim: rate == 0 ? "Zero🎉" : "\(rate)%") + Text(verbatim: rate == 0 ? "\(R.string.localizable.zeroRates())🎉" : "\(rate)%") .surchargeStatusConfirmed() } } diff --git a/Surcharges/PresentationLayer/UIs/Main/Sources/Tip/SurchargeStatusTip.swift b/Surcharges/PresentationLayer/UIs/Main/Sources/Tip/SurchargeStatusTip.swift index aca5e9b..e4bb13c 100644 --- a/Surcharges/PresentationLayer/UIs/Main/Sources/Tip/SurchargeStatusTip.swift +++ b/Surcharges/PresentationLayer/UIs/Main/Sources/Tip/SurchargeStatusTip.swift @@ -9,15 +9,16 @@ import SwiftUI import TipKit +import Resources import CommonUI struct SurchargeStatusTip: Tip { var title: Text { - Text("Surcharge Status") + Text(R.string.localizable.tipSurchargeStatusTitle()) } var message: Text? { - Text("Get to know different surcharge status") + Text(R.string.localizable.tipSurchargeStatusMessage()) } var image: Image? { diff --git a/Surcharges/PresentationLayer/UIs/Main/Sources/Tip/UseLocationTip.swift b/Surcharges/PresentationLayer/UIs/Main/Sources/Tip/UseLocationTip.swift index 63b14bb..2699529 100644 --- a/Surcharges/PresentationLayer/UIs/Main/Sources/Tip/UseLocationTip.swift +++ b/Surcharges/PresentationLayer/UIs/Main/Sources/Tip/UseLocationTip.swift @@ -9,13 +9,15 @@ import SwiftUI import TipKit +import Resources + struct UseLocationTip: Tip { var title: Text { - Text("Use Your Location") + Text(R.string.localizable.tipUseLocationTitle()) } var message: Text? { - Text("Search for nearby places.") + Text(R.string.localizable.tipUseLocationMessage()) } var image: Image? { diff --git a/Surcharges/PresentationLayer/UIs/Main/Sources/Welcome/WelcomeView.swift b/Surcharges/PresentationLayer/UIs/Main/Sources/Welcome/WelcomeView.swift index 36fb6de..d2ffa43 100644 --- a/Surcharges/PresentationLayer/UIs/Main/Sources/Welcome/WelcomeView.swift +++ b/Surcharges/PresentationLayer/UIs/Main/Sources/Welcome/WelcomeView.swift @@ -8,9 +8,11 @@ import SwiftUI +import Resources + struct WelcomeView: View { var body: some View { - Text("Welcome to the Surcharge App") + Text(R.string.localizable.welcomeToSurchargeApp()) } } diff --git a/Surcharges/PresentationLayer/UIs/PlaceDetail/Sources/Button/ContributeButton.swift b/Surcharges/PresentationLayer/UIs/PlaceDetail/Sources/Button/ContributeButton.swift index e6e14e4..c769dd3 100644 --- a/Surcharges/PresentationLayer/UIs/PlaceDetail/Sources/Button/ContributeButton.swift +++ b/Surcharges/PresentationLayer/UIs/PlaceDetail/Sources/Button/ContributeButton.swift @@ -8,6 +8,7 @@ import SwiftUI +import Resources import Models struct ContributeButton: View { @@ -45,9 +46,9 @@ struct ContributeButton: View { switch _surcharge.status { case .notDetermined: return "" - case .unknown: return "We're welcome to your contribution🙏🏻" + case .unknown: return "\(R.string.localizable.reportForUnknownButtonTitle())🙏🏻" case .reported: return "" - case .confirmed: return "Something wrong?🤔" + case .confirmed: return "\(R.string.localizable.reportForConfirmedButtonTitle())🤔" } } } diff --git a/Surcharges/PresentationLayer/UIs/PlaceDetail/Sources/Surcharge/SurchargeView.swift b/Surcharges/PresentationLayer/UIs/PlaceDetail/Sources/Surcharge/SurchargeView.swift index 67a1f12..35a3546 100644 --- a/Surcharges/PresentationLayer/UIs/PlaceDetail/Sources/Surcharge/SurchargeView.swift +++ b/Surcharges/PresentationLayer/UIs/PlaceDetail/Sources/Surcharge/SurchargeView.swift @@ -77,10 +77,10 @@ struct SurchargeView: View { case .unknown: - Text("Haven't reported the surcharge rate yet😞") + Text("\(R.string.localizable.surchargeStatusUnknownMessage())😞") .font(.headline) - Text(verbatim: "Unknown") + Text(verbatim: R.string.localizable.surchargeStatusUnknown()) .font(.headline) .surchargeStatusUnknown() @@ -89,7 +89,7 @@ struct SurchargeView: View { Text(verbatim: _rateText(rate: _surcharge.rate)) .font(.largeTitle) - Text(verbatim: "Reported") + Text(verbatim: R.string.localizable.surchargeStatusReported()) .font(.headline) .surchargeStatusReported() @@ -98,14 +98,14 @@ struct SurchargeView: View { Text(verbatim: _rateText(rate: _surcharge.rate)) .font(.largeTitle) - Text(verbatim: "Confirmed") + Text(verbatim: R.string.localizable.surchargeStatusConfirmed()) .font(.headline) .surchargeStatusConfirmed() } if let updatedDate = _surcharge.updatedDate { - Text("Updated at \(updatedDate.formatted(date: .numeric, time: .omitted))") + Text(R.string.localizable.updatedAt(updatedDate.formatted(date: .numeric, time: .omitted))) .font(.footnote) } @@ -118,9 +118,9 @@ struct SurchargeView: View { private func _rateText(rate: Double?) -> String { if let rate = rate { - return rate == 0 ? "Zero🎉" : "\(rate)%" + return rate == 0 ? "\(R.string.localizable.zeroRates)🎉" : "\(rate)%" } else { - return "Unknown" + return R.string.localizable.surchargeStatusUnknown() } } } diff --git a/Surcharges/PresentationLayer/UIs/ReportSurcharge/Sources/RecognisedMessage/RecognisedResultMessageView.swift b/Surcharges/PresentationLayer/UIs/ReportSurcharge/Sources/RecognisedMessage/RecognisedResultMessageView.swift index 5879dee..d3224cf 100644 --- a/Surcharges/PresentationLayer/UIs/ReportSurcharge/Sources/RecognisedMessage/RecognisedResultMessageView.swift +++ b/Surcharges/PresentationLayer/UIs/ReportSurcharge/Sources/RecognisedMessage/RecognisedResultMessageView.swift @@ -27,45 +27,45 @@ struct RecognisedResultMessageView: View { switch _recognisedModel.result { case .recognised: - Text("Recognised🎉, Just in case please check the amounts, if it may not accurate.") + Text(R.string.localizable.recognitionSuccess()) .messageStyle(color: R.color.green800.color) case .placeNotMatched: VStack(spacing: 5) { - Text("Place name is not matched.") + Text(R.string.localizable.recognitionNameNotMatched()) .messageStyle(color: R.color.red800.color) Button { _howeverProceedAnyway() } label: { - Text("Do you think the recognition sucks?\nTap here to proceed anyway.") + Text(R.string.localizable.reportProceedAnyway()) .font(.subheadline) } } case .extractedPartially: - Text("It is recognised partially. Please input the rest manually.") + Text(R.string.localizable.recognitionExtractedPartially()) .messageStyle(color: R.color.red800.color) case .mayNotReceiptOrPurchaseTerminal: VStack(spacing: 5) { - Text("It may not be a receipt or purchase terminal.") + Text(R.string.localizable.notReceipt()) .messageStyle(color: R.color.red800.color) Button { _howeverProceedAnyway() } label: { - Text("Do you think the recognition sucks?\nTap here to proceed anyway.") + Text(R.string.localizable.reportProceedAnyway()) .font(.subheadline) } } case .notExtractable: - Text("It is not extractable. Place input manually.") + Text(R.string.localizable.recognitionFailed()) .messageStyle(color: R.color.red800.color) } diff --git a/Surcharges/PresentationLayer/UIs/ReportSurcharge/Sources/ReportSurchargeView.swift b/Surcharges/PresentationLayer/UIs/ReportSurcharge/Sources/ReportSurchargeView.swift index 8adb05b..947deb8 100644 --- a/Surcharges/PresentationLayer/UIs/ReportSurcharge/Sources/ReportSurchargeView.swift +++ b/Surcharges/PresentationLayer/UIs/ReportSurcharge/Sources/ReportSurchargeView.swift @@ -40,7 +40,7 @@ public struct ReportSurchargeView String { + if let name = (Locale.current as NSLocale).displayName(forKey: .countryCode, value: countryCode) { + // Country name was found + + let flag = _flag(countryCode: countryCode) + + return "\(name)\(flag)" + } else { + // Country name cannot be found + return countryCode + } + } + + public func _flag(countryCode:String) -> String { + let baseFlagScalar: UInt32 = 127397 + var flagString = "" + for scalarValue in countryCode.uppercased().unicodeScalars { + guard let scalar = UnicodeScalar(baseFlagScalar + scalarValue.value) else { + continue + } + flagString.unicodeScalars.append(scalar) + } + return flagString + } } diff --git a/Surcharges/Shared/Services/AppStatus/AppStatusServiceProtocol/Sources/Toast.swift b/Surcharges/Shared/Services/AppStatus/AppStatusServiceProtocol/Sources/Toast.swift index 7bbf5f2..05d7f25 100644 --- a/Surcharges/Shared/Services/AppStatus/AppStatusServiceProtocol/Sources/Toast.swift +++ b/Surcharges/Shared/Services/AppStatus/AppStatusServiceProtocol/Sources/Toast.swift @@ -10,7 +10,7 @@ import Foundation public enum Toast: Identifiable, Equatable, Sendable { case unauthorised - case outOfNZ + case outOfRegion(availableRegions: [String]) case noInternet case needToUpdate case unknown(message: String) diff --git a/Tuist/ProjectDescriptionHelpers/Project/Surcharges.swift b/Tuist/ProjectDescriptionHelpers/Project/Surcharges.swift index d757ea3..1b90b96 100644 --- a/Tuist/ProjectDescriptionHelpers/Project/Surcharges.swift +++ b/Tuist/ProjectDescriptionHelpers/Project/Surcharges.swift @@ -25,10 +25,10 @@ public final class Surcharges: @unchecked Sendable { ) -> Target { return .target( name: _project.name, - destinations: [.iPhone, .iPad, .mac], + destinations: [.iPhone], product: .framework, bundleId: _project.bundleId, - deploymentTargets: .multiplatform(iOS: "17.0", macOS: "13.0"), + deploymentTargets: .iOS("17.0"), infoPlist: .extendingDefault( with: [ "UILaunchScreen": [ @@ -75,10 +75,10 @@ public final class Surcharges: @unchecked Sendable { return .target( name: "\(_project.name)Demo", - destinations: [.iPhone, .iPad, .mac], + destinations: [.iPhone], product: .app, bundleId: "\( _project.bundleId)Demo", - deploymentTargets: .multiplatform(iOS: "17.0", macOS: "13.0"), + deploymentTargets: .iOS("17.0"), infoPlist: .extendingDefault( with: [ "UILaunchScreen": [ @@ -88,6 +88,8 @@ public final class Surcharges: @unchecked Sendable { "NSHumanReadableCopyright": .string("©2025 Bonsung Koo. All rights reserved."), "NSLocationWhenInUseUsageDescription": .string("Surcharges uses your location to provide nearest places to you."), "NSCameraUsageDescription": .string("Surcharges uses your camera to take your receipt."), + "ITSAppUsesNonExemptEncryption": .boolean(false), + "UISupportedInterfaceOrientations": .array([.string("UIInterfaceOrientationPortrait")]) ] ), sources: ["App/Sources/**"], @@ -110,10 +112,10 @@ public final class Surcharges: @unchecked Sendable { return .target( name: "\(_project.name)Tests", - destinations: [.iPhone, .iPad, .mac], + destinations: [.iPhone], product: .unitTests, bundleId: "\( _project.bundleId)Tests", - deploymentTargets: .multiplatform(iOS: "17.0", macOS: "13.0"), + deploymentTargets: .iOS("17.0"), sources: ["Tests/**"], dependencies: projects .map { diff --git a/ci_scripts/ci_post_clone.sh b/ci_scripts/ci_post_clone.sh index dd646a4..889eb4c 100644 --- a/ci_scripts/ci_post_clone.sh +++ b/ci_scripts/ci_post_clone.sh @@ -10,22 +10,42 @@ curl https://mise.jdx.dev/install.sh | sh /Users/local/.local/bin/mise install # Installs the version from .mise.toml -if [[ -n $CI_PULL_REQUEST_NUMBER ]]; + +if [ $CI_WORKFLOW_ID = 'DF8E0AB2-31E7-450B-B956-B1F0D3EC3CAF' ]; then + + # Dev release workflow + + sh ./make_files/dev/make_endpoint.sh + sh ./make_files/dev/make_firebase.sh cd .. - /Users/local/.local/bin/mise exec -- tuist generate UseCasesTests ViewModelsTests --no-open + TUIST_DEVELOPMENT_TEAM=$DEVELOPMENT_TEAM /Users/local/.local/bin/mise exec -- tuist generate --no-open else + + # Prod release, Prod tests workflow + + if [[ -n $CI_PULL_REQUEST_NUMBER ]]; + then + + # Prod tests workflow + + cd .. - echo $CI_TAG + /Users/local/.local/bin/mise exec -- tuist generate UseCasesTests ViewModelsTests --no-open - sh ./make_files/make_endpoint.sh - sh ./make_files/make_firebase.sh + else - cd .. + # Prod release workflow + + sh ./make_files/prod/make_endpoint.sh + sh ./make_files/prod/make_firebase.sh + + cd .. - TUIST_APP_VERSION=$CI_TAG TUIST_DEVELOPMENT_TEAM=$DEVELOPMENT_TEAM /Users/local/.local/bin/mise exec -- tuist generate --no-open + TUIST_APP_VERSION=$CI_TAG TUIST_DEVELOPMENT_TEAM=$DEVELOPMENT_TEAM /Users/local/.local/bin/mise exec -- tuist generate --no-open + fi fi \ No newline at end of file diff --git a/ci_scripts/make_files/dev/make_endpoint.sh b/ci_scripts/make_files/dev/make_endpoint.sh new file mode 100644 index 0000000..8519394 --- /dev/null +++ b/ci_scripts/make_files/dev/make_endpoint.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +# make_endpoint.sh +# +# +# Created by Bonsung Koo on 28/01/2025. +# + +echo "import Foundation" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/DataSource/Endpoints/DevelopmentEndpoint/Sources/DevelopmentEndpoint.swift +echo "import EndpointProtocol" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/DataSource/Endpoints/DevelopmentEndpoint/Sources/DevelopmentEndpoint.swift +echo "public struct DevelopmentEndpoint: EndpointProtocol {" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/DataSource/Endpoints/DevelopmentEndpoint/Sources/DevelopmentEndpoint.swift +echo "public init() { }" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/DataSource/Endpoints/DevelopmentEndpoint/Sources/DevelopmentEndpoint.swift +echo "public var baseURL: String {" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/DataSource/Endpoints/DevelopmentEndpoint/Sources/DevelopmentEndpoint.swift +echo "return \"$BASE_URL\"" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/DataSource/Endpoints/DevelopmentEndpoint/Sources/DevelopmentEndpoint.swift +echo "}" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/DataSource/Endpoints/DevelopmentEndpoint/Sources/DevelopmentEndpoint.swift +echo "public var authorisationScheme: String {" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/DataSource/Endpoints/DevelopmentEndpoint/Sources/DevelopmentEndpoint.swift +echo "return \"$AUTHORISATION_SCHEME\"" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/DataSource/Endpoints/DevelopmentEndpoint/Sources/DevelopmentEndpoint.swift +echo "}" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/DataSource/Endpoints/DevelopmentEndpoint/Sources/DevelopmentEndpoint.swift +echo "public var authorisationToken: String {" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/DataSource/Endpoints/DevelopmentEndpoint/Sources/DevelopmentEndpoint.swift +echo "return \"$AUTHORISATION_TOKEN\"" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/DataSource/Endpoints/DevelopmentEndpoint/Sources/DevelopmentEndpoint.swift +echo "}" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/DataSource/Endpoints/DevelopmentEndpoint/Sources/DevelopmentEndpoint.swift +echo "}" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/DataSource/Endpoints/DevelopmentEndpoint/Sources/DevelopmentEndpoint.swift \ No newline at end of file diff --git a/ci_scripts/make_files/dev/make_firebase.sh b/ci_scripts/make_files/dev/make_firebase.sh new file mode 100644 index 0000000..dd18316 --- /dev/null +++ b/ci_scripts/make_files/dev/make_firebase.sh @@ -0,0 +1,42 @@ +#!/bin/sh + +# make_firebase.sh +# +# +# Created by Bonsung Koo on 28/01/2025. +# + +echo "" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/Applications/Resources/Dev/GoogleService-Info.plist +echo "" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/Applications/Resources/Dev/GoogleService-Info.plist +echo "" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/Applications/Resources/Dev/GoogleService-Info.plist +echo "" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/Applications/Resources/Dev/GoogleService-Info.plist +echo "CLIENT_ID" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/Applications/Resources/Dev/GoogleService-Info.plist +echo "$FIREBASE_CLIENT_ID" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/Applications/Resources/Dev/GoogleService-Info.plist +echo "REVERSED_CLIENT_ID" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/Applications/Resources/Dev/GoogleService-Info.plist +echo "$FIREBASE_REVERSED_CLIENT_ID" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/Applications/Resources/Dev/GoogleService-Info.plist +echo "API_KEY" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/Applications/Resources/Dev/GoogleService-Info.plist +echo "$FIREBASE_API_KEY" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/Applications/Resources/Dev/GoogleService-Info.plist +echo "GCM_SENDER_ID" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/Applications/Resources/Dev/GoogleService-Info.plist +echo "$FIREBASE_GCM_SENDER_ID" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/Applications/Resources/Dev/GoogleService-Info.plist +echo "PLIST_VERSION" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/Applications/Resources/Dev/GoogleService-Info.plist +echo "1" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/Applications/Resources/Dev/GoogleService-Info.plist +echo "BUNDLE_ID" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/Applications/Resources/Dev/GoogleService-Info.plist +echo "nz.surcharges.development" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/Applications/Resources/Dev/GoogleService-Info.plist +echo "PROJECT_ID" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/Applications/Resources/Dev/GoogleService-Info.plist +echo "surcharges-dev" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/Applications/Resources/Dev/GoogleService-Info.plist +echo "STORAGE_BUCKET" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/Applications/Resources/Dev/GoogleService-Info.plist +echo "$FIREBASE_STORAGE_BUCKET" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/Applications/Resources/Dev/GoogleService-Info.plist +echo "IS_ADS_ENABLED" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/Applications/Resources/Dev/GoogleService-Info.plist +echo "" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/Applications/Resources/Dev/GoogleService-Info.plist +echo "IS_ANALYTICS_ENABLED" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/Applications/Resources/Dev/GoogleService-Info.plist +echo "" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/Applications/Resources/Dev/GoogleService-Info.plist +echo "IS_APPINVITE_ENABLED" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/Applications/Resources/Dev/GoogleService-Info.plist +echo "" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/Applications/Resources/Dev/GoogleService-Info.plist +echo "IS_GCM_ENABLED" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/Applications/Resources/Dev/GoogleService-Info.plist +echo "" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/Applications/Resources/Dev/GoogleService-Info.plist +echo "IS_SIGNIN_ENABLED" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/Applications/Resources/Dev/GoogleService-Info.plist +echo "" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/Applications/Resources/Dev/GoogleService-Info.plist +echo "GOOGLE_APP_ID" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/Applications/Resources/Dev/GoogleService-Info.plist +echo "$FIREBASE_GOOGLE_APP_ID" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/Applications/Resources/Dev/GoogleService-Info.plist +echo "" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/Applications/Resources/Dev/GoogleService-Info.plist +echo "" >> $CI_PRIMARY_REPOSITORY_PATH/Surcharges/Applications/Resources/Dev/GoogleService-Info.plist \ No newline at end of file diff --git a/ci_scripts/make_files/make_endpoint.sh b/ci_scripts/make_files/prod/make_endpoint.sh similarity index 100% rename from ci_scripts/make_files/make_endpoint.sh rename to ci_scripts/make_files/prod/make_endpoint.sh diff --git a/ci_scripts/make_files/make_firebase.sh b/ci_scripts/make_files/prod/make_firebase.sh similarity index 100% rename from ci_scripts/make_files/make_firebase.sh rename to ci_scripts/make_files/prod/make_firebase.sh diff --git a/graph.png b/graph.png index b9df82b..54e8de1 100644 Binary files a/graph.png and b/graph.png differ