mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge branch 'master' into webrtc-m122
This commit is contained in:
commit
e010513747
@ -705,6 +705,8 @@ private final class NotificationServiceHandler {
|
||||
let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil)
|
||||
|
||||
setupSharedLogger(rootPath: logsPath, path: logsPath)
|
||||
|
||||
Logger.shared.log("NotificationService \(episode)", "Started handling notification")
|
||||
|
||||
initializeAccountManagement()
|
||||
|
||||
|
@ -11347,6 +11347,9 @@ Sorry for the inconvenience.";
|
||||
"Premium.Business.Away.Title" = "Away Messages";
|
||||
"Premium.Business.Away.Text" = "Define messages that are automatically sent when you are off.";
|
||||
|
||||
"Premium.Business.Intro.Title" = "Intro";
|
||||
"Premium.Business.Intro.Text" = "Customize the message people see before they start a chat with you.";
|
||||
|
||||
"Premium.Business.Chatbots.Title" = "Chatbots";
|
||||
"Premium.Business.Chatbots.Text" = "Add any third party chatbots that will process customer interactions.";
|
||||
|
||||
@ -11360,6 +11363,7 @@ Sorry for the inconvenience.";
|
||||
"Business.GreetingMessages" = "Greeting Messages";
|
||||
"Business.AwayMessages" = "Away Messages";
|
||||
"Business.Chatbots" = "Chatbots";
|
||||
"Business.Intro" = "Intro";
|
||||
|
||||
"Business.LocationInfo" = "Display the location of your business on your account.";
|
||||
"Business.OpeningHoursInfo" = "Show to your customers when you are open for business.";
|
||||
@ -11367,6 +11371,7 @@ Sorry for the inconvenience.";
|
||||
"Business.GreetingMessagesInfo" = "Create greetings that will be automatically sent to new customers.";
|
||||
"Business.AwayMessagesInfo" = "Define messages that are automatically sent when you are off.";
|
||||
"Business.ChatbotsInfo" = "Add any third-party chatbots that will process customer interactions.";
|
||||
"Business.IntroInfo" = "Customize the message people see before they start a chat with you.";
|
||||
|
||||
"Business.MoreFeaturesTitle" = "MORE BUSINESS FEATURES";
|
||||
"Business.MoreFeaturesInfo" = "Check this section later for new business features.";
|
||||
@ -11594,3 +11599,20 @@ Sorry for the inconvenience.";
|
||||
|
||||
"Chat.QuickReplyMediaMessageLimitReachedText_1" = "There can be at most %d message in this chat.";
|
||||
"Chat.QuickReplyMediaMessageLimitReachedText_any" = "There can be at most %d messages in this chat.";
|
||||
|
||||
"CollectibleItemInfo.StoreName" = "Fragment";
|
||||
"CollectibleItemInfo.UsernameTitle" = "%@ is a collectible username that belongs to";
|
||||
"CollectibleItemInfo.UsernameText" = "The %1$@ username was acquired on %2$@ on %3$@ for %4$@ (%5$@).";
|
||||
"CollectibleItemInfo.PhoneTitle" = "%@ is a collectible phone number that belongs to";
|
||||
"CollectibleItemInfo.PhoneText" = "The %1$@ phone number was acquired on %2$@ on %3$@ for %4$@ (%5$@).";
|
||||
"CollectibleItemInfo.ButtonOpenInfo" = "Learn More";
|
||||
"CollectibleItemInfo.ButtonCopyUsername" = "Copy Link";
|
||||
"CollectibleItemInfo.ButtonCopyPhone" = "Copy Phone Number";
|
||||
"CollectibleItemInfo.ShareInlineText.LearnMore" = "Learn more >";
|
||||
|
||||
"Stickers.Edit" = "EDIT";
|
||||
|
||||
"ChannelBoost.Table.NoAds" = "Switch Off Ads";
|
||||
|
||||
"ChannelBoost.NoAds" = "Switch Off Ads";
|
||||
"ChannelBoost.EnableNoAdsLevelText" = "Your channel needs **Level %1$@** to switch off ads.";
|
||||
|
@ -729,9 +729,22 @@ struct WidgetView: View {
|
||||
chatUpdateView(size: geometry.size)
|
||||
})
|
||||
})
|
||||
.background(Rectangle().foregroundColor(getBackgroundColor()))
|
||||
.padding(0.0)
|
||||
.unredacted()
|
||||
.widgetBackground(Rectangle().foregroundColor(getBackgroundColor()))
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOSApplicationExtension 14.0, iOS 14.0, *)
|
||||
extension View {
|
||||
func widgetBackground(_ backgroundView: some View) -> some View {
|
||||
if #available(iOSApplicationExtension 17.0, iOS 17.0, *) {
|
||||
return containerBackground(for: .widget) {
|
||||
backgroundView
|
||||
}
|
||||
} else {
|
||||
return background(backgroundView)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -766,6 +779,17 @@ struct AvatarsWidgetView: View {
|
||||
}
|
||||
}
|
||||
|
||||
func getBackgroundColor() -> Color {
|
||||
switch colorScheme {
|
||||
case .light:
|
||||
return .white
|
||||
case .dark:
|
||||
return Color(.sRGB, red: 28.0 / 255.0, green: 28.0 / 255.0, blue: 30.0 / 255.0, opacity: 1.0)
|
||||
@unknown default:
|
||||
return .secondary
|
||||
}
|
||||
}
|
||||
|
||||
func itemView(index: Int) -> some View {
|
||||
let peers: ParsedPeers?
|
||||
var isPlaceholder = false
|
||||
@ -821,6 +845,7 @@ struct AvatarsWidgetView: View {
|
||||
})
|
||||
.padding(EdgeInsets(top: 10.0, leading: 10.0, bottom: 10.0, trailing: 10.0))
|
||||
.unredacted()
|
||||
.widgetBackground(Rectangle().foregroundColor(getBackgroundColor()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -854,12 +879,22 @@ struct Static_Widget: Widget {
|
||||
public var body: some WidgetConfiguration {
|
||||
let presentationData = WidgetPresentationData.getForExtension()
|
||||
|
||||
return IntentConfiguration(kind: kind, intent: SelectFriendsIntent.self, provider: Provider(), content: { entry in
|
||||
WidgetView(data: getWidgetData(contents: entry.contents), presentationData: presentationData)
|
||||
})
|
||||
.supportedFamilies([.systemMedium])
|
||||
.configurationDisplayName(presentationData.widgetChatsGalleryTitle)
|
||||
.description(presentationData.widgetChatsGalleryDescription)
|
||||
if #available(iOSApplicationExtension 15.0, iOS 15.0, *) {
|
||||
return IntentConfiguration(kind: kind, intent: SelectFriendsIntent.self, provider: Provider(), content: { entry in
|
||||
WidgetView(data: getWidgetData(contents: entry.contents), presentationData: presentationData)
|
||||
})
|
||||
.supportedFamilies([.systemMedium])
|
||||
.configurationDisplayName(presentationData.widgetChatsGalleryTitle)
|
||||
.contentMarginsDisabled()
|
||||
.description(presentationData.widgetChatsGalleryDescription)
|
||||
} else {
|
||||
return IntentConfiguration(kind: kind, intent: SelectFriendsIntent.self, provider: Provider(), content: { entry in
|
||||
WidgetView(data: getWidgetData(contents: entry.contents), presentationData: presentationData)
|
||||
})
|
||||
.supportedFamilies([.systemMedium])
|
||||
.configurationDisplayName(presentationData.widgetChatsGalleryTitle)
|
||||
.description(presentationData.widgetChatsGalleryDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -870,12 +905,22 @@ struct Static_AvatarsWidget: Widget {
|
||||
public var body: some WidgetConfiguration {
|
||||
let presentationData = WidgetPresentationData.getForExtension()
|
||||
|
||||
return IntentConfiguration(kind: kind, intent: SelectAvatarFriendsIntent.self, provider: AvatarsProvider(), content: { entry in
|
||||
AvatarsWidgetView(data: getWidgetData(contents: entry.contents), presentationData: presentationData)
|
||||
})
|
||||
.supportedFamilies([.systemMedium])
|
||||
.configurationDisplayName(presentationData.widgetShortcutsGalleryTitle)
|
||||
.description(presentationData.widgetShortcutsGalleryDescription)
|
||||
if #available(iOSApplicationExtension 15.0, iOS 15.0, *) {
|
||||
return IntentConfiguration(kind: kind, intent: SelectAvatarFriendsIntent.self, provider: AvatarsProvider(), content: { entry in
|
||||
AvatarsWidgetView(data: getWidgetData(contents: entry.contents), presentationData: presentationData)
|
||||
})
|
||||
.supportedFamilies([.systemMedium])
|
||||
.configurationDisplayName(presentationData.widgetShortcutsGalleryTitle)
|
||||
.contentMarginsDisabled()
|
||||
.description(presentationData.widgetShortcutsGalleryDescription)
|
||||
} else {
|
||||
return IntentConfiguration(kind: kind, intent: SelectAvatarFriendsIntent.self, provider: AvatarsProvider(), content: { entry in
|
||||
AvatarsWidgetView(data: getWidgetData(contents: entry.contents), presentationData: presentationData)
|
||||
})
|
||||
.supportedFamilies([.systemMedium])
|
||||
.configurationDisplayName(presentationData.widgetShortcutsGalleryTitle)
|
||||
.description(presentationData.widgetShortcutsGalleryDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,7 +101,7 @@ public enum TextLinkItemActionType {
|
||||
case longTap
|
||||
}
|
||||
|
||||
public enum TextLinkItem {
|
||||
public enum TextLinkItem: Equatable {
|
||||
case url(url: String, concealed: Bool)
|
||||
case mention(String)
|
||||
case hashtag(String?, String)
|
||||
@ -856,6 +856,15 @@ public protocol AutomaticBusinessMessageSetupScreenInitialData: AnyObject {
|
||||
public protocol ChatbotSetupScreenInitialData: AnyObject {
|
||||
}
|
||||
|
||||
public protocol CollectibleItemInfoScreenInitialData: AnyObject {
|
||||
var collectibleItemInfo: TelegramCollectibleItemInfo { get }
|
||||
}
|
||||
|
||||
public enum CollectibleItemInfoScreenSubject {
|
||||
case phoneNumber(String)
|
||||
case username(String)
|
||||
}
|
||||
|
||||
public protocol SharedAccountContext: AnyObject {
|
||||
var sharedContainerPath: String { get }
|
||||
var basePath: String { get }
|
||||
@ -951,6 +960,8 @@ public protocol SharedAccountContext: AnyObject {
|
||||
func makeAutomaticBusinessMessageSetupScreenInitialData(context: AccountContext) -> Signal<AutomaticBusinessMessageSetupScreenInitialData, NoError>
|
||||
func makeQuickReplySetupScreen(context: AccountContext, initialData: QuickReplySetupScreenInitialData) -> ViewController
|
||||
func makeQuickReplySetupScreenInitialData(context: AccountContext) -> Signal<QuickReplySetupScreenInitialData, NoError>
|
||||
func makeCollectibleItemInfoScreen(context: AccountContext, initialData: CollectibleItemInfoScreenInitialData) -> ViewController
|
||||
func makeCollectibleItemInfoScreenInitialData(context: AccountContext, peerId: EnginePeer.Id, subject: CollectibleItemInfoScreenSubject) -> Signal<CollectibleItemInfoScreenInitialData?, NoError>
|
||||
func navigateToChatController(_ params: NavigateToChatControllerParams)
|
||||
func navigateToForumChannel(context: AccountContext, peerId: EnginePeer.Id, navigationController: NavigationController)
|
||||
func navigateToForumThread(context: AccountContext, peerId: EnginePeer.Id, threadId: Int64, messageId: EngineMessage.Id?, navigationController: NavigationController, activateInput: ChatControllerActivateInput?, keepStack: NavigateToChatKeepStack) -> Signal<Never, NoError>
|
||||
@ -980,14 +991,19 @@ public protocol SharedAccountContext: AnyObject {
|
||||
func makePremiumLimitController(context: AccountContext, subject: PremiumLimitSubject, count: Int32, forceDark: Bool, cancel: @escaping () -> Void, action: @escaping () -> Bool) -> ViewController
|
||||
func makePremiumGiftController(context: AccountContext, source: PremiumGiftSource, completion: (() -> Void)?) -> ViewController
|
||||
func makePremiumPrivacyControllerController(context: AccountContext, subject: PremiumPrivacySubject, peerId: EnginePeer.Id) -> ViewController
|
||||
func makePremiumBoostLevelsController(context: AccountContext, peerId: EnginePeer.Id, boostStatus: ChannelBoostStatus, myBoostStatus: MyBoostStatus, forceDark: Bool, openStats: (() -> Void)?) -> ViewController
|
||||
func makePremiumBoostLevelsController(context: AccountContext, peerId: EnginePeer.Id, subject: BoostSubject, boostStatus: ChannelBoostStatus, myBoostStatus: MyBoostStatus, forceDark: Bool, openStats: (() -> Void)?) -> ViewController
|
||||
|
||||
func makeStickerPackScreen(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, mainStickerPack: StickerPackReference, stickerPacks: [StickerPackReference], loadedStickerPacks: [LoadedStickerPack], parentNavigationController: NavigationController?, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?) -> ViewController
|
||||
func makeStickerPackScreen(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, mainStickerPack: StickerPackReference, stickerPacks: [StickerPackReference], loadedStickerPacks: [LoadedStickerPack], isEditing: Bool, parentNavigationController: NavigationController?, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?) -> ViewController
|
||||
|
||||
func makeMediaPickerScreen(context: AccountContext, hasSearch: Bool, completion: @escaping (Any) -> Void) -> ViewController
|
||||
|
||||
func makeStickerEditorScreen(context: AccountContext, source: Any, transitionArguments: (UIView, CGRect, UIImage?)?, completion: @escaping (TelegramMediaFile) -> Void) -> ViewController
|
||||
|
||||
func makeStickerMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect, completion: @escaping (Any, UIView, CGRect, UIImage?, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void) -> ViewController
|
||||
func makeStoryMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect, completion: @escaping (Any, UIView, CGRect, UIImage?, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void, groupsPresented: @escaping () -> Void) -> ViewController
|
||||
|
||||
func makeStickerPickerScreen(context: AccountContext, inputData: Promise<StickerPickerInput>, completion: @escaping (TelegramMediaFile) -> Void) -> ViewController
|
||||
|
||||
func makeProxySettingsController(sharedContext: SharedAccountContext, account: UnauthorizedAccount) -> ViewController
|
||||
|
||||
func makeInstalledStickerPacksController(context: AccountContext, mode: InstalledStickerPacksControllerMode, forceTheme: PresentationTheme?) -> ViewController
|
||||
|
@ -35,3 +35,11 @@ public final class GalleryControllerActionInteraction {
|
||||
self.updateCanReadHistory = updateCanReadHistory
|
||||
}
|
||||
}
|
||||
|
||||
public protocol StickerPackScreen {
|
||||
|
||||
}
|
||||
|
||||
public protocol StickerPickerInput {
|
||||
|
||||
}
|
||||
|
@ -101,6 +101,21 @@ public enum PremiumPrivacySubject {
|
||||
case readTime
|
||||
}
|
||||
|
||||
public enum BoostSubject: Equatable {
|
||||
case stories
|
||||
case channelReactions(reactionCount: Int32)
|
||||
case nameColors(colors: PeerNameColor)
|
||||
case nameIcon
|
||||
case profileColors(colors: PeerNameColor)
|
||||
case profileIcon
|
||||
case emojiStatus
|
||||
case wallpaper
|
||||
case customWallpaper
|
||||
case audioTranscription
|
||||
case emojiPack
|
||||
case noAds
|
||||
}
|
||||
|
||||
public struct PremiumConfiguration {
|
||||
public static var defaultValue: PremiumConfiguration {
|
||||
return PremiumConfiguration(
|
||||
|
@ -231,17 +231,52 @@ public enum ChatRecordedMediaPreview: Equatable {
|
||||
case video(Video)
|
||||
}
|
||||
|
||||
public final class ChatManagingBot: Equatable {
|
||||
public let bot: EnginePeer
|
||||
public let isPaused: Bool
|
||||
public let canReply: Bool
|
||||
public let settingsUrl: String?
|
||||
|
||||
public init(bot: EnginePeer, isPaused: Bool, canReply: Bool, settingsUrl: String?) {
|
||||
self.bot = bot
|
||||
self.isPaused = isPaused
|
||||
self.canReply = canReply
|
||||
self.settingsUrl = settingsUrl
|
||||
}
|
||||
|
||||
public static func ==(lhs: ChatManagingBot, rhs: ChatManagingBot) -> Bool {
|
||||
if lhs === rhs {
|
||||
return true
|
||||
}
|
||||
if lhs.bot != rhs.bot {
|
||||
return false
|
||||
}
|
||||
if lhs.isPaused != rhs.isPaused {
|
||||
return false
|
||||
}
|
||||
if lhs.canReply != rhs.canReply {
|
||||
return false
|
||||
}
|
||||
if lhs.settingsUrl != rhs.settingsUrl {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
public struct ChatContactStatus: Equatable {
|
||||
public var canAddContact: Bool
|
||||
public var canReportIrrelevantLocation: Bool
|
||||
public var peerStatusSettings: PeerStatusSettings?
|
||||
public var invitedBy: Peer?
|
||||
public var managingBot: ChatManagingBot?
|
||||
|
||||
public init(canAddContact: Bool, canReportIrrelevantLocation: Bool, peerStatusSettings: PeerStatusSettings?, invitedBy: Peer?) {
|
||||
public init(canAddContact: Bool, canReportIrrelevantLocation: Bool, peerStatusSettings: PeerStatusSettings?, invitedBy: Peer?, managingBot: ChatManagingBot?) {
|
||||
self.canAddContact = canAddContact
|
||||
self.canReportIrrelevantLocation = canReportIrrelevantLocation
|
||||
self.peerStatusSettings = peerStatusSettings
|
||||
self.invitedBy = invitedBy
|
||||
self.managingBot = managingBot
|
||||
}
|
||||
|
||||
public var isEmpty: Bool {
|
||||
@ -270,6 +305,9 @@ public struct ChatContactStatus: Equatable {
|
||||
if !arePeersEqual(lhs.invitedBy, rhs.invitedBy) {
|
||||
return false
|
||||
}
|
||||
if lhs.managingBot != rhs.managingBot {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -45,9 +45,11 @@ public final class PeekController: ViewController, ContextControllerProtocol {
|
||||
}
|
||||
|
||||
public func pushItems(items: Signal<ContextController.Items, NoError>) {
|
||||
self.controllerNode.pushItems(items: items)
|
||||
}
|
||||
|
||||
public func popItems() {
|
||||
self.controllerNode.popItems()
|
||||
}
|
||||
|
||||
private var controllerNode: PeekControllerNode {
|
||||
@ -61,6 +63,7 @@ public final class PeekController: ViewController, ContextControllerProtocol {
|
||||
private let presentationData: PresentationData
|
||||
private let content: PeekControllerContent
|
||||
var sourceView: () -> (UIView, CGRect)?
|
||||
private let activateImmediately: Bool
|
||||
|
||||
public var visibilityUpdated: ((Bool) -> Void)?
|
||||
|
||||
@ -73,10 +76,11 @@ public final class PeekController: ViewController, ContextControllerProtocol {
|
||||
return self._ready
|
||||
}
|
||||
|
||||
public init(presentationData: PresentationData, content: PeekControllerContent, sourceView: @escaping () -> (UIView, CGRect)?) {
|
||||
public init(presentationData: PresentationData, content: PeekControllerContent, sourceView: @escaping () -> (UIView, CGRect)?, activateImmediately: Bool = false) {
|
||||
self.presentationData = presentationData
|
||||
self.content = content
|
||||
self.sourceView = sourceView
|
||||
self.activateImmediately = activateImmediately
|
||||
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
|
||||
@ -111,6 +115,10 @@ public final class PeekController: ViewController, ContextControllerProtocol {
|
||||
self.controllerNode.animateIn(from: self.getSourceRect())
|
||||
|
||||
self.visibilityUpdated?(true)
|
||||
|
||||
if self.activateImmediately {
|
||||
self.controllerNode.activateMenu()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@ public final class PeekControllerGestureRecognizer: UIPanGestureRecognizer {
|
||||
private let present: (PeekControllerContent, UIView, CGRect) -> ViewController?
|
||||
private let updateContent: (PeekControllerContent?) -> Void
|
||||
private let activateBySingleTap: Bool
|
||||
public var longPressEnabled = true
|
||||
public var checkSingleTapActivationAtPoint: ((CGPoint) -> Bool)?
|
||||
|
||||
private var tapLocation: CGPoint?
|
||||
@ -54,6 +55,9 @@ public final class PeekControllerGestureRecognizer: UIPanGestureRecognizer {
|
||||
}
|
||||
|
||||
private func startLongTapTimer() {
|
||||
guard self.longPressEnabled else {
|
||||
return
|
||||
}
|
||||
self.longTapTimer?.invalidate()
|
||||
let longTapTimer = SwiftSignalKit.Timer(timeout: 0.4, repeat: false, completion: { [weak self] in
|
||||
self?.longTapTimerFired()
|
||||
|
@ -2,6 +2,7 @@ import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import SwiftSignalKit
|
||||
import TelegramPresentationData
|
||||
|
||||
private let animationDurationFactor: Double = 1.0
|
||||
@ -28,11 +29,11 @@ final class PeekControllerNode: ViewControllerTracingNode {
|
||||
|
||||
private var topAccessoryNode: ASDisplayNode?
|
||||
private var fullScreenAccessoryNode: (PeekControllerAccessoryNode & ASDisplayNode)?
|
||||
|
||||
private var actionsContainerNode: ContextActionsContainerNode
|
||||
|
||||
private var actionsStackNode: ContextControllerActionsStackNode
|
||||
|
||||
private var hapticFeedback = HapticFeedback()
|
||||
|
||||
|
||||
private var initialContinueGesturePoint: CGPoint?
|
||||
private var didMoveFromInitialGesturePoint = false
|
||||
private var highlightedActionNode: ContextActionNodeProtocol?
|
||||
@ -55,12 +56,12 @@ final class PeekControllerNode: ViewControllerTracingNode {
|
||||
self.darkDimNode.isUserInteractionEnabled = false
|
||||
|
||||
switch content.menuActivation() {
|
||||
case .drag:
|
||||
self.dimNode.backgroundColor = nil
|
||||
self.blurView.alpha = 1.0
|
||||
case .press:
|
||||
self.dimNode.backgroundColor = UIColor(white: self.theme.isDark ? 0.0 : 1.0, alpha: 0.5)
|
||||
self.blurView.alpha = 0.0
|
||||
case .drag:
|
||||
self.dimNode.backgroundColor = nil
|
||||
self.blurView.alpha = 1.0
|
||||
case .press:
|
||||
self.dimNode.backgroundColor = UIColor(white: self.theme.isDark ? 0.0 : 1.0, alpha: 0.5)
|
||||
self.blurView.alpha = 0.0
|
||||
}
|
||||
|
||||
self.containerBackgroundNode = ASImageNode()
|
||||
@ -75,28 +76,50 @@ final class PeekControllerNode: ViewControllerTracingNode {
|
||||
self.fullScreenAccessoryNode = content.fullScreenAccessoryNode(blurView: blurView)
|
||||
self.fullScreenAccessoryNode?.alpha = 0.0
|
||||
|
||||
var feedbackTapImpl: (() -> Void)?
|
||||
var activatedActionImpl: (() -> Void)?
|
||||
var requestLayoutImpl: (() -> Void)?
|
||||
self.actionsContainerNode = ContextActionsContainerNode(presentationData: presentationData, items: ContextController.Items(content: .list(content.menuItems()), animationCache: nil), getController: { [weak controller] in
|
||||
return controller
|
||||
}, actionSelected: { result in
|
||||
activatedActionImpl?()
|
||||
}, requestLayout: {
|
||||
requestLayoutImpl?()
|
||||
}, feedbackTap: {
|
||||
feedbackTapImpl?()
|
||||
}, blurBackground: true)
|
||||
self.actionsContainerNode.alpha = 0.0
|
||||
var requestLayoutImpl: ((ContainedViewLayoutTransition) -> Void)?
|
||||
|
||||
super.init()
|
||||
self.actionsStackNode = ContextControllerActionsStackNode(
|
||||
getController: { [weak controller] in
|
||||
return controller
|
||||
},
|
||||
requestDismiss: { result in
|
||||
activatedActionImpl?()
|
||||
},
|
||||
requestUpdate: { transition in
|
||||
requestLayoutImpl?(transition)
|
||||
}
|
||||
)
|
||||
self.actionsStackNode.alpha = 0.0
|
||||
|
||||
feedbackTapImpl = { [weak self] in
|
||||
self?.hapticFeedback.tap()
|
||||
let items = ContextController.Items(
|
||||
id: 0,
|
||||
content: .list(content.menuItems()),
|
||||
context: nil,
|
||||
reactionItems: [],
|
||||
selectedReactionItems: Set(),
|
||||
reactionsTitle: nil,
|
||||
reactionsLocked: false,
|
||||
animationCache: nil,
|
||||
alwaysAllowPremiumReactions: false,
|
||||
allPresetReactionsAreAvailable: false,
|
||||
getEmojiContent: nil,
|
||||
disablePositionLock: false,
|
||||
tip: nil,
|
||||
tipSignal: nil,
|
||||
dismissed: nil
|
||||
)
|
||||
if let item = makeContextControllerActionsStackItem(items: items).first {
|
||||
self.actionsStackNode.replace(
|
||||
item: item,
|
||||
animated: false
|
||||
)
|
||||
}
|
||||
|
||||
requestLayoutImpl = { [weak self] in
|
||||
self?.updateLayout()
|
||||
|
||||
super.init()
|
||||
|
||||
requestLayoutImpl = { [weak self] transition in
|
||||
self?.updateLayout(transition: transition)
|
||||
}
|
||||
|
||||
if content.presentation() == .freeform {
|
||||
@ -112,7 +135,7 @@ final class PeekControllerNode: ViewControllerTracingNode {
|
||||
self.containerNode.addSubnode(self.contentNode)
|
||||
|
||||
self.addSubnode(self.containerNode)
|
||||
self.addSubnode(self.actionsContainerNode)
|
||||
self.addSubnode(self.actionsStackNode)
|
||||
|
||||
if let fullScreenAccessoryNode = self.fullScreenAccessoryNode {
|
||||
self.fullScreenAccessoryNode?.dismiss = { [weak self] in
|
||||
@ -139,13 +162,41 @@ final class PeekControllerNode: ViewControllerTracingNode {
|
||||
self.dimNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimNodeTap(_:))))
|
||||
self.view.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:))))
|
||||
}
|
||||
|
||||
func updateLayout() {
|
||||
|
||||
func updateLayout(transition: ContainedViewLayoutTransition = .immediate) {
|
||||
if let layout = self.validLayout {
|
||||
self.containerLayoutUpdated(layout, transition: .immediate)
|
||||
self.containerLayoutUpdated(layout, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
func replaceItem(items: Signal<ContextController.Items, NoError>) {
|
||||
let _ = (items
|
||||
|> deliverOnMainQueue).start(next: { [weak self] items in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if let item = makeContextControllerActionsStackItem(items: items).first {
|
||||
self.actionsStackNode.replace(item: item, animated: false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func pushItems(items: Signal<ContextController.Items, NoError>) {
|
||||
let _ = (items
|
||||
|> deliverOnMainQueue).start(next: { [weak self] items in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if let item = makeContextControllerActionsStackItem(items: items).first {
|
||||
self.actionsStackNode.push(item: item, currentScrollingState: nil, positionLock: nil, animated: true)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func popItems() {
|
||||
self.actionsStackNode.pop()
|
||||
}
|
||||
|
||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = layout
|
||||
|
||||
@ -172,12 +223,18 @@ final class PeekControllerNode: ViewControllerTracingNode {
|
||||
}
|
||||
|
||||
let actionsSideInset: CGFloat = layout.safeInsets.left + 11.0
|
||||
let actionsSize = self.actionsContainerNode.updateLayout(widthClass: layout.metrics.widthClass, presentation: .inline, constrainedWidth: layout.size.width - actionsSideInset * 2.0, constrainedHeight: layout.size.height, transition: .immediate)
|
||||
|
||||
let actionsSize = self.actionsStackNode.update(
|
||||
presentationData: self.presentationData,
|
||||
constrainedSize: CGSize(width: layout.size.width - actionsSideInset * 2.0, height: layout.size.height),
|
||||
presentation: .inline,
|
||||
transition: transition
|
||||
)
|
||||
|
||||
let containerFrame: CGRect
|
||||
let actionsFrame: CGRect
|
||||
if layout.size.width > layout.size.height {
|
||||
if self.actionsContainerNode.alpha.isZero {
|
||||
if self.actionsStackNode.alpha.isZero {
|
||||
containerFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - contentSize.width) / 2.0), y: floor((layout.size.height - contentSize.height) / 2.0)), size: contentSize)
|
||||
} else {
|
||||
containerFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - contentSize.width) / 3.0), y: floor((layout.size.height - contentSize.height) / 2.0)), size: contentSize)
|
||||
@ -194,12 +251,11 @@ final class PeekControllerNode: ViewControllerTracingNode {
|
||||
}
|
||||
transition.updateFrame(node: self.containerNode, frame: containerFrame)
|
||||
|
||||
self.actionsContainerNode.updateSize(containerSize: actionsSize, contentSize: actionsSize)
|
||||
transition.updateFrame(node: self.actionsContainerNode, frame: actionsFrame)
|
||||
transition.updateFrame(node: self.actionsStackNode, frame: actionsFrame)
|
||||
|
||||
if let fullScreenAccessoryNode = self.fullScreenAccessoryNode {
|
||||
fullScreenAccessoryNode.updateLayout(size: layout.size, transition: transition)
|
||||
transition.updateFrame(node: fullScreenAccessoryNode, frame: CGRect(origin: .zero, size: layout.size))
|
||||
fullScreenAccessoryNode.updateLayout(size: layout.size, transition: transition)
|
||||
}
|
||||
|
||||
self.contentNodeHasValidLayout = true
|
||||
@ -244,11 +300,11 @@ final class PeekControllerNode: ViewControllerTracingNode {
|
||||
self.containerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||
self.containerNode.layer.animateScale(from: 1.0, to: 0.1, duration: 0.25, removeOnCompletion: false)
|
||||
|
||||
if !self.actionsContainerNode.alpha.isZero {
|
||||
let actionsOffset = CGPoint(x: rect.midX - self.actionsContainerNode.position.x, y: rect.midY - self.actionsContainerNode.position.y)
|
||||
self.actionsContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2 * animationDurationFactor, removeOnCompletion: false)
|
||||
self.actionsContainerNode.layer.animateSpring(from: 1.0 as NSNumber, to: 0.0 as NSNumber, keyPath: "transform.scale", duration: springDuration, initialVelocity: 0.0, damping: springDamping, removeOnCompletion: false)
|
||||
self.actionsContainerNode.layer.animateSpring(from: NSValue(cgPoint: CGPoint()), to: NSValue(cgPoint: actionsOffset), keyPath: "position", duration: springDuration, initialVelocity: 0.0, damping: springDamping, additive: true)
|
||||
if !self.actionsStackNode.alpha.isZero {
|
||||
let actionsOffset = CGPoint(x: rect.midX - self.actionsStackNode.position.x, y: rect.midY - self.actionsStackNode.position.y)
|
||||
self.actionsStackNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2 * animationDurationFactor, removeOnCompletion: false)
|
||||
self.actionsStackNode.layer.animateSpring(from: 1.0 as NSNumber, to: 0.0 as NSNumber, keyPath: "transform.scale", duration: springDuration, initialVelocity: 0.0, damping: springDamping, removeOnCompletion: false)
|
||||
self.actionsStackNode.layer.animateSpring(from: NSValue(cgPoint: CGPoint()), to: NSValue(cgPoint: actionsOffset), keyPath: "position", duration: springDuration, initialVelocity: 0.0, damping: springDamping, additive: true)
|
||||
}
|
||||
|
||||
if let fullScreenAccessoryNode = self.fullScreenAccessoryNode, !fullScreenAccessoryNode.alpha.isZero {
|
||||
@ -289,7 +345,7 @@ final class PeekControllerNode: ViewControllerTracingNode {
|
||||
initialPoint = localPoint
|
||||
self.initialContinueGesturePoint = localPoint
|
||||
}
|
||||
if !self.actionsContainerNode.alpha.isZero {
|
||||
if !self.actionsStackNode.alpha.isZero {
|
||||
if !self.didMoveFromInitialGesturePoint {
|
||||
let distance = abs(localPoint.y - initialPoint.y)
|
||||
if distance > 12.0 {
|
||||
@ -297,16 +353,19 @@ final class PeekControllerNode: ViewControllerTracingNode {
|
||||
}
|
||||
}
|
||||
if self.didMoveFromInitialGesturePoint {
|
||||
let actionPoint = self.view.convert(localPoint, to: self.actionsContainerNode.view)
|
||||
let actionNode = self.actionsContainerNode.actionNode(at: actionPoint)
|
||||
if self.highlightedActionNode !== actionNode {
|
||||
self.highlightedActionNode?.setIsHighlighted(false)
|
||||
self.highlightedActionNode = actionNode
|
||||
if let actionNode = actionNode {
|
||||
actionNode.setIsHighlighted(true)
|
||||
self.hapticFeedback.tap()
|
||||
}
|
||||
}
|
||||
let actionPoint = self.view.convert(localPoint, to: self.actionsStackNode.view)
|
||||
self.actionsStackNode.highlightGestureMoved(location: actionPoint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func endDragging(_ location: CGPoint) {
|
||||
if self.didMoveFromInitialGesturePoint {
|
||||
self.actionsStackNode.highlightGestureFinished(performAction: true)
|
||||
} else if self.actionsStackNode.alpha.isZero {
|
||||
if let fullScreenAccessoryNode = self.fullScreenAccessoryNode, !fullScreenAccessoryNode.alpha.isZero {
|
||||
} else {
|
||||
self.requestDismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -322,6 +381,11 @@ final class PeekControllerNode: ViewControllerTracingNode {
|
||||
self.blurView.layer.animateAlpha(from: previousBlurAlpha, to: self.blurView.alpha, duration: 0.3)
|
||||
}
|
||||
return
|
||||
} else {
|
||||
if let fullScreenAccessoryNode = self.fullScreenAccessoryNode {
|
||||
fullScreenAccessoryNode.alpha = 1.0
|
||||
fullScreenAccessoryNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
|
||||
}
|
||||
}
|
||||
if case .press = self.content.menuActivation() {
|
||||
self.hapticFeedback.impact()
|
||||
@ -338,32 +402,20 @@ final class PeekControllerNode: ViewControllerTracingNode {
|
||||
self.darkDimNode.alpha = 1.0
|
||||
self.darkDimNode.layer.animateAlpha(from: previousDarkDimAlpha, to: 1.0, duration: 0.3)
|
||||
|
||||
self.actionsContainerNode.alpha = 1.0
|
||||
self.actionsContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2 * animationDurationFactor)
|
||||
self.actionsContainerNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: springDuration, initialVelocity: 0.0, damping: springDamping)
|
||||
|
||||
let localContentSourceFrame = self.containerNode.frame
|
||||
self.actionsContainerNode.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: localContentSourceFrame.center.x - self.actionsContainerNode.position.x, y: localContentSourceFrame.center.y - self.actionsContainerNode.position.y)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, initialVelocity: 0.0, damping: springDamping, additive: true)
|
||||
Queue.mainQueue().justDispatch {
|
||||
self.actionsStackNode.alpha = 1.0
|
||||
self.actionsStackNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2 * animationDurationFactor)
|
||||
self.actionsStackNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: springDuration, initialVelocity: 0.0, damping: springDamping)
|
||||
|
||||
let localContentSourceFrame = self.containerNode.frame
|
||||
self.actionsStackNode.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: localContentSourceFrame.center.x - self.actionsStackNode.position.x, y: localContentSourceFrame.center.y - self.actionsStackNode.position.y)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, initialVelocity: 0.0, damping: springDamping, additive: true)
|
||||
}
|
||||
|
||||
if let layout = self.validLayout {
|
||||
self.containerLayoutUpdated(layout, transition: .animated(duration: springDuration, curve: .spring))
|
||||
}
|
||||
}
|
||||
|
||||
func endDragging(_ location: CGPoint) {
|
||||
if self.didMoveFromInitialGesturePoint {
|
||||
if let highlightedActionNode = self.highlightedActionNode {
|
||||
self.highlightedActionNode = nil
|
||||
highlightedActionNode.performAction()
|
||||
}
|
||||
} else if self.actionsContainerNode.alpha.isZero {
|
||||
if let fullScreenAccessoryNode = self.fullScreenAccessoryNode, !fullScreenAccessoryNode.alpha.isZero {
|
||||
} else {
|
||||
self.requestDismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateContent(content: PeekControllerContent) {
|
||||
let contentNode = self.contentNode
|
||||
contentNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak contentNode] _ in
|
||||
@ -376,19 +428,7 @@ final class PeekControllerNode: ViewControllerTracingNode {
|
||||
self.containerNode.addSubnode(self.contentNode)
|
||||
self.contentNodeHasValidLayout = false
|
||||
|
||||
let previousActionsContainerNode = self.actionsContainerNode
|
||||
self.actionsContainerNode = ContextActionsContainerNode(presentationData: self.presentationData, items: ContextController.Items(content: .list(content.menuItems()), animationCache: nil), getController: { [weak self] in
|
||||
return self?.controller
|
||||
}, actionSelected: { [weak self] result in
|
||||
self?.requestDismiss()
|
||||
}, requestLayout: { [weak self] in
|
||||
self?.updateLayout()
|
||||
}, feedbackTap: { [weak self] in
|
||||
self?.hapticFeedback.tap()
|
||||
}, blurBackground: true)
|
||||
self.actionsContainerNode.alpha = 0.0
|
||||
self.insertSubnode(self.actionsContainerNode, aboveSubnode: previousActionsContainerNode)
|
||||
previousActionsContainerNode.removeFromSupernode()
|
||||
self.replaceItem(items: .single(ContextController.Items(content: .list(content.menuItems()))))
|
||||
|
||||
self.contentNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1)
|
||||
self.contentNode.layer.animateSpring(from: 0.35 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.5)
|
||||
|
@ -5,7 +5,7 @@ import SwiftSignalKit
|
||||
private var backArrowImageCache: [Int32: UIImage] = [:]
|
||||
|
||||
open class SparseNode: ASDisplayNode {
|
||||
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
override open func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
if self.alpha.isZero {
|
||||
return nil
|
||||
}
|
||||
|
@ -107,6 +107,7 @@ swift_library(
|
||||
"//submodules/Camera",
|
||||
"//submodules/TelegramUI/Components/DustEffect",
|
||||
"//submodules/TelegramUI/Components/DynamicCornerRadiusView",
|
||||
"//submodules/TelegramUI/Components/StickerPickerScreen",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -145,7 +145,7 @@ public final class DrawingEntitiesView: UIView, TGPhotoDrawingEntitiesView {
|
||||
self.angleLayer.opacity = 0.0
|
||||
self.angleLayer.lineDashPattern = [12, 12] as [NSNumber]
|
||||
|
||||
self.stickerOverlayLayer.fillColor = UIColor(rgb: 0x000000, alpha: 0.6).cgColor
|
||||
self.stickerOverlayLayer.fillColor = UIColor(rgb: 0x000000, alpha: 0.7).cgColor
|
||||
|
||||
self.stickerFrameLayer.fillColor = UIColor.clear.cgColor
|
||||
self.stickerFrameLayer.strokeColor = UIColor(rgb: 0xffffff, alpha: 0.55).cgColor
|
||||
|
@ -177,7 +177,7 @@ public class DrawingReactionEntityView: DrawingStickerEntityView {
|
||||
reactionContextNode.forceTailToRight = true
|
||||
reactionContextNode.forceDark = true
|
||||
self.reactionContextNode = reactionContextNode
|
||||
|
||||
|
||||
reactionContextNode.reactionSelected = { [weak self] updateReaction, _ in
|
||||
guard let self else {
|
||||
return
|
||||
|
@ -24,6 +24,7 @@ import EntityKeyboard
|
||||
import TelegramUIPreferences
|
||||
import FastBlur
|
||||
import MediaEditor
|
||||
import StickerPickerScreen
|
||||
|
||||
public struct DrawingResultData {
|
||||
public let data: Data?
|
||||
@ -493,7 +494,7 @@ private final class DrawingScreenComponent: CombinedComponent {
|
||||
|
||||
let context: AccountContext
|
||||
let sourceHint: DrawingScreen.SourceHint?
|
||||
let existingStickerPickerInputData: Promise<StickerPickerInputData>?
|
||||
let existingStickerPickerInputData: Promise<StickerPickerInput>?
|
||||
let isVideo: Bool
|
||||
let isAvatar: Bool
|
||||
let isInteractingWithEntities: Bool
|
||||
@ -529,7 +530,7 @@ private final class DrawingScreenComponent: CombinedComponent {
|
||||
init(
|
||||
context: AccountContext,
|
||||
sourceHint: DrawingScreen.SourceHint?,
|
||||
existingStickerPickerInputData: Promise<StickerPickerInputData>?,
|
||||
existingStickerPickerInputData: Promise<StickerPickerInput>?,
|
||||
isVideo: Bool,
|
||||
isAvatar: Bool,
|
||||
isInteractingWithEntities: Bool,
|
||||
@ -682,11 +683,11 @@ private final class DrawingScreenComponent: CombinedComponent {
|
||||
|
||||
var lastSize: CGFloat = 0.5
|
||||
|
||||
private let stickerPickerInputData: Promise<StickerPickerInputData>
|
||||
private let stickerPickerInputData: Promise<StickerPickerInput>
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
existingStickerPickerInputData: Promise<StickerPickerInputData>?,
|
||||
existingStickerPickerInputData: Promise<StickerPickerInput>?,
|
||||
updateToolState: ActionSlot<DrawingToolState>,
|
||||
insertEntity: ActionSlot<DrawingEntity>,
|
||||
deselectEntity: ActionSlot<Void>,
|
||||
@ -728,7 +729,7 @@ private final class DrawingScreenComponent: CombinedComponent {
|
||||
if let existingStickerPickerInputData {
|
||||
self.stickerPickerInputData = existingStickerPickerInputData
|
||||
} else {
|
||||
self.stickerPickerInputData = Promise<StickerPickerInputData>()
|
||||
self.stickerPickerInputData = Promise<StickerPickerInput>()
|
||||
|
||||
let stickerPickerInputData = self.stickerPickerInputData
|
||||
Queue.concurrentDefaultQueue().after(0.5, {
|
||||
@ -762,7 +763,7 @@ private final class DrawingScreenComponent: CombinedComponent {
|
||||
let signal = combineLatest(queue: .mainQueue(),
|
||||
emojiItems,
|
||||
stickerItems
|
||||
) |> map { emoji, stickers -> StickerPickerInputData in
|
||||
) |> map { emoji, stickers -> StickerPickerInput in
|
||||
return StickerPickerInputData(emoji: emoji, stickers: stickers, gifs: nil)
|
||||
}
|
||||
|
||||
@ -1012,7 +1013,7 @@ private final class DrawingScreenComponent: CombinedComponent {
|
||||
self.currentMode = .sticker
|
||||
|
||||
self.updateEntitiesPlayback.invoke(false)
|
||||
let controller = StickerPickerScreen(context: self.context, inputData: self.stickerPickerInputData.get())
|
||||
let controller = StickerPickerScreen(context: self.context, inputData: self.stickerPickerInputData.get(), forceDark: true, hasInteractiveStickers: false)
|
||||
if let presentGallery = self.presentGallery {
|
||||
controller.presentGallery = presentGallery
|
||||
}
|
||||
@ -2753,7 +2754,7 @@ public class DrawingScreen: ViewController, TGPhotoDrawingInterfaceController, U
|
||||
private let externalDrawingView: DrawingView?
|
||||
private let externalEntitiesView: DrawingEntitiesView?
|
||||
private let externalSelectionContainerView: DrawingSelectionContainerView?
|
||||
private let existingStickerPickerInputData: Promise<StickerPickerInputData>?
|
||||
private let existingStickerPickerInputData: Promise<StickerPickerInput>?
|
||||
|
||||
public var requestDismiss: () -> Void = {}
|
||||
public var requestApply: () -> Void = {}
|
||||
@ -2762,7 +2763,7 @@ public class DrawingScreen: ViewController, TGPhotoDrawingInterfaceController, U
|
||||
|
||||
public var presentGallery: (() -> Void)?
|
||||
|
||||
public init(context: AccountContext, sourceHint: SourceHint? = nil, size: CGSize, originalSize: CGSize, isVideo: Bool, isAvatar: Bool, drawingView: DrawingView?, entitiesView: (UIView & TGPhotoDrawingEntitiesView)?, selectionContainerView: DrawingSelectionContainerView?, existingStickerPickerInputData: Promise<StickerPickerInputData>? = nil) {
|
||||
public init(context: AccountContext, sourceHint: SourceHint? = nil, size: CGSize, originalSize: CGSize, isVideo: Bool, isAvatar: Bool, drawingView: DrawingView?, entitiesView: (UIView & TGPhotoDrawingEntitiesView)?, selectionContainerView: DrawingSelectionContainerView?, existingStickerPickerInputData: Promise<StickerPickerInput>? = nil) {
|
||||
self.context = context
|
||||
self.sourceHint = sourceHint
|
||||
self.size = size
|
||||
|
@ -496,8 +496,8 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode {
|
||||
}
|
||||
if let searchNode = strongSelf.searchNode, searchNode.isActive {
|
||||
if let (itemNode, item) = searchNode.itemAt(point: strongSelf.view.convert(point, to: searchNode.view)) {
|
||||
if let item = item as? StickerPreviewPeekItem {
|
||||
return strongSelf.context.engine.stickers.isStickerSaved(id: item.file.fileId)
|
||||
if let item = item as? StickerPreviewPeekItem, let file = item.file {
|
||||
return strongSelf.context.engine.stickers.isStickerSaved(id: file.fileId)
|
||||
|> deliverOnMainQueue
|
||||
|> map { isStarred -> (UIView, CGRect, PeekControllerContent)? in
|
||||
if let strongSelf = self {
|
||||
@ -506,9 +506,9 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode {
|
||||
.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.StickerPack_Send, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Resend"), color: theme.contextMenu.primaryColor) }, action: { _, f in
|
||||
if let strongSelf = self, let peekController = strongSelf.peekController {
|
||||
if let animationNode = (peekController.contentNode as? StickerPreviewPeekContentNode)?.animationNode {
|
||||
let _ = strongSelf.sendSticker?(.standalone(media: item.file), animationNode.view, animationNode.bounds)
|
||||
let _ = strongSelf.sendSticker?(.standalone(media: file), animationNode.view, animationNode.bounds)
|
||||
} else if let imageNode = (peekController.contentNode as? StickerPreviewPeekContentNode)?.imageNode {
|
||||
let _ = strongSelf.sendSticker?(.standalone(media: item.file), imageNode.view, imageNode.bounds)
|
||||
let _ = strongSelf.sendSticker?(.standalone(media: file), imageNode.view, imageNode.bounds)
|
||||
}
|
||||
}
|
||||
f(.default)
|
||||
@ -517,11 +517,11 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode {
|
||||
f(.default)
|
||||
|
||||
if let strongSelf = self {
|
||||
let _ = (strongSelf.context.engine.stickers.toggleStickerSaved(file: item.file, saved: !isStarred)
|
||||
let _ = (strongSelf.context.engine.stickers.toggleStickerSaved(file: file, saved: !isStarred)
|
||||
|> deliverOnMainQueue).start(next: { result in
|
||||
switch result {
|
||||
case .generic:
|
||||
strongSelf.controller?.presentInGlobalOverlay(UndoOverlayController(presentationData: strongSelf.presentationData, content: .sticker(context: strongSelf.context, file: item.file, loop: true, title: nil, text: !isStarred ? strongSelf.presentationData.strings.Conversation_StickerAddedToFavorites : strongSelf.presentationData.strings.Conversation_StickerRemovedFromFavorites, undoText: nil, customAction: nil), elevatedLayout: false, action: { _ in return false }), with: nil)
|
||||
strongSelf.controller?.presentInGlobalOverlay(UndoOverlayController(presentationData: strongSelf.presentationData, content: .sticker(context: strongSelf.context, file: file, loop: true, title: nil, text: !isStarred ? strongSelf.presentationData.strings.Conversation_StickerAddedToFavorites : strongSelf.presentationData.strings.Conversation_StickerRemovedFromFavorites, undoText: nil, customAction: nil), elevatedLayout: false, action: { _ in return false }), with: nil)
|
||||
case let .limitExceeded(limit, premiumLimit):
|
||||
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: strongSelf.context.currentAppConfiguration.with { $0 })
|
||||
let text: String
|
||||
@ -530,7 +530,7 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode {
|
||||
} else {
|
||||
text = strongSelf.presentationData.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string
|
||||
}
|
||||
strongSelf.controller?.presentInGlobalOverlay(UndoOverlayController(presentationData: strongSelf.presentationData, content: .sticker(context: strongSelf.context, file: item.file, loop: true, title: strongSelf.presentationData.strings.Premium_MaxFavedStickersTitle("\(limit)").string, text: text, undoText: nil, customAction: nil), elevatedLayout: false, action: { [weak self] action in
|
||||
strongSelf.controller?.presentInGlobalOverlay(UndoOverlayController(presentationData: strongSelf.presentationData, content: .sticker(context: strongSelf.context, file: file, loop: true, title: strongSelf.presentationData.strings.Premium_MaxFavedStickersTitle("\(limit)").string, text: text, undoText: nil, customAction: nil), elevatedLayout: false, action: { [weak self] action in
|
||||
if let strongSelf = self {
|
||||
if case .info = action {
|
||||
let controller = PremiumIntroScreen(context: strongSelf.context, source: .savedStickers)
|
||||
@ -548,7 +548,7 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode {
|
||||
f(.default)
|
||||
|
||||
if let strongSelf = self {
|
||||
loop: for attribute in item.file.attributes {
|
||||
loop: for attribute in file.attributes {
|
||||
switch attribute {
|
||||
case let .Sticker(_, packReference, _):
|
||||
if let packReference = packReference {
|
||||
|
@ -14,6 +14,7 @@ import ContextUI
|
||||
import RadialStatusNode
|
||||
import UndoUI
|
||||
import StickerPackPreviewUI
|
||||
import StickerPackEditTitleController
|
||||
|
||||
private struct StickerPackPreviewGridEntry: Comparable, Equatable, Identifiable {
|
||||
let index: Int
|
||||
@ -718,7 +719,7 @@ final class ImportStickerPackControllerNode: ViewControllerTracingNode, UIScroll
|
||||
|
||||
@objc private func createActionButtonPressed() {
|
||||
var proceedImpl: ((String, String?) -> Void)?
|
||||
let titleController = importStickerPackTitleController(context: self.context, title: self.presentationData.strings.ImportStickerPack_ChooseName, text: self.presentationData.strings.ImportStickerPack_ChooseNameDescription, placeholder: self.presentationData.strings.ImportStickerPack_NamePlaceholder, value: nil, maxLength: 128, apply: { [weak self] title in
|
||||
let titleController = stickerPackEditTitleController(context: self.context, title: self.presentationData.strings.ImportStickerPack_ChooseName, text: self.presentationData.strings.ImportStickerPack_ChooseNameDescription, placeholder: self.presentationData.strings.ImportStickerPack_NamePlaceholder, value: nil, maxLength: 128, apply: { [weak self] title in
|
||||
if let strongSelf = self, let title = title {
|
||||
strongSelf.shortNameSuggestionDisposable.set((strongSelf.context.engine.stickers.getStickerSetShortNameSuggestion(title: title)
|
||||
|> deliverOnMainQueue).start(next: { suggestedShortName in
|
||||
|
@ -31,6 +31,7 @@ swift_library(
|
||||
"//submodules/TelegramCore",
|
||||
"//submodules/ComponentFlow",
|
||||
"//submodules/TelegramUI/Components/TabSelectorComponent",
|
||||
"//submodules/Components/ComponentDisplayAdapters",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -60,6 +60,7 @@ public enum ItemListControllerTitle: Equatable {
|
||||
case text(String)
|
||||
case textWithSubtitle(String, String)
|
||||
case sectionControl([String], Int)
|
||||
case textWithTabs(String, [String], Int)
|
||||
}
|
||||
|
||||
public final class ItemListControllerTabBarItem: Equatable {
|
||||
@ -113,6 +114,7 @@ open class ItemListController: ViewController, KeyShortcutResponder, Presentable
|
||||
private var tabBarItemInfo: ItemListControllerTabBarItem?
|
||||
private var navigationButtonActions: (left: (() -> Void)?, right: (() -> Void)?, secondaryRight: (() -> Void)?) = (nil, nil, nil)
|
||||
private var segmentedTitleView: ItemListControllerSegmentedTitleView?
|
||||
private var tabsNavigationContentNode: ItemListControllerTabsContentNode?
|
||||
|
||||
private var presentationData: ItemListPresentationData
|
||||
|
||||
@ -318,10 +320,12 @@ open class ItemListController: ViewController, KeyShortcutResponder, Presentable
|
||||
strongSelf.title = text
|
||||
strongSelf.navigationItem.titleView = nil
|
||||
strongSelf.segmentedTitleView = nil
|
||||
strongSelf.navigationBar?.setContentNode(nil, animated: false)
|
||||
case let .textWithSubtitle(title, subtitle):
|
||||
strongSelf.title = ""
|
||||
strongSelf.navigationItem.titleView = ItemListTextWithSubtitleTitleView(theme: controllerState.presentationData.theme, title: title, subtitle: subtitle)
|
||||
strongSelf.segmentedTitleView = nil
|
||||
strongSelf.navigationBar?.setContentNode(nil, animated: false)
|
||||
case let .sectionControl(sections, index):
|
||||
strongSelf.title = ""
|
||||
if let segmentedTitleView = strongSelf.segmentedTitleView, segmentedTitleView.segments == sections {
|
||||
@ -336,6 +340,21 @@ open class ItemListController: ViewController, KeyShortcutResponder, Presentable
|
||||
}
|
||||
}
|
||||
}
|
||||
strongSelf.navigationBar?.setContentNode(nil, animated: false)
|
||||
case let .textWithTabs(title, sections, index):
|
||||
strongSelf.title = title
|
||||
if let tabsNavigationContentNode = strongSelf.tabsNavigationContentNode, tabsNavigationContentNode.segments == sections {
|
||||
tabsNavigationContentNode.index = index
|
||||
} else {
|
||||
let tabsNavigationContentNode = ItemListControllerTabsContentNode(theme: controllerState.presentationData.theme, segments: sections, selectedIndex: index)
|
||||
strongSelf.tabsNavigationContentNode = tabsNavigationContentNode
|
||||
strongSelf.navigationBar?.setContentNode(tabsNavigationContentNode, animated: false)
|
||||
tabsNavigationContentNode.indexUpdated = { index in
|
||||
if let strongSelf = self {
|
||||
strongSelf.titleControlValueChanged?(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
strongSelf.navigationButtonActions = (left: controllerState.leftNavigationButton?.action, right: controllerState.rightNavigationButton?.action, secondaryRight: controllerState.secondaryRightNavigationButton?.action)
|
||||
|
@ -361,14 +361,18 @@ open class ItemListControllerNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
self.listNode.visibleContentOffsetChanged = { [weak self] offset in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
var inVoiceOver = false
|
||||
if let validLayout = self?.validLayout {
|
||||
inVoiceOver = validLayout.0.inVoiceOver
|
||||
}
|
||||
strongSelf.contentOffsetChanged?(offset, inVoiceOver)
|
||||
|
||||
self?.contentOffsetChanged?(offset, inVoiceOver)
|
||||
|
||||
if let strongSelf = self {
|
||||
if let navigationContentNode = strongSelf.navigationBar.contentNode, case .expansion = navigationContentNode.mode {
|
||||
strongSelf.navigationBar.updateBackgroundAlpha(1.0, transition: .immediate)
|
||||
} else {
|
||||
var previousContentOffsetValue: CGFloat?
|
||||
if let previousContentOffset = strongSelf.previousContentOffset {
|
||||
if case let .known(value) = previousContentOffset {
|
||||
@ -378,30 +382,30 @@ open class ItemListControllerNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
switch offset {
|
||||
case let .known(value):
|
||||
let transition: ContainedViewLayoutTransition
|
||||
if let previousContentOffsetValue = previousContentOffsetValue, value <= 0.0, previousContentOffsetValue >= 30.0 {
|
||||
transition = .animated(duration: 0.2, curve: .easeInOut)
|
||||
} else {
|
||||
transition = .immediate
|
||||
}
|
||||
if let headerItemNode = strongSelf.headerItemNode {
|
||||
headerItemNode.updateContentOffset(value, transition: transition)
|
||||
strongSelf.navigationBar.updateBackgroundAlpha(0.0, transition: .immediate)
|
||||
} else {
|
||||
strongSelf.navigationBar.updateBackgroundAlpha(min(30.0, value) / 30.0, transition: transition)
|
||||
}
|
||||
case .unknown, .none:
|
||||
if let headerItemNode = strongSelf.headerItemNode {
|
||||
headerItemNode.updateContentOffset(1000.0, transition: .immediate)
|
||||
strongSelf.navigationBar.updateBackgroundAlpha(0.0, transition: .immediate)
|
||||
} else {
|
||||
strongSelf.navigationBar.updateBackgroundAlpha(1.0, transition: .immediate)
|
||||
}
|
||||
case let .known(value):
|
||||
let transition: ContainedViewLayoutTransition
|
||||
if let previousContentOffsetValue = previousContentOffsetValue, value <= 0.0, previousContentOffsetValue >= 30.0 {
|
||||
transition = .animated(duration: 0.2, curve: .easeInOut)
|
||||
} else {
|
||||
transition = .immediate
|
||||
}
|
||||
if let headerItemNode = strongSelf.headerItemNode {
|
||||
headerItemNode.updateContentOffset(value, transition: transition)
|
||||
strongSelf.navigationBar.updateBackgroundAlpha(0.0, transition: .immediate)
|
||||
} else {
|
||||
strongSelf.navigationBar.updateBackgroundAlpha(min(30.0, value) / 30.0, transition: transition)
|
||||
}
|
||||
case .unknown, .none:
|
||||
if let headerItemNode = strongSelf.headerItemNode {
|
||||
headerItemNode.updateContentOffset(1000.0, transition: .immediate)
|
||||
strongSelf.navigationBar.updateBackgroundAlpha(0.0, transition: .immediate)
|
||||
} else {
|
||||
strongSelf.navigationBar.updateBackgroundAlpha(1.0, transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
strongSelf.previousContentOffset = offset
|
||||
}
|
||||
|
||||
strongSelf.previousContentOffset = offset
|
||||
}
|
||||
|
||||
self.listNode.beganInteractiveDragging = { [weak self] _ in
|
||||
|
@ -1,10 +1,9 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import SegmentedControlNode
|
||||
import Display
|
||||
import TelegramPresentationData
|
||||
import ComponentFlow
|
||||
import TabSelectorComponent
|
||||
import Display
|
||||
|
||||
public final class ItemListControllerSegmentedTitleView: UIView {
|
||||
private let tabSelector = ComponentView<Empty>()
|
||||
|
@ -0,0 +1,124 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import TelegramPresentationData
|
||||
import ComponentFlow
|
||||
import ComponentDisplayAdapters
|
||||
import TabSelectorComponent
|
||||
|
||||
private let searchBarFont = Font.regular(17.0)
|
||||
|
||||
final class ItemListControllerTabsContentNode: NavigationBarContentNode {
|
||||
private let tabSelector = ComponentView<Empty>()
|
||||
|
||||
var theme: PresentationTheme {
|
||||
didSet {
|
||||
if self.theme !== oldValue {
|
||||
self.update()
|
||||
}
|
||||
}
|
||||
}
|
||||
var segments: [String] {
|
||||
didSet {
|
||||
if self.segments != oldValue {
|
||||
self.update()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var index: Int {
|
||||
didSet {
|
||||
if self.index != oldValue {
|
||||
self.update(transition: .animated(duration: 0.35, curve: .spring))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var indexUpdated: ((Int) -> Void)?
|
||||
|
||||
private var validLayout: (CGSize, CGFloat, CGFloat)?
|
||||
|
||||
init(theme: PresentationTheme, segments: [String], selectedIndex: Int) {
|
||||
self.theme = theme
|
||||
self.segments = segments
|
||||
self.index = selectedIndex
|
||||
|
||||
super.init()
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
}
|
||||
|
||||
private func update(transition: ContainedViewLayoutTransition = .immediate) {
|
||||
guard let (size, leftInset, rightInset) = self.validLayout else {
|
||||
return
|
||||
}
|
||||
self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: transition)
|
||||
}
|
||||
|
||||
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
let isFirstTime = self.validLayout == nil
|
||||
self.validLayout = (size, leftInset, rightInset)
|
||||
|
||||
let mappedItems = zip(0 ..< self.segments.count, self.segments).map { index, segment in
|
||||
return TabSelectorComponent.Item(
|
||||
id: AnyHashable(index),
|
||||
title: segment
|
||||
)
|
||||
}
|
||||
|
||||
let tabSelectorSize = self.tabSelector.update(
|
||||
transition: Transition(transition),
|
||||
component: AnyComponent(TabSelectorComponent(
|
||||
colors: TabSelectorComponent.Colors(
|
||||
foreground: self.theme.list.itemSecondaryTextColor,
|
||||
selection: self.theme.list.itemAccentColor
|
||||
),
|
||||
customLayout: TabSelectorComponent.CustomLayout(
|
||||
font: Font.medium(14.0),
|
||||
spacing: 24.0,
|
||||
lineSelection: true
|
||||
),
|
||||
items: mappedItems,
|
||||
selectedId: AnyHashable(self.index),
|
||||
setSelectedId: { [weak self] id in
|
||||
guard let self, let index = id.base as? Int else {
|
||||
return
|
||||
}
|
||||
self.indexUpdated?(index)
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: size.width, height: 44.0)
|
||||
)
|
||||
let tabSelectorFrame = CGRect(origin: CGPoint(x: floor((size.width - tabSelectorSize.width) / 2.0), y: floor((size.height - tabSelectorSize.height) / 2.0) + 4.0), size: tabSelectorSize)
|
||||
if let tabSelectorView = self.tabSelector.view {
|
||||
if tabSelectorView.superview == nil {
|
||||
self.view.addSubview(tabSelectorView)
|
||||
}
|
||||
transition.updateFrame(view: tabSelectorView, frame: tabSelectorFrame)
|
||||
}
|
||||
|
||||
if isFirstTime {
|
||||
self.requestContainerLayout(.immediate)
|
||||
}
|
||||
}
|
||||
|
||||
override var height: CGFloat {
|
||||
return self.nominalHeight
|
||||
}
|
||||
|
||||
override var clippedHeight: CGFloat {
|
||||
return self.nominalHeight
|
||||
}
|
||||
|
||||
override var nominalHeight: CGFloat {
|
||||
return 54.0// + self.additionalHeight
|
||||
}
|
||||
|
||||
override var mode: NavigationBarContentMode {
|
||||
return .expansion
|
||||
}
|
||||
}
|
@ -39,19 +39,28 @@ public enum ItemListDisclosureLabelStyle {
|
||||
case image(image: UIImage, size: CGSize)
|
||||
}
|
||||
|
||||
public enum ItemListDisclosureItemDetailLabelColor {
|
||||
case generic
|
||||
case constructive
|
||||
case destructive
|
||||
}
|
||||
|
||||
public class ItemListDisclosureItem: ListViewItem, ItemListItem {
|
||||
let presentationData: ItemListPresentationData
|
||||
let icon: UIImage?
|
||||
let context: AccountContext?
|
||||
let iconPeer: EnginePeer?
|
||||
let title: String
|
||||
let attributedTitle: NSAttributedString?
|
||||
let titleColor: ItemListDisclosureItemTitleColor
|
||||
let titleFont: ItemListDisclosureItemTitleFont
|
||||
let titleIcon: UIImage?
|
||||
let enabled: Bool
|
||||
let label: String
|
||||
let attributedLabel: NSAttributedString?
|
||||
let labelStyle: ItemListDisclosureLabelStyle
|
||||
let additionalDetailLabel: String?
|
||||
let additionalDetailLabelColor: ItemListDisclosureItemDetailLabelColor
|
||||
public let sectionId: ItemListSectionId
|
||||
let style: ItemListStyle
|
||||
let disclosureStyle: ItemListDisclosureStyle
|
||||
@ -60,19 +69,22 @@ public class ItemListDisclosureItem: ListViewItem, ItemListItem {
|
||||
public let tag: ItemListItemTag?
|
||||
public let shimmeringIndex: Int?
|
||||
|
||||
public init(presentationData: ItemListPresentationData, icon: UIImage? = nil, context: AccountContext? = nil, iconPeer: EnginePeer? = nil, title: String, enabled: Bool = true, titleColor: ItemListDisclosureItemTitleColor = .primary, titleFont: ItemListDisclosureItemTitleFont = .regular, titleIcon: UIImage? = nil, label: String, labelStyle: ItemListDisclosureLabelStyle = .text, additionalDetailLabel: String? = nil, sectionId: ItemListSectionId, style: ItemListStyle, disclosureStyle: ItemListDisclosureStyle = .arrow, action: (() -> Void)?, clearHighlightAutomatically: Bool = true, tag: ItemListItemTag? = nil, shimmeringIndex: Int? = nil) {
|
||||
public init(presentationData: ItemListPresentationData, icon: UIImage? = nil, context: AccountContext? = nil, iconPeer: EnginePeer? = nil, title: String, attributedTitle: NSAttributedString? = nil, enabled: Bool = true, titleColor: ItemListDisclosureItemTitleColor = .primary, titleFont: ItemListDisclosureItemTitleFont = .regular, titleIcon: UIImage? = nil, label: String, attributedLabel: NSAttributedString? = nil, labelStyle: ItemListDisclosureLabelStyle = .text, additionalDetailLabel: String? = nil, additionalDetailLabelColor: ItemListDisclosureItemDetailLabelColor = .generic, sectionId: ItemListSectionId, style: ItemListStyle, disclosureStyle: ItemListDisclosureStyle = .arrow, action: (() -> Void)?, clearHighlightAutomatically: Bool = true, tag: ItemListItemTag? = nil, shimmeringIndex: Int? = nil) {
|
||||
self.presentationData = presentationData
|
||||
self.icon = icon
|
||||
self.context = context
|
||||
self.iconPeer = iconPeer
|
||||
self.title = title
|
||||
self.attributedTitle = attributedTitle
|
||||
self.titleColor = titleColor
|
||||
self.titleFont = titleFont
|
||||
self.titleIcon = titleIcon
|
||||
self.enabled = enabled
|
||||
self.labelStyle = labelStyle
|
||||
self.label = label
|
||||
self.attributedLabel = attributedLabel
|
||||
self.additionalDetailLabel = additionalDetailLabel
|
||||
self.additionalDetailLabelColor = additionalDetailLabelColor
|
||||
self.sectionId = sectionId
|
||||
self.style = style
|
||||
self.disclosureStyle = disclosureStyle
|
||||
@ -358,7 +370,7 @@ public class ItemListDisclosureItemNode: ListViewItemNode, ItemListItemNode {
|
||||
maxTitleWidth -= 12.0
|
||||
}
|
||||
|
||||
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.title, font: titleFont, textColor: titleColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: maxTitleWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: item.attributedTitle ?? NSAttributedString(string: item.title, font: titleFont, textColor: titleColor), backgroundColor: nil, maximumNumberOfLines: item.attributedTitle != nil ? 0 : 1, truncationType: .end, constrainedSize: CGSize(width: maxTitleWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let detailFont = Font.regular(floor(item.presentationData.fontSize.itemListBaseFontSize * 15.0 / 17.0))
|
||||
|
||||
@ -392,7 +404,7 @@ public class ItemListDisclosureItemNode: ListViewItemNode, ItemListItemNode {
|
||||
multilineLabel = true
|
||||
}
|
||||
|
||||
let (labelLayout, labelApply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.label, font: labelFont, textColor: labelBadgeColor), backgroundColor: nil, maximumNumberOfLines: multilineLabel ? 0 : 1, truncationType: .end, constrainedSize: CGSize(width: labelConstrain, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let (labelLayout, labelApply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: item.attributedLabel ?? NSAttributedString(string: item.label, font: labelFont, textColor: labelBadgeColor), backgroundColor: nil, maximumNumberOfLines: multilineLabel ? 0 : 1, truncationType: .end, constrainedSize: CGSize(width: labelConstrain, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
var additionalDetailLabelInfo: (TextNodeLayout, () -> TextNode)?
|
||||
if let additionalDetailLabel = item.additionalDetailLabel {
|
||||
@ -400,7 +412,16 @@ public class ItemListDisclosureItemNode: ListViewItemNode, ItemListItemNode {
|
||||
if labelLayout.size.width != 0 {
|
||||
detailRightInset += labelLayout.size.width + 12.0
|
||||
}
|
||||
additionalDetailLabelInfo = makeAdditionalDetailLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: additionalDetailLabel, font: detailFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - detailRightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let additionalDetailColor: UIColor
|
||||
switch item.additionalDetailLabelColor {
|
||||
case .generic:
|
||||
additionalDetailColor = item.presentationData.theme.list.itemSecondaryTextColor
|
||||
case .constructive:
|
||||
additionalDetailColor = item.presentationData.theme.list.itemDisclosureActions.constructive.fillColor
|
||||
case .destructive:
|
||||
additionalDetailColor = item.presentationData.theme.list.itemDestructiveColor
|
||||
}
|
||||
additionalDetailLabelInfo = makeAdditionalDetailLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: additionalDetailLabel, font: detailFont, textColor: additionalDetailColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - detailRightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
}
|
||||
|
||||
let verticalInset: CGFloat
|
||||
|
@ -91,6 +91,7 @@ final class JoinLinkPreviewPeerContentNode: ASDisplayNode, ShareContentContainer
|
||||
}
|
||||
}
|
||||
|
||||
private var contentDidBeginDragging: (() -> Void)?
|
||||
private var contentOffsetUpdated: ((CGFloat, ContainedViewLayoutTransition) -> Void)?
|
||||
|
||||
private let avatarNode: AvatarNode
|
||||
@ -220,6 +221,10 @@ final class JoinLinkPreviewPeerContentNode: ASDisplayNode, ShareContentContainer
|
||||
func setEnsurePeerVisibleOnLayout(_ peerId: EnginePeer.Id?) {
|
||||
}
|
||||
|
||||
func setDidBeginDragging(_ f: (() -> Void)?) {
|
||||
self.contentDidBeginDragging = f
|
||||
}
|
||||
|
||||
func setContentOffsetUpdated(_ f: ((CGFloat, ContainedViewLayoutTransition) -> Void)?) {
|
||||
self.contentOffsetUpdated = f
|
||||
}
|
||||
@ -385,6 +390,7 @@ public enum ShareLoadingState {
|
||||
}
|
||||
|
||||
public final class JoinLinkPreviewLoadingContainerNode: ASDisplayNode, ShareContentContainerNode {
|
||||
private var contentDidBeginDragging: (() -> Void)?
|
||||
private var contentOffsetUpdated: ((CGFloat, ContainedViewLayoutTransition) -> Void)?
|
||||
|
||||
private var theme: PresentationTheme
|
||||
@ -408,6 +414,10 @@ public final class JoinLinkPreviewLoadingContainerNode: ASDisplayNode, ShareCont
|
||||
public func setEnsurePeerVisibleOnLayout(_ peerId: EnginePeer.Id?) {
|
||||
}
|
||||
|
||||
public func setDidBeginDragging(_ f: (() -> Void)?) {
|
||||
self.contentDidBeginDragging = f
|
||||
}
|
||||
|
||||
public func setContentOffsetUpdated(_ f: ((CGFloat, ContainedViewLayoutTransition) -> Void)?) {
|
||||
self.contentOffsetUpdated = f
|
||||
}
|
||||
|
@ -78,6 +78,9 @@ final class LanguageLinkPreviewContentNode: ASDisplayNode, ShareContentContainer
|
||||
func setEnsurePeerVisibleOnLayout(_ peerId: EnginePeer.Id?) {
|
||||
}
|
||||
|
||||
func setDidBeginDragging(_ f: (() -> Void)?) {
|
||||
}
|
||||
|
||||
func setContentOffsetUpdated(_ f: ((CGFloat, ContainedViewLayoutTransition) -> Void)?) {
|
||||
self.contentOffsetUpdated = f
|
||||
}
|
||||
|
@ -561,6 +561,9 @@
|
||||
|
||||
if (_editingContext != nil)
|
||||
{
|
||||
if (_timersChangedDisposable) {
|
||||
[_timersChangedDisposable dispose];
|
||||
}
|
||||
_timersChangedDisposable = [[SMetaDisposable alloc] init];
|
||||
[_timersChangedDisposable setDisposable:[_editingContext.timersUpdatedSignal startStrictWithNext:^(__unused NSNumber *next)
|
||||
{
|
||||
@ -586,6 +589,7 @@
|
||||
self.delegate = nil;
|
||||
[_selectionChangedDisposable dispose];
|
||||
[_tooltipDismissDisposable dispose];
|
||||
[_timersChangedDisposable dispose];
|
||||
[_adjustmentsChangedDisposable dispose];
|
||||
}
|
||||
|
||||
|
@ -248,11 +248,11 @@
|
||||
|
||||
}]];
|
||||
|
||||
if (!item.asFile) {
|
||||
[_facesDisposable setDisposable:[[TGPaintFaceDetector detectFacesInItem:item.editableMediaItem editingContext:item.editingContext] startStrictWithNext:nil file:__FILE_NAME__ line:__LINE__]];
|
||||
|
||||
return;
|
||||
}
|
||||
// if (!item.asFile) {
|
||||
// [_facesDisposable setDisposable:[[TGPaintFaceDetector detectFacesInItem:item.editableMediaItem editingContext:item.editingContext] startStrictWithNext:nil file:__FILE_NAME__ line:__LINE__]];
|
||||
//
|
||||
// return;
|
||||
// }
|
||||
|
||||
_fileInfoLabel.text = nil;
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
import Foundation
|
||||
import LocalAuthentication
|
||||
import SwiftSignalKit
|
||||
import Security
|
||||
|
||||
public enum LocalAuthBiometricAuthentication {
|
||||
case touchId
|
||||
@ -8,10 +9,83 @@ public enum LocalAuthBiometricAuthentication {
|
||||
}
|
||||
|
||||
public struct LocalAuth {
|
||||
public static let biometricAuthentication: LocalAuthBiometricAuthentication? = {
|
||||
private static let customKeyIdPrefix = "$#_".data(using: .utf8)!
|
||||
|
||||
public enum DecryptionResult {
|
||||
public enum Error {
|
||||
case cancelled
|
||||
case generic
|
||||
}
|
||||
|
||||
case result(Data)
|
||||
case error(Error)
|
||||
}
|
||||
|
||||
#if targetEnvironment(simulator)
|
||||
public final class PrivateKey {
|
||||
public let publicKeyRepresentation: Data
|
||||
|
||||
fileprivate init() {
|
||||
self.publicKeyRepresentation = Data(count: 32)
|
||||
}
|
||||
|
||||
public func encrypt(data: Data) -> Data? {
|
||||
return data
|
||||
}
|
||||
|
||||
public func decrypt(data: Data) -> DecryptionResult {
|
||||
return .result(data)
|
||||
}
|
||||
}
|
||||
#else
|
||||
public final class PrivateKey {
|
||||
private let privateKey: SecKey
|
||||
private let publicKey: SecKey
|
||||
public let publicKeyRepresentation: Data
|
||||
|
||||
fileprivate init(privateKey: SecKey, publicKey: SecKey, publicKeyRepresentation: Data) {
|
||||
self.privateKey = privateKey
|
||||
self.publicKey = publicKey
|
||||
self.publicKeyRepresentation = publicKeyRepresentation
|
||||
}
|
||||
|
||||
public func encrypt(data: Data) -> Data? {
|
||||
var error: Unmanaged<CFError>?
|
||||
let cipherText = SecKeyCreateEncryptedData(self.publicKey, .eciesEncryptionCofactorVariableIVX963SHA512AESGCM, data as CFData, &error)
|
||||
if let error {
|
||||
error.release()
|
||||
}
|
||||
guard let cipherText else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let result = cipherText as Data
|
||||
return result
|
||||
}
|
||||
|
||||
public func decrypt(data: Data) -> DecryptionResult {
|
||||
var maybeError: Unmanaged<CFError>?
|
||||
let plainText = SecKeyCreateDecryptedData(self.privateKey, .eciesEncryptionCofactorVariableIVX963SHA512AESGCM, data as CFData, &maybeError)
|
||||
let error = maybeError?.takeRetainedValue()
|
||||
|
||||
guard let plainText else {
|
||||
if let error {
|
||||
if CFErrorGetCode(error) == -2 {
|
||||
return .error(.cancelled)
|
||||
}
|
||||
}
|
||||
return .error(.generic)
|
||||
}
|
||||
|
||||
let result = plainText as Data
|
||||
return .result(result)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
public static var biometricAuthentication: LocalAuthBiometricAuthentication? {
|
||||
let context = LAContext()
|
||||
if context.canEvaluatePolicy(LAPolicy(rawValue: Int(kLAPolicyDeviceOwnerAuthenticationWithBiometrics))!, error: nil) {
|
||||
#if swift(>=5.9)
|
||||
switch context.biometryType {
|
||||
case .faceID, .opticID:
|
||||
return .faceId
|
||||
@ -22,22 +96,10 @@ public struct LocalAuth {
|
||||
@unknown default:
|
||||
return nil
|
||||
}
|
||||
#else
|
||||
switch context.biometryType {
|
||||
case .faceID://, .opticID:
|
||||
return .faceId
|
||||
case .touchID:
|
||||
return .touchId
|
||||
case .none:
|
||||
return nil
|
||||
@unknown default:
|
||||
return nil
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
public static let evaluatedPolicyDomainState: Data? = {
|
||||
let context = LAContext()
|
||||
@ -78,4 +140,161 @@ public struct LocalAuth {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static func bundleSeedId() -> String? {
|
||||
let query: [String: Any] = [
|
||||
kSecClass as String: kSecClassGenericPassword as String,
|
||||
kSecAttrAccount as String: "bundleSeedID",
|
||||
kSecAttrService as String: "",
|
||||
kSecReturnAttributes as String: true
|
||||
]
|
||||
var result: CFTypeRef?
|
||||
var status = SecItemCopyMatching(query as CFDictionary, &result)
|
||||
if status == errSecItemNotFound {
|
||||
status = SecItemAdd(query as CFDictionary, &result)
|
||||
}
|
||||
if status != errSecSuccess {
|
||||
return nil
|
||||
}
|
||||
guard let result = result else {
|
||||
return nil
|
||||
}
|
||||
if CFGetTypeID(result) != CFDictionaryGetTypeID() {
|
||||
return nil
|
||||
}
|
||||
guard let resultDict = (result as! CFDictionary) as? [String: Any] else {
|
||||
return nil
|
||||
}
|
||||
guard let accessGroup = resultDict[kSecAttrAccessGroup as String] as? String else {
|
||||
return nil
|
||||
}
|
||||
let components = accessGroup.components(separatedBy: ".")
|
||||
guard let seedId = components.first else {
|
||||
return nil
|
||||
}
|
||||
return seedId;
|
||||
}
|
||||
|
||||
public static func getOrCreatePrivateKey(baseAppBundleId: String, keyId: Data) -> PrivateKey? {
|
||||
if let key = self.getPrivateKey(baseAppBundleId: baseAppBundleId, keyId: keyId) {
|
||||
return key
|
||||
} else {
|
||||
return self.addPrivateKey(baseAppBundleId: baseAppBundleId, keyId: keyId)
|
||||
}
|
||||
}
|
||||
|
||||
private static func getPrivateKey(baseAppBundleId: String, keyId: Data) -> PrivateKey? {
|
||||
#if targetEnvironment(simulator)
|
||||
return PrivateKey()
|
||||
#else
|
||||
guard let bundleSeedId = self.bundleSeedId() else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let applicationTag = customKeyIdPrefix + keyId
|
||||
let accessGroup = "\(bundleSeedId).\(baseAppBundleId)"
|
||||
|
||||
let query: [String: Any] = [
|
||||
kSecClass as String: kSecClassKey as String,
|
||||
kSecAttrApplicationTag as String: applicationTag,
|
||||
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom as String,
|
||||
kSecAttrAccessGroup as String: accessGroup,
|
||||
kSecReturnRef as String: true
|
||||
]
|
||||
|
||||
var maybePrivateKey: CFTypeRef?
|
||||
let status = SecItemCopyMatching(query as CFDictionary, &maybePrivateKey)
|
||||
if status != errSecSuccess {
|
||||
return nil
|
||||
}
|
||||
guard let maybePrivateKey else {
|
||||
return nil
|
||||
}
|
||||
if CFGetTypeID(maybePrivateKey) != SecKeyGetTypeID() {
|
||||
return nil
|
||||
}
|
||||
let privateKey = maybePrivateKey as! SecKey
|
||||
|
||||
guard let publicKey = SecKeyCopyPublicKey(privateKey) else {
|
||||
return nil
|
||||
}
|
||||
guard let publicKeyRepresentation = SecKeyCopyExternalRepresentation(publicKey, nil) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let result = PrivateKey(privateKey: privateKey, publicKey: publicKey, publicKeyRepresentation: publicKeyRepresentation as Data)
|
||||
|
||||
return result
|
||||
#endif
|
||||
}
|
||||
|
||||
public static func removePrivateKey(baseAppBundleId: String, keyId: Data) -> Bool {
|
||||
guard let bundleSeedId = self.bundleSeedId() else {
|
||||
return false
|
||||
}
|
||||
|
||||
let applicationTag = customKeyIdPrefix + keyId
|
||||
let accessGroup = "\(bundleSeedId).\(baseAppBundleId)"
|
||||
|
||||
let query: [String: Any] = [
|
||||
kSecClass as String: kSecClassKey as String,
|
||||
kSecAttrApplicationTag as String: applicationTag,
|
||||
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom as String,
|
||||
kSecAttrIsPermanent as String: true,
|
||||
kSecAttrAccessGroup as String: accessGroup
|
||||
]
|
||||
|
||||
let status = SecItemDelete(query as CFDictionary)
|
||||
if status != errSecSuccess {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private static func addPrivateKey(baseAppBundleId: String, keyId: Data) -> PrivateKey? {
|
||||
#if targetEnvironment(simulator)
|
||||
return PrivateKey()
|
||||
#else
|
||||
guard let bundleSeedId = self.bundleSeedId() else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let applicationTag = customKeyIdPrefix + keyId
|
||||
let accessGroup = "\(bundleSeedId).\(baseAppBundleId)"
|
||||
|
||||
guard let access = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, [.userPresence, .privateKeyUsage], nil) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let attributes: [String: Any] = [
|
||||
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom as String,
|
||||
kSecAttrKeySizeInBits as String: 256 as NSNumber,
|
||||
kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave as String,
|
||||
kSecPrivateKeyAttrs as String: [
|
||||
kSecAttrIsPermanent as String: true,
|
||||
kSecAttrApplicationTag as String: applicationTag,
|
||||
kSecAttrAccessControl as String: access,
|
||||
kSecAttrAccessGroup as String: accessGroup,
|
||||
] as [String: Any]
|
||||
]
|
||||
var error: Unmanaged<CFError>?
|
||||
let maybePrivateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error)
|
||||
if let error {
|
||||
error.release()
|
||||
}
|
||||
guard let privateKey = maybePrivateKey else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let publicKey = SecKeyCopyPublicKey(privateKey) else {
|
||||
return nil
|
||||
}
|
||||
guard let publicKeyRepresentation = SecKeyCopyExternalRepresentation(publicKey, nil) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let result = PrivateKey(privateKey: privateKey, publicKey: publicKey, publicKeyRepresentation: publicKeyRepresentation as Data)
|
||||
return result
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -678,7 +678,11 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
|
||||
if !controller.didSetupGroups {
|
||||
controller.didSetupGroups = true
|
||||
Queue.concurrentDefaultQueue().after(0.3) {
|
||||
Queue.concurrentDefaultQueue().after(0.4) {
|
||||
var isCreateSticker = false
|
||||
if case .assets(_, .createSticker) = controller.subject {
|
||||
isCreateSticker = true
|
||||
}
|
||||
controller.groupsPromise.set(
|
||||
combineLatest(
|
||||
self.mediaAssetsContext.fetchAssetsCollections(.album),
|
||||
@ -707,6 +711,16 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
supportedAlbums.append(.smartAlbumDepthEffect)
|
||||
supportedAlbums.append(.smartAlbumLivePhotos)
|
||||
}
|
||||
|
||||
if isCreateSticker {
|
||||
supportedAlbums = supportedAlbums.filter { type in
|
||||
if type == .smartAlbumSlomoVideos || type == .smartAlbumTimelapses || type == .smartAlbumVideos {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if supportedAlbums.contains(collection.assetCollectionSubtype) {
|
||||
let result = PHAsset.fetchAssets(in: collection, options: nil)
|
||||
if result.count > 0 {
|
||||
@ -2590,3 +2604,51 @@ public func storyMediaPickerController(
|
||||
controller.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
||||
return controller
|
||||
}
|
||||
|
||||
public func stickerMediaPickerController(
|
||||
context: AccountContext,
|
||||
getSourceRect: @escaping () -> CGRect,
|
||||
completion: @escaping (Any, UIView, CGRect, UIImage?, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void,
|
||||
dismissed: @escaping () -> Void
|
||||
) -> ViewController {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with({ $0 })
|
||||
let updatedPresentationData: (PresentationData, Signal<PresentationData, NoError>) = (presentationData, .single(presentationData))
|
||||
let controller = AttachmentController(context: context, updatedPresentationData: updatedPresentationData, chatLocation: nil, buttons: [.standalone], initialButton: .standalone, fromMenu: false, hasTextInput: false, makeEntityInputView: {
|
||||
return nil
|
||||
})
|
||||
controller.forceSourceRect = true
|
||||
controller.getSourceRect = getSourceRect
|
||||
controller.requestController = { _, present in
|
||||
let mediaPickerController = MediaPickerScreen(context: context, updatedPresentationData: updatedPresentationData, peer: nil, threadTitle: nil, chatLocation: nil, bannedSendPhotos: nil, bannedSendVideos: nil, subject: .assets(nil, .createSticker), mainButtonState: nil, mainButtonAction: nil)
|
||||
mediaPickerController.customSelection = { controller, result in
|
||||
if let result = result as? PHAsset {
|
||||
controller.updateHiddenMediaId(result.localIdentifier)
|
||||
if let transitionView = controller.transitionView(for: result.localIdentifier, snapshot: false) {
|
||||
let transitionOut: (Bool?) -> (UIView, CGRect)? = { isNew in
|
||||
if let isNew {
|
||||
if isNew {
|
||||
controller.updateHiddenMediaId(nil)
|
||||
if let transitionView = controller.defaultTransitionView() {
|
||||
return (transitionView, transitionView.bounds)
|
||||
}
|
||||
} else if let transitionView = controller.transitionView(for: result.localIdentifier, snapshot: false) {
|
||||
return (transitionView, transitionView.bounds)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
completion(result, transitionView, transitionView.bounds, controller.transitionImage(for: result.localIdentifier), transitionOut, { [weak controller] in
|
||||
controller?.updateHiddenMediaId(nil)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
present(mediaPickerController, mediaPickerController.mediaPickerContext)
|
||||
}
|
||||
controller.willDismiss = {
|
||||
dismissed()
|
||||
}
|
||||
controller.navigationPresentation = .flatModal
|
||||
controller.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
||||
return controller
|
||||
}
|
||||
|
@ -1163,7 +1163,6 @@ private class ReorderingGestureRecognizer: UIGestureRecognizer {
|
||||
}
|
||||
}
|
||||
|
||||
private var currentItemNode: ASDisplayNode?
|
||||
override public func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
|
||||
super.touchesBegan(touches, with: event)
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
@property (nonatomic) NSUInteger internalServerErrorCount;
|
||||
@property (nonatomic) NSUInteger floodWaitSeconds;
|
||||
@property (nonatomic, strong) NSString *floodWaitErrorText;
|
||||
|
||||
@property (nonatomic) bool waitingForTokenExport;
|
||||
@property (nonatomic, strong) id waitingForRequestToComplete;
|
||||
|
@ -808,7 +808,7 @@
|
||||
}
|
||||
restartRequest = true;
|
||||
}
|
||||
else if (rpcError.errorCode == 420 || [rpcError.errorDescription rangeOfString:@"FLOOD_WAIT_"].location != NSNotFound) {
|
||||
else if (rpcError.errorCode == 420 || [rpcError.errorDescription rangeOfString:@"FLOOD_WAIT_"].location != NSNotFound || [rpcError.errorDescription rangeOfString:@"FLOOD_PREMIUM_WAIT_"].location != NSNotFound) {
|
||||
if (request.errorContext == nil)
|
||||
request.errorContext = [[MTRequestErrorContext alloc] init];
|
||||
|
||||
@ -821,6 +821,32 @@
|
||||
if ([scanner scanInt:&errorWaitTime])
|
||||
{
|
||||
request.errorContext.floodWaitSeconds = errorWaitTime;
|
||||
request.errorContext.floodWaitErrorText = rpcError.errorDescription;
|
||||
|
||||
if (request.shouldContinueExecutionWithErrorContext != nil)
|
||||
{
|
||||
if (request.shouldContinueExecutionWithErrorContext(request.errorContext))
|
||||
{
|
||||
restartRequest = true;
|
||||
request.errorContext.minimalExecuteTime = MAX(request.errorContext.minimalExecuteTime, MTAbsoluteSystemTime() + (CFAbsoluteTime)errorWaitTime);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
restartRequest = true;
|
||||
request.errorContext.minimalExecuteTime = MAX(request.errorContext.minimalExecuteTime, MTAbsoluteSystemTime() + (CFAbsoluteTime)errorWaitTime);
|
||||
}
|
||||
}
|
||||
} else if ([rpcError.errorDescription rangeOfString:@"FLOOD_PREMIUM_WAIT_"].location != NSNotFound) {
|
||||
int errorWaitTime = 0;
|
||||
|
||||
NSScanner *scanner = [[NSScanner alloc] initWithString:rpcError.errorDescription];
|
||||
[scanner scanUpToString:@"FLOOD_PREMIUM_WAIT_" intoString:nil];
|
||||
[scanner scanString:@"FLOOD_PREMIUM_WAIT_" intoString:nil];
|
||||
if ([scanner scanInt:&errorWaitTime])
|
||||
{
|
||||
request.errorContext.floodWaitSeconds = errorWaitTime;
|
||||
request.errorContext.floodWaitErrorText = rpcError.errorDescription;
|
||||
|
||||
if (request.shouldContinueExecutionWithErrorContext != nil)
|
||||
{
|
||||
|
@ -2999,6 +2999,16 @@ final class PostboxImpl {
|
||||
|
||||
let startTime = CFAbsoluteTimeGetCurrent()
|
||||
|
||||
#if os(macOS)
|
||||
#if DEBUG || BETA
|
||||
var crashDisposable: Disposable?
|
||||
crashDisposable = (Signal<Void, NoError>.single(Void())
|
||||
|> delay(0.1, queue: .concurrentDefaultQueue())).startStandalone(next: { _ in
|
||||
preconditionFailure()
|
||||
})
|
||||
#endif
|
||||
#endif
|
||||
|
||||
self.valueBox.begin()
|
||||
let transaction = Transaction(queue: self.queue, postbox: self)
|
||||
self.afterBegin(transaction: transaction)
|
||||
@ -3013,6 +3023,12 @@ final class PostboxImpl {
|
||||
postboxLog("Postbox transaction took \(transactionDuration * 1000.0) ms, from: \(file), on:\(line)")
|
||||
}
|
||||
|
||||
#if os(macOS)
|
||||
#if DEBUG || BETA
|
||||
crashDisposable?.dispose()
|
||||
#endif
|
||||
#endif
|
||||
|
||||
let _ = self.isInTransaction.swap(false)
|
||||
|
||||
if let currentUpdatedState = self.currentUpdatedState {
|
||||
|
@ -19,8 +19,9 @@ public func printOpenFiles() {
|
||||
var flags: Int32 = 0
|
||||
var fd: Int32 = 0
|
||||
var buf = Data(count: Int(MAXPATHLEN) + 1)
|
||||
let maxFd = min(1024, FD_SETSIZE)
|
||||
|
||||
while fd < FD_SETSIZE {
|
||||
while fd < maxFd {
|
||||
errno = 0;
|
||||
flags = fcntl(fd, F_GETFD, 0);
|
||||
if flags == -1 && errno != 0 {
|
||||
|
@ -116,6 +116,7 @@ swift_library(
|
||||
"//submodules/TelegramUI/Components/ListActionItemComponent",
|
||||
"//submodules/TelegramUI/Components/EmojiStatusSelectionComponent",
|
||||
"//submodules/TelegramUI/Components/EntityKeyboard",
|
||||
"//submodules/TelegramUI/Components/PremiumPeerShortcutComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
Binary file not shown.
BIN
submodules/PremiumUI/Resources/coin.scn
Normal file
BIN
submodules/PremiumUI/Resources/coin.scn
Normal file
Binary file not shown.
@ -301,14 +301,13 @@ private final class BusinessListComponent: CombinedComponent {
|
||||
let strings = context.component.context.sharedContext.currentPresentationData.with { $0 }.strings
|
||||
|
||||
let colors = [
|
||||
UIColor(rgb: 0x007aff),
|
||||
UIColor(rgb: 0x798aff),
|
||||
UIColor(rgb: 0xac64f3),
|
||||
UIColor(rgb: 0xc456ae),
|
||||
UIColor(rgb: 0xe95d44),
|
||||
UIColor(rgb: 0xf2822a),
|
||||
UIColor(rgb: 0xe79519),
|
||||
UIColor(rgb: 0xe7ad19)
|
||||
UIColor(rgb: 0xef6922),
|
||||
UIColor(rgb: 0xe54937),
|
||||
UIColor(rgb: 0xdb374b),
|
||||
UIColor(rgb: 0xbc4395),
|
||||
UIColor(rgb: 0x9b4fed),
|
||||
UIColor(rgb: 0x8958ff),
|
||||
UIColor(rgb: 0x676bff)
|
||||
]
|
||||
|
||||
let titleColor = theme.list.itemPrimaryTextColor
|
||||
@ -397,6 +396,20 @@ private final class BusinessListComponent: CombinedComponent {
|
||||
)
|
||||
)
|
||||
|
||||
items.append(
|
||||
AnyComponentWithIdentity(
|
||||
id: "intro",
|
||||
component: AnyComponent(ParagraphComponent(
|
||||
title: strings.Premium_Business_Intro_Title,
|
||||
titleColor: titleColor,
|
||||
text: strings.Premium_Business_Intro_Text,
|
||||
textColor: textColor,
|
||||
iconName: "Premium/Business/Intro",
|
||||
iconColor: colors[5]
|
||||
))
|
||||
)
|
||||
)
|
||||
|
||||
items.append(
|
||||
AnyComponentWithIdentity(
|
||||
id: "chatbots",
|
||||
@ -406,7 +419,7 @@ private final class BusinessListComponent: CombinedComponent {
|
||||
text: strings.Premium_Business_Chatbots_Text,
|
||||
textColor: textColor,
|
||||
iconName: "Premium/Business/Chatbots",
|
||||
iconColor: colors[5]
|
||||
iconColor: colors[6]
|
||||
))
|
||||
)
|
||||
)
|
||||
|
@ -20,6 +20,7 @@ import SolidRoundedButtonComponent
|
||||
import BlurredBackgroundComponent
|
||||
import UndoUI
|
||||
import ConfettiEffect
|
||||
import PremiumPeerShortcutComponent
|
||||
|
||||
func requiredBoostSubjectLevel(subject: BoostSubject, group: Bool, context: AccountContext, configuration: PremiumConfiguration) -> Int32 {
|
||||
switch subject {
|
||||
@ -55,22 +56,12 @@ func requiredBoostSubjectLevel(subject: BoostSubject, group: Bool, context: Acco
|
||||
return configuration.minGroupAudioTranscriptionLevel
|
||||
case .emojiPack:
|
||||
return configuration.minGroupEmojiPackLevel
|
||||
case .noAds:
|
||||
return 30
|
||||
}
|
||||
}
|
||||
|
||||
public enum BoostSubject: Equatable {
|
||||
case stories
|
||||
case channelReactions(reactionCount: Int32)
|
||||
case nameColors(colors: PeerNameColor)
|
||||
case nameIcon
|
||||
case profileColors(colors: PeerNameColor)
|
||||
case profileIcon
|
||||
case emojiStatus
|
||||
case wallpaper
|
||||
case customWallpaper
|
||||
case audioTranscription
|
||||
case emojiPack
|
||||
|
||||
extension BoostSubject {
|
||||
public func requiredLevel(group: Bool, context: AccountContext, configuration: PremiumConfiguration) -> Int32 {
|
||||
return requiredBoostSubjectLevel(subject: self, group: group, context: context, configuration: configuration)
|
||||
}
|
||||
@ -247,6 +238,7 @@ private final class LevelSectionComponent: CombinedComponent {
|
||||
case customWallpaper
|
||||
case audioTranscription
|
||||
case emojiPack
|
||||
case noAds
|
||||
|
||||
func title(strings: PresentationStrings, isGroup: Bool) -> String {
|
||||
switch self {
|
||||
@ -274,6 +266,8 @@ private final class LevelSectionComponent: CombinedComponent {
|
||||
return strings.GroupBoost_Table_Group_VoiceToText
|
||||
case .emojiPack:
|
||||
return strings.GroupBoost_Table_Group_EmojiPack
|
||||
case .noAds:
|
||||
return strings.ChannelBoost_Table_NoAds
|
||||
}
|
||||
}
|
||||
|
||||
@ -303,6 +297,8 @@ private final class LevelSectionComponent: CombinedComponent {
|
||||
return "Premium/BoostPerk/AudioTranscription"
|
||||
case .emojiPack:
|
||||
return "Premium/BoostPerk/EmojiPack"
|
||||
case .noAds:
|
||||
return "Premium/BoostPerk/NoAds"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -639,6 +635,8 @@ private final class SheetContent: CombinedComponent {
|
||||
textString = ""
|
||||
case .emojiPack:
|
||||
textString = strings.GroupBoost_EnableEmojiPackLevelText("\(requiredLevel)").string
|
||||
case .noAds:
|
||||
textString = strings.ChannelBoost_EnableNoAdsLevelText("\(requiredLevel)").string
|
||||
}
|
||||
} else {
|
||||
let boostsString = strings.ChannelBoost_MoreBoostsNeeded_Boosts(Int32(remaining))
|
||||
@ -733,11 +731,10 @@ private final class SheetContent: CombinedComponent {
|
||||
let peerShortcut = peerShortcut.update(
|
||||
component: Button(
|
||||
content: AnyComponent(
|
||||
PeerShortcutComponent(
|
||||
PremiumPeerShortcutComponent(
|
||||
context: component.context,
|
||||
theme: component.theme,
|
||||
peer: peer
|
||||
|
||||
)
|
||||
),
|
||||
action: {
|
||||
@ -1095,85 +1092,100 @@ private final class SheetContent: CombinedComponent {
|
||||
isFeatures = true
|
||||
}
|
||||
|
||||
if let nextLevels {
|
||||
for level in nextLevels {
|
||||
var perks: [LevelSectionComponent.Perk] = []
|
||||
|
||||
perks.append(.story(level))
|
||||
|
||||
if !isGroup {
|
||||
perks.append(.reaction(level))
|
||||
}
|
||||
|
||||
var nameColorsCount: Int32 = 0
|
||||
for (colorLevel, count) in nameColorsAtLevel {
|
||||
if level >= colorLevel && colorLevel == 1 {
|
||||
nameColorsCount = count
|
||||
}
|
||||
}
|
||||
if !isGroup && nameColorsCount > 0 {
|
||||
perks.append(.nameColor(nameColorsCount))
|
||||
}
|
||||
|
||||
var profileColorsCount: Int32 = 0
|
||||
for (colorLevel, count) in profileColorsAtLevel {
|
||||
if level >= colorLevel {
|
||||
profileColorsCount += count
|
||||
}
|
||||
}
|
||||
if profileColorsCount > 0 {
|
||||
perks.append(.profileColor(profileColorsCount))
|
||||
}
|
||||
|
||||
func layoutLevel(_ level: Int32) {
|
||||
var perks: [LevelSectionComponent.Perk] = []
|
||||
|
||||
if isGroup && level >= requiredBoostSubjectLevel(subject: .emojiPack, group: isGroup, context: component.context, configuration: premiumConfiguration) {
|
||||
perks.append(.emojiPack)
|
||||
}
|
||||
perks.append(.story(level))
|
||||
|
||||
if level >= requiredBoostSubjectLevel(subject: .profileIcon, group: isGroup, context: component.context, configuration: premiumConfiguration) {
|
||||
perks.append(.profileIcon)
|
||||
if !isGroup {
|
||||
perks.append(.reaction(level))
|
||||
}
|
||||
|
||||
var nameColorsCount: Int32 = 0
|
||||
for (colorLevel, count) in nameColorsAtLevel {
|
||||
if level >= colorLevel && colorLevel == 1 {
|
||||
nameColorsCount = count
|
||||
}
|
||||
|
||||
if isGroup && level >= requiredBoostSubjectLevel(subject: .audioTranscription, group: isGroup, context: component.context, configuration: premiumConfiguration) {
|
||||
perks.append(.audioTranscription)
|
||||
}
|
||||
if !isGroup && nameColorsCount > 0 {
|
||||
perks.append(.nameColor(nameColorsCount))
|
||||
}
|
||||
|
||||
var profileColorsCount: Int32 = 0
|
||||
for (colorLevel, count) in profileColorsAtLevel {
|
||||
if level >= colorLevel {
|
||||
profileColorsCount += count
|
||||
}
|
||||
|
||||
var linkColorsCount: Int32 = 0
|
||||
for (colorLevel, count) in nameColorsAtLevel {
|
||||
if level >= colorLevel {
|
||||
linkColorsCount += count
|
||||
}
|
||||
}
|
||||
if profileColorsCount > 0 {
|
||||
perks.append(.profileColor(profileColorsCount))
|
||||
}
|
||||
|
||||
if isGroup && level >= requiredBoostSubjectLevel(subject: .emojiPack, group: isGroup, context: component.context, configuration: premiumConfiguration) {
|
||||
perks.append(.emojiPack)
|
||||
}
|
||||
|
||||
if level >= requiredBoostSubjectLevel(subject: .profileIcon, group: isGroup, context: component.context, configuration: premiumConfiguration) {
|
||||
perks.append(.profileIcon)
|
||||
}
|
||||
|
||||
if isGroup && level >= requiredBoostSubjectLevel(subject: .audioTranscription, group: isGroup, context: component.context, configuration: premiumConfiguration) {
|
||||
perks.append(.audioTranscription)
|
||||
}
|
||||
|
||||
var linkColorsCount: Int32 = 0
|
||||
for (colorLevel, count) in nameColorsAtLevel {
|
||||
if level >= colorLevel {
|
||||
linkColorsCount += count
|
||||
}
|
||||
if !isGroup && linkColorsCount > 0 {
|
||||
perks.append(.linkColor(linkColorsCount))
|
||||
}
|
||||
|
||||
if !isGroup && level >= requiredBoostSubjectLevel(subject: .nameIcon, group: isGroup, context: component.context, configuration: premiumConfiguration) {
|
||||
perks.append(.linkIcon)
|
||||
}
|
||||
if level >= requiredBoostSubjectLevel(subject: .emojiStatus, group: isGroup, context: component.context, configuration: premiumConfiguration) {
|
||||
perks.append(.emojiStatus)
|
||||
}
|
||||
if level >= requiredBoostSubjectLevel(subject: .wallpaper, group: isGroup, context: component.context, configuration: premiumConfiguration) {
|
||||
perks.append(.wallpaper(8))
|
||||
}
|
||||
if level >= requiredBoostSubjectLevel(subject: .customWallpaper, group: isGroup, context: component.context, configuration: premiumConfiguration) {
|
||||
perks.append(.customWallpaper)
|
||||
}
|
||||
|
||||
levelItems.append(
|
||||
AnyComponentWithIdentity(
|
||||
id: level, component: AnyComponent(
|
||||
LevelSectionComponent(
|
||||
theme: component.theme,
|
||||
strings: component.strings,
|
||||
level: level,
|
||||
isFirst: !isFeatures && levelItems.isEmpty,
|
||||
perks: perks.reversed(),
|
||||
isGroup: isGroup
|
||||
)
|
||||
}
|
||||
if !isGroup && linkColorsCount > 0 {
|
||||
perks.append(.linkColor(linkColorsCount))
|
||||
}
|
||||
|
||||
if !isGroup && level >= requiredBoostSubjectLevel(subject: .nameIcon, group: isGroup, context: component.context, configuration: premiumConfiguration) {
|
||||
perks.append(.linkIcon)
|
||||
}
|
||||
if level >= requiredBoostSubjectLevel(subject: .emojiStatus, group: isGroup, context: component.context, configuration: premiumConfiguration) {
|
||||
perks.append(.emojiStatus)
|
||||
}
|
||||
if level >= requiredBoostSubjectLevel(subject: .wallpaper, group: isGroup, context: component.context, configuration: premiumConfiguration) {
|
||||
perks.append(.wallpaper(8))
|
||||
}
|
||||
if level >= requiredBoostSubjectLevel(subject: .customWallpaper, group: isGroup, context: component.context, configuration: premiumConfiguration) {
|
||||
perks.append(.customWallpaper)
|
||||
}
|
||||
if !isGroup && level >= requiredBoostSubjectLevel(subject: .noAds, group: isGroup, context: component.context, configuration: premiumConfiguration) {
|
||||
perks.append(.noAds)
|
||||
}
|
||||
|
||||
levelItems.append(
|
||||
AnyComponentWithIdentity(
|
||||
id: level, component: AnyComponent(
|
||||
LevelSectionComponent(
|
||||
theme: component.theme,
|
||||
strings: component.strings,
|
||||
level: level,
|
||||
isFirst: !isFeatures && levelItems.isEmpty,
|
||||
perks: perks.reversed(),
|
||||
isGroup: isGroup
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if let nextLevels {
|
||||
for level in nextLevels {
|
||||
layoutLevel(level)
|
||||
}
|
||||
}
|
||||
|
||||
if !isGroup {
|
||||
let noAdsLevel = requiredBoostSubjectLevel(subject: .noAds, group: false, context: component.context, configuration: premiumConfiguration)
|
||||
if level < noAdsLevel {
|
||||
layoutLevel(noAdsLevel)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1443,6 +1455,8 @@ private final class BoostLevelsContainerComponent: CombinedComponent {
|
||||
titleString = strings.GroupBoost_AudioTranscription
|
||||
case .emojiPack:
|
||||
titleString = strings.GroupBoost_EmojiPack
|
||||
case .noAds:
|
||||
titleString = strings.ChannelBoost_NoAds
|
||||
}
|
||||
} else {
|
||||
titleString = isGroup == true ? strings.GroupBoost_Title_Current : strings.ChannelBoost_Title_Current
|
||||
|
@ -8,7 +8,7 @@ import GZip
|
||||
import AppBundle
|
||||
import LegacyComponents
|
||||
|
||||
private let sceneVersion: Int = 2
|
||||
private let sceneVersion: Int = 3
|
||||
|
||||
private func deg2rad(_ number: Float) -> Float {
|
||||
return number * .pi / 180
|
||||
|
@ -1424,6 +1424,7 @@ public class PremiumDemoScreen: ViewControllerComponentContainer {
|
||||
case businessQuickReplies
|
||||
case businessAwayMessage
|
||||
case businessChatBots
|
||||
case businessIntro
|
||||
}
|
||||
|
||||
public enum Source: Equatable {
|
||||
|
@ -456,6 +456,7 @@ public enum PremiumPerk: CaseIterable {
|
||||
case businessQuickReplies
|
||||
case businessAwayMessage
|
||||
case businessChatBots
|
||||
case businessIntro
|
||||
|
||||
public static var allCases: [PremiumPerk] {
|
||||
return [
|
||||
@ -488,10 +489,11 @@ public enum PremiumPerk: CaseIterable {
|
||||
return [
|
||||
.businessLocation,
|
||||
.businessHours,
|
||||
.businessGreetingMessage,
|
||||
.businessQuickReplies,
|
||||
.businessGreetingMessage,
|
||||
.businessAwayMessage,
|
||||
.businessChatBots,
|
||||
.businessIntro,
|
||||
.businessChatBots
|
||||
// .emojiStatus,
|
||||
// .folderTags,
|
||||
// .stories,
|
||||
@ -567,6 +569,8 @@ public enum PremiumPerk: CaseIterable {
|
||||
return "away_message"
|
||||
case .businessChatBots:
|
||||
return "business_bots"
|
||||
case .businessIntro:
|
||||
return "business_intro"
|
||||
}
|
||||
}
|
||||
|
||||
@ -629,6 +633,8 @@ public enum PremiumPerk: CaseIterable {
|
||||
return strings.Business_AwayMessages
|
||||
case .businessChatBots:
|
||||
return strings.Business_Chatbots
|
||||
case .businessIntro:
|
||||
return strings.Business_Intro
|
||||
}
|
||||
}
|
||||
|
||||
@ -691,6 +697,8 @@ public enum PremiumPerk: CaseIterable {
|
||||
return strings.Business_AwayMessagesInfo
|
||||
case .businessChatBots:
|
||||
return strings.Business_ChatbotsInfo
|
||||
case .businessIntro:
|
||||
return strings.Business_IntroInfo
|
||||
}
|
||||
}
|
||||
|
||||
@ -753,6 +761,8 @@ public enum PremiumPerk: CaseIterable {
|
||||
return "Premium/BusinessPerk/Away"
|
||||
case .businessChatBots:
|
||||
return "Premium/BusinessPerk/Chatbots"
|
||||
case .businessIntro:
|
||||
return "Premium/BusinessPerk/Intro"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -782,12 +792,13 @@ struct PremiumIntroConfiguration {
|
||||
.premiumStickers,
|
||||
.business
|
||||
], businessPerks: [
|
||||
.businessLocation,
|
||||
.businessHours,
|
||||
.businessQuickReplies,
|
||||
.businessGreetingMessage,
|
||||
.businessAwayMessage,
|
||||
.businessQuickReplies,
|
||||
.businessChatBots,
|
||||
.businessHours,
|
||||
.businessLocation
|
||||
.businessIntro,
|
||||
.businessChatBots
|
||||
// .emojiStatus,
|
||||
// .folderTags,
|
||||
// .stories
|
||||
@ -853,6 +864,9 @@ struct PremiumIntroConfiguration {
|
||||
if businessPerks.count < 4 {
|
||||
businessPerks = PremiumIntroConfiguration.defaultValue.businessPerks
|
||||
}
|
||||
if !businessPerks.contains(.businessIntro) {
|
||||
businessPerks.append(.businessIntro)
|
||||
}
|
||||
|
||||
return PremiumIntroConfiguration(perks: perks, businessPerks: businessPerks)
|
||||
} else {
|
||||
@ -2134,6 +2148,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
||||
UIColor(rgb: 0xdb374b),
|
||||
UIColor(rgb: 0xbc4395),
|
||||
UIColor(rgb: 0x9b4fed),
|
||||
UIColor(rgb: 0x8958ff),
|
||||
UIColor(rgb: 0x8958ff)
|
||||
]
|
||||
|
||||
@ -2244,6 +2259,8 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
||||
demoSubject = .businessAwayMessage
|
||||
case .businessChatBots:
|
||||
demoSubject = .businessChatBots
|
||||
case .businessIntro:
|
||||
demoSubject = .businessIntro
|
||||
default:
|
||||
fatalError()
|
||||
}
|
||||
@ -3243,7 +3260,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
||||
if let emojiFile = state?.emojiFile, let controller = environment?.controller() as? PremiumIntroScreen, let navigationController = controller.navigationController as? NavigationController {
|
||||
for attribute in emojiFile.attributes {
|
||||
if case let .CustomEmoji(_, _, _, packReference) = attribute, let packReference = packReference {
|
||||
let controller = accountContext.sharedContext.makeStickerPackScreen(context: accountContext, updatedPresentationData: nil, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: loadedEmojiPack.flatMap { [$0] } ?? [], parentNavigationController: navigationController, sendSticker: { _, _, _ in
|
||||
let controller = accountContext.sharedContext.makeStickerPackScreen(context: accountContext, updatedPresentationData: nil, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: loadedEmojiPack.flatMap { [$0] } ?? [], isEditing: false, parentNavigationController: navigationController, sendSticker: { _, _, _ in
|
||||
return false
|
||||
})
|
||||
presentController(controller)
|
||||
|
@ -19,6 +19,7 @@ import ConfettiEffect
|
||||
import AvatarNode
|
||||
import TextFormat
|
||||
import RoundedRectWithTailPath
|
||||
import PremiumPeerShortcutComponent
|
||||
|
||||
func generateCloseButtonImage(backgroundColor: UIColor, foregroundColor: UIColor) -> UIImage? {
|
||||
return generateImage(CGSize(width: 30.0, height: 30.0), contextGenerator: { size, context in
|
||||
@ -1140,7 +1141,7 @@ private final class LimitSheetContent: CombinedComponent {
|
||||
peerShortcutChild = peerShortcut.update(
|
||||
component: Button(
|
||||
content: AnyComponent(
|
||||
PeerShortcutComponent(
|
||||
PremiumPeerShortcutComponent(
|
||||
context: component.context,
|
||||
theme: environment.theme,
|
||||
peer: peer
|
||||
@ -1894,103 +1895,6 @@ public class PremiumLimitScreen: ViewControllerComponentContainer {
|
||||
}
|
||||
}
|
||||
|
||||
final class PeerShortcutComponent: Component {
|
||||
let context: AccountContext
|
||||
let theme: PresentationTheme
|
||||
let peer: EnginePeer
|
||||
|
||||
init(context: AccountContext, theme: PresentationTheme, peer: EnginePeer) {
|
||||
self.context = context
|
||||
self.theme = theme
|
||||
self.peer = peer
|
||||
}
|
||||
|
||||
static func ==(lhs: PeerShortcutComponent, rhs: PeerShortcutComponent) -> Bool {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.peer != rhs.peer {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class View: UIView {
|
||||
private let backgroundView = UIView()
|
||||
private let avatarNode: AvatarNode
|
||||
private let text = ComponentView<Empty>()
|
||||
|
||||
private var component: PeerShortcutComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 18.0))
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.backgroundView.clipsToBounds = true
|
||||
self.backgroundView.layer.cornerRadius = 16.0
|
||||
|
||||
self.addSubview(self.backgroundView)
|
||||
self.addSubnode(self.avatarNode)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func update(component: PeerShortcutComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||
self.component = component
|
||||
self.state = state
|
||||
|
||||
self.backgroundView.backgroundColor = component.theme.list.itemBlocksSeparatorColor.withAlphaComponent(0.3)
|
||||
|
||||
self.avatarNode.frame = CGRect(origin: CGPoint(x: 1.0, y: 1.0), size: CGSize(width: 30.0, height: 30.0))
|
||||
self.avatarNode.setPeer(
|
||||
context: component.context,
|
||||
theme: component.context.sharedContext.currentPresentationData.with({ $0 }).theme,
|
||||
peer: component.peer,
|
||||
synchronousLoad: true
|
||||
)
|
||||
|
||||
let textSize = self.text.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: component.peer.compactDisplayTitle, font: Font.medium(15.0), textColor: component.theme.list.itemPrimaryTextColor, paragraphAlignment: .left))
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - 50.0, height: availableSize.height)
|
||||
)
|
||||
|
||||
let size = CGSize(width: 30.0 + textSize.width + 20.0, height: 32.0)
|
||||
if let view = self.text.view {
|
||||
if view.superview == nil {
|
||||
self.addSubview(view)
|
||||
}
|
||||
let textFrame = CGRect(origin: CGPoint(x: 38.0, y: floorToScreenPixels((size.height - textSize.height) / 2.0)), size: textSize)
|
||||
view.frame = textFrame
|
||||
}
|
||||
|
||||
self.backgroundView.frame = CGRect(origin: .zero, size: size)
|
||||
|
||||
return size
|
||||
}
|
||||
}
|
||||
|
||||
func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
public final class BoostIconComponent: Component {
|
||||
let hasIcon: Bool
|
||||
let text: String
|
||||
|
@ -979,6 +979,26 @@ public class PremiumLimitsListScreen: ViewController {
|
||||
)
|
||||
)
|
||||
|
||||
availableItems[.businessIntro] = DemoPagerComponent.Item(
|
||||
AnyComponentWithIdentity(
|
||||
id: PremiumDemoScreen.Subject.businessIntro,
|
||||
component: AnyComponent(
|
||||
PageComponent(
|
||||
content: AnyComponent(PhoneDemoComponent(
|
||||
context: context,
|
||||
position: .top,
|
||||
model: .island,
|
||||
videoFile: videos["business_intro"],
|
||||
decoration: .business
|
||||
)),
|
||||
title: strings.Business_Intro,
|
||||
text: strings.Business_IntroInfo,
|
||||
textColor: textColor
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
if let order = controller.order {
|
||||
var items: [DemoPagerComponent.Item] = order.compactMap { availableItems[$0] }
|
||||
let initialIndex: Int
|
||||
|
@ -82,6 +82,7 @@ public final class QrCodeScanScreen: ViewController {
|
||||
public enum Subject {
|
||||
case authTransfer(activeSessionsContext: ActiveSessionsContext)
|
||||
case peer
|
||||
case cryptoAddress
|
||||
case custom(info: String)
|
||||
}
|
||||
|
||||
@ -264,6 +265,8 @@ public final class QrCodeScanScreen: ViewController {
|
||||
strongSelf.controllerNode.updateFocusedRect(nil)
|
||||
}))
|
||||
}
|
||||
case .cryptoAddress:
|
||||
break
|
||||
case .peer:
|
||||
if let _ = URL(string: code) {
|
||||
strongSelf.controllerNode.resolveCode(code: code, completion: { [weak self] result in
|
||||
@ -479,6 +482,9 @@ private final class QrCodeScanScreenNode: ViewControllerTracingNode, UIScrollVie
|
||||
case .peer:
|
||||
title = ""
|
||||
text = ""
|
||||
case .cryptoAddress:
|
||||
title = ""
|
||||
text = ""
|
||||
case let .custom(info):
|
||||
title = presentationData.strings.AuthSessions_AddDevice_ScanTitle
|
||||
text = info
|
||||
@ -596,6 +602,8 @@ private final class QrCodeScanScreenNode: ViewControllerTracingNode, UIScrollVie
|
||||
filteredCodes = codes.filter { $0.message.hasPrefix("tg://") }
|
||||
case .peer:
|
||||
filteredCodes = codes.filter { $0.message.hasPrefix("https://t.me/") || $0.message.hasPrefix("t.me/") }
|
||||
case .cryptoAddress:
|
||||
filteredCodes = codes.filter { $0.message.hasPrefix("ton://") }
|
||||
case .custom:
|
||||
filteredCodes = codes
|
||||
}
|
||||
|
@ -1595,6 +1595,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
context.sharedContext.mainWindow?.presentInGlobalOverlay(actionSheet)
|
||||
}
|
||||
},
|
||||
editAction: { _ in },
|
||||
pushController: { _ in
|
||||
},
|
||||
presentController: { _ in
|
||||
@ -1728,6 +1729,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
isPremiumLocked: false,
|
||||
isEmbedded: false,
|
||||
hasClear: false,
|
||||
hasEdit: false,
|
||||
collapsedLineCount: nil,
|
||||
displayPremiumBadges: false,
|
||||
headerItem: nil,
|
||||
@ -1777,6 +1779,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
isPremiumLocked: false,
|
||||
isEmbedded: false,
|
||||
hasClear: false,
|
||||
hasEdit: false,
|
||||
collapsedLineCount: 3,
|
||||
displayPremiumBadges: false,
|
||||
headerItem: nil,
|
||||
@ -1836,6 +1839,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
isPremiumLocked: false,
|
||||
isEmbedded: false,
|
||||
hasClear: false,
|
||||
hasEdit: false,
|
||||
collapsedLineCount: nil,
|
||||
displayPremiumBadges: false,
|
||||
headerItem: nil,
|
||||
@ -1868,6 +1872,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
isPremiumLocked: false,
|
||||
isEmbedded: false,
|
||||
hasClear: false,
|
||||
hasEdit: false,
|
||||
collapsedLineCount: nil,
|
||||
displayPremiumBadges: false,
|
||||
headerItem: nil,
|
||||
@ -2561,6 +2566,23 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
self.isExpandedUpdated(.animated(duration: 0.4, curve: .spring))
|
||||
}
|
||||
|
||||
public func collapse() {
|
||||
if self.hapticFeedback == nil {
|
||||
self.hapticFeedback = HapticFeedback()
|
||||
}
|
||||
self.hapticFeedback?.tap()
|
||||
|
||||
self.longPressRecognizer?.isEnabled = false
|
||||
|
||||
self.animateFromExtensionDistance = 0.0
|
||||
self.extensionDistance = 0.0
|
||||
self.visibleExtensionDistance = 0.0
|
||||
self.contentTopInset = self.titleLabelHeight ?? 0.0
|
||||
self.currentContentHeight = 46.0
|
||||
self.isExpanded = false
|
||||
self.isExpandedUpdated(.animated(duration: 0.4, curve: .spring))
|
||||
}
|
||||
|
||||
public func highlightGestureMoved(location: CGPoint, hover: Bool) {
|
||||
if self.allPresetReactionsAreAvailable {
|
||||
return
|
||||
|
@ -40,6 +40,9 @@ swift_library(
|
||||
"//submodules/TelegramUI/Components/AnimationCache",
|
||||
"//submodules/TelegramUI/Components/MultiAnimationRenderer",
|
||||
"//submodules/UndoUI",
|
||||
"//submodules/Components/MultilineTextComponent",
|
||||
"//submodules/Components/BundleIconComponent",
|
||||
"//submodules/TelegramUI/Components/LottieComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -8,6 +8,7 @@ public protocol ShareContentContainerNode: AnyObject {
|
||||
func activate()
|
||||
func deactivate()
|
||||
func setEnsurePeerVisibleOnLayout(_ peerId: EnginePeer.Id?)
|
||||
func setDidBeginDragging(_ f: (() -> Void)?)
|
||||
func setContentOffsetUpdated(_ f: ((CGFloat, ContainedViewLayoutTransition) -> Void)?)
|
||||
func updateLayout(size: CGSize, isLandscape: Bool, bottomInset: CGFloat, transition: ContainedViewLayoutTransition)
|
||||
func updateTheme(_ theme: PresentationTheme)
|
||||
|
@ -450,6 +450,7 @@ public final class ShareController: ViewController {
|
||||
private let immediatePeerId: PeerId?
|
||||
private let segmentedValues: [ShareControllerSegmentedValue]?
|
||||
private let fromForeignApp: Bool
|
||||
private let collectibleItemInfo: TelegramCollectibleItemInfo?
|
||||
|
||||
private let peers = Promise<([(peer: EngineRenderedPeer, presence: EnginePeer.Presence?, requiresPremiumForMessaging: Bool)], EnginePeer)>()
|
||||
private let peersDisposable = MetaDisposable()
|
||||
@ -484,7 +485,7 @@ public final class ShareController: ViewController {
|
||||
|
||||
public var parentNavigationController: NavigationController?
|
||||
|
||||
public convenience init(context: AccountContext, subject: ShareControllerSubject, presetText: String? = nil, preferredAction: ShareControllerPreferredAction = .default, showInChat: ((Message) -> Void)? = nil, fromForeignApp: Bool = false, segmentedValues: [ShareControllerSegmentedValue]? = nil, externalShare: Bool = true, immediateExternalShare: Bool = false, switchableAccounts: [AccountWithInfo] = [], immediatePeerId: PeerId? = nil, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, forceTheme: PresentationTheme? = nil, forcedActionTitle: String? = nil, shareAsLink: Bool = false) {
|
||||
public convenience init(context: AccountContext, subject: ShareControllerSubject, presetText: String? = nil, preferredAction: ShareControllerPreferredAction = .default, showInChat: ((Message) -> Void)? = nil, fromForeignApp: Bool = false, segmentedValues: [ShareControllerSegmentedValue]? = nil, externalShare: Bool = true, immediateExternalShare: Bool = false, switchableAccounts: [AccountWithInfo] = [], immediatePeerId: PeerId? = nil, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, forceTheme: PresentationTheme? = nil, forcedActionTitle: String? = nil, shareAsLink: Bool = false, collectibleItemInfo: TelegramCollectibleItemInfo? = nil) {
|
||||
self.init(
|
||||
environment: ShareControllerAppEnvironment(sharedContext: context.sharedContext),
|
||||
currentContext: ShareControllerAppAccountContext(context: context),
|
||||
@ -503,11 +504,12 @@ public final class ShareController: ViewController {
|
||||
updatedPresentationData: updatedPresentationData,
|
||||
forceTheme: forceTheme,
|
||||
forcedActionTitle: forcedActionTitle,
|
||||
shareAsLink: shareAsLink
|
||||
shareAsLink: shareAsLink,
|
||||
collectibleItemInfo: collectibleItemInfo
|
||||
)
|
||||
}
|
||||
|
||||
public init(environment: ShareControllerEnvironment, currentContext: ShareControllerAccountContext, subject: ShareControllerSubject, presetText: String? = nil, preferredAction: ShareControllerPreferredAction = .default, showInChat: ((Message) -> Void)? = nil, fromForeignApp: Bool = false, segmentedValues: [ShareControllerSegmentedValue]? = nil, externalShare: Bool = true, immediateExternalShare: Bool = false, switchableAccounts: [ShareControllerSwitchableAccount] = [], immediatePeerId: PeerId? = nil, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, forceTheme: PresentationTheme? = nil, forcedActionTitle: String? = nil, shareAsLink: Bool = false) {
|
||||
public init(environment: ShareControllerEnvironment, currentContext: ShareControllerAccountContext, subject: ShareControllerSubject, presetText: String? = nil, preferredAction: ShareControllerPreferredAction = .default, showInChat: ((Message) -> Void)? = nil, fromForeignApp: Bool = false, segmentedValues: [ShareControllerSegmentedValue]? = nil, externalShare: Bool = true, immediateExternalShare: Bool = false, switchableAccounts: [ShareControllerSwitchableAccount] = [], immediatePeerId: PeerId? = nil, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, forceTheme: PresentationTheme? = nil, forcedActionTitle: String? = nil, shareAsLink: Bool = false, collectibleItemInfo: TelegramCollectibleItemInfo? = nil) {
|
||||
self.environment = environment
|
||||
self.currentContext = currentContext
|
||||
self.subject = subject
|
||||
@ -520,6 +522,7 @@ public final class ShareController: ViewController {
|
||||
self.segmentedValues = segmentedValues
|
||||
self.forceTheme = forceTheme
|
||||
self.shareAsLink = shareAsLink
|
||||
self.collectibleItemInfo = collectibleItemInfo
|
||||
|
||||
self.presentationData = updatedPresentationData?.initial ?? environment.presentationData
|
||||
if let forceTheme = self.forceTheme {
|
||||
@ -717,7 +720,7 @@ public final class ShareController: ViewController {
|
||||
return
|
||||
}
|
||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: title, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
}, externalShare: self.externalShare, immediateExternalShare: self.immediateExternalShare, immediatePeerId: self.immediatePeerId, fromForeignApp: self.fromForeignApp, forceTheme: self.forceTheme, fromPublicChannel: fromPublicChannel, segmentedValues: self.segmentedValues, shareStory: self.shareStory)
|
||||
}, externalShare: self.externalShare, immediateExternalShare: self.immediateExternalShare, immediatePeerId: self.immediatePeerId, fromForeignApp: self.fromForeignApp, forceTheme: self.forceTheme, fromPublicChannel: fromPublicChannel, segmentedValues: self.segmentedValues, shareStory: self.shareStory, collectibleItemInfo: self.collectibleItemInfo)
|
||||
self.controllerNode.completed = self.completed
|
||||
self.controllerNode.enqueued = self.enqueued
|
||||
self.controllerNode.present = { [weak self] c in
|
||||
|
@ -9,6 +9,11 @@ import TelegramPresentationData
|
||||
import AccountContext
|
||||
import TelegramIntents
|
||||
import ContextUI
|
||||
import ComponentFlow
|
||||
import MultilineTextComponent
|
||||
import TelegramStringFormatting
|
||||
import BundleIconComponent
|
||||
import LottieComponent
|
||||
|
||||
enum ShareState {
|
||||
case preparing(Bool)
|
||||
@ -21,6 +26,276 @@ enum ShareExternalState {
|
||||
case done
|
||||
}
|
||||
|
||||
private final class ShareContentInfoView: UIView {
|
||||
private struct Params: Equatable {
|
||||
var environment: ShareControllerEnvironment
|
||||
var theme: PresentationTheme
|
||||
var strings: PresentationStrings
|
||||
var collectibleItemInfo: TelegramCollectibleItemInfo
|
||||
var availableSize: CGSize
|
||||
|
||||
init(environment: ShareControllerEnvironment, theme: PresentationTheme, strings: PresentationStrings, collectibleItemInfo: TelegramCollectibleItemInfo, availableSize: CGSize) {
|
||||
self.environment = environment
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.collectibleItemInfo = collectibleItemInfo
|
||||
self.availableSize = availableSize
|
||||
}
|
||||
|
||||
static func ==(lhs: Params, rhs: Params) -> Bool {
|
||||
if lhs.environment !== rhs.environment {
|
||||
return false
|
||||
}
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.strings !== rhs.strings {
|
||||
return false
|
||||
}
|
||||
if lhs.collectibleItemInfo != rhs.collectibleItemInfo {
|
||||
return false
|
||||
}
|
||||
if lhs.availableSize != rhs.availableSize {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private struct Layout {
|
||||
var params: Params
|
||||
var size: CGSize
|
||||
|
||||
init(params: Params, size: CGSize) {
|
||||
self.params = params
|
||||
self.size = size
|
||||
}
|
||||
}
|
||||
|
||||
private let icon = ComponentView<Empty>()
|
||||
private let text = ComponentView<Empty>()
|
||||
private var currencySymbolIcon: UIImage?
|
||||
private var arrowIcon: UIImage?
|
||||
private let backgroundView: BlurredBackgroundView
|
||||
|
||||
private var currentLayout: Layout?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
self.backgroundView = BlurredBackgroundView(color: nil, enableBlur: true)
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.addSubview(self.backgroundView)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func update(environment: ShareControllerEnvironment, presentationData: PresentationData, collectibleItemInfo: TelegramCollectibleItemInfo, availableSize: CGSize) -> CGSize {
|
||||
let params = Params(
|
||||
environment: environment,
|
||||
theme: presentationData.theme,
|
||||
strings: presentationData.strings,
|
||||
collectibleItemInfo: collectibleItemInfo,
|
||||
availableSize: availableSize
|
||||
)
|
||||
if let currentLayout = self.currentLayout, currentLayout.params == params {
|
||||
return currentLayout.size
|
||||
}
|
||||
let size = self.updateInternal(params: params)
|
||||
self.currentLayout = Layout(params: params, size: size)
|
||||
return size
|
||||
}
|
||||
|
||||
private func updateInternal(params: Params) -> CGSize {
|
||||
var username: String = ""
|
||||
if case let .username(value) = params.collectibleItemInfo.subject {
|
||||
username = value
|
||||
}
|
||||
|
||||
let textText = NSMutableAttributedString()
|
||||
|
||||
let dateText = stringForDate(timestamp: params.collectibleItemInfo.purchaseDate, strings: params.strings)
|
||||
|
||||
let (rawCryptoCurrencyText, cryptoCurrencySign, _) = formatCurrencyAmountCustom(params.collectibleItemInfo.cryptoCurrencyAmount, currency: params.collectibleItemInfo.cryptoCurrency, customFormat: CurrencyFormatterEntry(
|
||||
symbol: "~",
|
||||
thousandsSeparator: ",",
|
||||
decimalSeparator: ".",
|
||||
symbolOnLeft: true,
|
||||
spaceBetweenAmountAndSymbol: false,
|
||||
decimalDigits: 9
|
||||
))
|
||||
var cryptoCurrencyText = rawCryptoCurrencyText
|
||||
while cryptoCurrencyText.hasSuffix("0") {
|
||||
cryptoCurrencyText = String(cryptoCurrencyText[cryptoCurrencyText.startIndex ..< cryptoCurrencyText.index(before: cryptoCurrencyText.endIndex)])
|
||||
}
|
||||
if cryptoCurrencyText.hasSuffix(".") {
|
||||
cryptoCurrencyText = String(cryptoCurrencyText[cryptoCurrencyText.startIndex ..< cryptoCurrencyText.index(before: cryptoCurrencyText.endIndex)])
|
||||
}
|
||||
|
||||
let (currencyText, currencySign, _) = formatCurrencyAmountCustom(params.collectibleItemInfo.currencyAmount, currency: params.collectibleItemInfo.currency)
|
||||
|
||||
let rawTextString = params.strings.CollectibleItemInfo_UsernameText("@\(username)", params.strings.CollectibleItemInfo_StoreName, dateText, "\(cryptoCurrencySign)\(cryptoCurrencyText)", "\(currencySign)\(currencyText)")
|
||||
textText.append(NSAttributedString(string: rawTextString.string, font: Font.regular(14.0), textColor: .white))
|
||||
for range in rawTextString.ranges {
|
||||
switch range.index {
|
||||
case 0:
|
||||
textText.addAttribute(.font, value: Font.semibold(14.0), range: range.range)
|
||||
case 1:
|
||||
textText.addAttribute(.font, value: Font.semibold(14.0), range: range.range)
|
||||
case 3:
|
||||
textText.addAttribute(.font, value: Font.semibold(14.0), range: range.range)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
let currencySymbolRange = (textText.string as NSString).range(of: "~")
|
||||
|
||||
if self.currencySymbolIcon == nil {
|
||||
if let templateImage = UIImage(bundleImageName: "Peer Info/CollectibleTonSymbolInline") {
|
||||
self.currencySymbolIcon = generateImage(CGSize(width: templateImage.size.width, height: templateImage.size.height + 2.0), contextGenerator: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
if let cgImage = templateImage.cgImage {
|
||||
context.draw(cgImage, in: CGRect(origin: CGPoint(x: 0.0, y: 4.0), size: CGSize(width: templateImage.size.width - 2.0, height: templateImage.size.height - 2.0)))
|
||||
}
|
||||
})?.withRenderingMode(.alwaysTemplate)
|
||||
}
|
||||
}
|
||||
|
||||
if currencySymbolRange.location != NSNotFound, let currencySymbolIcon = self.currencySymbolIcon {
|
||||
textText.replaceCharacters(in: currencySymbolRange, with: "$")
|
||||
textText.addAttribute(.attachment, value: currencySymbolIcon, range: currencySymbolRange)
|
||||
|
||||
final class RunDelegateData {
|
||||
let ascent: CGFloat
|
||||
let descent: CGFloat
|
||||
let width: CGFloat
|
||||
|
||||
init(ascent: CGFloat, descent: CGFloat, width: CGFloat) {
|
||||
self.ascent = ascent
|
||||
self.descent = descent
|
||||
self.width = width
|
||||
}
|
||||
}
|
||||
let font = Font.semibold(14.0)
|
||||
let runDelegateData = RunDelegateData(
|
||||
ascent: font.ascender,
|
||||
descent: font.descender,
|
||||
width: currencySymbolIcon.size.width + 4.0
|
||||
)
|
||||
var callbacks = CTRunDelegateCallbacks(
|
||||
version: kCTRunDelegateCurrentVersion,
|
||||
dealloc: { dataRef in
|
||||
Unmanaged<RunDelegateData>.fromOpaque(dataRef).release()
|
||||
},
|
||||
getAscent: { dataRef in
|
||||
let data = Unmanaged<RunDelegateData>.fromOpaque(dataRef)
|
||||
return data.takeUnretainedValue().ascent
|
||||
},
|
||||
getDescent: { dataRef in
|
||||
let data = Unmanaged<RunDelegateData>.fromOpaque(dataRef)
|
||||
return data.takeUnretainedValue().descent
|
||||
},
|
||||
getWidth: { dataRef in
|
||||
let data = Unmanaged<RunDelegateData>.fromOpaque(dataRef)
|
||||
return data.takeUnretainedValue().width
|
||||
}
|
||||
)
|
||||
|
||||
if let runDelegate = CTRunDelegateCreate(&callbacks, Unmanaged.passRetained(runDelegateData).toOpaque()) {
|
||||
textText.addAttribute(NSAttributedString.Key(rawValue: kCTRunDelegateAttributeName as String), value: runDelegate, range: currencySymbolRange)
|
||||
}
|
||||
}
|
||||
|
||||
let accentColor = params.theme.list.itemAccentColor.withMultiplied(hue: 0.933, saturation: 0.61, brightness: 1.0)
|
||||
if self.arrowIcon == nil {
|
||||
if let templateImage = UIImage(bundleImageName: "Item List/InlineTextRightArrow") {
|
||||
let scaleFactor: CGFloat = 0.8
|
||||
let imageSize = CGSize(width: floor(templateImage.size.width * scaleFactor), height: floor(templateImage.size.height * scaleFactor))
|
||||
self.arrowIcon = generateImage(imageSize, contextGenerator: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
if let cgImage = templateImage.cgImage {
|
||||
context.draw(cgImage, in: CGRect(origin: CGPoint(), size: size))
|
||||
}
|
||||
})?.withRenderingMode(.alwaysTemplate)
|
||||
}
|
||||
}
|
||||
|
||||
textText.append(NSAttributedString(string: "\n\(params.strings.CollectibleItemInfo_ShareInlineText_LearnMore)", attributes: [
|
||||
.font: Font.medium(14.0),
|
||||
.foregroundColor: accentColor,
|
||||
NSAttributedString.Key(rawValue: "URL"): ""
|
||||
]))
|
||||
if let range = textText.string.range(of: ">"), let arrowIcon = self.arrowIcon {
|
||||
textText.addAttribute(.attachment, value: arrowIcon, range: NSRange(range, in: textText.string))
|
||||
}
|
||||
|
||||
let textInsets = UIEdgeInsets(top: 8.0, left: 50.0, bottom: 8.0, right: 10.0)
|
||||
|
||||
let textSize = self.text.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(textText),
|
||||
maximumNumberOfLines: 0,
|
||||
lineSpacing: 0.185,
|
||||
highlightColor: accentColor.withMultipliedAlpha(0.1),
|
||||
highlightAction: { attributes in
|
||||
if let _ = attributes[NSAttributedString.Key(rawValue: "URL")] {
|
||||
return NSAttributedString.Key(rawValue: "URL")
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
},
|
||||
tapAction: { [weak self] _, _ in
|
||||
guard let self, let params = self.currentLayout?.params else {
|
||||
return
|
||||
}
|
||||
if let environment = params.environment as? ShareControllerAppEnvironment {
|
||||
environment.sharedContext.applicationBindings.openUrl(params.collectibleItemInfo.url)
|
||||
}
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: params.availableSize.width - textInsets.left - textInsets.right, height: 1000.0)
|
||||
)
|
||||
let textFrame = CGRect(origin: CGPoint(x: textInsets.left, y: textInsets.top), size: textSize)
|
||||
if let textView = self.text.view {
|
||||
if textView.superview == nil {
|
||||
self.addSubview(textView)
|
||||
}
|
||||
textView.frame = textFrame
|
||||
}
|
||||
|
||||
let size = CGSize(width: params.availableSize.width, height: textInsets.top + textSize.height + textInsets.bottom)
|
||||
|
||||
let iconSize = self.icon.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(LottieComponent(
|
||||
content: LottieComponent.AppBundleContent(name: "ToastCollectibleUsernameEmoji"),
|
||||
loop: false
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 30.0, height: 30.0)
|
||||
)
|
||||
let iconFrame = CGRect(origin: CGPoint(x: floor((textInsets.left - iconSize.width) * 0.5), y: floor((size.height - iconSize.height) * 0.5)), size: iconSize)
|
||||
if let iconView = self.icon.view as? LottieComponent.View {
|
||||
if iconView.superview == nil {
|
||||
self.addSubview(iconView)
|
||||
iconView.playOnce(delay: 0.1)
|
||||
}
|
||||
iconView.frame = iconFrame
|
||||
}
|
||||
|
||||
self.backgroundView.updateColor(color: UIColor(rgb: 0x1C2023), transition: .immediate)
|
||||
self.backgroundView.update(size: size, cornerRadius: 16.0, transition: .immediate)
|
||||
self.backgroundView.frame = CGRect(origin: CGPoint(), size: size)
|
||||
|
||||
return size
|
||||
}
|
||||
}
|
||||
|
||||
final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate {
|
||||
private weak var controller: ShareController?
|
||||
private let environment: ShareControllerEnvironment
|
||||
@ -33,6 +308,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
private let fromForeignApp: Bool
|
||||
private let fromPublicChannel: Bool
|
||||
private let segmentedValues: [ShareControllerSegmentedValue]?
|
||||
private let collectibleItemInfo: TelegramCollectibleItemInfo?
|
||||
var selectedSegmentedIndex: Int = 0
|
||||
|
||||
private let defaultAction: ShareControllerAction?
|
||||
@ -48,6 +324,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
|
||||
private let contentContainerNode: ASDisplayNode
|
||||
private let contentBackgroundNode: ASImageNode
|
||||
private var contentInfoView: ShareContentInfoView?
|
||||
|
||||
private var contentNode: (ASDisplayNode & ShareContentContainerNode)?
|
||||
private var previousContentNode: (ASDisplayNode & ShareContentContainerNode)?
|
||||
@ -90,7 +367,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
|
||||
private let showNames = ValuePromise<Bool>(true)
|
||||
|
||||
init(controller: ShareController, environment: ShareControllerEnvironment, presentationData: PresentationData, presetText: String?, defaultAction: ShareControllerAction?, requestLayout: @escaping (ContainedViewLayoutTransition) -> Void, presentError: @escaping (String?, String) -> Void, externalShare: Bool, immediateExternalShare: Bool, immediatePeerId: PeerId?, fromForeignApp: Bool, forceTheme: PresentationTheme?, fromPublicChannel: Bool, segmentedValues: [ShareControllerSegmentedValue]?, shareStory: (() -> Void)?) {
|
||||
init(controller: ShareController, environment: ShareControllerEnvironment, presentationData: PresentationData, presetText: String?, defaultAction: ShareControllerAction?, requestLayout: @escaping (ContainedViewLayoutTransition) -> Void, presentError: @escaping (String?, String) -> Void, externalShare: Bool, immediateExternalShare: Bool, immediatePeerId: PeerId?, fromForeignApp: Bool, forceTheme: PresentationTheme?, fromPublicChannel: Bool, segmentedValues: [ShareControllerSegmentedValue]?, shareStory: (() -> Void)?, collectibleItemInfo: TelegramCollectibleItemInfo?) {
|
||||
self.controller = controller
|
||||
self.environment = environment
|
||||
self.presentationData = presentationData
|
||||
@ -102,6 +379,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
self.presentError = presentError
|
||||
self.fromPublicChannel = fromPublicChannel
|
||||
self.segmentedValues = segmentedValues
|
||||
self.collectibleItemInfo = collectibleItemInfo
|
||||
|
||||
self.presetText = presetText
|
||||
|
||||
@ -156,6 +434,8 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
self.contentBackgroundNode.displayWithoutProcessing = true
|
||||
self.contentBackgroundNode.image = roundedBackground
|
||||
|
||||
self.contentInfoView = ShareContentInfoView(frame: CGRect())
|
||||
|
||||
self.actionsBackgroundNode = ASImageNode()
|
||||
self.actionsBackgroundNode.isLayerBacked = true
|
||||
self.actionsBackgroundNode.displayWithoutProcessing = true
|
||||
@ -356,6 +636,10 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
|
||||
self.wrappingScrollNode.addSubnode(self.contentBackgroundNode)
|
||||
|
||||
if let contentInfoView = self.contentInfoView {
|
||||
self.wrappingScrollNode.view.addSubview(contentInfoView)
|
||||
}
|
||||
|
||||
self.wrappingScrollNode.addSubnode(self.contentContainerNode)
|
||||
self.contentContainerNode.addSubnode(self.actionSeparatorNode)
|
||||
self.contentContainerNode.addSubnode(self.actionsBackgroundNode)
|
||||
@ -433,12 +717,14 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
}
|
||||
|
||||
if let searchContentNode = strongSelf.contentNode as? ShareSearchContainerNode {
|
||||
searchContentNode.setDidBeginDragging(nil)
|
||||
searchContentNode.setContentOffsetUpdated(nil)
|
||||
let scrollDelta = topicsContentNode.contentGridNode.scrollView.contentOffset.y - searchContentNode.contentGridNode.scrollView.contentOffset.y
|
||||
if let sourceFrame = searchContentNode.animateOut(peerId: peer.peerId, scrollDelta: scrollDelta) {
|
||||
topicsContentNode.animateIn(sourceFrame: sourceFrame, scrollDelta: scrollDelta)
|
||||
}
|
||||
} else if let peersContentNode = strongSelf.peersContentNode {
|
||||
peersContentNode.setDidBeginDragging(nil)
|
||||
peersContentNode.setContentOffsetUpdated(nil)
|
||||
let scrollDelta = topicsContentNode.contentGridNode.scrollView.contentOffset.y - peersContentNode.contentGridNode.scrollView.contentOffset.y
|
||||
if let sourceFrame = peersContentNode.animateOut(peerId: peer.peerId, scrollDelta: scrollDelta) {
|
||||
@ -446,6 +732,9 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
}
|
||||
}
|
||||
|
||||
topicsContentNode.setDidBeginDragging({ [weak self] in
|
||||
self?.contentNodeDidBeginDragging()
|
||||
})
|
||||
topicsContentNode.setContentOffsetUpdated({ [weak self] contentOffset, transition in
|
||||
self?.contentNodeOffsetUpdated(contentOffset, transition: transition)
|
||||
})
|
||||
@ -459,6 +748,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
guard let topicsContentNode = self.topicsContentNode else {
|
||||
return
|
||||
}
|
||||
topicsContentNode.setDidBeginDragging(nil)
|
||||
topicsContentNode.setContentOffsetUpdated(nil)
|
||||
|
||||
if let searchContentNode = self.contentNode as? ShareSearchContainerNode {
|
||||
@ -472,6 +762,9 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
}
|
||||
|
||||
if let searchContentNode = self.contentNode as? ShareSearchContainerNode {
|
||||
searchContentNode.setDidBeginDragging({ [weak self] in
|
||||
self?.contentNodeDidBeginDragging()
|
||||
})
|
||||
searchContentNode.setContentOffsetUpdated({ [weak self] contentOffset, transition in
|
||||
self?.contentNodeOffsetUpdated(contentOffset, transition: transition)
|
||||
})
|
||||
@ -487,6 +780,9 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
})
|
||||
}
|
||||
} else if let peersContentNode = self.peersContentNode {
|
||||
peersContentNode.setDidBeginDragging({ [weak self] in
|
||||
self?.contentNodeDidBeginDragging()
|
||||
})
|
||||
peersContentNode.setContentOffsetUpdated({ [weak self] contentOffset, transition in
|
||||
self?.contentNodeOffsetUpdated(contentOffset, transition: transition)
|
||||
})
|
||||
@ -579,6 +875,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
|
||||
let previous = self.contentNode
|
||||
if let previous = previous {
|
||||
previous.setDidBeginDragging(nil)
|
||||
previous.setContentOffsetUpdated(nil)
|
||||
if animated {
|
||||
transition = .animated(duration: 0.4, curve: .spring)
|
||||
@ -597,6 +894,8 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
previous.removeFromSupernode()
|
||||
self.previousContentNode = nil
|
||||
}
|
||||
|
||||
self.contentNodeDidBeginDragging()
|
||||
} else {
|
||||
transition = .immediate
|
||||
}
|
||||
@ -607,6 +906,9 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
contentNode.frame = previous.frame
|
||||
contentNode.updateLayout(size: previous.bounds.size, isLandscape: layout.size.width > layout.size.height, bottomInset: bottomGridInset, transition: .immediate)
|
||||
|
||||
contentNode.setDidBeginDragging({ [weak self] in
|
||||
self?.contentNodeDidBeginDragging()
|
||||
})
|
||||
contentNode.setContentOffsetUpdated({ [weak self] contentOffset, transition in
|
||||
self?.contentNodeOffsetUpdated(contentOffset, transition: transition)
|
||||
})
|
||||
@ -635,6 +937,9 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
}
|
||||
} else {
|
||||
if let contentNode = self.contentNode {
|
||||
contentNode.setDidBeginDragging({ [weak self] in
|
||||
self?.contentNodeDidBeginDragging()
|
||||
})
|
||||
contentNode.setContentOffsetUpdated({ [weak self] contentOffset, transition in
|
||||
self?.contentNodeOffsetUpdated(contentOffset, transition: transition)
|
||||
})
|
||||
@ -737,6 +1042,13 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
}
|
||||
}
|
||||
|
||||
private func contentNodeDidBeginDragging() {
|
||||
if let contentInfoView = self.contentInfoView, contentInfoView.alpha != 0.0 {
|
||||
Transition.easeInOut(duration: 0.2).setAlpha(view: contentInfoView, alpha: 0.0)
|
||||
Transition.easeInOut(duration: 0.2).setScale(view: contentInfoView, scale: 0.5)
|
||||
}
|
||||
}
|
||||
|
||||
private func contentNodeOffsetUpdated(_ contentOffset: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
if let (layout, _, _) = self.containerLayout {
|
||||
var insets = layout.insets(options: [.statusBar, .input])
|
||||
@ -770,6 +1082,25 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
}
|
||||
transition.updateFrame(node: self.contentBackgroundNode, frame: backgroundFrame)
|
||||
|
||||
if let contentInfoView = self.contentInfoView, let collectibleItemInfo = self.collectibleItemInfo {
|
||||
let contentInfoSize = contentInfoView.update(
|
||||
environment: self.environment,
|
||||
presentationData: self.presentationData,
|
||||
collectibleItemInfo: collectibleItemInfo,
|
||||
availableSize: CGSize(width: backgroundFrame.width, height: 1000.0)
|
||||
)
|
||||
let contentInfoFrame = CGRect(origin: CGPoint(x: backgroundFrame.minX, y: backgroundFrame.minY - 8.0 - contentInfoSize.height), size: contentInfoSize)
|
||||
|
||||
if contentInfoView.bounds.isEmpty {
|
||||
if contentInfoFrame.minY < 0.0 {
|
||||
contentInfoView.alpha = 0.0
|
||||
}
|
||||
}
|
||||
|
||||
transition.updatePosition(layer: contentInfoView.layer, position: contentInfoFrame.center)
|
||||
transition.updateBounds(layer: contentInfoView.layer, bounds: CGRect(origin: CGPoint(), size: contentInfoFrame.size))
|
||||
}
|
||||
|
||||
if let animateContentNodeOffsetFromBackgroundOffset = self.animateContentNodeOffsetFromBackgroundOffset {
|
||||
self.animateContentNodeOffsetFromBackgroundOffset = nil
|
||||
let offset = backgroundFrame.minY - animateContentNodeOffsetFromBackgroundOffset
|
||||
@ -1019,6 +1350,11 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
}
|
||||
self.animatingOut = true
|
||||
|
||||
if let contentInfoView = self.contentInfoView, contentInfoView.alpha != 0.0 {
|
||||
Transition.easeInOut(duration: 0.2).setAlpha(view: contentInfoView, alpha: 0.0)
|
||||
Transition.easeInOut(duration: 0.2).setScale(view: contentInfoView, scale: 0.5)
|
||||
}
|
||||
|
||||
if self.contentNode != nil {
|
||||
var dimCompleted = false
|
||||
var offsetCompleted = false
|
||||
@ -1223,6 +1559,12 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
return result
|
||||
}
|
||||
if self.bounds.contains(point) {
|
||||
if let contentInfoView = self.contentInfoView, contentInfoView.alpha != 0.0 {
|
||||
if let result = contentInfoView.hitTest(self.view.convert(point, to: contentInfoView), with: event) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
if !self.contentBackgroundNode.bounds.contains(self.convert(point, to: self.contentBackgroundNode)) && !self.cancelButtonNode.bounds.contains(self.convert(point, to: self.cancelButtonNode)) {
|
||||
return self.dimNode.view
|
||||
}
|
||||
|
@ -94,6 +94,9 @@ public final class ShareLoadingContainerNode: ASDisplayNode, ShareContentContain
|
||||
public func setEnsurePeerVisibleOnLayout(_ peerId: EnginePeer.Id?) {
|
||||
}
|
||||
|
||||
public func setDidBeginDragging(_ f: (() -> Void)?) {
|
||||
}
|
||||
|
||||
public func setContentOffsetUpdated(_ f: ((CGFloat, ContainedViewLayoutTransition) -> Void)?) {
|
||||
self.contentOffsetUpdated = f
|
||||
}
|
||||
@ -308,6 +311,9 @@ public final class ShareProlongedLoadingContainerNode: ASDisplayNode, ShareConte
|
||||
public func setEnsurePeerVisibleOnLayout(_ peerId: EnginePeer.Id?) {
|
||||
}
|
||||
|
||||
public func setDidBeginDragging(_ f: (() -> Void)?) {
|
||||
}
|
||||
|
||||
public func setContentOffsetUpdated(_ f: ((CGFloat, ContainedViewLayoutTransition) -> Void)?) {
|
||||
self.contentOffsetUpdated = f
|
||||
}
|
||||
|
@ -126,6 +126,7 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode {
|
||||
|
||||
private let segmentedValues: [ShareControllerSegmentedValue]?
|
||||
|
||||
private var contentDidBeginDragging: (() -> Void)?
|
||||
private var contentOffsetUpdated: ((CGFloat, ContainedViewLayoutTransition) -> Void)?
|
||||
|
||||
var openSearch: (() -> Void)?
|
||||
@ -297,6 +298,10 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode {
|
||||
strongSelf.enqueueTransition(transition, firstTime: firstTime)
|
||||
}
|
||||
}))
|
||||
|
||||
self.contentGridNode.scrollingInitiated = { [weak self] in
|
||||
self?.contentDidBeginDragging?()
|
||||
}
|
||||
|
||||
self.contentGridNode.presentationLayoutUpdated = { [weak self] presentationLayout, transition in
|
||||
self?.gridPresentationLayoutUpdated(presentationLayout, transition: transition)
|
||||
@ -350,6 +355,10 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode {
|
||||
self.ensurePeerVisibleOnLayout = peerId
|
||||
}
|
||||
|
||||
func setDidBeginDragging(_ f: (() -> Void)?) {
|
||||
self.contentDidBeginDragging = f
|
||||
}
|
||||
|
||||
func setContentOffsetUpdated(_ f: ((CGFloat, ContainedViewLayoutTransition) -> Void)?) {
|
||||
self.contentOffsetUpdated = f
|
||||
}
|
||||
|
@ -194,6 +194,7 @@ final class ShareSearchContainerNode: ASDisplayNode, ShareContentContainerNode {
|
||||
private let searchNode: ShareSearchBarNode
|
||||
private let cancelButtonNode: HighlightableButtonNode
|
||||
|
||||
private var contentDidBeginDragging: (() -> Void)?
|
||||
private var contentOffsetUpdated: ((CGFloat, ContainedViewLayoutTransition) -> Void)?
|
||||
|
||||
var cancel: (() -> Void)?
|
||||
@ -240,6 +241,14 @@ final class ShareSearchContainerNode: ASDisplayNode, ShareContentContainerNode {
|
||||
self.addSubnode(self.cancelButtonNode)
|
||||
self.addSubnode(self.contentSeparatorNode)
|
||||
|
||||
self.recentGridNode.scrollingInitiated = { [weak self] in
|
||||
self?.contentDidBeginDragging?()
|
||||
}
|
||||
|
||||
self.contentGridNode.scrollingInitiated = { [weak self] in
|
||||
self?.contentDidBeginDragging?()
|
||||
}
|
||||
|
||||
self.recentGridNode.presentationLayoutUpdated = { [weak self] presentationLayout, transition in
|
||||
if let strongSelf = self, !strongSelf.recentGridNode.isHidden {
|
||||
strongSelf.gridPresentationLayoutUpdated(presentationLayout, transition: transition)
|
||||
@ -466,6 +475,10 @@ final class ShareSearchContainerNode: ASDisplayNode, ShareContentContainerNode {
|
||||
self.ensurePeerVisibleOnLayout = peerId
|
||||
}
|
||||
|
||||
func setDidBeginDragging(_ f: (() -> Void)?) {
|
||||
self.contentDidBeginDragging = f
|
||||
}
|
||||
|
||||
func setContentOffsetUpdated(_ f: ((CGFloat, ContainedViewLayoutTransition) -> Void)?) {
|
||||
self.contentOffsetUpdated = f
|
||||
}
|
||||
|
@ -174,6 +174,7 @@ final class ShareTopicsContainerNode: ASDisplayNode, ShareContentContainerNode {
|
||||
private let contentSubtitleNode: ASTextNode
|
||||
private let backNode: CancelButtonNode
|
||||
|
||||
private var contentDidBeginDragging: (() -> Void)?
|
||||
private var contentOffsetUpdated: ((CGFloat, ContainedViewLayoutTransition) -> Void)?
|
||||
|
||||
private var validLayout: (CGSize, CGFloat)?
|
||||
@ -249,6 +250,10 @@ final class ShareTopicsContainerNode: ASDisplayNode, ShareContentContainerNode {
|
||||
strongSelf.enqueueTransition(transition, firstTime: firstTime)
|
||||
}
|
||||
}))
|
||||
|
||||
self.contentGridNode.scrollingInitiated = { [weak self] in
|
||||
self?.contentDidBeginDragging?()
|
||||
}
|
||||
|
||||
self.contentGridNode.presentationLayoutUpdated = { [weak self] presentationLayout, transition in
|
||||
self?.gridPresentationLayoutUpdated(presentationLayout, transition: transition)
|
||||
@ -286,6 +291,10 @@ final class ShareTopicsContainerNode: ASDisplayNode, ShareContentContainerNode {
|
||||
self.contentGridNode.transaction(GridNodeTransaction(deleteItems: transition.deletions, insertItems: transition.insertions, updateItems: transition.updates, scrollToItem: nil, updateLayout: nil, itemTransition: itemTransition, stationaryItems: .none, updateFirstIndexInSectionOffset: nil), completion: { _ in })
|
||||
}
|
||||
}
|
||||
|
||||
func setDidBeginDragging(_ f: (() -> Void)?) {
|
||||
self.contentDidBeginDragging = f
|
||||
}
|
||||
|
||||
func setContentOffsetUpdated(_ f: ((CGFloat, ContainedViewLayoutTransition) -> Void)?) {
|
||||
self.contentOffsetUpdated = f
|
||||
@ -458,8 +467,6 @@ final class ShareTopicsContainerNode: ASDisplayNode, ShareContentContainerNode {
|
||||
self.contentSubtitleNode.frame = originalSubtitleFrame
|
||||
transition.updateFrame(node: self.contentSubtitleNode, frame: subtitleFrame)
|
||||
|
||||
|
||||
|
||||
self.contentOffsetUpdated?(presentationLayout.contentOffset.y, actualTransition)
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,10 @@ public final class SolidRoundedButtonTheme: Equatable {
|
||||
self.disabledForegroundColor = disabledForegroundColor
|
||||
}
|
||||
|
||||
public func withUpdated(disabledBackgroundColor: UIColor, disabledForegroundColor: UIColor) -> SolidRoundedButtonTheme {
|
||||
return SolidRoundedButtonTheme(backgroundColor: self.backgroundColor, backgroundColors: self.backgroundColors, foregroundColor: self.foregroundColor, disabledBackgroundColor: disabledBackgroundColor, disabledForegroundColor: disabledForegroundColor)
|
||||
}
|
||||
|
||||
public static func ==(lhs: SolidRoundedButtonTheme, rhs: SolidRoundedButtonTheme) -> Bool {
|
||||
if lhs.backgroundColor != rhs.backgroundColor {
|
||||
return false
|
||||
|
@ -10,35 +10,41 @@ swift_library(
|
||||
"-warnings-as-errors",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
|
||||
"//submodules/Display:Display",
|
||||
"//submodules/SSignalKit/SwiftSignalKit",
|
||||
"//submodules/AsyncDisplayKit",
|
||||
"//submodules/Display",
|
||||
"//submodules/ComponentFlow",
|
||||
"//submodules/Postbox:Postbox",
|
||||
"//submodules/TelegramCore:TelegramCore",
|
||||
"//submodules/TelegramPresentationData:TelegramPresentationData",
|
||||
"//submodules/TelegramUIPreferences:TelegramUIPreferences",
|
||||
"//submodules/AccountContext:AccountContext",
|
||||
"//submodules/ItemListUI:ItemListUI",
|
||||
"//submodules/AvatarNode:AvatarNode",
|
||||
"//submodules/TelegramStringFormatting:TelegramStringFormatting",
|
||||
"//submodules/AlertUI:AlertUI",
|
||||
"//submodules/PresentationDataUtils:PresentationDataUtils",
|
||||
"//submodules/MergeLists:MergeLists",
|
||||
"//submodules/PhotoResources:PhotoResources",
|
||||
"//submodules/GraphCore:GraphCore",
|
||||
"//submodules/GraphUI:GraphUI",
|
||||
"//submodules/AnimatedStickerNode:AnimatedStickerNode",
|
||||
"//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode",
|
||||
"//submodules/ItemListPeerItem:ItemListPeerItem",
|
||||
"//submodules/ItemListPeerActionItem:ItemListPeerActionItem",
|
||||
"//submodules/ContextUI:ContextUI",
|
||||
"//submodules/PremiumUI:PremiumUI",
|
||||
"//submodules/InviteLinksUI:InviteLinksUI",
|
||||
"//submodules/ShareController:ShareController",
|
||||
"//submodules/Postbox",
|
||||
"//submodules/TelegramCore",
|
||||
"//submodules/TelegramPresentationData",
|
||||
"//submodules/TelegramUIPreferences",
|
||||
"//submodules/AccountContext",
|
||||
"//submodules/ItemListUI",
|
||||
"//submodules/AvatarNode",
|
||||
"//submodules/TelegramStringFormatting",
|
||||
"//submodules/AlertUI",
|
||||
"//submodules/PresentationDataUtils",
|
||||
"//submodules/MergeLists",
|
||||
"//submodules/PhotoResources",
|
||||
"//submodules/GraphCore",
|
||||
"//submodules/GraphUI",
|
||||
"//submodules/AnimatedStickerNode",
|
||||
"//submodules/TelegramAnimatedStickerNode",
|
||||
"//submodules/ItemListPeerItem",
|
||||
"//submodules/ItemListPeerActionItem",
|
||||
"//submodules/ContextUI",
|
||||
"//submodules/PremiumUI",
|
||||
"//submodules/InviteLinksUI",
|
||||
"//submodules/ShareController",
|
||||
"//submodules/TextFormat",
|
||||
"//submodules/TelegramUI/Components/Stories/AvatarStoryIndicatorComponent",
|
||||
"//submodules/TelegramUI/Components/Stories/StoryContainerScreen",
|
||||
"//submodules/TelegramUI/Components/PlainButtonComponent",
|
||||
"//submodules/TelegramUI/Components/EmojiTextAttachmentView",
|
||||
"//submodules/Components/MultilineTextComponent",
|
||||
"//submodules/Components/MultilineTextWithEntitiesComponent",
|
||||
"//submodules/QrCodeUI",
|
||||
"//submodules/UIKitRuntimeUtils",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
File diff suppressed because it is too large
Load Diff
322
submodules/StatisticsUI/Sources/CpmSliderItem.swift
Normal file
322
submodules/StatisticsUI/Sources/CpmSliderItem.swift
Normal file
@ -0,0 +1,322 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import AsyncDisplayKit
|
||||
import SwiftSignalKit
|
||||
import TelegramCore
|
||||
import TelegramUIPreferences
|
||||
import TelegramPresentationData
|
||||
import LegacyComponents
|
||||
import ItemListUI
|
||||
import PresentationDataUtils
|
||||
import EmojiTextAttachmentView
|
||||
import TextFormat
|
||||
import AccountContext
|
||||
import UIKitRuntimeUtils
|
||||
|
||||
final class CpmSliderItem: ListViewItem, ItemListItem {
|
||||
let context: AccountContext
|
||||
let theme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let value: Int32
|
||||
let animatedEmoji: TelegramMediaFile?
|
||||
let sectionId: ItemListSectionId
|
||||
let updated: (Int32) -> Void
|
||||
|
||||
init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, value: Int32, enabled: Bool, animatedEmoji: TelegramMediaFile?, sectionId: ItemListSectionId, updated: @escaping (Int32) -> Void) {
|
||||
self.context = context
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.value = value
|
||||
self.animatedEmoji = animatedEmoji
|
||||
self.sectionId = sectionId
|
||||
self.updated = updated
|
||||
}
|
||||
|
||||
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 = CpmSliderItemNode()
|
||||
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? CpmSliderItemNode {
|
||||
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 let allowedValues: [Int32] = [1, 2, 3, 4, 5]
|
||||
|
||||
class CpmSliderItemNode: ListViewItemNode {
|
||||
private let backgroundNode: ASDisplayNode
|
||||
private let topStripeNode: ASDisplayNode
|
||||
private let bottomStripeNode: ASDisplayNode
|
||||
private let maskNode: ASImageNode
|
||||
|
||||
private let minTextNode: TextNode
|
||||
private let maxTextNode: TextNode
|
||||
private let textNode: TextNode
|
||||
private var sliderView: TGPhotoEditorSliderView?
|
||||
private var animatedEmojiLayer: InlineStickerItemLayer?
|
||||
private var maxAnimatedEmojiLayer: InlineStickerItemLayer?
|
||||
|
||||
private var item: CpmSliderItem?
|
||||
private var layoutParams: ListViewItemLayoutParams?
|
||||
private var reportedValue: Int32?
|
||||
|
||||
init() {
|
||||
self.backgroundNode = ASDisplayNode()
|
||||
self.backgroundNode.isLayerBacked = true
|
||||
|
||||
self.maskNode = ASImageNode()
|
||||
|
||||
self.topStripeNode = ASDisplayNode()
|
||||
self.topStripeNode.isLayerBacked = true
|
||||
|
||||
self.bottomStripeNode = ASDisplayNode()
|
||||
self.bottomStripeNode.isLayerBacked = true
|
||||
|
||||
self.textNode = TextNode()
|
||||
self.textNode.isUserInteractionEnabled = false
|
||||
self.textNode.displaysAsynchronously = false
|
||||
|
||||
self.minTextNode = TextNode()
|
||||
self.minTextNode.isUserInteractionEnabled = false
|
||||
self.minTextNode.displaysAsynchronously = false
|
||||
|
||||
self.maxTextNode = TextNode()
|
||||
self.maxTextNode.isUserInteractionEnabled = false
|
||||
self.maxTextNode.displaysAsynchronously = false
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false)
|
||||
|
||||
self.addSubnode(self.textNode)
|
||||
self.addSubnode(self.minTextNode)
|
||||
self.addSubnode(self.maxTextNode)
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
self.view.disablesInteractiveTransitionGestureRecognizer = true
|
||||
|
||||
let sliderView = TGPhotoEditorSliderView()
|
||||
sliderView.enablePanHandling = true
|
||||
sliderView.trackCornerRadius = 2.0
|
||||
sliderView.lineSize = 4.0
|
||||
sliderView.dotSize = 5.0
|
||||
sliderView.minimumValue = 0.0
|
||||
sliderView.maximumValue = 1.0
|
||||
sliderView.startValue = 0.0
|
||||
sliderView.displayEdges = true
|
||||
sliderView.disablesInteractiveTransitionGestureRecognizer = true
|
||||
if let item = self.item, let params = self.layoutParams {
|
||||
sliderView.value = CGFloat(item.value) / 50.0
|
||||
sliderView.backgroundColor = item.theme.list.itemBlocksBackgroundColor
|
||||
sliderView.backColor = item.theme.list.itemSwitchColors.frameColor
|
||||
sliderView.startColor = item.theme.list.itemSwitchColors.frameColor
|
||||
sliderView.trackColor = item.theme.list.itemAccentColor
|
||||
sliderView.knobImage = PresentationResourcesItemList.knobImage(item.theme)
|
||||
|
||||
sliderView.frame = CGRect(origin: CGPoint(x: params.leftInset + 15.0, y: 37.0), size: CGSize(width: params.width - params.leftInset - params.rightInset - 15.0 * 2.0, height: 44.0))
|
||||
sliderView.hitTestEdgeInsets = UIEdgeInsets(top: -sliderView.frame.minX, left: 0.0, bottom: 0.0, right: -sliderView.frame.minX)
|
||||
}
|
||||
self.view.addSubview(sliderView)
|
||||
sliderView.addTarget(self, action: #selector(self.sliderValueChanged), for: .valueChanged)
|
||||
self.sliderView = sliderView
|
||||
}
|
||||
|
||||
func asyncLayout() -> (_ item: CpmSliderItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
|
||||
let currentItem = self.item
|
||||
let makeTextLayout = TextNode.asyncLayout(self.textNode)
|
||||
let makeMinTextLayout = TextNode.asyncLayout(self.minTextNode)
|
||||
let makeMaxTextLayout = TextNode.asyncLayout(self.maxTextNode)
|
||||
|
||||
return { item, params, neighbors in
|
||||
var themeUpdated = false
|
||||
if currentItem?.theme !== item.theme {
|
||||
themeUpdated = true
|
||||
}
|
||||
|
||||
let contentSize: CGSize
|
||||
let insets: UIEdgeInsets
|
||||
let separatorHeight = UIScreenPixel
|
||||
|
||||
//TODO:localize
|
||||
let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.value == 0 ? "No Ads" : "\(item.value) CPM", font: Font.regular(17.0), textColor: item.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .center, lineSpacing: 0.0, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (minTextLayout, minTextApply) = makeMinTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "No Ads", font: Font.regular(13.0), textColor: item.theme.list.itemSecondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .center, lineSpacing: 0.0, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (maxTextLayout, maxTextApply) = makeMaxTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "50 CPM", font: Font.regular(13.0), textColor: item.theme.list.itemSecondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .center, lineSpacing: 0.0, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
contentSize = CGSize(width: params.width, height: 88.0)
|
||||
insets = itemListNeighborsGroupedInsets(neighbors, params)
|
||||
|
||||
let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets)
|
||||
let layoutSize = layout.size
|
||||
|
||||
return (layout, { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.item = item
|
||||
strongSelf.layoutParams = params
|
||||
|
||||
strongSelf.backgroundNode.backgroundColor = item.theme.list.itemBlocksBackgroundColor
|
||||
strongSelf.topStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor
|
||||
strongSelf.bottomStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor
|
||||
|
||||
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 = 0.0
|
||||
bottomStripeOffset = -separatorHeight
|
||||
strongSelf.bottomStripeNode.isHidden = false
|
||||
default:
|
||||
bottomStripeInset = 0.0
|
||||
hasBottomCorners = true
|
||||
strongSelf.bottomStripeNode.isHidden = hasCorners
|
||||
bottomStripeOffset = 0.0
|
||||
}
|
||||
|
||||
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))
|
||||
|
||||
let _ = textApply()
|
||||
let textFrame = CGRect(origin: CGPoint(x: floor((params.width - textLayout.size.width) / 2.0), y: 12.0), size: textLayout.size)
|
||||
strongSelf.textNode.frame = textFrame
|
||||
|
||||
if let animatedEmoji = item.animatedEmoji {
|
||||
let itemSize = floorToScreenPixels(17.0 * 20.0 / 17.0)
|
||||
|
||||
var itemFrame = CGRect(origin: CGPoint(x: textFrame.minX - itemSize / 2.0 - 1.0, y: textFrame.midY), size: CGSize()).insetBy(dx: -itemSize / 2.0, dy: -itemSize / 2.0)
|
||||
itemFrame.origin.x = floorToScreenPixels(itemFrame.origin.x)
|
||||
itemFrame.origin.y = floorToScreenPixels(itemFrame.origin.y)
|
||||
|
||||
let itemLayer: InlineStickerItemLayer
|
||||
if let current = strongSelf.animatedEmojiLayer {
|
||||
itemLayer = current
|
||||
} else {
|
||||
let pointSize = floor(itemSize * 1.3)
|
||||
itemLayer = InlineStickerItemLayer(context: item.context, userLocation: .other, attemptSynchronousLoad: true, emoji: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: animatedEmoji.fileId.id, file: animatedEmoji, custom: nil), file: animatedEmoji, cache: item.context.animationCache, renderer: item.context.animationRenderer, placeholderColor: item.theme.list.mediaPlaceholderColor, pointSize: CGSize(width: pointSize, height: pointSize), dynamicColor: nil)
|
||||
strongSelf.animatedEmojiLayer = itemLayer
|
||||
strongSelf.layer.addSublayer(itemLayer)
|
||||
|
||||
itemLayer.isVisibleForAnimations = true
|
||||
}
|
||||
itemLayer.frame = itemFrame
|
||||
}
|
||||
|
||||
let _ = minTextApply()
|
||||
strongSelf.minTextNode.frame = CGRect(origin: CGPoint(x: params.leftInset + 16.0, y: 16.0), size: minTextLayout.size)
|
||||
|
||||
let _ = maxTextApply()
|
||||
let maxTextFrame = CGRect(origin: CGPoint(x: params.width - params.rightInset - 16.0 - maxTextLayout.size.width, y: 16.0), size: maxTextLayout.size)
|
||||
strongSelf.maxTextNode.frame = maxTextFrame
|
||||
|
||||
if let animatedEmoji = item.animatedEmoji {
|
||||
let itemSize = floorToScreenPixels(13.0 * 20.0 / 17.0)
|
||||
|
||||
var itemFrame = CGRect(origin: CGPoint(x: maxTextFrame.minX - itemSize / 2.0 - 1.0, y: maxTextFrame.midY), size: CGSize()).insetBy(dx: -itemSize / 2.0, dy: -itemSize / 2.0)
|
||||
itemFrame.origin.x = floorToScreenPixels(itemFrame.origin.x)
|
||||
itemFrame.origin.y = floorToScreenPixels(itemFrame.origin.y)
|
||||
|
||||
let itemLayer: InlineStickerItemLayer
|
||||
if let current = strongSelf.maxAnimatedEmojiLayer {
|
||||
itemLayer = current
|
||||
} else {
|
||||
let pointSize = floor(itemSize * 1.3)
|
||||
itemLayer = InlineStickerItemLayer(context: item.context, userLocation: .other, attemptSynchronousLoad: true, emoji: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: animatedEmoji.fileId.id, file: animatedEmoji, custom: nil), file: animatedEmoji, cache: item.context.animationCache, renderer: item.context.animationRenderer, placeholderColor: item.theme.list.mediaPlaceholderColor, pointSize: CGSize(width: pointSize, height: pointSize), dynamicColor: nil)
|
||||
strongSelf.maxAnimatedEmojiLayer = itemLayer
|
||||
strongSelf.layer.addSublayer(itemLayer)
|
||||
|
||||
itemLayer.isVisibleForAnimations = true
|
||||
|
||||
if let filter = makeMonochromeFilter() {
|
||||
filter.setValue([1.0, 1.0, 1.0, 1.0] as [NSNumber], forKey: "inputColor")
|
||||
filter.setValue(1.0 as NSNumber, forKey: "inputAmount")
|
||||
itemLayer.filters = [filter]
|
||||
}
|
||||
}
|
||||
itemLayer.frame = itemFrame
|
||||
}
|
||||
|
||||
if let sliderView = strongSelf.sliderView {
|
||||
if themeUpdated {
|
||||
sliderView.backgroundColor = item.theme.list.itemBlocksBackgroundColor
|
||||
sliderView.backColor = item.theme.list.itemSwitchColors.frameColor
|
||||
sliderView.startColor = item.theme.list.itemSwitchColors.frameColor
|
||||
sliderView.trackColor = item.theme.list.itemAccentColor
|
||||
sliderView.knobImage = PresentationResourcesItemList.knobImage(item.theme)
|
||||
}
|
||||
|
||||
sliderView.frame = CGRect(origin: CGPoint(x: params.leftInset + 15.0, y: 37.0), size: CGSize(width: params.width - params.leftInset - params.rightInset - 15.0 * 2.0, height: 44.0))
|
||||
sliderView.hitTestEdgeInsets = UIEdgeInsets(top: -sliderView.frame.minX, left: 0.0, bottom: 0.0, right: -sliderView.frame.minX)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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 item = self.item, let sliderView = self.sliderView else {
|
||||
return
|
||||
}
|
||||
item.updated(Int32(sliderView.value * 50.0))
|
||||
}
|
||||
}
|
@ -381,7 +381,7 @@ private enum StatsEntry: ItemListNodeEntry {
|
||||
let .topInvitersTitle(_, text, dates):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, accessoryText: ItemListSectionHeaderAccessoryText(value: dates, color: .generic), sectionId: self.section)
|
||||
case let .overview(_, stats):
|
||||
return StatsOverviewItem(presentationData: presentationData, isGroup: true, stats: stats, sectionId: self.section, style: .blocks)
|
||||
return StatsOverviewItem(context: arguments.context, presentationData: presentationData, isGroup: true, stats: stats, sectionId: self.section, style: .blocks)
|
||||
case let .growthGraph(_, _, _, graph, type),
|
||||
let .membersGraph(_, _, _, graph, type),
|
||||
let .newMembersBySourceGraph(_, _, _, graph, type),
|
||||
|
@ -160,7 +160,7 @@ private enum StatsEntry: ItemListNodeEntry {
|
||||
let .publicForwardsTitle(_, text):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||
case let .overview(_, stats, storyViews, publicShares):
|
||||
return StatsOverviewItem(presentationData: presentationData, isGroup: false, stats: stats as! Stats, storyViews: storyViews, publicShares: publicShares, sectionId: self.section, style: .blocks)
|
||||
return StatsOverviewItem(context: arguments.context, presentationData: presentationData, isGroup: false, stats: stats as! Stats, storyViews: storyViews, publicShares: publicShares, sectionId: self.section, style: .blocks)
|
||||
case let .interactionsGraph(_, _, _, graph, type, noInitialZoom), let .reactionsGraph(_, _, _, graph, type, noInitialZoom):
|
||||
return StatsGraphItem(presentationData: presentationData, graph: graph, type: type, noInitialZoom: noInitialZoom, getDetailsData: { date, completion in
|
||||
let _ = arguments.loadDetailedGraph(graph, Int64(date.timeIntervalSince1970) * 1000).start(next: { graph in
|
||||
|
488
submodules/StatisticsUI/Sources/MonetizationBalanceItem.swift
Normal file
488
submodules/StatisticsUI/Sources/MonetizationBalanceItem.swift
Normal file
@ -0,0 +1,488 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import AsyncDisplayKit
|
||||
import SwiftSignalKit
|
||||
import AccountContext
|
||||
import TelegramPresentationData
|
||||
import ItemListUI
|
||||
import SolidRoundedButtonNode
|
||||
import TelegramCore
|
||||
import EmojiTextAttachmentView
|
||||
import TextFormat
|
||||
|
||||
final class MonetizationBalanceItem: ListViewItem, ItemListItem {
|
||||
let context: AccountContext
|
||||
let presentationData: ItemListPresentationData
|
||||
let stats: MonetizationStats
|
||||
let animatedEmoji: TelegramMediaFile?
|
||||
let address: String
|
||||
let withdrawAction: () -> Void
|
||||
let qrAction: () -> Void
|
||||
let action: (() -> Void)?
|
||||
let textUpdated: (String) -> Void
|
||||
let shouldUpdateText: (String) -> Bool
|
||||
let processPaste: ((String) -> Void)?
|
||||
let sectionId: ItemListSectionId
|
||||
let style: ItemListStyle
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
presentationData: ItemListPresentationData,
|
||||
stats: MonetizationStats,
|
||||
animatedEmoji: TelegramMediaFile?,
|
||||
address: String,
|
||||
withdrawAction: @escaping () -> Void,
|
||||
qrAction: @escaping () -> Void,
|
||||
action: (() -> Void)?,
|
||||
textUpdated: @escaping (String) -> Void,
|
||||
shouldUpdateText: @escaping (String) -> Bool,
|
||||
processPaste: ((String) -> Void)?,
|
||||
sectionId: ItemListSectionId,
|
||||
style: ItemListStyle
|
||||
) {
|
||||
self.context = context
|
||||
self.presentationData = presentationData
|
||||
self.stats = stats
|
||||
self.animatedEmoji = animatedEmoji
|
||||
self.address = address
|
||||
self.withdrawAction = withdrawAction
|
||||
self.qrAction = qrAction
|
||||
self.action = action
|
||||
self.textUpdated = textUpdated
|
||||
self.shouldUpdateText = shouldUpdateText
|
||||
self.processPaste = processPaste
|
||||
self.sectionId = sectionId
|
||||
self.style = style
|
||||
}
|
||||
|
||||
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 = MonetizationBalanceItemNode()
|
||||
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? MonetizationBalanceItemNode {
|
||||
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()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var selectable: Bool = false
|
||||
}
|
||||
|
||||
final class MonetizationBalanceItemNode: ListViewItemNode, ItemListItemNode, ASEditableTextNodeDelegate {
|
||||
private let backgroundNode: ASDisplayNode
|
||||
private let topStripeNode: ASDisplayNode
|
||||
private let bottomStripeNode: ASDisplayNode
|
||||
private let maskNode: ASImageNode
|
||||
|
||||
private var animatedEmojiLayer: InlineStickerItemLayer?
|
||||
private let balanceTextNode: TextNode
|
||||
private let valueTextNode: TextNode
|
||||
|
||||
private let fieldNode: ASImageNode
|
||||
private let textClippingNode: ASDisplayNode
|
||||
private let textNode: EditableTextNode
|
||||
private let measureTextNode: TextNode
|
||||
|
||||
private let qrButtonNode: HighlightableButtonNode
|
||||
private var withdrawButtonNode: SolidRoundedButtonNode?
|
||||
|
||||
private let activateArea: AccessibilityAreaNode
|
||||
|
||||
private var item: MonetizationBalanceItem?
|
||||
|
||||
override var canBeSelected: Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
var tag: ItemListItemTag? {
|
||||
return self.item?.tag
|
||||
}
|
||||
|
||||
init() {
|
||||
self.backgroundNode = ASDisplayNode()
|
||||
self.backgroundNode.isLayerBacked = true
|
||||
self.backgroundNode.backgroundColor = .white
|
||||
|
||||
self.maskNode = ASImageNode()
|
||||
|
||||
self.topStripeNode = ASDisplayNode()
|
||||
self.topStripeNode.isLayerBacked = true
|
||||
|
||||
self.bottomStripeNode = ASDisplayNode()
|
||||
self.bottomStripeNode.isLayerBacked = true
|
||||
|
||||
self.balanceTextNode = TextNode()
|
||||
self.balanceTextNode.isUserInteractionEnabled = false
|
||||
self.balanceTextNode.displaysAsynchronously = false
|
||||
|
||||
self.valueTextNode = TextNode()
|
||||
self.valueTextNode.isUserInteractionEnabled = false
|
||||
self.valueTextNode.displaysAsynchronously = false
|
||||
|
||||
self.fieldNode = ASImageNode()
|
||||
self.fieldNode.displaysAsynchronously = false
|
||||
self.fieldNode.displayWithoutProcessing = true
|
||||
|
||||
self.textClippingNode = ASDisplayNode()
|
||||
self.textClippingNode.clipsToBounds = true
|
||||
|
||||
self.textNode = EditableTextNode()
|
||||
self.measureTextNode = TextNode()
|
||||
|
||||
self.qrButtonNode = HighlightableButtonNode()
|
||||
|
||||
self.activateArea = AccessibilityAreaNode()
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false)
|
||||
|
||||
self.addSubnode(self.balanceTextNode)
|
||||
self.addSubnode(self.valueTextNode)
|
||||
self.addSubnode(self.fieldNode)
|
||||
self.addSubnode(self.qrButtonNode)
|
||||
|
||||
self.textClippingNode.addSubnode(self.textNode)
|
||||
self.addSubnode(self.textClippingNode)
|
||||
|
||||
self.qrButtonNode.addTarget(self, action: #selector(self.qrButtonPressed), forControlEvents: .touchUpInside)
|
||||
}
|
||||
|
||||
override public func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
var textColor: UIColor = .black
|
||||
if let item = self.item {
|
||||
textColor = item.presentationData.theme.list.itemPrimaryTextColor
|
||||
self.textNode.typingAttributes = [NSAttributedString.Key.font.rawValue: Font.regular(item.presentationData.fontSize.itemListBaseFontSize), NSAttributedString.Key.foregroundColor.rawValue: textColor]
|
||||
} else {
|
||||
self.textNode.typingAttributes = [NSAttributedString.Key.font.rawValue: Font.regular(17.0), NSAttributedString.Key.foregroundColor.rawValue: textColor]
|
||||
}
|
||||
self.textNode.clipsToBounds = true
|
||||
self.textNode.delegate = self
|
||||
self.textNode.hitTestSlop = UIEdgeInsets(top: -5.0, left: -5.0, bottom: -5.0, right: -5.0)
|
||||
}
|
||||
|
||||
@objc private func qrButtonPressed() {
|
||||
guard let item = self.item else {
|
||||
return
|
||||
}
|
||||
item.qrAction()
|
||||
}
|
||||
|
||||
func asyncLayout() -> (_ item: MonetizationBalanceItem, _ params: ListViewItemLayoutParams, _ insets: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
|
||||
let currentItem = self.item
|
||||
let makeBalanceTextLayout = TextNode.asyncLayout(self.balanceTextNode)
|
||||
let makeValueTextLayout = TextNode.asyncLayout(self.valueTextNode)
|
||||
let makeTextLayout = TextNode.asyncLayout(self.measureTextNode)
|
||||
|
||||
return { item, params, neighbors in
|
||||
var updatedTheme: PresentationTheme?
|
||||
if currentItem?.presentationData.theme !== item.presentationData.theme {
|
||||
updatedTheme = item.presentationData.theme
|
||||
}
|
||||
|
||||
let contentSize: CGSize
|
||||
let insets: UIEdgeInsets
|
||||
let separatorHeight = UIScreenPixel
|
||||
let itemBackgroundColor: UIColor
|
||||
let itemSeparatorColor: UIColor
|
||||
|
||||
let leftInset = 16.0 + params.leftInset
|
||||
let rightInset = 16.0 + params.rightInset
|
||||
let constrainedWidth = params.width - leftInset - rightInset
|
||||
|
||||
let integralFont = Font.with(size: 48.0, design: .round, weight: .semibold)
|
||||
let fractionalFont = Font.with(size: 24.0, design: .round, weight: .semibold)
|
||||
|
||||
let cryptoValue = formatBalanceText(item.stats.availableBalance.cryptoAmount, decimalSeparator: item.presentationData.dateTimeFormat.decimalSeparator)
|
||||
|
||||
let amountString = amountAttributedString(cryptoValue, integralFont: integralFont, fractionalFont: fractionalFont, color: item.presentationData.theme.list.itemPrimaryTextColor)
|
||||
|
||||
let (balanceLayout, balanceApply) = makeBalanceTextLayout(TextNodeLayoutArguments(attributedString: amountString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .middle, constrainedSize: CGSize(width: constrainedWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let value = "≈$100"
|
||||
let (valueLayout, valueApply) = makeValueTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: value, font: Font.regular(17.0), textColor: item.presentationData.theme.list.itemSecondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .middle, constrainedSize: CGSize(width: constrainedWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
var measureText = item.address
|
||||
if measureText.hasSuffix("\n") || measureText.isEmpty {
|
||||
measureText += "|"
|
||||
}
|
||||
let attributedMeasureText = NSAttributedString(string: measureText, font: Font.regular(item.presentationData.fontSize.itemListBaseFontSize), textColor: .black)
|
||||
let attributedText = NSAttributedString(string: item.address, font: Font.regular(item.presentationData.fontSize.itemListBaseFontSize), textColor: item.presentationData.theme.list.itemPrimaryTextColor)
|
||||
let (textLayout, _) = makeTextLayout(TextNodeLayoutArguments(attributedString: attributedMeasureText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - rightInset - 12.0 - 36.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, lineSpacing: 0.0, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let attributedPlaceholderText = NSAttributedString(string: "Enter your TON address", font: Font.regular(item.presentationData.fontSize.itemListBaseFontSize), textColor: item.presentationData.theme.list.itemPlaceholderTextColor)
|
||||
|
||||
let verticalInset: CGFloat = 16.0
|
||||
let fieldHeight: CGFloat = max(52.0, textLayout.size.height + 32.0)
|
||||
let fieldSpacing: CGFloat = 16.0
|
||||
let buttonHeight: CGFloat = 50.0
|
||||
|
||||
var height: CGFloat = verticalInset * 2.0 + balanceLayout.size.height + 7.0
|
||||
if valueLayout.size.height > 0.0 {
|
||||
height += valueLayout.size.height
|
||||
height += fieldHeight + fieldSpacing + buttonHeight
|
||||
}
|
||||
|
||||
switch item.style {
|
||||
case .plain:
|
||||
itemBackgroundColor = item.presentationData.theme.list.plainBackgroundColor
|
||||
itemSeparatorColor = .clear
|
||||
insets = UIEdgeInsets()
|
||||
case .blocks:
|
||||
itemBackgroundColor = item.presentationData.theme.list.itemBlocksBackgroundColor
|
||||
itemSeparatorColor = item.presentationData.theme.list.itemBlocksSeparatorColor
|
||||
insets = itemListNeighborsGroupedInsets(neighbors, params)
|
||||
}
|
||||
|
||||
contentSize = CGSize(width: params.width, height: height)
|
||||
|
||||
let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets)
|
||||
|
||||
return (ListViewItemNodeLayout(contentSize: contentSize, insets: insets), { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.item = item
|
||||
|
||||
let _ = balanceApply()
|
||||
let _ = valueApply()
|
||||
|
||||
strongSelf.activateArea.frame = CGRect(origin: CGPoint(x: params.leftInset, y: 0.0), size: CGSize(width: params.width - params.leftInset - params.rightInset, height: layout.contentSize.height))
|
||||
strongSelf.activateArea.accessibilityTraits = []
|
||||
|
||||
if let _ = updatedTheme {
|
||||
strongSelf.topStripeNode.backgroundColor = itemSeparatorColor
|
||||
strongSelf.bottomStripeNode.backgroundColor = itemSeparatorColor
|
||||
strongSelf.backgroundNode.backgroundColor = itemBackgroundColor
|
||||
strongSelf.fieldNode.image = generateStretchableFilledCircleImage(diameter: 18.0, color: item.presentationData.theme.list.itemInputField.backgroundColor)
|
||||
|
||||
strongSelf.qrButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Settings/QrButtonIcon"), color: item.presentationData.theme.list.itemAccentColor), for: .normal)
|
||||
}
|
||||
|
||||
switch item.style {
|
||||
case .plain:
|
||||
if strongSelf.backgroundNode.supernode != nil {
|
||||
strongSelf.backgroundNode.removeFromSupernode()
|
||||
}
|
||||
if strongSelf.topStripeNode.supernode != nil {
|
||||
strongSelf.topStripeNode.removeFromSupernode()
|
||||
}
|
||||
if strongSelf.bottomStripeNode.supernode == nil {
|
||||
strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 0)
|
||||
}
|
||||
if strongSelf.maskNode.supernode != nil {
|
||||
strongSelf.maskNode.removeFromSupernode()
|
||||
}
|
||||
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: leftInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - leftInset, height: separatorHeight))
|
||||
case .blocks:
|
||||
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
|
||||
switch neighbors.bottom {
|
||||
case .sameSection(false):
|
||||
bottomStripeInset = leftInset
|
||||
strongSelf.bottomStripeNode.isHidden = false
|
||||
default:
|
||||
bottomStripeInset = 0.0
|
||||
hasBottomCorners = true
|
||||
strongSelf.bottomStripeNode.isHidden = hasCorners
|
||||
}
|
||||
|
||||
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.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: params.width, height: separatorHeight))
|
||||
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - bottomStripeInset, height: separatorHeight))
|
||||
}
|
||||
|
||||
var emojiItemFrame: CGRect = .zero
|
||||
var emojiItemSize: CGFloat = 0.0
|
||||
if let animatedEmoji = item.animatedEmoji {
|
||||
emojiItemSize = floorToScreenPixels(46.0 * 20.0 / 17.0)
|
||||
|
||||
emojiItemFrame = CGRect(origin: CGPoint(x: -emojiItemSize / 2.0 - 5.0, y: -3.0), size: CGSize()).insetBy(dx: -emojiItemSize / 2.0, dy: -emojiItemSize / 2.0)
|
||||
emojiItemFrame.origin.x = floorToScreenPixels(emojiItemFrame.origin.x)
|
||||
emojiItemFrame.origin.y = floorToScreenPixels(emojiItemFrame.origin.y)
|
||||
|
||||
let itemLayer: InlineStickerItemLayer
|
||||
if let current = strongSelf.animatedEmojiLayer {
|
||||
itemLayer = current
|
||||
} else {
|
||||
let pointSize = floor(emojiItemSize * 1.3)
|
||||
itemLayer = InlineStickerItemLayer(context: item.context, userLocation: .other, attemptSynchronousLoad: true, emoji: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: animatedEmoji.fileId.id, file: animatedEmoji, custom: nil), file: animatedEmoji, cache: item.context.animationCache, renderer: item.context.animationRenderer, placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, pointSize: CGSize(width: pointSize, height: pointSize), dynamicColor: nil)
|
||||
strongSelf.animatedEmojiLayer = itemLayer
|
||||
strongSelf.layer.addSublayer(itemLayer)
|
||||
|
||||
itemLayer.isVisibleForAnimations = true
|
||||
}
|
||||
}
|
||||
|
||||
let balanceTotalWidth: CGFloat = emojiItemSize + balanceLayout.size.width
|
||||
let balanceTextFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((params.width - balanceTotalWidth) / 2.0) + emojiItemSize, y: 13.0), size: balanceLayout.size)
|
||||
strongSelf.balanceTextNode.frame = balanceTextFrame
|
||||
strongSelf.animatedEmojiLayer?.frame = emojiItemFrame.offsetBy(dx: balanceTextFrame.minX, dy: balanceTextFrame.midY)
|
||||
|
||||
strongSelf.valueTextNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((params.width - valueLayout.size.width) / 2.0), y: balanceTextFrame.maxY - 5.0), size: valueLayout.size)
|
||||
|
||||
strongSelf.textNode.textView.autocapitalizationType = .none
|
||||
strongSelf.textNode.textView.autocorrectionType = .no
|
||||
strongSelf.textNode.textView.returnKeyType = .done
|
||||
|
||||
if let currentText = strongSelf.textNode.attributedText {
|
||||
if currentText.string != attributedText.string || updatedTheme != nil {
|
||||
strongSelf.textNode.attributedText = attributedText
|
||||
}
|
||||
} else {
|
||||
strongSelf.textNode.attributedText = attributedText
|
||||
}
|
||||
|
||||
if strongSelf.textNode.attributedPlaceholderText == nil || !strongSelf.textNode.attributedPlaceholderText!.isEqual(to: attributedPlaceholderText) {
|
||||
strongSelf.textNode.attributedPlaceholderText = attributedPlaceholderText
|
||||
}
|
||||
strongSelf.textNode.keyboardAppearance = item.presentationData.theme.rootController.keyboardColor.keyboardAppearance
|
||||
|
||||
let textTopInset: CGFloat = 108.0
|
||||
if strongSelf.animationForKey("apparentHeight") == 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.textClippingNode.frame = CGRect(origin: CGPoint(x: leftInset + 12.0, y: textTopInset + 15.0), size: CGSize(width: params.width - leftInset - rightInset - 12.0 - 36.0, height: textLayout.size.height))
|
||||
}
|
||||
strongSelf.textNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: params.width - leftInset - rightInset - 12.0 - 36.0, height: textLayout.size.height + 1.0))
|
||||
|
||||
let fieldFrame = CGRect(origin: CGPoint(x: leftInset, y: textTopInset), size: CGSize(width: params.width - leftInset - rightInset, height: fieldHeight))
|
||||
strongSelf.fieldNode.frame = fieldFrame
|
||||
|
||||
let qrButtonSize = CGSize(width: 32.0, height: 32.0)
|
||||
let qrButtonFrame = CGRect(origin: CGPoint(x: fieldFrame.maxX - qrButtonSize.width - 5.0, y: fieldFrame.midY - qrButtonSize.height / 2.0), size: qrButtonSize)
|
||||
strongSelf.qrButtonNode.frame = qrButtonFrame
|
||||
|
||||
let withdrawButtonNode: SolidRoundedButtonNode
|
||||
if let currentShareButtonNode = strongSelf.withdrawButtonNode {
|
||||
withdrawButtonNode = currentShareButtonNode
|
||||
} else {
|
||||
var buttonTheme = SolidRoundedButtonTheme(theme: item.presentationData.theme)
|
||||
buttonTheme = buttonTheme.withUpdated(disabledBackgroundColor: buttonTheme.backgroundColor, disabledForegroundColor: buttonTheme.foregroundColor.withAlphaComponent(0.6))
|
||||
withdrawButtonNode = SolidRoundedButtonNode(theme: buttonTheme, height: buttonHeight, cornerRadius: 11.0)
|
||||
withdrawButtonNode.pressed = { [weak self] in
|
||||
if let self, let item = self.item {
|
||||
item.withdrawAction()
|
||||
}
|
||||
}
|
||||
strongSelf.addSubnode(withdrawButtonNode)
|
||||
strongSelf.withdrawButtonNode = withdrawButtonNode
|
||||
}
|
||||
if cryptoValue != "0" {
|
||||
withdrawButtonNode.title = "Transfer \(cryptoValue) TON"
|
||||
}
|
||||
withdrawButtonNode.isEnabled = (strongSelf.textNode.attributedText?.string.count ?? 0) == walletAddressLength
|
||||
|
||||
let buttonWidth = contentSize.width - leftInset - rightInset
|
||||
let _ = withdrawButtonNode.updateLayout(width: buttonWidth, transition: .immediate)
|
||||
withdrawButtonNode.frame = CGRect(x: leftInset, y: fieldFrame.maxY + fieldSpacing, width: buttonWidth, height: buttonHeight)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
public func editableTextNode(_ editableTextNode: ASEditableTextNode, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
|
||||
if let item = self.item {
|
||||
if text.count > 1, let processPaste = item.processPaste {
|
||||
processPaste(text)
|
||||
return false
|
||||
}
|
||||
|
||||
if let action = item.action, text == "\n" {
|
||||
action()
|
||||
return false
|
||||
}
|
||||
|
||||
let newText = (editableTextNode.textView.text as NSString).replacingCharacters(in: range, with: text)
|
||||
if !item.shouldUpdateText(newText) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
public func editableTextNodeDidUpdateText(_ editableTextNode: ASEditableTextNode) {
|
||||
if let item = self.item {
|
||||
if let text = self.textNode.attributedText {
|
||||
let updatedText = text.string
|
||||
let updatedAttributedText = NSAttributedString(string: updatedText, font: Font.regular(item.presentationData.fontSize.itemListBaseFontSize), textColor: item.presentationData.theme.list.itemPrimaryTextColor)
|
||||
if text.string != updatedAttributedText.string {
|
||||
self.textNode.attributedText = updatedAttributedText
|
||||
}
|
||||
self.withdrawButtonNode?.isEnabled = (self.textNode.attributedText?.string.count ?? 0) == walletAddressLength
|
||||
item.textUpdated(updatedText)
|
||||
} else {
|
||||
item.textUpdated("")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func editableTextNodeShouldPaste(_ editableTextNode: ASEditableTextNode) -> Bool {
|
||||
if let _ = self.item {
|
||||
let text: String? = UIPasteboard.general.string
|
||||
if let _ = text {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override public func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) {
|
||||
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4)
|
||||
}
|
||||
|
||||
override public func animateAdded(_ currentTimestamp: Double, duration: Double) {
|
||||
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
}
|
||||
|
||||
override public func animateRemoved(_ currentTimestamp: Double, duration: Double) {
|
||||
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false)
|
||||
}
|
||||
}
|
591
submodules/StatisticsUI/Sources/MonetizationIntroScreen.swift
Normal file
591
submodules/StatisticsUI/Sources/MonetizationIntroScreen.swift
Normal file
@ -0,0 +1,591 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import ComponentFlow
|
||||
import SwiftSignalKit
|
||||
import TelegramCore
|
||||
import Markdown
|
||||
import TextFormat
|
||||
import TelegramPresentationData
|
||||
import ViewControllerComponent
|
||||
import SheetComponent
|
||||
import BundleIconComponent
|
||||
import BalancedTextComponent
|
||||
import MultilineTextComponent
|
||||
import MultilineTextWithEntitiesComponent
|
||||
import SolidRoundedButtonComponent
|
||||
import LottieComponent
|
||||
import AccountContext
|
||||
|
||||
private final class SheetContent: CombinedComponent {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
||||
let context: AccountContext
|
||||
let animatedEmojis: [String: TelegramMediaFile]
|
||||
let openMore: () -> Void
|
||||
let dismiss: () -> Void
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
animatedEmojis: [String: TelegramMediaFile],
|
||||
openMore: @escaping () -> Void,
|
||||
dismiss: @escaping () -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.animatedEmojis = animatedEmojis
|
||||
self.openMore = openMore
|
||||
self.dismiss = dismiss
|
||||
}
|
||||
|
||||
static func ==(lhs: SheetContent, rhs: SheetContent) -> Bool {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class State: ComponentState {
|
||||
var cachedIconImage: (UIImage, PresentationTheme)?
|
||||
var cachedChevronImage: (UIImage, PresentationTheme)?
|
||||
|
||||
let playOnce = ActionSlot<Void>()
|
||||
private var didPlayAnimation = false
|
||||
|
||||
func playAnimationIfNeeded() {
|
||||
guard !self.didPlayAnimation else {
|
||||
return
|
||||
}
|
||||
self.didPlayAnimation = true
|
||||
self.playOnce.invoke(Void())
|
||||
}
|
||||
}
|
||||
|
||||
func makeState() -> State {
|
||||
return State()
|
||||
}
|
||||
|
||||
static var body: Body {
|
||||
let iconBackground = Child(Image.self)
|
||||
let icon = Child(BundleIconComponent.self)
|
||||
|
||||
let title = Child(BalancedTextComponent.self)
|
||||
let list = Child(List<Empty>.self)
|
||||
let actionButton = Child(SolidRoundedButtonComponent.self)
|
||||
|
||||
let infoBackground = Child(RoundedRectangle.self)
|
||||
let infoTitle = Child(MultilineTextWithEntitiesComponent.self)
|
||||
let infoText = Child(MultilineTextComponent.self)
|
||||
|
||||
return { context in
|
||||
let environment = context.environment[EnvironmentType.self]
|
||||
let component = context.component
|
||||
let state = context.state
|
||||
|
||||
let theme = environment.theme
|
||||
// let strings = environment.strings
|
||||
|
||||
let sideInset: CGFloat = 16.0 + environment.safeInsets.left
|
||||
let textSideInset: CGFloat = 30.0 + environment.safeInsets.left
|
||||
|
||||
let titleFont = Font.semibold(20.0)
|
||||
let textFont = Font.regular(15.0)
|
||||
|
||||
let textColor = theme.actionSheet.primaryTextColor
|
||||
let secondaryTextColor = theme.actionSheet.secondaryTextColor
|
||||
let linkColor = theme.actionSheet.controlAccentColor
|
||||
|
||||
let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: textFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: linkColor), linkAttribute: { contents in
|
||||
return (TelegramTextAttributes.URL, contents)
|
||||
})
|
||||
|
||||
//TODO:localize
|
||||
|
||||
let spacing: CGFloat = 16.0
|
||||
var contentSize = CGSize(width: context.availableSize.width, height: 32.0)
|
||||
|
||||
let iconSize = CGSize(width: 90.0, height: 90.0)
|
||||
let gradientImage: UIImage
|
||||
|
||||
if let (current, currentTheme) = state.cachedIconImage, currentTheme === theme {
|
||||
gradientImage = current
|
||||
} else {
|
||||
gradientImage = generateGradientFilledCircleImage(diameter: iconSize.width, colors: [
|
||||
UIColor(rgb: 0x4bbb45).cgColor,
|
||||
UIColor(rgb: 0x9ad164).cgColor
|
||||
])!
|
||||
context.state.cachedIconImage = (gradientImage, theme)
|
||||
}
|
||||
|
||||
let iconBackground = iconBackground.update(
|
||||
component: Image(image: gradientImage),
|
||||
availableSize: iconSize,
|
||||
transition: .immediate
|
||||
)
|
||||
context.add(iconBackground
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + iconBackground.size.height / 2.0))
|
||||
)
|
||||
|
||||
let icon = icon.update(
|
||||
component: BundleIconComponent(name: "Chart/Monetization", tintColor: theme.list.itemCheckColors.foregroundColor),
|
||||
availableSize: CGSize(width: 90, height: 90),
|
||||
transition: .immediate
|
||||
)
|
||||
|
||||
context.add(icon
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + iconBackground.size.height / 2.0))
|
||||
)
|
||||
contentSize.height += iconSize.height
|
||||
contentSize.height += spacing + 5.0
|
||||
|
||||
let title = title.update(
|
||||
component: BalancedTextComponent(
|
||||
text: .plain(NSAttributedString(string: "Earn From Your Channel", font: titleFont, textColor: textColor)),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 0,
|
||||
lineSpacing: 0.1
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - textSideInset * 2.0, height: context.availableSize.height),
|
||||
transition: .immediate
|
||||
)
|
||||
context.add(title
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + title.size.height / 2.0))
|
||||
)
|
||||
contentSize.height += title.size.height
|
||||
contentSize.height += spacing
|
||||
|
||||
|
||||
var items: [AnyComponentWithIdentity<Empty>] = []
|
||||
items.append(
|
||||
AnyComponentWithIdentity(
|
||||
id: "ads",
|
||||
component: AnyComponent(ParagraphComponent(
|
||||
title: "Telegram Ads",
|
||||
titleColor: textColor,
|
||||
text: "Telegram can display ads in your channel.",
|
||||
textColor: secondaryTextColor,
|
||||
iconName: "Chart/Ads",
|
||||
iconColor: linkColor
|
||||
))
|
||||
)
|
||||
)
|
||||
items.append(
|
||||
AnyComponentWithIdentity(
|
||||
id: "split",
|
||||
component: AnyComponent(ParagraphComponent(
|
||||
title: "50:50 Revenue Split",
|
||||
titleColor: textColor,
|
||||
text: "You receive 50% of the ad revenue in TON.",
|
||||
textColor: secondaryTextColor,
|
||||
iconName: "Chart/Split",
|
||||
iconColor: linkColor
|
||||
))
|
||||
)
|
||||
)
|
||||
items.append(
|
||||
AnyComponentWithIdentity(
|
||||
id: "withdrawal",
|
||||
component: AnyComponent(ParagraphComponent(
|
||||
title: "Flexible Withdrawals",
|
||||
titleColor: textColor,
|
||||
text: "You can withdraw your TON any time.",
|
||||
textColor: secondaryTextColor,
|
||||
iconName: "Chart/Withdrawal",
|
||||
iconColor: linkColor
|
||||
))
|
||||
)
|
||||
)
|
||||
|
||||
let list = list.update(
|
||||
component: List(items),
|
||||
availableSize: CGSize(width: context.availableSize.width - sideInset, height: 10000.0),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(list
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + list.size.height / 2.0))
|
||||
)
|
||||
contentSize.height += list.size.height
|
||||
contentSize.height += spacing - 9.0
|
||||
|
||||
let infoTitleString = "What's #TON?"//.replacingOccurrences(of: "#", with: "# ")
|
||||
let infoTitleAttributedString = NSMutableAttributedString(string: infoTitleString, font: titleFont, textColor: textColor)
|
||||
let range = (infoTitleAttributedString.string as NSString).range(of: "#")
|
||||
if range.location != NSNotFound, let emojiFile = component.animatedEmojis["💎"] {
|
||||
infoTitleAttributedString.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: emojiFile.fileId.id, file: emojiFile), range: range)
|
||||
}
|
||||
let infoTitle = infoTitle.update(
|
||||
component: MultilineTextWithEntitiesComponent(
|
||||
context: component.context,
|
||||
animationCache: component.context.animationCache,
|
||||
animationRenderer: component.context.animationRenderer,
|
||||
placeholderColor: environment.theme.list.mediaPlaceholderColor,
|
||||
text: .plain(infoTitleAttributedString),
|
||||
horizontalAlignment: .center
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - textSideInset * 2.0, height: context.availableSize.height),
|
||||
transition: .immediate
|
||||
)
|
||||
|
||||
if state.cachedChevronImage == nil || state.cachedChevronImage?.1 !== environment.theme {
|
||||
state.cachedChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Settings/TextArrowRight"), color: linkColor)!, theme)
|
||||
}
|
||||
|
||||
let infoString = "TON is a blockchain platform and cryptocurrency that Telegram uses for its record scalability and ultra low commissions on transactions.\n[Learn More >]()"
|
||||
let infoAttributedString = parseMarkdownIntoAttributedString(infoString, attributes: markdownAttributes).mutableCopy() as! NSMutableAttributedString
|
||||
if let range = infoAttributedString.string.range(of: ">"), let chevronImage = state.cachedChevronImage?.0 {
|
||||
infoAttributedString.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: infoAttributedString.string))
|
||||
}
|
||||
let infoText = infoText.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(infoAttributedString),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 0,
|
||||
lineSpacing: 0.2
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - (textSideInset + sideInset - 2.0) * 2.0, height: context.availableSize.height),
|
||||
transition: .immediate
|
||||
)
|
||||
|
||||
let infoPadding: CGFloat = 17.0
|
||||
let infoSpacing: CGFloat = 12.0
|
||||
let totalInfoHeight = infoPadding + infoTitle.size.height + infoSpacing + infoText.size.height + infoPadding
|
||||
|
||||
let infoBackground = infoBackground.update(
|
||||
component: RoundedRectangle(
|
||||
color: theme.list.blocksBackgroundColor,
|
||||
cornerRadius: 10.0
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: totalInfoHeight),
|
||||
transition: .immediate
|
||||
)
|
||||
context.add(infoBackground
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + infoBackground.size.height / 2.0))
|
||||
)
|
||||
contentSize.height += infoPadding
|
||||
|
||||
context.add(infoTitle
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + infoTitle.size.height / 2.0))
|
||||
)
|
||||
contentSize.height += infoTitle.size.height
|
||||
contentSize.height += infoSpacing
|
||||
|
||||
context.add(infoText
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + infoText.size.height / 2.0))
|
||||
)
|
||||
contentSize.height += infoText.size.height
|
||||
contentSize.height += infoPadding
|
||||
contentSize.height += spacing
|
||||
|
||||
let actionButton = actionButton.update(
|
||||
component: SolidRoundedButtonComponent(
|
||||
title: "Understood",
|
||||
theme: SolidRoundedButtonComponent.Theme(
|
||||
backgroundColor: theme.list.itemCheckColors.fillColor,
|
||||
backgroundColors: [],
|
||||
foregroundColor: theme.list.itemCheckColors.foregroundColor
|
||||
),
|
||||
font: .bold,
|
||||
fontSize: 17.0,
|
||||
height: 50.0,
|
||||
cornerRadius: 10.0,
|
||||
gloss: false,
|
||||
iconName: nil,
|
||||
animationName: nil,
|
||||
iconPosition: .left,
|
||||
action: {
|
||||
component.dismiss()
|
||||
}
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(actionButton
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + actionButton.size.height / 2.0))
|
||||
)
|
||||
contentSize.height += actionButton.size.height
|
||||
contentSize.height += 22.0
|
||||
|
||||
contentSize.height += environment.safeInsets.bottom
|
||||
|
||||
state.playAnimationIfNeeded()
|
||||
|
||||
return contentSize
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class SheetContainerComponent: CombinedComponent {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
||||
let context: AccountContext
|
||||
let animatedEmojis: [String: TelegramMediaFile]
|
||||
let openMore: () -> Void
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
animatedEmojis: [String: TelegramMediaFile],
|
||||
openMore: @escaping () -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.animatedEmojis = animatedEmojis
|
||||
self.openMore = openMore
|
||||
}
|
||||
|
||||
static func ==(lhs: SheetContainerComponent, rhs: SheetContainerComponent) -> Bool {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
static var body: Body {
|
||||
let sheet = Child(SheetComponent<EnvironmentType>.self)
|
||||
let animateOut = StoredActionSlot(Action<Void>.self)
|
||||
|
||||
let sheetExternalState = SheetComponent<EnvironmentType>.ExternalState()
|
||||
|
||||
return { context in
|
||||
let environment = context.environment[EnvironmentType.self]
|
||||
|
||||
let controller = environment.controller
|
||||
|
||||
let sheet = sheet.update(
|
||||
component: SheetComponent<EnvironmentType>(
|
||||
content: AnyComponent<EnvironmentType>(SheetContent(
|
||||
context: context.component.context,
|
||||
animatedEmojis: context.component.animatedEmojis,
|
||||
openMore: context.component.openMore,
|
||||
dismiss: {
|
||||
animateOut.invoke(Action { _ in
|
||||
if let controller = controller() {
|
||||
controller.dismiss(completion: nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
)),
|
||||
backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor),
|
||||
followContentSizeChanges: true,
|
||||
externalState: sheetExternalState,
|
||||
animateOut: animateOut
|
||||
),
|
||||
environment: {
|
||||
environment
|
||||
SheetComponentEnvironment(
|
||||
isDisplaying: environment.value.isVisible,
|
||||
isCentered: environment.metrics.widthClass == .regular,
|
||||
hasInputHeight: !environment.inputHeight.isZero,
|
||||
regularMetricsSize: CGSize(width: 430.0, height: 900.0),
|
||||
dismiss: { animated in
|
||||
if animated {
|
||||
animateOut.invoke(Action { _ in
|
||||
if let controller = controller() {
|
||||
controller.dismiss(completion: nil)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
if let controller = controller() {
|
||||
controller.dismiss(completion: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
availableSize: context.availableSize,
|
||||
transition: context.transition
|
||||
)
|
||||
|
||||
context.add(sheet
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0))
|
||||
)
|
||||
|
||||
if let controller = controller(), !controller.automaticallyControlPresentationContextLayout {
|
||||
let layout = ContainerViewLayout(
|
||||
size: context.availableSize,
|
||||
metrics: environment.metrics,
|
||||
deviceMetrics: environment.deviceMetrics,
|
||||
intrinsicInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: max(environment.safeInsets.bottom, sheetExternalState.contentHeight), right: 0.0),
|
||||
safeInsets: UIEdgeInsets(top: 0.0, left: environment.safeInsets.left, bottom: 0.0, right: environment.safeInsets.right),
|
||||
additionalInsets: .zero,
|
||||
statusBarHeight: environment.statusBarHeight,
|
||||
inputHeight: nil,
|
||||
inputHeightIsInteractivellyChanging: false,
|
||||
inVoiceOver: false
|
||||
)
|
||||
controller.presentationContext.containerLayoutUpdated(layout, transition: context.transition.containedViewLayoutTransition)
|
||||
}
|
||||
|
||||
return context.availableSize
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final class MonetizationIntroScreen: ViewControllerComponentContainer {
|
||||
private let context: AccountContext
|
||||
private let animatedEmojis: [String: TelegramMediaFile]
|
||||
private var openMore: (() -> Void)?
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
animatedEmojis: [String: TelegramMediaFile],
|
||||
openMore: @escaping () -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.animatedEmojis = animatedEmojis
|
||||
self.openMore = openMore
|
||||
|
||||
super.init(
|
||||
context: context,
|
||||
component: SheetContainerComponent(
|
||||
context: context,
|
||||
animatedEmojis: animatedEmojis,
|
||||
openMore: openMore
|
||||
),
|
||||
navigationBarAppearance: .none,
|
||||
statusBarStyle: .ignore,
|
||||
theme: .default
|
||||
)
|
||||
|
||||
self.navigationPresentation = .flatModal
|
||||
}
|
||||
|
||||
required public init(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
self.view.disablesInteractiveModalDismiss = true
|
||||
}
|
||||
|
||||
func dismissAnimated() {
|
||||
if let view = self.node.hostView.findTaggedView(tag: SheetComponent<ViewControllerComponentContainer.Environment>.View.Tag()) as? SheetComponent<ViewControllerComponentContainer.Environment>.View {
|
||||
view.dismissAnimated()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class ParagraphComponent: CombinedComponent {
|
||||
let title: String
|
||||
let titleColor: UIColor
|
||||
let text: String
|
||||
let textColor: UIColor
|
||||
let iconName: String
|
||||
let iconColor: UIColor
|
||||
|
||||
public init(
|
||||
title: String,
|
||||
titleColor: UIColor,
|
||||
text: String,
|
||||
textColor: UIColor,
|
||||
iconName: String,
|
||||
iconColor: UIColor
|
||||
) {
|
||||
self.title = title
|
||||
self.titleColor = titleColor
|
||||
self.text = text
|
||||
self.textColor = textColor
|
||||
self.iconName = iconName
|
||||
self.iconColor = iconColor
|
||||
}
|
||||
|
||||
static func ==(lhs: ParagraphComponent, rhs: ParagraphComponent) -> Bool {
|
||||
if lhs.title != rhs.title {
|
||||
return false
|
||||
}
|
||||
if lhs.titleColor != rhs.titleColor {
|
||||
return false
|
||||
}
|
||||
if lhs.text != rhs.text {
|
||||
return false
|
||||
}
|
||||
if lhs.textColor != rhs.textColor {
|
||||
return false
|
||||
}
|
||||
if lhs.iconName != rhs.iconName {
|
||||
return false
|
||||
}
|
||||
if lhs.iconColor != rhs.iconColor {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
static var body: Body {
|
||||
let title = Child(MultilineTextComponent.self)
|
||||
let text = Child(MultilineTextComponent.self)
|
||||
let icon = Child(BundleIconComponent.self)
|
||||
|
||||
return { context in
|
||||
let component = context.component
|
||||
|
||||
let leftInset: CGFloat = 64.0
|
||||
let rightInset: CGFloat = 32.0
|
||||
let textSideInset: CGFloat = leftInset + 8.0
|
||||
let spacing: CGFloat = 5.0
|
||||
|
||||
let textTopInset: CGFloat = 9.0
|
||||
|
||||
let title = title.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: component.title,
|
||||
font: Font.semibold(15.0),
|
||||
textColor: component.titleColor,
|
||||
paragraphAlignment: .natural
|
||||
)),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 1
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - leftInset - rightInset, height: CGFloat.greatestFiniteMagnitude),
|
||||
transition: .immediate
|
||||
)
|
||||
|
||||
let textFont = Font.regular(15.0)
|
||||
let boldTextFont = Font.semibold(15.0)
|
||||
let textColor = component.textColor
|
||||
let markdownAttributes = MarkdownAttributes(
|
||||
body: MarkdownAttributeSet(font: textFont, textColor: textColor),
|
||||
bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor),
|
||||
link: MarkdownAttributeSet(font: textFont, textColor: textColor),
|
||||
linkAttribute: { _ in
|
||||
return nil
|
||||
}
|
||||
)
|
||||
|
||||
let text = text.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .markdown(text: component.text, attributes: markdownAttributes),
|
||||
horizontalAlignment: .natural,
|
||||
maximumNumberOfLines: 0,
|
||||
lineSpacing: 0.2
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - leftInset - rightInset, height: context.availableSize.height),
|
||||
transition: .immediate
|
||||
)
|
||||
|
||||
let icon = icon.update(
|
||||
component: BundleIconComponent(
|
||||
name: component.iconName,
|
||||
tintColor: component.iconColor
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width, height: context.availableSize.height),
|
||||
transition: .immediate
|
||||
)
|
||||
|
||||
context.add(title
|
||||
.position(CGPoint(x: textSideInset + title.size.width / 2.0, y: textTopInset + title.size.height / 2.0))
|
||||
)
|
||||
|
||||
context.add(text
|
||||
.position(CGPoint(x: textSideInset + text.size.width / 2.0, y: textTopInset + title.size.height + spacing + text.size.height / 2.0))
|
||||
)
|
||||
|
||||
context.add(icon
|
||||
.position(CGPoint(x: 47.0, y: textTopInset + 18.0))
|
||||
)
|
||||
|
||||
return CGSize(width: context.availableSize.width, height: textTopInset + title.size.height + text.size.height + 20.0)
|
||||
}
|
||||
}
|
||||
}
|
62
submodules/StatisticsUI/Sources/MonetizationUtils.swift
Normal file
62
submodules/StatisticsUI/Sources/MonetizationUtils.swift
Normal file
@ -0,0 +1,62 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
let walletAddressLength: Int = 48
|
||||
|
||||
func formatAddress(_ address: String) -> String {
|
||||
var address = address
|
||||
address.insert("\n", at: address.index(address.startIndex, offsetBy: address.count / 2))
|
||||
return address
|
||||
}
|
||||
|
||||
func formatBalanceText(_ value: Int64, decimalSeparator: String, showPlus: Bool = false) -> String {
|
||||
var balanceText = "\(abs(value))"
|
||||
while balanceText.count < 10 {
|
||||
balanceText.insert("0", at: balanceText.startIndex)
|
||||
}
|
||||
balanceText.insert(contentsOf: decimalSeparator, at: balanceText.index(balanceText.endIndex, offsetBy: -9))
|
||||
while true {
|
||||
if balanceText.hasSuffix("0") {
|
||||
if balanceText.hasSuffix("\(decimalSeparator)0") {
|
||||
balanceText.removeLast()
|
||||
balanceText.removeLast()
|
||||
break
|
||||
} else {
|
||||
balanceText.removeLast()
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if value < 0 {
|
||||
balanceText.insert("-", at: balanceText.startIndex)
|
||||
} else if showPlus {
|
||||
balanceText.insert("+", at: balanceText.startIndex)
|
||||
}
|
||||
return balanceText
|
||||
}
|
||||
|
||||
private let invalidAddressCharacters = CharacterSet(charactersIn: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=").inverted
|
||||
func isValidAddress(_ address: String, exactLength: Bool = false) -> Bool {
|
||||
if address.count > walletAddressLength || address.rangeOfCharacter(from: invalidAddressCharacters) != nil {
|
||||
return false
|
||||
}
|
||||
if exactLength && address.count != walletAddressLength {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private let amountDelimeterCharacters = CharacterSet(charactersIn: "0123456789-+").inverted
|
||||
func amountAttributedString(_ string: String, integralFont: UIFont, fractionalFont: UIFont, color: UIColor) -> NSAttributedString {
|
||||
let result = NSMutableAttributedString()
|
||||
if let range = string.rangeOfCharacter(from: amountDelimeterCharacters) {
|
||||
let integralPart = String(string[..<range.lowerBound])
|
||||
let fractionalPart = String(string[range.lowerBound...])
|
||||
result.append(NSAttributedString(string: integralPart, font: integralFont, textColor: color))
|
||||
result.append(NSAttributedString(string: fractionalPart, font: fractionalFont, textColor: color))
|
||||
} else {
|
||||
result.append(NSAttributedString(string: string, font: integralFont, textColor: color))
|
||||
}
|
||||
return result
|
||||
}
|
@ -7,6 +7,9 @@ import TelegramCore
|
||||
import TelegramPresentationData
|
||||
import ItemListUI
|
||||
import PresentationDataUtils
|
||||
import EmojiTextAttachmentView
|
||||
import TextFormat
|
||||
import AccountContext
|
||||
|
||||
protocol Stats {
|
||||
|
||||
@ -32,21 +35,29 @@ extension StoryStats: Stats {
|
||||
|
||||
}
|
||||
|
||||
extension MonetizationStats: Stats {
|
||||
|
||||
}
|
||||
|
||||
class StatsOverviewItem: ListViewItem, ItemListItem {
|
||||
let context: AccountContext
|
||||
let presentationData: ItemListPresentationData
|
||||
let isGroup: Bool
|
||||
let stats: Stats
|
||||
let storyViews: EngineStoryItem.Views?
|
||||
let publicShares: Int32?
|
||||
let animatedEmoji: TelegramMediaFile?
|
||||
let sectionId: ItemListSectionId
|
||||
let style: ItemListStyle
|
||||
|
||||
init(presentationData: ItemListPresentationData, isGroup: Bool, stats: Stats, storyViews: EngineStoryItem.Views? = nil, publicShares: Int32? = nil, sectionId: ItemListSectionId, style: ItemListStyle) {
|
||||
init(context: AccountContext, presentationData: ItemListPresentationData, isGroup: Bool, stats: Stats, storyViews: EngineStoryItem.Views? = nil, publicShares: Int32? = nil, animatedEmoji: TelegramMediaFile? = nil, sectionId: ItemListSectionId, style: ItemListStyle) {
|
||||
self.context = context
|
||||
self.presentationData = presentationData
|
||||
self.isGroup = isGroup
|
||||
self.stats = stats
|
||||
self.storyViews = storyViews
|
||||
self.publicShares = publicShares
|
||||
self.animatedEmoji = animatedEmoji
|
||||
self.sectionId = sectionId
|
||||
self.style = style
|
||||
}
|
||||
@ -97,6 +108,7 @@ private final class ValueItemNode: ASDisplayNode {
|
||||
private let valueNode: TextNode
|
||||
private let titleNode: TextNode
|
||||
private let deltaNode: TextNode
|
||||
private var animatedEmojiLayer: InlineStickerItemLayer?
|
||||
|
||||
var currentBackgroundColor: UIColor?
|
||||
var pressed: (() -> Void)?
|
||||
@ -115,13 +127,13 @@ private final class ValueItemNode: ASDisplayNode {
|
||||
self.addSubnode(self.deltaNode)
|
||||
}
|
||||
|
||||
static func asyncLayout(_ current: ValueItemNode?) -> (_ width: CGFloat, _ presentationData: ItemListPresentationData, _ value: String, _ title: String, _ delta: (String, DeltaColor)?) -> (CGSize, () -> ValueItemNode) {
|
||||
static func asyncLayout(_ current: ValueItemNode?) -> (_ context: AccountContext, _ width: CGFloat, _ presentationData: ItemListPresentationData, _ value: String, _ title: String, _ delta: (String, DeltaColor)?, _ animatedEmoji: TelegramMediaFile?) -> (CGSize, () -> ValueItemNode) {
|
||||
|
||||
let maybeMakeValueLayout = (current?.valueNode).flatMap(TextNode.asyncLayout)
|
||||
let maybeMakeTitleLayout = (current?.titleNode).flatMap(TextNode.asyncLayout)
|
||||
let maybeMakeDeltaLayout = (current?.deltaNode).flatMap(TextNode.asyncLayout)
|
||||
|
||||
return { width, presentationData, value, title, delta in
|
||||
return { context, width, presentationData, value, title, delta, animatedEmoji in
|
||||
let targetNode: ValueItemNode
|
||||
if let current = current {
|
||||
targetNode = current
|
||||
@ -150,7 +162,8 @@ private final class ValueItemNode: ASDisplayNode {
|
||||
makeDeltaLayout = TextNode.asyncLayout(targetNode.deltaNode)
|
||||
}
|
||||
|
||||
let valueFont = Font.semibold(presentationData.fontSize.itemListBaseFontSize)
|
||||
let fontSize = presentationData.fontSize.itemListBaseFontSize
|
||||
let valueFont = Font.semibold(fontSize)
|
||||
let titleFont = Font.regular(presentationData.fontSize.itemListBaseHeaderFontSize)
|
||||
let deltaFont = Font.regular(presentationData.fontSize.itemListBaseHeaderFontSize)
|
||||
|
||||
@ -170,6 +183,7 @@ private final class ValueItemNode: ASDisplayNode {
|
||||
} else {
|
||||
deltaColor = presentationData.theme.list.freeTextErrorColor
|
||||
}
|
||||
let placeholderColor = presentationData.theme.list.mediaPlaceholderColor
|
||||
|
||||
let constrainedSize = CGSize(width: width, height: CGFloat.greatestFiniteMagnitude)
|
||||
let (valueLayout, valueApply) = makeValueLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: value, font: valueFont, textColor: valueColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: constrainedSize, alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
@ -185,9 +199,33 @@ private final class ValueItemNode: ASDisplayNode {
|
||||
let _ = titleApply()
|
||||
let _ = deltaApply()
|
||||
|
||||
let valueFrame = CGRect(origin: .zero, size: valueLayout.size)
|
||||
var valueOffset: CGFloat = 0.0
|
||||
if let animatedEmoji {
|
||||
let itemSize = floorToScreenPixels(fontSize * 20.0 / 17.0)
|
||||
|
||||
var itemFrame = CGRect(origin: CGPoint(x: itemSize / 2.0 - 1.0, y: itemSize / 2.0), size: CGSize()).insetBy(dx: -itemSize / 2.0, dy: -itemSize / 2.0)
|
||||
itemFrame.origin.x = floorToScreenPixels(itemFrame.origin.x)
|
||||
itemFrame.origin.y = floorToScreenPixels(itemFrame.origin.y)
|
||||
|
||||
let itemLayer: InlineStickerItemLayer
|
||||
if let current = targetNode.animatedEmojiLayer {
|
||||
itemLayer = current
|
||||
} else {
|
||||
let pointSize = floor(itemSize * 1.3)
|
||||
itemLayer = InlineStickerItemLayer(context: context, userLocation: .other, attemptSynchronousLoad: true, emoji: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: animatedEmoji.fileId.id, file: animatedEmoji, custom: nil), file: animatedEmoji, cache: context.animationCache, renderer: context.animationRenderer, placeholderColor: placeholderColor, pointSize: CGSize(width: pointSize, height: pointSize), dynamicColor: nil)
|
||||
targetNode.animatedEmojiLayer = itemLayer
|
||||
targetNode.layer.addSublayer(itemLayer)
|
||||
|
||||
itemLayer.isVisibleForAnimations = true
|
||||
}
|
||||
valueOffset += 22.0
|
||||
|
||||
itemLayer.frame = itemFrame
|
||||
}
|
||||
|
||||
let valueFrame = CGRect(origin: CGPoint(x: valueOffset, y: 0.0), size: valueLayout.size)
|
||||
let titleFrame = CGRect(origin: CGPoint(x: 0.0, y: valueFrame.maxY), size: titleLayout.size)
|
||||
let deltaFrame = CGRect(origin: CGPoint(x: valueFrame.maxX + horizontalSpacing, y: valueFrame.maxY - deltaLayout.size.height - 2.0), size: deltaLayout.size)
|
||||
let deltaFrame = CGRect(origin: CGPoint(x: valueFrame.maxX + horizontalSpacing, y: valueFrame.maxY - deltaLayout.size.height - 2.0 - UIScreenPixel), size: deltaLayout.size)
|
||||
|
||||
targetNode.valueNode.frame = valueFrame
|
||||
targetNode.titleNode.frame = titleFrame
|
||||
@ -296,7 +334,7 @@ class StatsOverviewItemNode: ListViewItemNode {
|
||||
insets = itemListNeighborsGroupedInsets(neighbors, params)
|
||||
}
|
||||
|
||||
let twoColumnLayout = "".isEmpty
|
||||
var twoColumnLayout = true
|
||||
|
||||
var topLeftItemLayoutAndApply: (CGSize, () -> ValueItemNode)?
|
||||
var topRightItemLayoutAndApply: (CGSize, () -> ValueItemNode)?
|
||||
@ -321,78 +359,96 @@ class StatsOverviewItemNode: ListViewItemNode {
|
||||
|
||||
if let stats = item.stats as? MessageStats {
|
||||
topLeftItemLayoutAndApply = makeTopLeftItemLayout(
|
||||
item.context,
|
||||
params.width,
|
||||
item.presentationData,
|
||||
compactNumericCountString(stats.views),
|
||||
item.presentationData.strings.Stats_Message_Views,
|
||||
nil,
|
||||
nil
|
||||
)
|
||||
|
||||
topRightItemLayoutAndApply = makeTopRightItemLayout(
|
||||
item.context,
|
||||
params.width,
|
||||
item.presentationData,
|
||||
item.publicShares.flatMap { compactNumericCountString(Int($0)) } ?? "–",
|
||||
item.presentationData.strings.Stats_Message_PublicShares,
|
||||
nil,
|
||||
nil
|
||||
)
|
||||
|
||||
middle1LeftItemLayoutAndApply = makeMiddle1LeftItemLayout(
|
||||
item.context,
|
||||
params.width,
|
||||
item.presentationData,
|
||||
compactNumericCountString(stats.reactions),
|
||||
item.presentationData.strings.Stats_Message_Reactions,
|
||||
nil,
|
||||
nil
|
||||
)
|
||||
|
||||
middle1RightItemLayoutAndApply = makeMiddle1RightItemLayout(
|
||||
item.context,
|
||||
params.width,
|
||||
item.presentationData,
|
||||
item.publicShares.flatMap { "≈\( compactNumericCountString(max(0, stats.forwards - Int($0))))" } ?? "–",
|
||||
item.presentationData.strings.Stats_Message_PrivateShares,
|
||||
nil,
|
||||
nil
|
||||
)
|
||||
|
||||
height += topRightItemLayoutAndApply!.0.height * 2.0 + verticalSpacing
|
||||
} else if let _ = item.stats as? StoryStats, let views = item.storyViews {
|
||||
topLeftItemLayoutAndApply = makeTopLeftItemLayout(
|
||||
item.context,
|
||||
params.width,
|
||||
item.presentationData,
|
||||
compactNumericCountString(views.seenCount),
|
||||
item.presentationData.strings.Stats_Message_Views,
|
||||
nil,
|
||||
nil
|
||||
)
|
||||
|
||||
topRightItemLayoutAndApply = makeTopRightItemLayout(
|
||||
item.context,
|
||||
params.width,
|
||||
item.presentationData,
|
||||
item.publicShares.flatMap { compactNumericCountString(Int($0)) } ?? "–",
|
||||
item.presentationData.strings.Stats_Message_PublicShares,
|
||||
nil,
|
||||
nil
|
||||
)
|
||||
|
||||
middle1LeftItemLayoutAndApply = makeMiddle1LeftItemLayout(
|
||||
item.context,
|
||||
params.width,
|
||||
item.presentationData,
|
||||
compactNumericCountString(views.reactedCount),
|
||||
item.presentationData.strings.Stats_Message_Reactions,
|
||||
nil,
|
||||
nil
|
||||
)
|
||||
|
||||
middle1RightItemLayoutAndApply = makeMiddle1RightItemLayout(
|
||||
item.context,
|
||||
params.width,
|
||||
item.presentationData,
|
||||
item.publicShares.flatMap { "≈\( compactNumericCountString(max(0, views.forwardCount - Int($0))))" } ?? "–",
|
||||
item.presentationData.strings.Stats_Message_PrivateShares,
|
||||
nil,
|
||||
nil
|
||||
)
|
||||
|
||||
height += topRightItemLayoutAndApply!.0.height * 2.0 + verticalSpacing
|
||||
} else if let stats = item.stats as? ChannelBoostStatus {
|
||||
topLeftItemLayoutAndApply = makeTopLeftItemLayout(
|
||||
item.context,
|
||||
params.width,
|
||||
item.presentationData,
|
||||
"\(stats.level)",
|
||||
item.presentationData.strings.Stats_Boosts_Level,
|
||||
nil,
|
||||
nil
|
||||
)
|
||||
|
||||
@ -402,18 +458,22 @@ class StatsOverviewItemNode: ListViewItemNode {
|
||||
}
|
||||
|
||||
topRightItemLayoutAndApply = makeTopRightItemLayout(
|
||||
item.context,
|
||||
params.width,
|
||||
item.presentationData,
|
||||
"≈\(Int(stats.premiumAudience?.value ?? 0))",
|
||||
item.isGroup ? item.presentationData.strings.Stats_Boosts_PremiumMembers : item.presentationData.strings.Stats_Boosts_PremiumSubscribers,
|
||||
(String(format: "%.02f%%", premiumSubscribers * 100.0), .generic)
|
||||
(String(format: "%.02f%%", premiumSubscribers * 100.0), .generic),
|
||||
nil
|
||||
)
|
||||
|
||||
middle1LeftItemLayoutAndApply = makeMiddle1LeftItemLayout(
|
||||
item.context,
|
||||
params.width,
|
||||
item.presentationData,
|
||||
"\(stats.boosts)",
|
||||
item.presentationData.strings.Stats_Boosts_ExistingBoosts,
|
||||
nil,
|
||||
nil
|
||||
)
|
||||
|
||||
@ -424,10 +484,12 @@ class StatsOverviewItemNode: ListViewItemNode {
|
||||
boostsLeft = 0
|
||||
}
|
||||
middle1RightItemLayoutAndApply = makeMiddle1RightItemLayout(
|
||||
item.context,
|
||||
params.width,
|
||||
item.presentationData,
|
||||
"\(boostsLeft)",
|
||||
item.presentationData.strings.Stats_Boosts_BoostsToLevelUp,
|
||||
nil,
|
||||
nil
|
||||
)
|
||||
|
||||
@ -447,11 +509,13 @@ class StatsOverviewItemNode: ListViewItemNode {
|
||||
|
||||
let followersDelta = deltaText(stats.followers)
|
||||
topLeftItemLayoutAndApply = makeTopLeftItemLayout(
|
||||
item.context,
|
||||
params.width,
|
||||
item.presentationData,
|
||||
compactNumericCountString(Int(stats.followers.current)),
|
||||
item.presentationData.strings.Stats_Followers,
|
||||
(followersDelta.text, followersDelta.positive ? .positive : .negative)
|
||||
(followersDelta.text, followersDelta.positive ? .positive : .negative),
|
||||
nil
|
||||
)
|
||||
|
||||
var enabledNotifications: Double = 0.0
|
||||
@ -459,10 +523,12 @@ class StatsOverviewItemNode: ListViewItemNode {
|
||||
enabledNotifications = stats.enabledNotifications.value / stats.enabledNotifications.total
|
||||
}
|
||||
topRightItemLayoutAndApply = makeTopRightItemLayout(
|
||||
item.context,
|
||||
params.width,
|
||||
item.presentationData,
|
||||
String(format: "%.02f%%", enabledNotifications * 100.0),
|
||||
item.presentationData.strings.Stats_EnabledNotifications,
|
||||
nil,
|
||||
nil
|
||||
)
|
||||
|
||||
@ -516,56 +582,68 @@ class StatsOverviewItemNode: ListViewItemNode {
|
||||
|
||||
if let (value, title, delta) = items[0] {
|
||||
middle1LeftItemLayoutAndApply = makeMiddle1LeftItemLayout(
|
||||
item.context,
|
||||
params.width,
|
||||
item.presentationData,
|
||||
value,
|
||||
title,
|
||||
delta
|
||||
delta,
|
||||
nil
|
||||
)
|
||||
}
|
||||
if let (value, title, delta) = items[1] {
|
||||
middle1RightItemLayoutAndApply = makeMiddle1RightItemLayout(
|
||||
item.context,
|
||||
params.width,
|
||||
item.presentationData,
|
||||
value,
|
||||
title,
|
||||
delta
|
||||
delta,
|
||||
nil
|
||||
)
|
||||
}
|
||||
if let (value, title, delta) = items[2] {
|
||||
middle2LeftItemLayoutAndApply = makeMiddle2LeftItemLayout(
|
||||
item.context,
|
||||
params.width,
|
||||
item.presentationData,
|
||||
value,
|
||||
title,
|
||||
delta
|
||||
delta,
|
||||
nil
|
||||
)
|
||||
}
|
||||
if let (value, title, delta) = items[3] {
|
||||
middle2RightItemLayoutAndApply = makeMiddle2RightItemLayout(
|
||||
item.context,
|
||||
params.width,
|
||||
item.presentationData,
|
||||
value,
|
||||
title,
|
||||
delta
|
||||
delta,
|
||||
nil
|
||||
)
|
||||
}
|
||||
if let (value, title, delta) = items[4] {
|
||||
bottomLeftItemLayoutAndApply = makeBottomLeftItemLayout(
|
||||
item.context,
|
||||
params.width,
|
||||
item.presentationData,
|
||||
value,
|
||||
title,
|
||||
delta
|
||||
delta,
|
||||
nil
|
||||
)
|
||||
}
|
||||
if let (value, title, delta) = items[5] {
|
||||
bottomRightItemLayoutAndApply = makeBottomRightItemLayout(
|
||||
item.context,
|
||||
params.width,
|
||||
item.presentationData,
|
||||
value,
|
||||
title,
|
||||
delta
|
||||
delta,
|
||||
nil
|
||||
)
|
||||
}
|
||||
|
||||
@ -583,37 +661,45 @@ class StatsOverviewItemNode: ListViewItemNode {
|
||||
|
||||
let membersDelta = deltaText(stats.members)
|
||||
topLeftItemLayoutAndApply = makeTopLeftItemLayout(
|
||||
item.context,
|
||||
params.width,
|
||||
item.presentationData,
|
||||
compactNumericCountString(Int(stats.members.current)),
|
||||
item.presentationData.strings.Stats_GroupMembers,
|
||||
(membersDelta.text, membersDelta.positive ? .positive : .negative)
|
||||
(membersDelta.text, membersDelta.positive ? .positive : .negative),
|
||||
nil
|
||||
)
|
||||
|
||||
let messagesDelta = deltaText(stats.messages)
|
||||
topRightItemLayoutAndApply = makeTopRightItemLayout(
|
||||
item.context,
|
||||
params.width,
|
||||
item.presentationData,
|
||||
compactNumericCountString(Int(stats.messages.current)),
|
||||
item.presentationData.strings.Stats_GroupMessages,
|
||||
(messagesDelta.text, messagesDelta.positive ? .positive : .negative)
|
||||
(messagesDelta.text, messagesDelta.positive ? .positive : .negative),
|
||||
nil
|
||||
)
|
||||
|
||||
if displayBottomRow {
|
||||
middle1LeftItemLayoutAndApply = makeMiddle1LeftItemLayout(
|
||||
item.context,
|
||||
params.width,
|
||||
item.presentationData,
|
||||
compactNumericCountString(Int(stats.viewers.current)),
|
||||
item.presentationData.strings.Stats_GroupViewers,
|
||||
(viewersDelta.text, viewersDelta.positive ? .positive : .negative)
|
||||
(viewersDelta.text, viewersDelta.positive ? .positive : .negative),
|
||||
nil
|
||||
)
|
||||
|
||||
middle1RightItemLayoutAndApply = makeMiddle1RightItemLayout(
|
||||
item.context,
|
||||
params.width,
|
||||
item.presentationData,
|
||||
compactNumericCountString(Int(stats.posters.current)),
|
||||
item.presentationData.strings.Stats_GroupPosters,
|
||||
(postersDelta.text, postersDelta.positive ? .positive : .negative)
|
||||
(postersDelta.text, postersDelta.positive ? .positive : .negative),
|
||||
nil
|
||||
)
|
||||
}
|
||||
|
||||
@ -622,6 +708,40 @@ class StatsOverviewItemNode: ListViewItemNode {
|
||||
} else {
|
||||
height += topLeftItemLayoutAndApply!.0.height * 4.0 + verticalSpacing * 3.0
|
||||
}
|
||||
} else if let _ = item.stats as? MonetizationStats {
|
||||
twoColumnLayout = false
|
||||
|
||||
topLeftItemLayoutAndApply = makeTopLeftItemLayout(
|
||||
item.context,
|
||||
params.width,
|
||||
item.presentationData,
|
||||
"54.12",
|
||||
"Balance Available to Withdraw",
|
||||
("≈$123", .generic),
|
||||
item.animatedEmoji
|
||||
)
|
||||
|
||||
topRightItemLayoutAndApply = makeTopRightItemLayout(
|
||||
item.context,
|
||||
params.width,
|
||||
item.presentationData,
|
||||
"84.52",
|
||||
"Proceeds Since Last Withdrawal",
|
||||
("≈$226", .generic),
|
||||
item.animatedEmoji
|
||||
)
|
||||
|
||||
middle1LeftItemLayoutAndApply = makeMiddle1LeftItemLayout(
|
||||
item.context,
|
||||
params.width,
|
||||
item.presentationData,
|
||||
"692.52",
|
||||
"Total Lifetime Proceeds",
|
||||
("≈$1858", .generic),
|
||||
item.animatedEmoji
|
||||
)
|
||||
|
||||
height += topLeftItemLayoutAndApply!.0.height * 3.0 + verticalSpacing * 2.0
|
||||
}
|
||||
|
||||
let contentSize = CGSize(width: params.width, height: height)
|
||||
|
475
submodules/StatisticsUI/Sources/TransactionInfoScreen.swift
Normal file
475
submodules/StatisticsUI/Sources/TransactionInfoScreen.swift
Normal file
@ -0,0 +1,475 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import ComponentFlow
|
||||
import SwiftSignalKit
|
||||
import TelegramCore
|
||||
import Markdown
|
||||
import TextFormat
|
||||
import TelegramPresentationData
|
||||
import ViewControllerComponent
|
||||
import SheetComponent
|
||||
import BundleIconComponent
|
||||
import BalancedTextComponent
|
||||
import MultilineTextComponent
|
||||
import SolidRoundedButtonComponent
|
||||
import LottieComponent
|
||||
import AccountContext
|
||||
import TelegramStringFormatting
|
||||
import PremiumPeerShortcutComponent
|
||||
|
||||
enum MonetizationTransaction: Equatable {
|
||||
case incoming(amount: Int64, fromTimestamp: Int32, toTimestamp: Int32)
|
||||
case outgoing(amount: Int64, timestamp: Int32, address: String, explorerUrl: String)
|
||||
|
||||
var amount: Int64 {
|
||||
switch self {
|
||||
case let .incoming(amount, _, _), let .outgoing(amount, _, _, _):
|
||||
return amount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class SheetContent: CombinedComponent {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
||||
let context: AccountContext
|
||||
let peer: EnginePeer
|
||||
let transaction: MonetizationTransaction
|
||||
let openExplorer: (String) -> Void
|
||||
let dismiss: () -> Void
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
peer: EnginePeer,
|
||||
transaction: MonetizationTransaction,
|
||||
openExplorer: @escaping (String) -> Void,
|
||||
dismiss: @escaping () -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.peer = peer
|
||||
self.transaction = transaction
|
||||
self.openExplorer = openExplorer
|
||||
self.dismiss = dismiss
|
||||
}
|
||||
|
||||
static func ==(lhs: SheetContent, rhs: SheetContent) -> Bool {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
if lhs.peer != rhs.peer {
|
||||
return false
|
||||
}
|
||||
if lhs.transaction != rhs.transaction {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class State: ComponentState {
|
||||
var cachedCloseImage: (UIImage, PresentationTheme)?
|
||||
|
||||
let playOnce = ActionSlot<Void>()
|
||||
private var didPlayAnimation = false
|
||||
|
||||
func playAnimationIfNeeded() {
|
||||
guard !self.didPlayAnimation else {
|
||||
return
|
||||
}
|
||||
self.didPlayAnimation = true
|
||||
self.playOnce.invoke(Void())
|
||||
}
|
||||
}
|
||||
|
||||
func makeState() -> State {
|
||||
return State()
|
||||
}
|
||||
|
||||
static var body: Body {
|
||||
let closeButton = Child(Button.self)
|
||||
|
||||
let amount = Child(MultilineTextComponent.self)
|
||||
let title = Child(MultilineTextComponent.self)
|
||||
let date = Child(MultilineTextComponent.self)
|
||||
let address = Child(MultilineTextComponent.self)
|
||||
let peerShortcut = Child(PremiumPeerShortcutComponent.self)
|
||||
|
||||
let actionButton = Child(SolidRoundedButtonComponent.self)
|
||||
|
||||
return { context in
|
||||
let environment = context.environment[EnvironmentType.self]
|
||||
let component = context.component
|
||||
let state = context.state
|
||||
|
||||
let theme = environment.theme
|
||||
let strings = environment.strings
|
||||
let dateTimeFormat = component.context.sharedContext.currentPresentationData.with { $0 }.dateTimeFormat
|
||||
|
||||
let sideInset: CGFloat = 16.0 + environment.safeInsets.left
|
||||
let textSideInset: CGFloat = 32.0 + environment.safeInsets.left
|
||||
|
||||
let titleFont = Font.semibold(17.0)
|
||||
let textFont = Font.regular(17.0)
|
||||
let fixedFont = Font.monospace(17.0)
|
||||
|
||||
let textColor = theme.actionSheet.primaryTextColor
|
||||
let secondaryTextColor = theme.actionSheet.secondaryTextColor
|
||||
|
||||
var contentSize = CGSize(width: context.availableSize.width, height: 45.0)
|
||||
|
||||
let closeImage: UIImage
|
||||
if let (image, theme) = state.cachedCloseImage, theme === environment.theme {
|
||||
closeImage = image
|
||||
} else {
|
||||
closeImage = generateCloseButtonImage(backgroundColor: UIColor(rgb: 0x808084, alpha: 0.1), foregroundColor: theme.actionSheet.inputClearButtonColor)!
|
||||
state.cachedCloseImage = (closeImage, theme)
|
||||
}
|
||||
|
||||
let closeButton = closeButton.update(
|
||||
component: Button(
|
||||
content: AnyComponent(Image(image: closeImage)),
|
||||
action: { [weak component] in
|
||||
component?.dismiss()
|
||||
}
|
||||
),
|
||||
availableSize: CGSize(width: 30.0, height: 30.0),
|
||||
transition: .immediate
|
||||
)
|
||||
context.add(closeButton
|
||||
.position(CGPoint(x: context.availableSize.width - environment.safeInsets.left - closeButton.size.width, y: 28.0))
|
||||
)
|
||||
|
||||
let amountString: NSMutableAttributedString
|
||||
let dateString: String
|
||||
let titleString: String
|
||||
let subtitleString: String
|
||||
let buttonTitle: String
|
||||
let explorerUrl: String?
|
||||
|
||||
let integralFont = Font.with(size: 48.0, design: .round, weight: .semibold)
|
||||
let fractionalFont = Font.with(size: 24.0, design: .round, weight: .semibold)
|
||||
|
||||
//TODO:localize
|
||||
switch component.transaction {
|
||||
case let .incoming(amount, fromTimestamp, toTimestamp):
|
||||
amountString = amountAttributedString(formatBalanceText(amount, decimalSeparator: dateTimeFormat.decimalSeparator, showPlus: true), integralFont: integralFont, fractionalFont: fractionalFont, color: theme.list.itemDisclosureActions.constructive.fillColor).mutableCopy() as! NSMutableAttributedString
|
||||
amountString.append(NSAttributedString(string: " TON", font: fractionalFont, textColor: theme.list.itemDisclosureActions.constructive.fillColor))
|
||||
dateString = "\(stringForFullDate(timestamp: fromTimestamp, strings: strings, dateTimeFormat: dateTimeFormat)) – \(stringForFullDate(timestamp: toTimestamp, strings: strings, dateTimeFormat: dateTimeFormat))"
|
||||
titleString = "Proceeds from Ads displayed in"
|
||||
subtitleString = ""
|
||||
buttonTitle = strings.Common_OK
|
||||
explorerUrl = nil
|
||||
case let .outgoing(amount, timestamp, address, explorerUrlValue):
|
||||
amountString = amountAttributedString(formatBalanceText(amount, decimalSeparator: dateTimeFormat.decimalSeparator), integralFont: integralFont, fractionalFont: fractionalFont, color: theme.list.itemDestructiveColor).mutableCopy() as! NSMutableAttributedString
|
||||
amountString.append(NSAttributedString(string: " TON", font: fractionalFont, textColor: theme.list.itemDestructiveColor))
|
||||
dateString = stringForFullDate(timestamp: timestamp, strings: strings, dateTimeFormat: dateTimeFormat)
|
||||
titleString = "Balance Withdrawal to"
|
||||
subtitleString = formatAddress(address)
|
||||
buttonTitle = "View in Blockchain Explorer"
|
||||
explorerUrl = explorerUrlValue
|
||||
}
|
||||
|
||||
let amount = amount.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(amountString),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 1,
|
||||
lineSpacing: 0.1
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - textSideInset * 2.0, height: context.availableSize.height),
|
||||
transition: .immediate
|
||||
)
|
||||
context.add(amount
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + amount.size.height / 2.0))
|
||||
)
|
||||
contentSize.height += amount.size.height
|
||||
contentSize.height += -5.0
|
||||
|
||||
let date = date.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: dateString, font: textFont, textColor: secondaryTextColor)),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 0,
|
||||
lineSpacing: 0.1
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - textSideInset * 2.0, height: context.availableSize.height),
|
||||
transition: .immediate
|
||||
)
|
||||
context.add(date
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + date.size.height / 2.0))
|
||||
)
|
||||
contentSize.height += date.size.height
|
||||
contentSize.height += 32.0
|
||||
|
||||
let title = title.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: titleString, font: titleFont, textColor: textColor)),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 0,
|
||||
lineSpacing: 0.1
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - textSideInset * 2.0, height: context.availableSize.height),
|
||||
transition: .immediate
|
||||
)
|
||||
context.add(title
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + title.size.height / 2.0))
|
||||
)
|
||||
contentSize.height += title.size.height
|
||||
contentSize.height += 3.0
|
||||
|
||||
if !subtitleString.isEmpty {
|
||||
let address = address.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: subtitleString, font: fixedFont, textColor: textColor)),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 0,
|
||||
lineSpacing: 0.1
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - textSideInset * 2.0, height: context.availableSize.height),
|
||||
transition: .immediate
|
||||
)
|
||||
context.add(address
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + address.size.height / 2.0))
|
||||
)
|
||||
contentSize.height += address.size.height
|
||||
contentSize.height += 50.0
|
||||
} else {
|
||||
contentSize.height += 5.0
|
||||
let peerShortcut = peerShortcut.update(
|
||||
component: PremiumPeerShortcutComponent(
|
||||
context: component.context,
|
||||
theme: theme,
|
||||
peer: component.peer
|
||||
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - 32.0, height: context.availableSize.height),
|
||||
transition: .immediate
|
||||
)
|
||||
context.add(peerShortcut
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + peerShortcut.size.height / 2.0))
|
||||
)
|
||||
contentSize.height += peerShortcut.size.height
|
||||
contentSize.height += 50.0
|
||||
}
|
||||
|
||||
let actionButton = actionButton.update(
|
||||
component: SolidRoundedButtonComponent(
|
||||
title: buttonTitle,
|
||||
theme: SolidRoundedButtonComponent.Theme(
|
||||
backgroundColor: theme.list.itemCheckColors.fillColor,
|
||||
backgroundColors: [],
|
||||
foregroundColor: theme.list.itemCheckColors.foregroundColor
|
||||
),
|
||||
font: .bold,
|
||||
fontSize: 17.0,
|
||||
height: 50.0,
|
||||
cornerRadius: 10.0,
|
||||
gloss: false,
|
||||
iconName: nil,
|
||||
animationName: nil,
|
||||
iconPosition: .left,
|
||||
action: {
|
||||
component.dismiss()
|
||||
if let explorerUrl {
|
||||
component.openExplorer(explorerUrl)
|
||||
}
|
||||
}
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(actionButton
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + actionButton.size.height / 2.0))
|
||||
)
|
||||
contentSize.height += actionButton.size.height
|
||||
contentSize.height += 22.0
|
||||
|
||||
contentSize.height += environment.safeInsets.bottom
|
||||
|
||||
state.playAnimationIfNeeded()
|
||||
|
||||
return contentSize
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class SheetContainerComponent: CombinedComponent {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
||||
let context: AccountContext
|
||||
let peer: EnginePeer
|
||||
let transaction: MonetizationTransaction
|
||||
let openExplorer: (String) -> Void
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
peer: EnginePeer,
|
||||
transaction: MonetizationTransaction,
|
||||
openExplorer: @escaping (String) -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.peer = peer
|
||||
self.transaction = transaction
|
||||
self.openExplorer = openExplorer
|
||||
}
|
||||
|
||||
static func ==(lhs: SheetContainerComponent, rhs: SheetContainerComponent) -> Bool {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
if lhs.peer != rhs.peer {
|
||||
return false
|
||||
}
|
||||
if lhs.transaction != rhs.transaction {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
static var body: Body {
|
||||
let sheet = Child(SheetComponent<EnvironmentType>.self)
|
||||
let animateOut = StoredActionSlot(Action<Void>.self)
|
||||
|
||||
let sheetExternalState = SheetComponent<EnvironmentType>.ExternalState()
|
||||
|
||||
return { context in
|
||||
let environment = context.environment[EnvironmentType.self]
|
||||
|
||||
let controller = environment.controller
|
||||
|
||||
let sheet = sheet.update(
|
||||
component: SheetComponent<EnvironmentType>(
|
||||
content: AnyComponent<EnvironmentType>(SheetContent(
|
||||
context: context.component.context,
|
||||
peer: context.component.peer,
|
||||
transaction: context.component.transaction,
|
||||
openExplorer: context.component.openExplorer,
|
||||
dismiss: {
|
||||
animateOut.invoke(Action { _ in
|
||||
if let controller = controller() {
|
||||
controller.dismiss(completion: nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
)),
|
||||
backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor),
|
||||
followContentSizeChanges: true,
|
||||
externalState: sheetExternalState,
|
||||
animateOut: animateOut
|
||||
),
|
||||
environment: {
|
||||
environment
|
||||
SheetComponentEnvironment(
|
||||
isDisplaying: environment.value.isVisible,
|
||||
isCentered: environment.metrics.widthClass == .regular,
|
||||
hasInputHeight: !environment.inputHeight.isZero,
|
||||
regularMetricsSize: CGSize(width: 430.0, height: 900.0),
|
||||
dismiss: { animated in
|
||||
if animated {
|
||||
animateOut.invoke(Action { _ in
|
||||
if let controller = controller() {
|
||||
controller.dismiss(completion: nil)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
if let controller = controller() {
|
||||
controller.dismiss(completion: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
availableSize: context.availableSize,
|
||||
transition: context.transition
|
||||
)
|
||||
|
||||
context.add(sheet
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0))
|
||||
)
|
||||
|
||||
if let controller = controller(), !controller.automaticallyControlPresentationContextLayout {
|
||||
let layout = ContainerViewLayout(
|
||||
size: context.availableSize,
|
||||
metrics: environment.metrics,
|
||||
deviceMetrics: environment.deviceMetrics,
|
||||
intrinsicInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: max(environment.safeInsets.bottom, sheetExternalState.contentHeight), right: 0.0),
|
||||
safeInsets: UIEdgeInsets(top: 0.0, left: environment.safeInsets.left, bottom: 0.0, right: environment.safeInsets.right),
|
||||
additionalInsets: .zero,
|
||||
statusBarHeight: environment.statusBarHeight,
|
||||
inputHeight: nil,
|
||||
inputHeightIsInteractivellyChanging: false,
|
||||
inVoiceOver: false
|
||||
)
|
||||
controller.presentationContext.containerLayoutUpdated(layout, transition: context.transition.containedViewLayoutTransition)
|
||||
}
|
||||
|
||||
return context.availableSize
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final class TransactionInfoScreen: ViewControllerComponentContainer {
|
||||
private let context: AccountContext
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
peer: EnginePeer,
|
||||
transaction: MonetizationTransaction,
|
||||
openExplorer: @escaping (String) -> Void
|
||||
) {
|
||||
self.context = context
|
||||
|
||||
super.init(
|
||||
context: context,
|
||||
component: SheetContainerComponent(
|
||||
context: context,
|
||||
peer: peer,
|
||||
transaction: transaction,
|
||||
openExplorer: openExplorer
|
||||
),
|
||||
navigationBarAppearance: .none,
|
||||
statusBarStyle: .ignore,
|
||||
theme: .default
|
||||
)
|
||||
|
||||
self.navigationPresentation = .flatModal
|
||||
}
|
||||
|
||||
required public init(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
self.view.disablesInteractiveModalDismiss = true
|
||||
}
|
||||
|
||||
func dismissAnimated() {
|
||||
if let view = self.node.hostView.findTaggedView(tag: SheetComponent<ViewControllerComponentContainer.Environment>.View.Tag()) as? SheetComponent<ViewControllerComponentContainer.Environment>.View {
|
||||
view.dismissAnimated()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func generateCloseButtonImage(backgroundColor: UIColor, foregroundColor: UIColor) -> UIImage? {
|
||||
return generateImage(CGSize(width: 30.0, height: 30.0), contextGenerator: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
context.setFillColor(backgroundColor.cgColor)
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
context.setLineWidth(2.0)
|
||||
context.setLineCap(.round)
|
||||
context.setStrokeColor(foregroundColor.cgColor)
|
||||
|
||||
context.move(to: CGPoint(x: 10.0, y: 10.0))
|
||||
context.addLine(to: CGPoint(x: 20.0, y: 20.0))
|
||||
context.strokePath()
|
||||
|
||||
context.move(to: CGPoint(x: 20.0, y: 10.0))
|
||||
context.addLine(to: CGPoint(x: 10.0, y: 20.0))
|
||||
context.strokePath()
|
||||
})
|
||||
}
|
@ -40,6 +40,7 @@ swift_library(
|
||||
"//submodules/TelegramUI/Components/MultiAnimationRenderer:MultiAnimationRenderer",
|
||||
"//submodules/StickerPeekUI:StickerPeekUI",
|
||||
"//submodules/Pasteboard:Pasteboard",
|
||||
"//submodules/TelegramUI/Components/Stickers/StickerPackEditTitleController",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -28,7 +28,7 @@ private struct StickerPackPreviewGridEntry: Comparable, Identifiable {
|
||||
}
|
||||
|
||||
func item(context: AccountContext, interaction: StickerPackPreviewInteraction, theme: PresentationTheme) -> StickerPackPreviewGridItem {
|
||||
return StickerPackPreviewGridItem(context: context, stickerItem: self.stickerItem, interaction: interaction, theme: theme, isPremium: false, isLocked: false, isEmpty: false)
|
||||
return StickerPackPreviewGridItem(context: context, stickerItem: self.stickerItem, interaction: interaction, theme: theme, isPremium: false, isLocked: false, isEmpty: false, isEditing: false)
|
||||
}
|
||||
}
|
||||
|
||||
@ -144,7 +144,7 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol
|
||||
|
||||
super.init()
|
||||
|
||||
self.interaction = StickerPackPreviewInteraction(playAnimatedStickers: false, addStickerPack: { _, _ in }, removeStickerPack: { _ in }, emojiSelected: { _, _ in }, emojiLongPressed: { _, _, _, _ in })
|
||||
self.interaction = StickerPackPreviewInteraction(playAnimatedStickers: false, addStickerPack: { _, _ in }, removeStickerPack: { _ in }, emojiSelected: { _, _ in }, emojiLongPressed: { _, _, _, _ in }, addPressed: {})
|
||||
|
||||
self.backgroundColor = nil
|
||||
self.isOpaque = false
|
||||
|
@ -16,19 +16,22 @@ import TextFormat
|
||||
|
||||
final class StickerPackPreviewInteraction {
|
||||
var previewedItem: StickerPreviewPeekItem?
|
||||
var reorderingFileId: MediaId?
|
||||
var playAnimatedStickers: Bool
|
||||
|
||||
let addStickerPack: (StickerPackCollectionInfo, [StickerPackItem]) -> Void
|
||||
let removeStickerPack: (StickerPackCollectionInfo) -> Void
|
||||
let emojiSelected: (String, ChatTextInputTextCustomEmojiAttribute) -> Void
|
||||
let emojiLongPressed: (String, ChatTextInputTextCustomEmojiAttribute, ASDisplayNode, CGRect) -> Void
|
||||
let addPressed: () -> Void
|
||||
|
||||
init(playAnimatedStickers: Bool, addStickerPack: @escaping (StickerPackCollectionInfo, [StickerPackItem]) -> Void, removeStickerPack: @escaping (StickerPackCollectionInfo) -> Void, emojiSelected: @escaping (String, ChatTextInputTextCustomEmojiAttribute) -> Void, emojiLongPressed: @escaping (String, ChatTextInputTextCustomEmojiAttribute, ASDisplayNode, CGRect) -> Void) {
|
||||
init(playAnimatedStickers: Bool, addStickerPack: @escaping (StickerPackCollectionInfo, [StickerPackItem]) -> Void, removeStickerPack: @escaping (StickerPackCollectionInfo) -> Void, emojiSelected: @escaping (String, ChatTextInputTextCustomEmojiAttribute) -> Void, emojiLongPressed: @escaping (String, ChatTextInputTextCustomEmojiAttribute, ASDisplayNode, CGRect) -> Void, addPressed: @escaping () -> Void) {
|
||||
self.playAnimatedStickers = playAnimatedStickers
|
||||
self.addStickerPack = addStickerPack
|
||||
self.removeStickerPack = removeStickerPack
|
||||
self.emojiSelected = emojiSelected
|
||||
self.emojiLongPressed = emojiLongPressed
|
||||
self.addPressed = addPressed
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,10 +43,12 @@ final class StickerPackPreviewGridItem: GridItem {
|
||||
let isPremium: Bool
|
||||
let isLocked: Bool
|
||||
let isEmpty: Bool
|
||||
let isEditing: Bool
|
||||
let isAdd: Bool
|
||||
|
||||
let section: GridSection? = nil
|
||||
|
||||
init(context: AccountContext, stickerItem: StickerPackItem?, interaction: StickerPackPreviewInteraction, theme: PresentationTheme, isPremium: Bool, isLocked: Bool, isEmpty: Bool) {
|
||||
init(context: AccountContext, stickerItem: StickerPackItem?, interaction: StickerPackPreviewInteraction, theme: PresentationTheme, isPremium: Bool, isLocked: Bool, isEmpty: Bool, isEditing: Bool, isAdd: Bool = false) {
|
||||
self.context = context
|
||||
self.stickerItem = stickerItem
|
||||
self.interaction = interaction
|
||||
@ -51,11 +56,13 @@ final class StickerPackPreviewGridItem: GridItem {
|
||||
self.isPremium = isPremium
|
||||
self.isLocked = isLocked
|
||||
self.isEmpty = isEmpty
|
||||
self.isEditing = isEditing
|
||||
self.isAdd = isAdd
|
||||
}
|
||||
|
||||
func node(layout: GridNodeLayout, synchronousLoad: Bool) -> GridItemNode {
|
||||
let node = StickerPackPreviewGridItemNode()
|
||||
node.setup(context: self.context, stickerItem: self.stickerItem, interaction: self.interaction, theme: self.theme, isLocked: self.isLocked, isPremium: self.isPremium, isEmpty: self.isEmpty)
|
||||
node.setup(context: self.context, stickerItem: self.stickerItem, interaction: self.interaction, theme: self.theme, isLocked: self.isLocked, isPremium: self.isPremium, isEmpty: self.isEmpty, isEditing: self.isEditing, isAdd: self.isAdd)
|
||||
return node
|
||||
}
|
||||
|
||||
@ -64,17 +71,18 @@ final class StickerPackPreviewGridItem: GridItem {
|
||||
assertionFailure()
|
||||
return
|
||||
}
|
||||
node.setup(context: self.context, stickerItem: self.stickerItem, interaction: self.interaction, theme: self.theme, isLocked: self.isLocked, isPremium: self.isPremium, isEmpty: self.isEmpty)
|
||||
node.setup(context: self.context, stickerItem: self.stickerItem, interaction: self.interaction, theme: self.theme, isLocked: self.isLocked, isPremium: self.isPremium, isEmpty: self.isEmpty, isEditing: self.isEditing, isAdd: self.isAdd)
|
||||
}
|
||||
}
|
||||
|
||||
private let textFont = Font.regular(20.0)
|
||||
|
||||
final class StickerPackPreviewGridItemNode: GridItemNode {
|
||||
private var currentState: (AccountContext, StickerPackItem?)?
|
||||
private var currentState: (AccountContext, StickerPackItem?, Bool, Bool)?
|
||||
private var isLocked: Bool?
|
||||
private var isPremium: Bool?
|
||||
private var isEmpty: Bool?
|
||||
private let containerNode: ASDisplayNode
|
||||
private let imageNode: TransformImageNode
|
||||
private var animationNode: AnimatedStickerNode?
|
||||
private var placeholderNode: StickerShimmerEffectNode
|
||||
@ -85,6 +93,8 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
|
||||
|
||||
private var theme: PresentationTheme?
|
||||
|
||||
private var isEditing = false
|
||||
|
||||
override var isVisibleInGrid: Bool {
|
||||
didSet {
|
||||
let visibility = self.isVisibleInGrid && (self.interaction?.playAnimatedStickers ?? true)
|
||||
@ -103,14 +113,18 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
|
||||
private let effectFetchedDisposable = MetaDisposable()
|
||||
|
||||
var interaction: StickerPackPreviewInteraction?
|
||||
|
||||
var selected: (() -> Void)?
|
||||
|
||||
|
||||
var stickerPackItem: StickerPackItem? {
|
||||
return self.currentState?.1
|
||||
}
|
||||
|
||||
var isAdd: Bool {
|
||||
return self.currentState?.2 == true
|
||||
}
|
||||
|
||||
override init() {
|
||||
self.containerNode = ASDisplayNode()
|
||||
|
||||
self.imageNode = TransformImageNode()
|
||||
self.imageNode.isLayerBacked = !smartInvertColorsEnabled()
|
||||
self.placeholderNode = StickerShimmerEffectNode()
|
||||
@ -118,8 +132,9 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.imageNode)
|
||||
self.addSubnode(self.placeholderNode)
|
||||
self.addSubnode(self.containerNode)
|
||||
self.containerNode.addSubnode(self.imageNode)
|
||||
self.containerNode.addSubnode(self.placeholderNode)
|
||||
|
||||
var firstTime = true
|
||||
self.imageNode.imageUpdated = { [weak self] image in
|
||||
@ -172,15 +187,62 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
|
||||
self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.imageNodeTap(_:))))
|
||||
}
|
||||
|
||||
@objc private func handleAddTap() {
|
||||
self.interaction?.addPressed()
|
||||
}
|
||||
|
||||
private var setupTimestamp: Double?
|
||||
func setup(context: AccountContext, stickerItem: StickerPackItem?, interaction: StickerPackPreviewInteraction, theme: PresentationTheme, isLocked: Bool, isPremium: Bool, isEmpty: Bool) {
|
||||
func setup(context: AccountContext, stickerItem: StickerPackItem?, interaction: StickerPackPreviewInteraction, theme: PresentationTheme, isLocked: Bool, isPremium: Bool, isEmpty: Bool, isEditing: Bool, isAdd: Bool) {
|
||||
self.interaction = interaction
|
||||
self.theme = theme
|
||||
|
||||
let isFirstTime = self.currentState == nil
|
||||
if isAdd {
|
||||
if !isFirstTime {
|
||||
return
|
||||
}
|
||||
|
||||
let color = theme.actionSheet.controlAccentColor
|
||||
self.imageNode.setSignal(.single({ arguments in
|
||||
let drawingContext = DrawingContext(size: arguments.imageSize, opaque: false)
|
||||
let size = arguments.imageSize
|
||||
drawingContext?.withContext({ context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
UIGraphicsPushContext(context)
|
||||
|
||||
context.setFillColor(color.withMultipliedAlpha(0.1).cgColor)
|
||||
context.fillEllipse(in: CGRect(origin: .zero, size: size).insetBy(dx: 4.0, dy: 4.0))
|
||||
context.setFillColor(color.cgColor)
|
||||
|
||||
let plusSize = CGSize(width: 3.0, height: 21.0)
|
||||
context.addPath(UIBezierPath(roundedRect: CGRect(x: floorToScreenPixels((size.width - plusSize.width) / 2.0), y: floorToScreenPixels((size.height - plusSize.height) / 2.0), width: plusSize.width, height: plusSize.height), cornerRadius: plusSize.width / 2.0).cgPath)
|
||||
context.addPath(UIBezierPath(roundedRect: CGRect(x: floorToScreenPixels((size.width - plusSize.height) / 2.0), y: floorToScreenPixels((size.height - plusSize.width) / 2.0), width: plusSize.height, height: plusSize.width), cornerRadius: plusSize.width / 2.0).cgPath)
|
||||
context.fillPath()
|
||||
|
||||
UIGraphicsPopContext()
|
||||
})
|
||||
return drawingContext
|
||||
}))
|
||||
|
||||
self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.handleAddTap)))
|
||||
|
||||
self.currentState = (context, nil, true, false)
|
||||
self.setNeedsLayout()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if interaction.reorderingFileId != nil {
|
||||
self.isHidden = stickerItem?.file.fileId == interaction.reorderingFileId
|
||||
} else {
|
||||
self.isHidden = false
|
||||
}
|
||||
|
||||
if self.currentState == nil || self.currentState!.0 !== context || self.currentState!.1 != stickerItem || self.isLocked != isLocked || self.isPremium != isPremium || self.isEmpty != isEmpty {
|
||||
self.isLocked = isLocked
|
||||
|
||||
if isLocked {
|
||||
|
||||
if isLocked || isEditing {
|
||||
let lockBackground: UIVisualEffectView
|
||||
let lockIconNode: ASImageNode
|
||||
if let currentBackground = self.lockBackground, let currentIcon = self.lockIconNode {
|
||||
@ -198,7 +260,24 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
|
||||
lockBackground.isUserInteractionEnabled = false
|
||||
lockIconNode = ASImageNode()
|
||||
lockIconNode.displaysAsynchronously = false
|
||||
lockIconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat List/PeerPremiumIcon"), color: .white)
|
||||
|
||||
if isEditing {
|
||||
lockIconNode.image = generateImage(CGSize(width: 24.0, height: 24.0), contextGenerator: { size, context in
|
||||
context.clear(CGRect(origin: .zero, size: size))
|
||||
context.setFillColor(UIColor.white.cgColor)
|
||||
|
||||
context.addEllipse(in: CGRect(x: 5.5, y: 11.0, width: 3.0, height: 3.0))
|
||||
context.fillPath()
|
||||
|
||||
context.addEllipse(in: CGRect(x: size.width / 2.0 - 1.5, y: 11.0, width: 3.0, height: 3.0))
|
||||
context.fillPath()
|
||||
|
||||
context.addEllipse(in: CGRect(x: size.width - 3.0 - 5.5, y: 11.0, width: 3.0, height: 3.0))
|
||||
context.fillPath()
|
||||
})
|
||||
} else {
|
||||
lockIconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat List/PeerPremiumIcon"), color: .white)
|
||||
}
|
||||
|
||||
let lockTintView = UIView()
|
||||
lockTintView.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.15)
|
||||
@ -209,15 +288,21 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
|
||||
self.lockIconNode = lockIconNode
|
||||
|
||||
self.view.addSubview(lockBackground)
|
||||
self.addSubnode(lockIconNode)
|
||||
lockBackground.contentView.addSubview(lockIconNode.view)
|
||||
|
||||
if !isFirstTime {
|
||||
lockBackground.layer.animateScale(from: 0.01, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
} else if let lockBackground = self.lockBackground, let lockTintView = self.lockTintView, let lockIconNode = self.lockIconNode {
|
||||
} else if let lockBackground = self.lockBackground {
|
||||
self.lockBackground = nil
|
||||
self.lockTintView = nil
|
||||
self.lockIconNode = nil
|
||||
lockBackground.removeFromSuperview()
|
||||
lockTintView.removeFromSuperview()
|
||||
lockIconNode.removeFromSupernode()
|
||||
|
||||
lockBackground.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||
lockBackground.layer.animateScale(from: 1.0, to: 0.01, duration: 0.2, removeOnCompletion: false, completion: { _ in
|
||||
lockBackground.removeFromSuperview()
|
||||
})
|
||||
}
|
||||
|
||||
if let stickerItem = stickerItem {
|
||||
@ -237,7 +322,7 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
|
||||
if self.animationNode == nil {
|
||||
let animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
self.animationNode = animationNode
|
||||
self.insertSubnode(animationNode, aboveSubnode: self.imageNode)
|
||||
self.containerNode.insertSubnode(animationNode, aboveSubnode: self.imageNode)
|
||||
animationNode.started = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
@ -287,21 +372,85 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
|
||||
self.animationNode?.alpha = isLocked ? 0.5 : 1.0
|
||||
self.imageNode.alpha = isLocked ? 0.5 : 1.0
|
||||
|
||||
self.currentState = (context, stickerItem)
|
||||
self.currentState = (context, stickerItem, false, isEditing)
|
||||
self.setNeedsLayout()
|
||||
}
|
||||
self.isEmpty = isEmpty
|
||||
|
||||
if self.isEditing != isEditing {
|
||||
self.isEditing = isEditing
|
||||
if self.isEditing {
|
||||
self.startShaking()
|
||||
} else {
|
||||
self.containerNode.layer.removeAnimation(forKey: "shaking_position")
|
||||
self.containerNode.layer.removeAnimation(forKey: "shaking_rotation")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func startShaking() {
|
||||
func degreesToRadians(_ x: CGFloat) -> CGFloat {
|
||||
return .pi * x / 180.0
|
||||
}
|
||||
|
||||
let duration: Double = 0.4
|
||||
let displacement: CGFloat = 1.0
|
||||
let degreesRotation: CGFloat = 2.0
|
||||
|
||||
let negativeDisplacement = -1.0 * displacement
|
||||
let position = CAKeyframeAnimation.init(keyPath: "position")
|
||||
position.beginTime = 0.8
|
||||
position.duration = duration
|
||||
position.values = [
|
||||
NSValue(cgPoint: CGPoint(x: negativeDisplacement, y: negativeDisplacement)),
|
||||
NSValue(cgPoint: CGPoint(x: 0, y: 0)),
|
||||
NSValue(cgPoint: CGPoint(x: negativeDisplacement, y: 0)),
|
||||
NSValue(cgPoint: CGPoint(x: 0, y: negativeDisplacement)),
|
||||
NSValue(cgPoint: CGPoint(x: negativeDisplacement, y: negativeDisplacement))
|
||||
]
|
||||
position.calculationMode = .linear
|
||||
position.isRemovedOnCompletion = false
|
||||
position.repeatCount = Float.greatestFiniteMagnitude
|
||||
position.beginTime = CFTimeInterval(Float(arc4random()).truncatingRemainder(dividingBy: Float(25)) / Float(100))
|
||||
position.isAdditive = true
|
||||
|
||||
let transform = CAKeyframeAnimation.init(keyPath: "transform")
|
||||
transform.beginTime = 2.6
|
||||
transform.duration = 0.3
|
||||
transform.valueFunction = CAValueFunction(name: CAValueFunctionName.rotateZ)
|
||||
transform.values = [
|
||||
degreesToRadians(-1.0 * degreesRotation),
|
||||
degreesToRadians(degreesRotation),
|
||||
degreesToRadians(-1.0 * degreesRotation)
|
||||
]
|
||||
transform.calculationMode = .linear
|
||||
transform.isRemovedOnCompletion = false
|
||||
transform.repeatCount = Float.greatestFiniteMagnitude
|
||||
transform.isAdditive = true
|
||||
transform.beginTime = CFTimeInterval(Float(arc4random()).truncatingRemainder(dividingBy: Float(25)) / Float(100))
|
||||
|
||||
self.containerNode.layer.add(position, forKey: "shaking_position")
|
||||
self.containerNode.layer.add(transform, forKey: "shaking_rotation")
|
||||
}
|
||||
|
||||
override func layout() {
|
||||
super.layout()
|
||||
|
||||
let bounds = self.bounds
|
||||
self.containerNode.frame = bounds
|
||||
|
||||
let boundsSide = min(bounds.size.width - 14.0, bounds.size.height - 14.0)
|
||||
var boundingSize = CGSize(width: boundsSide, height: boundsSide)
|
||||
|
||||
if let (_, item) = self.currentState {
|
||||
if let item = item, let dimensions = item.file.dimensions?.cgSize {
|
||||
if let (_, item, isAdd, _) = self.currentState {
|
||||
if isAdd {
|
||||
let imageSize = CGSize(width: 512, height: 512).aspectFitted(boundingSize)
|
||||
let imageFrame = CGRect(origin: CGPoint(x: floor((bounds.size.width - imageSize.width) / 2.0), y: (bounds.size.height - imageSize.height) / 2.0), size: imageSize)
|
||||
self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets()))()
|
||||
self.imageNode.frame = imageFrame
|
||||
|
||||
return
|
||||
} else if let item = item, let dimensions = item.file.dimensions?.cgSize {
|
||||
if item.file.isPremiumSticker {
|
||||
boundingSize = CGSize(width: boundingSize.width * 1.1, height: boundingSize.width * 1.1)
|
||||
}
|
||||
@ -322,13 +471,20 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
|
||||
let placeholderFrame = imageFrame
|
||||
self.placeholderNode.frame = imageFrame
|
||||
|
||||
if let theme = self.theme, let (context, stickerItem) = self.currentState, let item = stickerItem {
|
||||
if let theme = self.theme, let (context, stickerItem, _, _) = self.currentState, let item = stickerItem {
|
||||
self.placeholderNode.update(backgroundColor: theme.list.itemBlocksBackgroundColor, foregroundColor: theme.list.mediaPlaceholderColor, shimmeringColor: theme.list.itemBlocksBackgroundColor.withAlphaComponent(0.4), data: item.file.immediateThumbnailData, size: placeholderFrame.size, enableEffect: context.sharedContext.energyUsageSettings.fullTranslucency)
|
||||
}
|
||||
|
||||
if let lockBackground = self.lockBackground, let lockTintView = self.lockTintView, let lockIconNode = self.lockIconNode {
|
||||
let lockSize = CGSize(width: 16.0, height: 16.0)
|
||||
let lockBackgroundFrame = CGRect(origin: CGPoint(x: bounds.width - lockSize.width, y: bounds.height - lockSize.height), size: lockSize)
|
||||
let lockSize: CGSize
|
||||
let lockBackgroundFrame: CGRect
|
||||
if let (_, _, _, isEditing) = self.currentState, isEditing {
|
||||
lockSize = CGSize(width: 24.0, height: 24.0)
|
||||
lockBackgroundFrame = CGRect(origin: CGPoint(x: 3.0, y: 3.0), size: lockSize)
|
||||
} else {
|
||||
lockSize = CGSize(width: 16.0, height: 16.0)
|
||||
lockBackgroundFrame = CGRect(origin: CGPoint(x: bounds.width - lockSize.width, y: bounds.height - lockSize.height), size: lockSize)
|
||||
}
|
||||
lockBackground.frame = lockBackgroundFrame
|
||||
lockBackground.layer.cornerRadius = lockSize.width / 2.0
|
||||
if #available(iOS 13.0, *) {
|
||||
@ -337,7 +493,7 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
|
||||
lockTintView.frame = CGRect(origin: CGPoint(), size: lockBackgroundFrame.size)
|
||||
if let icon = lockIconNode.image {
|
||||
let iconSize = CGSize(width: icon.size.width - 4.0, height: icon.size.height - 4.0)
|
||||
lockIconNode.frame = CGRect(origin: CGPoint(x: lockBackgroundFrame.minX + floorToScreenPixels((lockBackgroundFrame.width - iconSize.width) / 2.0), y: lockBackgroundFrame.minY + floorToScreenPixels((lockBackgroundFrame.height - iconSize.height) / 2.0)), size: iconSize)
|
||||
lockIconNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((lockBackgroundFrame.width - iconSize.width) / 2.0), y: floorToScreenPixels((lockBackgroundFrame.height - iconSize.height) / 2.0)), size: iconSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -355,7 +511,10 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
|
||||
|
||||
func updatePreviewing(animated: Bool) {
|
||||
var isPreviewing = false
|
||||
if let (_, maybeItem) = self.currentState, let interaction = self.interaction, let item = maybeItem {
|
||||
if let (_, maybeItem, isAdd, _) = self.currentState, let interaction = self.interaction, let item = maybeItem {
|
||||
if isAdd {
|
||||
return
|
||||
}
|
||||
isPreviewing = interaction.previewedItem == .pack(item.file)
|
||||
}
|
||||
if self.currentIsPreviewing != isPreviewing {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -28,6 +28,8 @@ swift_library(
|
||||
"//submodules/ShimmerEffect:ShimmerEffect",
|
||||
"//submodules/ContextUI:ContextUI",
|
||||
"//submodules/SolidRoundedButtonNode:SolidRoundedButtonNode",
|
||||
"//submodules/ReactionSelectionNode",
|
||||
"//submodules/TelegramUI/Components/EntityKeyboard",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -13,17 +13,22 @@ import SolidRoundedButtonNode
|
||||
import TelegramPresentationData
|
||||
import AccountContext
|
||||
import AppBundle
|
||||
import ReactionSelectionNode
|
||||
import EntityKeyboard
|
||||
|
||||
public enum StickerPreviewPeekItem: Equatable {
|
||||
case pack(TelegramMediaFile)
|
||||
case found(FoundStickerItem)
|
||||
case image(UIImage)
|
||||
|
||||
public var file: TelegramMediaFile {
|
||||
public var file: TelegramMediaFile? {
|
||||
switch self {
|
||||
case let .pack(file):
|
||||
return file
|
||||
case let .found(item):
|
||||
return item.file
|
||||
case .image:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -34,20 +39,24 @@ public final class StickerPreviewPeekContent: PeekControllerContent {
|
||||
let strings: PresentationStrings
|
||||
public let item: StickerPreviewPeekItem
|
||||
let isLocked: Bool
|
||||
let isCreating: Bool
|
||||
let reactionItems: [ReactionItem]
|
||||
let menu: [ContextMenuItem]
|
||||
let openPremiumIntro: () -> Void
|
||||
|
||||
public init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, item: StickerPreviewPeekItem, isLocked: Bool = false, menu: [ContextMenuItem], openPremiumIntro: @escaping () -> Void) {
|
||||
public init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, item: StickerPreviewPeekItem, isLocked: Bool = false, isCreating: Bool = false, menu: [ContextMenuItem], reactionItems: [ReactionItem] = [], openPremiumIntro: @escaping () -> Void) {
|
||||
self.context = context
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.item = item
|
||||
self.isLocked = isLocked
|
||||
self.isCreating = isCreating
|
||||
if isLocked {
|
||||
self.menu = []
|
||||
} else {
|
||||
self.menu = menu
|
||||
}
|
||||
self.reactionItems = reactionItems
|
||||
self.openPremiumIntro = openPremiumIntro
|
||||
}
|
||||
|
||||
@ -72,10 +81,11 @@ public final class StickerPreviewPeekContent: PeekControllerContent {
|
||||
}
|
||||
|
||||
public func fullScreenAccessoryNode(blurView: UIVisualEffectView) -> (PeekControllerAccessoryNode & ASDisplayNode)? {
|
||||
if self.isCreating {
|
||||
return EmojiStickerAccessoryNode(context: self.context, theme: self.theme, reactionItems: self.reactionItems)
|
||||
}
|
||||
if self.isLocked {
|
||||
let isEmoji = self.item.file.isCustomEmoji
|
||||
|
||||
return PremiumStickerPackAccessoryNode(theme: self.theme, strings: self.strings, isEmoji: isEmoji, proceed: self.openPremiumIntro)
|
||||
return PremiumStickerPackAccessoryNode(theme: self.theme, strings: self.strings, isEmoji: self.item.file?.isCustomEmoji ?? false, proceed: self.openPremiumIntro)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
@ -112,51 +122,57 @@ public final class StickerPreviewPeekContentNode: ASDisplayNode, PeekControllerC
|
||||
self.textNode = ASTextNode()
|
||||
self.imageNode = TransformImageNode()
|
||||
|
||||
for case let .Sticker(text, _, _) in item.file.attributes {
|
||||
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(32.0), textColor: .black)
|
||||
break
|
||||
}
|
||||
|
||||
let isPremiumSticker = item.file.isPremiumSticker
|
||||
|
||||
if item.file.isAnimatedSticker || item.file.isVideoSticker {
|
||||
let animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
animationNode.overrideVisibility = true
|
||||
self.animationNode = animationNode
|
||||
|
||||
let dimensions = item.file.dimensions ?? PixelDimensions(width: 512, height: 512)
|
||||
let fitSize: CGSize
|
||||
if item.file.isCustomEmoji {
|
||||
fitSize = CGSize(width: 200.0, height: 200.0)
|
||||
} else {
|
||||
fitSize = CGSize(width: 400.0, height: 400.0)
|
||||
}
|
||||
let fittedDimensions = dimensions.cgSize.aspectFitted(fitSize)
|
||||
|
||||
if item.file.isCustomTemplateEmoji {
|
||||
animationNode.dynamicColor = theme.list.itemPrimaryTextColor
|
||||
var isPremiumSticker = false
|
||||
if let file = item.file {
|
||||
for case let .Sticker(text, _, _) in file.attributes {
|
||||
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(32.0), textColor: .black)
|
||||
break
|
||||
}
|
||||
|
||||
animationNode.setup(source: AnimatedStickerResourceSource(account: context.account, resource: item.file.resource, isVideo: item.file.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), playbackMode: isPremiumSticker ? .once : .loop, mode: .direct(cachePathPrefix: nil))
|
||||
animationNode.visibility = true
|
||||
animationNode.addSubnode(self.textNode)
|
||||
isPremiumSticker = file.isPremiumSticker
|
||||
|
||||
if isPremiumSticker, let effect = item.file.videoThumbnails.first {
|
||||
self.effectDisposable.set(freeMediaFileResourceInteractiveFetched(account: context.account, userLocation: .other, fileReference: .standalone(media: item.file), resource: effect.resource).start())
|
||||
if file.isAnimatedSticker || file.isVideoSticker {
|
||||
let animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
animationNode.overrideVisibility = true
|
||||
self.animationNode = animationNode
|
||||
|
||||
let source = AnimatedStickerResourceSource(account: context.account, resource: effect.resource, fitzModifier: nil)
|
||||
let additionalAnimationNode = DefaultAnimatedStickerNodeImpl()
|
||||
additionalAnimationNode.setup(source: source, width: Int(fittedDimensions.width * 2.0), height: Int(fittedDimensions.height * 2.0), playbackMode: .once, mode: .direct(cachePathPrefix: nil))
|
||||
additionalAnimationNode.visibility = true
|
||||
self.additionalAnimationNode = additionalAnimationNode
|
||||
let dimensions = file.dimensions ?? PixelDimensions(width: 512, height: 512)
|
||||
let fitSize: CGSize
|
||||
if file.isCustomEmoji {
|
||||
fitSize = CGSize(width: 200.0, height: 200.0)
|
||||
} else {
|
||||
fitSize = CGSize(width: 400.0, height: 400.0)
|
||||
}
|
||||
let fittedDimensions = dimensions.cgSize.aspectFitted(fitSize)
|
||||
|
||||
if file.isCustomTemplateEmoji {
|
||||
animationNode.dynamicColor = theme.list.itemPrimaryTextColor
|
||||
}
|
||||
|
||||
animationNode.setup(source: AnimatedStickerResourceSource(account: context.account, resource: file.resource, isVideo: file.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), playbackMode: isPremiumSticker ? .once : .loop, mode: .direct(cachePathPrefix: nil))
|
||||
animationNode.visibility = true
|
||||
animationNode.addSubnode(self.textNode)
|
||||
|
||||
if isPremiumSticker, let effect = file.videoThumbnails.first {
|
||||
self.effectDisposable.set(freeMediaFileResourceInteractiveFetched(account: context.account, userLocation: .other, fileReference: .standalone(media: file), resource: effect.resource).start())
|
||||
|
||||
let source = AnimatedStickerResourceSource(account: context.account, resource: effect.resource, fitzModifier: nil)
|
||||
let additionalAnimationNode = DefaultAnimatedStickerNodeImpl()
|
||||
additionalAnimationNode.setup(source: source, width: Int(fittedDimensions.width * 2.0), height: Int(fittedDimensions.height * 2.0), playbackMode: .once, mode: .direct(cachePathPrefix: nil))
|
||||
additionalAnimationNode.visibility = true
|
||||
self.additionalAnimationNode = additionalAnimationNode
|
||||
}
|
||||
} else {
|
||||
self.imageNode.addSubnode(self.textNode)
|
||||
self.animationNode = nil
|
||||
}
|
||||
} else {
|
||||
self.imageNode.addSubnode(self.textNode)
|
||||
self.animationNode = nil
|
||||
|
||||
self.imageNode.setSignal(chatMessageSticker(account: context.account, userLocation: .other, file: file, small: false, fetched: true))
|
||||
} else if case let .image(image) = item {
|
||||
self.imageNode.contents = image.cgImage
|
||||
self._ready.set(.single(true))
|
||||
}
|
||||
|
||||
self.imageNode.setSignal(chatMessageSticker(account: context.account, userLocation: .other, file: item.file, small: false, fetched: true))
|
||||
|
||||
super.init()
|
||||
|
||||
self.isUserInteractionEnabled = false
|
||||
@ -209,57 +225,60 @@ public final class StickerPreviewPeekContentNode: ASDisplayNode, PeekControllerC
|
||||
|
||||
public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||
let boundingSize: CGSize
|
||||
if self.item.file.isCustomEmoji {
|
||||
if self.item.file?.isCustomEmoji == true {
|
||||
boundingSize = CGSize(width: 120.0, height: 120.0)
|
||||
} else if let _ = self.additionalAnimationNode {
|
||||
boundingSize = CGSize(width: 240.0, height: 240.0).fitted(size)
|
||||
} else {
|
||||
boundingSize = CGSize(width: 180.0, height: 180.0).fitted(size)
|
||||
}
|
||||
|
||||
if let dimensitons = self.item.file.dimensions {
|
||||
var topOffset: CGFloat = 0.0
|
||||
var textSpacing: CGFloat = 50.0
|
||||
|
||||
if size.width == 292.0 {
|
||||
topOffset = 60.0
|
||||
textSpacing -= 10.0
|
||||
} else if size.width == 347.0 && size.height == 577.0 {
|
||||
topOffset = 60.0
|
||||
textSpacing -= 10.0
|
||||
}
|
||||
|
||||
let textSize = self.textNode.measure(CGSize(width: 100.0, height: 100.0))
|
||||
|
||||
let imageSize = dimensitons.cgSize.aspectFitted(boundingSize)
|
||||
self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets()))()
|
||||
var imageFrame = CGRect(origin: CGPoint(x: floor((boundingSize.width - imageSize.width) / 2.0), y: textSize.height + textSpacing - topOffset), size: imageSize)
|
||||
var centerOffset: CGFloat = 0.0
|
||||
if self.item.file.isPremiumSticker {
|
||||
let originalImageFrame = imageFrame
|
||||
imageFrame.origin.x = min(imageFrame.minX + imageFrame.width * 0.1, size.width - imageFrame.width - 18.0)
|
||||
centerOffset = imageFrame.minX - originalImageFrame.minX
|
||||
}
|
||||
self.imageNode.frame = imageFrame
|
||||
if let animationNode = self.animationNode {
|
||||
animationNode.frame = imageFrame
|
||||
animationNode.updateLayout(size: imageSize)
|
||||
|
||||
if let additionalAnimationNode = self.additionalAnimationNode {
|
||||
additionalAnimationNode.frame = imageFrame.offsetBy(dx: -imageFrame.width * 0.245 + 21.0, dy: -1.0).insetBy(dx: -imageFrame.width * 0.245, dy: -imageFrame.height * 0.245)
|
||||
additionalAnimationNode.updateLayout(size: additionalAnimationNode.frame.size)
|
||||
}
|
||||
}
|
||||
|
||||
self.textNode.frame = CGRect(origin: CGPoint(x: floor((imageFrame.size.width - textSize.width) / 2.0) - centerOffset, y: -textSize.height - textSpacing), size: textSize)
|
||||
|
||||
if self.item.file.isCustomEmoji {
|
||||
return CGSize(width: boundingSize.width, height: imageFrame.height)
|
||||
} else {
|
||||
return CGSize(width: boundingSize.width, height: imageFrame.height + textSize.height + textSpacing)
|
||||
}
|
||||
|
||||
let dimensions: PixelDimensions
|
||||
if let dimensionsValue = self.item.file?.dimensions {
|
||||
dimensions = dimensionsValue
|
||||
} else {
|
||||
return CGSize(width: size.width, height: 10.0)
|
||||
dimensions = PixelDimensions(width: 512, height: 512)
|
||||
}
|
||||
|
||||
var topOffset: CGFloat = 0.0
|
||||
var textSpacing: CGFloat = 50.0
|
||||
|
||||
if size.width == 292.0 {
|
||||
topOffset = 60.0
|
||||
textSpacing -= 10.0
|
||||
} else if size.width == 347.0 && size.height == 577.0 {
|
||||
topOffset = 60.0
|
||||
textSpacing -= 10.0
|
||||
}
|
||||
|
||||
let textSize = self.textNode.measure(CGSize(width: 100.0, height: 100.0))
|
||||
|
||||
let imageSize = dimensions.cgSize.aspectFitted(boundingSize)
|
||||
self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets()))()
|
||||
var imageFrame = CGRect(origin: CGPoint(x: floor((boundingSize.width - imageSize.width) / 2.0), y: textSize.height + textSpacing - topOffset), size: imageSize)
|
||||
var centerOffset: CGFloat = 0.0
|
||||
if self.item.file?.isPremiumSticker == true {
|
||||
let originalImageFrame = imageFrame
|
||||
imageFrame.origin.x = min(imageFrame.minX + imageFrame.width * 0.1, size.width - imageFrame.width - 18.0)
|
||||
centerOffset = imageFrame.minX - originalImageFrame.minX
|
||||
}
|
||||
self.imageNode.frame = imageFrame
|
||||
if let animationNode = self.animationNode {
|
||||
animationNode.frame = imageFrame
|
||||
animationNode.updateLayout(size: imageSize)
|
||||
|
||||
if let additionalAnimationNode = self.additionalAnimationNode {
|
||||
additionalAnimationNode.frame = imageFrame.offsetBy(dx: -imageFrame.width * 0.245 + 21.0, dy: -1.0).insetBy(dx: -imageFrame.width * 0.245, dy: -imageFrame.height * 0.245)
|
||||
additionalAnimationNode.updateLayout(size: additionalAnimationNode.frame.size)
|
||||
}
|
||||
}
|
||||
|
||||
self.textNode.frame = CGRect(origin: CGPoint(x: floor((imageFrame.size.width - textSize.width) / 2.0) - centerOffset, y: -textSize.height - textSpacing), size: textSize)
|
||||
|
||||
if self.item.file?.isCustomEmoji == true {
|
||||
return CGSize(width: boundingSize.width, height: imageFrame.height)
|
||||
} else {
|
||||
return CGSize(width: boundingSize.width, height: imageFrame.height + textSize.height + textSpacing)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -337,3 +356,146 @@ final class PremiumStickerPackAccessoryNode: SparseNode, PeekControllerAccessory
|
||||
self.textNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: size.height - cancelSize.height - 48.0 - buttonHeight - 20.0 - textSize.height - 31.0 + bottomOffset), size: textSize)
|
||||
}
|
||||
}
|
||||
|
||||
final class EmojiStickerAccessoryNode: SparseNode, PeekControllerAccessoryNode {
|
||||
let context: AccountContext
|
||||
|
||||
let reactionContextNode: ReactionContextNode
|
||||
|
||||
var dismiss: () -> Void = {}
|
||||
|
||||
init(context: AccountContext, theme: PresentationTheme, reactionItems: [ReactionItem]) {
|
||||
self.context = context
|
||||
|
||||
var layoutImpl: ((ContainedViewLayoutTransition) -> Void)?
|
||||
|
||||
var items: [ReactionItem] = []
|
||||
if let reaction = reactionItems.first(where: { $0.reaction.rawValue == .builtin("👍") }) {
|
||||
items.append(reaction)
|
||||
}
|
||||
if let reaction = reactionItems.first(where: { $0.reaction.rawValue == .builtin("👎") }) {
|
||||
items.append(reaction)
|
||||
}
|
||||
if let reaction = reactionItems.first(where: { $0.reaction.rawValue == .builtin("❤") }) {
|
||||
items.append(reaction)
|
||||
}
|
||||
if let reaction = reactionItems.first(where: { $0.reaction.rawValue == .builtin("🔥") }) {
|
||||
items.append(reaction)
|
||||
}
|
||||
if let reaction = reactionItems.first(where: { $0.reaction.rawValue == .builtin("🥰") }) {
|
||||
items.append(reaction)
|
||||
}
|
||||
if let reaction = reactionItems.first(where: { $0.reaction.rawValue == .builtin("👏") }) {
|
||||
items.append(reaction)
|
||||
}
|
||||
if let reaction = reactionItems.first(where: { $0.reaction.rawValue == .builtin("😁") }) {
|
||||
items.append(reaction)
|
||||
}
|
||||
|
||||
let selectedItems = ValuePromise<Set<MessageReaction.Reaction>>()
|
||||
//TODO:localize
|
||||
let reactionContextNode = ReactionContextNode(
|
||||
context: self.context,
|
||||
animationCache: self.context.animationCache,
|
||||
presentationData: self.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: defaultDarkPresentationTheme),
|
||||
items: items.map { .reaction($0) },
|
||||
selectedItems: Set(),
|
||||
title: "Set emoji that corresponds to your sticker",
|
||||
reactionsLocked: false,
|
||||
alwaysAllowPremiumReactions: true,
|
||||
allPresetReactionsAreAvailable: true,
|
||||
getEmojiContent: { animationCache, animationRenderer in
|
||||
let mappedReactionItems: [EmojiComponentReactionItem] = items.map { reaction -> EmojiComponentReactionItem in
|
||||
return EmojiComponentReactionItem(reaction: reaction.reaction.rawValue, file: reaction.stillAnimation)
|
||||
}
|
||||
|
||||
return selectedItems.get()
|
||||
|> mapToSignal { selectedItems in
|
||||
let selected = Set<MediaId>()
|
||||
|
||||
return EmojiPagerContentComponent.emojiInputData(
|
||||
context: context,
|
||||
animationCache: animationCache,
|
||||
animationRenderer: animationRenderer,
|
||||
isStandalone: false,
|
||||
subject: .stickerAlt,
|
||||
hasTrending: false,
|
||||
topReactionItems: mappedReactionItems,
|
||||
areUnicodeEmojiEnabled: true,
|
||||
areCustomEmojiEnabled: false,
|
||||
chatPeerId: context.account.peerId,
|
||||
selectedItems: selected,
|
||||
hasRecent: false,
|
||||
premiumIfSavedMessages: false
|
||||
)
|
||||
}
|
||||
},
|
||||
isExpandedUpdated: { transition in
|
||||
layoutImpl?(transition)
|
||||
},
|
||||
requestLayout: { transition in
|
||||
layoutImpl?(transition)
|
||||
},
|
||||
requestUpdateOverlayWantsToBeBelowKeyboard: { transition in
|
||||
layoutImpl?(transition)
|
||||
}
|
||||
)
|
||||
reactionContextNode.displayTail = true
|
||||
reactionContextNode.forceTailToRight = true
|
||||
reactionContextNode.forceDark = true
|
||||
self.reactionContextNode = reactionContextNode
|
||||
|
||||
super.init()
|
||||
|
||||
layoutImpl = { [weak self] transition in
|
||||
self?.requestLayout(transition: transition)
|
||||
}
|
||||
|
||||
reactionContextNode.reactionSelected = { [weak self] updateReaction, _ in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.reactionContextNode.collapse()
|
||||
|
||||
let _ = (selectedItems.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).startStandalone(next: { items in
|
||||
var items = items
|
||||
items.insert(updateReaction.reaction)
|
||||
selectedItems.set(items)
|
||||
})
|
||||
}
|
||||
|
||||
self.addSubnode(reactionContextNode)
|
||||
}
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
let result = super.hitTest(point, with: event)
|
||||
if let result, result.isDescendant(of: self.reactionContextNode.view) {
|
||||
return result
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func requestLayout(transition: ContainedViewLayoutTransition) {
|
||||
guard let size = self.currentLayout else {
|
||||
return
|
||||
}
|
||||
self.updateLayout(size: size, transition: transition)
|
||||
}
|
||||
|
||||
private var currentLayout: CGSize?
|
||||
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
|
||||
let isFirstTime = self.currentLayout == nil
|
||||
self.currentLayout = size
|
||||
|
||||
let anchorRect = CGRect(x: size.width / 2.0, y: size.height / 3.0 - 50.0, width: 0.0, height: 0.0)
|
||||
|
||||
transition.updateFrame(view: self.reactionContextNode.view, frame: CGRect(origin: CGPoint(), size: size))
|
||||
self.reactionContextNode.updateLayout(size: size, insets: UIEdgeInsets(top: 64.0, left: 0.0, bottom: 0.0, right: 0.0), anchorRect: anchorRect, centerAligned: true, isCoveredByInput: false, isAnimatingOut: false, transition: transition)
|
||||
|
||||
if isFirstTime {
|
||||
self.reactionContextNode.animateIn(from: anchorRect)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ public enum Api {
|
||||
public enum channels {}
|
||||
public enum chatlists {}
|
||||
public enum contacts {}
|
||||
public enum fragment {}
|
||||
public enum help {}
|
||||
public enum messages {}
|
||||
public enum payments {}
|
||||
@ -28,6 +29,7 @@ public enum Api {
|
||||
public enum chatlists {}
|
||||
public enum contacts {}
|
||||
public enum folders {}
|
||||
public enum fragment {}
|
||||
public enum help {}
|
||||
public enum langpack {}
|
||||
public enum messages {}
|
||||
@ -321,6 +323,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-1736378792] = { return Api.InputCheckPasswordSRP.parse_inputCheckPasswordEmpty($0) }
|
||||
dict[-763367294] = { return Api.InputCheckPasswordSRP.parse_inputCheckPasswordSRP($0) }
|
||||
dict[1968737087] = { return Api.InputClientProxy.parse_inputClientProxy($0) }
|
||||
dict[-1562241884] = { return Api.InputCollectible.parse_inputCollectiblePhone($0) }
|
||||
dict[-476815191] = { return Api.InputCollectible.parse_inputCollectibleUsername($0) }
|
||||
dict[-208488460] = { return Api.InputContact.parse_inputPhoneContact($0) }
|
||||
dict[-55902537] = { return Api.InputDialogPeer.parse_inputDialogPeer($0) }
|
||||
dict[1684014375] = { return Api.InputDialogPeer.parse_inputDialogPeerFolder($0) }
|
||||
@ -491,7 +495,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[340088945] = { return Api.MediaArea.parse_mediaAreaSuggestedReaction($0) }
|
||||
dict[-1098720356] = { return Api.MediaArea.parse_mediaAreaVenue($0) }
|
||||
dict[64088654] = { return Api.MediaAreaCoordinates.parse_mediaAreaCoordinates($0) }
|
||||
dict[-1502839044] = { return Api.Message.parse_message($0) }
|
||||
dict[592953125] = { return Api.Message.parse_message($0) }
|
||||
dict[-1868117372] = { return Api.Message.parse_messageEmpty($0) }
|
||||
dict[721967202] = { return Api.Message.parse_messageService($0) }
|
||||
dict[-872240531] = { return Api.MessageAction.parse_messageActionBoostApply($0) }
|
||||
@ -667,7 +671,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-901375139] = { return Api.PeerLocated.parse_peerLocated($0) }
|
||||
dict[-118740917] = { return Api.PeerLocated.parse_peerSelfLocated($0) }
|
||||
dict[-1721619444] = { return Api.PeerNotifySettings.parse_peerNotifySettings($0) }
|
||||
dict[-1525149427] = { return Api.PeerSettings.parse_peerSettings($0) }
|
||||
dict[-1395233698] = { return Api.PeerSettings.parse_peerSettings($0) }
|
||||
dict[-1707742823] = { return Api.PeerStories.parse_peerStories($0) }
|
||||
dict[-1770029977] = { return Api.PhoneCall.parse_phoneCall($0) }
|
||||
dict[912311057] = { return Api.PhoneCall.parse_phoneCallAccepted($0) }
|
||||
@ -884,7 +888,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-1873947492] = { return Api.Update.parse_updateBotChatBoost($0) }
|
||||
dict[299870598] = { return Api.Update.parse_updateBotChatInviteRequester($0) }
|
||||
dict[1299263278] = { return Api.Update.parse_updateBotCommands($0) }
|
||||
dict[-1590796039] = { return Api.Update.parse_updateBotDeleteBusinessMessage($0) }
|
||||
dict[-1607821266] = { return Api.Update.parse_updateBotDeleteBusinessMessage($0) }
|
||||
dict[1420915171] = { return Api.Update.parse_updateBotEditBusinessMessage($0) }
|
||||
dict[1232025500] = { return Api.Update.parse_updateBotInlineQuery($0) }
|
||||
dict[317794823] = { return Api.Update.parse_updateBotInlineSend($0) }
|
||||
@ -1125,6 +1129,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[1891070632] = { return Api.contacts.TopPeers.parse_topPeers($0) }
|
||||
dict[-1255369827] = { return Api.contacts.TopPeers.parse_topPeersDisabled($0) }
|
||||
dict[-567906571] = { return Api.contacts.TopPeers.parse_topPeersNotModified($0) }
|
||||
dict[1857945489] = { return Api.fragment.CollectibleInfo.parse_collectibleInfo($0) }
|
||||
dict[-585598930] = { return Api.help.AppConfig.parse_appConfig($0) }
|
||||
dict[2094949405] = { return Api.help.AppConfig.parse_appConfigNotModified($0) }
|
||||
dict[-860107216] = { return Api.help.AppUpdate.parse_appUpdate($0) }
|
||||
@ -1203,6 +1208,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-1938715001] = { return Api.messages.Messages.parse_messages($0) }
|
||||
dict[1951620897] = { return Api.messages.Messages.parse_messagesNotModified($0) }
|
||||
dict[978610270] = { return Api.messages.Messages.parse_messagesSlice($0) }
|
||||
dict[-83926371] = { return Api.messages.MyStickers.parse_myStickers($0) }
|
||||
dict[863093588] = { return Api.messages.PeerDialogs.parse_peerDialogs($0) }
|
||||
dict[1753266509] = { return Api.messages.PeerSettings.parse_peerSettings($0) }
|
||||
dict[-963811691] = { return Api.messages.QuickReplies.parse_quickReplies($0) }
|
||||
@ -1315,7 +1321,7 @@ public extension Api {
|
||||
return parser(reader)
|
||||
}
|
||||
else {
|
||||
telegramApiLog("Type constructor \(String(signature, radix: 16, uppercase: false)) not found")
|
||||
telegramApiLog("Type constructor \(String(UInt32(bitPattern: signature), radix: 16, uppercase: false)) not found")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@ -1587,6 +1593,8 @@ public extension Api {
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.InputClientProxy:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.InputCollectible:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.InputContact:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.InputDialogPeer:
|
||||
@ -2043,6 +2051,8 @@ public extension Api {
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.contacts.TopPeers:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.fragment.CollectibleInfo:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.help.AppConfig:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.help.AppUpdate:
|
||||
@ -2149,6 +2159,8 @@ public extension Api {
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.messages.Messages:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.messages.MyStickers:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.messages.PeerDialogs:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.messages.PeerSettings:
|
||||
|
@ -1,3 +1,183 @@
|
||||
public extension Api {
|
||||
enum InputPrivacyRule: TypeConstructorDescription {
|
||||
case inputPrivacyValueAllowAll
|
||||
case inputPrivacyValueAllowChatParticipants(chats: [Int64])
|
||||
case inputPrivacyValueAllowCloseFriends
|
||||
case inputPrivacyValueAllowContacts
|
||||
case inputPrivacyValueAllowUsers(users: [Api.InputUser])
|
||||
case inputPrivacyValueDisallowAll
|
||||
case inputPrivacyValueDisallowChatParticipants(chats: [Int64])
|
||||
case inputPrivacyValueDisallowContacts
|
||||
case inputPrivacyValueDisallowUsers(users: [Api.InputUser])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputPrivacyValueAllowAll:
|
||||
if boxed {
|
||||
buffer.appendInt32(407582158)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputPrivacyValueAllowChatParticipants(let chats):
|
||||
if boxed {
|
||||
buffer.appendInt32(-2079962673)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(chats.count))
|
||||
for item in chats {
|
||||
serializeInt64(item, buffer: buffer, boxed: false)
|
||||
}
|
||||
break
|
||||
case .inputPrivacyValueAllowCloseFriends:
|
||||
if boxed {
|
||||
buffer.appendInt32(793067081)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputPrivacyValueAllowContacts:
|
||||
if boxed {
|
||||
buffer.appendInt32(218751099)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputPrivacyValueAllowUsers(let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(320652927)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
case .inputPrivacyValueDisallowAll:
|
||||
if boxed {
|
||||
buffer.appendInt32(-697604407)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputPrivacyValueDisallowChatParticipants(let chats):
|
||||
if boxed {
|
||||
buffer.appendInt32(-380694650)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(chats.count))
|
||||
for item in chats {
|
||||
serializeInt64(item, buffer: buffer, boxed: false)
|
||||
}
|
||||
break
|
||||
case .inputPrivacyValueDisallowContacts:
|
||||
if boxed {
|
||||
buffer.appendInt32(195371015)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputPrivacyValueDisallowUsers(let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1877932953)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputPrivacyValueAllowAll:
|
||||
return ("inputPrivacyValueAllowAll", [])
|
||||
case .inputPrivacyValueAllowChatParticipants(let chats):
|
||||
return ("inputPrivacyValueAllowChatParticipants", [("chats", chats as Any)])
|
||||
case .inputPrivacyValueAllowCloseFriends:
|
||||
return ("inputPrivacyValueAllowCloseFriends", [])
|
||||
case .inputPrivacyValueAllowContacts:
|
||||
return ("inputPrivacyValueAllowContacts", [])
|
||||
case .inputPrivacyValueAllowUsers(let users):
|
||||
return ("inputPrivacyValueAllowUsers", [("users", users as Any)])
|
||||
case .inputPrivacyValueDisallowAll:
|
||||
return ("inputPrivacyValueDisallowAll", [])
|
||||
case .inputPrivacyValueDisallowChatParticipants(let chats):
|
||||
return ("inputPrivacyValueDisallowChatParticipants", [("chats", chats as Any)])
|
||||
case .inputPrivacyValueDisallowContacts:
|
||||
return ("inputPrivacyValueDisallowContacts", [])
|
||||
case .inputPrivacyValueDisallowUsers(let users):
|
||||
return ("inputPrivacyValueDisallowUsers", [("users", users as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputPrivacyValueAllowAll(_ reader: BufferReader) -> InputPrivacyRule? {
|
||||
return Api.InputPrivacyRule.inputPrivacyValueAllowAll
|
||||
}
|
||||
public static func parse_inputPrivacyValueAllowChatParticipants(_ reader: BufferReader) -> InputPrivacyRule? {
|
||||
var _1: [Int64]?
|
||||
if let _ = reader.readInt32() {
|
||||
_1 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputPrivacyRule.inputPrivacyValueAllowChatParticipants(chats: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputPrivacyValueAllowCloseFriends(_ reader: BufferReader) -> InputPrivacyRule? {
|
||||
return Api.InputPrivacyRule.inputPrivacyValueAllowCloseFriends
|
||||
}
|
||||
public static func parse_inputPrivacyValueAllowContacts(_ reader: BufferReader) -> InputPrivacyRule? {
|
||||
return Api.InputPrivacyRule.inputPrivacyValueAllowContacts
|
||||
}
|
||||
public static func parse_inputPrivacyValueAllowUsers(_ reader: BufferReader) -> InputPrivacyRule? {
|
||||
var _1: [Api.InputUser]?
|
||||
if let _ = reader.readInt32() {
|
||||
_1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.InputUser.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputPrivacyRule.inputPrivacyValueAllowUsers(users: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputPrivacyValueDisallowAll(_ reader: BufferReader) -> InputPrivacyRule? {
|
||||
return Api.InputPrivacyRule.inputPrivacyValueDisallowAll
|
||||
}
|
||||
public static func parse_inputPrivacyValueDisallowChatParticipants(_ reader: BufferReader) -> InputPrivacyRule? {
|
||||
var _1: [Int64]?
|
||||
if let _ = reader.readInt32() {
|
||||
_1 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputPrivacyRule.inputPrivacyValueDisallowChatParticipants(chats: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputPrivacyValueDisallowContacts(_ reader: BufferReader) -> InputPrivacyRule? {
|
||||
return Api.InputPrivacyRule.inputPrivacyValueDisallowContacts
|
||||
}
|
||||
public static func parse_inputPrivacyValueDisallowUsers(_ reader: BufferReader) -> InputPrivacyRule? {
|
||||
var _1: [Api.InputUser]?
|
||||
if let _ = reader.readInt32() {
|
||||
_1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.InputUser.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputPrivacyRule.inputPrivacyValueDisallowUsers(users: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum InputQuickReplyShortcut: TypeConstructorDescription {
|
||||
case inputQuickReplyShortcut(shortcut: String)
|
||||
@ -844,229 +1024,3 @@ public extension Api {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum InputTheme: TypeConstructorDescription {
|
||||
case inputTheme(id: Int64, accessHash: Int64)
|
||||
case inputThemeSlug(slug: String)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputTheme(let id, let accessHash):
|
||||
if boxed {
|
||||
buffer.appendInt32(1012306921)
|
||||
}
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputThemeSlug(let slug):
|
||||
if boxed {
|
||||
buffer.appendInt32(-175567375)
|
||||
}
|
||||
serializeString(slug, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputTheme(let id, let accessHash):
|
||||
return ("inputTheme", [("id", id as Any), ("accessHash", accessHash as Any)])
|
||||
case .inputThemeSlug(let slug):
|
||||
return ("inputThemeSlug", [("slug", slug as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputTheme(_ reader: BufferReader) -> InputTheme? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputTheme.inputTheme(id: _1!, accessHash: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputThemeSlug(_ reader: BufferReader) -> InputTheme? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputTheme.inputThemeSlug(slug: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum InputThemeSettings: TypeConstructorDescription {
|
||||
case inputThemeSettings(flags: Int32, baseTheme: Api.BaseTheme, accentColor: Int32, outboxAccentColor: Int32?, messageColors: [Int32]?, wallpaper: Api.InputWallPaper?, wallpaperSettings: Api.WallPaperSettings?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputThemeSettings(let flags, let baseTheme, let accentColor, let outboxAccentColor, let messageColors, let wallpaper, let wallpaperSettings):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1881255857)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
baseTheme.serialize(buffer, true)
|
||||
serializeInt32(accentColor, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 3) != 0 {serializeInt32(outboxAccentColor!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(messageColors!.count))
|
||||
for item in messageColors! {
|
||||
serializeInt32(item, buffer: buffer, boxed: false)
|
||||
}}
|
||||
if Int(flags) & Int(1 << 1) != 0 {wallpaper!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 1) != 0 {wallpaperSettings!.serialize(buffer, true)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputThemeSettings(let flags, let baseTheme, let accentColor, let outboxAccentColor, let messageColors, let wallpaper, let wallpaperSettings):
|
||||
return ("inputThemeSettings", [("flags", flags as Any), ("baseTheme", baseTheme as Any), ("accentColor", accentColor as Any), ("outboxAccentColor", outboxAccentColor as Any), ("messageColors", messageColors as Any), ("wallpaper", wallpaper as Any), ("wallpaperSettings", wallpaperSettings as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputThemeSettings(_ reader: BufferReader) -> InputThemeSettings? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Api.BaseTheme?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.BaseTheme
|
||||
}
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
var _4: Int32?
|
||||
if Int(_1!) & Int(1 << 3) != 0 {_4 = reader.readInt32() }
|
||||
var _5: [Int32]?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {if let _ = reader.readInt32() {
|
||||
_5 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self)
|
||||
} }
|
||||
var _6: Api.InputWallPaper?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() {
|
||||
_6 = Api.parse(reader, signature: signature) as? Api.InputWallPaper
|
||||
} }
|
||||
var _7: Api.WallPaperSettings?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() {
|
||||
_7 = Api.parse(reader, signature: signature) as? Api.WallPaperSettings
|
||||
} }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 3) == 0) || _4 != nil
|
||||
let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil
|
||||
let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil
|
||||
let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
|
||||
return Api.InputThemeSettings.inputThemeSettings(flags: _1!, baseTheme: _2!, accentColor: _3!, outboxAccentColor: _4, messageColors: _5, wallpaper: _6, wallpaperSettings: _7)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
indirect enum InputUser: TypeConstructorDescription {
|
||||
case inputUser(userId: Int64, accessHash: Int64)
|
||||
case inputUserEmpty
|
||||
case inputUserFromMessage(peer: Api.InputPeer, msgId: Int32, userId: Int64)
|
||||
case inputUserSelf
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputUser(let userId, let accessHash):
|
||||
if boxed {
|
||||
buffer.appendInt32(-233744186)
|
||||
}
|
||||
serializeInt64(userId, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputUserEmpty:
|
||||
if boxed {
|
||||
buffer.appendInt32(-1182234929)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputUserFromMessage(let peer, let msgId, let userId):
|
||||
if boxed {
|
||||
buffer.appendInt32(497305826)
|
||||
}
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(msgId, buffer: buffer, boxed: false)
|
||||
serializeInt64(userId, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputUserSelf:
|
||||
if boxed {
|
||||
buffer.appendInt32(-138301121)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputUser(let userId, let accessHash):
|
||||
return ("inputUser", [("userId", userId as Any), ("accessHash", accessHash as Any)])
|
||||
case .inputUserEmpty:
|
||||
return ("inputUserEmpty", [])
|
||||
case .inputUserFromMessage(let peer, let msgId, let userId):
|
||||
return ("inputUserFromMessage", [("peer", peer as Any), ("msgId", msgId as Any), ("userId", userId as Any)])
|
||||
case .inputUserSelf:
|
||||
return ("inputUserSelf", [])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputUser(_ reader: BufferReader) -> InputUser? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputUser.inputUser(userId: _1!, accessHash: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputUserEmpty(_ reader: BufferReader) -> InputUser? {
|
||||
return Api.InputUser.inputUserEmpty
|
||||
}
|
||||
public static func parse_inputUserFromMessage(_ reader: BufferReader) -> InputUser? {
|
||||
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: Int64?
|
||||
_3 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.InputUser.inputUserFromMessage(peer: _1!, msgId: _2!, userId: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputUserSelf(_ reader: BufferReader) -> InputUser? {
|
||||
return Api.InputUser.inputUserSelf
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,229 @@
|
||||
public extension Api {
|
||||
enum InputTheme: TypeConstructorDescription {
|
||||
case inputTheme(id: Int64, accessHash: Int64)
|
||||
case inputThemeSlug(slug: String)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputTheme(let id, let accessHash):
|
||||
if boxed {
|
||||
buffer.appendInt32(1012306921)
|
||||
}
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputThemeSlug(let slug):
|
||||
if boxed {
|
||||
buffer.appendInt32(-175567375)
|
||||
}
|
||||
serializeString(slug, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputTheme(let id, let accessHash):
|
||||
return ("inputTheme", [("id", id as Any), ("accessHash", accessHash as Any)])
|
||||
case .inputThemeSlug(let slug):
|
||||
return ("inputThemeSlug", [("slug", slug as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputTheme(_ reader: BufferReader) -> InputTheme? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputTheme.inputTheme(id: _1!, accessHash: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputThemeSlug(_ reader: BufferReader) -> InputTheme? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputTheme.inputThemeSlug(slug: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum InputThemeSettings: TypeConstructorDescription {
|
||||
case inputThemeSettings(flags: Int32, baseTheme: Api.BaseTheme, accentColor: Int32, outboxAccentColor: Int32?, messageColors: [Int32]?, wallpaper: Api.InputWallPaper?, wallpaperSettings: Api.WallPaperSettings?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputThemeSettings(let flags, let baseTheme, let accentColor, let outboxAccentColor, let messageColors, let wallpaper, let wallpaperSettings):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1881255857)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
baseTheme.serialize(buffer, true)
|
||||
serializeInt32(accentColor, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 3) != 0 {serializeInt32(outboxAccentColor!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(messageColors!.count))
|
||||
for item in messageColors! {
|
||||
serializeInt32(item, buffer: buffer, boxed: false)
|
||||
}}
|
||||
if Int(flags) & Int(1 << 1) != 0 {wallpaper!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 1) != 0 {wallpaperSettings!.serialize(buffer, true)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputThemeSettings(let flags, let baseTheme, let accentColor, let outboxAccentColor, let messageColors, let wallpaper, let wallpaperSettings):
|
||||
return ("inputThemeSettings", [("flags", flags as Any), ("baseTheme", baseTheme as Any), ("accentColor", accentColor as Any), ("outboxAccentColor", outboxAccentColor as Any), ("messageColors", messageColors as Any), ("wallpaper", wallpaper as Any), ("wallpaperSettings", wallpaperSettings as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputThemeSettings(_ reader: BufferReader) -> InputThemeSettings? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Api.BaseTheme?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.BaseTheme
|
||||
}
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
var _4: Int32?
|
||||
if Int(_1!) & Int(1 << 3) != 0 {_4 = reader.readInt32() }
|
||||
var _5: [Int32]?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {if let _ = reader.readInt32() {
|
||||
_5 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self)
|
||||
} }
|
||||
var _6: Api.InputWallPaper?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() {
|
||||
_6 = Api.parse(reader, signature: signature) as? Api.InputWallPaper
|
||||
} }
|
||||
var _7: Api.WallPaperSettings?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() {
|
||||
_7 = Api.parse(reader, signature: signature) as? Api.WallPaperSettings
|
||||
} }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 3) == 0) || _4 != nil
|
||||
let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil
|
||||
let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil
|
||||
let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
|
||||
return Api.InputThemeSettings.inputThemeSettings(flags: _1!, baseTheme: _2!, accentColor: _3!, outboxAccentColor: _4, messageColors: _5, wallpaper: _6, wallpaperSettings: _7)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
indirect enum InputUser: TypeConstructorDescription {
|
||||
case inputUser(userId: Int64, accessHash: Int64)
|
||||
case inputUserEmpty
|
||||
case inputUserFromMessage(peer: Api.InputPeer, msgId: Int32, userId: Int64)
|
||||
case inputUserSelf
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputUser(let userId, let accessHash):
|
||||
if boxed {
|
||||
buffer.appendInt32(-233744186)
|
||||
}
|
||||
serializeInt64(userId, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputUserEmpty:
|
||||
if boxed {
|
||||
buffer.appendInt32(-1182234929)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputUserFromMessage(let peer, let msgId, let userId):
|
||||
if boxed {
|
||||
buffer.appendInt32(497305826)
|
||||
}
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(msgId, buffer: buffer, boxed: false)
|
||||
serializeInt64(userId, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputUserSelf:
|
||||
if boxed {
|
||||
buffer.appendInt32(-138301121)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputUser(let userId, let accessHash):
|
||||
return ("inputUser", [("userId", userId as Any), ("accessHash", accessHash as Any)])
|
||||
case .inputUserEmpty:
|
||||
return ("inputUserEmpty", [])
|
||||
case .inputUserFromMessage(let peer, let msgId, let userId):
|
||||
return ("inputUserFromMessage", [("peer", peer as Any), ("msgId", msgId as Any), ("userId", userId as Any)])
|
||||
case .inputUserSelf:
|
||||
return ("inputUserSelf", [])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputUser(_ reader: BufferReader) -> InputUser? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputUser.inputUser(userId: _1!, accessHash: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputUserEmpty(_ reader: BufferReader) -> InputUser? {
|
||||
return Api.InputUser.inputUserEmpty
|
||||
}
|
||||
public static func parse_inputUserFromMessage(_ reader: BufferReader) -> InputUser? {
|
||||
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: Int64?
|
||||
_3 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.InputUser.inputUserFromMessage(peer: _1!, msgId: _2!, userId: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputUserSelf(_ reader: BufferReader) -> InputUser? {
|
||||
return Api.InputUser.inputUserSelf
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum InputWallPaper: TypeConstructorDescription {
|
||||
case inputWallPaper(id: Int64, accessHash: Int64)
|
||||
@ -942,45 +1168,3 @@ public extension Api {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum KeyboardButtonRow: TypeConstructorDescription {
|
||||
case keyboardButtonRow(buttons: [Api.KeyboardButton])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .keyboardButtonRow(let buttons):
|
||||
if boxed {
|
||||
buffer.appendInt32(2002815875)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(buttons.count))
|
||||
for item in buttons {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .keyboardButtonRow(let buttons):
|
||||
return ("keyboardButtonRow", [("buttons", buttons as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_keyboardButtonRow(_ reader: BufferReader) -> KeyboardButtonRow? {
|
||||
var _1: [Api.KeyboardButton]?
|
||||
if let _ = reader.readInt32() {
|
||||
_1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.KeyboardButton.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.KeyboardButtonRow.keyboardButtonRow(buttons: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,45 @@
|
||||
public extension Api {
|
||||
enum KeyboardButtonRow: TypeConstructorDescription {
|
||||
case keyboardButtonRow(buttons: [Api.KeyboardButton])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .keyboardButtonRow(let buttons):
|
||||
if boxed {
|
||||
buffer.appendInt32(2002815875)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(buttons.count))
|
||||
for item in buttons {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .keyboardButtonRow(let buttons):
|
||||
return ("keyboardButtonRow", [("buttons", buttons as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_keyboardButtonRow(_ reader: BufferReader) -> KeyboardButtonRow? {
|
||||
var _1: [Api.KeyboardButton]?
|
||||
if let _ = reader.readInt32() {
|
||||
_1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.KeyboardButton.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.KeyboardButtonRow.keyboardButtonRow(buttons: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum LabeledPrice: TypeConstructorDescription {
|
||||
case labeledPrice(label: String, amount: Int64)
|
||||
@ -586,17 +628,18 @@ public extension Api {
|
||||
}
|
||||
public extension Api {
|
||||
indirect enum Message: TypeConstructorDescription {
|
||||
case message(flags: Int32, id: Int32, fromId: Api.Peer?, fromBoostsApplied: Int32?, peerId: Api.Peer, savedPeerId: Api.Peer?, fwdFrom: Api.MessageFwdHeader?, viaBotId: Int64?, replyTo: Api.MessageReplyHeader?, date: Int32, message: String, media: Api.MessageMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, views: Int32?, forwards: Int32?, replies: Api.MessageReplies?, editDate: Int32?, postAuthor: String?, groupedId: Int64?, reactions: Api.MessageReactions?, restrictionReason: [Api.RestrictionReason]?, ttlPeriod: Int32?, quickReplyShortcutId: Int32?)
|
||||
case message(flags: Int32, flags2: Int32, id: Int32, fromId: Api.Peer?, fromBoostsApplied: Int32?, peerId: Api.Peer, savedPeerId: Api.Peer?, fwdFrom: Api.MessageFwdHeader?, viaBotId: Int64?, viaBusinessBotId: Int64?, replyTo: Api.MessageReplyHeader?, date: Int32, message: String, media: Api.MessageMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, views: Int32?, forwards: Int32?, replies: Api.MessageReplies?, editDate: Int32?, postAuthor: String?, groupedId: Int64?, reactions: Api.MessageReactions?, restrictionReason: [Api.RestrictionReason]?, ttlPeriod: Int32?, quickReplyShortcutId: Int32?)
|
||||
case messageEmpty(flags: Int32, id: Int32, peerId: Api.Peer?)
|
||||
case messageService(flags: Int32, id: Int32, fromId: Api.Peer?, peerId: Api.Peer, replyTo: Api.MessageReplyHeader?, date: Int32, action: Api.MessageAction, ttlPeriod: Int32?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .message(let flags, let id, let fromId, let fromBoostsApplied, let peerId, let savedPeerId, let fwdFrom, let viaBotId, let replyTo, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let replies, let editDate, let postAuthor, let groupedId, let reactions, let restrictionReason, let ttlPeriod, let quickReplyShortcutId):
|
||||
case .message(let flags, let flags2, let id, let fromId, let fromBoostsApplied, let peerId, let savedPeerId, let fwdFrom, let viaBotId, let viaBusinessBotId, let replyTo, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let replies, let editDate, let postAuthor, let groupedId, let reactions, let restrictionReason, let ttlPeriod, let quickReplyShortcutId):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1502839044)
|
||||
buffer.appendInt32(592953125)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(flags2, buffer: buffer, boxed: false)
|
||||
serializeInt32(id, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 8) != 0 {fromId!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 29) != 0 {serializeInt32(fromBoostsApplied!, buffer: buffer, boxed: false)}
|
||||
@ -604,6 +647,7 @@ public extension Api {
|
||||
if Int(flags) & Int(1 << 28) != 0 {savedPeerId!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 2) != 0 {fwdFrom!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 11) != 0 {serializeInt64(viaBotId!, buffer: buffer, boxed: false)}
|
||||
if Int(flags2) & Int(1 << 0) != 0 {serializeInt64(viaBusinessBotId!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 3) != 0 {replyTo!.serialize(buffer, true)}
|
||||
serializeInt32(date, buffer: buffer, boxed: false)
|
||||
serializeString(message, buffer: buffer, boxed: false)
|
||||
@ -655,8 +699,8 @@ public extension Api {
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .message(let flags, let id, let fromId, let fromBoostsApplied, let peerId, let savedPeerId, let fwdFrom, let viaBotId, let replyTo, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let replies, let editDate, let postAuthor, let groupedId, let reactions, let restrictionReason, let ttlPeriod, let quickReplyShortcutId):
|
||||
return ("message", [("flags", flags as Any), ("id", id as Any), ("fromId", fromId as Any), ("fromBoostsApplied", fromBoostsApplied as Any), ("peerId", peerId as Any), ("savedPeerId", savedPeerId as Any), ("fwdFrom", fwdFrom as Any), ("viaBotId", viaBotId as Any), ("replyTo", replyTo as Any), ("date", date as Any), ("message", message as Any), ("media", media as Any), ("replyMarkup", replyMarkup as Any), ("entities", entities as Any), ("views", views as Any), ("forwards", forwards as Any), ("replies", replies as Any), ("editDate", editDate as Any), ("postAuthor", postAuthor as Any), ("groupedId", groupedId as Any), ("reactions", reactions as Any), ("restrictionReason", restrictionReason as Any), ("ttlPeriod", ttlPeriod as Any), ("quickReplyShortcutId", quickReplyShortcutId as Any)])
|
||||
case .message(let flags, let flags2, let id, let fromId, let fromBoostsApplied, let peerId, let savedPeerId, let fwdFrom, let viaBotId, let viaBusinessBotId, let replyTo, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let replies, let editDate, let postAuthor, let groupedId, let reactions, let restrictionReason, let ttlPeriod, let quickReplyShortcutId):
|
||||
return ("message", [("flags", flags as Any), ("flags2", flags2 as Any), ("id", id as Any), ("fromId", fromId as Any), ("fromBoostsApplied", fromBoostsApplied as Any), ("peerId", peerId as Any), ("savedPeerId", savedPeerId as Any), ("fwdFrom", fwdFrom as Any), ("viaBotId", viaBotId as Any), ("viaBusinessBotId", viaBusinessBotId as Any), ("replyTo", replyTo as Any), ("date", date as Any), ("message", message as Any), ("media", media as Any), ("replyMarkup", replyMarkup as Any), ("entities", entities as Any), ("views", views as Any), ("forwards", forwards as Any), ("replies", replies as Any), ("editDate", editDate as Any), ("postAuthor", postAuthor as Any), ("groupedId", groupedId as Any), ("reactions", reactions as Any), ("restrictionReason", restrictionReason as Any), ("ttlPeriod", ttlPeriod as Any), ("quickReplyShortcutId", quickReplyShortcutId as Any)])
|
||||
case .messageEmpty(let flags, let id, let peerId):
|
||||
return ("messageEmpty", [("flags", flags as Any), ("id", id as Any), ("peerId", peerId as Any)])
|
||||
case .messageService(let flags, let id, let fromId, let peerId, let replyTo, let date, let action, let ttlPeriod):
|
||||
@ -669,98 +713,104 @@ public extension Api {
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Api.Peer?
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
var _4: Api.Peer?
|
||||
if Int(_1!) & Int(1 << 8) != 0 {if let signature = reader.readInt32() {
|
||||
_3 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
_4 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
} }
|
||||
var _4: Int32?
|
||||
if Int(_1!) & Int(1 << 29) != 0 {_4 = reader.readInt32() }
|
||||
var _5: Api.Peer?
|
||||
if let signature = reader.readInt32() {
|
||||
_5 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
}
|
||||
var _5: Int32?
|
||||
if Int(_1!) & Int(1 << 29) != 0 {_5 = reader.readInt32() }
|
||||
var _6: Api.Peer?
|
||||
if Int(_1!) & Int(1 << 28) != 0 {if let signature = reader.readInt32() {
|
||||
if let signature = reader.readInt32() {
|
||||
_6 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
}
|
||||
var _7: Api.Peer?
|
||||
if Int(_1!) & Int(1 << 28) != 0 {if let signature = reader.readInt32() {
|
||||
_7 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
} }
|
||||
var _7: Api.MessageFwdHeader?
|
||||
var _8: Api.MessageFwdHeader?
|
||||
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
|
||||
_7 = Api.parse(reader, signature: signature) as? Api.MessageFwdHeader
|
||||
_8 = Api.parse(reader, signature: signature) as? Api.MessageFwdHeader
|
||||
} }
|
||||
var _8: Int64?
|
||||
if Int(_1!) & Int(1 << 11) != 0 {_8 = reader.readInt64() }
|
||||
var _9: Api.MessageReplyHeader?
|
||||
var _9: Int64?
|
||||
if Int(_1!) & Int(1 << 11) != 0 {_9 = reader.readInt64() }
|
||||
var _10: Int64?
|
||||
if Int(_2!) & Int(1 << 0) != 0 {_10 = reader.readInt64() }
|
||||
var _11: Api.MessageReplyHeader?
|
||||
if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() {
|
||||
_9 = Api.parse(reader, signature: signature) as? Api.MessageReplyHeader
|
||||
_11 = Api.parse(reader, signature: signature) as? Api.MessageReplyHeader
|
||||
} }
|
||||
var _10: Int32?
|
||||
_10 = reader.readInt32()
|
||||
var _11: String?
|
||||
_11 = parseString(reader)
|
||||
var _12: Api.MessageMedia?
|
||||
var _12: Int32?
|
||||
_12 = reader.readInt32()
|
||||
var _13: String?
|
||||
_13 = parseString(reader)
|
||||
var _14: Api.MessageMedia?
|
||||
if Int(_1!) & Int(1 << 9) != 0 {if let signature = reader.readInt32() {
|
||||
_12 = Api.parse(reader, signature: signature) as? Api.MessageMedia
|
||||
_14 = Api.parse(reader, signature: signature) as? Api.MessageMedia
|
||||
} }
|
||||
var _13: Api.ReplyMarkup?
|
||||
var _15: Api.ReplyMarkup?
|
||||
if Int(_1!) & Int(1 << 6) != 0 {if let signature = reader.readInt32() {
|
||||
_13 = Api.parse(reader, signature: signature) as? Api.ReplyMarkup
|
||||
_15 = Api.parse(reader, signature: signature) as? Api.ReplyMarkup
|
||||
} }
|
||||
var _14: [Api.MessageEntity]?
|
||||
var _16: [Api.MessageEntity]?
|
||||
if Int(_1!) & Int(1 << 7) != 0 {if let _ = reader.readInt32() {
|
||||
_14 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self)
|
||||
} }
|
||||
var _15: Int32?
|
||||
if Int(_1!) & Int(1 << 10) != 0 {_15 = reader.readInt32() }
|
||||
var _16: Int32?
|
||||
if Int(_1!) & Int(1 << 10) != 0 {_16 = reader.readInt32() }
|
||||
var _17: Api.MessageReplies?
|
||||
if Int(_1!) & Int(1 << 23) != 0 {if let signature = reader.readInt32() {
|
||||
_17 = Api.parse(reader, signature: signature) as? Api.MessageReplies
|
||||
_16 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self)
|
||||
} }
|
||||
var _17: Int32?
|
||||
if Int(_1!) & Int(1 << 10) != 0 {_17 = reader.readInt32() }
|
||||
var _18: Int32?
|
||||
if Int(_1!) & Int(1 << 15) != 0 {_18 = reader.readInt32() }
|
||||
var _19: String?
|
||||
if Int(_1!) & Int(1 << 16) != 0 {_19 = parseString(reader) }
|
||||
var _20: Int64?
|
||||
if Int(_1!) & Int(1 << 17) != 0 {_20 = reader.readInt64() }
|
||||
var _21: Api.MessageReactions?
|
||||
if Int(_1!) & Int(1 << 10) != 0 {_18 = reader.readInt32() }
|
||||
var _19: Api.MessageReplies?
|
||||
if Int(_1!) & Int(1 << 23) != 0 {if let signature = reader.readInt32() {
|
||||
_19 = Api.parse(reader, signature: signature) as? Api.MessageReplies
|
||||
} }
|
||||
var _20: Int32?
|
||||
if Int(_1!) & Int(1 << 15) != 0 {_20 = reader.readInt32() }
|
||||
var _21: String?
|
||||
if Int(_1!) & Int(1 << 16) != 0 {_21 = parseString(reader) }
|
||||
var _22: Int64?
|
||||
if Int(_1!) & Int(1 << 17) != 0 {_22 = reader.readInt64() }
|
||||
var _23: Api.MessageReactions?
|
||||
if Int(_1!) & Int(1 << 20) != 0 {if let signature = reader.readInt32() {
|
||||
_21 = Api.parse(reader, signature: signature) as? Api.MessageReactions
|
||||
_23 = Api.parse(reader, signature: signature) as? Api.MessageReactions
|
||||
} }
|
||||
var _22: [Api.RestrictionReason]?
|
||||
var _24: [Api.RestrictionReason]?
|
||||
if Int(_1!) & Int(1 << 22) != 0 {if let _ = reader.readInt32() {
|
||||
_22 = Api.parseVector(reader, elementSignature: 0, elementType: Api.RestrictionReason.self)
|
||||
_24 = Api.parseVector(reader, elementSignature: 0, elementType: Api.RestrictionReason.self)
|
||||
} }
|
||||
var _23: Int32?
|
||||
if Int(_1!) & Int(1 << 25) != 0 {_23 = reader.readInt32() }
|
||||
var _24: Int32?
|
||||
if Int(_1!) & Int(1 << 30) != 0 {_24 = reader.readInt32() }
|
||||
var _25: Int32?
|
||||
if Int(_1!) & Int(1 << 25) != 0 {_25 = reader.readInt32() }
|
||||
var _26: Int32?
|
||||
if Int(_1!) & Int(1 << 30) != 0 {_26 = reader.readInt32() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 8) == 0) || _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 29) == 0) || _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = (Int(_1!) & Int(1 << 28) == 0) || _6 != nil
|
||||
let _c7 = (Int(_1!) & Int(1 << 2) == 0) || _7 != nil
|
||||
let _c8 = (Int(_1!) & Int(1 << 11) == 0) || _8 != nil
|
||||
let _c9 = (Int(_1!) & Int(1 << 3) == 0) || _9 != nil
|
||||
let _c10 = _10 != nil
|
||||
let _c11 = _11 != nil
|
||||
let _c12 = (Int(_1!) & Int(1 << 9) == 0) || _12 != nil
|
||||
let _c13 = (Int(_1!) & Int(1 << 6) == 0) || _13 != nil
|
||||
let _c14 = (Int(_1!) & Int(1 << 7) == 0) || _14 != nil
|
||||
let _c15 = (Int(_1!) & Int(1 << 10) == 0) || _15 != nil
|
||||
let _c16 = (Int(_1!) & Int(1 << 10) == 0) || _16 != nil
|
||||
let _c17 = (Int(_1!) & Int(1 << 23) == 0) || _17 != nil
|
||||
let _c18 = (Int(_1!) & Int(1 << 15) == 0) || _18 != nil
|
||||
let _c19 = (Int(_1!) & Int(1 << 16) == 0) || _19 != nil
|
||||
let _c20 = (Int(_1!) & Int(1 << 17) == 0) || _20 != nil
|
||||
let _c21 = (Int(_1!) & Int(1 << 20) == 0) || _21 != nil
|
||||
let _c22 = (Int(_1!) & Int(1 << 22) == 0) || _22 != nil
|
||||
let _c23 = (Int(_1!) & Int(1 << 25) == 0) || _23 != nil
|
||||
let _c24 = (Int(_1!) & Int(1 << 30) == 0) || _24 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 {
|
||||
return Api.Message.message(flags: _1!, id: _2!, fromId: _3, fromBoostsApplied: _4, peerId: _5!, savedPeerId: _6, fwdFrom: _7, viaBotId: _8, replyTo: _9, date: _10!, message: _11!, media: _12, replyMarkup: _13, entities: _14, views: _15, forwards: _16, replies: _17, editDate: _18, postAuthor: _19, groupedId: _20, reactions: _21, restrictionReason: _22, ttlPeriod: _23, quickReplyShortcutId: _24)
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 8) == 0) || _4 != nil
|
||||
let _c5 = (Int(_1!) & Int(1 << 29) == 0) || _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
let _c7 = (Int(_1!) & Int(1 << 28) == 0) || _7 != nil
|
||||
let _c8 = (Int(_1!) & Int(1 << 2) == 0) || _8 != nil
|
||||
let _c9 = (Int(_1!) & Int(1 << 11) == 0) || _9 != nil
|
||||
let _c10 = (Int(_2!) & Int(1 << 0) == 0) || _10 != nil
|
||||
let _c11 = (Int(_1!) & Int(1 << 3) == 0) || _11 != nil
|
||||
let _c12 = _12 != nil
|
||||
let _c13 = _13 != nil
|
||||
let _c14 = (Int(_1!) & Int(1 << 9) == 0) || _14 != nil
|
||||
let _c15 = (Int(_1!) & Int(1 << 6) == 0) || _15 != nil
|
||||
let _c16 = (Int(_1!) & Int(1 << 7) == 0) || _16 != nil
|
||||
let _c17 = (Int(_1!) & Int(1 << 10) == 0) || _17 != nil
|
||||
let _c18 = (Int(_1!) & Int(1 << 10) == 0) || _18 != nil
|
||||
let _c19 = (Int(_1!) & Int(1 << 23) == 0) || _19 != nil
|
||||
let _c20 = (Int(_1!) & Int(1 << 15) == 0) || _20 != nil
|
||||
let _c21 = (Int(_1!) & Int(1 << 16) == 0) || _21 != nil
|
||||
let _c22 = (Int(_1!) & Int(1 << 17) == 0) || _22 != nil
|
||||
let _c23 = (Int(_1!) & Int(1 << 20) == 0) || _23 != nil
|
||||
let _c24 = (Int(_1!) & Int(1 << 22) == 0) || _24 != nil
|
||||
let _c25 = (Int(_1!) & Int(1 << 25) == 0) || _25 != nil
|
||||
let _c26 = (Int(_1!) & Int(1 << 30) == 0) || _26 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 && _c25 && _c26 {
|
||||
return Api.Message.message(flags: _1!, flags2: _2!, id: _3!, fromId: _4, fromBoostsApplied: _5, peerId: _6!, savedPeerId: _7, fwdFrom: _8, viaBotId: _9, viaBusinessBotId: _10, replyTo: _11, date: _12!, message: _13!, media: _14, replyMarkup: _15, entities: _16, views: _17, forwards: _18, replies: _19, editDate: _20, postAuthor: _21, groupedId: _22, reactions: _23, restrictionReason: _24, ttlPeriod: _25, quickReplyShortcutId: _26)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
@ -898,26 +898,28 @@ public extension Api {
|
||||
}
|
||||
public extension Api {
|
||||
enum PeerSettings: TypeConstructorDescription {
|
||||
case peerSettings(flags: Int32, geoDistance: Int32?, requestChatTitle: String?, requestChatDate: Int32?)
|
||||
case peerSettings(flags: Int32, geoDistance: Int32?, requestChatTitle: String?, requestChatDate: Int32?, businessBotId: Int64?, businessBotManageUrl: String?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .peerSettings(let flags, let geoDistance, let requestChatTitle, let requestChatDate):
|
||||
case .peerSettings(let flags, let geoDistance, let requestChatTitle, let requestChatDate, let businessBotId, let businessBotManageUrl):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1525149427)
|
||||
buffer.appendInt32(-1395233698)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 6) != 0 {serializeInt32(geoDistance!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 9) != 0 {serializeString(requestChatTitle!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 9) != 0 {serializeInt32(requestChatDate!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 13) != 0 {serializeInt64(businessBotId!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 13) != 0 {serializeString(businessBotManageUrl!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .peerSettings(let flags, let geoDistance, let requestChatTitle, let requestChatDate):
|
||||
return ("peerSettings", [("flags", flags as Any), ("geoDistance", geoDistance as Any), ("requestChatTitle", requestChatTitle as Any), ("requestChatDate", requestChatDate as Any)])
|
||||
case .peerSettings(let flags, let geoDistance, let requestChatTitle, let requestChatDate, let businessBotId, let businessBotManageUrl):
|
||||
return ("peerSettings", [("flags", flags as Any), ("geoDistance", geoDistance as Any), ("requestChatTitle", requestChatTitle as Any), ("requestChatDate", requestChatDate as Any), ("businessBotId", businessBotId as Any), ("businessBotManageUrl", businessBotManageUrl as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
@ -930,12 +932,18 @@ public extension Api {
|
||||
if Int(_1!) & Int(1 << 9) != 0 {_3 = parseString(reader) }
|
||||
var _4: Int32?
|
||||
if Int(_1!) & Int(1 << 9) != 0 {_4 = reader.readInt32() }
|
||||
var _5: Int64?
|
||||
if Int(_1!) & Int(1 << 13) != 0 {_5 = reader.readInt64() }
|
||||
var _6: String?
|
||||
if Int(_1!) & Int(1 << 13) != 0 {_6 = parseString(reader) }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = (Int(_1!) & Int(1 << 6) == 0) || _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 9) == 0) || _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 9) == 0) || _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.PeerSettings.peerSettings(flags: _1!, geoDistance: _2, requestChatTitle: _3, requestChatDate: _4)
|
||||
let _c5 = (Int(_1!) & Int(1 << 13) == 0) || _5 != nil
|
||||
let _c6 = (Int(_1!) & Int(1 << 13) == 0) || _6 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
||||
return Api.PeerSettings.peerSettings(flags: _1!, geoDistance: _2, requestChatTitle: _3, requestChatDate: _4, businessBotId: _5, businessBotManageUrl: _6)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
@ -513,7 +513,7 @@ public extension Api {
|
||||
case updateBotChatBoost(peer: Api.Peer, boost: Api.Boost, qts: Int32)
|
||||
case updateBotChatInviteRequester(peer: Api.Peer, date: Int32, userId: Int64, about: String, invite: Api.ExportedChatInvite, qts: Int32)
|
||||
case updateBotCommands(peer: Api.Peer, botId: Int64, commands: [Api.BotCommand])
|
||||
case updateBotDeleteBusinessMessage(connectionId: String, messages: [Int32], qts: Int32)
|
||||
case updateBotDeleteBusinessMessage(connectionId: String, peer: Api.Peer, messages: [Int32], qts: Int32)
|
||||
case updateBotEditBusinessMessage(connectionId: String, message: Api.Message, qts: Int32)
|
||||
case updateBotInlineQuery(flags: Int32, queryId: Int64, userId: Int64, query: String, geo: Api.GeoPoint?, peerType: Api.InlineQueryPeerType?, offset: String)
|
||||
case updateBotInlineSend(flags: Int32, userId: Int64, query: String, geo: Api.GeoPoint?, id: String, msgId: Api.InputBotInlineMessageID?)
|
||||
@ -707,11 +707,12 @@ public extension Api {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
case .updateBotDeleteBusinessMessage(let connectionId, let messages, let qts):
|
||||
case .updateBotDeleteBusinessMessage(let connectionId, let peer, let messages, let qts):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1590796039)
|
||||
buffer.appendInt32(-1607821266)
|
||||
}
|
||||
serializeString(connectionId, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(messages.count))
|
||||
for item in messages {
|
||||
@ -1827,8 +1828,8 @@ public extension Api {
|
||||
return ("updateBotChatInviteRequester", [("peer", peer as Any), ("date", date as Any), ("userId", userId as Any), ("about", about as Any), ("invite", invite as Any), ("qts", qts as Any)])
|
||||
case .updateBotCommands(let peer, let botId, let commands):
|
||||
return ("updateBotCommands", [("peer", peer as Any), ("botId", botId as Any), ("commands", commands as Any)])
|
||||
case .updateBotDeleteBusinessMessage(let connectionId, let messages, let qts):
|
||||
return ("updateBotDeleteBusinessMessage", [("connectionId", connectionId as Any), ("messages", messages as Any), ("qts", qts as Any)])
|
||||
case .updateBotDeleteBusinessMessage(let connectionId, let peer, let messages, let qts):
|
||||
return ("updateBotDeleteBusinessMessage", [("connectionId", connectionId as Any), ("peer", peer as Any), ("messages", messages as Any), ("qts", qts as Any)])
|
||||
case .updateBotEditBusinessMessage(let connectionId, let message, let qts):
|
||||
return ("updateBotEditBusinessMessage", [("connectionId", connectionId as Any), ("message", message as Any), ("qts", qts as Any)])
|
||||
case .updateBotInlineQuery(let flags, let queryId, let userId, let query, let geo, let peerType, let offset):
|
||||
@ -2217,17 +2218,22 @@ public extension Api {
|
||||
public static func parse_updateBotDeleteBusinessMessage(_ reader: BufferReader) -> Update? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
var _2: [Int32]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self)
|
||||
var _2: Api.Peer?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
}
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
var _3: [Int32]?
|
||||
if let _ = reader.readInt32() {
|
||||
_3 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self)
|
||||
}
|
||||
var _4: Int32?
|
||||
_4 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.Update.updateBotDeleteBusinessMessage(connectionId: _1!, messages: _2!, qts: _3!)
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.Update.updateBotDeleteBusinessMessage(connectionId: _1!, peer: _2!, messages: _3!, qts: _4!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
@ -634,6 +634,62 @@ public extension Api.contacts {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.fragment {
|
||||
enum CollectibleInfo: TypeConstructorDescription {
|
||||
case collectibleInfo(purchaseDate: Int32, currency: String, amount: Int64, cryptoCurrency: String, cryptoAmount: Int64, url: String)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .collectibleInfo(let purchaseDate, let currency, let amount, let cryptoCurrency, let cryptoAmount, let url):
|
||||
if boxed {
|
||||
buffer.appendInt32(1857945489)
|
||||
}
|
||||
serializeInt32(purchaseDate, buffer: buffer, boxed: false)
|
||||
serializeString(currency, buffer: buffer, boxed: false)
|
||||
serializeInt64(amount, buffer: buffer, boxed: false)
|
||||
serializeString(cryptoCurrency, buffer: buffer, boxed: false)
|
||||
serializeInt64(cryptoAmount, buffer: buffer, boxed: false)
|
||||
serializeString(url, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .collectibleInfo(let purchaseDate, let currency, let amount, let cryptoCurrency, let cryptoAmount, let url):
|
||||
return ("collectibleInfo", [("purchaseDate", purchaseDate as Any), ("currency", currency as Any), ("amount", amount as Any), ("cryptoCurrency", cryptoCurrency as Any), ("cryptoAmount", cryptoAmount as Any), ("url", url as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_collectibleInfo(_ reader: BufferReader) -> CollectibleInfo? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: String?
|
||||
_2 = parseString(reader)
|
||||
var _3: Int64?
|
||||
_3 = reader.readInt64()
|
||||
var _4: String?
|
||||
_4 = parseString(reader)
|
||||
var _5: Int64?
|
||||
_5 = reader.readInt64()
|
||||
var _6: String?
|
||||
_6 = parseString(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
||||
return Api.fragment.CollectibleInfo.collectibleInfo(purchaseDate: _1!, currency: _2!, amount: _3!, cryptoCurrency: _4!, cryptoAmount: _5!, url: _6!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.help {
|
||||
enum AppConfig: TypeConstructorDescription {
|
||||
case appConfig(hash: Int32, config: Api.JSONValue)
|
||||
@ -1308,89 +1364,3 @@ public extension Api.help {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.help {
|
||||
enum PremiumPromo: TypeConstructorDescription {
|
||||
case premiumPromo(statusText: String, statusEntities: [Api.MessageEntity], videoSections: [String], videos: [Api.Document], periodOptions: [Api.PremiumSubscriptionOption], users: [Api.User])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .premiumPromo(let statusText, let statusEntities, let videoSections, let videos, let periodOptions, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(1395946908)
|
||||
}
|
||||
serializeString(statusText, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(statusEntities.count))
|
||||
for item in statusEntities {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(videoSections.count))
|
||||
for item in videoSections {
|
||||
serializeString(item, buffer: buffer, boxed: false)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(videos.count))
|
||||
for item in videos {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(periodOptions.count))
|
||||
for item in periodOptions {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .premiumPromo(let statusText, let statusEntities, let videoSections, let videos, let periodOptions, let users):
|
||||
return ("premiumPromo", [("statusText", statusText as Any), ("statusEntities", statusEntities as Any), ("videoSections", videoSections as Any), ("videos", videos as Any), ("periodOptions", periodOptions as Any), ("users", users as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_premiumPromo(_ reader: BufferReader) -> PremiumPromo? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
var _2: [Api.MessageEntity]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self)
|
||||
}
|
||||
var _3: [String]?
|
||||
if let _ = reader.readInt32() {
|
||||
_3 = Api.parseVector(reader, elementSignature: -1255641564, elementType: String.self)
|
||||
}
|
||||
var _4: [Api.Document]?
|
||||
if let _ = reader.readInt32() {
|
||||
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self)
|
||||
}
|
||||
var _5: [Api.PremiumSubscriptionOption]?
|
||||
if let _ = reader.readInt32() {
|
||||
_5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PremiumSubscriptionOption.self)
|
||||
}
|
||||
var _6: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
||||
return Api.help.PremiumPromo.premiumPromo(statusText: _1!, statusEntities: _2!, videoSections: _3!, videos: _4!, periodOptions: _5!, users: _6!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,89 @@
|
||||
public extension Api.help {
|
||||
enum PremiumPromo: TypeConstructorDescription {
|
||||
case premiumPromo(statusText: String, statusEntities: [Api.MessageEntity], videoSections: [String], videos: [Api.Document], periodOptions: [Api.PremiumSubscriptionOption], users: [Api.User])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .premiumPromo(let statusText, let statusEntities, let videoSections, let videos, let periodOptions, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(1395946908)
|
||||
}
|
||||
serializeString(statusText, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(statusEntities.count))
|
||||
for item in statusEntities {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(videoSections.count))
|
||||
for item in videoSections {
|
||||
serializeString(item, buffer: buffer, boxed: false)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(videos.count))
|
||||
for item in videos {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(periodOptions.count))
|
||||
for item in periodOptions {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .premiumPromo(let statusText, let statusEntities, let videoSections, let videos, let periodOptions, let users):
|
||||
return ("premiumPromo", [("statusText", statusText as Any), ("statusEntities", statusEntities as Any), ("videoSections", videoSections as Any), ("videos", videos as Any), ("periodOptions", periodOptions as Any), ("users", users as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_premiumPromo(_ reader: BufferReader) -> PremiumPromo? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
var _2: [Api.MessageEntity]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self)
|
||||
}
|
||||
var _3: [String]?
|
||||
if let _ = reader.readInt32() {
|
||||
_3 = Api.parseVector(reader, elementSignature: -1255641564, elementType: String.self)
|
||||
}
|
||||
var _4: [Api.Document]?
|
||||
if let _ = reader.readInt32() {
|
||||
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self)
|
||||
}
|
||||
var _5: [Api.PremiumSubscriptionOption]?
|
||||
if let _ = reader.readInt32() {
|
||||
_5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PremiumSubscriptionOption.self)
|
||||
}
|
||||
var _6: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
||||
return Api.help.PremiumPromo.premiumPromo(statusText: _1!, statusEntities: _2!, videoSections: _3!, videos: _4!, periodOptions: _5!, users: _6!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.help {
|
||||
enum PromoData: TypeConstructorDescription {
|
||||
case promoData(flags: Int32, expires: Int32, peer: Api.Peer, chats: [Api.Chat], users: [Api.User], psaType: String?, psaMessage: String?)
|
||||
@ -1290,49 +1376,3 @@ public extension Api.messages {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.messages {
|
||||
enum DialogFilters: TypeConstructorDescription {
|
||||
case dialogFilters(flags: Int32, filters: [Api.DialogFilter])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .dialogFilters(let flags, let filters):
|
||||
if boxed {
|
||||
buffer.appendInt32(718878489)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(filters.count))
|
||||
for item in filters {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .dialogFilters(let flags, let filters):
|
||||
return ("dialogFilters", [("flags", flags as Any), ("filters", filters as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_dialogFilters(_ reader: BufferReader) -> DialogFilters? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: [Api.DialogFilter]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.DialogFilter.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.messages.DialogFilters.dialogFilters(flags: _1!, filters: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,49 @@
|
||||
public extension Api.messages {
|
||||
enum DialogFilters: TypeConstructorDescription {
|
||||
case dialogFilters(flags: Int32, filters: [Api.DialogFilter])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .dialogFilters(let flags, let filters):
|
||||
if boxed {
|
||||
buffer.appendInt32(718878489)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(filters.count))
|
||||
for item in filters {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .dialogFilters(let flags, let filters):
|
||||
return ("dialogFilters", [("flags", flags as Any), ("filters", filters as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_dialogFilters(_ reader: BufferReader) -> DialogFilters? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: [Api.DialogFilter]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.DialogFilter.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.messages.DialogFilters.dialogFilters(flags: _1!, filters: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.messages {
|
||||
enum Dialogs: TypeConstructorDescription {
|
||||
case dialogs(dialogs: [Api.Dialog], messages: [Api.Message], chats: [Api.Chat], users: [Api.User])
|
||||
@ -1304,6 +1350,52 @@ public extension Api.messages {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.messages {
|
||||
enum MyStickers: TypeConstructorDescription {
|
||||
case myStickers(count: Int32, sets: [Api.StickerSetCovered])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .myStickers(let count, let sets):
|
||||
if boxed {
|
||||
buffer.appendInt32(-83926371)
|
||||
}
|
||||
serializeInt32(count, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(sets.count))
|
||||
for item in sets {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .myStickers(let count, let sets):
|
||||
return ("myStickers", [("count", count as Any), ("sets", sets as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_myStickers(_ reader: BufferReader) -> MyStickers? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: [Api.StickerSetCovered]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerSetCovered.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.messages.MyStickers.myStickers(count: _1!, sets: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.messages {
|
||||
enum PeerDialogs: TypeConstructorDescription {
|
||||
case peerDialogs(dialogs: [Api.Dialog], messages: [Api.Message], chats: [Api.Chat], users: [Api.User], state: Api.updates.State)
|
||||
@ -1524,61 +1616,3 @@ public extension Api.messages {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.messages {
|
||||
enum Reactions: TypeConstructorDescription {
|
||||
case reactions(hash: Int64, reactions: [Api.Reaction])
|
||||
case reactionsNotModified
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .reactions(let hash, let reactions):
|
||||
if boxed {
|
||||
buffer.appendInt32(-352454890)
|
||||
}
|
||||
serializeInt64(hash, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(reactions.count))
|
||||
for item in reactions {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
case .reactionsNotModified:
|
||||
if boxed {
|
||||
buffer.appendInt32(-1334846497)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .reactions(let hash, let reactions):
|
||||
return ("reactions", [("hash", hash as Any), ("reactions", reactions as Any)])
|
||||
case .reactionsNotModified:
|
||||
return ("reactionsNotModified", [])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_reactions(_ reader: BufferReader) -> Reactions? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: [Api.Reaction]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Reaction.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.messages.Reactions.reactions(hash: _1!, reactions: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_reactionsNotModified(_ reader: BufferReader) -> Reactions? {
|
||||
return Api.messages.Reactions.reactionsNotModified
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,61 @@
|
||||
public extension Api.messages {
|
||||
enum Reactions: TypeConstructorDescription {
|
||||
case reactions(hash: Int64, reactions: [Api.Reaction])
|
||||
case reactionsNotModified
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .reactions(let hash, let reactions):
|
||||
if boxed {
|
||||
buffer.appendInt32(-352454890)
|
||||
}
|
||||
serializeInt64(hash, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(reactions.count))
|
||||
for item in reactions {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
case .reactionsNotModified:
|
||||
if boxed {
|
||||
buffer.appendInt32(-1334846497)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .reactions(let hash, let reactions):
|
||||
return ("reactions", [("hash", hash as Any), ("reactions", reactions as Any)])
|
||||
case .reactionsNotModified:
|
||||
return ("reactionsNotModified", [])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_reactions(_ reader: BufferReader) -> Reactions? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: [Api.Reaction]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Reaction.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.messages.Reactions.reactions(hash: _1!, reactions: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_reactionsNotModified(_ reader: BufferReader) -> Reactions? {
|
||||
return Api.messages.Reactions.reactionsNotModified
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.messages {
|
||||
enum RecentStickers: TypeConstructorDescription {
|
||||
case recentStickers(hash: Int64, packs: [Api.StickerPack], stickers: [Api.Document], dates: [Int32])
|
||||
@ -1346,121 +1404,3 @@ public extension Api.payments {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.payments {
|
||||
enum PaymentForm: TypeConstructorDescription {
|
||||
case paymentForm(flags: Int32, formId: Int64, botId: Int64, title: String, description: String, photo: Api.WebDocument?, invoice: Api.Invoice, providerId: Int64, url: String, nativeProvider: String?, nativeParams: Api.DataJSON?, additionalMethods: [Api.PaymentFormMethod]?, savedInfo: Api.PaymentRequestedInfo?, savedCredentials: [Api.PaymentSavedCredentials]?, users: [Api.User])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .paymentForm(let flags, let formId, let botId, let title, let description, let photo, let invoice, let providerId, let url, let nativeProvider, let nativeParams, let additionalMethods, let savedInfo, let savedCredentials, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1610250415)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt64(formId, buffer: buffer, boxed: false)
|
||||
serializeInt64(botId, buffer: buffer, boxed: false)
|
||||
serializeString(title, buffer: buffer, boxed: false)
|
||||
serializeString(description, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 5) != 0 {photo!.serialize(buffer, true)}
|
||||
invoice.serialize(buffer, true)
|
||||
serializeInt64(providerId, buffer: buffer, boxed: false)
|
||||
serializeString(url, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 4) != 0 {serializeString(nativeProvider!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 4) != 0 {nativeParams!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 6) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(additionalMethods!.count))
|
||||
for item in additionalMethods! {
|
||||
item.serialize(buffer, true)
|
||||
}}
|
||||
if Int(flags) & Int(1 << 0) != 0 {savedInfo!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(savedCredentials!.count))
|
||||
for item in savedCredentials! {
|
||||
item.serialize(buffer, true)
|
||||
}}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .paymentForm(let flags, let formId, let botId, let title, let description, let photo, let invoice, let providerId, let url, let nativeProvider, let nativeParams, let additionalMethods, let savedInfo, let savedCredentials, let users):
|
||||
return ("paymentForm", [("flags", flags as Any), ("formId", formId as Any), ("botId", botId as Any), ("title", title as Any), ("description", description as Any), ("photo", photo as Any), ("invoice", invoice as Any), ("providerId", providerId as Any), ("url", url as Any), ("nativeProvider", nativeProvider as Any), ("nativeParams", nativeParams as Any), ("additionalMethods", additionalMethods as Any), ("savedInfo", savedInfo as Any), ("savedCredentials", savedCredentials as Any), ("users", users as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_paymentForm(_ reader: BufferReader) -> PaymentForm? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
var _3: Int64?
|
||||
_3 = reader.readInt64()
|
||||
var _4: String?
|
||||
_4 = parseString(reader)
|
||||
var _5: String?
|
||||
_5 = parseString(reader)
|
||||
var _6: Api.WebDocument?
|
||||
if Int(_1!) & Int(1 << 5) != 0 {if let signature = reader.readInt32() {
|
||||
_6 = Api.parse(reader, signature: signature) as? Api.WebDocument
|
||||
} }
|
||||
var _7: Api.Invoice?
|
||||
if let signature = reader.readInt32() {
|
||||
_7 = Api.parse(reader, signature: signature) as? Api.Invoice
|
||||
}
|
||||
var _8: Int64?
|
||||
_8 = reader.readInt64()
|
||||
var _9: String?
|
||||
_9 = parseString(reader)
|
||||
var _10: String?
|
||||
if Int(_1!) & Int(1 << 4) != 0 {_10 = parseString(reader) }
|
||||
var _11: Api.DataJSON?
|
||||
if Int(_1!) & Int(1 << 4) != 0 {if let signature = reader.readInt32() {
|
||||
_11 = Api.parse(reader, signature: signature) as? Api.DataJSON
|
||||
} }
|
||||
var _12: [Api.PaymentFormMethod]?
|
||||
if Int(_1!) & Int(1 << 6) != 0 {if let _ = reader.readInt32() {
|
||||
_12 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PaymentFormMethod.self)
|
||||
} }
|
||||
var _13: Api.PaymentRequestedInfo?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() {
|
||||
_13 = Api.parse(reader, signature: signature) as? Api.PaymentRequestedInfo
|
||||
} }
|
||||
var _14: [Api.PaymentSavedCredentials]?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() {
|
||||
_14 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PaymentSavedCredentials.self)
|
||||
} }
|
||||
var _15: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_15 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = (Int(_1!) & Int(1 << 5) == 0) || _6 != nil
|
||||
let _c7 = _7 != nil
|
||||
let _c8 = _8 != nil
|
||||
let _c9 = _9 != nil
|
||||
let _c10 = (Int(_1!) & Int(1 << 4) == 0) || _10 != nil
|
||||
let _c11 = (Int(_1!) & Int(1 << 4) == 0) || _11 != nil
|
||||
let _c12 = (Int(_1!) & Int(1 << 6) == 0) || _12 != nil
|
||||
let _c13 = (Int(_1!) & Int(1 << 0) == 0) || _13 != nil
|
||||
let _c14 = (Int(_1!) & Int(1 << 1) == 0) || _14 != nil
|
||||
let _c15 = _15 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 {
|
||||
return Api.payments.PaymentForm.paymentForm(flags: _1!, formId: _2!, botId: _3!, title: _4!, description: _5!, photo: _6, invoice: _7!, providerId: _8!, url: _9!, nativeProvider: _10, nativeParams: _11, additionalMethods: _12, savedInfo: _13, savedCredentials: _14, users: _15!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,121 @@
|
||||
public extension Api.payments {
|
||||
enum PaymentForm: TypeConstructorDescription {
|
||||
case paymentForm(flags: Int32, formId: Int64, botId: Int64, title: String, description: String, photo: Api.WebDocument?, invoice: Api.Invoice, providerId: Int64, url: String, nativeProvider: String?, nativeParams: Api.DataJSON?, additionalMethods: [Api.PaymentFormMethod]?, savedInfo: Api.PaymentRequestedInfo?, savedCredentials: [Api.PaymentSavedCredentials]?, users: [Api.User])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .paymentForm(let flags, let formId, let botId, let title, let description, let photo, let invoice, let providerId, let url, let nativeProvider, let nativeParams, let additionalMethods, let savedInfo, let savedCredentials, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1610250415)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt64(formId, buffer: buffer, boxed: false)
|
||||
serializeInt64(botId, buffer: buffer, boxed: false)
|
||||
serializeString(title, buffer: buffer, boxed: false)
|
||||
serializeString(description, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 5) != 0 {photo!.serialize(buffer, true)}
|
||||
invoice.serialize(buffer, true)
|
||||
serializeInt64(providerId, buffer: buffer, boxed: false)
|
||||
serializeString(url, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 4) != 0 {serializeString(nativeProvider!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 4) != 0 {nativeParams!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 6) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(additionalMethods!.count))
|
||||
for item in additionalMethods! {
|
||||
item.serialize(buffer, true)
|
||||
}}
|
||||
if Int(flags) & Int(1 << 0) != 0 {savedInfo!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(savedCredentials!.count))
|
||||
for item in savedCredentials! {
|
||||
item.serialize(buffer, true)
|
||||
}}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .paymentForm(let flags, let formId, let botId, let title, let description, let photo, let invoice, let providerId, let url, let nativeProvider, let nativeParams, let additionalMethods, let savedInfo, let savedCredentials, let users):
|
||||
return ("paymentForm", [("flags", flags as Any), ("formId", formId as Any), ("botId", botId as Any), ("title", title as Any), ("description", description as Any), ("photo", photo as Any), ("invoice", invoice as Any), ("providerId", providerId as Any), ("url", url as Any), ("nativeProvider", nativeProvider as Any), ("nativeParams", nativeParams as Any), ("additionalMethods", additionalMethods as Any), ("savedInfo", savedInfo as Any), ("savedCredentials", savedCredentials as Any), ("users", users as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_paymentForm(_ reader: BufferReader) -> PaymentForm? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
var _3: Int64?
|
||||
_3 = reader.readInt64()
|
||||
var _4: String?
|
||||
_4 = parseString(reader)
|
||||
var _5: String?
|
||||
_5 = parseString(reader)
|
||||
var _6: Api.WebDocument?
|
||||
if Int(_1!) & Int(1 << 5) != 0 {if let signature = reader.readInt32() {
|
||||
_6 = Api.parse(reader, signature: signature) as? Api.WebDocument
|
||||
} }
|
||||
var _7: Api.Invoice?
|
||||
if let signature = reader.readInt32() {
|
||||
_7 = Api.parse(reader, signature: signature) as? Api.Invoice
|
||||
}
|
||||
var _8: Int64?
|
||||
_8 = reader.readInt64()
|
||||
var _9: String?
|
||||
_9 = parseString(reader)
|
||||
var _10: String?
|
||||
if Int(_1!) & Int(1 << 4) != 0 {_10 = parseString(reader) }
|
||||
var _11: Api.DataJSON?
|
||||
if Int(_1!) & Int(1 << 4) != 0 {if let signature = reader.readInt32() {
|
||||
_11 = Api.parse(reader, signature: signature) as? Api.DataJSON
|
||||
} }
|
||||
var _12: [Api.PaymentFormMethod]?
|
||||
if Int(_1!) & Int(1 << 6) != 0 {if let _ = reader.readInt32() {
|
||||
_12 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PaymentFormMethod.self)
|
||||
} }
|
||||
var _13: Api.PaymentRequestedInfo?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() {
|
||||
_13 = Api.parse(reader, signature: signature) as? Api.PaymentRequestedInfo
|
||||
} }
|
||||
var _14: [Api.PaymentSavedCredentials]?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() {
|
||||
_14 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PaymentSavedCredentials.self)
|
||||
} }
|
||||
var _15: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_15 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = (Int(_1!) & Int(1 << 5) == 0) || _6 != nil
|
||||
let _c7 = _7 != nil
|
||||
let _c8 = _8 != nil
|
||||
let _c9 = _9 != nil
|
||||
let _c10 = (Int(_1!) & Int(1 << 4) == 0) || _10 != nil
|
||||
let _c11 = (Int(_1!) & Int(1 << 4) == 0) || _11 != nil
|
||||
let _c12 = (Int(_1!) & Int(1 << 6) == 0) || _12 != nil
|
||||
let _c13 = (Int(_1!) & Int(1 << 0) == 0) || _13 != nil
|
||||
let _c14 = (Int(_1!) & Int(1 << 1) == 0) || _14 != nil
|
||||
let _c15 = _15 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 {
|
||||
return Api.payments.PaymentForm.paymentForm(flags: _1!, formId: _2!, botId: _3!, title: _4!, description: _5!, photo: _6, invoice: _7!, providerId: _8!, url: _9!, nativeProvider: _10, nativeParams: _11, additionalMethods: _12, savedInfo: _13, savedCredentials: _14, users: _15!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.payments {
|
||||
enum PaymentReceipt: TypeConstructorDescription {
|
||||
case paymentReceipt(flags: Int32, date: Int32, botId: Int64, providerId: Int64, title: String, description: String, photo: Api.WebDocument?, invoice: Api.Invoice, info: Api.PaymentRequestedInfo?, shipping: Api.ShippingOption?, tipAmount: Int64?, currency: String, totalAmount: Int64, credentialsTitle: String, users: [Api.User])
|
||||
|
@ -221,6 +221,21 @@ public extension Api.functions.account {
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.account {
|
||||
static func disablePeerConnectedBot(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(1581481689)
|
||||
peer.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "account.disablePeerConnectedBot", parameters: [("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Bool?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.Bool
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.account {
|
||||
static func finishTakeoutSession(flags: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
let buffer = Buffer()
|
||||
@ -1254,6 +1269,22 @@ public extension Api.functions.account {
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.account {
|
||||
static func toggleConnectedBotPaused(peer: Api.InputPeer, paused: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(1684934807)
|
||||
peer.serialize(buffer, true)
|
||||
paused.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "account.toggleConnectedBotPaused", parameters: [("peer", String(describing: peer)), ("paused", String(describing: paused))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Bool?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.Bool
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.account {
|
||||
static func toggleUsername(username: String, active: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
let buffer = Buffer()
|
||||
@ -3934,6 +3965,21 @@ public extension Api.functions.folders {
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.fragment {
|
||||
static func getCollectibleInfo(collectible: Api.InputCollectible) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.fragment.CollectibleInfo>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-1105295942)
|
||||
collectible.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "fragment.getCollectibleInfo", parameters: [("collectible", String(describing: collectible))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.fragment.CollectibleInfo? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.fragment.CollectibleInfo?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.fragment.CollectibleInfo
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.help {
|
||||
static func acceptTermsOfService(id: Api.DataJSON) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
let buffer = Buffer()
|
||||
@ -5851,6 +5897,22 @@ public extension Api.functions.messages {
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.messages {
|
||||
static func getMyStickers(offsetId: Int64, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.MyStickers>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-793386500)
|
||||
serializeInt64(offsetId, buffer: buffer, boxed: false)
|
||||
serializeInt32(limit, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "messages.getMyStickers", parameters: [("offsetId", String(describing: offsetId)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.MyStickers? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.messages.MyStickers?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.messages.MyStickers
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.messages {
|
||||
static func getOldFeaturedStickers(offset: Int32, limit: Int32, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.FeaturedStickers>) {
|
||||
let buffer = Buffer()
|
||||
@ -7385,12 +7447,22 @@ public extension Api.functions.messages {
|
||||
}
|
||||
}
|
||||
public extension Api.functions.messages {
|
||||
static func sendQuickReplyMessages(peer: Api.InputPeer, shortcutId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
static func sendQuickReplyMessages(peer: Api.InputPeer, shortcutId: Int32, id: [Int32], randomId: [Int64]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(857029332)
|
||||
buffer.appendInt32(1819610593)
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(shortcutId, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "messages.sendQuickReplyMessages", parameters: [("peer", String(describing: peer)), ("shortcutId", String(describing: shortcutId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(id.count))
|
||||
for item in id {
|
||||
serializeInt32(item, buffer: buffer, boxed: false)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(randomId.count))
|
||||
for item in randomId {
|
||||
serializeInt64(item, buffer: buffer, boxed: false)
|
||||
}
|
||||
return (FunctionDescription(name: "messages.sendQuickReplyMessages", parameters: [("peer", String(describing: peer)), ("shortcutId", String(describing: shortcutId)), ("id", String(describing: id)), ("randomId", String(describing: randomId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Updates?
|
||||
if let signature = reader.readInt32() {
|
||||
@ -8095,12 +8167,14 @@ public extension Api.functions.messages {
|
||||
}
|
||||
}
|
||||
public extension Api.functions.messages {
|
||||
static func uploadMedia(peer: Api.InputPeer, media: Api.InputMedia) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.MessageMedia>) {
|
||||
static func uploadMedia(flags: Int32, businessConnectionId: String?, peer: Api.InputPeer, media: Api.InputMedia) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.MessageMedia>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(1369162417)
|
||||
buffer.appendInt32(345405816)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeString(businessConnectionId!, buffer: buffer, boxed: false)}
|
||||
peer.serialize(buffer, true)
|
||||
media.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "messages.uploadMedia", parameters: [("peer", String(describing: peer)), ("media", String(describing: media))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.MessageMedia? in
|
||||
return (FunctionDescription(name: "messages.uploadMedia", parameters: [("flags", String(describing: flags)), ("businessConnectionId", String(describing: businessConnectionId)), ("peer", String(describing: peer)), ("media", String(describing: media))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.MessageMedia? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.MessageMedia?
|
||||
if let signature = reader.readInt32() {
|
||||
@ -9444,6 +9518,22 @@ public extension Api.functions.stickers {
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.stickers {
|
||||
static func replaceSticker(sticker: Api.InputDocument, newSticker: Api.InputStickerSetItem) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.StickerSet>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(1184253338)
|
||||
sticker.serialize(buffer, true)
|
||||
newSticker.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "stickers.replaceSticker", parameters: [("sticker", String(describing: sticker)), ("newSticker", String(describing: newSticker))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.StickerSet? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.messages.StickerSet?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.messages.StickerSet
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.stickers {
|
||||
static func setStickerSetThumb(flags: Int32, stickerset: Api.InputStickerSet, thumb: Api.InputDocument?, thumbDocumentId: Int64?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.StickerSet>) {
|
||||
let buffer = Buffer()
|
||||
|
@ -712,6 +712,62 @@ public extension Api {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum InputCollectible: TypeConstructorDescription {
|
||||
case inputCollectiblePhone(phone: String)
|
||||
case inputCollectibleUsername(username: String)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputCollectiblePhone(let phone):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1562241884)
|
||||
}
|
||||
serializeString(phone, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputCollectibleUsername(let username):
|
||||
if boxed {
|
||||
buffer.appendInt32(-476815191)
|
||||
}
|
||||
serializeString(username, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputCollectiblePhone(let phone):
|
||||
return ("inputCollectiblePhone", [("phone", phone as Any)])
|
||||
case .inputCollectibleUsername(let username):
|
||||
return ("inputCollectibleUsername", [("username", username as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputCollectiblePhone(_ reader: BufferReader) -> InputCollectible? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputCollectible.inputCollectiblePhone(phone: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputCollectibleUsername(_ reader: BufferReader) -> InputCollectible? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputCollectible.inputCollectibleUsername(username: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum InputContact: TypeConstructorDescription {
|
||||
case inputPhoneContact(clientId: Int64, phone: String, firstName: String, lastName: String)
|
||||
@ -1102,313 +1158,3 @@ public extension Api {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
indirect enum InputFileLocation: TypeConstructorDescription {
|
||||
case inputDocumentFileLocation(id: Int64, accessHash: Int64, fileReference: Buffer, thumbSize: String)
|
||||
case inputEncryptedFileLocation(id: Int64, accessHash: Int64)
|
||||
case inputFileLocation(volumeId: Int64, localId: Int32, secret: Int64, fileReference: Buffer)
|
||||
case inputGroupCallStream(flags: Int32, call: Api.InputGroupCall, timeMs: Int64, scale: Int32, videoChannel: Int32?, videoQuality: Int32?)
|
||||
case inputPeerPhotoFileLocation(flags: Int32, peer: Api.InputPeer, photoId: Int64)
|
||||
case inputPhotoFileLocation(id: Int64, accessHash: Int64, fileReference: Buffer, thumbSize: String)
|
||||
case inputPhotoLegacyFileLocation(id: Int64, accessHash: Int64, fileReference: Buffer, volumeId: Int64, localId: Int32, secret: Int64)
|
||||
case inputSecureFileLocation(id: Int64, accessHash: Int64)
|
||||
case inputStickerSetThumb(stickerset: Api.InputStickerSet, thumbVersion: Int32)
|
||||
case inputTakeoutFileLocation
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputDocumentFileLocation(let id, let accessHash, let fileReference, let thumbSize):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1160743548)
|
||||
}
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
serializeBytes(fileReference, buffer: buffer, boxed: false)
|
||||
serializeString(thumbSize, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputEncryptedFileLocation(let id, let accessHash):
|
||||
if boxed {
|
||||
buffer.appendInt32(-182231723)
|
||||
}
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputFileLocation(let volumeId, let localId, let secret, let fileReference):
|
||||
if boxed {
|
||||
buffer.appendInt32(-539317279)
|
||||
}
|
||||
serializeInt64(volumeId, buffer: buffer, boxed: false)
|
||||
serializeInt32(localId, buffer: buffer, boxed: false)
|
||||
serializeInt64(secret, buffer: buffer, boxed: false)
|
||||
serializeBytes(fileReference, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputGroupCallStream(let flags, let call, let timeMs, let scale, let videoChannel, let videoQuality):
|
||||
if boxed {
|
||||
buffer.appendInt32(93890858)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
call.serialize(buffer, true)
|
||||
serializeInt64(timeMs, buffer: buffer, boxed: false)
|
||||
serializeInt32(scale, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(videoChannel!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(videoQuality!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
case .inputPeerPhotoFileLocation(let flags, let peer, let photoId):
|
||||
if boxed {
|
||||
buffer.appendInt32(925204121)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt64(photoId, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputPhotoFileLocation(let id, let accessHash, let fileReference, let thumbSize):
|
||||
if boxed {
|
||||
buffer.appendInt32(1075322878)
|
||||
}
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
serializeBytes(fileReference, buffer: buffer, boxed: false)
|
||||
serializeString(thumbSize, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputPhotoLegacyFileLocation(let id, let accessHash, let fileReference, let volumeId, let localId, let secret):
|
||||
if boxed {
|
||||
buffer.appendInt32(-667654413)
|
||||
}
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
serializeBytes(fileReference, buffer: buffer, boxed: false)
|
||||
serializeInt64(volumeId, buffer: buffer, boxed: false)
|
||||
serializeInt32(localId, buffer: buffer, boxed: false)
|
||||
serializeInt64(secret, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputSecureFileLocation(let id, let accessHash):
|
||||
if boxed {
|
||||
buffer.appendInt32(-876089816)
|
||||
}
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputStickerSetThumb(let stickerset, let thumbVersion):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1652231205)
|
||||
}
|
||||
stickerset.serialize(buffer, true)
|
||||
serializeInt32(thumbVersion, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputTakeoutFileLocation:
|
||||
if boxed {
|
||||
buffer.appendInt32(700340377)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputDocumentFileLocation(let id, let accessHash, let fileReference, let thumbSize):
|
||||
return ("inputDocumentFileLocation", [("id", id as Any), ("accessHash", accessHash as Any), ("fileReference", fileReference as Any), ("thumbSize", thumbSize as Any)])
|
||||
case .inputEncryptedFileLocation(let id, let accessHash):
|
||||
return ("inputEncryptedFileLocation", [("id", id as Any), ("accessHash", accessHash as Any)])
|
||||
case .inputFileLocation(let volumeId, let localId, let secret, let fileReference):
|
||||
return ("inputFileLocation", [("volumeId", volumeId as Any), ("localId", localId as Any), ("secret", secret as Any), ("fileReference", fileReference as Any)])
|
||||
case .inputGroupCallStream(let flags, let call, let timeMs, let scale, let videoChannel, let videoQuality):
|
||||
return ("inputGroupCallStream", [("flags", flags as Any), ("call", call as Any), ("timeMs", timeMs as Any), ("scale", scale as Any), ("videoChannel", videoChannel as Any), ("videoQuality", videoQuality as Any)])
|
||||
case .inputPeerPhotoFileLocation(let flags, let peer, let photoId):
|
||||
return ("inputPeerPhotoFileLocation", [("flags", flags as Any), ("peer", peer as Any), ("photoId", photoId as Any)])
|
||||
case .inputPhotoFileLocation(let id, let accessHash, let fileReference, let thumbSize):
|
||||
return ("inputPhotoFileLocation", [("id", id as Any), ("accessHash", accessHash as Any), ("fileReference", fileReference as Any), ("thumbSize", thumbSize as Any)])
|
||||
case .inputPhotoLegacyFileLocation(let id, let accessHash, let fileReference, let volumeId, let localId, let secret):
|
||||
return ("inputPhotoLegacyFileLocation", [("id", id as Any), ("accessHash", accessHash as Any), ("fileReference", fileReference as Any), ("volumeId", volumeId as Any), ("localId", localId as Any), ("secret", secret as Any)])
|
||||
case .inputSecureFileLocation(let id, let accessHash):
|
||||
return ("inputSecureFileLocation", [("id", id as Any), ("accessHash", accessHash as Any)])
|
||||
case .inputStickerSetThumb(let stickerset, let thumbVersion):
|
||||
return ("inputStickerSetThumb", [("stickerset", stickerset as Any), ("thumbVersion", thumbVersion as Any)])
|
||||
case .inputTakeoutFileLocation:
|
||||
return ("inputTakeoutFileLocation", [])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputDocumentFileLocation(_ reader: BufferReader) -> InputFileLocation? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
var _3: Buffer?
|
||||
_3 = parseBytes(reader)
|
||||
var _4: String?
|
||||
_4 = parseString(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.InputFileLocation.inputDocumentFileLocation(id: _1!, accessHash: _2!, fileReference: _3!, thumbSize: _4!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputEncryptedFileLocation(_ reader: BufferReader) -> InputFileLocation? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputFileLocation.inputEncryptedFileLocation(id: _1!, accessHash: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputFileLocation(_ reader: BufferReader) -> InputFileLocation? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int64?
|
||||
_3 = reader.readInt64()
|
||||
var _4: Buffer?
|
||||
_4 = parseBytes(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.InputFileLocation.inputFileLocation(volumeId: _1!, localId: _2!, secret: _3!, fileReference: _4!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputGroupCallStream(_ reader: BufferReader) -> InputFileLocation? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Api.InputGroupCall?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.InputGroupCall
|
||||
}
|
||||
var _3: Int64?
|
||||
_3 = reader.readInt64()
|
||||
var _4: Int32?
|
||||
_4 = reader.readInt32()
|
||||
var _5: Int32?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_5 = reader.readInt32() }
|
||||
var _6: Int32?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_6 = reader.readInt32() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil
|
||||
let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
||||
return Api.InputFileLocation.inputGroupCallStream(flags: _1!, call: _2!, timeMs: _3!, scale: _4!, videoChannel: _5, videoQuality: _6)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputPeerPhotoFileLocation(_ reader: BufferReader) -> InputFileLocation? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Api.InputPeer?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.InputPeer
|
||||
}
|
||||
var _3: Int64?
|
||||
_3 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.InputFileLocation.inputPeerPhotoFileLocation(flags: _1!, peer: _2!, photoId: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputPhotoFileLocation(_ reader: BufferReader) -> InputFileLocation? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
var _3: Buffer?
|
||||
_3 = parseBytes(reader)
|
||||
var _4: String?
|
||||
_4 = parseString(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.InputFileLocation.inputPhotoFileLocation(id: _1!, accessHash: _2!, fileReference: _3!, thumbSize: _4!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputPhotoLegacyFileLocation(_ reader: BufferReader) -> InputFileLocation? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
var _3: Buffer?
|
||||
_3 = parseBytes(reader)
|
||||
var _4: Int64?
|
||||
_4 = reader.readInt64()
|
||||
var _5: Int32?
|
||||
_5 = reader.readInt32()
|
||||
var _6: Int64?
|
||||
_6 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
||||
return Api.InputFileLocation.inputPhotoLegacyFileLocation(id: _1!, accessHash: _2!, fileReference: _3!, volumeId: _4!, localId: _5!, secret: _6!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputSecureFileLocation(_ reader: BufferReader) -> InputFileLocation? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputFileLocation.inputSecureFileLocation(id: _1!, accessHash: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputStickerSetThumb(_ reader: BufferReader) -> InputFileLocation? {
|
||||
var _1: Api.InputStickerSet?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.InputStickerSet
|
||||
}
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputFileLocation.inputStickerSetThumb(stickerset: _1!, thumbVersion: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputTakeoutFileLocation(_ reader: BufferReader) -> InputFileLocation? {
|
||||
return Api.InputFileLocation.inputTakeoutFileLocation
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,313 @@
|
||||
public extension Api {
|
||||
indirect enum InputFileLocation: TypeConstructorDescription {
|
||||
case inputDocumentFileLocation(id: Int64, accessHash: Int64, fileReference: Buffer, thumbSize: String)
|
||||
case inputEncryptedFileLocation(id: Int64, accessHash: Int64)
|
||||
case inputFileLocation(volumeId: Int64, localId: Int32, secret: Int64, fileReference: Buffer)
|
||||
case inputGroupCallStream(flags: Int32, call: Api.InputGroupCall, timeMs: Int64, scale: Int32, videoChannel: Int32?, videoQuality: Int32?)
|
||||
case inputPeerPhotoFileLocation(flags: Int32, peer: Api.InputPeer, photoId: Int64)
|
||||
case inputPhotoFileLocation(id: Int64, accessHash: Int64, fileReference: Buffer, thumbSize: String)
|
||||
case inputPhotoLegacyFileLocation(id: Int64, accessHash: Int64, fileReference: Buffer, volumeId: Int64, localId: Int32, secret: Int64)
|
||||
case inputSecureFileLocation(id: Int64, accessHash: Int64)
|
||||
case inputStickerSetThumb(stickerset: Api.InputStickerSet, thumbVersion: Int32)
|
||||
case inputTakeoutFileLocation
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputDocumentFileLocation(let id, let accessHash, let fileReference, let thumbSize):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1160743548)
|
||||
}
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
serializeBytes(fileReference, buffer: buffer, boxed: false)
|
||||
serializeString(thumbSize, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputEncryptedFileLocation(let id, let accessHash):
|
||||
if boxed {
|
||||
buffer.appendInt32(-182231723)
|
||||
}
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputFileLocation(let volumeId, let localId, let secret, let fileReference):
|
||||
if boxed {
|
||||
buffer.appendInt32(-539317279)
|
||||
}
|
||||
serializeInt64(volumeId, buffer: buffer, boxed: false)
|
||||
serializeInt32(localId, buffer: buffer, boxed: false)
|
||||
serializeInt64(secret, buffer: buffer, boxed: false)
|
||||
serializeBytes(fileReference, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputGroupCallStream(let flags, let call, let timeMs, let scale, let videoChannel, let videoQuality):
|
||||
if boxed {
|
||||
buffer.appendInt32(93890858)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
call.serialize(buffer, true)
|
||||
serializeInt64(timeMs, buffer: buffer, boxed: false)
|
||||
serializeInt32(scale, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(videoChannel!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(videoQuality!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
case .inputPeerPhotoFileLocation(let flags, let peer, let photoId):
|
||||
if boxed {
|
||||
buffer.appendInt32(925204121)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt64(photoId, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputPhotoFileLocation(let id, let accessHash, let fileReference, let thumbSize):
|
||||
if boxed {
|
||||
buffer.appendInt32(1075322878)
|
||||
}
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
serializeBytes(fileReference, buffer: buffer, boxed: false)
|
||||
serializeString(thumbSize, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputPhotoLegacyFileLocation(let id, let accessHash, let fileReference, let volumeId, let localId, let secret):
|
||||
if boxed {
|
||||
buffer.appendInt32(-667654413)
|
||||
}
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
serializeBytes(fileReference, buffer: buffer, boxed: false)
|
||||
serializeInt64(volumeId, buffer: buffer, boxed: false)
|
||||
serializeInt32(localId, buffer: buffer, boxed: false)
|
||||
serializeInt64(secret, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputSecureFileLocation(let id, let accessHash):
|
||||
if boxed {
|
||||
buffer.appendInt32(-876089816)
|
||||
}
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputStickerSetThumb(let stickerset, let thumbVersion):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1652231205)
|
||||
}
|
||||
stickerset.serialize(buffer, true)
|
||||
serializeInt32(thumbVersion, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputTakeoutFileLocation:
|
||||
if boxed {
|
||||
buffer.appendInt32(700340377)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputDocumentFileLocation(let id, let accessHash, let fileReference, let thumbSize):
|
||||
return ("inputDocumentFileLocation", [("id", id as Any), ("accessHash", accessHash as Any), ("fileReference", fileReference as Any), ("thumbSize", thumbSize as Any)])
|
||||
case .inputEncryptedFileLocation(let id, let accessHash):
|
||||
return ("inputEncryptedFileLocation", [("id", id as Any), ("accessHash", accessHash as Any)])
|
||||
case .inputFileLocation(let volumeId, let localId, let secret, let fileReference):
|
||||
return ("inputFileLocation", [("volumeId", volumeId as Any), ("localId", localId as Any), ("secret", secret as Any), ("fileReference", fileReference as Any)])
|
||||
case .inputGroupCallStream(let flags, let call, let timeMs, let scale, let videoChannel, let videoQuality):
|
||||
return ("inputGroupCallStream", [("flags", flags as Any), ("call", call as Any), ("timeMs", timeMs as Any), ("scale", scale as Any), ("videoChannel", videoChannel as Any), ("videoQuality", videoQuality as Any)])
|
||||
case .inputPeerPhotoFileLocation(let flags, let peer, let photoId):
|
||||
return ("inputPeerPhotoFileLocation", [("flags", flags as Any), ("peer", peer as Any), ("photoId", photoId as Any)])
|
||||
case .inputPhotoFileLocation(let id, let accessHash, let fileReference, let thumbSize):
|
||||
return ("inputPhotoFileLocation", [("id", id as Any), ("accessHash", accessHash as Any), ("fileReference", fileReference as Any), ("thumbSize", thumbSize as Any)])
|
||||
case .inputPhotoLegacyFileLocation(let id, let accessHash, let fileReference, let volumeId, let localId, let secret):
|
||||
return ("inputPhotoLegacyFileLocation", [("id", id as Any), ("accessHash", accessHash as Any), ("fileReference", fileReference as Any), ("volumeId", volumeId as Any), ("localId", localId as Any), ("secret", secret as Any)])
|
||||
case .inputSecureFileLocation(let id, let accessHash):
|
||||
return ("inputSecureFileLocation", [("id", id as Any), ("accessHash", accessHash as Any)])
|
||||
case .inputStickerSetThumb(let stickerset, let thumbVersion):
|
||||
return ("inputStickerSetThumb", [("stickerset", stickerset as Any), ("thumbVersion", thumbVersion as Any)])
|
||||
case .inputTakeoutFileLocation:
|
||||
return ("inputTakeoutFileLocation", [])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputDocumentFileLocation(_ reader: BufferReader) -> InputFileLocation? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
var _3: Buffer?
|
||||
_3 = parseBytes(reader)
|
||||
var _4: String?
|
||||
_4 = parseString(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.InputFileLocation.inputDocumentFileLocation(id: _1!, accessHash: _2!, fileReference: _3!, thumbSize: _4!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputEncryptedFileLocation(_ reader: BufferReader) -> InputFileLocation? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputFileLocation.inputEncryptedFileLocation(id: _1!, accessHash: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputFileLocation(_ reader: BufferReader) -> InputFileLocation? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int64?
|
||||
_3 = reader.readInt64()
|
||||
var _4: Buffer?
|
||||
_4 = parseBytes(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.InputFileLocation.inputFileLocation(volumeId: _1!, localId: _2!, secret: _3!, fileReference: _4!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputGroupCallStream(_ reader: BufferReader) -> InputFileLocation? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Api.InputGroupCall?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.InputGroupCall
|
||||
}
|
||||
var _3: Int64?
|
||||
_3 = reader.readInt64()
|
||||
var _4: Int32?
|
||||
_4 = reader.readInt32()
|
||||
var _5: Int32?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_5 = reader.readInt32() }
|
||||
var _6: Int32?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_6 = reader.readInt32() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil
|
||||
let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
||||
return Api.InputFileLocation.inputGroupCallStream(flags: _1!, call: _2!, timeMs: _3!, scale: _4!, videoChannel: _5, videoQuality: _6)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputPeerPhotoFileLocation(_ reader: BufferReader) -> InputFileLocation? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Api.InputPeer?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.InputPeer
|
||||
}
|
||||
var _3: Int64?
|
||||
_3 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.InputFileLocation.inputPeerPhotoFileLocation(flags: _1!, peer: _2!, photoId: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputPhotoFileLocation(_ reader: BufferReader) -> InputFileLocation? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
var _3: Buffer?
|
||||
_3 = parseBytes(reader)
|
||||
var _4: String?
|
||||
_4 = parseString(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.InputFileLocation.inputPhotoFileLocation(id: _1!, accessHash: _2!, fileReference: _3!, thumbSize: _4!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputPhotoLegacyFileLocation(_ reader: BufferReader) -> InputFileLocation? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
var _3: Buffer?
|
||||
_3 = parseBytes(reader)
|
||||
var _4: Int64?
|
||||
_4 = reader.readInt64()
|
||||
var _5: Int32?
|
||||
_5 = reader.readInt32()
|
||||
var _6: Int64?
|
||||
_6 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
||||
return Api.InputFileLocation.inputPhotoLegacyFileLocation(id: _1!, accessHash: _2!, fileReference: _3!, volumeId: _4!, localId: _5!, secret: _6!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputSecureFileLocation(_ reader: BufferReader) -> InputFileLocation? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputFileLocation.inputSecureFileLocation(id: _1!, accessHash: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputStickerSetThumb(_ reader: BufferReader) -> InputFileLocation? {
|
||||
var _1: Api.InputStickerSet?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.InputStickerSet
|
||||
}
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputFileLocation.inputStickerSetThumb(stickerset: _1!, thumbVersion: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputTakeoutFileLocation(_ reader: BufferReader) -> InputFileLocation? {
|
||||
return Api.InputFileLocation.inputTakeoutFileLocation
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
indirect enum InputFolderPeer: TypeConstructorDescription {
|
||||
case inputFolderPeer(peer: Api.InputPeer, folderId: Int32)
|
||||
@ -884,195 +1194,3 @@ public extension Api {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum InputMessage: TypeConstructorDescription {
|
||||
case inputMessageCallbackQuery(id: Int32, queryId: Int64)
|
||||
case inputMessageID(id: Int32)
|
||||
case inputMessagePinned
|
||||
case inputMessageReplyTo(id: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputMessageCallbackQuery(let id, let queryId):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1392895362)
|
||||
}
|
||||
serializeInt32(id, buffer: buffer, boxed: false)
|
||||
serializeInt64(queryId, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputMessageID(let id):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1502174430)
|
||||
}
|
||||
serializeInt32(id, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputMessagePinned:
|
||||
if boxed {
|
||||
buffer.appendInt32(-2037963464)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputMessageReplyTo(let id):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1160215659)
|
||||
}
|
||||
serializeInt32(id, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputMessageCallbackQuery(let id, let queryId):
|
||||
return ("inputMessageCallbackQuery", [("id", id as Any), ("queryId", queryId as Any)])
|
||||
case .inputMessageID(let id):
|
||||
return ("inputMessageID", [("id", id as Any)])
|
||||
case .inputMessagePinned:
|
||||
return ("inputMessagePinned", [])
|
||||
case .inputMessageReplyTo(let id):
|
||||
return ("inputMessageReplyTo", [("id", id as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputMessageCallbackQuery(_ reader: BufferReader) -> InputMessage? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputMessage.inputMessageCallbackQuery(id: _1!, queryId: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputMessageID(_ reader: BufferReader) -> InputMessage? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputMessage.inputMessageID(id: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputMessagePinned(_ reader: BufferReader) -> InputMessage? {
|
||||
return Api.InputMessage.inputMessagePinned
|
||||
}
|
||||
public static func parse_inputMessageReplyTo(_ reader: BufferReader) -> InputMessage? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputMessage.inputMessageReplyTo(id: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
indirect enum InputNotifyPeer: TypeConstructorDescription {
|
||||
case inputNotifyBroadcasts
|
||||
case inputNotifyChats
|
||||
case inputNotifyForumTopic(peer: Api.InputPeer, topMsgId: Int32)
|
||||
case inputNotifyPeer(peer: Api.InputPeer)
|
||||
case inputNotifyUsers
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputNotifyBroadcasts:
|
||||
if boxed {
|
||||
buffer.appendInt32(-1311015810)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputNotifyChats:
|
||||
if boxed {
|
||||
buffer.appendInt32(1251338318)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputNotifyForumTopic(let peer, let topMsgId):
|
||||
if boxed {
|
||||
buffer.appendInt32(1548122514)
|
||||
}
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(topMsgId, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputNotifyPeer(let peer):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1195615476)
|
||||
}
|
||||
peer.serialize(buffer, true)
|
||||
break
|
||||
case .inputNotifyUsers:
|
||||
if boxed {
|
||||
buffer.appendInt32(423314455)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputNotifyBroadcasts:
|
||||
return ("inputNotifyBroadcasts", [])
|
||||
case .inputNotifyChats:
|
||||
return ("inputNotifyChats", [])
|
||||
case .inputNotifyForumTopic(let peer, let topMsgId):
|
||||
return ("inputNotifyForumTopic", [("peer", peer as Any), ("topMsgId", topMsgId as Any)])
|
||||
case .inputNotifyPeer(let peer):
|
||||
return ("inputNotifyPeer", [("peer", peer as Any)])
|
||||
case .inputNotifyUsers:
|
||||
return ("inputNotifyUsers", [])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputNotifyBroadcasts(_ reader: BufferReader) -> InputNotifyPeer? {
|
||||
return Api.InputNotifyPeer.inputNotifyBroadcasts
|
||||
}
|
||||
public static func parse_inputNotifyChats(_ reader: BufferReader) -> InputNotifyPeer? {
|
||||
return Api.InputNotifyPeer.inputNotifyChats
|
||||
}
|
||||
public static func parse_inputNotifyForumTopic(_ reader: BufferReader) -> InputNotifyPeer? {
|
||||
var _1: Api.InputPeer?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.InputPeer
|
||||
}
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputNotifyPeer.inputNotifyForumTopic(peer: _1!, topMsgId: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputNotifyPeer(_ reader: BufferReader) -> InputNotifyPeer? {
|
||||
var _1: Api.InputPeer?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.InputPeer
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputNotifyPeer.inputNotifyPeer(peer: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputNotifyUsers(_ reader: BufferReader) -> InputNotifyPeer? {
|
||||
return Api.InputNotifyPeer.inputNotifyUsers
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,195 @@
|
||||
public extension Api {
|
||||
enum InputMessage: TypeConstructorDescription {
|
||||
case inputMessageCallbackQuery(id: Int32, queryId: Int64)
|
||||
case inputMessageID(id: Int32)
|
||||
case inputMessagePinned
|
||||
case inputMessageReplyTo(id: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputMessageCallbackQuery(let id, let queryId):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1392895362)
|
||||
}
|
||||
serializeInt32(id, buffer: buffer, boxed: false)
|
||||
serializeInt64(queryId, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputMessageID(let id):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1502174430)
|
||||
}
|
||||
serializeInt32(id, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputMessagePinned:
|
||||
if boxed {
|
||||
buffer.appendInt32(-2037963464)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputMessageReplyTo(let id):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1160215659)
|
||||
}
|
||||
serializeInt32(id, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputMessageCallbackQuery(let id, let queryId):
|
||||
return ("inputMessageCallbackQuery", [("id", id as Any), ("queryId", queryId as Any)])
|
||||
case .inputMessageID(let id):
|
||||
return ("inputMessageID", [("id", id as Any)])
|
||||
case .inputMessagePinned:
|
||||
return ("inputMessagePinned", [])
|
||||
case .inputMessageReplyTo(let id):
|
||||
return ("inputMessageReplyTo", [("id", id as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputMessageCallbackQuery(_ reader: BufferReader) -> InputMessage? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputMessage.inputMessageCallbackQuery(id: _1!, queryId: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputMessageID(_ reader: BufferReader) -> InputMessage? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputMessage.inputMessageID(id: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputMessagePinned(_ reader: BufferReader) -> InputMessage? {
|
||||
return Api.InputMessage.inputMessagePinned
|
||||
}
|
||||
public static func parse_inputMessageReplyTo(_ reader: BufferReader) -> InputMessage? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputMessage.inputMessageReplyTo(id: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
indirect enum InputNotifyPeer: TypeConstructorDescription {
|
||||
case inputNotifyBroadcasts
|
||||
case inputNotifyChats
|
||||
case inputNotifyForumTopic(peer: Api.InputPeer, topMsgId: Int32)
|
||||
case inputNotifyPeer(peer: Api.InputPeer)
|
||||
case inputNotifyUsers
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputNotifyBroadcasts:
|
||||
if boxed {
|
||||
buffer.appendInt32(-1311015810)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputNotifyChats:
|
||||
if boxed {
|
||||
buffer.appendInt32(1251338318)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputNotifyForumTopic(let peer, let topMsgId):
|
||||
if boxed {
|
||||
buffer.appendInt32(1548122514)
|
||||
}
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(topMsgId, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputNotifyPeer(let peer):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1195615476)
|
||||
}
|
||||
peer.serialize(buffer, true)
|
||||
break
|
||||
case .inputNotifyUsers:
|
||||
if boxed {
|
||||
buffer.appendInt32(423314455)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputNotifyBroadcasts:
|
||||
return ("inputNotifyBroadcasts", [])
|
||||
case .inputNotifyChats:
|
||||
return ("inputNotifyChats", [])
|
||||
case .inputNotifyForumTopic(let peer, let topMsgId):
|
||||
return ("inputNotifyForumTopic", [("peer", peer as Any), ("topMsgId", topMsgId as Any)])
|
||||
case .inputNotifyPeer(let peer):
|
||||
return ("inputNotifyPeer", [("peer", peer as Any)])
|
||||
case .inputNotifyUsers:
|
||||
return ("inputNotifyUsers", [])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputNotifyBroadcasts(_ reader: BufferReader) -> InputNotifyPeer? {
|
||||
return Api.InputNotifyPeer.inputNotifyBroadcasts
|
||||
}
|
||||
public static func parse_inputNotifyChats(_ reader: BufferReader) -> InputNotifyPeer? {
|
||||
return Api.InputNotifyPeer.inputNotifyChats
|
||||
}
|
||||
public static func parse_inputNotifyForumTopic(_ reader: BufferReader) -> InputNotifyPeer? {
|
||||
var _1: Api.InputPeer?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.InputPeer
|
||||
}
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputNotifyPeer.inputNotifyForumTopic(peer: _1!, topMsgId: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputNotifyPeer(_ reader: BufferReader) -> InputNotifyPeer? {
|
||||
var _1: Api.InputPeer?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.InputPeer
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputNotifyPeer.inputNotifyPeer(peer: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputNotifyUsers(_ reader: BufferReader) -> InputNotifyPeer? {
|
||||
return Api.InputNotifyPeer.inputNotifyUsers
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum InputPaymentCredentials: TypeConstructorDescription {
|
||||
case inputPaymentCredentials(flags: Int32, data: Api.DataJSON)
|
||||
@ -584,183 +776,3 @@ public extension Api {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum InputPrivacyRule: TypeConstructorDescription {
|
||||
case inputPrivacyValueAllowAll
|
||||
case inputPrivacyValueAllowChatParticipants(chats: [Int64])
|
||||
case inputPrivacyValueAllowCloseFriends
|
||||
case inputPrivacyValueAllowContacts
|
||||
case inputPrivacyValueAllowUsers(users: [Api.InputUser])
|
||||
case inputPrivacyValueDisallowAll
|
||||
case inputPrivacyValueDisallowChatParticipants(chats: [Int64])
|
||||
case inputPrivacyValueDisallowContacts
|
||||
case inputPrivacyValueDisallowUsers(users: [Api.InputUser])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputPrivacyValueAllowAll:
|
||||
if boxed {
|
||||
buffer.appendInt32(407582158)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputPrivacyValueAllowChatParticipants(let chats):
|
||||
if boxed {
|
||||
buffer.appendInt32(-2079962673)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(chats.count))
|
||||
for item in chats {
|
||||
serializeInt64(item, buffer: buffer, boxed: false)
|
||||
}
|
||||
break
|
||||
case .inputPrivacyValueAllowCloseFriends:
|
||||
if boxed {
|
||||
buffer.appendInt32(793067081)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputPrivacyValueAllowContacts:
|
||||
if boxed {
|
||||
buffer.appendInt32(218751099)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputPrivacyValueAllowUsers(let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(320652927)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
case .inputPrivacyValueDisallowAll:
|
||||
if boxed {
|
||||
buffer.appendInt32(-697604407)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputPrivacyValueDisallowChatParticipants(let chats):
|
||||
if boxed {
|
||||
buffer.appendInt32(-380694650)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(chats.count))
|
||||
for item in chats {
|
||||
serializeInt64(item, buffer: buffer, boxed: false)
|
||||
}
|
||||
break
|
||||
case .inputPrivacyValueDisallowContacts:
|
||||
if boxed {
|
||||
buffer.appendInt32(195371015)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputPrivacyValueDisallowUsers(let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1877932953)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputPrivacyValueAllowAll:
|
||||
return ("inputPrivacyValueAllowAll", [])
|
||||
case .inputPrivacyValueAllowChatParticipants(let chats):
|
||||
return ("inputPrivacyValueAllowChatParticipants", [("chats", chats as Any)])
|
||||
case .inputPrivacyValueAllowCloseFriends:
|
||||
return ("inputPrivacyValueAllowCloseFriends", [])
|
||||
case .inputPrivacyValueAllowContacts:
|
||||
return ("inputPrivacyValueAllowContacts", [])
|
||||
case .inputPrivacyValueAllowUsers(let users):
|
||||
return ("inputPrivacyValueAllowUsers", [("users", users as Any)])
|
||||
case .inputPrivacyValueDisallowAll:
|
||||
return ("inputPrivacyValueDisallowAll", [])
|
||||
case .inputPrivacyValueDisallowChatParticipants(let chats):
|
||||
return ("inputPrivacyValueDisallowChatParticipants", [("chats", chats as Any)])
|
||||
case .inputPrivacyValueDisallowContacts:
|
||||
return ("inputPrivacyValueDisallowContacts", [])
|
||||
case .inputPrivacyValueDisallowUsers(let users):
|
||||
return ("inputPrivacyValueDisallowUsers", [("users", users as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputPrivacyValueAllowAll(_ reader: BufferReader) -> InputPrivacyRule? {
|
||||
return Api.InputPrivacyRule.inputPrivacyValueAllowAll
|
||||
}
|
||||
public static func parse_inputPrivacyValueAllowChatParticipants(_ reader: BufferReader) -> InputPrivacyRule? {
|
||||
var _1: [Int64]?
|
||||
if let _ = reader.readInt32() {
|
||||
_1 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputPrivacyRule.inputPrivacyValueAllowChatParticipants(chats: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputPrivacyValueAllowCloseFriends(_ reader: BufferReader) -> InputPrivacyRule? {
|
||||
return Api.InputPrivacyRule.inputPrivacyValueAllowCloseFriends
|
||||
}
|
||||
public static func parse_inputPrivacyValueAllowContacts(_ reader: BufferReader) -> InputPrivacyRule? {
|
||||
return Api.InputPrivacyRule.inputPrivacyValueAllowContacts
|
||||
}
|
||||
public static func parse_inputPrivacyValueAllowUsers(_ reader: BufferReader) -> InputPrivacyRule? {
|
||||
var _1: [Api.InputUser]?
|
||||
if let _ = reader.readInt32() {
|
||||
_1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.InputUser.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputPrivacyRule.inputPrivacyValueAllowUsers(users: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputPrivacyValueDisallowAll(_ reader: BufferReader) -> InputPrivacyRule? {
|
||||
return Api.InputPrivacyRule.inputPrivacyValueDisallowAll
|
||||
}
|
||||
public static func parse_inputPrivacyValueDisallowChatParticipants(_ reader: BufferReader) -> InputPrivacyRule? {
|
||||
var _1: [Int64]?
|
||||
if let _ = reader.readInt32() {
|
||||
_1 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputPrivacyRule.inputPrivacyValueDisallowChatParticipants(chats: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputPrivacyValueDisallowContacts(_ reader: BufferReader) -> InputPrivacyRule? {
|
||||
return Api.InputPrivacyRule.inputPrivacyValueDisallowContacts
|
||||
}
|
||||
public static func parse_inputPrivacyValueDisallowUsers(_ reader: BufferReader) -> InputPrivacyRule? {
|
||||
var _1: [Api.InputUser]?
|
||||
if let _ = reader.readInt32() {
|
||||
_1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.InputUser.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputPrivacyRule.inputPrivacyValueDisallowUsers(users: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -686,6 +686,9 @@ final class VoiceChatPreviewContentNode: ASDisplayNode, ShareContentContainerNod
|
||||
func setEnsurePeerVisibleOnLayout(_ peerId: PeerId?) {
|
||||
}
|
||||
|
||||
func setDidBeginDragging(_ f: (() -> Void)?) {
|
||||
}
|
||||
|
||||
func setContentOffsetUpdated(_ f: ((CGFloat, ContainedViewLayoutTransition) -> Void)?) {
|
||||
self.contentOffsetUpdated = f
|
||||
}
|
||||
|
@ -144,13 +144,13 @@ public class UnauthorizedAccount {
|
||||
return accountManager.transaction { transaction -> (LocalizationSettings?, ProxySettings?) in
|
||||
return (transaction.getSharedData(SharedDataKeys.localizationSettings)?.get(LocalizationSettings.self), transaction.getSharedData(SharedDataKeys.proxySettings)?.get(ProxySettings.self))
|
||||
}
|
||||
|> mapToSignal { localizationSettings, proxySettings -> Signal<(LocalizationSettings?, ProxySettings?, NetworkSettings?), NoError> in
|
||||
return self.postbox.transaction { transaction -> (LocalizationSettings?, ProxySettings?, NetworkSettings?) in
|
||||
return (localizationSettings, proxySettings, transaction.getPreferencesEntry(key: PreferencesKeys.networkSettings)?.get(NetworkSettings.self))
|
||||
|> mapToSignal { localizationSettings, proxySettings -> Signal<(LocalizationSettings?, ProxySettings?, NetworkSettings?, AppConfiguration), NoError> in
|
||||
return self.postbox.transaction { transaction -> (LocalizationSettings?, ProxySettings?, NetworkSettings?, AppConfiguration) in
|
||||
return (localizationSettings, proxySettings, transaction.getPreferencesEntry(key: PreferencesKeys.networkSettings)?.get(NetworkSettings.self), transaction.getPreferencesEntry(key: PreferencesKeys.appConfiguration)?.get(AppConfiguration.self) ?? .defaultValue)
|
||||
}
|
||||
}
|
||||
|> mapToSignal { (localizationSettings, proxySettings, networkSettings) -> Signal<UnauthorizedAccount, NoError> in
|
||||
return initializedNetwork(accountId: self.id, arguments: self.networkArguments, supplementary: false, datacenterId: Int(masterDatacenterId), keychain: keychain, basePath: self.basePath, testingEnvironment: self.testingEnvironment, languageCode: localizationSettings?.primaryComponent.languageCode, proxySettings: proxySettings, networkSettings: networkSettings, phoneNumber: nil, useRequestTimeoutTimers: false)
|
||||
|> mapToSignal { localizationSettings, proxySettings, networkSettings, appConfiguration -> Signal<UnauthorizedAccount, NoError> in
|
||||
return initializedNetwork(accountId: self.id, arguments: self.networkArguments, supplementary: false, datacenterId: Int(masterDatacenterId), keychain: keychain, basePath: self.basePath, testingEnvironment: self.testingEnvironment, languageCode: localizationSettings?.primaryComponent.languageCode, proxySettings: proxySettings, networkSettings: networkSettings, phoneNumber: nil, useRequestTimeoutTimers: false, appConfiguration: appConfiguration)
|
||||
|> map { network in
|
||||
let updated = UnauthorizedAccount(networkArguments: self.networkArguments, id: self.id, rootPath: self.rootPath, basePath: self.basePath, testingEnvironment: self.testingEnvironment, postbox: self.postbox, network: network)
|
||||
updated.shouldBeServiceTaskMaster.set(self.shouldBeServiceTaskMaster.get())
|
||||
@ -248,7 +248,7 @@ public func accountWithId(accountManager: AccountManager<TelegramAccountManagerT
|
||||
if let accountState = accountState {
|
||||
switch accountState {
|
||||
case let unauthorizedState as UnauthorizedAccountState:
|
||||
return initializedNetwork(accountId: id, arguments: networkArguments, supplementary: supplementary, datacenterId: Int(unauthorizedState.masterDatacenterId), keychain: keychain, basePath: path, testingEnvironment: unauthorizedState.isTestingEnvironment, languageCode: localizationSettings?.primaryComponent.languageCode, proxySettings: proxySettings, networkSettings: networkSettings, phoneNumber: nil, useRequestTimeoutTimers: useRequestTimeoutTimers)
|
||||
return initializedNetwork(accountId: id, arguments: networkArguments, supplementary: supplementary, datacenterId: Int(unauthorizedState.masterDatacenterId), keychain: keychain, basePath: path, testingEnvironment: unauthorizedState.isTestingEnvironment, languageCode: localizationSettings?.primaryComponent.languageCode, proxySettings: proxySettings, networkSettings: networkSettings, phoneNumber: nil, useRequestTimeoutTimers: useRequestTimeoutTimers, appConfiguration: appConfig)
|
||||
|> map { network -> AccountResult in
|
||||
return .unauthorized(UnauthorizedAccount(networkArguments: networkArguments, id: id, rootPath: rootPath, basePath: path, testingEnvironment: unauthorizedState.isTestingEnvironment, postbox: postbox, network: network, shouldKeepAutoConnection: shouldKeepAutoConnection))
|
||||
}
|
||||
@ -257,7 +257,7 @@ public func accountWithId(accountManager: AccountManager<TelegramAccountManagerT
|
||||
return (transaction.getPeer(authorizedState.peerId) as? TelegramUser)?.phone
|
||||
}
|
||||
|> mapToSignal { phoneNumber in
|
||||
return initializedNetwork(accountId: id, arguments: networkArguments, supplementary: supplementary, datacenterId: Int(authorizedState.masterDatacenterId), keychain: keychain, basePath: path, testingEnvironment: authorizedState.isTestingEnvironment, languageCode: localizationSettings?.primaryComponent.languageCode, proxySettings: proxySettings, networkSettings: networkSettings, phoneNumber: phoneNumber, useRequestTimeoutTimers: useRequestTimeoutTimers)
|
||||
return initializedNetwork(accountId: id, arguments: networkArguments, supplementary: supplementary, datacenterId: Int(authorizedState.masterDatacenterId), keychain: keychain, basePath: path, testingEnvironment: authorizedState.isTestingEnvironment, languageCode: localizationSettings?.primaryComponent.languageCode, proxySettings: proxySettings, networkSettings: networkSettings, phoneNumber: phoneNumber, useRequestTimeoutTimers: useRequestTimeoutTimers, appConfiguration: appConfig)
|
||||
|> map { network -> AccountResult in
|
||||
return .authorized(Account(accountManager: accountManager, id: id, basePath: path, testingEnvironment: authorizedState.isTestingEnvironment, postbox: postbox, network: network, networkArguments: networkArguments, peerId: authorizedState.peerId, auxiliaryMethods: auxiliaryMethods, supplementary: supplementary))
|
||||
}
|
||||
@ -267,7 +267,7 @@ public func accountWithId(accountManager: AccountManager<TelegramAccountManagerT
|
||||
}
|
||||
}
|
||||
|
||||
return initializedNetwork(accountId: id, arguments: networkArguments, supplementary: supplementary, datacenterId: 2, keychain: keychain, basePath: path, testingEnvironment: beginWithTestingEnvironment, languageCode: localizationSettings?.primaryComponent.languageCode, proxySettings: proxySettings, networkSettings: networkSettings, phoneNumber: nil, useRequestTimeoutTimers: useRequestTimeoutTimers)
|
||||
return initializedNetwork(accountId: id, arguments: networkArguments, supplementary: supplementary, datacenterId: 2, keychain: keychain, basePath: path, testingEnvironment: beginWithTestingEnvironment, languageCode: localizationSettings?.primaryComponent.languageCode, proxySettings: proxySettings, networkSettings: networkSettings, phoneNumber: nil, useRequestTimeoutTimers: useRequestTimeoutTimers, appConfiguration: appConfig)
|
||||
|> map { network -> AccountResult in
|
||||
return .unauthorized(UnauthorizedAccount(networkArguments: networkArguments, id: id, rootPath: rootPath, basePath: path, testingEnvironment: beginWithTestingEnvironment, postbox: postbox, network: network, shouldKeepAutoConnection: shouldKeepAutoConnection))
|
||||
}
|
||||
@ -889,6 +889,11 @@ public func accountBackupData(postbox: Postbox) -> Signal<AccountBackupData?, No
|
||||
}
|
||||
}
|
||||
|
||||
public enum NetworkSpeedLimitedEvent {
|
||||
case upload
|
||||
case download
|
||||
}
|
||||
|
||||
public class Account {
|
||||
static let sharedQueue = Queue(name: "Account-Shared")
|
||||
|
||||
@ -1519,7 +1524,8 @@ public func standaloneStateManager(
|
||||
proxySettings: proxySettings,
|
||||
networkSettings: networkSettings,
|
||||
phoneNumber: phoneNumber,
|
||||
useRequestTimeoutTimers: false
|
||||
useRequestTimeoutTimers: false,
|
||||
appConfiguration: .defaultValue
|
||||
)
|
||||
|> map { network -> AccountStateManager? in
|
||||
Logger.shared.log("StandaloneStateManager", "received network")
|
||||
|
@ -90,6 +90,7 @@ private var declaredEncodables: Void = {
|
||||
declareEncodable(ChannelState.self, f: { ChannelState(decoder: $0) })
|
||||
declareEncodable(RegularChatState.self, f: { RegularChatState(decoder: $0) })
|
||||
declareEncodable(InlineBotMessageAttribute.self, f: { InlineBotMessageAttribute(decoder: $0) })
|
||||
declareEncodable(InlineBusinessBotMessageAttribute.self, f: { InlineBusinessBotMessageAttribute(decoder: $0) })
|
||||
declareEncodable(TextEntitiesMessageAttribute.self, f: { TextEntitiesMessageAttribute(decoder: $0) })
|
||||
declareEncodable(ReplyMessageAttribute.self, f: { ReplyMessageAttribute(decoder: $0) })
|
||||
declareEncodable(QuotedReplyMessageAttribute.self, f: { QuotedReplyMessageAttribute(decoder: $0) })
|
||||
|
@ -126,7 +126,7 @@ public func tagsForStoreMessage(incoming: Bool, attributes: [MessageAttribute],
|
||||
|
||||
func apiMessagePeerId(_ messsage: Api.Message) -> PeerId? {
|
||||
switch messsage {
|
||||
case let .message(_, _, _, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
case let .message(_, _, _, _, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
let chatPeerId = messagePeerId
|
||||
return chatPeerId.peerId
|
||||
case let .messageEmpty(_, _, peerId):
|
||||
@ -142,7 +142,7 @@ func apiMessagePeerId(_ messsage: Api.Message) -> PeerId? {
|
||||
|
||||
func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] {
|
||||
switch message {
|
||||
case let .message(_, _, fromId, _, chatPeerId, savedPeerId, fwdHeader, viaBotId, replyTo, _, _, media, _, entities, _, _, _, _, _, _, _, _, _, _):
|
||||
case let .message(_, _, _, fromId, _, chatPeerId, savedPeerId, fwdHeader, viaBotId, viaBusinessBotId, replyTo, _, _, media, _, entities, _, _, _, _, _, _, _, _, _, _):
|
||||
let peerId: PeerId = chatPeerId.peerId
|
||||
|
||||
var result = [peerId]
|
||||
@ -171,6 +171,9 @@ func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] {
|
||||
if let viaBotId = viaBotId {
|
||||
result.append(PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(viaBotId)))
|
||||
}
|
||||
if let viaBusinessBotId {
|
||||
result.append(PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(viaBusinessBotId)))
|
||||
}
|
||||
|
||||
if let savedPeerId = savedPeerId {
|
||||
result.append(savedPeerId.peerId)
|
||||
@ -263,7 +266,7 @@ func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] {
|
||||
|
||||
func apiMessageAssociatedMessageIds(_ message: Api.Message) -> (replyIds: ReferencedReplyMessageIds, generalIds: [MessageId])? {
|
||||
switch message {
|
||||
case let .message(_, id, _, _, chatPeerId, _, _, _, replyTo, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
case let .message(_, _, id, _, _, chatPeerId, _, _, _, _, replyTo, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
if let replyTo = replyTo {
|
||||
let peerId: PeerId = chatPeerId.peerId
|
||||
|
||||
@ -597,7 +600,7 @@ func messageTextEntitiesFromApiEntities(_ entities: [Api.MessageEntity]) -> [Mes
|
||||
extension StoreMessage {
|
||||
convenience init?(apiMessage: Api.Message, accountPeerId: PeerId, peerIsForum: Bool, namespace: MessageId.Namespace = Namespaces.Message.Cloud) {
|
||||
switch apiMessage {
|
||||
case let .message(flags, id, fromId, boosts, chatPeerId, savedPeerId, fwdFrom, viaBotId, replyTo, date, message, media, replyMarkup, entities, views, forwards, replies, editDate, postAuthor, groupingId, reactions, restrictionReason, ttlPeriod, quickReplyShortcutId):
|
||||
case let .message(flags, _, id, fromId, boosts, chatPeerId, savedPeerId, fwdFrom, viaBotId, viaBusinessBotId, replyTo, date, message, media, replyMarkup, entities, views, forwards, replies, editDate, postAuthor, groupingId, reactions, restrictionReason, ttlPeriod, quickReplyShortcutId):
|
||||
let resolvedFromId = fromId?.peerId ?? chatPeerId.peerId
|
||||
|
||||
var namespace = namespace
|
||||
@ -799,6 +802,10 @@ extension StoreMessage {
|
||||
if let viaBotId = viaBotId {
|
||||
attributes.append(InlineBotMessageAttribute(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(viaBotId)), title: nil))
|
||||
}
|
||||
|
||||
if let viaBusinessBotId {
|
||||
attributes.append(InlineBusinessBotMessageAttribute(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(viaBusinessBotId)), title: nil))
|
||||
}
|
||||
|
||||
if namespace != Namespaces.Message.ScheduledCloud && namespace != Namespaces.Message.QuickReplyCloud {
|
||||
if let views = views {
|
||||
|
@ -103,7 +103,7 @@ class Download: NSObject, MTRequestMessageServiceDelegate {
|
||||
self.context.authTokenForDatacenter(withIdRequired: self.datacenterId, authToken:self.mtProto.requiredAuthToken, masterDatacenterId: self.mtProto.authTokenMasterDatacenterId)
|
||||
}
|
||||
|
||||
static func uploadPart(multiplexedManager: MultiplexedRequestManager, datacenterId: Int, consumerId: Int64, tag: MediaResourceFetchTag?, fileId: Int64, index: Int, data: Data, asBigPart: Bool, bigTotalParts: Int? = nil, useCompression: Bool = false) -> Signal<Void, UploadPartError> {
|
||||
static func uploadPart(multiplexedManager: MultiplexedRequestManager, datacenterId: Int, consumerId: Int64, tag: MediaResourceFetchTag?, fileId: Int64, index: Int, data: Data, asBigPart: Bool, bigTotalParts: Int? = nil, useCompression: Bool = false, onFloodWaitError: ((String) -> Void)? = nil) -> Signal<Void, UploadPartError> {
|
||||
let saveFilePart: (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>)
|
||||
if asBigPart {
|
||||
let totalParts: Int32
|
||||
@ -117,7 +117,7 @@ class Download: NSObject, MTRequestMessageServiceDelegate {
|
||||
saveFilePart = Api.functions.upload.saveFilePart(fileId: fileId, filePart: Int32(index), bytes: Buffer(data: data))
|
||||
}
|
||||
|
||||
return multiplexedManager.request(to: .main(datacenterId), consumerId: consumerId, resourceId: nil, data: wrapMethodBody(saveFilePart, useCompression: useCompression), tag: tag, continueInBackground: true, expectedResponseSize: nil)
|
||||
return multiplexedManager.request(to: .main(datacenterId), consumerId: consumerId, resourceId: nil, data: wrapMethodBody(saveFilePart, useCompression: useCompression), tag: tag, continueInBackground: true, onFloodWaitError: onFloodWaitError, expectedResponseSize: nil)
|
||||
|> mapError { error -> UploadPartError in
|
||||
if error.errorCode == 400 {
|
||||
return .invalidMedia
|
||||
@ -130,7 +130,7 @@ class Download: NSObject, MTRequestMessageServiceDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
func uploadPart(fileId: Int64, index: Int, data: Data, asBigPart: Bool, bigTotalParts: Int? = nil, useCompression: Bool = false) -> Signal<Void, UploadPartError> {
|
||||
func uploadPart(fileId: Int64, index: Int, data: Data, asBigPart: Bool, bigTotalParts: Int? = nil, useCompression: Bool = false, onFloodWaitError: ((String) -> Void)? = nil) -> Signal<Void, UploadPartError> {
|
||||
return Signal<Void, MTRpcError> { subscriber in
|
||||
let request = MTRequest()
|
||||
|
||||
@ -159,6 +159,13 @@ class Download: NSObject, MTRequestMessageServiceDelegate {
|
||||
request.dependsOnPasswordEntry = false
|
||||
|
||||
request.shouldContinueExecutionWithErrorContext = { errorContext in
|
||||
guard let errorContext = errorContext else {
|
||||
return true
|
||||
}
|
||||
if let onFloodWaitError, errorContext.floodWaitSeconds > 0, let errorText = errorContext.floodWaitErrorText {
|
||||
onFloodWaitError(errorText)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@ -295,7 +302,7 @@ class Download: NSObject, MTRequestMessageServiceDelegate {
|
||||
|> retryRequest
|
||||
}
|
||||
|
||||
func request<T>(_ data: (FunctionDescription, Buffer, DeserializeFunctionResponse<T>), expectedResponseSize: Int32? = nil, automaticFloodWait: Bool = true) -> Signal<T, MTRpcError> {
|
||||
func request<T>(_ data: (FunctionDescription, Buffer, DeserializeFunctionResponse<T>), expectedResponseSize: Int32? = nil, automaticFloodWait: Bool = true, onFloodWaitError: ((String) -> Void)? = nil) -> Signal<T, MTRpcError> {
|
||||
return Signal { subscriber in
|
||||
let request = MTRequest()
|
||||
request.expectedResponseSize = expectedResponseSize ?? 0
|
||||
@ -314,6 +321,9 @@ class Download: NSObject, MTRequestMessageServiceDelegate {
|
||||
guard let errorContext = errorContext else {
|
||||
return true
|
||||
}
|
||||
if let onFloodWaitError, errorContext.floodWaitSeconds > 0, let errorText = errorContext.floodWaitErrorText {
|
||||
onFloodWaitError(errorText)
|
||||
}
|
||||
if errorContext.floodWaitSeconds > 0 && !automaticFloodWait {
|
||||
return false
|
||||
}
|
||||
@ -344,7 +354,7 @@ class Download: NSObject, MTRequestMessageServiceDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
func requestWithAdditionalData<T>(_ data: (FunctionDescription, Buffer, DeserializeFunctionResponse<T>), automaticFloodWait: Bool = true, failOnServerErrors: Bool = false, expectedResponseSize: Int32? = nil) -> Signal<(T, Double), (MTRpcError, Double)> {
|
||||
func requestWithAdditionalData<T>(_ data: (FunctionDescription, Buffer, DeserializeFunctionResponse<T>), automaticFloodWait: Bool = true, onFloodWaitError: ((String) -> Void)? = nil, failOnServerErrors: Bool = false, expectedResponseSize: Int32? = nil) -> Signal<(T, Double), (MTRpcError, Double)> {
|
||||
return Signal { subscriber in
|
||||
let request = MTRequest()
|
||||
request.expectedResponseSize = expectedResponseSize ?? 0
|
||||
@ -363,6 +373,9 @@ class Download: NSObject, MTRequestMessageServiceDelegate {
|
||||
guard let errorContext = errorContext else {
|
||||
return true
|
||||
}
|
||||
if let onFloodWaitError, errorContext.floodWaitSeconds > 0, let errorText = errorContext.floodWaitErrorText {
|
||||
onFloodWaitError(errorText)
|
||||
}
|
||||
if errorContext.floodWaitSeconds > 0 && !automaticFloodWait {
|
||||
return false
|
||||
}
|
||||
@ -396,7 +409,7 @@ class Download: NSObject, MTRequestMessageServiceDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
func rawRequest(_ data: (FunctionDescription, Buffer, (Buffer) -> Any?), automaticFloodWait: Bool = true, failOnServerErrors: Bool = false, logPrefix: String = "", expectedResponseSize: Int32? = nil) -> Signal<(Any, NetworkResponseInfo), (MTRpcError, Double)> {
|
||||
func rawRequest(_ data: (FunctionDescription, Buffer, (Buffer) -> Any?), automaticFloodWait: Bool = true, onFloodWaitError: ((String) -> Void)? = nil, failOnServerErrors: Bool = false, logPrefix: String = "", expectedResponseSize: Int32? = nil) -> Signal<(Any, NetworkResponseInfo), (MTRpcError, Double)> {
|
||||
let requestService = self.requestService
|
||||
return Signal { subscriber in
|
||||
let request = MTRequest()
|
||||
@ -416,6 +429,9 @@ class Download: NSObject, MTRequestMessageServiceDelegate {
|
||||
guard let errorContext = errorContext else {
|
||||
return true
|
||||
}
|
||||
if let onFloodWaitError, errorContext.floodWaitSeconds > 0, let errorText = errorContext.floodWaitErrorText {
|
||||
onFloodWaitError(errorText)
|
||||
}
|
||||
if errorContext.floodWaitSeconds > 0 && !automaticFloodWait {
|
||||
return false
|
||||
}
|
||||
|
@ -104,14 +104,14 @@ private struct DownloadWrapper {
|
||||
self.useMainConnection = useMainConnection
|
||||
}
|
||||
|
||||
func request<T>(_ data: (FunctionDescription, Buffer, DeserializeFunctionResponse<T>), tag: MediaResourceFetchTag?, continueInBackground: Bool, expectedResponseSize: Int32?) -> Signal<(T, NetworkResponseInfo), MTRpcError> {
|
||||
func request<T>(_ data: (FunctionDescription, Buffer, DeserializeFunctionResponse<T>), tag: MediaResourceFetchTag?, continueInBackground: Bool, expectedResponseSize: Int32?, onFloodWaitError: @escaping (String) -> Void) -> Signal<(T, NetworkResponseInfo), MTRpcError> {
|
||||
let target: MultiplexedRequestTarget
|
||||
if self.isCdn {
|
||||
target = .cdn(Int(self.datacenterId))
|
||||
} else {
|
||||
target = .main(Int(self.datacenterId))
|
||||
}
|
||||
return network.multiplexedRequestManager.requestWithAdditionalInfo(to: target, consumerId: self.consumerId, resourceId: self.resourceId, data: data, tag: tag, continueInBackground: continueInBackground, expectedResponseSize: expectedResponseSize)
|
||||
return network.multiplexedRequestManager.requestWithAdditionalInfo(to: target, consumerId: self.consumerId, resourceId: self.resourceId, data: data, tag: tag, continueInBackground: continueInBackground, onFloodWaitError: onFloodWaitError, expectedResponseSize: expectedResponseSize)
|
||||
|> mapError { error, _ -> MTRpcError in
|
||||
return error
|
||||
}
|
||||
@ -192,7 +192,7 @@ private final class MultipartCdnHashSource {
|
||||
clusterContext = ClusterContext(disposable: disposable)
|
||||
self.clusterContexts[offset] = clusterContext
|
||||
|
||||
disposable.set((self.masterDownload.request(Api.functions.upload.getCdnFileHashes(fileToken: Buffer(data: self.fileToken), offset: offset), tag: nil, continueInBackground: self.continueInBackground, expectedResponseSize: nil)
|
||||
disposable.set((self.masterDownload.request(Api.functions.upload.getCdnFileHashes(fileToken: Buffer(data: self.fileToken), offset: offset), tag: nil, continueInBackground: self.continueInBackground, expectedResponseSize: nil, onFloodWaitError: { _ in })
|
||||
|> map { partHashes, _ -> [Int64: Data] in
|
||||
var parsedPartHashes: [Int64: Data] = [:]
|
||||
for part in partHashes {
|
||||
@ -322,7 +322,7 @@ private enum MultipartFetchSource {
|
||||
}
|
||||
}
|
||||
|
||||
func request(offset: Int64, limit: Int64, tag: MediaResourceFetchTag?, resource: TelegramMediaResource, resourceReference: FetchResourceReference, fileReference: Data?, continueInBackground: Bool) -> Signal<(Data, NetworkResponseInfo), MultipartFetchDownloadError> {
|
||||
func request(offset: Int64, limit: Int64, tag: MediaResourceFetchTag?, resource: TelegramMediaResource, resourceReference: FetchResourceReference, fileReference: Data?, continueInBackground: Bool, onFloodWaitError: @escaping (String) -> Void) -> Signal<(Data, NetworkResponseInfo), MultipartFetchDownloadError> {
|
||||
var resourceReferenceValue: MediaResourceReference?
|
||||
switch resourceReference {
|
||||
case .forceRevalidate:
|
||||
@ -348,7 +348,9 @@ private enum MultipartFetchSource {
|
||||
case .revalidate:
|
||||
return .fail(.revalidateMediaReference)
|
||||
case let .location(parsedLocation):
|
||||
return download.request(Api.functions.upload.getFile(flags: 0, location: parsedLocation, offset: offset, limit: Int32(limit)), tag: tag, continueInBackground: continueInBackground, expectedResponseSize: Int32(limit))
|
||||
return download.request(Api.functions.upload.getFile(flags: 0, location: parsedLocation, offset: offset, limit: Int32(limit)), tag: tag, continueInBackground: continueInBackground, expectedResponseSize: Int32(limit), onFloodWaitError: { error in
|
||||
onFloodWaitError(error)
|
||||
})
|
||||
|> mapError { error -> MultipartFetchDownloadError in
|
||||
if error.errorDescription.hasPrefix("FILEREF_INVALID") || error.errorDescription.hasPrefix("FILE_REFERENCE_") {
|
||||
return .revalidateMediaReference
|
||||
@ -380,7 +382,9 @@ private enum MultipartFetchSource {
|
||||
}
|
||||
}
|
||||
case let .web(_, location):
|
||||
return download.request(Api.functions.upload.getWebFile(location: location, offset: Int32(offset), limit: Int32(limit)), tag: tag, continueInBackground: continueInBackground, expectedResponseSize: Int32(limit))
|
||||
return download.request(Api.functions.upload.getWebFile(location: location, offset: Int32(offset), limit: Int32(limit)), tag: tag, continueInBackground: continueInBackground, expectedResponseSize: Int32(limit), onFloodWaitError: { error in
|
||||
onFloodWaitError(error)
|
||||
})
|
||||
|> mapError { error -> MultipartFetchDownloadError in
|
||||
if error.errorDescription == "WEBFILE_NOT_AVAILABLE" {
|
||||
return .webfileNotAvailable
|
||||
@ -404,7 +408,9 @@ private enum MultipartFetchSource {
|
||||
updatedLength += 1
|
||||
}
|
||||
|
||||
let part = download.request(Api.functions.upload.getCdnFile(fileToken: Buffer(data: fileToken), offset: offset, limit: Int32(updatedLength)), tag: nil, continueInBackground: continueInBackground, expectedResponseSize: Int32(updatedLength))
|
||||
let part = download.request(Api.functions.upload.getCdnFile(fileToken: Buffer(data: fileToken), offset: offset, limit: Int32(updatedLength)), tag: nil, continueInBackground: continueInBackground, expectedResponseSize: Int32(updatedLength), onFloodWaitError: { error in
|
||||
onFloodWaitError(error)
|
||||
})
|
||||
|> mapError { _ -> MultipartFetchDownloadError in
|
||||
return .generic
|
||||
}
|
||||
@ -723,6 +729,13 @@ private final class MultipartFetchManager {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func processFloodWaitError(error: String) {
|
||||
if error.hasPrefix("FLOOD_PREMIUM_WAIT") {
|
||||
self.network.addNetworkSpeedLimitedEvent(event: .download)
|
||||
}
|
||||
}
|
||||
|
||||
func checkState() {
|
||||
guard let currentIntervals = self.currentIntervals else {
|
||||
return
|
||||
@ -836,7 +849,15 @@ private final class MultipartFetchManager {
|
||||
}
|
||||
|
||||
let partSize: Int32 = Int32(downloadRange.upperBound - downloadRange.lowerBound)
|
||||
let part = self.source.request(offset: downloadRange.lowerBound, limit: downloadRange.upperBound - downloadRange.lowerBound, tag: self.parameters?.tag, resource: self.resource, resourceReference: self.resourceReference, fileReference: self.fileReference, continueInBackground: self.continueInBackground)
|
||||
let queue = self.queue
|
||||
let part = self.source.request(offset: downloadRange.lowerBound, limit: downloadRange.upperBound - downloadRange.lowerBound, tag: self.parameters?.tag, resource: self.resource, resourceReference: self.resourceReference, fileReference: self.fileReference, continueInBackground: self.continueInBackground, onFloodWaitError: { [weak self] error in
|
||||
queue.async {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.processFloodWaitError(error: error)
|
||||
}
|
||||
})
|
||||
|> deliverOn(self.queue)
|
||||
let partDisposable = MetaDisposable()
|
||||
self.fetchingParts[downloadRange.lowerBound] = FetchingPart(size: Int64(downloadRange.count), disposable: partDisposable)
|
||||
@ -919,7 +940,7 @@ private final class MultipartFetchManager {
|
||||
case let .cdn(_, _, fileToken, _, _, _, masterDownload, _):
|
||||
if !strongSelf.reuploadingToCdn {
|
||||
strongSelf.reuploadingToCdn = true
|
||||
let reupload: Signal<[Api.FileHash], NoError> = masterDownload.request(Api.functions.upload.reuploadCdnFile(fileToken: Buffer(data: fileToken), requestToken: Buffer(data: token)), tag: nil, continueInBackground: strongSelf.continueInBackground, expectedResponseSize: nil)
|
||||
let reupload: Signal<[Api.FileHash], NoError> = masterDownload.request(Api.functions.upload.reuploadCdnFile(fileToken: Buffer(data: fileToken), requestToken: Buffer(data: token)), tag: nil, continueInBackground: strongSelf.continueInBackground, expectedResponseSize: nil, onFloodWaitError: { _ in })
|
||||
|> map { result, _ -> [Api.FileHash] in
|
||||
return result
|
||||
}
|
||||
|
@ -470,12 +470,21 @@ func multipartUpload(network: Network, postbox: Postbox, source: MultipartUpload
|
||||
fetchedResource = .complete()
|
||||
}
|
||||
|
||||
let onFloodWaitError: (String) -> Void = { [weak network] error in
|
||||
guard let network else {
|
||||
return
|
||||
}
|
||||
if error.hasPrefix("FLOOD_PREMIUM_WAIT") {
|
||||
network.addNetworkSpeedLimitedEvent(event: .upload)
|
||||
}
|
||||
}
|
||||
|
||||
let manager = MultipartUploadManager(headerSize: headerSize, data: dataSignal, encryptionKey: encryptionKey, hintFileSize: hintFileSize, hintFileIsLarge: hintFileIsLarge, forceNoBigParts: forceNoBigParts, useLargerParts: useLargerParts, increaseParallelParts: increaseParallelParts, uploadPart: { part in
|
||||
switch uploadInterface {
|
||||
case let .download(download):
|
||||
return download.uploadPart(fileId: part.fileId, index: part.index, data: part.data, asBigPart: part.bigPart, bigTotalParts: part.bigTotalParts, useCompression: useCompression)
|
||||
return download.uploadPart(fileId: part.fileId, index: part.index, data: part.data, asBigPart: part.bigPart, bigTotalParts: part.bigTotalParts, useCompression: useCompression, onFloodWaitError: onFloodWaitError)
|
||||
case let .multiplexed(multiplexed, datacenterId, consumerId):
|
||||
return Download.uploadPart(multiplexedManager: multiplexed, datacenterId: datacenterId, consumerId: consumerId, tag: nil, fileId: part.fileId, index: part.index, data: part.data, asBigPart: part.bigPart, bigTotalParts: part.bigTotalParts, useCompression: useCompression)
|
||||
return Download.uploadPart(multiplexedManager: multiplexed, datacenterId: datacenterId, consumerId: consumerId, tag: nil, fileId: part.fileId, index: part.index, data: part.data, asBigPart: part.bigPart, bigTotalParts: part.bigTotalParts, useCompression: useCompression, onFloodWaitError: onFloodWaitError)
|
||||
}
|
||||
}, progress: { progress in
|
||||
subscriber.putNext(.progress(progress))
|
||||
|
@ -33,12 +33,13 @@ private final class RequestData {
|
||||
let tag: MediaResourceFetchTag?
|
||||
let continueInBackground: Bool
|
||||
let automaticFloodWait: Bool
|
||||
let onFloodWaitError: ((String) -> Void)?
|
||||
let expectedResponseSize: Int32?
|
||||
let deserializeResponse: (Buffer) -> Any?
|
||||
let completed: (Any, NetworkResponseInfo) -> Void
|
||||
let error: (MTRpcError, Double) -> Void
|
||||
|
||||
init(id: Int32, consumerId: Int64, resourceId: String?, target: MultiplexedRequestTarget, functionDescription: FunctionDescription, payload: Buffer, tag: MediaResourceFetchTag?, continueInBackground: Bool, automaticFloodWait: Bool, expectedResponseSize: Int32?, deserializeResponse: @escaping (Buffer) -> Any?, completed: @escaping (Any, NetworkResponseInfo) -> Void, error: @escaping (MTRpcError, Double) -> Void) {
|
||||
init(id: Int32, consumerId: Int64, resourceId: String?, target: MultiplexedRequestTarget, functionDescription: FunctionDescription, payload: Buffer, tag: MediaResourceFetchTag?, continueInBackground: Bool, automaticFloodWait: Bool, onFloodWaitError: ((String) -> Void)?, expectedResponseSize: Int32?, deserializeResponse: @escaping (Buffer) -> Any?, completed: @escaping (Any, NetworkResponseInfo) -> Void, error: @escaping (MTRpcError, Double) -> Void) {
|
||||
self.id = id
|
||||
self.consumerId = consumerId
|
||||
self.resourceId = resourceId
|
||||
@ -47,6 +48,7 @@ private final class RequestData {
|
||||
self.tag = tag
|
||||
self.continueInBackground = continueInBackground
|
||||
self.automaticFloodWait = automaticFloodWait
|
||||
self.onFloodWaitError = onFloodWaitError
|
||||
self.expectedResponseSize = expectedResponseSize
|
||||
self.payload = payload
|
||||
self.deserializeResponse = deserializeResponse
|
||||
@ -155,12 +157,12 @@ private final class MultiplexedRequestManagerContext {
|
||||
}
|
||||
}
|
||||
|
||||
func request(to target: MultiplexedRequestTarget, consumerId: Int64, resourceId: String?, data: (FunctionDescription, Buffer, (Buffer) -> Any?), tag: MediaResourceFetchTag?, continueInBackground: Bool, automaticFloodWait: Bool, expectedResponseSize: Int32?, completed: @escaping (Any, NetworkResponseInfo) -> Void, error: @escaping (MTRpcError, Double) -> Void) -> Disposable {
|
||||
func request(to target: MultiplexedRequestTarget, consumerId: Int64, resourceId: String?, data: (FunctionDescription, Buffer, (Buffer) -> Any?), tag: MediaResourceFetchTag?, continueInBackground: Bool, automaticFloodWait: Bool, onFloodWaitError: ((String) -> Void)? = nil, expectedResponseSize: Int32?, completed: @escaping (Any, NetworkResponseInfo) -> Void, error: @escaping (MTRpcError, Double) -> Void) -> Disposable {
|
||||
let targetKey = MultiplexedRequestTargetKey(target: target, continueInBackground: continueInBackground)
|
||||
|
||||
let requestId = self.nextId
|
||||
self.nextId += 1
|
||||
self.queuedRequests.append(RequestData(id: requestId, consumerId: consumerId, resourceId: resourceId, target: target, functionDescription: data.0, payload: data.1, tag: tag, continueInBackground: continueInBackground, automaticFloodWait: automaticFloodWait, expectedResponseSize: expectedResponseSize, deserializeResponse: { buffer in
|
||||
self.queuedRequests.append(RequestData(id: requestId, consumerId: consumerId, resourceId: resourceId, target: target, functionDescription: data.0, payload: data.1, tag: tag, continueInBackground: continueInBackground, automaticFloodWait: automaticFloodWait, onFloodWaitError: onFloodWaitError, expectedResponseSize: expectedResponseSize, deserializeResponse: { buffer in
|
||||
return data.2(buffer)
|
||||
}, completed: { result, info in
|
||||
completed(result, info)
|
||||
@ -254,7 +256,7 @@ private final class MultiplexedRequestManagerContext {
|
||||
let requestId = request.id
|
||||
selectedContext.requests.append(ExecutingRequestData(requestId: requestId, disposable: disposable))
|
||||
let queue = self.queue
|
||||
disposable.set(selectedContext.worker.rawRequest((request.functionDescription, request.payload, request.deserializeResponse), automaticFloodWait: request.automaticFloodWait, expectedResponseSize: request.expectedResponseSize).start(next: { [weak self, weak selectedContext] result, info in
|
||||
disposable.set(selectedContext.worker.rawRequest((request.functionDescription, request.payload, request.deserializeResponse), automaticFloodWait: request.automaticFloodWait, onFloodWaitError: request.onFloodWaitError, expectedResponseSize: request.expectedResponseSize).start(next: { [weak self, weak selectedContext] result, info in
|
||||
queue.async {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
@ -354,13 +356,13 @@ final class MultiplexedRequestManager {
|
||||
return disposable
|
||||
}
|
||||
|
||||
func request<T>(to target: MultiplexedRequestTarget, consumerId: Int64, resourceId: String?, data: (FunctionDescription, Buffer, DeserializeFunctionResponse<T>), tag: MediaResourceFetchTag?, continueInBackground: Bool, automaticFloodWait: Bool = true, expectedResponseSize: Int32?) -> Signal<T, MTRpcError> {
|
||||
func request<T>(to target: MultiplexedRequestTarget, consumerId: Int64, resourceId: String?, data: (FunctionDescription, Buffer, DeserializeFunctionResponse<T>), tag: MediaResourceFetchTag?, continueInBackground: Bool, automaticFloodWait: Bool = true, onFloodWaitError: ((String) -> Void)? = nil, expectedResponseSize: Int32?) -> Signal<T, MTRpcError> {
|
||||
return Signal { subscriber in
|
||||
let disposable = MetaDisposable()
|
||||
self.context.with { context in
|
||||
disposable.set(context.request(to: target, consumerId: consumerId, resourceId: resourceId, data: (data.0, data.1, { buffer in
|
||||
return data.2.parse(buffer)
|
||||
}), tag: tag, continueInBackground: continueInBackground, automaticFloodWait: automaticFloodWait, expectedResponseSize: expectedResponseSize, completed: { result, _ in
|
||||
}), tag: tag, continueInBackground: continueInBackground, automaticFloodWait: automaticFloodWait, onFloodWaitError: onFloodWaitError, expectedResponseSize: expectedResponseSize, completed: { result, _ in
|
||||
if let result = result as? T {
|
||||
subscriber.putNext(result)
|
||||
subscriber.putCompletion()
|
||||
@ -375,13 +377,13 @@ final class MultiplexedRequestManager {
|
||||
}
|
||||
}
|
||||
|
||||
func requestWithAdditionalInfo<T>(to target: MultiplexedRequestTarget, consumerId: Int64, resourceId: String?, data: (FunctionDescription, Buffer, DeserializeFunctionResponse<T>), tag: MediaResourceFetchTag?, continueInBackground: Bool, automaticFloodWait: Bool = true, expectedResponseSize: Int32?) -> Signal<(T, NetworkResponseInfo), (MTRpcError, Double)> {
|
||||
func requestWithAdditionalInfo<T>(to target: MultiplexedRequestTarget, consumerId: Int64, resourceId: String?, data: (FunctionDescription, Buffer, DeserializeFunctionResponse<T>), tag: MediaResourceFetchTag?, continueInBackground: Bool, automaticFloodWait: Bool = true, onFloodWaitError: ((String) -> Void)? = nil, expectedResponseSize: Int32?) -> Signal<(T, NetworkResponseInfo), (MTRpcError, Double)> {
|
||||
return Signal { subscriber in
|
||||
let disposable = MetaDisposable()
|
||||
self.context.with { context in
|
||||
disposable.set(context.request(to: target, consumerId: consumerId, resourceId: resourceId, data: (data.0, data.1, { buffer in
|
||||
return data.2.parse(buffer)
|
||||
}), tag: tag, continueInBackground: continueInBackground, automaticFloodWait: automaticFloodWait, expectedResponseSize: expectedResponseSize, completed: { result, info in
|
||||
}), tag: tag, continueInBackground: continueInBackground, automaticFloodWait: automaticFloodWait, onFloodWaitError: onFloodWaitError, expectedResponseSize: expectedResponseSize, completed: { result, info in
|
||||
if let result = result as? T {
|
||||
subscriber.putNext((result, info))
|
||||
subscriber.putCompletion()
|
||||
|
@ -459,7 +459,7 @@ public struct NetworkInitializationArguments {
|
||||
private let cloudDataContext = Atomic<CloudDataContext?>(value: nil)
|
||||
#endif
|
||||
|
||||
func initializedNetwork(accountId: AccountRecordId, arguments: NetworkInitializationArguments, supplementary: Bool, datacenterId: Int, keychain: Keychain, basePath: String, testingEnvironment: Bool, languageCode: String?, proxySettings: ProxySettings?, networkSettings: NetworkSettings?, phoneNumber: String?, useRequestTimeoutTimers: Bool) -> Signal<Network, NoError> {
|
||||
func initializedNetwork(accountId: AccountRecordId, arguments: NetworkInitializationArguments, supplementary: Bool, datacenterId: Int, keychain: Keychain, basePath: String, testingEnvironment: Bool, languageCode: String?, proxySettings: ProxySettings?, networkSettings: NetworkSettings?, phoneNumber: String?, useRequestTimeoutTimers: Bool, appConfiguration: AppConfiguration) -> Signal<Network, NoError> {
|
||||
return Signal { subscriber in
|
||||
let queue = Queue()
|
||||
queue.async {
|
||||
@ -612,6 +612,11 @@ func initializedNetwork(accountId: AccountRecordId, arguments: NetworkInitializa
|
||||
let useExperimentalFeatures = networkSettings?.useExperimentalDownload ?? false
|
||||
|
||||
let network = Network(queue: queue, datacenterId: datacenterId, context: context, mtProto: mtProto, requestService: requestService, connectionStatusDelegate: connectionStatusDelegate, _connectionStatus: connectionStatus, basePath: basePath, appDataDisposable: appDataDisposable, encryptionProvider: arguments.encryptionProvider, useRequestTimeoutTimers: useRequestTimeoutTimers, useBetaFeatures: arguments.useBetaFeatures, useExperimentalFeatures: useExperimentalFeatures)
|
||||
|
||||
if let data = appConfiguration.data, let notifyInterval = data["upload_premium_speedup_notify_period"] as? Double {
|
||||
network.updateNetworkSpeedLimitedEventNotifyInterval(value: notifyInterval)
|
||||
}
|
||||
|
||||
appDataUpdatedImpl = { [weak network] data in
|
||||
guard let data = data else {
|
||||
return
|
||||
@ -734,6 +739,22 @@ public enum NetworkRequestResult<T> {
|
||||
case progress(Float, Int32)
|
||||
}
|
||||
|
||||
private final class NetworkSpeedLimitedEventState {
|
||||
var notifyInterval: Double = 60.0 * 60.0
|
||||
var lastNotifyTimestamp: Double = 0.0
|
||||
|
||||
func add(event: NetworkSpeedLimitedEvent) -> Bool {
|
||||
let timestamp = CFAbsoluteTimeGetCurrent()
|
||||
|
||||
if self.lastNotifyTimestamp + self.notifyInterval < timestamp {
|
||||
self.lastNotifyTimestamp = timestamp
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final class Network: NSObject, MTRequestMessageServiceDelegate {
|
||||
public let encryptionProvider: EncryptionProvider
|
||||
|
||||
@ -766,6 +787,12 @@ public final class Network: NSObject, MTRequestMessageServiceDelegate {
|
||||
return self._connectionStatus.get() |> distinctUntilChanged
|
||||
}
|
||||
|
||||
public var networkSpeedLimitedEvents: Signal<NetworkSpeedLimitedEvent, NoError> {
|
||||
return self.networkSpeedLimitedEventPipe.signal()
|
||||
}
|
||||
private let networkSpeedLimitedEventPipe = ValuePipe<NetworkSpeedLimitedEvent>()
|
||||
private let networkSpeedLimitedEventState = Atomic<NetworkSpeedLimitedEventState>(value: NetworkSpeedLimitedEventState())
|
||||
|
||||
public func dropConnectionStatus() {
|
||||
_connectionStatus.set(.single(.waitingForNetwork))
|
||||
}
|
||||
@ -826,18 +853,18 @@ public final class Network: NSObject, MTRequestMessageServiceDelegate {
|
||||
let array = NSMutableArray()
|
||||
if let result = result {
|
||||
switch result {
|
||||
case let .cdnConfig(publicKeys):
|
||||
for key in publicKeys {
|
||||
switch key {
|
||||
case let .cdnPublicKey(dcId, publicKey):
|
||||
if id == Int(dcId) {
|
||||
let dict = NSMutableDictionary()
|
||||
dict["key"] = publicKey
|
||||
dict["fingerprint"] = MTRsaFingerprint(encryptionProvider, publicKey)
|
||||
array.add(dict)
|
||||
}
|
||||
case let .cdnConfig(publicKeys):
|
||||
for key in publicKeys {
|
||||
switch key {
|
||||
case let .cdnPublicKey(dcId, publicKey):
|
||||
if id == Int(dcId) {
|
||||
let dict = NSMutableDictionary()
|
||||
dict["key"] = publicKey
|
||||
dict["fingerprint"] = MTRsaFingerprint(encryptionProvider, publicKey)
|
||||
array.add(dict)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return array
|
||||
@ -867,12 +894,12 @@ public final class Network: NSObject, MTRequestMessageServiceDelegate {
|
||||
let isCdn: Bool
|
||||
let isMedia: Bool = true
|
||||
switch target {
|
||||
case let .main(id):
|
||||
datacenterId = id
|
||||
isCdn = false
|
||||
case let .cdn(id):
|
||||
datacenterId = id
|
||||
isCdn = true
|
||||
case let .main(id):
|
||||
datacenterId = id
|
||||
isCdn = false
|
||||
case let .cdn(id):
|
||||
datacenterId = id
|
||||
isCdn = true
|
||||
}
|
||||
return strongSelf.makeWorker(datacenterId: datacenterId, isCdn: isCdn, isMedia: isMedia, tag: tag, continueInBackground: continueInBackground)
|
||||
}
|
||||
@ -880,7 +907,7 @@ public final class Network: NSObject, MTRequestMessageServiceDelegate {
|
||||
})
|
||||
|
||||
let shouldKeepConnectionSignal = self.shouldKeepConnection.get()
|
||||
|> distinctUntilChanged |> deliverOn(queue)
|
||||
|> distinctUntilChanged |> deliverOn(queue)
|
||||
self.shouldKeepConnectionDisposable.set(shouldKeepConnectionSignal.start(next: { [weak self] value in
|
||||
if let strongSelf = self {
|
||||
if value {
|
||||
@ -967,11 +994,11 @@ public final class Network: NSObject, MTRequestMessageServiceDelegate {
|
||||
self.context.addAddressForDatacenter(withId: Int(datacenterId), address: address)
|
||||
|
||||
/*let currentScheme = self.context.transportSchemeForDatacenter(withId: Int(datacenterId), media: false, isProxy: false)
|
||||
if let currentScheme = currentScheme, currentScheme.address.isEqual(to: address) {
|
||||
} else {
|
||||
let scheme = MTTransportScheme(transport: MTTcpTransport.self, address: address, media: false)
|
||||
self.context.updateTransportSchemeForDatacenter(withId: Int(datacenterId), transportScheme: scheme, media: false, isProxy: false)
|
||||
}*/
|
||||
if let currentScheme = currentScheme, currentScheme.address.isEqual(to: address) {
|
||||
} else {
|
||||
let scheme = MTTransportScheme(transport: MTTcpTransport.self, address: address, media: false)
|
||||
self.context.updateTransportSchemeForDatacenter(withId: Int(datacenterId), transportScheme: scheme, media: false, isProxy: false)
|
||||
}*/
|
||||
|
||||
let currentSchemes = self.context.transportSchemesForDatacenter(withId: Int(datacenterId), media: false, enforceMedia: false, isProxy: false)
|
||||
var found = false
|
||||
@ -988,7 +1015,7 @@ public final class Network: NSObject, MTRequestMessageServiceDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
public func requestWithAdditionalInfo<T>(_ data: (FunctionDescription, Buffer, DeserializeFunctionResponse<T>), info: NetworkRequestAdditionalInfo, tag: NetworkRequestDependencyTag? = nil, automaticFloodWait: Bool = true) -> Signal<NetworkRequestResult<T>, MTRpcError> {
|
||||
public func requestWithAdditionalInfo<T>(_ data: (FunctionDescription, Buffer, DeserializeFunctionResponse<T>), info: NetworkRequestAdditionalInfo, tag: NetworkRequestDependencyTag? = nil, automaticFloodWait: Bool = true, onFloodWaitError: ((String) -> Void)? = nil) -> Signal<NetworkRequestResult<T>, MTRpcError> {
|
||||
let requestService = self.requestService
|
||||
return Signal { subscriber in
|
||||
let request = MTRequest()
|
||||
@ -1006,6 +1033,9 @@ public final class Network: NSObject, MTRequestMessageServiceDelegate {
|
||||
guard let errorContext = errorContext else {
|
||||
return true
|
||||
}
|
||||
if let onFloodWaitError, errorContext.floodWaitSeconds > 0, let errorText = errorContext.floodWaitErrorText {
|
||||
onFloodWaitError(errorText)
|
||||
}
|
||||
if errorContext.floodWaitSeconds > 0 && !automaticFloodWait {
|
||||
return false
|
||||
}
|
||||
@ -1056,8 +1086,8 @@ public final class Network: NSObject, MTRequestMessageServiceDelegate {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func request<T>(_ data: (FunctionDescription, Buffer, DeserializeFunctionResponse<T>), tag: NetworkRequestDependencyTag? = nil, automaticFloodWait: Bool = true) -> Signal<T, MTRpcError> {
|
||||
|
||||
public func request<T>(_ data: (FunctionDescription, Buffer, DeserializeFunctionResponse<T>), tag: NetworkRequestDependencyTag? = nil, automaticFloodWait: Bool = true, onFloodWaitError: ((String) -> Void)? = nil) -> Signal<T, MTRpcError> {
|
||||
let requestService = self.requestService
|
||||
return Signal { subscriber in
|
||||
let request = MTRequest()
|
||||
@ -1075,6 +1105,9 @@ public final class Network: NSObject, MTRequestMessageServiceDelegate {
|
||||
guard let errorContext = errorContext else {
|
||||
return true
|
||||
}
|
||||
if let onFloodWaitError, errorContext.floodWaitSeconds > 0, let errorText = errorContext.floodWaitErrorText {
|
||||
onFloodWaitError(errorText)
|
||||
}
|
||||
if errorContext.floodWaitSeconds > 0 && !automaticFloodWait {
|
||||
return false
|
||||
}
|
||||
@ -1113,6 +1146,21 @@ public final class Network: NSObject, MTRequestMessageServiceDelegate {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateNetworkSpeedLimitedEventNotifyInterval(value: Double) {
|
||||
let _ = self.networkSpeedLimitedEventState.with { state in
|
||||
state.notifyInterval = value
|
||||
}
|
||||
}
|
||||
|
||||
func addNetworkSpeedLimitedEvent(event: NetworkSpeedLimitedEvent) {
|
||||
let notify = self.networkSpeedLimitedEventState.with { state in
|
||||
return state.add(event: event)
|
||||
}
|
||||
if notify {
|
||||
self.networkSpeedLimitedEventPipe.putNext(event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func retryRequest<T>(signal: Signal<T, MTRpcError>) -> Signal<T, NoError> {
|
||||
|
@ -526,7 +526,7 @@ if "".isEmpty {
|
||||
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaUploadedPhoto(flags: flags, file: file, stickers: stickers, ttlSeconds: ttlSeconds), text), reuploadInfo: nil, cacheReferenceKey: nil)))
|
||||
}
|
||||
|
||||
return network.request(Api.functions.messages.uploadMedia(peer: inputPeer, media: Api.InputMedia.inputMediaUploadedPhoto(flags: flags, file: file, stickers: stickers, ttlSeconds: ttlSeconds)))
|
||||
return network.request(Api.functions.messages.uploadMedia(flags: 0, businessConnectionId: nil, peer: inputPeer, media: Api.InputMedia.inputMediaUploadedPhoto(flags: flags, file: file, stickers: stickers, ttlSeconds: ttlSeconds)))
|
||||
|> mapError { _ -> PendingMessageUploadError in return .generic }
|
||||
|> mapToSignal { result -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
|
||||
switch result {
|
||||
@ -924,7 +924,7 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
|
||||
|> mapError { _ -> PendingMessageUploadError in }
|
||||
|> mapToSignal { inputPeer -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
|
||||
if let inputPeer = inputPeer {
|
||||
return network.request(Api.functions.messages.uploadMedia(peer: inputPeer, media: .inputMediaUploadedDocument(flags: flags, file: inputFile, thumb: thumbnailFile, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFileAttributes(file.attributes), stickers: stickers, ttlSeconds: ttlSeconds)))
|
||||
return network.request(Api.functions.messages.uploadMedia(flags: 0, businessConnectionId: nil, peer: inputPeer, media: .inputMediaUploadedDocument(flags: flags, file: inputFile, thumb: thumbnailFile, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFileAttributes(file.attributes), stickers: stickers, ttlSeconds: ttlSeconds)))
|
||||
|> mapError { _ -> PendingMessageUploadError in return .generic }
|
||||
|> mapToSignal { result -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
|
||||
switch result {
|
||||
|
@ -64,7 +64,7 @@ public func standaloneUploadedImage(postbox: Postbox, network: Network, peerId:
|
||||
|> mapError { _ -> StandaloneUploadMediaError in }
|
||||
|> mapToSignal { inputPeer -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> in
|
||||
if let inputPeer = inputPeer {
|
||||
return network.request(Api.functions.messages.uploadMedia(peer: inputPeer, media: Api.InputMedia.inputMediaUploadedPhoto(flags: 0, file: inputFile, stickers: nil, ttlSeconds: nil)))
|
||||
return network.request(Api.functions.messages.uploadMedia(flags: 0, businessConnectionId: nil, peer: inputPeer, media: Api.InputMedia.inputMediaUploadedPhoto(flags: 0, file: inputFile, stickers: nil, ttlSeconds: nil)))
|
||||
|> mapError { _ -> StandaloneUploadMediaError in return .generic }
|
||||
|> mapToSignal { media -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> in
|
||||
switch media {
|
||||
@ -158,7 +158,7 @@ public func standaloneUploadedFile(postbox: Postbox, network: Network, peerId: P
|
||||
if let _ = thumbnailFile {
|
||||
flags |= 1 << 2
|
||||
}
|
||||
return network.request(Api.functions.messages.uploadMedia(peer: inputPeer, media: Api.InputMedia.inputMediaUploadedDocument(flags: flags, file: inputFile, thumb: thumbnailFile, mimeType: mimeType, attributes: inputDocumentAttributesFromFileAttributes(attributes), stickers: nil, ttlSeconds: nil)))
|
||||
return network.request(Api.functions.messages.uploadMedia(flags: 0, businessConnectionId: nil, peer: inputPeer, media: Api.InputMedia.inputMediaUploadedDocument(flags: flags, file: inputFile, thumb: thumbnailFile, mimeType: mimeType, attributes: inputDocumentAttributesFromFileAttributes(attributes), stickers: nil, ttlSeconds: nil)))
|
||||
|> mapError { _ -> StandaloneUploadMediaError in return .generic }
|
||||
|> mapToSignal { media -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> in
|
||||
switch media {
|
||||
|
@ -6,33 +6,44 @@ import SwiftSignalKit
|
||||
extension PeerStatusSettings {
|
||||
init(apiSettings: Api.PeerSettings) {
|
||||
switch apiSettings {
|
||||
case let .peerSettings(flags, geoDistance, requestChatTitle, requestChatDate):
|
||||
var result = PeerStatusSettings.Flags()
|
||||
if (flags & (1 << 1)) != 0 {
|
||||
result.insert(.canAddContact)
|
||||
}
|
||||
if (flags & (1 << 0)) != 0 {
|
||||
result.insert(.canReport)
|
||||
}
|
||||
if (flags & (1 << 2)) != 0 {
|
||||
result.insert(.canBlock)
|
||||
}
|
||||
if (flags & (1 << 3)) != 0 {
|
||||
result.insert(.canShareContact)
|
||||
}
|
||||
if (flags & (1 << 4)) != 0 {
|
||||
result.insert(.addExceptionWhenAddingContact)
|
||||
}
|
||||
if (flags & (1 << 5)) != 0 {
|
||||
result.insert(.canReportIrrelevantGeoLocation)
|
||||
}
|
||||
if (flags & (1 << 7)) != 0 {
|
||||
result.insert(.autoArchived)
|
||||
}
|
||||
if (flags & (1 << 8)) != 0 {
|
||||
result.insert(.suggestAddMembers)
|
||||
}
|
||||
self = PeerStatusSettings(flags: result, geoDistance: geoDistance, requestChatTitle: requestChatTitle, requestChatDate: requestChatDate, requestChatIsChannel: (flags & (1 << 10)) != 0)
|
||||
case let .peerSettings(flags, geoDistance, requestChatTitle, requestChatDate, businessBotId, businessBotManageUrl):
|
||||
var result = PeerStatusSettings.Flags()
|
||||
if (flags & (1 << 1)) != 0 {
|
||||
result.insert(.canAddContact)
|
||||
}
|
||||
if (flags & (1 << 0)) != 0 {
|
||||
result.insert(.canReport)
|
||||
}
|
||||
if (flags & (1 << 2)) != 0 {
|
||||
result.insert(.canBlock)
|
||||
}
|
||||
if (flags & (1 << 3)) != 0 {
|
||||
result.insert(.canShareContact)
|
||||
}
|
||||
if (flags & (1 << 4)) != 0 {
|
||||
result.insert(.addExceptionWhenAddingContact)
|
||||
}
|
||||
if (flags & (1 << 5)) != 0 {
|
||||
result.insert(.canReportIrrelevantGeoLocation)
|
||||
}
|
||||
if (flags & (1 << 7)) != 0 {
|
||||
result.insert(.autoArchived)
|
||||
}
|
||||
if (flags & (1 << 8)) != 0 {
|
||||
result.insert(.suggestAddMembers)
|
||||
}
|
||||
|
||||
var managingBot: ManagingBot?
|
||||
if let businessBotId {
|
||||
managingBot = ManagingBot(
|
||||
id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(businessBotId)),
|
||||
manageUrl: businessBotManageUrl,
|
||||
isPaused: (flags & (1 << 11)) != 0,
|
||||
canReply: (flags & (1 << 12)) != 0
|
||||
)
|
||||
}
|
||||
|
||||
self = PeerStatusSettings(flags: result, geoDistance: geoDistance, requestChatTitle: requestChatTitle, requestChatDate: requestChatDate, requestChatIsChannel: (flags & (1 << 10)) != 0, managingBot: managingBot)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes
|
||||
var updatedTimestamp: Int32?
|
||||
if let apiMessage = apiMessage {
|
||||
switch apiMessage {
|
||||
case let .message(_, _, _, _, _, _, _, _, _, date, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
case let .message(_, _, _, _, _, _, _, _, _, _, _, date, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
updatedTimestamp = date
|
||||
case .messageEmpty:
|
||||
break
|
||||
|
@ -360,11 +360,11 @@ final class ChatHistoryPreloadManager {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
#if DEBUG
|
||||
/*#if DEBUG
|
||||
if "".isEmpty {
|
||||
return
|
||||
}
|
||||
#endif
|
||||
#endif*/
|
||||
|
||||
var indices: [(ChatHistoryPreloadIndex, Bool, Bool)] = []
|
||||
for item in loadItems {
|
||||
|
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