no message

This commit is contained in:
Peter
2018-09-05 00:30:59 +03:00
parent d7b5f5c3cc
commit d41418ffe3
11 changed files with 413 additions and 179 deletions

View File

@@ -3527,6 +3527,128 @@
}; };
name = "Release AppStore"; name = "Release AppStore";
}; };
D0CE6EF5213DC30700BCD44B /* Release AppStore LLC */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = D03B0E591D63215200955575 /* TelegramCore.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MACOSX_DEPLOYMENT_TARGET = 10.10;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
USER_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/third-party/FFmpeg-iOS/include";
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = "Release AppStore LLC";
};
D0CE6EF6213DC30700BCD44B /* Release AppStore LLC */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = D03B0E591D63215200955575 /* TelegramCore.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
APPLICATION_EXTENSION_API_ONLY = YES;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
COPY_PHASE_STRIP = YES;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = X834Q8SBVP;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = TelegramCore/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/third-party/FFmpeg-iOS/lib",
"$(PROJECT_DIR)/third-party/libwebp/lib",
);
MODULEMAP_PRIVATE_FILE = "$(SRCROOT)/TelegramCore/module.private.modulemap";
OTHER_LDFLAGS = "-Wl,-dead_strip";
PRODUCT_BUNDLE_IDENTIFIER = org.telegram.TelegramCore;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_REFLECTION_METADATA_LEVEL = none;
SWIFT_VERSION = 4.0;
USER_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/third-party/FFmpeg-iOS/include";
};
name = "Release AppStore LLC";
};
D0CE6EF7213DC30700BCD44B /* Release AppStore LLC */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = D03B0E591D63215200955575 /* TelegramCore.xcconfig */;
buildSettings = {
INFOPLIST_FILE = TelegramCoreTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = org.telegram.Telegram.TelegramCoreTests;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = "Release AppStore LLC";
};
D0CE6EF8213DC30700BCD44B /* Release AppStore LLC */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = D03B0E591D63215200955575 /* TelegramCore.xcconfig */;
buildSettings = {
APPLICATION_EXTENSION_API_ONLY = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CODE_SIGN_IDENTITY = "-";
COMBINE_HIDPI_IMAGES = YES;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_VERSION = A;
INFOPLIST_FILE = TelegramCoreMac/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.11;
MODULEMAP_PRIVATE_FILE = "$(SRCROOT)/TelegramCore/module.private-mac.modulemap";
PRODUCT_BUNDLE_IDENTIFIER = org.telegram.Telegram.TelegramCoreMac;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 4.0;
};
name = "Release AppStore LLC";
};
/* End XCBuildConfiguration section */ /* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */ /* Begin XCConfigurationList section */
@@ -3539,6 +3661,7 @@
C22069BE1E8EB4A200E82730 /* Release Hockeyapp */, C22069BE1E8EB4A200E82730 /* Release Hockeyapp */,
D0924FE81FE52C12003F693F /* Release Hockeyapp Internal */, D0924FE81FE52C12003F693F /* Release Hockeyapp Internal */,
D06706551D51162400DED3E3 /* Release AppStore */, D06706551D51162400DED3E3 /* Release AppStore */,
D0CE6EF5213DC30700BCD44B /* Release AppStore LLC */,
); );
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = "Debug AppStore"; defaultConfigurationName = "Debug AppStore";
@@ -3552,6 +3675,7 @@
C22069BF1E8EB4A200E82730 /* Release Hockeyapp */, C22069BF1E8EB4A200E82730 /* Release Hockeyapp */,
D0924FE91FE52C12003F693F /* Release Hockeyapp Internal */, D0924FE91FE52C12003F693F /* Release Hockeyapp Internal */,
D06706561D51162400DED3E3 /* Release AppStore */, D06706561D51162400DED3E3 /* Release AppStore */,
D0CE6EF6213DC30700BCD44B /* Release AppStore LLC */,
); );
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = "Debug AppStore"; defaultConfigurationName = "Debug AppStore";
@@ -3565,6 +3689,7 @@
C22069C01E8EB4A200E82730 /* Release Hockeyapp */, C22069C01E8EB4A200E82730 /* Release Hockeyapp */,
D0924FEA1FE52C12003F693F /* Release Hockeyapp Internal */, D0924FEA1FE52C12003F693F /* Release Hockeyapp Internal */,
D06706571D51162400DED3E3 /* Release AppStore */, D06706571D51162400DED3E3 /* Release AppStore */,
D0CE6EF7213DC30700BCD44B /* Release AppStore LLC */,
); );
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = "Debug AppStore"; defaultConfigurationName = "Debug AppStore";
@@ -3578,6 +3703,7 @@
C22069C11E8EB4A200E82730 /* Release Hockeyapp */, C22069C11E8EB4A200E82730 /* Release Hockeyapp */,
D0924FEB1FE52C12003F693F /* Release Hockeyapp Internal */, D0924FEB1FE52C12003F693F /* Release Hockeyapp Internal */,
D0B4186F1D7E03D5004562A4 /* Release AppStore */, D0B4186F1D7E03D5004562A4 /* Release AppStore */,
D0CE6EF8213DC30700BCD44B /* Release AppStore LLC */,
); );
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = "Debug AppStore"; defaultConfigurationName = "Debug AppStore";

View File

