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";
};
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 */
/* Begin XCConfigurationList section */
@@ -3539,6 +3661,7 @@
C22069BE1E8EB4A200E82730 /* Release Hockeyapp */,
D0924FE81FE52C12003F693F /* Release Hockeyapp Internal */,
D06706551D51162400DED3E3 /* Release AppStore */,
D0CE6EF5213DC30700BCD44B /* Release AppStore LLC */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = "Debug AppStore";
@@ -3552,6 +3675,7 @@
C22069BF1E8EB4A200E82730 /* Release Hockeyapp */,
D0924FE91FE52C12003F693F /* Release Hockeyapp Internal */,
D06706561D51162400DED3E3 /* Release AppStore */,
D0CE6EF6213DC30700BCD44B /* Release AppStore LLC */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = "Debug AppStore";
@@ -3565,6 +3689,7 @@
C22069C01E8EB4A200E82730 /* Release Hockeyapp */,
D0924FEA1FE52C12003F693F /* Release Hockeyapp Internal */,
D06706571D51162400DED3E3 /* Release AppStore */,
D0CE6EF7213DC30700BCD44B /* Release AppStore LLC */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = "Debug AppStore";
@@ -3578,6 +3703,7 @@
C22069C11E8EB4A200E82730 /* Release Hockeyapp */,
D0924FEB1FE52C12003F693F /* Release Hockeyapp Internal */,
D0B4186F1D7E03D5004562A4 /* Release AppStore */,
D0CE6EF8213DC30700BCD44B /* Release AppStore LLC */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = "Debug AppStore";

View File

