mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 14:45:21 +00:00
no message
This commit is contained in:
@@ -129,6 +129,12 @@
|
|||||||
D03C53741DAD5CA9004C17B3 /* CachedChannelData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B843841DA6EDC4005F29E1 /* CachedChannelData.swift */; };
|
D03C53741DAD5CA9004C17B3 /* CachedChannelData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B843841DA6EDC4005F29E1 /* CachedChannelData.swift */; };
|
||||||
D03C53751DAD5CA9004C17B3 /* TelegramUserPresence.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B844521DAC0773005F29E1 /* TelegramUserPresence.swift */; };
|
D03C53751DAD5CA9004C17B3 /* TelegramUserPresence.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B844521DAC0773005F29E1 /* TelegramUserPresence.swift */; };
|
||||||
D03C53771DAFF20F004C17B3 /* MultipartUpload.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03C53761DAFF20F004C17B3 /* MultipartUpload.swift */; };
|
D03C53771DAFF20F004C17B3 /* MultipartUpload.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03C53761DAFF20F004C17B3 /* MultipartUpload.swift */; };
|
||||||
|
D03E5E0C1E55E02D0029569A /* LoggedOutAccountAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E5E0B1E55E02D0029569A /* LoggedOutAccountAttribute.swift */; };
|
||||||
|
D03E5E0D1E55E02D0029569A /* LoggedOutAccountAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E5E0B1E55E02D0029569A /* LoggedOutAccountAttribute.swift */; };
|
||||||
|
D041E3F51E535464008C24B4 /* AddPeerMember.swift in Sources */ = {isa = PBXBuildFile; fileRef = D041E3F41E535464008C24B4 /* AddPeerMember.swift */; };
|
||||||
|
D041E3F61E535464008C24B4 /* AddPeerMember.swift in Sources */ = {isa = PBXBuildFile; fileRef = D041E3F41E535464008C24B4 /* AddPeerMember.swift */; };
|
||||||
|
D041E3F81E535A88008C24B4 /* RemovePeerMember.swift in Sources */ = {isa = PBXBuildFile; fileRef = D041E3F71E535A88008C24B4 /* RemovePeerMember.swift */; };
|
||||||
|
D041E3F91E535A88008C24B4 /* RemovePeerMember.swift in Sources */ = {isa = PBXBuildFile; fileRef = D041E3F71E535A88008C24B4 /* RemovePeerMember.swift */; };
|
||||||
D0448C8E1E22993C005A61A7 /* ProcessSecretChatIncomingDecryptedOperations.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0448C8D1E22993C005A61A7 /* ProcessSecretChatIncomingDecryptedOperations.swift */; };
|
D0448C8E1E22993C005A61A7 /* ProcessSecretChatIncomingDecryptedOperations.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0448C8D1E22993C005A61A7 /* ProcessSecretChatIncomingDecryptedOperations.swift */; };
|
||||||
D0448C8F1E22993C005A61A7 /* ProcessSecretChatIncomingDecryptedOperations.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0448C8D1E22993C005A61A7 /* ProcessSecretChatIncomingDecryptedOperations.swift */; };
|
D0448C8F1E22993C005A61A7 /* ProcessSecretChatIncomingDecryptedOperations.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0448C8D1E22993C005A61A7 /* ProcessSecretChatIncomingDecryptedOperations.swift */; };
|
||||||
D0448C911E251F96005A61A7 /* SecretChatEncryption.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0448C901E251F96005A61A7 /* SecretChatEncryption.swift */; };
|
D0448C911E251F96005A61A7 /* SecretChatEncryption.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0448C901E251F96005A61A7 /* SecretChatEncryption.swift */; };
|
||||||
@@ -178,6 +184,10 @@
|
|||||||
D050F26C1E4A5B6D00988324 /* UpdatePeers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BC386F1E40853E0044D6FE /* UpdatePeers.swift */; };
|
D050F26C1E4A5B6D00988324 /* UpdatePeers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BC386F1E40853E0044D6FE /* UpdatePeers.swift */; };
|
||||||
D050F26D1E4A5B6D00988324 /* CreateGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BC386D1E3FDAB70044D6FE /* CreateGroup.swift */; };
|
D050F26D1E4A5B6D00988324 /* CreateGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BC386D1E3FDAB70044D6FE /* CreateGroup.swift */; };
|
||||||
D050F26E1E4A5B6D00988324 /* RemovePeerChat.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BC38741E40A7F70044D6FE /* RemovePeerChat.swift */; };
|
D050F26E1E4A5B6D00988324 /* RemovePeerChat.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BC38741E40A7F70044D6FE /* RemovePeerChat.swift */; };
|
||||||
|
D0561DE31E5737FC00E6B9E9 /* UpdatePeerInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0561DE21E5737FC00E6B9E9 /* UpdatePeerInfo.swift */; };
|
||||||
|
D0561DE41E5737FC00E6B9E9 /* UpdatePeerInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0561DE21E5737FC00E6B9E9 /* UpdatePeerInfo.swift */; };
|
||||||
|
D0561DEA1E5754FA00E6B9E9 /* ChannelAdmins.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0561DE91E5754FA00E6B9E9 /* ChannelAdmins.swift */; };
|
||||||
|
D0561DEB1E5754FA00E6B9E9 /* ChannelAdmins.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0561DE91E5754FA00E6B9E9 /* ChannelAdmins.swift */; };
|
||||||
D067066C1D512ADB00DED3E3 /* Postbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D06706671D512ADB00DED3E3 /* Postbox.framework */; };
|
D067066C1D512ADB00DED3E3 /* Postbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D06706671D512ADB00DED3E3 /* Postbox.framework */; };
|
||||||
D067066D1D512ADB00DED3E3 /* SwiftSignalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D06706681D512ADB00DED3E3 /* SwiftSignalKit.framework */; };
|
D067066D1D512ADB00DED3E3 /* SwiftSignalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D06706681D512ADB00DED3E3 /* SwiftSignalKit.framework */; };
|
||||||
D073CE5D1DCB97F6007511FD /* ForwardSourceInfoAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = D073CE5C1DCB97F6007511FD /* ForwardSourceInfoAttribute.swift */; };
|
D073CE5D1DCB97F6007511FD /* ForwardSourceInfoAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = D073CE5C1DCB97F6007511FD /* ForwardSourceInfoAttribute.swift */; };
|
||||||
@@ -310,6 +320,10 @@
|
|||||||
D0BC38791E40BAF20044D6FE /* SynchronizePinnedChatsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BC38781E40BAF20044D6FE /* SynchronizePinnedChatsOperation.swift */; };
|
D0BC38791E40BAF20044D6FE /* SynchronizePinnedChatsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BC38781E40BAF20044D6FE /* SynchronizePinnedChatsOperation.swift */; };
|
||||||
D0BC387B1E40D2880044D6FE /* TogglePeerChatPinned.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BC387A1E40D2880044D6FE /* TogglePeerChatPinned.swift */; };
|
D0BC387B1E40D2880044D6FE /* TogglePeerChatPinned.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BC387A1E40D2880044D6FE /* TogglePeerChatPinned.swift */; };
|
||||||
D0BC387C1E40D2880044D6FE /* TogglePeerChatPinned.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BC387A1E40D2880044D6FE /* TogglePeerChatPinned.swift */; };
|
D0BC387C1E40D2880044D6FE /* TogglePeerChatPinned.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BC387A1E40D2880044D6FE /* TogglePeerChatPinned.swift */; };
|
||||||
|
D0BEAF5D1E54941B00BD963D /* Authorization.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BEAF5C1E54941B00BD963D /* Authorization.swift */; };
|
||||||
|
D0BEAF5E1E54941B00BD963D /* Authorization.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BEAF5C1E54941B00BD963D /* Authorization.swift */; };
|
||||||
|
D0BEAF601E54ACF900BD963D /* AccountManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BEAF5F1E54ACF900BD963D /* AccountManager.swift */; };
|
||||||
|
D0BEAF611E54ACF900BD963D /* AccountManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BEAF5F1E54ACF900BD963D /* AccountManager.swift */; };
|
||||||
D0CAF2EA1D75EC600011F558 /* MtProtoKitDynamic.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0CAF2E91D75EC600011F558 /* MtProtoKitDynamic.framework */; };
|
D0CAF2EA1D75EC600011F558 /* MtProtoKitDynamic.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0CAF2E91D75EC600011F558 /* MtProtoKitDynamic.framework */; };
|
||||||
D0DC354E1DE368F7000195EB /* RequestChatContextResults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DC354D1DE368F7000195EB /* RequestChatContextResults.swift */; };
|
D0DC354E1DE368F7000195EB /* RequestChatContextResults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DC354D1DE368F7000195EB /* RequestChatContextResults.swift */; };
|
||||||
D0DC35501DE36900000195EB /* ChatContextResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DC354F1DE36900000195EB /* ChatContextResult.swift */; };
|
D0DC35501DE36900000195EB /* ChatContextResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DC354F1DE36900000195EB /* ChatContextResult.swift */; };
|
||||||
@@ -464,6 +478,9 @@
|
|||||||
D03B0E691D63283000955575 /* libwebp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libwebp.a; path = "third-party/libwebp/lib/libwebp.a"; sourceTree = "<group>"; };
|
D03B0E691D63283000955575 /* libwebp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libwebp.a; path = "third-party/libwebp/lib/libwebp.a"; sourceTree = "<group>"; };
|
||||||
D03B0E6B1D63283C00955575 /* libiconv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libiconv.tbd; path = usr/lib/libiconv.tbd; sourceTree = SDKROOT; };
|
D03B0E6B1D63283C00955575 /* libiconv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libiconv.tbd; path = usr/lib/libiconv.tbd; sourceTree = SDKROOT; };
|
||||||
D03C53761DAFF20F004C17B3 /* MultipartUpload.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MultipartUpload.swift; sourceTree = "<group>"; };
|
D03C53761DAFF20F004C17B3 /* MultipartUpload.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MultipartUpload.swift; sourceTree = "<group>"; };
|
||||||
|
D03E5E0B1E55E02D0029569A /* LoggedOutAccountAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoggedOutAccountAttribute.swift; sourceTree = "<group>"; };
|
||||||
|
D041E3F41E535464008C24B4 /* AddPeerMember.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddPeerMember.swift; sourceTree = "<group>"; };
|
||||||
|
D041E3F71E535A88008C24B4 /* RemovePeerMember.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemovePeerMember.swift; sourceTree = "<group>"; };
|
||||||
D0448C8D1E22993C005A61A7 /* ProcessSecretChatIncomingDecryptedOperations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProcessSecretChatIncomingDecryptedOperations.swift; sourceTree = "<group>"; };
|
D0448C8D1E22993C005A61A7 /* ProcessSecretChatIncomingDecryptedOperations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProcessSecretChatIncomingDecryptedOperations.swift; sourceTree = "<group>"; };
|
||||||
D0448C901E251F96005A61A7 /* SecretChatEncryption.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretChatEncryption.swift; sourceTree = "<group>"; };
|
D0448C901E251F96005A61A7 /* SecretChatEncryption.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretChatEncryption.swift; sourceTree = "<group>"; };
|
||||||
D0448C981E268F9A005A61A7 /* SecretApiLayer46.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretApiLayer46.swift; sourceTree = "<group>"; };
|
D0448C981E268F9A005A61A7 /* SecretApiLayer46.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretApiLayer46.swift; sourceTree = "<group>"; };
|
||||||
@@ -477,6 +494,8 @@
|
|||||||
D049EAF41E44DF3300A2CD3A /* AccountState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountState.swift; sourceTree = "<group>"; };
|
D049EAF41E44DF3300A2CD3A /* AccountState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountState.swift; sourceTree = "<group>"; };
|
||||||
D050F20F1E48AB0600988324 /* InteractivePhoneFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InteractivePhoneFormatter.swift; sourceTree = "<group>"; };
|
D050F20F1E48AB0600988324 /* InteractivePhoneFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InteractivePhoneFormatter.swift; sourceTree = "<group>"; };
|
||||||
D050F2501E4A59C200988324 /* JoinLink.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JoinLink.swift; sourceTree = "<group>"; };
|
D050F2501E4A59C200988324 /* JoinLink.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JoinLink.swift; sourceTree = "<group>"; };
|
||||||
|
D0561DE21E5737FC00E6B9E9 /* UpdatePeerInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpdatePeerInfo.swift; sourceTree = "<group>"; };
|
||||||
|
D0561DE91E5754FA00E6B9E9 /* ChannelAdmins.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChannelAdmins.swift; sourceTree = "<group>"; };
|
||||||
D06706641D512ADB00DED3E3 /* AsyncDisplayKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AsyncDisplayKit.framework; path = "../../../../Library/Developer/Xcode/DerivedData/Telegram-iOS-diblohvjozhgaifjcniwdlixlilx/Build/Products/Debug-iphonesimulator/AsyncDisplayKit.framework"; sourceTree = "<group>"; };
|
D06706641D512ADB00DED3E3 /* AsyncDisplayKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AsyncDisplayKit.framework; path = "../../../../Library/Developer/Xcode/DerivedData/Telegram-iOS-diblohvjozhgaifjcniwdlixlilx/Build/Products/Debug-iphonesimulator/AsyncDisplayKit.framework"; sourceTree = "<group>"; };
|
||||||
D06706651D512ADB00DED3E3 /* Display.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Display.framework; path = "../../../../Library/Developer/Xcode/DerivedData/Telegram-iOS-diblohvjozhgaifjcniwdlixlilx/Build/Products/Debug-iphonesimulator/Display.framework"; sourceTree = "<group>"; };
|
D06706651D512ADB00DED3E3 /* Display.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Display.framework; path = "../../../../Library/Developer/Xcode/DerivedData/Telegram-iOS-diblohvjozhgaifjcniwdlixlilx/Build/Products/Debug-iphonesimulator/Display.framework"; sourceTree = "<group>"; };
|
||||||
D06706671D512ADB00DED3E3 /* Postbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Postbox.framework; path = "../../../../Library/Developer/Xcode/DerivedData/Telegram-iOS-diblohvjozhgaifjcniwdlixlilx/Build/Products/Debug-iphonesimulator/Postbox.framework"; sourceTree = "<group>"; };
|
D06706671D512ADB00DED3E3 /* Postbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Postbox.framework; path = "../../../../Library/Developer/Xcode/DerivedData/Telegram-iOS-diblohvjozhgaifjcniwdlixlilx/Build/Products/Debug-iphonesimulator/Postbox.framework"; sourceTree = "<group>"; };
|
||||||
@@ -556,6 +575,8 @@
|
|||||||
D0BC38761E40BAAA0044D6FE /* ManagedSynchronizePinnedChatsOperations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedSynchronizePinnedChatsOperations.swift; sourceTree = "<group>"; };
|
D0BC38761E40BAAA0044D6FE /* ManagedSynchronizePinnedChatsOperations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedSynchronizePinnedChatsOperations.swift; sourceTree = "<group>"; };
|
||||||
D0BC38781E40BAF20044D6FE /* SynchronizePinnedChatsOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SynchronizePinnedChatsOperation.swift; sourceTree = "<group>"; };
|
D0BC38781E40BAF20044D6FE /* SynchronizePinnedChatsOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SynchronizePinnedChatsOperation.swift; sourceTree = "<group>"; };
|
||||||
D0BC387A1E40D2880044D6FE /* TogglePeerChatPinned.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TogglePeerChatPinned.swift; sourceTree = "<group>"; };
|
D0BC387A1E40D2880044D6FE /* TogglePeerChatPinned.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TogglePeerChatPinned.swift; sourceTree = "<group>"; };
|
||||||
|
D0BEAF5C1E54941B00BD963D /* Authorization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Authorization.swift; sourceTree = "<group>"; };
|
||||||
|
D0BEAF5F1E54ACF900BD963D /* AccountManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountManager.swift; sourceTree = "<group>"; };
|
||||||
D0CAF2E91D75EC600011F558 /* MtProtoKitDynamic.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MtProtoKitDynamic.framework; path = "../../../../Library/Developer/Xcode/DerivedData/Telegram-iOS-diblohvjozhgaifjcniwdlixlilx/Build/Products/Debug-iphonesimulator/MtProtoKitDynamic.framework"; sourceTree = "<group>"; };
|
D0CAF2E91D75EC600011F558 /* MtProtoKitDynamic.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MtProtoKitDynamic.framework; path = "../../../../Library/Developer/Xcode/DerivedData/Telegram-iOS-diblohvjozhgaifjcniwdlixlilx/Build/Products/Debug-iphonesimulator/MtProtoKitDynamic.framework"; sourceTree = "<group>"; };
|
||||||
D0DC354D1DE368F7000195EB /* RequestChatContextResults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestChatContextResults.swift; sourceTree = "<group>"; };
|
D0DC354D1DE368F7000195EB /* RequestChatContextResults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestChatContextResults.swift; sourceTree = "<group>"; };
|
||||||
D0DC354F1DE36900000195EB /* ChatContextResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatContextResult.swift; sourceTree = "<group>"; };
|
D0DC354F1DE36900000195EB /* ChatContextResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatContextResult.swift; sourceTree = "<group>"; };
|
||||||
@@ -708,6 +729,7 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
D03B0CD21D62244300955575 /* Namespaces.swift */,
|
D03B0CD21D62244300955575 /* Namespaces.swift */,
|
||||||
|
D03E5E0A1E55E0220029569A /* Accounts */,
|
||||||
D03B0CD01D62242C00955575 /* Peers */,
|
D03B0CD01D62242C00955575 /* Peers */,
|
||||||
D03B0CD11D62242F00955575 /* Messages */,
|
D03B0CD11D62242F00955575 /* Messages */,
|
||||||
D0FA8B961E1E952D001E855B /* Secret Chats */,
|
D0FA8B961E1E952D001E855B /* Secret Chats */,
|
||||||
@@ -864,6 +886,8 @@
|
|||||||
D00C7CCE1E3628180080C3D5 /* UpdateCachedChannelParticipants.swift */,
|
D00C7CCE1E3628180080C3D5 /* UpdateCachedChannelParticipants.swift */,
|
||||||
D0E6521E1E3A364A004EEA91 /* UpdateAccountPeerName.swift */,
|
D0E6521E1E3A364A004EEA91 /* UpdateAccountPeerName.swift */,
|
||||||
D08774FB1E3E39F600A97350 /* ManagedGlobalNotificationSettings.swift */,
|
D08774FB1E3E39F600A97350 /* ManagedGlobalNotificationSettings.swift */,
|
||||||
|
D0BEAF5C1E54941B00BD963D /* Authorization.swift */,
|
||||||
|
D0BEAF5F1E54ACF900BD963D /* AccountManager.swift */,
|
||||||
);
|
);
|
||||||
name = Account;
|
name = Account;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -904,6 +928,14 @@
|
|||||||
name = "Supporting Files";
|
name = "Supporting Files";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
D03E5E0A1E55E0220029569A /* Accounts */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
D03E5E0B1E55E02D0029569A /* LoggedOutAccountAttribute.swift */,
|
||||||
|
);
|
||||||
|
name = Accounts;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
D06706631D512ADA00DED3E3 /* Frameworks */ = {
|
D06706631D512ADA00DED3E3 /* Frameworks */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -1009,6 +1041,10 @@
|
|||||||
C2366C821E4F3EAA0097CCFF /* GroupReturnAndLeft.swift */,
|
C2366C821E4F3EAA0097CCFF /* GroupReturnAndLeft.swift */,
|
||||||
C2366C851E4F403C0097CCFF /* UsernameAvailability.swift */,
|
C2366C851E4F403C0097CCFF /* UsernameAvailability.swift */,
|
||||||
C2366C881E4F40480097CCFF /* SupportPeerId.swift */,
|
C2366C881E4F40480097CCFF /* SupportPeerId.swift */,
|
||||||
|
D041E3F41E535464008C24B4 /* AddPeerMember.swift */,
|
||||||
|
D041E3F71E535A88008C24B4 /* RemovePeerMember.swift */,
|
||||||
|
D0561DE21E5737FC00E6B9E9 /* UpdatePeerInfo.swift */,
|
||||||
|
D0561DE91E5754FA00E6B9E9 /* ChannelAdmins.swift */,
|
||||||
);
|
);
|
||||||
name = Peers;
|
name = Peers;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -1267,6 +1303,7 @@
|
|||||||
D03B0CDB1D62245F00955575 /* ApiUtils.swift in Sources */,
|
D03B0CDB1D62245F00955575 /* ApiUtils.swift in Sources */,
|
||||||
D0B843C91DA7FF30005F29E1 /* NBPhoneNumberDesc.m in Sources */,
|
D0B843C91DA7FF30005F29E1 /* NBPhoneNumberDesc.m in Sources */,
|
||||||
D03B0CE61D6224A700955575 /* ReplyMessageAttribute.swift in Sources */,
|
D03B0CE61D6224A700955575 /* ReplyMessageAttribute.swift in Sources */,
|
||||||
|
D0BEAF601E54ACF900BD963D /* AccountManager.swift in Sources */,
|
||||||
D02ABC7E1E3109F000CAE539 /* CloudChatRemoveMessagesOperation.swift in Sources */,
|
D02ABC7E1E3109F000CAE539 /* CloudChatRemoveMessagesOperation.swift in Sources */,
|
||||||
D0448CA51E29215A005A61A7 /* MediaResourceApiUtils.swift in Sources */,
|
D0448CA51E29215A005A61A7 /* MediaResourceApiUtils.swift in Sources */,
|
||||||
D03C53771DAFF20F004C17B3 /* MultipartUpload.swift in Sources */,
|
D03C53771DAFF20F004C17B3 /* MultipartUpload.swift in Sources */,
|
||||||
@@ -1306,6 +1343,7 @@
|
|||||||
D03B0CD31D62244300955575 /* Namespaces.swift in Sources */,
|
D03B0CD31D62244300955575 /* Namespaces.swift in Sources */,
|
||||||
D01D6BF91E42A713006151C6 /* SearchStickers.swift in Sources */,
|
D01D6BF91E42A713006151C6 /* SearchStickers.swift in Sources */,
|
||||||
D0FA8BB91E2240B4001E855B /* SecretChatIncomingDecryptedOperation.swift in Sources */,
|
D0FA8BB91E2240B4001E855B /* SecretChatIncomingDecryptedOperation.swift in Sources */,
|
||||||
|
D0561DE31E5737FC00E6B9E9 /* UpdatePeerInfo.swift in Sources */,
|
||||||
D0DF0C8A1D819C7E008AEB01 /* JoinChannel.swift in Sources */,
|
D0DF0C8A1D819C7E008AEB01 /* JoinChannel.swift in Sources */,
|
||||||
D0F7AB2F1DCF507E009AD9A1 /* ReplyMarkupMessageAttribute.swift in Sources */,
|
D0F7AB2F1DCF507E009AD9A1 /* ReplyMarkupMessageAttribute.swift in Sources */,
|
||||||
D0B843971DA7FBBC005F29E1 /* ChangePeerNotificationSettings.swift in Sources */,
|
D0B843971DA7FBBC005F29E1 /* ChangePeerNotificationSettings.swift in Sources */,
|
||||||
@@ -1320,6 +1358,7 @@
|
|||||||
D0DC354E1DE368F7000195EB /* RequestChatContextResults.swift in Sources */,
|
D0DC354E1DE368F7000195EB /* RequestChatContextResults.swift in Sources */,
|
||||||
D0BC38771E40BAAA0044D6FE /* ManagedSynchronizePinnedChatsOperations.swift in Sources */,
|
D0BC38771E40BAAA0044D6FE /* ManagedSynchronizePinnedChatsOperations.swift in Sources */,
|
||||||
D0B843851DA6EDC4005F29E1 /* CachedChannelData.swift in Sources */,
|
D0B843851DA6EDC4005F29E1 /* CachedChannelData.swift in Sources */,
|
||||||
|
D0BEAF5D1E54941B00BD963D /* Authorization.swift in Sources */,
|
||||||
D0B843831DA6EDB8005F29E1 /* CachedGroupData.swift in Sources */,
|
D0B843831DA6EDB8005F29E1 /* CachedGroupData.swift in Sources */,
|
||||||
D0E35A121DE4A25E00BC6096 /* OutgoingChatContextResultMessageAttribute.swift in Sources */,
|
D0E35A121DE4A25E00BC6096 /* OutgoingChatContextResultMessageAttribute.swift in Sources */,
|
||||||
D0B844531DAC0773005F29E1 /* TelegramUserPresence.swift in Sources */,
|
D0B844531DAC0773005F29E1 /* TelegramUserPresence.swift in Sources */,
|
||||||
@@ -1331,6 +1370,7 @@
|
|||||||
D03B0D0C1D62255C00955575 /* AccountStateManagementUtils.swift in Sources */,
|
D03B0D0C1D62255C00955575 /* AccountStateManagementUtils.swift in Sources */,
|
||||||
D073CE5D1DCB97F6007511FD /* ForwardSourceInfoAttribute.swift in Sources */,
|
D073CE5D1DCB97F6007511FD /* ForwardSourceInfoAttribute.swift in Sources */,
|
||||||
D0FA8B9E1E1F973B001E855B /* SecretChatIncomingEncryptedOperation.swift in Sources */,
|
D0FA8B9E1E1F973B001E855B /* SecretChatIncomingEncryptedOperation.swift in Sources */,
|
||||||
|
D0561DEA1E5754FA00E6B9E9 /* ChannelAdmins.swift in Sources */,
|
||||||
D03B0D721D631ABA00955575 /* SearchMessages.swift in Sources */,
|
D03B0D721D631ABA00955575 /* SearchMessages.swift in Sources */,
|
||||||
D0DC35501DE36900000195EB /* ChatContextResult.swift in Sources */,
|
D0DC35501DE36900000195EB /* ChatContextResult.swift in Sources */,
|
||||||
D00D97CA1E32917C00E5C2B6 /* PeerInputActivityManager.swift in Sources */,
|
D00D97CA1E32917C00E5C2B6 /* PeerInputActivityManager.swift in Sources */,
|
||||||
@@ -1346,6 +1386,7 @@
|
|||||||
D03B0D5D1D631A6900955575 /* MultipartFetch.swift in Sources */,
|
D03B0D5D1D631A6900955575 /* MultipartFetch.swift in Sources */,
|
||||||
D0BC38751E40A7F70044D6FE /* RemovePeerChat.swift in Sources */,
|
D0BC38751E40A7F70044D6FE /* RemovePeerChat.swift in Sources */,
|
||||||
D0AB0B961D662F0B002C78E7 /* ManagedChatListHoles.swift in Sources */,
|
D0AB0B961D662F0B002C78E7 /* ManagedChatListHoles.swift in Sources */,
|
||||||
|
D03E5E0C1E55E02D0029569A /* LoggedOutAccountAttribute.swift in Sources */,
|
||||||
D02ABC841E32183300CAE539 /* ManagedSynchronizePinnedCloudChatsOperations.swift in Sources */,
|
D02ABC841E32183300CAE539 /* ManagedSynchronizePinnedCloudChatsOperations.swift in Sources */,
|
||||||
D03B0CD71D62245300955575 /* TelegramGroup.swift in Sources */,
|
D03B0CD71D62245300955575 /* TelegramGroup.swift in Sources */,
|
||||||
D0B8438C1DA7CF50005F29E1 /* BotInfo.swift in Sources */,
|
D0B8438C1DA7CF50005F29E1 /* BotInfo.swift in Sources */,
|
||||||
@@ -1369,7 +1410,9 @@
|
|||||||
D0AB0B941D662ECE002C78E7 /* ManagedMessageHistoryHoles.swift in Sources */,
|
D0AB0B941D662ECE002C78E7 /* ManagedMessageHistoryHoles.swift in Sources */,
|
||||||
D08774FC1E3E39F600A97350 /* ManagedGlobalNotificationSettings.swift in Sources */,
|
D08774FC1E3E39F600A97350 /* ManagedGlobalNotificationSettings.swift in Sources */,
|
||||||
D03B0CF41D62250800955575 /* TelegramMediaAction.swift in Sources */,
|
D03B0CF41D62250800955575 /* TelegramMediaAction.swift in Sources */,
|
||||||
|
D041E3F81E535A88008C24B4 /* RemovePeerMember.swift in Sources */,
|
||||||
D0B417C11D7DCEEF004562A4 /* ApiGroupOrChannel.swift in Sources */,
|
D0B417C11D7DCEEF004562A4 /* ApiGroupOrChannel.swift in Sources */,
|
||||||
|
D041E3F51E535464008C24B4 /* AddPeerMember.swift in Sources */,
|
||||||
D0B843BF1DA7FF30005F29E1 /* NBNumberFormat.m in Sources */,
|
D0B843BF1DA7FF30005F29E1 /* NBNumberFormat.m in Sources */,
|
||||||
D0B843C51DA7FF30005F29E1 /* NBPhoneNumber.m in Sources */,
|
D0B843C51DA7FF30005F29E1 /* NBPhoneNumber.m in Sources */,
|
||||||
D03B0D0D1D62255C00955575 /* SynchronizePeerReadState.swift in Sources */,
|
D03B0D0D1D62255C00955575 /* SynchronizePeerReadState.swift in Sources */,
|
||||||
@@ -1441,6 +1484,7 @@
|
|||||||
D049EAEC1E44B71B00A2CD3A /* RecentlySearchedPeerIds.swift in Sources */,
|
D049EAEC1E44B71B00A2CD3A /* RecentlySearchedPeerIds.swift in Sources */,
|
||||||
D0FA8B991E1E955C001E855B /* SecretChatOutgoingOperation.swift in Sources */,
|
D0FA8B991E1E955C001E855B /* SecretChatOutgoingOperation.swift in Sources */,
|
||||||
D001F3F01E128A1C007A8C60 /* AccountStateManagementUtils.swift in Sources */,
|
D001F3F01E128A1C007A8C60 /* AccountStateManagementUtils.swift in Sources */,
|
||||||
|
D0BEAF611E54ACF900BD963D /* AccountManager.swift in Sources */,
|
||||||
D0F3CC791DDE2859008148FA /* SearchMessages.swift in Sources */,
|
D0F3CC791DDE2859008148FA /* SearchMessages.swift in Sources */,
|
||||||
D0B8442B1DAB91E0005F29E1 /* NBMetadataCore.m in Sources */,
|
D0B8442B1DAB91E0005F29E1 /* NBMetadataCore.m in Sources */,
|
||||||
D00C7CD01E3628180080C3D5 /* UpdateCachedChannelParticipants.swift in Sources */,
|
D00C7CD01E3628180080C3D5 /* UpdateCachedChannelParticipants.swift in Sources */,
|
||||||
@@ -1480,6 +1524,7 @@
|
|||||||
D02ABC851E32183300CAE539 /* ManagedSynchronizePinnedCloudChatsOperations.swift in Sources */,
|
D02ABC851E32183300CAE539 /* ManagedSynchronizePinnedCloudChatsOperations.swift in Sources */,
|
||||||
D073CE6C1DCBCF17007511FD /* TextEntitiesMessageAttribute.swift in Sources */,
|
D073CE6C1DCBCF17007511FD /* TextEntitiesMessageAttribute.swift in Sources */,
|
||||||
D03C53751DAD5CA9004C17B3 /* TelegramUserPresence.swift in Sources */,
|
D03C53751DAD5CA9004C17B3 /* TelegramUserPresence.swift in Sources */,
|
||||||
|
D0561DE41E5737FC00E6B9E9 /* UpdatePeerInfo.swift in Sources */,
|
||||||
D0DC35521DE36908000195EB /* ChatContextResult.swift in Sources */,
|
D0DC35521DE36908000195EB /* ChatContextResult.swift in Sources */,
|
||||||
D0F7AB301DCF507E009AD9A1 /* ReplyMarkupMessageAttribute.swift in Sources */,
|
D0F7AB301DCF507E009AD9A1 /* ReplyMarkupMessageAttribute.swift in Sources */,
|
||||||
D073CE6D1DCBCF17007511FD /* InlineBotMessageAttribute.swift in Sources */,
|
D073CE6D1DCBCF17007511FD /* InlineBotMessageAttribute.swift in Sources */,
|
||||||
@@ -1494,6 +1539,7 @@
|
|||||||
D0BC387C1E40D2880044D6FE /* TogglePeerChatPinned.swift in Sources */,
|
D0BC387C1E40D2880044D6FE /* TogglePeerChatPinned.swift in Sources */,
|
||||||
D0B844111DAB91CD005F29E1 /* Regex.swift in Sources */,
|
D0B844111DAB91CD005F29E1 /* Regex.swift in Sources */,
|
||||||
D0B844321DAB91E0005F29E1 /* NBPhoneMetaDataGenerator.m in Sources */,
|
D0B844321DAB91E0005F29E1 /* NBPhoneMetaDataGenerator.m in Sources */,
|
||||||
|
D0BEAF5E1E54941B00BD963D /* Authorization.swift in Sources */,
|
||||||
D073CEA41DCBF3EA007511FD /* MultipartUpload.swift in Sources */,
|
D073CEA41DCBF3EA007511FD /* MultipartUpload.swift in Sources */,
|
||||||
D03C53701DAD5CA9004C17B3 /* ExportedInvitation.swift in Sources */,
|
D03C53701DAD5CA9004C17B3 /* ExportedInvitation.swift in Sources */,
|
||||||
D0F7B1E31E045C7B007EB8A5 /* RichText.swift in Sources */,
|
D0F7B1E31E045C7B007EB8A5 /* RichText.swift in Sources */,
|
||||||
@@ -1505,6 +1551,7 @@
|
|||||||
D03C53681DAD5CA9004C17B3 /* PeerUtils.swift in Sources */,
|
D03C53681DAD5CA9004C17B3 /* PeerUtils.swift in Sources */,
|
||||||
D050F2621E4A5AE700988324 /* GlobalNotificationSettings.swift in Sources */,
|
D050F2621E4A5AE700988324 /* GlobalNotificationSettings.swift in Sources */,
|
||||||
D0B418991D7E0580004562A4 /* TelegramMediaMap.swift in Sources */,
|
D0B418991D7E0580004562A4 /* TelegramMediaMap.swift in Sources */,
|
||||||
|
D0561DEB1E5754FA00E6B9E9 /* ChannelAdmins.swift in Sources */,
|
||||||
D0B844471DAB91FD005F29E1 /* ManagedServiceViews.swift in Sources */,
|
D0B844471DAB91FD005F29E1 /* ManagedServiceViews.swift in Sources */,
|
||||||
D03C53691DAD5CA9004C17B3 /* PeerAccessRestrictionInfo.swift in Sources */,
|
D03C53691DAD5CA9004C17B3 /* PeerAccessRestrictionInfo.swift in Sources */,
|
||||||
D0B8440E1DAB91CD005F29E1 /* MessageUtils.swift in Sources */,
|
D0B8440E1DAB91CD005F29E1 /* MessageUtils.swift in Sources */,
|
||||||
@@ -1520,6 +1567,7 @@
|
|||||||
D03C53741DAD5CA9004C17B3 /* CachedChannelData.swift in Sources */,
|
D03C53741DAD5CA9004C17B3 /* CachedChannelData.swift in Sources */,
|
||||||
D0B418861D7E056D004562A4 /* Namespaces.swift in Sources */,
|
D0B418861D7E056D004562A4 /* Namespaces.swift in Sources */,
|
||||||
D0F7B1E41E045C7B007EB8A5 /* InstantPage.swift in Sources */,
|
D0F7B1E41E045C7B007EB8A5 /* InstantPage.swift in Sources */,
|
||||||
|
D03E5E0D1E55E02D0029569A /* LoggedOutAccountAttribute.swift in Sources */,
|
||||||
D0B418AD1D7E0597004562A4 /* Serialization.swift in Sources */,
|
D0B418AD1D7E0597004562A4 /* Serialization.swift in Sources */,
|
||||||
D03C536F1DAD5CA9004C17B3 /* BotInfo.swift in Sources */,
|
D03C536F1DAD5CA9004C17B3 /* BotInfo.swift in Sources */,
|
||||||
D0FA8BBA1E2240B4001E855B /* SecretChatIncomingDecryptedOperation.swift in Sources */,
|
D0FA8BBA1E2240B4001E855B /* SecretChatIncomingDecryptedOperation.swift in Sources */,
|
||||||
@@ -1543,7 +1591,9 @@
|
|||||||
D0B844301DAB91E0005F29E1 /* NBNumberFormat.m in Sources */,
|
D0B844301DAB91E0005F29E1 /* NBNumberFormat.m in Sources */,
|
||||||
D001F3F71E128A1C007A8C60 /* ApplyUpdateMessage.swift in Sources */,
|
D001F3F71E128A1C007A8C60 /* ApplyUpdateMessage.swift in Sources */,
|
||||||
D0B418971D7E0580004562A4 /* TelegramMediaImage.swift in Sources */,
|
D0B418971D7E0580004562A4 /* TelegramMediaImage.swift in Sources */,
|
||||||
|
D041E3F91E535A88008C24B4 /* RemovePeerMember.swift in Sources */,
|
||||||
D049EAF61E44DF3300A2CD3A /* AccountState.swift in Sources */,
|
D049EAF61E44DF3300A2CD3A /* AccountState.swift in Sources */,
|
||||||
|
D041E3F61E535464008C24B4 /* AddPeerMember.swift in Sources */,
|
||||||
D0B844361DAB91E0005F29E1 /* NBPhoneNumberUtil.m in Sources */,
|
D0B844361DAB91E0005F29E1 /* NBPhoneNumberUtil.m in Sources */,
|
||||||
D073CE6F1DCBCF17007511FD /* OutgoingMessageInfoAttribute.swift in Sources */,
|
D073CE6F1DCBCF17007511FD /* OutgoingMessageInfoAttribute.swift in Sources */,
|
||||||
D0B844431DAB91FD005F29E1 /* Account.swift in Sources */,
|
D0B844431DAB91FD005F29E1 /* Account.swift in Sources */,
|
||||||
|
|||||||
@@ -10,22 +10,6 @@ import Foundation
|
|||||||
#endif
|
#endif
|
||||||
import TelegramCorePrivateModule
|
import TelegramCorePrivateModule
|
||||||
|
|
||||||
public struct AccountId: Comparable, Hashable {
|
|
||||||
let stringValue: String
|
|
||||||
|
|
||||||
public static func ==(lhs: AccountId, rhs: AccountId) -> Bool {
|
|
||||||
return lhs.stringValue == rhs.stringValue
|
|
||||||
}
|
|
||||||
|
|
||||||
public static func <(lhs: AccountId, rhs: AccountId) -> Bool {
|
|
||||||
return lhs.stringValue < rhs.stringValue
|
|
||||||
}
|
|
||||||
|
|
||||||
public var hashValue: Int {
|
|
||||||
return self.stringValue.hash
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AccountState: Coding, Equatable {
|
public class AccountState: Coding, Equatable {
|
||||||
public required init(decoder: Decoder) {
|
public required init(decoder: Decoder) {
|
||||||
}
|
}
|
||||||
@@ -129,47 +113,25 @@ public func ==(lhs: AuthorizedAccountState.State, rhs: AuthorizedAccountState.St
|
|||||||
lhs.seq == rhs.seq
|
lhs.seq == rhs.seq
|
||||||
}
|
}
|
||||||
|
|
||||||
public func currentAccountId(appGroupPath: String, testingEnvironment: Bool) -> AccountId {
|
|
||||||
let filePath: String
|
|
||||||
if testingEnvironment {
|
|
||||||
filePath = "\(appGroupPath)/currentAccountId_test"
|
|
||||||
} else {
|
|
||||||
filePath = "\(appGroupPath)/currentAccountId"
|
|
||||||
}
|
|
||||||
if let id = try? String(contentsOfFile: filePath) {
|
|
||||||
return AccountId(stringValue: id)
|
|
||||||
} else {
|
|
||||||
let id = generateAccountId()
|
|
||||||
let _ = try? id.stringValue.write(toFile: filePath, atomically: true, encoding: .utf8)
|
|
||||||
return id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func generateAccountId() -> AccountId {
|
|
||||||
return AccountId(stringValue: NSUUID().uuidString)
|
|
||||||
}
|
|
||||||
|
|
||||||
public class UnauthorizedAccount {
|
public class UnauthorizedAccount {
|
||||||
public let id: AccountId
|
public let id: AccountRecordId
|
||||||
public let appGroupPath: String
|
public let appGroupPath: String
|
||||||
public let basePath: String
|
public let basePath: String
|
||||||
public let testingEnvironment: Bool
|
public let testingEnvironment: Bool
|
||||||
public let postbox: Postbox
|
public let postbox: Postbox
|
||||||
public let network: Network
|
public let network: Network
|
||||||
public let logger: Logger
|
|
||||||
|
|
||||||
public var masterDatacenterId: Int32 {
|
public var masterDatacenterId: Int32 {
|
||||||
return Int32(self.network.mtProto.datacenterId)
|
return Int32(self.network.mtProto.datacenterId)
|
||||||
}
|
}
|
||||||
|
|
||||||
init(id: AccountId, appGroupPath: String, basePath: String, logger: Logger, testingEnvironment: Bool, postbox: Postbox, network: Network) {
|
init(id: AccountRecordId, appGroupPath: String, basePath: String, testingEnvironment: Bool, postbox: Postbox, network: Network) {
|
||||||
self.id = id
|
self.id = id
|
||||||
self.appGroupPath = appGroupPath
|
self.appGroupPath = appGroupPath
|
||||||
self.basePath = basePath
|
self.basePath = basePath
|
||||||
self.testingEnvironment = testingEnvironment
|
self.testingEnvironment = testingEnvironment
|
||||||
self.postbox = postbox
|
self.postbox = postbox
|
||||||
self.network = network
|
self.network = network
|
||||||
self.logger = logger
|
|
||||||
network.shouldKeepConnection.set(.single(true))
|
network.shouldKeepConnection.set(.single(true))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,7 +150,7 @@ public class UnauthorizedAccount {
|
|||||||
|
|
||||||
return initializedNetwork(datacenterId: Int(masterDatacenterId), keychain: keychain, networkUsageInfoPath: accountNetworkUsageInfoPath(basePath: self.basePath), testingEnvironment: self.testingEnvironment)
|
return initializedNetwork(datacenterId: Int(masterDatacenterId), keychain: keychain, networkUsageInfoPath: accountNetworkUsageInfoPath(basePath: self.basePath), testingEnvironment: self.testingEnvironment)
|
||||||
|> map { network in
|
|> map { network in
|
||||||
return UnauthorizedAccount(id: self.id, appGroupPath: self.appGroupPath, basePath: self.basePath, logger: self.logger, testingEnvironment: self.testingEnvironment, postbox: self.postbox, network: network)
|
return UnauthorizedAccount(id: self.id, appGroupPath: self.appGroupPath, basePath: self.basePath, testingEnvironment: self.testingEnvironment, postbox: self.postbox, network: network)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -247,6 +209,7 @@ private var declaredEncodables: Void = {
|
|||||||
declareEncodable(SynchronizePinnedChatsOperation.self, f: { SynchronizePinnedChatsOperation(decoder: $0) })
|
declareEncodable(SynchronizePinnedChatsOperation.self, f: { SynchronizePinnedChatsOperation(decoder: $0) })
|
||||||
declareEncodable(RecentMediaItem.self, f: { RecentMediaItem(decoder: $0) })
|
declareEncodable(RecentMediaItem.self, f: { RecentMediaItem(decoder: $0) })
|
||||||
declareEncodable(RecentPeerItem.self, f: { RecentPeerItem(decoder: $0) })
|
declareEncodable(RecentPeerItem.self, f: { RecentPeerItem(decoder: $0) })
|
||||||
|
declareEncodable(LoggedOutAccountAttribute.self, f: { LoggedOutAccountAttribute(decoder: $0) })
|
||||||
|
|
||||||
return
|
return
|
||||||
}()
|
}()
|
||||||
@@ -255,16 +218,15 @@ func accountNetworkUsageInfoPath(basePath: String) -> String {
|
|||||||
return basePath + "/network-usage"
|
return basePath + "/network-usage"
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum AccountLogger {
|
private func accountRecordIdPathName(_ id: AccountRecordId) -> String {
|
||||||
case named(String)
|
return "account-\(UInt64(bitPattern: id.int64))"
|
||||||
case instance(Logger)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func accountWithId(_ id: AccountId, appGroupPath: String, logger: AccountLogger, testingEnvironment: Bool) -> Signal<Either<UnauthorizedAccount, Account>, NoError> {
|
public func accountWithId(_ id: AccountRecordId, appGroupPath: String, testingEnvironment: Bool) -> Signal<Either<UnauthorizedAccount, Account>, NoError> {
|
||||||
return Signal<(String, Postbox, Coding?), NoError> { subscriber in
|
return Signal<(String, Postbox, Coding?), NoError> { subscriber in
|
||||||
let _ = declaredEncodables
|
let _ = declaredEncodables
|
||||||
|
|
||||||
let path = "\(appGroupPath)/account\(id.stringValue)"
|
let path = "\(appGroupPath)/\(accountRecordIdPathName(id))"
|
||||||
|
|
||||||
var initializeMessageNamespacesWithHoles: [(PeerId.Namespace, MessageId.Namespace)] = []
|
var initializeMessageNamespacesWithHoles: [(PeerId.Namespace, MessageId.Namespace)] = []
|
||||||
for peerNamespace in peerIdNamespacesWithInitialCloudMessageHoles {
|
for peerNamespace in peerIdNamespacesWithInitialCloudMessageHoles {
|
||||||
@@ -274,6 +236,7 @@ public func accountWithId(_ id: AccountId, appGroupPath: String, logger: Account
|
|||||||
let seedConfiguration = SeedConfiguration(initializeChatListWithHoles: [ChatListHole(index: MessageIndex(id: MessageId(peerId: PeerId(namespace: Namespaces.Peer.Empty, id: 0), namespace: Namespaces.Message.Cloud, id: 1), timestamp: 1))], initializeMessageNamespacesWithHoles: initializeMessageNamespacesWithHoles, existingMessageTags: allMessageTags)
|
let seedConfiguration = SeedConfiguration(initializeChatListWithHoles: [ChatListHole(index: MessageIndex(id: MessageId(peerId: PeerId(namespace: Namespaces.Peer.Empty, id: 0), namespace: Namespaces.Message.Cloud, id: 1), timestamp: 1))], initializeMessageNamespacesWithHoles: initializeMessageNamespacesWithHoles, existingMessageTags: allMessageTags)
|
||||||
|
|
||||||
let postbox = Postbox(basePath: path + "/postbox", globalMessageIdsNamespace: Namespaces.Message.Cloud, seedConfiguration: seedConfiguration)
|
let postbox = Postbox(basePath: path + "/postbox", globalMessageIdsNamespace: Namespaces.Message.Cloud, seedConfiguration: seedConfiguration)
|
||||||
|
|
||||||
return (postbox.stateView() |> take(1) |> map { view -> (String, Postbox, Coding?) in
|
return (postbox.stateView() |> take(1) |> map { view -> (String, Postbox, Coding?) in
|
||||||
let accountState = view.state
|
let accountState = view.state
|
||||||
return (path, postbox, accountState)
|
return (path, postbox, accountState)
|
||||||
@@ -290,25 +253,17 @@ public func accountWithId(_ id: AccountId, appGroupPath: String, logger: Account
|
|||||||
postbox.removeKeychainEntryForKey(key)
|
postbox.removeKeychainEntryForKey(key)
|
||||||
})
|
})
|
||||||
|
|
||||||
let concreteLogger: Logger
|
|
||||||
switch logger {
|
|
||||||
case let .named(name):
|
|
||||||
concreteLogger = Logger(basePath: basePath + "/" + name)
|
|
||||||
case let .instance(instance):
|
|
||||||
concreteLogger = instance
|
|
||||||
}
|
|
||||||
|
|
||||||
if let accountState = accountState {
|
if let accountState = accountState {
|
||||||
switch accountState {
|
switch accountState {
|
||||||
case let unauthorizedState as UnauthorizedAccountState:
|
case let unauthorizedState as UnauthorizedAccountState:
|
||||||
return initializedNetwork(datacenterId: Int(unauthorizedState.masterDatacenterId), keychain: keychain, networkUsageInfoPath: accountNetworkUsageInfoPath(basePath: basePath), testingEnvironment: testingEnvironment)
|
return initializedNetwork(datacenterId: Int(unauthorizedState.masterDatacenterId), keychain: keychain, networkUsageInfoPath: accountNetworkUsageInfoPath(basePath: basePath), testingEnvironment: testingEnvironment)
|
||||||
|> map { network -> Either<UnauthorizedAccount, Account> in
|
|> map { network -> Either<UnauthorizedAccount, Account> in
|
||||||
.left(value: UnauthorizedAccount(id: id, appGroupPath: appGroupPath, basePath: basePath, logger: concreteLogger, testingEnvironment: testingEnvironment, postbox: postbox, network: network))
|
.left(value: UnauthorizedAccount(id: id, appGroupPath: appGroupPath, basePath: basePath, testingEnvironment: testingEnvironment, postbox: postbox, network: network))
|
||||||
}
|
}
|
||||||
case let authorizedState as AuthorizedAccountState:
|
case let authorizedState as AuthorizedAccountState:
|
||||||
return initializedNetwork(datacenterId: Int(authorizedState.masterDatacenterId), keychain: keychain, networkUsageInfoPath: accountNetworkUsageInfoPath(basePath: basePath), testingEnvironment: testingEnvironment)
|
return initializedNetwork(datacenterId: Int(authorizedState.masterDatacenterId), keychain: keychain, networkUsageInfoPath: accountNetworkUsageInfoPath(basePath: basePath), testingEnvironment: testingEnvironment)
|
||||||
|> map { network -> Either<UnauthorizedAccount, Account> in
|
|> map { network -> Either<UnauthorizedAccount, Account> in
|
||||||
return .right(value: Account(id: id, basePath: basePath, logger: concreteLogger, testingEnvironment: testingEnvironment, postbox: postbox, network: network, peerId: authorizedState.peerId))
|
return .right(value: Account(id: id, basePath: basePath, testingEnvironment: testingEnvironment, postbox: postbox, network: network, peerId: authorizedState.peerId))
|
||||||
}
|
}
|
||||||
case _:
|
case _:
|
||||||
assertionFailure("Unexpected accountState \(accountState)")
|
assertionFailure("Unexpected accountState \(accountState)")
|
||||||
@@ -317,7 +272,7 @@ public func accountWithId(_ id: AccountId, appGroupPath: String, logger: Account
|
|||||||
|
|
||||||
return initializedNetwork(datacenterId: 2, keychain: keychain, networkUsageInfoPath: accountNetworkUsageInfoPath(basePath: basePath), testingEnvironment: testingEnvironment)
|
return initializedNetwork(datacenterId: 2, keychain: keychain, networkUsageInfoPath: accountNetworkUsageInfoPath(basePath: basePath), testingEnvironment: testingEnvironment)
|
||||||
|> map { network -> Either<UnauthorizedAccount, Account> in
|
|> map { network -> Either<UnauthorizedAccount, Account> in
|
||||||
return .left(value: UnauthorizedAccount(id: id, appGroupPath: appGroupPath, basePath: basePath, logger: concreteLogger, testingEnvironment: testingEnvironment, postbox: postbox, network: network))
|
return .left(value: UnauthorizedAccount(id: id, appGroupPath: appGroupPath, basePath: basePath, testingEnvironment: testingEnvironment, postbox: postbox, network: network))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -380,15 +335,13 @@ public enum AccountNetworkState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public class Account {
|
public class Account {
|
||||||
public let id: AccountId
|
public let id: AccountRecordId
|
||||||
public let basePath: String
|
public let basePath: String
|
||||||
public let testingEnvironment: Bool
|
public let testingEnvironment: Bool
|
||||||
public let postbox: Postbox
|
public let postbox: Postbox
|
||||||
public let network: Network
|
public let network: Network
|
||||||
public let peerId: PeerId
|
public let peerId: PeerId
|
||||||
|
|
||||||
public let logger: Logger
|
|
||||||
|
|
||||||
public private(set) var stateManager: AccountStateManager!
|
public private(set) var stateManager: AccountStateManager!
|
||||||
public private(set) var viewTracker: AccountViewTracker!
|
public private(set) var viewTracker: AccountViewTracker!
|
||||||
public private(set) var pendingMessageManager: PendingMessageManager!
|
public private(set) var pendingMessageManager: PendingMessageManager!
|
||||||
@@ -422,14 +375,13 @@ public class Account {
|
|||||||
return self.networkStateValue.get()
|
return self.networkStateValue.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(id: AccountId, basePath: String, logger: Logger, testingEnvironment: Bool, postbox: Postbox, network: Network, peerId: PeerId) {
|
public init(id: AccountRecordId, basePath: String, testingEnvironment: Bool, postbox: Postbox, network: Network, peerId: PeerId) {
|
||||||
self.id = id
|
self.id = id
|
||||||
self.basePath = basePath
|
self.basePath = basePath
|
||||||
self.testingEnvironment = testingEnvironment
|
self.testingEnvironment = testingEnvironment
|
||||||
self.postbox = postbox
|
self.postbox = postbox
|
||||||
self.network = network
|
self.network = network
|
||||||
self.peerId = peerId
|
self.peerId = peerId
|
||||||
self.logger = logger
|
|
||||||
|
|
||||||
self.peerInputActivityManager = PeerInputActivityManager()
|
self.peerInputActivityManager = PeerInputActivityManager()
|
||||||
self.stateManager = AccountStateManager(account: self, peerInputActivityManager: self.peerInputActivityManager)
|
self.stateManager = AccountStateManager(account: self, peerInputActivityManager: self.peerInputActivityManager)
|
||||||
@@ -546,10 +498,10 @@ public class Account {
|
|||||||
|> deliverOn(Queue.concurrentDefaultQueue())
|
|> deliverOn(Queue.concurrentDefaultQueue())
|
||||||
|> mapToSignal { [weak self] value -> Signal<Void, NoError> in
|
|> mapToSignal { [weak self] value -> Signal<Void, NoError> in
|
||||||
if let strongSelf = self, value {
|
if let strongSelf = self, value {
|
||||||
trace("Account", what: "Became master")
|
Logger.shared.log("Account", "Became master")
|
||||||
return managedServiceViews(network: strongSelf.network, postbox: strongSelf.postbox, stateManager: strongSelf.stateManager, pendingMessageManager: strongSelf.pendingMessageManager)
|
return managedServiceViews(network: strongSelf.network, postbox: strongSelf.postbox, stateManager: strongSelf.stateManager, pendingMessageManager: strongSelf.pendingMessageManager)
|
||||||
} else {
|
} else {
|
||||||
trace("Account", what: "Resigned master")
|
Logger.shared.log("Account", "Resigned master")
|
||||||
return .never()
|
return .never()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ enum AccountStateMutationOperation {
|
|||||||
case UpdatePeerNotificationSettings(PeerId, PeerNotificationSettings)
|
case UpdatePeerNotificationSettings(PeerId, PeerNotificationSettings)
|
||||||
case AddHole(MessageId)
|
case AddHole(MessageId)
|
||||||
case MergeApiChats([Api.Chat])
|
case MergeApiChats([Api.Chat])
|
||||||
case UpdatePeer(PeerId, (Peer) -> Peer)
|
case UpdatePeer(PeerId, (Peer?) -> Peer?)
|
||||||
case MergeApiUsers([Api.User])
|
case MergeApiUsers([Api.User])
|
||||||
case MergePeerPresences([PeerId: PeerPresence])
|
case MergePeerPresences([PeerId: PeerPresence])
|
||||||
case UpdateSecretChat(chat: Api.EncryptedChat, timestamp: Int32)
|
case UpdateSecretChat(chat: Api.EncryptedChat, timestamp: Int32)
|
||||||
@@ -166,7 +166,7 @@ struct AccountMutableState {
|
|||||||
self.addOperation(.MergeApiChats(chats))
|
self.addOperation(.MergeApiChats(chats))
|
||||||
}
|
}
|
||||||
|
|
||||||
mutating func updatePeer(_ id: PeerId, _ f: @escaping (Peer) -> Peer) {
|
mutating func updatePeer(_ id: PeerId, _ f: @escaping (Peer?) -> Peer?) {
|
||||||
self.addOperation(.UpdatePeer(id, f))
|
self.addOperation(.UpdatePeer(id, f))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,8 +241,8 @@ struct AccountMutableState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case let .UpdatePeer(id, f):
|
case let .UpdatePeer(id, f):
|
||||||
if let peer = self.peers[id] {
|
let peer = self.peers[id]
|
||||||
let updatedPeer = f(peer)
|
if let updatedPeer = f(peer) {
|
||||||
peers[id] = updatedPeer
|
peers[id] = updatedPeer
|
||||||
insertedPeers[id] = updatedPeer
|
insertedPeers[id] = updatedPeer
|
||||||
}
|
}
|
||||||
|
|||||||
176
TelegramCore/AccountManager.swift
Normal file
176
TelegramCore/AccountManager.swift
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
import Foundation
|
||||||
|
#if os(macOS)
|
||||||
|
import PostboxMac
|
||||||
|
import SwiftSignalKitMac
|
||||||
|
import MtProtoKitMac
|
||||||
|
#else
|
||||||
|
import Postbox
|
||||||
|
import SwiftSignalKit
|
||||||
|
import MtProtoKitDynamic
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private enum AccountKind {
|
||||||
|
case authorized
|
||||||
|
case unauthorized
|
||||||
|
}
|
||||||
|
|
||||||
|
public func currentAccount(manager: AccountManager, appGroupPath: String, testingEnvironment: Bool) -> Signal<Either<UnauthorizedAccount, Account>?, NoError> {
|
||||||
|
return manager.allocatedCurrentAccountId()
|
||||||
|
|> distinctUntilChanged(isEqual: { lhs, rhs in
|
||||||
|
return lhs == rhs
|
||||||
|
})
|
||||||
|
|> mapToSignal { id -> Signal<Either<UnauthorizedAccount, Account>?, NoError> in
|
||||||
|
if let id = id {
|
||||||
|
let reload = ValuePromise<Bool>(true, ignoreRepeated: false)
|
||||||
|
return reload.get() |> mapToSignal { _ -> Signal<Either<UnauthorizedAccount, Account>?, NoError> in
|
||||||
|
return accountWithId(id, appGroupPath: appGroupPath, testingEnvironment: testingEnvironment)
|
||||||
|
|> mapToSignal { account -> Signal<Either<UnauthorizedAccount, Account>?, NoError> in
|
||||||
|
let postbox: Postbox
|
||||||
|
let initialKind: AccountKind
|
||||||
|
switch account {
|
||||||
|
case let .left(value: account):
|
||||||
|
postbox = account.postbox
|
||||||
|
initialKind = .unauthorized
|
||||||
|
case let.right(value: account):
|
||||||
|
postbox = account.postbox
|
||||||
|
initialKind = .authorized
|
||||||
|
}
|
||||||
|
let updatedKind = postbox.stateView()
|
||||||
|
|> map { view -> Bool in
|
||||||
|
let kind: AccountKind
|
||||||
|
if view.state is AuthorizedAccountState {
|
||||||
|
kind = .authorized
|
||||||
|
} else {
|
||||||
|
kind = .unauthorized
|
||||||
|
}
|
||||||
|
if kind != initialKind {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|> distinctUntilChanged
|
||||||
|
|
||||||
|
return Signal { subscriber in
|
||||||
|
subscriber.putNext(account)
|
||||||
|
|
||||||
|
return updatedKind.start(next: { value in
|
||||||
|
if value {
|
||||||
|
reload.set(true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return .single(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func logoutFromAccount(id: AccountRecordId, accountManager: AccountManager) -> Signal<Void, NoError> {
|
||||||
|
return accountManager.modify { modifier -> Void in
|
||||||
|
let currentId = modifier.getCurrentId()
|
||||||
|
if let currentId = currentId {
|
||||||
|
modifier.updateRecord(currentId, { current in
|
||||||
|
if let current = current {
|
||||||
|
var found = false
|
||||||
|
for attribute in current.attributes {
|
||||||
|
if attribute is LoggedOutAccountAttribute {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if found {
|
||||||
|
return current
|
||||||
|
} else {
|
||||||
|
return AccountRecord(id: current.id, attributes: current.attributes + [LoggedOutAccountAttribute()])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
})
|
||||||
|
let id = modifier.createRecord([])
|
||||||
|
modifier.setCurrentId(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func managedCleanupAccounts(accountManager: AccountManager, appGroupPath: String) -> Signal<Void, NoError> {
|
||||||
|
return Signal { subscriber in
|
||||||
|
let loggedOutAccounts = Atomic<[AccountRecordId: MetaDisposable]>(value: [:])
|
||||||
|
let disposable = accountManager.accountRecords().start(next: { view in
|
||||||
|
var disposeList: [(AccountRecordId, MetaDisposable)] = []
|
||||||
|
var beginList: [(AccountRecordId, MetaDisposable)] = []
|
||||||
|
let _ = loggedOutAccounts.modify { disposables in
|
||||||
|
let validIds = Set(view.records.filter {
|
||||||
|
for attribute in $0.attributes {
|
||||||
|
if attribute is LoggedOutAccountAttribute {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}.map { $0.id })
|
||||||
|
|
||||||
|
|
||||||
|
var disposables = disposables
|
||||||
|
|
||||||
|
for id in disposables.keys {
|
||||||
|
if !validIds.contains(id) {
|
||||||
|
disposeList.append((id, disposables[id]!))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (id, _) in disposeList {
|
||||||
|
disposables.removeValue(forKey: id)
|
||||||
|
}
|
||||||
|
|
||||||
|
for id in validIds {
|
||||||
|
if disposables[id] == nil {
|
||||||
|
let disposable = MetaDisposable()
|
||||||
|
beginList.append((id, disposable))
|
||||||
|
disposables[id] = disposable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return disposables
|
||||||
|
}
|
||||||
|
for (_, disposable) in disposeList {
|
||||||
|
disposable.dispose()
|
||||||
|
}
|
||||||
|
for (id, disposable) in beginList {
|
||||||
|
disposable.set(cleanupAccount(accountManager: accountManager, id: id, appGroupPath: appGroupPath).start())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return ActionDisposable {
|
||||||
|
disposable.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private func cleanupAccount(accountManager: AccountManager, id: AccountRecordId, appGroupPath: String) -> Signal<Void, NoError> {
|
||||||
|
return accountWithId(id, appGroupPath: appGroupPath, testingEnvironment: false)
|
||||||
|
|> mapToSignal { account -> Signal<Void, NoError> in
|
||||||
|
switch account {
|
||||||
|
case .left:
|
||||||
|
return .complete()
|
||||||
|
case let .right(account):
|
||||||
|
account.shouldBeServiceTaskMaster.set(.single(.always))
|
||||||
|
return account.network.request(Api.functions.auth.logOut())
|
||||||
|
|> map { Optional($0) }
|
||||||
|
|> `catch` { _ -> Signal<Api.Bool?, NoError> in
|
||||||
|
return .single(.boolFalse)
|
||||||
|
}
|
||||||
|
|> mapToSignal { _ -> Signal<Void, NoError> in
|
||||||
|
account.shouldBeServiceTaskMaster.set(.single(.never))
|
||||||
|
return accountManager.modify { modifier -> Void in
|
||||||
|
modifier.updateRecord(id, { _ in
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -376,7 +376,7 @@ func finalStateWithUpdateGroups(_ account: Account, state: AccountMutableState,
|
|||||||
updatedState.updateState(AuthorizedAccountState.State(pts: update.ptsRange.0, qts: updatedState.state.qts, date: updatedState.state.date, seq: updatedState.state.seq))
|
updatedState.updateState(AuthorizedAccountState.State(pts: update.ptsRange.0, qts: updatedState.state.qts, date: updatedState.state.date, seq: updatedState.state.seq))
|
||||||
} else {
|
} else {
|
||||||
if ptsUpdatesAfterHole.count == 0 {
|
if ptsUpdatesAfterHole.count == 0 {
|
||||||
trace("State", what: "update pts hole: \(update.ptsRange.0) != \(updatedState.state.pts) + \(update.ptsRange.1)")
|
Logger.shared.log("State", "update pts hole: \(update.ptsRange.0) != \(updatedState.state.pts) + \(update.ptsRange.1)")
|
||||||
}
|
}
|
||||||
ptsUpdatesAfterHole.append(update)
|
ptsUpdatesAfterHole.append(update)
|
||||||
}
|
}
|
||||||
@@ -396,7 +396,7 @@ func finalStateWithUpdateGroups(_ account: Account, state: AccountMutableState,
|
|||||||
updatedState.updateState(AuthorizedAccountState.State(pts: updatedState.state.pts, qts: update.qtsRange.0, date: updatedState.state.date, seq: updatedState.state.seq))
|
updatedState.updateState(AuthorizedAccountState.State(pts: updatedState.state.pts, qts: update.qtsRange.0, date: updatedState.state.date, seq: updatedState.state.seq))
|
||||||
} else {
|
} else {
|
||||||
if qtsUpdatesAfterHole.count == 0 {
|
if qtsUpdatesAfterHole.count == 0 {
|
||||||
trace("State", what: "update qts hole: \(update.qtsRange.0) != \(updatedState.state.qts) + \(update.qtsRange.1)")
|
Logger.shared.log("State", "update qts hole: \(update.qtsRange.0) != \(updatedState.state.qts) + \(update.qtsRange.1)")
|
||||||
}
|
}
|
||||||
qtsUpdatesAfterHole.append(update)
|
qtsUpdatesAfterHole.append(update)
|
||||||
}
|
}
|
||||||
@@ -414,7 +414,7 @@ func finalStateWithUpdateGroups(_ account: Account, state: AccountMutableState,
|
|||||||
updatedState.updateState(AuthorizedAccountState.State(pts: updatedState.state.pts, qts: updatedState.state.qts, date: group.date, seq: group.seqRange.0))
|
updatedState.updateState(AuthorizedAccountState.State(pts: updatedState.state.pts, qts: updatedState.state.qts, date: group.date, seq: group.seqRange.0))
|
||||||
} else {
|
} else {
|
||||||
if seqGroupsAfterHole.count == 0 {
|
if seqGroupsAfterHole.count == 0 {
|
||||||
print("update seq hole: \(group.seqRange.0) != \(updatedState.state.seq) + \(group.seqRange.1)")
|
Logger.shared.log("State", "update seq hole: \(group.seqRange.0) != \(updatedState.state.seq) + \(group.seqRange.1)")
|
||||||
}
|
}
|
||||||
seqGroupsAfterHole.append(group)
|
seqGroupsAfterHole.append(group)
|
||||||
}
|
}
|
||||||
@@ -616,27 +616,27 @@ private func finalStateWithUpdates(account: Account, state: AccountMutableState,
|
|||||||
case let .updateChannelTooLong(_, channelId, _):
|
case let .updateChannelTooLong(_, channelId, _):
|
||||||
let peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId)
|
let peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId)
|
||||||
if !channelsToPoll.contains(peerId) {
|
if !channelsToPoll.contains(peerId) {
|
||||||
//trace("State", what: "channel \(peerId) (\((updatedState.peers[peerId] as? TelegramChannel)?.title ?? "nil")) updateChannelTooLong")
|
//Logger.shared.log("State", "channel \(peerId) (\((updatedState.peers[peerId] as? TelegramChannel)?.title ?? "nil")) updateChannelTooLong")
|
||||||
channelsToPoll.insert(peerId)
|
channelsToPoll.insert(peerId)
|
||||||
}
|
}
|
||||||
case let .updateDeleteChannelMessages(channelId, messages, pts: pts, ptsCount):
|
case let .updateDeleteChannelMessages(channelId, messages, pts: pts, ptsCount):
|
||||||
let peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId)
|
let peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId)
|
||||||
if let previousState = updatedState.channelStates[peerId] {
|
if let previousState = updatedState.channelStates[peerId] {
|
||||||
if previousState.pts >= pts {
|
if previousState.pts >= pts {
|
||||||
//trace("State", what: "channel \(peerId) (\((updatedState.peers[peerId] as? TelegramChannel)?.title ?? "nil")) skip old delete update")
|
//Logger.shared.log("State", "channel \(peerId) (\((updatedState.peers[peerId] as? TelegramChannel)?.title ?? "nil")) skip old delete update")
|
||||||
} else if previousState.pts + ptsCount == pts {
|
} else if previousState.pts + ptsCount == pts {
|
||||||
updatedState.deleteMessages(messages.map({ MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: $0) }))
|
updatedState.deleteMessages(messages.map({ MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: $0) }))
|
||||||
updatedState.updateChannelState(peerId, state: previousState.setPts(pts))
|
updatedState.updateChannelState(peerId, state: previousState.setPts(pts))
|
||||||
} else {
|
} else {
|
||||||
if !channelsToPoll.contains(peerId) {
|
if !channelsToPoll.contains(peerId) {
|
||||||
trace("State", what: "channel \(peerId) (\((updatedState.peers[peerId] as? TelegramChannel)?.title ?? "nil")) delete pts hole")
|
Logger.shared.log("State", "channel \(peerId) (\((updatedState.peers[peerId] as? TelegramChannel)?.title ?? "nil")) delete pts hole")
|
||||||
channelsToPoll.insert(peerId)
|
channelsToPoll.insert(peerId)
|
||||||
//updatedMissingUpdates = true
|
//updatedMissingUpdates = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if !channelsToPoll.contains(peerId) {
|
if !channelsToPoll.contains(peerId) {
|
||||||
//trace("State", what: "channel \(peerId) (\((updatedState.peers[peerId] as? TelegramChannel)?.title ?? "nil")) state unknown")
|
//Logger.shared.log("State", "channel \(peerId) (\((updatedState.peers[peerId] as? TelegramChannel)?.title ?? "nil")) state unknown")
|
||||||
channelsToPoll.insert(peerId)
|
channelsToPoll.insert(peerId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -645,7 +645,7 @@ private func finalStateWithUpdates(account: Account, state: AccountMutableState,
|
|||||||
let peerId = messageId.peerId
|
let peerId = messageId.peerId
|
||||||
if let previousState = updatedState.channelStates[peerId] {
|
if let previousState = updatedState.channelStates[peerId] {
|
||||||
if previousState.pts >= pts {
|
if previousState.pts >= pts {
|
||||||
//trace("State", what: "channel \(peerId) (\((updatedState.peers[peerId] as? TelegramChannel)?.title ?? "nil")) skip old delete update")
|
//Logger.shared.log("State", "channel \(peerId) (\((updatedState.peers[peerId] as? TelegramChannel)?.title ?? "nil")) skip old edit update")
|
||||||
} else if previousState.pts + ptsCount == pts {
|
} else if previousState.pts + ptsCount == pts {
|
||||||
if let preCachedResources = apiMessage.preCachedResources {
|
if let preCachedResources = apiMessage.preCachedResources {
|
||||||
for (resource, data) in preCachedResources {
|
for (resource, data) in preCachedResources {
|
||||||
@@ -656,19 +656,19 @@ private func finalStateWithUpdates(account: Account, state: AccountMutableState,
|
|||||||
updatedState.updateChannelState(peerId, state: previousState.setPts(pts))
|
updatedState.updateChannelState(peerId, state: previousState.setPts(pts))
|
||||||
} else {
|
} else {
|
||||||
if !channelsToPoll.contains(peerId) {
|
if !channelsToPoll.contains(peerId) {
|
||||||
trace("State", what: "channel \(peerId) (\((updatedState.peers[peerId] as? TelegramChannel)?.title ?? "nil")) edit message pts hole")
|
Logger.shared.log("State", "channel \(peerId) (\((updatedState.peers[peerId] as? TelegramChannel)?.title ?? "nil")) edit message pts hole")
|
||||||
channelsToPoll.insert(peerId)
|
channelsToPoll.insert(peerId)
|
||||||
//updatedMissingUpdates = true
|
//updatedMissingUpdates = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if !channelsToPoll.contains(peerId) {
|
if !channelsToPoll.contains(peerId) {
|
||||||
//trace("State", what: "channel \(peerId) (\((updatedState.peers[peerId] as? TelegramChannel)?.title ?? "nil")) state unknown")
|
//Logger.shared.log("State", "channel \(peerId) (\((updatedState.peers[peerId] as? TelegramChannel)?.title ?? "nil")) state unknown")
|
||||||
channelsToPoll.insert(peerId)
|
channelsToPoll.insert(peerId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
trace("State", what: "Invalid updateEditChannelMessage")
|
Logger.shared.log("State", "Invalid updateEditChannelMessage")
|
||||||
}
|
}
|
||||||
case let .updateChannelWebPage(channelId, apiWebpage, pts, ptsCount):
|
case let .updateChannelWebPage(channelId, apiWebpage, pts, ptsCount):
|
||||||
let peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId)
|
let peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId)
|
||||||
@@ -687,7 +687,7 @@ private func finalStateWithUpdates(account: Account, state: AccountMutableState,
|
|||||||
updatedState.updateChannelState(peerId, state: previousState.setPts(pts))
|
updatedState.updateChannelState(peerId, state: previousState.setPts(pts))
|
||||||
} else {
|
} else {
|
||||||
if !channelsToPoll.contains(peerId) {
|
if !channelsToPoll.contains(peerId) {
|
||||||
trace("State", what: "channel \(peerId) (\((updatedState.peers[peerId] as? TelegramChannel)?.title ?? "nil")) updateWebPage pts hole")
|
Logger.shared.log("State", "channel \(peerId) (\((updatedState.peers[peerId] as? TelegramChannel)?.title ?? "nil")) updateWebPage pts hole")
|
||||||
channelsToPoll.insert(peerId)
|
channelsToPoll.insert(peerId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -711,7 +711,7 @@ private func finalStateWithUpdates(account: Account, state: AccountMutableState,
|
|||||||
if let message = StoreMessage(apiMessage: apiMessage) {
|
if let message = StoreMessage(apiMessage: apiMessage) {
|
||||||
if let previousState = updatedState.channelStates[message.id.peerId] {
|
if let previousState = updatedState.channelStates[message.id.peerId] {
|
||||||
if previousState.pts >= pts {
|
if previousState.pts >= pts {
|
||||||
//trace("State", what: "channel \(message.id.peerId) (\((updatedState.peers[message.id.peerId] as? TelegramChannel)?.title ?? "nil")) skip old message \(message.id) (\(message.text))")
|
//Logger.shared.log("State", "channel \(message.id.peerId) (\((updatedState.peers[message.id.peerId] as? TelegramChannel)?.title ?? "nil")) skip old message \(message.id) (\(message.text))")
|
||||||
} else if previousState.pts + ptsCount == pts {
|
} else if previousState.pts + ptsCount == pts {
|
||||||
if let preCachedResources = apiMessage.preCachedResources {
|
if let preCachedResources = apiMessage.preCachedResources {
|
||||||
for (resource, data) in preCachedResources {
|
for (resource, data) in preCachedResources {
|
||||||
@@ -722,7 +722,7 @@ private func finalStateWithUpdates(account: Account, state: AccountMutableState,
|
|||||||
updatedState.updateChannelState(message.id.peerId, state: previousState.setPts(pts))
|
updatedState.updateChannelState(message.id.peerId, state: previousState.setPts(pts))
|
||||||
} else {
|
} else {
|
||||||
if !channelsToPoll.contains(message.id.peerId) {
|
if !channelsToPoll.contains(message.id.peerId) {
|
||||||
trace("State", what: "channel \(message.id.peerId) (\((updatedState.peers[message.id.peerId] as? TelegramChannel)?.title ?? "nil")) message pts hole")
|
Logger.shared.log("State", "channel \(message.id.peerId) (\((updatedState.peers[message.id.peerId] as? TelegramChannel)?.title ?? "nil")) message pts hole")
|
||||||
;
|
;
|
||||||
channelsToPoll.insert(message.id.peerId)
|
channelsToPoll.insert(message.id.peerId)
|
||||||
//updatedMissingUpdates = true
|
//updatedMissingUpdates = true
|
||||||
@@ -730,7 +730,7 @@ private func finalStateWithUpdates(account: Account, state: AccountMutableState,
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if !channelsToPoll.contains(message.id.peerId) {
|
if !channelsToPoll.contains(message.id.peerId) {
|
||||||
trace("State", what: "channel \(message.id.peerId) (\((updatedState.peers[message.id.peerId] as? TelegramChannel)?.title ?? "nil")) state unknown")
|
Logger.shared.log("State", "channel \(message.id.peerId) (\((updatedState.peers[message.id.peerId] as? TelegramChannel)?.title ?? "nil")) state unknown")
|
||||||
channelsToPoll.insert(message.id.peerId)
|
channelsToPoll.insert(message.id.peerId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -769,7 +769,7 @@ private func finalStateWithUpdates(account: Account, state: AccountMutableState,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if alreadyStored {
|
if alreadyStored {
|
||||||
trace("State", what: "skipping message at \(date) for \(peerId): already stored")
|
Logger.shared.log("State", "skipping message at \(date) for \(peerId): already stored")
|
||||||
} else {
|
} else {
|
||||||
var attributes: [MessageAttribute] = []
|
var attributes: [MessageAttribute] = []
|
||||||
if !entities.isEmpty {
|
if !entities.isEmpty {
|
||||||
@@ -864,7 +864,7 @@ private func finalStateWithUpdates(account: Account, state: AccountMutableState,
|
|||||||
if let peer = updatedState.peers[peerId] {
|
if let peer = updatedState.peers[peerId] {
|
||||||
pollChannelSignals.append(pollChannel(account, peer: peer, state: updatedState.branch()))
|
pollChannelSignals.append(pollChannel(account, peer: peer, state: updatedState.branch()))
|
||||||
} else {
|
} else {
|
||||||
trace("State", what: "can't poll channel \(peerId): no peer found")
|
Logger.shared.log("State", "can't poll channel \(peerId): no peer found")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -972,7 +972,7 @@ private func resolveMissingPeerNotificationSettings(account: Account, state: Acc
|
|||||||
if let peer = state.peers[peerId], let inputPeer = apiInputPeer(peer) {
|
if let peer = state.peers[peerId], let inputPeer = apiInputPeer(peer) {
|
||||||
missingPeers[peerId] = inputPeer
|
missingPeers[peerId] = inputPeer
|
||||||
} else {
|
} else {
|
||||||
trace("State", what: "can't fetch notification settings for peer \(peerId): can't create inputPeer")
|
Logger.shared.log("State", "can't fetch notification settings for peer \(peerId): can't create inputPeer")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -980,7 +980,7 @@ private func resolveMissingPeerNotificationSettings(account: Account, state: Acc
|
|||||||
if missingPeers.isEmpty {
|
if missingPeers.isEmpty {
|
||||||
return .single(state)
|
return .single(state)
|
||||||
} else {
|
} else {
|
||||||
trace("State", what: "will fetch notification settings for \(missingPeers.count) peers")
|
Logger.shared.log("State", "will fetch notification settings for \(missingPeers.count) peers")
|
||||||
var signals: [Signal<(PeerId, PeerNotificationSettings)?, NoError>] = []
|
var signals: [Signal<(PeerId, PeerNotificationSettings)?, NoError>] = []
|
||||||
for (peerId, peer) in missingPeers {
|
for (peerId, peer) in missingPeers {
|
||||||
let fetchSettings = account.network.request(Api.functions.account.getNotifySettings(peer: .inputNotifyPeer(peer: peer)))
|
let fetchSettings = account.network.request(Api.functions.account.getNotifySettings(peer: .inputNotifyPeer(peer: peer)))
|
||||||
@@ -1088,7 +1088,7 @@ private func pollChannel(_ account: Account, peer: Peer, state: AccountMutableSt
|
|||||||
return updatedState
|
return updatedState
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
trace("State", what: "can't poll channel \(peer.id): can't create inputChannel")
|
Logger.shared.log("State", "can't poll channel \(peer.id): can't create inputChannel")
|
||||||
return single(state, NoError.self)
|
return single(state, NoError.self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1105,7 +1105,7 @@ private func verifyTransaction(_ modifier: Modifier, finalState: AccountMutableS
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !missingPeerIds.isEmpty {
|
if !missingPeerIds.isEmpty {
|
||||||
trace("State", what: "missing peers \(missingPeerIds)")
|
Logger.shared.log("State", "missing peers \(missingPeerIds)")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1133,7 +1133,7 @@ private func verifyTransaction(_ modifier: Modifier, finalState: AccountMutableS
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !previousStateMatches {
|
if !previousStateMatches {
|
||||||
trace("State", what: ".UpdateState previous state \(previousState) doesn't match current state \(String(describing: currentState))")
|
Logger.shared.log("State", ".UpdateState previous state \(previousState) doesn't match current state \(String(describing: currentState))")
|
||||||
failed = true
|
failed = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1150,7 +1150,7 @@ private func verifyTransaction(_ modifier: Modifier, finalState: AccountMutableS
|
|||||||
previousStateMatches = true
|
previousStateMatches = true
|
||||||
}
|
}
|
||||||
if !previousStateMatches {
|
if !previousStateMatches {
|
||||||
trace("State", what: ".UpdateChannelState for \(peerId), previous state \(previousState) doesn't match current state \(String(describing: currentState))")
|
Logger.shared.log("State", ".UpdateChannelState for \(peerId), previous state \(previousState) doesn't match current state \(String(describing: currentState))")
|
||||||
failed = true
|
failed = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1268,10 +1268,8 @@ func replayFinalState(mediaBox: MediaBox, modifier: Modifier, finalState: Accoun
|
|||||||
case let .UpdateState(state):
|
case let .UpdateState(state):
|
||||||
let currentState = modifier.getState() as! AuthorizedAccountState
|
let currentState = modifier.getState() as! AuthorizedAccountState
|
||||||
modifier.setState(currentState.changedState(state))
|
modifier.setState(currentState.changedState(state))
|
||||||
//trace("State", what: "setting state \(state)")
|
|
||||||
case let .UpdateChannelState(peerId, channelState):
|
case let .UpdateChannelState(peerId, channelState):
|
||||||
modifier.setPeerChatState(peerId, state: channelState)
|
modifier.setPeerChatState(peerId, state: channelState)
|
||||||
//trace("State", what: "setting channel \(peerId) \(finalState.state.peers[peerId]?.displayTitle ?? "nil") state \(channelState)")
|
|
||||||
case let .UpdatePeerNotificationSettings(peerId, notificationSettings):
|
case let .UpdatePeerNotificationSettings(peerId, notificationSettings):
|
||||||
modifier.updatePeerNotificationSettings([peerId: notificationSettings])
|
modifier.updatePeerNotificationSettings([peerId: notificationSettings])
|
||||||
case let .AddHole(messageId):
|
case let .AddHole(messageId):
|
||||||
@@ -1297,8 +1295,8 @@ func replayFinalState(mediaBox: MediaBox, modifier: Modifier, finalState: Accoun
|
|||||||
return updated
|
return updated
|
||||||
})
|
})
|
||||||
case let .UpdatePeer(id, f):
|
case let .UpdatePeer(id, f):
|
||||||
if let peer = modifier.getPeer(id) {
|
if let peer = f(modifier.getPeer(id)) {
|
||||||
updatePeers(modifier: modifier, peers: [f(peer)], update: { _, updated in
|
updatePeers(modifier: modifier, peers: [peer], update: { _, updated in
|
||||||
return updated
|
return updated
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1319,7 +1317,7 @@ func replayFinalState(mediaBox: MediaBox, modifier: Modifier, finalState: Accoun
|
|||||||
modifier.setPeerChatState(peer.id, state: state)
|
modifier.setPeerChatState(peer.id, state: state)
|
||||||
modifier.operationLogRemoveAllEntries(peerId: peer.id, tag: OperationLogTags.SecretOutgoing)
|
modifier.operationLogRemoveAllEntries(peerId: peer.id, tag: OperationLogTags.SecretOutgoing)
|
||||||
} else {
|
} else {
|
||||||
trace("State", what: "got encryptedChatDiscarded, but peer doesn't exist")
|
Logger.shared.log("State", "got encryptedChatDiscarded, but peer doesn't exist")
|
||||||
}
|
}
|
||||||
case .encryptedChatEmpty(_):
|
case .encryptedChatEmpty(_):
|
||||||
break
|
break
|
||||||
@@ -1343,7 +1341,7 @@ func replayFinalState(mediaBox: MediaBox, modifier: Modifier, finalState: Accoun
|
|||||||
assertionFailure()
|
assertionFailure()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
trace("State", what: "got encryptedChatRequested, but peer already exists")
|
Logger.shared.log("State", "got encryptedChatRequested, but peer already exists")
|
||||||
}
|
}
|
||||||
case let .encryptedChatWaiting(_, accessHash, date, adminId, participantId):
|
case let .encryptedChatWaiting(_, accessHash, date, adminId, participantId):
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ private enum AccountStateManagerOperation {
|
|||||||
case collectUpdateGroups([UpdateGroup], Double)
|
case collectUpdateGroups([UpdateGroup], Double)
|
||||||
case processUpdateGroups([UpdateGroup])
|
case processUpdateGroups([UpdateGroup])
|
||||||
case custom(Int32, Signal<Void, NoError>)
|
case custom(Int32, Signal<Void, NoError>)
|
||||||
case pollCompletion(Int32, [(Int32, () -> Void)])
|
case pollCompletion(Int32, [MessageId], [(Int32, ([MessageId]) -> Void)])
|
||||||
case processEvents(Int32, AccountFinalStateEvents)
|
case processEvents(Int32, AccountFinalStateEvents)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,11 +161,13 @@ public final class AccountStateManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func replaceOperations(with operation: AccountStateManagerOperation) {
|
private func replaceOperations(with operation: AccountStateManagerOperation) {
|
||||||
var collectedPollCompletionSubscribers: [(Int32, () -> Void)] = []
|
var collectedMessageIds: [MessageId] = []
|
||||||
|
var collectedPollCompletionSubscribers: [(Int32, ([MessageId]) -> Void)] = []
|
||||||
|
|
||||||
if !self.operations.isEmpty {
|
if !self.operations.isEmpty {
|
||||||
for operation in self.operations {
|
for operation in self.operations {
|
||||||
if case let .pollCompletion(_, subscribers) = operation {
|
if case let .pollCompletion(_, messageIds, subscribers) = operation {
|
||||||
|
collectedMessageIds.append(contentsOf: messageIds)
|
||||||
collectedPollCompletionSubscribers.append(contentsOf: subscribers)
|
collectedPollCompletionSubscribers.append(contentsOf: subscribers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -173,8 +175,9 @@ public final class AccountStateManager {
|
|||||||
|
|
||||||
self.operations.removeAll()
|
self.operations.removeAll()
|
||||||
self.operations.append(operation)
|
self.operations.append(operation)
|
||||||
for (id, f) in collectedPollCompletionSubscribers {
|
|
||||||
let _ = self.addPollCompletion(f, id: id)
|
if !collectedPollCompletionSubscribers.isEmpty || !collectedMessageIds.isEmpty {
|
||||||
|
self.operations.append(.pollCompletion(self.getNextId(), collectedMessageIds, collectedPollCompletionSubscribers))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,7 +218,7 @@ public final class AccountStateManager {
|
|||||||
return initialStateWithDifference(account, difference: difference)
|
return initialStateWithDifference(account, difference: difference)
|
||||||
|> mapToSignal { state -> Signal<(Api.updates.Difference?, AccountReplayedFinalState?), NoError> in
|
|> mapToSignal { state -> Signal<(Api.updates.Difference?, AccountReplayedFinalState?), NoError> in
|
||||||
if state.initialState.state != authorizedState {
|
if state.initialState.state != authorizedState {
|
||||||
trace("State", what: "pollDifference initial state \(authorizedState) != current state \(state.initialState.state)")
|
Logger.shared.log("State", "pollDifference initial state \(authorizedState) != current state \(state.initialState.state)")
|
||||||
return .single((nil, nil))
|
return .single((nil, nil))
|
||||||
} else {
|
} else {
|
||||||
return finalStateWithDifference(account: account, state: state, difference: difference)
|
return finalStateWithDifference(account: account, state: state, difference: difference)
|
||||||
@@ -286,7 +289,7 @@ public final class AccountStateManager {
|
|||||||
}
|
}
|
||||||
}, error: { _ in
|
}, error: { _ in
|
||||||
assertionFailure()
|
assertionFailure()
|
||||||
trace("AccountStateManager", what: "processUpdateGroups signal completed with error")
|
Logger.shared.log("AccountStateManager", "processUpdateGroups signal completed with error")
|
||||||
})
|
})
|
||||||
case let .collectUpdateGroups(_, timeout):
|
case let .collectUpdateGroups(_, timeout):
|
||||||
self.operationTimer?.invalidate()
|
self.operationTimer?.invalidate()
|
||||||
@@ -296,7 +299,7 @@ public final class AccountStateManager {
|
|||||||
if timeout.isEqual(to: 0.0) {
|
if timeout.isEqual(to: 0.0) {
|
||||||
strongSelf.operations[0] = .processUpdateGroups(groups)
|
strongSelf.operations[0] = .processUpdateGroups(groups)
|
||||||
} else {
|
} else {
|
||||||
trace("AccountStateManager", what: "timeout while waiting for updates")
|
Logger.shared.log("AccountStateManager", "timeout while waiting for updates")
|
||||||
strongSelf.replaceOperations(with: .pollDifference(AccountFinalStateEvents()))
|
strongSelf.replaceOperations(with: .pollDifference(AccountFinalStateEvents()))
|
||||||
}
|
}
|
||||||
strongSelf.startFirstOperation()
|
strongSelf.startFirstOperation()
|
||||||
@@ -350,7 +353,7 @@ public final class AccountStateManager {
|
|||||||
}
|
}
|
||||||
}, error: { _ in
|
}, error: { _ in
|
||||||
assertionFailure()
|
assertionFailure()
|
||||||
trace("AccountStateManager", what: "processUpdateGroups signal completed with error")
|
Logger.shared.log("AccountStateManager", "processUpdateGroups signal completed with error")
|
||||||
})
|
})
|
||||||
case let .custom(operationId, signal):
|
case let .custom(operationId, signal):
|
||||||
self.operationTimer?.invalidate()
|
self.operationTimer?.invalidate()
|
||||||
@@ -388,6 +391,16 @@ public final class AccountStateManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
strongSelf.operations.removeFirst()
|
strongSelf.operations.removeFirst()
|
||||||
|
var pollCount = 0
|
||||||
|
for i in 0 ..< strongSelf.operations.count {
|
||||||
|
if case let .pollCompletion(pollId, messageIds, subscribers) = strongSelf.operations[i] {
|
||||||
|
pollCount += 1
|
||||||
|
var updatedMessageIds = messageIds
|
||||||
|
updatedMessageIds.append(contentsOf: events.addedIncomingMessageIds)
|
||||||
|
strongSelf.operations[i] = .pollCompletion(pollId, updatedMessageIds, subscribers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(pollCount <= 1)
|
||||||
strongSelf.startFirstOperation()
|
strongSelf.startFirstOperation()
|
||||||
} else {
|
} else {
|
||||||
assertionFailure()
|
assertionFailure()
|
||||||
@@ -396,46 +409,12 @@ public final class AccountStateManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let signal = self.account.postbox.modify { modifier -> [Message] in
|
let signal = self.account.postbox.modify { modifier -> [Message] in
|
||||||
let timestamp = Int32(self.account.network.context.globalTime())
|
|
||||||
var messages: [Message] = []
|
var messages: [Message] = []
|
||||||
for id in events.addedIncomingMessageIds {
|
for id in events.addedIncomingMessageIds {
|
||||||
var notify = true
|
let (message, notify) = messageForNotification(modifier: modifier, id: id, alwaysReturnMessage: false)
|
||||||
|
if let message = message, notify {
|
||||||
if let notificationSettings = modifier.getPeerNotificationSettings(id.peerId) as? TelegramPeerNotificationSettings {
|
|
||||||
switch notificationSettings.muteState {
|
|
||||||
case let .muted(until):
|
|
||||||
if until >= timestamp {
|
|
||||||
notify = false
|
|
||||||
}
|
|
||||||
case .unmuted:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
trace("AccountStateManager", what: "notification settings for \(id.peerId) are undefined")
|
|
||||||
}
|
|
||||||
|
|
||||||
if notify {
|
|
||||||
if let message = modifier.getMessage(id) {
|
|
||||||
var foundReadState = false
|
|
||||||
var isUnread = true
|
|
||||||
if let readState = modifier.getCombinedPeerReadState(id.peerId) {
|
|
||||||
if readState.isIncomingMessageIndexRead(MessageIndex(message)) {
|
|
||||||
isUnread = false
|
|
||||||
}
|
|
||||||
foundReadState = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if !foundReadState {
|
|
||||||
trace("AccountStateManager", what: "read state for \(id.peerId) is undefined")
|
|
||||||
}
|
|
||||||
|
|
||||||
if isUnread {
|
|
||||||
messages.append(message)
|
messages.append(message)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
trace("AccountStateManager", what: "notification message doesn't exist")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return messages
|
return messages
|
||||||
}
|
}
|
||||||
@@ -443,7 +422,7 @@ public final class AccountStateManager {
|
|||||||
let _ = (signal |> deliverOn(self.queue)).start(next: { [weak self] messages in
|
let _ = (signal |> deliverOn(self.queue)).start(next: { [weak self] messages in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
for message in messages {
|
for message in messages {
|
||||||
print("notify: \(String(describing: messageMainPeer(message)?.displayTitle)): \(message.id)")
|
Logger.shared.log("State" , "notify: \(String(describing: messageMainPeer(message)?.displayTitle)): \(message.id)")
|
||||||
}
|
}
|
||||||
|
|
||||||
strongSelf.notificationMessagesPipe.putNext(messages)
|
strongSelf.notificationMessagesPipe.putNext(messages)
|
||||||
@@ -453,12 +432,10 @@ public final class AccountStateManager {
|
|||||||
}, completed: {
|
}, completed: {
|
||||||
completed()
|
completed()
|
||||||
})
|
})
|
||||||
case let .pollCompletion(pollId, preSubscribers):
|
case let .pollCompletion(pollId, preMessageIds, preSubscribers):
|
||||||
if self.operations.count > 1 {
|
if self.operations.count > 1 {
|
||||||
self.operations.removeFirst()
|
self.operations.removeFirst()
|
||||||
for (id, f) in preSubscribers {
|
self.postponePollCompletionOperation(messageIds: preMessageIds, subscribers: preSubscribers)
|
||||||
let _ = self.addPollCompletion(f, id: id)
|
|
||||||
}
|
|
||||||
self.startFirstOperation()
|
self.startFirstOperation()
|
||||||
} else {
|
} else {
|
||||||
self.operationTimer?.invalidate()
|
self.operationTimer?.invalidate()
|
||||||
@@ -466,18 +443,16 @@ public final class AccountStateManager {
|
|||||||
|> deliverOn(self.queue)
|
|> deliverOn(self.queue)
|
||||||
let completed: () -> Void = { [weak self] in
|
let completed: () -> Void = { [weak self] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
if let topOperation = strongSelf.operations.first, case let .pollCompletion(topPollId, subscribers) = topOperation {
|
if let topOperation = strongSelf.operations.first, case let .pollCompletion(topPollId, messageIds, subscribers) = topOperation {
|
||||||
assert(topPollId == pollId)
|
assert(topPollId == pollId)
|
||||||
|
|
||||||
strongSelf.operations.removeFirst()
|
strongSelf.operations.removeFirst()
|
||||||
if strongSelf.operations.isEmpty {
|
if strongSelf.operations.isEmpty {
|
||||||
for (_, f) in subscribers {
|
for (_, f) in subscribers {
|
||||||
f()
|
f(messageIds)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (id, f) in subscribers {
|
strongSelf.postponePollCompletionOperation(messageIds: messageIds, subscribers: subscribers)
|
||||||
let _ = strongSelf.addPollCompletion(f, id: id)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
strongSelf.startFirstOperation()
|
strongSelf.startFirstOperation()
|
||||||
} else {
|
} else {
|
||||||
@@ -506,29 +481,34 @@ public final class AccountStateManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func addPollCompletion(_ f: @escaping () -> Void, id: Int32?) -> Int32 {
|
private func postponePollCompletionOperation(messageIds: [MessageId], subscribers: [(Int32, ([MessageId]) -> Void)]) {
|
||||||
|
self.operations.append(.pollCompletion(self.getNextId(), messageIds, subscribers))
|
||||||
|
|
||||||
|
for i in 0 ..< self.operations.count {
|
||||||
|
if case .pollCompletion = self.operations[i] {
|
||||||
|
if i != self.operations.count - 1 {
|
||||||
|
assertionFailure()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func addPollCompletion(_ f: @escaping ([MessageId]) -> Void) -> Int32 {
|
||||||
assert(self.queue.isCurrent())
|
assert(self.queue.isCurrent())
|
||||||
|
|
||||||
let updatedId: Int32
|
let updatedId: Int32 = self.getNextId()
|
||||||
if let id = id {
|
|
||||||
updatedId = id
|
|
||||||
} else {
|
|
||||||
updatedId = self.getNextId()
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.operations.isEmpty {
|
for i in 0 ..< self.operations.count {
|
||||||
for i in 1 ..< self.operations.count {
|
if case let .pollCompletion(pollId, messageIds, subscribers) = self.operations[i] {
|
||||||
if case let .pollCompletion(pollId, subscribers) = self.operations[i] {
|
|
||||||
var subscribers = subscribers
|
var subscribers = subscribers
|
||||||
subscribers.append((updatedId, f))
|
subscribers.append((updatedId, f))
|
||||||
self.operations[i] = .pollCompletion(pollId, subscribers)
|
self.operations[i] = .pollCompletion(pollId, messageIds, subscribers)
|
||||||
return updatedId
|
return updatedId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let beginFirst = self.operations.isEmpty
|
let beginFirst = self.operations.isEmpty
|
||||||
self.operations.append(.pollCompletion(self.getNextId(), [(updatedId, f)]))
|
self.operations.append(.pollCompletion(self.getNextId(), [], [(updatedId, f)]))
|
||||||
if beginFirst {
|
if beginFirst {
|
||||||
self.startFirstOperation()
|
self.startFirstOperation()
|
||||||
}
|
}
|
||||||
@@ -538,12 +518,12 @@ public final class AccountStateManager {
|
|||||||
|
|
||||||
private func removePollCompletion(_ id: Int32) {
|
private func removePollCompletion(_ id: Int32) {
|
||||||
for i in 0 ..< self.operations.count {
|
for i in 0 ..< self.operations.count {
|
||||||
if case let .pollCompletion(pollId, subscribers) = self.operations[i] {
|
if case let .pollCompletion(pollId, messages, subscribers) = self.operations[i] {
|
||||||
for j in 0 ..< subscribers.count {
|
for j in 0 ..< subscribers.count {
|
||||||
if subscribers[j].0 == id {
|
if subscribers[j].0 == id {
|
||||||
var subscribers = subscribers
|
var subscribers = subscribers
|
||||||
subscribers.remove(at: j)
|
subscribers.remove(at: j)
|
||||||
self.operations[i] = .pollCompletion(pollId, subscribers)
|
self.operations[i] = .pollCompletion(pollId, messages, subscribers)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -551,14 +531,15 @@ public final class AccountStateManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func wakeup() -> Signal<Void, NoError> {
|
public func pollStateUpdateCompletion() -> Signal<[MessageId], NoError> {
|
||||||
return Signal { [weak self] subscriber in
|
return Signal { [weak self] subscriber in
|
||||||
let disposable = MetaDisposable()
|
let disposable = MetaDisposable()
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.queue.async {
|
strongSelf.queue.async {
|
||||||
let id = strongSelf.addPollCompletion({
|
let id = strongSelf.addPollCompletion({ messageIds in
|
||||||
|
subscriber.putNext(messageIds)
|
||||||
subscriber.putCompletion()
|
subscriber.putCompletion()
|
||||||
}, id: nil)
|
})
|
||||||
|
|
||||||
disposable.set(ActionDisposable {
|
disposable.set(ActionDisposable {
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
@@ -573,3 +554,52 @@ public final class AccountStateManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func messageForNotification(modifier: Modifier, id: MessageId, alwaysReturnMessage: Bool) -> (message: Message?, notify: Bool) {
|
||||||
|
var notify = true
|
||||||
|
|
||||||
|
let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
|
||||||
|
|
||||||
|
if let notificationSettings = modifier.getPeerNotificationSettings(id.peerId) as? TelegramPeerNotificationSettings {
|
||||||
|
switch notificationSettings.muteState {
|
||||||
|
case let .muted(until):
|
||||||
|
if until >= timestamp {
|
||||||
|
notify = false
|
||||||
|
}
|
||||||
|
case .unmuted:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Logger.shared.log("AccountStateManager", "notification settings for \(id.peerId) are undefined")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if notify {
|
||||||
|
let message = modifier.getMessage(id)
|
||||||
|
if let message = message {
|
||||||
|
var foundReadState = false
|
||||||
|
var isUnread = true
|
||||||
|
if let readState = modifier.getCombinedPeerReadState(id.peerId) {
|
||||||
|
if readState.isIncomingMessageIndexRead(MessageIndex(message)) {
|
||||||
|
isUnread = false
|
||||||
|
}
|
||||||
|
foundReadState = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !foundReadState {
|
||||||
|
Logger.shared.log("AccountStateManager", "read state for \(id.peerId) is undefined")
|
||||||
|
}
|
||||||
|
|
||||||
|
return (message, isUnread)
|
||||||
|
} else {
|
||||||
|
Logger.shared.log("AccountStateManager", "notification message doesn't exist")
|
||||||
|
return (nil, false)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var message: Message?
|
||||||
|
if alwaysReturnMessage {
|
||||||
|
message = modifier.getMessage(id)
|
||||||
|
}
|
||||||
|
return (message, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -322,7 +322,6 @@ public final class AccountViewTracker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func updatedCachedChannelParticipants(_ peerId: PeerId, forceImmediateUpdate: Bool = false) -> Signal<Void, NoError> {
|
public func updatedCachedChannelParticipants(_ peerId: PeerId, forceImmediateUpdate: Bool = false) -> Signal<Void, NoError> {
|
||||||
if let account = self.account {
|
|
||||||
let queue = self.queue
|
let queue = self.queue
|
||||||
return Signal { [weak self] subscriber in
|
return Signal { [weak self] subscriber in
|
||||||
let disposable = MetaDisposable()
|
let disposable = MetaDisposable()
|
||||||
@@ -362,9 +361,5 @@ public final class AccountViewTracker {
|
|||||||
}
|
}
|
||||||
return disposable
|
return disposable
|
||||||
}
|
}
|
||||||
return .never()
|
|
||||||
} else {
|
|
||||||
return .never()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
85
TelegramCore/AddPeerMember.swift
Normal file
85
TelegramCore/AddPeerMember.swift
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
import Foundation
|
||||||
|
#if os(macOS)
|
||||||
|
import PostboxMac
|
||||||
|
import SwiftSignalKitMac
|
||||||
|
import MtProtoKitMac
|
||||||
|
#else
|
||||||
|
import Postbox
|
||||||
|
import SwiftSignalKit
|
||||||
|
import MtProtoKitDynamic
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public enum AddPeerMemberError {
|
||||||
|
case generic
|
||||||
|
}
|
||||||
|
|
||||||
|
public func addPeerMember(account: Account, peerId: PeerId, memberId: PeerId) -> Signal<Void, AddPeerMemberError> {
|
||||||
|
return account.postbox.modify { modifier -> Signal<Void, AddPeerMemberError> in
|
||||||
|
if let peer = modifier.getPeer(peerId), let memberPeer = modifier.getPeer(memberId), let inputUser = apiInputUser(memberPeer) {
|
||||||
|
if let group = peer as? TelegramGroup {
|
||||||
|
return account.network.request(Api.functions.messages.addChatUser(chatId: group.id.id, userId: inputUser, fwdLimit: 100))
|
||||||
|
|> mapError { error -> AddPeerMemberError in
|
||||||
|
return .generic
|
||||||
|
}
|
||||||
|
|> mapToSignal { result -> Signal<Void, AddPeerMemberError> in
|
||||||
|
account.stateManager.addUpdates(result)
|
||||||
|
return account.postbox.modify { modifier -> Void in
|
||||||
|
if let message = result.messages.first, let timestamp = message.timestamp {
|
||||||
|
modifier.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in
|
||||||
|
if let cachedData = cachedData as? CachedGroupData, let participants = cachedData.participants {
|
||||||
|
var updatedParticipants = participants.participants
|
||||||
|
var found = false
|
||||||
|
for participant in participants.participants {
|
||||||
|
if participant.peerId == memberId {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
updatedParticipants.append(.member(id: memberId, invitedBy: account.peerId, invitedAt: timestamp))
|
||||||
|
}
|
||||||
|
return CachedGroupData(participants: CachedGroupParticipants(participants: updatedParticipants, version: participants.version), exportedInvitation: cachedData.exportedInvitation, botInfos: cachedData.botInfos)
|
||||||
|
} else {
|
||||||
|
return cachedData
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} |> mapError { _ -> AddPeerMemberError in return .generic }
|
||||||
|
}
|
||||||
|
} else if let channel = peer as? TelegramChannel, let inputChannel = apiInputChannel(channel) {
|
||||||
|
return account.network.request(Api.functions.channels.inviteToChannel(channel: inputChannel, users: [inputUser]))
|
||||||
|
|> mapError { error -> AddPeerMemberError in
|
||||||
|
return .generic
|
||||||
|
}
|
||||||
|
|> mapToSignal { result -> Signal<Void, AddPeerMemberError> in
|
||||||
|
account.stateManager.addUpdates(result)
|
||||||
|
return account.postbox.modify { modifier -> Void in
|
||||||
|
modifier.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in
|
||||||
|
if let cachedData = cachedData as? CachedChannelData, let participants = cachedData.topParticipants {
|
||||||
|
var updatedParticipants = participants.participants
|
||||||
|
var found = false
|
||||||
|
for participant in participants.participants {
|
||||||
|
if participant.peerId == memberId {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
|
||||||
|
if !found {
|
||||||
|
updatedParticipants.insert(.member(id: memberId, invitedAt: timestamp), at: 0)
|
||||||
|
}
|
||||||
|
return cachedData.withUpdatedTopParticipants(CachedChannelParticipants(participants: updatedParticipants))
|
||||||
|
} else {
|
||||||
|
return cachedData
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} |> mapError { _ -> AddPeerMemberError in return .generic }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return .fail(.generic)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return .fail(.generic)
|
||||||
|
}
|
||||||
|
} |> mapError { _ -> AddPeerMemberError in return .generic } |> switchToLatest
|
||||||
|
}
|
||||||
174
TelegramCore/Authorization.swift
Normal file
174
TelegramCore/Authorization.swift
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
import Foundation
|
||||||
|
#if os(macOS)
|
||||||
|
import PostboxMac
|
||||||
|
import SwiftSignalKitMac
|
||||||
|
import MtProtoKitMac
|
||||||
|
#else
|
||||||
|
import Postbox
|
||||||
|
import SwiftSignalKit
|
||||||
|
import MtProtoKitDynamic
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public enum AuthorizationCodeRequestError {
|
||||||
|
case invalidPhoneNumber
|
||||||
|
case limitExceeded
|
||||||
|
case generic
|
||||||
|
}
|
||||||
|
|
||||||
|
public func sendAuthorizationCode(account: UnauthorizedAccount, phoneNumber: String, apiId: Int32, apiHash: String) -> Signal<UnauthorizedAccount, AuthorizationCodeRequestError> {
|
||||||
|
let sendCode = Api.functions.auth.sendCode(flags: 0, phoneNumber: phoneNumber, currentNumber: nil, apiId: apiId, apiHash: apiHash)
|
||||||
|
|
||||||
|
let codeAndAccount = account.network.request(sendCode, automaticFloodWait: false)
|
||||||
|
|> map { result in
|
||||||
|
return (result, account)
|
||||||
|
} |> `catch` { error -> Signal<(Api.auth.SentCode, UnauthorizedAccount), MTRpcError> in
|
||||||
|
switch error.errorDescription {
|
||||||
|
case Regex("(PHONE_|USER_|NETWORK_)MIGRATE_(\\d+)"):
|
||||||
|
let range = error.errorDescription.range(of: "MIGRATE_")!
|
||||||
|
let updatedMasterDatacenterId = Int32(error.errorDescription.substring(from: range.upperBound))!
|
||||||
|
let updatedAccount = account.changedMasterDatacenterId(updatedMasterDatacenterId)
|
||||||
|
return updatedAccount
|
||||||
|
|> mapToSignalPromotingError { updatedAccount -> Signal<(Api.auth.SentCode, UnauthorizedAccount), MTRpcError> in
|
||||||
|
return updatedAccount.network.request(sendCode, automaticFloodWait: false)
|
||||||
|
|> map { sentCode in
|
||||||
|
return (sentCode, updatedAccount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case _:
|
||||||
|
return .fail(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|> mapError { error -> AuthorizationCodeRequestError in
|
||||||
|
if error.errorDescription.hasPrefix("FLOOD_WAIT") {
|
||||||
|
return .limitExceeded
|
||||||
|
} else if error.errorDescription == "PHONE_NUMBER_INVALID" {
|
||||||
|
return .invalidPhoneNumber
|
||||||
|
} else {
|
||||||
|
return .generic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return codeAndAccount
|
||||||
|
|> mapToSignal { (sentCode, account) -> Signal<UnauthorizedAccount, AuthorizationCodeRequestError> in
|
||||||
|
return account.postbox.modify { modifier -> UnauthorizedAccount in
|
||||||
|
switch sentCode {
|
||||||
|
case let .sentCode(_, type, phoneCodeHash, nextType, timeout):
|
||||||
|
var parsedNextType: AuthorizationCodeNextType?
|
||||||
|
if let nextType = nextType {
|
||||||
|
parsedNextType = AuthorizationCodeNextType(apiType: nextType)
|
||||||
|
}
|
||||||
|
modifier.setState(UnauthorizedAccountState(masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: phoneNumber, type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: timeout, nextType: parsedNextType)))
|
||||||
|
}
|
||||||
|
return account
|
||||||
|
} |> mapError { _ -> AuthorizationCodeRequestError in return .generic }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum AuthorizationCodeVerificationError {
|
||||||
|
case invalidCode
|
||||||
|
case limitExceeded
|
||||||
|
case generic
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum AuthorizationCodeResult {
|
||||||
|
case Authorization(Api.auth.Authorization)
|
||||||
|
case Password(String)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func authorizeWithCode(account: UnauthorizedAccount, code: String) -> Signal<Void, AuthorizationCodeVerificationError> {
|
||||||
|
return account.postbox.modify { modifier -> Signal<Void, AuthorizationCodeVerificationError> in
|
||||||
|
if let state = modifier.getState() as? UnauthorizedAccountState {
|
||||||
|
switch state.contents {
|
||||||
|
case let .confirmationCodeEntry(number, _, hash, _, _):
|
||||||
|
return account.network.request(Api.functions.auth.signIn(phoneNumber: number, phoneCodeHash: hash, phoneCode: code), automaticFloodWait: false) |> map { authorization in
|
||||||
|
return AuthorizationCodeResult.Authorization(authorization)
|
||||||
|
} |> `catch` { error -> Signal<AuthorizationCodeResult, AuthorizationCodeVerificationError> in
|
||||||
|
switch (error.errorCode, error.errorDescription) {
|
||||||
|
case (401, "SESSION_PASSWORD_NEEDED"):
|
||||||
|
return account.network.request(Api.functions.account.getPassword(), automaticFloodWait: false)
|
||||||
|
|> mapError { error -> AuthorizationCodeVerificationError in
|
||||||
|
if error.errorDescription.hasPrefix("FLOOD_WAIT") {
|
||||||
|
return .limitExceeded
|
||||||
|
} else {
|
||||||
|
return .generic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|> mapToSignal { result -> Signal<AuthorizationCodeResult, AuthorizationCodeVerificationError> in
|
||||||
|
switch result {
|
||||||
|
case .noPassword:
|
||||||
|
return .fail(.generic)
|
||||||
|
case let .password(_, _, hint, _, _):
|
||||||
|
return .single(.Password(hint))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case let (_, errorDescription):
|
||||||
|
if errorDescription.hasPrefix("FLOOD_WAIT") {
|
||||||
|
return .fail(.limitExceeded)
|
||||||
|
} else if errorDescription == "PHONE_CODE_INVALID" {
|
||||||
|
return .fail(.invalidCode)
|
||||||
|
} else {
|
||||||
|
return .fail(.generic)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|> mapToSignal { result -> Signal<Void, AuthorizationCodeVerificationError> in
|
||||||
|
return account.postbox.modify { modifier -> Void in
|
||||||
|
switch result {
|
||||||
|
case let .Password(hint):
|
||||||
|
modifier.setState(UnauthorizedAccountState(masterDatacenterId: account.masterDatacenterId, contents: .passwordEntry(hint: hint)))
|
||||||
|
case let .Authorization(authorization):
|
||||||
|
switch authorization {
|
||||||
|
case let .authorization(_, _, user):
|
||||||
|
let user = TelegramUser(user: user)
|
||||||
|
let state = AuthorizedAccountState(masterDatacenterId: account.masterDatacenterId, peerId: user.id, state: nil)
|
||||||
|
modifier.setState(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} |> mapError { _ -> AuthorizationCodeVerificationError in
|
||||||
|
return .generic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return .fail(.generic)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return .fail(.generic)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|> mapError { _ -> AuthorizationCodeVerificationError in
|
||||||
|
return .generic
|
||||||
|
}
|
||||||
|
|> switchToLatest
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum AuthorizationPasswordVerificationError {
|
||||||
|
case limitExceeded
|
||||||
|
case invalidPassword
|
||||||
|
case generic
|
||||||
|
}
|
||||||
|
|
||||||
|
public func authorizeWithPassword(account: UnauthorizedAccount, password: String) -> Signal<Void, AuthorizationPasswordVerificationError> {
|
||||||
|
return verifyPassword(account, password: password)
|
||||||
|
|> `catch` { error -> Signal<Api.auth.Authorization, AuthorizationPasswordVerificationError> in
|
||||||
|
if error.errorDescription.hasPrefix("FLOOD_WAIT") {
|
||||||
|
return .fail(.limitExceeded)
|
||||||
|
} else if error.errorDescription == "PASSWORD_HASH_INVALID" {
|
||||||
|
return .fail(.invalidPassword)
|
||||||
|
} else {
|
||||||
|
return .fail(.generic)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|> mapToSignal { result -> Signal<Void, AuthorizationPasswordVerificationError> in
|
||||||
|
return account.postbox.modify { modifier -> Void in
|
||||||
|
switch result {
|
||||||
|
case let .authorization(_, _, user):
|
||||||
|
let user = TelegramUser(user: user)
|
||||||
|
let state = AuthorizedAccountState(masterDatacenterId: account.masterDatacenterId, peerId: user.id, state: nil)
|
||||||
|
modifier.setState(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|> mapError { _ -> AuthorizationPasswordVerificationError in
|
||||||
|
return .generic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -106,6 +106,10 @@ public final class CachedChannelData: CachedPeerData {
|
|||||||
return CachedChannelData(flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, topParticipants: topParticipants)
|
return CachedChannelData(flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, topParticipants: topParticipants)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func withUpdatedParticipantsSummary(_ participantsSummary: CachedChannelParticipantsSummary) -> CachedChannelData {
|
||||||
|
return CachedChannelData(flags: self.flags, about: self.about, participantsSummary: participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, topParticipants: self.topParticipants)
|
||||||
|
}
|
||||||
|
|
||||||
public init(decoder: Decoder) {
|
public init(decoder: Decoder) {
|
||||||
self.flags = CachedChannelFlags(rawValue: decoder.decodeInt32ForKey("f"))
|
self.flags = CachedChannelFlags(rawValue: decoder.decodeInt32ForKey("f"))
|
||||||
self.about = decoder.decodeStringForKey("a")
|
self.about = decoder.decodeStringForKey("a")
|
||||||
@@ -178,6 +182,10 @@ public final class CachedChannelData: CachedPeerData {
|
|||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func withUpdatedAbout(_ about: String?) -> CachedChannelData {
|
||||||
|
return CachedChannelData(flags: self.flags, about: about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, topParticipants: self.topParticipants)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension CachedChannelData {
|
extension CachedChannelData {
|
||||||
|
|||||||
@@ -74,6 +74,17 @@ public enum GroupParticipant: Coding, Equatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public var invitedBy: PeerId {
|
||||||
|
switch self {
|
||||||
|
case let .admin(_, invitedBy, _):
|
||||||
|
return invitedBy
|
||||||
|
case let .member(_, invitedBy, _):
|
||||||
|
return invitedBy
|
||||||
|
case let .creator(id):
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class CachedGroupParticipants: Coding, Equatable {
|
public final class CachedGroupParticipants: Coding, Equatable {
|
||||||
|
|||||||
53
TelegramCore/ChannelAdmins.swift
Normal file
53
TelegramCore/ChannelAdmins.swift
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import Foundation
|
||||||
|
#if os(macOS)
|
||||||
|
import PostboxMac
|
||||||
|
import SwiftSignalKitMac
|
||||||
|
import MtProtoKitMac
|
||||||
|
#else
|
||||||
|
import Postbox
|
||||||
|
import SwiftSignalKit
|
||||||
|
import MtProtoKitDynamic
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public struct RenderedChannelParticipant: Equatable {
|
||||||
|
public let participant: ChannelParticipant
|
||||||
|
public let peer: Peer
|
||||||
|
|
||||||
|
public init(participant: ChannelParticipant, peer: Peer) {
|
||||||
|
self.participant = participant
|
||||||
|
self.peer = peer
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func ==(lhs: RenderedChannelParticipant, rhs: RenderedChannelParticipant) -> Bool {
|
||||||
|
return lhs.participant == rhs.participant && lhs.peer.isEqual(rhs.peer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func channelAdmins(account: Account, peerId: PeerId) -> Signal<[RenderedChannelParticipant], NoError> {
|
||||||
|
return account.postbox.modify { modifier -> Signal<[RenderedChannelParticipant], NoError> in
|
||||||
|
if let peer = modifier.getPeer(peerId), let inputChannel = apiInputChannel(peer) {
|
||||||
|
return account.network.request(Api.functions.channels.getParticipants(channel: inputChannel, filter: .channelParticipantsAdmins, offset: 0, limit: 100))
|
||||||
|
|> retryRequest
|
||||||
|
|> map { result -> [RenderedChannelParticipant] in
|
||||||
|
var items: [RenderedChannelParticipant] = []
|
||||||
|
switch result {
|
||||||
|
case let .channelParticipants(_, participants, users):
|
||||||
|
var peers: [PeerId: Peer] = [:]
|
||||||
|
for user in users {
|
||||||
|
let peer = TelegramUser(user: user)
|
||||||
|
peers[peer.id] = peer
|
||||||
|
}
|
||||||
|
|
||||||
|
for participant in CachedChannelParticipants(apiParticipants: participants).participants {
|
||||||
|
if let peer = peers[participant.peerId] {
|
||||||
|
items.append(RenderedChannelParticipant(participant: participant, peer: peer))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return .single([])
|
||||||
|
}
|
||||||
|
} |> switchToLatest
|
||||||
|
}
|
||||||
@@ -9,7 +9,7 @@ import Foundation
|
|||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class Download {
|
class Download: NSObject, MTRequestMessageServiceDelegate {
|
||||||
let datacenterId: Int
|
let datacenterId: Int
|
||||||
let context: MTContext
|
let context: MTContext
|
||||||
let mtProto: MTProto
|
let mtProto: MTProto
|
||||||
@@ -25,6 +25,10 @@ class Download {
|
|||||||
self.mtProto.requiredAuthToken = Int(datacenterId) as NSNumber
|
self.mtProto.requiredAuthToken = Int(datacenterId) as NSNumber
|
||||||
}
|
}
|
||||||
self.requestService = MTRequestMessageService(context: self.context)
|
self.requestService = MTRequestMessageService(context: self.context)
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
self.requestService.delegate = self
|
||||||
self.mtProto.add(self.requestService)
|
self.mtProto.add(self.requestService)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,6 +37,11 @@ class Download {
|
|||||||
self.mtProto.stop()
|
self.mtProto.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func requestMessageServiceAuthorizationRequired(_ requestMessageService: MTRequestMessageService!) {
|
||||||
|
self.context.updateAuthTokenForDatacenter(withId: self.datacenterId, authToken: nil)
|
||||||
|
self.context.authTokenForDatacenter(withIdRequired: self.datacenterId, authToken:self.mtProto.requiredAuthToken, masterDatacenterId: self.mtProto.authTokenMasterDatacenterId)
|
||||||
|
}
|
||||||
|
|
||||||
func uploadPart(fileId: Int64, index: Int, data: Data) -> Signal<Void, NoError> {
|
func uploadPart(fileId: Int64, index: Int, data: Data) -> Signal<Void, NoError> {
|
||||||
return Signal<Void, MTRpcError> { subscriber in
|
return Signal<Void, MTRpcError> { subscriber in
|
||||||
let request = MTRequest()
|
let request = MTRequest()
|
||||||
|
|||||||
@@ -53,16 +53,17 @@ private func filterMessageAttributesForForwardedMessage(_ attributes: [MessageAt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func enqueueMessages(account: Account, peerId: PeerId, messages: [EnqueueMessage]) -> Signal<Void, NoError> {
|
public func enqueueMessages(account: Account, peerId: PeerId, messages: [EnqueueMessage]) -> Signal<[MessageId?], NoError> {
|
||||||
return account.postbox.modify { modifier -> Void in
|
return account.postbox.modify { modifier -> [MessageId?] in
|
||||||
enqueueMessages(modifier: modifier, account: account, peerId: peerId, messages: messages)
|
return enqueueMessages(modifier: modifier, account: account, peerId: peerId, messages: messages)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func enqueueMessages(modifier: Modifier, account: Account, peerId: PeerId, messages: [EnqueueMessage]) {
|
func enqueueMessages(modifier: Modifier, account: Account, peerId: PeerId, messages: [EnqueueMessage]) -> [MessageId?] {
|
||||||
if let peer = modifier.getPeer(peerId) {
|
if let peer = modifier.getPeer(peerId) {
|
||||||
var storeMessages: [StoreMessage] = []
|
var storeMessages: [StoreMessage] = []
|
||||||
let timestamp = Int32(account.network.context.globalTime())
|
let timestamp = Int32(account.network.context.globalTime())
|
||||||
|
var globallyUniqueIds: [Int64] = []
|
||||||
for message in messages {
|
for message in messages {
|
||||||
var attributes: [MessageAttribute] = []
|
var attributes: [MessageAttribute] = []
|
||||||
var flags = StoreMessageFlags()
|
var flags = StoreMessageFlags()
|
||||||
@@ -71,6 +72,7 @@ func enqueueMessages(modifier: Modifier, account: Account, peerId: PeerId, messa
|
|||||||
var randomId: Int64 = 0
|
var randomId: Int64 = 0
|
||||||
arc4random_buf(&randomId, 8)
|
arc4random_buf(&randomId, 8)
|
||||||
attributes.append(OutgoingMessageInfoAttribute(uniqueId: randomId))
|
attributes.append(OutgoingMessageInfoAttribute(uniqueId: randomId))
|
||||||
|
globallyUniqueIds.append(randomId)
|
||||||
|
|
||||||
switch message {
|
switch message {
|
||||||
case let .message(text, requestedAttributes, media, replyToMessageId):
|
case let .message(text, requestedAttributes, media, replyToMessageId):
|
||||||
@@ -122,8 +124,15 @@ func enqueueMessages(modifier: Modifier, account: Account, peerId: PeerId, messa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var messageIds: [MessageId?] = []
|
||||||
if !storeMessages.isEmpty {
|
if !storeMessages.isEmpty {
|
||||||
modifier.addMessages(storeMessages, location: .Random)
|
let globallyUniqueIdToMessageId = modifier.addMessages(storeMessages, location: .Random)
|
||||||
|
for globallyUniqueId in globallyUniqueIds {
|
||||||
|
messageIds.append(globallyUniqueIdToMessageId[globallyUniqueId])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return messageIds
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import TelegramCorePrivateModule
|
import TelegramCorePrivateModule
|
||||||
|
import SwiftSignalKit
|
||||||
|
|
||||||
private let queue = DispatchQueue(label: "org.telegram.Telegram.trace", qos: .utility)
|
private let queue = DispatchQueue(label: "org.telegram.Telegram.trace", qos: .utility)
|
||||||
|
|
||||||
public func trace(_ what: @autoclosure() -> String) {
|
public func trace2(_ what: @autoclosure() -> String) {
|
||||||
let string = what()
|
let string = what()
|
||||||
var rawTime = time_t()
|
var rawTime = time_t()
|
||||||
time(&rawTime)
|
time(&rawTime)
|
||||||
@@ -20,7 +21,7 @@ public func trace(_ what: @autoclosure() -> String) {
|
|||||||
//}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func trace(_ domain: String, what: @autoclosure() -> String) {
|
public func trace1(_ domain: String, what: @autoclosure() -> String) {
|
||||||
let string = what()
|
let string = what()
|
||||||
var rawTime = time_t()
|
var rawTime = time_t()
|
||||||
time(&rawTime)
|
time(&rawTime)
|
||||||
@@ -43,27 +44,57 @@ public func registerLoggingFunctions() {
|
|||||||
setBridgingTraceFunction({ domain, what in
|
setBridgingTraceFunction({ domain, what in
|
||||||
if let what = what {
|
if let what = what {
|
||||||
if let domain = domain {
|
if let domain = domain {
|
||||||
trace(domain, what: what as String)
|
Logger.shared.log(domain, what as String)
|
||||||
} else {
|
} else {
|
||||||
trace("", what: what as String)
|
Logger.shared.log("", what as String)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var sharedLogger: Logger?
|
||||||
|
|
||||||
public final class Logger {
|
public final class Logger {
|
||||||
private let queue = DispatchQueue(label: "org.telegram.Telegram.log", qos: .utility)
|
private let queue = DispatchQueue(label: "org.telegram.Telegram.log", qos: .utility)
|
||||||
private let maxLength: Int = 512 * 1024
|
private let maxLength: Int = 2 * 1024 * 1024
|
||||||
|
//private let maxLength: Int = 4 * 1024
|
||||||
private let maxFiles: Int = 20
|
private let maxFiles: Int = 20
|
||||||
|
|
||||||
private let basePath: String
|
private let basePath: String
|
||||||
private var file: (Int32, Int)?
|
private var file: (Int32, Int)?
|
||||||
|
|
||||||
init(basePath: String) {
|
public static func setSharedLogger(_ logger: Logger) {
|
||||||
|
sharedLogger = logger
|
||||||
|
}
|
||||||
|
|
||||||
|
public static var shared: Logger {
|
||||||
|
return sharedLogger!
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(basePath: String) {
|
||||||
self.basePath = basePath
|
self.basePath = basePath
|
||||||
}
|
}
|
||||||
|
|
||||||
func log(_ tag: String, _ what: @autoclosure () -> String) {
|
public func collectLogs() -> Signal<[(String, String)], NoError> {
|
||||||
|
return Signal { subscriber in
|
||||||
|
self.queue.async {
|
||||||
|
var result: [(String, String)] = []
|
||||||
|
if let files = try? FileManager.default.contentsOfDirectory(at: URL(fileURLWithPath: self.basePath), includingPropertiesForKeys: [URLResourceKey.creationDateKey], options: []) {
|
||||||
|
for url in files {
|
||||||
|
if url.lastPathComponent.hasPrefix("log-") {
|
||||||
|
result.append((url.lastPathComponent, url.path))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
subscriber.putNext(result)
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}
|
||||||
|
|
||||||
|
return EmptyDisposable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func log(_ tag: String, _ what: @autoclosure () -> String) {
|
||||||
let string = what()
|
let string = what()
|
||||||
|
|
||||||
var rawTime = time_t()
|
var rawTime = time_t()
|
||||||
@@ -73,23 +104,35 @@ public final class Logger {
|
|||||||
|
|
||||||
var curTime = timeval()
|
var curTime = timeval()
|
||||||
gettimeofday(&curTime, nil)
|
gettimeofday(&curTime, nil)
|
||||||
let seconds = curTime.tv_sec
|
|
||||||
let milliseconds = curTime.tv_usec / 1000
|
let milliseconds = curTime.tv_usec / 1000
|
||||||
|
|
||||||
let content = String(format: "[%@] %d-%d-%d %02d:%02d:%02d.%03d %@", arguments: [tag, Int(timeinfo.tm_year) + 1900, Int(timeinfo.tm_mon + 1), Int(timeinfo.tm_yday), Int(timeinfo.tm_hour), Int(timeinfo.tm_min), Int(seconds), Int(milliseconds), string])
|
#if TARGET_IPHONE_SIMULATOR || DEBUG
|
||||||
|
let content = String(format: "[%@] %d-%d-%d %02d:%02d:%02d.%03d %@", arguments: [tag, Int(timeinfo.tm_year) + 1900, Int(timeinfo.tm_mon + 1), Int(timeinfo.tm_yday), Int(timeinfo.tm_hour), Int(timeinfo.tm_min), Int(timeinfo.tm_sec), Int(milliseconds), string])
|
||||||
|
|
||||||
|
print(content)
|
||||||
|
#endif
|
||||||
|
|
||||||
self.queue.async {
|
self.queue.async {
|
||||||
|
#if !(TARGET_IPHONE_SIMULATOR || DEBUG)
|
||||||
|
let content = String(format: "[%@] %d-%d-%d %02d:%02d:%02d.%03d %@", arguments: [tag, Int(timeinfo.tm_year) + 1900, Int(timeinfo.tm_mon + 1), Int(timeinfo.tm_yday), Int(timeinfo.tm_hour), Int(timeinfo.tm_min), Int(timeinfo.tm_sec), Int(milliseconds), string])
|
||||||
|
#endif
|
||||||
|
|
||||||
var fd: Int32?
|
var fd: Int32?
|
||||||
var createNew = false
|
var openNew = false
|
||||||
if let (file, length) = self.file {
|
if let (file, length) = self.file {
|
||||||
if length < self.maxLength {
|
if length >= self.maxLength {
|
||||||
close(file)
|
close(file)
|
||||||
createNew = true
|
openNew = true
|
||||||
} else {
|
} else {
|
||||||
fd = file
|
fd = file
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
openNew = true
|
||||||
|
}
|
||||||
|
if openNew {
|
||||||
let _ = try? FileManager.default.createDirectory(atPath: self.basePath, withIntermediateDirectories: true, attributes: nil)
|
let _ = try? FileManager.default.createDirectory(atPath: self.basePath, withIntermediateDirectories: true, attributes: nil)
|
||||||
|
|
||||||
|
var createNew = false
|
||||||
if let files = try? FileManager.default.contentsOfDirectory(at: URL(fileURLWithPath: self.basePath), includingPropertiesForKeys: [URLResourceKey.creationDateKey], options: []) {
|
if let files = try? FileManager.default.contentsOfDirectory(at: URL(fileURLWithPath: self.basePath), includingPropertiesForKeys: [URLResourceKey.creationDateKey], options: []) {
|
||||||
var minCreationDate: (Date, URL)?
|
var minCreationDate: (Date, URL)?
|
||||||
var maxCreationDate: (Date, URL)?
|
var maxCreationDate: (Date, URL)?
|
||||||
@@ -125,9 +168,11 @@ public final class Logger {
|
|||||||
createNew = true
|
createNew = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if createNew {
|
if createNew {
|
||||||
let path = self.basePath + "/log-\(Date()).txt"
|
let fileName = String(format: "log-%d-%d-%d_%02d-%02d-%02d.%03d.txt", arguments: [Int(timeinfo.tm_year) + 1900, Int(timeinfo.tm_mon + 1), Int(timeinfo.tm_yday), Int(timeinfo.tm_hour), Int(timeinfo.tm_min), Int(timeinfo.tm_sec), Int(milliseconds)])
|
||||||
|
|
||||||
|
let path = self.basePath + "/" + fileName
|
||||||
|
|
||||||
let handle = open(path, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR)
|
let handle = open(path, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR)
|
||||||
if handle >= 0 {
|
if handle >= 0 {
|
||||||
@@ -135,12 +180,15 @@ public final class Logger {
|
|||||||
self.file = (handle, 0)
|
self.file = (handle, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let fd = fd {
|
if let fd = fd {
|
||||||
if let data = content.data(using: .utf8) {
|
if let data = content.data(using: .utf8) {
|
||||||
data.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Void in
|
data.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Void in
|
||||||
write(fd, bytes, data.count)
|
write(fd, bytes, data.count)
|
||||||
}
|
}
|
||||||
|
var newline: UInt8 = 0x0a
|
||||||
|
write(fd, &newline, 1)
|
||||||
if let file = self.file {
|
if let file = self.file {
|
||||||
self.file = (file.0, file.1 + data.count)
|
self.file = (file.0, file.1 + data.count)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
21
TelegramCore/LoggedOutAccountAttribute.swift
Normal file
21
TelegramCore/LoggedOutAccountAttribute.swift
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import Foundation
|
||||||
|
#if os(macOS)
|
||||||
|
import PostboxMac
|
||||||
|
#else
|
||||||
|
import Postbox
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public final class LoggedOutAccountAttribute: AccountRecordAttribute {
|
||||||
|
public init() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(decoder: Decoder) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public func encode(_ encoder: Encoder) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public func isEqual(to: AccountRecordAttribute) -> Bool {
|
||||||
|
return to is LoggedOutAccountAttribute
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -665,12 +665,12 @@ private func sendBoxedDecryptedMessage(postbox: Postbox, network: Network, peer:
|
|||||||
}
|
}
|
||||||
let canonicalOperationIndex = sequenceState.canonicalOutgoingOperationIndex(operationIndex)
|
let canonicalOperationIndex = sequenceState.canonicalOutgoingOperationIndex(operationIndex)
|
||||||
maybeKey = state.keychain.latestKey(validForSequenceBasedCanonicalIndex: canonicalOperationIndex)
|
maybeKey = state.keychain.latestKey(validForSequenceBasedCanonicalIndex: canonicalOperationIndex)
|
||||||
print("sending message with index \(canonicalOperationIndex) key \(maybeKey?.fingerprint)")
|
Logger.shared.log("SecretChat", "sending message with index \(canonicalOperationIndex) key \(maybeKey?.fingerprint)")
|
||||||
sequenceInfo = SecretChatOperationSequenceInfo(topReceivedOperationIndex: topReceivedOperationIndex, operationIndex: canonicalOperationIndex)
|
sequenceInfo = SecretChatOperationSequenceInfo(topReceivedOperationIndex: topReceivedOperationIndex, operationIndex: canonicalOperationIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let key = maybeKey else {
|
guard let key = maybeKey else {
|
||||||
trace("SecretChat", what: "no valid key found")
|
Logger.shared.log("SecretChat", "no valid key found")
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -194,10 +194,10 @@ public class Network {
|
|||||||
self.shouldKeepConnectionDisposable.set(shouldKeepConnectionSignal.start(next: { [weak self] value in
|
self.shouldKeepConnectionDisposable.set(shouldKeepConnectionSignal.start(next: { [weak self] value in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
if value {
|
if value {
|
||||||
trace("Network", what: "Resume network connection")
|
Logger.shared.log("Network", "Resume network connection")
|
||||||
strongSelf.mtProto.resume()
|
strongSelf.mtProto.resume()
|
||||||
} else {
|
} else {
|
||||||
trace("Network", what: "Pause network connection")
|
Logger.shared.log("Network", "Pause network connection")
|
||||||
strongSelf.mtProto.pause()
|
strongSelf.mtProto.pause()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ void setBridgingTraceFunction(void (*f)(NSString *, NSString *)) {
|
|||||||
#if TARGET_IPHONE_SIMULATOR
|
#if TARGET_IPHONE_SIMULATOR
|
||||||
static bool loggingEnabled = true;
|
static bool loggingEnabled = true;
|
||||||
#elif defined(DEBUG)
|
#elif defined(DEBUG)
|
||||||
static bool loggingEnabled = false;
|
static bool loggingEnabled = true;
|
||||||
#else
|
#else
|
||||||
static bool loggingEnabled = false;
|
static bool loggingEnabled = false;
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -249,7 +249,7 @@ func processSecretChatIncomingDecryptedOperations(mediaBox: MediaBox, modifier:
|
|||||||
if let error = error as? MessageParsingError {
|
if let error = error as? MessageParsingError {
|
||||||
switch error {
|
switch error {
|
||||||
case .contentParsingError:
|
case .contentParsingError:
|
||||||
print("Couldn't parse secret message payload")
|
Logger.shared.log("SecretChat", "Couldn't parse secret message payload")
|
||||||
removeTagLocalIndices.append(entry.tagLocalIndex)
|
removeTagLocalIndices.append(entry.tagLocalIndex)
|
||||||
return true
|
return true
|
||||||
case .unsupportedLayer:
|
case .unsupportedLayer:
|
||||||
@@ -261,10 +261,10 @@ func processSecretChatIncomingDecryptedOperations(mediaBox: MediaBox, modifier:
|
|||||||
removeTagLocalIndices.append(entry.tagLocalIndex)
|
removeTagLocalIndices.append(entry.tagLocalIndex)
|
||||||
return true
|
return true
|
||||||
case .holesInSequenceBasedLayer:
|
case .holesInSequenceBasedLayer:
|
||||||
print("Found holes in incoming operation sequence")
|
Logger.shared.log("SecretChat", "Found holes in incoming operation sequence")
|
||||||
return false
|
return false
|
||||||
case .secretChatCorruption:
|
case .secretChatCorruption:
|
||||||
print("Secret chat corrupted")
|
Logger.shared.log("SecretChat", "Secret chat corrupted")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -285,7 +285,7 @@ func processSecretChatIncomingDecryptedOperations(mediaBox: MediaBox, modifier:
|
|||||||
switch updatedState.embeddedState {
|
switch updatedState.embeddedState {
|
||||||
case let .sequenceBasedLayer(sequenceState):
|
case let .sequenceBasedLayer(sequenceState):
|
||||||
let tagLocalIndex = max(0, sequenceState.outgoingOperationIndexFromCanonicalOperationIndex(maxAcknowledgedCanonicalOperationIndex) - 1)
|
let tagLocalIndex = max(0, sequenceState.outgoingOperationIndexFromCanonicalOperationIndex(maxAcknowledgedCanonicalOperationIndex) - 1)
|
||||||
//trace("SecretChat", what: "peer \(peerId) dropping acknowledged operations <= \(tagLocalIndex)")
|
//Logger.shared.log("SecretChat", "peer \(peerId) dropping acknowledged operations <= \(tagLocalIndex)")
|
||||||
modifier.operationLogRemoveEntries(peerId: peerId, tag: OperationLogTags.SecretOutgoing, withTagLocalIndicesEqualToOrLowerThan: tagLocalIndex)
|
modifier.operationLogRemoveEntries(peerId: peerId, tag: OperationLogTags.SecretOutgoing, withTagLocalIndicesEqualToOrLowerThan: tagLocalIndex)
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -112,18 +112,18 @@ func processSecretChatIncomingEncryptedOperations(modifier: Modifier, peerId: Pe
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
trace("SecretChat", what: "peerId \(peerId) malformed data after decryption")
|
Logger.shared.log("SecretChat", "peerId \(peerId) malformed data after decryption")
|
||||||
}
|
}
|
||||||
|
|
||||||
removeTagLocalIndices.append(entry.tagLocalIndex)
|
removeTagLocalIndices.append(entry.tagLocalIndex)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
trace("SecretChat", what: "peerId \(peerId) couldn't decrypt message content")
|
Logger.shared.log("SecretChat", "peerId \(peerId) couldn't decrypt message content")
|
||||||
removeTagLocalIndices.append(entry.tagLocalIndex)
|
removeTagLocalIndices.append(entry.tagLocalIndex)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
trace("SecretChat", what: "peerId \(peerId) key \(operation.keyFingerprint) doesn't exist")
|
Logger.shared.log("SecretChat", "peerId \(peerId) key \(operation.keyFingerprint) doesn't exist")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
assertionFailure()
|
assertionFailure()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
func arc4random64() -> Int64 {
|
public func arc4random64() -> Int64 {
|
||||||
var value: Int64 = 0
|
var value: Int64 = 0
|
||||||
arc4random_buf(&value, 8)
|
arc4random_buf(&value, 8)
|
||||||
return value
|
return value
|
||||||
|
|||||||
78
TelegramCore/RemovePeerMember.swift
Normal file
78
TelegramCore/RemovePeerMember.swift
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import Foundation
|
||||||
|
#if os(macOS)
|
||||||
|
import PostboxMac
|
||||||
|
import SwiftSignalKitMac
|
||||||
|
import MtProtoKitMac
|
||||||
|
#else
|
||||||
|
import Postbox
|
||||||
|
import SwiftSignalKit
|
||||||
|
import MtProtoKitDynamic
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public func removePeerMember(account: Account, peerId: PeerId, memberId: PeerId) -> Signal<Void, NoError> {
|
||||||
|
return account.postbox.modify { modifier -> Signal<Void, NoError> in
|
||||||
|
if let peer = modifier.getPeer(peerId), let memberPeer = modifier.getPeer(memberId), let inputUser = apiInputUser(memberPeer) {
|
||||||
|
if let group = peer as? TelegramGroup {
|
||||||
|
return account.network.request(Api.functions.messages.deleteChatUser(chatId: group.id.id, userId: inputUser))
|
||||||
|
|> mapError { error -> Void in
|
||||||
|
return Void()
|
||||||
|
}
|
||||||
|
|> `catch` { _ -> Signal<Api.Updates, NoError> in
|
||||||
|
return .complete()
|
||||||
|
}
|
||||||
|
|> mapToSignal { result -> Signal<Void, NoError> in
|
||||||
|
account.stateManager.addUpdates(result)
|
||||||
|
|
||||||
|
return account.postbox.modify { modifier -> Void in
|
||||||
|
modifier.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in
|
||||||
|
if let cachedData = cachedData as? CachedGroupData, let participants = cachedData.participants {
|
||||||
|
var updatedParticipants = participants.participants
|
||||||
|
for i in 0 ..< participants.participants.count {
|
||||||
|
if participants.participants[i].peerId == memberId {
|
||||||
|
updatedParticipants.remove(at: i)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return CachedGroupData(participants: CachedGroupParticipants(participants: updatedParticipants, version: participants.version), exportedInvitation: cachedData.exportedInvitation, botInfos: cachedData.botInfos)
|
||||||
|
} else {
|
||||||
|
return cachedData
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let channel = peer as? TelegramChannel, let inputChannel = apiInputChannel(channel) {
|
||||||
|
return account.network.request(Api.functions.channels.kickFromChannel(channel: inputChannel, userId: inputUser, kicked: .boolTrue))
|
||||||
|
|> mapError { error -> Void in
|
||||||
|
return Void()
|
||||||
|
}
|
||||||
|
|> `catch` { _ -> Signal<Api.Updates, NoError> in
|
||||||
|
return .complete()
|
||||||
|
}
|
||||||
|
|> mapToSignal { result -> Signal<Void, NoError> in
|
||||||
|
account.stateManager.addUpdates(result)
|
||||||
|
|
||||||
|
return account.postbox.modify { modifier -> Void in
|
||||||
|
modifier.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in
|
||||||
|
if let cachedData = cachedData as? CachedChannelData, let participants = cachedData.topParticipants {
|
||||||
|
var updatedParticipants = participants.participants
|
||||||
|
for i in 0 ..< participants.participants.count {
|
||||||
|
if participants.participants[i].peerId == memberId {
|
||||||
|
updatedParticipants.remove(at: i)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cachedData.withUpdatedTopParticipants(CachedChannelParticipants(participants: updatedParticipants))
|
||||||
|
} else {
|
||||||
|
return cachedData
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return .complete()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return .complete()
|
||||||
|
}
|
||||||
|
} |> switchToLatest
|
||||||
|
}
|
||||||
@@ -31,6 +31,8 @@ public func requestStartBot(account: Account, botPeerId: PeerId, payload: String
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return enqueueMessages(account: account, peerId: botPeerId, messages: [.message(text: "/start", attributes: [], media: nil, replyToMessageId: nil)])
|
return enqueueMessages(account: account, peerId: botPeerId, messages: [.message(text: "/start", attributes: [], media: nil, replyToMessageId: nil)]) |> mapToSignal { _ -> Signal<Void, NoError> in
|
||||||
|
return .complete()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
import PostboxMac
|
import PostboxMac
|
||||||
import SwiftSignalKitMac
|
import SwiftSignalKitMac
|
||||||
|
|||||||
@@ -269,4 +269,8 @@ public final class TelegramChannel: Peer {
|
|||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func withUpdatedAddressName(_ addressName: String?) -> TelegramChannel {
|
||||||
|
return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: addressName, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, role: self.role, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,5 +119,4 @@ func fetchAndUpdateCachedPeerData(peerId: PeerId, network: Network, postbox: Pos
|
|||||||
return .complete()
|
return .complete()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return .never()
|
|
||||||
}
|
}
|
||||||
|
|||||||
92
TelegramCore/UpdatePeerInfo.swift
Normal file
92
TelegramCore/UpdatePeerInfo.swift
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
import Foundation
|
||||||
|
#if os(macOS)
|
||||||
|
import PostboxMac
|
||||||
|
import SwiftSignalKitMac
|
||||||
|
import MtProtoKitMac
|
||||||
|
#else
|
||||||
|
import Postbox
|
||||||
|
import SwiftSignalKit
|
||||||
|
import MtProtoKitDynamic
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public enum UpdatePeerTitleError {
|
||||||
|
case generic
|
||||||
|
}
|
||||||
|
|
||||||
|
public func updatePeerTitle(account: Account, peerId: PeerId, title: String) -> Signal<Void, UpdatePeerTitleError> {
|
||||||
|
return account.postbox.modify { modifier -> Signal<Void, UpdatePeerTitleError> in
|
||||||
|
if let peer = modifier.getPeer(peerId) {
|
||||||
|
if let peer = peer as? TelegramChannel, let inputChannel = apiInputChannel(peer) {
|
||||||
|
return account.network.request(Api.functions.channels.editTitle(channel: inputChannel, title: title))
|
||||||
|
|> mapError { _ -> UpdatePeerTitleError in
|
||||||
|
return .generic
|
||||||
|
}
|
||||||
|
|> mapToSignal { result -> Signal<Void, UpdatePeerTitleError> in
|
||||||
|
account.stateManager.addUpdates(result)
|
||||||
|
|
||||||
|
return account.postbox.modify { modifier -> Void in
|
||||||
|
if let apiChat = result.groups.first, let updatedPeer = parseTelegramGroupOrChannel(chat: apiChat) {
|
||||||
|
updatePeers(modifier: modifier, peers: [updatedPeer], update: { _, updated in
|
||||||
|
return updated
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} |> mapError { _ -> UpdatePeerTitleError in return .generic }
|
||||||
|
}
|
||||||
|
} else if let peer = peer as? TelegramGroup {
|
||||||
|
return account.network.request(Api.functions.messages.editChatTitle(chatId: peer.id.id, title: title))
|
||||||
|
|> mapError { _ -> UpdatePeerTitleError in
|
||||||
|
return .generic
|
||||||
|
}
|
||||||
|
|> mapToSignal { result -> Signal<Void, UpdatePeerTitleError> in
|
||||||
|
account.stateManager.addUpdates(result)
|
||||||
|
|
||||||
|
return account.postbox.modify { modifier -> Void in
|
||||||
|
if let apiChat = result.groups.first, let updatedPeer = parseTelegramGroupOrChannel(chat: apiChat) {
|
||||||
|
updatePeers(modifier: modifier, peers: [updatedPeer], update: { _, updated in
|
||||||
|
return updated
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} |> mapError { _ -> UpdatePeerTitleError in return .generic }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return .fail(.generic)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return .fail(.generic)
|
||||||
|
}
|
||||||
|
} |> mapError { _ -> UpdatePeerTitleError in return .generic } |> switchToLatest
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum UpdatePeerDescriptionError {
|
||||||
|
case generic
|
||||||
|
}
|
||||||
|
|
||||||
|
public func updatePeerDescription(account: Account, peerId: PeerId, description: String?) -> Signal<Void, UpdatePeerDescriptionError> {
|
||||||
|
return account.postbox.modify { modifier -> Signal<Void, UpdatePeerDescriptionError> in
|
||||||
|
if let peer = modifier.getPeer(peerId) {
|
||||||
|
if let peer = peer as? TelegramChannel, let inputChannel = apiInputChannel(peer) {
|
||||||
|
return account.network.request(Api.functions.channels.editAbout(channel: inputChannel, about: description ?? ""))
|
||||||
|
|> mapError { _ -> UpdatePeerDescriptionError in
|
||||||
|
return .generic
|
||||||
|
}
|
||||||
|
|> mapToSignal { result -> Signal<Void, UpdatePeerDescriptionError> in
|
||||||
|
return account.postbox.modify { modifier -> Void in
|
||||||
|
if case .boolTrue = result {
|
||||||
|
modifier.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in
|
||||||
|
if let current = current as? CachedChannelData {
|
||||||
|
return current.withUpdatedAbout(description)
|
||||||
|
} else {
|
||||||
|
return current
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} |> mapError { _ -> UpdatePeerDescriptionError in return .generic }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return .fail(.generic)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return .fail(.generic)
|
||||||
|
}
|
||||||
|
} |> mapError { _ -> UpdatePeerDescriptionError in return .generic } |> switchToLatest
|
||||||
|
}
|
||||||
@@ -66,11 +66,14 @@ public func ==(lhs:UsernameAvailabilityState, rhs:UsernameAvailabilityState) ->
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum AddressNameAvailabilityDomain {
|
||||||
|
case account
|
||||||
|
case peer(PeerId)
|
||||||
|
}
|
||||||
|
|
||||||
public func usernameAvailability(account:Account, def:String?, current:String) -> Signal<UsernameAvailabilityState,Void> {
|
public func addressNameAvailability(account: Account, domain: AddressNameAvailabilityDomain, def: String?, current: String) -> Signal<UsernameAvailabilityState, NoError> {
|
||||||
|
|
||||||
return Signal { subscriber in
|
return Signal { subscriber in
|
||||||
|
|
||||||
let none = { () -> Disposable in
|
let none = { () -> Disposable in
|
||||||
subscriber.putNext(.none(username: current))
|
subscriber.putNext(.none(username: current))
|
||||||
subscriber.putCompletion()
|
subscriber.putCompletion()
|
||||||
@@ -117,11 +120,17 @@ public func usernameAvailability(account:Account, def:String?, current:String) -
|
|||||||
return fail(.short)
|
return fail(.short)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
subscriber.putNext(.progress(username: current))
|
subscriber.putNext(.progress(username: current))
|
||||||
|
|
||||||
let disposable:Disposable
|
let disposable:Disposable
|
||||||
|
|
||||||
|
switch domain {
|
||||||
|
case .account:
|
||||||
|
break
|
||||||
|
case let .peer(peerId):
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
let req = account.network.request(Api.functions.account.checkUsername(username: current)) |> delay(0.3, queue: Queue.concurrentDefaultQueue()) |> map {result in
|
let req = account.network.request(Api.functions.account.checkUsername(username: current)) |> delay(0.3, queue: Queue.concurrentDefaultQueue()) |> map {result in
|
||||||
switch result {
|
switch result {
|
||||||
case .boolFalse:
|
case .boolFalse:
|
||||||
@@ -150,7 +159,7 @@ public func usernameAvailability(account:Account, def:String?, current:String) -
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func updateUsername(account:Account, username:String) -> Signal<Bool,Void> {
|
public func updateAddressName(account: Account, username: String) -> Signal<Bool,Void> {
|
||||||
|
|
||||||
return account.network.request(Api.functions.account.updateUsername(username: username)) |> map { result in
|
return account.network.request(Api.functions.account.updateUsername(username: username)) |> map { result in
|
||||||
return TelegramUser(user: result)
|
return TelegramUser(user: result)
|
||||||
@@ -177,3 +186,54 @@ public func updateUsername(account:Account, username:String) -> Signal<Bool,Void
|
|||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum UpdatePeerAddressNameError {
|
||||||
|
case generic
|
||||||
|
}
|
||||||
|
|
||||||
|
public func updatePeerAddressName(account: Account, peerId: PeerId, username: String?) -> Signal<Void, UpdatePeerAddressNameError> {
|
||||||
|
return account.postbox.modify { modifier -> Signal<Void, UpdatePeerAddressNameError> in
|
||||||
|
if let peer = modifier.getPeer(peerId) as? TelegramChannel, let inputChannel = apiInputChannel(peer) {
|
||||||
|
return account.network.request(Api.functions.channels.updateUsername(channel: inputChannel, username: username ?? ""))
|
||||||
|
|> mapError { _ -> UpdatePeerAddressNameError in
|
||||||
|
return .generic
|
||||||
|
}
|
||||||
|
|> mapToSignal { result -> Signal<Void, UpdatePeerAddressNameError> in
|
||||||
|
return account.postbox.modify { modifier -> Void in
|
||||||
|
if case .boolTrue = result {
|
||||||
|
if let peer = modifier.getPeer(peerId) as? TelegramChannel {
|
||||||
|
updatePeers(modifier: modifier, peers: [peer.withUpdatedAddressName(username)], update: { _, updated in
|
||||||
|
return updated
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} |> mapError { _ -> UpdatePeerAddressNameError in return .generic }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return .fail(.generic)
|
||||||
|
}
|
||||||
|
} |> mapError { _ -> UpdatePeerAddressNameError in return .generic } |> switchToLatest
|
||||||
|
}
|
||||||
|
|
||||||
|
public func adminedPublicChannels(account: Account) -> Signal<[Peer], NoError> {
|
||||||
|
return account.network.request(Api.functions.channels.getAdminedPublicChannels())
|
||||||
|
|> retryRequest
|
||||||
|
|> map { result -> [Peer] in
|
||||||
|
var peers: [Peer] = []
|
||||||
|
switch result {
|
||||||
|
case let .chats(apiChats):
|
||||||
|
for chat in apiChats {
|
||||||
|
if let peer = parseTelegramGroupOrChannel(chat: chat) {
|
||||||
|
peers.append(peer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case let .chatsSlice(_, apiChats):
|
||||||
|
for chat in apiChats {
|
||||||
|
if let peer = parseTelegramGroupOrChannel(chat: chat) {
|
||||||
|
peers.append(peer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return peers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user