@@ -850,7 +850,7 @@ public class Account {
private let serviceQueue = Queue() private let serviceQueue = Queue()
public private(set) var stateManager: AccountStateManager! public private(set) var stateManager: AccountStateManager!
private var contactSyncManager: ContactSyncManager! private(set) var contactSyncManager: ContactSyncManager!
public private(set) var callSessionManager: CallSessionManager! public private(set) var callSessionManager: CallSessionManager!
public private(set) var viewTracker: AccountViewTracker! public private(set) var viewTracker: AccountViewTracker!
public private(set) var pendingMessageManager: PendingMessageManager! public private(set) var pendingMessageManager: PendingMessageManager!
@@ -1183,6 +1183,7 @@ public class Account {
public func resetStateManagement() { public func resetStateManagement() {
self.stateManager.reset() self.stateManager.reset()
self.contactSyncManager.beginSync(importableContacts: self.importableContacts.get()) self.contactSyncManager.beginSync(importableContacts: self.importableContacts.get())
self.managedStickerPacksDisposable.set(manageStickerPacks(network: self.network, postbox: self.postbox).start())
} }
public func peerInputActivities(peerId: PeerId) -> Signal<[(PeerId, PeerInputActivity)], NoError> { public func peerInputActivities(peerId: PeerId) -> Signal<[(PeerId, PeerInputActivity)], NoError> {
@@ -1246,5 +1247,4 @@ public func setupAccount(_ account: Account, fetchCachedResourceRepresentation:
account.transformOutgoingMessageMedia = transformOutgoingMessageMedia account.transformOutgoingMessageMedia = transformOutgoingMessageMedia
account.pendingMessageManager.transformOutgoingMessageMedia = transformOutgoingMessageMedia account.pendingMessageManager.transformOutgoingMessageMedia = transformOutgoingMessageMedia
account.managedStickerPacksDisposable.set(manageStickerPacks(network: account.network, postbox: account.postbox).start())
} }

View File

@@ -73,6 +73,7 @@ enum AccountStateMutationOperation {
case UpdateGlobalNotificationSettings(AccountStateGlobalNotificationSettingsSubject, MessageNotificationSettings) case UpdateGlobalNotificationSettings(AccountStateGlobalNotificationSettingsSubject, MessageNotificationSettings)
case MergeApiChats([Api.Chat]) case MergeApiChats([Api.Chat])
case UpdatePeer(PeerId, (Peer?) -> Peer?) case UpdatePeer(PeerId, (Peer?) -> Peer?)
case UpdateIsContact(PeerId, Bool)
case UpdateCachedPeerData(PeerId, (CachedPeerData?) -> CachedPeerData?) case UpdateCachedPeerData(PeerId, (CachedPeerData?) -> CachedPeerData?)
case MergeApiUsers([Api.User]) case MergeApiUsers([Api.User])
case MergePeerPresences([PeerId: PeerPresence]) case MergePeerPresences([PeerId: PeerPresence])
@@ -246,6 +247,10 @@ struct AccountMutableState {
self.addOperation(.UpdatePeer(id, f)) self.addOperation(.UpdatePeer(id, f))
} }
mutating func updatePeerIsContact(_ id: PeerId, isContact: Bool) {
self.addOperation(.UpdateIsContact(id, isContact))
}
mutating func updateCachedPeerData(_ id: PeerId, _ f: @escaping (CachedPeerData?) -> CachedPeerData?) { mutating func updateCachedPeerData(_ id: PeerId, _ f: @escaping (CachedPeerData?) -> CachedPeerData?) {
self.addOperation(.UpdateCachedPeerData(id, f)) self.addOperation(.UpdateCachedPeerData(id, f))
} }
@@ -328,7 +333,7 @@ struct AccountMutableState {
mutating func addOperation(_ operation: AccountStateMutationOperation) { mutating func addOperation(_ operation: AccountStateMutationOperation) {
switch operation { switch operation {
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark: case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact:
break break
case let .AddMessages(messages, _): case let .AddMessages(messages, _):
for message in messages { for message in messages {
@@ -399,6 +404,7 @@ struct AccountReplayedFinalState {
let updatedTypingActivities: [PeerId: [PeerId: PeerInputActivity?]] let updatedTypingActivities: [PeerId: [PeerId: PeerInputActivity?]]
let updatedWebpages: [MediaId: TelegramMediaWebpage] let updatedWebpages: [MediaId: TelegramMediaWebpage]
let updatedCalls: [Api.PhoneCall] let updatedCalls: [Api.PhoneCall]
let isContactUpdates: [(PeerId, Bool)]
let delayNotificatonsUntil: Int32? let delayNotificatonsUntil: Int32?
} }
@@ -407,11 +413,12 @@ struct AccountFinalStateEvents {
let updatedTypingActivities: [PeerId: [PeerId: PeerInputActivity?]] let updatedTypingActivities: [PeerId: [PeerId: PeerInputActivity?]]
let updatedWebpages: [MediaId: TelegramMediaWebpage] let updatedWebpages: [MediaId: TelegramMediaWebpage]
let updatedCalls: [Api.PhoneCall] let updatedCalls: [Api.PhoneCall]
let isContactUpdates: [(PeerId, Bool)]
let displayAlerts: [String] let displayAlerts: [String]
let delayNotificatonsUntil: Int32? let delayNotificatonsUntil: Int32?
var isEmpty: Bool { var isEmpty: Bool {
return self.addedIncomingMessageIds.isEmpty && self.updatedTypingActivities.isEmpty && self.updatedWebpages.isEmpty && self.updatedCalls.isEmpty && self.displayAlerts.isEmpty && delayNotificatonsUntil == nil return self.addedIncomingMessageIds.isEmpty && self.updatedTypingActivities.isEmpty && self.updatedWebpages.isEmpty && self.updatedCalls.isEmpty && self.isContactUpdates.isEmpty && self.displayAlerts.isEmpty && delayNotificatonsUntil == nil
} }
init() { init() {
@@ -419,15 +426,17 @@ struct AccountFinalStateEvents {
self.updatedTypingActivities = [:] self.updatedTypingActivities = [:]
self.updatedWebpages = [:] self.updatedWebpages = [:]
self.updatedCalls = [] self.updatedCalls = []
self.isContactUpdates = []
self.displayAlerts = [] self.displayAlerts = []
self.delayNotificatonsUntil = nil self.delayNotificatonsUntil = nil
} }
init(addedIncomingMessageIds: [MessageId], updatedTypingActivities: [PeerId: [PeerId: PeerInputActivity?]], updatedWebpages: [MediaId: TelegramMediaWebpage], updatedCalls: [Api.PhoneCall], displayAlerts: [String], delayNotificatonsUntil: Int32?) { init(addedIncomingMessageIds: [MessageId], updatedTypingActivities: [PeerId: [PeerId: PeerInputActivity?]], updatedWebpages: [MediaId: TelegramMediaWebpage], updatedCalls: [Api.PhoneCall], isContactUpdates: [(PeerId, Bool)], displayAlerts: [String], delayNotificatonsUntil: Int32?) {
self.addedIncomingMessageIds = addedIncomingMessageIds self.addedIncomingMessageIds = addedIncomingMessageIds
self.updatedTypingActivities = updatedTypingActivities self.updatedTypingActivities = updatedTypingActivities
self.updatedWebpages = updatedWebpages self.updatedWebpages = updatedWebpages
self.updatedCalls = updatedCalls self.updatedCalls = updatedCalls
self.isContactUpdates = isContactUpdates
self.displayAlerts = displayAlerts self.displayAlerts = displayAlerts
self.delayNotificatonsUntil = delayNotificatonsUntil self.delayNotificatonsUntil = delayNotificatonsUntil
} }
@@ -437,6 +446,7 @@ struct AccountFinalStateEvents {
self.updatedTypingActivities = state.updatedTypingActivities self.updatedTypingActivities = state.updatedTypingActivities
self.updatedWebpages = state.updatedWebpages self.updatedWebpages = state.updatedWebpages
self.updatedCalls = state.updatedCalls self.updatedCalls = state.updatedCalls
self.isContactUpdates = state.isContactUpdates
self.displayAlerts = state.state.state.displayAlerts self.displayAlerts = state.state.state.displayAlerts
self.delayNotificatonsUntil = state.delayNotificatonsUntil self.delayNotificatonsUntil = state.delayNotificatonsUntil
} }
@@ -448,6 +458,6 @@ struct AccountFinalStateEvents {
delayNotificatonsUntil = other delayNotificatonsUntil = other
} }
} }
return AccountFinalStateEvents(addedIncomingMessageIds: self.addedIncomingMessageIds + other.addedIncomingMessageIds, updatedTypingActivities: self.updatedTypingActivities, updatedWebpages: self.updatedWebpages, updatedCalls: self.updatedCalls + other.updatedCalls, displayAlerts: self.displayAlerts + other.displayAlerts, delayNotificatonsUntil: delayNotificatonsUntil) return AccountFinalStateEvents(addedIncomingMessageIds: self.addedIncomingMessageIds + other.addedIncomingMessageIds, updatedTypingActivities: self.updatedTypingActivities, updatedWebpages: self.updatedWebpages, updatedCalls: self.updatedCalls + other.updatedCalls, isContactUpdates: self.isContactUpdates + other.isContactUpdates, displayAlerts: self.displayAlerts + other.displayAlerts, delayNotificatonsUntil: delayNotificatonsUntil)
} }
} }

View File

@@ -1081,6 +1081,15 @@ private func finalStateWithUpdatesAndServerTime(account: Account, state: Account
return peer return peer
} }
}) })
case let .updateContactLink(userId, myLink, _):
let isContact: Bool
switch myLink {
case .contactLinkContact:
isContact = true
default:
isContact = false
}
updatedState.updatePeerIsContact(PeerId(namespace: Namespaces.Peer.CloudUser, id: userId), isContact: isContact)
case let .updateEncryption(chat, date): case let .updateEncryption(chat, date):
updatedState.updateSecretChat(chat: chat, timestamp: date) updatedState.updateSecretChat(chat: chat, timestamp: date)
case let .updateNewEncryptedMessage(message, _): case let .updateNewEncryptedMessage(message, _):
@@ -1813,7 +1822,7 @@ private func optimizedOperations(_ operations: [AccountStateMutationOperation])
var currentAddMessages: OptimizeAddMessagesState? var currentAddMessages: OptimizeAddMessagesState?
for operation in operations { for operation in operations {
switch operation { switch operation {
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .UpdateLangPack, .UpdateMinAvailableMessage: case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact:
if let currentAddMessages = currentAddMessages, !currentAddMessages.messages.isEmpty { if let currentAddMessages = currentAddMessages, !currentAddMessages.messages.isEmpty {
result.append(.AddMessages(currentAddMessages.messages, currentAddMessages.location)) result.append(.AddMessages(currentAddMessages.messages, currentAddMessages.location))
} }
@@ -1864,6 +1873,7 @@ func replayFinalState(accountPeerId: PeerId, mediaBox: MediaBox, transaction: Tr
var updatedSecretChatTypingActivities = Set<PeerId>() var updatedSecretChatTypingActivities = Set<PeerId>()
var updatedWebpages: [MediaId: TelegramMediaWebpage] = [:] var updatedWebpages: [MediaId: TelegramMediaWebpage] = [:]
var updatedCalls: [Api.PhoneCall] = [] var updatedCalls: [Api.PhoneCall] = []
var isContactUpdates: [(PeerId, Bool)] = []
var stickerPackOperations: [AccountStateUpdateStickerPacksOperation] = [] var stickerPackOperations: [AccountStateUpdateStickerPacksOperation] = []
var recentlyUsedStickers: [MediaId: (MessageIndex, TelegramMediaFile)] = [:] var recentlyUsedStickers: [MediaId: (MessageIndex, TelegramMediaFile)] = [:]
var recentlyUsedGifs: [MediaId: (MessageIndex, TelegramMediaFile)] = [:] var recentlyUsedGifs: [MediaId: (MessageIndex, TelegramMediaFile)] = [:]
@@ -2221,6 +2231,8 @@ func replayFinalState(accountPeerId: PeerId, mediaBox: MediaBox, transaction: Tr
} else { } else {
pollLangPack = true pollLangPack = true
} }
case let .UpdateIsContact(peerId, value):
isContactUpdates.append((peerId, value))
} }
} }
@@ -2445,5 +2457,5 @@ func replayFinalState(accountPeerId: PeerId, mediaBox: MediaBox, transaction: Tr
addedIncomingMessageIds.append(contentsOf: addedSecretMessageIds) addedIncomingMessageIds.append(contentsOf: addedSecretMessageIds)
return AccountReplayedFinalState(state: finalState, addedIncomingMessageIds: addedIncomingMessageIds, addedSecretMessageIds: addedSecretMessageIds, updatedTypingActivities: updatedTypingActivities, updatedWebpages: updatedWebpages, updatedCalls: updatedCalls, delayNotificatonsUntil: delayNotificatonsUntil) return AccountReplayedFinalState(state: finalState, addedIncomingMessageIds: addedIncomingMessageIds, addedSecretMessageIds: addedSecretMessageIds, updatedTypingActivities: updatedTypingActivities, updatedWebpages: updatedWebpages, updatedCalls: updatedCalls, isContactUpdates: isContactUpdates, delayNotificatonsUntil: delayNotificatonsUntil)
} }

