Added support for derived localizations

Added support for confirming two-step verification email via code
This commit is contained in:
Peter Iakovlev 2018-11-06 00:37:00 +04:00
parent 532285b062
commit eecd183a28
13 changed files with 347 additions and 199 deletions

View File

@ -113,6 +113,8 @@
D017495E1E118F790057C89A /* AccountStateManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D017495D1E118F790057C89A /* AccountStateManager.swift */; };
D01749601E118FC30057C89A /* AccountIntermediateState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D017495F1E118FC30057C89A /* AccountIntermediateState.swift */; };
D0177B7B1DF8A16C00A5083A /* SecretChatState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0177B7A1DF8A16C00A5083A /* SecretChatState.swift */; };
D01843A82190C28100278AFF /* ConfirmTwoStepRecoveryEmail.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01843A72190C28100278AFF /* ConfirmTwoStepRecoveryEmail.swift */; };
D01843A92190C28100278AFF /* ConfirmTwoStepRecoveryEmail.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01843A72190C28100278AFF /* ConfirmTwoStepRecoveryEmail.swift */; };
D018D3371E648ACF00C5E089 /* ChannelCreation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D018D3361E648ACF00C5E089 /* ChannelCreation.swift */; };
D018D3381E648ACF00C5E089 /* ChannelCreation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D018D3361E648ACF00C5E089 /* ChannelCreation.swift */; };
D018EE002044939F00CBB130 /* SecretApiLayer73.swift in Sources */ = {isa = PBXBuildFile; fileRef = D018EDFF2044939F00CBB130 /* SecretApiLayer73.swift */; };
@ -807,6 +809,7 @@
D017495D1E118F790057C89A /* AccountStateManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountStateManager.swift; sourceTree = "<group>"; };
D017495F1E118FC30057C89A /* AccountIntermediateState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountIntermediateState.swift; sourceTree = "<group>"; };
D0177B7A1DF8A16C00A5083A /* SecretChatState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretChatState.swift; sourceTree = "<group>"; };
D01843A72190C28100278AFF /* ConfirmTwoStepRecoveryEmail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmTwoStepRecoveryEmail.swift; sourceTree = "<group>"; };
D018D3361E648ACF00C5E089 /* ChannelCreation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChannelCreation.swift; sourceTree = "<group>"; };
D018EDFF2044939F00CBB130 /* SecretApiLayer73.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretApiLayer73.swift; sourceTree = "<group>"; };
D018EE0120458E1E00CBB130 /* SecretChatLayerNegotiation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecretChatLayerNegotiation.swift; sourceTree = "<group>"; };
@ -1552,6 +1555,7 @@
D0B1671C1F9EA2C300976B40 /* ChatHistoryPreloadManager.swift */,
D06ECFC720B810D300C576C2 /* TermsOfService.swift */,
D051DB16215ECC4D00F30F92 /* AppChangelog.swift */,
D01843A72190C28100278AFF /* ConfirmTwoStepRecoveryEmail.swift */,
);
name = Account;
sourceTree = "<group>";
@ -2209,6 +2213,7 @@
D0ADF911212B00DD00310BBC /* SecureIdConfiguration.swift in Sources */,
D0448CA51E29215A005A61A7 /* MediaResourceApiUtils.swift in Sources */,
D0C26D661FE022DB004ABF18 /* SynchronizeGroupedPeersOperation.swift in Sources */,
D01843A82190C28100278AFF /* ConfirmTwoStepRecoveryEmail.swift in Sources */,
D03C53771DAFF20F004C17B3 /* MultipartUpload.swift in Sources */,
D00C7CE01E3785710080C3D5 /* MarkMessageContentAsConsumedInteractively.swift in Sources */,
C2E0646D1ECF171D00387BB8 /* TelegramMediaWebDocument.swift in Sources */,
@ -2790,6 +2795,7 @@
D0B844301DAB91E0005F29E1 /* NBNumberFormat.m in Sources */,
D001F3F71E128A1C007A8C60 /* ApplyUpdateMessage.swift in Sources */,
D0B418971D7E0580004562A4 /* TelegramMediaImage.swift in Sources */,
D01843A92190C28100278AFF /* ConfirmTwoStepRecoveryEmail.swift in Sources */,
D041E3F91E535A88008C24B4 /* RemovePeerMember.swift in Sources */,
D049EAF61E44DF3300A2CD3A /* AccountState.swift in Sources */,
D0467D1620D7F2C90055C28F /* ManagedSynchronizeMarkAllUnseenPersonalMessagesOperations.swift in Sources */,

View File

