mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Invite Links Improvements
This commit is contained in:
commit
a7b81d68ae
.bazelrc.gitmodulesBUILDIntents.intentdefinition
Telegram
build-system/bazel-rules
submodules
AccountContext/Sources
CameraUI/Sources
ChatListFilterSettingsHeaderItem/Sources
ContextUI/Sources
DatePickerNode/Sources
FFMpegBinding/Sources
InviteLinksUI/Sources
InviteLinkEditController.swiftInviteLinkListController.swiftItemListDatePickerItem.swiftItemListInviteLinkItem.swift
ItemListUI/Sources
LegacyComponents
PeerInfoUI/Sources
ChannelVisibilityController.swiftPeerAutoremoveMessagesScreen.swiftPeerAutoremoveSetupScreen.swiftPeerAutoremoveTimeoutItem.swift
Postbox/Sources
AccountManagerMetadataTable.swiftChatListView.swiftContactPeersView.swiftMessage.swiftMessageHistoryTable.swiftMessageView.swiftPeerMergedOperationLogView.swiftPostbox.swiftTimestampBasedMessageAttributesView.swiftViewTracker.swift
SSignalKit/SSignalKit/Source/SSignalKit
SegmentedControlNode/Sources
SettingsUI/Sources
SyncCore/Sources
AutoremoveTimeoutMessageAttribute.swiftCachedChannelData.swiftCachedGroupData.swiftCachedUserData.swift
TelegramApi/Sources
TelegramCallsUI/Sources
CallStatusBarNode.swiftPresentationCall.swiftPresentationGroupCall.swiftVoiceChatController.swiftVoiceChatParticipantItem.swift
TelegramCore/Sources
AccountIntermediateState.swiftAccountStateManagementUtils.swiftApiGroupOrChannel.swiftApplyMaxReadIndexInteractively.swiftApplyUpdateMessage.swiftAuthorization.swiftCancelAccountReset.swiftChangeAccountPhoneNumber.swiftChatListFiltering.swiftDeleteMessages.swiftEnqueueMessage.swiftGlobalNotificationSettings.swiftGroupCalls.swiftInvitationLinks.swiftManagedAutoremoveMessageOperations.swiftManagedGlobalNotificationSettings.swiftManagedLocalizationUpdatesOperations.swiftMarkMessageContentAsConsumedInteractively.swiftSearchMessages.swiftSerialization.swiftStoreMessage_Telegram.swiftTelegramMediaAction.swiftTelegramPeerNotificationSettings.swiftUpdateCachedPeerData.swiftUpdateMessageService.swiftVerifySecureIdValue.swiftWallets.swift
TelegramNotices/Sources
TelegramPresentationData/Sources
TelegramStringFormatting/Sources
TelegramUI
4
.bazelrc
4
.bazelrc
@ -14,4 +14,6 @@ build --features=swift.enable_vfsoverlays
|
||||
|
||||
build --strategy=Genrule=standalone
|
||||
build --spawn_strategy=standalone
|
||||
build --strategy=SwiftCompile=standalone
|
||||
|
||||
build --strategy=SwiftCompile=standalone
|
||||
build --define RULES_SWIFT_BUILD_DUMMY_WORKER=1
|
6
.gitmodules
vendored
6
.gitmodules
vendored
@ -17,12 +17,12 @@ url=https://github.com/ali-fareed/rules_swift.git
|
||||
[submodule "build-system/tulsi"]
|
||||
path = build-system/tulsi
|
||||
url=https://github.com/bazelbuild/tulsi.git
|
||||
[submodule "third-party/webrtc/webrtc-ios"]
|
||||
path = third-party/webrtc/webrtc-ios
|
||||
url=https://github.com/ali-fareed/webrtc-ios.git
|
||||
[submodule "submodules/TgVoipWebrtc/tgcalls"]
|
||||
path = submodules/TgVoipWebrtc/tgcalls
|
||||
url=../tgcalls.git
|
||||
[submodule "third-party/libvpx/libvpx"]
|
||||
path = third-party/libvpx/libvpx
|
||||
url = https://github.com/webmproject/libvpx.git
|
||||
[submodule "third-party/webrtc/webrtc"]
|
||||
path = third-party/webrtc/webrtc
|
||||
url = https://github.com/ali-fareed/webrtc.git
|
||||
|
10
BUILD
Normal file
10
BUILD
Normal file
@ -0,0 +1,10 @@
|
||||
|
||||
load("@build_bazel_rules_apple//apple:resources.bzl",
|
||||
"swift_intent_library",
|
||||
)
|
||||
|
||||
swift_intent_library(
|
||||
name = "GeneratedSources",
|
||||
src = "Intents.intentdefinition",
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
@ -75,66 +75,6 @@
|
||||
<string>SelectFriends</string>
|
||||
<key>INIntentParameters</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>INIntentParameterConfigurable</key>
|
||||
<true/>
|
||||
<key>INIntentParameterDisplayName</key>
|
||||
<string>Contents</string>
|
||||
<key>INIntentParameterDisplayNameID</key>
|
||||
<string>WAiyZm</string>
|
||||
<key>INIntentParameterDisplayPriority</key>
|
||||
<integer>1</integer>
|
||||
<key>INIntentParameterEnumType</key>
|
||||
<string>Contents</string>
|
||||
<key>INIntentParameterEnumTypeNamespace</key>
|
||||
<string>p74MWb</string>
|
||||
<key>INIntentParameterMetadata</key>
|
||||
<dict>
|
||||
<key>INIntentParameterMetadataDefaultValue</key>
|
||||
<string>recent</string>
|
||||
</dict>
|
||||
<key>INIntentParameterName</key>
|
||||
<string>contents</string>
|
||||
<key>INIntentParameterPromptDialogs</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>INIntentParameterPromptDialogCustom</key>
|
||||
<true/>
|
||||
<key>INIntentParameterPromptDialogType</key>
|
||||
<string>Configuration</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>INIntentParameterPromptDialogCustom</key>
|
||||
<true/>
|
||||
<key>INIntentParameterPromptDialogType</key>
|
||||
<string>Primary</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>INIntentParameterPromptDialogCustom</key>
|
||||
<true/>
|
||||
<key>INIntentParameterPromptDialogFormatString</key>
|
||||
<string>There are ${count} options matching ‘${contents}’.</string>
|
||||
<key>INIntentParameterPromptDialogFormatStringID</key>
|
||||
<string>oSRWBb</string>
|
||||
<key>INIntentParameterPromptDialogType</key>
|
||||
<string>DisambiguationIntroduction</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>INIntentParameterPromptDialogCustom</key>
|
||||
<true/>
|
||||
<key>INIntentParameterPromptDialogFormatString</key>
|
||||
<string>Just to confirm, you wanted ‘${contents}’?</string>
|
||||
<key>INIntentParameterPromptDialogFormatStringID</key>
|
||||
<string>jvYJCG</string>
|
||||
<key>INIntentParameterPromptDialogType</key>
|
||||
<string>Confirmation</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>INIntentParameterTag</key>
|
||||
<integer>5</integer>
|
||||
<key>INIntentParameterType</key>
|
||||
<string>Integer</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>INIntentParameterArraySizes</key>
|
||||
<array>
|
||||
@ -146,7 +86,7 @@
|
||||
</dict>
|
||||
<dict>
|
||||
<key>INIntentParameterArraySizeSize</key>
|
||||
<integer>8</integer>
|
||||
<integer>2</integer>
|
||||
<key>INIntentParameterArraySizeSizeClass</key>
|
||||
<string>Medium</string>
|
||||
</dict>
|
||||
@ -160,11 +100,11 @@
|
||||
<key>INIntentParameterConfigurable</key>
|
||||
<true/>
|
||||
<key>INIntentParameterDisplayName</key>
|
||||
<string>Chats</string>
|
||||
<string> </string>
|
||||
<key>INIntentParameterDisplayNameID</key>
|
||||
<string>WIf4LD</string>
|
||||
<key>INIntentParameterDisplayPriority</key>
|
||||
<integer>2</integer>
|
||||
<integer>1</integer>
|
||||
<key>INIntentParameterFixedSizeArray</key>
|
||||
<integer>1</integer>
|
||||
<key>INIntentParameterName</key>
|
||||
@ -178,6 +118,10 @@
|
||||
<dict>
|
||||
<key>INIntentParameterPromptDialogCustom</key>
|
||||
<true/>
|
||||
<key>INIntentParameterPromptDialogFormatString</key>
|
||||
<string>Search</string>
|
||||
<key>INIntentParameterPromptDialogFormatStringID</key>
|
||||
<string>ORCbLf</string>
|
||||
<key>INIntentParameterPromptDialogType</key>
|
||||
<string>Configuration</string>
|
||||
</dict>
|
||||
@ -190,8 +134,6 @@
|
||||
</array>
|
||||
<key>INIntentParameterRelationship</key>
|
||||
<dict>
|
||||
<key>INIntentParameterRelationshipParentName</key>
|
||||
<string>contents</string>
|
||||
<key>INIntentParameterRelationshipPredicateName</key>
|
||||
<string>EnumHasExactValue</string>
|
||||
<key>INIntentParameterRelationshipPredicateValue</key>
|
||||
@ -201,6 +143,8 @@
|
||||
<true/>
|
||||
<key>INIntentParameterSupportsMultipleValues</key>
|
||||
<true/>
|
||||
<key>INIntentParameterSupportsSearch</key>
|
||||
<true/>
|
||||
<key>INIntentParameterTag</key>
|
||||
<integer>19</integer>
|
||||
<key>INIntentParameterType</key>
|
@ -8,6 +8,10 @@ load("@build_bazel_rules_apple//apple:ios.bzl",
|
||||
"ios_framework",
|
||||
)
|
||||
|
||||
load("@build_bazel_rules_apple//apple:resources.bzl",
|
||||
"swift_intent_library",
|
||||
)
|
||||
|
||||
load("@build_bazel_rules_apple//apple:watchos.bzl",
|
||||
"watchos_application",
|
||||
"watchos_extension",
|
||||
@ -1158,7 +1162,7 @@ plist_fragment(
|
||||
)
|
||||
|
||||
swift_library(
|
||||
name = "GeneratedSources",
|
||||
name = "GeneratedSources1",
|
||||
module_name = "GeneratedSources",
|
||||
srcs = glob([
|
||||
"Generated/**/*.swift",
|
||||
@ -1166,6 +1170,12 @@ swift_library(
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
'''swift_intent_library(
|
||||
name = "GeneratedSources",
|
||||
src = "SiriIntents/Intents.intentdefinition",
|
||||
visibility = ["//visibility:public"],
|
||||
)'''
|
||||
|
||||
swift_library(
|
||||
name = "WidgetExtensionLib",
|
||||
module_name = "WidgetExtensionLib",
|
||||
@ -1173,7 +1183,7 @@ swift_library(
|
||||
"WidgetKitWidget/**/*.swift",
|
||||
]),
|
||||
data = [
|
||||
"SiriIntents/Intents.intentdefinition",
|
||||
#"SiriIntents/Intents.intentdefinition",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/BuildConfig:BuildConfig",
|
||||
@ -1185,7 +1195,7 @@ swift_library(
|
||||
"//submodules/TelegramCore:TelegramCore",
|
||||
"//submodules/SyncCore:SyncCore",
|
||||
"//submodules/OpenSSLEncryptionProvider:OpenSSLEncryptionProvider",
|
||||
":GeneratedSources",
|
||||
"//:GeneratedSources",
|
||||
],
|
||||
)
|
||||
|
||||
@ -1267,7 +1277,7 @@ swift_library(
|
||||
"SiriIntents/**/*.swift",
|
||||
]),
|
||||
data = [
|
||||
"SiriIntents/Intents.intentdefinition",
|
||||
#"SiriIntents/Intents.intentdefinition",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||
@ -1278,7 +1288,7 @@ swift_library(
|
||||
"//submodules/BuildConfig:BuildConfig",
|
||||
"//submodules/OpenSSLEncryptionProvider:OpenSSLEncryptionProvider",
|
||||
"//submodules/AppLockState:AppLockState",
|
||||
":GeneratedSources",
|
||||
"//:GeneratedSources",
|
||||
],
|
||||
)
|
||||
|
||||
@ -1578,7 +1588,7 @@ ios_application(
|
||||
":NotificationContentExtension",
|
||||
":NotificationServiceExtension",
|
||||
":IntentsExtension",
|
||||
# ":WidgetExtension",
|
||||
":WidgetExtension",
|
||||
],
|
||||
}),
|
||||
watch_application = select({
|
||||
|
@ -1,36 +0,0 @@
|
||||
//
|
||||
// Contents.swift
|
||||
//
|
||||
// This file was automatically generated and should not be edited.
|
||||
//
|
||||
|
||||
#if canImport(Intents)
|
||||
|
||||
import Intents
|
||||
|
||||
@available(iOS 12.0, macOS 10.16, watchOS 5.0, *) @available(tvOS, unavailable)
|
||||
@objc public enum Contents: Int {
|
||||
case `unknown` = 0
|
||||
case `recent` = 1
|
||||
case `custom` = 2
|
||||
}
|
||||
|
||||
@available(iOS 13.0, macOS 10.16, watchOS 6.0, *) @available(tvOS, unavailable)
|
||||
@objc(ContentsResolutionResult)
|
||||
public class ContentsResolutionResult: INEnumResolutionResult {
|
||||
|
||||
// This resolution result is for when the app extension wants to tell Siri to proceed, with a given Contents. The resolvedValue can be different than the original Contents. This allows app extensions to apply business logic constraints.
|
||||
// Use notRequired() to continue with a 'nil' value.
|
||||
@objc(successWithResolvedContents:)
|
||||
public class func success(with resolvedValue: Contents) -> Self {
|
||||
return __success(withResolvedValue: resolvedValue.rawValue)
|
||||
}
|
||||
|
||||
// This resolution result is to ask Siri to confirm if this is the value with which the user wants to continue.
|
||||
@objc(confirmationRequiredWithContentsToConfirm:)
|
||||
public class func confirmationRequired(with valueToConfirm: Contents) -> Self {
|
||||
return __confirmationRequiredWithValue(toConfirm: valueToConfirm.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -1,60 +0,0 @@
|
||||
//
|
||||
// Friend.swift
|
||||
//
|
||||
// This file was automatically generated and should not be edited.
|
||||
//
|
||||
|
||||
#if canImport(Intents)
|
||||
|
||||
import Intents
|
||||
|
||||
@available(iOS 12.0, macOS 10.16, watchOS 5.0, *) @available(tvOS, unavailable)
|
||||
@objc(Friend)
|
||||
public class Friend: INObject {
|
||||
|
||||
@available(iOS 13.0, macOS 10.16, watchOS 6.0, *)
|
||||
@NSManaged public var subtitle: String?
|
||||
|
||||
}
|
||||
|
||||
@available(iOS 13.0, macOS 10.16, watchOS 6.0, *) @available(tvOS, unavailable)
|
||||
@objc(FriendResolutionResult)
|
||||
public class FriendResolutionResult: INObjectResolutionResult {
|
||||
|
||||
// This resolution result is for when the app extension wants to tell Siri to proceed, with a given Friend. The resolvedValue can be different than the original Friend. This allows app extensions to apply business logic constraints.
|
||||
// Use notRequired() to continue with a 'nil' value.
|
||||
@objc(successWithResolvedFriend:)
|
||||
public class func success(with resolvedObject: Friend) -> Self {
|
||||
return super.success(with: resolvedObject) as! Self
|
||||
}
|
||||
|
||||
// This resolution result is to ask Siri to disambiguate between the provided Friend.
|
||||
@objc(disambiguationWithFriendsToDisambiguate:)
|
||||
public class func disambiguation(with objectsToDisambiguate: [Friend]) -> Self {
|
||||
return super.disambiguation(with: objectsToDisambiguate) as! Self
|
||||
}
|
||||
|
||||
// This resolution result is to ask Siri to confirm if this is the value with which the user wants to continue.
|
||||
@objc(confirmationRequiredWithFriendToConfirm:)
|
||||
public class func confirmationRequired(with objectToConfirm: Friend?) -> Self {
|
||||
return super.confirmationRequired(with: objectToConfirm) as! Self
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
override public class func success(with resolvedObject: INObject) -> Self {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
override public class func disambiguation(with objectsToDisambiguate: [INObject]) -> Self {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
override public class func confirmationRequired(with objectToConfirm: INObject?) -> Self {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,120 +0,0 @@
|
||||
//
|
||||
// SelectFriendsIntent.swift
|
||||
//
|
||||
// This file was automatically generated and should not be edited.
|
||||
//
|
||||
|
||||
#if canImport(Intents)
|
||||
|
||||
import Intents
|
||||
|
||||
@available(iOS 12.0, macOS 10.16, watchOS 5.0, *) @available(tvOS, unavailable)
|
||||
@objc(SelectFriendsIntent)
|
||||
public class SelectFriendsIntent: INIntent {
|
||||
|
||||
@NSManaged public var contents: Contents
|
||||
@NSManaged public var friends: [Friend]?
|
||||
|
||||
}
|
||||
|
||||
/*!
|
||||
@abstract Protocol to declare support for handling a SelectFriendsIntent. By implementing this protocol, a class can provide logic for resolving, confirming and handling the intent.
|
||||
@discussion The minimum requirement for an implementing class is that it should be able to handle the intent. The confirmation method is optional. The handling method is always called last, after confirming the intent.
|
||||
*/
|
||||
@available(iOS 12.0, macOS 10.16, watchOS 5.0, *) @available(tvOS, unavailable)
|
||||
@objc(SelectFriendsIntentHandling)
|
||||
public protocol SelectFriendsIntentHandling: NSObjectProtocol {
|
||||
|
||||
/*!
|
||||
@abstract Dynamic options methods - provide options for the parameter at runtime
|
||||
@discussion Called to query dynamic options for the parameter and this intent in its current form.
|
||||
|
||||
@param intent The input intent
|
||||
@param completion The response block contains options for the parameter
|
||||
*/
|
||||
@available(iOS 14.0, macOS 10.16, watchOS 7.0, *)
|
||||
@objc(provideFriendsOptionsCollectionForSelectFriends:withCompletion:)
|
||||
func provideFriendsOptionsCollection(for intent: SelectFriendsIntent, with completion: @escaping (INObjectCollection<Friend>?, Error?) -> Swift.Void)
|
||||
|
||||
/*!
|
||||
@abstract Confirmation method - Validate that this intent is ready for the next step (i.e. handling)
|
||||
@discussion Called prior to asking the app to handle the intent. The app should return a response object that contains additional information about the intent, which may be relevant for the system to show the user prior to handling. If unimplemented, the system will assume the intent is valid, and will assume there is no additional information relevant to this intent.
|
||||
|
||||
@param intent The input intent
|
||||
@param completion The response block contains a SelectFriendsIntentResponse containing additional details about the intent that may be relevant for the system to show the user prior to handling.
|
||||
|
||||
@see SelectFriendsIntentResponse
|
||||
*/
|
||||
@objc(confirmSelectFriends:completion:)
|
||||
optional func confirm(intent: SelectFriendsIntent, completion: @escaping (SelectFriendsIntentResponse) -> Swift.Void)
|
||||
|
||||
/*!
|
||||
@abstract Handling method - Execute the task represented by the SelectFriendsIntent that's passed in
|
||||
@discussion Called to actually execute the intent. The app must return a response for this intent.
|
||||
|
||||
@param intent The input intent
|
||||
@param completion The response handling block takes a SelectFriendsIntentResponse containing the details of the result of having executed the intent
|
||||
|
||||
@see SelectFriendsIntentResponse
|
||||
*/
|
||||
@objc(handleSelectFriends:completion:)
|
||||
optional func handle(intent: SelectFriendsIntent, completion: @escaping (SelectFriendsIntentResponse) -> Swift.Void)
|
||||
|
||||
/*!
|
||||
@abstract Default values for parameters with dynamic options
|
||||
@discussion Called to query the parameter default value.
|
||||
*/
|
||||
@available(iOS 14.0, macOS 10.16, watchOS 7.0, *)
|
||||
@objc(defaultFriendsForSelectFriends:)
|
||||
optional func defaultFriends(for intent: SelectFriendsIntent) -> [Friend]?
|
||||
|
||||
/*!
|
||||
@abstract Deprecated dynamic options methods.
|
||||
*/
|
||||
@available(iOS, introduced: 13.0, deprecated: 14.0, message: "")
|
||||
@available(watchOS, introduced: 6.0, deprecated: 7.0, message: "")
|
||||
@objc(provideFriendsOptionsForSelectFriends:withCompletion:)
|
||||
optional func provideFriendsOptions(for intent: SelectFriendsIntent, with completion: @escaping ([Friend]?, Error?) -> Swift.Void)
|
||||
|
||||
}
|
||||
|
||||
/*!
|
||||
@abstract Constants indicating the state of the response.
|
||||
*/
|
||||
@available(iOS 12.0, macOS 10.16, watchOS 5.0, *) @available(tvOS, unavailable)
|
||||
@objc public enum SelectFriendsIntentResponseCode: Int {
|
||||
case unspecified = 0
|
||||
case ready
|
||||
case continueInApp
|
||||
case inProgress
|
||||
case success
|
||||
case failure
|
||||
case failureRequiringAppLaunch
|
||||
}
|
||||
|
||||
@available(iOS 12.0, macOS 10.16, watchOS 5.0, *) @available(tvOS, unavailable)
|
||||
@objc(SelectFriendsIntentResponse)
|
||||
public class SelectFriendsIntentResponse: INIntentResponse {
|
||||
|
||||
/*!
|
||||
@abstract The response code indicating your success or failure in confirming or handling the intent.
|
||||
*/
|
||||
@objc public fileprivate(set) var code: SelectFriendsIntentResponseCode = .unspecified
|
||||
|
||||
/*!
|
||||
@abstract Initializes the response object with the specified code and user activity object.
|
||||
@discussion The app extension has the option of capturing its private state as an NSUserActivity and returning it as the 'currentActivity'. If the app is launched, an NSUserActivity will be passed in with the private state. The NSUserActivity may also be used to query the app's UI extension (if provided) for a view controller representing the current intent handling state. In the case of app launch, the NSUserActivity will have its activityType set to the name of the intent. This intent object will also be available in the NSUserActivity.interaction property.
|
||||
|
||||
@param code The response code indicating your success or failure in confirming or handling the intent.
|
||||
@param userActivity The user activity object to use when launching your app. Provide an object if you want to add information that is specific to your app. If you specify nil, the system automatically creates a user activity object for you, sets its type to the class name of the intent being handled, and fills it with an INInteraction object containing the intent and your response.
|
||||
*/
|
||||
@objc(initWithCode:userActivity:)
|
||||
public convenience init(code: SelectFriendsIntentResponseCode, userActivity: NSUserActivity?) {
|
||||
self.init()
|
||||
self.code = code
|
||||
self.userActivity = userActivity
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -56,10 +56,15 @@ enum IntentHandlingError {
|
||||
@objc(IntentHandler)
|
||||
class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessagesIntentHandling, INSetMessageAttributeIntentHandling, INStartAudioCallIntentHandling, INSearchCallHistoryIntentHandling, SelectFriendsIntentHandling {
|
||||
private let accountPromise = Promise<Account?>()
|
||||
private let allAccounts = Promise<[(AccountRecordId, PeerId)]>()
|
||||
|
||||
private let resolvePersonsDisposable = MetaDisposable()
|
||||
private let actionDisposable = MetaDisposable()
|
||||
private let searchDisposable = MetaDisposable()
|
||||
|
||||
private var rootPath: String?
|
||||
private var accountManager: AccountManager?
|
||||
private var encryptionParameters: ValueBoxEncryptionParameters?
|
||||
private var appGroupUrl: URL?
|
||||
|
||||
override init() {
|
||||
@ -88,6 +93,8 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
|
||||
let rootPath = rootPathForBasePath(appGroupUrl.path)
|
||||
performAppGroupUpgrades(appGroupPath: appGroupUrl.path, rootPath: rootPath)
|
||||
|
||||
self.rootPath = rootPath
|
||||
|
||||
TempBox.initializeShared(basePath: rootPath, processType: "siri", launchSpecificId: arc4random64())
|
||||
|
||||
let logsPath = rootPath + "/siri-logs"
|
||||
@ -97,16 +104,61 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
|
||||
|
||||
let appVersion = (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? "unknown"
|
||||
|
||||
initializeAccountManagement()
|
||||
let accountManager = AccountManager(basePath: rootPath + "/accounts-metadata")
|
||||
self.accountManager = accountManager
|
||||
|
||||
let deviceSpecificEncryptionParameters = BuildConfig.deviceSpecificEncryptionParameters(rootPath, baseAppBundleId: baseAppBundleId)
|
||||
let encryptionParameters = ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: deviceSpecificEncryptionParameters.key)!, salt: ValueBoxEncryptionParameters.Salt(data: deviceSpecificEncryptionParameters.salt)!)
|
||||
self.encryptionParameters = encryptionParameters
|
||||
|
||||
self.allAccounts.set(accountManager.accountRecords()
|
||||
|> take(1)
|
||||
|> map { view -> [(AccountRecordId, PeerId)] in
|
||||
var result: [(AccountRecordId, Int, PeerId)] = []
|
||||
for record in view.records {
|
||||
let isLoggedOut = record.attributes.contains(where: { attribute in
|
||||
return attribute is LoggedOutAccountAttribute
|
||||
})
|
||||
if isLoggedOut {
|
||||
continue
|
||||
}
|
||||
/*let isTestingEnvironment = record.attributes.contains(where: { attribute in
|
||||
if let attribute = attribute as? AccountEnvironmentAttribute, case .test = attribute.environment {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})*/
|
||||
var backupData: AccountBackupData?
|
||||
var sortIndex: Int32 = 0
|
||||
for attribute in record.attributes {
|
||||
if let attribute = attribute as? AccountSortOrderAttribute {
|
||||
sortIndex = attribute.order
|
||||
} else if let attribute = attribute as? AccountBackupDataAttribute {
|
||||
backupData = attribute.data
|
||||
}
|
||||
}
|
||||
if let backupData = backupData {
|
||||
result.append((record.id, Int(sortIndex), PeerId(backupData.peerId)))
|
||||
}
|
||||
}
|
||||
result.sort(by: { lhs, rhs in
|
||||
if lhs.1 != rhs.1 {
|
||||
return lhs.1 < rhs.1
|
||||
} else {
|
||||
return lhs.0 < rhs.0
|
||||
}
|
||||
})
|
||||
return result.map { record -> (AccountRecordId, PeerId) in
|
||||
return (record.0, record.2)
|
||||
}
|
||||
})
|
||||
|
||||
let account: Signal<Account?, NoError>
|
||||
if let accountCache = accountCache {
|
||||
account = .single(accountCache)
|
||||
} else {
|
||||
initializeAccountManagement()
|
||||
let accountManager = AccountManager(basePath: rootPath + "/accounts-metadata")
|
||||
|
||||
let deviceSpecificEncryptionParameters = BuildConfig.deviceSpecificEncryptionParameters(rootPath, baseAppBundleId: baseAppBundleId)
|
||||
let encryptionParameters = ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: deviceSpecificEncryptionParameters.key)!, salt: ValueBoxEncryptionParameters.Salt(data: deviceSpecificEncryptionParameters.salt)!)
|
||||
|
||||
account = currentAccount(allocateIfNotExists: false, networkArguments: NetworkInitializationArguments(apiId: apiId, apiHash: apiHash, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0, voipVersions: [], appData: .single(buildConfig.bundleData(withAppToken: nil, signatureDict: nil)), autolockDeadine: .single(nil), encryptionProvider: OpenSSLEncryptionProvider()), supplementary: true, manager: accountManager, rootPath: rootPath, auxiliaryMethods: accountAuxiliaryMethods, encryptionParameters: encryptionParameters)
|
||||
|> mapToSignal { account -> Signal<Account?, NoError> in
|
||||
if let account = account {
|
||||
@ -139,6 +191,7 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
|
||||
deinit {
|
||||
self.resolvePersonsDisposable.dispose()
|
||||
self.actionDisposable.dispose()
|
||||
self.searchDisposable.dispose()
|
||||
}
|
||||
|
||||
override public func handler(for intent: INIntent) -> Any {
|
||||
@ -701,45 +754,181 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
|
||||
}
|
||||
|
||||
@available(iOSApplicationExtension 14.0, iOS 14.0, *)
|
||||
func provideFriendsOptionsCollection(for intent: SelectFriendsIntent, with completion: @escaping (INObjectCollection<Friend>?, Error?) -> Void) {
|
||||
let _ = (self.accountPromise.get()
|
||||
func provideFriendsOptionsCollection(for intent: SelectFriendsIntent, searchTerm: String?, with completion: @escaping (INObjectCollection<Friend>?, Error?) -> Void) {
|
||||
guard let rootPath = self.rootPath, let _ = self.accountManager, let encryptionParameters = self.encryptionParameters else {
|
||||
completion(nil, nil)
|
||||
return
|
||||
}
|
||||
|
||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: appLockStatePath(rootPath: rootPath))), let state = try? JSONDecoder().decode(LockState.self, from: data) {
|
||||
if state.isManuallyLocked || state.autolockTimeout != nil {
|
||||
let error = NSError(domain: "Locked", code: 1, userInfo: [
|
||||
NSLocalizedDescriptionKey: "Open Telegram and enter passcode to edit widget."
|
||||
])
|
||||
|
||||
completion(nil, error)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
self.searchDisposable.set((self.allAccounts.get()
|
||||
|> castError(Error.self)
|
||||
|> take(1)
|
||||
|> mapToSignal { accounts -> Signal<INObjectCollection<Friend>, Error> in
|
||||
var accountResults: [Signal<INObjectSection<Friend>, Error>] = []
|
||||
|
||||
for (accountId, accountPeerId) in accounts {
|
||||
accountResults.append(accountTransaction(rootPath: rootPath, id: accountId, encryptionParameters: encryptionParameters, transaction: { postbox, transaction -> INObjectSection<Friend> in
|
||||
var accountTitle: String = ""
|
||||
if let peer = transaction.getPeer(accountPeerId) as? TelegramUser {
|
||||
if let username = peer.username, !username.isEmpty {
|
||||
accountTitle = "@\(username)"
|
||||
} else {
|
||||
accountTitle = peer.debugDisplayTitle
|
||||
}
|
||||
}
|
||||
|
||||
var peers: [Peer] = []
|
||||
|
||||
if let searchTerm = searchTerm {
|
||||
if !searchTerm.isEmpty {
|
||||
for renderedPeer in transaction.searchPeers(query: searchTerm) {
|
||||
if let peer = renderedPeer.peer, !(peer is TelegramSecretChat) {
|
||||
peers.append(peer)
|
||||
}
|
||||
}
|
||||
|
||||
if peers.count > 30 {
|
||||
peers = Array(peers.dropLast(peers.count - 30))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for renderedPeer in transaction.getTopChatListEntries(groupId: .root, count: 50) {
|
||||
if let peer = renderedPeer.peer, !(peer is TelegramSecretChat) {
|
||||
peers.append(peer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var items: [Friend] = []
|
||||
for peer in peers {
|
||||
let profileImage = smallestImageRepresentation(peer.profileImageRepresentations).flatMap { representation in
|
||||
return postbox.mediaBox.resourcePath(representation.resource)
|
||||
}.flatMap { path -> INImage? in
|
||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: path)) {
|
||||
return INImage(imageData: data)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
items.append(Friend(identifier: "\(accountId.int64):\(peer.id.toInt64())", display: peer.debugDisplayTitle, subtitle: nil, image: nil))
|
||||
}
|
||||
|
||||
return INObjectSection<Friend>(title: accountTitle, items: items)
|
||||
})
|
||||
|> castError(Error.self))
|
||||
}
|
||||
|
||||
return combineLatest(accountResults)
|
||||
|> map { accountResults -> INObjectCollection<Friend> in
|
||||
let filteredSections = accountResults.filter { section in
|
||||
return !section.items.isEmpty
|
||||
}
|
||||
if filteredSections.count == 1 {
|
||||
return INObjectCollection<Friend>(items: filteredSections[0].items)
|
||||
} else {
|
||||
return INObjectCollection<Friend>(sections: filteredSections)
|
||||
}
|
||||
}
|
||||
}).start(next: { result in
|
||||
completion(result, nil)
|
||||
}, error: { error in
|
||||
completion(nil, error)
|
||||
}))
|
||||
|
||||
/*let _ = (self.accountPromise.get()
|
||||
|> take(1)
|
||||
|> mapToSignal { account -> Signal<[Friend], NoError> in
|
||||
guard let account = account else {
|
||||
return .single([])
|
||||
}
|
||||
return account.postbox.transaction { transaction -> [Friend] in
|
||||
var peers: [Peer] = []
|
||||
|
||||
outer: for peerId in transaction.getContactPeerIds() {
|
||||
if let peer = transaction.getPeer(peerId) as? TelegramUser {
|
||||
peers.append(peer)
|
||||
|
||||
if let searchTerm = searchTerm {
|
||||
if !searchTerm.isEmpty {
|
||||
return account.postbox.searchPeers(query: searchTerm)
|
||||
|> map { renderedPeers -> [Friend] in
|
||||
var peers: [Peer] = []
|
||||
|
||||
for renderedPeer in renderedPeers {
|
||||
if let peer = renderedPeer.peer, !(peer is TelegramSecretChat) {
|
||||
peers.append(peer)
|
||||
}
|
||||
}
|
||||
|
||||
peers.sort(by: { lhs, rhs in
|
||||
return lhs.debugDisplayTitle < rhs.debugDisplayTitle
|
||||
})
|
||||
|
||||
if peers.count > 30 {
|
||||
peers = Array(peers.dropLast(peers.count - 30))
|
||||
}
|
||||
|
||||
var result: [Friend] = []
|
||||
for peer in peers {
|
||||
let profileImage = smallestImageRepresentation(peer.profileImageRepresentations).flatMap { representation in
|
||||
return account.postbox.mediaBox.resourcePath(representation.resource)
|
||||
}.flatMap { path -> INImage? in
|
||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: path)) {
|
||||
return INImage(imageData: data)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
result.append(Friend(identifier: "\(peer.id.toInt64())", display: peer.debugDisplayTitle, subtitle: nil, image: nil))
|
||||
}
|
||||
return result
|
||||
}
|
||||
} else {
|
||||
return .single([])
|
||||
}
|
||||
|
||||
peers.sort(by: { lhs, rhs in
|
||||
return lhs.debugDisplayTitle < rhs.debugDisplayTitle
|
||||
})
|
||||
|
||||
var result: [Friend] = []
|
||||
for peer in peers {
|
||||
let profileImage = smallestImageRepresentation(peer.profileImageRepresentations).flatMap { representation in
|
||||
return account.postbox.mediaBox.resourcePath(representation.resource)
|
||||
}.flatMap { path -> INImage? in
|
||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: path)) {
|
||||
return INImage(imageData: data)
|
||||
} else {
|
||||
return nil
|
||||
} else {
|
||||
return account.postbox.transaction { transaction -> [Friend] in
|
||||
var peers: [Peer] = []
|
||||
|
||||
for peerId in transaction.getContactPeerIds() {
|
||||
if let peer = transaction.getPeer(peerId) as? TelegramUser {
|
||||
peers.append(peer)
|
||||
}
|
||||
}
|
||||
result.append(Friend(identifier: "\(peer.id.toInt64())", display: peer.debugDisplayTitle, subtitle: nil, image: nil))
|
||||
|
||||
peers.sort(by: { lhs, rhs in
|
||||
return lhs.debugDisplayTitle < rhs.debugDisplayTitle
|
||||
})
|
||||
|
||||
if peers.count > 50 {
|
||||
peers = Array(peers.dropLast(peers.count - 50))
|
||||
}
|
||||
|
||||
var result: [Friend] = []
|
||||
for peer in peers {
|
||||
let profileImage = smallestImageRepresentation(peer.profileImageRepresentations).flatMap { representation in
|
||||
return account.postbox.mediaBox.resourcePath(representation.resource)
|
||||
}.flatMap { path -> INImage? in
|
||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: path)) {
|
||||
return INImage(imageData: data)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
result.append(Friend(identifier: "\(peer.id.toInt64())", display: peer.debugDisplayTitle, subtitle: nil, image: nil))
|
||||
}
|
||||
return result
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { result in
|
||||
let collection = INObjectCollection(items: result)
|
||||
completion(collection, nil)
|
||||
})
|
||||
})*/
|
||||
}
|
||||
}
|
||||
|
@ -2187,7 +2187,7 @@ Unused sets are archived when you add more.";
|
||||
"Widget.AuthRequired" = "Log in to Telegram";
|
||||
"Widget.NoUsers" = "Start messaging to see your friends here";
|
||||
"Widget.GalleryTitle" = "Telegram";
|
||||
"Widget.GalleryDescription" = "See your friends here";
|
||||
"Widget.GalleryDescription" = "Select chats";
|
||||
|
||||
"ShareMenu.CopyShareLinkGame" = "Copy link to game";
|
||||
|
||||
@ -6056,3 +6056,13 @@ Sorry for the inconvenience.";
|
||||
"Report.Report" = "Report";
|
||||
"Report.Succeed" = "Telegram moderators will study your report. Thank you!";
|
||||
|
||||
|
||||
"Conversation.AutoremoveRemainingTime" = "auto-delete in %@";
|
||||
"Conversation.AutoremoveRemainingDays_1" = "auto-delete in %@ day";
|
||||
"Conversation.AutoremoveRemainingDays_any" = "auto-delete in %@ days";
|
||||
|
||||
"PeerInfo.AutoremoveMessages" = "Auto-Delete Messages";
|
||||
"PeerInfo.AutoremoveMessagesDisabled" = "Never";
|
||||
|
||||
"Conversation.AutoremoveChanged" = "Auto-Delete timer set to %@";
|
||||
|
||||
|
@ -94,11 +94,11 @@ private func avatarViewLettersImage(size: CGSize, peerId: Int64, accountPeerId:
|
||||
|
||||
private let avatarSize = CGSize(width: 50.0, height: 50.0)
|
||||
|
||||
func avatarImage(accountPeerId: Int64, peer: WidgetDataPeer, size: CGSize) -> UIImage {
|
||||
if let path = peer.avatarPath, let image = UIImage(contentsOfFile: path), let roundImage = avatarRoundImage(size: size, source: image) {
|
||||
func avatarImage(accountPeerId: Int64?, peer: WidgetDataPeer?, size: CGSize) -> UIImage {
|
||||
if let path = peer?.avatarPath, let image = UIImage(contentsOfFile: path), let roundImage = avatarRoundImage(size: size, source: image) {
|
||||
return roundImage
|
||||
} else {
|
||||
return avatarViewLettersImage(size: size, peerId: peer.id, accountPeerId: accountPeerId, letters: peer.letters)!
|
||||
return avatarViewLettersImage(size: size, peerId: peer?.id ?? 1, accountPeerId: accountPeerId ?? 1, letters: peer?.letters ?? [" "])!
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,29 @@ import WidgetItemsUtils
|
||||
|
||||
import GeneratedSources
|
||||
|
||||
struct ParsedPeer {
|
||||
var accountId: Int64
|
||||
var accountPeerId: Int64
|
||||
var peer: WidgetDataPeer
|
||||
}
|
||||
|
||||
struct ParsedPeers {
|
||||
var peers: [ParsedPeer]
|
||||
var updateTimestamp: Int32
|
||||
}
|
||||
|
||||
private extension ParsedPeers {
|
||||
init(accountId: Int64, peers: WidgetDataPeers) {
|
||||
self.init(peers: peers.peers.map { peer -> ParsedPeer in
|
||||
return ParsedPeer(
|
||||
accountId: accountId,
|
||||
accountPeerId: peers.accountPeerId,
|
||||
peer: peer
|
||||
)
|
||||
}, updateTimestamp: peers.updateTimestamp)
|
||||
}
|
||||
}
|
||||
|
||||
private var installedSharedLogger = false
|
||||
|
||||
private func setupSharedLogger(rootPath: String, path: String) {
|
||||
@ -64,14 +87,7 @@ struct Provider: IntentTimelineProvider {
|
||||
}
|
||||
|
||||
func getSnapshot(for configuration: SelectFriendsIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) {
|
||||
let contents: SimpleEntry.Contents
|
||||
switch configuration.contents {
|
||||
case .unknown, .recent:
|
||||
contents = .recent
|
||||
case .custom:
|
||||
contents = .recent
|
||||
}
|
||||
let entry = SimpleEntry(date: Date(), contents: contents)
|
||||
let entry = SimpleEntry(date: Date(), contents: .peers(ParsedPeers(accountId: 0, peers: WidgetDataPeers(accountPeerId: 0, peers: [], updateTimestamp: 0))))
|
||||
completion(entry)
|
||||
}
|
||||
|
||||
@ -79,111 +95,209 @@ struct Provider: IntentTimelineProvider {
|
||||
let currentDate = Date()
|
||||
let entryDate = Calendar.current.date(byAdding: .hour, value: 0, to: currentDate)!
|
||||
|
||||
switch configuration.contents {
|
||||
case .unknown, .recent:
|
||||
guard let appBundleIdentifier = Bundle.main.bundleIdentifier, let lastDotRange = appBundleIdentifier.range(of: ".", options: [.backwards]) else {
|
||||
completion(Timeline(entries: [SimpleEntry(date: entryDate, contents: .recent)], policy: .atEnd))
|
||||
case .custom:
|
||||
guard let appBundleIdentifier = Bundle.main.bundleIdentifier, let lastDotRange = appBundleIdentifier.range(of: ".", options: [.backwards]) else {
|
||||
completion(Timeline(entries: [SimpleEntry(date: entryDate, contents: .recent)], policy: .atEnd))
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
let baseAppBundleId = String(appBundleIdentifier[..<lastDotRange.lowerBound])
|
||||
|
||||
let appGroupName = "group.\(baseAppBundleId)"
|
||||
let maybeAppGroupUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupName)
|
||||
|
||||
guard let appGroupUrl = maybeAppGroupUrl else {
|
||||
completion(Timeline(entries: [SimpleEntry(date: entryDate, contents: .recent)], policy: .atEnd))
|
||||
return
|
||||
}
|
||||
|
||||
let rootPath = rootPathForBasePath(appGroupUrl.path)
|
||||
|
||||
let dataPath = rootPath + "/widget-data"
|
||||
|
||||
guard let data = try? Data(contentsOf: URL(fileURLWithPath: dataPath)), let widgetData = try? JSONDecoder().decode(WidgetData.self, from: data), case let .peers(widgetPeers) = widgetData.content else {
|
||||
completion(Timeline(entries: [SimpleEntry(date: entryDate, contents: .recent)], policy: .atEnd))
|
||||
return
|
||||
}
|
||||
|
||||
TempBox.initializeShared(basePath: rootPath, processType: "widget", launchSpecificId: arc4random64())
|
||||
|
||||
let logsPath = rootPath + "/widget-logs"
|
||||
let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil)
|
||||
|
||||
setupSharedLogger(rootPath: rootPath, path: logsPath)
|
||||
|
||||
initializeAccountManagement()
|
||||
|
||||
let deviceSpecificEncryptionParameters = BuildConfig.deviceSpecificEncryptionParameters(rootPath, baseAppBundleId: baseAppBundleId)
|
||||
let encryptionParameters = ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: deviceSpecificEncryptionParameters.key)!, salt: ValueBoxEncryptionParameters.Salt(data: deviceSpecificEncryptionParameters.salt)!)
|
||||
|
||||
var itemsByAccount: [Int64: [(Int64, Friend)]] = [:]
|
||||
var itemOrder: [(Int64, Int64)] = []
|
||||
if let friends = configuration.friends {
|
||||
for item in friends {
|
||||
guard let identifier = item.identifier else {
|
||||
continue
|
||||
}
|
||||
guard let index = identifier.firstIndex(of: ":") else {
|
||||
continue
|
||||
}
|
||||
guard let accountId = Int64(identifier[identifier.startIndex ..< index]) else {
|
||||
continue
|
||||
}
|
||||
guard let peerId = Int64(identifier[identifier.index(after: index)...]) else {
|
||||
continue
|
||||
}
|
||||
if itemsByAccount[accountId] == nil {
|
||||
itemsByAccount[accountId] = []
|
||||
}
|
||||
itemsByAccount[accountId]?.append((peerId, item))
|
||||
itemOrder.append((accountId, peerId))
|
||||
}
|
||||
|
||||
let baseAppBundleId = String(appBundleIdentifier[..<lastDotRange.lowerBound])
|
||||
|
||||
let appGroupName = "group.\(baseAppBundleId)"
|
||||
let maybeAppGroupUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupName)
|
||||
|
||||
guard let appGroupUrl = maybeAppGroupUrl else {
|
||||
completion(Timeline(entries: [SimpleEntry(date: entryDate, contents: .recent)], policy: .atEnd))
|
||||
return
|
||||
}
|
||||
|
||||
let rootPath = rootPathForBasePath(appGroupUrl.path)
|
||||
|
||||
let dataPath = rootPath + "/widget-data"
|
||||
|
||||
guard let data = try? Data(contentsOf: URL(fileURLWithPath: dataPath)), let widgetData = try? JSONDecoder().decode(WidgetData.self, from: data), case let .peers(widgetPeers) = widgetData.content else {
|
||||
completion(Timeline(entries: [SimpleEntry(date: entryDate, contents: .recent)], policy: .atEnd))
|
||||
return
|
||||
}
|
||||
|
||||
TempBox.initializeShared(basePath: rootPath, processType: "widget", launchSpecificId: arc4random64())
|
||||
|
||||
let logsPath = rootPath + "/widget-logs"
|
||||
let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil)
|
||||
|
||||
setupSharedLogger(rootPath: rootPath, path: logsPath)
|
||||
|
||||
initializeAccountManagement()
|
||||
|
||||
let deviceSpecificEncryptionParameters = BuildConfig.deviceSpecificEncryptionParameters(rootPath, baseAppBundleId: baseAppBundleId)
|
||||
let encryptionParameters = ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: deviceSpecificEncryptionParameters.key)!, salt: ValueBoxEncryptionParameters.Salt(data: deviceSpecificEncryptionParameters.salt)!)
|
||||
|
||||
let _ = (accountTransaction(rootPath: rootPath, id: AccountRecordId(rawValue: widgetData.accountId), encryptionParameters: encryptionParameters, transaction: { postbox, transaction -> WidgetDataPeers in
|
||||
var peers: [WidgetDataPeer] = []
|
||||
if let items = configuration.friends {
|
||||
for item in items {
|
||||
guard let identifier = item.identifier, let peerIdValue = Int64(identifier) else {
|
||||
continue
|
||||
}
|
||||
|
||||
var friendsByAccount: [Signal<[ParsedPeer], NoError>] = []
|
||||
for (accountId, items) in itemsByAccount {
|
||||
friendsByAccount.append(accountTransaction(rootPath: rootPath, id: AccountRecordId(rawValue: accountId), encryptionParameters: encryptionParameters, transaction: { postbox, transaction -> [ParsedPeer] in
|
||||
guard let state = transaction.getState() as? AuthorizedAccountState else {
|
||||
return []
|
||||
}
|
||||
|
||||
var result: [ParsedPeer] = []
|
||||
|
||||
for (peerId, _) in items {
|
||||
guard let peer = transaction.getPeer(PeerId(peerId)) else {
|
||||
continue
|
||||
}
|
||||
|
||||
var name: String = ""
|
||||
var lastName: String?
|
||||
|
||||
if let user = peer as? TelegramUser {
|
||||
if let firstName = user.firstName {
|
||||
name = firstName
|
||||
lastName = user.lastName
|
||||
} else if let lastName = user.lastName {
|
||||
name = lastName
|
||||
} else if let phone = user.phone, !phone.isEmpty {
|
||||
name = phone
|
||||
}
|
||||
guard let peer = transaction.getPeer(PeerId(peerIdValue)) else {
|
||||
continue
|
||||
} else {
|
||||
name = peer.debugDisplayTitle
|
||||
}
|
||||
|
||||
var badge: WidgetDataPeer.Badge?
|
||||
|
||||
if let readState = transaction.getCombinedPeerReadState(peer.id), readState.count > 0 {
|
||||
var isMuted = false
|
||||
if let notificationSettings = transaction.getPeerNotificationSettings(peer.id) as? TelegramPeerNotificationSettings {
|
||||
isMuted = notificationSettings.isRemovedFromTotalUnreadCount(default: false)
|
||||
}
|
||||
|
||||
var name: String = ""
|
||||
var lastName: String?
|
||||
|
||||
if let user = peer as? TelegramUser {
|
||||
if let firstName = user.firstName {
|
||||
name = firstName
|
||||
lastName = user.lastName
|
||||
} else if let lastName = user.lastName {
|
||||
name = lastName
|
||||
} else if let phone = user.phone, !phone.isEmpty {
|
||||
name = phone
|
||||
}
|
||||
} else {
|
||||
name = peer.debugDisplayTitle
|
||||
badge = WidgetDataPeer.Badge(
|
||||
count: Int(readState.count),
|
||||
isMuted: isMuted
|
||||
)
|
||||
}
|
||||
|
||||
var mappedMessage: WidgetDataPeer.Message?
|
||||
if let index = transaction.getTopPeerMessageIndex(peerId: peer.id) {
|
||||
if let message = transaction.getMessage(index.id) {
|
||||
mappedMessage = WidgetDataPeer.Message(message: message)
|
||||
}
|
||||
|
||||
var badge: WidgetDataPeer.Badge?
|
||||
|
||||
if let readState = transaction.getCombinedPeerReadState(peer.id), readState.count > 0 {
|
||||
var isMuted = false
|
||||
if let notificationSettings = transaction.getPeerNotificationSettings(peer.id) as? TelegramPeerNotificationSettings {
|
||||
isMuted = notificationSettings.isRemovedFromTotalUnreadCount(default: false)
|
||||
}
|
||||
badge = WidgetDataPeer.Badge(
|
||||
count: Int(readState.count),
|
||||
isMuted: isMuted
|
||||
)
|
||||
}
|
||||
|
||||
let widgetPeer = WidgetDataPeer(id: peer.id.toInt64(), name: name, lastName: lastName, letters: peer.displayLetters, avatarPath: smallestImageRepresentation(peer.profileImageRepresentations).flatMap { representation in
|
||||
return postbox.mediaBox.resourcePath(representation.resource)
|
||||
}, badge: badge, message: mappedMessage)
|
||||
|
||||
result.append(ParsedPeer(accountId: accountId, accountPeerId: state.peerId.toInt64(), peer: widgetPeer))
|
||||
}
|
||||
|
||||
return result
|
||||
}))
|
||||
}
|
||||
|
||||
let _ = combineLatest(friendsByAccount).start(next: { allPeers in
|
||||
var orderedPeers: [ParsedPeer] = []
|
||||
|
||||
outer: for (accountId, peerId) in itemOrder {
|
||||
for peerSet in allPeers {
|
||||
for peer in peerSet {
|
||||
if peer.accountId == accountId && peer.peer.id == peerId {
|
||||
orderedPeers.append(peer)
|
||||
continue outer
|
||||
}
|
||||
|
||||
var mappedMessage: WidgetDataPeer.Message?
|
||||
if let index = transaction.getTopPeerMessageIndex(peerId: peer.id) {
|
||||
if let message = transaction.getMessage(index.id) {
|
||||
mappedMessage = WidgetDataPeer.Message(message: message)
|
||||
}
|
||||
}
|
||||
|
||||
peers.append(WidgetDataPeer(id: peer.id.toInt64(), name: name, lastName: lastName, letters: peer.displayLetters, avatarPath: smallestImageRepresentation(peer.profileImageRepresentations).flatMap { representation in
|
||||
return postbox.mediaBox.resourcePath(representation.resource)
|
||||
}, badge: badge, message: mappedMessage))
|
||||
}
|
||||
}
|
||||
return WidgetDataPeers(accountPeerId: widgetPeers.accountPeerId, peers: peers)
|
||||
})
|
||||
|> deliverOnMainQueue).start(next: { peers in
|
||||
completion(Timeline(entries: [SimpleEntry(date: entryDate, contents: .peers(peers))], policy: .atEnd))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
let result = ParsedPeers(peers: orderedPeers, updateTimestamp: Int32(Date().timeIntervalSince1970))
|
||||
completion(Timeline(entries: [SimpleEntry(date: entryDate, contents: .peers(result))], policy: .atEnd))
|
||||
})
|
||||
|
||||
/*let _ = (accountTransaction(rootPath: rootPath, id: AccountRecordId(rawValue: widgetData.accountId), encryptionParameters: encryptionParameters, transaction: { postbox, transaction -> ParsedPeers in
|
||||
var peers: [ParsedPeer] = []
|
||||
if let items = configuration.friends {
|
||||
for item in items {
|
||||
guard let identifier = item.identifier, let peerIdValue = Int64(identifier) else {
|
||||
continue
|
||||
}
|
||||
guard let peer = transaction.getPeer(PeerId(peerIdValue)) else {
|
||||
continue
|
||||
}
|
||||
|
||||
var name: String = ""
|
||||
var lastName: String?
|
||||
|
||||
if let user = peer as? TelegramUser {
|
||||
if let firstName = user.firstName {
|
||||
name = firstName
|
||||
lastName = user.lastName
|
||||
} else if let lastName = user.lastName {
|
||||
name = lastName
|
||||
} else if let phone = user.phone, !phone.isEmpty {
|
||||
name = phone
|
||||
}
|
||||
} else {
|
||||
name = peer.debugDisplayTitle
|
||||
}
|
||||
|
||||
var badge: WidgetDataPeer.Badge?
|
||||
|
||||
if let readState = transaction.getCombinedPeerReadState(peer.id), readState.count > 0 {
|
||||
var isMuted = false
|
||||
if let notificationSettings = transaction.getPeerNotificationSettings(peer.id) as? TelegramPeerNotificationSettings {
|
||||
isMuted = notificationSettings.isRemovedFromTotalUnreadCount(default: false)
|
||||
}
|
||||
badge = WidgetDataPeer.Badge(
|
||||
count: Int(readState.count),
|
||||
isMuted: isMuted
|
||||
)
|
||||
}
|
||||
|
||||
var mappedMessage: WidgetDataPeer.Message?
|
||||
if let index = transaction.getTopPeerMessageIndex(peerId: peer.id) {
|
||||
if let message = transaction.getMessage(index.id) {
|
||||
mappedMessage = WidgetDataPeer.Message(message: message)
|
||||
}
|
||||
}
|
||||
|
||||
peers.append(WidgetDataPeer(id: peer.id.toInt64(), name: name, lastName: lastName, letters: peer.displayLetters, avatarPath: smallestImageRepresentation(peer.profileImageRepresentations).flatMap { representation in
|
||||
return postbox.mediaBox.resourcePath(representation.resource)
|
||||
}, badge: badge, message: mappedMessage))
|
||||
}
|
||||
}
|
||||
return ParsedPeers(peers: peers, updateTimestamp: Int32(Date().timeIntervalSince1970))
|
||||
})
|
||||
|> deliverOnMainQueue).start(next: { peers in
|
||||
completion(Timeline(entries: [SimpleEntry(date: entryDate, contents: .peers(peers))], policy: .atEnd))
|
||||
})*/
|
||||
}
|
||||
}
|
||||
|
||||
struct SimpleEntry: TimelineEntry {
|
||||
enum Contents {
|
||||
case recent
|
||||
case peers(WidgetDataPeers)
|
||||
case peers(ParsedPeers)
|
||||
}
|
||||
|
||||
let date: Date
|
||||
@ -191,27 +305,32 @@ struct SimpleEntry: TimelineEntry {
|
||||
}
|
||||
|
||||
enum PeersWidgetData {
|
||||
case placeholder
|
||||
case empty
|
||||
case locked
|
||||
case peers(WidgetDataPeers)
|
||||
case peers(ParsedPeers)
|
||||
}
|
||||
|
||||
extension PeersWidgetData {
|
||||
static let previewData = PeersWidgetData.placeholder
|
||||
static let previewData = PeersWidgetData.empty
|
||||
}
|
||||
|
||||
struct AvatarItemView: View {
|
||||
var accountPeerId: Int64
|
||||
var peer: WidgetDataPeer
|
||||
var peer: ParsedPeer?
|
||||
var itemSize: CGFloat
|
||||
var displayBadge: Bool = true
|
||||
|
||||
var body: some View {
|
||||
return ZStack {
|
||||
Image(uiImage: avatarImage(accountPeerId: accountPeerId, peer: peer, size: CGSize(width: itemSize, height: itemSize)))
|
||||
if let peer = peer {
|
||||
Image(uiImage: avatarImage(accountPeerId: peer.accountPeerId, peer: peer.peer, size: CGSize(width: itemSize, height: itemSize)))
|
||||
.clipShape(Circle())
|
||||
if displayBadge, let badge = peer.badge, badge.count > 0 {
|
||||
} else {
|
||||
Image(uiImage: avatarImage(accountPeerId: nil, peer: nil, size: CGSize(width: itemSize, height: itemSize)))
|
||||
//Rectangle()
|
||||
.frame(width: itemSize, height: itemSize)
|
||||
.clipShape(Circle())
|
||||
.redacted(reason: .placeholder)
|
||||
}
|
||||
/*if let peer = peer, displayBadge, let badge = peer.badge, badge.count > 0 {
|
||||
Text("\(badge.count)")
|
||||
.font(Font.system(size: 16.0))
|
||||
.multilineTextAlignment(.center)
|
||||
@ -223,7 +342,7 @@ struct AvatarItemView: View {
|
||||
.frame(minWidth: 20, idealWidth: 20, maxWidth: .infinity, minHeight: 20, idealHeight: 20, maxHeight: 20.0, alignment: .center)
|
||||
)
|
||||
.position(x: floor(0.84 * itemSize), y: floor(0.16 * itemSize))
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -259,7 +378,7 @@ struct WidgetView: View {
|
||||
}
|
||||
}
|
||||
|
||||
func peersView(geometry: GeometryProxy, peers: WidgetDataPeers) -> some View {
|
||||
func peersView(geometry: GeometryProxy, peers: ParsedPeers) -> some View {
|
||||
let columnCount: Int
|
||||
let rowCount: Int
|
||||
|
||||
@ -308,9 +427,8 @@ struct WidgetView: View {
|
||||
|
||||
return ZStack {
|
||||
ForEach(0 ..< min(peers.peers.count, columnCount * rowCount), content: { i in
|
||||
Link(destination: URL(string: linkForPeer(id: peers.peers[i].id))!, label: {
|
||||
Link(destination: URL(string: linkForPeer(id: peers.peers[i].peer.id))!, label: {
|
||||
AvatarItemView(
|
||||
accountPeerId: peers.accountPeerId,
|
||||
peer: peers.peers[i],
|
||||
itemSize: itemSize
|
||||
).frame(width: itemSize, height: itemSize)
|
||||
@ -322,18 +440,10 @@ struct WidgetView: View {
|
||||
|
||||
func peerViews() -> AnyView {
|
||||
switch data {
|
||||
case .placeholder:
|
||||
case .empty:
|
||||
return AnyView(GeometryReader { geometry in
|
||||
placeholder(geometry: geometry)
|
||||
})
|
||||
case .empty:
|
||||
return AnyView(VStack {
|
||||
Text(presentationData.applicationStartRequiredString)
|
||||
})
|
||||
case .locked:
|
||||
return AnyView(VStack {
|
||||
Text(presentationData.applicationLockedString)
|
||||
})
|
||||
case let .peers(peers):
|
||||
return AnyView(GeometryReader { geometry in
|
||||
peersView(geometry: geometry, peers: peers)
|
||||
@ -348,23 +458,50 @@ struct WidgetView: View {
|
||||
.padding(0.0)
|
||||
}
|
||||
|
||||
func chatTopLine(_ peer: WidgetDataPeer) -> some View {
|
||||
func chatTopLine(_ peer: ParsedPeer?) -> some View {
|
||||
let dateText: String
|
||||
if let message = peer.message {
|
||||
dateText = DateFormatter.localizedString(from: Date(timeIntervalSince1970: Double(message.timestamp)), dateStyle: .none, timeStyle: .short)
|
||||
|
||||
let chatTitle: Text
|
||||
let date: Text
|
||||
|
||||
if let peer = peer {
|
||||
if let message = peer.peer.message {
|
||||
dateText = DateFormatter.localizedString(from: Date(timeIntervalSince1970: Double(message.timestamp)), dateStyle: .none, timeStyle: .short)
|
||||
} else {
|
||||
dateText = ""
|
||||
}
|
||||
chatTitle = Text(peer.peer.name).font(Font.system(size: 16.0, weight: .medium, design: .default)).foregroundColor(.primary)
|
||||
date = Text(dateText)
|
||||
.font(Font.system(size: 14.0, weight: .regular, design: .default)).foregroundColor(.secondary)
|
||||
} else {
|
||||
dateText = ""
|
||||
dateText = "10:00"
|
||||
chatTitle = Text("Chat Title").font(Font.system(size: 16.0, weight: .medium, design: .default)).foregroundColor(.primary)
|
||||
date = Text(dateText)
|
||||
.font(Font.system(size: 14.0, weight: .regular, design: .default)).foregroundColor(.secondary)
|
||||
}
|
||||
return HStack(alignment: .center, spacing: 0.0, content: {
|
||||
Text(peer.name).font(Font.system(size: 16.0, weight: .medium, design: .default)).foregroundColor(.primary)
|
||||
if peer != nil {
|
||||
chatTitle
|
||||
} else {
|
||||
chatTitle.redacted(reason: .placeholder)
|
||||
}
|
||||
Spacer()
|
||||
Text(dateText).font(Font.system(size: 14.0, weight: .regular, design: .default)).foregroundColor(.secondary)
|
||||
if peer != nil {
|
||||
date
|
||||
} else {
|
||||
date.redacted(reason: .placeholder)
|
||||
}
|
||||
})
|
||||
.padding(0.0)
|
||||
}
|
||||
|
||||
func chatBottomLine(_ peer: WidgetDataPeer) -> some View {
|
||||
var text = peer.message?.text ?? ""
|
||||
if let message = peer.message {
|
||||
func chatBottomLine(_ peer: ParsedPeer?) -> AnyView {
|
||||
var text = peer?.peer.message?.text ?? ""
|
||||
text += "\n"
|
||||
if peer == nil {
|
||||
text = "First Line Of Text Here\nSecond line fwqefeqwfqwef qwef wq"
|
||||
}
|
||||
if let message = peer?.peer.message {
|
||||
//TODO:localize
|
||||
switch message.content {
|
||||
case .text:
|
||||
@ -406,17 +543,47 @@ struct WidgetView: View {
|
||||
case let .poll(poll):
|
||||
text = "📊 \(poll.title)"
|
||||
}
|
||||
|
||||
if let author = message.author {
|
||||
if author.isMe {
|
||||
text = "You: \(text)"
|
||||
} else {
|
||||
text = "\(author.title): \(text)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var hasBadge = false
|
||||
if let badge = peer.badge, badge.count > 0 {
|
||||
if let peer = peer, let badge = peer.peer.badge, badge.count > 0 {
|
||||
hasBadge = true
|
||||
}
|
||||
|
||||
return HStack(alignment: .center, spacing: hasBadge ? 6.0 : 0.0, content: {
|
||||
Text(text).lineLimit(nil).font(Font.system(size: 15.0, weight: .regular, design: .default)).foregroundColor(.secondary).multilineTextAlignment(.leading).frame(maxHeight: .infinity, alignment: .topLeading)
|
||||
Spacer()
|
||||
if let badge = peer.badge, badge.count > 0 {
|
||||
let textView = Text(text)
|
||||
.lineLimit(2)
|
||||
.font(Font.system(size: 15.0, weight: .regular, design: .default))
|
||||
.foregroundColor(.secondary)
|
||||
.multilineTextAlignment(.leading)
|
||||
.padding(0.0)
|
||||
//.frame(maxHeight: .infinity, alignment: .topLeading)
|
||||
//.background(Rectangle().foregroundColor(.gray))
|
||||
|
||||
if peer != nil {
|
||||
return AnyView(textView)
|
||||
} else {
|
||||
return AnyView(
|
||||
textView
|
||||
.redacted(reason: .placeholder)
|
||||
)
|
||||
}
|
||||
|
||||
/*return HStack(alignment: .center, spacing: hasBadge ? 6.0 : 0.0, content: {
|
||||
if peer != nil {
|
||||
textView
|
||||
} else {
|
||||
textView.redacted(reason: .placeholder)
|
||||
}
|
||||
//Spacer()
|
||||
/*if let peer = peer, let badge = peer.badge, badge.count > 0 {
|
||||
VStack {
|
||||
Spacer()
|
||||
Text("\(badge.count)")
|
||||
@ -431,39 +598,116 @@ struct WidgetView: View {
|
||||
)
|
||||
.padding(EdgeInsets(top: 0.0, leading: 0.0, bottom: 6.0, trailing: 3.0))
|
||||
}
|
||||
}
|
||||
})
|
||||
}*/
|
||||
})*/
|
||||
}
|
||||
|
||||
func chatContent(_ peer: WidgetDataPeer) -> some View {
|
||||
return VStack(alignment: .leading, spacing: 2.0, content: {
|
||||
func chatContent(_ peer: ParsedPeer?) -> some View {
|
||||
return VStack(alignment: .leading, spacing: 0.0, content: {
|
||||
chatTopLine(peer)
|
||||
chatBottomLine(peer).frame(maxHeight: .infinity)
|
||||
chatBottomLine(peer)
|
||||
})
|
||||
}
|
||||
|
||||
func chatContentView(_ index: Int) -> AnyView {
|
||||
let peers: WidgetDataPeers
|
||||
func chatContentView(_ index: Int, size: CGSize) -> AnyView {
|
||||
let peers: ParsedPeers?
|
||||
switch data {
|
||||
case let .peers(peersValue):
|
||||
peers = peersValue
|
||||
if peers.peers.count <= index {
|
||||
return AnyView(Spacer())
|
||||
if peersValue.peers.count <= index {
|
||||
peers = nil
|
||||
} else {
|
||||
peers = peersValue
|
||||
}
|
||||
default:
|
||||
return AnyView(Spacer())
|
||||
peers = nil
|
||||
}
|
||||
|
||||
let itemHeight = (size.height - 22.0) / 2.0
|
||||
|
||||
let url: URL
|
||||
if let peers = peers {
|
||||
url = URL(string: linkForPeer(id: peers.peers[index].peer.id))!
|
||||
} else {
|
||||
url = URL(string: "\(buildConfig.appSpecificUrlScheme)://")!
|
||||
}
|
||||
|
||||
return AnyView(
|
||||
Link(destination: URL(string: linkForPeer(id: peers.peers[index].id))!, label: {
|
||||
Link(destination: url, label: {
|
||||
HStack(alignment: .center, spacing: 0.0, content: {
|
||||
AvatarItemView(accountPeerId: peers.accountPeerId, peer: peers.peers[index], itemSize: 60.0, displayBadge: false).frame(width: 60.0, height: 60.0, alignment: .leading).padding(EdgeInsets(top: 0.0, leading: 10.0, bottom: 0.0, trailing: 10.0))
|
||||
chatContent(peers.peers[index]).frame(maxWidth: .infinity).padding(EdgeInsets(top: 10.0, leading: 0.0, bottom: 10.0, trailing: 10.0))
|
||||
AvatarItemView(peer: peers?.peers[index], itemSize: 54.0, displayBadge: false).frame(width: 54.0, height: 54.0, alignment: .leading).padding(EdgeInsets(top: 0.0, leading: 10.0, bottom: 0.0, trailing: 10.0))
|
||||
chatContent(peers?.peers[index]).frame(maxWidth: .infinity).padding(EdgeInsets(top: 0.0, leading: 0.0, bottom: 0.0, trailing: 10.0))
|
||||
})
|
||||
})
|
||||
.frame(width: size.width, height: itemHeight, alignment: .leading)
|
||||
)
|
||||
}
|
||||
|
||||
func chatSeparatorView(size: CGSize) -> some View {
|
||||
return HStack(alignment: .center, spacing: 0.0, content: {
|
||||
Spacer()
|
||||
Rectangle()
|
||||
.foregroundColor(getSeparatorColor())
|
||||
.frame(width: size.width - 54.0 - 20.0, height: 0.5, alignment: .leading)
|
||||
})
|
||||
.frame(width: size.width, height: 1.0, alignment: .leading)
|
||||
/*let separatorWidth = size.width - 54.0 - 20.0
|
||||
let itemHeight = (size.height - 22.0) / 2.0
|
||||
return Rectangle().foregroundColor(getSeparatorColor())
|
||||
//.position(x: (54.0 + 20.0 + separatorWidth) / 2.0, y: itemHeight / 2.0)
|
||||
.frame(width: size.width, height: 1.0, alignment: .leading)*/
|
||||
}
|
||||
|
||||
func chatsUpdateBackgroundView(size: CGSize) -> some View {
|
||||
return Rectangle().foregroundColor(getUpdatedBackgroundColor())
|
||||
//.position(x: size.width / 2.0, y: size.height - 11.0)
|
||||
.frame(width: size.width, height: 22.0, alignment: .center)
|
||||
}
|
||||
|
||||
func chatUpdateView(size: CGSize) -> some View {
|
||||
return ZStack(alignment: Alignment(horizontal: .center, vertical: .center), content: {
|
||||
Rectangle().foregroundColor(getUpdatedBackgroundColor())
|
||||
chatsUpdateTimestampView(size: size)
|
||||
})
|
||||
.frame(width: size.width, height: 22.0 - 1.0, alignment: .center)
|
||||
}
|
||||
|
||||
func chatsUpdateTimestampView(size: CGSize) -> some View {
|
||||
let text: String
|
||||
switch data {
|
||||
case let .peers(peersValue):
|
||||
if peersValue.peers.isEmpty {
|
||||
text = "Long tap to edit widget"
|
||||
} else {
|
||||
let date = Date(timeIntervalSince1970: Double(peersValue.updateTimestamp))
|
||||
let calendar = Calendar.current
|
||||
//TODO:localize
|
||||
if !calendar.isDate(Date(), inSameDayAs: date) {
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateStyle = .short
|
||||
formatter.timeStyle = .none
|
||||
text = "updated on \(formatter.string(from: date))"
|
||||
} else {
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateStyle = .none
|
||||
formatter.timeStyle = .short
|
||||
text = "updated at \(formatter.string(from: date))"
|
||||
}
|
||||
}
|
||||
default:
|
||||
text = ""
|
||||
}
|
||||
|
||||
return Text(text)
|
||||
.font(Font.system(size: 12.0))
|
||||
.foregroundColor(getUpdatedTextColor())
|
||||
|
||||
/*return HStack(alignment: .center, spacing: 0.0, content: {
|
||||
Text(text)
|
||||
.font(Font.system(size: 12.0))
|
||||
.foregroundColor(getUpdatedTextColor())
|
||||
}).position(x: size.width / 2.0, y: size.height - 11.0).frame(width: size.width, height: 22.0, alignment: .leading)*/
|
||||
}
|
||||
|
||||
func getSeparatorColor() -> Color {
|
||||
switch colorScheme {
|
||||
case .light:
|
||||
@ -475,13 +719,36 @@ struct WidgetView: View {
|
||||
}
|
||||
}
|
||||
|
||||
func getUpdatedBackgroundColor() -> Color {
|
||||
switch colorScheme {
|
||||
case .light:
|
||||
return Color(.sRGB, red: 242.0 / 255.0, green: 242.0 / 255.0, blue: 247.0 / 255.0, opacity: 1.0)
|
||||
case .dark:
|
||||
return Color(.sRGB, red: 21.0 / 255.0, green: 21.0 / 255.0, blue: 21.0 / 255.0, opacity: 1.0)
|
||||
@unknown default:
|
||||
return .secondary
|
||||
}
|
||||
}
|
||||
|
||||
func getUpdatedTextColor() -> Color {
|
||||
switch colorScheme {
|
||||
case .light:
|
||||
return Color(.sRGB, red: 142.0 / 255.0, green: 142.0 / 255.0, blue: 146.0 / 255.0, opacity: 1.0)
|
||||
case .dark:
|
||||
return Color(.sRGB, red: 142.0 / 255.0, green: 142.0 / 255.0, blue: 146.0 / 255.0, opacity: 1.0)
|
||||
@unknown default:
|
||||
return .secondary
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
GeometryReader(content: { geometry in
|
||||
ZStack {
|
||||
chatContentView(0).position(x: geometry.size.width / 2.0, y: geometry.size.height / 4.0).frame(width: geometry.size.width, height: geometry.size.height / 2.0, alignment: .leading)
|
||||
chatContentView(1).position(x: geometry.size.width / 2.0, y: geometry.size.height / 2.0 + geometry.size.height / 4.0).frame(width: geometry.size.width, height: geometry.size.height / 2.0, alignment: .leading)
|
||||
Rectangle().foregroundColor(getSeparatorColor()).position(x: geometry.size.width / 2.0, y: geometry.size.height / 4.0).frame(width: geometry.size.width, height: 0.33, alignment: .leading)
|
||||
}
|
||||
return VStack(alignment: .center, spacing: 0.0, content: {
|
||||
chatContentView(0, size: geometry.size)
|
||||
chatSeparatorView(size: geometry.size)
|
||||
chatContentView(1, size: geometry.size)
|
||||
chatUpdateView(size: geometry.size)
|
||||
})
|
||||
})
|
||||
.padding(0.0)
|
||||
}
|
||||
@ -535,7 +802,7 @@ func getWidgetData(contents: SimpleEntry.Contents) -> PeersWidgetData {
|
||||
case .recent:
|
||||
let appBundleIdentifier = Bundle.main.bundleIdentifier!
|
||||
guard let lastDotRange = appBundleIdentifier.range(of: ".", options: [.backwards]) else {
|
||||
return .placeholder
|
||||
return .empty
|
||||
}
|
||||
let baseAppBundleId = String(appBundleIdentifier[..<lastDotRange.lowerBound])
|
||||
|
||||
@ -543,28 +810,28 @@ func getWidgetData(contents: SimpleEntry.Contents) -> PeersWidgetData {
|
||||
let maybeAppGroupUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupName)
|
||||
|
||||
guard let appGroupUrl = maybeAppGroupUrl else {
|
||||
return .placeholder
|
||||
return .empty
|
||||
}
|
||||
|
||||
let rootPath = rootPathForBasePath(appGroupUrl.path)
|
||||
|
||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: appLockStatePath(rootPath: rootPath))), let state = try? JSONDecoder().decode(LockState.self, from: data), isAppLocked(state: state) {
|
||||
return .locked
|
||||
}
|
||||
/*if let data = try? Data(contentsOf: URL(fileURLWithPath: appLockStatePath(rootPath: rootPath))), let state = try? JSONDecoder().decode(LockState.self, from: data) {
|
||||
if state.isManuallyLocked || state.autolockTimeout != nil {
|
||||
return .empty
|
||||
}
|
||||
}*/
|
||||
|
||||
let dataPath = rootPath + "/widget-data"
|
||||
|
||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: dataPath)), let widgetData = try? JSONDecoder().decode(WidgetData.self, from: data) {
|
||||
switch widgetData.content {
|
||||
case let .peers(peers):
|
||||
return .peers(peers)
|
||||
case .disabled:
|
||||
return .placeholder
|
||||
case .notAuthorized:
|
||||
return .locked
|
||||
return .peers(ParsedPeers(accountId: widgetData.accountId, peers: peers))
|
||||
case .empty:
|
||||
return .empty
|
||||
}
|
||||
} else {
|
||||
return .placeholder
|
||||
return .empty
|
||||
}
|
||||
case let .peers(peers):
|
||||
return .peers(peers)
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit f7f2b6d7c952f3cf6bdcedce6a0a2a40a27ff596
|
||||
Subproject commit c1f83903e864d753477e51d66d3ada6c2c6d096f
|
@ -1 +1 @@
|
||||
Subproject commit e2b1e1e4399bd168a00d1c3125eaa3ae52835340
|
||||
Subproject commit fe541dc3d45a4389d8bf973f7a16803429c5e212
|
@ -1 +1 @@
|
||||
Subproject commit bfb54953cee1bc985ba8113dd2e635e1d294abdb
|
||||
Subproject commit e80b9795db5e35f86d643e0fba7776f1b1f71066
|
@ -96,6 +96,7 @@ public final class PresentationCallVideoView {
|
||||
case rotation270
|
||||
}
|
||||
|
||||
public let holder: AnyObject
|
||||
public let view: UIView
|
||||
public let setOnFirstFrameReceived: (((Float) -> Void)?) -> Void
|
||||
|
||||
@ -105,6 +106,7 @@ public final class PresentationCallVideoView {
|
||||
public let setOnIsMirroredUpdated: (((Bool) -> Void)?) -> Void
|
||||
|
||||
public init(
|
||||
holder: AnyObject,
|
||||
view: UIView,
|
||||
setOnFirstFrameReceived: @escaping (((Float) -> Void)?) -> Void,
|
||||
getOrientation: @escaping () -> Orientation,
|
||||
@ -112,6 +114,7 @@ public final class PresentationCallVideoView {
|
||||
setOnOrientationUpdated: @escaping (((Orientation, CGFloat) -> Void)?) -> Void,
|
||||
setOnIsMirroredUpdated: @escaping (((Bool) -> Void)?) -> Void
|
||||
) {
|
||||
self.holder = holder
|
||||
self.view = view
|
||||
self.setOnFirstFrameReceived = setOnFirstFrameReceived
|
||||
self.getOrientation = getOrientation
|
||||
@ -277,13 +280,15 @@ public protocol PresentationGroupCall: class {
|
||||
var internalId: CallSessionInternalId { get }
|
||||
var peerId: PeerId { get }
|
||||
|
||||
var isVideo: Bool { get }
|
||||
|
||||
var audioOutputState: Signal<([AudioSessionOutput], AudioSessionOutput?), NoError> { get }
|
||||
|
||||
var canBeRemoved: Signal<Bool, NoError> { get }
|
||||
var state: Signal<PresentationGroupCallState, NoError> { get }
|
||||
var summaryState: Signal<PresentationGroupCallSummaryState?, NoError> { get }
|
||||
var members: Signal<PresentationGroupCallMembers?, NoError> { get }
|
||||
var audioLevels: Signal<[(PeerId, Float, Bool)], NoError> { get }
|
||||
var audioLevels: Signal<[(PeerId, UInt32, Float, Bool)], NoError> { get }
|
||||
var myAudioLevel: Signal<Float, NoError> { get }
|
||||
var isMuted: Signal<Bool, NoError> { get }
|
||||
|
||||
@ -293,8 +298,11 @@ public protocol PresentationGroupCall: class {
|
||||
|
||||
func toggleIsMuted()
|
||||
func setIsMuted(action: PresentationGroupCallMuteAction)
|
||||
func requestVideo()
|
||||
func disableVideo()
|
||||
func updateDefaultParticipantsAreMuted(isMuted: Bool)
|
||||
func setVolume(peerId: PeerId, volume: Int32, sync: Bool)
|
||||
func setFullSizeVideo(peerId: PeerId?)
|
||||
func setCurrentAudioOutput(_ output: AudioSessionOutput)
|
||||
|
||||
func updateMuteState(peerId: PeerId, isMuted: Bool) -> GroupCallParticipantsContext.Participant.MuteState?
|
||||
@ -303,6 +311,10 @@ public protocol PresentationGroupCall: class {
|
||||
func removedPeer(_ peerId: PeerId)
|
||||
var invitedPeers: Signal<[PeerId], NoError> { get }
|
||||
|
||||
var incomingVideoSources: Signal<[PeerId: UInt32], NoError> { get }
|
||||
|
||||
func makeIncomingVideoView(source: UInt32, completion: @escaping (PresentationCallVideoView?) -> Void)
|
||||
|
||||
func loadMoreMembers(token: String)
|
||||
}
|
||||
|
||||
|
11
submodules/CameraUI/Sources/Camera.h
Normal file
11
submodules/CameraUI/Sources/Camera.h
Normal file
@ -0,0 +1,11 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
//! Project version number for Camera.
|
||||
FOUNDATION_EXPORT double CameraVersionNumber;
|
||||
|
||||
//! Project version string for Camera.
|
||||
FOUNDATION_EXPORT const unsigned char CameraVersionString[];
|
||||
|
||||
// In this header, you should import all the public headers of your framework using statements like #import <Camera/PublicHeader.h>
|
||||
|
||||
|
@ -13,6 +13,7 @@ public enum ChatListFilterSettingsHeaderAnimation {
|
||||
case folders
|
||||
case newFolder
|
||||
case discussionGroupSetup
|
||||
case autoRemove
|
||||
}
|
||||
|
||||
public class ChatListFilterSettingsHeaderItem: ListViewItem, ItemListItem {
|
||||
@ -106,7 +107,26 @@ class ChatListFilterSettingsHeaderItemNode: ListViewItemNode {
|
||||
|
||||
return { item, params, neighbors in
|
||||
let leftInset: CGFloat = 32.0 + params.leftInset
|
||||
let topInset: CGFloat = 92.0
|
||||
|
||||
let animationName: String
|
||||
var size = 192
|
||||
var insetDifference = 100
|
||||
var playbackMode: AnimatedStickerPlaybackMode = .once
|
||||
switch item.animation {
|
||||
case .folders:
|
||||
animationName = "ChatListFolders"
|
||||
case .newFolder:
|
||||
animationName = "ChatListNewFolder"
|
||||
case .discussionGroupSetup:
|
||||
animationName = "DiscussionGroupSetup"
|
||||
case .autoRemove:
|
||||
animationName = "MessageAutoRemove"
|
||||
size = 260
|
||||
insetDifference = 120
|
||||
playbackMode = .loop
|
||||
}
|
||||
|
||||
let topInset: CGFloat = CGFloat(size - insetDifference)
|
||||
|
||||
let attributedText = NSAttributedString(string: item.text, font: titleFont, textColor: item.theme.list.freeTextColor)
|
||||
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: attributedText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - params.rightInset - leftInset * 2.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||
@ -119,17 +139,8 @@ class ChatListFilterSettingsHeaderItemNode: ListViewItemNode {
|
||||
return (layout, { [weak self] in
|
||||
if let strongSelf = self {
|
||||
if strongSelf.item == nil {
|
||||
let animationName: String
|
||||
switch item.animation {
|
||||
case .folders:
|
||||
animationName = "ChatListFolders"
|
||||
case .newFolder:
|
||||
animationName = "ChatListNewFolder"
|
||||
case .discussionGroupSetup:
|
||||
animationName = "DiscussionGroupSetup"
|
||||
}
|
||||
if let path = getAppBundle().path(forResource: animationName, ofType: "tgs") {
|
||||
strongSelf.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 192, height: 192, playbackMode: .once, mode: .direct(cachePathPrefix: nil))
|
||||
strongSelf.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: size, height: size, playbackMode: playbackMode, mode: .direct(cachePathPrefix: nil))
|
||||
strongSelf.animationNode.visibility = true
|
||||
}
|
||||
}
|
||||
@ -137,7 +148,7 @@ class ChatListFilterSettingsHeaderItemNode: ListViewItemNode {
|
||||
strongSelf.item = item
|
||||
strongSelf.accessibilityLabel = attributedText.string
|
||||
|
||||
let iconSize = CGSize(width: 96.0, height: 96.0)
|
||||
let iconSize = CGSize(width: CGFloat(size) / 2.0, height: CGFloat(size) / 2.0)
|
||||
strongSelf.animationNode.frame = CGRect(origin: CGPoint(x: floor((layout.size.width - iconSize.width) / 2.0), y: -10.0), size: iconSize)
|
||||
strongSelf.animationNode.updateLayout(size: iconSize)
|
||||
|
||||
|
@ -84,17 +84,27 @@ private final class InnerActionsContainerNode: ASDisplayNode {
|
||||
switch items[i] {
|
||||
case let .action(action):
|
||||
itemNodes.append(.action(ContextActionNode(presentationData: presentationData, action: action, getController: getController, actionSelected: actionSelected)))
|
||||
if i != items.count - 1, case .action = items[i + 1] {
|
||||
let separatorNode = ASDisplayNode()
|
||||
separatorNode.backgroundColor = presentationData.theme.contextMenu.itemSeparatorColor
|
||||
itemNodes.append(.itemSeparator(separatorNode))
|
||||
if i != items.count - 1 {
|
||||
switch items[i + 1] {
|
||||
case .action, .custom:
|
||||
let separatorNode = ASDisplayNode()
|
||||
separatorNode.backgroundColor = presentationData.theme.contextMenu.itemSeparatorColor
|
||||
itemNodes.append(.itemSeparator(separatorNode))
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
case let .custom(item, _):
|
||||
itemNodes.append(.custom(item.node(presentationData: presentationData, getController: getController, actionSelected: actionSelected)))
|
||||
if i != items.count - 1, case .action = items[i + 1] {
|
||||
let separatorNode = ASDisplayNode()
|
||||
separatorNode.backgroundColor = presentationData.theme.contextMenu.itemSeparatorColor
|
||||
itemNodes.append(.itemSeparator(separatorNode))
|
||||
if i != items.count - 1 {
|
||||
switch items[i + 1] {
|
||||
case .action, .custom:
|
||||
let separatorNode = ASDisplayNode()
|
||||
separatorNode.backgroundColor = presentationData.theme.contextMenu.itemSeparatorColor
|
||||
itemNodes.append(.itemSeparator(separatorNode))
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
case .separator:
|
||||
let separatorNode = ASDisplayNode()
|
||||
|
@ -15,8 +15,9 @@ public final class DatePickerTheme: Equatable {
|
||||
public let selectionColor: UIColor
|
||||
public let selectionTextColor: UIColor
|
||||
public let separatorColor: UIColor
|
||||
public let segmentedControlTheme: SegmentedControlTheme
|
||||
|
||||
public init(backgroundColor: UIColor, textColor: UIColor, secondaryTextColor: UIColor, accentColor: UIColor, disabledColor: UIColor, selectionColor: UIColor, selectionTextColor: UIColor, separatorColor: UIColor) {
|
||||
public init(backgroundColor: UIColor, textColor: UIColor, secondaryTextColor: UIColor, accentColor: UIColor, disabledColor: UIColor, selectionColor: UIColor, selectionTextColor: UIColor, separatorColor: UIColor, segmentedControlTheme: SegmentedControlTheme) {
|
||||
self.backgroundColor = backgroundColor
|
||||
self.textColor = textColor
|
||||
self.secondaryTextColor = secondaryTextColor
|
||||
@ -25,6 +26,7 @@ public final class DatePickerTheme: Equatable {
|
||||
self.selectionColor = selectionColor
|
||||
self.selectionTextColor = selectionTextColor
|
||||
self.separatorColor = separatorColor
|
||||
self.segmentedControlTheme = segmentedControlTheme
|
||||
}
|
||||
|
||||
public static func ==(lhs: DatePickerTheme, rhs: DatePickerTheme) -> Bool {
|
||||
@ -55,7 +57,7 @@ public final class DatePickerTheme: Equatable {
|
||||
|
||||
public extension DatePickerTheme {
|
||||
convenience init(theme: PresentationTheme) {
|
||||
self.init(backgroundColor: theme.list.itemBlocksBackgroundColor, textColor: theme.list.itemPrimaryTextColor, secondaryTextColor: theme.list.itemSecondaryTextColor, accentColor: theme.list.itemAccentColor, disabledColor: theme.list.itemDisabledTextColor, selectionColor: theme.list.itemCheckColors.fillColor, selectionTextColor: theme.list.itemCheckColors.foregroundColor, separatorColor: theme.list.itemBlocksSeparatorColor)
|
||||
self.init(backgroundColor: theme.list.itemBlocksBackgroundColor, textColor: theme.list.itemPrimaryTextColor, secondaryTextColor: theme.list.itemSecondaryTextColor, accentColor: theme.list.itemAccentColor, disabledColor: theme.list.itemDisabledTextColor, selectionColor: theme.list.itemCheckColors.fillColor, selectionTextColor: theme.list.itemCheckColors.foregroundColor, separatorColor: theme.list.itemBlocksSeparatorColor, segmentedControlTheme: SegmentedControlTheme(theme: theme))
|
||||
}
|
||||
}
|
||||
|
||||
@ -278,7 +280,7 @@ public final class DatePickerNode: ASDisplayNode {
|
||||
private let strings: PresentationStrings
|
||||
|
||||
private let timeTitleNode: ImmediateTextNode
|
||||
private let timeFieldNode: ASImageNode
|
||||
private let timePickerNode: TimePickerNode
|
||||
private let timeSeparatorNode: ASDisplayNode
|
||||
|
||||
private let dayNodes: [ImmediateTextNode]
|
||||
@ -357,7 +359,7 @@ public final class DatePickerNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
public init(theme: DatePickerTheme, strings: PresentationStrings) {
|
||||
public init(theme: DatePickerTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat) {
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.state = State(minDate: telegramReleaseDate, maxDate: upperLimitDate, date: Date(), displayingMonthSelection: false, selectedMonth: monthForDate(Date()))
|
||||
@ -365,9 +367,7 @@ public final class DatePickerNode: ASDisplayNode {
|
||||
self.timeTitleNode = ImmediateTextNode()
|
||||
self.timeTitleNode.attributedText = NSAttributedString(string: "Time", font: Font.regular(17.0), textColor: theme.textColor)
|
||||
|
||||
self.timeFieldNode = ASImageNode()
|
||||
self.timeFieldNode.displaysAsynchronously = false
|
||||
self.timeFieldNode.displayWithoutProcessing = true
|
||||
self.timePickerNode = TimePickerNode(theme: theme, dateTimeFormat: dateTimeFormat, date: self.state.date)
|
||||
|
||||
self.timeSeparatorNode = ASDisplayNode()
|
||||
self.timeSeparatorNode.backgroundColor = theme.separatorColor
|
||||
@ -403,7 +403,7 @@ public final class DatePickerNode: ASDisplayNode {
|
||||
self.backgroundColor = theme.backgroundColor
|
||||
|
||||
self.addSubnode(self.timeTitleNode)
|
||||
self.addSubnode(self.timeFieldNode)
|
||||
self.addSubnode(self.timePickerNode)
|
||||
|
||||
self.addSubnode(self.contentNode)
|
||||
|
||||
@ -421,7 +421,9 @@ public final class DatePickerNode: ASDisplayNode {
|
||||
|
||||
self.monthArrowNode.image = generateSmallArrowImage(color: theme.accentColor)
|
||||
self.previousButtonNode.setImage(generateNavigationArrowImage(color: theme.accentColor, mirror: true), for: .normal)
|
||||
self.previousButtonNode.setImage(generateNavigationArrowImage(color: theme.disabledColor, mirror: true), for: .disabled)
|
||||
self.nextButtonNode.setImage(generateNavigationArrowImage(color: theme.accentColor, mirror: false), for: .normal)
|
||||
self.nextButtonNode.setImage(generateNavigationArrowImage(color: theme.disabledColor, mirror: false), for: .disabled)
|
||||
|
||||
self.setupItems()
|
||||
|
||||
@ -445,6 +447,15 @@ public final class DatePickerNode: ASDisplayNode {
|
||||
self.previousButtonNode.addTarget(self, action: #selector(self.previousButtonPressed), forControlEvents: .touchUpInside)
|
||||
self.nextButtonNode.addTarget(self, action: #selector(self.nextButtonPressed), forControlEvents: .touchUpInside)
|
||||
|
||||
self.timePickerNode.valueChanged = { [weak self] date in
|
||||
if let strongSelf = self {
|
||||
let updatedState = State(minDate: strongSelf.state.minDate, maxDate: strongSelf.state.maxDate, date: date, displayingMonthSelection: strongSelf.state.displayingMonthSelection, selectedMonth: strongSelf.state.selectedMonth)
|
||||
strongSelf.updateState(updatedState, animated: false)
|
||||
|
||||
strongSelf.valueUpdated?(date)
|
||||
}
|
||||
}
|
||||
|
||||
monthChangedImpl = { [weak self] date in
|
||||
if let strongSelf = self {
|
||||
let updatedState = State(minDate: strongSelf.state.minDate, maxDate: strongSelf.state.maxDate, date: date, displayingMonthSelection: strongSelf.state.displayingMonthSelection, selectedMonth: monthForDate(date))
|
||||
@ -523,7 +534,7 @@ public final class DatePickerNode: ASDisplayNode {
|
||||
return
|
||||
}
|
||||
self.theme = theme
|
||||
|
||||
|
||||
self.backgroundColor = self.theme.backgroundColor
|
||||
self.monthArrowNode.image = generateSmallArrowImage(color: theme.accentColor)
|
||||
self.previousButtonNode.setImage(generateNavigationArrowImage(color: theme.accentColor, mirror: true), for: .normal)
|
||||
@ -668,9 +679,12 @@ public final class DatePickerNode: ASDisplayNode {
|
||||
|
||||
let timeTitleSize = self.timeTitleNode.updateLayout(size)
|
||||
self.timeTitleNode.frame = CGRect(origin: CGPoint(x: 16.0, y: 14.0), size: timeTitleSize)
|
||||
|
||||
let timePickerSize = self.timePickerNode.updateLayout(size: size)
|
||||
self.timePickerNode.frame = CGRect(origin: CGPoint(x: size.width - timePickerSize.width - 16.0, y: 6.0), size: timePickerSize)
|
||||
self.timeSeparatorNode.frame = CGRect(x: 16.0, y: timeHeight, width: size.width - 16.0, height: UIScreenPixel)
|
||||
|
||||
self.monthTextNode.attributedText = NSAttributedString(string: stringForMonth(strings: self.strings, month: components.month.flatMap { Int32($0) - 1 } ?? 0, ofYear: components.year.flatMap { Int32($0) - 1900 } ?? 100), font: controlFont, textColor: theme.textColor)
|
||||
self.monthTextNode.attributedText = NSAttributedString(string: stringForMonth(strings: self.strings, month: components.month.flatMap { Int32($0) - 1 } ?? 0, ofYear: components.year.flatMap { Int32($0) - 1900 } ?? 100), font: controlFont, textColor: self.state.displayingMonthSelection ? self.theme.accentColor : self.theme.textColor)
|
||||
let monthSize = self.monthTextNode.updateLayout(size)
|
||||
|
||||
let monthTextFrame = CGRect(x: sideInset, y: 11.0 + timeHeight, width: monthSize.width, height: monthSize.height)
|
||||
@ -684,7 +698,9 @@ public final class DatePickerNode: ASDisplayNode {
|
||||
|
||||
self.monthButtonNode.frame = monthTextFrame.inset(by: UIEdgeInsets(top: -6.0, left: -6.0, bottom: -6.0, right: -30.0))
|
||||
|
||||
self.previousButtonNode.isEnabled = self.currentIndex > 0
|
||||
self.previousButtonNode.frame = CGRect(x: size.width - sideInset - 54.0, y: monthTextFrame.minY + 1.0, width: 10.0, height: 17.0)
|
||||
self.nextButtonNode.isEnabled = self.currentIndex < self.months.count - 1
|
||||
self.nextButtonNode.frame = CGRect(x: size.width - sideInset - 13.0, y: monthTextFrame.minY + 1.0, width: 10.0, height: 17.0)
|
||||
|
||||
let daysSideInset: CGFloat = 12.0
|
||||
@ -752,6 +768,9 @@ private final class MonthPickerNode: ASDisplayNode, UIPickerViewDelegate, UIPick
|
||||
}
|
||||
}
|
||||
|
||||
var minDate: Date?
|
||||
var maxDate: Date?
|
||||
|
||||
private let valueChanged: (Date) -> Void
|
||||
private let pickerView: UIPickerView
|
||||
|
||||
@ -840,3 +859,85 @@ private final class MonthPickerNode: ASDisplayNode, UIPickerViewDelegate, UIPick
|
||||
self.pickerView.frame = CGRect(origin: CGPoint(), size: CGSize(width: self.bounds.size.width, height: 180.0))
|
||||
}
|
||||
}
|
||||
|
||||
private final class TimePickerNode: ASDisplayNode {
|
||||
private var theme: DatePickerTheme
|
||||
private let dateTimeFormat: PresentationDateTimeFormat
|
||||
|
||||
private let backgroundNode: ASDisplayNode
|
||||
private let textNode: ImmediateTextNode
|
||||
private let amPMSelectorNode: SegmentedControlNode
|
||||
|
||||
var date: Date
|
||||
var minDate: Date?
|
||||
var maxDate: Date?
|
||||
|
||||
var valueChanged: ((Date) -> Void)?
|
||||
|
||||
init(theme: DatePickerTheme, dateTimeFormat: PresentationDateTimeFormat, date: Date) {
|
||||
self.theme = theme
|
||||
self.dateTimeFormat = dateTimeFormat
|
||||
self.date = date
|
||||
|
||||
self.backgroundNode = ASDisplayNode()
|
||||
self.backgroundNode.backgroundColor = theme.segmentedControlTheme.backgroundColor
|
||||
self.backgroundNode.cornerRadius = 9.0
|
||||
|
||||
self.textNode = ImmediateTextNode()
|
||||
|
||||
let hours = calendar.component(.hour, from: date)
|
||||
|
||||
self.amPMSelectorNode = SegmentedControlNode(theme: theme.segmentedControlTheme, items: [SegmentedControlItem(title: "AM"), SegmentedControlItem(title: "PM")], selectedIndex: hours > 12 ? 1 : 0)
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.backgroundNode)
|
||||
self.addSubnode(self.textNode)
|
||||
self.addSubnode(self.amPMSelectorNode)
|
||||
|
||||
self.amPMSelectorNode.selectedIndexChanged = { index in
|
||||
let hours = calendar.component(.hour, from: date)
|
||||
var components = calendar.dateComponents([.year, .month, .day, .hour, .minute], from: self.date)
|
||||
if index == 0 && hours >= 12 {
|
||||
components.hour = hours - 12
|
||||
} else if index == 1 && hours < 12 {
|
||||
components.hour = hours + 12
|
||||
}
|
||||
if let newDate = calendar.date(from: components) {
|
||||
self.valueChanged?(newDate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateTheme(_ theme: DatePickerTheme) {
|
||||
self.theme = theme
|
||||
|
||||
self.backgroundNode.backgroundColor = theme.segmentedControlTheme.backgroundColor
|
||||
}
|
||||
|
||||
func updateLayout(size: CGSize) -> CGSize {
|
||||
self.backgroundNode.frame = CGRect(x: 0.0, y: 0.0, width: 75.0, height: 36.0)
|
||||
|
||||
var contentSize = CGSize()
|
||||
|
||||
let hours = Int32(calendar.component(.hour, from: self.date))
|
||||
let minutes = Int32(calendar.component(.hour, from: self.date))
|
||||
let string = stringForShortTimestamp(hours: hours, minutes: minutes, dateTimeFormat: self.dateTimeFormat).replacingOccurrences(of: " AM", with: "").replacingOccurrences(of: " PM", with: "")
|
||||
|
||||
self.textNode.attributedText = NSAttributedString(string: string, font: Font.with(size: 21.0, design: .monospace, weight: .regular, traits: []), textColor: self.theme.textColor)
|
||||
let textSize = self.textNode.updateLayout(size)
|
||||
self.textNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((self.backgroundNode.frame.width - textSize.width) / 2.0), y: floorToScreenPixels((self.backgroundNode.frame.height - textSize.height) / 2.0)), size: textSize)
|
||||
|
||||
if self.dateTimeFormat.timeFormat == .military {
|
||||
contentSize = self.backgroundNode.frame.size
|
||||
self.amPMSelectorNode.isHidden = true
|
||||
} else {
|
||||
self.amPMSelectorNode.isHidden = false
|
||||
let segmentedSize = self.amPMSelectorNode.updateLayout(.sizeToFit(maximumWidth: 120.0, minimumWidth: 80.0, height: 36.0), transition: .immediate)
|
||||
self.amPMSelectorNode.frame = CGRect(x: 85.0, y: 0.0, width: segmentedSize.width, height: 36.0)
|
||||
contentSize = CGSize(width: 85.0 + segmentedSize.width, height: 36.0)
|
||||
}
|
||||
|
||||
return contentSize
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,7 @@
|
||||
(int)sourceSampleRate,
|
||||
0,
|
||||
NULL);
|
||||
_ratio = MAX(1, destinationSampleRate / sourceSampleRate) * MAX(1, destinationChannelCount / sourceChannelCount) * 2;
|
||||
_ratio = MAX(1, destinationSampleRate / MAX(sourceSampleRate, 1)) * MAX(1, destinationChannelCount / sourceChannelCount) * 2;
|
||||
swr_init(_context);
|
||||
}
|
||||
return self;
|
||||
|
@ -51,7 +51,7 @@ private enum InviteLinksEditEntry: ItemListNodeEntry {
|
||||
case timeHeader(PresentationTheme, String)
|
||||
case timePicker(PresentationTheme, InviteLinkTimeLimit)
|
||||
case timeExpiryDate(PresentationTheme, Int32?, Bool)
|
||||
case timeCustomPicker(PresentationTheme, Int32?)
|
||||
case timeCustomPicker(PresentationTheme, PresentationDateTimeFormat, Int32?)
|
||||
case timeInfo(PresentationTheme, String)
|
||||
|
||||
case usageHeader(PresentationTheme, String)
|
||||
@ -117,8 +117,8 @@ private enum InviteLinksEditEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .timeCustomPicker(lhsTheme, lhsDate):
|
||||
if case let .timeCustomPicker(rhsTheme, rhsDate) = rhs, lhsTheme === rhsTheme, lhsDate == rhsDate {
|
||||
case let .timeCustomPicker(lhsTheme, lhsDateTimeFormat, lhsDate):
|
||||
if case let .timeCustomPicker(rhsTheme, rhsDateTimeFormat, rhsDate) = rhs, lhsTheme === rhsTheme, lhsDateTimeFormat == rhsDateTimeFormat, lhsDate == rhsDate {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@ -197,8 +197,8 @@ private enum InviteLinksEditEntry: ItemListNodeEntry {
|
||||
return updatedState
|
||||
}
|
||||
})
|
||||
case let .timeCustomPicker(_, date):
|
||||
return ItemListDatePickerItem(presentationData: presentationData, date: date, sectionId: self.section, style: .blocks, updated: { date in
|
||||
case let .timeCustomPicker(_, dateTimeFormat, date):
|
||||
return ItemListDatePickerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, date: date, sectionId: self.section, style: .blocks, updated: { date in
|
||||
arguments.updateState({ state in
|
||||
var updatedState = state
|
||||
updatedState.time = .custom(date)
|
||||
@ -283,7 +283,7 @@ private func inviteLinkEditControllerEntries(invite: ExportedInvitation?, state:
|
||||
}
|
||||
entries.append(.timeExpiryDate(presentationData.theme, time, state.pickingTimeLimit))
|
||||
if state.pickingTimeLimit {
|
||||
entries.append(.timeCustomPicker(presentationData.theme, time ?? currentTime))
|
||||
entries.append(.timeCustomPicker(presentationData.theme, presentationData.dateTimeFormat, time ?? currentTime))
|
||||
}
|
||||
entries.append(.timeInfo(presentationData.theme, presentationData.strings.InviteLink_Create_TimeLimitInfo))
|
||||
|
||||
@ -373,7 +373,14 @@ public func inviteLinkEditController(context: AccountContext, peerId: PeerId, in
|
||||
let _ = (revokePeerExportedInvitation(account: context.account, peerId: peerId, link: invite.link)
|
||||
|> timeout(10, queue: Queue.mainQueue(), alternate: .fail(.generic))
|
||||
|> deliverOnMainQueue).start(next: { invite in
|
||||
completion?(invite)
|
||||
switch invite {
|
||||
case .none:
|
||||
completion?(nil)
|
||||
case let .update(invitation):
|
||||
completion?(invitation)
|
||||
case let .replace(_, invitation):
|
||||
completion?(invitation)
|
||||
}
|
||||
}, error: { _ in
|
||||
updateState { state in
|
||||
var updatedState = state
|
||||
|
@ -467,16 +467,24 @@ public func inviteLinkListController(context: AccountContext, peerId: PeerId, ad
|
||||
}
|
||||
}
|
||||
if revoke {
|
||||
// revokeLinkDisposable.set((revokePersistentPeerExportedInvitation(account: context.account, peerId: peerId) |> deliverOnMainQueue).start(completed: {
|
||||
// updateState { state in
|
||||
// var updatedState = state
|
||||
// updatedState.revokingPrivateLink = false
|
||||
// return updatedState
|
||||
// }
|
||||
//
|
||||
// invitesContext.reload()
|
||||
// revokedInvitesContext.reload()
|
||||
// }))
|
||||
revokeLinkDisposable.set((revokePeerExportedInvitation(account: context.account, peerId: peerId, link: invite.link) |> deliverOnMainQueue).start(next: { result in
|
||||
updateState { state in
|
||||
var updatedState = state
|
||||
updatedState.revokingPrivateLink = false
|
||||
return updatedState
|
||||
}
|
||||
if let result = result {
|
||||
switch result {
|
||||
case let .update(newInvite):
|
||||
invitesContext.remove(newInvite)
|
||||
revokedInvitesContext.add(newInvite)
|
||||
case let .replace(previousInvite, newInvite):
|
||||
revokedInvitesContext.add(previousInvite)
|
||||
invitesContext.remove(previousInvite)
|
||||
invitesContext.add(newInvite)
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
})
|
||||
]),
|
||||
@ -512,7 +520,7 @@ public func inviteLinkListController(context: AccountContext, peerId: PeerId, ad
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextCopy, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { _, f in
|
||||
f(.dismissWithoutContent)
|
||||
f(.default)
|
||||
|
||||
dismissTooltipsImpl?()
|
||||
|
||||
@ -526,7 +534,7 @@ public func inviteLinkListController(context: AccountContext, peerId: PeerId, ad
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextShare, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { _, f in
|
||||
f(.dismissWithoutContent)
|
||||
f(.default)
|
||||
|
||||
let shareController = ShareController(context: context, subject: .url(invite.link))
|
||||
presentControllerImpl?(shareController, nil)
|
||||
@ -535,7 +543,7 @@ public func inviteLinkListController(context: AccountContext, peerId: PeerId, ad
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextGetQRCode, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Wallet/QrIcon"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { _, f in
|
||||
f(.dismissWithoutContent)
|
||||
f(.default)
|
||||
|
||||
let controller = InviteLinkQRCodeController(context: context, invite: invite)
|
||||
presentControllerImpl?(controller, nil)
|
||||
@ -544,7 +552,7 @@ public func inviteLinkListController(context: AccountContext, peerId: PeerId, ad
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextEdit, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { _, f in
|
||||
f(.dismissWithoutContent)
|
||||
f(.default)
|
||||
|
||||
let controller = inviteLinkEditController(context: context, peerId: peerId, invite: invite, completion: { invite in
|
||||
if let invite = invite {
|
||||
@ -565,7 +573,7 @@ public func inviteLinkListController(context: AccountContext, peerId: PeerId, ad
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextDelete, textColor: .destructive, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.actionSheet.destructiveActionTextColor)
|
||||
}, action: { _, f in
|
||||
f(.dismissWithoutContent)
|
||||
f(.default)
|
||||
|
||||
let controller = ActionSheetController(presentationData: presentationData)
|
||||
let dismissAction: () -> Void = { [weak controller] in
|
||||
@ -578,7 +586,6 @@ public func inviteLinkListController(context: AccountContext, peerId: PeerId, ad
|
||||
dismissAction()
|
||||
|
||||
revokeLinkDisposable.set((deletePeerExportedInvitation(account: context.account, peerId: peerId, link: invite.link) |> deliverOnMainQueue).start(completed: {
|
||||
|
||||
}))
|
||||
|
||||
revokedInvitesContext.remove(invite)
|
||||
@ -592,7 +599,7 @@ public func inviteLinkListController(context: AccountContext, peerId: PeerId, ad
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextRevoke, textColor: .destructive, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.actionSheet.destructiveActionTextColor)
|
||||
}, action: { _, f in
|
||||
f(.dismissWithoutContent)
|
||||
f(.default)
|
||||
|
||||
let controller = ActionSheetController(presentationData: presentationData)
|
||||
let dismissAction: () -> Void = { [weak controller] in
|
||||
|
@ -10,6 +10,7 @@ import DatePickerNode
|
||||
|
||||
public class ItemListDatePickerItem: ListViewItem, ItemListItem {
|
||||
let presentationData: ItemListPresentationData
|
||||
let dateTimeFormat: PresentationDateTimeFormat
|
||||
let date: Int32?
|
||||
public let sectionId: ItemListSectionId
|
||||
let style: ItemListStyle
|
||||
@ -18,6 +19,7 @@ public class ItemListDatePickerItem: ListViewItem, ItemListItem {
|
||||
|
||||
public init(
|
||||
presentationData: ItemListPresentationData,
|
||||
dateTimeFormat: PresentationDateTimeFormat,
|
||||
date: Int32?,
|
||||
sectionId: ItemListSectionId,
|
||||
style: ItemListStyle,
|
||||
@ -25,6 +27,7 @@ public class ItemListDatePickerItem: ListViewItem, ItemListItem {
|
||||
tag: ItemListItemTag? = nil
|
||||
) {
|
||||
self.presentationData = presentationData
|
||||
self.dateTimeFormat = dateTimeFormat
|
||||
self.date = date
|
||||
self.sectionId = sectionId
|
||||
self.style = style
|
||||
@ -92,6 +95,7 @@ public class ItemListDatePickerItemNode: ListViewItemNode, ItemListItemNode {
|
||||
self.backgroundNode.backgroundColor = .white
|
||||
|
||||
self.maskNode = ASImageNode()
|
||||
self.maskNode.isUserInteractionEnabled = false
|
||||
|
||||
self.topStripeNode = ASDisplayNode()
|
||||
self.topStripeNode.isLayerBacked = true
|
||||
@ -100,15 +104,7 @@ public class ItemListDatePickerItemNode: ListViewItemNode, ItemListItemNode {
|
||||
self.bottomStripeNode.isLayerBacked = true
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false)
|
||||
|
||||
}
|
||||
|
||||
// @objc private func datePickerUpdated() {
|
||||
// guard let datePicker = self.datePicker else {
|
||||
// return
|
||||
// }
|
||||
// self.item?.updated?(Int32(datePicker.date.timeIntervalSince1970))
|
||||
// }
|
||||
|
||||
public func asyncLayout() -> (_ item: ItemListDatePickerItem, _ params: ListViewItemLayoutParams, _ insets: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
|
||||
let currentItem = self.item
|
||||
@ -128,7 +124,9 @@ public class ItemListDatePickerItemNode: ListViewItemNode, ItemListItemNode {
|
||||
let leftInset = 16.0 + params.leftInset
|
||||
let rightInset = 16.0 + params.rightInset
|
||||
|
||||
let height: CGFloat = 360.0
|
||||
let width = min(390.0, params.width - params.leftInset - params.rightInset)
|
||||
let cellSize = floor((width - 12.0 * 2.0) / 7.0)
|
||||
let height: CGFloat = 122.0 + cellSize * 6.0
|
||||
|
||||
switch item.style {
|
||||
case .plain:
|
||||
@ -154,26 +152,7 @@ public class ItemListDatePickerItemNode: ListViewItemNode, ItemListItemNode {
|
||||
|
||||
strongSelf.datePickerNode?.updateTheme(DatePickerTheme(theme: item.presentationData.theme))
|
||||
}
|
||||
|
||||
let datePickerNode: DatePickerNode
|
||||
if let current = strongSelf.datePickerNode {
|
||||
datePickerNode = current
|
||||
} else {
|
||||
datePickerNode = DatePickerNode(theme: DatePickerTheme(theme: item.presentationData.theme), strings: item.presentationData.strings)
|
||||
strongSelf.addSubnode(datePickerNode)
|
||||
strongSelf.datePickerNode = datePickerNode
|
||||
}
|
||||
datePickerNode.valueUpdated = { date in
|
||||
strongSelf.item?.updated?(Int32(date.timeIntervalSince1970))
|
||||
}
|
||||
|
||||
datePickerNode.minimumDate = Date()
|
||||
datePickerNode.date = item.date.flatMap { Date(timeIntervalSince1970: TimeInterval($0)) } ?? Date()
|
||||
|
||||
let datePickerSize = CGSize(width: contentSize.width - params.leftInset - params.rightInset, height: contentSize.height)
|
||||
datePickerNode.frame = CGRect(origin: CGPoint(x: params.leftInset, y: 0.0), size: datePickerSize)
|
||||
datePickerNode.updateLayout(size: datePickerSize, transition: .immediate)
|
||||
|
||||
|
||||
switch item.style {
|
||||
case .plain:
|
||||
if strongSelf.backgroundNode.supernode != nil {
|
||||
@ -230,6 +209,25 @@ public class ItemListDatePickerItemNode: ListViewItemNode, ItemListItemNode {
|
||||
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: separatorHeight))
|
||||
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - bottomStripeInset, height: separatorHeight))
|
||||
}
|
||||
|
||||
let datePickerNode: DatePickerNode
|
||||
if let current = strongSelf.datePickerNode {
|
||||
datePickerNode = current
|
||||
} else {
|
||||
datePickerNode = DatePickerNode(theme: DatePickerTheme(theme: item.presentationData.theme), strings: item.presentationData.strings, dateTimeFormat: item.dateTimeFormat)
|
||||
strongSelf.insertSubnode(datePickerNode, belowSubnode: strongSelf.topStripeNode)
|
||||
strongSelf.datePickerNode = datePickerNode
|
||||
}
|
||||
datePickerNode.valueUpdated = { date in
|
||||
strongSelf.item?.updated?(Int32(date.timeIntervalSince1970))
|
||||
}
|
||||
|
||||
datePickerNode.minimumDate = Date()
|
||||
datePickerNode.date = item.date.flatMap { Date(timeIntervalSince1970: TimeInterval($0)) } ?? Date()
|
||||
|
||||
let datePickerSize = CGSize(width: width, height: contentSize.height)
|
||||
datePickerNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((params.width - datePickerSize.width) / 2.0), y: 0.0), size: datePickerSize)
|
||||
datePickerNode.updateLayout(size: datePickerSize, transition: .immediate)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -408,7 +408,6 @@ public class ItemListInviteLinkItemNode: ListViewItemNode, ItemListItemNode {
|
||||
} else {
|
||||
titleText = " "
|
||||
subtitleText = " "
|
||||
self.iconNode.isHidden = true
|
||||
}
|
||||
|
||||
let titleAttributedString = NSAttributedString(string: titleText, font: titleFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor)
|
||||
|
@ -54,7 +54,7 @@ public final class ItemListControllerSegmentedTitleView: UIView {
|
||||
super.layoutSubviews()
|
||||
|
||||
let size = self.bounds.size
|
||||
let controlSize = self.segmentedControlNode.updateLayout(.sizeToFit(maximumWidth: size.width, minimumWidth: 160.0), transition: .immediate)
|
||||
let controlSize = self.segmentedControlNode.updateLayout(.sizeToFit(maximumWidth: size.width, minimumWidth: 160.0, height: 32.0), transition: .immediate)
|
||||
self.segmentedControlNode.frame = CGRect(origin: CGPoint(x: floor((size.width - controlSize.width) / 2.0), y: floor((size.height - controlSize.height) / 2.0)), size: controlSize)
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
@property (nonatomic, assign) CGFloat startValue;
|
||||
@property (nonatomic, assign) CGFloat value;
|
||||
@property (nonatomic, assign) int minimumUndottedValue;
|
||||
|
||||
@property (nonatomic, assign) CGFloat markValue;
|
||||
|
||||
|
@ -40,6 +40,7 @@ const CGFloat TGPhotoEditorSliderViewInternalMargin = 7.0f;
|
||||
_startValue = 0.0f;
|
||||
_value = _startValue;
|
||||
_dotSize = 10.5f;
|
||||
_minimumUndottedValue = -1;
|
||||
|
||||
_lineSize = TGPhotoEditorSliderViewLineSize;
|
||||
_knobPadding = TGPhotoEditorSliderViewInternalMargin;
|
||||
@ -174,13 +175,59 @@ const CGFloat TGPhotoEditorSliderViewInternalMargin = 7.0f;
|
||||
CGContextSetBlendMode(context, kCGBlendModeCopy);
|
||||
}
|
||||
|
||||
CGContextSetFillColorWithColor(context, _backColor.CGColor);
|
||||
[self drawRectangle:backFrame cornerRadius:self.trackCornerRadius context:context];
|
||||
if (_minimumUndottedValue > -1 && self.positionsCount > 1) {
|
||||
CGContextSetLineWidth(context, backFrame.size.height);
|
||||
CGContextSetLineCap(context, kCGLineCapRound);
|
||||
|
||||
for (NSInteger i = 1; i < self.positionsCount; i++)
|
||||
{
|
||||
CGFloat previousX = margin + totalLength / (self.positionsCount - 1) * (i - 1);
|
||||
CGFloat currentX = margin + totalLength / (self.positionsCount - 1) * i;
|
||||
|
||||
if (_minimumUndottedValue < i) {
|
||||
CGFloat normalDashWidth = 16.0f;
|
||||
CGFloat dashFraction = 0.6f;
|
||||
CGFloat totalLineWidth = currentX - previousX;
|
||||
int numberOfDashes = (int)floor((double)(totalLineWidth / normalDashWidth));
|
||||
CGFloat dashWidth = (totalLineWidth / (CGFloat)numberOfDashes);
|
||||
|
||||
CGFloat innerWidth = dashWidth * dashFraction - 2.0f;
|
||||
CGFloat innerOffset = (dashWidth - innerWidth) / 2.0f;
|
||||
|
||||
CGFloat dottedX = previousX;
|
||||
|
||||
while (dottedX + innerWidth < currentX) {
|
||||
bool highlighted = dottedX + dashWidth / 2.0f < CGRectGetMaxX(trackFrame);
|
||||
|
||||
CGContextSetStrokeColorWithColor(context, highlighted ? _trackColor.CGColor : _backColor.CGColor);
|
||||
|
||||
CGContextMoveToPoint(context, dottedX + innerOffset, CGRectGetMidY(backFrame));
|
||||
CGContextAddLineToPoint(context, dottedX + innerOffset + innerWidth, CGRectGetMidY(backFrame));
|
||||
CGContextStrokePath(context);
|
||||
|
||||
dottedX += dashWidth;
|
||||
}
|
||||
} else {
|
||||
bool highlighted = (previousX + (currentX - previousX) / 2.0f) < CGRectGetMaxX(trackFrame);
|
||||
CGContextSetStrokeColorWithColor(context, highlighted ? _trackColor.CGColor : _backColor.CGColor);
|
||||
|
||||
CGContextMoveToPoint(context, previousX, CGRectGetMidY(backFrame));
|
||||
CGContextAddLineToPoint(context, currentX, CGRectGetMidY(backFrame));
|
||||
CGContextStrokePath(context);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
CGContextSetFillColorWithColor(context, _backColor.CGColor);
|
||||
[self drawRectangle:backFrame cornerRadius:self.trackCornerRadius context:context];
|
||||
}
|
||||
|
||||
CGContextSetBlendMode(context, kCGBlendModeNormal);
|
||||
|
||||
CGContextSetFillColorWithColor(context, _trackColor.CGColor);
|
||||
[self drawRectangle:trackFrame cornerRadius:self.trackCornerRadius context:context];
|
||||
if (_minimumUndottedValue > -1) {
|
||||
} else {
|
||||
CGContextSetFillColorWithColor(context, _trackColor.CGColor);
|
||||
[self drawRectangle:trackFrame cornerRadius:self.trackCornerRadius context:context];
|
||||
}
|
||||
|
||||
if (!_startHidden || self.displayEdges)
|
||||
{
|
||||
@ -313,6 +360,13 @@ const CGFloat TGPhotoEditorSliderViewInternalMargin = 7.0f;
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
|
||||
- (void)setMinimumUndottedValue:(int)minimumUndottedValue {
|
||||
if (_minimumUndottedValue != minimumUndottedValue) {
|
||||
_minimumUndottedValue = minimumUndottedValue;
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Properties
|
||||
|
||||
- (bool)isTracking
|
||||
|
@ -989,22 +989,35 @@ public func channelVisibilityController(context: AccountContext, peerId: PeerId,
|
||||
ActionSheetButtonItem(title: presentationData.strings.GroupInfo_InviteLink_RevokeLink, color: .destructive, action: {
|
||||
dismissAction()
|
||||
|
||||
var revoke = false
|
||||
updateState { state in
|
||||
if !state.revokingPrivateLink {
|
||||
revoke = true
|
||||
return state.withUpdatedRevokingPrivateLink(true)
|
||||
} else {
|
||||
return state
|
||||
let _ = (context.account.postbox.transaction { transaction -> String? in
|
||||
if let cachedData = transaction.getPeerCachedData(peerId: peerId) {
|
||||
if let cachedData = cachedData as? CachedChannelData {
|
||||
return cachedData.exportedInvitation?.link
|
||||
} else if let cachedData = cachedData as? CachedGroupData {
|
||||
return cachedData.exportedInvitation?.link
|
||||
}
|
||||
}
|
||||
}
|
||||
if revoke {
|
||||
// revokeLinkDisposable.set((revokePersistentPeerExportedInvitation(account: context.account, peerId: peerId) |> deliverOnMainQueue).start(completed: {
|
||||
// updateState {
|
||||
// $0.withUpdatedRevokingPrivateLink(false)
|
||||
// }
|
||||
// }))
|
||||
}
|
||||
return nil
|
||||
} |> deliverOnMainQueue).start(next: { link in
|
||||
if let link = link {
|
||||
var revoke = false
|
||||
updateState { state in
|
||||
if !state.revokingPrivateLink {
|
||||
revoke = true
|
||||
return state.withUpdatedRevokingPrivateLink(true)
|
||||
} else {
|
||||
return state
|
||||
}
|
||||
}
|
||||
if revoke {
|
||||
revokeLinkDisposable.set((revokePeerExportedInvitation(account: context.account, peerId: peerId, link: link) |> deliverOnMainQueue).start(completed: {
|
||||
updateState {
|
||||
$0.withUpdatedRevokingPrivateLink(false)
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
]),
|
||||
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
|
||||
|
321
submodules/PeerInfoUI/Sources/PeerAutoremoveSetupScreen.swift
Normal file
321
submodules/PeerInfoUI/Sources/PeerAutoremoveSetupScreen.swift
Normal file
@ -0,0 +1,321 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import SwiftSignalKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import SyncCore
|
||||
import TelegramPresentationData
|
||||
import ItemListUI
|
||||
import PresentationDataUtils
|
||||
import AccountContext
|
||||
import ChatListFilterSettingsHeaderItem
|
||||
|
||||
private final class PeerAutoremoveSetupArguments {
|
||||
let toggleGlobal: (Bool) -> Void
|
||||
let updateValue: (Int32) -> Void
|
||||
|
||||
init(toggleGlobal: @escaping (Bool) -> Void, updateValue: @escaping (Int32) -> Void) {
|
||||
self.toggleGlobal = toggleGlobal
|
||||
self.updateValue = updateValue
|
||||
}
|
||||
}
|
||||
|
||||
private enum PeerAutoremoveSetupSection: Int32 {
|
||||
case header
|
||||
case time
|
||||
case global
|
||||
}
|
||||
|
||||
private enum PeerAutoremoveSetupEntry: ItemListNodeEntry {
|
||||
case header
|
||||
case timeHeader(String)
|
||||
case timeValue(Int32, Int32)
|
||||
case timeComment(String)
|
||||
case globalSwitch(String, Bool)
|
||||
|
||||
var section: ItemListSectionId {
|
||||
switch self {
|
||||
case .header:
|
||||
return PeerAutoremoveSetupSection.header.rawValue
|
||||
case .timeHeader, .timeValue, .timeComment:
|
||||
return PeerAutoremoveSetupSection.time.rawValue
|
||||
case .globalSwitch:
|
||||
return PeerAutoremoveSetupSection.global.rawValue
|
||||
}
|
||||
}
|
||||
|
||||
var stableId: Int32 {
|
||||
switch self {
|
||||
case .header:
|
||||
return 0
|
||||
case .timeHeader:
|
||||
return 1
|
||||
case .timeValue:
|
||||
return 2
|
||||
case .timeComment:
|
||||
return 3
|
||||
case .globalSwitch:
|
||||
return 4
|
||||
}
|
||||
}
|
||||
|
||||
static func ==(lhs: PeerAutoremoveSetupEntry, rhs: PeerAutoremoveSetupEntry) -> Bool {
|
||||
switch lhs {
|
||||
case .header:
|
||||
if case .header = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .timeHeader(lhsText):
|
||||
if case let .timeHeader(rhsText) = rhs, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .timeValue(lhsValue, lhsMaxValue):
|
||||
if case let .timeValue(rhsValue, rhsMaxValue) = rhs, lhsValue == rhsValue, lhsMaxValue == rhsMaxValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .timeComment(lhsText):
|
||||
if case let .timeComment(rhsText) = rhs, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .globalSwitch(lhsText, lhsValue):
|
||||
if case let .globalSwitch(rhsText, rhsValue) = rhs, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func <(lhs: PeerAutoremoveSetupEntry, rhs: PeerAutoremoveSetupEntry) -> Bool {
|
||||
return lhs.stableId < rhs.stableId
|
||||
}
|
||||
|
||||
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
|
||||
let arguments = arguments as! PeerAutoremoveSetupArguments
|
||||
switch self {
|
||||
case .header:
|
||||
return ChatListFilterSettingsHeaderItem(theme: presentationData.theme, text: "", animation: .autoRemove, sectionId: self.section)
|
||||
case let .timeHeader(text):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||
case let .timeValue(value, maxValue):
|
||||
return PeerRemoveTimeoutItem(theme: presentationData.theme, value: value, maxValue: maxValue, enabled: true, sectionId: self.section, updated: { value in
|
||||
arguments.updateValue(value)
|
||||
}, tag: nil)
|
||||
case let .timeComment(text):
|
||||
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
||||
case let .globalSwitch(text, value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
arguments.toggleGlobal(value)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct PeerAutoremoveSetupState: Equatable {
|
||||
var changedValue: Int32?
|
||||
var changedGlobalValue: Bool?
|
||||
var applyingSetting: Bool = false
|
||||
}
|
||||
|
||||
private func peerAutoremoveSetupEntries(peer: Peer?, presentationData: PresentationData, defaultMyValue: Int32, peerValue: Int32, defaultGlobalValue: Bool, state: PeerAutoremoveSetupState) -> [PeerAutoremoveSetupEntry] {
|
||||
var entries: [PeerAutoremoveSetupEntry] = []
|
||||
let globalValue = state.changedGlobalValue ?? defaultGlobalValue
|
||||
|
||||
let resolvedValue: Int32
|
||||
let resolvedMaxValue: Int32
|
||||
|
||||
if peer is TelegramUser {
|
||||
resolvedValue = state.changedValue ?? defaultMyValue
|
||||
resolvedMaxValue = peerValue
|
||||
} else {
|
||||
resolvedValue = state.changedValue ?? peerValue
|
||||
resolvedMaxValue = Int32.max
|
||||
}
|
||||
|
||||
//TODO:localize
|
||||
entries.append(.header)
|
||||
entries.append(.timeHeader("AUTO-DELETE MESSAGES"))
|
||||
entries.append(.timeValue(resolvedValue, resolvedMaxValue))
|
||||
if let channel = peer as? TelegramChannel, case .broadcast = channel.info {
|
||||
entries.append(.timeComment("Automatically delete messages sent in this channel after a certain period of time."))
|
||||
} else {
|
||||
if resolvedMaxValue != Int32.max {
|
||||
entries.append(.timeComment("\(peer?.compactDisplayTitle ?? "") has set messages to auto-delete in \(timeIntervalString(strings: presentationData.strings, value: resolvedMaxValue)). You can't cancel it or make this interval longer."))
|
||||
} else {
|
||||
entries.append(.timeComment("Automatically delete messages sent in this chat after a certain period of time."))
|
||||
}
|
||||
}
|
||||
if let user = peer as? TelegramUser {
|
||||
entries.append(.globalSwitch("Also auto-delete for \(user.compactDisplayTitle)", globalValue))
|
||||
}
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
public enum PeerAutoremoveSetupScreenResult {
|
||||
public struct Updated {
|
||||
public var myValue: Int32?
|
||||
public var limitedByValue: Int32?
|
||||
}
|
||||
|
||||
case unchanged
|
||||
case updated(Updated)
|
||||
}
|
||||
|
||||
public func peerAutoremoveSetupScreen(context: AccountContext, peerId: PeerId, completion: @escaping (PeerAutoremoveSetupScreenResult) -> Void = { _ in }) -> ViewController {
|
||||
let statePromise = ValuePromise(PeerAutoremoveSetupState(), ignoreRepeated: true)
|
||||
let stateValue = Atomic(value: PeerAutoremoveSetupState())
|
||||
let updateState: ((PeerAutoremoveSetupState) -> PeerAutoremoveSetupState) -> Void = { f in
|
||||
statePromise.set(stateValue.modify { f($0) })
|
||||
}
|
||||
|
||||
var pushControllerImpl: ((ViewController) -> Void)?
|
||||
var dismissImpl: (() -> Void)?
|
||||
|
||||
let actionsDisposable = DisposableSet()
|
||||
|
||||
let applyDisposable = MetaDisposable()
|
||||
actionsDisposable.add(applyDisposable)
|
||||
|
||||
let arguments = PeerAutoremoveSetupArguments(toggleGlobal: { value in
|
||||
updateState { state in
|
||||
var state = state
|
||||
state.changedGlobalValue = value
|
||||
return state
|
||||
}
|
||||
}, updateValue: { value in
|
||||
updateState { state in
|
||||
var state = state
|
||||
state.changedValue = value
|
||||
return state
|
||||
}
|
||||
})
|
||||
|
||||
let signal = combineLatest(context.sharedContext.presentationData, statePromise.get(), context.account.viewTracker.peerView(peerId))
|
||||
|> deliverOnMainQueue
|
||||
|> map { presentationData, state, view -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||
var defaultMyValue: Int32 = Int32.max
|
||||
var peerValue: Int32 = Int32.max
|
||||
var defaultGlobalValue = true
|
||||
if let cachedData = view.cachedData as? CachedChannelData {
|
||||
if case let .known(value) = cachedData.autoremoveTimeout {
|
||||
defaultMyValue = value?.myValue ?? Int32.max
|
||||
peerValue = value?.peerValue ?? Int32.max
|
||||
defaultGlobalValue = value?.isGlobal ?? true
|
||||
}
|
||||
} else if let cachedData = view.cachedData as? CachedGroupData {
|
||||
if case let .known(value) = cachedData.autoremoveTimeout {
|
||||
defaultMyValue = value?.myValue ?? Int32.max
|
||||
peerValue = value?.peerValue ?? Int32.max
|
||||
defaultGlobalValue = value?.isGlobal ?? true
|
||||
}
|
||||
} else if let cachedData = view.cachedData as? CachedUserData {
|
||||
if case let .known(value) = cachedData.autoremoveTimeout {
|
||||
defaultMyValue = value?.myValue ?? Int32.max
|
||||
peerValue = value?.peerValue ?? Int32.max
|
||||
defaultGlobalValue = value?.isGlobal ?? true
|
||||
}
|
||||
}
|
||||
|
||||
let peer = view.peers[view.peerId]
|
||||
|
||||
let leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: {
|
||||
dismissImpl?()
|
||||
})
|
||||
var rightNavigationButton: ItemListNavigationButton?
|
||||
if state.applyingSetting {
|
||||
rightNavigationButton = ItemListNavigationButton(content: .none, style: .activity, enabled: true, action: {})
|
||||
} else {
|
||||
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: true, action: {
|
||||
var changedValue: Int32?
|
||||
var globalValue: Bool?
|
||||
updateState { state in
|
||||
var state = state
|
||||
state.applyingSetting = true
|
||||
changedValue = state.changedValue
|
||||
globalValue = state.changedGlobalValue
|
||||
return state
|
||||
}
|
||||
|
||||
let resolvedDefaultValue: Int32
|
||||
if peer is TelegramUser {
|
||||
resolvedDefaultValue = defaultMyValue
|
||||
} else {
|
||||
resolvedDefaultValue = peerValue
|
||||
}
|
||||
|
||||
var updated = false
|
||||
if let changedValue = changedValue, changedValue != resolvedDefaultValue {
|
||||
updated = true
|
||||
}
|
||||
if let globalValue = globalValue, globalValue != defaultGlobalValue {
|
||||
updated = true
|
||||
}
|
||||
if updated {
|
||||
var resolvedValue: Int32? = changedValue ?? resolvedDefaultValue
|
||||
if resolvedValue == Int32.max {
|
||||
resolvedValue = nil
|
||||
}
|
||||
|
||||
let resolvedMaxValue: Int32
|
||||
if peer is TelegramUser {
|
||||
resolvedMaxValue = peerValue
|
||||
} else {
|
||||
resolvedMaxValue = Int32.max
|
||||
}
|
||||
|
||||
let resolvedGlobalValue = globalValue ?? defaultGlobalValue
|
||||
|
||||
let signal = setChatMessageAutoremoveTimeoutInteractively(account: context.account, peerId: peerId, timeout: resolvedValue, isGlobal: resolvedGlobalValue)
|
||||
|> deliverOnMainQueue
|
||||
|
||||
applyDisposable.set((signal
|
||||
|> deliverOnMainQueue).start(error: { _ in
|
||||
}, completed: {
|
||||
dismissImpl?()
|
||||
if resolvedValue != resolvedDefaultValue {
|
||||
completion(.updated(PeerAutoremoveSetupScreenResult.Updated(
|
||||
myValue: resolvedValue,
|
||||
limitedByValue: resolvedMaxValue == Int32.max ? nil : resolvedMaxValue
|
||||
)))
|
||||
} else {
|
||||
completion(.unchanged)
|
||||
}
|
||||
}))
|
||||
} else {
|
||||
dismissImpl?()
|
||||
completion(.unchanged)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
//TODO:localize
|
||||
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text("Auto-Deletion"), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
|
||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: peerAutoremoveSetupEntries(peer: peer, presentationData: presentationData, defaultMyValue: defaultMyValue, peerValue: peerValue, defaultGlobalValue: defaultGlobalValue, state: state), style: .blocks)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
}
|
||||
|> afterDisposed {
|
||||
actionsDisposable.dispose()
|
||||
}
|
||||
|
||||
let controller = ItemListController(context: context, state: signal)
|
||||
controller.navigationPresentation = .modal
|
||||
dismissImpl = { [weak controller] in
|
||||
controller?.view.endEditing(true)
|
||||
controller?.dismiss()
|
||||
}
|
||||
pushControllerImpl = { [weak controller] c in
|
||||
controller?.push(c)
|
||||
}
|
||||
return controller
|
||||
}
|
343
submodules/PeerInfoUI/Sources/PeerAutoremoveTimeoutItem.swift
Normal file
343
submodules/PeerInfoUI/Sources/PeerAutoremoveTimeoutItem.swift
Normal file
@ -0,0 +1,343 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import AsyncDisplayKit
|
||||
import SwiftSignalKit
|
||||
import TelegramCore
|
||||
import SyncCore
|
||||
import TelegramPresentationData
|
||||
import TelegramUIPreferences
|
||||
import LegacyComponents
|
||||
import ItemListUI
|
||||
import PresentationDataUtils
|
||||
import AppBundle
|
||||
|
||||
private func mapTimeoutToSliderValue(_ value: Int32) -> CGFloat {
|
||||
switch value {
|
||||
case 24 * 60 * 60:
|
||||
return 0.0
|
||||
case 7 * 24 * 60 * 60:
|
||||
return 1.0
|
||||
default:
|
||||
return 2.0
|
||||
}
|
||||
}
|
||||
|
||||
private func mapSliderValueToTimeout(_ value: CGFloat) -> Int32 {
|
||||
switch value {
|
||||
case 0.0:
|
||||
return 24 * 60 * 60
|
||||
case 1.0:
|
||||
return 7 * 24 * 60 * 60
|
||||
default:
|
||||
return Int32.max
|
||||
}
|
||||
}
|
||||
|
||||
class PeerRemoveTimeoutItem: ListViewItem, ItemListItem {
|
||||
let theme: PresentationTheme
|
||||
let value: Int32
|
||||
let maxValue: Int32
|
||||
let enabled: Bool
|
||||
let sectionId: ItemListSectionId
|
||||
let updated: (Int32) -> Void
|
||||
let tag: ItemListItemTag?
|
||||
|
||||
init(theme: PresentationTheme, value: Int32, maxValue: Int32, enabled: Bool = true, sectionId: ItemListSectionId, updated: @escaping (Int32) -> Void, tag: ItemListItemTag? = nil) {
|
||||
self.theme = theme
|
||||
self.value = value
|
||||
self.maxValue = maxValue
|
||||
self.enabled = enabled
|
||||
self.sectionId = sectionId
|
||||
self.updated = updated
|
||||
self.tag = tag
|
||||
}
|
||||
|
||||
func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
||||
async {
|
||||
let node = PeerRemoveTimeoutItemNode()
|
||||
let (layout, apply) = node.asyncLayout()(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem))
|
||||
|
||||
node.contentSize = layout.contentSize
|
||||
node.insets = layout.insets
|
||||
|
||||
Queue.mainQueue().async {
|
||||
completion(node, {
|
||||
return (nil, { _ in apply() })
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) {
|
||||
Queue.mainQueue().async {
|
||||
if let nodeValue = node() as? PeerRemoveTimeoutItemNode {
|
||||
let makeLayout = nodeValue.asyncLayout()
|
||||
|
||||
async {
|
||||
let (layout, apply) = makeLayout(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem))
|
||||
Queue.mainQueue().async {
|
||||
completion(layout, { _ in
|
||||
apply()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func generateKnobImage() -> UIImage? {
|
||||
return generateImage(CGSize(width: 40.0, height: 40.0), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.setShadow(offset: CGSize(width: 0.0, height: -3.0), blur: 8.0, color: UIColor(white: 0.0, alpha: 0.15).cgColor)
|
||||
context.setFillColor(UIColor.white.cgColor)
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(x: 6.0, y: 6.0), size: CGSize(width: 28.0, height: 28.0)))
|
||||
})
|
||||
}
|
||||
|
||||
class PeerRemoveTimeoutItemNode: ListViewItemNode, ItemListItemNode {
|
||||
private let backgroundNode: ASDisplayNode
|
||||
private let topStripeNode: ASDisplayNode
|
||||
private let bottomStripeNode: ASDisplayNode
|
||||
private let maskNode: ASImageNode
|
||||
|
||||
private var sliderView: TGPhotoEditorSliderView?
|
||||
private let titleNodes: [TextNode]
|
||||
private let disabledOverlayNode: ASDisplayNode
|
||||
|
||||
private var item: PeerRemoveTimeoutItem?
|
||||
private var layoutParams: ListViewItemLayoutParams?
|
||||
|
||||
var tag: ItemListItemTag? {
|
||||
return self.item?.tag
|
||||
}
|
||||
|
||||
init() {
|
||||
self.backgroundNode = ASDisplayNode()
|
||||
self.backgroundNode.isLayerBacked = true
|
||||
|
||||
self.topStripeNode = ASDisplayNode()
|
||||
self.topStripeNode.isLayerBacked = true
|
||||
|
||||
self.bottomStripeNode = ASDisplayNode()
|
||||
self.bottomStripeNode.isLayerBacked = true
|
||||
|
||||
self.maskNode = ASImageNode()
|
||||
|
||||
self.disabledOverlayNode = ASDisplayNode()
|
||||
|
||||
self.titleNodes = (0 ..< 3).map { _ in
|
||||
return TextNode()
|
||||
}
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false)
|
||||
|
||||
self.titleNodes.forEach(self.addSubnode)
|
||||
|
||||
self.addSubnode(self.disabledOverlayNode)
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
let sliderView = TGPhotoEditorSliderView()
|
||||
sliderView.enablePanHandling = true
|
||||
sliderView.trackCornerRadius = 1.0
|
||||
sliderView.lineSize = 2.0
|
||||
sliderView.dotSize = 5.0
|
||||
sliderView.minimumValue = 0.0
|
||||
sliderView.maximumValue = 2.0
|
||||
sliderView.startValue = 0.0
|
||||
sliderView.positionsCount = 3
|
||||
sliderView.useLinesForPositions = true
|
||||
sliderView.minimumUndottedValue = 0
|
||||
sliderView.disablesInteractiveTransitionGestureRecognizer = true
|
||||
if let item = self.item, let params = self.layoutParams {
|
||||
sliderView.isUserInteractionEnabled = item.enabled
|
||||
|
||||
sliderView.minimumUndottedValue = 0
|
||||
|
||||
sliderView.value = mapTimeoutToSliderValue(item.value)
|
||||
|
||||
sliderView.minimumUndottedValue = Int32(mapTimeoutToSliderValue(item.maxValue))
|
||||
|
||||
sliderView.backgroundColor = item.theme.list.itemBlocksBackgroundColor
|
||||
sliderView.backColor = item.theme.list.disclosureArrowColor
|
||||
sliderView.trackColor = item.enabled ? item.theme.list.itemAccentColor : item.theme.list.itemDisabledTextColor
|
||||
sliderView.knobImage = generateKnobImage()
|
||||
|
||||
let sliderInset: CGFloat = params.leftInset + 16.0
|
||||
|
||||
sliderView.frame = CGRect(origin: CGPoint(x: params.leftInset + sliderInset, y: 38.0), size: CGSize(width: params.width - params.leftInset - params.rightInset - sliderInset * 2.0, height: 44.0))
|
||||
}
|
||||
self.view.insertSubview(sliderView, belowSubview: self.disabledOverlayNode.view)
|
||||
sliderView.addTarget(self, action: #selector(self.sliderValueChanged), for: .valueChanged)
|
||||
self.sliderView = sliderView
|
||||
}
|
||||
|
||||
func asyncLayout() -> (_ item: PeerRemoveTimeoutItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
|
||||
let currentItem = self.item
|
||||
|
||||
let makeTitleNodeLayouts = self.titleNodes.map(TextNode.asyncLayout)
|
||||
|
||||
return { item, params, neighbors in
|
||||
var themeUpdated = false
|
||||
if currentItem?.theme !== item.theme {
|
||||
themeUpdated = true
|
||||
}
|
||||
|
||||
let contentSize: CGSize
|
||||
var insets: UIEdgeInsets
|
||||
let separatorHeight = UIScreenPixel
|
||||
|
||||
let titleLayouts = zip(0 ..< makeTitleNodeLayouts.count, makeTitleNodeLayouts).map { index, makeLayout -> (TextNodeLayout, () -> TextNode) in
|
||||
let text: String
|
||||
//TODO:localize
|
||||
switch index {
|
||||
case 0:
|
||||
text = "After 24 hours"
|
||||
case 1:
|
||||
text = "After 7 days"
|
||||
default:
|
||||
text = "Never"
|
||||
}
|
||||
return makeLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: text, font: Font.regular(13.0), textColor: item.theme.list.itemSecondaryTextColor), maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 100.0, height: 100.0)))
|
||||
}
|
||||
|
||||
contentSize = CGSize(width: params.width, height: 88.0)
|
||||
insets = itemListNeighborsGroupedInsets(neighbors)
|
||||
|
||||
let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets)
|
||||
let layoutSize = layout.size
|
||||
|
||||
return (layout, { [weak self] in
|
||||
if let strongSelf = self {
|
||||
let firstTime = strongSelf.item == nil
|
||||
strongSelf.item = item
|
||||
strongSelf.layoutParams = params
|
||||
|
||||
let leftInset = 16.0 + params.leftInset
|
||||
|
||||
strongSelf.backgroundNode.backgroundColor = item.theme.list.itemBlocksBackgroundColor
|
||||
strongSelf.topStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor
|
||||
strongSelf.bottomStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor
|
||||
|
||||
strongSelf.disabledOverlayNode.backgroundColor = item.theme.list.itemBlocksBackgroundColor.withAlphaComponent(0.4)
|
||||
strongSelf.disabledOverlayNode.isHidden = item.enabled
|
||||
strongSelf.disabledOverlayNode.frame = CGRect(origin: CGPoint(x: params.leftInset, y: 8.0), size: CGSize(width: params.width - params.leftInset - params.rightInset, height: 44.0))
|
||||
|
||||
if strongSelf.backgroundNode.supernode == nil {
|
||||
strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0)
|
||||
}
|
||||
if strongSelf.topStripeNode.supernode == nil {
|
||||
strongSelf.insertSubnode(strongSelf.topStripeNode, at: 1)
|
||||
}
|
||||
if strongSelf.bottomStripeNode.supernode == nil {
|
||||
strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2)
|
||||
}
|
||||
if strongSelf.maskNode.supernode == nil {
|
||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
case .sameSection(false):
|
||||
strongSelf.topStripeNode.isHidden = true
|
||||
default:
|
||||
hasTopCorners = true
|
||||
strongSelf.topStripeNode.isHidden = hasCorners
|
||||
}
|
||||
let bottomStripeInset: CGFloat
|
||||
let bottomStripeOffset: CGFloat
|
||||
switch neighbors.bottom {
|
||||
case .sameSection(false):
|
||||
bottomStripeInset = params.leftInset + 16.0
|
||||
bottomStripeOffset = -separatorHeight
|
||||
default:
|
||||
bottomStripeInset = 0.0
|
||||
bottomStripeOffset = 0.0
|
||||
hasBottomCorners = true
|
||||
strongSelf.bottomStripeNode.isHidden = hasCorners
|
||||
}
|
||||
|
||||
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
||||
|
||||
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
|
||||
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
|
||||
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight))
|
||||
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight))
|
||||
|
||||
zip(0 ..< titleLayouts.count, titleLayouts).forEach { index, layoutAndApply in
|
||||
let textNode = layoutAndApply.1()
|
||||
|
||||
let size = layoutAndApply.0.size
|
||||
switch index {
|
||||
case 0:
|
||||
textNode.frame = CGRect(origin: CGPoint(x: leftInset, y: 13.0), size: size)
|
||||
case 1:
|
||||
textNode.frame = CGRect(origin: CGPoint(x: floor((params.width - size.width) / 2.0), y: 13.0), size: size)
|
||||
default:
|
||||
textNode.frame = CGRect(origin: CGPoint(x: params.width - size.width - 16.0, y: 13.0), size: size)
|
||||
}
|
||||
}
|
||||
|
||||
if let sliderView = strongSelf.sliderView {
|
||||
sliderView.isUserInteractionEnabled = item.enabled
|
||||
sliderView.trackColor = item.enabled ? item.theme.list.itemAccentColor : item.theme.list.itemDisabledTextColor
|
||||
sliderView.minimumUndottedValue = Int32(mapTimeoutToSliderValue(item.maxValue))
|
||||
|
||||
if themeUpdated {
|
||||
sliderView.backgroundColor = item.theme.list.itemBlocksBackgroundColor
|
||||
sliderView.backColor = item.theme.list.disclosureArrowColor
|
||||
sliderView.knobImage = generateKnobImage()
|
||||
}
|
||||
|
||||
let value: CGFloat
|
||||
switch item.value {
|
||||
case 24 * 60 * 60:
|
||||
value = 0.0
|
||||
case 7 * 24 * 60 * 60:
|
||||
value = 1.0
|
||||
default:
|
||||
value = 2.0
|
||||
}
|
||||
if firstTime {
|
||||
sliderView.value = value
|
||||
}
|
||||
|
||||
let sliderInset: CGFloat = leftInset
|
||||
sliderView.frame = CGRect(origin: CGPoint(x: params.leftInset + sliderInset, y: 38.0), size: CGSize(width: params.width - params.leftInset - params.rightInset - sliderInset * 2.0, height: 44.0))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) {
|
||||
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4)
|
||||
}
|
||||
|
||||
override func animateRemoved(_ currentTimestamp: Double, duration: Double) {
|
||||
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false)
|
||||
}
|
||||
|
||||
@objc func sliderValueChanged() {
|
||||
guard let sliderView = self.sliderView else {
|
||||
return
|
||||
}
|
||||
let value: Int32
|
||||
switch sliderView.value {
|
||||
case 0.0:
|
||||
value = 24 * 60 * 60
|
||||
case 1.0:
|
||||
value = 7 * 24 * 60 * 60
|
||||
default:
|
||||
value = Int32.max
|
||||
}
|
||||
self.item?.updated(value)
|
||||
}
|
||||
}
|
||||
|
@ -79,6 +79,17 @@ public enum PostboxAccessChallengeData: PostboxCoding, Equatable, Codable {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
public var lockId: String? {
|
||||
switch self {
|
||||
case .none:
|
||||
return nil
|
||||
case let .numericalPassword(value):
|
||||
return "numericalPassword:\(value)"
|
||||
case let .plaintextPassword(value):
|
||||
return "plaintextPassword:\(value)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct AuthAccountRecord: PostboxCoding, Codable {
|
||||
|
@ -559,31 +559,31 @@ final class MutableChatListView {
|
||||
return self.sampledState.hole
|
||||
}
|
||||
|
||||
private func renderEntry(_ entry: MutableChatListEntry, postbox: Postbox, renderMessage: (IntermediateMessage) -> Message, getPeer: (PeerId) -> Peer?, getPeerNotificationSettings: (PeerId) -> PeerNotificationSettings?, getPeerPresence: (PeerId) -> PeerPresence?) -> MutableChatListEntry? {
|
||||
private func renderEntry(_ entry: MutableChatListEntry, postbox: Postbox) -> MutableChatListEntry? {
|
||||
switch entry {
|
||||
case let .IntermediateMessageEntry(index, messageIndex):
|
||||
var renderedMessages: [Message] = []
|
||||
if let messageIndex = messageIndex {
|
||||
if let messageGroup = postbox.messageHistoryTable.getMessageGroup(at: messageIndex, limit: 10) {
|
||||
renderedMessages.append(contentsOf: messageGroup.compactMap(renderMessage))
|
||||
renderedMessages.append(contentsOf: messageGroup.compactMap(postbox.renderIntermediateMessage))
|
||||
}
|
||||
}
|
||||
var peers = SimpleDictionary<PeerId, Peer>()
|
||||
var notificationSettings: PeerNotificationSettings?
|
||||
var presence: PeerPresence?
|
||||
var isContact: Bool = false
|
||||
if let peer = getPeer(index.messageIndex.id.peerId) {
|
||||
if let peer = postbox.peerTable.get(index.messageIndex.id.peerId) {
|
||||
peers[peer.id] = peer
|
||||
if let associatedPeerId = peer.associatedPeerId {
|
||||
if let associatedPeer = getPeer(associatedPeerId) {
|
||||
if let associatedPeer = postbox.peerTable.get(associatedPeerId) {
|
||||
peers[associatedPeer.id] = associatedPeer
|
||||
}
|
||||
notificationSettings = getPeerNotificationSettings(associatedPeerId)
|
||||
presence = getPeerPresence(associatedPeerId)
|
||||
notificationSettings = postbox.peerNotificationSettingsTable.getEffective(associatedPeerId)
|
||||
presence = postbox.peerPresenceTable.get(associatedPeerId)
|
||||
isContact = postbox.contactsTable.isContact(peerId: associatedPeerId)
|
||||
} else {
|
||||
notificationSettings = getPeerNotificationSettings(index.messageIndex.id.peerId)
|
||||
presence = getPeerPresence(index.messageIndex.id.peerId)
|
||||
notificationSettings = postbox.peerNotificationSettingsTable.getEffective(index.messageIndex.id.peerId)
|
||||
presence = postbox.peerPresenceTable.get(index.messageIndex.id.peerId)
|
||||
isContact = postbox.contactsTable.isContact(peerId: peer.id)
|
||||
}
|
||||
}
|
||||
@ -597,9 +597,9 @@ final class MutableChatListView {
|
||||
}
|
||||
}
|
||||
|
||||
func render(postbox: Postbox, renderMessage: (IntermediateMessage) -> Message, getPeer: (PeerId) -> Peer?, getPeerNotificationSettings: (PeerId) -> PeerNotificationSettings?, getPeerPresence: (PeerId) -> PeerPresence?) {
|
||||
func render(postbox: Postbox) {
|
||||
for i in 0 ..< self.additionalItemEntries.count {
|
||||
if let updatedEntry = self.renderEntry(self.additionalItemEntries[i].entry, postbox: postbox, renderMessage: renderMessage, getPeer: getPeer, getPeerNotificationSettings: getPeerNotificationSettings, getPeerPresence: getPeerPresence) {
|
||||
if let updatedEntry = self.renderEntry(self.additionalItemEntries[i].entry, postbox: postbox) {
|
||||
self.additionalItemEntries[i].entry = updatedEntry
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ final class MutableContactPeersView {
|
||||
self.includePresences = includePresences
|
||||
}
|
||||
|
||||
func replay(replacePeerIds: Set<PeerId>?, updatedPeerPresences: [PeerId: PeerPresence], getPeer: (PeerId) -> Peer?, getPeerPresence: (PeerId) -> PeerPresence?) -> Bool {
|
||||
func replay(postbox: Postbox, replacePeerIds: Set<PeerId>?, updatedPeerPresences: [PeerId: PeerPresence]) -> Bool {
|
||||
var updated = false
|
||||
if let replacePeerIds = replacePeerIds {
|
||||
let removedPeerIds = self.peerIds.subtracting(replacePeerIds)
|
||||
@ -29,11 +29,11 @@ final class MutableContactPeersView {
|
||||
}
|
||||
|
||||
for peerId in addedPeerIds {
|
||||
if let peer = getPeer(peerId) {
|
||||
if let peer = postbox.peerTable.get(peerId) {
|
||||
self.peers[peerId] = peer
|
||||
}
|
||||
if self.includePresences {
|
||||
if let presence = getPeerPresence(peerId) {
|
||||
if let presence = postbox.peerPresenceTable.get(peerId) {
|
||||
self.peerPresences[peerId] = presence
|
||||
}
|
||||
}
|
||||
|
@ -493,6 +493,7 @@ public struct MessageForwardInfo: Equatable {
|
||||
public protocol MessageAttribute: class, PostboxCoding {
|
||||
var associatedPeerIds: [PeerId] { get }
|
||||
var associatedMessageIds: [MessageId] { get }
|
||||
var automaticTimestampBasedAttribute: (UInt16, Int32)? { get }
|
||||
}
|
||||
|
||||
public extension MessageAttribute {
|
||||
@ -503,6 +504,10 @@ public extension MessageAttribute {
|
||||
var associatedMessageIds: [MessageId] {
|
||||
return []
|
||||
}
|
||||
|
||||
var automaticTimestampBasedAttribute: (UInt16, Int32)? {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
public struct MessageGroupInfo: Equatable {
|
||||
|
@ -83,13 +83,14 @@ final class MessageHistoryTable: Table {
|
||||
let threadsTable: MessageHistoryThreadsTable
|
||||
let globalTagsTable: GlobalMessageHistoryTagsTable
|
||||
let localTagsTable: LocalMessageHistoryTagsTable
|
||||
let timeBasedAttributesTable: TimestampBasedMessageAttributesTable
|
||||
let readStateTable: MessageHistoryReadStateTable
|
||||
let synchronizeReadStateTable: MessageHistorySynchronizeReadStateTable
|
||||
let textIndexTable: MessageHistoryTextIndexTable
|
||||
let summaryTable: MessageHistoryTagsSummaryTable
|
||||
let pendingActionsTable: PendingMessageActionsTable
|
||||
|
||||
init(valueBox: ValueBox, table: ValueBoxTable, seedConfiguration: SeedConfiguration, messageHistoryIndexTable: MessageHistoryIndexTable, messageHistoryHoleIndexTable: MessageHistoryHoleIndexTable, messageMediaTable: MessageMediaTable, historyMetadataTable: MessageHistoryMetadataTable, globallyUniqueMessageIdsTable: MessageGloballyUniqueIdTable, unsentTable: MessageHistoryUnsentTable, failedTable: MessageHistoryFailedTable, tagsTable: MessageHistoryTagsTable, threadsTable: MessageHistoryThreadsTable, globalTagsTable: GlobalMessageHistoryTagsTable, localTagsTable: LocalMessageHistoryTagsTable, readStateTable: MessageHistoryReadStateTable, synchronizeReadStateTable: MessageHistorySynchronizeReadStateTable, textIndexTable: MessageHistoryTextIndexTable, summaryTable: MessageHistoryTagsSummaryTable, pendingActionsTable: PendingMessageActionsTable) {
|
||||
init(valueBox: ValueBox, table: ValueBoxTable, seedConfiguration: SeedConfiguration, messageHistoryIndexTable: MessageHistoryIndexTable, messageHistoryHoleIndexTable: MessageHistoryHoleIndexTable, messageMediaTable: MessageMediaTable, historyMetadataTable: MessageHistoryMetadataTable, globallyUniqueMessageIdsTable: MessageGloballyUniqueIdTable, unsentTable: MessageHistoryUnsentTable, failedTable: MessageHistoryFailedTable, tagsTable: MessageHistoryTagsTable, threadsTable: MessageHistoryThreadsTable, globalTagsTable: GlobalMessageHistoryTagsTable, localTagsTable: LocalMessageHistoryTagsTable, timeBasedAttributesTable: TimestampBasedMessageAttributesTable, readStateTable: MessageHistoryReadStateTable, synchronizeReadStateTable: MessageHistorySynchronizeReadStateTable, textIndexTable: MessageHistoryTextIndexTable, summaryTable: MessageHistoryTagsSummaryTable, pendingActionsTable: PendingMessageActionsTable) {
|
||||
self.seedConfiguration = seedConfiguration
|
||||
self.messageHistoryIndexTable = messageHistoryIndexTable
|
||||
self.messageHistoryHoleIndexTable = messageHistoryHoleIndexTable
|
||||
@ -102,6 +103,7 @@ final class MessageHistoryTable: Table {
|
||||
self.threadsTable = threadsTable
|
||||
self.globalTagsTable = globalTagsTable
|
||||
self.localTagsTable = localTagsTable
|
||||
self.timeBasedAttributesTable = timeBasedAttributesTable
|
||||
self.readStateTable = readStateTable
|
||||
self.synchronizeReadStateTable = synchronizeReadStateTable
|
||||
self.textIndexTable = textIndexTable
|
||||
@ -175,7 +177,7 @@ final class MessageHistoryTable: Table {
|
||||
return dict
|
||||
}
|
||||
|
||||
private func processIndexOperationsCommitAccumulatedRemoveIndices(peerId: PeerId, accumulatedRemoveIndices: inout [MessageIndex], updatedCombinedState: inout CombinedPeerReadState?, invalidateReadState: inout Bool, unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], outputOperations: inout [MessageHistoryOperation], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation]) {
|
||||
private func processIndexOperationsCommitAccumulatedRemoveIndices(peerId: PeerId, accumulatedRemoveIndices: inout [MessageIndex], updatedCombinedState: inout CombinedPeerReadState?, invalidateReadState: inout Bool, unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], outputOperations: inout [MessageHistoryOperation], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], timestampBasedMessageAttributesOperations: inout [TimestampBasedMessageAttributesOperation]) {
|
||||
if !accumulatedRemoveIndices.isEmpty {
|
||||
let (combinedState, invalidate) = self.readStateTable.deleteMessages(peerId, indices: accumulatedRemoveIndices, incomingStatsInIndices: { peerId, namespace, indices in
|
||||
return self.incomingMessageStatsInIndices(peerId, namespace: namespace, indices: indices)
|
||||
@ -193,7 +195,7 @@ final class MessageHistoryTable: Table {
|
||||
var globalIndicesWithMetadata: [(GlobalMessageTags, MessageIndex)] = []
|
||||
|
||||
for index in bucket {
|
||||
let tagsAndGlobalTags = self.justRemove(index, unsentMessageOperations: &unsentMessageOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations)
|
||||
let tagsAndGlobalTags = self.justRemove(index, unsentMessageOperations: &unsentMessageOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: ×tampBasedMessageAttributesOperations)
|
||||
if let (tags, globalTags) = tagsAndGlobalTags {
|
||||
indicesWithMetadata.append((index, tags))
|
||||
|
||||
@ -220,7 +222,7 @@ final class MessageHistoryTable: Table {
|
||||
}
|
||||
}
|
||||
|
||||
private func processIndexOperations(_ peerId: PeerId, operations: [MessageHistoryIndexOperation], processedOperationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation]) {
|
||||
private func processIndexOperations(_ peerId: PeerId, operations: [MessageHistoryIndexOperation], processedOperationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], timestampBasedMessageAttributesOperations: inout [TimestampBasedMessageAttributesOperation]) {
|
||||
let sharedKey = self.key(MessageIndex(id: MessageId(peerId: PeerId(namespace: 0, id: 0), namespace: 0, id: 0), timestamp: 0))
|
||||
let sharedBuffer = WriteBuffer()
|
||||
let sharedEncoder = PostboxEncoder()
|
||||
@ -251,7 +253,7 @@ final class MessageHistoryTable: Table {
|
||||
for operation in operations {
|
||||
switch operation {
|
||||
case let .InsertMessage(storeMessage):
|
||||
processIndexOperationsCommitAccumulatedRemoveIndices(peerId: peerId, accumulatedRemoveIndices: &accumulatedRemoveIndices, updatedCombinedState: &updatedCombinedState, invalidateReadState: &invalidateReadState, unsentMessageOperations: &unsentMessageOperations, outputOperations: &outputOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations)
|
||||
processIndexOperationsCommitAccumulatedRemoveIndices(peerId: peerId, accumulatedRemoveIndices: &accumulatedRemoveIndices, updatedCombinedState: &updatedCombinedState, invalidateReadState: &invalidateReadState, unsentMessageOperations: &unsentMessageOperations, outputOperations: &outputOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: ×tampBasedMessageAttributesOperations)
|
||||
|
||||
let (message, updatedGroupInfos) = self.justInsertMessage(storeMessage, sharedKey: sharedKey, sharedBuffer: sharedBuffer, sharedEncoder: sharedEncoder, localTagsOperations: &localTagsOperations, updateExistingMedia: &updateExistingMedia)
|
||||
outputOperations.append(.InsertMessage(message))
|
||||
@ -301,15 +303,20 @@ final class MessageHistoryTable: Table {
|
||||
if !message.localTags.isEmpty {
|
||||
self.localTagsTable.set(id: message.id, tags: message.localTags, previousTags: [], operations: &localTagsOperations)
|
||||
}
|
||||
for attribute in MessageHistoryTable.renderMessageAttributes(message) {
|
||||
if let (tag, timestamp) = attribute.automaticTimestampBasedAttribute {
|
||||
self.timeBasedAttributesTable.set(tag: tag, id: message.id, timestamp: timestamp, operations: ×tampBasedMessageAttributesOperations)
|
||||
}
|
||||
}
|
||||
if !message.flags.intersection(.IsIncomingMask).isEmpty {
|
||||
accumulatedAddedIncomingMessageIndices.insert(message.index)
|
||||
}
|
||||
case let .InsertExistingMessage(storeMessage):
|
||||
commitAccumulatedAddedIndices()
|
||||
processIndexOperationsCommitAccumulatedRemoveIndices(peerId: peerId, accumulatedRemoveIndices: &accumulatedRemoveIndices, updatedCombinedState: &updatedCombinedState, invalidateReadState: &invalidateReadState, unsentMessageOperations: &unsentMessageOperations, outputOperations: &outputOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations)
|
||||
processIndexOperationsCommitAccumulatedRemoveIndices(peerId: peerId, accumulatedRemoveIndices: &accumulatedRemoveIndices, updatedCombinedState: &updatedCombinedState, invalidateReadState: &invalidateReadState, unsentMessageOperations: &unsentMessageOperations, outputOperations: &outputOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: ×tampBasedMessageAttributesOperations)
|
||||
|
||||
var updatedGroupInfos: [MessageId: MessageGroupInfo] = [:]
|
||||
if let (message, previousTags) = self.justUpdate(storeMessage.index, message: storeMessage, keepLocalTags: true, sharedKey: sharedKey, sharedBuffer: sharedBuffer, sharedEncoder: sharedEncoder, unsentMessageOperations: &unsentMessageOperations, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, updatedGroupInfos: &updatedGroupInfos, localTagsOperations: &localTagsOperations, updatedMedia: &updatedMedia) {
|
||||
if let (message, previousTags) = self.justUpdate(storeMessage.index, message: storeMessage, keepLocalTags: true, sharedKey: sharedKey, sharedBuffer: sharedBuffer, sharedEncoder: sharedEncoder, unsentMessageOperations: &unsentMessageOperations, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, updatedGroupInfos: &updatedGroupInfos, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: ×tampBasedMessageAttributesOperations, updatedMedia: &updatedMedia) {
|
||||
outputOperations.append(.Remove([(storeMessage.index, previousTags)]))
|
||||
outputOperations.append(.InsertMessage(message))
|
||||
if !updatedGroupInfos.isEmpty {
|
||||
@ -321,10 +328,10 @@ final class MessageHistoryTable: Table {
|
||||
accumulatedRemoveIndices.append(index)
|
||||
case let .Update(index, storeMessage):
|
||||
commitAccumulatedAddedIndices()
|
||||
processIndexOperationsCommitAccumulatedRemoveIndices(peerId: peerId, accumulatedRemoveIndices: &accumulatedRemoveIndices, updatedCombinedState: &updatedCombinedState, invalidateReadState: &invalidateReadState, unsentMessageOperations: &unsentMessageOperations, outputOperations: &outputOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations)
|
||||
processIndexOperationsCommitAccumulatedRemoveIndices(peerId: peerId, accumulatedRemoveIndices: &accumulatedRemoveIndices, updatedCombinedState: &updatedCombinedState, invalidateReadState: &invalidateReadState, unsentMessageOperations: &unsentMessageOperations, outputOperations: &outputOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: ×tampBasedMessageAttributesOperations)
|
||||
|
||||
var updatedGroupInfos: [MessageId: MessageGroupInfo] = [:]
|
||||
if let (message, previousTags) = self.justUpdate(index, message: storeMessage, keepLocalTags: false, sharedKey: sharedKey, sharedBuffer: sharedBuffer, sharedEncoder: sharedEncoder, unsentMessageOperations: &unsentMessageOperations, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, updatedGroupInfos: &updatedGroupInfos, localTagsOperations: &localTagsOperations, updatedMedia: &updatedMedia) {
|
||||
if let (message, previousTags) = self.justUpdate(index, message: storeMessage, keepLocalTags: false, sharedKey: sharedKey, sharedBuffer: sharedBuffer, sharedEncoder: sharedEncoder, unsentMessageOperations: &unsentMessageOperations, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, updatedGroupInfos: &updatedGroupInfos, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: ×tampBasedMessageAttributesOperations, updatedMedia: &updatedMedia) {
|
||||
outputOperations.append(.Remove([(index, previousTags)]))
|
||||
outputOperations.append(.InsertMessage(message))
|
||||
if !updatedGroupInfos.isEmpty {
|
||||
@ -340,10 +347,10 @@ final class MessageHistoryTable: Table {
|
||||
}
|
||||
case let .UpdateTimestamp(index, timestamp):
|
||||
commitAccumulatedAddedIndices()
|
||||
processIndexOperationsCommitAccumulatedRemoveIndices(peerId: peerId, accumulatedRemoveIndices: &accumulatedRemoveIndices, updatedCombinedState: &updatedCombinedState, invalidateReadState: &invalidateReadState, unsentMessageOperations: &unsentMessageOperations, outputOperations: &outputOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations)
|
||||
processIndexOperationsCommitAccumulatedRemoveIndices(peerId: peerId, accumulatedRemoveIndices: &accumulatedRemoveIndices, updatedCombinedState: &updatedCombinedState, invalidateReadState: &invalidateReadState, unsentMessageOperations: &unsentMessageOperations, outputOperations: &outputOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: ×tampBasedMessageAttributesOperations)
|
||||
|
||||
var updatedGroupInfos: [MessageId: MessageGroupInfo] = [:]
|
||||
let tagsAndGlobalTags = self.justUpdateTimestamp(index, timestamp: timestamp, unsentMessageOperations: &unsentMessageOperations, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, updatedGroupInfos: &updatedGroupInfos, localTagsOperations: &localTagsOperations, updatedMedia: &updatedMedia)
|
||||
let tagsAndGlobalTags = self.justUpdateTimestamp(index, timestamp: timestamp, unsentMessageOperations: &unsentMessageOperations, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, updatedGroupInfos: &updatedGroupInfos, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: ×tampBasedMessageAttributesOperations, updatedMedia: &updatedMedia)
|
||||
outputOperations.append(.UpdateTimestamp(index, timestamp))
|
||||
if !updatedGroupInfos.isEmpty {
|
||||
outputOperations.append(.UpdateGroupInfos(updatedGroupInfos))
|
||||
@ -357,7 +364,7 @@ final class MessageHistoryTable: Table {
|
||||
}
|
||||
|
||||
commitAccumulatedAddedIndices()
|
||||
processIndexOperationsCommitAccumulatedRemoveIndices(peerId: peerId, accumulatedRemoveIndices: &accumulatedRemoveIndices, updatedCombinedState: &updatedCombinedState, invalidateReadState: &invalidateReadState, unsentMessageOperations: &unsentMessageOperations, outputOperations: &outputOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations)
|
||||
processIndexOperationsCommitAccumulatedRemoveIndices(peerId: peerId, accumulatedRemoveIndices: &accumulatedRemoveIndices, updatedCombinedState: &updatedCombinedState, invalidateReadState: &invalidateReadState, unsentMessageOperations: &unsentMessageOperations, outputOperations: &outputOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: ×tampBasedMessageAttributesOperations)
|
||||
|
||||
if let updatedCombinedState = updatedCombinedState {
|
||||
outputOperations.append(.UpdateReadState(peerId, updatedCombinedState))
|
||||
@ -397,7 +404,7 @@ final class MessageHistoryTable: Table {
|
||||
return internalStoreMessages
|
||||
}
|
||||
|
||||
func addMessages(messages: [StoreMessage], operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], processMessages: (([PeerId : [StoreMessage]]) -> Void)?) -> [Int64: MessageId] {
|
||||
func addMessages(messages: [StoreMessage], operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], timestampBasedMessageAttributesOperations: inout [TimestampBasedMessageAttributesOperation], processMessages: (([PeerId : [StoreMessage]]) -> Void)?) -> [Int64: MessageId] {
|
||||
let messagesByPeerId = self.messagesGroupedByPeerId(messages)
|
||||
var globallyUniqueIdToMessageId: [Int64: MessageId] = [:]
|
||||
var globalTagsInitialized = Set<GlobalMessageTags>()
|
||||
@ -419,7 +426,7 @@ final class MessageHistoryTable: Table {
|
||||
}
|
||||
self.messageHistoryIndexTable.addMessages(internalPeerMessages, operations: &operations)
|
||||
|
||||
self.processIndexOperations(peerId, operations: operations, processedOperationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations)
|
||||
self.processIndexOperations(peerId, operations: operations, processedOperationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: ×tampBasedMessageAttributesOperations)
|
||||
}
|
||||
|
||||
processMessages?(messagesByPeerId)
|
||||
@ -427,7 +434,7 @@ final class MessageHistoryTable: Table {
|
||||
return globallyUniqueIdToMessageId
|
||||
}
|
||||
|
||||
func removeMessages(_ messageIds: [MessageId], operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], forEachMedia: (Media) -> Void) {
|
||||
func removeMessages(_ messageIds: [MessageId], operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], timestampBasedMessageAttributesOperations: inout [TimestampBasedMessageAttributesOperation], forEachMedia: (Media) -> Void) {
|
||||
for (peerId, messageIds) in self.messageIdsByPeerId(messageIds) {
|
||||
var operations: [MessageHistoryIndexOperation] = []
|
||||
|
||||
@ -443,11 +450,11 @@ final class MessageHistoryTable: Table {
|
||||
}
|
||||
}
|
||||
}
|
||||
self.processIndexOperations(peerId, operations: operations, processedOperationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations)
|
||||
self.processIndexOperations(peerId, operations: operations, processedOperationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: ×tampBasedMessageAttributesOperations)
|
||||
}
|
||||
}
|
||||
|
||||
func removeMessagesInRange(peerId: PeerId, namespace: MessageId.Namespace, minId: MessageId.Id, maxId: MessageId.Id, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], forEachMedia: (Media) -> Void) {
|
||||
func removeMessagesInRange(peerId: PeerId, namespace: MessageId.Namespace, minId: MessageId.Id, maxId: MessageId.Id, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], timestampBasedMessageAttributesOperations: inout [TimestampBasedMessageAttributesOperation], forEachMedia: (Media) -> Void) {
|
||||
var operations: [MessageHistoryIndexOperation] = []
|
||||
self.messageHistoryIndexTable.removeMessagesInRange(peerId: peerId, namespace: namespace, minId: minId, maxId: maxId, operations: &operations)
|
||||
for operation in operations {
|
||||
@ -460,20 +467,20 @@ final class MessageHistoryTable: Table {
|
||||
}
|
||||
}
|
||||
|
||||
self.processIndexOperations(peerId, operations: operations, processedOperationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations)
|
||||
self.processIndexOperations(peerId, operations: operations, processedOperationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: ×tampBasedMessageAttributesOperations)
|
||||
}
|
||||
|
||||
func clearHistory(peerId: PeerId, namespaces: MessageIdNamespaces, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], forEachMedia: (Media) -> Void) {
|
||||
func clearHistory(peerId: PeerId, namespaces: MessageIdNamespaces, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], timestampBasedMessageAttributesOperations: inout [TimestampBasedMessageAttributesOperation], forEachMedia: (Media) -> Void) {
|
||||
let indices = self.allMessageIndices(peerId: peerId).filter { namespaces.contains($0.id.namespace) }
|
||||
self.removeMessages(indices.map { $0.id }, operationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, forEachMedia: forEachMedia)
|
||||
self.removeMessages(indices.map { $0.id }, operationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: ×tampBasedMessageAttributesOperations, forEachMedia: forEachMedia)
|
||||
}
|
||||
|
||||
func removeAllMessagesWithAuthor(peerId: PeerId, authorId: PeerId, namespace: MessageId.Namespace, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], forEachMedia: (Media) -> Void) {
|
||||
func removeAllMessagesWithAuthor(peerId: PeerId, authorId: PeerId, namespace: MessageId.Namespace, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], timestampBasedMessageAttributesOperations: inout [TimestampBasedMessageAttributesOperation], forEachMedia: (Media) -> Void) {
|
||||
let indices = self.allIndicesWithAuthor(peerId: peerId, authorId: authorId, namespace: namespace)
|
||||
self.removeMessages(indices.map { $0.id }, operationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, forEachMedia: forEachMedia)
|
||||
self.removeMessages(indices.map { $0.id }, operationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: ×tampBasedMessageAttributesOperations, forEachMedia: forEachMedia)
|
||||
}
|
||||
|
||||
func removeAllMessagesWithGlobalTag(tag: GlobalMessageTags, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], forEachMedia: (Media) -> Void) {
|
||||
func removeAllMessagesWithGlobalTag(tag: GlobalMessageTags, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], timestampBasedMessageAttributesOperations: inout [TimestampBasedMessageAttributesOperation], forEachMedia: (Media) -> Void) {
|
||||
var indices: [MessageIndex] = []
|
||||
for entry in self.allIndicesWithGlobalTag(tag: tag) {
|
||||
switch entry {
|
||||
@ -483,24 +490,24 @@ final class MessageHistoryTable: Table {
|
||||
break
|
||||
}
|
||||
}
|
||||
self.removeMessages(indices.map { $0.id }, operationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, forEachMedia: forEachMedia)
|
||||
self.removeMessages(indices.map { $0.id }, operationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: ×tampBasedMessageAttributesOperations, forEachMedia: forEachMedia)
|
||||
}
|
||||
|
||||
func removeAllMessagesWithForwardAuthor(peerId: PeerId, forwardAuthorId: PeerId, namespace: MessageId.Namespace, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], forEachMedia: (Media) -> Void) {
|
||||
func removeAllMessagesWithForwardAuthor(peerId: PeerId, forwardAuthorId: PeerId, namespace: MessageId.Namespace, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], timestampBasedMessageAttributesOperations: inout [TimestampBasedMessageAttributesOperation], forEachMedia: (Media) -> Void) {
|
||||
let indices = self.allIndicesWithForwardAuthor(peerId: peerId, forwardAuthorId: forwardAuthorId, namespace: namespace)
|
||||
self.removeMessages(indices.map { $0.id }, operationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, forEachMedia: forEachMedia)
|
||||
self.removeMessages(indices.map { $0.id }, operationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: ×tampBasedMessageAttributesOperations, forEachMedia: forEachMedia)
|
||||
}
|
||||
|
||||
func updateMessage(_ id: MessageId, message: StoreMessage, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation]) {
|
||||
func updateMessage(_ id: MessageId, message: StoreMessage, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], timestampBasedMessageAttributesOperations: inout [TimestampBasedMessageAttributesOperation]) {
|
||||
var operations: [MessageHistoryIndexOperation] = []
|
||||
self.messageHistoryIndexTable.updateMessage(id, message: self.internalStoreMessages([message]).first!, operations: &operations)
|
||||
self.processIndexOperations(id.peerId, operations: operations, processedOperationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations)
|
||||
self.processIndexOperations(id.peerId, operations: operations, processedOperationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: ×tampBasedMessageAttributesOperations)
|
||||
}
|
||||
|
||||
func updateMessageTimestamp(_ id: MessageId, timestamp: Int32, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation]) {
|
||||
func updateMessageTimestamp(_ id: MessageId, timestamp: Int32, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], timestampBasedMessageAttributesOperations: inout [TimestampBasedMessageAttributesOperation]) {
|
||||
var operations: [MessageHistoryIndexOperation] = []
|
||||
self.messageHistoryIndexTable.updateTimestamp(id, timestamp: timestamp, operations: &operations)
|
||||
self.processIndexOperations(id.peerId, operations: operations, processedOperationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations)
|
||||
self.processIndexOperations(id.peerId, operations: operations, processedOperationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: ×tampBasedMessageAttributesOperations)
|
||||
}
|
||||
|
||||
func updateMedia(_ id: MediaId, media: Media?, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], updatedMessageIndices: inout Set<MessageIndex>) {
|
||||
@ -812,7 +819,7 @@ final class MessageHistoryTable: Table {
|
||||
})
|
||||
}
|
||||
|
||||
func offsetPendingMessagesTimestamps(lowerBound: MessageId, excludeIds: Set<MessageId>, timestamp: Int32, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation]) {
|
||||
func offsetPendingMessagesTimestamps(lowerBound: MessageId, excludeIds: Set<MessageId>, timestamp: Int32, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], timestampBasedMessageAttributesOperations: inout [TimestampBasedMessageAttributesOperation]) {
|
||||
var peerMessageIds: [MessageId] = []
|
||||
for messageId in self.unsentTable.get() {
|
||||
if messageId.peerId == lowerBound.peerId && messageId.namespace == lowerBound.namespace && messageId.id > lowerBound.id {
|
||||
@ -825,7 +832,7 @@ final class MessageHistoryTable: Table {
|
||||
peerMessageIds.sort()
|
||||
|
||||
for messageId in peerMessageIds.reversed() {
|
||||
self.updateMessageTimestamp(messageId, timestamp: timestamp, operationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations)
|
||||
self.updateMessageTimestamp(messageId, timestamp: timestamp, operationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: ×tampBasedMessageAttributesOperations)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1247,7 +1254,7 @@ final class MessageHistoryTable: Table {
|
||||
return result
|
||||
}
|
||||
|
||||
private func justRemove(_ index: MessageIndex, unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation]) -> (MessageTags, GlobalMessageTags)? {
|
||||
private func justRemove(_ index: MessageIndex, unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], timestampBasedMessageAttributesOperations: inout [TimestampBasedMessageAttributesOperation]) -> (MessageTags, GlobalMessageTags)? {
|
||||
let key = self.key(index)
|
||||
if let value = self.valueBox.get(self.table, key: key) {
|
||||
let resultTags: MessageTags
|
||||
@ -1292,6 +1299,11 @@ final class MessageHistoryTable: Table {
|
||||
if !message.localTags.isEmpty {
|
||||
self.localTagsTable.set(id: index.id, tags: [], previousTags: message.localTags, operations: &localTagsOperations)
|
||||
}
|
||||
for attribute in MessageHistoryTable.renderMessageAttributes(message) {
|
||||
if let (tag, _) = attribute.automaticTimestampBasedAttribute {
|
||||
self.timeBasedAttributesTable.remove(tag: tag, id: message.id, operations: ×tampBasedMessageAttributesOperations)
|
||||
}
|
||||
}
|
||||
|
||||
for mediaId in message.referencedMedia {
|
||||
let _ = self.messageMediaTable.removeReference(mediaId)
|
||||
@ -1409,7 +1421,7 @@ final class MessageHistoryTable: Table {
|
||||
})
|
||||
}
|
||||
|
||||
private func justUpdate(_ index: MessageIndex, message: InternalStoreMessage, keepLocalTags: Bool, sharedKey: ValueBoxKey, sharedBuffer: WriteBuffer, sharedEncoder: PostboxEncoder, unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], updatedGroupInfos: inout [MessageId: MessageGroupInfo], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], updatedMedia: inout [MediaId: Media?]) -> (IntermediateMessage, MessageTags)? {
|
||||
private func justUpdate(_ index: MessageIndex, message: InternalStoreMessage, keepLocalTags: Bool, sharedKey: ValueBoxKey, sharedBuffer: WriteBuffer, sharedEncoder: PostboxEncoder, unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], updatedGroupInfos: inout [MessageId: MessageGroupInfo], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], timestampBasedMessageAttributesOperations: inout [TimestampBasedMessageAttributesOperation], updatedMedia: inout [MediaId: Media?]) -> (IntermediateMessage, MessageTags)? {
|
||||
if let previousMessage = self.getMessage(index) {
|
||||
var mediaToUpdate: [Media] = []
|
||||
|
||||
@ -1503,6 +1515,41 @@ final class MessageHistoryTable: Table {
|
||||
}
|
||||
}
|
||||
|
||||
var previousTimestampBasedAttibutes: [UInt16: Int32] = [:]
|
||||
for attribute in MessageHistoryTable.renderMessageAttributes(previousMessage) {
|
||||
if let (tag, timestamp) = attribute.automaticTimestampBasedAttribute {
|
||||
previousTimestampBasedAttibutes[tag] = timestamp
|
||||
}
|
||||
}
|
||||
if previousMessage.id != message.id {
|
||||
for tag in previousTimestampBasedAttibutes.keys {
|
||||
self.timeBasedAttributesTable.remove(tag: tag, id: previousMessage.id, operations: ×tampBasedMessageAttributesOperations)
|
||||
}
|
||||
for attribute in message.attributes {
|
||||
if let (tag, timestamp) = attribute.automaticTimestampBasedAttribute {
|
||||
self.timeBasedAttributesTable.set(tag: tag, id: message.id, timestamp: timestamp, operations: ×tampBasedMessageAttributesOperations)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var updatedTimestampBasedAttibuteTags: [UInt16] = []
|
||||
for attribute in message.attributes {
|
||||
if let (tag, timestamp) = attribute.automaticTimestampBasedAttribute {
|
||||
updatedTimestampBasedAttibuteTags.append(tag)
|
||||
if previousTimestampBasedAttibutes[tag] != timestamp {
|
||||
self.timeBasedAttributesTable.remove(tag: tag, id: previousMessage.id, operations: ×tampBasedMessageAttributesOperations)
|
||||
self.timeBasedAttributesTable.set(tag: tag, id: message.id, timestamp: timestamp, operations: ×tampBasedMessageAttributesOperations)
|
||||
}
|
||||
}
|
||||
}
|
||||
for tag in previousTimestampBasedAttibutes.keys {
|
||||
if !updatedTimestampBasedAttibuteTags.contains(tag) {
|
||||
self.timeBasedAttributesTable.remove(tag: tag, id: previousMessage.id, operations: ×tampBasedMessageAttributesOperations)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//self.timeBasedAttributesTable.remove(tag: tag, id: message.id, operations: ×tampBasedMessageAttributesOperations)
|
||||
|
||||
if message.globallyUniqueId != previousMessage.globallyUniqueId {
|
||||
if let globallyUniqueId = previousMessage.globallyUniqueId {
|
||||
self.globallyUniqueMessageIdsTable.remove(peerId: message.id.peerId, globallyUniqueId: globallyUniqueId)
|
||||
@ -1809,7 +1856,7 @@ final class MessageHistoryTable: Table {
|
||||
}
|
||||
}
|
||||
|
||||
private func justUpdateTimestamp(_ index: MessageIndex, timestamp: Int32, unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], updatedGroupInfos: inout [MessageId: MessageGroupInfo], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], updatedMedia: inout [MediaId: Media?]) -> (MessageTags, GlobalMessageTags)? {
|
||||
private func justUpdateTimestamp(_ index: MessageIndex, timestamp: Int32, unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], updatedGroupInfos: inout [MessageId: MessageGroupInfo], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], timestampBasedMessageAttributesOperations: inout [TimestampBasedMessageAttributesOperation], updatedMedia: inout [MediaId: Media?]) -> (MessageTags, GlobalMessageTags)? {
|
||||
if let previousMessage = self.getMessage(index) {
|
||||
var storeForwardInfo: StoreMessageForwardInfo?
|
||||
if let forwardInfo = previousMessage.forwardInfo {
|
||||
@ -1857,7 +1904,7 @@ final class MessageHistoryTable: Table {
|
||||
|
||||
let updatedIndex = MessageIndex(id: index.id, timestamp: timestamp)
|
||||
|
||||
let _ = self.justUpdate(index, message: InternalStoreMessage(id: previousMessage.id, timestamp: timestamp, globallyUniqueId: previousMessage.globallyUniqueId, groupingKey: previousMessage.groupingKey, threadId: previousMessage.threadId, flags: StoreMessageFlags(previousMessage.flags), tags: previousMessage.tags, globalTags: previousMessage.globalTags, localTags: previousMessage.localTags, forwardInfo: storeForwardInfo, authorId: previousMessage.authorId, text: previousMessage.text, attributes: parsedAttributes, media: parsedMedia), keepLocalTags: false, sharedKey: self.key(updatedIndex), sharedBuffer: WriteBuffer(), sharedEncoder: PostboxEncoder(), unsentMessageOperations: &unsentMessageOperations, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, updatedGroupInfos: &updatedGroupInfos, localTagsOperations: &localTagsOperations, updatedMedia: &updatedMedia)
|
||||
let _ = self.justUpdate(index, message: InternalStoreMessage(id: previousMessage.id, timestamp: timestamp, globallyUniqueId: previousMessage.globallyUniqueId, groupingKey: previousMessage.groupingKey, threadId: previousMessage.threadId, flags: StoreMessageFlags(previousMessage.flags), tags: previousMessage.tags, globalTags: previousMessage.globalTags, localTags: previousMessage.localTags, forwardInfo: storeForwardInfo, authorId: previousMessage.authorId, text: previousMessage.text, attributes: parsedAttributes, media: parsedMedia), keepLocalTags: false, sharedKey: self.key(updatedIndex), sharedBuffer: WriteBuffer(), sharedEncoder: PostboxEncoder(), unsentMessageOperations: &unsentMessageOperations, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, updatedGroupInfos: &updatedGroupInfos, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: ×tampBasedMessageAttributesOperations, updatedMedia: &updatedMedia)
|
||||
return (previousMessage.tags, previousMessage.globalTags)
|
||||
} else {
|
||||
return nil
|
||||
|
@ -11,7 +11,7 @@ final class MutableMessageView {
|
||||
self.stableId = message?.stableId
|
||||
}
|
||||
|
||||
func replay(_ operations: [MessageHistoryOperation], updatedMedia: [MediaId: Media?], renderIntermediateMessage: (IntermediateMessage) -> Message) -> Bool {
|
||||
func replay(postbox: Postbox, operations: [MessageHistoryOperation], updatedMedia: [MediaId: Media?]) -> Bool {
|
||||
var updated = false
|
||||
for operation in operations {
|
||||
switch operation {
|
||||
@ -28,7 +28,7 @@ final class MutableMessageView {
|
||||
}
|
||||
case let .InsertMessage(message):
|
||||
if message.id == self.messageId || message.stableId == self.stableId {
|
||||
self.message = renderIntermediateMessage(message)
|
||||
self.message = postbox.renderIntermediateMessage(message)
|
||||
self.stableId = message.stableId
|
||||
updated = true
|
||||
}
|
||||
|
@ -6,14 +6,14 @@ final class MutablePeerMergedOperationLogView {
|
||||
var tailIndex: Int32?
|
||||
let limit: Int
|
||||
|
||||
init(tag: PeerOperationLogTag, limit: Int, getOperations: (PeerOperationLogTag, Int32, Int) -> [PeerMergedOperationLogEntry], getTailIndex: (PeerOperationLogTag) -> Int32?) {
|
||||
init(postbox: Postbox, tag: PeerOperationLogTag, limit: Int) {
|
||||
self.tag = tag
|
||||
self.entries = getOperations(tag, 0, limit)
|
||||
self.tailIndex = getTailIndex(tag)
|
||||
self.entries = postbox.peerOperationLogTable.getMergedEntries(tag: tag, fromIndex: 0, limit: limit)
|
||||
self.tailIndex = postbox.peerMergedOperationLogIndexTable.tailIndex(tag: tag)
|
||||
self.limit = limit
|
||||
}
|
||||
|
||||
func replay(operations: [PeerMergedOperationLogOperation], getOperations: (PeerOperationLogTag, Int32, Int) -> [PeerMergedOperationLogEntry], getTailIndex: (PeerOperationLogTag) -> Int32?) -> Bool {
|
||||
func replay(postbox: Postbox, operations: [PeerMergedOperationLogOperation]) -> Bool {
|
||||
var updated = false
|
||||
var invalidatedTail = false
|
||||
|
||||
@ -65,7 +65,7 @@ final class MutablePeerMergedOperationLogView {
|
||||
|
||||
if updated {
|
||||
if invalidatedTail {
|
||||
self.tailIndex = getTailIndex(self.tag)
|
||||
self.tailIndex = postbox.peerMergedOperationLogIndexTable.tailIndex(tag: self.tag)
|
||||
}
|
||||
if self.entries.count < self.limit {
|
||||
if let tailIndex = self.tailIndex {
|
||||
@ -74,7 +74,7 @@ final class MutablePeerMergedOperationLogView {
|
||||
if !self.entries.isEmpty {
|
||||
fromIndex = self.entries.last!.mergedIndex + 1
|
||||
}
|
||||
for entry in getOperations(self.tag, fromIndex, self.limit - self.entries.count) {
|
||||
for entry in postbox.peerOperationLogTable.getMergedEntries(tag: self.tag, fromIndex: fromIndex, limit: self.limit - self.entries.count) {
|
||||
self.entries.append(entry)
|
||||
}
|
||||
for i in 0 ..< self.entries.count {
|
||||
|
@ -765,16 +765,6 @@ public final class Transaction {
|
||||
self.postbox?.operationLogEnumerateEntries(peerId: peerId, tag: tag, f)
|
||||
}
|
||||
|
||||
public func addTimestampBasedMessageAttribute(tag: UInt16, timestamp: Int32, messageId: MessageId) {
|
||||
assert(!self.disposed)
|
||||
self.postbox?.addTimestampBasedMessageAttribute(tag: tag, timestamp: timestamp, messageId: messageId)
|
||||
}
|
||||
|
||||
public func removeTimestampBasedMessageAttribute(tag: UInt16, messageId: MessageId) {
|
||||
assert(!self.disposed)
|
||||
self.postbox?.removeTimestampBasedMessageAttribute(tag: tag, messageId: messageId)
|
||||
}
|
||||
|
||||
public func enumeratePreferencesEntries(_ f: (PreferencesEntry) -> Bool) {
|
||||
assert(!self.disposed)
|
||||
self.postbox?.enumeratePreferencesEntries(f)
|
||||
@ -1068,6 +1058,25 @@ public final class Transaction {
|
||||
return postbox.chatListTable.getNamespaceEntries(groupId: groupId, namespace: namespace, summaryTag: summaryTag, messageIndexTable: postbox.messageHistoryIndexTable, messageHistoryTable: postbox.messageHistoryTable, peerChatInterfaceStateTable: postbox.peerChatInterfaceStateTable, readStateTable: postbox.readStateTable, summaryTable: postbox.messageHistoryTagsSummaryTable)
|
||||
}
|
||||
|
||||
public func getTopChatListEntries(groupId: PeerGroupId, count: Int) -> [RenderedPeer] {
|
||||
assert(!self.disposed)
|
||||
guard let postbox = self.postbox else {
|
||||
return []
|
||||
}
|
||||
return postbox.chatListTable.earlierEntryInfos(groupId: groupId, index: nil, messageHistoryTable: postbox.messageHistoryTable, peerChatInterfaceStateTable: postbox.peerChatInterfaceStateTable, count: count).compactMap { entry -> RenderedPeer? in
|
||||
switch entry {
|
||||
case let .message(index, _):
|
||||
if let peer = self.getPeer(index.messageIndex.id.peerId) {
|
||||
return RenderedPeer(peer: peer)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
case .hole:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func addHolesEverywhere(peerNamespaces: [PeerId.Namespace], holeNamespace: MessageId.Namespace) {
|
||||
assert(!self.disposed)
|
||||
self.postbox?.addHolesEverywhere(peerNamespaces: peerNamespaces, holeNamespace: holeNamespace)
|
||||
@ -1077,6 +1086,11 @@ public final class Transaction {
|
||||
assert(!self.disposed)
|
||||
self.postbox?.reindexUnreadCounters()
|
||||
}
|
||||
|
||||
public func searchPeers(query: String) -> [RenderedPeer] {
|
||||
assert(!self.disposed)
|
||||
return self.postbox?.searchPeers(query: query) ?? []
|
||||
}
|
||||
}
|
||||
|
||||
public enum PostboxResult {
|
||||
@ -1394,7 +1408,7 @@ public final class Postbox {
|
||||
self.timestampBasedMessageAttributesTable = TimestampBasedMessageAttributesTable(valueBox: self.valueBox, table: TimestampBasedMessageAttributesTable.tableSpec(34), indexTable: self.timestampBasedMessageAttributesIndexTable)
|
||||
self.textIndexTable = MessageHistoryTextIndexTable(valueBox: self.valueBox, table: MessageHistoryTextIndexTable.tableSpec(41))
|
||||
self.additionalChatListItemsTable = AdditionalChatListItemsTable(valueBox: self.valueBox, table: AdditionalChatListItemsTable.tableSpec(55))
|
||||
self.messageHistoryTable = MessageHistoryTable(valueBox: self.valueBox, table: MessageHistoryTable.tableSpec(7), seedConfiguration: seedConfiguration, messageHistoryIndexTable: self.messageHistoryIndexTable, messageHistoryHoleIndexTable: self.messageHistoryHoleIndexTable, messageMediaTable: self.mediaTable, historyMetadataTable: self.messageHistoryMetadataTable, globallyUniqueMessageIdsTable: self.globallyUniqueMessageIdsTable, unsentTable: self.messageHistoryUnsentTable, failedTable: self.messageHistoryFailedTable, tagsTable: self.messageHistoryTagsTable, threadsTable: self.messageHistoryThreadsTable, globalTagsTable: self.globalMessageHistoryTagsTable, localTagsTable: self.localMessageHistoryTagsTable, readStateTable: self.readStateTable, synchronizeReadStateTable: self.synchronizeReadStateTable, textIndexTable: self.textIndexTable, summaryTable: self.messageHistoryTagsSummaryTable, pendingActionsTable: self.pendingMessageActionsTable)
|
||||
self.messageHistoryTable = MessageHistoryTable(valueBox: self.valueBox, table: MessageHistoryTable.tableSpec(7), seedConfiguration: seedConfiguration, messageHistoryIndexTable: self.messageHistoryIndexTable, messageHistoryHoleIndexTable: self.messageHistoryHoleIndexTable, messageMediaTable: self.mediaTable, historyMetadataTable: self.messageHistoryMetadataTable, globallyUniqueMessageIdsTable: self.globallyUniqueMessageIdsTable, unsentTable: self.messageHistoryUnsentTable, failedTable: self.messageHistoryFailedTable, tagsTable: self.messageHistoryTagsTable, threadsTable: self.messageHistoryThreadsTable, globalTagsTable: self.globalMessageHistoryTagsTable, localTagsTable: self.localMessageHistoryTagsTable, timeBasedAttributesTable: self.timestampBasedMessageAttributesTable, readStateTable: self.readStateTable, synchronizeReadStateTable: self.synchronizeReadStateTable, textIndexTable: self.textIndexTable, summaryTable: self.messageHistoryTagsSummaryTable, pendingActionsTable: self.pendingMessageActionsTable)
|
||||
self.peerChatStateTable = PeerChatStateTable(valueBox: self.valueBox, table: PeerChatStateTable.tableSpec(13))
|
||||
self.peerNameTokenIndexTable = ReverseIndexReferenceTable<PeerIdReverseIndexReference>(valueBox: self.valueBox, table: ReverseIndexReferenceTable<PeerIdReverseIndexReference>.tableSpec(26))
|
||||
self.peerNameIndexTable = PeerNameIndexTable(valueBox: self.valueBox, table: PeerNameIndexTable.tableSpec(27), peerTable: self.peerTable, peerNameTokenIndexTable: self.peerNameTokenIndexTable)
|
||||
@ -1491,27 +1505,13 @@ public final class Postbox {
|
||||
|
||||
self.transactionStateVersion = self.metadataTable.transactionStateVersion()
|
||||
|
||||
self.viewTracker = ViewTracker(queue: self.queue, renderMessage: self.renderIntermediateMessage, getPeer: { peerId in
|
||||
return self.peerTable.get(peerId)
|
||||
}, getPeerNotificationSettings: { peerId in
|
||||
return self.peerNotificationSettingsTable.getEffective(peerId)
|
||||
}, getCachedPeerData: { peerId in
|
||||
return self.cachedPeerDataTable.get(peerId)
|
||||
}, getPeerPresence: { peerId in
|
||||
return self.peerPresenceTable.get(peerId)
|
||||
}, getPeerReadState: { peerId in
|
||||
return self.readStateTable.getCombinedState(peerId)
|
||||
}, operationLogGetOperations: { tag, fromIndex, limit in
|
||||
return self.peerOperationLogTable.getMergedEntries(tag: tag, fromIndex: fromIndex, limit: limit)
|
||||
}, operationLogGetTailIndex: { tag in
|
||||
return self.peerMergedOperationLogIndexTable.tailIndex(tag: tag)
|
||||
}, getTimestampBasedMessageAttributesHead: { tag in
|
||||
return self.timestampBasedMessageAttributesTable.head(tag: tag)
|
||||
}, getPreferencesEntry: { key in
|
||||
return self.preferencesTable.get(key: key)
|
||||
}, unsentMessageIds: self.messageHistoryUnsentTable.get(), synchronizePeerReadStateOperations: self.synchronizeReadStateTable.get(getCombinedPeerReadState: { peerId in
|
||||
return self.readStateTable.getCombinedState(peerId)
|
||||
}))
|
||||
self.viewTracker = ViewTracker(
|
||||
queue: self.queue,
|
||||
unsentMessageIds: self.messageHistoryUnsentTable.get(),
|
||||
synchronizePeerReadStateOperations: self.synchronizeReadStateTable.get(getCombinedPeerReadState: { peerId in
|
||||
return self.readStateTable.getCombinedState(peerId)
|
||||
})
|
||||
)
|
||||
|
||||
print("(Postbox initialization took \((CFAbsoluteTimeGetCurrent() - startTime) * 1000.0) ms")
|
||||
|
||||
@ -1640,7 +1640,7 @@ public final class Postbox {
|
||||
|
||||
fileprivate func addMessages(transaction: Transaction, messages: [StoreMessage], location: AddMessagesLocation) -> [Int64: MessageId] {
|
||||
var addedMessagesByPeerId: [PeerId: [StoreMessage]] = [:]
|
||||
let addResult = self.messageHistoryTable.addMessages(messages: messages, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: ¤tUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations, processMessages: { messagesByPeerId in
|
||||
let addResult = self.messageHistoryTable.addMessages(messages: messages, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: ¤tUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations, timestampBasedMessageAttributesOperations: &self.currentTimestampBasedMessageAttributesOperations, processMessages: { messagesByPeerId in
|
||||
addedMessagesByPeerId = messagesByPeerId
|
||||
})
|
||||
|
||||
@ -1713,11 +1713,11 @@ public final class Postbox {
|
||||
}
|
||||
|
||||
fileprivate func deleteMessages(_ messageIds: [MessageId], forEachMedia: (Media) -> Void) {
|
||||
self.messageHistoryTable.removeMessages(messageIds, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: ¤tUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations, forEachMedia: forEachMedia)
|
||||
self.messageHistoryTable.removeMessages(messageIds, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: ¤tUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations, timestampBasedMessageAttributesOperations: &self.currentTimestampBasedMessageAttributesOperations, forEachMedia: forEachMedia)
|
||||
}
|
||||
|
||||
fileprivate func deleteMessagesInRange(peerId: PeerId, namespace: MessageId.Namespace, minId: MessageId.Id, maxId: MessageId.Id, forEachMedia: (Media) -> Void) {
|
||||
self.messageHistoryTable.removeMessagesInRange(peerId: peerId, namespace: namespace, minId: minId, maxId: maxId, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: ¤tUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations, forEachMedia: forEachMedia)
|
||||
self.messageHistoryTable.removeMessagesInRange(peerId: peerId, namespace: namespace, minId: minId, maxId: maxId, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: ¤tUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations, timestampBasedMessageAttributesOperations: &self.currentTimestampBasedMessageAttributesOperations, forEachMedia: forEachMedia)
|
||||
}
|
||||
|
||||
fileprivate func withAllMessages(peerId: PeerId, namespace: MessageId.Namespace?, _ f: (Message) -> Bool) {
|
||||
@ -1733,22 +1733,22 @@ public final class Postbox {
|
||||
}
|
||||
|
||||
fileprivate func clearHistory(_ peerId: PeerId, namespaces: MessageIdNamespaces, forEachMedia: (Media) -> Void) {
|
||||
self.messageHistoryTable.clearHistory(peerId: peerId, namespaces: namespaces, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: ¤tUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations, forEachMedia: forEachMedia)
|
||||
self.messageHistoryTable.clearHistory(peerId: peerId, namespaces: namespaces, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: ¤tUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations, timestampBasedMessageAttributesOperations: &self.currentTimestampBasedMessageAttributesOperations, forEachMedia: forEachMedia)
|
||||
for namespace in self.messageHistoryHoleIndexTable.existingNamespaces(peerId: peerId, holeSpace: .everywhere) where namespaces.contains(namespace) {
|
||||
self.messageHistoryHoleIndexTable.remove(peerId: peerId, namespace: namespace, space: .everywhere, range: 1 ... Int32.max - 1, operations: &self.currentPeerHoleOperations)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func removeAllMessagesWithAuthor(_ peerId: PeerId, authorId: PeerId, namespace: MessageId.Namespace, forEachMedia: (Media) -> Void) {
|
||||
self.messageHistoryTable.removeAllMessagesWithAuthor(peerId: peerId, authorId: authorId, namespace: namespace, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: ¤tUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations, forEachMedia: forEachMedia)
|
||||
self.messageHistoryTable.removeAllMessagesWithAuthor(peerId: peerId, authorId: authorId, namespace: namespace, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: ¤tUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations, timestampBasedMessageAttributesOperations: &self.currentTimestampBasedMessageAttributesOperations, forEachMedia: forEachMedia)
|
||||
}
|
||||
|
||||
fileprivate func removeAllMessagesWithGlobalTag(tag: GlobalMessageTags) {
|
||||
self.messageHistoryTable.removeAllMessagesWithGlobalTag(tag: tag, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: ¤tUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations, forEachMedia: { _ in })
|
||||
self.messageHistoryTable.removeAllMessagesWithGlobalTag(tag: tag, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: ¤tUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations, timestampBasedMessageAttributesOperations: &self.currentTimestampBasedMessageAttributesOperations, forEachMedia: { _ in })
|
||||
}
|
||||
|
||||
fileprivate func removeAllMessagesWithForwardAuthor(_ peerId: PeerId, forwardAuthorId: PeerId, namespace: MessageId.Namespace, forEachMedia: (Media) -> Void) {
|
||||
self.messageHistoryTable.removeAllMessagesWithForwardAuthor(peerId: peerId, forwardAuthorId: forwardAuthorId, namespace: namespace, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: ¤tUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations, forEachMedia: forEachMedia)
|
||||
self.messageHistoryTable.removeAllMessagesWithForwardAuthor(peerId: peerId, forwardAuthorId: forwardAuthorId, namespace: namespace, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: ¤tUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations, timestampBasedMessageAttributesOperations: &self.currentTimestampBasedMessageAttributesOperations, forEachMedia: forEachMedia)
|
||||
}
|
||||
|
||||
fileprivate func resetIncomingReadStates(_ states: [PeerId: [MessageId.Namespace: PeerReadState]]) {
|
||||
@ -2176,13 +2176,13 @@ public final class Postbox {
|
||||
if let index = self.messageHistoryIndexTable.getIndex(id), let intermediateMessage = self.messageHistoryTable.getMessage(index) {
|
||||
let message = self.renderIntermediateMessage(intermediateMessage)
|
||||
if case let .update(updatedMessage) = update(message) {
|
||||
self.messageHistoryTable.updateMessage(id, message: updatedMessage, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: &self.currentUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations)
|
||||
self.messageHistoryTable.updateMessage(id, message: updatedMessage, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: &self.currentUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations, timestampBasedMessageAttributesOperations: &self.currentTimestampBasedMessageAttributesOperations)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func offsetPendingMessagesTimestamps(lowerBound: MessageId, excludeIds: Set<MessageId>, timestamp: Int32) {
|
||||
self.messageHistoryTable.offsetPendingMessagesTimestamps(lowerBound: lowerBound, excludeIds: excludeIds, timestamp: timestamp, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: &self.currentUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations)
|
||||
self.messageHistoryTable.offsetPendingMessagesTimestamps(lowerBound: lowerBound, excludeIds: excludeIds, timestamp: timestamp, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: &self.currentUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations, timestampBasedMessageAttributesOperations: &self.currentTimestampBasedMessageAttributesOperations)
|
||||
}
|
||||
|
||||
fileprivate func updateMessageGroupingKeysAtomically(_ ids: [MessageId], groupingKey: Int64) {
|
||||
@ -2794,13 +2794,7 @@ public final class Postbox {
|
||||
public func aroundChatListView(groupId: PeerGroupId, filterPredicate: ChatListFilterPredicate? = nil, index: ChatListIndex, count: Int, summaryComponents: ChatListEntrySummaryComponents, userInteractive: Bool = false) -> Signal<(ChatListView, ViewUpdateType), NoError> {
|
||||
return self.transactionSignal(userInteractive: userInteractive, { subscriber, transaction in
|
||||
let mutableView = MutableChatListView(postbox: self, groupId: groupId, filterPredicate: filterPredicate, aroundIndex: index, count: count, summaryComponents: summaryComponents)
|
||||
mutableView.render(postbox: self, renderMessage: self.renderIntermediateMessage, getPeer: { id in
|
||||
return self.peerTable.get(id)
|
||||
}, getPeerNotificationSettings: {
|
||||
self.peerNotificationSettingsTable.getEffective($0)
|
||||
}, getPeerPresence: {
|
||||
self.peerPresenceTable.get($0)
|
||||
})
|
||||
mutableView.render(postbox: self)
|
||||
|
||||
let (index, signal) = self.viewTracker.addChatListView(mutableView)
|
||||
|
||||
@ -2900,52 +2894,56 @@ public final class Postbox {
|
||||
|
||||
public func searchPeers(query: String) -> Signal<[RenderedPeer], NoError> {
|
||||
return self.transaction { transaction -> Signal<[RenderedPeer], NoError> in
|
||||
var peerIds = Set<PeerId>()
|
||||
var chatPeers: [RenderedPeer] = []
|
||||
|
||||
var (chatPeerIds, contactPeerIds) = self.peerNameIndexTable.matchingPeerIds(tokens: (regular: stringIndexTokens(query, transliteration: .none), transliterated: stringIndexTokens(query, transliteration: .transliterated)), categories: [.chats, .contacts], chatListIndexTable: self.chatListIndexTable, contactTable: self.contactsTable)
|
||||
|
||||
var additionalChatPeerIds: [PeerId] = []
|
||||
for peerId in chatPeerIds {
|
||||
for associatedId in self.reverseAssociatedPeerTable.get(peerId: peerId) {
|
||||
let inclusionIndex = self.chatListIndexTable.get(peerId: associatedId)
|
||||
if inclusionIndex.includedIndex(peerId: associatedId) != nil {
|
||||
additionalChatPeerIds.append(associatedId)
|
||||
}
|
||||
return .single(transaction.searchPeers(query: query))
|
||||
} |> switchToLatest
|
||||
}
|
||||
|
||||
fileprivate func searchPeers(query: String) -> [RenderedPeer] {
|
||||
var peerIds = Set<PeerId>()
|
||||
var chatPeers: [RenderedPeer] = []
|
||||
|
||||
var (chatPeerIds, contactPeerIds) = self.peerNameIndexTable.matchingPeerIds(tokens: (regular: stringIndexTokens(query, transliteration: .none), transliterated: stringIndexTokens(query, transliteration: .transliterated)), categories: [.chats, .contacts], chatListIndexTable: self.chatListIndexTable, contactTable: self.contactsTable)
|
||||
|
||||
var additionalChatPeerIds: [PeerId] = []
|
||||
for peerId in chatPeerIds {
|
||||
for associatedId in self.reverseAssociatedPeerTable.get(peerId: peerId) {
|
||||
let inclusionIndex = self.chatListIndexTable.get(peerId: associatedId)
|
||||
if inclusionIndex.includedIndex(peerId: associatedId) != nil {
|
||||
additionalChatPeerIds.append(associatedId)
|
||||
}
|
||||
}
|
||||
chatPeerIds.append(contentsOf: additionalChatPeerIds)
|
||||
|
||||
for peerId in chatPeerIds {
|
||||
}
|
||||
chatPeerIds.append(contentsOf: additionalChatPeerIds)
|
||||
|
||||
for peerId in chatPeerIds {
|
||||
if let peer = self.peerTable.get(peerId) {
|
||||
var peers = SimpleDictionary<PeerId, Peer>()
|
||||
peers[peer.id] = peer
|
||||
if let associatedPeerId = peer.associatedPeerId {
|
||||
if let associatedPeer = self.peerTable.get(associatedPeerId) {
|
||||
peers[associatedPeer.id] = associatedPeer
|
||||
}
|
||||
}
|
||||
chatPeers.append(RenderedPeer(peerId: peer.id, peers: peers))
|
||||
peerIds.insert(peerId)
|
||||
}
|
||||
}
|
||||
|
||||
var contactPeers: [RenderedPeer] = []
|
||||
for peerId in contactPeerIds {
|
||||
if !peerIds.contains(peerId) {
|
||||
if let peer = self.peerTable.get(peerId) {
|
||||
var peers = SimpleDictionary<PeerId, Peer>()
|
||||
peers[peer.id] = peer
|
||||
if let associatedPeerId = peer.associatedPeerId {
|
||||
if let associatedPeer = self.peerTable.get(associatedPeerId) {
|
||||
peers[associatedPeer.id] = associatedPeer
|
||||
}
|
||||
}
|
||||
chatPeers.append(RenderedPeer(peerId: peer.id, peers: peers))
|
||||
peerIds.insert(peerId)
|
||||
contactPeers.append(RenderedPeer(peerId: peer.id, peers: peers))
|
||||
}
|
||||
}
|
||||
|
||||
var contactPeers: [RenderedPeer] = []
|
||||
for peerId in contactPeerIds {
|
||||
if !peerIds.contains(peerId) {
|
||||
if let peer = self.peerTable.get(peerId) {
|
||||
var peers = SimpleDictionary<PeerId, Peer>()
|
||||
peers[peer.id] = peer
|
||||
contactPeers.append(RenderedPeer(peerId: peer.id, peers: peers))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contactPeers.sort(by: { lhs, rhs in
|
||||
lhs.peers[lhs.peerId]!.indexName.indexName(.lastNameFirst) < rhs.peers[rhs.peerId]!.indexName.indexName(.lastNameFirst)
|
||||
})
|
||||
return .single(chatPeers + contactPeers)
|
||||
} |> switchToLatest
|
||||
}
|
||||
|
||||
contactPeers.sort(by: { lhs, rhs in
|
||||
lhs.peers[lhs.peerId]!.indexName.indexName(.lastNameFirst) < rhs.peers[rhs.peerId]!.indexName.indexName(.lastNameFirst)
|
||||
})
|
||||
return chatPeers + contactPeers
|
||||
}
|
||||
|
||||
public func peerView(id: PeerId) -> Signal<PeerView, NoError> {
|
||||
@ -3138,11 +3136,7 @@ public final class Postbox {
|
||||
|
||||
public func mergedOperationLogView(tag: PeerOperationLogTag, limit: Int) -> Signal<PeerMergedOperationLogView, NoError> {
|
||||
return self.transactionSignal { subscriber, transaction in
|
||||
let view = MutablePeerMergedOperationLogView(tag: tag, limit: limit, getOperations: { tag, fromIndex, limit in
|
||||
return self.peerOperationLogTable.getMergedEntries(tag: tag, fromIndex: fromIndex, limit: limit)
|
||||
}, getTailIndex: { tag in
|
||||
return self.peerMergedOperationLogIndexTable.tailIndex(tag: tag)
|
||||
})
|
||||
let view = MutablePeerMergedOperationLogView(postbox: self, tag: tag, limit: limit)
|
||||
|
||||
subscriber.putNext(PeerMergedOperationLogView(view))
|
||||
|
||||
@ -3165,9 +3159,7 @@ public final class Postbox {
|
||||
|
||||
public func timestampBasedMessageAttributesView(tag: UInt16) -> Signal<TimestampBasedMessageAttributesView, NoError> {
|
||||
return self.transactionSignal { subscriber, transaction in
|
||||
let view = MutableTimestampBasedMessageAttributesView(tag: tag, getHead: { tag in
|
||||
return self.timestampBasedMessageAttributesTable.head(tag: tag)
|
||||
})
|
||||
let view = MutableTimestampBasedMessageAttributesView(postbox: self, tag: tag)
|
||||
let (index, signal) = self.viewTracker.addTimestampBasedMessageAttributesView(view)
|
||||
|
||||
subscriber.putNext(TimestampBasedMessageAttributesView(view))
|
||||
@ -3215,14 +3207,6 @@ public final class Postbox {
|
||||
self.peerOperationLogTable.enumerateEntries(peerId: peerId, tag: tag, f)
|
||||
}
|
||||
|
||||
fileprivate func addTimestampBasedMessageAttribute(tag: UInt16, timestamp: Int32, messageId: MessageId) {
|
||||
self.timestampBasedMessageAttributesTable.set(tag: tag, id: messageId, timestamp: timestamp, operations: &self.currentTimestampBasedMessageAttributesOperations)
|
||||
}
|
||||
|
||||
fileprivate func removeTimestampBasedMessageAttribute(tag: UInt16, messageId: MessageId) {
|
||||
self.timestampBasedMessageAttributesTable.remove(tag: tag, id: messageId, operations: &self.currentTimestampBasedMessageAttributesOperations)
|
||||
}
|
||||
|
||||
public func messageView(_ messageId: MessageId) -> Signal<MessageView, NoError> {
|
||||
return self.transactionSignal { subscriber, transaction in
|
||||
let view = MutableMessageView(messageId: messageId, message: transaction.getMessage(messageId))
|
||||
|
@ -4,12 +4,12 @@ final class MutableTimestampBasedMessageAttributesView {
|
||||
let tag: UInt16
|
||||
var head: TimestampBasedMessageAttributesEntry?
|
||||
|
||||
init(tag: UInt16, getHead: (UInt16) -> TimestampBasedMessageAttributesEntry?) {
|
||||
init(postbox: Postbox, tag: UInt16) {
|
||||
self.tag = tag
|
||||
self.head = getHead(tag)
|
||||
self.head = postbox.timestampBasedMessageAttributesTable.head(tag: tag)
|
||||
}
|
||||
|
||||
func replay(operations: [TimestampBasedMessageAttributesOperation], getHead: (UInt16) -> TimestampBasedMessageAttributesEntry?) -> Bool {
|
||||
func replay(postbox: Postbox, operations: [TimestampBasedMessageAttributesOperation]) -> Bool {
|
||||
var updated = false
|
||||
var invalidatedHead = false
|
||||
for operation in operations {
|
||||
@ -37,7 +37,7 @@ final class MutableTimestampBasedMessageAttributesView {
|
||||
}
|
||||
}
|
||||
if invalidatedHead {
|
||||
self.head = getHead(self.tag)
|
||||
self.head = postbox.timestampBasedMessageAttributesTable.head(tag: self.tag)
|
||||
}
|
||||
return updated
|
||||
}
|
||||
|
@ -11,15 +11,6 @@ public enum ViewUpdateType {
|
||||
|
||||
final class ViewTracker {
|
||||
private let queue: Queue
|
||||
private let renderMessage: (IntermediateMessage) -> Message
|
||||
private let getPeer: (PeerId) -> Peer?
|
||||
private let getPeerNotificationSettings: (PeerId) -> PeerNotificationSettings?
|
||||
private let getCachedPeerData: (PeerId) -> CachedPeerData?
|
||||
private let getPeerPresence: (PeerId) -> PeerPresence?
|
||||
private let getPeerReadState: (PeerId) -> CombinedPeerReadState?
|
||||
private let operationLogGetOperations: (PeerOperationLogTag, Int32, Int) -> [PeerMergedOperationLogEntry]
|
||||
private let operationLogGetTailIndex: (PeerOperationLogTag) -> Int32?
|
||||
private let getPreferencesEntry: (ValueBoxKey) -> PreferencesEntry?
|
||||
|
||||
private var chatListViews = Bag<(MutableChatListView, ValuePipe<(ChatListView, ViewUpdateType)>)>()
|
||||
private var messageHistoryViews = Bag<(MutableMessageHistoryView, ValuePipe<(MessageHistoryView, ViewUpdateType)>)>()
|
||||
@ -46,7 +37,6 @@ final class ViewTracker {
|
||||
private var unreadMessageCountsViews = Bag<(MutableUnreadMessageCountsView, ValuePipe<UnreadMessageCountsView>)>()
|
||||
private var peerMergedOperationLogViews = Bag<(MutablePeerMergedOperationLogView, ValuePipe<PeerMergedOperationLogView>)>()
|
||||
|
||||
private let getTimestampBasedMessageAttributesHead: (UInt16) -> TimestampBasedMessageAttributesEntry?
|
||||
private var timestampBasedMessageAttributesViews = Bag<(MutableTimestampBasedMessageAttributesView, ValuePipe<TimestampBasedMessageAttributesView>)>()
|
||||
|
||||
private var combinedViews = Bag<(CombinedMutableView, ValuePipe<CombinedView>)>()
|
||||
@ -56,23 +46,14 @@ final class ViewTracker {
|
||||
private var preferencesViews = Bag<(MutablePreferencesView, ValuePipe<PreferencesView>)>()
|
||||
private var multiplePeersViews = Bag<(MutableMultiplePeersView, ValuePipe<MultiplePeersView>)>()
|
||||
private var itemCollectionsViews = Bag<(MutableItemCollectionsView, ValuePipe<ItemCollectionsView>)>()
|
||||
|
||||
|
||||
private var failedMessageIdsViews = Bag<(MutableFailedMessageIdsView, ValuePipe<FailedMessageIdsView>)>()
|
||||
|
||||
|
||||
init(queue: Queue, renderMessage: @escaping (IntermediateMessage) -> Message, getPeer: @escaping (PeerId) -> Peer?, getPeerNotificationSettings: @escaping (PeerId) -> PeerNotificationSettings?, getCachedPeerData: @escaping (PeerId) -> CachedPeerData?, getPeerPresence: @escaping (PeerId) -> PeerPresence?, getPeerReadState: @escaping (PeerId) -> CombinedPeerReadState?, operationLogGetOperations: @escaping (PeerOperationLogTag, Int32, Int) -> [PeerMergedOperationLogEntry], operationLogGetTailIndex: @escaping (PeerOperationLogTag) -> Int32?, getTimestampBasedMessageAttributesHead: @escaping (UInt16) -> TimestampBasedMessageAttributesEntry?, getPreferencesEntry: @escaping (ValueBoxKey) -> PreferencesEntry?, unsentMessageIds: [MessageId], synchronizePeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation]) {
|
||||
init(
|
||||
queue: Queue,
|
||||
unsentMessageIds: [MessageId],
|
||||
synchronizePeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation]
|
||||
) {
|
||||
self.queue = queue
|
||||
self.renderMessage = renderMessage
|
||||
self.getPeer = getPeer
|
||||
self.getPeerNotificationSettings = getPeerNotificationSettings
|
||||
self.getCachedPeerData = getCachedPeerData
|
||||
self.getPeerPresence = getPeerPresence
|
||||
self.getPeerReadState = getPeerReadState
|
||||
self.operationLogGetOperations = operationLogGetOperations
|
||||
self.operationLogGetTailIndex = operationLogGetTailIndex
|
||||
self.getTimestampBasedMessageAttributesHead = getTimestampBasedMessageAttributesHead
|
||||
self.getPreferencesEntry = getPreferencesEntry
|
||||
|
||||
self.unsentMessageView = UnsentMessageHistoryView(ids: unsentMessageIds)
|
||||
self.synchronizeReadStatesView = MutableSynchronizePeerReadStatesView(operations: synchronizePeerReadStateOperations)
|
||||
@ -254,9 +235,7 @@ final class ViewTracker {
|
||||
|
||||
for (mutableView, pipe) in self.chatListViews.copyItems() {
|
||||
if mutableView.refreshDueToExternalTransaction(postbox: postbox) {
|
||||
mutableView.render(postbox: postbox, renderMessage: self.renderMessage, getPeer: { id in
|
||||
return self.getPeer(id)
|
||||
}, getPeerNotificationSettings: self.getPeerNotificationSettings, getPeerPresence: self.getPeerPresence)
|
||||
mutableView.render(postbox: postbox)
|
||||
pipe.putNext((ChatListView(mutableView), .Generic))
|
||||
}
|
||||
}
|
||||
@ -350,7 +329,7 @@ final class ViewTracker {
|
||||
for (mutableView, pipe) in self.messageViews.copyItems() {
|
||||
let operations = transaction.currentOperationsByPeerId[mutableView.messageId.peerId]
|
||||
if operations != nil || !transaction.updatedMedia.isEmpty || !transaction.currentUpdatedCachedPeerData.isEmpty {
|
||||
if mutableView.replay(operations ?? [], updatedMedia: transaction.updatedMedia, renderIntermediateMessage: self.renderMessage) {
|
||||
if mutableView.replay(postbox: postbox, operations: operations ?? [], updatedMedia: transaction.updatedMedia) {
|
||||
pipe.putNext(MessageView(mutableView))
|
||||
}
|
||||
}
|
||||
@ -360,9 +339,7 @@ final class ViewTracker {
|
||||
let context = MutableChatListViewReplayContext()
|
||||
if mutableView.replay(postbox: postbox, operations: transaction.chatListOperations, updatedPeerNotificationSettings: transaction.currentUpdatedPeerNotificationSettings, updatedPeers: transaction.currentUpdatedPeers, updatedPeerPresences: transaction.currentUpdatedPeerPresences, transaction: transaction, context: context) {
|
||||
mutableView.complete(postbox: postbox, context: context)
|
||||
mutableView.render(postbox: postbox, renderMessage: self.renderMessage, getPeer: { id in
|
||||
return self.getPeer(id)
|
||||
}, getPeerNotificationSettings: self.getPeerNotificationSettings, getPeerPresence: self.getPeerPresence)
|
||||
mutableView.render(postbox: postbox)
|
||||
pipe.putNext((ChatListView(mutableView), .Generic))
|
||||
}
|
||||
}
|
||||
@ -396,7 +373,7 @@ final class ViewTracker {
|
||||
}
|
||||
|
||||
for (mutableView, pipe) in self.contactPeersViews.copyItems() {
|
||||
if mutableView.replay(replacePeerIds: transaction.replaceContactPeerIds, updatedPeerPresences: transaction.currentUpdatedPeerPresences, getPeer: self.getPeer, getPeerPresence: self.getPeerPresence) {
|
||||
if mutableView.replay(postbox: postbox, replacePeerIds: transaction.replaceContactPeerIds, updatedPeerPresences: transaction.currentUpdatedPeerPresences) {
|
||||
pipe.putNext(ContactPeersView(mutableView))
|
||||
}
|
||||
}
|
||||
@ -408,13 +385,13 @@ final class ViewTracker {
|
||||
}
|
||||
|
||||
for (mutableView, pipe) in self.peerMergedOperationLogViews.copyItems() {
|
||||
if mutableView.replay(operations: transaction.currentPeerMergedOperationLogOperations, getOperations: self.operationLogGetOperations, getTailIndex: self.operationLogGetTailIndex) {
|
||||
if mutableView.replay(postbox: postbox, operations: transaction.currentPeerMergedOperationLogOperations) {
|
||||
pipe.putNext(PeerMergedOperationLogView(mutableView))
|
||||
}
|
||||
}
|
||||
|
||||
for (mutableView, pipe) in self.timestampBasedMessageAttributesViews.copyItems() {
|
||||
if mutableView.replay(operations: transaction.currentTimestampBasedMessageAttributesOperations, getHead: self.getTimestampBasedMessageAttributesHead) {
|
||||
if mutableView.replay(postbox: postbox, operations: transaction.currentTimestampBasedMessageAttributesOperations) {
|
||||
pipe.putNext(TimestampBasedMessageAttributesView(mutableView))
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,19 @@
|
||||
//
|
||||
// SQueueLocalObject.h
|
||||
// SSignalKit
|
||||
//
|
||||
// Created by Mikhail Filimonov on 13.01.2021.
|
||||
// Copyright © 2021 Telegram. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <SSignalKit/SQueue.h>
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface SQueueLocalObject : NSObject
|
||||
-(id)initWithQueue:(SQueue *)queue generate:(id (^)(void))next;
|
||||
-(void)with:(void (^)(id object))f;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
@ -0,0 +1,40 @@
|
||||
//
|
||||
// SQueueLocalObject.m
|
||||
// SSignalKit
|
||||
//
|
||||
// Created by Mikhail Filimonov on 13.01.2021.
|
||||
// Copyright © 2021 Telegram. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SQueueLocalObject.h"
|
||||
|
||||
@implementation SQueueLocalObject {
|
||||
SQueue *queue;
|
||||
id valueRef;
|
||||
}
|
||||
-(id)initWithQueue:(SQueue *)queue generate:(id _Nonnull (^)(void))next {
|
||||
if (self = [super init]) {
|
||||
self->queue = queue;
|
||||
[queue dispatch:^{
|
||||
self->valueRef = next();
|
||||
}];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void)with:(void (^)(id object))f {
|
||||
[self->queue dispatch:^{
|
||||
f(self->valueRef);
|
||||
}];
|
||||
}
|
||||
|
||||
-(void)dealloc {
|
||||
__block id value = self->valueRef;
|
||||
self->valueRef = nil;
|
||||
[queue dispatch:^{
|
||||
value = nil;
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -35,3 +35,4 @@ FOUNDATION_EXPORT const unsigned char SSignalKitVersionString[];
|
||||
#import <SSignalKit/SMulticastSignalManager.h>
|
||||
#import <SSignalKit/STimer.h>
|
||||
#import <SSignalKit/SVariable.h>
|
||||
#import <SSignalKit/SQueueLocalObject.h>
|
||||
|
@ -9,7 +9,7 @@ private let selectedTextFont = Font.bold(13.0)
|
||||
|
||||
public enum SegmentedControlLayout {
|
||||
case stretchToFill(width: CGFloat)
|
||||
case sizeToFit(maximumWidth: CGFloat, minimumWidth: CGFloat)
|
||||
case sizeToFit(maximumWidth: CGFloat, minimumWidth: CGFloat, height: CGFloat)
|
||||
}
|
||||
|
||||
public final class SegmentedControlTheme: Equatable {
|
||||
@ -294,11 +294,14 @@ public final class SegmentedControlNode: ASDisplayNode, UIGestureRecognizerDeleg
|
||||
let calculatedWidth: CGFloat = 0.0
|
||||
|
||||
let width: CGFloat
|
||||
let height: CGFloat
|
||||
switch layout {
|
||||
case let .stretchToFill(targetWidth):
|
||||
width = targetWidth
|
||||
case let .sizeToFit(maximumWidth, minimumWidth):
|
||||
height = 32.0
|
||||
case let .sizeToFit(maximumWidth, minimumWidth, targetHeight):
|
||||
width = max(minimumWidth, min(maximumWidth, calculatedWidth))
|
||||
height = targetHeight
|
||||
}
|
||||
|
||||
let selectedIndex: Int
|
||||
@ -308,7 +311,7 @@ public final class SegmentedControlNode: ASDisplayNode, UIGestureRecognizerDeleg
|
||||
selectedIndex = self.selectedIndex
|
||||
}
|
||||
|
||||
let size = CGSize(width: width, height: 32.0)
|
||||
let size = CGSize(width: width, height: height)
|
||||
if !self.itemNodes.isEmpty {
|
||||
let itemSize = CGSize(width: floorToScreenPixels(size.width / CGFloat(self.itemNodes.count)), height: size.height)
|
||||
|
||||
|
@ -748,6 +748,9 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
})
|
||||
case .voiceConference:
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: "Voice Conference (Test)", label: "", sectionId: self.section, style: .blocks, action: {
|
||||
guard let _ = arguments.context else {
|
||||
return
|
||||
}
|
||||
})
|
||||
case let .preferredVideoCodec(_, title, value, isSelected):
|
||||
return ItemListCheckboxItem(presentationData: presentationData, title: title, style: .right, checked: isSelected, zeroSeparatorInsets: false, sectionId: self.section, action: {
|
||||
|
@ -2,19 +2,43 @@ import Foundation
|
||||
import Postbox
|
||||
|
||||
public class AutoremoveTimeoutMessageAttribute: MessageAttribute {
|
||||
public enum Action: Int32 {
|
||||
case remove = 0
|
||||
case clear = 1
|
||||
}
|
||||
|
||||
public let timeout: Int32
|
||||
public let countdownBeginTime: Int32?
|
||||
public let action: Action
|
||||
|
||||
public var associatedMessageIds: [MessageId] = []
|
||||
|
||||
public init(timeout: Int32, countdownBeginTime: Int32?) {
|
||||
public let automaticTimestampBasedAttribute: (UInt16, Int32)?
|
||||
|
||||
public init(timeout: Int32, countdownBeginTime: Int32?, action: Action = .remove) {
|
||||
self.timeout = timeout
|
||||
self.countdownBeginTime = countdownBeginTime
|
||||
|
||||
if let countdownBeginTime = countdownBeginTime {
|
||||
self.automaticTimestampBasedAttribute = (0, countdownBeginTime + timeout)
|
||||
} else {
|
||||
self.automaticTimestampBasedAttribute = nil
|
||||
}
|
||||
|
||||
self.action = action
|
||||
}
|
||||
|
||||
required public init(decoder: PostboxDecoder) {
|
||||
self.timeout = decoder.decodeInt32ForKey("t", orElse: 0)
|
||||
self.countdownBeginTime = decoder.decodeOptionalInt32ForKey("c")
|
||||
|
||||
if let countdownBeginTime = self.countdownBeginTime {
|
||||
self.automaticTimestampBasedAttribute = (0, countdownBeginTime + self.timeout)
|
||||
} else {
|
||||
self.automaticTimestampBasedAttribute = nil
|
||||
}
|
||||
|
||||
self.action = Action(rawValue: decoder.decodeInt32ForKey("a", orElse: 0)) ?? .remove
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
@ -24,6 +48,7 @@ public class AutoremoveTimeoutMessageAttribute: MessageAttribute {
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "c")
|
||||
}
|
||||
encoder.encodeInt32(self.action.rawValue, forKey: "a")
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,4 +84,13 @@ public extension Message {
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
var isSelfExpiring: Bool {
|
||||
for attribute in self.attributes {
|
||||
if let _ = attribute as? AutoremoveTimeoutMessageAttribute {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -194,6 +194,7 @@ public final class CachedChannelData: CachedPeerData {
|
||||
public let slowModeTimeout: Int32?
|
||||
public let slowModeValidUntilTimestamp: Int32?
|
||||
public let hasScheduledMessages: Bool
|
||||
public let autoremoveTimeout: CachedPeerAutoremoveTimeout
|
||||
public let statsDatacenterId: Int32
|
||||
public let invitedBy: PeerId?
|
||||
public let photo: TelegramMediaImage?
|
||||
@ -224,13 +225,14 @@ public final class CachedChannelData: CachedPeerData {
|
||||
self.slowModeTimeout = nil
|
||||
self.slowModeValidUntilTimestamp = nil
|
||||
self.hasScheduledMessages = false
|
||||
self.autoremoveTimeout = .unknown
|
||||
self.statsDatacenterId = 0
|
||||
self.invitedBy = nil
|
||||
self.photo = nil
|
||||
self.activeCall = nil
|
||||
}
|
||||
|
||||
public init(isNotAccessible: Bool, flags: CachedChannelFlags, about: String?, participantsSummary: CachedChannelParticipantsSummary, exportedInvitation: ExportedInvitation?, botInfos: [CachedPeerBotInfo], peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, stickerPack: StickerPackCollectionInfo?, minAvailableMessageId: MessageId?, migrationReference: ChannelMigrationReference?, linkedDiscussionPeerId: LinkedDiscussionPeerId, peerGeoLocation: PeerGeoLocation?, slowModeTimeout: Int32?, slowModeValidUntilTimestamp: Int32?, hasScheduledMessages: Bool, statsDatacenterId: Int32, invitedBy: PeerId?, photo: TelegramMediaImage?, activeCall: ActiveCall?) {
|
||||
public init(isNotAccessible: Bool, flags: CachedChannelFlags, about: String?, participantsSummary: CachedChannelParticipantsSummary, exportedInvitation: ExportedInvitation?, botInfos: [CachedPeerBotInfo], peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, stickerPack: StickerPackCollectionInfo?, minAvailableMessageId: MessageId?, migrationReference: ChannelMigrationReference?, linkedDiscussionPeerId: LinkedDiscussionPeerId, peerGeoLocation: PeerGeoLocation?, slowModeTimeout: Int32?, slowModeValidUntilTimestamp: Int32?, hasScheduledMessages: Bool, statsDatacenterId: Int32, invitedBy: PeerId?, photo: TelegramMediaImage?, activeCall: ActiveCall?, autoremoveTimeout: CachedPeerAutoremoveTimeout) {
|
||||
self.isNotAccessible = isNotAccessible
|
||||
self.flags = flags
|
||||
self.about = about
|
||||
@ -251,6 +253,7 @@ public final class CachedChannelData: CachedPeerData {
|
||||
self.invitedBy = invitedBy
|
||||
self.photo = photo
|
||||
self.activeCall = activeCall
|
||||
self.autoremoveTimeout = autoremoveTimeout
|
||||
|
||||
var peerIds = Set<PeerId>()
|
||||
for botInfo in botInfos {
|
||||
@ -278,83 +281,87 @@ public final class CachedChannelData: CachedPeerData {
|
||||
}
|
||||
|
||||
public func withUpdatedIsNotAccessible(_ isNotAccessible: Bool) -> CachedChannelData {
|
||||
return CachedChannelData(isNotAccessible: isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
|
||||
return CachedChannelData(isNotAccessible: isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedFlags(_ flags: CachedChannelFlags) -> CachedChannelData {
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedAbout(_ about: String?) -> CachedChannelData {
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedParticipantsSummary(_ participantsSummary: CachedChannelParticipantsSummary) -> CachedChannelData {
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedExportedInvitation(_ exportedInvitation: ExportedInvitation?) -> CachedChannelData {
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedBotInfos(_ botInfos: [CachedPeerBotInfo]) -> CachedChannelData {
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedPeerStatusSettings(_ peerStatusSettings: PeerStatusSettings?) -> CachedChannelData {
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> CachedChannelData {
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedStickerPack(_ stickerPack: StickerPackCollectionInfo?) -> CachedChannelData {
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedMinAvailableMessageId(_ minAvailableMessageId: MessageId?) -> CachedChannelData {
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedMigrationReference(_ migrationReference: ChannelMigrationReference?) -> CachedChannelData {
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedLinkedDiscussionPeerId(_ linkedDiscussionPeerId: LinkedDiscussionPeerId) -> CachedChannelData {
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedPeerGeoLocation(_ peerGeoLocation: PeerGeoLocation?) -> CachedChannelData {
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedSlowModeTimeout(_ slowModeTimeout: Int32?) -> CachedChannelData {
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedSlowModeValidUntilTimestamp(_ slowModeValidUntilTimestamp: Int32?) -> CachedChannelData {
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedHasScheduledMessages(_ hasScheduledMessages: Bool) -> CachedChannelData {
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedStatsDatacenterId(_ statsDatacenterId: Int32) -> CachedChannelData {
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedInvitedBy(_ invitedBy: PeerId?) -> CachedChannelData {
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: invitedBy, photo: self.photo, activeCall: self.activeCall)
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedPhoto(_ photo: TelegramMediaImage?) -> CachedChannelData {
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: photo, activeCall: self.activeCall)
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedActiveCall(_ activeCall: ActiveCall?) -> CachedChannelData {
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: activeCall)
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: activeCall, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedAutoremoveTimeout(_ autoremoveTimeout: CachedPeerAutoremoveTimeout) -> CachedChannelData {
|
||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: autoremoveTimeout)
|
||||
}
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
@ -422,6 +429,7 @@ public final class CachedChannelData: CachedPeerData {
|
||||
self.slowModeTimeout = decoder.decodeOptionalInt32ForKey("smt")
|
||||
self.slowModeValidUntilTimestamp = decoder.decodeOptionalInt32ForKey("smv")
|
||||
self.hasScheduledMessages = decoder.decodeBoolForKey("hsm", orElse: false)
|
||||
self.autoremoveTimeout = decoder.decodeObjectForKey("artv", decoder: CachedPeerAutoremoveTimeout.init(decoder:)) as? CachedPeerAutoremoveTimeout ?? .unknown
|
||||
self.statsDatacenterId = decoder.decodeInt32ForKey("sdi", orElse: 0)
|
||||
|
||||
self.invitedBy = decoder.decodeOptionalInt64ForKey("invBy").flatMap(PeerId.init)
|
||||
@ -529,6 +537,7 @@ public final class CachedChannelData: CachedPeerData {
|
||||
encoder.encodeNil(forKey: "smv")
|
||||
}
|
||||
encoder.encodeBool(self.hasScheduledMessages, forKey: "hsm")
|
||||
encoder.encodeObject(self.autoremoveTimeout, forKey: "artv")
|
||||
encoder.encodeInt32(self.statsDatacenterId, forKey: "sdi")
|
||||
|
||||
if let invitedBy = self.invitedBy {
|
||||
@ -613,6 +622,10 @@ public final class CachedChannelData: CachedPeerData {
|
||||
return false
|
||||
}
|
||||
|
||||
if other.autoremoveTimeout != self.autoremoveTimeout {
|
||||
return false
|
||||
}
|
||||
|
||||
if other.statsDatacenterId != self.statsDatacenterId {
|
||||
return false
|
||||
}
|
||||
|
@ -50,6 +50,7 @@ public final class CachedGroupData: CachedPeerData {
|
||||
public let hasScheduledMessages: Bool
|
||||
public let invitedBy: PeerId?
|
||||
public let photo: TelegramMediaImage?
|
||||
public let autoremoveTimeout: CachedPeerAutoremoveTimeout
|
||||
|
||||
public let peerIds: Set<PeerId>
|
||||
public let messageIds: Set<MessageId>
|
||||
@ -70,11 +71,12 @@ public final class CachedGroupData: CachedPeerData {
|
||||
self.hasScheduledMessages = false
|
||||
self.invitedBy = nil
|
||||
self.photo = nil
|
||||
self.autoremoveTimeout = .unknown
|
||||
|
||||
self.activeCall = nil
|
||||
}
|
||||
|
||||
public init(participants: CachedGroupParticipants?, exportedInvitation: ExportedInvitation?, botInfos: [CachedPeerBotInfo], peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, about: String?, flags: CachedGroupFlags, hasScheduledMessages: Bool, invitedBy: PeerId?, photo: TelegramMediaImage?, activeCall: CachedChannelData.ActiveCall?) {
|
||||
public init(participants: CachedGroupParticipants?, exportedInvitation: ExportedInvitation?, botInfos: [CachedPeerBotInfo], peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, about: String?, flags: CachedGroupFlags, hasScheduledMessages: Bool, invitedBy: PeerId?, photo: TelegramMediaImage?, activeCall: CachedChannelData.ActiveCall?, autoremoveTimeout: CachedPeerAutoremoveTimeout) {
|
||||
self.participants = participants
|
||||
self.exportedInvitation = exportedInvitation
|
||||
self.botInfos = botInfos
|
||||
@ -86,6 +88,7 @@ public final class CachedGroupData: CachedPeerData {
|
||||
self.invitedBy = invitedBy
|
||||
self.photo = photo
|
||||
self.activeCall = activeCall
|
||||
self.autoremoveTimeout = autoremoveTimeout
|
||||
|
||||
var messageIds = Set<MessageId>()
|
||||
if let pinnedMessageId = self.pinnedMessageId {
|
||||
@ -128,6 +131,7 @@ public final class CachedGroupData: CachedPeerData {
|
||||
self.about = decoder.decodeOptionalStringForKey("ab")
|
||||
self.flags = CachedGroupFlags(rawValue: decoder.decodeInt32ForKey("fl", orElse: 0))
|
||||
self.hasScheduledMessages = decoder.decodeBoolForKey("hsm", orElse: false)
|
||||
self.autoremoveTimeout = decoder.decodeObjectForKey("artv", decoder: CachedPeerAutoremoveTimeout.init(decoder:)) as? CachedPeerAutoremoveTimeout ?? .unknown
|
||||
|
||||
self.invitedBy = decoder.decodeOptionalInt64ForKey("invBy").flatMap(PeerId.init)
|
||||
|
||||
@ -195,6 +199,7 @@ public final class CachedGroupData: CachedPeerData {
|
||||
}
|
||||
encoder.encodeInt32(self.flags.rawValue, forKey: "fl")
|
||||
encoder.encodeBool(self.hasScheduledMessages, forKey: "hsm")
|
||||
encoder.encodeObject(self.autoremoveTimeout, forKey: "artv")
|
||||
|
||||
if let invitedBy = self.invitedBy {
|
||||
encoder.encodeInt64(invitedBy.toInt64(), forKey: "invBy")
|
||||
@ -224,50 +229,54 @@ public final class CachedGroupData: CachedPeerData {
|
||||
return false
|
||||
}
|
||||
|
||||
return self.participants == other.participants && self.exportedInvitation == other.exportedInvitation && self.botInfos == other.botInfos && self.peerStatusSettings == other.peerStatusSettings && self.pinnedMessageId == other.pinnedMessageId && self.about == other.about && self.flags == other.flags && self.hasScheduledMessages == other.hasScheduledMessages && self.invitedBy == other.invitedBy
|
||||
return self.participants == other.participants && self.exportedInvitation == other.exportedInvitation && self.botInfos == other.botInfos && self.peerStatusSettings == other.peerStatusSettings && self.pinnedMessageId == other.pinnedMessageId && self.about == other.about && self.flags == other.flags && self.hasScheduledMessages == other.hasScheduledMessages && self.autoremoveTimeout == other.autoremoveTimeout && self.invitedBy == other.invitedBy
|
||||
}
|
||||
|
||||
public func withUpdatedParticipants(_ participants: CachedGroupParticipants?) -> CachedGroupData {
|
||||
return CachedGroupData(participants: participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
|
||||
return CachedGroupData(participants: participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedExportedInvitation(_ exportedInvitation: ExportedInvitation?) -> CachedGroupData {
|
||||
return CachedGroupData(participants: self.participants, exportedInvitation: exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
|
||||
return CachedGroupData(participants: self.participants, exportedInvitation: exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedBotInfos(_ botInfos: [CachedPeerBotInfo]) -> CachedGroupData {
|
||||
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
|
||||
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedPeerStatusSettings(_ peerStatusSettings: PeerStatusSettings?) -> CachedGroupData {
|
||||
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
|
||||
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> CachedGroupData {
|
||||
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
|
||||
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedAbout(_ about: String?) -> CachedGroupData {
|
||||
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
|
||||
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedFlags(_ flags: CachedGroupFlags) -> CachedGroupData {
|
||||
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
|
||||
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedHasScheduledMessages(_ hasScheduledMessages: Bool) -> CachedGroupData {
|
||||
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall)
|
||||
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedInvitedBy(_ invitedBy: PeerId?) -> CachedGroupData {
|
||||
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: invitedBy, photo: self.photo, activeCall: self.activeCall)
|
||||
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedPhoto(_ photo: TelegramMediaImage?) -> CachedGroupData {
|
||||
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: photo, activeCall: self.activeCall)
|
||||
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedActiveCall(_ activeCall: CachedChannelData.ActiveCall?) -> CachedGroupData {
|
||||
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: activeCall)
|
||||
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: activeCall, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedAutoremoveTimeout(_ autoremoveTimeout: CachedPeerAutoremoveTimeout) -> CachedGroupData {
|
||||
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: autoremoveTimeout)
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,78 @@
|
||||
import Foundation
|
||||
import Postbox
|
||||
|
||||
public enum CachedPeerAutoremoveTimeout: Equatable, PostboxCoding {
|
||||
public struct Value: Equatable, PostboxCoding {
|
||||
public var myValue: Int32?
|
||||
public var peerValue: Int32?
|
||||
public var isGlobal: Bool
|
||||
|
||||
public init(myValue: Int32?, peerValue: Int32?, isGlobal: Bool) {
|
||||
self.myValue = myValue
|
||||
self.peerValue = peerValue
|
||||
self.isGlobal = isGlobal
|
||||
}
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
self.myValue = decoder.decodeOptionalInt32ForKey("myValue")
|
||||
self.peerValue = decoder.decodeOptionalInt32ForKey("peerValue")
|
||||
self.isGlobal = decoder.decodeInt32ForKey("isGlobal", orElse: 1) != 0
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
if let myValue = self.myValue {
|
||||
encoder.encodeInt32(myValue, forKey: "myValue")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "myValue")
|
||||
}
|
||||
if let peerValue = self.peerValue {
|
||||
encoder.encodeInt32(peerValue, forKey: "peerValue")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "peerValue")
|
||||
}
|
||||
encoder.encodeInt32(self.isGlobal ? 1 : 0, forKey: "isGlobal")
|
||||
}
|
||||
|
||||
public var effectiveValue: Int32? {
|
||||
if let myValue = self.myValue, let peerValue = self.peerValue {
|
||||
return min(myValue, peerValue)
|
||||
} else if let myValue = self.myValue {
|
||||
return myValue
|
||||
} else if let peerValue = self.peerValue {
|
||||
return peerValue
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case unknown
|
||||
case known(Value?)
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
switch decoder.decodeInt32ForKey("_v", orElse: 0) {
|
||||
case 1:
|
||||
self = .known(decoder.decodeObjectForKey("v", decoder: Value.init(decoder:)) as? Value)
|
||||
default:
|
||||
self = .unknown
|
||||
}
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
switch self {
|
||||
case .unknown:
|
||||
encoder.encodeInt32(0, forKey: "_v")
|
||||
case let .known(value):
|
||||
encoder.encodeInt32(1, forKey: "_v")
|
||||
if let value = value {
|
||||
encoder.encodeObject(value, forKey: "v")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "v")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final class CachedUserData: CachedPeerData {
|
||||
public let about: String?
|
||||
public let botInfo: BotInfo?
|
||||
@ -13,6 +85,7 @@ public final class CachedUserData: CachedPeerData {
|
||||
public let callsPrivate: Bool
|
||||
public let canPinMessages: Bool
|
||||
public let hasScheduledMessages: Bool
|
||||
public let autoremoveTimeout: CachedPeerAutoremoveTimeout
|
||||
|
||||
public let peerIds = Set<PeerId>()
|
||||
public let messageIds: Set<MessageId>
|
||||
@ -30,10 +103,11 @@ public final class CachedUserData: CachedPeerData {
|
||||
self.callsPrivate = false
|
||||
self.canPinMessages = false
|
||||
self.hasScheduledMessages = false
|
||||
self.autoremoveTimeout = .unknown
|
||||
self.messageIds = Set()
|
||||
}
|
||||
|
||||
public init(about: String?, botInfo: BotInfo?, peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, isBlocked: Bool, commonGroupCount: Int32, voiceCallsAvailable: Bool, videoCallsAvailable: Bool, callsPrivate: Bool, canPinMessages: Bool, hasScheduledMessages: Bool) {
|
||||
public init(about: String?, botInfo: BotInfo?, peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, isBlocked: Bool, commonGroupCount: Int32, voiceCallsAvailable: Bool, videoCallsAvailable: Bool, callsPrivate: Bool, canPinMessages: Bool, hasScheduledMessages: Bool, autoremoveTimeout: CachedPeerAutoremoveTimeout) {
|
||||
self.about = about
|
||||
self.botInfo = botInfo
|
||||
self.peerStatusSettings = peerStatusSettings
|
||||
@ -45,6 +119,7 @@ public final class CachedUserData: CachedPeerData {
|
||||
self.callsPrivate = callsPrivate
|
||||
self.canPinMessages = canPinMessages
|
||||
self.hasScheduledMessages = hasScheduledMessages
|
||||
self.autoremoveTimeout = autoremoveTimeout
|
||||
var messageIds = Set<MessageId>()
|
||||
if let pinnedMessageId = self.pinnedMessageId {
|
||||
messageIds.insert(pinnedMessageId)
|
||||
@ -74,6 +149,7 @@ public final class CachedUserData: CachedPeerData {
|
||||
self.callsPrivate = decoder.decodeInt32ForKey("cp", orElse: 0) != 0
|
||||
self.canPinMessages = decoder.decodeInt32ForKey("cpm", orElse: 0) != 0
|
||||
self.hasScheduledMessages = decoder.decodeBoolForKey("hsm", orElse: false)
|
||||
self.autoremoveTimeout = decoder.decodeObjectForKey("artv", decoder: CachedPeerAutoremoveTimeout.init(decoder:)) as? CachedPeerAutoremoveTimeout ?? .unknown
|
||||
|
||||
var messageIds = Set<MessageId>()
|
||||
if let pinnedMessageId = self.pinnedMessageId {
|
||||
@ -114,6 +190,7 @@ public final class CachedUserData: CachedPeerData {
|
||||
encoder.encodeInt32(self.callsPrivate ? 1 : 0, forKey: "cp")
|
||||
encoder.encodeInt32(self.canPinMessages ? 1 : 0, forKey: "cpm")
|
||||
encoder.encodeBool(self.hasScheduledMessages, forKey: "hsm")
|
||||
encoder.encodeObject(self.autoremoveTimeout, forKey: "artv")
|
||||
}
|
||||
|
||||
public func isEqual(to: CachedPeerData) -> Bool {
|
||||
@ -128,50 +205,54 @@ public final class CachedUserData: CachedPeerData {
|
||||
return false
|
||||
}
|
||||
|
||||
return other.about == self.about && other.botInfo == self.botInfo && self.peerStatusSettings == other.peerStatusSettings && self.isBlocked == other.isBlocked && self.commonGroupCount == other.commonGroupCount && self.voiceCallsAvailable == other.voiceCallsAvailable && self.videoCallsAvailable == other.videoCallsAvailable && self.callsPrivate == other.callsPrivate && self.hasScheduledMessages == other.hasScheduledMessages
|
||||
return other.about == self.about && other.botInfo == self.botInfo && self.peerStatusSettings == other.peerStatusSettings && self.isBlocked == other.isBlocked && self.commonGroupCount == other.commonGroupCount && self.voiceCallsAvailable == other.voiceCallsAvailable && self.videoCallsAvailable == other.videoCallsAvailable && self.callsPrivate == other.callsPrivate && self.hasScheduledMessages == other.hasScheduledMessages && self.autoremoveTimeout == other.autoremoveTimeout
|
||||
}
|
||||
|
||||
public func withUpdatedAbout(_ about: String?) -> CachedUserData {
|
||||
return CachedUserData(about: about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages)
|
||||
return CachedUserData(about: about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedBotInfo(_ botInfo: BotInfo?) -> CachedUserData {
|
||||
return CachedUserData(about: self.about, botInfo: botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages)
|
||||
return CachedUserData(about: self.about, botInfo: botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedPeerStatusSettings(_ peerStatusSettings: PeerStatusSettings) -> CachedUserData {
|
||||
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages)
|
||||
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> CachedUserData {
|
||||
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages)
|
||||
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedIsBlocked(_ isBlocked: Bool) -> CachedUserData {
|
||||
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages)
|
||||
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedCommonGroupCount(_ commonGroupCount: Int32) -> CachedUserData {
|
||||
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages)
|
||||
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedVoiceCallsAvailable(_ voiceCallsAvailable: Bool) -> CachedUserData {
|
||||
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages)
|
||||
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedVideoCallsAvailable(_ videoCallsAvailable: Bool) -> CachedUserData {
|
||||
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages)
|
||||
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedCallsPrivate(_ callsPrivate: Bool) -> CachedUserData {
|
||||
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages)
|
||||
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedCanPinMessages(_ canPinMessages: Bool) -> CachedUserData {
|
||||
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: canPinMessages, hasScheduledMessages: self.hasScheduledMessages)
|
||||
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedHasScheduledMessages(_ hasScheduledMessages: Bool) -> CachedUserData {
|
||||
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: hasScheduledMessages)
|
||||
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout)
|
||||
}
|
||||
|
||||
public func withUpdatedAutoremoveTimeout(_ autoremoveTimeout: CachedPeerAutoremoveTimeout) -> CachedUserData {
|
||||
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: autoremoveTimeout)
|
||||
}
|
||||
}
|
||||
|
@ -11,8 +11,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-457104426] = { return Api.InputGeoPoint.parse_inputGeoPointEmpty($0) }
|
||||
dict[1210199983] = { return Api.InputGeoPoint.parse_inputGeoPoint($0) }
|
||||
dict[-784000893] = { return Api.payments.ValidatedRequestedInfo.parse_validatedRequestedInfo($0) }
|
||||
dict[-500874592] = { return Api.ChatFull.parse_chatFull($0) }
|
||||
dict[-66811386] = { return Api.ChatFull.parse_channelFull($0) }
|
||||
dict[-500874592] = { return Api.ChatFull.parse_chatFull($0) }
|
||||
dict[-1159937629] = { return Api.PollResults.parse_pollResults($0) }
|
||||
dict[-925415106] = { return Api.ChatParticipant.parse_chatParticipant($0) }
|
||||
dict[-636267638] = { return Api.ChatParticipant.parse_chatParticipantCreator($0) }
|
||||
@ -109,6 +109,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-1522240089] = { return Api.UserFull.parse_userFull($0) }
|
||||
dict[-292807034] = { return Api.InputChannel.parse_inputChannelEmpty($0) }
|
||||
dict[-1343524562] = { return Api.InputChannel.parse_inputChannel($0) }
|
||||
dict[707290417] = { return Api.InputChannel.parse_inputChannelFromMessage($0) }
|
||||
dict[414687501] = { return Api.DcOption.parse_dcOption($0) }
|
||||
dict[997055186] = { return Api.PollAnswerVoters.parse_pollAnswerVoters($0) }
|
||||
dict[-1705233435] = { return Api.account.PasswordSettings.parse_passwordSettings($0) }
|
||||
@ -117,7 +118,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-1000708810] = { return Api.help.AppUpdate.parse_noAppUpdate($0) }
|
||||
dict[497489295] = { return Api.help.AppUpdate.parse_appUpdate($0) }
|
||||
dict[-209337866] = { return Api.LangPackDifference.parse_langPackDifference($0) }
|
||||
dict[-794007333] = { return Api.PeerHistoryTTL.parse_peerHistoryTTL($0) }
|
||||
dict[-815649386] = { return Api.PeerHistoryTTL.parse_peerHistoryTTLPM($0) }
|
||||
dict[1041354473] = { return Api.PeerHistoryTTL.parse_peerHistoryTTL($0) }
|
||||
dict[84438264] = { return Api.WallPaperSettings.parse_wallPaperSettings($0) }
|
||||
dict[-1519029347] = { return Api.EmojiURL.parse_emojiURL($0) }
|
||||
dict[1611985938] = { return Api.StatsGroupTopAdmin.parse_statsGroupTopAdmin($0) }
|
||||
@ -140,7 +142,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[1511503333] = { return Api.InputEncryptedFile.parse_inputEncryptedFile($0) }
|
||||
dict[767652808] = { return Api.InputEncryptedFile.parse_inputEncryptedFileBigUploaded($0) }
|
||||
dict[-1456996667] = { return Api.messages.InactiveChats.parse_inactiveChats($0) }
|
||||
dict[1690708501] = { return Api.GroupCallParticipant.parse_groupCallParticipant($0) }
|
||||
dict[-817921892] = { return Api.GroupCallParticipant.parse_groupCallParticipant($0) }
|
||||
dict[1443858741] = { return Api.messages.SentEncryptedMessage.parse_sentEncryptedMessage($0) }
|
||||
dict[-1802240206] = { return Api.messages.SentEncryptedMessage.parse_sentEncryptedFile($0) }
|
||||
dict[1571494644] = { return Api.ExportedMessageLink.parse_exportedMessageLink($0) }
|
||||
@ -274,6 +276,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-219423922] = { return Api.Update.parse_updateGroupCallParticipants($0) }
|
||||
dict[-1537295973] = { return Api.Update.parse_updateGroupCall($0) }
|
||||
dict[1059076315] = { return Api.Update.parse_updateBotInlineQuery($0) }
|
||||
dict[19291112] = { return Api.Update.parse_updatePeerHistoryTTL($0) }
|
||||
dict[136574537] = { return Api.messages.VotesList.parse_votesList($0) }
|
||||
dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) }
|
||||
dict[-373643672] = { return Api.FolderPeer.parse_folderPeer($0) }
|
||||
@ -398,6 +401,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[396093539] = { return Api.InputPeer.parse_inputPeerChat($0) }
|
||||
dict[2072935910] = { return Api.InputPeer.parse_inputPeerUser($0) }
|
||||
dict[548253432] = { return Api.InputPeer.parse_inputPeerChannel($0) }
|
||||
dict[398123750] = { return Api.InputPeer.parse_inputPeerUserFromMessage($0) }
|
||||
dict[-1667893317] = { return Api.InputPeer.parse_inputPeerChannelFromMessage($0) }
|
||||
dict[568808380] = { return Api.upload.WebFile.parse_webFile($0) }
|
||||
dict[-116274796] = { return Api.Contact.parse_contact($0) }
|
||||
dict[-1078332329] = { return Api.help.PassportConfig.parse_passportConfigNotModified($0) }
|
||||
@ -816,7 +821,6 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-1730095465] = { return Api.MessageAction.parse_messageActionGeoProximityReached($0) }
|
||||
dict[2047704898] = { return Api.MessageAction.parse_messageActionGroupCall($0) }
|
||||
dict[1991897370] = { return Api.MessageAction.parse_messageActionInviteToGroupCall($0) }
|
||||
dict[-1441072131] = { return Api.MessageAction.parse_messageActionSetMessagesTTL($0) }
|
||||
dict[1399245077] = { return Api.PhoneCall.parse_phoneCallEmpty($0) }
|
||||
dict[462375633] = { return Api.PhoneCall.parse_phoneCallWaiting($0) }
|
||||
dict[-2014659757] = { return Api.PhoneCall.parse_phoneCallRequested($0) }
|
||||
@ -834,7 +838,6 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-1290580579] = { return Api.contacts.Found.parse_found($0) }
|
||||
dict[-368018716] = { return Api.ChannelAdminLogEventsFilter.parse_channelAdminLogEventsFilter($0) }
|
||||
dict[-1676371894] = { return Api.ThemeSettings.parse_themeSettings($0) }
|
||||
dict[1889961234] = { return Api.PeerNotifySettings.parse_peerNotifySettingsEmpty($0) }
|
||||
dict[-1353671392] = { return Api.PeerNotifySettings.parse_peerNotifySettings($0) }
|
||||
dict[-1995686519] = { return Api.InputBotInlineMessageID.parse_inputBotInlineMessageID($0) }
|
||||
dict[-1282352120] = { return Api.PageRelatedArticle.parse_pageRelatedArticle($0) }
|
||||
|
@ -142,32 +142,11 @@ public extension Api {
|
||||
|
||||
}
|
||||
public enum ChatFull: TypeConstructorDescription {
|
||||
case chatFull(flags: Int32, id: Int32, about: String, participants: Api.ChatParticipants, chatPhoto: Api.Photo?, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite?, botInfo: [Api.BotInfo]?, pinnedMsgId: Int32?, folderId: Int32?, call: Api.InputGroupCall?, ttl: Api.PeerHistoryTTL?)
|
||||
case channelFull(flags: Int32, id: Int32, about: String, participantsCount: Int32?, adminsCount: Int32?, kickedCount: Int32?, bannedCount: Int32?, onlineCount: Int32?, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, chatPhoto: Api.Photo, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite?, botInfo: [Api.BotInfo], migratedFromChatId: Int32?, migratedFromMaxId: Int32?, pinnedMsgId: Int32?, stickerset: Api.StickerSet?, availableMinId: Int32?, folderId: Int32?, linkedChatId: Int32?, location: Api.ChannelLocation?, slowmodeSeconds: Int32?, slowmodeNextSendDate: Int32?, statsDc: Int32?, pts: Int32, call: Api.InputGroupCall?, ttl: Api.PeerHistoryTTL?)
|
||||
case chatFull(flags: Int32, id: Int32, about: String, participants: Api.ChatParticipants, chatPhoto: Api.Photo?, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite?, botInfo: [Api.BotInfo]?, pinnedMsgId: Int32?, folderId: Int32?, call: Api.InputGroupCall?, ttl: Api.PeerHistoryTTL?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .chatFull(let flags, let id, let about, let participants, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let pinnedMsgId, let folderId, let call, let ttl):
|
||||
if boxed {
|
||||
buffer.appendInt32(-500874592)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(id, buffer: buffer, boxed: false)
|
||||
serializeString(about, buffer: buffer, boxed: false)
|
||||
participants.serialize(buffer, true)
|
||||
if Int(flags) & Int(1 << 2) != 0 {chatPhoto!.serialize(buffer, true)}
|
||||
notifySettings.serialize(buffer, true)
|
||||
if Int(flags) & Int(1 << 13) != 0 {exportedInvite!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(botInfo!.count))
|
||||
for item in botInfo! {
|
||||
item.serialize(buffer, true)
|
||||
}}
|
||||
if Int(flags) & Int(1 << 6) != 0 {serializeInt32(pinnedMsgId!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 11) != 0 {serializeInt32(folderId!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 12) != 0 {call!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 14) != 0 {ttl!.serialize(buffer, true)}
|
||||
break
|
||||
case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts, let call, let ttl):
|
||||
if boxed {
|
||||
buffer.appendInt32(-66811386)
|
||||
@ -206,76 +185,39 @@ public extension Api {
|
||||
if Int(flags) & Int(1 << 21) != 0 {call!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 24) != 0 {ttl!.serialize(buffer, true)}
|
||||
break
|
||||
case .chatFull(let flags, let id, let about, let participants, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let pinnedMsgId, let folderId, let call, let ttl):
|
||||
if boxed {
|
||||
buffer.appendInt32(-500874592)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(id, buffer: buffer, boxed: false)
|
||||
serializeString(about, buffer: buffer, boxed: false)
|
||||
participants.serialize(buffer, true)
|
||||
if Int(flags) & Int(1 << 2) != 0 {chatPhoto!.serialize(buffer, true)}
|
||||
notifySettings.serialize(buffer, true)
|
||||
if Int(flags) & Int(1 << 13) != 0 {exportedInvite!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(botInfo!.count))
|
||||
for item in botInfo! {
|
||||
item.serialize(buffer, true)
|
||||
}}
|
||||
if Int(flags) & Int(1 << 6) != 0 {serializeInt32(pinnedMsgId!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 11) != 0 {serializeInt32(folderId!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 12) != 0 {call!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 14) != 0 {ttl!.serialize(buffer, true)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .chatFull(let flags, let id, let about, let participants, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let pinnedMsgId, let folderId, let call, let ttl):
|
||||
return ("chatFull", [("flags", flags), ("id", id), ("about", about), ("participants", participants), ("chatPhoto", chatPhoto), ("notifySettings", notifySettings), ("exportedInvite", exportedInvite), ("botInfo", botInfo), ("pinnedMsgId", pinnedMsgId), ("folderId", folderId), ("call", call), ("ttl", ttl)])
|
||||
case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts, let call, let ttl):
|
||||
return ("channelFull", [("flags", flags), ("id", id), ("about", about), ("participantsCount", participantsCount), ("adminsCount", adminsCount), ("kickedCount", kickedCount), ("bannedCount", bannedCount), ("onlineCount", onlineCount), ("readInboxMaxId", readInboxMaxId), ("readOutboxMaxId", readOutboxMaxId), ("unreadCount", unreadCount), ("chatPhoto", chatPhoto), ("notifySettings", notifySettings), ("exportedInvite", exportedInvite), ("botInfo", botInfo), ("migratedFromChatId", migratedFromChatId), ("migratedFromMaxId", migratedFromMaxId), ("pinnedMsgId", pinnedMsgId), ("stickerset", stickerset), ("availableMinId", availableMinId), ("folderId", folderId), ("linkedChatId", linkedChatId), ("location", location), ("slowmodeSeconds", slowmodeSeconds), ("slowmodeNextSendDate", slowmodeNextSendDate), ("statsDc", statsDc), ("pts", pts), ("call", call), ("ttl", ttl)])
|
||||
case .chatFull(let flags, let id, let about, let participants, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let pinnedMsgId, let folderId, let call, let ttl):
|
||||
return ("chatFull", [("flags", flags), ("id", id), ("about", about), ("participants", participants), ("chatPhoto", chatPhoto), ("notifySettings", notifySettings), ("exportedInvite", exportedInvite), ("botInfo", botInfo), ("pinnedMsgId", pinnedMsgId), ("folderId", folderId), ("call", call), ("ttl", ttl)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_chatFull(_ reader: BufferReader) -> ChatFull? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: String?
|
||||
_3 = parseString(reader)
|
||||
var _4: Api.ChatParticipants?
|
||||
if let signature = reader.readInt32() {
|
||||
_4 = Api.parse(reader, signature: signature) as? Api.ChatParticipants
|
||||
}
|
||||
var _5: Api.Photo?
|
||||
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
|
||||
_5 = Api.parse(reader, signature: signature) as? Api.Photo
|
||||
} }
|
||||
var _6: Api.PeerNotifySettings?
|
||||
if let signature = reader.readInt32() {
|
||||
_6 = Api.parse(reader, signature: signature) as? Api.PeerNotifySettings
|
||||
}
|
||||
var _7: Api.ExportedChatInvite?
|
||||
if Int(_1!) & Int(1 << 13) != 0 {if let signature = reader.readInt32() {
|
||||
_7 = Api.parse(reader, signature: signature) as? Api.ExportedChatInvite
|
||||
} }
|
||||
var _8: [Api.BotInfo]?
|
||||
if Int(_1!) & Int(1 << 3) != 0 {if let _ = reader.readInt32() {
|
||||
_8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.BotInfo.self)
|
||||
} }
|
||||
var _9: Int32?
|
||||
if Int(_1!) & Int(1 << 6) != 0 {_9 = reader.readInt32() }
|
||||
var _10: Int32?
|
||||
if Int(_1!) & Int(1 << 11) != 0 {_10 = reader.readInt32() }
|
||||
var _11: Api.InputGroupCall?
|
||||
if Int(_1!) & Int(1 << 12) != 0 {if let signature = reader.readInt32() {
|
||||
_11 = Api.parse(reader, signature: signature) as? Api.InputGroupCall
|
||||
} }
|
||||
var _12: Api.PeerHistoryTTL?
|
||||
if Int(_1!) & Int(1 << 14) != 0 {if let signature = reader.readInt32() {
|
||||
_12 = Api.parse(reader, signature: signature) as? Api.PeerHistoryTTL
|
||||
} }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
let _c7 = (Int(_1!) & Int(1 << 13) == 0) || _7 != nil
|
||||
let _c8 = (Int(_1!) & Int(1 << 3) == 0) || _8 != nil
|
||||
let _c9 = (Int(_1!) & Int(1 << 6) == 0) || _9 != nil
|
||||
let _c10 = (Int(_1!) & Int(1 << 11) == 0) || _10 != nil
|
||||
let _c11 = (Int(_1!) & Int(1 << 12) == 0) || _11 != nil
|
||||
let _c12 = (Int(_1!) & Int(1 << 14) == 0) || _12 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 {
|
||||
return Api.ChatFull.chatFull(flags: _1!, id: _2!, about: _3!, participants: _4!, chatPhoto: _5, notifySettings: _6!, exportedInvite: _7, botInfo: _8, pinnedMsgId: _9, folderId: _10, call: _11, ttl: _12)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_channelFull(_ reader: BufferReader) -> ChatFull? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
@ -387,6 +329,64 @@ public extension Api {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_chatFull(_ reader: BufferReader) -> ChatFull? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: String?
|
||||
_3 = parseString(reader)
|
||||
var _4: Api.ChatParticipants?
|
||||
if let signature = reader.readInt32() {
|
||||
_4 = Api.parse(reader, signature: signature) as? Api.ChatParticipants
|
||||
}
|
||||
var _5: Api.Photo?
|
||||
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
|
||||
_5 = Api.parse(reader, signature: signature) as? Api.Photo
|
||||
} }
|
||||
var _6: Api.PeerNotifySettings?
|
||||
if let signature = reader.readInt32() {
|
||||
_6 = Api.parse(reader, signature: signature) as? Api.PeerNotifySettings
|
||||
}
|
||||
var _7: Api.ExportedChatInvite?
|
||||
if Int(_1!) & Int(1 << 13) != 0 {if let signature = reader.readInt32() {
|
||||
_7 = Api.parse(reader, signature: signature) as? Api.ExportedChatInvite
|
||||
} }
|
||||
var _8: [Api.BotInfo]?
|
||||
if Int(_1!) & Int(1 << 3) != 0 {if let _ = reader.readInt32() {
|
||||
_8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.BotInfo.self)
|
||||
} }
|
||||
var _9: Int32?
|
||||
if Int(_1!) & Int(1 << 6) != 0 {_9 = reader.readInt32() }
|
||||
var _10: Int32?
|
||||
if Int(_1!) & Int(1 << 11) != 0 {_10 = reader.readInt32() }
|
||||
var _11: Api.InputGroupCall?
|
||||
if Int(_1!) & Int(1 << 12) != 0 {if let signature = reader.readInt32() {
|
||||
_11 = Api.parse(reader, signature: signature) as? Api.InputGroupCall
|
||||
} }
|
||||
var _12: Api.PeerHistoryTTL?
|
||||
if Int(_1!) & Int(1 << 14) != 0 {if let signature = reader.readInt32() {
|
||||
_12 = Api.parse(reader, signature: signature) as? Api.PeerHistoryTTL
|
||||
} }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
let _c7 = (Int(_1!) & Int(1 << 13) == 0) || _7 != nil
|
||||
let _c8 = (Int(_1!) & Int(1 << 3) == 0) || _8 != nil
|
||||
let _c9 = (Int(_1!) & Int(1 << 6) == 0) || _9 != nil
|
||||
let _c10 = (Int(_1!) & Int(1 << 11) == 0) || _10 != nil
|
||||
let _c11 = (Int(_1!) & Int(1 << 12) == 0) || _11 != nil
|
||||
let _c12 = (Int(_1!) & Int(1 << 14) == 0) || _12 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 {
|
||||
return Api.ChatFull.chatFull(flags: _1!, id: _2!, about: _3!, participants: _4!, chatPhoto: _5, notifySettings: _6!, exportedInvite: _7, botInfo: _8, pinnedMsgId: _9, folderId: _10, call: _11, ttl: _12)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public enum PollResults: TypeConstructorDescription {
|
||||
@ -2934,6 +2934,7 @@ public extension Api {
|
||||
public enum InputChannel: TypeConstructorDescription {
|
||||
case inputChannelEmpty
|
||||
case inputChannel(channelId: Int32, accessHash: Int64)
|
||||
case inputChannelFromMessage(peer: Api.InputPeer, msgId: Int32, channelId: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
@ -2950,6 +2951,14 @@ public extension Api {
|
||||
serializeInt32(channelId, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputChannelFromMessage(let peer, let msgId, let channelId):
|
||||
if boxed {
|
||||
buffer.appendInt32(707290417)
|
||||
}
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(msgId, buffer: buffer, boxed: false)
|
||||
serializeInt32(channelId, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@ -2959,6 +2968,8 @@ public extension Api {
|
||||
return ("inputChannelEmpty", [])
|
||||
case .inputChannel(let channelId, let accessHash):
|
||||
return ("inputChannel", [("channelId", channelId), ("accessHash", accessHash)])
|
||||
case .inputChannelFromMessage(let peer, let msgId, let channelId):
|
||||
return ("inputChannelFromMessage", [("peer", peer), ("msgId", msgId), ("channelId", channelId)])
|
||||
}
|
||||
}
|
||||
|
||||
@ -2979,6 +2990,25 @@ public extension Api {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputChannelFromMessage(_ reader: BufferReader) -> InputChannel? {
|
||||
var _1: Api.InputPeer?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.InputPeer
|
||||
}
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.InputChannel.inputChannelFromMessage(peer: _1!, msgId: _2!, channelId: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public enum DcOption: TypeConstructorDescription {
|
||||
@ -3252,40 +3282,60 @@ public extension Api {
|
||||
|
||||
}
|
||||
public enum PeerHistoryTTL: TypeConstructorDescription {
|
||||
case peerHistoryTTL(flags: Int32, ttlPeriod: Int32, ttlPeriodMy: Int32?)
|
||||
case peerHistoryTTLPM(flags: Int32, myTtlPeriod: Int32?, peerTtlPeriod: Int32?)
|
||||
case peerHistoryTTL(ttlPeriod: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .peerHistoryTTL(let flags, let ttlPeriod, let ttlPeriodMy):
|
||||
case .peerHistoryTTLPM(let flags, let myTtlPeriod, let peerTtlPeriod):
|
||||
if boxed {
|
||||
buffer.appendInt32(-794007333)
|
||||
buffer.appendInt32(-815649386)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(myTtlPeriod!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 2) != 0 {serializeInt32(peerTtlPeriod!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
case .peerHistoryTTL(let ttlPeriod):
|
||||
if boxed {
|
||||
buffer.appendInt32(1041354473)
|
||||
}
|
||||
serializeInt32(ttlPeriod, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(ttlPeriodMy!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .peerHistoryTTL(let flags, let ttlPeriod, let ttlPeriodMy):
|
||||
return ("peerHistoryTTL", [("flags", flags), ("ttlPeriod", ttlPeriod), ("ttlPeriodMy", ttlPeriodMy)])
|
||||
case .peerHistoryTTLPM(let flags, let myTtlPeriod, let peerTtlPeriod):
|
||||
return ("peerHistoryTTLPM", [("flags", flags), ("myTtlPeriod", myTtlPeriod), ("peerTtlPeriod", peerTtlPeriod)])
|
||||
case .peerHistoryTTL(let ttlPeriod):
|
||||
return ("peerHistoryTTL", [("ttlPeriod", ttlPeriod)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_peerHistoryTTL(_ reader: BufferReader) -> PeerHistoryTTL? {
|
||||
public static func parse_peerHistoryTTLPM(_ reader: BufferReader) -> PeerHistoryTTL? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
if Int(_1!) & Int(1 << 1) != 0 {_2 = reader.readInt32() }
|
||||
var _3: Int32?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {_3 = reader.readInt32() }
|
||||
if Int(_1!) & Int(1 << 2) != 0 {_3 = reader.readInt32() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil
|
||||
let _c2 = (Int(_1!) & Int(1 << 1) == 0) || _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 2) == 0) || _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.PeerHistoryTTL.peerHistoryTTL(flags: _1!, ttlPeriod: _2!, ttlPeriodMy: _3)
|
||||
return Api.PeerHistoryTTL.peerHistoryTTLPM(flags: _1!, myTtlPeriod: _2, peerTtlPeriod: _3)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_peerHistoryTTL(_ reader: BufferReader) -> PeerHistoryTTL? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.PeerHistoryTTL.peerHistoryTTL(ttlPeriod: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
@ -3588,13 +3638,13 @@ public extension Api {
|
||||
|
||||
}
|
||||
public enum GroupCallParticipant: TypeConstructorDescription {
|
||||
case groupCallParticipant(flags: Int32, userId: Int32, date: Int32, activeDate: Int32?, source: Int32, volume: Int32?)
|
||||
case groupCallParticipant(flags: Int32, userId: Int32, date: Int32, activeDate: Int32?, source: Int32, volume: Int32?, params: Api.DataJSON?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .groupCallParticipant(let flags, let userId, let date, let activeDate, let source, let volume):
|
||||
case .groupCallParticipant(let flags, let userId, let date, let activeDate, let source, let volume, let params):
|
||||
if boxed {
|
||||
buffer.appendInt32(1690708501)
|
||||
buffer.appendInt32(-817921892)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(userId, buffer: buffer, boxed: false)
|
||||
@ -3602,14 +3652,15 @@ public extension Api {
|
||||
if Int(flags) & Int(1 << 3) != 0 {serializeInt32(activeDate!, buffer: buffer, boxed: false)}
|
||||
serializeInt32(source, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 7) != 0 {serializeInt32(volume!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 10) != 0 {params!.serialize(buffer, true)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .groupCallParticipant(let flags, let userId, let date, let activeDate, let source, let volume):
|
||||
return ("groupCallParticipant", [("flags", flags), ("userId", userId), ("date", date), ("activeDate", activeDate), ("source", source), ("volume", volume)])
|
||||
case .groupCallParticipant(let flags, let userId, let date, let activeDate, let source, let volume, let params):
|
||||
return ("groupCallParticipant", [("flags", flags), ("userId", userId), ("date", date), ("activeDate", activeDate), ("source", source), ("volume", volume), ("params", params)])
|
||||
}
|
||||
}
|
||||
|
||||
@ -3626,14 +3677,19 @@ public extension Api {
|
||||
_5 = reader.readInt32()
|
||||
var _6: Int32?
|
||||
if Int(_1!) & Int(1 << 7) != 0 {_6 = reader.readInt32() }
|
||||
var _7: Api.DataJSON?
|
||||
if Int(_1!) & Int(1 << 10) != 0 {if let signature = reader.readInt32() {
|
||||
_7 = Api.parse(reader, signature: signature) as? Api.DataJSON
|
||||
} }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 3) == 0) || _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = (Int(_1!) & Int(1 << 7) == 0) || _6 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
||||
return Api.GroupCallParticipant.groupCallParticipant(flags: _1!, userId: _2!, date: _3!, activeDate: _4, source: _5!, volume: _6)
|
||||
let _c7 = (Int(_1!) & Int(1 << 10) == 0) || _7 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
|
||||
return Api.GroupCallParticipant.groupCallParticipant(flags: _1!, userId: _2!, date: _3!, activeDate: _4, source: _5!, volume: _6, params: _7)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
@ -4602,6 +4658,7 @@ public extension Api {
|
||||
case updateGroupCallParticipants(call: Api.InputGroupCall, participants: [Api.GroupCallParticipant], version: Int32)
|
||||
case updateGroupCall(chatId: Int32, call: Api.GroupCall)
|
||||
case updateBotInlineQuery(flags: Int32, queryId: Int64, userId: Int32, query: String, geo: Api.GeoPoint?, peerType: Api.InlineQueryPeerType?, offset: String)
|
||||
case updatePeerHistoryTTL(flags: Int32, peer: Api.Peer, ttl: Api.PeerHistoryTTL?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
@ -5363,6 +5420,14 @@ public extension Api {
|
||||
if Int(flags) & Int(1 << 1) != 0 {peerType!.serialize(buffer, true)}
|
||||
serializeString(offset, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .updatePeerHistoryTTL(let flags, let peer, let ttl):
|
||||
if boxed {
|
||||
buffer.appendInt32(19291112)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
if Int(flags) & Int(1 << 0) != 0 {ttl!.serialize(buffer, true)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@ -5544,6 +5609,8 @@ public extension Api {
|
||||
return ("updateGroupCall", [("chatId", chatId), ("call", call)])
|
||||
case .updateBotInlineQuery(let flags, let queryId, let userId, let query, let geo, let peerType, let offset):
|
||||
return ("updateBotInlineQuery", [("flags", flags), ("queryId", queryId), ("userId", userId), ("query", query), ("geo", geo), ("peerType", peerType), ("offset", offset)])
|
||||
case .updatePeerHistoryTTL(let flags, let peer, let ttl):
|
||||
return ("updatePeerHistoryTTL", [("flags", flags), ("peer", peer), ("ttl", ttl)])
|
||||
}
|
||||
}
|
||||
|
||||
@ -7069,6 +7136,27 @@ public extension Api {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_updatePeerHistoryTTL(_ reader: BufferReader) -> Update? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Api.Peer?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
}
|
||||
var _3: Api.PeerHistoryTTL?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() {
|
||||
_3 = Api.parse(reader, signature: signature) as? Api.PeerHistoryTTL
|
||||
} }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.Update.updatePeerHistoryTTL(flags: _1!, peer: _2!, ttl: _3)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public enum PopularContact: TypeConstructorDescription {
|
||||
@ -10287,12 +10375,14 @@ public extension Api {
|
||||
}
|
||||
|
||||
}
|
||||
public enum InputPeer: TypeConstructorDescription {
|
||||
indirect public enum InputPeer: TypeConstructorDescription {
|
||||
case inputPeerEmpty
|
||||
case inputPeerSelf
|
||||
case inputPeerChat(chatId: Int32)
|
||||
case inputPeerUser(userId: Int32, accessHash: Int64)
|
||||
case inputPeerChannel(channelId: Int32, accessHash: Int64)
|
||||
case inputPeerUserFromMessage(peer: Api.InputPeer, msgId: Int32, userId: Int32)
|
||||
case inputPeerChannelFromMessage(peer: Api.InputPeer, msgId: Int32, channelId: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
@ -10328,6 +10418,22 @@ public extension Api {
|
||||
serializeInt32(channelId, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputPeerUserFromMessage(let peer, let msgId, let userId):
|
||||
if boxed {
|
||||
buffer.appendInt32(398123750)
|
||||
}
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(msgId, buffer: buffer, boxed: false)
|
||||
serializeInt32(userId, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputPeerChannelFromMessage(let peer, let msgId, let channelId):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1667893317)
|
||||
}
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(msgId, buffer: buffer, boxed: false)
|
||||
serializeInt32(channelId, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@ -10343,6 +10449,10 @@ public extension Api {
|
||||
return ("inputPeerUser", [("userId", userId), ("accessHash", accessHash)])
|
||||
case .inputPeerChannel(let channelId, let accessHash):
|
||||
return ("inputPeerChannel", [("channelId", channelId), ("accessHash", accessHash)])
|
||||
case .inputPeerUserFromMessage(let peer, let msgId, let userId):
|
||||
return ("inputPeerUserFromMessage", [("peer", peer), ("msgId", msgId), ("userId", userId)])
|
||||
case .inputPeerChannelFromMessage(let peer, let msgId, let channelId):
|
||||
return ("inputPeerChannelFromMessage", [("peer", peer), ("msgId", msgId), ("channelId", channelId)])
|
||||
}
|
||||
}
|
||||
|
||||
@ -10391,6 +10501,44 @@ public extension Api {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputPeerUserFromMessage(_ reader: BufferReader) -> InputPeer? {
|
||||
var _1: Api.InputPeer?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.InputPeer
|
||||
}
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.InputPeer.inputPeerUserFromMessage(peer: _1!, msgId: _2!, userId: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputPeerChannelFromMessage(_ reader: BufferReader) -> InputPeer? {
|
||||
var _1: Api.InputPeer?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.InputPeer
|
||||
}
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.InputPeer.inputPeerChannelFromMessage(peer: _1!, msgId: _2!, channelId: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public enum Contact: TypeConstructorDescription {
|
||||
@ -20092,7 +20240,6 @@ public extension Api {
|
||||
case messageActionGeoProximityReached(fromId: Api.Peer, toId: Api.Peer, distance: Int32)
|
||||
case messageActionGroupCall(flags: Int32, call: Api.InputGroupCall, duration: Int32?)
|
||||
case messageActionInviteToGroupCall(call: Api.InputGroupCall, users: [Int32])
|
||||
case messageActionSetMessagesTTL(period: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
@ -20291,12 +20438,6 @@ public extension Api {
|
||||
serializeInt32(item, buffer: buffer, boxed: false)
|
||||
}
|
||||
break
|
||||
case .messageActionSetMessagesTTL(let period):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1441072131)
|
||||
}
|
||||
serializeInt32(period, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@ -20354,8 +20495,6 @@ public extension Api {
|
||||
return ("messageActionGroupCall", [("flags", flags), ("call", call), ("duration", duration)])
|
||||
case .messageActionInviteToGroupCall(let call, let users):
|
||||
return ("messageActionInviteToGroupCall", [("call", call), ("users", users)])
|
||||
case .messageActionSetMessagesTTL(let period):
|
||||
return ("messageActionSetMessagesTTL", [("period", period)])
|
||||
}
|
||||
}
|
||||
|
||||
@ -20682,17 +20821,6 @@ public extension Api {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_messageActionSetMessagesTTL(_ reader: BufferReader) -> MessageAction? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.MessageAction.messageActionSetMessagesTTL(period: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public enum PhoneCall: TypeConstructorDescription {
|
||||
@ -21290,17 +21418,10 @@ public extension Api {
|
||||
|
||||
}
|
||||
public enum PeerNotifySettings: TypeConstructorDescription {
|
||||
case peerNotifySettingsEmpty
|
||||
case peerNotifySettings(flags: Int32, showPreviews: Api.Bool?, silent: Api.Bool?, muteUntil: Int32?, sound: String?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .peerNotifySettingsEmpty:
|
||||
if boxed {
|
||||
buffer.appendInt32(1889961234)
|
||||
}
|
||||
|
||||
break
|
||||
case .peerNotifySettings(let flags, let showPreviews, let silent, let muteUntil, let sound):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1353671392)
|
||||
@ -21316,16 +21437,11 @@ public extension Api {
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .peerNotifySettingsEmpty:
|
||||
return ("peerNotifySettingsEmpty", [])
|
||||
case .peerNotifySettings(let flags, let showPreviews, let silent, let muteUntil, let sound):
|
||||
return ("peerNotifySettings", [("flags", flags), ("showPreviews", showPreviews), ("silent", silent), ("muteUntil", muteUntil), ("sound", sound)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_peerNotifySettingsEmpty(_ reader: BufferReader) -> PeerNotifySettings? {
|
||||
return Api.PeerNotifySettings.peerNotifySettingsEmpty
|
||||
}
|
||||
public static func parse_peerNotifySettings(_ reader: BufferReader) -> PeerNotifySettings? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
|
@ -4111,16 +4111,17 @@ public extension Api {
|
||||
})
|
||||
}
|
||||
|
||||
public static func setPeerMessagesTTL(peer: Api.InputPeer, period: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
public static func setHistoryTTL(flags: Int32, peer: Api.InputPeer, period: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-56903344)
|
||||
buffer.appendInt32(-859093215)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(period, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "messages.setPeerMessagesTTL", parameters: [("peer", peer), ("period", period)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
|
||||
return (FunctionDescription(name: "messages.setHistoryTTL", parameters: [("flags", flags), ("peer", peer), ("period", period)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Bool?
|
||||
var result: Api.Updates?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.Bool
|
||||
result = Api.parse(reader, signature: signature) as? Api.Updates
|
||||
}
|
||||
return result
|
||||
})
|
||||
@ -4908,24 +4909,6 @@ public extension Api {
|
||||
})
|
||||
}
|
||||
|
||||
public static func sendCode(flags: Int32, phoneNumber: String, currentNumber: Api.Bool?, apiId: Int32, apiHash: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.auth.SentCode>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-2035355412)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeString(phoneNumber, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {currentNumber!.serialize(buffer, true)}
|
||||
serializeInt32(apiId, buffer: buffer, boxed: false)
|
||||
serializeString(apiHash, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "auth.sendCode", parameters: [("flags", flags), ("phoneNumber", phoneNumber), ("currentNumber", currentNumber), ("apiId", apiId), ("apiHash", apiHash)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.SentCode? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.auth.SentCode?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.auth.SentCode
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func signIn(phoneNumber: String, phoneCodeHash: String, phoneCode: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.auth.Authorization>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-1126886015)
|
||||
@ -5206,6 +5189,23 @@ public extension Api {
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func sendCode(phoneNumber: String, apiId: Int32, apiHash: String, settings: Api.CodeSettings) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.auth.SentCode>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-1502141361)
|
||||
serializeString(phoneNumber, buffer: buffer, boxed: false)
|
||||
serializeInt32(apiId, buffer: buffer, boxed: false)
|
||||
serializeString(apiHash, buffer: buffer, boxed: false)
|
||||
settings.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "auth.sendCode", parameters: [("phoneNumber", phoneNumber), ("apiId", apiId), ("apiHash", apiHash), ("settings", settings)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.SentCode? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.auth.SentCode?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.auth.SentCode
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public struct bots {
|
||||
public static func sendCustomRequest(customMethod: String, params: Api.DataJSON) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.DataJSON>) {
|
||||
@ -6322,22 +6322,6 @@ public extension Api {
|
||||
})
|
||||
}
|
||||
|
||||
public static func sendChangePhoneCode(flags: Int32, phoneNumber: String, currentNumber: Api.Bool?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.auth.SentCode>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(149257707)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeString(phoneNumber, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {currentNumber!.serialize(buffer, true)}
|
||||
return (FunctionDescription(name: "account.sendChangePhoneCode", parameters: [("flags", flags), ("phoneNumber", phoneNumber), ("currentNumber", currentNumber)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.SentCode? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.auth.SentCode?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.auth.SentCode
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func changePhone(phoneNumber: String, phoneCodeHash: String, phoneCode: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.User>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(1891839707)
|
||||
@ -6410,22 +6394,6 @@ public extension Api {
|
||||
})
|
||||
}
|
||||
|
||||
public static func sendConfirmPhoneCode(flags: Int32, hash: String, currentNumber: Api.Bool?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.auth.SentCode>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(353818557)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeString(hash, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {currentNumber!.serialize(buffer, true)}
|
||||
return (FunctionDescription(name: "account.sendConfirmPhoneCode", parameters: [("flags", flags), ("hash", hash), ("currentNumber", currentNumber)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.SentCode? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.auth.SentCode?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.auth.SentCode
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func confirmPhone(phoneCodeHash: String, phoneCode: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(1596029123)
|
||||
@ -6606,22 +6574,6 @@ public extension Api {
|
||||
})
|
||||
}
|
||||
|
||||
public static func sendVerifyPhoneCode(flags: Int32, phoneNumber: String, currentNumber: Api.Bool?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.auth.SentCode>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-2110553932)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeString(phoneNumber, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {currentNumber!.serialize(buffer, true)}
|
||||
return (FunctionDescription(name: "account.sendVerifyPhoneCode", parameters: [("flags", flags), ("phoneNumber", phoneNumber), ("currentNumber", currentNumber)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.SentCode? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.auth.SentCode?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.auth.SentCode
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func verifyPhone(phoneNumber: String, phoneCodeHash: String, phoneCode: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(1305716726)
|
||||
@ -7128,6 +7080,51 @@ public extension Api {
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func sendChangePhoneCode(phoneNumber: String, settings: Api.CodeSettings) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.auth.SentCode>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-2108208411)
|
||||
serializeString(phoneNumber, buffer: buffer, boxed: false)
|
||||
settings.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "account.sendChangePhoneCode", parameters: [("phoneNumber", phoneNumber), ("settings", settings)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.SentCode? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.auth.SentCode?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.auth.SentCode
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func sendConfirmPhoneCode(hash: String, settings: Api.CodeSettings) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.auth.SentCode>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(457157256)
|
||||
serializeString(hash, buffer: buffer, boxed: false)
|
||||
settings.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "account.sendConfirmPhoneCode", parameters: [("hash", hash), ("settings", settings)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.SentCode? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.auth.SentCode?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.auth.SentCode
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func sendVerifyPhoneCode(phoneNumber: String, settings: Api.CodeSettings) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.auth.SentCode>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-1516022023)
|
||||
serializeString(phoneNumber, buffer: buffer, boxed: false)
|
||||
settings.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "account.sendVerifyPhoneCode", parameters: [("phoneNumber", phoneNumber), ("settings", settings)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.SentCode? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.auth.SentCode?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.auth.SentCode
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public struct wallet {
|
||||
public static func sendLiteRequest(body: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.wallet.LiteResponse>) {
|
||||
@ -7208,21 +7205,6 @@ public extension Api {
|
||||
})
|
||||
}
|
||||
|
||||
public static func getDifference(langCode: String, fromVersion: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.LangPackDifference>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-1655576556)
|
||||
serializeString(langCode, buffer: buffer, boxed: false)
|
||||
serializeInt32(fromVersion, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "langpack.getDifference", parameters: [("langCode", langCode), ("fromVersion", fromVersion)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.LangPackDifference? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.LangPackDifference?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.LangPackDifference
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func getLanguage(langPack: String, langCode: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.LangPackLanguage>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(1784243458)
|
||||
@ -7237,6 +7219,22 @@ public extension Api {
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func getDifference(langPack: String, langCode: String, fromVersion: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.LangPackDifference>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-845657435)
|
||||
serializeString(langPack, buffer: buffer, boxed: false)
|
||||
serializeString(langCode, buffer: buffer, boxed: false)
|
||||
serializeInt32(fromVersion, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "langpack.getDifference", parameters: [("langPack", langPack), ("langCode", langCode), ("fromVersion", fromVersion)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.LangPackDifference? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.LangPackDifference?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.LangPackDifference
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public struct photos {
|
||||
public static func deletePhotos(id: [Api.InputPhoto]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Int64]>) {
|
||||
|
@ -305,9 +305,9 @@ public class CallStatusBarNodeImpl: CallStatusBarNode {
|
||||
var effectiveLevel: Float = 0.0
|
||||
var audioLevels = audioLevels
|
||||
if !strongSelf.currentIsMuted {
|
||||
audioLevels.append((PeerId(0), myAudioLevel, true))
|
||||
audioLevels.append((PeerId(0), 0, myAudioLevel, true))
|
||||
}
|
||||
effectiveLevel = audioLevels.map { $0.1 }.max() ?? 0.0
|
||||
effectiveLevel = audioLevels.map { $0.2 }.max() ?? 0.0
|
||||
strongSelf.backgroundNode.audioLevel = effectiveLevel
|
||||
}))
|
||||
}
|
||||
|
@ -928,6 +928,7 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
let setOnOrientationUpdated = view.setOnOrientationUpdated
|
||||
let setOnIsMirroredUpdated = view.setOnIsMirroredUpdated
|
||||
completion(PresentationCallVideoView(
|
||||
holder: view,
|
||||
view: view.view,
|
||||
setOnFirstFrameReceived: { f in
|
||||
setOnFirstFrameReceived(f)
|
||||
@ -997,6 +998,7 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
let setOnOrientationUpdated = view.setOnOrientationUpdated
|
||||
let setOnIsMirroredUpdated = view.setOnIsMirroredUpdated
|
||||
completion(PresentationCallVideoView(
|
||||
holder: view,
|
||||
view: view.view,
|
||||
setOnFirstFrameReceived: { f in
|
||||
setOnFirstFrameReceived(f)
|
||||
|
@ -16,6 +16,27 @@ import UniversalMediaPlayer
|
||||
import AccountContext
|
||||
import DeviceProximity
|
||||
|
||||
private extension GroupCallParticipantsContext.Participant {
|
||||
var allSsrcs: Set<UInt32> {
|
||||
var participantSsrcs = Set<UInt32>()
|
||||
participantSsrcs.insert(self.ssrc)
|
||||
if let jsonParams = self.jsonParams, let jsonData = jsonParams.data(using: .utf8), let json = try? JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any] {
|
||||
if let groups = json["ssrc-groups"] as? [Any] {
|
||||
for group in groups {
|
||||
if let group = group as? [String: Any] {
|
||||
if let groupSources = group["sources"] as? [UInt32] {
|
||||
for source in groupSources {
|
||||
participantSsrcs.insert(source)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return participantSsrcs
|
||||
}
|
||||
}
|
||||
|
||||
public final class AccountGroupCallContextImpl: AccountGroupCallContext {
|
||||
public final class Proxy {
|
||||
public let context: AccountGroupCallContextImpl
|
||||
@ -241,7 +262,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
}
|
||||
}
|
||||
|
||||
private let audioLevelsPromise = Promise<[(PeerId, Float, Bool)]>()
|
||||
private let audioLevelsPromise = Promise<[(PeerId, UInt32, Float, Bool)]>()
|
||||
|
||||
init() {
|
||||
}
|
||||
@ -278,10 +299,10 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
}
|
||||
}
|
||||
|
||||
var audioLevels: [(PeerId, Float, Bool)] = []
|
||||
for (peerId, _, level, hasVoice) in levels {
|
||||
var audioLevels: [(PeerId, UInt32, Float, Bool)] = []
|
||||
for (peerId, source, level, hasVoice) in levels {
|
||||
if level > 0.001 {
|
||||
audioLevels.append((peerId, level, hasVoice))
|
||||
audioLevels.append((peerId, source, level, hasVoice))
|
||||
}
|
||||
}
|
||||
|
||||
@ -294,7 +315,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
return self.speakingParticipantsPromise.get()
|
||||
}
|
||||
|
||||
func getAudioLevels() -> Signal<[(PeerId, Float, Bool)], NoError> {
|
||||
func getAudioLevels() -> Signal<[(PeerId, UInt32, Float, Bool)], NoError> {
|
||||
return self.audioLevelsPromise.get()
|
||||
}
|
||||
}
|
||||
@ -314,6 +335,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
public let peerId: PeerId
|
||||
public let peer: Peer?
|
||||
|
||||
public private(set) var isVideo: Bool
|
||||
|
||||
private let temporaryJoinTimestamp: Int32
|
||||
|
||||
private var internalState: InternalState = .requesting
|
||||
@ -321,6 +344,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
private var callContext: OngoingGroupCallContext?
|
||||
private var ssrcMapping: [UInt32: PeerId] = [:]
|
||||
|
||||
private var requestedSsrcs = Set<UInt32>()
|
||||
|
||||
private var summaryInfoState = Promise<SummaryInfoState?>(nil)
|
||||
private var summaryParticipantsState = Promise<SummaryParticipantsState?>(nil)
|
||||
|
||||
@ -363,7 +388,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
|
||||
private let speakingParticipantsContext = SpeakingParticipantsContext()
|
||||
private var speakingParticipantsReportTimestamp: [PeerId: Double] = [:]
|
||||
public var audioLevels: Signal<[(PeerId, Float, Bool)], NoError> {
|
||||
public var audioLevels: Signal<[(PeerId, UInt32, Float, Bool)], NoError> {
|
||||
return self.speakingParticipantsContext.getAudioLevels()
|
||||
}
|
||||
|
||||
@ -435,6 +460,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
}
|
||||
private let memberEventsPipeDisposable = MetaDisposable()
|
||||
|
||||
private let joinDisposable = MetaDisposable()
|
||||
private let requestDisposable = MetaDisposable()
|
||||
private var groupCallParticipantUpdatesDisposable: Disposable?
|
||||
|
||||
@ -456,6 +482,18 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
private var didConnectOnce: Bool = false
|
||||
private var toneRenderer: PresentationCallToneRenderer?
|
||||
|
||||
private var videoCapturer: OngoingCallVideoCapturer?
|
||||
|
||||
private let incomingVideoSourcePromise = Promise<[PeerId: UInt32]>([:])
|
||||
public var incomingVideoSources: Signal<[PeerId: UInt32], NoError> {
|
||||
return self.incomingVideoSourcePromise.get()
|
||||
}
|
||||
|
||||
private var missingSsrcs = Set<UInt32>()
|
||||
private var processedMissingSsrcs = Set<UInt32>()
|
||||
private let missingSsrcsDisposable = MetaDisposable()
|
||||
private var isRequestingMissingSsrcs: Bool = false
|
||||
|
||||
init(
|
||||
accountContext: AccountContext,
|
||||
audioSession: ManagedAudioSession,
|
||||
@ -479,6 +517,9 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
|
||||
self.temporaryJoinTimestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
|
||||
|
||||
//self.videoCapturer = OngoingCallVideoCapturer(keepLandscape: true)
|
||||
self.isVideo = self.videoCapturer != nil
|
||||
|
||||
var didReceiveAudioOutputs = false
|
||||
|
||||
if !audioSession.getIsHeadsetPluggedIn() {
|
||||
@ -581,6 +622,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
return
|
||||
}
|
||||
if case let .estabilished(callInfo, _, _, _) = strongSelf.internalState {
|
||||
var addedParticipants: [(UInt32, String?)] = []
|
||||
var removedSsrc: [UInt32] = []
|
||||
for (callId, update) in updates {
|
||||
if callId == callInfo.id {
|
||||
@ -600,6 +642,9 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
strongSelf._canBeRemoved.set(.single(true))
|
||||
}
|
||||
} else if case .joined = participantUpdate.participationStatusChange {
|
||||
addedParticipants.append((participantUpdate.ssrc, participantUpdate.jsonParams))
|
||||
} else if strongSelf.ssrcMapping[participantUpdate.ssrc] == nil {
|
||||
addedParticipants.append((participantUpdate.ssrc, participantUpdate.jsonParams))
|
||||
}
|
||||
}
|
||||
case let .call(isTerminated, _):
|
||||
@ -612,6 +657,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
if !removedSsrc.isEmpty {
|
||||
strongSelf.callContext?.removeSsrcs(ssrcs: removedSsrc)
|
||||
}
|
||||
//strongSelf.callContext?.addParticipants(participants: addedParticipants)
|
||||
}
|
||||
})
|
||||
|
||||
@ -669,6 +715,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
participants.append(GroupCallParticipantsContext.Participant(
|
||||
peer: accountPeer,
|
||||
ssrc: 0,
|
||||
jsonParams: nil,
|
||||
joinTimestamp: strongSelf.temporaryJoinTimestamp,
|
||||
activityTimestamp: nil,
|
||||
muteState: GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false),
|
||||
@ -731,6 +778,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
self.audioSessionActiveDisposable?.dispose()
|
||||
self.summaryStateDisposable?.dispose()
|
||||
self.audioSessionDisposable?.dispose()
|
||||
self.joinDisposable.dispose()
|
||||
self.requestDisposable.dispose()
|
||||
self.groupCallParticipantUpdatesDisposable?.dispose()
|
||||
self.leaveDisposable.dispose()
|
||||
@ -742,6 +790,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
self.participantsContextStateDisposable.dispose()
|
||||
self.myAudioLevelDisposable.dispose()
|
||||
self.memberEventsPipeDisposable.dispose()
|
||||
self.missingSsrcsDisposable.dispose()
|
||||
|
||||
self.myAudioLevelTimer?.invalidate()
|
||||
self.typingDisposable.dispose()
|
||||
@ -788,10 +837,39 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
break
|
||||
default:
|
||||
if case let .active(callInfo) = internalState {
|
||||
let callContext = OngoingGroupCallContext()
|
||||
let callContext = OngoingGroupCallContext(video: self.videoCapturer, participantDescriptionsRequired: { [weak self] ssrcs in
|
||||
Queue.mainQueue().async {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.maybeRequestParticipants(ssrcs: ssrcs)
|
||||
}
|
||||
})
|
||||
self.incomingVideoSourcePromise.set(callContext.videoSources
|
||||
|> deliverOnMainQueue
|
||||
|> map { [weak self] sources -> [PeerId: UInt32] in
|
||||
guard let strongSelf = self else {
|
||||
return [:]
|
||||
}
|
||||
var result: [PeerId: UInt32] = [:]
|
||||
for source in sources {
|
||||
if let peerId = strongSelf.ssrcMapping[source] {
|
||||
result[peerId] = source
|
||||
}
|
||||
}
|
||||
return result
|
||||
})
|
||||
self.callContext = callContext
|
||||
self.requestDisposable.set((callContext.joinPayload
|
||||
|> take(1)
|
||||
self.joinDisposable.set((callContext.joinPayload
|
||||
|> distinctUntilChanged(isEqual: { lhs, rhs in
|
||||
if lhs.0 != rhs.0 {
|
||||
return false
|
||||
}
|
||||
if lhs.1 != rhs.1 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
|> deliverOnMainQueue).start(next: { [weak self] joinPayload, ssrc in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
@ -809,6 +887,14 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
return
|
||||
}
|
||||
if let clientParams = joinCallResult.callInfo.clientParams {
|
||||
strongSelf.ssrcMapping.removeAll()
|
||||
var addedParticipants: [(UInt32, String?)] = []
|
||||
for participant in joinCallResult.state.participants {
|
||||
strongSelf.ssrcMapping[participant.ssrc] = participant.peer.id
|
||||
//addedParticipants.append((participant.ssrc, participant.jsonParams))
|
||||
}
|
||||
strongSelf.callContext?.setJoinResponse(payload: clientParams, participants: addedParticipants)
|
||||
|
||||
strongSelf.updateSessionState(internalState: .estabilished(info: joinCallResult.callInfo, clientParams: clientParams, localSsrc: ssrc, initialState: joinCallResult.state), audioSessionControl: strongSelf.audioSessionControl)
|
||||
}
|
||||
}, error: { error in
|
||||
@ -941,14 +1027,6 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
self.stateValue.defaultParticipantMuteState = initialState.defaultParticipantsAreMuted.isMuted ? .muted : .unmuted
|
||||
}
|
||||
|
||||
self.ssrcMapping.removeAll()
|
||||
var ssrcs: [UInt32] = []
|
||||
for participant in initialState.participants {
|
||||
self.ssrcMapping[participant.ssrc] = participant.peer.id
|
||||
ssrcs.append(participant.ssrc)
|
||||
}
|
||||
self.callContext?.setJoinResponse(payload: clientParams, ssrcs: ssrcs)
|
||||
|
||||
let accountContext = self.accountContext
|
||||
let peerId = self.peerId
|
||||
let rawAdminIds = Signal<Set<PeerId>, NoError> { subscriber in
|
||||
@ -1118,6 +1196,73 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
}
|
||||
}
|
||||
|
||||
private func maybeRequestParticipants(ssrcs: Set<UInt32>) {
|
||||
var missingSsrcs = ssrcs
|
||||
missingSsrcs.subtract(self.processedMissingSsrcs)
|
||||
if missingSsrcs.isEmpty {
|
||||
return
|
||||
}
|
||||
self.processedMissingSsrcs.formUnion(ssrcs)
|
||||
|
||||
var addedParticipants: [(UInt32, String?)] = []
|
||||
|
||||
if let membersValue = self.membersValue {
|
||||
for participant in membersValue.participants {
|
||||
let participantSsrcs = participant.allSsrcs
|
||||
|
||||
if !missingSsrcs.intersection(participantSsrcs).isEmpty {
|
||||
missingSsrcs.subtract(participantSsrcs)
|
||||
self.processedMissingSsrcs.formUnion(participantSsrcs)
|
||||
|
||||
addedParticipants.append((participant.ssrc, participant.jsonParams))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !addedParticipants.isEmpty {
|
||||
self.callContext?.addParticipants(participants: addedParticipants)
|
||||
}
|
||||
|
||||
if !missingSsrcs.isEmpty {
|
||||
self.missingSsrcs.formUnion(missingSsrcs)
|
||||
self.maybeRequestMissingSsrcs()
|
||||
}
|
||||
}
|
||||
|
||||
private func maybeRequestMissingSsrcs() {
|
||||
if self.isRequestingMissingSsrcs {
|
||||
return
|
||||
}
|
||||
if self.missingSsrcs.isEmpty {
|
||||
return
|
||||
}
|
||||
if case let .estabilished(callInfo, _, _, _) = self.internalState {
|
||||
self.isRequestingMissingSsrcs = true
|
||||
|
||||
let requestedSsrcs = self.missingSsrcs
|
||||
self.missingSsrcsDisposable.set((getGroupCallParticipants(account: self.account, callId: callInfo.id, accessHash: callInfo.accessHash, offset: "", ssrcs: Array(requestedSsrcs), limit: 100)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] state in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.isRequestingMissingSsrcs = false
|
||||
strongSelf.missingSsrcs.subtract(requestedSsrcs)
|
||||
|
||||
var addedParticipants: [(UInt32, String?)] = []
|
||||
|
||||
for participant in state.participants {
|
||||
addedParticipants.append((participant.ssrc, participant.jsonParams))
|
||||
}
|
||||
|
||||
if !addedParticipants.isEmpty {
|
||||
strongSelf.callContext?.addParticipants(participants: addedParticipants)
|
||||
}
|
||||
|
||||
strongSelf.maybeRequestMissingSsrcs()
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
private func startCheckingCallIfNeeded() {
|
||||
if self.checkCallDisposable != nil {
|
||||
return
|
||||
@ -1253,7 +1398,26 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
self.stateValue.muteState = nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public func requestVideo() {
|
||||
if self.videoCapturer == nil {
|
||||
let videoCapturer = OngoingCallVideoCapturer()
|
||||
self.videoCapturer = videoCapturer
|
||||
}
|
||||
self.isVideo = true
|
||||
if let videoCapturer = self.videoCapturer {
|
||||
self.callContext?.requestVideo(videoCapturer)
|
||||
}
|
||||
}
|
||||
|
||||
public func disableVideo() {
|
||||
self.isVideo = false
|
||||
if let _ = self.videoCapturer {
|
||||
self.videoCapturer = nil
|
||||
self.callContext?.disableVideo()
|
||||
}
|
||||
}
|
||||
|
||||
public func setVolume(peerId: PeerId, volume: Int32, sync: Bool) {
|
||||
for (ssrc, id) in self.ssrcMapping {
|
||||
if id == peerId {
|
||||
@ -1266,6 +1430,19 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
}
|
||||
}
|
||||
|
||||
public func setFullSizeVideo(peerId: PeerId?) {
|
||||
var resolvedSsrc: UInt32?
|
||||
if let peerId = peerId {
|
||||
for (ssrc, id) in self.ssrcMapping {
|
||||
if id == peerId {
|
||||
resolvedSsrc = ssrc
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
self.callContext?.setFullSizeVideoSsrc(ssrc: resolvedSsrc)
|
||||
}
|
||||
|
||||
public func setCurrentAudioOutput(_ output: AudioSessionOutput) {
|
||||
guard self.currentSelectedAudioOutputValue != output else {
|
||||
return
|
||||
@ -1376,6 +1553,10 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
self.callContext?.stop()
|
||||
self.callContext = nil
|
||||
|
||||
self.missingSsrcsDisposable.set(nil)
|
||||
self.missingSsrcs.removeAll()
|
||||
self.processedMissingSsrcs.removeAll()
|
||||
|
||||
self.internalState = .requesting
|
||||
self.isCurrentlyConnecting = nil
|
||||
|
||||
@ -1407,6 +1588,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
}
|
||||
}
|
||||
|
||||
self.joinDisposable.set(nil)
|
||||
self.requestDisposable.set((currentOrRequestedCall
|
||||
|> deliverOnMainQueue).start(next: { [weak self] value in
|
||||
guard let strongSelf = self else {
|
||||
@ -1497,6 +1679,71 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
self.participantsContext?.updateDefaultParticipantsAreMuted(isMuted: isMuted)
|
||||
}
|
||||
|
||||
public func makeIncomingVideoView(source: UInt32, completion: @escaping (PresentationCallVideoView?) -> Void) {
|
||||
self.callContext?.makeIncomingVideoView(source: source, completion: { view in
|
||||
if let view = view {
|
||||
let setOnFirstFrameReceived = view.setOnFirstFrameReceived
|
||||
let setOnOrientationUpdated = view.setOnOrientationUpdated
|
||||
let setOnIsMirroredUpdated = view.setOnIsMirroredUpdated
|
||||
completion(PresentationCallVideoView(
|
||||
holder: view,
|
||||
view: view.view,
|
||||
setOnFirstFrameReceived: { f in
|
||||
setOnFirstFrameReceived(f)
|
||||
},
|
||||
getOrientation: { [weak view] in
|
||||
if let view = view {
|
||||
let mappedValue: PresentationCallVideoView.Orientation
|
||||
switch view.getOrientation() {
|
||||
case .rotation0:
|
||||
mappedValue = .rotation0
|
||||
case .rotation90:
|
||||
mappedValue = .rotation90
|
||||
case .rotation180:
|
||||
mappedValue = .rotation180
|
||||
case .rotation270:
|
||||
mappedValue = .rotation270
|
||||
}
|
||||
return mappedValue
|
||||
} else {
|
||||
return .rotation0
|
||||
}
|
||||
},
|
||||
getAspect: { [weak view] in
|
||||
if let view = view {
|
||||
return view.getAspect()
|
||||
} else {
|
||||
return 0.0
|
||||
}
|
||||
},
|
||||
setOnOrientationUpdated: { f in
|
||||
setOnOrientationUpdated { value, aspect in
|
||||
let mappedValue: PresentationCallVideoView.Orientation
|
||||
switch value {
|
||||
case .rotation0:
|
||||
mappedValue = .rotation0
|
||||
case .rotation90:
|
||||
mappedValue = .rotation90
|
||||
case .rotation180:
|
||||
mappedValue = .rotation180
|
||||
case .rotation270:
|
||||
mappedValue = .rotation270
|
||||
}
|
||||
f?(mappedValue, aspect)
|
||||
}
|
||||
},
|
||||
setOnIsMirroredUpdated: { f in
|
||||
setOnIsMirroredUpdated { value in
|
||||
f?(value)
|
||||
}
|
||||
}
|
||||
))
|
||||
} else {
|
||||
completion(nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public func loadMoreMembers(token: String) {
|
||||
self.participantsContext?.loadMore(token: token)
|
||||
}
|
||||
|
@ -118,6 +118,169 @@ private final class VoiceChatControllerTitleNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
final class GroupVideoNode: ASDisplayNode {
|
||||
private let videoViewContainer: UIView
|
||||
private let videoView: PresentationCallVideoView
|
||||
|
||||
private var validLayout: CGSize?
|
||||
|
||||
var tapped: (() -> Void)?
|
||||
|
||||
init(videoView: PresentationCallVideoView) {
|
||||
self.videoViewContainer = UIView()
|
||||
self.videoView = videoView
|
||||
|
||||
super.init()
|
||||
|
||||
self.videoViewContainer.addSubview(self.videoView.view)
|
||||
self.view.addSubview(self.videoViewContainer)
|
||||
|
||||
videoView.setOnFirstFrameReceived({ [weak self] _ in
|
||||
Queue.mainQueue().async {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if let size = strongSelf.validLayout {
|
||||
strongSelf.updateLayout(size: size, transition: .immediate)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
videoView.setOnOrientationUpdated({ [weak self] _, _ in
|
||||
Queue.mainQueue().async {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if let size = strongSelf.validLayout {
|
||||
strongSelf.updateLayout(size: size, transition: .immediate)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
|
||||
}
|
||||
|
||||
@objc private func tapGesture(_ recognizer: UITapGestureRecognizer) {
|
||||
if case .ended = recognizer.state {
|
||||
self.tapped?()
|
||||
}
|
||||
}
|
||||
|
||||
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = size
|
||||
self.videoViewContainer.frame = CGRect(origin: CGPoint(), size: size)
|
||||
|
||||
let orientation = self.videoView.getOrientation()
|
||||
var aspect = self.videoView.getAspect()
|
||||
if aspect <= 0.01 {
|
||||
aspect = 3.0 / 4.0
|
||||
}
|
||||
|
||||
let rotatedAspect: CGFloat
|
||||
let angle: CGFloat
|
||||
let switchOrientation: Bool
|
||||
switch orientation {
|
||||
case .rotation0:
|
||||
angle = 0.0
|
||||
rotatedAspect = 1 / aspect
|
||||
switchOrientation = false
|
||||
case .rotation90:
|
||||
angle = CGFloat.pi / 2.0
|
||||
rotatedAspect = aspect
|
||||
switchOrientation = true
|
||||
case .rotation180:
|
||||
angle = CGFloat.pi
|
||||
rotatedAspect = 1 / aspect
|
||||
switchOrientation = false
|
||||
case .rotation270:
|
||||
angle = CGFloat.pi * 3.0 / 2.0
|
||||
rotatedAspect = aspect
|
||||
switchOrientation = true
|
||||
}
|
||||
|
||||
var rotatedVideoSize = CGSize(width: 100.0, height: rotatedAspect * 100.0)
|
||||
|
||||
if size.width < 100.0 || true {
|
||||
rotatedVideoSize = rotatedVideoSize.aspectFilled(size)
|
||||
} else {
|
||||
rotatedVideoSize = rotatedVideoSize.aspectFitted(size)
|
||||
}
|
||||
|
||||
if switchOrientation {
|
||||
rotatedVideoSize = CGSize(width: rotatedVideoSize.height, height: rotatedVideoSize.width)
|
||||
}
|
||||
var rotatedVideoFrame = CGRect(origin: CGPoint(x: floor((size.width - rotatedVideoSize.width) / 2.0), y: floor((size.height - rotatedVideoSize.height) / 2.0)), size: rotatedVideoSize)
|
||||
rotatedVideoFrame.origin.x = floor(rotatedVideoFrame.origin.x)
|
||||
rotatedVideoFrame.origin.y = floor(rotatedVideoFrame.origin.y)
|
||||
rotatedVideoFrame.size.width = ceil(rotatedVideoFrame.size.width)
|
||||
rotatedVideoFrame.size.height = ceil(rotatedVideoFrame.size.height)
|
||||
self.videoView.view.center = rotatedVideoFrame.center
|
||||
self.videoView.view.bounds = CGRect(origin: CGPoint(), size: rotatedVideoFrame.size)
|
||||
|
||||
let transition: ContainedViewLayoutTransition = .immediate
|
||||
transition.updateTransformRotation(view: self.videoView.view, angle: angle)
|
||||
}
|
||||
}
|
||||
|
||||
private final class MainVideoContainerNode: ASDisplayNode {
|
||||
private let context: AccountContext
|
||||
private let call: PresentationGroupCall
|
||||
|
||||
private var currentVideoNode: GroupVideoNode?
|
||||
private var currentPeer: (PeerId, UInt32)?
|
||||
|
||||
private var validLayout: CGSize?
|
||||
|
||||
init(context: AccountContext, call: PresentationGroupCall) {
|
||||
self.context = context
|
||||
self.call = call
|
||||
|
||||
super.init()
|
||||
|
||||
self.backgroundColor = .black
|
||||
}
|
||||
|
||||
func updatePeer(peer: (peerId: PeerId, source: UInt32)?) {
|
||||
if self.currentPeer?.0 == peer?.0 && self.currentPeer?.1 == peer?.1 {
|
||||
return
|
||||
}
|
||||
self.currentPeer = peer
|
||||
if let (peerId, source) = peer {
|
||||
self.call.makeIncomingVideoView(source: source, completion: { [weak self] videoView in
|
||||
Queue.mainQueue().async {
|
||||
guard let strongSelf = self, let videoView = videoView else {
|
||||
return
|
||||
}
|
||||
let videoNode = GroupVideoNode(videoView: videoView)
|
||||
if let currentVideoNode = strongSelf.currentVideoNode {
|
||||
currentVideoNode.removeFromSupernode()
|
||||
strongSelf.currentVideoNode = nil
|
||||
}
|
||||
strongSelf.currentVideoNode = videoNode
|
||||
strongSelf.addSubnode(videoNode)
|
||||
if let size = strongSelf.validLayout {
|
||||
strongSelf.update(size: size, transition: .immediate)
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
if let currentVideoNode = self.currentVideoNode {
|
||||
currentVideoNode.removeFromSupernode()
|
||||
self.currentVideoNode = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func update(size: CGSize, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = size
|
||||
|
||||
if let currentVideoNode = self.currentVideoNode {
|
||||
transition.updateFrame(node: currentVideoNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||
currentVideoNode.updateLayout(size: size, transition: .immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final class VoiceChatController: ViewController {
|
||||
private final class Node: ViewControllerTracingNode, UIGestureRecognizerDelegate {
|
||||
private struct ListTransition {
|
||||
@ -137,22 +300,28 @@ public final class VoiceChatController: ViewController {
|
||||
|
||||
private final class Interaction {
|
||||
let updateIsMuted: (PeerId, Bool) -> Void
|
||||
let openPeer: (PeerId) -> Void
|
||||
let openInvite: () -> Void
|
||||
let peerContextAction: (PeerEntry, ASDisplayNode, ContextGesture?) -> Void
|
||||
let setPeerIdWithRevealedOptions: (PeerId?, PeerId?) -> Void
|
||||
let getPeerVideo: (UInt32) -> GroupVideoNode?
|
||||
|
||||
private var audioLevels: [PeerId: ValuePipe<Float>] = [:]
|
||||
|
||||
init(
|
||||
updateIsMuted: @escaping (PeerId, Bool) -> Void,
|
||||
openPeer: @escaping (PeerId) -> Void,
|
||||
openInvite: @escaping () -> Void,
|
||||
peerContextAction: @escaping (PeerEntry, ASDisplayNode, ContextGesture?) -> Void,
|
||||
setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void
|
||||
setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void,
|
||||
getPeerVideo: @escaping (UInt32) -> GroupVideoNode?
|
||||
) {
|
||||
self.updateIsMuted = updateIsMuted
|
||||
self.openPeer = openPeer
|
||||
self.openInvite = openInvite
|
||||
self.peerContextAction = peerContextAction
|
||||
self.setPeerIdWithRevealedOptions = setPeerIdWithRevealedOptions
|
||||
self.getPeerVideo = getPeerVideo
|
||||
}
|
||||
|
||||
func getAudioLevel(_ peerId: PeerId) -> Signal<Float, NoError> {
|
||||
@ -170,9 +339,9 @@ public final class VoiceChatController: ViewController {
|
||||
}
|
||||
}
|
||||
|
||||
func updateAudioLevels(_ levels: [(PeerId, Float, Bool)], reset: Bool = false) {
|
||||
func updateAudioLevels(_ levels: [(PeerId, UInt32, Float, Bool)], reset: Bool = false) {
|
||||
var updated = Set<PeerId>()
|
||||
for (peerId, level, _) in levels {
|
||||
for (peerId, _, level, _) in levels {
|
||||
if let pipe = self.audioLevels[peerId] {
|
||||
if reset {
|
||||
pipe.putNext(level)
|
||||
@ -200,6 +369,7 @@ public final class VoiceChatController: ViewController {
|
||||
}
|
||||
|
||||
var peer: Peer
|
||||
var ssrc: UInt32
|
||||
var presence: TelegramUserPresence?
|
||||
var activityTimestamp: Int32
|
||||
var state: State
|
||||
@ -216,6 +386,9 @@ public final class VoiceChatController: ViewController {
|
||||
if !lhs.peer.isEqual(rhs.peer) {
|
||||
return false
|
||||
}
|
||||
if lhs.ssrc != rhs.ssrc {
|
||||
return false
|
||||
}
|
||||
if lhs.presence != rhs.presence {
|
||||
return false
|
||||
}
|
||||
@ -366,7 +539,9 @@ public final class VoiceChatController: ViewController {
|
||||
|
||||
let revealOptions: [VoiceChatParticipantItem.RevealOption] = []
|
||||
|
||||
return VoiceChatParticipantItem(presentationData: ItemListPresentationData(presentationData), dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, context: context, peer: peer, presence: peerEntry.presence, text: text, icon: icon, enabled: true, selectable: peer.id != context.account.peerId || peerEntry.canManageCall, getAudioLevel: { return interaction.getAudioLevel(peer.id) }, revealOptions: revealOptions, revealed: peerEntry.revealed, setPeerIdWithRevealedOptions: { peerId, fromPeerId in
|
||||
return VoiceChatParticipantItem(presentationData: ItemListPresentationData(presentationData), dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, context: context, peer: peer, ssrc: peerEntry.ssrc, presence: peerEntry.presence, text: text, icon: icon, enabled: true, selectable: peer.id != context.account.peerId || peerEntry.canManageCall, getAudioLevel: { return interaction.getAudioLevel(peer.id) }, getVideo: {
|
||||
return interaction.getPeerVideo(peerEntry.ssrc)
|
||||
}, revealOptions: revealOptions, revealed: peerEntry.revealed, setPeerIdWithRevealedOptions: { peerId, fromPeerId in
|
||||
interaction.setPeerIdWithRevealedOptions(peerId, fromPeerId)
|
||||
}, action: { node in
|
||||
interaction.peerContextAction(peerEntry, node, nil)
|
||||
@ -396,6 +571,7 @@ public final class VoiceChatController: ViewController {
|
||||
private let dimNode: ASDisplayNode
|
||||
private let contentContainer: ASDisplayNode
|
||||
private let backgroundNode: ASDisplayNode
|
||||
private var mainVideoContainer: MainVideoContainerNode?
|
||||
private let listNode: ListView
|
||||
private let topPanelNode: ASDisplayNode
|
||||
private let topPanelEdgeNode: ASDisplayNode
|
||||
@ -408,6 +584,7 @@ public final class VoiceChatController: ViewController {
|
||||
private let bottomPanelBackgroundNode: ASDisplayNode
|
||||
private let bottomCornersNode: ASImageNode
|
||||
fileprivate let audioOutputNode: CallControllerButtonItemNode
|
||||
fileprivate let cameraButtonNode: CallControllerButtonItemNode
|
||||
fileprivate let leaveNode: CallControllerButtonItemNode
|
||||
fileprivate let actionButton: VoiceChatActionButton
|
||||
private let leftBorderNode: ASDisplayNode
|
||||
@ -468,6 +645,12 @@ public final class VoiceChatController: ViewController {
|
||||
private let inviteDisposable = MetaDisposable()
|
||||
|
||||
private let memberEventsDisposable = MetaDisposable()
|
||||
private let voiceSourcesDisposable = MetaDisposable()
|
||||
|
||||
private var requestedVideoSources = Set<UInt32>()
|
||||
private var videoNodes: [(PeerId, UInt32, GroupVideoNode)] = []
|
||||
|
||||
private var currentDominantSpeakerWithVideo: (PeerId, UInt32)?
|
||||
|
||||
init(controller: VoiceChatController, sharedContext: SharedAccountContext, call: PresentationGroupCall) {
|
||||
self.controller = controller
|
||||
@ -489,6 +672,10 @@ public final class VoiceChatController: ViewController {
|
||||
self.backgroundNode.backgroundColor = secondaryPanelBackgroundColor
|
||||
self.backgroundNode.clipsToBounds = false
|
||||
|
||||
if false {
|
||||
self.mainVideoContainer = MainVideoContainerNode(context: call.accountContext, call: call)
|
||||
}
|
||||
|
||||
self.listNode = ListView()
|
||||
self.listNode.verticalScrollIndicatorColor = UIColor(white: 1.0, alpha: 0.3)
|
||||
self.listNode.clipsToBounds = true
|
||||
@ -537,6 +724,7 @@ public final class VoiceChatController: ViewController {
|
||||
self.bottomCornersNode.image = cornersImage(top: false, bottom: true, dark: false)
|
||||
|
||||
self.audioOutputNode = CallControllerButtonItemNode()
|
||||
self.cameraButtonNode = CallControllerButtonItemNode()
|
||||
self.leaveNode = CallControllerButtonItemNode()
|
||||
self.actionButton = VoiceChatActionButton()
|
||||
|
||||
@ -561,6 +749,34 @@ public final class VoiceChatController: ViewController {
|
||||
self.itemInteraction = Interaction(
|
||||
updateIsMuted: { [weak self] peerId, isMuted in
|
||||
let _ = self?.call.updateMuteState(peerId: peerId, isMuted: isMuted)
|
||||
}, openPeer: { [weak self] peerId in
|
||||
if let strongSelf = self, let navigationController = strongSelf.controller?.parentNavigationController {
|
||||
/*let context = strongSelf.context
|
||||
strongSelf.controller?.dismiss(completion: {
|
||||
Queue.mainQueue().justDispatch {
|
||||
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), keepStack: .always, purposefulAction: {}, peekData: nil))
|
||||
}
|
||||
})*/
|
||||
for entry in strongSelf.currentEntries {
|
||||
switch entry {
|
||||
case let .peer(peer):
|
||||
if peer.peer.id == peerId {
|
||||
let source = peer.ssrc
|
||||
if strongSelf.currentDominantSpeakerWithVideo?.0 != peerId || strongSelf.currentDominantSpeakerWithVideo?.1 != source {
|
||||
strongSelf.currentDominantSpeakerWithVideo = (peerId, source)
|
||||
strongSelf.call.setFullSizeVideo(peerId: peerId)
|
||||
strongSelf.mainVideoContainer?.updatePeer(peer: (peerId: peerId, source: source))
|
||||
} else {
|
||||
strongSelf.currentDominantSpeakerWithVideo = nil
|
||||
strongSelf.call.setFullSizeVideo(peerId: nil)
|
||||
strongSelf.mainVideoContainer?.updatePeer(peer: nil)
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}, openInvite: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
@ -815,6 +1031,17 @@ public final class VoiceChatController: ViewController {
|
||||
}), true))
|
||||
}
|
||||
|
||||
/*items.append(.action(ContextMenuActionItem(text: "Toggle Full Screen", icon: { theme in
|
||||
return nil
|
||||
}, action: { _, f in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.itemInteraction?.openPeer(peer.id)
|
||||
f(.default)
|
||||
})))*/
|
||||
|
||||
if peer.id != strongSelf.context.account.peerId {
|
||||
if let callState = strongSelf.callState, (callState.canManageCall || callState.adminIds.contains(strongSelf.context.account.peerId)) {
|
||||
if callState.adminIds.contains(peer.id) {
|
||||
@ -955,6 +1182,16 @@ public final class VoiceChatController: ViewController {
|
||||
updated.revealedPeerId = peerId
|
||||
return updated
|
||||
}
|
||||
}, getPeerVideo: { [weak self] ssrc in
|
||||
guard let strongSelf = self else {
|
||||
return nil
|
||||
}
|
||||
for (_, listSsrc, videoNode) in strongSelf.videoNodes {
|
||||
if listSsrc == ssrc {
|
||||
return videoNode
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
self.topPanelNode.addSubnode(self.topPanelEdgeNode)
|
||||
@ -968,6 +1205,7 @@ public final class VoiceChatController: ViewController {
|
||||
self.bottomPanelNode.addSubnode(self.bottomCornersNode)
|
||||
self.bottomPanelNode.addSubnode(self.bottomPanelBackgroundNode)
|
||||
self.bottomPanelNode.addSubnode(self.audioOutputNode)
|
||||
//self.bottomPanelNode.addSubnode(self.cameraButtonNode)
|
||||
self.bottomPanelNode.addSubnode(self.leaveNode)
|
||||
self.bottomPanelNode.addSubnode(self.actionButton)
|
||||
|
||||
@ -976,6 +1214,9 @@ public final class VoiceChatController: ViewController {
|
||||
self.contentContainer.addSubnode(self.backgroundNode)
|
||||
|
||||
self.contentContainer.addSubnode(self.listNode)
|
||||
if let mainVideoContainer = self.mainVideoContainer {
|
||||
self.contentContainer.addSubnode(mainVideoContainer)
|
||||
}
|
||||
self.contentContainer.addSubnode(self.topPanelNode)
|
||||
self.contentContainer.addSubnode(self.leftBorderNode)
|
||||
self.contentContainer.addSubnode(self.rightBorderNode)
|
||||
@ -1092,6 +1333,28 @@ public final class VoiceChatController: ViewController {
|
||||
if strongSelf.effectiveMuteState != nil {
|
||||
levels = levels.filter { $0.0 != strongSelf.context.account.peerId }
|
||||
}
|
||||
|
||||
var maxLevelWithVideo: (PeerId, UInt32, Float)?
|
||||
for (peerId, source, level, hasSpeech) in levels {
|
||||
if hasSpeech && source != 0 {
|
||||
if let (_, _, currentLevel) = maxLevelWithVideo {
|
||||
if currentLevel < level {
|
||||
maxLevelWithVideo = (peerId, source, level)
|
||||
}
|
||||
} else {
|
||||
maxLevelWithVideo = (peerId, source, level)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let (peerId, source, _) = maxLevelWithVideo {
|
||||
if strongSelf.currentDominantSpeakerWithVideo?.0 != peerId || strongSelf.currentDominantSpeakerWithVideo?.1 != source {
|
||||
strongSelf.currentDominantSpeakerWithVideo = (peerId, source)
|
||||
strongSelf.call.setFullSizeVideo(peerId: peerId)
|
||||
strongSelf.mainVideoContainer?.updatePeer(peer: (peerId: peerId, source: source))
|
||||
}
|
||||
}
|
||||
|
||||
strongSelf.itemInteraction?.updateAudioLevels(levels)
|
||||
})
|
||||
|
||||
@ -1113,6 +1376,8 @@ public final class VoiceChatController: ViewController {
|
||||
|
||||
self.audioOutputNode.addTarget(self, action: #selector(self.audioOutputPressed), forControlEvents: .touchUpInside)
|
||||
|
||||
self.cameraButtonNode.addTarget(self, action: #selector(self.cameraPressed), forControlEvents: .touchUpInside)
|
||||
|
||||
self.optionsButton.contextAction = { [weak self] sourceNode, gesture in
|
||||
guard let strongSelf = self, let controller = strongSelf.controller else {
|
||||
return
|
||||
@ -1246,6 +1511,90 @@ public final class VoiceChatController: ViewController {
|
||||
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: event.peer, text: strongSelf.presentationData.strings.VoiceChat_PeerJoinedText(event.peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).0), action: { _ in return false })
|
||||
}
|
||||
}))
|
||||
|
||||
self.voiceSourcesDisposable.set((self.call.incomingVideoSources
|
||||
|> deliverOnMainQueue).start(next: { [weak self] sources in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
var validSources = Set<UInt32>()
|
||||
for (peerId, source) in sources {
|
||||
validSources.insert(source)
|
||||
|
||||
if !strongSelf.requestedVideoSources.contains(source) {
|
||||
strongSelf.requestedVideoSources.insert(source)
|
||||
strongSelf.call.makeIncomingVideoView(source: source, completion: { videoView in
|
||||
Queue.mainQueue().async {
|
||||
guard let strongSelf = self, let videoView = videoView else {
|
||||
return
|
||||
}
|
||||
let videoNode = GroupVideoNode(videoView: videoView)
|
||||
strongSelf.videoNodes.append((peerId, source, videoNode))
|
||||
//strongSelf.addSubnode(videoNode)
|
||||
if let (layout, navigationHeight) = strongSelf.validLayout {
|
||||
strongSelf.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .immediate)
|
||||
|
||||
loop: for i in 0 ..< strongSelf.currentEntries.count {
|
||||
let entry = strongSelf.currentEntries[i]
|
||||
switch entry {
|
||||
case let .peer(peerEntry):
|
||||
if peerEntry.ssrc == source {
|
||||
let presentationData = strongSelf.presentationData.withUpdated(theme: strongSelf.darkTheme)
|
||||
strongSelf.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [ListViewUpdateItem(index: i, previousIndex: i, item: entry.item(context: strongSelf.context, presentationData: presentationData, interaction: strongSelf.itemInteraction!), directionHint: nil)], options: [.Synchronous], updateOpaqueState: nil)
|
||||
break loop
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var updated = false
|
||||
for i in (0 ..< strongSelf.videoNodes.count).reversed() {
|
||||
if !validSources.contains(strongSelf.videoNodes[i].1) {
|
||||
let ssrc = strongSelf.videoNodes[i].1
|
||||
strongSelf.videoNodes.remove(at: i)
|
||||
|
||||
loop: for j in 0 ..< strongSelf.currentEntries.count {
|
||||
let entry = strongSelf.currentEntries[j]
|
||||
switch entry {
|
||||
case let .peer(peerEntry):
|
||||
if peerEntry.ssrc == ssrc {
|
||||
let presentationData = strongSelf.presentationData.withUpdated(theme: strongSelf.darkTheme)
|
||||
strongSelf.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [ListViewUpdateItem(index: i, previousIndex: i, item: entry.item(context: strongSelf.context, presentationData: presentationData, interaction: strongSelf.itemInteraction!), directionHint: nil)], options: [.Synchronous], updateOpaqueState: nil)
|
||||
break loop
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
//strongSelf.videoNodes[i].2.removeFromSupernode()
|
||||
updated = true
|
||||
}
|
||||
}
|
||||
|
||||
if let (_, source) = strongSelf.currentDominantSpeakerWithVideo {
|
||||
if !validSources.contains(source) {
|
||||
strongSelf.currentDominantSpeakerWithVideo = nil
|
||||
strongSelf.call.setFullSizeVideo(peerId: nil)
|
||||
strongSelf.mainVideoContainer?.updatePeer(peer: nil)
|
||||
}
|
||||
}
|
||||
|
||||
if updated {
|
||||
if let (layout, navigationHeight) = strongSelf.validLayout {
|
||||
strongSelf.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .immediate)
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
//self.isFullscreen = true
|
||||
//self.isExpanded = true
|
||||
}
|
||||
|
||||
deinit {
|
||||
@ -1259,6 +1608,8 @@ public final class VoiceChatController: ViewController {
|
||||
self.audioLevelsDisposable?.dispose()
|
||||
self.myAudioLevelDisposable?.dispose()
|
||||
self.inviteDisposable.dispose()
|
||||
self.memberEventsDisposable.dispose()
|
||||
self.voiceSourcesDisposable.dispose()
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
@ -1390,7 +1741,7 @@ public final class VoiceChatController: ViewController {
|
||||
self.call.setIsMuted(action: .muted(isPushToTalkActive: false))
|
||||
}
|
||||
|
||||
self.itemInteraction?.updateAudioLevels([(self.context.account.peerId, 0.0, false)], reset: true)
|
||||
self.itemInteraction?.updateAudioLevels([(self.context.account.peerId, 0, 0.0, false)], reset: true)
|
||||
|
||||
if let (layout, navigationHeight) = self.validLayout {
|
||||
self.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.3, curve: .spring))
|
||||
@ -1468,6 +1819,14 @@ public final class VoiceChatController: ViewController {
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func cameraPressed() {
|
||||
if self.call.isVideo {
|
||||
self.call.disableVideo()
|
||||
} else {
|
||||
self.call.requestVideo()
|
||||
}
|
||||
}
|
||||
|
||||
private func updateFloatingHeaderOffset(offset: CGFloat, transition: ContainedViewLayoutTransition, completion: (() -> Void)? = nil) {
|
||||
guard let (layout, _) = self.validLayout else {
|
||||
return
|
||||
@ -1507,6 +1866,12 @@ public final class VoiceChatController: ViewController {
|
||||
let panelOffset = max(layoutTopInset, rawPanelOffset)
|
||||
let topPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: panelOffset), size: CGSize(width: size.width, height: topPanelHeight))
|
||||
|
||||
if let mainVideoContainer = self.mainVideoContainer {
|
||||
let videoContainerFrame = CGRect(origin: CGPoint(x: 0.0, y: topPanelFrame.maxY), size: CGSize(width: layout.size.width, height: 200.0))
|
||||
transition.updateFrameAdditive(node: mainVideoContainer, frame: videoContainerFrame)
|
||||
mainVideoContainer.update(size: videoContainerFrame.size, transition: transition)
|
||||
}
|
||||
|
||||
let backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: topPanelFrame.maxY), size: CGSize(width: size.width, height: layout.size.height))
|
||||
let sideInset: CGFloat = 16.0
|
||||
let leftBorderFrame = CGRect(origin: CGPoint(x: 0.0, y: topPanelFrame.maxY - 16.0), size: CGSize(width: sideInset, height: layout.size.height))
|
||||
@ -1720,6 +2085,10 @@ public final class VoiceChatController: ViewController {
|
||||
let sideButtonSize = CGSize(width: 60.0, height: 60.0)
|
||||
self.audioOutputNode.update(size: sideButtonSize, content: CallControllerButtonItemNode.Content(appearance: soundAppearance, image: soundImage), text: soundTitle, transition: .animated(duration: 0.3, curve: .linear))
|
||||
|
||||
let cameraButtonSize = CGSize(width: 40.0, height: 40.0)
|
||||
|
||||
self.cameraButtonNode.update(size: cameraButtonSize, content: CallControllerButtonItemNode.Content(appearance: CallControllerButtonItemNode.Content.Appearance.blurred(isFilled: false), image: .camera), text: " ", transition: .animated(duration: 0.3, curve: .linear))
|
||||
|
||||
self.leaveNode.update(size: sideButtonSize, content: CallControllerButtonItemNode.Content(appearance: .color(.custom(0xff3b30, 0.3)), image: .end), text: self.presentationData.strings.VoiceChat_Leave, transition: .immediate)
|
||||
}
|
||||
|
||||
@ -1769,7 +2138,10 @@ public final class VoiceChatController: ViewController {
|
||||
}
|
||||
|
||||
let bottomPanelHeight = bottomAreaHeight + layout.intrinsicInsets.bottom
|
||||
let listTopInset = layoutTopInset + topPanelHeight
|
||||
var listTopInset = layoutTopInset + topPanelHeight
|
||||
if self.mainVideoContainer != nil {
|
||||
listTopInset += 200.0
|
||||
}
|
||||
let listSize = CGSize(width: size.width, height: layout.size.height - listTopInset - bottomPanelHeight)
|
||||
|
||||
let topInset: CGFloat
|
||||
@ -1800,6 +2172,7 @@ public final class VoiceChatController: ViewController {
|
||||
transition.updateFrame(node: self.bottomPanelNode, frame: bottomPanelFrame)
|
||||
|
||||
let sideButtonSize = CGSize(width: 60.0, height: 60.0)
|
||||
let cameraButtonSize = CGSize(width: 40.0, height: 40.0)
|
||||
let centralButtonSize = CGSize(width: 440.0, height: 440.0)
|
||||
|
||||
let actionButtonFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - centralButtonSize.width) / 2.0), y: floorToScreenPixels((bottomAreaHeight - centralButtonSize.height) / 2.0)), size: centralButtonSize)
|
||||
@ -1851,12 +2224,41 @@ public final class VoiceChatController: ViewController {
|
||||
|
||||
self.updateButtons(transition: transition)
|
||||
|
||||
/*var currentVideoOrigin = CGPoint(x: 4.0, y: (layout.statusBarHeight ?? 0.0) + 4.0)
|
||||
for (_, _, videoNode) in self.videoNodes {
|
||||
let videoSize = CGSize(width: 300.0, height: 500.0)
|
||||
if currentVideoOrigin.x + videoSize.width > layout.size.width {
|
||||
currentVideoOrigin.x = 0.0
|
||||
currentVideoOrigin.y += videoSize.height
|
||||
}
|
||||
|
||||
videoNode.frame = CGRect(origin: currentVideoOrigin, size: videoSize)
|
||||
videoNode.updateLayout(size: videoSize, transition: .immediate)
|
||||
if videoNode.supernode == nil {
|
||||
self.contentContainer.addSubnode(videoNode)
|
||||
}
|
||||
|
||||
currentVideoOrigin.x += videoSize.width + 4.0
|
||||
}*/
|
||||
|
||||
let sideButtonMinimalInset: CGFloat = 16.0
|
||||
let sideButtonOffset = min(36.0, floor((((size.width - 144.0) / 2.0) - sideButtonSize.width) / 2.0))
|
||||
let sideButtonOrigin = max(sideButtonMinimalInset, floor((size.width - 144.0) / 2.0) - sideButtonOffset - sideButtonSize.width)
|
||||
|
||||
if self.audioOutputNode.supernode === self.bottomPanelNode {
|
||||
transition.updateFrame(node: self.audioOutputNode, frame: CGRect(origin: CGPoint(x: sideButtonOrigin, y: floor((bottomAreaHeight - sideButtonSize.height) / 2.0)), size: sideButtonSize))
|
||||
if true {
|
||||
let audioOutputFrame = CGRect(origin: CGPoint(x: sideButtonOrigin, y: floor((bottomAreaHeight - sideButtonSize.height) / 2.0)), size: sideButtonSize)
|
||||
transition.updateFrame(node: self.audioOutputNode, frame: audioOutputFrame)
|
||||
} else {
|
||||
let cameraButtonDistance: CGFloat = 4.0
|
||||
|
||||
let audioOutputFrame = CGRect(origin: CGPoint(x: sideButtonOrigin, y: floor((bottomAreaHeight - sideButtonSize.height - cameraButtonDistance - cameraButtonSize.height) / 2.0) + cameraButtonDistance + cameraButtonSize.height), size: sideButtonSize)
|
||||
|
||||
transition.updateFrame(node: self.audioOutputNode, frame: audioOutputFrame)
|
||||
|
||||
transition.updateFrame(node: self.cameraButtonNode, frame: CGRect(origin: CGPoint(x: floor(audioOutputFrame.midX - cameraButtonSize.width / 2.0), y: audioOutputFrame.minY - cameraButtonDistance - cameraButtonSize.height), size: cameraButtonSize))
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.leaveNode, frame: CGRect(origin: CGPoint(x: size.width - sideButtonOrigin - sideButtonSize.width, y: floor((bottomAreaHeight - sideButtonSize.height) / 2.0)), size: sideButtonSize))
|
||||
}
|
||||
if isFirstTime {
|
||||
@ -1883,10 +2285,13 @@ public final class VoiceChatController: ViewController {
|
||||
if self.actionButton.supernode !== self.bottomPanelNode {
|
||||
self.actionButton.ignoreHierarchyChanges = true
|
||||
self.audioOutputNode.isHidden = false
|
||||
self.cameraButtonNode.isHidden = false
|
||||
self.leaveNode.isHidden = false
|
||||
self.audioOutputNode.layer.removeAllAnimations()
|
||||
self.cameraButtonNode.layer.removeAllAnimations()
|
||||
self.leaveNode.layer.removeAllAnimations()
|
||||
self.bottomPanelNode.addSubnode(self.audioOutputNode)
|
||||
//self.bottomPanelNode.addSubnode(self.cameraButtonNode)
|
||||
self.bottomPanelNode.addSubnode(self.leaveNode)
|
||||
self.bottomPanelNode.addSubnode(self.actionButton)
|
||||
self.containerLayoutUpdated(layout, navigationHeight :navigationHeight, transition: .immediate)
|
||||
@ -2085,6 +2490,7 @@ public final class VoiceChatController: ViewController {
|
||||
|
||||
entries.append(.peer(PeerEntry(
|
||||
peer: member.peer,
|
||||
ssrc: member.ssrc,
|
||||
presence: nil,
|
||||
activityTimestamp: Int32.max - 1 - index,
|
||||
state: memberState,
|
||||
@ -2098,6 +2504,7 @@ public final class VoiceChatController: ViewController {
|
||||
if let accountPeer = self.accountPeer, !processedPeerIds.contains(accountPeer.id) {
|
||||
entries.insert(.peer(PeerEntry(
|
||||
peer: accountPeer,
|
||||
ssrc: 0,
|
||||
presence: nil,
|
||||
activityTimestamp: Int32.max - 1 - index,
|
||||
state: .listening,
|
||||
@ -2115,6 +2522,7 @@ public final class VoiceChatController: ViewController {
|
||||
|
||||
entries.append(.peer(PeerEntry(
|
||||
peer: peer,
|
||||
ssrc: 0,
|
||||
presence: nil,
|
||||
activityTimestamp: Int32.max - 1 - index,
|
||||
state: .invited,
|
||||
@ -2135,7 +2543,7 @@ public final class VoiceChatController: ViewController {
|
||||
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
if gestureRecognizer is DirectionalPanGestureRecognizer {
|
||||
let location = gestureRecognizer.location(in: self.bottomPanelNode.view)
|
||||
if self.audioOutputNode.frame.contains(location) || self.leaveNode.frame.contains(location) {
|
||||
if self.audioOutputNode.frame.contains(location) || (!self.cameraButtonNode.isHidden && self.cameraButtonNode.frame.contains(location)) || self.leaveNode.frame.contains(location) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@ -2328,6 +2736,14 @@ public final class VoiceChatController: ViewController {
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
let result = super.hitTest(point, with: event)
|
||||
|
||||
if let result = result {
|
||||
for (_, _, videoNode) in self.videoNodes {
|
||||
if videoNode.view === result || result.isDescendant(of: videoNode.view) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result === self.topPanelNode.view {
|
||||
return self.view
|
||||
|
@ -62,30 +62,34 @@ final class VoiceChatParticipantItem: ListViewItem {
|
||||
let nameDisplayOrder: PresentationPersonNameOrder
|
||||
let context: AccountContext
|
||||
let peer: Peer
|
||||
let ssrc: UInt32?
|
||||
let presence: PeerPresence?
|
||||
let text: ParticipantText
|
||||
let icon: Icon
|
||||
let enabled: Bool
|
||||
public let selectable: Bool
|
||||
let getAudioLevel: (() -> Signal<Float, NoError>)?
|
||||
let getVideo: () -> GroupVideoNode?
|
||||
let revealOptions: [RevealOption]
|
||||
let revealed: Bool?
|
||||
let setPeerIdWithRevealedOptions: (PeerId?, PeerId?) -> Void
|
||||
let action: ((ASDisplayNode) -> Void)?
|
||||
let contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?
|
||||
|
||||
public init(presentationData: ItemListPresentationData, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, context: AccountContext, peer: Peer, presence: PeerPresence?, text: ParticipantText, icon: Icon, enabled: Bool, selectable: Bool, getAudioLevel: (() -> Signal<Float, NoError>)?, revealOptions: [RevealOption], revealed: Bool?, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, action: ((ASDisplayNode) -> Void)?, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? = nil) {
|
||||
public init(presentationData: ItemListPresentationData, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, context: AccountContext, peer: Peer, ssrc: UInt32?, presence: PeerPresence?, text: ParticipantText, icon: Icon, enabled: Bool, selectable: Bool, getAudioLevel: (() -> Signal<Float, NoError>)?, getVideo: @escaping () -> GroupVideoNode?, revealOptions: [RevealOption], revealed: Bool?, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, action: ((ASDisplayNode) -> Void)?, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? = nil) {
|
||||
self.presentationData = presentationData
|
||||
self.dateTimeFormat = dateTimeFormat
|
||||
self.nameDisplayOrder = nameDisplayOrder
|
||||
self.context = context
|
||||
self.peer = peer
|
||||
self.ssrc = ssrc
|
||||
self.presence = presence
|
||||
self.text = text
|
||||
self.icon = icon
|
||||
self.enabled = enabled
|
||||
self.selectable = selectable
|
||||
self.getAudioLevel = getAudioLevel
|
||||
self.getVideo = getVideo
|
||||
self.revealOptions = revealOptions
|
||||
self.revealed = revealed
|
||||
self.setPeerIdWithRevealedOptions = setPeerIdWithRevealedOptions
|
||||
@ -171,6 +175,12 @@ class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode {
|
||||
private var layoutParams: (VoiceChatParticipantItem, ListViewItemLayoutParams, Bool, Bool)?
|
||||
private var wavesColor: UIColor?
|
||||
|
||||
private var videoNode: GroupVideoNode?
|
||||
|
||||
var item: VoiceChatParticipantItem? {
|
||||
return self.layoutParams?.0
|
||||
}
|
||||
|
||||
init() {
|
||||
self.topStripeNode = ASDisplayNode()
|
||||
self.topStripeNode.isLayerBacked = true
|
||||
@ -188,7 +198,6 @@ class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode {
|
||||
self.offsetContainerNode = ASDisplayNode()
|
||||
|
||||
self.avatarNode = AvatarNode(font: avatarFont)
|
||||
self.avatarNode.isLayerBacked = !smartInvertColorsEnabled()
|
||||
self.avatarNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 40.0, height: 40.0))
|
||||
|
||||
self.titleNode = TextNode()
|
||||
@ -667,11 +676,31 @@ class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode {
|
||||
})
|
||||
}
|
||||
|
||||
let videoSize = CGSize(width: avatarSize, height: avatarSize)
|
||||
|
||||
let videoNode = item.getVideo()
|
||||
if let current = strongSelf.videoNode, current !== videoNode {
|
||||
current.removeFromSupernode()
|
||||
}
|
||||
let actionOffset: CGFloat = 0.0
|
||||
strongSelf.videoNode = videoNode
|
||||
if let videoNode = videoNode {
|
||||
|
||||
videoNode.updateLayout(size: videoSize, transition: .immediate)
|
||||
if videoNode.supernode !== strongSelf.avatarNode {
|
||||
videoNode.clipsToBounds = true
|
||||
videoNode.cornerRadius = avatarSize / 2.0
|
||||
strongSelf.avatarNode.addSubnode(videoNode)
|
||||
}
|
||||
|
||||
videoNode.frame = CGRect(origin: CGPoint(), size: videoSize)
|
||||
}
|
||||
|
||||
let animationSize = CGSize(width: 36.0, height: 36.0)
|
||||
strongSelf.iconNode?.frame = CGRect(origin: CGPoint(), size: animationSize)
|
||||
strongSelf.animationNode?.frame = CGRect(origin: CGPoint(), size: animationSize)
|
||||
|
||||
strongSelf.actionButtonNode.frame = CGRect(x: params.width - animationSize.width - 6.0 - params.rightInset, y: floor((layout.contentSize.height - animationSize.height) / 2.0) + 1.0, width: animationSize.width, height: animationSize.height)
|
||||
strongSelf.actionButtonNode.frame = CGRect(x: params.width - animationSize.width - 6.0 - params.rightInset + actionOffset, y: floor((layout.contentSize.height - animationSize.height) / 2.0) + 1.0, width: animationSize.width, height: animationSize.height)
|
||||
|
||||
if let presence = item.presence as? TelegramUserPresence {
|
||||
strongSelf.peerPresenceManager?.reset(presence: presence)
|
||||
|
@ -112,6 +112,7 @@ enum AccountStateMutationOperation {
|
||||
case UpdateReadThread(threadMessageId: MessageId, readMaxId: Int32, isIncoming: Bool, mainChannelMessage: MessageId?)
|
||||
case UpdateGroupCallParticipants(id: Int64, accessHash: Int64, participants: [Api.GroupCallParticipant], version: Int32)
|
||||
case UpdateGroupCall(peerId: PeerId, call: Api.GroupCall)
|
||||
case UpdateAutoremoveTimeout(peer: Api.Peer, value: CachedPeerAutoremoveTimeout.Value?)
|
||||
}
|
||||
|
||||
struct HoleFromPreviousState {
|
||||
@ -286,6 +287,10 @@ struct AccountMutableState {
|
||||
self.addOperation(.UpdateGroupCall(peerId: peerId, call: call))
|
||||
}
|
||||
|
||||
mutating func updateAutoremoveTimeout(peer: Api.Peer, value: CachedPeerAutoremoveTimeout.Value?) {
|
||||
self.addOperation(.UpdateAutoremoveTimeout(peer: peer, value: value))
|
||||
}
|
||||
|
||||
mutating func readGroupFeedInbox(groupId: PeerGroupId, index: MessageIndex) {
|
||||
self.addOperation(.ReadGroupFeedInbox(groupId, index))
|
||||
}
|
||||
@ -494,7 +499,7 @@ struct AccountMutableState {
|
||||
|
||||
mutating func addOperation(_ operation: AccountStateMutationOperation) {
|
||||
switch operation {
|
||||
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll/*, .UpdateMessageReactions*/, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter, .UpdateReadThread, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateMessagesPinned:
|
||||
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll/*, .UpdateMessageReactions*/, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter, .UpdateReadThread, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateMessagesPinned, .UpdateAutoremoveTimeout:
|
||||
break
|
||||
case let .AddMessages(messages, location):
|
||||
for message in messages {
|
||||
|
@ -1358,6 +1358,8 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo
|
||||
case let .updateGroupCall(channelId, call):
|
||||
updatedState.updateGroupCall(peerId: PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId), call: call)
|
||||
updatedState.updateGroupCall(peerId: PeerId(namespace: Namespaces.Peer.CloudGroup, id: channelId), call: call)
|
||||
case let .updatePeerHistoryTTL(_, peer, ttl):
|
||||
updatedState.updateAutoremoveTimeout(peer: peer, value: CachedPeerAutoremoveTimeout.Value(ttl))
|
||||
case let .updateLangPackTooLong(langCode):
|
||||
updatedState.updateLangPack(langCode: langCode, difference: nil)
|
||||
case let .updateLangPack(difference):
|
||||
@ -2148,7 +2150,7 @@ private func optimizedOperations(_ operations: [AccountStateMutationOperation])
|
||||
var currentAddScheduledMessages: OptimizeAddMessagesState?
|
||||
for operation in operations {
|
||||
switch operation {
|
||||
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll/*, .UpdateMessageReactions*/, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder, .UpdateReadThread, .UpdateMessagesPinned, .UpdateGroupCallParticipants, .UpdateGroupCall:
|
||||
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll/*, .UpdateMessageReactions*/, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder, .UpdateReadThread, .UpdateMessagesPinned, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateAutoremoveTimeout:
|
||||
if let currentAddMessages = currentAddMessages, !currentAddMessages.messages.isEmpty {
|
||||
result.append(.AddMessages(currentAddMessages.messages, currentAddMessages.location))
|
||||
}
|
||||
@ -3021,6 +3023,18 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
|
||||
}
|
||||
})
|
||||
}
|
||||
case let .UpdateAutoremoveTimeout(peer, value):
|
||||
transaction.updatePeerCachedData(peerIds: Set([peer.peerId]), update: { _, current in
|
||||
if let current = current as? CachedUserData {
|
||||
return current.withUpdatedAutoremoveTimeout(.known(value))
|
||||
} else if let current = current as? CachedGroupData {
|
||||
return current.withUpdatedAutoremoveTimeout(.known(value))
|
||||
} else if let current = current as? CachedChannelData {
|
||||
return current.withUpdatedAutoremoveTimeout(.known(value))
|
||||
} else {
|
||||
return current
|
||||
}
|
||||
})
|
||||
case let .UpdateLangPack(langCode, difference):
|
||||
if let difference = difference {
|
||||
if langPackDifferences[langCode] == nil {
|
||||
|
@ -37,6 +37,8 @@ func parseTelegramGroupOrChannel(chat: Api.Chat) -> Peer? {
|
||||
migrationReference = TelegramGroupToChannelMigrationReference(peerId: PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId), accessHash: accessHash)
|
||||
case .inputChannelEmpty:
|
||||
break
|
||||
case .inputChannelFromMessage:
|
||||
break
|
||||
}
|
||||
}
|
||||
var groupFlags = TelegramGroupFlags()
|
||||
|
@ -34,7 +34,6 @@ func applyMaxReadIndexInteractively(transaction: Transaction, stateManager: Acco
|
||||
})
|
||||
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: updatedAttributes, media: currentMessage.media))
|
||||
})
|
||||
transaction.addTimestampBasedMessageAttribute(tag: 0, timestamp: timestamp + attribute.timeout, messageId: id)
|
||||
}
|
||||
break
|
||||
}
|
||||
@ -103,7 +102,6 @@ func applySecretOutgoingMessageReadActions(transaction: Transaction, id: Message
|
||||
})
|
||||
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: updatedAttributes, media: currentMessage.media))
|
||||
})
|
||||
transaction.addTimestampBasedMessageAttribute(tag: 0, timestamp: timestamp + attribute.timeout, messageId: id)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes
|
||||
attributes = updatedMessage.attributes
|
||||
text = updatedMessage.text
|
||||
forwardInfo = updatedMessage.forwardInfo
|
||||
} else if case let .updateShortSentMessage(_, _, _, _, _, apiMedia, entities, _) = result {
|
||||
} else if case let .updateShortSentMessage(_, _, _, _, _, apiMedia, entities, ttlPeriod) = result {
|
||||
let (mediaValue, _) = textMediaAndExpirationTimerFromApiMedia(apiMedia, currentMessage.id.peerId)
|
||||
if let mediaValue = mediaValue {
|
||||
media = [mediaValue]
|
||||
@ -136,6 +136,11 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes
|
||||
updatedAttributes.append(TextEntitiesMessageAttribute(entities: messageTextEntitiesFromApiEntities(entities)))
|
||||
}
|
||||
|
||||
updatedAttributes = updatedAttributes.filter({ !($0 is AutoremoveTimeoutMessageAttribute) })
|
||||
if let ttlPeriod = ttlPeriod {
|
||||
updatedAttributes.append(AutoremoveTimeoutMessageAttribute(timeout: ttlPeriod, countdownBeginTime: updatedTimestamp))
|
||||
}
|
||||
|
||||
if Namespaces.Message.allScheduled.contains(message.id.namespace) && updatedId.namespace == Namespaces.Message.Cloud {
|
||||
for i in 0 ..< updatedAttributes.count {
|
||||
if updatedAttributes[i] is OutgoingScheduleInfoMessageAttribute {
|
||||
|
@ -32,7 +32,7 @@ func switchToAuthorizedAccount(transaction: AccountManagerModifier, account: Una
|
||||
}
|
||||
|
||||
public func sendAuthorizationCode(accountManager: AccountManager, account: UnauthorizedAccount, phoneNumber: String, apiId: Int32, apiHash: String, syncContacts: Bool) -> Signal<UnauthorizedAccount, AuthorizationCodeRequestError> {
|
||||
let sendCode = Api.functions.auth.sendCode(flags: 0, phoneNumber: phoneNumber, currentNumber: nil, apiId: apiId, apiHash: apiHash)
|
||||
let sendCode = Api.functions.auth.sendCode(phoneNumber: phoneNumber, apiId: apiId, apiHash: apiHash, settings: .codeSettings(flags: 0))
|
||||
|
||||
let codeAndAccount = account.network.request(sendCode, automaticFloodWait: false)
|
||||
|> map { result in
|
||||
|
@ -19,7 +19,7 @@ public enum RequestCancelAccountResetDataError {
|
||||
}
|
||||
|
||||
public func requestCancelAccountResetData(network: Network, hash: String) -> Signal<CancelAccountResetData, RequestCancelAccountResetDataError> {
|
||||
return network.request(Api.functions.account.sendConfirmPhoneCode(flags: 0, hash: hash, currentNumber: nil), automaticFloodWait: false)
|
||||
return network.request(Api.functions.account.sendConfirmPhoneCode(hash: hash, settings: .codeSettings(flags: 0)), automaticFloodWait: false)
|
||||
|> mapError { error -> RequestCancelAccountResetDataError in
|
||||
if error.errorDescription.hasPrefix("FLOOD_WAIT") {
|
||||
return .limitExceeded
|
||||
|
@ -37,7 +37,7 @@ public enum RequestChangeAccountPhoneNumberVerificationError {
|
||||
}
|
||||
|
||||
public func requestChangeAccountPhoneNumberVerification(account: Account, phoneNumber: String) -> Signal<ChangeAccountPhoneNumberData, RequestChangeAccountPhoneNumberVerificationError> {
|
||||
return account.network.request(Api.functions.account.sendChangePhoneCode(flags: 0, phoneNumber: phoneNumber, currentNumber: nil), automaticFloodWait: false)
|
||||
return account.network.request(Api.functions.account.sendChangePhoneCode(phoneNumber: phoneNumber, settings: .codeSettings(flags: 0)), automaticFloodWait: false)
|
||||
|> mapError { error -> RequestChangeAccountPhoneNumberVerificationError in
|
||||
if error.errorDescription.hasPrefix("FLOOD_WAIT") {
|
||||
return .limitExceeded
|
||||
|
@ -484,6 +484,10 @@ private func requestChatListFilters(accountPeerId: PeerId, postbox: Postbox, net
|
||||
missingGroups.append(id)
|
||||
case .inputPeerEmpty:
|
||||
break
|
||||
case .inputPeerUserFromMessage:
|
||||
break
|
||||
case .inputPeerChannelFromMessage:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -141,3 +141,77 @@ public func clearCallHistory(account: Account, forEveryone: Bool) -> Signal<Neve
|
||||
|> ignoreValues
|
||||
|> castError(ClearCallHistoryError.self)
|
||||
}
|
||||
|
||||
public enum SetChatMessageAutoremoveTimeoutError {
|
||||
case generic
|
||||
}
|
||||
|
||||
public func setChatMessageAutoremoveTimeoutInteractively(account: Account, peerId: PeerId, timeout: Int32?, isGlobal: Bool) -> Signal<Never, SetChatMessageAutoremoveTimeoutError> {
|
||||
return account.postbox.transaction { transaction -> Api.InputPeer? in
|
||||
return transaction.getPeer(peerId).flatMap(apiInputPeer)
|
||||
}
|
||||
|> castError(SetChatMessageAutoremoveTimeoutError.self)
|
||||
|> mapToSignal { inputPeer -> Signal<Never, SetChatMessageAutoremoveTimeoutError> in
|
||||
guard let inputPeer = inputPeer else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
var flags: Int32 = 0
|
||||
if !isGlobal {
|
||||
flags |= 1 << 0
|
||||
}
|
||||
return account.network.request(Api.functions.messages.setHistoryTTL(flags: flags, peer: inputPeer, period: timeout ?? 0))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> castError(SetChatMessageAutoremoveTimeoutError.self)
|
||||
|> mapToSignal { result -> Signal<Never, SetChatMessageAutoremoveTimeoutError> in
|
||||
if let result = result {
|
||||
account.stateManager.addUpdates(result)
|
||||
|
||||
return account.postbox.transaction { transaction -> Void in
|
||||
transaction.updatePeerCachedData(peerIds: [peerId], update: { _, current in
|
||||
var currentPeerValue: Int32?
|
||||
if let current = current as? CachedUserData {
|
||||
if case let .known(value?) = current.autoremoveTimeout {
|
||||
currentPeerValue = value.peerValue
|
||||
}
|
||||
} else if let current = current as? CachedGroupData {
|
||||
if case let .known(value?) = current.autoremoveTimeout {
|
||||
currentPeerValue = value.peerValue
|
||||
}
|
||||
} else if let current = current as? CachedChannelData {
|
||||
if case let .known(value?) = current.autoremoveTimeout {
|
||||
currentPeerValue = value.peerValue
|
||||
}
|
||||
}
|
||||
|
||||
let updatedTimeout: CachedPeerAutoremoveTimeout
|
||||
if let timeout = timeout {
|
||||
updatedTimeout = .known(CachedPeerAutoremoveTimeout.Value(myValue: timeout, peerValue: currentPeerValue ?? timeout, isGlobal: isGlobal))
|
||||
} else {
|
||||
updatedTimeout = .known(nil)
|
||||
}
|
||||
|
||||
if let current = current as? CachedUserData {
|
||||
return current.withUpdatedAutoremoveTimeout(updatedTimeout)
|
||||
} else if let current = current as? CachedGroupData {
|
||||
return current.withUpdatedAutoremoveTimeout(updatedTimeout)
|
||||
} else if let current = current as? CachedChannelData {
|
||||
return current.withUpdatedAutoremoveTimeout(updatedTimeout)
|
||||
} else {
|
||||
return current
|
||||
}
|
||||
})
|
||||
}
|
||||
|> castError(SetChatMessageAutoremoveTimeoutError.self)
|
||||
|> ignoreValues
|
||||
} else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
}
|
||||
|> `catch` { _ -> Signal<Never, SetChatMessageAutoremoveTimeoutError> in
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -332,6 +332,25 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId,
|
||||
if !disableAutoremove, let messageAutoremoveTimeout = peer.messageAutoremoveTimeout, !isAction {
|
||||
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: messageAutoremoveTimeout, countdownBeginTime: nil))
|
||||
}
|
||||
} else if let cachedData = transaction.getPeerCachedData(peerId: peer.id) {
|
||||
var messageAutoremoveTimeout: Int32?
|
||||
if let cachedData = cachedData as? CachedUserData {
|
||||
if case let .known(value) = cachedData.autoremoveTimeout {
|
||||
messageAutoremoveTimeout = value?.effectiveValue
|
||||
}
|
||||
} else if let cachedData = cachedData as? CachedGroupData {
|
||||
if case let .known(value) = cachedData.autoremoveTimeout {
|
||||
messageAutoremoveTimeout = value?.effectiveValue
|
||||
}
|
||||
} else if let cachedData = cachedData as? CachedChannelData {
|
||||
if case let .known(value) = cachedData.autoremoveTimeout {
|
||||
messageAutoremoveTimeout = value?.effectiveValue
|
||||
}
|
||||
}
|
||||
|
||||
if let messageAutoremoveTimeout = messageAutoremoveTimeout {
|
||||
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: messageAutoremoveTimeout, countdownBeginTime: nil))
|
||||
}
|
||||
}
|
||||
|
||||
attributes.append(contentsOf: filterMessageAttributesForOutgoingMessage(requestedAttributes))
|
||||
|
@ -7,8 +7,6 @@ import SyncCore
|
||||
extension MessageNotificationSettings {
|
||||
init(apiSettings: Api.PeerNotifySettings) {
|
||||
switch apiSettings {
|
||||
case .peerNotifySettingsEmpty:
|
||||
self = .defaultSettings
|
||||
case let .peerNotifySettings(_, showPreviews, _, muteUntil, sound):
|
||||
let displayPreviews: Bool
|
||||
if let showPreviews = showPreviews, case .boolFalse = showPreviews {
|
||||
|
@ -88,7 +88,7 @@ public func getCurrentGroupCall(account: Account, callId: Int64, accessHash: Int
|
||||
|
||||
loop: for participant in participants {
|
||||
switch participant {
|
||||
case let .groupCallParticipant(flags, userId, date, activeDate, source, volume):
|
||||
case let .groupCallParticipant(flags, userId, date, activeDate, source, volume, params):
|
||||
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
|
||||
let ssrc = UInt32(bitPattern: source)
|
||||
guard let peer = transaction.getPeer(peerId) else {
|
||||
@ -103,9 +103,17 @@ public func getCurrentGroupCall(account: Account, callId: Int64, accessHash: Int
|
||||
} else if mutedByYou {
|
||||
muteState = GroupCallParticipantsContext.Participant.MuteState(canUnmute: false, mutedByYou: mutedByYou)
|
||||
}
|
||||
var jsonParams: String?
|
||||
if let params = params {
|
||||
switch params {
|
||||
case let .dataJSON(data):
|
||||
jsonParams = data
|
||||
}
|
||||
}
|
||||
parsedParticipants.append(GroupCallParticipantsContext.Participant(
|
||||
peer: peer,
|
||||
ssrc: ssrc,
|
||||
jsonParams: jsonParams,
|
||||
joinTimestamp: date,
|
||||
activityTimestamp: activeDate.flatMap(Double.init),
|
||||
muteState: muteState,
|
||||
@ -228,7 +236,7 @@ public func getGroupCallParticipants(account: Account, callId: Int64, accessHash
|
||||
|
||||
loop: for participant in participants {
|
||||
switch participant {
|
||||
case let .groupCallParticipant(flags, userId, date, activeDate, source, volume):
|
||||
case let .groupCallParticipant(flags, userId, date, activeDate, source, volume, params):
|
||||
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
|
||||
let ssrc = UInt32(bitPattern: source)
|
||||
guard let peer = transaction.getPeer(peerId) else {
|
||||
@ -243,9 +251,17 @@ public func getGroupCallParticipants(account: Account, callId: Int64, accessHash
|
||||
} else if mutedByYou {
|
||||
muteState = GroupCallParticipantsContext.Participant.MuteState(canUnmute: false, mutedByYou: mutedByYou)
|
||||
}
|
||||
var jsonParams: String?
|
||||
if let params = params {
|
||||
switch params {
|
||||
case let .dataJSON(data):
|
||||
jsonParams = data
|
||||
}
|
||||
}
|
||||
parsedParticipants.append(GroupCallParticipantsContext.Participant(
|
||||
peer: peer,
|
||||
ssrc: ssrc,
|
||||
jsonParams: jsonParams,
|
||||
joinTimestamp: date,
|
||||
activityTimestamp: activeDate.flatMap(Double.init),
|
||||
muteState: muteState,
|
||||
@ -564,6 +580,7 @@ public final class GroupCallParticipantsContext {
|
||||
|
||||
public var peer: Peer
|
||||
public var ssrc: UInt32
|
||||
public var jsonParams: String?
|
||||
public var joinTimestamp: Int32
|
||||
public var activityTimestamp: Double?
|
||||
public var muteState: MuteState?
|
||||
@ -572,6 +589,7 @@ public final class GroupCallParticipantsContext {
|
||||
public init(
|
||||
peer: Peer,
|
||||
ssrc: UInt32,
|
||||
jsonParams: String?,
|
||||
joinTimestamp: Int32,
|
||||
activityTimestamp: Double?,
|
||||
muteState: MuteState?,
|
||||
@ -579,6 +597,7 @@ public final class GroupCallParticipantsContext {
|
||||
) {
|
||||
self.peer = peer
|
||||
self.ssrc = ssrc
|
||||
self.jsonParams = jsonParams
|
||||
self.joinTimestamp = joinTimestamp
|
||||
self.activityTimestamp = activityTimestamp
|
||||
self.muteState = muteState
|
||||
@ -687,11 +706,32 @@ public final class GroupCallParticipantsContext {
|
||||
|
||||
public var peerId: PeerId
|
||||
public var ssrc: UInt32
|
||||
public var jsonParams: String?
|
||||
public var joinTimestamp: Int32
|
||||
public var activityTimestamp: Double?
|
||||
public var muteState: Participant.MuteState?
|
||||
public var participationStatusChange: ParticipationStatusChange
|
||||
public var volume: Int32?
|
||||
|
||||
init(
|
||||
peerId: PeerId,
|
||||
ssrc: UInt32,
|
||||
jsonParams: String?,
|
||||
joinTimestamp: Int32,
|
||||
activityTimestamp: Double?,
|
||||
muteState: Participant.MuteState?,
|
||||
participationStatusChange: ParticipationStatusChange,
|
||||
volume: Int32?
|
||||
) {
|
||||
self.peerId = peerId
|
||||
self.ssrc = ssrc
|
||||
self.jsonParams = jsonParams
|
||||
self.joinTimestamp = joinTimestamp
|
||||
self.activityTimestamp = activityTimestamp
|
||||
self.muteState = muteState
|
||||
self.participationStatusChange = participationStatusChange
|
||||
self.volume = volume
|
||||
}
|
||||
}
|
||||
|
||||
public var participantUpdates: [ParticipantUpdate]
|
||||
@ -1107,6 +1147,7 @@ public final class GroupCallParticipantsContext {
|
||||
let participant = Participant(
|
||||
peer: peer,
|
||||
ssrc: participantUpdate.ssrc,
|
||||
jsonParams: participantUpdate.jsonParams,
|
||||
joinTimestamp: participantUpdate.joinTimestamp,
|
||||
activityTimestamp: activityTimestamp,
|
||||
muteState: participantUpdate.muteState,
|
||||
@ -1322,7 +1363,7 @@ public final class GroupCallParticipantsContext {
|
||||
extension GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate {
|
||||
init(_ apiParticipant: Api.GroupCallParticipant) {
|
||||
switch apiParticipant {
|
||||
case let .groupCallParticipant(flags, userId, date, activeDate, source, volume):
|
||||
case let .groupCallParticipant(flags, userId, date, activeDate, source, volume, params):
|
||||
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
|
||||
let ssrc = UInt32(bitPattern: source)
|
||||
let muted = (flags & (1 << 0)) != 0
|
||||
@ -1346,9 +1387,18 @@ extension GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate {
|
||||
participationStatusChange = .none
|
||||
}
|
||||
|
||||
var jsonParams: String?
|
||||
if let params = params {
|
||||
switch params {
|
||||
case let .dataJSON(data):
|
||||
jsonParams = data
|
||||
}
|
||||
}
|
||||
|
||||
self.init(
|
||||
peerId: peerId,
|
||||
ssrc: ssrc,
|
||||
jsonParams: jsonParams,
|
||||
joinTimestamp: date,
|
||||
activityTimestamp: activeDate.flatMap(Double.init),
|
||||
muteState: muteState,
|
||||
@ -1364,7 +1414,7 @@ extension GroupCallParticipantsContext.Update.StateUpdate {
|
||||
var participantUpdates: [GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate] = []
|
||||
for participant in participants {
|
||||
switch participant {
|
||||
case let .groupCallParticipant(flags, userId, date, activeDate, source, volume):
|
||||
case let .groupCallParticipant(flags, userId, date, activeDate, source, volume, params):
|
||||
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
|
||||
let ssrc = UInt32(bitPattern: source)
|
||||
let muted = (flags & (1 << 0)) != 0
|
||||
@ -1388,9 +1438,18 @@ extension GroupCallParticipantsContext.Update.StateUpdate {
|
||||
participationStatusChange = .none
|
||||
}
|
||||
|
||||
var jsonParams: String?
|
||||
if let params = params {
|
||||
switch params {
|
||||
case let .dataJSON(data):
|
||||
jsonParams = data
|
||||
}
|
||||
}
|
||||
|
||||
participantUpdates.append(GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate(
|
||||
peerId: peerId,
|
||||
ssrc: ssrc,
|
||||
jsonParams: jsonParams,
|
||||
joinTimestamp: date,
|
||||
activityTimestamp: activeDate.flatMap(Double.init),
|
||||
muteState: muteState,
|
||||
|
@ -78,35 +78,41 @@ public enum RevokePeerExportedInvitationError {
|
||||
case generic
|
||||
}
|
||||
|
||||
public func revokePeerExportedInvitation(account: Account, peerId: PeerId, link: String) -> Signal<ExportedInvitation?, RevokePeerExportedInvitationError> {
|
||||
return account.postbox.transaction { transaction -> Signal<ExportedInvitation?, RevokePeerExportedInvitationError> in
|
||||
public enum RevokeExportedInvitationResult {
|
||||
case update(ExportedInvitation)
|
||||
case replace(ExportedInvitation, ExportedInvitation)
|
||||
}
|
||||
|
||||
public func revokePeerExportedInvitation(account: Account, peerId: PeerId, link: String) -> Signal<RevokeExportedInvitationResult?, RevokePeerExportedInvitationError> {
|
||||
return account.postbox.transaction { transaction -> Signal<RevokeExportedInvitationResult?, RevokePeerExportedInvitationError> in
|
||||
if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) {
|
||||
let flags: Int32 = (1 << 2)
|
||||
return account.network.request(Api.functions.messages.editExportedChatInvite(flags: flags, peer: inputPeer, link: link, expireDate: nil, usageLimit: nil))
|
||||
|> mapError { _ in return RevokePeerExportedInvitationError.generic }
|
||||
|> mapToSignal { result -> Signal<ExportedInvitation?, RevokePeerExportedInvitationError> in
|
||||
|> mapToSignal { result -> Signal<RevokeExportedInvitationResult?, RevokePeerExportedInvitationError> in
|
||||
return account.postbox.transaction { transaction in
|
||||
switch result {
|
||||
case let .exportedChatInvite(invite, users):
|
||||
var peers: [Peer] = []
|
||||
for user in users {
|
||||
let telegramUser = TelegramUser(user: user)
|
||||
peers.append(telegramUser)
|
||||
}
|
||||
updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in
|
||||
return updated
|
||||
})
|
||||
return ExportedInvitation(apiExportedInvite: invite)
|
||||
case let .exportedChatInviteReplaced(_, newInvite, users):
|
||||
var peers: [Peer] = []
|
||||
for user in users {
|
||||
let telegramUser = TelegramUser(user: user)
|
||||
peers.append(telegramUser)
|
||||
}
|
||||
updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in
|
||||
return updated
|
||||
})
|
||||
return ExportedInvitation(apiExportedInvite: newInvite)
|
||||
if case let .exportedChatInvite(invite, users) = result {
|
||||
var peers: [Peer] = []
|
||||
for user in users {
|
||||
let telegramUser = TelegramUser(user: user)
|
||||
peers.append(telegramUser)
|
||||
}
|
||||
updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in
|
||||
return updated
|
||||
})
|
||||
return .update(ExportedInvitation(apiExportedInvite: invite))
|
||||
} else if case let .exportedChatInviteReplaced(invite, newInvite, users) = result {
|
||||
var peers: [Peer] = []
|
||||
for user in users {
|
||||
let telegramUser = TelegramUser(user: user)
|
||||
peers.append(telegramUser)
|
||||
}
|
||||
updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in
|
||||
return updated
|
||||
})
|
||||
return .replace(ExportedInvitation(apiExportedInvite: invite), ExportedInvitation(apiExportedInvite: newInvite))
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
} |> mapError { _ in .generic }
|
||||
}
|
||||
@ -123,9 +129,9 @@ public struct ExportedInvitations : Equatable {
|
||||
public let totalCount: Int32
|
||||
}
|
||||
|
||||
public func peerExportedInvitations(account: Account, peerId: PeerId, revoked: Bool, offsetLink: ExportedInvitation? = nil) -> Signal<ExportedInvitations?, NoError> {
|
||||
public func peerExportedInvitations(account: Account, peerId: PeerId, revoked: Bool, adminId: PeerId? = nil, offsetLink: ExportedInvitation? = nil) -> Signal<ExportedInvitations?, NoError> {
|
||||
return account.postbox.transaction { transaction -> Signal<ExportedInvitations?, NoError> in
|
||||
if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer), let accountPeer = transaction.getPeer(account.peerId), let adminId = apiInputUser(accountPeer) {
|
||||
if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer), let adminPeer = transaction.getPeer(adminId ?? account.peerId), let adminId = apiInputUser(adminPeer) {
|
||||
var flags: Int32 = 0
|
||||
if let _ = offsetLink {
|
||||
flags |= (1 << 2)
|
||||
|
@ -76,7 +76,14 @@ func managedAutoremoveMessageOperations(network: Network, postbox: Postbox) -> S
|
||||
|> suspendAwareDelay(max(0.0, Double(entry.timestamp) - timestamp), queue: Queue.concurrentDefaultQueue())
|
||||
|> then(postbox.transaction { transaction -> Void in
|
||||
if let message = transaction.getMessage(entry.messageId) {
|
||||
if message.id.peerId.namespace == Namespaces.Peer.SecretChat {
|
||||
var action: AutoremoveTimeoutMessageAttribute.Action = .remove
|
||||
for attribute in message.attributes {
|
||||
if let attribute = attribute as? AutoremoveTimeoutMessageAttribute {
|
||||
action = attribute.action
|
||||
}
|
||||
}
|
||||
|
||||
if message.id.peerId.namespace == Namespaces.Peer.SecretChat || action == .remove {
|
||||
deleteMessages(transaction: transaction, mediaBox: postbox.mediaBox, ids: [entry.messageId])
|
||||
} else {
|
||||
transaction.updateMessage(message.id, update: { currentMessage in
|
||||
@ -92,11 +99,17 @@ func managedAutoremoveMessageOperations(network: Network, postbox: Postbox) -> S
|
||||
updatedMedia[i] = TelegramMediaExpiredContent(data: .file)
|
||||
}
|
||||
}
|
||||
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: updatedMedia))
|
||||
var updatedAttributes = currentMessage.attributes
|
||||
for i in 0 ..< updatedAttributes.count {
|
||||
if let _ = updatedAttributes[i] as? AutoremoveTimeoutMessageAttribute {
|
||||
updatedAttributes.remove(at: i)
|
||||
break
|
||||
}
|
||||
}
|
||||
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: updatedAttributes, media: updatedMedia))
|
||||
})
|
||||
}
|
||||
}
|
||||
transaction.removeTimestampBasedMessageAttribute(tag: 0, messageId: entry.messageId)
|
||||
})
|
||||
disposable.set(signal.start())
|
||||
}
|
||||
|
@ -114,8 +114,6 @@ private func fetchedNotificationSettings(network: Network) -> Signal<GlobalNotif
|
||||
|> map { chats, users, channels, contactsJoinedMuted in
|
||||
let chatsSettings: MessageNotificationSettings
|
||||
switch chats {
|
||||
case .peerNotifySettingsEmpty:
|
||||
chatsSettings = MessageNotificationSettings.defaultSettings
|
||||
case let .peerNotifySettings(_, showPreviews, _, muteUntil, sound):
|
||||
let enabled: Bool
|
||||
if muteUntil != nil && muteUntil != 0 {
|
||||
@ -134,8 +132,6 @@ private func fetchedNotificationSettings(network: Network) -> Signal<GlobalNotif
|
||||
|
||||
let userSettings: MessageNotificationSettings
|
||||
switch users {
|
||||
case .peerNotifySettingsEmpty:
|
||||
userSettings = MessageNotificationSettings.defaultSettings
|
||||
case let .peerNotifySettings(_, showPreviews, _, muteUntil, sound):
|
||||
let enabled: Bool
|
||||
if muteUntil != nil && muteUntil != 0 {
|
||||
@ -154,8 +150,6 @@ private func fetchedNotificationSettings(network: Network) -> Signal<GlobalNotif
|
||||
|
||||
let channelSettings: MessageNotificationSettings
|
||||
switch channels {
|
||||
case .peerNotifySettingsEmpty:
|
||||
channelSettings = MessageNotificationSettings.defaultSettings
|
||||
case let .peerNotifySettings(_, showPreviews, _, muteUntil, sound):
|
||||
let enabled: Bool
|
||||
if muteUntil != nil && muteUntil != 0 {
|
||||
|
@ -159,9 +159,9 @@ private func synchronizeLocalizationUpdates(accountManager: AccountManager, post
|
||||
|> castError(SynchronizeLocalizationUpdatesError.self)
|
||||
|> mapToSignal { (primary, secondary) -> Signal<Void, SynchronizeLocalizationUpdatesError> in
|
||||
var differences: [Signal<Api.LangPackDifference, MTRpcError>] = []
|
||||
differences.append(network.request(Api.functions.langpack.getDifference(langCode: primary.code, fromVersion: primary.version)))
|
||||
differences.append(network.request(Api.functions.langpack.getDifference(langPack: "", langCode: primary.code, fromVersion: primary.version)))
|
||||
if let secondary = secondary {
|
||||
differences.append(network.request(Api.functions.langpack.getDifference(langCode: secondary.code, fromVersion: secondary.version)))
|
||||
differences.append(network.request(Api.functions.langpack.getDifference(langPack: "", langCode: secondary.code, fromVersion: secondary.version)))
|
||||
}
|
||||
|
||||
return combineLatest(differences)
|
||||
|
@ -60,8 +60,6 @@ public func markMessageContentAsConsumedInteractively(postbox: Postbox, messageI
|
||||
updatedAttributes[i] = AutoremoveTimeoutMessageAttribute(timeout: timeout, countdownBeginTime: timestamp)
|
||||
updateMessage = true
|
||||
|
||||
transaction.addTimestampBasedMessageAttribute(tag: 0, timestamp: timestamp + timeout, messageId: messageId)
|
||||
|
||||
if messageId.peerId.namespace == Namespaces.Peer.SecretChat {
|
||||
var layer: SecretChatLayer?
|
||||
let state = transaction.getPeerChatState(message.id.peerId) as? SecretChatState
|
||||
@ -132,7 +130,6 @@ func markMessageContentAsConsumedRemotely(transaction: Transaction, messageId: M
|
||||
updateMessage = true
|
||||
|
||||
if message.id.peerId.namespace == Namespaces.Peer.SecretChat {
|
||||
transaction.addTimestampBasedMessageAttribute(tag: 0, timestamp: timestamp + attribute.timeout, messageId: messageId)
|
||||
} else {
|
||||
for i in 0 ..< updatedMedia.count {
|
||||
if let _ = updatedMedia[i] as? TelegramMediaImage {
|
||||
|
@ -289,7 +289,7 @@ public func searchMessages(account: Account, location: SearchMessagesLocation, q
|
||||
return (state?.main.nextRate ?? 0, lowerBound, inputPeer)
|
||||
} else {
|
||||
return (0, lowerBound, .inputPeerEmpty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|> mapToSignal { (nextRate, lowerBound, inputPeer) in
|
||||
return account.network.request(Api.functions.messages.searchGlobal(flags: flags, folderId: folderId, q: query, filter: filter, minDate: minDate ?? 0, maxDate: maxDate ?? (Int32.max - 1), offsetRate: nextRate, offsetPeer: inputPeer, offsetId: lowerBound?.id.id ?? 0, limit: limit), automaticFloodWait: false)
|
||||
|
@ -210,7 +210,7 @@ public class BoxedMessage: NSObject {
|
||||
|
||||
public class Serialization: NSObject, MTSerialization {
|
||||
public func currentLayer() -> UInt {
|
||||
return 124
|
||||
return 125
|
||||
}
|
||||
|
||||
public func parseMessage(_ data: Data!) -> Any! {
|
||||
|
@ -192,7 +192,7 @@ func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] {
|
||||
}
|
||||
|
||||
switch action {
|
||||
case .messageActionChannelCreate, .messageActionChatDeletePhoto, .messageActionChatEditPhoto, .messageActionChatEditTitle, .messageActionEmpty, .messageActionPinMessage, .messageActionHistoryClear, .messageActionGameScore, .messageActionPaymentSent, .messageActionPaymentSentMe, .messageActionPhoneCall, .messageActionScreenshotTaken, .messageActionCustomAction, .messageActionBotAllowed, .messageActionSecureValuesSent, .messageActionSecureValuesSentMe, .messageActionContactSignUp, .messageActionGroupCall, .messageActionSetMessagesTTL:
|
||||
case .messageActionChannelCreate, .messageActionChatDeletePhoto, .messageActionChatEditPhoto, .messageActionChatEditTitle, .messageActionEmpty, .messageActionPinMessage, .messageActionHistoryClear, .messageActionGameScore, .messageActionPaymentSent, .messageActionPaymentSentMe, .messageActionPhoneCall, .messageActionScreenshotTaken, .messageActionCustomAction, .messageActionBotAllowed, .messageActionSecureValuesSent, .messageActionSecureValuesSentMe, .messageActionContactSignUp, .messageActionGroupCall:
|
||||
break
|
||||
case let .messageActionChannelMigrateFrom(_, chatId):
|
||||
result.append(PeerId(namespace: Namespaces.Peer.CloudGroup, id: chatId))
|
||||
@ -371,7 +371,7 @@ func messageTextEntitiesFromApiEntities(_ entities: [Api.MessageEntity]) -> [Mes
|
||||
extension StoreMessage {
|
||||
convenience init?(apiMessage: Api.Message, namespace: MessageId.Namespace = Namespaces.Message.Cloud) {
|
||||
switch apiMessage {
|
||||
case let .message(flags, id, fromId, chatPeerId, fwdFrom, viaBotId, replyTo, date, message, media, replyMarkup, entities, views, forwards, replies, editDate, postAuthor, groupingId, restrictionReason, _):
|
||||
case let .message(flags, id, fromId, chatPeerId, fwdFrom, viaBotId, replyTo, date, message, media, replyMarkup, entities, views, forwards, replies, editDate, postAuthor, groupingId, restrictionReason, ttlPeriod):
|
||||
let resolvedFromId = fromId?.peerId ?? chatPeerId.peerId
|
||||
|
||||
let peerId: PeerId
|
||||
@ -468,19 +468,29 @@ extension StoreMessage {
|
||||
|
||||
var consumableContent: (Bool, Bool)? = nil
|
||||
|
||||
var resolvedTtlPeriod: Int32? = ttlPeriod
|
||||
|
||||
if let media = media {
|
||||
let (mediaValue, expirationTimer) = textMediaAndExpirationTimerFromApiMedia(media, peerId)
|
||||
if let mediaValue = mediaValue {
|
||||
medias.append(mediaValue)
|
||||
|
||||
if let expirationTimer = expirationTimer, expirationTimer > 0 {
|
||||
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: expirationTimer, countdownBeginTime: nil))
|
||||
if let resolvedTtlPeriodValue = resolvedTtlPeriod {
|
||||
resolvedTtlPeriod = min(resolvedTtlPeriodValue, expirationTimer)
|
||||
} else {
|
||||
resolvedTtlPeriod = expirationTimer
|
||||
}
|
||||
|
||||
consumableContent = (true, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let resolvedTtlPeriod = resolvedTtlPeriod {
|
||||
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: resolvedTtlPeriod, countdownBeginTime: ttlPeriod == nil ? nil : date))
|
||||
}
|
||||
|
||||
if let postAuthor = postAuthor {
|
||||
attributes.append(AuthorSignatureMessageAttribute(signature: postAuthor))
|
||||
}
|
||||
|
@ -71,8 +71,6 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe
|
||||
PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
|
||||
}))
|
||||
}
|
||||
case .messageActionSetMessagesTTL(period: let period):
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,8 +7,6 @@ import SyncCore
|
||||
extension TelegramPeerNotificationSettings {
|
||||
convenience init(apiSettings: Api.PeerNotifySettings) {
|
||||
switch apiSettings {
|
||||
case .peerNotifySettingsEmpty:
|
||||
self.init(muteState: .unmuted, messageSound: .bundledModern(id: 0), displayPreviews: .default)
|
||||
case let .peerNotifySettings(_, showPreviews, _, muteUntil, sound):
|
||||
let muteState: PeerMuteState
|
||||
if let muteUntil = muteUntil {
|
||||
|
@ -206,8 +206,11 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
|
||||
let peerStatusSettings = PeerStatusSettings(apiSettings: userFull.settings)
|
||||
|
||||
let hasScheduledMessages = (userFull.flags & 1 << 12) != 0
|
||||
|
||||
let autoremoveTimeout: CachedPeerAutoremoveTimeout = .known(CachedPeerAutoremoveTimeout.Value(userFull.ttl))
|
||||
|
||||
return previous.withUpdatedAbout(userFull.about).withUpdatedBotInfo(botInfo).withUpdatedCommonGroupCount(userFull.commonChatsCount).withUpdatedIsBlocked(isBlocked).withUpdatedVoiceCallsAvailable(voiceCallsAvailable).withUpdatedVideoCallsAvailable(videoCallsAvailable).withUpdatedCallsPrivate(callsPrivate).withUpdatedCanPinMessages(canPinMessages).withUpdatedPeerStatusSettings(peerStatusSettings).withUpdatedPinnedMessageId(pinnedMessageId).withUpdatedHasScheduledMessages(hasScheduledMessages)
|
||||
.withUpdatedAutoremoveTimeout(autoremoveTimeout)
|
||||
}
|
||||
})
|
||||
return true
|
||||
@ -352,7 +355,7 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
|
||||
}
|
||||
|
||||
switch fullChat {
|
||||
case let .channelFull(flags, _, about, participantsCount, adminsCount, kickedCount, bannedCount, _, _, _, _, chatPhoto, _, apiExportedInvite, apiBotInfos, migratedFromChatId, migratedFromMaxId, pinnedMsgId, stickerSet, minAvailableMsgId, folderId, linkedChatId, location, slowmodeSeconds, slowmodeNextSendDate, statsDc, pts, inputCall, _):
|
||||
case let .channelFull(flags, _, about, participantsCount, adminsCount, kickedCount, bannedCount, _, _, _, _, chatPhoto, _, apiExportedInvite, apiBotInfos, migratedFromChatId, migratedFromMaxId, pinnedMsgId, stickerSet, minAvailableMsgId, folderId, linkedChatId, location, slowmodeSeconds, slowmodeNextSendDate, statsDc, pts, inputCall, ttl):
|
||||
var channelFlags = CachedChannelFlags()
|
||||
if (flags & (1 << 3)) != 0 {
|
||||
channelFlags.insert(.canDisplayParticipants)
|
||||
@ -379,7 +382,9 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
|
||||
} else {
|
||||
linkedDiscussionPeerId = nil
|
||||
}
|
||||
|
||||
|
||||
let autoremoveTimeout: CachedPeerAutoremoveTimeout = .known(CachedPeerAutoremoveTimeout.Value(ttl))
|
||||
|
||||
let peerGeoLocation: PeerGeoLocation?
|
||||
if let location = location {
|
||||
peerGeoLocation = PeerGeoLocation(apiLocation: location)
|
||||
@ -525,6 +530,7 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
|
||||
.withUpdatedInvitedBy(invitedBy)
|
||||
.withUpdatedPhoto(photo)
|
||||
.withUpdatedActiveCall(updatedActiveCall)
|
||||
.withUpdatedAutoremoveTimeout(autoremoveTimeout)
|
||||
})
|
||||
|
||||
if let minAvailableMessageId = minAvailableMessageId, minAvailableMessageIdUpdated {
|
||||
@ -556,3 +562,19 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension CachedPeerAutoremoveTimeout.Value {
|
||||
init?(_ apiValue: Api.PeerHistoryTTL?) {
|
||||
if let apiValue = apiValue {
|
||||
switch apiValue {
|
||||
case let .peerHistoryTTLPM(flags, ttlPeriodMy, ttlPeriodPeer):
|
||||
let pmOneSide = flags & (1 << 0) != 0
|
||||
self.init(myValue: ttlPeriodMy, peerValue: ttlPeriodPeer, isGlobal: !pmOneSide)
|
||||
case let .peerHistoryTTL(ttlPeriodPeer):
|
||||
self.init(myValue: ttlPeriodPeer, peerValue: ttlPeriodPeer, isGlobal: true)
|
||||
}
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -57,14 +57,14 @@ class UpdateMessageService: NSObject, MTMessageService {
|
||||
if groups.count != 0 {
|
||||
self.putNext(groups)
|
||||
}
|
||||
case let .updateShortChatMessage(flags, id, fromId, chatId, message, pts, ptsCount, date, fwdFrom, viaBotId, replyHeader, entities, _):
|
||||
let generatedMessage = Api.Message.message(flags: flags, id: id, fromId: .peerUser(userId: fromId), peerId: Api.Peer.peerChat(chatId: chatId), fwdFrom: fwdFrom, viaBotId: viaBotId, replyTo: replyHeader, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, forwards: nil, replies: nil, editDate: nil, postAuthor: nil, groupedId: nil, restrictionReason: nil, ttlPeriod: nil)
|
||||
case let .updateShortChatMessage(flags, id, fromId, chatId, message, pts, ptsCount, date, fwdFrom, viaBotId, replyHeader, entities, ttlPeriod):
|
||||
let generatedMessage = Api.Message.message(flags: flags, id: id, fromId: .peerUser(userId: fromId), peerId: Api.Peer.peerChat(chatId: chatId), fwdFrom: fwdFrom, viaBotId: viaBotId, replyTo: replyHeader, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, forwards: nil, replies: nil, editDate: nil, postAuthor: nil, groupedId: nil, restrictionReason: nil, ttlPeriod: ttlPeriod)
|
||||
let update = Api.Update.updateNewMessage(message: generatedMessage, pts: pts, ptsCount: ptsCount)
|
||||
let groups = groupUpdates([update], users: [], chats: [], date: date, seqRange: nil)
|
||||
if groups.count != 0 {
|
||||
self.putNext(groups)
|
||||
}
|
||||
case let .updateShortMessage(flags, id, userId, message, pts, ptsCount, date, fwdFrom, viaBotId, replyHeader, entities, _):
|
||||
case let .updateShortMessage(flags, id, userId, message, pts, ptsCount, date, fwdFrom, viaBotId, replyHeader, entities, ttlPeriod):
|
||||
let generatedFromId: Api.Peer
|
||||
if (Int(flags) & 1 << 1) != 0 {
|
||||
generatedFromId = Api.Peer.peerUser(userId: self.peerId.id)
|
||||
@ -74,7 +74,7 @@ class UpdateMessageService: NSObject, MTMessageService {
|
||||
|
||||
let generatedPeerId = Api.Peer.peerUser(userId: userId)
|
||||
|
||||
let generatedMessage = Api.Message.message(flags: flags, id: id, fromId: generatedFromId, peerId: generatedPeerId, fwdFrom: fwdFrom, viaBotId: viaBotId, replyTo: replyHeader, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, forwards: nil, replies: nil, editDate: nil, postAuthor: nil, groupedId: nil, restrictionReason: nil, ttlPeriod: nil)
|
||||
let generatedMessage = Api.Message.message(flags: flags, id: id, fromId: generatedFromId, peerId: generatedPeerId, fwdFrom: fwdFrom, viaBotId: viaBotId, replyTo: replyHeader, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, forwards: nil, replies: nil, editDate: nil, postAuthor: nil, groupedId: nil, restrictionReason: nil, ttlPeriod: ttlPeriod)
|
||||
let update = Api.Update.updateNewMessage(message: generatedMessage, pts: pts, ptsCount: ptsCount)
|
||||
let groups = groupUpdates([update], users: [], chats: [], date: date, seqRange: nil)
|
||||
if groups.count != 0 {
|
||||
|
@ -20,7 +20,7 @@ public struct SecureIdPreparePhoneVerificationPayload {
|
||||
}
|
||||
|
||||
public func secureIdPreparePhoneVerification(network: Network, value: SecureIdPhoneValue) -> Signal<SecureIdPreparePhoneVerificationPayload, SecureIdPreparePhoneVerificationError> {
|
||||
return network.request(Api.functions.account.sendVerifyPhoneCode(flags: 0, phoneNumber: value.phone, currentNumber: nil), automaticFloodWait: false)
|
||||
return network.request(Api.functions.account.sendVerifyPhoneCode(phoneNumber: value.phone, settings: .codeSettings(flags: 0)), automaticFloodWait: false)
|
||||
|> mapError { error -> SecureIdPreparePhoneVerificationError in
|
||||
if error.errorDescription.hasPrefix("FLOOD_WAIT") {
|
||||
return .flood
|
||||
|
@ -1,39 +0,0 @@
|
||||
import Foundation
|
||||
import Postbox
|
||||
import SwiftSignalKit
|
||||
import MtProtoKit
|
||||
import TelegramApi
|
||||
|
||||
public enum GetServerWalletSaltError {
|
||||
case generic
|
||||
}
|
||||
|
||||
public func getServerWalletSalt(network: Network) -> Signal<Data, GetServerWalletSaltError> {
|
||||
return network.request(Api.functions.wallet.getKeySecretSalt(revoke: .boolFalse))
|
||||
|> mapError { _ -> GetServerWalletSaltError in
|
||||
return .generic
|
||||
}
|
||||
|> map { result -> Data in
|
||||
switch result {
|
||||
case let .secretSalt(salt):
|
||||
return salt.makeData()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum WalletProxyRequestError {
|
||||
case generic(Int32, String)
|
||||
}
|
||||
|
||||
public func walletProxyRequest(network: Network, data: Data) -> Signal<Data, WalletProxyRequestError> {
|
||||
return network.request(Api.functions.wallet.sendLiteRequest(body: Buffer(data: data)))
|
||||
|> mapError { error -> WalletProxyRequestError in
|
||||
return .generic(error.errorCode, error.errorDescription)
|
||||
}
|
||||
|> map { result -> Data in
|
||||
switch result {
|
||||
case let .liteResponse(response):
|
||||
return response.makeData()
|
||||
}
|
||||
}
|
||||
}
|
@ -167,6 +167,7 @@ private struct ApplicationSpecificNoticeKeys {
|
||||
private static let peerReportNamespace: Int32 = 4
|
||||
private static let inlineBotLocationRequestNamespace: Int32 = 5
|
||||
private static let psaAcknowledgementNamespace: Int32 = 6
|
||||
private static let botGameNoticeNamespace: Int32 = 7
|
||||
|
||||
static func inlineBotLocationRequestNotice(peerId: PeerId) -> NoticeEntryKey {
|
||||
return NoticeEntryKey(namespace: noticeNamespace(namespace: inlineBotLocationRequestNamespace), key: noticeKey(peerId: peerId, key: 0))
|
||||
@ -176,6 +177,10 @@ private struct ApplicationSpecificNoticeKeys {
|
||||
return NoticeEntryKey(namespace: noticeNamespace(namespace: botPaymentLiabilityNamespace), key: noticeKey(peerId: peerId, key: 0))
|
||||
}
|
||||
|
||||
static func botGameNotice(peerId: PeerId) -> NoticeEntryKey {
|
||||
return NoticeEntryKey(namespace: noticeNamespace(namespace: botGameNoticeNamespace), key: noticeKey(peerId: peerId, key: 0))
|
||||
}
|
||||
|
||||
static func irrelevantPeerGeoNotice(peerId: PeerId) -> NoticeEntryKey {
|
||||
return NoticeEntryKey(namespace: noticeNamespace(namespace: peerReportNamespace), key: noticeKey(peerId: peerId, key: 0))
|
||||
}
|
||||
@ -292,6 +297,22 @@ public struct ApplicationSpecificNotice {
|
||||
}
|
||||
}
|
||||
|
||||
public static func getBotGameNotice(accountManager: AccountManager, peerId: PeerId) -> Signal<Bool, NoError> {
|
||||
return accountManager.transaction { transaction -> Bool in
|
||||
if let _ = transaction.getNotice(ApplicationSpecificNoticeKeys.botGameNotice(peerId: peerId)) as? ApplicationSpecificBoolNotice {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static func setBotGameNotice(accountManager: AccountManager, peerId: PeerId) -> Signal<Void, NoError> {
|
||||
return accountManager.transaction { transaction -> Void in
|
||||
transaction.setNotice(ApplicationSpecificNoticeKeys.botGameNotice(peerId: peerId), ApplicationSpecificBoolNotice())
|
||||
}
|
||||
}
|
||||
|
||||
public static func getInlineBotLocationRequest(accountManager: AccountManager, peerId: PeerId) -> Signal<Int32?, NoError> {
|
||||
return accountManager.transaction { transaction -> Int32? in
|
||||
if let notice = transaction.getNotice(ApplicationSpecificNoticeKeys.inlineBotLocationRequestNotice(peerId: peerId)) as? ApplicationSpecificTimestampNotice {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -176,6 +176,10 @@ public final class PrincipalThemeEssentialGraphics {
|
||||
public let outgoingDateAndStatusPinnedIcon: UIImage
|
||||
public let mediaPinnedIcon: UIImage
|
||||
public let freePinnedIcon: UIImage
|
||||
public let incomingDateAndStatusSelfExpiringIcon: UIImage
|
||||
public let outgoingDateAndStatusSelfExpiringIcon: UIImage
|
||||
public let mediaSelfExpiringIcon: UIImage
|
||||
public let freeSelfExpiringIcon: UIImage
|
||||
|
||||
public let dateStaticBackground: UIImage
|
||||
public let dateFloatingBackground: UIImage
|
||||
@ -350,6 +354,12 @@ public final class PrincipalThemeEssentialGraphics {
|
||||
self.mediaPinnedIcon = generateTintedImage(image: pinnedImage, color: .white)!
|
||||
self.freePinnedIcon = generateTintedImage(image: pinnedImage, color: serviceColor.primaryText)!
|
||||
|
||||
let selfExpiringImage = UIImage(bundleImageName: "Chat/Message/SelfExpiring")!
|
||||
self.incomingDateAndStatusSelfExpiringIcon = generateTintedImage(image: selfExpiringImage, color: theme.message.incoming.secondaryTextColor)!
|
||||
self.outgoingDateAndStatusSelfExpiringIcon = generateTintedImage(image: selfExpiringImage, color: theme.message.outgoing.secondaryTextColor)!
|
||||
self.mediaSelfExpiringIcon = generateTintedImage(image: selfExpiringImage, color: .white)!
|
||||
self.freeSelfExpiringIcon = generateTintedImage(image: selfExpiringImage, color: serviceColor.primaryText)!
|
||||
|
||||
self.radialIndicatorFileIconIncoming = emptyImage
|
||||
self.radialIndicatorFileIconOutgoing = emptyImage
|
||||
} else {
|
||||
@ -463,6 +473,12 @@ public final class PrincipalThemeEssentialGraphics {
|
||||
self.mediaPinnedIcon = generateTintedImage(image: pinnedImage, color: .white)!
|
||||
self.freePinnedIcon = generateTintedImage(image: pinnedImage, color: serviceColor.primaryText)!
|
||||
|
||||
let selfExpiringImage = UIImage(bundleImageName: "Chat/Message/SelfExpiring")!
|
||||
self.incomingDateAndStatusSelfExpiringIcon = generateTintedImage(image: selfExpiringImage, color: theme.message.incoming.secondaryTextColor)!
|
||||
self.outgoingDateAndStatusSelfExpiringIcon = generateTintedImage(image: selfExpiringImage, color: theme.message.outgoing.secondaryTextColor)!
|
||||
self.mediaSelfExpiringIcon = generateTintedImage(image: selfExpiringImage, color: .white)!
|
||||
self.freeSelfExpiringIcon = generateTintedImage(image: selfExpiringImage, color: serviceColor.primaryText)!
|
||||
|
||||
self.radialIndicatorFileIconIncoming = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/RadialProgressIconDocument"), color: .black)!
|
||||
self.radialIndicatorFileIconOutgoing = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/RadialProgressIconDocument"), color: .black)!
|
||||
}
|
||||
|
@ -16,6 +16,3 @@ public func stringForDuration(_ duration: Int32, position: Int32? = nil) -> Stri
|
||||
}
|
||||
return durationString
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -212,7 +212,7 @@ swift_library(
|
||||
"//submodules/SlotMachineAnimationNode:SlotMachineAnimationNode",
|
||||
"//submodules/AnimatedNavigationStripeNode:AnimatedNavigationStripeNode",
|
||||
"//submodules/AudioBlob:AudioBlob",
|
||||
"//Telegram:GeneratedSources",
|
||||
"//:GeneratedSources",
|
||||
"//third-party/ZipArchive:ZipArchive",
|
||||
"//submodules/ChatImportUI:ChatImportUI",
|
||||
"//submodules/ChatHistoryImportTasks:ChatHistoryImportTasks",
|
||||
|
12
submodules/TelegramUI/Images.xcassets/Chat/Message/SelfExpiring.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Message/SelfExpiring.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic_autodeleteindicator.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
submodules/TelegramUI/Images.xcassets/Chat/Message/SelfExpiring.imageset/ic_autodeleteindicator.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Chat/Message/SelfExpiring.imageset/ic_autodeleteindicator.pdf
vendored
Normal file
Binary file not shown.
BIN
submodules/TelegramUI/Resources/Animations/MessageAutoRemove.tgs
Normal file
BIN
submodules/TelegramUI/Resources/Animations/MessageAutoRemove.tgs
Normal file
Binary file not shown.
Binary file not shown.
@ -1040,8 +1040,35 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
strongSelf.botCallbackAlertMessage.set(message |> then(delayedNoMessage))
|
||||
case let .url(url):
|
||||
if isGame {
|
||||
strongSelf.chatDisplayNode.dismissInput()
|
||||
strongSelf.effectiveNavigationController?.pushViewController(GameController(context: strongSelf.context, url: url, message: message))
|
||||
let openBot: () -> Void = {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.chatDisplayNode.dismissInput()
|
||||
strongSelf.effectiveNavigationController?.pushViewController(GameController(context: strongSelf.context, url: url, message: message))
|
||||
}
|
||||
|
||||
if let botPeer = message.author as? TelegramUser, botPeer.flags.contains(.isVerified) {
|
||||
openBot()
|
||||
} else {
|
||||
let _ = (ApplicationSpecificNotice.getBotGameNotice(accountManager: strongSelf.context.sharedContext.accountManager, peerId: message.id.peerId)
|
||||
|> deliverOnMainQueue).start(next: { value in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if value {
|
||||
openBot()
|
||||
} else if let botPeer = message.author as? TelegramUser {
|
||||
strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Conversation_BotInteractiveUrlAlert(botPeer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).0, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: { }), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
|
||||
if let strongSelf = self {
|
||||
let _ = ApplicationSpecificNotice.setBotGameNotice(accountManager: strongSelf.context.sharedContext.accountManager, peerId: botPeer.id).start()
|
||||
openBot()
|
||||
}
|
||||
})]), in: .window(.root), with: nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
strongSelf.openUrl(url, concealed: false)
|
||||
}
|
||||
@ -2888,14 +2915,27 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
|
||||
var hasBots: Bool = false
|
||||
var autoremoveTimeout: Int32?
|
||||
if let peer = peerView.peers[peerView.peerId] {
|
||||
if let cachedGroupData = peerView.cachedData as? CachedGroupData {
|
||||
if !cachedGroupData.botInfos.isEmpty {
|
||||
hasBots = true
|
||||
}
|
||||
} else if let cachedChannelData = peerView.cachedData as? CachedChannelData, let channel = peer as? TelegramChannel, case .group = channel.info {
|
||||
if !cachedChannelData.botInfos.isEmpty {
|
||||
hasBots = true
|
||||
if case let .known(value) = cachedGroupData.autoremoveTimeout {
|
||||
autoremoveTimeout = value?.effectiveValue
|
||||
}
|
||||
} else if let cachedChannelData = peerView.cachedData as? CachedChannelData {
|
||||
if let channel = peer as? TelegramChannel, case .group = channel.info {
|
||||
if !cachedChannelData.botInfos.isEmpty {
|
||||
hasBots = true
|
||||
}
|
||||
}
|
||||
if case let .known(value) = cachedChannelData.autoremoveTimeout {
|
||||
autoremoveTimeout = value?.effectiveValue
|
||||
}
|
||||
} else if let cachedUserData = peerView.cachedData as? CachedUserData {
|
||||
if case let .known(value) = cachedUserData.autoremoveTimeout {
|
||||
autoremoveTimeout = value?.effectiveValue
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2973,6 +3013,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
return $0.updatedPeer { _ in
|
||||
return renderedPeer
|
||||
}.updatedIsNotAccessible(isNotAccessible).updatedContactStatus(contactStatus).updatedHasBots(hasBots).updatedIsArchived(isArchived).updatedPeerIsMuted(peerIsMuted).updatedPeerDiscussionId(peerDiscussionId).updatedPeerGeoLocation(peerGeoLocation).updatedExplicitelyCanPinMessages(explicitelyCanPinMessages).updatedHasScheduledMessages(hasScheduledMessages)
|
||||
.updatedAutoremoveTimeout(autoremoveTimeout)
|
||||
})
|
||||
if !strongSelf.didSetChatLocationInfoReady {
|
||||
strongSelf.didSetChatLocationInfoReady = true
|
||||
@ -5471,10 +5512,16 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
strongSelf.displayMediaRecordingTooltip()
|
||||
}
|
||||
}, setupMessageAutoremoveTimeout: { [weak self] in
|
||||
if let strongSelf = self, case let .peer(peerId) = strongSelf.chatLocation, peerId.namespace == Namespaces.Peer.SecretChat {
|
||||
guard let strongSelf = self, case let .peer(peerId) = strongSelf.chatLocation else {
|
||||
return
|
||||
}
|
||||
guard let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else {
|
||||
return
|
||||
}
|
||||
if peerId.namespace == Namespaces.Peer.SecretChat {
|
||||
strongSelf.chatDisplayNode.dismissInput()
|
||||
|
||||
if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramSecretChat {
|
||||
if let peer = peer as? TelegramSecretChat {
|
||||
let controller = ChatSecretAutoremoveTimerActionSheetController(context: strongSelf.context, currentValue: peer.messageAutoremoveTimeout == nil ? 0 : peer.messageAutoremoveTimeout!, applyValue: { value in
|
||||
if let strongSelf = self {
|
||||
let _ = setSecretChatMessageAutoremoveTimeoutInteractively(account: strongSelf.context.account, peerId: peer.id, timeout: value == 0 ? nil : value).start()
|
||||
@ -5482,6 +5529,59 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
})
|
||||
strongSelf.present(controller, in: .window(.root))
|
||||
}
|
||||
} else {
|
||||
var currentAutoremoveTimeout: Int32? = strongSelf.presentationInterfaceState.autoremoveTimeout
|
||||
var canSetupAutoremoveTimeout = false
|
||||
|
||||
if let secretChat = peer as? TelegramSecretChat {
|
||||
currentAutoremoveTimeout = secretChat.messageAutoremoveTimeout
|
||||
canSetupAutoremoveTimeout = true
|
||||
} else if let group = peer as? TelegramGroup {
|
||||
if case .creator = group.role {
|
||||
canSetupAutoremoveTimeout = true
|
||||
} else if case let .admin(rights, _) = group.role {
|
||||
if rights.flags.contains(.canChangeInfo) {
|
||||
canSetupAutoremoveTimeout = true
|
||||
}
|
||||
} else if let defaultBannedRights = group.defaultBannedRights {
|
||||
if !defaultBannedRights.flags.contains(.banChangeInfo) {
|
||||
canSetupAutoremoveTimeout = true
|
||||
}
|
||||
}
|
||||
} else if let _ = peer as? TelegramUser {
|
||||
canSetupAutoremoveTimeout = true
|
||||
} else if let channel = peer as? TelegramChannel {
|
||||
if channel.hasPermission(.changeInfo) {
|
||||
canSetupAutoremoveTimeout = true
|
||||
}
|
||||
}
|
||||
|
||||
if canSetupAutoremoveTimeout {
|
||||
strongSelf.presentAutoremoveSetup()
|
||||
} else if let currentAutoremoveTimeout = currentAutoremoveTimeout, let rect = strongSelf.chatDisplayNode.frameForInputPanelAccessoryButton(.messageAutoremoveTimeout(currentAutoremoveTimeout)) {
|
||||
|
||||
//TODO:localize
|
||||
let intervalText = timeIntervalString(strings: strongSelf.presentationData.strings, value: currentAutoremoveTimeout)
|
||||
let text: String = "Messages in this chat are automatically\ndeleted \(intervalText) after they have been sent."
|
||||
|
||||
if let tooltipController = strongSelf.silentPostTooltipController {
|
||||
tooltipController.updateContent(.text(text), animated: true, extendTimer: true)
|
||||
} else {
|
||||
let tooltipController = TooltipController(content: .text(text), baseFontSize: strongSelf.presentationData.listsFontSize.baseDisplaySize)
|
||||
strongSelf.silentPostTooltipController = tooltipController
|
||||
tooltipController.dismissed = { [weak tooltipController] _ in
|
||||
if let strongSelf = self, let tooltipController = tooltipController, strongSelf.silentPostTooltipController === tooltipController {
|
||||
strongSelf.silentPostTooltipController = nil
|
||||
}
|
||||
}
|
||||
strongSelf.present(tooltipController, in: .window(.root), with: TooltipControllerPresentationArguments(sourceNodeAndRect: {
|
||||
if let strongSelf = self {
|
||||
return (strongSelf.chatDisplayNode, rect)
|
||||
}
|
||||
return nil
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
}, sendSticker: { [weak self] file, sourceNode, sourceRect in
|
||||
if let strongSelf = self, canSendMessagesToChat(strongSelf.presentationInterfaceState) {
|
||||
@ -7490,13 +7590,19 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
return
|
||||
}
|
||||
|
||||
var isClearCache = false
|
||||
let text: String
|
||||
if peerId == self.context.account.peerId {
|
||||
text = self.presentationData.strings.Conversation_ClearSelfHistory
|
||||
} else if peerId.namespace == Namespaces.Peer.SecretChat {
|
||||
text = self.presentationData.strings.Conversation_ClearSecretHistory
|
||||
} else if peerId.namespace == Namespaces.Peer.CloudGroup || peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
text = self.presentationData.strings.Conversation_ClearGroupHistory
|
||||
if let channel = peer.peer as? TelegramChannel, case .broadcast = channel.info {
|
||||
isClearCache = true
|
||||
text = self.presentationData.strings.Conversation_ClearCache
|
||||
} else {
|
||||
text = self.presentationData.strings.Conversation_ClearGroupHistory
|
||||
}
|
||||
} else {
|
||||
text = self.presentationData.strings.Conversation_ClearPrivateHistory
|
||||
}
|
||||
@ -7565,6 +7671,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}))
|
||||
} else if canRemoveGlobally {
|
||||
items.append(DeleteChatPeerActionSheetItem(context: self.context, peer: mainPeer, chatPeer: chatPeer, action: .clearHistory, strings: self.presentationData.strings, nameDisplayOrder: self.presentationData.nameDisplayOrder))
|
||||
items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForCurrentUser, color: .destructive, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
beginClear(.forLocalPeer)
|
||||
}))
|
||||
items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForEveryone(mainPeer.compactDisplayTitle).0, color: .destructive, action: { [weak self, weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
|
||||
@ -7580,28 +7690,75 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
})
|
||||
], parseMarkdown: true), in: .window(.root))
|
||||
}))
|
||||
items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForCurrentUser, color: .destructive, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
beginClear(.forLocalPeer)
|
||||
}))
|
||||
} else {
|
||||
items.append(ActionSheetTextItem(title: text))
|
||||
items.append(ActionSheetButtonItem(title: self.presentationData.strings.Conversation_ClearAll, color: .destructive, action: { [weak self, weak actionSheet] in
|
||||
items.append(ActionSheetButtonItem(title: isClearCache ? self.presentationData.strings.Conversation_ClearCache : self.presentationData.strings.Conversation_ClearAll, color: isClearCache ? .accent : .destructive, action: { [weak self, weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationTitle, text: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationText, actions: [
|
||||
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
|
||||
}),
|
||||
TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationAction, action: {
|
||||
beginClear(.forLocalPeer)
|
||||
})
|
||||
], parseMarkdown: true), in: .window(.root))
|
||||
if isClearCache {
|
||||
strongSelf.navigationButtonAction(.clearCache)
|
||||
} else {
|
||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationTitle, text: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationText, actions: [
|
||||
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
|
||||
}),
|
||||
TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationAction, action: {
|
||||
beginClear(.forLocalPeer)
|
||||
})
|
||||
], parseMarkdown: true), in: .window(.root))
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
if let peer = self.presentationInterfaceState.renderedPeer?.peer {
|
||||
var currentAutoremoveTimeout: Int32? = self.presentationInterfaceState.autoremoveTimeout
|
||||
var canSetupAutoremoveTimeout = false
|
||||
|
||||
if let secretChat = peer as? TelegramSecretChat {
|
||||
currentAutoremoveTimeout = secretChat.messageAutoremoveTimeout
|
||||
canSetupAutoremoveTimeout = true
|
||||
} else if let group = peer as? TelegramGroup {
|
||||
if case .creator = group.role {
|
||||
canSetupAutoremoveTimeout = true
|
||||
} else if case let .admin(rights, _) = group.role {
|
||||
if rights.flags.contains(.canChangeInfo) {
|
||||
canSetupAutoremoveTimeout = true
|
||||
}
|
||||
} else if let defaultBannedRights = group.defaultBannedRights {
|
||||
if !defaultBannedRights.flags.contains(.banChangeInfo) {
|
||||
canSetupAutoremoveTimeout = true
|
||||
}
|
||||
}
|
||||
} else if let _ = self.presentationInterfaceState.renderedPeer?.peer as? TelegramUser {
|
||||
canSetupAutoremoveTimeout = true
|
||||
} else if let channel = self.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel {
|
||||
if channel.hasPermission(.changeInfo) {
|
||||
canSetupAutoremoveTimeout = true
|
||||
}
|
||||
}
|
||||
|
||||
if canSetupAutoremoveTimeout {
|
||||
//TODO:localize
|
||||
items.append(ActionSheetButtonItem(title: currentAutoremoveTimeout == nil ? "Enable Auto-Delete" : "Edit Auto-Delete Settings", color: .accent, action: { [weak self, weak actionSheet] in
|
||||
guard let actionSheet = actionSheet else {
|
||||
return
|
||||
}
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
Queue.mainQueue().after(0.8, {
|
||||
self?.updateChatPresentationInterfaceState(animated: false, interactive: false, { $0.updatedInterfaceState({ $0.withoutSelectionState() }) })
|
||||
})
|
||||
|
||||
actionSheet.dismissAnimated()
|
||||
|
||||
strongSelf.presentAutoremoveSetup()
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [
|
||||
ActionSheetButtonItem(title: self.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
|
||||
@ -11750,6 +11907,36 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
return false
|
||||
}
|
||||
|
||||
private func presentAutoremoveSetup() {
|
||||
guard let peer = self.presentationInterfaceState.renderedPeer?.peer else {
|
||||
return
|
||||
}
|
||||
|
||||
let controller = peerAutoremoveSetupScreen(context: self.context, peerId: peer.id, completion: { [weak self] updatedValue in
|
||||
if case let .updated(value) = updatedValue {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
var text: String?
|
||||
if let myValue = value.myValue {
|
||||
if let limitedByValue = value.limitedByValue, limitedByValue < myValue {
|
||||
text = "\(peer.compactDisplayTitle) has set messages to auto-delete in \(timeIntervalString(strings: strongSelf.presentationData.strings, value: limitedByValue)). You can't cancel it or make this interval longer."
|
||||
} else {
|
||||
text = strongSelf.presentationData.strings.Conversation_AutoremoveChanged("\(timeIntervalString(strings: strongSelf.presentationData.strings, value: myValue))").0
|
||||
}
|
||||
} else if let limitedByValue = value.limitedByValue {
|
||||
text = "\(peer.compactDisplayTitle) has set messages to auto-delete in \(timeIntervalString(strings: strongSelf.presentationData.strings, value: limitedByValue)). You can't cancel it or make this interval longer."
|
||||
}
|
||||
if let text = text {
|
||||
strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .succeed(text: text), elevatedLayout: false, action: { _ in return false }), in: .current)
|
||||
}
|
||||
}
|
||||
})
|
||||
self.chatDisplayNode.dismissInput()
|
||||
self.push(controller)
|
||||
}
|
||||
|
||||
private var effectiveNavigationController: NavigationController? {
|
||||
if let navigationController = self.navigationController as? NavigationController {
|
||||
return navigationController
|
||||
|
@ -221,6 +221,9 @@ func inputTextPanelStateForChatPresentationInterfaceState(_ chatPresentationInte
|
||||
}
|
||||
}
|
||||
|
||||
var currentAutoremoveTimeout: Int32? = chatPresentationInterfaceState.autoremoveTimeout
|
||||
var canSetupAutoremoveTimeout = false
|
||||
|
||||
var accessoryItems: [ChatTextInputAccessoryItem] = []
|
||||
if let peer = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramSecretChat {
|
||||
var extendedSearchLayout = false
|
||||
@ -232,7 +235,32 @@ func inputTextPanelStateForChatPresentationInterfaceState(_ chatPresentationInte
|
||||
}
|
||||
|
||||
if !extendedSearchLayout {
|
||||
accessoryItems.append(.messageAutoremoveTimeout(peer.messageAutoremoveTimeout))
|
||||
currentAutoremoveTimeout = peer.messageAutoremoveTimeout
|
||||
canSetupAutoremoveTimeout = true
|
||||
}
|
||||
} else if let group = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramGroup {
|
||||
if case .creator = group.role {
|
||||
canSetupAutoremoveTimeout = true
|
||||
} else if case let .admin(rights, _) = group.role {
|
||||
if rights.flags.contains(.canChangeInfo) {
|
||||
canSetupAutoremoveTimeout = true
|
||||
}
|
||||
} else if let defaultBannedRights = group.defaultBannedRights {
|
||||
if !defaultBannedRights.flags.contains(.banChangeInfo) {
|
||||
canSetupAutoremoveTimeout = true
|
||||
}
|
||||
}
|
||||
} else if let _ = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramUser {
|
||||
canSetupAutoremoveTimeout = true
|
||||
} else if let channel = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel {
|
||||
if channel.hasPermission(.changeInfo) {
|
||||
canSetupAutoremoveTimeout = true
|
||||
}
|
||||
}
|
||||
|
||||
if canSetupAutoremoveTimeout {
|
||||
if currentAutoremoveTimeout != nil || chatPresentationInterfaceState.renderedPeer?.peer is TelegramSecretChat {
|
||||
accessoryItems.append(.messageAutoremoveTimeout(currentAutoremoveTimeout))
|
||||
}
|
||||
}
|
||||
|
||||
@ -247,17 +275,18 @@ func inputTextPanelStateForChatPresentationInterfaceState(_ chatPresentationInte
|
||||
return ChatTextInputPanelState(accessoryItems: [], contextPlaceholder: contextPlaceholder, mediaRecordingState: chatPresentationInterfaceState.inputTextPanelState.mediaRecordingState)
|
||||
} else {
|
||||
var accessoryItems: [ChatTextInputAccessoryItem] = []
|
||||
if let peer = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramSecretChat {
|
||||
var extendedSearchLayout = false
|
||||
loop: for (_, result) in chatPresentationInterfaceState.inputQueryResults {
|
||||
if case let .contextRequestResult(peer, _) = result, peer != nil {
|
||||
extendedSearchLayout = true
|
||||
break loop
|
||||
}
|
||||
var extendedSearchLayout = false
|
||||
loop: for (_, result) in chatPresentationInterfaceState.inputQueryResults {
|
||||
if case let .contextRequestResult(peer, _) = result, peer != nil {
|
||||
extendedSearchLayout = true
|
||||
break loop
|
||||
}
|
||||
|
||||
if !extendedSearchLayout {
|
||||
}
|
||||
if !extendedSearchLayout {
|
||||
if let peer = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramSecretChat {
|
||||
accessoryItems.append(.messageAutoremoveTimeout(peer.messageAutoremoveTimeout))
|
||||
} else if currentAutoremoveTimeout != nil {
|
||||
accessoryItems.append(.messageAutoremoveTimeout(currentAutoremoveTimeout))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@ import UIKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import SyncCore
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import UIKit
|
||||
import SwiftSignalKit
|
||||
@ -15,6 +16,8 @@ import LegacyUI
|
||||
import AppBundle
|
||||
import SaveToCameraRoll
|
||||
import PresentationDataUtils
|
||||
import TelegramPresentationData
|
||||
import TelegramStringFormatting
|
||||
|
||||
private struct MessageContextMenuData {
|
||||
let starStatus: Bool?
|
||||
@ -890,6 +893,16 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
||||
}
|
||||
|
||||
if !isReplyThreadHead, (!data.messageActions.options.intersection([.deleteLocally, .deleteGlobally]).isEmpty || clearCacheAsDelete) && !isAction {
|
||||
var autoremoveDeadline: Int32?
|
||||
for attribute in message.attributes {
|
||||
if let attribute = attribute as? AutoremoveTimeoutMessageAttribute {
|
||||
if let countdownBeginTime = attribute.countdownBeginTime {
|
||||
autoremoveDeadline = countdownBeginTime + attribute.timeout
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
let title: String
|
||||
var isSending = false
|
||||
var isEditing = false
|
||||
@ -903,16 +916,27 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
||||
} else {
|
||||
title = chatPresentationInterfaceState.strings.Conversation_ContextMenuDelete
|
||||
}
|
||||
actions.append(.action(ContextMenuActionItem(text: title, textColor: .destructive, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: isSending ? "Chat/Context Menu/Clear" : "Chat/Context Menu/Delete"), color: theme.actionSheet.destructiveActionTextColor)
|
||||
}, action: { controller, f in
|
||||
if isEditing {
|
||||
context.account.pendingUpdateMessageManager.cancel(messageId: message.id)
|
||||
f(.default)
|
||||
} else {
|
||||
interfaceInteraction.deleteMessages(selectAll ? messages : [message], controller, f)
|
||||
}
|
||||
})))
|
||||
if let autoremoveDeadline = autoremoveDeadline, !isEditing, !isSending {
|
||||
actions.append(.custom(ChatDeleteMessageContextItem(timestamp: Double(autoremoveDeadline), action: { controller, f in
|
||||
if isEditing {
|
||||
context.account.pendingUpdateMessageManager.cancel(messageId: message.id)
|
||||
f(.default)
|
||||
} else {
|
||||
interfaceInteraction.deleteMessages(selectAll ? messages : [message], controller, f)
|
||||
}
|
||||
}), false))
|
||||
} else {
|
||||
actions.append(.action(ContextMenuActionItem(text: title, textColor: .destructive, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: isSending ? "Chat/Context Menu/Clear" : "Chat/Context Menu/Delete"), color: theme.actionSheet.destructiveActionTextColor)
|
||||
}, action: { controller, f in
|
||||
if isEditing {
|
||||
context.account.pendingUpdateMessageManager.cancel(messageId: message.id)
|
||||
f(.default)
|
||||
} else {
|
||||
interfaceInteraction.deleteMessages(selectAll ? messages : [message], controller, f)
|
||||
}
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
if !isPinnedMessages, !isReplyThreadHead, data.canSelect {
|
||||
@ -1204,3 +1228,229 @@ func chatAvailableMessageActionsImpl(postbox: Postbox, accountPeerId: PeerId, me
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class ChatDeleteMessageContextItem: ContextMenuCustomItem {
|
||||
fileprivate let timestamp: Double
|
||||
fileprivate let action: (ContextController, @escaping (ContextMenuActionResult) -> Void) -> Void
|
||||
|
||||
init(timestamp: Double, action: @escaping (ContextController, @escaping (ContextMenuActionResult) -> Void) -> Void) {
|
||||
self.timestamp = timestamp
|
||||
self.action = action
|
||||
}
|
||||
|
||||
func node(presentationData: PresentationData, getController: @escaping () -> ContextController?, actionSelected: @escaping (ContextMenuActionResult) -> Void) -> ContextMenuCustomNode {
|
||||
return ChatDeleteMessageContextItemNode(presentationData: presentationData, item: self, getController: getController, actionSelected: actionSelected)
|
||||
}
|
||||
}
|
||||
|
||||
private let textFont = Font.regular(17.0)
|
||||
|
||||
private final class ChatDeleteMessageContextItemNode: ASDisplayNode, ContextMenuCustomNode {
|
||||
private let item: ChatDeleteMessageContextItem
|
||||
private let presentationData: PresentationData
|
||||
private let getController: () -> ContextController?
|
||||
private let actionSelected: (ContextMenuActionResult) -> Void
|
||||
|
||||
private let backgroundNode: ASDisplayNode
|
||||
private let highlightedBackgroundNode: ASDisplayNode
|
||||
private let textNode: ImmediateTextNode
|
||||
private let statusNode: ImmediateTextNode
|
||||
private let iconNode: ASImageNode
|
||||
private let textIconNode: ASImageNode
|
||||
private let buttonNode: HighlightTrackingButtonNode
|
||||
|
||||
private var timer: SwiftSignalKit.Timer?
|
||||
|
||||
private var pointerInteraction: PointerInteraction?
|
||||
|
||||
init(presentationData: PresentationData, item: ChatDeleteMessageContextItem, getController: @escaping () -> ContextController?, actionSelected: @escaping (ContextMenuActionResult) -> Void) {
|
||||
self.item = item
|
||||
self.presentationData = presentationData
|
||||
self.getController = getController
|
||||
self.actionSelected = actionSelected
|
||||
|
||||
let textFont = Font.regular(presentationData.listsFontSize.baseDisplaySize)
|
||||
let subtextFont = Font.regular(presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0)
|
||||
|
||||
self.backgroundNode = ASDisplayNode()
|
||||
self.backgroundNode.isAccessibilityElement = false
|
||||
self.backgroundNode.backgroundColor = presentationData.theme.contextMenu.itemBackgroundColor
|
||||
self.highlightedBackgroundNode = ASDisplayNode()
|
||||
self.highlightedBackgroundNode.isAccessibilityElement = false
|
||||
self.highlightedBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor
|
||||
self.highlightedBackgroundNode.alpha = 0.0
|
||||
|
||||
self.textNode = ImmediateTextNode()
|
||||
self.textNode.isAccessibilityElement = false
|
||||
self.textNode.isUserInteractionEnabled = false
|
||||
self.textNode.displaysAsynchronously = false
|
||||
self.textNode.attributedText = NSAttributedString(string: presentationData.strings.Conversation_ContextMenuDelete, font: textFont, textColor: presentationData.theme.contextMenu.destructiveColor)
|
||||
|
||||
self.textNode.maximumNumberOfLines = 1
|
||||
let statusNode = ImmediateTextNode()
|
||||
statusNode.isAccessibilityElement = false
|
||||
statusNode.isUserInteractionEnabled = false
|
||||
statusNode.displaysAsynchronously = false
|
||||
statusNode.attributedText = NSAttributedString(string: stringForRemainingTime(Int32(max(0.0, self.item.timestamp - Date().timeIntervalSince1970)), strings: presentationData.strings), font: subtextFont, textColor: presentationData.theme.contextMenu.destructiveColor)
|
||||
statusNode.maximumNumberOfLines = 1
|
||||
self.statusNode = statusNode
|
||||
|
||||
self.buttonNode = HighlightTrackingButtonNode()
|
||||
self.buttonNode.isAccessibilityElement = true
|
||||
self.buttonNode.accessibilityLabel = presentationData.strings.VoiceChat_StopRecording
|
||||
|
||||
self.iconNode = ASImageNode()
|
||||
self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: presentationData.theme.actionSheet.destructiveActionTextColor)
|
||||
|
||||
self.textIconNode = ASImageNode()
|
||||
self.textIconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/SelfExpiring"), color: presentationData.theme.actionSheet.destructiveActionTextColor)
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.backgroundNode)
|
||||
self.addSubnode(self.highlightedBackgroundNode)
|
||||
self.addSubnode(self.textNode)
|
||||
self.addSubnode(self.statusNode)
|
||||
self.addSubnode(self.iconNode)
|
||||
self.addSubnode(self.textIconNode)
|
||||
self.addSubnode(self.buttonNode)
|
||||
|
||||
self.buttonNode.highligthedChanged = { [weak self] highligted in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if highligted {
|
||||
strongSelf.highlightedBackgroundNode.alpha = 1.0
|
||||
} else {
|
||||
strongSelf.highlightedBackgroundNode.alpha = 0.0
|
||||
strongSelf.highlightedBackgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3)
|
||||
}
|
||||
}
|
||||
self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.timer?.invalidate()
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
self.pointerInteraction = PointerInteraction(node: self.buttonNode, style: .hover, willEnter: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.highlightedBackgroundNode.alpha = 0.75
|
||||
}
|
||||
}, willExit: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.highlightedBackgroundNode.alpha = 0.0
|
||||
}
|
||||
})
|
||||
|
||||
let timer = SwiftSignalKit.Timer(timeout: 0.5, repeat: true, completion: { [weak self] in
|
||||
self?.updateTime(transition: .immediate)
|
||||
}, queue: Queue.mainQueue())
|
||||
self.timer = timer
|
||||
timer.start()
|
||||
}
|
||||
|
||||
private var validLayout: CGSize?
|
||||
func updateTime(transition: ContainedViewLayoutTransition) {
|
||||
guard let size = self.validLayout else {
|
||||
return
|
||||
}
|
||||
|
||||
let subtextFont = Font.regular(self.presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0)
|
||||
self.statusNode.attributedText = NSAttributedString(string: stringForRemainingTime(Int32(max(0.0, self.item.timestamp - Date().timeIntervalSince1970)), strings: presentationData.strings), font: subtextFont, textColor: presentationData.theme.contextMenu.destructiveColor)
|
||||
|
||||
let sideInset: CGFloat = 16.0
|
||||
let statusSize = self.statusNode.updateLayout(CGSize(width: size.width - sideInset - 32.0, height: .greatestFiniteMagnitude))
|
||||
transition.updateFrameAdditive(node: self.statusNode, frame: CGRect(origin: CGPoint(x: self.statusNode.frame.minX, y: self.statusNode.frame.minY), size: statusSize))
|
||||
}
|
||||
|
||||
func updateLayout(constrainedWidth: CGFloat) -> (CGSize, (CGSize, ContainedViewLayoutTransition) -> Void) {
|
||||
let sideInset: CGFloat = 16.0
|
||||
let iconSideInset: CGFloat = 12.0
|
||||
let verticalInset: CGFloat = 12.0
|
||||
|
||||
let iconSize: CGSize = self.iconNode.image?.size ?? CGSize(width: 10.0, height: 10.0)
|
||||
let textIconSize: CGSize = self.textIconNode.image?.size ?? CGSize(width: 2.0, height: 2.0)
|
||||
|
||||
let standardIconWidth: CGFloat = 32.0
|
||||
var rightTextInset: CGFloat = sideInset
|
||||
if !iconSize.width.isZero {
|
||||
rightTextInset = max(iconSize.width, standardIconWidth) + iconSideInset + sideInset
|
||||
}
|
||||
|
||||
let textSize = self.textNode.updateLayout(CGSize(width: constrainedWidth - sideInset - rightTextInset, height: .greatestFiniteMagnitude))
|
||||
let statusSize = self.statusNode.updateLayout(CGSize(width: constrainedWidth - sideInset - rightTextInset - textIconSize.width - 2.0, height: .greatestFiniteMagnitude))
|
||||
|
||||
let verticalSpacing: CGFloat = 2.0
|
||||
let combinedTextHeight = textSize.height + verticalSpacing + statusSize.height
|
||||
return (CGSize(width: max(textSize.width, statusSize.width) + sideInset + rightTextInset, height: verticalInset * 2.0 + combinedTextHeight), { size, transition in
|
||||
self.validLayout = size
|
||||
let verticalOrigin = floor((size.height - combinedTextHeight) / 2.0)
|
||||
let textFrame = CGRect(origin: CGPoint(x: sideInset, y: verticalOrigin), size: textSize)
|
||||
transition.updateFrameAdditive(node: self.textNode, frame: textFrame)
|
||||
|
||||
transition.updateFrame(node: self.textIconNode, frame: CGRect(origin: CGPoint(x: sideInset, y: verticalOrigin + verticalSpacing + textSize.height + floorToScreenPixels((statusSize.height - textIconSize.height) / 2.0) + 1.0), size: textIconSize))
|
||||
transition.updateFrameAdditive(node: self.statusNode, frame: CGRect(origin: CGPoint(x: sideInset + textIconSize.width + 2.0, y: verticalOrigin + verticalSpacing + textSize.height), size: statusSize))
|
||||
|
||||
if !iconSize.width.isZero {
|
||||
transition.updateFrameAdditive(node: self.iconNode, frame: CGRect(origin: CGPoint(x: size.width - standardIconWidth - iconSideInset + floor((standardIconWidth - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize))
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)))
|
||||
transition.updateFrame(node: self.highlightedBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)))
|
||||
transition.updateFrame(node: self.buttonNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)))
|
||||
})
|
||||
}
|
||||
|
||||
func updateTheme(presentationData: PresentationData) {
|
||||
self.backgroundNode.backgroundColor = presentationData.theme.contextMenu.itemBackgroundColor
|
||||
self.highlightedBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor
|
||||
|
||||
let textFont = Font.regular(presentationData.listsFontSize.baseDisplaySize)
|
||||
let subtextFont = Font.regular(presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0)
|
||||
|
||||
self.textNode.attributedText = NSAttributedString(string: self.textNode.attributedText?.string ?? "", font: textFont, textColor: presentationData.theme.contextMenu.primaryColor)
|
||||
self.statusNode.attributedText = NSAttributedString(string: self.statusNode.attributedText?.string ?? "", font: subtextFont, textColor: presentationData.theme.contextMenu.secondaryColor)
|
||||
}
|
||||
|
||||
@objc private func buttonPressed() {
|
||||
self.performAction()
|
||||
}
|
||||
|
||||
func performAction() {
|
||||
guard let controller = self.getController() else {
|
||||
return
|
||||
}
|
||||
self.item.action(controller, { [weak self] result in
|
||||
self?.actionSelected(result)
|
||||
})
|
||||
}
|
||||
|
||||
func setIsHighlighted(_ value: Bool) {
|
||||
if value {
|
||||
self.highlightedBackgroundNode.alpha = 1.0
|
||||
} else {
|
||||
self.highlightedBackgroundNode.alpha = 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func stringForRemainingTime(_ duration: Int32, strings: PresentationStrings) -> String {
|
||||
let days = duration / (3600 * 24)
|
||||
let hours = duration / 3600
|
||||
let minutes = duration / 60 % 60
|
||||
let seconds = duration % 60
|
||||
let durationString: String
|
||||
if days > 0 {
|
||||
let roundDays = round(Double(duration) / (3600.0 * 24.0))
|
||||
return strings.Conversation_AutoremoveRemainingDays(Int32(roundDays))
|
||||
} else if hours > 0 {
|
||||
durationString = String(format: "%d:%02d:%02d", hours, minutes, seconds)
|
||||
} else {
|
||||
durationString = String(format: "%d:%02d", minutes, seconds)
|
||||
}
|
||||
return strings.Conversation_AutoremoveRemainingTime(durationString).0
|
||||
}
|
||||
|
@ -48,6 +48,16 @@ func leftNavigationButtonForChatInterfaceState(_ presentationInterfaceState: Cha
|
||||
canClear = true
|
||||
} else if let peer = peer as? TelegramChannel, case .group = peer.info, peer.addressName == nil && presentationInterfaceState.peerGeoLocation == nil {
|
||||
canClear = true
|
||||
} else if let peer = peer as? TelegramChannel {
|
||||
if case .broadcast = peer.info {
|
||||
//TODO:localize
|
||||
title = "Clear Channel"
|
||||
}
|
||||
if peer.hasPermission(.changeInfo) {
|
||||
canClear = true
|
||||
} else {
|
||||
canClear = false
|
||||
}
|
||||
} else {
|
||||
canClear = false
|
||||
}
|
||||
|
@ -773,7 +773,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
isReplyThread = true
|
||||
}
|
||||
|
||||
let (dateAndStatusSize, dateAndStatusApply) = makeDateAndStatusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), dateReactions, dateReplies, item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread)
|
||||
let (dateAndStatusSize, dateAndStatusApply) = makeDateAndStatusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), dateReactions, dateReplies, item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread, item.message.isSelfExpiring)
|
||||
|
||||
var viaBotApply: (TextNodeLayout, () -> TextNode)?
|
||||
var replyInfoApply: (CGSize, () -> ChatMessageReplyInfoNode)?
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user