View File

@@ -9,7 +9,7 @@ import Foundation
import MtProtoKitDynamic import MtProtoKitDynamic
#endif #endif
private enum AccountStateManagerOperation { private enum AccountStateManagerOperationContent {
case pollDifference(AccountFinalStateEvents) case pollDifference(AccountFinalStateEvents)
case collectUpdateGroups([UpdateGroup], Double) case collectUpdateGroups([UpdateGroup], Double)
case processUpdateGroups([UpdateGroup]) case processUpdateGroups([UpdateGroup])
@@ -19,6 +19,20 @@ private enum AccountStateManagerOperation {
case replayAsynchronouslyBuiltFinalState(AccountFinalState, () -> Void) case replayAsynchronouslyBuiltFinalState(AccountFinalState, () -> Void)
} }
private final class AccountStateManagerOperation {
var isRunning: Bool = false
let content: AccountStateManagerOperationContent
init(content: AccountStateManagerOperationContent) {
self.content = content
}
}
private enum AccountStateManagerAddOperationPosition {
case first
case last
}
#if os(macOS) #if os(macOS)
private typealias SignalKitTimer = SwiftSignalKitMac.Timer private typealias SignalKitTimer = SwiftSignalKitMac.Timer
#else #else
@@ -137,22 +151,17 @@ public final class AccountStateManager {
func addUpdateGroups(_ groups: [UpdateGroup]) { func addUpdateGroups(_ groups: [UpdateGroup]) {
self.queue.async { self.queue.async {
if let last = self.operations.last { if let last = self.operations.last {
switch last { switch last.content {
case .pollDifference, .processUpdateGroups, .custom, .pollCompletion, .processEvents, .replayAsynchronouslyBuiltFinalState: case .pollDifference, .processUpdateGroups, .custom, .pollCompletion, .processEvents, .replayAsynchronouslyBuiltFinalState:
self.operations.append(.collectUpdateGroups(groups, 0.0)) self.addOperation(.collectUpdateGroups(groups, 0.0), position: .last)
case let .collectUpdateGroups(currentGroups, timeout): case let .collectUpdateGroups(currentGroups, timeout):
if timeout.isEqual(to: 0.0) { let operation = AccountStateManagerOperation(content: .collectUpdateGroups(currentGroups + groups, timeout))
self.operations[self.operations.count - 1] = .collectUpdateGroups(currentGroups + groups, timeout) operation.isRunning = last.isRunning
} else { self.operations[self.operations.count - 1] = operation
self.operations[self.operations.count - 1] = .processUpdateGroups(currentGroups + groups)
if self.operations.count == 1 {
self.startFirstOperation() self.startFirstOperation()
} }
}
}
} else { } else {
self.operations.append(.collectUpdateGroups(groups, 0.0)) self.addOperation(.collectUpdateGroups(groups, 0.0), position: .last)
self.startFirstOperation()
} }
} }
} }
@@ -160,14 +169,10 @@ public final class AccountStateManager {
func addReplayAsynchronouslyBuiltFinalState(_ finalState: AccountFinalState) -> Signal<Bool, NoError> { func addReplayAsynchronouslyBuiltFinalState(_ finalState: AccountFinalState) -> Signal<Bool, NoError> {
return Signal { subscriber in return Signal { subscriber in
self.queue.async { self.queue.async {
let begin = self.operations.isEmpty self.addOperation(.replayAsynchronouslyBuiltFinalState(finalState, {
self.operations.append(.replayAsynchronouslyBuiltFinalState(finalState, {
subscriber.putNext(true) subscriber.putNext(true)
subscriber.putCompletion() subscriber.putCompletion()
})) }), position: .last)
if begin {
self.startFirstOperation()
}
} }
return EmptyDisposable return EmptyDisposable
} }
@@ -199,23 +204,28 @@ public final class AccountStateManager {
}) })
} }
self.addOperation(.custom(self.getNextId(), signal)) self.addOperation(.custom(self.getNextId(), signal), position: .last)
return disposable return disposable
} |> runOn(self.queue) } |> runOn(self.queue)
} }
private func replaceOperations(with operation: AccountStateManagerOperation) { private func replaceOperations(with content: AccountStateManagerOperationContent) {
var collectedProcessUpdateGroups: [(AccountStateManagerOperation, Bool)] = [] var collectedProcessUpdateGroups: [AccountStateManagerOperationContent] = []
var collectedMessageIds: [MessageId] = [] var collectedMessageIds: [MessageId] = []
var collectedPollCompletionSubscribers: [(Int32, ([MessageId]) -> Void)] = [] var collectedPollCompletionSubscribers: [(Int32, ([MessageId]) -> Void)] = []
var collectedReplayAsynchronouslyBuiltFinalState: [(AccountFinalState, () -> Void)] = [] var collectedReplayAsynchronouslyBuiltFinalState: [(AccountFinalState, () -> Void)] = []
var processEvents: [(Int32, AccountFinalStateEvents)] = [] var processEvents: [(Int32, AccountFinalStateEvents)] = []
var replacedOperations: [AccountStateManagerOperation] = []
for i in 0 ..< self.operations.count { for i in 0 ..< self.operations.count {
switch self.operations[i] { if self.operations[i].isRunning {
replacedOperations.append(self.operations[i])
} else {
switch self.operations[i].content {
case .processUpdateGroups: case .processUpdateGroups:
collectedProcessUpdateGroups.append((self.operations[i], i == 0)) collectedProcessUpdateGroups.append(self.operations[i].content)
case let .pollCompletion(_, messageIds, subscribers): case let .pollCompletion(_, messageIds, subscribers):
collectedMessageIds.append(contentsOf: messageIds) collectedMessageIds.append(contentsOf: messageIds)
collectedPollCompletionSubscribers.append(contentsOf: subscribers) collectedPollCompletionSubscribers.append(contentsOf: subscribers)
@@ -227,28 +237,40 @@ public final class AccountStateManager {
break break
} }
} }
}
self.operations.removeAll() replacedOperations.append(contentsOf: collectedProcessUpdateGroups.map { AccountStateManagerOperation(content: $0) })
self.operations.append(contentsOf: collectedProcessUpdateGroups.map { $0.0 }) replacedOperations.append(AccountStateManagerOperation(content: content))
self.operations.append(operation)
if !collectedPollCompletionSubscribers.isEmpty || !collectedMessageIds.isEmpty { if !collectedPollCompletionSubscribers.isEmpty || !collectedMessageIds.isEmpty {
self.operations.append(.pollCompletion(self.getNextId(), collectedMessageIds, collectedPollCompletionSubscribers)) replacedOperations.append(AccountStateManagerOperation(content: .pollCompletion(self.getNextId(), collectedMessageIds, collectedPollCompletionSubscribers)))
} }
for (finalState, completion) in collectedReplayAsynchronouslyBuiltFinalState { for (finalState, completion) in collectedReplayAsynchronouslyBuiltFinalState {
self.operations.append(.replayAsynchronouslyBuiltFinalState(finalState, completion)) replacedOperations.append(AccountStateManagerOperation(content: .replayAsynchronouslyBuiltFinalState(finalState, completion)))
} }
for (operationId, events) in processEvents { for (operationId, events) in processEvents {
self.operations.append(.processEvents(operationId, events)) replacedOperations.append(AccountStateManagerOperation(content: .processEvents(operationId, events)))
}
} }
private func addOperation(_ operation: AccountStateManagerOperation) { self.operations.removeAll()
self.operations.append(contentsOf: replacedOperations)
}
private func addOperation(_ content: AccountStateManagerOperationContent, position: AccountStateManagerAddOperationPosition) {
self.queue.async { self.queue.async {
let operation = AccountStateManagerOperation(content: content)
switch position {
case .first:
if self.operations.isEmpty || !self.operations[0].isRunning {
self.operations.insert(operation, at: 0)
self.startFirstOperation()
} else {
self.operations.insert(operation, at: 1)
}
case .last:
let begin = self.operations.isEmpty let begin = self.operations.isEmpty
self.operations.append(operation) self.operations.append(operation)
if begin { if begin {
@@ -256,12 +278,17 @@ public final class AccountStateManager {
} }
} }
} }
}
private func startFirstOperation() { private func startFirstOperation() {
guard let operation = self.operations.first else { guard let operation = self.operations.first else {
return return
} }
switch operation { guard !operation.isRunning else {
return
}
operation.isRunning = true
switch operation.content {
case let .pollDifference(currentEvents): case let .pollDifference(currentEvents):
self.operationTimer?.invalidate() self.operationTimer?.invalidate()
self.currentIsUpdatingValue = true self.currentIsUpdatingValue = true
@@ -282,7 +309,9 @@ public final class AccountStateManager {
if let authorizedState = state.state { if let authorizedState = state.state {
let request = account.network.request(Api.functions.updates.getDifference(flags: 1 << 0, pts: authorizedState.pts, ptsTotalLimit: 1000, date: authorizedState.date, qts: authorizedState.qts)) let request = account.network.request(Api.functions.updates.getDifference(flags: 1 << 0, pts: authorizedState.pts, ptsTotalLimit: 1000, date: authorizedState.date, qts: authorizedState.qts))
|> retryRequest |> retryRequest
return request |> mapToSignal { difference -> Signal<(Api.updates.Difference?, AccountReplayedFinalState?), NoError> in
return request
|> mapToSignal { difference -> Signal<(Api.updates.Difference?, AccountReplayedFinalState?), NoError> in
switch difference { switch difference {
case .differenceTooLong: case .differenceTooLong:
return accountStateReset(postbox: account.postbox, network: account.network) |> mapToSignal { _ -> Signal<(Api.updates.Difference?, AccountReplayedFinalState?), NoError> in return accountStateReset(postbox: account.postbox, network: account.network) |> mapToSignal { _ -> Signal<(Api.updates.Difference?, AccountReplayedFinalState?), NoError> in
@@ -334,7 +363,7 @@ public final class AccountStateManager {
|> deliverOn(self.queue) |> deliverOn(self.queue)
let _ = signal.start(next: { [weak self] difference, finalState in let _ = signal.start(next: { [weak self] difference, finalState in
if let strongSelf = self { if let strongSelf = self {
if case .pollDifference = strongSelf.operations.removeFirst() { if case .pollDifference = strongSelf.operations.removeFirst().content {
let events: AccountFinalStateEvents let events: AccountFinalStateEvents
if let finalState = finalState { if let finalState = finalState {
events = currentEvents.union(with: AccountFinalStateEvents(state: finalState)) events = currentEvents.union(with: AccountFinalStateEvents(state: finalState))
@@ -344,7 +373,7 @@ public final class AccountStateManager {
if let difference = difference { if let difference = difference {
switch difference { switch difference {
case .differenceSlice: case .differenceSlice:
strongSelf.operations.insert(.pollDifference(events), at: 0) strongSelf.addOperation(.pollDifference(events), position: .first)
default: default:
if !events.isEmpty { if !events.isEmpty {
strongSelf.insertProcessEvents(events) strongSelf.insertProcessEvents(events)
@@ -370,9 +399,10 @@ public final class AccountStateManager {
self.operationTimer?.invalidate() self.operationTimer?.invalidate()
let operationTimer = SignalKitTimer(timeout: timeout, repeat: false, completion: { [weak self] in let operationTimer = SignalKitTimer(timeout: timeout, repeat: false, completion: { [weak self] in
if let strongSelf = self { if let strongSelf = self {
if let firstOperation = strongSelf.operations.first, case let .collectUpdateGroups(groups, _) = firstOperation { let firstOperation = strongSelf.operations.removeFirst()
if case let .collectUpdateGroups(groups, _) = firstOperation.content {
if timeout.isEqual(to: 0.0) { if timeout.isEqual(to: 0.0) {
strongSelf.operations[0] = .processUpdateGroups(groups) strongSelf.addOperation(.processUpdateGroups(groups), position: .first)
} else { } else {
Logger.shared.log("AccountStateManager", "timeout while waiting for updates") Logger.shared.log("AccountStateManager", "timeout while waiting for updates")
strongSelf.replaceOperations(with: .pollDifference(AccountFinalStateEvents())) strongSelf.replaceOperations(with: .pollDifference(AccountFinalStateEvents()))
@@ -411,14 +441,14 @@ public final class AccountStateManager {
} }
let _ = signal.start(next: { [weak self] replayedState, finalState in let _ = signal.start(next: { [weak self] replayedState, finalState in
if let strongSelf = self { if let strongSelf = self {
if case let .processUpdateGroups(groups) = strongSelf.operations.removeFirst() { if case let .processUpdateGroups(groups) = strongSelf.operations.removeFirst().content {
if let replayedState = replayedState, !finalState.shouldPoll { if let replayedState = replayedState, !finalState.shouldPoll {
let events = AccountFinalStateEvents(state: replayedState) let events = AccountFinalStateEvents(state: replayedState)
if !events.isEmpty { if !events.isEmpty {
strongSelf.insertProcessEvents(events) strongSelf.insertProcessEvents(events)
} }
if finalState.incomplete { if finalState.incomplete {
strongSelf.operations.append(.collectUpdateGroups(groups, 2.0)) strongSelf.addOperation(.collectUpdateGroups(groups, 2.0), position: .last)
} }
} else { } else {
strongSelf.replaceOperations(with: .pollDifference(AccountFinalStateEvents())) strongSelf.replaceOperations(with: .pollDifference(AccountFinalStateEvents()))
@@ -436,8 +466,8 @@ public final class AccountStateManager {
self.operationTimer?.invalidate() self.operationTimer?.invalidate()
let completed: () -> Void = { [weak self] in let completed: () -> Void = { [weak self] in
if let strongSelf = self { if let strongSelf = self {
if let topOperation = strongSelf.operations.first, case .custom(operationId, _) = topOperation { let topOperation = strongSelf.operations.removeFirst()
strongSelf.operations.removeFirst() if case .custom(operationId, _) = topOperation.content {
strongSelf.startFirstOperation() strongSelf.startFirstOperation()
} else { } else {
assertionFailure() assertionFailure()
@@ -453,7 +483,8 @@ public final class AccountStateManager {
self.operationTimer?.invalidate() self.operationTimer?.invalidate()
let completed: () -> Void = { [weak self] in let completed: () -> Void = { [weak self] in
if let strongSelf = self { if let strongSelf = self {
if let topOperation = strongSelf.operations.first, case .processEvents(operationId, _) = topOperation { let topOperation = strongSelf.operations.removeFirst()
if case .processEvents(operationId, _) = topOperation.content {
if !events.updatedTypingActivities.isEmpty { if !events.updatedTypingActivities.isEmpty {
strongSelf.peerInputActivityManager.transaction { manager in strongSelf.peerInputActivityManager.transaction { manager in
for (chatPeerId, peerActivities) in events.updatedTypingActivities { for (chatPeerId, peerActivities) in events.updatedTypingActivities {
@@ -475,14 +506,18 @@ public final class AccountStateManager {
strongSelf.account.callSessionManager.updateSession(call) strongSelf.account.callSessionManager.updateSession(call)
} }
} }
strongSelf.operations.removeFirst() if !events.isContactUpdates.isEmpty {
strongSelf.account.contactSyncManager.addIsContactUpdates(events.isContactUpdates)
}
var pollCount = 0 var pollCount = 0
for i in 0 ..< strongSelf.operations.count { for i in 0 ..< strongSelf.operations.count {
if case let .pollCompletion(pollId, messageIds, subscribers) = strongSelf.operations[i] { if case let .pollCompletion(pollId, messageIds, subscribers) = strongSelf.operations[i].content {
pollCount += 1 pollCount += 1
var updatedMessageIds = messageIds var updatedMessageIds = messageIds
updatedMessageIds.append(contentsOf: events.addedIncomingMessageIds) updatedMessageIds.append(contentsOf: events.addedIncomingMessageIds)
strongSelf.operations[i] = .pollCompletion(pollId, updatedMessageIds, subscribers) let operation = AccountStateManagerOperation(content: .pollCompletion(pollId, updatedMessageIds, subscribers))
operation.isRunning = strongSelf.operations[i].isRunning
strongSelf.operations[i] = operation
} }
} }
assert(pollCount <= 1) assert(pollCount <= 1)
@@ -508,7 +543,8 @@ public final class AccountStateManager {
return messages return messages
} }
let _ = (signal |> deliverOn(self.queue)).start(next: { [weak self] messages in let _ = (signal
|> deliverOn(self.queue)).start(next: { [weak self] messages in
if let strongSelf = self { if let strongSelf = self {
strongSelf.notificationMessagesPipe.putNext(messages) strongSelf.notificationMessagesPipe.putNext(messages)
} }
@@ -532,10 +568,10 @@ public final class AccountStateManager {
|> deliverOn(self.queue) |> deliverOn(self.queue)
let completed: () -> Void = { [weak self] in let completed: () -> Void = { [weak self] in
if let strongSelf = self { if let strongSelf = self {
if let topOperation = strongSelf.operations.first, case let .pollCompletion(topPollId, messageIds, subscribers) = topOperation { let topOperation = strongSelf.operations.removeFirst()
if case let .pollCompletion(topPollId, messageIds, subscribers) = topOperation.content {
assert(topPollId == pollId) assert(topPollId == pollId)
strongSelf.operations.removeFirst()
if strongSelf.operations.isEmpty { if strongSelf.operations.isEmpty {
for (_, f) in subscribers { for (_, f) in subscribers {
f(messageIds) f(messageIds)
@@ -573,7 +609,7 @@ public final class AccountStateManager {
let _ = signal.start(next: { [weak self] replayedState, finalState in let _ = signal.start(next: { [weak self] replayedState, finalState in
if let strongSelf = self { if let strongSelf = self {
if case .replayAsynchronouslyBuiltFinalState = strongSelf.operations.removeFirst() { if case .replayAsynchronouslyBuiltFinalState = strongSelf.operations.removeFirst().content {
if let replayedState = replayedState { if let replayedState = replayedState {
let events = AccountFinalStateEvents(state: replayedState) let events = AccountFinalStateEvents(state: replayedState)
if !events.isEmpty { if !events.isEmpty {
@@ -596,21 +632,30 @@ public final class AccountStateManager {
private func insertProcessEvents(_ events: AccountFinalStateEvents) { private func insertProcessEvents(_ events: AccountFinalStateEvents) {
if !events.isEmpty { if !events.isEmpty {
var index = 0 let operation = AccountStateManagerOperation(content: .processEvents(self.getNextId(), events))
if !self.operations.isEmpty { var inserted = false
while case .processEvents = self.operations[index] { for i in 0 ..< self.operations.count {
index += 1 if self.operations[i].isRunning {
continue
} }
if case .processEvents = self.operations[i].content {
continue
}
self.operations.insert(operation, at: i)
inserted = true
break
}
if !inserted {
self.operations.append(operation)
} }
self.operations.insert(.processEvents(self.getNextId(), events), at: 0)
} }
} }
private func postponePollCompletionOperation(messageIds: [MessageId], subscribers: [(Int32, ([MessageId]) -> Void)]) { private func postponePollCompletionOperation(messageIds: [MessageId], subscribers: [(Int32, ([MessageId]) -> Void)]) {
self.operations.append(.pollCompletion(self.getNextId(), messageIds, subscribers)) self.addOperation(.pollCompletion(self.getNextId(), messageIds, subscribers), position: .last)
for i in 0 ..< self.operations.count { for i in 0 ..< self.operations.count {
if case .pollCompletion = self.operations[i] { if case .pollCompletion = self.operations[i].content {
if i != self.operations.count - 1 { if i != self.operations.count - 1 {
assertionFailure() assertionFailure()
} }
@@ -624,31 +669,32 @@ public final class AccountStateManager {
let updatedId: Int32 = self.getNextId() let updatedId: Int32 = self.getNextId()
for i in 0 ..< self.operations.count { for i in 0 ..< self.operations.count {
if case let .pollCompletion(pollId, messageIds, subscribers) = self.operations[i] { if case let .pollCompletion(pollId, messageIds, subscribers) = self.operations[i].content {
var subscribers = subscribers var subscribers = subscribers
subscribers.append((updatedId, f)) subscribers.append((updatedId, f))
self.operations[i] = .pollCompletion(pollId, messageIds, subscribers) let operation = AccountStateManagerOperation(content: .pollCompletion(pollId, messageIds, subscribers))
operation.isRunning = self.operations[i].isRunning
self.operations[i] = operation
return updatedId return updatedId
} }
} }
let beginFirst = self.operations.isEmpty let beginFirst = self.operations.isEmpty
self.operations.append(.pollCompletion(self.getNextId(), [], [(updatedId, f)])) self.addOperation(.pollCompletion(self.getNextId(), [], [(updatedId, f)]), position: .last)
if beginFirst {
self.startFirstOperation()
}
return updatedId return updatedId
} }
private func removePollCompletion(_ id: Int32) { private func removePollCompletion(_ id: Int32) {
for i in 0 ..< self.operations.count { for i in 0 ..< self.operations.count {
if case let .pollCompletion(pollId, messages, subscribers) = self.operations[i] { if case let .pollCompletion(pollId, messages, subscribers) = self.operations[i].content {
for j in 0 ..< subscribers.count { for j in 0 ..< subscribers.count {
if subscribers[j].0 == id { if subscribers[j].0 == id {
var subscribers = subscribers var subscribers = subscribers
subscribers.remove(at: j) subscribers.remove(at: j)
self.operations[i] = .pollCompletion(pollId, messages, subscribers) let operation = AccountStateManagerOperation(content: .pollCompletion(pollId, messages, subscribers))
operation.isRunning = self.operations[i].isRunning
self.operations[i] = operation
break break
} }
} }

View File

@@ -37,15 +37,15 @@ private func updatedRemoteContactPeers(network: Network, hash: Int32) -> Signal<
} }
private func hashForCountAndIds(count: Int32, ids: [Int32]) -> Int32 { private func hashForCountAndIds(count: Int32, ids: [Int32]) -> Int32 {
var acc: UInt32 = 0 var acc: Int64 = 0
acc = (acc &* 20261) &+ UInt32(bitPattern: count) acc = (acc &* 20261) &+ Int64(count)
for id in ids { for id in ids {
let low = UInt32(bitPattern: id) acc = (acc &* 20261) &+ Int64(id)
acc = (acc &* 20261) &+ low acc = acc & Int64(0x7FFFFFFF)
} }
return Int32(bitPattern: acc & UInt32(0x7FFFFFFF)) return Int32(acc & Int64(0x7FFFFFFF))
} }
func syncContactsOnce(network: Network, postbox: Postbox) -> Signal<Never, NoError> { func syncContactsOnce(network: Network, postbox: Postbox) -> Signal<Never, NoError> {

View File

@@ -20,8 +20,9 @@ private final class ContactSyncOperation {
} }
private enum ContactSyncOperationContent { private enum ContactSyncOperationContent {
case waitForUpdatedState
case sync(importableContacts: [DeviceContactNormalizedPhoneNumber: ImportableDeviceContactData]?) case sync(importableContacts: [DeviceContactNormalizedPhoneNumber: ImportableDeviceContactData]?)
case updateIsContact(peerId: PeerId, isContact: Bool) case updateIsContact([(PeerId, Bool)])
} }
private final class ContactSyncManagerImpl { private final class ContactSyncManagerImpl {
@@ -53,15 +54,22 @@ private final class ContactSyncManagerImpl {
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
strongSelf.addOperation(.waitForUpdatedState)
strongSelf.addOperation(.sync(importableContacts: importableContacts)) strongSelf.addOperation(.sync(importableContacts: importableContacts))
})) }))
} }
func addIsContactUpdates(_ updates: [(PeerId, Bool)]) {
self.addOperation(.updateIsContact(updates))
}
func addOperation(_ content: ContactSyncOperationContent) { func addOperation(_ content: ContactSyncOperationContent) {
let id = self.nextId let id = self.nextId
self.nextId += 1 self.nextId += 1
let operation = ContactSyncOperation(id: id, content: content) let operation = ContactSyncOperation(id: id, content: content)
switch content { switch content {
case .waitForUpdatedState:
self.operations.append(operation)
case .sync: case .sync:
for i in (0 ..< self.operations.count).reversed() { for i in (0 ..< self.operations.count).reversed() {
if case .sync = self.operations[i].content { if case .sync = self.operations[i].content {
@@ -70,10 +78,28 @@ private final class ContactSyncManagerImpl {
} }
} }
} }
default:
break
}
self.operations.append(operation) self.operations.append(operation)
case let .updateIsContact(updates):
var mergedUpdates: [(PeerId, Bool)] = []
var removeIndices: [Int] = []
for i in 0 ..< self.operations.count {
if case let .updateIsContact(operationUpdates) = self.operations[i].content {
if !self.operations[i].isRunning {
mergedUpdates.append(contentsOf: operationUpdates)
removeIndices.append(i)
}
}
}
mergedUpdates.append(contentsOf: updates)
for index in removeIndices.reversed() {
self.operations.remove(at: index)
}
if self.operations.isEmpty || !self.operations[0].isRunning {
self.operations.insert(operation, at: 0)
} else {
self.operations.insert(operation, at: 1)
}
}
self.updateOperations() self.updateOperations()
} }
@@ -100,6 +126,11 @@ private final class ContactSyncManagerImpl {
func startOperation(_ operation: ContactSyncOperationContent, disposable: DisposableSet, completion: @escaping () -> Void) { func startOperation(_ operation: ContactSyncOperationContent, disposable: DisposableSet, completion: @escaping () -> Void) {
switch operation { switch operation {
case .waitForUpdatedState:
disposable.add((self.stateManager.pollStateUpdateCompletion()
|> deliverOn(self.queue)).start(next: { _ in
completion()
}))
case let .sync(importableContacts): case let .sync(importableContacts):
let importSignal: Signal<PushDeviceContactsResult, NoError> let importSignal: Signal<PushDeviceContactsResult, NoError>
if let importableContacts = importableContacts { if let importableContacts = importableContacts {
@@ -107,18 +138,14 @@ private final class ContactSyncManagerImpl {
} else { } else {
importSignal = .single(PushDeviceContactsResult(addedReimportAttempts: [:])) importSignal = .single(PushDeviceContactsResult(addedReimportAttempts: [:]))
} }
disposable.add((self.stateManager.pollStateUpdateCompletion() disposable.add(
|> mapToSignal { _ -> Signal<PushDeviceContactsResult, NoError> in (syncContactsOnce(network: self.network, postbox: self.postbox)
return .complete()
}
|> then(
syncContactsOnce(network: self.network, postbox: self.postbox)
|> mapToSignal { _ -> Signal<PushDeviceContactsResult, NoError> in |> mapToSignal { _ -> Signal<PushDeviceContactsResult, NoError> in
return .complete() return .complete()
} }
|> then(importSignal) |> then(importSignal)
) |> deliverOn(self.queue)
|> deliverOn(self.queue)).start(next: { [weak self] result in ).start(next: { [weak self] result in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
@@ -128,17 +155,17 @@ private final class ContactSyncManagerImpl {
completion() completion()
})) }))
case let .updateIsContact(peerId, isContact): case let .updateIsContact(updates):
disposable.add((self.postbox.transaction { transaction -> Void in disposable.add((self.postbox.transaction { transaction -> Void in
if transaction.isPeerContact(peerId: peerId) != isContact {
var contactPeerIds = transaction.getContactPeerIds() var contactPeerIds = transaction.getContactPeerIds()
for (peerId, isContact) in updates {
if isContact { if isContact {
contactPeerIds.insert(peerId) contactPeerIds.insert(peerId)
} else { } else {
contactPeerIds.remove(peerId) contactPeerIds.remove(peerId)
} }
transaction.replaceContactPeerIds(contactPeerIds)
} }
transaction.replaceContactPeerIds(contactPeerIds)
} }
|> deliverOnMainQueue).start(completed: { |> deliverOnMainQueue).start(completed: {
completion() completion()
@@ -320,4 +347,10 @@ final class ContactSyncManager {
impl.beginSync(importableContacts: importableContacts) impl.beginSync(importableContacts: importableContacts)
} }
} }
func addIsContactUpdates(_ updates: [(PeerId, Bool)]) {
self.impl.with { impl in
impl.addIsContactUpdates(updates)
}
}
} }

View File

@@ -91,7 +91,7 @@ public final class Localization: PostboxCoding, Equatable {
public let version: Int32 public let version: Int32
public let entries: [LocalizationEntry] public let entries: [LocalizationEntry]
init(version: Int32, entries: [LocalizationEntry]) { public init(version: Int32, entries: [LocalizationEntry]) {
self.version = version self.version = version
self.entries = entries self.entries = entries
} }

View File

@@ -9,7 +9,7 @@ public final class LocalizationSettings: PreferencesEntry, Equatable {
public let languageCode: String public let languageCode: String
public let localization: Localization public let localization: Localization
init(languageCode: String, localization: Localization) { public init(languageCode: String, localization: Localization) {
self.languageCode = languageCode self.languageCode = languageCode
self.localization = localization self.localization = localization
} }

View File

@@ -94,7 +94,13 @@ func managedSynchronizeInstalledStickerPacksOperations(postbox: Postbox, network
let signal = withTakenOperation(postbox: postbox, peerId: entry.peerId, tag: tag, tagLocalIndex: entry.tagLocalIndex, { transaction, entry -> Signal<Void, NoError> in let signal = withTakenOperation(postbox: postbox, peerId: entry.peerId, tag: tag, tagLocalIndex: entry.tagLocalIndex, { transaction, entry -> Signal<Void, NoError> in
if let entry = entry { if let entry = entry {
if let operation = entry.contents as? SynchronizeInstalledStickerPacksOperation { if let operation = entry.contents as? SynchronizeInstalledStickerPacksOperation {
return stateManager.pollStateUpdateCompletion()
|> mapToSignal { _ -> Signal<Void, NoError> in
return postbox.transaction { transaction -> Signal<Void, NoError> in
return synchronizeInstalledStickerPacks(transaction: transaction, postbox: postbox, network: network, stateManager: stateManager, namespace: namespace, operation: operation) return synchronizeInstalledStickerPacks(transaction: transaction, postbox: postbox, network: network, stateManager: stateManager, namespace: namespace, operation: operation)
}
|> switchToLatest
}
} else { } else {
assertionFailure() assertionFailure()
} }

View File

@@ -866,6 +866,7 @@ public final class PendingMessageManager {
} }
return sendMessageRequest return sendMessageRequest
|> deliverOn(queue)
|> mapToSignal { result -> Signal<Void, MTRpcError> in |> mapToSignal { result -> Signal<Void, MTRpcError> in
if let strongSelf = self { if let strongSelf = self {
return strongSelf.applySentMessage(postbox: postbox, stateManager: stateManager, message: message, result: result) return strongSelf.applySentMessage(postbox: postbox, stateManager: stateManager, message: message, result: result)