mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 06:35:51 +00:00
no message
This commit is contained in:
@@ -129,6 +129,12 @@
|
||||
D03C53741DAD5CA9004C17B3 /* CachedChannelData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B843841DA6EDC4005F29E1 /* CachedChannelData.swift */; };
|
||||
D03C53751DAD5CA9004C17B3 /* TelegramUserPresence.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B844521DAC0773005F29E1 /* TelegramUserPresence.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 */; };
|
||||
D0448C8F1E22993C005A61A7 /* ProcessSecretChatIncomingDecryptedOperations.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0448C8D1E22993C005A61A7 /* ProcessSecretChatIncomingDecryptedOperations.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 */; };
|
||||
D050F26D1E4A5B6D00988324 /* CreateGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BC386D1E3FDAB70044D6FE /* CreateGroup.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 */; };
|
||||
D067066D1D512ADB00DED3E3 /* SwiftSignalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D06706681D512ADB00DED3E3 /* SwiftSignalKit.framework */; };
|
||||
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 */; };
|
||||
D0BC387B1E40D2880044D6FE /* 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 */; };
|
||||
D0DC354E1DE368F7000195EB /* RequestChatContextResults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DC354D1DE368F7000195EB /* RequestChatContextResults.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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@@ -477,6 +494,8 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@@ -556,6 +575,8 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@@ -708,6 +729,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D03B0CD21D62244300955575 /* Namespaces.swift */,
|
||||
D03E5E0A1E55E0220029569A /* Accounts */,
|
||||
D03B0CD01D62242C00955575 /* Peers */,
|
||||
D03B0CD11D62242F00955575 /* Messages */,
|
||||
D0FA8B961E1E952D001E855B /* Secret Chats */,
|
||||
@@ -864,6 +886,8 @@
|
||||
D00C7CCE1E3628180080C3D5 /* UpdateCachedChannelParticipants.swift */,
|
||||
D0E6521E1E3A364A004EEA91 /* UpdateAccountPeerName.swift */,
|
||||
D08774FB1E3E39F600A97350 /* ManagedGlobalNotificationSettings.swift */,
|
||||
D0BEAF5C1E54941B00BD963D /* Authorization.swift */,
|
||||
D0BEAF5F1E54ACF900BD963D /* AccountManager.swift */,
|
||||
);
|
||||
name = Account;
|
||||
sourceTree = "<group>";
|
||||
@@ -904,6 +928,14 @@
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D03E5E0A1E55E0220029569A /* Accounts */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D03E5E0B1E55E02D0029569A /* LoggedOutAccountAttribute.swift */,
|
||||
);
|
||||
name = Accounts;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D06706631D512ADA00DED3E3 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -1009,6 +1041,10 @@
|
||||
C2366C821E4F3EAA0097CCFF /* GroupReturnAndLeft.swift */,
|
||||
C2366C851E4F403C0097CCFF /* UsernameAvailability.swift */,
|
||||
C2366C881E4F40480097CCFF /* SupportPeerId.swift */,
|
||||
D041E3F41E535464008C24B4 /* AddPeerMember.swift */,
|
||||
D041E3F71E535A88008C24B4 /* RemovePeerMember.swift */,
|
||||
D0561DE21E5737FC00E6B9E9 /* UpdatePeerInfo.swift */,
|
||||
D0561DE91E5754FA00E6B9E9 /* ChannelAdmins.swift */,
|
||||
);
|
||||
name = Peers;
|
||||
sourceTree = "<group>";
|
||||
@@ -1267,6 +1303,7 @@
|
||||
D03B0CDB1D62245F00955575 /* ApiUtils.swift in Sources */,
|
||||
D0B843C91DA7FF30005F29E1 /* NBPhoneNumberDesc.m in Sources */,
|
||||
D03B0CE61D6224A700955575 /* ReplyMessageAttribute.swift in Sources */,
|
||||
D0BEAF601E54ACF900BD963D /* AccountManager.swift in Sources */,
|
||||
D02ABC7E1E3109F000CAE539 /* CloudChatRemoveMessagesOperation.swift in Sources */,
|
||||
D0448CA51E29215A005A61A7 /* MediaResourceApiUtils.swift in Sources */,
|
||||
D03C53771DAFF20F004C17B3 /* MultipartUpload.swift in Sources */,
|
||||
@@ -1306,6 +1343,7 @@
|
||||
D03B0CD31D62244300955575 /* Namespaces.swift in Sources */,
|
||||
D01D6BF91E42A713006151C6 /* SearchStickers.swift in Sources */,
|
||||
D0FA8BB91E2240B4001E855B /* SecretChatIncomingDecryptedOperation.swift in Sources */,
|
||||
D0561DE31E5737FC00E6B9E9 /* UpdatePeerInfo.swift in Sources */,
|
||||
D0DF0C8A1D819C7E008AEB01 /* JoinChannel.swift in Sources */,
|
||||
D0F7AB2F1DCF507E009AD9A1 /* ReplyMarkupMessageAttribute.swift in Sources */,
|
||||
D0B843971DA7FBBC005F29E1 /* ChangePeerNotificationSettings.swift in Sources */,
|
||||
@@ -1320,6 +1358,7 @@
|
||||
D0DC354E1DE368F7000195EB /* RequestChatContextResults.swift in Sources */,
|
||||
D0BC38771E40BAAA0044D6FE /* ManagedSynchronizePinnedChatsOperations.swift in Sources */,
|
||||
D0B843851DA6EDC4005F29E1 /* CachedChannelData.swift in Sources */,
|
||||
D0BEAF5D1E54941B00BD963D /* Authorization.swift in Sources */,
|
||||
D0B843831DA6EDB8005F29E1 /* CachedGroupData.swift in Sources */,
|
||||
D0E35A121DE4A25E00BC6096 /* OutgoingChatContextResultMessageAttribute.swift in Sources */,
|
||||
D0B844531DAC0773005F29E1 /* TelegramUserPresence.swift in Sources */,
|
||||
@@ -1331,6 +1370,7 @@
|
||||
D03B0D0C1D62255C00955575 /* AccountStateManagementUtils.swift in Sources */,
|
||||
D073CE5D1DCB97F6007511FD /* ForwardSourceInfoAttribute.swift in Sources */,
|
||||
D0FA8B9E1E1F973B001E855B /* SecretChatIncomingEncryptedOperation.swift in Sources */,
|
||||
D0561DEA1E5754FA00E6B9E9 /* ChannelAdmins.swift in Sources */,
|
||||
D03B0D721D631ABA00955575 /* SearchMessages.swift in Sources */,
|
||||
D0DC35501DE36900000195EB /* ChatContextResult.swift in Sources */,
|
||||
D00D97CA1E32917C00E5C2B6 /* PeerInputActivityManager.swift in Sources */,
|
||||
@@ -1346,6 +1386,7 @@
|
||||
D03B0D5D1D631A6900955575 /* MultipartFetch.swift in Sources */,
|
||||
D0BC38751E40A7F70044D6FE /* RemovePeerChat.swift in Sources */,
|
||||
D0AB0B961D662F0B002C78E7 /* ManagedChatListHoles.swift in Sources */,
|
||||
D03E5E0C1E55E02D0029569A /* LoggedOutAccountAttribute.swift in Sources */,
|
||||
D02ABC841E32183300CAE539 /* ManagedSynchronizePinnedCloudChatsOperations.swift in Sources */,
|
||||
D03B0CD71D62245300955575 /* TelegramGroup.swift in Sources */,
|
||||
D0B8438C1DA7CF50005F29E1 /* BotInfo.swift in Sources */,
|
||||
@@ -1369,7 +1410,9 @@
|
||||
D0AB0B941D662ECE002C78E7 /* ManagedMessageHistoryHoles.swift in Sources */,
|
||||
D08774FC1E3E39F600A97350 /* ManagedGlobalNotificationSettings.swift in Sources */,
|
||||
D03B0CF41D62250800955575 /* TelegramMediaAction.swift in Sources */,
|
||||
D041E3F81E535A88008C24B4 /* RemovePeerMember.swift in Sources */,
|
||||
D0B417C11D7DCEEF004562A4 /* ApiGroupOrChannel.swift in Sources */,
|
||||
D041E3F51E535464008C24B4 /* AddPeerMember.swift in Sources */,
|
||||
D0B843BF1DA7FF30005F29E1 /* NBNumberFormat.m in Sources */,
|
||||
D0B843C51DA7FF30005F29E1 /* NBPhoneNumber.m in Sources */,
|
||||
D03B0D0D1D62255C00955575 /* SynchronizePeerReadState.swift in Sources */,
|
||||
@@ -1441,6 +1484,7 @@
|
||||
D049EAEC1E44B71B00A2CD3A /* RecentlySearchedPeerIds.swift in Sources */,
|
||||
D0FA8B991E1E955C001E855B /* SecretChatOutgoingOperation.swift in Sources */,
|
||||
D001F3F01E128A1C007A8C60 /* AccountStateManagementUtils.swift in Sources */,
|
||||
D0BEAF611E54ACF900BD963D /* AccountManager.swift in Sources */,
|
||||
D0F3CC791DDE2859008148FA /* SearchMessages.swift in Sources */,
|
||||
D0B8442B1DAB91E0005F29E1 /* NBMetadataCore.m in Sources */,
|
||||
D00C7CD01E3628180080C3D5 /* UpdateCachedChannelParticipants.swift in Sources */,
|
||||
@@ -1480,6 +1524,7 @@
|
||||
D02ABC851E32183300CAE539 /* ManagedSynchronizePinnedCloudChatsOperations.swift in Sources */,
|
||||
D073CE6C1DCBCF17007511FD /* TextEntitiesMessageAttribute.swift in Sources */,
|
||||
D03C53751DAD5CA9004C17B3 /* TelegramUserPresence.swift in Sources */,
|
||||
D0561DE41E5737FC00E6B9E9 /* UpdatePeerInfo.swift in Sources */,
|
||||
D0DC35521DE36908000195EB /* ChatContextResult.swift in Sources */,
|
||||
D0F7AB301DCF507E009AD9A1 /* ReplyMarkupMessageAttribute.swift in Sources */,
|
||||
D073CE6D1DCBCF17007511FD /* InlineBotMessageAttribute.swift in Sources */,
|
||||
@@ -1494,6 +1539,7 @@
|
||||
D0BC387C1E40D2880044D6FE /* TogglePeerChatPinned.swift in Sources */,
|
||||
D0B844111DAB91CD005F29E1 /* Regex.swift in Sources */,
|
||||
D0B844321DAB91E0005F29E1 /* NBPhoneMetaDataGenerator.m in Sources */,
|
||||
D0BEAF5E1E54941B00BD963D /* Authorization.swift in Sources */,
|
||||
D073CEA41DCBF3EA007511FD /* MultipartUpload.swift in Sources */,
|
||||
D03C53701DAD5CA9004C17B3 /* ExportedInvitation.swift in Sources */,
|
||||
D0F7B1E31E045C7B007EB8A5 /* RichText.swift in Sources */,
|
||||
@@ -1505,6 +1551,7 @@
|
||||
D03C53681DAD5CA9004C17B3 /* PeerUtils.swift in Sources */,
|
||||
D050F2621E4A5AE700988324 /* GlobalNotificationSettings.swift in Sources */,
|
||||
D0B418991D7E0580004562A4 /* TelegramMediaMap.swift in Sources */,
|
||||
D0561DEB1E5754FA00E6B9E9 /* ChannelAdmins.swift in Sources */,
|
||||
D0B844471DAB91FD005F29E1 /* ManagedServiceViews.swift in Sources */,
|
||||
D03C53691DAD5CA9004C17B3 /* PeerAccessRestrictionInfo.swift in Sources */,
|
||||
D0B8440E1DAB91CD005F29E1 /* MessageUtils.swift in Sources */,
|
||||
@@ -1520,6 +1567,7 @@
|
||||
D03C53741DAD5CA9004C17B3 /* CachedChannelData.swift in Sources */,
|
||||
D0B418861D7E056D004562A4 /* Namespaces.swift in Sources */,
|
||||
D0F7B1E41E045C7B007EB8A5 /* InstantPage.swift in Sources */,
|
||||
D03E5E0D1E55E02D0029569A /* LoggedOutAccountAttribute.swift in Sources */,
|
||||
D0B418AD1D7E0597004562A4 /* Serialization.swift in Sources */,
|
||||
D03C536F1DAD5CA9004C17B3 /* BotInfo.swift in Sources */,
|
||||
D0FA8BBA1E2240B4001E855B /* SecretChatIncomingDecryptedOperation.swift in Sources */,
|
||||
@@ -1543,7 +1591,9 @@
|
||||
D0B844301DAB91E0005F29E1 /* NBNumberFormat.m in Sources */,
|
||||
D001F3F71E128A1C007A8C60 /* ApplyUpdateMessage.swift in Sources */,
|
||||
D0B418971D7E0580004562A4 /* TelegramMediaImage.swift in Sources */,
|
||||
D041E3F91E535A88008C24B4 /* RemovePeerMember.swift in Sources */,
|
||||
D049EAF61E44DF3300A2CD3A /* AccountState.swift in Sources */,
|
||||
D041E3F61E535464008C24B4 /* AddPeerMember.swift in Sources */,
|
||||
D0B844361DAB91E0005F29E1 /* NBPhoneNumberUtil.m in Sources */,
|
||||
D073CE6F1DCBCF17007511FD /* OutgoingMessageInfoAttribute.swift in Sources */,
|
||||
D0B844431DAB91FD005F29E1 /* Account.swift in Sources */,
|
||||
|
||||
@@ -10,22 +10,6 @@ import Foundation
|
||||
#endif
|
||||
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 required init(decoder: Decoder) {
|
||||
}
|
||||
@@ -129,47 +113,25 @@ public func ==(lhs: AuthorizedAccountState.State, rhs: AuthorizedAccountState.St
|
||||
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 let id: AccountId
|
||||
public let id: AccountRecordId
|
||||
public let appGroupPath: String
|
||||
public let basePath: String
|
||||
public let testingEnvironment: Bool
|
||||
public let postbox: Postbox
|
||||
public let network: Network
|
||||
public let logger: Logger
|
||||
|
||||
public var masterDatacenterId: Int32 {
|
||||
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.appGroupPath = appGroupPath
|
||||
self.basePath = basePath
|
||||
self.testingEnvironment = testingEnvironment
|
||||
self.postbox = postbox
|
||||
self.network = network
|
||||
self.logger = logger
|
||||
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)
|
||||
|> 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(RecentMediaItem.self, f: { RecentMediaItem(decoder: $0) })
|
||||
declareEncodable(RecentPeerItem.self, f: { RecentPeerItem(decoder: $0) })
|
||||
declareEncodable(LoggedOutAccountAttribute.self, f: { LoggedOutAccountAttribute(decoder: $0) })
|
||||
|
||||
return
|
||||
}()
|
||||
@@ -255,16 +218,15 @@ func accountNetworkUsageInfoPath(basePath: String) -> String {
|
||||
return basePath + "/network-usage"
|
||||
}
|
||||
|
||||
public enum AccountLogger {
|
||||
case named(String)
|
||||
case instance(Logger)
|
||||
private func accountRecordIdPathName(_ id: AccountRecordId) -> String {
|
||||
return "account-\(UInt64(bitPattern: id.int64))"
|
||||
}
|
||||
|
||||
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
|
||||
let _ = declaredEncodables
|
||||
|
||||
let path = "\(appGroupPath)/account\(id.stringValue)"
|
||||
let path = "\(appGroupPath)/\(accountRecordIdPathName(id))"
|
||||
|
||||
var initializeMessageNamespacesWithHoles: [(PeerId.Namespace, MessageId.Namespace)] = []
|
||||
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 postbox = Postbox(basePath: path + "/postbox", globalMessageIdsNamespace: Namespaces.Message.Cloud, seedConfiguration: seedConfiguration)
|
||||
|
||||
return (postbox.stateView() |> take(1) |> map { view -> (String, Postbox, Coding?) in
|
||||
let accountState = view.state
|
||||
return (path, postbox, accountState)
|
||||
@@ -290,25 +253,17 @@ public func accountWithId(_ id: AccountId, appGroupPath: String, logger: Account
|
||||
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 {
|
||||
switch accountState {
|
||||
case let unauthorizedState as UnauthorizedAccountState:
|
||||
return initializedNetwork(datacenterId: Int(unauthorizedState.masterDatacenterId), keychain: keychain, networkUsageInfoPath: accountNetworkUsageInfoPath(basePath: basePath), testingEnvironment: testingEnvironment)
|
||||
|> 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:
|
||||
return initializedNetwork(datacenterId: Int(authorizedState.masterDatacenterId), keychain: keychain, networkUsageInfoPath: accountNetworkUsageInfoPath(basePath: basePath), testingEnvironment: testingEnvironment)
|
||||
|> 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 _:
|
||||
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)
|
||||
|> 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 let id: AccountId
|
||||
public let id: AccountRecordId
|
||||
public let basePath: String
|
||||
public let testingEnvironment: Bool
|
||||
public let postbox: Postbox
|
||||
public let network: Network
|
||||
public let peerId: PeerId
|
||||
|
||||
public let logger: Logger
|
||||
|
||||
public private(set) var stateManager: AccountStateManager!
|
||||
public private(set) var viewTracker: AccountViewTracker!
|
||||
public private(set) var pendingMessageManager: PendingMessageManager!
|
||||
@@ -422,14 +375,13 @@ public class Account {
|
||||
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.basePath = basePath
|
||||
self.testingEnvironment = testingEnvironment
|
||||
self.postbox = postbox
|
||||
self.network = network
|
||||
self.peerId = peerId
|
||||
self.logger = logger
|
||||
|
||||
self.peerInputActivityManager = PeerInputActivityManager()
|
||||
self.stateManager = AccountStateManager(account: self, peerInputActivityManager: self.peerInputActivityManager)
|
||||
@@ -546,10 +498,10 @@ public class Account {
|
||||
|> deliverOn(Queue.concurrentDefaultQueue())
|
||||
|> mapToSignal { [weak self] value -> Signal<Void, NoError> in
|
||||
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)
|
||||
} else {
|
||||
trace("Account", what: "Resigned master")
|
||||
Logger.shared.log("Account", "Resigned master")
|
||||
return .never()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ enum AccountStateMutationOperation {
|
||||
case UpdatePeerNotificationSettings(PeerId, PeerNotificationSettings)
|
||||
case AddHole(MessageId)
|
||||
case MergeApiChats([Api.Chat])
|
||||
case UpdatePeer(PeerId, (Peer) -> Peer)
|
||||
case UpdatePeer(PeerId, (Peer?) -> Peer?)
|
||||
case MergeApiUsers([Api.User])
|
||||
case MergePeerPresences([PeerId: PeerPresence])
|
||||
case UpdateSecretChat(chat: Api.EncryptedChat, timestamp: Int32)
|
||||
@@ -166,7 +166,7 @@ struct AccountMutableState {
|
||||
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))
|
||||
}
|
||||
|
||||
@@ -241,8 +241,8 @@ struct AccountMutableState {
|
||||
}
|
||||
}
|
||||
case let .UpdatePeer(id, f):
|
||||
if let peer = self.peers[id] {
|
||||
let updatedPeer = f(peer)
|
||||
let peer = self.peers[id]
|
||||
if let updatedPeer = f(peer) {
|
||||
peers[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))
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
@@ -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))
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
@@ -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))
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
@@ -616,27 +616,27 @@ private func finalStateWithUpdates(account: Account, state: AccountMutableState,
|
||||
case let .updateChannelTooLong(_, channelId, _):
|
||||
let peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId)
|
||||
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)
|
||||
}
|
||||
case let .updateDeleteChannelMessages(channelId, messages, pts: pts, ptsCount):
|
||||
let peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId)
|
||||
if let previousState = updatedState.channelStates[peerId] {
|
||||
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 {
|
||||
updatedState.deleteMessages(messages.map({ MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: $0) }))
|
||||
updatedState.updateChannelState(peerId, state: previousState.setPts(pts))
|
||||
} else {
|
||||
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)
|
||||
//updatedMissingUpdates = true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -645,7 +645,7 @@ private func finalStateWithUpdates(account: Account, state: AccountMutableState,
|
||||
let peerId = messageId.peerId
|
||||
if let previousState = updatedState.channelStates[peerId] {
|
||||
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 {
|
||||
if let preCachedResources = apiMessage.preCachedResources {
|
||||
for (resource, data) in preCachedResources {
|
||||
@@ -656,19 +656,19 @@ private func finalStateWithUpdates(account: Account, state: AccountMutableState,
|
||||
updatedState.updateChannelState(peerId, state: previousState.setPts(pts))
|
||||
} else {
|
||||
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)
|
||||
//updatedMissingUpdates = true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
trace("State", what: "Invalid updateEditChannelMessage")
|
||||
Logger.shared.log("State", "Invalid updateEditChannelMessage")
|
||||
}
|
||||
case let .updateChannelWebPage(channelId, apiWebpage, pts, ptsCount):
|
||||
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))
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -711,7 +711,7 @@ private func finalStateWithUpdates(account: Account, state: AccountMutableState,
|
||||
if let message = StoreMessage(apiMessage: apiMessage) {
|
||||
if let previousState = updatedState.channelStates[message.id.peerId] {
|
||||
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 {
|
||||
if let preCachedResources = apiMessage.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))
|
||||
} else {
|
||||
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)
|
||||
//updatedMissingUpdates = true
|
||||
@@ -730,7 +730,7 @@ private func finalStateWithUpdates(account: Account, state: AccountMutableState,
|
||||
}
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -769,7 +769,7 @@ private func finalStateWithUpdates(account: Account, state: AccountMutableState,
|
||||
}
|
||||
|
||||
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 {
|
||||
var attributes: [MessageAttribute] = []
|
||||
if !entities.isEmpty {
|
||||
@@ -864,7 +864,7 @@ private func finalStateWithUpdates(account: Account, state: AccountMutableState,
|
||||
if let peer = updatedState.peers[peerId] {
|
||||
pollChannelSignals.append(pollChannel(account, peer: peer, state: updatedState.branch()))
|
||||
} 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) {
|
||||
missingPeers[peerId] = inputPeer
|
||||
} 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 {
|
||||
return .single(state)
|
||||
} 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>] = []
|
||||
for (peerId, peer) in missingPeers {
|
||||
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
|
||||
}
|
||||
} 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)
|
||||
}
|
||||
}
|
||||
@@ -1105,7 +1105,7 @@ private func verifyTransaction(_ modifier: Modifier, finalState: AccountMutableS
|
||||
}
|
||||
|
||||
if !missingPeerIds.isEmpty {
|
||||
trace("State", what: "missing peers \(missingPeerIds)")
|
||||
Logger.shared.log("State", "missing peers \(missingPeerIds)")
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1133,7 +1133,7 @@ private func verifyTransaction(_ modifier: Modifier, finalState: AccountMutableS
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -1150,7 +1150,7 @@ private func verifyTransaction(_ modifier: Modifier, finalState: AccountMutableS
|
||||
previousStateMatches = true
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -1268,10 +1268,8 @@ func replayFinalState(mediaBox: MediaBox, modifier: Modifier, finalState: Accoun
|
||||
case let .UpdateState(state):
|
||||
let currentState = modifier.getState() as! AuthorizedAccountState
|
||||
modifier.setState(currentState.changedState(state))
|
||||
//trace("State", what: "setting state \(state)")
|
||||
case let .UpdateChannelState(peerId, 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):
|
||||
modifier.updatePeerNotificationSettings([peerId: notificationSettings])
|
||||
case let .AddHole(messageId):
|
||||
@@ -1297,8 +1295,8 @@ func replayFinalState(mediaBox: MediaBox, modifier: Modifier, finalState: Accoun
|
||||
return updated
|
||||
})
|
||||
case let .UpdatePeer(id, f):
|
||||
if let peer = modifier.getPeer(id) {
|
||||
updatePeers(modifier: modifier, peers: [f(peer)], update: { _, updated in
|
||||
if let peer = f(modifier.getPeer(id)) {
|
||||
updatePeers(modifier: modifier, peers: [peer], update: { _, updated in
|
||||
return updated
|
||||
})
|
||||
}
|
||||
@@ -1319,7 +1317,7 @@ func replayFinalState(mediaBox: MediaBox, modifier: Modifier, finalState: Accoun
|
||||
modifier.setPeerChatState(peer.id, state: state)
|
||||
modifier.operationLogRemoveAllEntries(peerId: peer.id, tag: OperationLogTags.SecretOutgoing)
|
||||
} else {
|
||||
trace("State", what: "got encryptedChatDiscarded, but peer doesn't exist")
|
||||
Logger.shared.log("State", "got encryptedChatDiscarded, but peer doesn't exist")
|
||||
}
|
||||
case .encryptedChatEmpty(_):
|
||||
break
|
||||
@@ -1343,7 +1341,7 @@ func replayFinalState(mediaBox: MediaBox, modifier: Modifier, finalState: Accoun
|
||||
assertionFailure()
|
||||
}
|
||||
} 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):
|
||||
break
|
||||
|
||||
@@ -14,7 +14,7 @@ private enum AccountStateManagerOperation {
|
||||
case collectUpdateGroups([UpdateGroup], Double)
|
||||
case processUpdateGroups([UpdateGroup])
|
||||
case custom(Int32, Signal<Void, NoError>)
|
||||
case pollCompletion(Int32, [(Int32, () -> Void)])
|
||||
case pollCompletion(Int32, [MessageId], [(Int32, ([MessageId]) -> Void)])
|
||||
case processEvents(Int32, AccountFinalStateEvents)
|
||||
}
|
||||
|
||||
@@ -161,11 +161,13 @@ public final class AccountStateManager {
|
||||
}
|
||||
|
||||
private func replaceOperations(with operation: AccountStateManagerOperation) {
|
||||
var collectedPollCompletionSubscribers: [(Int32, () -> Void)] = []
|
||||
var collectedMessageIds: [MessageId] = []
|
||||
var collectedPollCompletionSubscribers: [(Int32, ([MessageId]) -> Void)] = []
|
||||
|
||||
if !self.operations.isEmpty {
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -173,8 +175,9 @@ public final class AccountStateManager {
|
||||
|
||||
self.operations.removeAll()
|
||||
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)
|
||||
|> mapToSignal { state -> Signal<(Api.updates.Difference?, AccountReplayedFinalState?), NoError> in
|
||||
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))
|
||||
} else {
|
||||
return finalStateWithDifference(account: account, state: state, difference: difference)
|
||||
@@ -286,7 +289,7 @@ public final class AccountStateManager {
|
||||
}
|
||||
}, error: { _ in
|
||||
assertionFailure()
|
||||
trace("AccountStateManager", what: "processUpdateGroups signal completed with error")
|
||||
Logger.shared.log("AccountStateManager", "processUpdateGroups signal completed with error")
|
||||
})
|
||||
case let .collectUpdateGroups(_, timeout):
|
||||
self.operationTimer?.invalidate()
|
||||
@@ -296,7 +299,7 @@ public final class AccountStateManager {
|
||||
if timeout.isEqual(to: 0.0) {
|
||||
strongSelf.operations[0] = .processUpdateGroups(groups)
|
||||
} else {
|
||||
trace("AccountStateManager", what: "timeout while waiting for updates")
|
||||
Logger.shared.log("AccountStateManager", "timeout while waiting for updates")
|
||||
strongSelf.replaceOperations(with: .pollDifference(AccountFinalStateEvents()))
|
||||
}
|
||||
strongSelf.startFirstOperation()
|
||||
@@ -350,7 +353,7 @@ public final class AccountStateManager {
|
||||
}
|
||||
}, error: { _ in
|
||||
assertionFailure()
|
||||
trace("AccountStateManager", what: "processUpdateGroups signal completed with error")
|
||||
Logger.shared.log("AccountStateManager", "processUpdateGroups signal completed with error")
|
||||
})
|
||||
case let .custom(operationId, signal):
|
||||
self.operationTimer?.invalidate()
|
||||
@@ -388,6 +391,16 @@ public final class AccountStateManager {
|
||||
}
|
||||
}
|
||||
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()
|
||||
} else {
|
||||
assertionFailure()
|
||||
@@ -396,46 +409,12 @@ public final class AccountStateManager {
|
||||
}
|
||||
|
||||
let signal = self.account.postbox.modify { modifier -> [Message] in
|
||||
let timestamp = Int32(self.account.network.context.globalTime())
|
||||
var messages: [Message] = []
|
||||
for id in events.addedIncomingMessageIds {
|
||||
var notify = true
|
||||
|
||||
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 {
|
||||
let (message, notify) = messageForNotification(modifier: modifier, id: id, alwaysReturnMessage: false)
|
||||
if let message = message, notify {
|
||||
messages.append(message)
|
||||
}
|
||||
} else {
|
||||
trace("AccountStateManager", what: "notification message doesn't exist")
|
||||
}
|
||||
}
|
||||
}
|
||||
return messages
|
||||
}
|
||||
@@ -443,7 +422,7 @@ public final class AccountStateManager {
|
||||
let _ = (signal |> deliverOn(self.queue)).start(next: { [weak self] messages in
|
||||
if let strongSelf = self {
|
||||
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)
|
||||
@@ -453,12 +432,10 @@ public final class AccountStateManager {
|
||||
}, completed: {
|
||||
completed()
|
||||
})
|
||||
case let .pollCompletion(pollId, preSubscribers):
|
||||
case let .pollCompletion(pollId, preMessageIds, preSubscribers):
|
||||
if self.operations.count > 1 {
|
||||
self.operations.removeFirst()
|
||||
for (id, f) in preSubscribers {
|
||||
let _ = self.addPollCompletion(f, id: id)
|
||||
}
|
||||
self.postponePollCompletionOperation(messageIds: preMessageIds, subscribers: preSubscribers)
|
||||
self.startFirstOperation()
|
||||
} else {
|
||||
self.operationTimer?.invalidate()
|
||||
@@ -466,18 +443,16 @@ public final class AccountStateManager {
|
||||
|> deliverOn(self.queue)
|
||||
let completed: () -> Void = { [weak self] in
|
||||
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)
|
||||
|
||||
strongSelf.operations.removeFirst()
|
||||
if strongSelf.operations.isEmpty {
|
||||
for (_, f) in subscribers {
|
||||
f()
|
||||
f(messageIds)
|
||||
}
|
||||
} else {
|
||||
for (id, f) in subscribers {
|
||||
let _ = strongSelf.addPollCompletion(f, id: id)
|
||||
}
|
||||
strongSelf.postponePollCompletionOperation(messageIds: messageIds, subscribers: subscribers)
|
||||
}
|
||||
strongSelf.startFirstOperation()
|
||||
} 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())
|
||||
|
||||
let updatedId: Int32
|
||||
if let id = id {
|
||||
updatedId = id
|
||||
} else {
|
||||
updatedId = self.getNextId()
|
||||
}
|
||||
let updatedId: Int32 = self.getNextId()
|
||||
|
||||
if !self.operations.isEmpty {
|
||||
for i in 1 ..< self.operations.count {
|
||||
if case let .pollCompletion(pollId, subscribers) = self.operations[i] {
|
||||
for i in 0 ..< self.operations.count {
|
||||
if case let .pollCompletion(pollId, messageIds, subscribers) = self.operations[i] {
|
||||
var subscribers = subscribers
|
||||
subscribers.append((updatedId, f))
|
||||
self.operations[i] = .pollCompletion(pollId, subscribers)
|
||||
self.operations[i] = .pollCompletion(pollId, messageIds, subscribers)
|
||||
return updatedId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let beginFirst = self.operations.isEmpty
|
||||
self.operations.append(.pollCompletion(self.getNextId(), [(updatedId, f)]))
|
||||
self.operations.append(.pollCompletion(self.getNextId(), [], [(updatedId, f)]))
|
||||
if beginFirst {
|
||||
self.startFirstOperation()
|
||||
}
|
||||
@@ -538,12 +518,12 @@ public final class AccountStateManager {
|
||||
|
||||
private func removePollCompletion(_ id: Int32) {
|
||||
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 {
|
||||
if subscribers[j].0 == id {
|
||||
var subscribers = subscribers
|
||||
subscribers.remove(at: j)
|
||||
self.operations[i] = .pollCompletion(pollId, subscribers)
|
||||
self.operations[i] = .pollCompletion(pollId, messages, subscribers)
|
||||
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
|
||||
let disposable = MetaDisposable()
|
||||
if let strongSelf = self {
|
||||
strongSelf.queue.async {
|
||||
let id = strongSelf.addPollCompletion({
|
||||
let id = strongSelf.addPollCompletion({ messageIds in
|
||||
subscriber.putNext(messageIds)
|
||||
subscriber.putCompletion()
|
||||
}, id: nil)
|
||||
})
|
||||
|
||||
disposable.set(ActionDisposable {
|
||||
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> {
|
||||
if let account = self.account {
|
||||
let queue = self.queue
|
||||
return Signal { [weak self] subscriber in
|
||||
let disposable = MetaDisposable()
|
||||
@@ -362,9 +361,5 @@ public final class AccountViewTracker {
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
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) {
|
||||
self.flags = CachedChannelFlags(rawValue: decoder.decodeInt32ForKey("f"))
|
||||
self.about = decoder.decodeStringForKey("a")
|
||||
@@ -178,6 +182,10 @@ public final class CachedChannelData: CachedPeerData {
|
||||
|
||||
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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
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
|
||||
#endif
|
||||
|
||||
class Download {
|
||||
class Download: NSObject, MTRequestMessageServiceDelegate {
|
||||
let datacenterId: Int
|
||||
let context: MTContext
|
||||
let mtProto: MTProto
|
||||
@@ -25,6 +25,10 @@ class Download {
|
||||
self.mtProto.requiredAuthToken = Int(datacenterId) as NSNumber
|
||||
}
|
||||
self.requestService = MTRequestMessageService(context: self.context)
|
||||
|
||||
super.init()
|
||||
|
||||
self.requestService.delegate = self
|
||||
self.mtProto.add(self.requestService)
|
||||
}
|
||||
|
||||
@@ -33,6 +37,11 @@ class Download {
|
||||
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> {
|
||||
return Signal<Void, MTRpcError> { subscriber in
|
||||
let request = MTRequest()
|
||||
|
||||
@@ -53,16 +53,17 @@ private func filterMessageAttributesForForwardedMessage(_ attributes: [MessageAt
|
||||
}
|
||||
}
|
||||
|
||||
public func enqueueMessages(account: Account, peerId: PeerId, messages: [EnqueueMessage]) -> Signal<Void, NoError> {
|
||||
return account.postbox.modify { modifier -> Void in
|
||||
enqueueMessages(modifier: modifier, account: account, peerId: peerId, messages: messages)
|
||||
public func enqueueMessages(account: Account, peerId: PeerId, messages: [EnqueueMessage]) -> Signal<[MessageId?], NoError> {
|
||||
return account.postbox.modify { modifier -> [MessageId?] in
|
||||
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) {
|
||||
var storeMessages: [StoreMessage] = []
|
||||
let timestamp = Int32(account.network.context.globalTime())
|
||||
var globallyUniqueIds: [Int64] = []
|
||||
for message in messages {
|
||||
var attributes: [MessageAttribute] = []
|
||||
var flags = StoreMessageFlags()
|
||||
@@ -71,6 +72,7 @@ func enqueueMessages(modifier: Modifier, account: Account, peerId: PeerId, messa
|
||||
var randomId: Int64 = 0
|
||||
arc4random_buf(&randomId, 8)
|
||||
attributes.append(OutgoingMessageInfoAttribute(uniqueId: randomId))
|
||||
globallyUniqueIds.append(randomId)
|
||||
|
||||
switch message {
|
||||
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 {
|
||||
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 TelegramCorePrivateModule
|
||||
import SwiftSignalKit
|
||||
|
||||
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()
|
||||
var rawTime = time_t()
|
||||
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()
|
||||
var rawTime = time_t()
|
||||
time(&rawTime)
|
||||
@@ -43,27 +44,57 @@ public func registerLoggingFunctions() {
|
||||
setBridgingTraceFunction({ domain, what in
|
||||
if let what = what {
|
||||
if let domain = domain {
|
||||
trace(domain, what: what as String)
|
||||
Logger.shared.log(domain, what as String)
|
||||
} else {
|
||||
trace("", what: what as String)
|
||||
Logger.shared.log("", what as String)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private var sharedLogger: Logger?
|
||||
|
||||
public final class Logger {
|
||||
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 basePath: String
|
||||
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
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
var rawTime = time_t()
|
||||
@@ -73,23 +104,35 @@ public final class Logger {
|
||||
|
||||
var curTime = timeval()
|
||||
gettimeofday(&curTime, nil)
|
||||
let seconds = curTime.tv_sec
|
||||
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 {
|
||||
#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 createNew = false
|
||||
var openNew = false
|
||||
if let (file, length) = self.file {
|
||||
if length < self.maxLength {
|
||||
if length >= self.maxLength {
|
||||
close(file)
|
||||
createNew = true
|
||||
openNew = true
|
||||
} else {
|
||||
fd = file
|
||||
}
|
||||
} else {
|
||||
openNew = true
|
||||
}
|
||||
if openNew {
|
||||
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: []) {
|
||||
var minCreationDate: (Date, URL)?
|
||||
var maxCreationDate: (Date, URL)?
|
||||
@@ -125,9 +168,11 @@ public final class Logger {
|
||||
createNew = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
if handle >= 0 {
|
||||
@@ -135,12 +180,15 @@ public final class Logger {
|
||||
self.file = (handle, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let fd = fd {
|
||||
if let data = content.data(using: .utf8) {
|
||||
data.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Void in
|
||||
write(fd, bytes, data.count)
|
||||
}
|
||||
var newline: UInt8 = 0x0a
|
||||
write(fd, &newline, 1)
|
||||
if let file = self.file {
|
||||
self.file = (file.0, file.1 + data.count)
|
||||
} 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)
|
||||
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)
|
||||
}
|
||||
|
||||
guard let key = maybeKey else {
|
||||
trace("SecretChat", what: "no valid key found")
|
||||
Logger.shared.log("SecretChat", "no valid key found")
|
||||
return .single(nil)
|
||||
}
|
||||
|
||||
|
||||
@@ -194,10 +194,10 @@ public class Network {
|
||||
self.shouldKeepConnectionDisposable.set(shouldKeepConnectionSignal.start(next: { [weak self] value in
|
||||
if let strongSelf = self {
|
||||
if value {
|
||||
trace("Network", what: "Resume network connection")
|
||||
Logger.shared.log("Network", "Resume network connection")
|
||||
strongSelf.mtProto.resume()
|
||||
} else {
|
||||
trace("Network", what: "Pause network connection")
|
||||
Logger.shared.log("Network", "Pause network connection")
|
||||
strongSelf.mtProto.pause()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ void setBridgingTraceFunction(void (*f)(NSString *, NSString *)) {
|
||||
#if TARGET_IPHONE_SIMULATOR
|
||||
static bool loggingEnabled = true;
|
||||
#elif defined(DEBUG)
|
||||
static bool loggingEnabled = false;
|
||||
static bool loggingEnabled = true;
|
||||
#else
|
||||
static bool loggingEnabled = false;
|
||||
#endif
|
||||
|
||||
@@ -249,7 +249,7 @@ func processSecretChatIncomingDecryptedOperations(mediaBox: MediaBox, modifier:
|
||||
if let error = error as? MessageParsingError {
|
||||
switch error {
|
||||
case .contentParsingError:
|
||||
print("Couldn't parse secret message payload")
|
||||
Logger.shared.log("SecretChat", "Couldn't parse secret message payload")
|
||||
removeTagLocalIndices.append(entry.tagLocalIndex)
|
||||
return true
|
||||
case .unsupportedLayer:
|
||||
@@ -261,10 +261,10 @@ func processSecretChatIncomingDecryptedOperations(mediaBox: MediaBox, modifier:
|
||||
removeTagLocalIndices.append(entry.tagLocalIndex)
|
||||
return true
|
||||
case .holesInSequenceBasedLayer:
|
||||
print("Found holes in incoming operation sequence")
|
||||
Logger.shared.log("SecretChat", "Found holes in incoming operation sequence")
|
||||
return false
|
||||
case .secretChatCorruption:
|
||||
print("Secret chat corrupted")
|
||||
Logger.shared.log("SecretChat", "Secret chat corrupted")
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
@@ -285,7 +285,7 @@ func processSecretChatIncomingDecryptedOperations(mediaBox: MediaBox, modifier:
|
||||
switch updatedState.embeddedState {
|
||||
case let .sequenceBasedLayer(sequenceState):
|
||||
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)
|
||||
default:
|
||||
break
|
||||
|
||||
@@ -112,18 +112,18 @@ func processSecretChatIncomingEncryptedOperations(modifier: Modifier, peerId: Pe
|
||||
break
|
||||
}
|
||||
}
|
||||
trace("SecretChat", what: "peerId \(peerId) malformed data after decryption")
|
||||
Logger.shared.log("SecretChat", "peerId \(peerId) malformed data after decryption")
|
||||
}
|
||||
|
||||
removeTagLocalIndices.append(entry.tagLocalIndex)
|
||||
})
|
||||
} 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)
|
||||
}
|
||||
})
|
||||
} 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 {
|
||||
assertionFailure()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Foundation
|
||||
|
||||
func arc4random64() -> Int64 {
|
||||
public func arc4random64() -> Int64 {
|
||||
var value: Int64 = 0
|
||||
arc4random_buf(&value, 8)
|
||||
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 {
|
||||
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)
|
||||
import PostboxMac
|
||||
import SwiftSignalKitMac
|
||||
|
||||
@@ -269,4 +269,8 @@ public final class TelegramChannel: Peer {
|
||||
|
||||
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 .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
|
||||
|
||||
let none = { () -> Disposable in
|
||||
subscriber.putNext(.none(username: current))
|
||||
subscriber.putCompletion()
|
||||
@@ -117,11 +120,17 @@ public func usernameAvailability(account:Account, def:String?, current:String) -
|
||||
return fail(.short)
|
||||
}
|
||||
|
||||
|
||||
subscriber.putNext(.progress(username: current))
|
||||
|
||||
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
|
||||
switch result {
|
||||
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 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