@ -153,7 +153,7 @@ public class UnauthorizedAccount {
return self.postbox.transaction { transaction -> (LocalizationSettings?, ProxySettings?, NetworkSettings?) in
return (transaction.getPreferencesEntry(key: PreferencesKeys.localizationSettings) as? LocalizationSettings, transaction.getPreferencesEntry(key: PreferencesKeys.proxySettings) as? ProxySettings, transaction.getPreferencesEntry(key: PreferencesKeys.networkSettings) as? NetworkSettings)
} |> mapToSignal { (localizationSettings, proxySettings, networkSettings) -> Signal<UnauthorizedAccount, NoError> in
return initializedNetwork(arguments: self.networkArguments, supplementary: false, datacenterId: Int(masterDatacenterId), keychain: keychain, basePath: self.basePath, testingEnvironment: self.testingEnvironment, languageCode: localizationSettings?.languageCode, proxySettings: proxySettings, networkSettings: networkSettings, phoneNumber: nil)
return initializedNetwork(arguments: self.networkArguments, supplementary: false, datacenterId: Int(masterDatacenterId), keychain: keychain, basePath: self.basePath, testingEnvironment: self.testingEnvironment, languageCode: localizationSettings?.primaryComponent.languageCode, proxySettings: proxySettings, networkSettings: networkSettings, phoneNumber: nil)
|> map { network in
let updated = UnauthorizedAccount(networkArguments: self.networkArguments, id: self.id, rootPath: self.rootPath, basePath: self.basePath, testingEnvironment: self.testingEnvironment, postbox: self.postbox, network: network)
updated.shouldBeServiceTaskMaster.set(self.shouldBeServiceTaskMaster.get())
@ -340,7 +340,7 @@ public func accountWithId(networkArguments: NetworkInitializationArguments, id:
if let accountState = accountState {
switch accountState {
case let unauthorizedState as UnauthorizedAccountState:
return initializedNetwork(arguments: networkArguments, supplementary: supplementary, datacenterId: Int(unauthorizedState.masterDatacenterId), keychain: keychain, basePath: path, testingEnvironment: unauthorizedState.isTestingEnvironment, languageCode: localizationSettings?.languageCode, proxySettings: proxySettings, networkSettings: networkSettings, phoneNumber: nil)
return initializedNetwork(arguments: networkArguments, supplementary: supplementary, datacenterId: Int(unauthorizedState.masterDatacenterId), keychain: keychain, basePath: path, testingEnvironment: unauthorizedState.isTestingEnvironment, languageCode: localizationSettings?.primaryComponent.languageCode, proxySettings: proxySettings, networkSettings: networkSettings, phoneNumber: nil)
|> map { network -> AccountResult in
return .unauthorized(UnauthorizedAccount(networkArguments: networkArguments, id: id, rootPath: rootPath, basePath: path, testingEnvironment: unauthorizedState.isTestingEnvironment, postbox: postbox, network: network, shouldKeepAutoConnection: shouldKeepAutoConnection))
}
@ -349,7 +349,7 @@ public func accountWithId(networkArguments: NetworkInitializationArguments, id:
return (transaction.getPeer(authorizedState.peerId) as? TelegramUser)?.phone
}
|> mapToSignal { phoneNumber in
return initializedNetwork(arguments: networkArguments, supplementary: supplementary, datacenterId: Int(authorizedState.masterDatacenterId), keychain: keychain, basePath: path, testingEnvironment: authorizedState.isTestingEnvironment, languageCode: localizationSettings?.languageCode, proxySettings: proxySettings, networkSettings: networkSettings, phoneNumber: phoneNumber)
return initializedNetwork(arguments: networkArguments, supplementary: supplementary, datacenterId: Int(authorizedState.masterDatacenterId), keychain: keychain, basePath: path, testingEnvironment: authorizedState.isTestingEnvironment, languageCode: localizationSettings?.primaryComponent.languageCode, proxySettings: proxySettings, networkSettings: networkSettings, phoneNumber: phoneNumber)
|> map { network -> AccountResult in
return .authorized(Account(id: id, basePath: path, testingEnvironment: authorizedState.isTestingEnvironment, postbox: postbox, network: network, networkArguments: networkArguments, peerId: authorizedState.peerId, auxiliaryMethods: auxiliaryMethods))
}
@ -359,7 +359,7 @@ public func accountWithId(networkArguments: NetworkInitializationArguments, id:
}
}
return initializedNetwork(arguments: networkArguments, supplementary: supplementary, datacenterId: 2, keychain: keychain, basePath: path, testingEnvironment: beginWithTestingEnvironment, languageCode: localizationSettings?.languageCode, proxySettings: proxySettings, networkSettings: networkSettings, phoneNumber: nil)
return initializedNetwork(arguments: networkArguments, supplementary: supplementary, datacenterId: 2, keychain: keychain, basePath: path, testingEnvironment: beginWithTestingEnvironment, languageCode: localizationSettings?.primaryComponent.languageCode, proxySettings: proxySettings, networkSettings: networkSettings, phoneNumber: nil)
|> map { network -> AccountResult in
return .unauthorized(UnauthorizedAccount(networkArguments: networkArguments, id: id, rootPath: rootPath, basePath: path, testingEnvironment: beginWithTestingEnvironment, postbox: postbox, network: network, shouldKeepAutoConnection: shouldKeepAutoConnection))
}
@ -963,6 +963,9 @@ public class Account {
})
self.localInputActivityManager = PeerInputActivityManager()
self.accountPresenceManager = AccountPresenceManager(shouldKeepOnlinePresence: self.shouldKeepOnlinePresence.get(), network: network)
let _ = (postbox.transaction { transaction -> Void in
transaction.updatePeerPresencesInternal([peerId: TelegramUserPresence(status: .present(until: Int32.max - 1))])
}).start()
self.notificationAutolockReportManager = NotificationAutolockReportManager(deadline: self.autolockReportDeadline.get(), network: network)
self.autolockReportDeadline.set(
postbox.combinedView(keys: [.accessChallengeData])
@ -1128,27 +1131,27 @@ public class Account {
}))
let shouldBeMaster = combineLatest(shouldBeServiceTaskMaster.get(), postbox.isMasterClient())
|> map { [weak self] shouldBeMaster, isMaster -> Bool in
if shouldBeMaster == .always && !isMaster {
self?.postbox.becomeMasterClient()
}
return (shouldBeMaster == .now || shouldBeMaster == .always) && isMaster
|> map { [weak self] shouldBeMaster, isMaster -> Bool in
if shouldBeMaster == .always && !isMaster {
self?.postbox.becomeMasterClient()
}
|> distinctUntilChanged
return (shouldBeMaster == .now || shouldBeMaster == .always) && isMaster
}
|> distinctUntilChanged
self.network.shouldKeepConnection.set(shouldBeMaster)
self.network.shouldExplicitelyKeepWorkerConnections.set(self.shouldExplicitelyKeepWorkerConnections.get())
let serviceTasksMaster = shouldBeMaster
|> deliverOn(self.serviceQueue)
|> mapToSignal { [weak self] value -> Signal<Void, NoError> in
if let strongSelf = self, value {
Logger.shared.log("Account", "Became master")
return managedServiceViews(accountPeerId: peerId, network: strongSelf.network, postbox: strongSelf.postbox, stateManager: strongSelf.stateManager, pendingMessageManager: strongSelf.pendingMessageManager)
} else {
Logger.shared.log("Account", "Resigned master")
return .never()
}
|> deliverOn(self.serviceQueue)
|> mapToSignal { [weak self] value -> Signal<Void, NoError> in
if let strongSelf = self, value {
Logger.shared.log("Account", "Became master")
return managedServiceViews(accountPeerId: peerId, network: strongSelf.network, postbox: strongSelf.postbox, stateManager: strongSelf.stateManager, pendingMessageManager: strongSelf.pendingMessageManager)
} else {
Logger.shared.log("Account", "Resigned master")
return .never()
}
}
self.managedServiceViewsDisposable.set(serviceTasksMaster.start())
@ -1214,39 +1217,6 @@ public class Account {
let settings: CacheStorageSettings = ((view.views[storagePreferencesKey] as? PreferencesView)?.values[PreferencesKeys.cacheStorageSettings] as? CacheStorageSettings) ?? CacheStorageSettings.defaultSettings
mediaBox.setMaxStoreTime(settings.defaultCacheStorageTimeout)
})
/*let updatedPresence = self.shouldKeepOnlinePresence.get()
|> distinctUntilChanged
|> mapToSignal { [weak self] online -> Signal<Void, NoError> in
if let strongSelf = self {
let delayRequest: Signal<Void, NoError> = .complete()
|> delay(60.0, queue: Queue.concurrentDefaultQueue())
let pushStatusOnce = strongSelf.network.request(Api.functions.account.updateStatus(offline: online ? .boolFalse : .boolTrue))
|> retryRequest
|> mapToSignal { _ -> Signal<Void, NoError> in return .complete() }
let pushStatusRepeatedly = (pushStatusOnce
|> then(delayRequest))
|> restart
let peerId = strongSelf.peerId
let updatePresenceLocally = strongSelf.postbox.transaction { transaction -> Void in
let timestamp: Double
if online {
timestamp = CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970 + 60.0 * 60.0 * 24.0 * 356.0
} else {
timestamp = CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970 - 1.0
}
transaction.updatePeerPresences([peerId: TelegramUserPresence(status: .present(until: Int32(timestamp)))])
}
return combineLatest(pushStatusRepeatedly, updatePresenceLocally)
|> mapToSignal { _ -> Signal<Void, NoError> in return .complete()
}
} else {
return .complete()
}
}
self.updatedPresenceDisposable.set(updatedPresence.start())*/
}
deinit {

View File

@ -172,10 +172,14 @@ public final class AccountStateManager {
return .complete()
}
} else {
return network.request(Api.functions.messages.receivedQueue(maxQts: value))
|> ignoreValues
|> `catch` { _ -> Signal<Never, NoError> in
if value == 0 {
return .complete()
} else {
return network.request(Api.functions.messages.receivedQueue(maxQts: value))
|> ignoreValues
|> `catch` { _ -> Signal<Never, NoError> in
return .complete()
}
}
}
}).start())

View File

@ -416,7 +416,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[182649427] = { return Api.MessageRange.parse_messageRange($0) }
dict[946083368] = { return Api.messages.StickerSetInstallResult.parse_stickerSetInstallResultSuccess($0) }
dict[904138920] = { return Api.messages.StickerSetInstallResult.parse_stickerSetInstallResultArchive($0) }
dict[840162234] = { return Api.Config.parse_config($0) }
dict[-422959626] = { return Api.Config.parse_config($0) }
dict[-75283823] = { return Api.TopPeerCategoryPeers.parse_topPeerCategoryPeers($0) }
dict[-1107729093] = { return Api.Game.parse_game($0) }
dict[4883767] = { return Api.SecurePasswordKdfAlgo.parse_securePasswordKdfAlgoUnknown($0) }

View File

@ -10170,13 +10170,13 @@ extension Api {
}
enum Config: TypeConstructorDescription {
case config(flags: Int32, date: Int32, expires: Int32, testMode: Api.Bool, thisDc: Int32, dcOptions: [Api.DcOption], dcTxtDomainName: String, chatSizeMax: Int32, megagroupSizeMax: Int32, forwardedCountMax: Int32, onlineUpdatePeriodMs: Int32, offlineBlurTimeoutMs: Int32, offlineIdleTimeoutMs: Int32, onlineCloudTimeoutMs: Int32, notifyCloudDelayMs: Int32, notifyDefaultDelayMs: Int32, pushChatPeriodMs: Int32, pushChatLimit: Int32, savedGifsLimit: Int32, editTimeLimit: Int32, revokeTimeLimit: Int32, revokePmTimeLimit: Int32, ratingEDecay: Int32, stickersRecentLimit: Int32, stickersFavedLimit: Int32, channelsReadMediaPeriod: Int32, tmpSessions: Int32?, pinnedDialogsCountMax: Int32, callReceiveTimeoutMs: Int32, callRingTimeoutMs: Int32, callConnectTimeoutMs: Int32, callPacketTimeoutMs: Int32, meUrlPrefix: String, autoupdateUrlPrefix: String?, gifSearchUsername: String?, venueSearchUsername: String?, imgSearchUsername: String?, staticMapsProvider: String?, captionLengthMax: Int32, messageLengthMax: Int32, webfileDcId: Int32, suggestedLangCode: String?, langPackVersion: Int32?)
case config(flags: Int32, date: Int32, expires: Int32, testMode: Api.Bool, thisDc: Int32, dcOptions: [Api.DcOption], dcTxtDomainName: String, chatSizeMax: Int32, megagroupSizeMax: Int32, forwardedCountMax: Int32, onlineUpdatePeriodMs: Int32, offlineBlurTimeoutMs: Int32, offlineIdleTimeoutMs: Int32, onlineCloudTimeoutMs: Int32, notifyCloudDelayMs: Int32, notifyDefaultDelayMs: Int32, pushChatPeriodMs: Int32, pushChatLimit: Int32, savedGifsLimit: Int32, editTimeLimit: Int32, revokeTimeLimit: Int32, revokePmTimeLimit: Int32, ratingEDecay: Int32, stickersRecentLimit: Int32, stickersFavedLimit: Int32, channelsReadMediaPeriod: Int32, tmpSessions: Int32?, pinnedDialogsCountMax: Int32, callReceiveTimeoutMs: Int32, callRingTimeoutMs: Int32, callConnectTimeoutMs: Int32, callPacketTimeoutMs: Int32, meUrlPrefix: String, autoupdateUrlPrefix: String?, gifSearchUsername: String?, venueSearchUsername: String?, imgSearchUsername: String?, staticMapsProvider: String?, captionLengthMax: Int32, messageLengthMax: Int32, webfileDcId: Int32, suggestedLangCode: String?, langPackVersion: Int32?, baseLangPackVersion: Int32?)
func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .config(let flags, let date, let expires, let testMode, let thisDc, let dcOptions, let dcTxtDomainName, let chatSizeMax, let megagroupSizeMax, let forwardedCountMax, let onlineUpdatePeriodMs, let offlineBlurTimeoutMs, let offlineIdleTimeoutMs, let onlineCloudTimeoutMs, let notifyCloudDelayMs, let notifyDefaultDelayMs, let pushChatPeriodMs, let pushChatLimit, let savedGifsLimit, let editTimeLimit, let revokeTimeLimit, let revokePmTimeLimit, let ratingEDecay, let stickersRecentLimit, let stickersFavedLimit, let channelsReadMediaPeriod, let tmpSessions, let pinnedDialogsCountMax, let callReceiveTimeoutMs, let callRingTimeoutMs, let callConnectTimeoutMs, let callPacketTimeoutMs, let meUrlPrefix, let autoupdateUrlPrefix, let gifSearchUsername, let venueSearchUsername, let imgSearchUsername, let staticMapsProvider, let captionLengthMax, let messageLengthMax, let webfileDcId, let suggestedLangCode, let langPackVersion):
case .config(let flags, let date, let expires, let testMode, let thisDc, let dcOptions, let dcTxtDomainName, let chatSizeMax, let megagroupSizeMax, let forwardedCountMax, let onlineUpdatePeriodMs, let offlineBlurTimeoutMs, let offlineIdleTimeoutMs, let onlineCloudTimeoutMs, let notifyCloudDelayMs, let notifyDefaultDelayMs, let pushChatPeriodMs, let pushChatLimit, let savedGifsLimit, let editTimeLimit, let revokeTimeLimit, let revokePmTimeLimit, let ratingEDecay, let stickersRecentLimit, let stickersFavedLimit, let channelsReadMediaPeriod, let tmpSessions, let pinnedDialogsCountMax, let callReceiveTimeoutMs, let callRingTimeoutMs, let callConnectTimeoutMs, let callPacketTimeoutMs, let meUrlPrefix, let autoupdateUrlPrefix, let gifSearchUsername, let venueSearchUsername, let imgSearchUsername, let staticMapsProvider, let captionLengthMax, let messageLengthMax, let webfileDcId, let suggestedLangCode, let langPackVersion, let baseLangPackVersion):
if boxed {
buffer.appendInt32(840162234)
buffer.appendInt32(-422959626)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(date, buffer: buffer, boxed: false)
@ -10225,14 +10225,15 @@ extension Api {
serializeInt32(webfileDcId, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 2) != 0 {serializeString(suggestedLangCode!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 2) != 0 {serializeInt32(langPackVersion!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 2) != 0 {serializeInt32(baseLangPackVersion!, buffer: buffer, boxed: false)}
break
}
}
func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .config(let flags, let date, let expires, let testMode, let thisDc, let dcOptions, let dcTxtDomainName, let chatSizeMax, let megagroupSizeMax, let forwardedCountMax, let onlineUpdatePeriodMs, let offlineBlurTimeoutMs, let offlineIdleTimeoutMs, let onlineCloudTimeoutMs, let notifyCloudDelayMs, let notifyDefaultDelayMs, let pushChatPeriodMs, let pushChatLimit, let savedGifsLimit, let editTimeLimit, let revokeTimeLimit, let revokePmTimeLimit, let ratingEDecay, let stickersRecentLimit, let stickersFavedLimit, let channelsReadMediaPeriod, let tmpSessions, let pinnedDialogsCountMax, let callReceiveTimeoutMs, let callRingTimeoutMs, let callConnectTimeoutMs, let callPacketTimeoutMs, let meUrlPrefix, let autoupdateUrlPrefix, let gifSearchUsername, let venueSearchUsername, let imgSearchUsername, let staticMapsProvider, let captionLengthMax, let messageLengthMax, let webfileDcId, let suggestedLangCode, let langPackVersion):
return ("config", [("flags", flags), ("date", date), ("expires", expires), ("testMode", testMode), ("thisDc", thisDc), ("dcOptions", dcOptions), ("dcTxtDomainName", dcTxtDomainName), ("chatSizeMax", chatSizeMax), ("megagroupSizeMax", megagroupSizeMax), ("forwardedCountMax", forwardedCountMax), ("onlineUpdatePeriodMs", onlineUpdatePeriodMs), ("offlineBlurTimeoutMs", offlineBlurTimeoutMs), ("offlineIdleTimeoutMs", offlineIdleTimeoutMs), ("onlineCloudTimeoutMs", onlineCloudTimeoutMs), ("notifyCloudDelayMs", notifyCloudDelayMs), ("notifyDefaultDelayMs", notifyDefaultDelayMs), ("pushChatPeriodMs", pushChatPeriodMs), ("pushChatLimit", pushChatLimit), ("savedGifsLimit", savedGifsLimit), ("editTimeLimit", editTimeLimit), ("revokeTimeLimit", revokeTimeLimit), ("revokePmTimeLimit", revokePmTimeLimit), ("ratingEDecay", ratingEDecay), ("stickersRecentLimit", stickersRecentLimit), ("stickersFavedLimit", stickersFavedLimit), ("channelsReadMediaPeriod", channelsReadMediaPeriod), ("tmpSessions", tmpSessions), ("pinnedDialogsCountMax", pinnedDialogsCountMax), ("callReceiveTimeoutMs", callReceiveTimeoutMs), ("callRingTimeoutMs", callRingTimeoutMs), ("callConnectTimeoutMs", callConnectTimeoutMs), ("callPacketTimeoutMs", callPacketTimeoutMs), ("meUrlPrefix", meUrlPrefix), ("autoupdateUrlPrefix", autoupdateUrlPrefix), ("gifSearchUsername", gifSearchUsername), ("venueSearchUsername", venueSearchUsername), ("imgSearchUsername", imgSearchUsername), ("staticMapsProvider", staticMapsProvider), ("captionLengthMax", captionLengthMax), ("messageLengthMax", messageLengthMax), ("webfileDcId", webfileDcId), ("suggestedLangCode", suggestedLangCode), ("langPackVersion", langPackVersion)])
case .config(let flags, let date, let expires, let testMode, let thisDc, let dcOptions, let dcTxtDomainName, let chatSizeMax, let megagroupSizeMax, let forwardedCountMax, let onlineUpdatePeriodMs, let offlineBlurTimeoutMs, let offlineIdleTimeoutMs, let onlineCloudTimeoutMs, let notifyCloudDelayMs, let notifyDefaultDelayMs, let pushChatPeriodMs, let pushChatLimit, let savedGifsLimit, let editTimeLimit, let revokeTimeLimit, let revokePmTimeLimit, let ratingEDecay, let stickersRecentLimit, let stickersFavedLimit, let channelsReadMediaPeriod, let tmpSessions, let pinnedDialogsCountMax, let callReceiveTimeoutMs, let callRingTimeoutMs, let callConnectTimeoutMs, let callPacketTimeoutMs, let meUrlPrefix, let autoupdateUrlPrefix, let gifSearchUsername, let venueSearchUsername, let imgSearchUsername, let staticMapsProvider, let captionLengthMax, let messageLengthMax, let webfileDcId, let suggestedLangCode, let langPackVersion, let baseLangPackVersion):
return ("config", [("flags", flags), ("date", date), ("expires", expires), ("testMode", testMode), ("thisDc", thisDc), ("dcOptions", dcOptions), ("dcTxtDomainName", dcTxtDomainName), ("chatSizeMax", chatSizeMax), ("megagroupSizeMax", megagroupSizeMax), ("forwardedCountMax", forwardedCountMax), ("onlineUpdatePeriodMs", onlineUpdatePeriodMs), ("offlineBlurTimeoutMs", offlineBlurTimeoutMs), ("offlineIdleTimeoutMs", offlineIdleTimeoutMs), ("onlineCloudTimeoutMs", onlineCloudTimeoutMs), ("notifyCloudDelayMs", notifyCloudDelayMs), ("notifyDefaultDelayMs", notifyDefaultDelayMs), ("pushChatPeriodMs", pushChatPeriodMs), ("pushChatLimit", pushChatLimit), ("savedGifsLimit", savedGifsLimit), ("editTimeLimit", editTimeLimit), ("revokeTimeLimit", revokeTimeLimit), ("revokePmTimeLimit", revokePmTimeLimit), ("ratingEDecay", ratingEDecay), ("stickersRecentLimit", stickersRecentLimit), ("stickersFavedLimit", stickersFavedLimit), ("channelsReadMediaPeriod", channelsReadMediaPeriod), ("tmpSessions", tmpSessions), ("pinnedDialogsCountMax", pinnedDialogsCountMax), ("callReceiveTimeoutMs", callReceiveTimeoutMs), ("callRingTimeoutMs", callRingTimeoutMs), ("callConnectTimeoutMs", callConnectTimeoutMs), ("callPacketTimeoutMs", callPacketTimeoutMs), ("meUrlPrefix", meUrlPrefix), ("autoupdateUrlPrefix", autoupdateUrlPrefix), ("gifSearchUsername", gifSearchUsername), ("venueSearchUsername", venueSearchUsername), ("imgSearchUsername", imgSearchUsername), ("staticMapsProvider", staticMapsProvider), ("captionLengthMax", captionLengthMax), ("messageLengthMax", messageLengthMax), ("webfileDcId", webfileDcId), ("suggestedLangCode", suggestedLangCode), ("langPackVersion", langPackVersion), ("baseLangPackVersion", baseLangPackVersion)])
}
}
@ -10327,6 +10328,8 @@ extension Api {
if Int(_1!) & Int(1 << 2) != 0 {_42 = parseString(reader) }
var _43: Int32?
if Int(_1!) & Int(1 << 2) != 0 {_43 = reader.readInt32() }
var _44: Int32?
if Int(_1!) & Int(1 << 2) != 0 {_44 = reader.readInt32() }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
@ -10370,8 +10373,9 @@ extension Api {
let _c41 = _41 != nil
let _c42 = (Int(_1!) & Int(1 << 2) == 0) || _42 != nil
let _c43 = (Int(_1!) & Int(1 << 2) == 0) || _43 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 && _c25 && _c26 && _c27 && _c28 && _c29 && _c30 && _c31 && _c32 && _c33 && _c34 && _c35 && _c36 && _c37 && _c38 && _c39 && _c40 && _c41 && _c42 && _c43 {
return Api.Config.config(flags: _1!, date: _2!, expires: _3!, testMode: _4!, thisDc: _5!, dcOptions: _6!, dcTxtDomainName: _7!, chatSizeMax: _8!, megagroupSizeMax: _9!, forwardedCountMax: _10!, onlineUpdatePeriodMs: _11!, offlineBlurTimeoutMs: _12!, offlineIdleTimeoutMs: _13!, onlineCloudTimeoutMs: _14!, notifyCloudDelayMs: _15!, notifyDefaultDelayMs: _16!, pushChatPeriodMs: _17!, pushChatLimit: _18!, savedGifsLimit: _19!, editTimeLimit: _20!, revokeTimeLimit: _21!, revokePmTimeLimit: _22!, ratingEDecay: _23!, stickersRecentLimit: _24!, stickersFavedLimit: _25!, channelsReadMediaPeriod: _26!, tmpSessions: _27, pinnedDialogsCountMax: _28!, callReceiveTimeoutMs: _29!, callRingTimeoutMs: _30!, callConnectTimeoutMs: _31!, callPacketTimeoutMs: _32!, meUrlPrefix: _33!, autoupdateUrlPrefix: _34, gifSearchUsername: _35, venueSearchUsername: _36, imgSearchUsername: _37, staticMapsProvider: _38, captionLengthMax: _39!, messageLengthMax: _40!, webfileDcId: _41!, suggestedLangCode: _42, langPackVersion: _43)
let _c44 = (Int(_1!) & Int(1 << 2) == 0) || _44 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 && _c25 && _c26 && _c27 && _c28 && _c29 && _c30 && _c31 && _c32 && _c33 && _c34 && _c35 && _c36 && _c37 && _c38 && _c39 && _c40 && _c41 && _c42 && _c43 && _c44 {
return Api.Config.config(flags: _1!, date: _2!, expires: _3!, testMode: _4!, thisDc: _5!, dcOptions: _6!, dcTxtDomainName: _7!, chatSizeMax: _8!, megagroupSizeMax: _9!, forwardedCountMax: _10!, onlineUpdatePeriodMs: _11!, offlineBlurTimeoutMs: _12!, offlineIdleTimeoutMs: _13!, onlineCloudTimeoutMs: _14!, notifyCloudDelayMs: _15!, notifyDefaultDelayMs: _16!, pushChatPeriodMs: _17!, pushChatLimit: _18!, savedGifsLimit: _19!, editTimeLimit: _20!, revokeTimeLimit: _21!, revokePmTimeLimit: _22!, ratingEDecay: _23!, stickersRecentLimit: _24!, stickersFavedLimit: _25!, channelsReadMediaPeriod: _26!, tmpSessions: _27, pinnedDialogsCountMax: _28!, callReceiveTimeoutMs: _29!, callRingTimeoutMs: _30!, callConnectTimeoutMs: _31!, callPacketTimeoutMs: _32!, meUrlPrefix: _33!, autoupdateUrlPrefix: _34, gifSearchUsername: _35, venueSearchUsername: _36, imgSearchUsername: _37, staticMapsProvider: _38, captionLengthMax: _39!, messageLengthMax: _40!, webfileDcId: _41!, suggestedLangCode: _42, langPackVersion: _43, baseLangPackVersion: _44)
}
else {
return nil

View File

@ -0,0 +1,33 @@
import Foundation
#if os(macOS)
import SwiftSignalKitMac
import MtProtoKitMac
#else
import SwiftSignalKit
import MtProtoKitDynamic
#endif
public enum ConfirmTwoStepRecoveryEmailError {
case invalidEmail
case invalidCode
case flood
case expired
case generic
}
public func confirmTwoStepRecoveryEmail(network: Network, email: String, code: String) -> Signal<Never, ConfirmTwoStepRecoveryEmailError> {
return network.request(Api.functions.account.confirmPasswordEmail(email: email, code: code), automaticFloodWait: false)
|> mapError { error -> ConfirmTwoStepRecoveryEmailError in
if error.errorDescription == "EMAIL_INVALID" {
return .invalidEmail
} else if error.errorDescription == "CODE_INVALID" {
return .invalidCode
} else if error.errorDescription == "EMAIL_HASH_EXPIRED" {
return .expired
} else if error.errorDescription.hasPrefix("FLOOD_WAIT") {
return .flood
}
return .generic
}
|> ignoreValues
}

View File

@ -8,15 +8,17 @@ import Foundation
public final class LocalizationInfo: PostboxCoding {
public let languageCode: String
public let baseLanguageCode: String?
public let customPluralizationCode: String?
public let title: String
public let localizedTitle: String
public let isOfficial: Bool
public let totalStringCount: Int32
public let translatedStringCount: Int32
public init(languageCode: String, baseLanguageCode: String?, title: String, localizedTitle: String, isOfficial: Bool, totalStringCount: Int32, translatedStringCount: Int32) {
public init(languageCode: String, baseLanguageCode: String?, customPluralizationCode: String?, title: String, localizedTitle: String, isOfficial: Bool, totalStringCount: Int32, translatedStringCount: Int32) {
self.languageCode = languageCode
self.baseLanguageCode = baseLanguageCode
self.customPluralizationCode = customPluralizationCode
self.title = title
self.localizedTitle = localizedTitle
self.isOfficial = isOfficial
@ -27,6 +29,7 @@ public final class LocalizationInfo: PostboxCoding {
public init(decoder: PostboxDecoder) {
self.languageCode = decoder.decodeStringForKey("lc", orElse: "")
self.baseLanguageCode = decoder.decodeOptionalStringForKey("nlc")
self.customPluralizationCode = decoder.decodeOptionalStringForKey("cpc")
self.title = decoder.decodeStringForKey("t", orElse: "")
self.localizedTitle = decoder.decodeStringForKey("lt", orElse: "")
self.isOfficial = decoder.decodeInt32ForKey("of", orElse: 0) != 0
@ -41,6 +44,11 @@ public final class LocalizationInfo: PostboxCoding {
} else {
encoder.encodeNil(forKey: "nlc")
}
if let customPluralizationCode = self.customPluralizationCode {
encoder.encodeString(customPluralizationCode, forKey: "cpc")
} else {
encoder.encodeNil(forKey: "cpc")
}
encoder.encodeString(self.title, forKey: "t")
encoder.encodeString(self.localizedTitle, forKey: "lt")
encoder.encodeInt32(self.isOfficial ? 1 : 0, forKey: "of")
@ -53,7 +61,7 @@ extension LocalizationInfo {
convenience init(apiLanguage: Api.LangPackLanguage) {
switch apiLanguage {
case let .langPackLanguage(language):
self.init(languageCode: language.langCode, baseLanguageCode: nil/*language.baseLangCode*/, title: language.name, localizedTitle: language.nativeName, isOfficial: true/*(language.flags & (1 << 0)) != 0*/, totalStringCount: 1/*language.stringsCount*/, translatedStringCount: 1/*language.translatedCount*/)
self.init(languageCode: language.langCode, baseLanguageCode: language.baseLangCode, customPluralizationCode: language.pluralCode, title: language.name, localizedTitle: language.nativeName, isOfficial: (language.flags & (1 << 0)) != 0, totalStringCount: language.stringsCount, translatedStringCount: language.translatedCount)
}
}
}

View File

@ -12,13 +12,12 @@ public enum RequestLocalizationPreviewError {
case generic
}
public func requestLocalizationPreview(postbox: Postbox, network: Network, identifier: String) -> Signal<LocalizationInfo, RequestLocalizationPreviewError> {
return .never()
/*return network.request(Api.functions.langpack.getLanguage(langPack: "", langCode: identifier))
public func requestLocalizationPreview(network: Network, identifier: String) -> Signal<LocalizationInfo, RequestLocalizationPreviewError> {
return network.request(Api.functions.langpack.getLanguage(langPack: "", langCode: identifier))
|> mapError { _ -> RequestLocalizationPreviewError in
return .generic
}
|> map { language -> LocalizationInfo in
return LocalizationInfo(apiLanguage: language)
}*/
}
}

View File

@ -5,23 +5,73 @@ import Foundation
import Postbox
#endif
public final class LocalizationSettings: PreferencesEntry, Equatable {
public final class LocalizationComponent: Equatable, PostboxCoding {
public let languageCode: String
public let localization: Localization
public let customPluralizationCode: String?
public init(languageCode: String, localization: Localization) {
public init(languageCode: String, localization: Localization, customPluralizationCode: String?) {
self.languageCode = languageCode
self.localization = localization
self.customPluralizationCode = customPluralizationCode
}
public init(decoder: PostboxDecoder) {
self.languageCode = decoder.decodeStringForKey("lc", orElse: "en")
self.languageCode = decoder.decodeStringForKey("lc", orElse: "")
self.localization = decoder.decodeObjectForKey("loc", decoder: { Localization(decoder: $0) }) as! Localization
self.customPluralizationCode = decoder.decodeOptionalStringForKey("cpl")
}
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeString(self.languageCode, forKey: "lc")
encoder.encodeObject(self.localization, forKey: "loc")
if let customPluralizationCode = self.customPluralizationCode {
encoder.encodeString(customPluralizationCode, forKey: "cpl")
} else {
encoder.encodeNil(forKey: "cpl")
}
}
public static func ==(lhs: LocalizationComponent, rhs: LocalizationComponent) -> Bool {
if lhs.languageCode != rhs.languageCode {
return false
}
if lhs.localization != rhs.localization {
return false
}
if lhs.customPluralizationCode != rhs.customPluralizationCode {
return false
}
return true
}
}
public final class LocalizationSettings: PreferencesEntry, Equatable {
public let primaryComponent: LocalizationComponent
public let secondaryComponent: LocalizationComponent?
public init(primaryComponent: LocalizationComponent, secondaryComponent: LocalizationComponent?) {
self.primaryComponent = primaryComponent
self.secondaryComponent = secondaryComponent
}
public init(decoder: PostboxDecoder) {
if let languageCode = decoder.decodeOptionalStringForKey("lc") {
self.primaryComponent = LocalizationComponent(languageCode: languageCode, localization: decoder.decodeObjectForKey("loc", decoder: { Localization(decoder: $0) }) as! Localization, customPluralizationCode: nil)
self.secondaryComponent = nil
} else {
self.primaryComponent = decoder.decodeObjectForKey("primaryComponent", decoder: { LocalizationComponent(decoder: $0) }) as! LocalizationComponent
self.secondaryComponent = decoder.decodeObjectForKey("secondaryComponent", decoder: { LocalizationComponent(decoder: $0) }) as? LocalizationComponent
}
}
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeObject(self.primaryComponent, forKey: "primaryComponent")
if let secondaryComponent = self.secondaryComponent {
encoder.encodeObject(secondaryComponent, forKey: "secondaryComponent")
} else {
encoder.encodeNil(forKey: "secondaryComponent")
}
}
public func isEqual(to: PreferencesEntry) -> Bool {
@ -33,6 +83,6 @@ public final class LocalizationSettings: PreferencesEntry, Equatable {
}
public static func ==(lhs: LocalizationSettings, rhs: LocalizationSettings) -> Bool {
return lhs.languageCode == rhs.languageCode && lhs.localization == rhs.localization
return lhs.primaryComponent == rhs.primaryComponent && lhs.secondaryComponent == rhs.secondaryComponent
}
}

View File

@ -114,35 +114,53 @@ public func downloadLocalization(network: Network, languageCode: String) -> Sign
}
}
public enum DownoadAndApplyLocalizationError {
public enum DownloadAndApplyLocalizationError {
case generic
}
public func downoadAndApplyLocalization(postbox: Postbox, network: Network, languageCode: String) -> Signal<Void, DownoadAndApplyLocalizationError> {
return downloadLocalization(network: network, languageCode: languageCode)
|> mapError { _ -> DownoadAndApplyLocalizationError in
public func downloadAndApplyLocalization(postbox: Postbox, network: Network, languageCode: String) -> Signal<Void, DownloadAndApplyLocalizationError> {
return requestLocalizationPreview(network: network, identifier: languageCode)
|> mapError { _ -> DownloadAndApplyLocalizationError in
return .generic
}
|> mapToSignal { language -> Signal<Void, DownoadAndApplyLocalizationError> in
return postbox.transaction { transaction -> Signal<Void, DownoadAndApplyLocalizationError> in
transaction.updatePreferencesEntry(key: PreferencesKeys.localizationSettings, { _ in
return LocalizationSettings(languageCode: languageCode, localization: language)
})
network.context.updateApiEnvironment { current in
return current?.withUpdatedLangPackCode(languageCode)
}
return network.request(Api.functions.help.test())
|> `catch` { _ -> Signal<Api.Bool, NoError> in
return .complete()
}
|> mapToSignal { _ -> Signal<Void, NoError> in
return .complete()
}
|> introduceError(DownoadAndApplyLocalizationError.self)
|> mapToSignal { preview -> Signal<Void, DownloadAndApplyLocalizationError> in
var primaryAndSecondaryLocalizations: [Signal<Localization, DownloadLocalizationError>] = []
primaryAndSecondaryLocalizations.append(downloadLocalization(network: network, languageCode: preview.languageCode))
if let secondaryCode = preview.baseLanguageCode {
primaryAndSecondaryLocalizations.append(downloadLocalization(network: network, languageCode: secondaryCode))
}
return combineLatest(primaryAndSecondaryLocalizations)
|> mapError { _ -> DownloadAndApplyLocalizationError in
return .generic
}
|> mapToSignal { components -> Signal<Void, DownloadAndApplyLocalizationError> in
guard let primaryLocalization = components.first else {
return .fail(.generic)
}
var secondaryComponent: LocalizationComponent?
if let secondaryCode = preview.baseLanguageCode, components.count > 1 {
secondaryComponent = LocalizationComponent(languageCode: secondaryCode, localization: components[1], customPluralizationCode: nil)
}
return postbox.transaction { transaction -> Signal<Void, DownloadAndApplyLocalizationError> in
transaction.updatePreferencesEntry(key: PreferencesKeys.localizationSettings, { _ in
return LocalizationSettings(primaryComponent: LocalizationComponent(languageCode: preview.languageCode, localization: primaryLocalization, customPluralizationCode: preview.customPluralizationCode), secondaryComponent: secondaryComponent)
})
network.context.updateApiEnvironment { current in
return current?.withUpdatedLangPackCode(preview.languageCode)
}
return network.request(Api.functions.help.test())
|> `catch` { _ -> Signal<Api.Bool, NoError> in
return .complete()
}
|> mapToSignal { _ -> Signal<Void, NoError> in
return .complete()
}
|> introduceError(DownloadAndApplyLocalizationError.self)
}
|> introduceError(DownloadAndApplyLocalizationError.self)
|> switchToLatest
}
|> introduceError(DownoadAndApplyLocalizationError.self)
|> switchToLatest
}
}

View File

@ -68,8 +68,17 @@ func managedConfigurationUpdates(postbox: Postbox, network: Network) -> Signal<V
return configuration
})
let (_, version, _) = getLocalization(transaction)
if version != config.langPackVersion {
let (primary, secondary) = getLocalization(transaction)
var invalidateLocalization = false
if primary.version != config.langPackVersion {
invalidateLocalization = true
}
if let secondary = secondary, let baseLangPackVersion = config.baseLangPackVersion {
if secondary.version != baseLangPackVersion {
invalidateLocalization = true
}
}
if invalidateLocalization {
addSynchronizeLocalizationUpdatesOperation(transaction: transaction)
}
}

View File

@ -120,7 +120,7 @@ private enum SynchronizeLocalizationUpdatesError {
case reset
}
func getLocalization(_ transaction: Transaction) -> (String, Int32, [LocalizationEntry]) {
func getLocalization(_ transaction: Transaction) -> (primary: (code: String, version: Int32, entries: [LocalizationEntry]), secondary: (code: String, version: Int32, entries: [LocalizationEntry])?) {
let localizationSettings: LocalizationSettings?
if let current = transaction.getPreferencesEntry(key: PreferencesKeys.localizationSettings) as? LocalizationSettings {
localizationSettings = current
@ -128,76 +128,98 @@ func getLocalization(_ transaction: Transaction) -> (String, Int32, [Localizatio
localizationSettings = nil
}
if let localizationSettings = localizationSettings {
return (localizationSettings.languageCode, localizationSettings.localization.version, localizationSettings.localization.entries)
return (primary: (localizationSettings.primaryComponent.languageCode, localizationSettings.primaryComponent.localization.version, localizationSettings.primaryComponent.localization.entries), secondary: localizationSettings.secondaryComponent.flatMap({ ($0.languageCode, $0.localization.version, $0.localization.entries) }))
} else {
return ("en", 0, [])
return (primary: ("en", 0, []), secondary: nil)
}
}
private func parseLangPackDifference(_ difference: Api.LangPackDifference) -> (code: String, fromVersion: Int32, version: Int32, entries: [LocalizationEntry]) {
switch difference {
case let .langPackDifference(code, fromVersion, version, strings):
var entries: [LocalizationEntry] = []
for string in strings {
switch string {
case let .langPackString(key, value):
entries.append(.string(key: key, value: value))
case let .langPackStringPluralized(_, key, zeroValue, oneValue, twoValue, fewValue, manyValue, otherValue):
entries.append(.pluralizedString(key: key, zero: zeroValue, one: oneValue, two: twoValue, few: fewValue, many: manyValue, other: otherValue))
case let .langPackStringDeleted(key):
entries.append(.string(key: key, value: ""))
}
}
return (code, fromVersion, version, entries)
}
}
private func synchronizeLocalizationUpdates(transaction: Transaction, postbox: Postbox, network: Network) -> Signal<Void, NoError> {
let currentLanguageAndVersion = postbox.transaction { transaction -> (String, Int32) in
let (code, version, _) = getLocalization(transaction)
return (code, version)
let currentLanguageAndVersion = postbox.transaction { transaction -> (primary: (code: String, version: Int32), secondary: (code: String, version: Int32)?) in
let (primary, secondary) = getLocalization(transaction)
return ((primary.code, primary.version), secondary.flatMap({ ($0.code, $0.version) }))
}
let poll = currentLanguageAndVersion
|> mapError { _ -> SynchronizeLocalizationUpdatesError in return .done }
|> mapToSignal { (languageCode, fromVersion) -> Signal<Void, SynchronizeLocalizationUpdatesError> in
return network.request(Api.functions.langpack.getDifference(langCode: languageCode, fromVersion: fromVersion))
|> mapError { _ -> SynchronizeLocalizationUpdatesError in return .reset }
|> mapToSignal { result -> Signal<Void, SynchronizeLocalizationUpdatesError> in
let updatedCode: String
let updatedVersion: Int32
var updatedEntries: [LocalizationEntry] = []
switch result {
case let .langPackDifference(code, _, versionValue, strings):
updatedCode = code
updatedVersion = versionValue
for string in strings {
switch string {
case let .langPackString(key, value):
updatedEntries.append(.string(key: key, value: value))
case let .langPackStringPluralized(_, key, zeroValue, oneValue, twoValue, fewValue, manyValue, otherValue):
updatedEntries.append(.pluralizedString(key: key, zero: zeroValue, one: oneValue, two: twoValue, few: fewValue, many: manyValue, other: otherValue))
case let .langPackStringDeleted(key):
updatedEntries.append(.string(key: key, value: ""))
}
}
}
|> introduceError(SynchronizeLocalizationUpdatesError.self)
|> mapToSignal { (primary, secondary) -> Signal<Void, SynchronizeLocalizationUpdatesError> in
var differences: [Signal<Api.LangPackDifference, MTRpcError>] = []
differences.append(network.request(Api.functions.langpack.getDifference(langCode: primary.code, fromVersion: primary.version)))
if let secondary = secondary {
differences.append(network.request(Api.functions.langpack.getDifference(langCode: secondary.code, fromVersion: secondary.version)))
}
return combineLatest(differences)
|> mapError { _ -> SynchronizeLocalizationUpdatesError in return .reset }
|> mapToSignal { differences -> Signal<Void, SynchronizeLocalizationUpdatesError> in
let parsedDifferences = differences.map(parseLangPackDifference)
return postbox.transaction { transaction -> Signal<Void, SynchronizeLocalizationUpdatesError> in
let (primary, secondary) = getLocalization(transaction)
return postbox.transaction { transaction -> Signal<Void, SynchronizeLocalizationUpdatesError> in
let (code, version, entries) = getLocalization(transaction)
if code == updatedCode {
if fromVersion == version {
var updatedEntryKeys = Set<String>()
for entry in updatedEntries {
updatedEntryKeys.insert(entry.key)
}
var mergedEntries: [LocalizationEntry] = []
for entry in entries {
if !updatedEntryKeys.contains(entry.key) {
mergedEntries.append(entry)
}
}
mergedEntries.append(contentsOf: updatedEntries)
transaction.setPreferencesEntry(key: PreferencesKeys.localizationSettings, value: LocalizationSettings(languageCode: updatedCode, localization: Localization(version: updatedVersion, entries: mergedEntries)))
return .fail(.done)
} else {
var currentSettings = transaction.getPreferencesEntry(key: PreferencesKeys.localizationSettings) as? LocalizationSettings ?? LocalizationSettings(primaryComponent: LocalizationComponent(languageCode: "en", localization: Localization(version: 0, entries: []), customPluralizationCode: nil), secondaryComponent: nil)
for difference in parsedDifferences {
let current: (isPrimary: Bool, entries: [LocalizationEntry])
if difference.code == primary.code {
if primary.version != difference.fromVersion {
return .complete()
}
current = (true, primary.entries)
} else if let secondary = secondary, difference.code == secondary.code {
if secondary.version != difference.fromVersion {
return .complete()
}
current = (false, secondary.entries)
} else {
return .fail(.reset)
}
var updatedEntryKeys = Set<String>()
for entry in difference.entries {
updatedEntryKeys.insert(entry.key)
}
var mergedEntries: [LocalizationEntry] = []
for entry in current.entries {
if !updatedEntryKeys.contains(entry.key) {
mergedEntries.append(entry)
}
}
mergedEntries.append(contentsOf: difference.entries)
if current.isPrimary {
currentSettings = LocalizationSettings(primaryComponent: LocalizationComponent(languageCode: currentSettings.primaryComponent.languageCode, localization: Localization(version: difference.version, entries: mergedEntries), customPluralizationCode: currentSettings.primaryComponent.customPluralizationCode), secondaryComponent: currentSettings.secondaryComponent)
} else if let currentSecondary = currentSettings.secondaryComponent {
currentSettings = LocalizationSettings(primaryComponent: currentSettings.primaryComponent, secondaryComponent: LocalizationComponent(languageCode: currentSecondary.languageCode, localization: Localization(version: difference.version, entries: mergedEntries), customPluralizationCode: currentSecondary.customPluralizationCode))
}
}
|> mapError { _ -> SynchronizeLocalizationUpdatesError in return .reset
}
|> switchToLatest
transaction.setPreferencesEntry(key: PreferencesKeys.localizationSettings, value: currentSettings)
return .fail(.done)
}
|> mapError { _ -> SynchronizeLocalizationUpdatesError in
return .reset
}
|> switchToLatest
}
}
return ((poll
|> `catch` { error -> Signal<Void, Void> in
@ -206,8 +228,8 @@ private func synchronizeLocalizationUpdates(transaction: Transaction, postbox: P
return .fail(Void())
case .reset:
return postbox.transaction { transaction -> Signal<Void, Void> in
let (code, _, _) = getLocalization(transaction)
return downoadAndApplyLocalization(postbox: postbox, network: network, languageCode: code)
let (primary, _) = getLocalization(transaction)
return downloadAndApplyLocalization(postbox: postbox, network: network, languageCode: primary.code)
|> mapError { _ -> Void in
return Void()
}
@ -221,41 +243,61 @@ private func synchronizeLocalizationUpdates(transaction: Transaction, postbox: P
}
func tryApplyingLanguageDifference(transaction: Transaction, langCode: String, difference: Api.LangPackDifference) -> Bool {
let (code, version, entries) = getLocalization(transaction)
let (primary, secondary) = getLocalization(transaction)
switch difference {
case let .langPackDifference(updatedCode, fromVersion, updatedVersion, strings):
if fromVersion == version && updatedCode == code {
var updatedEntries: [LocalizationEntry] = []
for string in strings {
switch string {
case let .langPackString(key, value):
updatedEntries.append(.string(key: key, value: value))
case let .langPackStringPluralized(_, key, zeroValue, oneValue, twoValue, fewValue, manyValue, otherValue):
updatedEntries.append(.pluralizedString(key: key, zero: zeroValue, one: oneValue, two: twoValue, few: fewValue, many: manyValue, other: otherValue))
case let .langPackStringDeleted(key):
updatedEntries.append(.string(key: key, value: ""))
}
}
var updatedEntryKeys = Set<String>()
for entry in updatedEntries {
updatedEntryKeys.insert(entry.key)
}
var mergedEntries: [LocalizationEntry] = []
for entry in entries {
if !updatedEntryKeys.contains(entry.key) {
mergedEntries.append(entry)
}
}
mergedEntries.append(contentsOf: updatedEntries)
transaction.setPreferencesEntry(key: PreferencesKeys.localizationSettings, value: LocalizationSettings(languageCode: updatedCode, localization: Localization(version: updatedVersion, entries: mergedEntries)))
return true
} else {
var current: (isPrimary: Bool, version: Int32, entries: [LocalizationEntry])?
if updatedCode == primary.code {
current = (true, primary.version, primary.entries)
} else if let secondary = secondary, secondary.code == updatedCode {
current = (false, secondary.version, secondary.entries)
}
guard let (isPrimary, version, entries) = current else {
return false
}
guard fromVersion == version else {
return false
}
var updatedEntries: [LocalizationEntry] = []
for string in strings {
switch string {
case let .langPackString(key, value):
updatedEntries.append(.string(key: key, value: value))
case let .langPackStringPluralized(_, key, zeroValue, oneValue, twoValue, fewValue, manyValue, otherValue):
updatedEntries.append(.pluralizedString(key: key, zero: zeroValue, one: oneValue, two: twoValue, few: fewValue, many: manyValue, other: otherValue))
case let .langPackStringDeleted(key):
updatedEntries.append(.string(key: key, value: ""))
}
}
var updatedEntryKeys = Set<String>()
for entry in updatedEntries {
updatedEntryKeys.insert(entry.key)
}
var mergedEntries: [LocalizationEntry] = []
for entry in entries {
if !updatedEntryKeys.contains(entry.key) {
mergedEntries.append(entry)
}
}
mergedEntries.append(contentsOf: updatedEntries)
let currentSettings = transaction.getPreferencesEntry(key: PreferencesKeys.localizationSettings) as? LocalizationSettings ?? LocalizationSettings(primaryComponent: LocalizationComponent(languageCode: "en", localization: Localization(version: 0, entries: []), customPluralizationCode: nil), secondaryComponent: nil)
var updatedSettings: LocalizationSettings
if isPrimary {
updatedSettings = LocalizationSettings(primaryComponent: LocalizationComponent(languageCode: currentSettings.primaryComponent.languageCode, localization: Localization(version: updatedVersion, entries: mergedEntries), customPluralizationCode: currentSettings.primaryComponent.customPluralizationCode), secondaryComponent: currentSettings.secondaryComponent)
} else if let currentSecondary = currentSettings.secondaryComponent {
updatedSettings = LocalizationSettings(primaryComponent: currentSettings.primaryComponent, secondaryComponent: LocalizationComponent(languageCode: currentSecondary.languageCode, localization: Localization(version: updatedVersion, entries: mergedEntries), customPluralizationCode: currentSecondary.customPluralizationCode))
} else {
assertionFailure()
return false
}
transaction.setPreferencesEntry(key: PreferencesKeys.localizationSettings, value: updatedSettings)
return true
}
}

View File

@ -10,23 +10,23 @@ import Foundation
#endif
public enum TwoStepVerificationConfiguration {
case notSet(pendingEmailPattern: String)
case set(hint: String, hasRecoveryEmail: Bool, pendingEmailPattern: String, hasSecureValues: Bool)
case notSet(pendingEmail: TwoStepVerificationPendingEmail?)
case set(hint: String, hasRecoveryEmail: Bool, pendingEmail: TwoStepVerificationPendingEmail?, hasSecureValues: Bool)
}
public func twoStepVerificationConfiguration(account: Account) -> Signal<TwoStepVerificationConfiguration, NoError> {
return account.network.request(Api.functions.account.getPassword())
|> retryRequest
|> map { result -> TwoStepVerificationConfiguration in
switch result {
case let .password(password):
if password.currentAlgo != nil {
return .set(hint: password.hint ?? "", hasRecoveryEmail: (password.flags & (1 << 0)) != 0, pendingEmailPattern: password.emailUnconfirmedPattern ?? "", hasSecureValues: (password.flags & (1 << 1)) != 0)
} else {
return .notSet(pendingEmailPattern: password.emailUnconfirmedPattern ?? "")
}
}
|> retryRequest
|> map { result -> TwoStepVerificationConfiguration in
switch result {
case let .password(password):
if password.currentAlgo != nil {
return .set(hint: password.hint ?? "", hasRecoveryEmail: (password.flags & (1 << 0)) != 0, pendingEmail: password.emailUnconfirmedPattern.flatMap({ TwoStepVerificationPendingEmail(pattern: $0, codeLength: nil) }), hasSecureValues: (password.flags & (1 << 1)) != 0)
} else {
return .notSet(pendingEmail: password.emailUnconfirmedPattern.flatMap({ TwoStepVerificationPendingEmail(pattern: $0, codeLength: nil) }))
}
}
}
}
public struct TwoStepVerificationSecureSecret {
@ -97,6 +97,11 @@ public enum UpdateTwoStepVerificationPasswordError {
public struct TwoStepVerificationPendingEmail {
public let pattern: String
public let codeLength: Int32?
public init(pattern: String, codeLength: Int32?) {
self.pattern = pattern
self.codeLength = codeLength
}
}
public enum UpdateTwoStepVerificationPasswordResult {