diff --git a/Cable.xcodeproj/project.pbxproj b/Cable.xcodeproj/project.pbxproj index 073868c..7e3eddc 100644 --- a/Cable.xcodeproj/project.pbxproj +++ b/Cable.xcodeproj/project.pbxproj @@ -305,8 +305,8 @@ INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -336,8 +336,8 @@ INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/Cable/AmazonAffiliate.swift b/Cable/AmazonAffiliate.swift new file mode 100644 index 0000000..782cf00 --- /dev/null +++ b/Cable/AmazonAffiliate.swift @@ -0,0 +1,73 @@ +import Foundation + +enum AmazonAffiliate { + private static let fallbackDomain = "www.amazon.com" + private static let fallbackTag: String? = nil + + private static let domainsByCountry: [String: String] = [ + "US": "www.amazon.com", + "DE": "www.amazon.de", + "FR": "www.amazon.fr", + "ES": "www.amazon.es", + "IT": "www.amazon.it", + "GB": "www.amazon.co.uk", + "CA": "www.amazon.ca", + "JP": "www.amazon.co.jp", + "AU": "www.amazon.com.au", + "NL": "www.amazon.nl", + "SE": "www.amazon.se", + "PL": "www.amazon.pl", + "MX": "www.amazon.com.mx", + "BR": "www.amazon.com.br", + "IN": "www.amazon.in" + ] + + // Configure Amazon affiliate tracking IDs by country code. + private static let tagsByCountry: [String: String] = [ + "US": "voltplan-20", + "DE": "voltplan-21", + ] + + private static let countryAliases: [String: String] = [ + "UK": "GB" + ] + + static func searchURL(query: String, countryCode: String?) -> URL? { + guard !query.isEmpty else { return nil } + + var components = URLComponents() + components.scheme = "https" + components.host = domain(for: countryCode) + components.path = "/s" + + var queryItems = [URLQueryItem(name: "k", value: query)] + if let tag = affiliateTag(for: countryCode), !tag.isEmpty { + queryItems.append(URLQueryItem(name: "tag", value: tag)) + } + components.queryItems = queryItems + + return components.url + } + + static func domain(for countryCode: String?) -> String { + guard let normalized = normalizedCountryCode(from: countryCode) else { + return fallbackDomain + } + return domainsByCountry[normalized] ?? fallbackDomain + } + + static func affiliateTag(for countryCode: String?) -> String? { + guard let normalized = normalizedCountryCode(from: countryCode) else { + return fallbackTag + } + return tagsByCountry[normalized] ?? fallbackTag + } + + private static func normalizedCountryCode(from countryCode: String?) -> String? { + guard let raw = countryCode?.uppercased(), !raw.isEmpty else { return nil } + if let alias = countryAliases[raw] { + return alias + } + return raw + } +} diff --git a/Cable/CalculatorView.swift b/Cable/CalculatorView.swift index 9c279c1..441f650 100644 --- a/Cable/CalculatorView.swift +++ b/Cable/CalculatorView.swift @@ -874,31 +874,7 @@ private struct BillOfMaterialsView: View { case .affiliate(let url): return url case .amazonSearch(let query): - let domain = amazonDomain(for: info.countryCode) - guard let encodedQuery = query.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else { return nil } - return URL(string: "https://\(domain)/s?k=\(encodedQuery)") - } - } - - private func amazonDomain(for countryCode: String?) -> String { - guard let countryCode else { return "www.amazon.com" } - - switch countryCode.uppercased() { - case "DE": return "www.amazon.de" - case "FR": return "www.amazon.fr" - case "ES": return "www.amazon.es" - case "IT": return "www.amazon.it" - case "GB", "UK": return "www.amazon.co.uk" - case "CA": return "www.amazon.ca" - case "JP": return "www.amazon.co.jp" - case "AU": return "www.amazon.com.au" - case "NL": return "www.amazon.nl" - case "SE": return "www.amazon.se" - case "PL": return "www.amazon.pl" - case "MX": return "www.amazon.com.mx" - case "BR": return "www.amazon.com.br" - case "IN": return "www.amazon.in" - default: return "www.amazon.com" + return AmazonAffiliate.searchURL(query: query, countryCode: info.countryCode) } } } diff --git a/Cable/ContentView.swift b/Cable/ContentView.swift index 288a29d..6543d9b 100644 --- a/Cable/ContentView.swift +++ b/Cable/ContentView.swift @@ -1194,9 +1194,8 @@ private struct SystemBillOfMaterialsView: View { case .affiliate(let url): return url case .amazonSearch(let query): - guard let encoded = query.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else { return nil } - let domain = amazonDomain(for: load.affiliateCountryCode ?? Locale.current.regionCode) - return URL(string: "https://\(domain)/s?k=\(encoded)") + let countryCode = load.affiliateCountryCode ?? Locale.current.regionCode + return AmazonAffiliate.searchURL(query: query, countryCode: countryCode) } } @@ -1234,28 +1233,6 @@ private struct SystemBillOfMaterialsView: View { completedItemIDs = Set(keys) } - private func amazonDomain(for countryCode: String?) -> String { - guard let code = countryCode?.uppercased() else { return "www.amazon.com" } - - switch code { - case "DE": return "www.amazon.de" - case "FR": return "www.amazon.fr" - case "ES": return "www.amazon.es" - case "IT": return "www.amazon.it" - case "GB", "UK": return "www.amazon.co.uk" - case "CA": return "www.amazon.ca" - case "JP": return "www.amazon.co.jp" - case "AU": return "www.amazon.com.au" - case "NL": return "www.amazon.nl" - case "SE": return "www.amazon.se" - case "PL": return "www.amazon.pl" - case "MX": return "www.amazon.com.mx" - case "BR": return "www.amazon.com.br" - case "IN": return "www.amazon.in" - default: return "www.amazon.com" - } - } - private func recommendedFuse(for load: SavedLoad) -> Int { let targetFuse = load.current * 1.25 let standardFuses = [1, 2, 3, 5, 7, 10, 15, 20, 25, 30, 35, 40, 50, 60, 70, 80, 100, 125, 150, 175, 200, 225, 250, 300, 350, 400, 450, 500, 600, 700, 800]