import Foundation import Postbox import SwiftSignalKit import TelegramApi public struct Country: Codable, Equatable { public static func == (lhs: Country, rhs: Country) -> Bool { return lhs.id == rhs.id && lhs.name == rhs.name && lhs.localizedName == rhs.localizedName && lhs.countryCodes == rhs.countryCodes && lhs.hidden == rhs.hidden } public struct CountryCode: Codable, Equatable { public let code: String public let prefixes: [String] public let patterns: [String] public init(code: String, prefixes: [String], patterns: [String]) { self.code = code self.prefixes = prefixes self.patterns = patterns } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: StringCodingKey.self) self.code = try container.decode(String.self, forKey: "c") self.prefixes = try container.decode([String].self, forKey: "pfx") self.patterns = try container.decode([String].self, forKey: "ptrn") } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: StringCodingKey.self) try container.encode(self.code, forKey: "c") try container.encode(self.prefixes, forKey: "pfx") try container.encode(self.patterns, forKey: "ptrn") } } public let id: String public let name: String public let localizedName: String? public let countryCodes: [CountryCode] public let hidden: Bool public init(id: String, name: String, localizedName: String?, countryCodes: [CountryCode], hidden: Bool) { self.id = id self.name = name self.localizedName = localizedName self.countryCodes = countryCodes self.hidden = hidden } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: StringCodingKey.self) self.id = try container.decode(String.self, forKey: "c") self.name = try container.decode(String.self, forKey: "n") self.localizedName = try container.decodeIfPresent(String.self, forKey: "ln") self.countryCodes = try container.decode([CountryCode].self, forKey: "cc") self.hidden = try container.decode(Bool.self, forKey: "h") } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: StringCodingKey.self) try container.encode(self.id, forKey: "c") try container.encode(self.name, forKey: "n") try container.encodeIfPresent(self.localizedName, forKey: "ln") try container.encode(self.countryCodes, forKey: "cc") try container.encode(self.hidden, forKey: "h") } } public final class CountriesList: Codable, Equatable { public let countries: [Country] public let hash: Int32 public init(countries: [Country], hash: Int32) { self.countries = countries self.hash = hash } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: StringCodingKey.self) self.countries = try container.decode([Country].self, forKey: "c") self.hash = try container.decode(Int32.self, forKey: "h") } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: StringCodingKey.self) try container.encode(self.countries, forKey: "c") try container.encode(self.hash, forKey: "h") } public static func ==(lhs: CountriesList, rhs: CountriesList) -> Bool { return lhs.countries == rhs.countries && lhs.hash == rhs.hash } } func _internal_getCountriesList(accountManager: AccountManager, network: Network, langCode: String?, forceUpdate: Bool = false) -> Signal<[Country], NoError> { let fetch: ([Country]?, Int32?) -> Signal<[Country], NoError> = { current, hash in return network.request(Api.functions.help.getCountriesList(langCode: langCode ?? "", hash: hash ?? 0)) |> retryRequest |> mapToSignal { result -> Signal<[Country], NoError> in switch result { case let .countriesList(apiCountries, hash): let result = apiCountries.compactMap { Country(apiCountry: $0) } if result == current { return .complete() } else { let _ = accountManager.transaction { transaction in transaction.updateSharedData(SharedDataKeys.countriesList, { _ in return PreferencesEntry(CountriesList(countries: result, hash: hash)) }) }.start() return .single(result) } case .countriesListNotModified: return .complete() } } } if forceUpdate { return fetch(nil, nil) } else { return accountManager.sharedData(keys: [SharedDataKeys.countriesList]) |> take(1) |> map { sharedData -> ([Country], Int32) in if let countriesList = sharedData.entries[SharedDataKeys.countriesList]?.get(CountriesList.self) { return (countriesList.countries, countriesList.hash) } else { return ([], 0) } } |> mapToSignal { current, hash -> Signal<[Country], NoError> in return .single(current) |> then(fetch(current, hash)) } } } extension Country.CountryCode { init(apiCountryCode: Api.help.CountryCode) { switch apiCountryCode { case let .countryCode(_, countryCode, apiPrefixes, apiPatterns): let prefixes: [String] = apiPrefixes.flatMap { $0 } ?? [] let patterns: [String] = apiPatterns.flatMap { $0 } ?? [] self.init(code: countryCode, prefixes: prefixes, patterns: patterns) } } } extension Country { init(apiCountry: Api.help.Country) { switch apiCountry { case let .country(flags, iso2, defaultName, name, countryCodes): self.init(id: iso2, name: defaultName, localizedName: name, countryCodes: countryCodes.map { Country.CountryCode(apiCountryCode: $0) }, hidden: (flags & 1 << 0) != 0) } } }