diff --git a/TelegramCore.xcodeproj/project.pbxproj b/TelegramCore.xcodeproj/project.pbxproj index 186f62b4be..90a7834f94 100644 --- a/TelegramCore.xcodeproj/project.pbxproj +++ b/TelegramCore.xcodeproj/project.pbxproj @@ -85,6 +85,8 @@ D003702B1DA42586004308D3 /* PhoneNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = D003702A1DA42586004308D3 /* PhoneNumber.swift */; }; D00422D321677F4500719B67 /* ManagedAccountPresence.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00422D221677F4500719B67 /* ManagedAccountPresence.swift */; }; D00422D421677F4500719B67 /* ManagedAccountPresence.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00422D221677F4500719B67 /* ManagedAccountPresence.swift */; }; + D00580AE21E2A08900CB7CD3 /* AccountEnvironmentAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00580AD21E2A08900CB7CD3 /* AccountEnvironmentAttribute.swift */; }; + D00580AF21E2A08900CB7CD3 /* AccountEnvironmentAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00580AD21E2A08900CB7CD3 /* AccountEnvironmentAttribute.swift */; }; D00BDA191EE593D600C64C5E /* TelegramChannelAdminRights.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00BDA181EE593D600C64C5E /* TelegramChannelAdminRights.swift */; }; D00BDA1A1EE593D600C64C5E /* TelegramChannelAdminRights.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00BDA181EE593D600C64C5E /* TelegramChannelAdminRights.swift */; }; D00BDA1C1EE5952A00C64C5E /* TelegramChannelBannedRights.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00BDA1B1EE5952A00C64C5E /* TelegramChannelBannedRights.swift */; }; @@ -817,6 +819,7 @@ C2FD33EA1E696C78008D13D4 /* GroupsInCommon.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GroupsInCommon.swift; sourceTree = ""; }; D003702A1DA42586004308D3 /* PhoneNumber.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhoneNumber.swift; sourceTree = ""; }; D00422D221677F4500719B67 /* ManagedAccountPresence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManagedAccountPresence.swift; sourceTree = ""; }; + D00580AD21E2A08900CB7CD3 /* AccountEnvironmentAttribute.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountEnvironmentAttribute.swift; sourceTree = ""; }; D00BDA181EE593D600C64C5E /* TelegramChannelAdminRights.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TelegramChannelAdminRights.swift; sourceTree = ""; }; D00BDA1B1EE5952A00C64C5E /* TelegramChannelBannedRights.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TelegramChannelBannedRights.swift; sourceTree = ""; }; D00C7CCB1E3620C30080C3D5 /* CachedChannelParticipants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CachedChannelParticipants.swift; sourceTree = ""; }; @@ -1663,6 +1666,7 @@ isa = PBXGroup; children = ( D03E5E0B1E55E02D0029569A /* LoggedOutAccountAttribute.swift */, + D00580AD21E2A08900CB7CD3 /* AccountEnvironmentAttribute.swift */, ); name = Accounts; sourceTree = ""; @@ -2417,6 +2421,7 @@ D0613FCA1E60440600202CDB /* InvitationLinks.swift in Sources */, D03B0D721D631ABA00955575 /* SearchMessages.swift in Sources */, D0DC35501DE36900000195EB /* ChatContextResult.swift in Sources */, + D00580AE21E2A08900CB7CD3 /* AccountEnvironmentAttribute.swift in Sources */, D00D34391E6EC9520057B307 /* TeleramMediaUnsupported.swift in Sources */, D00D97CA1E32917C00E5C2B6 /* PeerInputActivityManager.swift in Sources */, C2FD33E11E680E9E008D13D4 /* RequestUserPhotos.swift in Sources */, @@ -2729,6 +2734,7 @@ D0AB262C21C3CE80008F6685 /* Polls.swift in Sources */, D073CE6C1DCBCF17007511FD /* TextEntitiesMessageAttribute.swift in Sources */, D03C53751DAD5CA9004C17B3 /* TelegramUserPresence.swift in Sources */, + D00580AF21E2A08900CB7CD3 /* AccountEnvironmentAttribute.swift in Sources */, D05452081E7B5093006EEF19 /* LoadedStickerPack.swift in Sources */, D0561DE41E5737FC00E6B9E9 /* UpdatePeerInfo.swift in Sources */, D042C6841E8D9DF800C863B0 /* Unixtime.swift in Sources */, diff --git a/TelegramCore/AccountEnvironmentAttribute.swift b/TelegramCore/AccountEnvironmentAttribute.swift new file mode 100644 index 0000000000..e850b9d881 --- /dev/null +++ b/TelegramCore/AccountEnvironmentAttribute.swift @@ -0,0 +1,37 @@ +import Foundation +#if os(macOS) +import PostboxMac +#else +import Postbox +#endif + +public enum AccountEnvironment: Int32 { + case production = 0 + case test = 1 +} + +public final class AccountEnvironmentAttribute: AccountRecordAttribute { + public let environment: AccountEnvironment + + public init(environment: AccountEnvironment) { + self.environment = environment + } + + public init(decoder: PostboxDecoder) { + self.environment = AccountEnvironment(rawValue: decoder.decodeInt32ForKey("environment", orElse: 0)) ?? .production + } + + public func encode(_ encoder: PostboxEncoder) { + encoder.encodeInt32(self.environment.rawValue, forKey: "environment") + } + + public func isEqual(to: AccountRecordAttribute) -> Bool { + guard let to = to as? AccountEnvironmentAttribute else { + return false + } + if self.environment != to.environment { + return false + } + return true + } +} diff --git a/TelegramCore/AccountManager.swift b/TelegramCore/AccountManager.swift index a62eff3af3..971b239aa9 100644 --- a/TelegramCore/AccountManager.swift +++ b/TelegramCore/AccountManager.swift @@ -74,6 +74,7 @@ private var declaredEncodables: Void = { declareEncodable(RecentPeerItem.self, f: { RecentPeerItem(decoder: $0) }) declareEncodable(RecentHashtagItem.self, f: { RecentHashtagItem(decoder: $0) }) declareEncodable(LoggedOutAccountAttribute.self, f: { LoggedOutAccountAttribute(decoder: $0) }) + declareEncodable(AccountEnvironmentAttribute.self, f: { AccountEnvironmentAttribute(decoder: $0) }) declareEncodable(CloudChatClearHistoryOperation.self, f: { CloudChatClearHistoryOperation(decoder: $0) }) declareEncodable(OutgoingContentInfoMessageAttribute.self, f: { OutgoingContentInfoMessageAttribute(decoder: $0) }) declareEncodable(ConsumableContentMessageAttribute.self, f: { ConsumableContentMessageAttribute(decoder: $0) }) @@ -189,17 +190,24 @@ public func temporaryAccount(manager: AccountManager, rootPath: String) -> Signa } } -public func currentAccount(allocateIfNotExists: Bool, networkArguments: NetworkInitializationArguments, supplementary: Bool, manager: AccountManager, rootPath: String, beginWithTestingEnvironment: Bool, auxiliaryMethods: AccountAuxiliaryMethods) -> Signal { - return manager.currentAccountId(allocateIfNotExists: allocateIfNotExists) +public func currentAccount(allocateIfNotExists: Bool, networkArguments: NetworkInitializationArguments, supplementary: Bool, manager: AccountManager, rootPath: String, auxiliaryMethods: AccountAuxiliaryMethods) -> Signal { + return manager.currentAccountRecord(allocateIfNotExists: allocateIfNotExists) |> distinctUntilChanged(isEqual: { lhs, rhs in - return lhs == rhs + return lhs?.0 == rhs?.0 }) - |> mapToSignal { id -> Signal in - if let id = id { + |> mapToSignal { record -> Signal in + if let record = record { let reload = ValuePromise(true, ignoreRepeated: false) return reload.get() |> mapToSignal { _ -> Signal in - return accountWithId(networkArguments: networkArguments, id: id, supplementary: supplementary, rootPath: rootPath, beginWithTestingEnvironment: beginWithTestingEnvironment, auxiliaryMethods: auxiliaryMethods) + let beginWithTestingEnvironment = record.1.contains(where: { attribute in + if let attribute = attribute as? AccountEnvironmentAttribute, case .test = attribute.environment { + return true + } else { + return false + } + }) + return accountWithId(networkArguments: networkArguments, id: record.0, supplementary: supplementary, rootPath: rootPath, beginWithTestingEnvironment: beginWithTestingEnvironment, auxiliaryMethods: auxiliaryMethods) |> mapToSignal { accountResult -> Signal in let postbox: Postbox let initialKind: AccountKind @@ -267,7 +275,7 @@ public func logoutFromAccount(id: AccountRecordId, accountManager: AccountManage return nil } }) - if transaction.getCurrentId() == id { + if transaction.getCurrent()?.0 == id { let updatedId = transaction.createRecord([]) transaction.setCurrentId(updatedId) } @@ -289,13 +297,13 @@ public func managedCleanupAccounts(networkArguments: NetworkInitializationArgume }).start() let disposable = accountManager.accountRecords().start(next: { view in var disposeList: [(AccountRecordId, MetaDisposable)] = [] - var beginList: [(AccountRecordId, MetaDisposable)] = [] + var beginList: [(AccountRecordId, [AccountRecordAttribute], MetaDisposable)] = [] let _ = loggedOutAccounts.modify { disposables in - var validIds = Set() + var validIds: [AccountRecordId: [AccountRecordAttribute]] = [:] outer: for record in view.records { for attribute in record.attributes { if attribute is LoggedOutAccountAttribute { - validIds.insert(record.id) + validIds[record.id] = record.attributes continue outer } } @@ -304,7 +312,7 @@ public func managedCleanupAccounts(networkArguments: NetworkInitializationArgume var disposables = disposables for id in disposables.keys { - if !validIds.contains(id) { + if validIds[id] == nil { disposeList.append((id, disposables[id]!)) } } @@ -313,10 +321,10 @@ public func managedCleanupAccounts(networkArguments: NetworkInitializationArgume disposables.removeValue(forKey: id) } - for id in validIds { + for (id, attributes) in validIds { if disposables[id] == nil { let disposable = MetaDisposable() - beginList.append((id, disposable)) + beginList.append((id, attributes, disposable)) disposables[id] = disposable } } @@ -326,9 +334,9 @@ public func managedCleanupAccounts(networkArguments: NetworkInitializationArgume for (_, disposable) in disposeList { disposable.dispose() } - for (id, disposable) in beginList { + for (id, attributes, disposable) in beginList { Logger.shared.log("managedCleanupAccounts", "cleanup \(id), current is \(String(describing: view.currentRecord?.id))") - disposable.set(cleanupAccount(networkArguments: networkArguments, accountManager: accountManager, id: id, rootPath: rootPath, auxiliaryMethods: auxiliaryMethods).start()) + disposable.set(cleanupAccount(networkArguments: networkArguments, accountManager: accountManager, id: id, attributes: attributes, rootPath: rootPath, auxiliaryMethods: auxiliaryMethods).start()) } var validPaths = Set() @@ -356,8 +364,15 @@ public func managedCleanupAccounts(networkArguments: NetworkInitializationArgume } } -private func cleanupAccount(networkArguments: NetworkInitializationArguments, accountManager: AccountManager, id: AccountRecordId, rootPath: String, auxiliaryMethods: AccountAuxiliaryMethods) -> Signal { - return accountWithId(networkArguments: networkArguments, id: id, supplementary: true, rootPath: rootPath, beginWithTestingEnvironment: false, auxiliaryMethods: auxiliaryMethods) +private func cleanupAccount(networkArguments: NetworkInitializationArguments, accountManager: AccountManager, id: AccountRecordId, attributes: [AccountRecordAttribute], rootPath: String, auxiliaryMethods: AccountAuxiliaryMethods) -> Signal { + let beginWithTestingEnvironment = attributes.contains(where: { attribute in + if let attribute = attribute as? AccountEnvironmentAttribute, case .test = attribute.environment { + return true + } else { + return false + } + }) + return accountWithId(networkArguments: networkArguments, id: id, supplementary: true, rootPath: rootPath, beginWithTestingEnvironment: beginWithTestingEnvironment, auxiliaryMethods: auxiliaryMethods) |> mapToSignal { account -> Signal in switch account { case .upgrading: diff --git a/TelegramCore/AddPeerMember.swift b/TelegramCore/AddPeerMember.swift index b92373ac67..6e6d222b12 100644 --- a/TelegramCore/AddPeerMember.swift +++ b/TelegramCore/AddPeerMember.swift @@ -59,6 +59,7 @@ public func addGroupMember(account: Account, peerId: PeerId, memberId: PeerId) - public enum AddChannelMemberError { case generic case restricted + case limitExceeded } public func addChannelMember(account: Account, peerId: PeerId, memberId: PeerId) -> Signal<(ChannelParticipant?, RenderedChannelParticipant), AddChannelMemberError> { @@ -79,7 +80,12 @@ public func addChannelMember(account: Account, peerId: PeerId, memberId: PeerId) return account.network.request(Api.functions.channels.inviteToChannel(channel: inputChannel, users: [inputUser])) |> map { [$0] } |> `catch` { error -> Signal<[Api.Updates], AddChannelMemberError> in - return .fail(.generic) + switch error.errorDescription { + case "USERS_TOO_MUCH": + return .fail(.limitExceeded) + default: + return .fail(.generic) + } } |> mapToSignal { result -> Signal<(ChannelParticipant?, RenderedChannelParticipant), AddChannelMemberError> in for updates in result { @@ -146,7 +152,6 @@ public func addChannelMember(account: Account, peerId: PeerId, memberId: PeerId) } } - public func addChannelMembers(account: Account, peerId: PeerId, memberIds: [PeerId]) -> Signal { let signal = account.postbox.transaction { transaction -> Signal in var memberPeerIds: [PeerId:Peer] = [:] @@ -165,10 +170,12 @@ public func addChannelMembers(account: Account, peerId: PeerId, memberIds: [Peer let signal = account.network.request(Api.functions.channels.inviteToChannel(channel: inputChannel, users: inputUsers)) |> mapError { error -> AddChannelMemberError in switch error.errorDescription { - case "USER_PRIVACY_RESTRICTED": - return .restricted - default: - return .generic + case "USER_PRIVACY_RESTRICTED": + return .restricted + case "USERS_TOO_MUCH": + return .limitExceeded + default: + return .generic } } |> map { result in diff --git a/TelegramCore/Namespaces.swift b/TelegramCore/Namespaces.swift index 6c82df15bc..25cb101035 100644 --- a/TelegramCore/Namespaces.swift +++ b/TelegramCore/Namespaces.swift @@ -255,6 +255,7 @@ public struct PreferencesKeys { private enum SharedDataKeyValues: Int32 { case loggingSettings = 0 + case accountInitializationSettings = 1 } public struct SharedDataKeys { @@ -263,6 +264,12 @@ public struct SharedDataKeys { key.setInt32(0, value: SharedDataKeyValues.loggingSettings.rawValue) return key }() + + public static let accountInitializationSettings: ValueBoxKey = { + let key = ValueBoxKey(length: 4) + key.setInt32(0, value: SharedDataKeyValues.accountInitializationSettings.rawValue) + return key + }() } public func applicationSpecificItemCacheCollectionId(_ value: Int8) -> Int8 {