@@ -850,7 +850,7 @@ public class Account {
private let serviceQueue = Queue()
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 viewTracker: AccountViewTracker!
public private(set) var pendingMessageManager: PendingMessageManager!
@@ -1183,6 +1183,7 @@ public class Account {
public func resetStateManagement() {
self.stateManager.reset()
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> {
@@ -1246,5 +1247,4 @@ public func setupAccount(_ account: Account, fetchCachedResourceRepresentation:
account.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 MergeApiChats([Api.Chat])
case UpdatePeer(PeerId, (Peer?) -> Peer?)
case UpdateIsContact(PeerId, Bool)
case UpdateCachedPeerData(PeerId, (CachedPeerData?) -> CachedPeerData?)
case MergeApiUsers([Api.User])
case MergePeerPresences([PeerId: PeerPresence])
@@ -246,6 +247,10 @@ struct AccountMutableState {
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?) {
self.addOperation(.UpdateCachedPeerData(id, f))
}
@@ -328,7 +333,7 @@ struct AccountMutableState {
mutating func addOperation(_ operation: AccountStateMutationOperation) {
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
case let .AddMessages(messages, _):
for message in messages {
@@ -399,6 +404,7 @@ struct AccountReplayedFinalState {
let updatedTypingActivities: [PeerId: [PeerId: PeerInputActivity?]]
let updatedWebpages: [MediaId: TelegramMediaWebpage]
let updatedCalls: [Api.PhoneCall]
let isContactUpdates: [(PeerId, Bool)]
let delayNotificatonsUntil: Int32?
}
@@ -407,11 +413,12 @@ struct AccountFinalStateEvents {
let updatedTypingActivities: [PeerId: [PeerId: PeerInputActivity?]]
let updatedWebpages: [MediaId: TelegramMediaWebpage]
let updatedCalls: [Api.PhoneCall]
let isContactUpdates: [(PeerId, Bool)]
let displayAlerts: [String]
let delayNotificatonsUntil: Int32?
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() {
@@ -419,15 +426,17 @@ struct AccountFinalStateEvents {
self.updatedTypingActivities = [:]
self.updatedWebpages = [:]
self.updatedCalls = []
self.isContactUpdates = []
self.displayAlerts = []
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.updatedTypingActivities = updatedTypingActivities
self.updatedWebpages = updatedWebpages
self.updatedCalls = updatedCalls
self.isContactUpdates = isContactUpdates
self.displayAlerts = displayAlerts
self.delayNotificatonsUntil = delayNotificatonsUntil
}
@@ -437,6 +446,7 @@ struct AccountFinalStateEvents {
self.updatedTypingActivities = state.updatedTypingActivities
self.updatedWebpages = state.updatedWebpages
self.updatedCalls = state.updatedCalls
self.isContactUpdates = state.isContactUpdates
self.displayAlerts = state.state.state.displayAlerts
self.delayNotificatonsUntil = state.delayNotificatonsUntil
}
@@ -448,6 +458,6 @@ struct AccountFinalStateEvents {
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
}
})
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):
updatedState.updateSecretChat(chat: chat, timestamp: date)
case let .updateNewEncryptedMessage(message, _):
@@ -1813,7 +1822,7 @@ private func optimizedOperations(_ operations: [AccountStateMutationOperation])
var currentAddMessages: OptimizeAddMessagesState?
for operation in operations {
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 {
result.append(.AddMessages(currentAddMessages.messages, currentAddMessages.location))
}
@@ -1864,6 +1873,7 @@ func replayFinalState(accountPeerId: PeerId, mediaBox: MediaBox, transaction: Tr
var updatedSecretChatTypingActivities = Set<PeerId>()
var updatedWebpages: [MediaId: TelegramMediaWebpage] = [:]
var updatedCalls: [Api.PhoneCall] = []
var isContactUpdates: [(PeerId, Bool)] = []
var stickerPackOperations: [AccountStateUpdateStickerPacksOperation] = []
var recentlyUsedStickers: [MediaId: (MessageIndex, TelegramMediaFile)] = [:]
var recentlyUsedGifs: [MediaId: (MessageIndex, TelegramMediaFile)] = [:]
@@ -2221,6 +2231,8 @@ func replayFinalState(accountPeerId: PeerId, mediaBox: MediaBox, transaction: Tr
} else {
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)
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
#endif
private enum AccountStateManagerOperation {
private enum AccountStateManagerOperationContent {
case pollDifference(AccountFinalStateEvents)
case collectUpdateGroups([UpdateGroup], Double)
case processUpdateGroups([UpdateGroup])
@@ -19,6 +19,20 @@ private enum AccountStateManagerOperation {
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)
private typealias SignalKitTimer = SwiftSignalKitMac.Timer
#else
@@ -137,22 +151,17 @@ public final class AccountStateManager {
func addUpdateGroups(_ groups: [UpdateGroup]) {
self.queue.async {
if let last = self.operations.last {
switch last {
switch last.content {
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):
if timeout.isEqual(to: 0.0) {
self.operations[self.operations.count - 1] = .collectUpdateGroups(currentGroups + groups, timeout)
} else {
self.operations[self.operations.count - 1] = .processUpdateGroups(currentGroups + groups)
if self.operations.count == 1 {
self.startFirstOperation()
}
}
let operation = AccountStateManagerOperation(content: .collectUpdateGroups(currentGroups + groups, timeout))
operation.isRunning = last.isRunning
self.operations[self.operations.count - 1] = operation
self.startFirstOperation()
}
} else {
self.operations.append(.collectUpdateGroups(groups, 0.0))
self.startFirstOperation()
self.addOperation(.collectUpdateGroups(groups, 0.0), position: .last)
}
}
}
@@ -160,14 +169,10 @@ public final class AccountStateManager {
func addReplayAsynchronouslyBuiltFinalState(_ finalState: AccountFinalState) -> Signal<Bool, NoError> {
return Signal { subscriber in
self.queue.async {
let begin = self.operations.isEmpty
self.operations.append(.replayAsynchronouslyBuiltFinalState(finalState, {
self.addOperation(.replayAsynchronouslyBuiltFinalState(finalState, {
subscriber.putNext(true)
subscriber.putCompletion()
}))
if begin {
self.startFirstOperation()
}
}), position: .last)
}
return EmptyDisposable
}
@@ -199,60 +204,78 @@ public final class AccountStateManager {
})
}
self.addOperation(.custom(self.getNextId(), signal))
self.addOperation(.custom(self.getNextId(), signal), position: .last)
return disposable
} |> runOn(self.queue)
}
private func replaceOperations(with operation: AccountStateManagerOperation) {
var collectedProcessUpdateGroups: [(AccountStateManagerOperation, Bool)] = []
private func replaceOperations(with content: AccountStateManagerOperationContent) {
var collectedProcessUpdateGroups: [AccountStateManagerOperationContent] = []
var collectedMessageIds: [MessageId] = []
var collectedPollCompletionSubscribers: [(Int32, ([MessageId]) -> Void)] = []
var collectedReplayAsynchronouslyBuiltFinalState: [(AccountFinalState, () -> Void)] = []
var processEvents: [(Int32, AccountFinalStateEvents)] = []
var replacedOperations: [AccountStateManagerOperation] = []
for i in 0 ..< self.operations.count {
switch self.operations[i] {
case .processUpdateGroups:
collectedProcessUpdateGroups.append((self.operations[i], i == 0))
case let .pollCompletion(_, messageIds, subscribers):
collectedMessageIds.append(contentsOf: messageIds)
collectedPollCompletionSubscribers.append(contentsOf: subscribers)
case let .replayAsynchronouslyBuiltFinalState(finalState, completion):
collectedReplayAsynchronouslyBuiltFinalState.append((finalState, completion))
case let .processEvents(operationId, events):
processEvents.append((operationId, events))
default:
break
if self.operations[i].isRunning {
replacedOperations.append(self.operations[i])
} else {
switch self.operations[i].content {
case .processUpdateGroups:
collectedProcessUpdateGroups.append(self.operations[i].content)
case let .pollCompletion(_, messageIds, subscribers):
collectedMessageIds.append(contentsOf: messageIds)
collectedPollCompletionSubscribers.append(contentsOf: subscribers)
case let .replayAsynchronouslyBuiltFinalState(finalState, completion):
collectedReplayAsynchronouslyBuiltFinalState.append((finalState, completion))
case let .processEvents(operationId, events):
processEvents.append((operationId, events))
default:
break
}
}
}
self.operations.removeAll()
replacedOperations.append(contentsOf: collectedProcessUpdateGroups.map { AccountStateManagerOperation(content: $0) })
self.operations.append(contentsOf: collectedProcessUpdateGroups.map { $0.0 })
self.operations.append(operation)
replacedOperations.append(AccountStateManagerOperation(content: content))
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 {
self.operations.append(.replayAsynchronouslyBuiltFinalState(finalState, completion))
replacedOperations.append(AccountStateManagerOperation(content: .replayAsynchronouslyBuiltFinalState(finalState, completion)))
}
for (operationId, events) in processEvents {
self.operations.append(.processEvents(operationId, events))
replacedOperations.append(AccountStateManagerOperation(content: .processEvents(operationId, events)))
}
self.operations.removeAll()
self.operations.append(contentsOf: replacedOperations)
}
private func addOperation(_ operation: AccountStateManagerOperation) {
private func addOperation(_ content: AccountStateManagerOperationContent, position: AccountStateManagerAddOperationPosition) {
self.queue.async {
let begin = self.operations.isEmpty
self.operations.append(operation)
if begin {
self.startFirstOperation()
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
self.operations.append(operation)
if begin {
self.startFirstOperation()
}
}
}
}
@@ -261,7 +284,11 @@ public final class AccountStateManager {
guard let operation = self.operations.first else {
return
}
switch operation {
guard !operation.isRunning else {
return
}
operation.isRunning = true
switch operation.content {
case let .pollDifference(currentEvents):
self.operationTimer?.invalidate()
self.currentIsUpdatingValue = true
@@ -270,71 +297,73 @@ public final class AccountStateManager {
let accountPeerId = account.peerId
let auxiliaryMethods = self.auxiliaryMethods
let signal = account.postbox.stateView()
|> mapToSignal { view -> Signal<AuthorizedAccountState, NoError> in
if let state = view.state as? AuthorizedAccountState {
return .single(state)
} else {
return .complete()
}
|> mapToSignal { view -> Signal<AuthorizedAccountState, NoError> in
if let state = view.state as? AuthorizedAccountState {
return .single(state)
} else {
return .complete()
}
|> take(1)
|> mapToSignal { state -> Signal<(Api.updates.Difference?, AccountReplayedFinalState?), NoError> in
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))
|> retryRequest
return request |> mapToSignal { difference -> Signal<(Api.updates.Difference?, AccountReplayedFinalState?), NoError> in
switch difference {
case .differenceTooLong:
return accountStateReset(postbox: account.postbox, network: account.network) |> mapToSignal { _ -> Signal<(Api.updates.Difference?, AccountReplayedFinalState?), NoError> in
return .complete()
} |> then(.single((nil, nil)))
default:
return initialStateWithDifference(account, difference: difference)
|> mapToSignal { state -> Signal<(Api.updates.Difference?, AccountReplayedFinalState?), NoError> in
if state.initialState.state != authorizedState {
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)
|> mapToSignal { finalState -> Signal<(Api.updates.Difference?, AccountReplayedFinalState?), NoError> in
if !finalState.state.preCachedResources.isEmpty {
for (resource, data) in finalState.state.preCachedResources {
account.postbox.mediaBox.storeResourceData(resource.id, data: data)
}
}
return account.postbox.transaction { transaction -> (Api.updates.Difference?, AccountReplayedFinalState?) in
if let replayedState = replayFinalState(accountPeerId: accountPeerId, mediaBox: mediaBox, transaction: transaction, auxiliaryMethods: auxiliaryMethods, finalState: finalState) {
return (difference, replayedState)
} else {
return (nil, nil)
}
}
}
|> take(1)
|> mapToSignal { state -> Signal<(Api.updates.Difference?, AccountReplayedFinalState?), NoError> in
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))
|> retryRequest
return request
|> mapToSignal { difference -> Signal<(Api.updates.Difference?, AccountReplayedFinalState?), NoError> in
switch difference {
case .differenceTooLong:
return accountStateReset(postbox: account.postbox, network: account.network) |> mapToSignal { _ -> Signal<(Api.updates.Difference?, AccountReplayedFinalState?), NoError> in
return .complete()
} |> then(.single((nil, nil)))
default:
return initialStateWithDifference(account, difference: difference)
|> mapToSignal { state -> Signal<(Api.updates.Difference?, AccountReplayedFinalState?), NoError> in
if state.initialState.state != authorizedState {
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)
|> mapToSignal { finalState -> Signal<(Api.updates.Difference?, AccountReplayedFinalState?), NoError> in
if !finalState.state.preCachedResources.isEmpty {
for (resource, data) in finalState.state.preCachedResources {
account.postbox.mediaBox.storeResourceData(resource.id, data: data)
}
}
return account.postbox.transaction { transaction -> (Api.updates.Difference?, AccountReplayedFinalState?) in
if let replayedState = replayFinalState(accountPeerId: accountPeerId, mediaBox: mediaBox, transaction: transaction, auxiliaryMethods: auxiliaryMethods, finalState: finalState) {
return (difference, replayedState)
} else {
return (nil, nil)
}
}
}
}
}
}
} else {
let appliedState = account.network.request(Api.functions.updates.getState())
|> retryRequest
|> mapToSignal { state in
return account.postbox.transaction { transaction -> (Api.updates.Difference?, AccountReplayedFinalState?) in
if let currentState = transaction.getState() as? AuthorizedAccountState {
switch state {
case let .state(pts, qts, date, seq, _):
transaction.setState(currentState.changedState(AuthorizedAccountState.State(pts: pts, qts: qts, date: date, seq: seq)))
}
}
return (nil, nil)
}
}
return appliedState
}
} else {
let appliedState = account.network.request(Api.functions.updates.getState())
|> retryRequest
|> mapToSignal { state in
return account.postbox.transaction { transaction -> (Api.updates.Difference?, AccountReplayedFinalState?) in
if let currentState = transaction.getState() as? AuthorizedAccountState {
switch state {
case let .state(pts, qts, date, seq, _):
transaction.setState(currentState.changedState(AuthorizedAccountState.State(pts: pts, qts: qts, date: date, seq: seq)))
}
}
return (nil, nil)
}
}
return appliedState
}
|> deliverOn(self.queue)
}
|> deliverOn(self.queue)
let _ = signal.start(next: { [weak self] difference, finalState in
if let strongSelf = self {
if case .pollDifference = strongSelf.operations.removeFirst() {
if case .pollDifference = strongSelf.operations.removeFirst().content {
let events: AccountFinalStateEvents
if let finalState = finalState {
events = currentEvents.union(with: AccountFinalStateEvents(state: finalState))
@@ -344,7 +373,7 @@ public final class AccountStateManager {
if let difference = difference {
switch difference {
case .differenceSlice:
strongSelf.operations.insert(.pollDifference(events), at: 0)
strongSelf.addOperation(.pollDifference(events), position: .first)
default:
if !events.isEmpty {
strongSelf.insertProcessEvents(events)
@@ -370,9 +399,10 @@ public final class AccountStateManager {
self.operationTimer?.invalidate()
let operationTimer = SignalKitTimer(timeout: timeout, repeat: false, completion: { [weak self] in
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) {
strongSelf.operations[0] = .processUpdateGroups(groups)
strongSelf.addOperation(.processUpdateGroups(groups), position: .first)
} else {
Logger.shared.log("AccountStateManager", "timeout while waiting for updates")
strongSelf.replaceOperations(with: .pollDifference(AccountFinalStateEvents()))
@@ -393,32 +423,32 @@ public final class AccountStateManager {
let mediaBox = account.postbox.mediaBox
let queue = self.queue
let signal = initialStateWithUpdateGroups(account, groups: groups)
|> mapToSignal { state -> Signal<(AccountReplayedFinalState?, AccountFinalState), NoError> in
return finalStateWithUpdateGroups(account, state: state, groups: groups)
|> mapToSignal { finalState in
if !finalState.state.preCachedResources.isEmpty {
for (resource, data) in finalState.state.preCachedResources {
account.postbox.mediaBox.storeResourceData(resource.id, data: data)
}
}
return account.postbox.transaction { transaction -> AccountReplayedFinalState? in
return replayFinalState(accountPeerId: accountPeerId, mediaBox: mediaBox, transaction: transaction, auxiliaryMethods: auxiliaryMethods, finalState: finalState)
}
|> map({ ($0, finalState) })
|> deliverOn(queue)
|> mapToSignal { state -> Signal<(AccountReplayedFinalState?, AccountFinalState), NoError> in
return finalStateWithUpdateGroups(account, state: state, groups: groups)
|> mapToSignal { finalState in
if !finalState.state.preCachedResources.isEmpty {
for (resource, data) in finalState.state.preCachedResources {
account.postbox.mediaBox.storeResourceData(resource.id, data: data)
}
}
return account.postbox.transaction { transaction -> AccountReplayedFinalState? in
return replayFinalState(accountPeerId: accountPeerId, mediaBox: mediaBox, transaction: transaction, auxiliaryMethods: auxiliaryMethods, finalState: finalState)
}
|> map({ ($0, finalState) })
|> deliverOn(queue)
}
}
let _ = signal.start(next: { [weak self] replayedState, finalState in
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 {
let events = AccountFinalStateEvents(state: replayedState)
if !events.isEmpty {
strongSelf.insertProcessEvents(events)
}
if finalState.incomplete {
strongSelf.operations.append(.collectUpdateGroups(groups, 2.0))
strongSelf.addOperation(.collectUpdateGroups(groups, 2.0), position: .last)
}
} else {
strongSelf.replaceOperations(with: .pollDifference(AccountFinalStateEvents()))
@@ -436,8 +466,8 @@ public final class AccountStateManager {
self.operationTimer?.invalidate()
let completed: () -> Void = { [weak self] in
if let strongSelf = self {
if let topOperation = strongSelf.operations.first, case .custom(operationId, _) = topOperation {
strongSelf.operations.removeFirst()
let topOperation = strongSelf.operations.removeFirst()
if case .custom(operationId, _) = topOperation.content {
strongSelf.startFirstOperation()
} else {
assertionFailure()
@@ -453,7 +483,8 @@ public final class AccountStateManager {
self.operationTimer?.invalidate()
let completed: () -> Void = { [weak self] in
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 {
strongSelf.peerInputActivityManager.transaction { manager in
for (chatPeerId, peerActivities) in events.updatedTypingActivities {
@@ -475,14 +506,18 @@ public final class AccountStateManager {
strongSelf.account.callSessionManager.updateSession(call)
}
}
strongSelf.operations.removeFirst()
if !events.isContactUpdates.isEmpty {
strongSelf.account.contactSyncManager.addIsContactUpdates(events.isContactUpdates)
}
var pollCount = 0
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
var updatedMessageIds = messageIds
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)
@@ -508,7 +543,8 @@ public final class AccountStateManager {
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 {
strongSelf.notificationMessagesPipe.putNext(messages)
}
@@ -529,13 +565,13 @@ public final class AccountStateManager {
} else {
self.operationTimer?.invalidate()
let signal = self.account.network.request(Api.functions.help.test())
|> deliverOn(self.queue)
|> deliverOn(self.queue)
let completed: () -> Void = { [weak self] in
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)
strongSelf.operations.removeFirst()
if strongSelf.operations.isEmpty {
for (_, f) in subscribers {
f(messageIds)
@@ -573,7 +609,7 @@ public final class AccountStateManager {
let _ = signal.start(next: { [weak self] replayedState, finalState in
if let strongSelf = self {
if case .replayAsynchronouslyBuiltFinalState = strongSelf.operations.removeFirst() {
if case .replayAsynchronouslyBuiltFinalState = strongSelf.operations.removeFirst().content {
if let replayedState = replayedState {
let events = AccountFinalStateEvents(state: replayedState)
if !events.isEmpty {
@@ -596,21 +632,30 @@ public final class AccountStateManager {
private func insertProcessEvents(_ events: AccountFinalStateEvents) {
if !events.isEmpty {
var index = 0
if !self.operations.isEmpty {
while case .processEvents = self.operations[index] {
index += 1
let operation = AccountStateManagerOperation(content: .processEvents(self.getNextId(), events))
var inserted = false
for i in 0 ..< self.operations.count {
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)]) {
self.operations.append(.pollCompletion(self.getNextId(), messageIds, subscribers))
self.addOperation(.pollCompletion(self.getNextId(), messageIds, subscribers), position: .last)
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 {
assertionFailure()
}
@@ -624,31 +669,32 @@ public final class AccountStateManager {
let updatedId: Int32 = self.getNextId()
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
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
}
}
let beginFirst = self.operations.isEmpty
self.operations.append(.pollCompletion(self.getNextId(), [], [(updatedId, f)]))
if beginFirst {
self.startFirstOperation()
}
self.addOperation(.pollCompletion(self.getNextId(), [], [(updatedId, f)]), position: .last)
return updatedId
}
private func removePollCompletion(_ id: Int32) {
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 {
if subscribers[j].0 == id {
var subscribers = subscribers
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
}
}

View File

@@ -37,15 +37,15 @@ private func updatedRemoteContactPeers(network: Network, hash: Int32) -> Signal<
}
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 {
let low = UInt32(bitPattern: id)
acc = (acc &* 20261) &+ low
acc = (acc &* 20261) &+ Int64(id)
acc = acc & Int64(0x7FFFFFFF)
}
return Int32(bitPattern: acc & UInt32(0x7FFFFFFF))
return Int32(acc & Int64(0x7FFFFFFF))
}
func syncContactsOnce(network: Network, postbox: Postbox) -> Signal<Never, NoError> {

View File

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

View File

@@ -9,7 +9,7 @@ public final class LocalizationSettings: PreferencesEntry, Equatable {
public let languageCode: String
public let localization: Localization
init(languageCode: String, localization: Localization) {
public init(languageCode: String, localization: Localization) {
self.languageCode = languageCode
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
if let entry = entry {
if let operation = entry.contents as? SynchronizeInstalledStickerPacksOperation {
return synchronizeInstalledStickerPacks(transaction: transaction, postbox: postbox, network: network, stateManager: stateManager, namespace: namespace, operation: operation)
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)
}
|> switchToLatest
}
} else {
assertionFailure()
}

View File

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