Merge branch 'master' into webrtc-m122

This commit is contained in:
Isaac 2024-03-14 15:59:49 +04:00
commit e010513747
214 changed files with 14943 additions and 2925 deletions

View File

@ -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()

View File

@ -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.";

View File

@ -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)
}
}
}

View File

@ -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

View File

@ -35,3 +35,11 @@ public final class GalleryControllerActionInteraction {
self.updateCanReadHistory = updateCanReadHistory
}
}
public protocol StickerPackScreen {
}
public protocol StickerPickerInput {
}

View File

@ -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(

View File

@ -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
}
}

View File

@ -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()
}
}
}

View File

@ -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()

View File

@ -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)

View File

@ -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
}

View File

@ -107,6 +107,7 @@ swift_library(
"//submodules/Camera",
"//submodules/TelegramUI/Components/DustEffect",
"//submodules/TelegramUI/Components/DynamicCornerRadiusView",
"//submodules/TelegramUI/Components/StickerPickerScreen",
],
visibility = [
"//visibility:public",

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -31,6 +31,7 @@ swift_library(
"//submodules/TelegramCore",
"//submodules/ComponentFlow",
"//submodules/TelegramUI/Components/TabSelectorComponent",
"//submodules/Components/ComponentDisplayAdapters",
],
visibility = [
"//visibility:public",

View File

@ -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)

View File

@ -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

View File

@ -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>()

View File

@ -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
}
}

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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];
}

View File

@ -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;

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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)

View File

@ -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;

View File

@ -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)
{

View File

@ -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 {

View File

@ -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 {

View File

@ -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.

Binary file not shown.

View File

@ -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]
))
)
)

View File

@ -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

View File

@ -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

View File

@ -1424,6 +1424,7 @@ public class PremiumDemoScreen: ViewControllerComponentContainer {
case businessQuickReplies
case businessAwayMessage
case businessChatBots
case businessIntro
}
public enum Source: Equatable {

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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",

View File

@ -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)

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -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

View File

@ -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

View 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))
}
}

View File

@ -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),

View File

@ -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

View 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)
}
}

View 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)
}
}
}

View 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
}

View File

@ -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)

View 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()
})
}

View File

@ -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",

View File

@ -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

View File

@ -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

View File

@ -28,6 +28,8 @@ swift_library(
"//submodules/ShimmerEffect:ShimmerEffect",
"//submodules/ContextUI:ContextUI",
"//submodules/SolidRoundedButtonNode:SolidRoundedButtonNode",
"//submodules/ReactionSelectionNode",
"//submodules/TelegramUI/Components/EntityKeyboard",
],
visibility = [
"//visibility:public",

View File

@ -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)
}
}
}

View File

@ -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:

View File

@ -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
}
}
}

View File

@ -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
}
}
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
}
}
}
}

View File

@ -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
}
}
}
}

View File

@ -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
}
}
}

View File

@ -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
}
}
}
}

View File

@ -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])

View File

@ -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()

View File

@ -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
}
}
}

View File

@ -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
}
}
}

View File

@ -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
}
}
}
}

View File

@ -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
}

View File

@ -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")

View File

@ -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) })

View File

@ -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 {

View File

@ -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
}

View File

@ -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
}

View File

@ -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))

View File

@ -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()

View File

@ -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> {

View File

@ -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 {

View File

@ -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 {

View File

@ -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)
}
}
}

View File

@ -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

View File

@ -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