Various improvements

This commit is contained in:
Ilya Laktyushin 2022-05-10 11:55:40 +04:00
parent 6b7ed2e6ac
commit ff7cc72a71
46 changed files with 3008 additions and 1060 deletions

View File

@ -337,6 +337,7 @@ swift_library(
"//submodules/OverlayStatusController:OverlayStatusControllerResources",
"//submodules/PasswordSetupUI:PasswordSetupUIResources",
"//submodules/PasswordSetupUI:PasswordSetupUIAssets",
"//submodules/PremiumUI:PremiumUIResources",
"//submodules/TelegramUI:TelegramUIResources",
"//submodules/TelegramUI:TelegramUIAssets",
":GeneratedPresentationStrings/Resources/PresentationStrings.data",

View File

@ -7548,7 +7548,7 @@ Sorry for the inconvenience.";
"Chat.MultipleTypingPair" = "%@ and %@";
"Chat.MultipleTypingMore" = "%@ and %@ others";
"DialogList.ExtendedPinLimitError" = "Sorry, you can pin more than **%1$@** chats to the top. Unpin some of the currently pinned ones or subscribe to **Telegram Premium** to double the limit to **%2$@** chats.";
"DialogList.ExtendedPinLimitError" = "Sorry, you can't pin more than **%1$@** chats to the top. Unpin some of the currently pinned ones or subscribe to **Telegram Premium** to double the limit to **%2$@** chats.";
"Group.Username.RemoveExistingUsernamesTitle" = "Too Many Public Links";
"Group.Username.RemoveExistingUsernamesOrExtendInfo" = "You have reserved too many public links. Try revoking a link from an older group or channel, or upgrade to **Telegram Premium** to double the limit to **%@** public links.";
@ -7564,3 +7564,35 @@ Sorry for the inconvenience.";
"Premium.MaxFoldersCountText" = "You have reached the limit of **%@** folders. You can double the limit to **%@** folders by subscribing to **Telegram Premium**.";
"Premium.MaxChatsInFolderCountText" = "Sorry, you can't add more than **%@** chats to a folder. You can increase this limit to **%@** by upgrading to **Telegram Premium**.";
"Premium.MaxFileSizeText" = "Double this limit to %@ per file by subscribing to **Telegram Premium**.";
"Premium.MaxPinsText" = "Sorry, you can't pin more than **%1$@** chats to the top. Unpin some of the currently pinned ones or subscribe to **Telegram Premium** to double the limit to **%2$@** chats.";
"Premium.Title" = "Telegram Premium";
"Premium.Description" = "Go **beyond the limits**, get **exclusive features** and support us by subscribing to **Telegram Premium**.";
"Premium.DoubledLimits" = "Doubled Limits";
"Premium.DoubledLimitsInfo" = "Up to 1000 channels, 20 folders, 10 pins, 20 public links, 4 accounts and more.";
"Premium.UploadSize" = "4 GB Upload Size";
"Premium.UploadSizeInfo" = "Increased upload size from 2 GB to 4 GB per document, unlimited storage overall.";
"Premium.FasterSpeed" = "Faster Download Speed";
"Premium.FasterSpeedInfo" = "No more limits on the speed with which media and documents are downloaded.";
"Premium.NoAds" = "No Ads";
"Premium.NoAdsInfo" = "No more ads in public channels where Telegram sometimes shows ads.";
"Premium.Reactions" = "Unique Reactions";
"Premium.ReactionsInfo" = "Additional animated reactions on messages, available only to the Premium subscribers.";
"Premium.Stickers" = "Premium Stickers";
"Premium.StickersInfo" = "Exclusive enlarged stickers featuring additional effects, updated monthly.";
"Premium.Badge" = "Profile Badge";
"Premium.BadgeInfo" = "A badge next to your name showing that you are helping support Telegram.";
"Premium.Avatar" = "Animated Profile Pictures";
"Premium.AvatarInfo" = "Video avatars animated in chat lists and chats to allow for additional self-expression.";
"Premium.HelpUs" = "Help us maintain Premium Features while keeping Telegram free for everyone.";
"Premium.SubscribeFor" = "Subscribe for %@ per month";

View File

@ -11,6 +11,7 @@ import OverlayStatusController
import AlertUI
import PresentationDataUtils
import UndoUI
import PremiumUI
func archiveContextMenuItems(context: AccountContext, groupId: PeerGroupId, chatListController: ChatListControllerImpl?) -> Signal<[ContextMenuItem], NoError> {
let presentationData = context.sharedContext.currentPresentationData.with({ $0 })
@ -318,29 +319,16 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
case .done:
f(.default)
case .limitExceeded:
var subItems: [ContextMenuItem] = []
f(.default)
subItems.append(.action(ContextMenuActionItem(text: strings.Common_Back, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.contextMenu.primaryColor)
}, action: { c, _ in
c.popItems()
})))
subItems.append(.separator)
subItems.append(.action(ContextMenuActionItem(text: strings.DialogList_ExtendedPinLimitError("5", "10").string, textLayout: .multiline, textFont: .small, parseMarkdown: true, icon: { _ in
return nil
}, action: nil as ((ContextControllerProtocol, @escaping (ContextMenuActionResult) -> Void) -> Void)?)))
subItems.append(.action(ContextMenuActionItem(text: strings.Premium_IncreaseLimit, icon: { _ in
return nil
}, action: { _, f in
f(.default)
})))
c.pushItems(items: .single(ContextController.Items(content: .list(subItems))))
let limitScreen = PremiumLimitScreen(context: context, subject: .pins, action: {
let premiumScreen = PremiumIntroScreen(context: context, action: {
})
chatListController?.push(premiumScreen)
})
chatListController?.push(limitScreen)
}
})
})))
}

View File

@ -866,6 +866,12 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
}
}
self.chatListDisplayNode.containerNode.push = { [weak self] c in
if let strongSelf = self {
strongSelf.push(c)
}
}
self.chatListDisplayNode.containerNode.toggleArchivedFolderHiddenByDefault = { [weak self] in
guard let strongSelf = self else {
return

View File

@ -468,6 +468,7 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
previousItemNode.listNode.activateSearch = nil
previousItemNode.listNode.presentAlert = nil
previousItemNode.listNode.present = nil
previousItemNode.listNode.push = nil
previousItemNode.listNode.toggleArchivedFolderHiddenByDefault = nil
previousItemNode.listNode.hidePsa = nil
previousItemNode.listNode.deletePeerChat = nil
@ -494,6 +495,9 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
itemNode.listNode.present = { [weak self] c in
self?.present?(c)
}
itemNode.listNode.push = { [weak self] c in
self?.push?(c)
}
itemNode.listNode.toggleArchivedFolderHiddenByDefault = { [weak self] in
self?.toggleArchivedFolderHiddenByDefault?()
}
@ -557,6 +561,7 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
var activateSearch: (() -> Void)?
var presentAlert: ((String) -> Void)?
var present: ((ViewController) -> Void)?
var push: ((ViewController) -> Void)?
var toggleArchivedFolderHiddenByDefault: (() -> Void)?
var hidePsa: ((EnginePeer.Id) -> Void)?
var deletePeerChat: ((EnginePeer.Id, Bool) -> Void)?

View File

@ -619,6 +619,7 @@ public final class ChatListNode: ListView {
public var updatePeerGrouping: ((EnginePeer.Id, Bool) -> Void)?
public var presentAlert: ((String) -> Void)?
public var present: ((ViewController) -> Void)?
public var push: ((ViewController) -> Void)?
public var toggleArchivedFolderHiddenByDefault: (() -> Void)?
public var hidePsa: ((EnginePeer.Id) -> Void)?
public var activateChatPreview: ((ChatListItem, ASDisplayNode, ContextGesture?) -> Void)?
@ -842,15 +843,17 @@ public final class ChatListNode: ListView {
case .done:
break
case .limitExceeded:
let controller = LimitScreen(context: strongSelf.context, subject: .pins)
strongSelf.present?(controller)
// let text: String
// if chatListFilter != nil {
// text = strongSelf.currentState.presentationData.strings.DialogList_UnknownPinLimitError
// } else {
// text = strongSelf.currentState.presentationData.strings.DialogList_PinLimitError("\(maxCount)").string
// }
// strongSelf.presentAlert?(text)
var dismissImpl: (() -> Void)?
let controller = PremiumLimitScreen(context: context, subject: .pins, action: { [weak self] in
let premiumScreen = PremiumIntroScreen(context: context, action: {
dismissImpl?()
})
self?.push?(premiumScreen)
})
dismissImpl = { [weak controller] in
controller?.dismiss()
}
strongSelf.push?(controller)
}
}
})

View File

@ -0,0 +1,20 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "SheetComponent",
module_name = "SheetComponent",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/Display:Display",
"//submodules/ComponentFlow:ComponentFlow",
"//submodules/Components/ViewControllerComponent:ViewControllerComponent",
],
visibility = [
"//visibility:public",
],
)

View File

@ -0,0 +1,174 @@
import Foundation
import UIKit
import Display
import ComponentFlow
import ViewControllerComponent
public final class SheetComponentEnvironment: Equatable {
public let isDisplaying: Bool
public let dismiss: () -> Void
public init(isDisplaying: Bool, dismiss: @escaping () -> Void) {
self.isDisplaying = isDisplaying
self.dismiss = dismiss
}
public static func ==(lhs: SheetComponentEnvironment, rhs: SheetComponentEnvironment) -> Bool {
if lhs.isDisplaying != rhs.isDisplaying {
return false
}
return true
}
}
public final class SheetComponent<ChildEnvironmentType: Equatable>: Component {
public typealias EnvironmentType = (ChildEnvironmentType, SheetComponentEnvironment)
public let content: AnyComponent<ChildEnvironmentType>
public let backgroundColor: UIColor
public let animateOut: ActionSlot<Action<()>>
public init(content: AnyComponent<ChildEnvironmentType>, backgroundColor: UIColor, animateOut: ActionSlot<Action<()>>) {
self.content = content
self.backgroundColor = backgroundColor
self.animateOut = animateOut
}
public static func ==(lhs: SheetComponent, rhs: SheetComponent) -> Bool {
if lhs.content != rhs.content {
return false
}
if lhs.backgroundColor != rhs.backgroundColor {
return false
}
if lhs.animateOut != rhs.animateOut {
return false
}
return true
}
public final class View: UIView, UIScrollViewDelegate {
private let dimView: UIView
private let scrollView: UIScrollView
private let backgroundView: UIView
private let contentView: ComponentHostView<ChildEnvironmentType>
private var previousIsDisplaying: Bool = false
private var dismiss: (() -> Void)?
override init(frame: CGRect) {
self.dimView = UIView()
self.dimView.backgroundColor = UIColor(white: 0.0, alpha: 0.4)
self.scrollView = UIScrollView()
self.scrollView.delaysContentTouches = false
if #available(iOSApplicationExtension 11.0, iOS 11.0, *) {
self.scrollView.contentInsetAdjustmentBehavior = .never
}
self.scrollView.showsVerticalScrollIndicator = false
self.scrollView.showsHorizontalScrollIndicator = false
self.scrollView.alwaysBounceVertical = true
self.backgroundView = UIView()
self.backgroundView.layer.cornerRadius = 10.0
self.backgroundView.layer.masksToBounds = true
self.contentView = ComponentHostView<ChildEnvironmentType>()
super.init(frame: frame)
self.addSubview(self.dimView)
self.scrollView.addSubview(self.backgroundView)
self.scrollView.addSubview(self.contentView)
self.addSubview(self.scrollView)
self.dimView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimViewTapGesture(_:))))
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc private func dimViewTapGesture(_ recognizer: UITapGestureRecognizer) {
if case .ended = recognizer.state {
self.dismiss?()
}
}
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
}
public func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
}
public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
}
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if !self.backgroundView.bounds.contains(self.convert(point, to: self.backgroundView)) {
return self.dimView
}
return super.hitTest(point, with: event)
}
private func animateOut(completion: @escaping () -> Void) {
self.dimView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false)
self.scrollView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: self.bounds.height - self.scrollView.contentInset.top), duration: 0.25, timingFunction: CAMediaTimingFunctionName.easeIn.rawValue, removeOnCompletion: false, additive: true, completion: { _ in
completion()
})
}
func update(component: SheetComponent<ChildEnvironmentType>, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
component.animateOut.connect { [weak self] completion in
guard let strongSelf = self else {
return
}
strongSelf.animateOut {
completion(Void())
}
}
if self.backgroundView.backgroundColor != component.backgroundColor {
self.backgroundView.backgroundColor = component.backgroundColor
}
transition.setFrame(view: self.dimView, frame: CGRect(origin: CGPoint(), size: availableSize), completion: nil)
let contentSize = self.contentView.update(
transition: transition,
component: component.content,
environment: {
environment[ChildEnvironmentType.self]
},
containerSize: CGSize(width: availableSize.width, height: .greatestFiniteMagnitude)
)
transition.setFrame(view: self.contentView, frame: CGRect(origin: CGPoint(), size: contentSize), completion: nil)
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: CGSize(width: contentSize.width, height: contentSize.height + 1000.0)), completion: nil)
transition.setFrame(view: self.scrollView, frame: CGRect(origin: CGPoint(), size: availableSize), completion: nil)
self.scrollView.contentSize = contentSize
self.scrollView.contentInset = UIEdgeInsets(top: max(0.0, availableSize.height - contentSize.height), left: 0.0, bottom: 0.0, right: 0.0)
if environment[SheetComponentEnvironment.self].value.isDisplaying, !self.previousIsDisplaying, let _ = transition.userData(ViewControllerComponentContainer.AnimateInTransition.self) {
self.dimView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
self.scrollView.layer.animatePosition(from: CGPoint(x: 0.0, y: availableSize.height - self.scrollView.contentInset.top), to: CGPoint(), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, additive: true, completion: nil)
}
self.previousIsDisplaying = environment[SheetComponentEnvironment.self].value.isDisplaying
self.dismiss = environment[SheetComponentEnvironment.self].value.dismiss
return availableSize
}
}
public func makeView() -> View {
return View(frame: CGRect())
}
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
}
}

View File

@ -149,7 +149,7 @@ final class GameControllerNode: ViewControllerTracingNode {
self.present(ShareController(context: self.context, subject: .fromExternal({ [weak self] peerIds, text, account, _ in
if let strongSelf = self, let message = strongSelf.message {
let signals = peerIds.map { TelegramEngine(account: account).messages.forwardGameWithScore(messageId: message.id, to: $0, as: nil) }
return .single(.preparing)
return .single(.preparing(false))
|> then(
combineLatest(signals)
|> mapToSignal { _ -> Signal<ShareControllerExternalStatus, NoError> in return .complete() }

View File

@ -14,6 +14,7 @@ swift_library(
"//submodules/TelegramPresentationData:TelegramPresentationData",
"//submodules/ComponentFlow:ComponentFlow",
"//submodules/Components/ViewControllerComponent:ViewControllerComponent",
"//submodules/Components/SheetComponent:SheetComponent",
"//submodules/Components/LottieAnimationComponent:LottieAnimationComponent",
"//submodules/Components/AnimatedStickerComponent:AnimatedStickerComponent",
"//submodules/Components/BundleIconComponent:BundleIconComponent",

View File

@ -4,180 +4,12 @@ import Display
import ComponentFlow
import ViewControllerComponent
import AccountContext
import SheetComponent
import AnimatedStickerComponent
import SolidRoundedButtonComponent
import MultilineTextComponent
import PresentationDataUtils
public final class SheetComponentEnvironment: Equatable {
public let isDisplaying: Bool
public let dismiss: () -> Void
public init(isDisplaying: Bool, dismiss: @escaping () -> Void) {
self.isDisplaying = isDisplaying
self.dismiss = dismiss
}
public static func ==(lhs: SheetComponentEnvironment, rhs: SheetComponentEnvironment) -> Bool {
if lhs.isDisplaying != rhs.isDisplaying {
return false
}
return true
}
}
public final class SheetComponent<ChildEnvironmentType: Equatable>: Component {
public typealias EnvironmentType = (ChildEnvironmentType, SheetComponentEnvironment)
public let content: AnyComponent<ChildEnvironmentType>
public let backgroundColor: UIColor
public let animateOut: ActionSlot<Action<()>>
public init(content: AnyComponent<ChildEnvironmentType>, backgroundColor: UIColor, animateOut: ActionSlot<Action<()>>) {
self.content = content
self.backgroundColor = backgroundColor
self.animateOut = animateOut
}
public static func ==(lhs: SheetComponent, rhs: SheetComponent) -> Bool {
if lhs.content != rhs.content {
return false
}
if lhs.backgroundColor != rhs.backgroundColor {
return false
}
if lhs.animateOut != rhs.animateOut {
return false
}
return true
}
public final class View: UIView, UIScrollViewDelegate {
private let dimView: UIView
private let scrollView: UIScrollView
private let backgroundView: UIView
private let contentView: ComponentHostView<ChildEnvironmentType>
private var previousIsDisplaying: Bool = false
private var dismiss: (() -> Void)?
override init(frame: CGRect) {
self.dimView = UIView()
self.dimView.backgroundColor = UIColor(white: 0.0, alpha: 0.4)
self.scrollView = UIScrollView()
self.scrollView.delaysContentTouches = false
if #available(iOSApplicationExtension 11.0, iOS 11.0, *) {
self.scrollView.contentInsetAdjustmentBehavior = .never
}
self.scrollView.showsVerticalScrollIndicator = false
self.scrollView.showsHorizontalScrollIndicator = false
self.scrollView.alwaysBounceVertical = true
self.backgroundView = UIView()
self.backgroundView.layer.cornerRadius = 10.0
self.backgroundView.layer.masksToBounds = true
self.contentView = ComponentHostView<ChildEnvironmentType>()
super.init(frame: frame)
self.addSubview(self.dimView)
self.scrollView.addSubview(self.backgroundView)
self.scrollView.addSubview(self.contentView)
self.addSubview(self.scrollView)
self.dimView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimViewTapGesture(_:))))
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc private func dimViewTapGesture(_ recognizer: UITapGestureRecognizer) {
if case .ended = recognizer.state {
self.dismiss?()
}
}
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
}
public func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
}
public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
}
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if !self.backgroundView.bounds.contains(self.convert(point, to: self.backgroundView)) {
return self.dimView
}
return super.hitTest(point, with: event)
}
private func animateOut(completion: @escaping () -> Void) {
self.dimView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false)
self.scrollView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: self.bounds.height - self.scrollView.contentInset.top), duration: 0.25, timingFunction: CAMediaTimingFunctionName.easeIn.rawValue, removeOnCompletion: false, additive: true, completion: { _ in
completion()
})
}
func update(component: SheetComponent<ChildEnvironmentType>, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
component.animateOut.connect { [weak self] completion in
guard let strongSelf = self else {
return
}
strongSelf.animateOut {
completion(Void())
}
}
if self.backgroundView.backgroundColor != component.backgroundColor {
self.backgroundView.backgroundColor = component.backgroundColor
}
transition.setFrame(view: self.dimView, frame: CGRect(origin: CGPoint(), size: availableSize), completion: nil)
let contentSize = self.contentView.update(
transition: transition,
component: component.content,
environment: {
environment[ChildEnvironmentType.self]
},
containerSize: CGSize(width: availableSize.width, height: .greatestFiniteMagnitude)
)
transition.setFrame(view: self.contentView, frame: CGRect(origin: CGPoint(), size: contentSize), completion: nil)
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: CGSize(width: contentSize.width, height: contentSize.height + 1000.0)), completion: nil)
transition.setFrame(view: self.scrollView, frame: CGRect(origin: CGPoint(), size: availableSize), completion: nil)
self.scrollView.contentSize = contentSize
self.scrollView.contentInset = UIEdgeInsets(top: max(0.0, availableSize.height - contentSize.height), left: 0.0, bottom: 0.0, right: 0.0)
if environment[SheetComponentEnvironment.self].value.isDisplaying, !self.previousIsDisplaying, let _ = transition.userData(ViewControllerComponentContainer.AnimateInTransition.self) {
self.dimView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
self.scrollView.layer.animatePosition(from: CGPoint(x: 0.0, y: availableSize.height - self.scrollView.contentInset.top), to: CGPoint(), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, additive: true, completion: nil)
}
self.previousIsDisplaying = environment[SheetComponentEnvironment.self].value.isDisplaying
self.dismiss = environment[SheetComponentEnvironment.self].value.dismiss
return availableSize
}
}
public func makeView() -> View {
return View(frame: CGRect())
}
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
}
}
private final class AddPaymentMethodSheetContent: CombinedComponent {
typealias EnvironmentType = ViewControllerComponentContainer.Environment

View File

@ -80,16 +80,18 @@ final class IncreaseLimitFooterItemNode: ItemListControllerFooterItemNode {
let backgroundColor = self.item.theme.list.itemCheckColors.fillColor
let backgroundColors: [UIColor]
let icon: UIImage?
if self.item.colorful {
backgroundColors = [UIColor(rgb: 0x407af0), UIColor(rgb: 0x9551e8), UIColor(rgb: 0xbf499a), UIColor(rgb: 0xf17b30)]
self.buttonNode.icon = UIImage(bundleImageName: "Premium/X2")
icon = UIImage(bundleImageName: "Premium/X2")
} else {
backgroundColors = []
self.buttonNode.icon = nil
icon = nil
}
self.buttonNode.updateTheme(SolidRoundedButtonTheme(backgroundColor: backgroundColor, backgroundColors: backgroundColors, foregroundColor: self.item.theme.list.itemCheckColors.foregroundColor), animated: true)
self.buttonNode.title = self.item.title
self.buttonNode.icon = icon
self.buttonNode.pressed = { [weak self] in
self?.item.action()

View File

@ -1,5 +1,13 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
filegroup(
name = "PremiumUIResources",
srcs = glob([
"Resources/**/*",
], exclude = ["Resources/**/.*"]),
visibility = ["//visibility:public"],
)
swift_library(
name = "PremiumUI",
module_name = "PremiumUI",
@ -28,8 +36,10 @@ swift_library(
"//submodules/ComponentFlow:ComponentFlow",
"//submodules/Components/ViewControllerComponent:ViewControllerComponent",
"//submodules/Components/MultilineTextComponent:MultilineTextComponent",
"//submodules/Components/SheetComponent:SheetComponent",
"//submodules/Components/BundleIconComponent:BundleIconComponent",
"//submodules/Components/SolidRoundedButtonComponent:SolidRoundedButtonComponent",
"//submodules/Components/Forms/PrefixSectionGroupComponent:PrefixSectionGroupComponent",
],
visibility = [
"//visibility:public",

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1002 B

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View File

@ -1,614 +0,0 @@
import Foundation
import UIKit
import Display
import AsyncDisplayKit
import Postbox
import TelegramCore
import SwiftSignalKit
import AccountContext
import TelegramPresentationData
import PresentationDataUtils
import ComponentFlow
import ViewControllerComponent
import MultilineTextComponent
import BundleIconComponent
import SolidRoundedButtonComponent
import Markdown
private final class LimitScreenComponent: CombinedComponent {
typealias EnvironmentType = ViewControllerComponentContainer.Environment
let context: AccountContext
let subject: LimitScreen.Subject
let proceed: () -> Void
init(context: AccountContext, subject: LimitScreen.Subject, proceed: @escaping () -> Void) {
self.context = context
self.subject = subject
self.proceed = proceed
}
static func ==(lhs: LimitScreenComponent, rhs: LimitScreenComponent) -> Bool {
if lhs.context !== rhs.context {
return false
}
if lhs.subject != rhs.subject {
return false
}
return true
}
final class State: ComponentState {
private let context: AccountContext
private var disposable: Disposable?
var limits: EngineConfiguration.UserLimits
var premiumLimits: EngineConfiguration.UserLimits
init(context: AccountContext, subject: LimitScreen.Subject) {
self.context = context
self.limits = EngineConfiguration.UserLimits.defaultValue
self.premiumLimits = EngineConfiguration.UserLimits.defaultValue
super.init()
self.disposable = (context.engine.data.get(
TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: false),
TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: true)
) |> deliverOnMainQueue).start(next: { [weak self] result in
if let strongSelf = self {
let (limits, premiumLimits) = result
strongSelf.limits = limits
strongSelf.premiumLimits = premiumLimits
strongSelf.updated(transition: .immediate)
}
})
}
deinit {
self.disposable?.dispose()
}
}
func makeState() -> State {
return State(context: self.context, subject: self.subject)
}
static var body: Body {
let badgeBackground = Child(RoundedRectangle.self)
let badgeIcon = Child(BundleIconComponent.self)
let badgeText = Child(MultilineTextComponent.self)
let title = Child(MultilineTextComponent.self)
let text = Child(MultilineTextComponent.self)
let button = Child(SolidRoundedButtonComponent.self)
let cancel = Child(Button.self)
return { context in
let environment = context.environment[ViewControllerComponentContainer.Environment.self].value
let component = context.component
let theme = environment.theme
let strings = environment.strings
let state = context.state
let subject = component.subject
let topInset: CGFloat = 34.0 + 38.0
let sideInset: CGFloat = 16.0 + environment.safeInsets.left
let textSideInset: CGFloat = 24.0 + environment.safeInsets.left
let iconName: String
let badgeString: String
let string: String
switch subject {
case .folders:
let limit = state.limits.maxFoldersCount
let premiumLimit = state.premiumLimits.maxFoldersCount
iconName = "Premium/Folder"
badgeString = "\(limit)"
string = strings.Premium_MaxFoldersCountText("\(limit)", "\(premiumLimit)").string
case .chatsInFolder:
let limit = state.limits.maxFolderChatsCount
let premiumLimit = state.premiumLimits.maxFolderChatsCount
iconName = "Premium/Chat"
badgeString = "\(limit)"
string = strings.Premium_MaxChatsInFolderCountText("\(limit)", "\(premiumLimit)").string
case .pins:
let limit = state.limits.maxPinnedChatCount
let premiumLimit = state.premiumLimits.maxPinnedChatCount
iconName = "Premium/Pin"
badgeString = "\(limit)"
string = strings.DialogList_ExtendedPinLimitError("\(limit)", "\(premiumLimit)").string
case .files:
let limit = 2048 * 1024 * 1024 //state.limits.maxPinnedChatCount
let premiumLimit = 4096 * 1024 * 1024 //state.premiumLimits.maxPinnedChatCount
iconName = "Premium/File"
badgeString = dataSizeString(limit, formatting: DataSizeStringFormatting(strings: environment.strings, decimalSeparator: environment.dateTimeFormat.decimalSeparator))
string = strings.Premium_MaxFileSizeText(dataSizeString(premiumLimit, formatting: DataSizeStringFormatting(strings: environment.strings, decimalSeparator: environment.dateTimeFormat.decimalSeparator))).string
}
let badgeIcon = badgeIcon.update(
component: BundleIconComponent(
name: iconName,
tintColor: .white
),
availableSize: context.availableSize,
transition: .immediate
)
let badgeText = badgeText.update(
component: MultilineTextComponent(
text: NSAttributedString(
string: badgeString,
font: Font.with(size: 24.0, design: .round, weight: .semibold, traits: []),
textColor: .white,
paragraphAlignment: .center
),
horizontalAlignment: .center,
maximumNumberOfLines: 1
),
availableSize: context.availableSize,
transition: .immediate
)
let badgeBackground = badgeBackground.update(
component: RoundedRectangle(
colors: [UIColor(rgb: 0xa34fcf), UIColor(rgb: 0xc8498a), UIColor(rgb: 0xff7a23)],
cornerRadius: 23.5
),
availableSize: CGSize(width: badgeText.size.width + 67.0, height: 47.0),
transition: .immediate
)
let title = title.update(
component: MultilineTextComponent(
text: .plain(NSAttributedString(
string: strings.Premium_LimitReached,
font: Font.semibold(17.0),
textColor: theme.actionSheet.primaryTextColor,
paragraphAlignment: .center
)),
horizontalAlignment: .center,
maximumNumberOfLines: 1
),
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: CGFloat.greatestFiniteMagnitude),
transition: .immediate
)
let textFont = Font.regular(16.0)
let boldTextFont = Font.semibold(16.0)
let textColor = theme.actionSheet.primaryTextColor
let attributedText = parseMarkdownIntoAttributedString(string, attributes: 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: .plain(attributedText),
horizontalAlignment: .center,
maximumNumberOfLines: 0,
lineSpacing: 0.2
),
availableSize: CGSize(width: context.availableSize.width - textSideInset * 2.0, height: context.availableSize.height),
transition: .immediate
)
let button = button.update(
component: SolidRoundedButtonComponent(
title: strings.Premium_IncreaseLimit,
theme: SolidRoundedButtonComponent.Theme(
backgroundColor: .black,
backgroundColors: [UIColor(rgb: 0x407af0), UIColor(rgb: 0x9551e8), UIColor(rgb: 0xbf499a), UIColor(rgb: 0xf17b30)],
foregroundColor: .white
),
font: .bold,
fontSize: 17.0,
height: 50.0,
cornerRadius: 10.0,
gloss: false,
iconName: "Premium/X2",
iconPosition: .right,
action: { [weak component] in
guard let component = component else {
return
}
component.proceed()
}
),
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0),
transition: context.transition
)
let cancel = cancel.update(component: Button(
content: AnyComponent(Text(text: strings.Common_Cancel, font: Font.regular(17.0), color: theme.actionSheet.controlAccentColor)),
action: {
}
),
availableSize: context.availableSize,
transition: context.transition)
let width = context.availableSize.width
let badgeFrame = CGRect(origin: CGPoint(x: floor((context.availableSize.width - badgeBackground.size.width) / 2.0), y: 33.0), size: badgeBackground.size)
context.add(badgeBackground
.position(CGPoint(x: badgeFrame.midX, y: badgeFrame.midY))
)
let badgeIconFrame = CGRect(origin: CGPoint(x: badgeFrame.minX + 18.0, y: badgeFrame.minY + floor((badgeFrame.height - badgeIcon.size.height) / 2.0)), size: badgeIcon.size)
context.add(badgeIcon
.position(CGPoint(x: badgeIconFrame.midX, y: badgeIconFrame.midY))
)
let badgeTextFrame = CGRect(origin: CGPoint(x: badgeFrame.maxX - badgeText.size.width - 15.0, y: badgeFrame.minY + floor((badgeFrame.height - badgeText.size.height) / 2.0)), size: badgeText.size)
context.add(badgeText
.position(CGPoint(x: badgeTextFrame.midX, y: badgeTextFrame.midY))
)
context.add(title
.position(CGPoint(x: width / 2.0, y: topInset + 39.0))
)
context.add(text
.position(CGPoint(x: width / 2.0, y: topInset + 101.0))
)
let buttonFrame = CGRect(origin: CGPoint(x: sideInset, y: topInset + 76.0 + text.size.height + 27.0), size: button.size)
context.add(button
.position(CGPoint(x: buttonFrame.midX, y: buttonFrame.midY))
)
context.add(cancel
.position(CGPoint(x: width / 2.0, y: topInset + 76.0 + text.size.height + 20.0 + button.size.height + 40.0))
)
let contentSize = CGSize(width: context.availableSize.width, height: topInset + 76.0 + text.size.height + 20.0 + button.size.height + 40.0 + 33.0 + environment.safeInsets.bottom)
return contentSize
}
}
}
public class LimitScreen: ViewController {
final class Node: ViewControllerTracingNode, UIGestureRecognizerDelegate {
private var presentationData: PresentationData
private weak var controller: LimitScreen?
private let component: AnyComponent<ViewControllerComponentContainer.Environment>
private let theme: PresentationTheme?
let dim: ASDisplayNode
let wrappingView: UIView
let containerView: UIView
let hostView: ComponentHostView<ViewControllerComponentContainer.Environment>
private var panGestureRecognizer: UIPanGestureRecognizer?
private var currentIsVisible: Bool = false
private var currentLayout: (layout: ContainerViewLayout, navigationHeight: CGFloat)?
fileprivate var temporaryDismiss = false
init(context: AccountContext, controller: LimitScreen, component: AnyComponent<ViewControllerComponentContainer.Environment>, theme: PresentationTheme?) {
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
self.controller = controller
self.component = component
self.theme = theme
self.dim = ASDisplayNode()
self.dim.alpha = 0.0
self.dim.backgroundColor = UIColor(white: 0.0, alpha: 0.25)
self.wrappingView = UIView()
self.containerView = UIView()
self.hostView = ComponentHostView()
super.init()
self.containerView.clipsToBounds = true
self.containerView.backgroundColor = self.presentationData.theme.actionSheet.opaqueItemBackgroundColor
self.addSubnode(self.dim)
self.view.addSubview(self.wrappingView)
self.wrappingView.addSubview(self.containerView)
self.containerView.addSubview(self.hostView)
}
override func didLoad() {
super.didLoad()
let panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:)))
panRecognizer.delegate = self
panRecognizer.delaysTouchesBegan = false
panRecognizer.cancelsTouchesInView = true
self.panGestureRecognizer = panRecognizer
self.wrappingView.addGestureRecognizer(panRecognizer)
self.dim.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTapGesture(_:))))
}
@objc func dimTapGesture(_ recognizer: UITapGestureRecognizer) {
if case .ended = recognizer.state {
self.controller?.dismiss(animated: true)
}
}
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
if let (layout, _) = self.currentLayout {
if case .regular = layout.metrics.widthClass {
return false
} else {
let location = gestureRecognizer.location(in: self.containerView)
if !self.hostView.frame.contains(location) {
return false
}
}
}
return true
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
if gestureRecognizer is UIPanGestureRecognizer && otherGestureRecognizer is UIPanGestureRecognizer {
return true
}
return false
}
private var isDismissing = false
func animateIn() {
ContainedViewLayoutTransition.animated(duration: 0.3, curve: .linear).updateAlpha(node: self.dim, alpha: 1.0)
let targetPosition = self.containerView.center
let startPosition = targetPosition.offsetBy(dx: 0.0, dy: self.bounds.height)
self.containerView.center = startPosition
let transition = ContainedViewLayoutTransition.animated(duration: 0.4, curve: .spring)
transition.animateView(allowUserInteraction: true, {
self.containerView.center = targetPosition
}, completion: { _ in
})
}
func animateOut(completion: @escaping () -> Void = {}) {
self.isDismissing = true
let positionTransition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .easeInOut)
positionTransition.updatePosition(layer: self.containerView.layer, position: CGPoint(x: self.containerView.center.x, y: self.bounds.height + self.containerView.bounds.height / 2.0), completion: { [weak self] _ in
self?.controller?.dismiss(animated: false, completion: completion)
})
let alphaTransition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .easeInOut)
alphaTransition.updateAlpha(node: self.dim, alpha: 0.0)
}
func containerLayoutUpdated(layout: ContainerViewLayout, navigationHeight: CGFloat, transition: Transition) {
self.currentLayout = (layout, navigationHeight)
if let controller = self.controller, let navigationBar = controller.navigationBar, navigationBar.view.superview !== self.wrappingView {
self.containerView.addSubview(navigationBar.view)
}
self.dim.frame = CGRect(origin: CGPoint(x: 0.0, y: -layout.size.height), size: CGSize(width: layout.size.width, height: layout.size.height * 3.0))
let isLandscape = layout.orientation == .landscape
transition.setFrame(view: self.wrappingView, frame: CGRect(origin: CGPoint(), size: layout.size), completion: nil)
var clipFrame: CGRect
if layout.metrics.widthClass == .compact {
self.dim.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.25)
if isLandscape {
self.containerView.layer.cornerRadius = 0.0
} else {
self.containerView.layer.cornerRadius = 10.0
}
if #available(iOS 11.0, *) {
if layout.safeInsets.bottom.isZero {
self.containerView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
} else {
self.containerView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMaxYCorner]
}
}
if isLandscape {
clipFrame = CGRect(origin: CGPoint(), size: layout.size)
} else {
let coveredByModalTransition: CGFloat = 0.0
var containerTopInset: CGFloat = 10.0
if let statusBarHeight = layout.statusBarHeight {
containerTopInset += statusBarHeight
}
let unscaledFrame = CGRect(origin: CGPoint(x: 0.0, y: containerTopInset - coveredByModalTransition * 10.0), size: CGSize(width: layout.size.width, height: layout.size.height - containerTopInset))
let maxScale: CGFloat = (layout.size.width - 16.0 * 2.0) / layout.size.width
let containerScale = 1.0 * (1.0 - coveredByModalTransition) + maxScale * coveredByModalTransition
let maxScaledTopInset: CGFloat = containerTopInset - 10.0
let scaledTopInset: CGFloat = containerTopInset * (1.0 - coveredByModalTransition) + maxScaledTopInset * coveredByModalTransition
let containerFrame = unscaledFrame.offsetBy(dx: 0.0, dy: scaledTopInset - (unscaledFrame.midY - containerScale * unscaledFrame.height / 2.0))
clipFrame = CGRect(x: containerFrame.minX, y: containerFrame.minY, width: containerFrame.width, height: containerFrame.height)
}
} else {
self.dim.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.4)
self.containerView.layer.cornerRadius = 10.0
let verticalInset: CGFloat = 44.0
let maxSide = max(layout.size.width, layout.size.height)
let minSide = min(layout.size.width, layout.size.height)
let containerSize = CGSize(width: min(layout.size.width - 20.0, floor(maxSide / 2.0)), height: min(layout.size.height, minSide) - verticalInset * 2.0)
clipFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - containerSize.width) / 2.0), y: floor((layout.size.height - containerSize.height) / 2.0)), size: containerSize)
}
let environment = ViewControllerComponentContainer.Environment(
statusBarHeight: 0.0,
navigationHeight: navigationHeight,
safeInsets: UIEdgeInsets(top: layout.intrinsicInsets.top + layout.safeInsets.top, left: layout.safeInsets.left, bottom: layout.intrinsicInsets.bottom + layout.safeInsets.bottom, right: layout.safeInsets.right),
isVisible: self.currentIsVisible,
theme: self.theme ?? self.presentationData.theme,
strings: self.presentationData.strings,
dateTimeFormat: self.presentationData.dateTimeFormat,
controller: { [weak self] in
return self?.controller
}
)
let contentSize = self.hostView.update(
transition: transition,
component: self.component,
environment: {
environment
},
forceUpdate: true,
containerSize: CGSize(width: clipFrame.size.width, height: clipFrame.size.height)
)
transition.setFrame(view: self.hostView, frame: CGRect(origin: CGPoint(), size: contentSize), completion: nil)
if !isLandscape {
clipFrame.origin.y = layout.size.height - contentSize.height
transition.setFrame(view: self.containerView, frame: clipFrame)
} else {
}
}
private var didPlayAppearAnimation = false
func updateIsVisible(isVisible: Bool) {
if self.currentIsVisible == isVisible {
return
}
self.currentIsVisible = isVisible
guard let currentLayout = self.currentLayout else {
return
}
self.containerLayoutUpdated(layout: currentLayout.layout, navigationHeight: currentLayout.navigationHeight, transition: .immediate)
if !self.didPlayAppearAnimation {
self.didPlayAppearAnimation = true
self.animateIn()
}
}
@objc func panGesture(_ recognizer: UIPanGestureRecognizer) {
switch recognizer.state {
case .began:
break
case .changed:
let translation = recognizer.translation(in: self.view).y
var bounds = self.bounds
bounds.origin.y = -translation
bounds.origin.y = min(0.0, bounds.origin.y)
self.bounds = bounds
case .ended:
let translation = recognizer.translation(in: self.view).y
let velocity = recognizer.velocity(in: self.view)
var bounds = self.bounds
bounds.origin.y = -translation
bounds.origin.y = min(0.0, bounds.origin.y)
if bounds.minY < -60 || (bounds.minY < 0.0 && velocity.y > 300.0) {
self.controller?.dismiss(animated: true, completion: nil)
} else {
var bounds = self.bounds
let previousBounds = bounds
bounds.origin.y = 0.0
self.bounds = bounds
self.layer.animateBounds(from: previousBounds, to: self.bounds, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue)
}
case .cancelled:
var bounds = self.bounds
let previousBounds = bounds
bounds.origin.y = 0.0
self.bounds = bounds
self.layer.animateBounds(from: previousBounds, to: self.bounds, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue)
default:
break
}
}
}
var node: Node {
return self.displayNode as! Node
}
private let context: AccountContext
private let theme: PresentationTheme?
private let component: AnyComponent<ViewControllerComponentContainer.Environment>
private var isInitiallyExpanded = false
private var currentLayout: ContainerViewLayout?
public var pushController: (ViewController) -> Void = { _ in }
public var presentController: (ViewController) -> Void = { _ in }
public enum Subject {
case folders
case chatsInFolder
case pins
case files
}
public convenience init(context: AccountContext, subject: Subject) {
self.init(context: context, component: LimitScreenComponent(context: context, subject: subject, proceed: {}))
}
private init<C: Component>(context: AccountContext, component: C, theme: PresentationTheme? = nil) where C.EnvironmentType == ViewControllerComponentContainer.Environment {
self.context = context
self.component = AnyComponent(component)
self.theme = nil
super.init(navigationBarPresentationData: nil)
}
required public init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc private func cancelPressed() {
self.dismiss(animated: true, completion: nil)
}
override open func loadDisplayNode() {
self.displayNode = Node(context: self.context, controller: self, component: self.component, theme: self.theme)
self.displayNodeDidLoad()
}
public override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
self.view.endEditing(true)
if flag {
self.node.animateOut(completion: {
super.dismiss(animated: false, completion: {})
completion?()
})
} else {
super.dismiss(animated: false, completion: {})
completion?()
}
}
override open func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.node.updateIsVisible(isVisible: true)
}
override open func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
self.node.updateIsVisible(isVisible: false)
}
override open func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
self.currentLayout = layout
super.containerLayoutUpdated(layout, transition: transition)
let navigationHeight: CGFloat = 56.0
self.node.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: Transition(transition))
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,360 @@
import Foundation
import UIKit
import Display
import AsyncDisplayKit
import Postbox
import TelegramCore
import SwiftSignalKit
import AccountContext
import TelegramPresentationData
import PresentationDataUtils
import ComponentFlow
import ViewControllerComponent
import SheetComponent
import MultilineTextComponent
import BundleIconComponent
import SolidRoundedButtonComponent
import Markdown
private final class LimitSheetContent: CombinedComponent {
typealias EnvironmentType = ViewControllerComponentContainer.Environment
let context: AccountContext
let subject: PremiumLimitScreen.Subject
let action: () -> Void
let dismiss: () -> Void
init(context: AccountContext, subject: PremiumLimitScreen.Subject, action: @escaping () -> Void, dismiss: @escaping () -> Void) {
self.context = context
self.subject = subject
self.action = action
self.dismiss = dismiss
}
static func ==(lhs: LimitSheetContent, rhs: LimitSheetContent) -> Bool {
if lhs.context !== rhs.context {
return false
}
if lhs.subject != rhs.subject {
return false
}
return true
}
final class State: ComponentState {
private let context: AccountContext
private var disposable: Disposable?
var limits: EngineConfiguration.UserLimits
var premiumLimits: EngineConfiguration.UserLimits
init(context: AccountContext, subject: PremiumLimitScreen.Subject) {
self.context = context
self.limits = EngineConfiguration.UserLimits.defaultValue
self.premiumLimits = EngineConfiguration.UserLimits.defaultValue
super.init()
self.disposable = (context.engine.data.get(
TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: false),
TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: true)
) |> deliverOnMainQueue).start(next: { [weak self] result in
if let strongSelf = self {
let (limits, premiumLimits) = result
strongSelf.limits = limits
strongSelf.premiumLimits = premiumLimits
strongSelf.updated(transition: .immediate)
}
})
}
deinit {
self.disposable?.dispose()
}
}
func makeState() -> State {
return State(context: self.context, subject: self.subject)
}
static var body: Body {
let badgeBackground = Child(RoundedRectangle.self)
let badgeIcon = Child(BundleIconComponent.self)
let badgeText = Child(MultilineTextComponent.self)
let title = Child(MultilineTextComponent.self)
let text = Child(MultilineTextComponent.self)
let button = Child(SolidRoundedButtonComponent.self)
return { context in
let environment = context.environment[ViewControllerComponentContainer.Environment.self].value
let component = context.component
let theme = environment.theme
let strings = environment.strings
let state = context.state
let subject = component.subject
let sideInset: CGFloat = 16.0 + environment.safeInsets.left
let textSideInset: CGFloat = 24.0 + environment.safeInsets.left
let iconName: String
let badgeString: String
let string: String
switch subject {
case .folders:
let limit = state.limits.maxFoldersCount
let premiumLimit = state.premiumLimits.maxFoldersCount
iconName = "Premium/Folder"
badgeString = "\(limit)"
string = strings.Premium_MaxFoldersCountText("\(limit)", "\(premiumLimit)").string
case .chatsInFolder:
let limit = state.limits.maxFolderChatsCount
let premiumLimit = state.premiumLimits.maxFolderChatsCount
iconName = "Premium/Chat"
badgeString = "\(limit)"
string = strings.Premium_MaxChatsInFolderCountText("\(limit)", "\(premiumLimit)").string
case .pins:
let limit = state.limits.maxPinnedChatCount
let premiumLimit = state.premiumLimits.maxPinnedChatCount
iconName = "Premium/Pin"
badgeString = "\(limit)"
string = strings.Premium_MaxPinsText("\(limit)", "\(premiumLimit)").string
case .files:
let limit = 2048 * 1024 * 1024 //state.limits.maxPinnedChatCount
let premiumLimit = 4096 * 1024 * 1024 //state.premiumLimits.maxPinnedChatCount
iconName = "Premium/File"
badgeString = dataSizeString(limit, formatting: DataSizeStringFormatting(strings: environment.strings, decimalSeparator: environment.dateTimeFormat.decimalSeparator))
string = strings.Premium_MaxFileSizeText(dataSizeString(premiumLimit, formatting: DataSizeStringFormatting(strings: environment.strings, decimalSeparator: environment.dateTimeFormat.decimalSeparator))).string
}
let badgeIcon = badgeIcon.update(
component: BundleIconComponent(
name: iconName,
tintColor: .white
),
availableSize: context.availableSize,
transition: .immediate
)
let badgeText = badgeText.update(
component: MultilineTextComponent(
text: .plain(NSAttributedString(
string: badgeString,
font: Font.with(size: 24.0, design: .round, weight: .semibold, traits: []),
textColor: .white,
paragraphAlignment: .center
)),
horizontalAlignment: .center,
maximumNumberOfLines: 1
),
availableSize: context.availableSize,
transition: .immediate
)
let badgeBackground = badgeBackground.update(
component: RoundedRectangle(
colors: [UIColor(rgb: 0xa34fcf), UIColor(rgb: 0xc8498a), UIColor(rgb: 0xff7a23)],
cornerRadius: 23.5
),
availableSize: CGSize(width: badgeText.size.width + 67.0, height: 47.0),
transition: .immediate
)
let title = title.update(
component: MultilineTextComponent(
text: .plain(NSAttributedString(
string: strings.Premium_LimitReached,
font: Font.semibold(17.0),
textColor: theme.actionSheet.primaryTextColor,
paragraphAlignment: .center
)),
horizontalAlignment: .center,
maximumNumberOfLines: 1
),
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: CGFloat.greatestFiniteMagnitude),
transition: .immediate
)
let textFont = Font.regular(17.0)
let boldTextFont = Font.semibold(17.0)
let textColor = theme.actionSheet.primaryTextColor
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: string, attributes: markdownAttributes),
horizontalAlignment: .center,
maximumNumberOfLines: 0,
lineSpacing: 0.0
),
availableSize: CGSize(width: context.availableSize.width - textSideInset * 2.0, height: context.availableSize.height),
transition: .immediate
)
let button = button.update(
component: SolidRoundedButtonComponent(
title: strings.Premium_IncreaseLimit,
theme: SolidRoundedButtonComponent.Theme(
backgroundColor: .black,
backgroundColors: [UIColor(rgb: 0x407af0), UIColor(rgb: 0x9551e8), UIColor(rgb: 0xbf499a), UIColor(rgb: 0xf17b30)],
foregroundColor: .white
),
font: .bold,
fontSize: 17.0,
height: 50.0,
cornerRadius: 10.0,
gloss: false,
iconName: "Premium/X2",
iconPosition: .right,
action: { [weak component] in
guard let component = component else {
return
}
component.dismiss()
component.action()
}
),
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0),
transition: context.transition
)
let width = context.availableSize.width
let badgeFrame = CGRect(origin: CGPoint(x: floor((context.availableSize.width - badgeBackground.size.width) / 2.0), y: 33.0), size: badgeBackground.size)
context.add(badgeBackground
.position(CGPoint(x: badgeFrame.midX, y: badgeFrame.midY))
)
let badgeIconFrame = CGRect(origin: CGPoint(x: badgeFrame.minX + 18.0, y: badgeFrame.minY + floor((badgeFrame.height - badgeIcon.size.height) / 2.0)), size: badgeIcon.size)
context.add(badgeIcon
.position(CGPoint(x: badgeIconFrame.midX, y: badgeIconFrame.midY))
)
let badgeTextFrame = CGRect(origin: CGPoint(x: badgeFrame.maxX - badgeText.size.width - 15.0, y: badgeFrame.minY + floor((badgeFrame.height - badgeText.size.height) / 2.0)), size: badgeText.size)
context.add(badgeText
.position(CGPoint(x: badgeTextFrame.midX, y: badgeTextFrame.midY))
)
context.add(title
.position(CGPoint(x: width / 2.0, y: 28.0))
)
context.add(text
.position(CGPoint(x: width / 2.0, y: 228.0))
)
let buttonFrame = CGRect(origin: CGPoint(x: sideInset, y: 228.0 + ceil(text.size.height / 2.0) + 38.0), size: button.size)
context.add(button
.position(CGPoint(x: buttonFrame.midX, y: buttonFrame.midY))
)
let contentSize = CGSize(width: context.availableSize.width, height: buttonFrame.maxY + 5.0 + environment.safeInsets.bottom)
return contentSize
}
}
}
private final class LimitSheetComponent: CombinedComponent {
typealias EnvironmentType = ViewControllerComponentContainer.Environment
let context: AccountContext
let subject: PremiumLimitScreen.Subject
let action: () -> Void
init(context: AccountContext, subject: PremiumLimitScreen.Subject, action: @escaping () -> Void) {
self.context = context
self.subject = subject
self.action = action
}
static func ==(lhs: LimitSheetComponent, rhs: LimitSheetComponent) -> Bool {
if lhs.context !== rhs.context {
return false
}
if lhs.subject != rhs.subject {
return false
}
return true
}
static var body: Body {
let sheet = Child(SheetComponent<EnvironmentType>.self)
let animateOut = StoredActionSlot(Action<Void>.self)
return { context in
let environment = context.environment[EnvironmentType.self]
let controller = environment.controller
let sheet = sheet.update(
component: SheetComponent<EnvironmentType>(
content: AnyComponent<EnvironmentType>(LimitSheetContent(
context: context.component.context,
subject: context.component.subject,
action: context.component.action,
dismiss: {
animateOut.invoke(Action { _ in
if let controller = controller() {
controller.dismiss(completion: nil)
}
})
}
)),
backgroundColor: environment.theme.actionSheet.opaqueItemBackgroundColor,
animateOut: animateOut
),
environment: {
environment
SheetComponentEnvironment(
isDisplaying: environment.value.isVisible,
dismiss: {
animateOut.invoke(Action { _ in
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))
)
return context.availableSize
}
}
}
public class PremiumLimitScreen: ViewControllerComponentContainer {
public enum Subject {
case folders
case chatsInFolder
case pins
case files
}
public init(context: AccountContext, subject: PremiumLimitScreen.Subject, action: @escaping () -> Void) {
super.init(context: context, component: LimitSheetComponent(context: context, subject: subject, action: action), navigationBarAppearance: .none)
self.navigationPresentation = .flatModal
}
required public init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public override func viewDidLoad() {
super.viewDidLoad()
self.view.disablesInteractiveModalDismiss = true
}
}

View File

@ -34,7 +34,7 @@ public enum ShareControllerPreferredAction {
}
public enum ShareControllerExternalStatus {
case preparing
case preparing(Bool)
case progress(Float)
case done
}
@ -638,8 +638,8 @@ public final class ShareController: ViewController {
return f(peerIds, text, strongSelf.currentAccount, silently)
|> map { state -> ShareState in
switch state {
case .preparing:
return .preparing
case let .preparing(long):
return .preparing(long)
case let .progress(value):
return .progress(value)
case .done:

View File

@ -11,7 +11,7 @@ import TelegramIntents
import ContextUI
enum ShareState {
case preparing
case preparing(Bool)
case progress(Float)
case done
}
@ -672,9 +672,9 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
})
})
}
if self.fromForeignApp {
self.transitionToContentNode(ShareProlongedLoadingContainerNode(theme: self.presentationData.theme, strings: self.presentationData.strings, forceNativeAppearance: true), fastOut: true)
} else {
//
if !self.fromForeignApp {
self.animateOut(shared: true, completion: {
})
self.completed?(peerIds)
@ -686,6 +686,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
self.hapticFeedback?.success()
}
}
var transitioned = false
let fromForeignApp = self.fromForeignApp
self.shareDisposable.set((signal
|> deliverOnMainQueue).start(next: { [weak self] status in
@ -693,11 +694,20 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
return
}
if fromForeignApp, case let .preparing(long) = status, !transitioned {
transitioned = true
if long {
strongSelf.transitionToContentNode(ShareProlongedLoadingContainerNode(theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, forceNativeAppearance: true), fastOut: true)
} else {
strongSelf.transitionToContentNode(ShareLoadingContainerNode(theme: strongSelf.presentationData.theme, forceNativeAppearance: true), fastOut: true)
}
}
if case .done = status, !fromForeignApp {
strongSelf.dismiss?(true)
return
}
guard let contentNode = strongSelf.contentNode as? ShareLoadingContainer else {
return
}

View File

@ -21,14 +21,14 @@ public enum PreparedShareItemContent {
}
public enum PreparedShareItem {
case preparing
case preparing(Bool)
case progress(Float)
case userInteractionRequired(UnpreparedShareItemContent)
case done(PreparedShareItemContent)
}
public enum PreparedShareItems {
case preparing
case preparing(Bool)
case progress(Float)
case userInteractionRequired([UnpreparedShareItemContent])
case done([PreparedShareItemContent])
@ -50,7 +50,7 @@ private func scalePhotoImage(_ image: UIImage, dimensions: CGSize) -> UIImage? {
private func preparedShareItem(account: Account, to peerId: PeerId, value: [String: Any]) -> Signal<PreparedShareItem, Void> {
if let imageData = value["scaledImageData"] as? Data, let dimensions = value["scaledImageDimensions"] as? NSValue {
let diminsionsSize = dimensions.cgSizeValue
return .single(.preparing)
return .single(.preparing(false))
|> then(
standaloneUploadedImage(account: account, peerId: peerId, text: "", data: imageData, dimensions: PixelDimensions(width: Int32(diminsionsSize.width), height: Int32(diminsionsSize.height)))
|> mapError { _ -> Void in
@ -69,8 +69,9 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri
let nativeImageSize = CGSize(width: image.size.width * image.scale, height: image.size.height * image.scale)
let dimensions = nativeImageSize.fitted(CGSize(width: 1280.0, height: 1280.0))
if let scaledImage = scalePhotoImage(image, dimensions: dimensions), let imageData = scaledImage.jpegData(compressionQuality: 0.52) {
return .single(.preparing)
|> then(standaloneUploadedImage(account: account, peerId: peerId, text: "", data: imageData, dimensions: PixelDimensions(width: Int32(dimensions.width), height: Int32(dimensions.height)))
return .single(.preparing(false))
|> then(
standaloneUploadedImage(account: account, peerId: peerId, text: "", data: imageData, dimensions: PixelDimensions(width: Int32(dimensions.width), height: Int32(dimensions.height)))
|> mapError { _ -> Void in
return Void()
}
@ -119,38 +120,41 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri
}
}
return loadValues(asset)
|> mapToSignal { asset -> Signal<PreparedShareItem, Void> in
let preset = adjustments?.preset ?? TGMediaVideoConversionPresetCompressedMedium
let finalDimensions = TGMediaVideoConverter.dimensions(for: asset.originalSize, adjustments: adjustments, preset: preset)
var resourceAdjustments: VideoMediaResourceAdjustments?
if let adjustments = adjustments {
if adjustments.trimApplied() {
finalDuration = adjustments.trimEndValue - adjustments.trimStartValue
return .single(.preparing(true))
|> then(
loadValues(asset)
|> mapToSignal { asset -> Signal<PreparedShareItem, Void> in
let preset = adjustments?.preset ?? TGMediaVideoConversionPresetCompressedMedium
let finalDimensions = TGMediaVideoConverter.dimensions(for: asset.originalSize, adjustments: adjustments, preset: preset)
var resourceAdjustments: VideoMediaResourceAdjustments?
if let adjustments = adjustments {
if adjustments.trimApplied() {
finalDuration = adjustments.trimEndValue - adjustments.trimStartValue
}
let adjustmentsData = MemoryBuffer(data: NSKeyedArchiver.archivedData(withRootObject: adjustments.dictionary()!))
let digest = MemoryBuffer(data: adjustmentsData.md5Digest())
resourceAdjustments = VideoMediaResourceAdjustments(data: adjustmentsData, digest: digest)
}
let adjustmentsData = MemoryBuffer(data: NSKeyedArchiver.archivedData(withRootObject: adjustments.dictionary()!))
let digest = MemoryBuffer(data: adjustmentsData.md5Digest())
resourceAdjustments = VideoMediaResourceAdjustments(data: adjustmentsData, digest: digest)
}
let estimatedSize = TGMediaVideoConverter.estimatedSize(for: preset, duration: finalDuration, hasAudio: true)
let resource = LocalFileVideoMediaResource(randomId: Int64.random(in: Int64.min ... Int64.max), path: asset.url.path, adjustments: resourceAdjustments)
return standaloneUploadedFile(account: account, peerId: peerId, text: "", source: .resource(.standalone(resource: resource)), mimeType: "video/mp4", attributes: [.Video(duration: Int(finalDuration), size: PixelDimensions(width: Int32(finalDimensions.width), height: Int32(finalDimensions.height)), flags: flags)], hintFileIsLarge: estimatedSize > 10 * 1024 * 1024)
|> mapError { _ -> Void in
return Void()
}
|> mapToSignal { event -> Signal<PreparedShareItem, Void> in
switch event {
case let .progress(value):
return .single(.progress(value))
case let .result(media):
return .single(.done(.media(media)))
let estimatedSize = TGMediaVideoConverter.estimatedSize(for: preset, duration: finalDuration, hasAudio: true)
let resource = LocalFileVideoMediaResource(randomId: Int64.random(in: Int64.min ... Int64.max), path: asset.url.path, adjustments: resourceAdjustments)
return standaloneUploadedFile(account: account, peerId: peerId, text: "", source: .resource(.standalone(resource: resource)), mimeType: "video/mp4", attributes: [.Video(duration: Int(finalDuration), size: PixelDimensions(width: Int32(finalDimensions.width), height: Int32(finalDimensions.height)), flags: flags)], hintFileIsLarge: estimatedSize > 10 * 1024 * 1024)
|> mapError { _ -> Void in
return Void()
}
|> mapToSignal { event -> Signal<PreparedShareItem, Void> in
switch event {
case let .progress(value):
return .single(.progress(value))
case let .result(media):
return .single(.done(.media(media)))
}
}
}
}
)
} else if let data = value["data"] as? Data {
let fileName = value["fileName"] as? String
let mimeType = (value["mimeType"] as? String) ?? "application/octet-stream"
@ -194,19 +198,38 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri
return disposable
}
return convertedData
|> castError(Void.self)
|> mapToSignal { data, dimensions, duration, converted in
var attributes: [TelegramMediaFileAttribute] = []
let mimeType: String
if converted {
mimeType = "video/mp4"
attributes = [.Video(duration: Int(duration), size: PixelDimensions(width: Int32(dimensions.width), height: Int32(dimensions.height)), flags: [.supportsStreaming]), .Animated, .FileName(fileName: "animation.mp4")]
} else {
mimeType = "animation/gif"
attributes = [.ImageSize(size: PixelDimensions(width: Int32(dimensions.width), height: Int32(dimensions.height))), .Animated, .FileName(fileName: fileName ?? "animation.gif")]
return .single(.preparing(true))
|> then(
convertedData
|> castError(Void.self)
|> mapToSignal { data, dimensions, duration, converted in
var attributes: [TelegramMediaFileAttribute] = []
let mimeType: String
if converted {
mimeType = "video/mp4"
attributes = [.Video(duration: Int(duration), size: PixelDimensions(width: Int32(dimensions.width), height: Int32(dimensions.height)), flags: [.supportsStreaming]), .Animated, .FileName(fileName: "animation.mp4")]
} else {
mimeType = "animation/gif"
attributes = [.ImageSize(size: PixelDimensions(width: Int32(dimensions.width), height: Int32(dimensions.height))), .Animated, .FileName(fileName: fileName ?? "animation.gif")]
}
return standaloneUploadedFile(account: account, peerId: peerId, text: "", source: .data(data), mimeType: mimeType, attributes: attributes, hintFileIsLarge: data.count > 10 * 1024 * 1024)
|> mapError { _ -> Void in return Void() }
|> mapToSignal { event -> Signal<PreparedShareItem, Void> in
switch event {
case let .progress(value):
return .single(.progress(value))
case let .result(media):
return .single(.done(.media(media)))
}
}
}
return standaloneUploadedFile(account: account, peerId: peerId, text: "", source: .data(data), mimeType: mimeType, attributes: attributes, hintFileIsLarge: data.count > 10 * 1024 * 1024)
)
} else {
let scaledImage = TGScaleImageToPixelSize(image, CGSize(width: image.size.width * image.scale, height: image.size.height * image.scale).fitted(CGSize(width: 1280.0, height: 1280.0)))!
let imageData = scaledImage.jpegData(compressionQuality: 0.54)!
return .single(.preparing(false))
|> then(
standaloneUploadedImage(account: account, peerId: peerId, text: "", data: imageData, dimensions: PixelDimensions(width: Int32(scaledImage.size.width), height: Int32(scaledImage.size.height)))
|> mapError { _ -> Void in return Void() }
|> mapToSignal { event -> Signal<PreparedShareItem, Void> in
switch event {
@ -216,11 +239,18 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri
return .single(.done(.media(media)))
}
}
}
} else {
let scaledImage = TGScaleImageToPixelSize(image, CGSize(width: image.size.width * image.scale, height: image.size.height * image.scale).fitted(CGSize(width: 1280.0, height: 1280.0)))!
let imageData = scaledImage.jpegData(compressionQuality: 0.54)!
return standaloneUploadedImage(account: account, peerId: peerId, text: "", data: imageData, dimensions: PixelDimensions(width: Int32(scaledImage.size.width), height: Int32(scaledImage.size.height)))
)
}
} else {
var thumbnailData: Data?
if mimeType == "application/pdf", let image = generatePdfPreviewImage(data: data, size: CGSize(width: 256.0, height: 256.0)), let jpegData = image.jpegData(compressionQuality: 0.5) {
thumbnailData = jpegData
}
let long = data.count > Int32(1.5 * 1024 * 1024)
return .single(.preparing(long))
|> then(
standaloneUploadedFile(account: account, peerId: peerId, text: "", source: .data(data), thumbnailData: thumbnailData, mimeType: mimeType, attributes: [.FileName(fileName: fileName ?? "file")], hintFileIsLarge: data.count > 10 * 1024 * 1024)
|> mapError { _ -> Void in return Void() }
|> mapToSignal { event -> Signal<PreparedShareItem, Void> in
switch event {
@ -230,23 +260,7 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri
return .single(.done(.media(media)))
}
}
}
} else {
var thumbnailData: Data?
if mimeType == "application/pdf", let image = generatePdfPreviewImage(data: data, size: CGSize(width: 256.0, height: 256.0)), let jpegData = image.jpegData(compressionQuality: 0.5) {
thumbnailData = jpegData
}
return standaloneUploadedFile(account: account, peerId: peerId, text: "", source: .data(data), thumbnailData: thumbnailData, mimeType: mimeType, attributes: [.FileName(fileName: fileName ?? "file")], hintFileIsLarge: data.count > 10 * 1024 * 1024)
|> mapError { _ -> Void in return Void() }
|> mapToSignal { event -> Signal<PreparedShareItem, Void> in
switch event {
case let .progress(value):
return .single(.progress(value))
case let .result(media):
return .single(.done(.media(media)))
}
}
)
}
} else if let url = value["audio"] as? URL {
if let audioData = try? Data(contentsOf: url, options: [.mappedIfSafe]) {
@ -262,25 +276,32 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri
waveform = MemoryBuffer(data: waveformData)
}
return standaloneUploadedFile(account: account, peerId: peerId, text: "", source: .data(audioData), mimeType: mimeType, attributes: [.Audio(isVoice: isVoice, duration: Int(duration), title: title, performer: artist, waveform: waveform?.makeData()), .FileName(fileName: fileName)], hintFileIsLarge: audioData.count > 10 * 1024 * 1024)
|> mapError { _ -> Void in return Void() }
|> mapToSignal { event -> Signal<PreparedShareItem, Void> in
switch event {
case let .progress(value):
return .single(.progress(value))
case let .result(media):
return .single(.done(.media(media)))
let long = audioData.count > Int32(1.5 * 1024 * 1024)
return .single(.preparing(long))
|> then(
standaloneUploadedFile(account: account, peerId: peerId, text: "", source: .data(audioData), mimeType: mimeType, attributes: [.Audio(isVoice: isVoice, duration: Int(duration), title: title, performer: artist, waveform: waveform?.makeData()), .FileName(fileName: fileName)], hintFileIsLarge: audioData.count > 10 * 1024 * 1024)
|> mapError { _ -> Void in return Void() }
|> mapToSignal { event -> Signal<PreparedShareItem, Void> in
switch event {
case let .progress(value):
return .single(.progress(value))
case let .result(media):
return .single(.done(.media(media)))
}
}
}
)
} else {
return .never()
}
} else if let text = value["text"] as? String {
return .single(.done(.text(text)))
return .single(.preparing(false))
|> then(
.single(.done(.text(text)))
)
} else if let url = value["url"] as? URL {
if TGShareLocationSignals.isLocationURL(url) {
return Signal<PreparedShareItem, Void> { subscriber in
subscriber.putNext(.preparing)
subscriber.putNext(.preparing(false))
let disposable = TGShareLocationSignals.locationMessageContent(for: url).start(next: { value in
if let value = value as? TGShareLocationResult {
if let title = value.title {
@ -299,7 +320,10 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri
}
}
} else {
return .single(.done(.text(url.absoluteString)))
return .single(.preparing(false))
|> then(
.single(.done(.text(url.absoluteString)))
)
}
} else if let vcard = value["contact"] as? Data, let contactData = DeviceContactExtendedData(vcard: vcard) {
return .single(.userInteractionRequired(.contact(contactData)))
@ -347,8 +371,8 @@ public func preparedShareItems(account: Account, to peerId: PeerId, dataItems: [
var progresses: [Float] = []
for item in items {
switch item {
case .preparing:
return .preparing
case let .preparing(long):
return .preparing(long)
case let .progress(value):
progresses.append(value)
case let .userInteractionRequired(value):

View File

@ -248,8 +248,8 @@ public final class SolidRoundedButtonNode: ASDisplayNode {
compositingFilter = nil
}
shimmerView.update(backgroundColor: .clear, foregroundColor: color.withAlphaComponent(alpha), gradientSize: 70.0, duration: 2.4, horizontal: true)
borderShimmerView.update(backgroundColor: .clear, foregroundColor: color.withAlphaComponent(borderAlpha), gradientSize: 70.0, duration: 2.4, horizontal: true)
shimmerView.update(backgroundColor: .clear, foregroundColor: color.withAlphaComponent(alpha), gradientSize: 70.0, duration: 3.0, horizontal: true)
borderShimmerView.update(backgroundColor: .clear, foregroundColor: color.withAlphaComponent(borderAlpha), gradientSize: 70.0, duration: 3.0, horizontal: true)
shimmerView.layer.compositingFilter = compositingFilter
borderShimmerView.layer.compositingFilter = compositingFilter
@ -259,15 +259,42 @@ public final class SolidRoundedButtonNode: ASDisplayNode {
guard theme !== self.theme else {
return
}
let previousTheme = self.theme
self.theme = theme
if animated {
let animateTransition = previousTheme.backgroundColors.count != theme.backgroundColors.count
if animated && animateTransition {
if let snapshotView = self.buttonBackgroundNode.view.snapshotView(afterScreenUpdates: false) {
self.view.insertSubview(snapshotView, aboveSubview: self.buttonBackgroundNode.view)
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak snapshotView] _ in
snapshotView?.removeFromSuperview()
})
}
if let snapshotView = self.titleNode.view.snapshotView(afterScreenUpdates: false) {
snapshotView.frame = self.titleNode.frame
self.view.insertSubview(snapshotView, aboveSubview: self.titleNode.view)
snapshotView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: 15.0), duration: 0.2, removeOnCompletion: false, additive: true)
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak snapshotView] _ in
snapshotView?.removeFromSuperview()
})
self.titleNode.layer.animatePosition(from: CGPoint(x: 0.0, y: -15.0), to: CGPoint(), duration: 0.2, additive: true)
self.titleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
if let snapshotView = self.iconNode.view.snapshotView(afterScreenUpdates: false) {
snapshotView.frame = self.iconNode.frame
self.view.insertSubview(snapshotView, aboveSubview: self.iconNode.view)
snapshotView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: 15.0), duration: 0.2, removeOnCompletion: false, additive: true)
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak snapshotView] _ in
snapshotView?.removeFromSuperview()
})
self.iconNode.layer.animatePosition(from: CGPoint(x: 0.0, y: -15.0), to: CGPoint(), duration: 0.2, additive: true)
self.iconNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
}
if theme.backgroundColors.count > 1 {
self.buttonBackgroundNode.backgroundColor = nil
@ -317,8 +344,8 @@ public final class SolidRoundedButtonNode: ASDisplayNode {
transition.updateFrame(view: borderMaskView, frame: buttonFrame)
transition.updateFrame(view: borderShimmerView, frame: buttonFrame)
shimmerView.updateAbsoluteRect(CGRect(origin: CGPoint(x: width * 3.0, y: 0.0), size: buttonSize), within: CGSize(width: width * 7.0, height: buttonHeight))
borderShimmerView.updateAbsoluteRect(CGRect(origin: CGPoint(x: width * 3.0, y: 0.0), size: buttonSize), within: CGSize(width: width * 7.0, height: buttonHeight))
shimmerView.updateAbsoluteRect(CGRect(origin: CGPoint(x: width * 4.0, y: 0.0), size: buttonSize), within: CGSize(width: width * 9.0, height: buttonHeight))
borderShimmerView.updateAbsoluteRect(CGRect(origin: CGPoint(x: width * 4.0, y: 0.0), size: buttonSize), within: CGSize(width: width * 9.0, height: buttonHeight))
}
transition.updateFrame(node: self.buttonNode, frame: buttonFrame)
@ -435,7 +462,12 @@ public final class SolidRoundedButtonView: UIView {
private let buttonBackgroundNode: UIImageView
private var buttonBackgroundAnimationView: UIImageView?
private let buttonGlossView: SolidRoundedButtonGlossView?
private var shimmerView: ShimmerEffectForegroundView?
private var borderView: UIView?
private var borderMaskView: UIView?
private var borderShimmerView: ShimmerEffectForegroundView?
private let buttonNode: HighlightTrackingButton
private let titleNode: ImmediateTextView
private let subtitleNode: ImmediateTextView
@ -515,13 +547,7 @@ public final class SolidRoundedButtonView: UIView {
} else {
self.buttonBackgroundNode.backgroundColor = theme.backgroundColor
}
if gloss {
self.buttonGlossView = SolidRoundedButtonGlossView(color: theme.foregroundColor, cornerRadius: cornerRadius)
} else {
self.buttonGlossView = nil
}
self.buttonNode = HighlightTrackingButton()
self.titleNode = ImmediateTextView()
@ -536,9 +562,6 @@ public final class SolidRoundedButtonView: UIView {
super.init(frame: CGRect())
self.addSubview(self.buttonBackgroundNode)
if let buttonGlossView = self.buttonGlossView {
self.addSubview(buttonGlossView)
}
self.addSubview(self.buttonNode)
self.addSubview(self.titleNode)
self.addSubview(self.subtitleNode)
@ -575,6 +598,36 @@ public final class SolidRoundedButtonView: UIView {
if #available(iOS 13.0, *) {
self.buttonBackgroundNode.layer.cornerCurve = .continuous
}
if gloss {
let shimmerView = ShimmerEffectForegroundView()
self.shimmerView = shimmerView
if #available(iOS 13.0, *) {
shimmerView.layer.cornerCurve = .continuous
shimmerView.layer.cornerRadius = self.buttonCornerRadius
}
let borderView = UIView()
borderView.isUserInteractionEnabled = false
self.borderView = borderView
let borderMaskView = UIView()
borderMaskView.layer.borderWidth = 1.0 + UIScreenPixel
borderMaskView.layer.borderColor = UIColor.white.cgColor
borderMaskView.layer.cornerRadius = self.buttonCornerRadius
borderView.mask = borderMaskView
self.borderMaskView = borderMaskView
let borderShimmerView = ShimmerEffectForegroundView()
self.borderShimmerView = borderShimmerView
borderView.addSubview(borderShimmerView)
self.insertSubview(shimmerView, belowSubview: self.buttonNode)
self.insertSubview(borderView, belowSubview: self.buttonNode)
self.updateShimmerParameters()
}
}
required public init(coder: NSCoder) {
@ -601,7 +654,7 @@ public final class SolidRoundedButtonView: UIView {
CATransaction.begin()
let animation = CABasicAnimation(keyPath: "position.x")
animation.duration = Double.random(in: 1.8 ..< 2.3)
animation.duration = 4.5
animation.fromValue = previousValue
animation.toValue = newValue
animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
@ -660,6 +713,33 @@ public final class SolidRoundedButtonView: UIView {
self.subtitleNode.layer.animateAlpha(from: 0.55, to: 0.0, duration: 0.2)
}
func updateShimmerParameters() {
guard let shimmerView = self.shimmerView, let borderShimmerView = self.borderShimmerView else {
return
}
let color = self.theme.foregroundColor
let alpha: CGFloat
let borderAlpha: CGFloat
let compositingFilter: String?
if color.lightness > 0.5 {
alpha = 0.5
borderAlpha = 0.75
compositingFilter = "overlayBlendMode"
} else {
alpha = 0.2
borderAlpha = 0.3
compositingFilter = nil
}
shimmerView.update(backgroundColor: .clear, foregroundColor: color.withAlphaComponent(alpha), gradientSize: 70.0, duration: 3.0, horizontal: true)
borderShimmerView.update(backgroundColor: .clear, foregroundColor: color.withAlphaComponent(borderAlpha), gradientSize: 70.0, duration: 3.0, horizontal: true)
shimmerView.layer.compositingFilter = compositingFilter
borderShimmerView.layer.compositingFilter = compositingFilter
}
public func updateTheme(_ theme: SolidRoundedButtonTheme) {
guard theme !== self.theme else {
return
@ -680,7 +760,6 @@ public final class SolidRoundedButtonView: UIView {
self.buttonBackgroundNode.image = nil
}
self.buttonGlossView?.color = theme.foregroundColor
self.titleNode.attributedText = NSAttributedString(string: self.title ?? "", font: self.font == .bold ? Font.semibold(self.fontSize) : Font.regular(self.fontSize), textColor: theme.foregroundColor)
self.subtitleNode.attributedText = NSAttributedString(string: self.subtitle ?? "", font: Font.regular(14.0), textColor: theme.foregroundColor)
@ -689,6 +768,8 @@ public final class SolidRoundedButtonView: UIView {
if let width = self.validLayout {
_ = self.updateLayout(width: width, transition: .immediate)
}
self.updateShimmerParameters()
}
public func updateLayout(width: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
@ -702,14 +783,21 @@ public final class SolidRoundedButtonView: UIView {
let buttonFrame = CGRect(origin: CGPoint(), size: buttonSize)
transition.updateFrame(view: self.buttonBackgroundNode, frame: buttonFrame)
if let shimmerView = self.shimmerView, let borderView = self.borderView, let borderMaskView = self.borderMaskView, let borderShimmerView = self.borderShimmerView {
transition.updateFrame(view: shimmerView, frame: buttonFrame)
transition.updateFrame(view: borderView, frame: buttonFrame)
transition.updateFrame(view: borderMaskView, frame: buttonFrame)
transition.updateFrame(view: borderShimmerView, frame: buttonFrame)
shimmerView.updateAbsoluteRect(CGRect(origin: CGPoint(x: width * 4.0, y: 0.0), size: buttonSize), within: CGSize(width: width * 9.0, height: buttonHeight))
borderShimmerView.updateAbsoluteRect(CGRect(origin: CGPoint(x: width * 4.0, y: 0.0), size: buttonSize), within: CGSize(width: width * 9.0, height: buttonHeight))
}
if let buttonBackgroundAnimationView = self.buttonBackgroundAnimationView {
transition.updateFrame(view: buttonBackgroundAnimationView, frame: CGRect(origin: CGPoint(), size: CGSize(width: buttonSize.width * 2.4, height: buttonSize.height)))
self.setupGradientAnimations()
}
if let buttonGlossView = self.buttonGlossView {
transition.updateFrame(view: buttonGlossView, frame: buttonFrame)
}
transition.updateFrame(view: self.buttonNode, frame: buttonFrame)
if self.title != self.titleNode.attributedText?.string {
@ -770,112 +858,3 @@ public final class SolidRoundedButtonView: UIView {
self.pressed?()
}
}
private final class SolidRoundedButtonGlossViewParameters: NSObject {
let gradientColors: NSArray?
let cornerRadius: CGFloat
let progress: CGFloat
init(gradientColors: NSArray?, cornerRadius: CGFloat, progress: CGFloat) {
self.gradientColors = gradientColors
self.cornerRadius = cornerRadius
self.progress = progress
}
}
public final class SolidRoundedButtonGlossView: UIView {
public var color: UIColor {
didSet {
self.updateGradientColors()
self.setNeedsDisplay()
}
}
private var progress: CGFloat = 0.0
private var animator: ConstantDisplayLinkAnimator?
private let buttonCornerRadius: CGFloat
private var gradientColors: NSArray?
private let trackingLayer: HierarchyTrackingLayer
public init(color: UIColor, cornerRadius: CGFloat) {
self.color = color
self.buttonCornerRadius = cornerRadius
self.trackingLayer = HierarchyTrackingLayer()
super.init(frame: CGRect())
self.layer.addSublayer(self.trackingLayer)
self.isOpaque = false
var previousTime: CFAbsoluteTime?
self.animator = ConstantDisplayLinkAnimator(update: { [weak self] in
guard let strongSelf = self else {
return
}
let currentTime = CFAbsoluteTimeGetCurrent()
if let previousTime = previousTime {
var delta: CGFloat
if strongSelf.progress < 0.05 || strongSelf.progress > 0.95 {
delta = 0.001
} else {
delta = 0.009
}
delta *= CGFloat(currentTime - previousTime) * 60.0
var newProgress = strongSelf.progress + delta
if newProgress > 1.0 {
newProgress = 0.0
}
strongSelf.progress = newProgress
strongSelf.setNeedsDisplay()
}
previousTime = currentTime
})
self.updateGradientColors()
self.trackingLayer.didEnterHierarchy = { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.animator?.isPaused = false
}
self.trackingLayer.didExitHierarchy = { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.animator?.isPaused = true
}
}
required public init(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func updateGradientColors() {
let transparentColor = self.color.withAlphaComponent(0.0).cgColor
self.gradientColors = [transparentColor, transparentColor, self.color.withAlphaComponent(0.12).cgColor, transparentColor, transparentColor]
}
override public func draw(_ rect: CGRect) {
let parameters = SolidRoundedButtonGlossViewParameters(gradientColors: self.gradientColors, cornerRadius: self.buttonCornerRadius, progress: self.progress)
guard let gradientColors = parameters.gradientColors else {
return
}
let context = UIGraphicsGetCurrentContext()!
let path = UIBezierPath(roundedRect: bounds, cornerRadius: parameters.cornerRadius)
context.addPath(path.cgPath)
context.clip()
var locations: [CGFloat] = [0.0, 0.15, 0.5, 0.85, 1.0]
let colorSpace = CGColorSpaceCreateDeviceRGB()
let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)!
let x = -4.0 * bounds.size.width + 8.0 * bounds.size.width * parameters.progress
context.drawLinearGradient(gradient, start: CGPoint(x: x, y: 0.0), end: CGPoint(x: x + bounds.size.width, y: 0.0), options: CGGradientDrawingOptions())
}
}

View File

@ -9,7 +9,8 @@ public struct UserLimitsConfiguration: Equatable {
public let maxFavedStickerCount: Int32
public let maxFoldersCount: Int32
public let maxFolderChatsCount: Int32
public let maxTextLengthCount: Int32
public let maxCaptionLengthCount: Int32
public let maxUploadFileParts: Int32
public static var defaultValue: UserLimitsConfiguration {
return UserLimitsConfiguration(
@ -20,7 +21,8 @@ public struct UserLimitsConfiguration: Equatable {
maxFavedStickerCount: 5,
maxFoldersCount: 10,
maxFolderChatsCount: 100,
maxTextLengthCount: 4096
maxCaptionLengthCount: 1024,
maxUploadFileParts: 4000
)
}
@ -32,7 +34,8 @@ public struct UserLimitsConfiguration: Equatable {
maxFavedStickerCount: Int32,
maxFoldersCount: Int32,
maxFolderChatsCount: Int32,
maxTextLengthCount: Int32
maxCaptionLengthCount: Int32,
maxUploadFileParts: Int32
) {
self.maxPinnedChatCount = maxPinnedChatCount
self.maxChannelsCount = maxChannelsCount
@ -41,7 +44,8 @@ public struct UserLimitsConfiguration: Equatable {
self.maxFavedStickerCount = maxFavedStickerCount
self.maxFoldersCount = maxFoldersCount
self.maxFolderChatsCount = maxFolderChatsCount
self.maxTextLengthCount = maxTextLengthCount
self.maxCaptionLengthCount = maxCaptionLengthCount
self.maxUploadFileParts = maxUploadFileParts
}
}
@ -65,16 +69,7 @@ extension UserLimitsConfiguration {
self.maxFavedStickerCount = getValue("stickers_faved_limit", orElse: defaultValue.maxPinnedChatCount)
self.maxFoldersCount = getValue("dialog_filters_limit", orElse: defaultValue.maxPinnedChatCount)
self.maxFolderChatsCount = getValue("dialog_filters_chats_limit", orElse: defaultValue.maxPinnedChatCount)
self.maxTextLengthCount = getValue("message_text_length_limit", orElse: defaultValue.maxPinnedChatCount)
}
}
public func getUserLimits(postbox: Postbox) -> Signal<Never, NoError> {
return postbox.preferencesView(keys: [PreferencesKeys.appConfiguration])
|> mapToSignal { preferencesView -> Signal<Never, NoError> in
let appConfiguration: AppConfiguration = preferencesView.values[PreferencesKeys.appConfiguration]?.get(AppConfiguration.self) ?? .defaultValue
let configuration = UserLimitsConfiguration(appConfiguration: appConfiguration, isPremium: false)
print(configuration)
return .never()
self.maxCaptionLengthCount = getValue("caption_length_limit", orElse: defaultValue.maxCaptionLengthCount)
self.maxUploadFileParts = getValue("upload_max_fileparts", orElse: defaultValue.maxUploadFileParts)
}
}

View File

@ -55,7 +55,8 @@ public enum EngineConfiguration {
public let maxFavedStickerCount: Int32
public let maxFoldersCount: Int32
public let maxFolderChatsCount: Int32
public let maxTextLengthCount: Int32
public let maxCaptionLengthCount: Int32
public let maxUploadFileParts: Int32
public static var defaultValue: UserLimits {
return UserLimits(UserLimitsConfiguration.defaultValue)
@ -69,7 +70,8 @@ public enum EngineConfiguration {
maxFavedStickerCount: Int32,
maxFoldersCount: Int32,
maxFolderChatsCount: Int32,
maxTextLengthCount: Int32
maxCaptionLengthCount: Int32,
maxUploadFileParts: Int32
) {
self.maxPinnedChatCount = maxPinnedChatCount
self.maxChannelsCount = maxChannelsCount
@ -78,7 +80,8 @@ public enum EngineConfiguration {
self.maxFavedStickerCount = maxFavedStickerCount
self.maxFoldersCount = maxFoldersCount
self.maxFolderChatsCount = maxFolderChatsCount
self.maxTextLengthCount = maxTextLengthCount
self.maxCaptionLengthCount = maxCaptionLengthCount
self.maxUploadFileParts = maxUploadFileParts
}
}
}
@ -112,7 +115,8 @@ extension EngineConfiguration.UserLimits {
maxFavedStickerCount: userLimitsConfiguration.maxFavedStickerCount,
maxFoldersCount: userLimitsConfiguration.maxFoldersCount,
maxFolderChatsCount: userLimitsConfiguration.maxFolderChatsCount,
maxTextLengthCount: userLimitsConfiguration.maxTextLengthCount
maxCaptionLengthCount: userLimitsConfiguration.maxCaptionLengthCount,
maxUploadFileParts: userLimitsConfiguration.maxUploadFileParts
)
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "Profile.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,79 @@
%PDF-1.7
1 0 obj
<< >>
endobj
2 0 obj
<< /Length 3 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 5.000000 5.000000 cm
1.000000 1.000000 1.000000 scn
10.000000 0.000000 m
15.522847 0.000000 20.000000 4.477153 20.000000 10.000000 c
20.000000 15.522848 15.522847 20.000000 10.000000 20.000000 c
4.477152 20.000000 0.000000 15.522848 0.000000 10.000000 c
0.000000 4.477153 4.477152 0.000000 10.000000 0.000000 c
h
14.751925 9.167950 m
8.554701 5.036467 l
7.890146 4.593431 7.000000 5.069821 7.000000 5.868517 c
7.000000 14.131483 l
7.000000 14.930179 7.890145 15.406569 8.554700 14.963533 c
14.751925 10.832050 l
15.345657 10.436228 15.345657 9.563771 14.751925 9.167950 c
h
f*
n
Q
endstream
endobj
3 0 obj
648
endobj
4 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
/Resources 1 0 R
/Contents 2 0 R
/Parent 5 0 R
>>
endobj
5 0 obj
<< /Kids [ 4 0 R ]
/Count 1
/Type /Pages
>>
endobj
6 0 obj
<< /Pages 5 0 R
/Type /Catalog
>>
endobj
xref
0 7
0000000000 65535 f
0000000010 00000 n
0000000034 00000 n
0000000738 00000 n
0000000760 00000 n
0000000933 00000 n
0000001007 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 6 0 R
/Size 7
>>
startxref
1066
%%EOF

View File

@ -0,0 +1,97 @@
%PDF-1.7
1 0 obj
<< >>
endobj
2 0 obj
<< /Length 3 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 4.575439 5.396620 cm
1.000000 1.000000 1.000000 scn
9.882595 3.317793 m
5.183871 0.439333 l
4.695292 0.140028 4.056584 0.293465 3.757279 0.782043 c
3.611097 1.020666 3.567514 1.308224 3.636422 1.579445 c
4.363782 4.442359 l
4.626348 5.475822 5.333547 6.339716 6.294793 6.801228 c
11.420868 9.262346 l
11.659848 9.377085 11.760565 9.663830 11.645826 9.902809 c
11.552907 10.096345 11.342772 10.204644 11.131233 10.168021 c
5.425255 9.180172 l
4.265362 8.979365 3.075904 9.299660 2.173681 10.055747 c
0.371115 11.566348 l
-0.068036 11.934370 -0.125698 12.588713 0.242323 13.027864 c
0.421316 13.241451 0.678710 13.374050 0.956533 13.395792 c
6.463908 13.826798 l
6.852990 13.857248 7.192065 14.103476 7.341429 14.464033 c
9.466071 19.592789 l
9.685358 20.122133 10.292245 20.373486 10.821590 20.154200 c
11.075765 20.048904 11.277706 19.846962 11.383000 19.592789 c
13.507643 14.464033 l
13.657007 14.103476 13.996081 13.857248 14.385163 13.826798 c
19.922800 13.393423 l
20.494022 13.348721 20.920851 12.849413 20.876146 12.278191 c
20.854641 12.003400 20.724669 11.748462 20.514915 11.569643 c
16.291594 7.969165 l
15.994287 7.715703 15.864580 7.316709 15.956014 6.936873 c
17.254391 1.543129 l
17.388485 0.986073 17.045605 0.425785 16.488548 0.291691 c
16.220879 0.227257 15.938575 0.271862 15.703809 0.415680 c
10.966477 3.317793 l
10.633905 3.521528 10.215167 3.521528 9.882595 3.317793 c
h
f*
n
Q
endstream
endobj
3 0 obj
1468
endobj
4 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
/Resources 1 0 R
/Contents 2 0 R
/Parent 5 0 R
>>
endobj
5 0 obj
<< /Kids [ 4 0 R ]
/Count 1
/Type /Pages
>>
endobj
6 0 obj
<< /Pages 5 0 R
/Type /Catalog
>>
endobj
xref
0 7
0000000000 65535 f
0000000010 00000 n
0000000034 00000 n
0000001558 00000 n
0000001581 00000 n
0000001754 00000 n
0000001828 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 6 0 R
/Size 7
>>
startxref
1887
%%EOF

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "Badge.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,9 @@
{
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"provides-namespace" : true
}
}

View File

@ -0,0 +1,107 @@
%PDF-1.7
1 0 obj
<< >>
endobj
2 0 obj
<< /Length 3 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 5.500000 8.728230 cm
1.000000 1.000000 1.000000 scn
0.426623 12.091039 m
0.879073 12.407753 1.502603 12.297718 1.819318 11.845269 c
4.500086 8.015600 l
7.180854 11.845269 l
7.497569 12.297718 8.121099 12.407753 8.573548 12.091039 c
9.025997 11.774324 9.136032 11.150794 8.819318 10.698344 c
5.720742 6.271807 l
8.819318 1.845269 l
9.136032 1.392819 9.025997 0.769289 8.573548 0.452575 c
8.121099 0.135860 7.497569 0.245895 7.180854 0.698344 c
4.500086 4.528013 l
1.819318 0.698344 l
1.502603 0.245895 0.879073 0.135860 0.426623 0.452575 c
-0.025826 0.769289 -0.135861 1.392819 0.180854 1.845269 c
3.279430 6.271807 l
0.180854 10.698344 l
-0.135861 11.150794 -0.025826 11.774324 0.426623 12.091039 c
h
15.000086 10.271807 m
13.895516 10.271807 13.000086 9.376376 13.000086 8.271807 c
13.000086 7.771807 l
13.000086 7.219522 12.552371 6.771807 12.000086 6.771807 c
11.447801 6.771807 11.000086 7.219522 11.000086 7.771807 c
11.000086 8.271807 l
11.000086 10.480946 12.790947 12.271807 15.000086 12.271807 c
17.209225 12.271807 19.000086 10.480946 19.000086 8.271807 c
19.000086 8.125909 l
19.000086 6.610820 18.144077 5.225768 16.788940 4.548200 c
13.552872 2.930165 l
13.286202 2.796831 13.096846 2.553910 13.028315 2.271807 c
18.000086 2.271807 l
18.552370 2.271807 19.000086 1.824092 19.000086 1.271807 c
19.000086 0.719522 18.552370 0.271807 18.000086 0.271807 c
12.000086 0.271807 l
11.447801 0.271807 11.000086 0.719522 11.000086 1.271807 c
11.000086 2.035739 l
11.000086 3.172054 11.642092 4.210844 12.658444 4.719020 c
15.894513 6.337054 l
16.572081 6.675838 17.000086 7.368365 17.000086 8.125909 c
17.000086 8.271807 l
17.000086 9.376376 16.104656 10.271807 15.000086 10.271807 c
h
f*
n
Q
endstream
endobj
3 0 obj
1763
endobj
4 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
/Resources 1 0 R
/Contents 2 0 R
/Parent 5 0 R
>>
endobj
5 0 obj
<< /Kids [ 4 0 R ]
/Count 1
/Type /Pages
>>
endobj
6 0 obj
<< /Pages 5 0 R
/Type /Catalog
>>
endobj
xref
0 7
0000000000 65535 f
0000000010 00000 n
0000000034 00000 n
0000001853 00000 n
0000001876 00000 n
0000002049 00000 n
0000002123 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 6 0 R
/Size 7
>>
startxref
2182
%%EOF

View File

@ -0,0 +1,127 @@
%PDF-1.7
1 0 obj
<< >>
endobj
2 0 obj
<< /Length 3 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 7.000000 4.804735 cm
1.000000 1.000000 1.000000 scn
0.707107 18.902372 m
0.316583 19.292896 -0.316583 19.292896 -0.707107 18.902372 c
-1.097631 18.511847 -1.097631 17.878683 -0.707107 17.488157 c
0.707107 18.902372 l
h
15.292893 1.488157 m
15.683417 1.097633 16.316582 1.097633 16.707108 1.488157 c
17.097631 1.878683 17.097631 2.511847 16.707108 2.902371 c
15.292893 1.488157 l
h
-0.707107 17.488157 m
15.292893 1.488157 l
16.707108 2.902371 l
0.707107 18.902372 l
-0.707107 17.488157 l
h
f
n
Q
q
1.000000 0.000000 -0.000000 1.000000 6.000000 5.493464 cm
1.000000 1.000000 1.000000 scn
6.634244 6.005881 m
4.000000 6.005881 l
1.790861 6.005881 0.000000 7.796742 0.000000 10.005881 c
0.000000 11.259813 0.576984 12.378983 1.479941 13.112381 c
10.638983 3.953340 l
9.078063 5.167388 l
8.815662 5.371477 8.684463 5.473521 8.546436 5.558720 c
8.166211 5.793416 7.738360 5.940215 7.294138 5.988393 c
7.132880 6.005881 6.966668 6.005881 6.634244 6.005881 c
h
16.036222 1.384529 m
3.452085 13.968666 l
3.631217 13.993204 3.814128 14.005881 4.000000 14.005881 c
6.634244 14.005881 l
6.966668 14.005881 7.132880 14.005881 7.294138 14.023371 c
7.738360 14.071548 8.166211 14.218346 8.546436 14.453043 c
8.684463 14.538241 8.815662 14.640285 9.078062 14.844374 c
11.835389 16.988962 l
11.835398 16.988970 l
11.835412 16.988979 l
13.492673 18.277960 14.321305 18.922451 15.017017 18.916531 c
15.622235 18.911381 16.192566 18.632441 16.568199 18.157871 c
17.000000 17.612343 17.000000 16.562572 17.000000 14.463034 c
17.000000 5.548728 l
17.000000 3.449189 17.000000 2.399420 16.568199 1.853891 c
16.418444 1.664694 16.237743 1.506588 16.036222 1.384529 c
h
4.000000 3.119659 m
4.000000 3.639083 4.000000 3.898794 4.094136 4.100118 c
4.193412 4.312436 4.364113 4.483137 4.576432 4.582415 c
4.777756 4.676550 5.037467 4.676550 5.556890 4.676550 c
6.443110 4.676550 l
6.962533 4.676550 7.222244 4.676550 7.423568 4.582415 c
7.635887 4.483137 7.806588 4.312436 7.905864 4.100118 c
8.000000 3.898794 8.000000 3.639083 8.000000 3.119659 c
8.000000 2.005878 l
8.000000 0.901310 7.104569 0.005878 6.000000 0.005878 c
4.895431 0.005878 4.000000 0.901310 4.000000 2.005878 c
4.000000 3.119659 l
h
f*
n
Q
endstream
endobj
3 0 obj
2250
endobj
4 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
/Resources 1 0 R
/Contents 2 0 R
/Parent 5 0 R
>>
endobj
5 0 obj
<< /Kids [ 4 0 R ]
/Count 1
/Type /Pages
>>
endobj
6 0 obj
<< /Pages 5 0 R
/Type /Catalog
>>
endobj
xref
0 7
0000000000 65535 f
0000000010 00000 n
0000000034 00000 n
0000002340 00000 n
0000002363 00000 n
0000002536 00000 n
0000002610 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 6 0 R
/Size 7
>>
startxref
2669
%%EOF

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "Ads.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "Reactions.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,75 @@
%PDF-1.7
1 0 obj
<< >>
endobj
2 0 obj
<< /Length 3 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 6.000000 6.327723 cm
1.000000 1.000000 1.000000 scn
9.000000 0.000000 m
9.231684 0.000000 9.561386 0.169308 9.828713 0.338614 c
14.809901 3.546535 18.000000 7.306931 18.000000 11.120792 c
18.000000 14.391089 15.745544 16.672277 12.902970 16.672277 c
11.129704 16.672277 9.801980 15.692080 9.000000 14.221783 c
8.215841 15.683168 6.879208 16.672277 5.105941 16.672277 c
2.263366 16.672277 0.000000 14.391089 0.000000 11.120792 c
0.000000 7.306931 3.190099 3.546535 8.171288 0.338614 c
8.447525 0.169308 8.777228 0.000000 9.000000 0.000000 c
h
f
n
Q
endstream
endobj
3 0 obj
615
endobj
4 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
/Resources 1 0 R
/Contents 2 0 R
/Parent 5 0 R
>>
endobj
5 0 obj
<< /Kids [ 4 0 R ]
/Count 1
/Type /Pages
>>
endobj
6 0 obj
<< /Pages 5 0 R
/Type /Catalog
>>
endobj
xref
0 7
0000000000 65535 f
0000000010 00000 n
0000000034 00000 n
0000000705 00000 n
0000000727 00000 n
0000000900 00000 n
0000000974 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 6 0 R
/Size 7
>>
startxref
1033
%%EOF

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "Speed.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,112 @@
%PDF-1.7
1 0 obj
<< >>
endobj
2 0 obj
<< /Length 3 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 12.294678 9.000013 cm
1.000000 1.000000 1.000000 scn
2.804684 0.000000 m
4.353669 0.000000 5.609368 1.274049 5.609368 2.845666 c
5.609368 4.417284 4.353669 5.691332 2.804684 5.691332 c
1.255700 5.691332 0.000000 4.417284 0.000000 2.845666 c
0.000000 1.274049 1.255700 0.000000 2.804684 0.000000 c
h
f
n
Q
q
1.000000 0.000000 -0.000000 1.000000 17.217842 12.915028 cm
1.000000 1.000000 1.000000 scn
0.202088 2.121548 m
0.590437 1.919203 0.904981 1.668863 1.145721 1.370527 c
1.385267 1.073672 1.586731 0.733007 1.750114 0.348534 c
1.831343 0.157340 2.052196 0.068219 2.243381 0.149469 c
2.291679 0.169995 2.335148 0.200401 2.369815 0.239834 c
9.410900 7.784840 l
9.694361 8.088588 9.677914 8.564614 9.374166 8.848076 c
9.108430 9.096064 8.703391 9.118585 8.411800 8.901587 c
0.153150 2.755597 l
-0.014851 2.633397 -0.049773 2.397816 0.073990 2.230975 c
0.107876 2.185294 0.151681 2.147893 0.202088 2.121548 c
h
f*
n
Q
q
1.000000 0.000000 -0.000000 1.000000 4.000000 9.669685 cm
1.000000 1.000000 1.000000 scn
22.003891 1.104424 m
22.003891 0.423388 21.718653 -0.000001 20.321684 0.037580 c
18.924715 0.075160 18.613684 0.350485 18.613684 1.031520 c
18.613684 2.190733 18.463017 3.313196 18.169523 4.353136 c
20.529579 7.033363 l
21.472769 5.266989 22.003891 3.231236 22.003891 1.104424 c
h
11.001945 12.973248 m
13.460424 12.973248 15.679217 12.126817 17.469568 10.716667 c
15.011708 9.067489 l
13.884562 9.852736 12.507846 10.307699 10.902919 10.307699 c
5.969246 10.307699 2.068742 6.093097 2.068742 1.116306 c
2.068742 0.101034 1.795549 0.039256 1.200545 0.035501 c
0.968838 0.035501 l
0.363240 0.039256 0.000000 0.101034 0.000000 1.116306 c
0.000000 7.455166 4.718006 12.973248 11.001945 12.973248 c
h
f
n
Q
endstream
endobj
3 0 obj
1778
endobj
4 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
/Resources 1 0 R
/Contents 2 0 R
/Parent 5 0 R
>>
endobj
5 0 obj
<< /Kids [ 4 0 R ]
/Count 1
/Type /Pages
>>
endobj
6 0 obj
<< /Pages 5 0 R
/Type /Catalog
>>
endobj
xref
0 7
0000000000 65535 f
0000000010 00000 n
0000000034 00000 n
0000001868 00000 n
0000001891 00000 n
0000002064 00000 n
0000002138 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 6 0 R
/Size 7
>>
startxref
2197
%%EOF

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "Stickers.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,149 @@
%PDF-1.7
1 0 obj
<< >>
endobj
2 0 obj
<< /Length 3 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 6.000000 6.000000 cm
1.000000 1.000000 1.000000 scn
0.435974 15.815962 m
0.000000 14.960315 0.000000 13.840210 0.000000 11.600000 c
0.000000 5.538462 l
0.000000 4.106961 0.000000 3.391212 0.181116 2.809988 c
0.572199 1.554960 1.554961 0.572199 2.809988 0.181116 c
3.391211 0.000000 4.106961 0.000000 5.538461 0.000000 c
7.132836 0.000000 8.332593 0.000000 9.296358 0.049431 c
9.392109 0.065231 9.458178 0.084789 9.510882 0.106621 c
9.837995 0.242115 10.097885 0.502007 10.233379 0.829117 c
10.269915 0.917324 10.300633 1.046162 10.317467 1.296604 c
10.334650 1.552246 10.335000 1.881901 10.335000 2.365276 c
10.335000 2.390583 l
10.334996 3.020454 10.334994 3.541773 10.356327 3.976810 c
9.927901 3.881188 9.474939 3.834996 8.999999 3.834996 c
7.566772 3.834998 6.432307 4.408777 5.671366 4.962191 c
5.289614 5.239828 4.994541 5.517408 4.793315 5.727383 c
4.692429 5.832656 4.614337 5.921766 4.559963 5.986553 c
4.532754 6.018972 4.511416 6.045382 4.496066 6.064772 c
4.477521 6.088497 l
4.471629 6.096197 l
4.469531 6.098966 l
4.468694 6.100076 l
4.468329 6.100561 l
4.468162 6.100784 4.468000 6.101000 5.000000 6.500000 c
5.532000 6.899000 l
5.531705 6.899393 l
5.538846 6.890306 l
5.546690 6.880400 5.560019 6.863840 5.578709 6.841572 c
5.616131 6.796984 5.674759 6.729844 5.753560 6.647617 c
5.911709 6.482592 6.147886 6.260172 6.453634 6.037808 c
7.067693 5.591221 7.933228 5.164998 9.000001 5.164996 c
9.630135 5.164996 10.171014 5.264124 10.633532 5.448587 c
10.639013 5.461485 10.644600 5.474353 10.650297 5.487185 c
11.018882 6.317377 11.682623 6.981118 12.512815 7.349703 c
12.904016 7.523386 13.325486 7.596285 13.811561 7.631045 c
14.286482 7.665009 14.873948 7.665005 15.609417 7.665000 c
15.634724 7.665000 l
16.118099 7.665000 16.447754 7.665350 16.703396 7.682533 c
16.953838 7.699367 17.082676 7.730085 17.170883 7.766621 c
17.497993 7.902115 17.757885 8.162005 17.893379 8.489118 c
17.915211 8.541822 17.934769 8.607892 17.950569 8.703643 c
18.000000 9.667408 18.000000 10.867164 18.000000 12.461538 c
18.000000 13.893039 18.000000 14.608788 17.818884 15.190012 c
17.427801 16.445040 16.445040 17.427801 15.190012 17.818884 c
14.608788 18.000000 13.893039 18.000000 12.461538 18.000000 c
6.400000 18.000000 l
4.159790 18.000000 3.039685 18.000000 2.184038 17.564026 c
1.431390 17.180532 0.819467 16.568611 0.435974 15.815962 c
h
17.652130 6.526627 m
17.381193 6.419186 17.097967 6.376053 16.792589 6.355527 c
16.487034 6.334990 16.112497 6.334994 15.657384 6.335000 c
15.634724 6.335000 l
14.868204 6.335000 14.328320 6.334603 13.906430 6.304433 c
13.490259 6.274672 13.242615 6.218527 13.052504 6.134122 c
12.523582 5.899294 12.100706 5.476418 11.865878 4.947496 c
11.781473 4.757385 11.725328 4.509741 11.695567 4.093570 c
11.665397 3.671680 11.665000 3.131796 11.665000 2.365276 c
11.665000 2.342616 l
11.665006 1.887503 11.665010 1.512966 11.644473 1.207411 c
11.623947 0.902033 11.580814 0.618807 11.473373 0.347870 c
11.542342 0.366713 11.610334 0.386574 11.677527 0.407513 c
14.501338 1.287447 16.712553 3.498662 17.592487 6.322473 c
17.613426 6.389666 17.633287 6.457658 17.652130 6.526627 c
h
5.531581 6.899558 m
5.311111 7.192892 4.894629 7.252222 4.601000 7.032000 c
4.307185 6.811639 4.247638 6.394815 4.468000 6.101000 c
5.000000 6.500000 l
5.532000 6.899000 5.531850 6.899200 5.531705 6.899393 c
5.531581 6.899558 l
h
7.500000 11.187500 m
7.500000 10.324555 6.940356 9.625000 6.250000 9.625000 c
5.559644 9.625000 5.000000 10.324555 5.000000 11.187500 c
5.000000 12.050446 5.559644 12.750000 6.250000 12.750000 c
6.940356 12.750000 7.500000 12.050446 7.500000 11.187500 c
h
13.000000 11.187500 m
13.000000 10.324555 12.440355 9.625000 11.750000 9.625000 c
11.059645 9.625000 10.500000 10.324555 10.500000 11.187500 c
10.500000 12.050446 11.059645 12.750000 11.750000 12.750000 c
12.440355 12.750000 13.000000 12.050446 13.000000 11.187500 c
h
f*
n
Q
endstream
endobj
3 0 obj
3949
endobj
4 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
/Resources 1 0 R
/Contents 2 0 R
/Parent 5 0 R
>>
endobj
5 0 obj
<< /Kids [ 4 0 R ]
/Count 1
/Type /Pages
>>
endobj
6 0 obj
<< /Pages 5 0 R
/Type /Catalog
>>
endobj
xref
0 7
0000000000 65535 f
0000000010 00000 n
0000000034 00000 n
0000004039 00000 n
0000004062 00000 n
0000004235 00000 n
0000004309 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 6 0 R
/Size 7
>>
startxref
4368
%%EOF

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "Files.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,119 @@
%PDF-1.7
1 0 obj
<< >>
endobj
2 0 obj
<< /Length 3 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 7.000000 6.000000 cm
1.000000 1.000000 1.000000 scn
5.330000 18.000000 m
5.807298 18.000000 6.123703 17.999550 6.367812 17.982895 c
6.604466 17.966749 6.711959 17.938187 6.777740 17.910938 c
7.064423 17.792191 7.292191 17.564423 7.410939 17.277740 c
7.438186 17.211960 7.466748 17.104465 7.482895 16.867813 c
7.499550 16.623703 7.500000 16.307299 7.500000 15.830000 c
7.500000 13.530003 l
7.500000 13.496872 l
7.499987 12.965037 7.499976 12.516354 7.530005 12.148819 c
7.561447 11.763987 7.629904 11.395631 7.808452 11.045210 c
8.079773 10.512712 8.512709 10.079777 9.045207 9.808455 c
9.395627 9.629908 9.763983 9.561451 10.148815 9.530008 c
10.516351 9.499980 10.965034 9.499990 11.496870 9.500004 c
11.530001 9.500004 l
13.829997 9.500004 l
14.307296 9.500004 14.623703 9.499555 14.867812 9.482899 c
15.104466 9.466752 15.211960 9.438190 15.277740 9.410942 c
15.564423 9.292194 15.792191 9.064426 15.910938 8.777744 c
15.938186 8.711964 15.966748 8.604470 15.982895 8.367816 c
15.999551 8.123706 16.000000 7.807300 16.000000 7.330000 c
16.000000 4.800000 l
16.000000 3.119843 16.000000 2.279763 15.673019 1.638029 c
15.385400 1.073542 14.926457 0.614601 14.361972 0.326981 c
13.720237 0.000000 12.880157 0.000000 11.200000 0.000000 c
4.800000 0.000000 l
3.119843 0.000000 2.279764 0.000000 1.638029 0.326981 c
1.073542 0.614601 0.614601 1.073542 0.326980 1.638029 c
0.000000 2.279763 0.000000 3.119843 0.000000 4.800000 c
0.000000 13.200001 l
0.000000 14.880157 0.000000 15.720236 0.326980 16.361971 c
0.614601 16.926458 1.073542 17.385399 1.638029 17.673019 c
2.279764 18.000000 3.119843 18.000000 4.800000 18.000000 c
5.330000 18.000000 l
h
15.621112 11.042418 m
15.558301 11.176268 15.488032 11.306717 15.410561 11.433140 c
15.163194 11.836806 14.817290 12.182710 14.125484 12.874516 c
10.874516 16.125484 l
10.182710 16.817291 9.836806 17.163193 9.433140 17.410561 c
9.306716 17.488033 9.176266 17.558304 9.042415 17.621115 c
9.097363 17.412268 9.124138 17.199299 9.139045 16.980810 c
9.160017 16.673437 9.160009 16.300030 9.160000 15.857494 c
9.160000 15.830000 l
9.160000 13.530003 l
9.160000 12.956255 9.160645 12.575861 9.184492 12.283996 c
9.207546 12.001820 9.248083 11.876238 9.287522 11.798835 c
9.399694 11.578686 9.578682 11.399698 9.798831 11.287526 c
9.876234 11.248087 10.001816 11.207550 10.283993 11.184496 c
10.575858 11.160649 10.956251 11.160004 11.530001 11.160004 c
13.829997 11.160004 l
13.857491 11.160004 l
14.300028 11.160013 14.673436 11.160021 14.980811 11.139048 c
15.199297 11.124141 15.412265 11.097366 15.621112 11.042418 c
h
f*
n
Q
endstream
endobj
3 0 obj
2637
endobj
4 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
/Resources 1 0 R
/Contents 2 0 R
/Parent 5 0 R
>>
endobj
5 0 obj
<< /Kids [ 4 0 R ]
/Count 1
/Type /Pages
>>
endobj
6 0 obj
<< /Pages 5 0 R
/Type /Catalog
>>
endobj
xref
0 7
0000000000 65535 f
0000000010 00000 n
0000000034 00000 n
0000002727 00000 n
0000002750 00000 n
0000002923 00000 n
0000002997 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 6 0 R
/Size 7
>>
startxref
3056
%%EOF

View File

@ -375,8 +375,8 @@ public class ShareRootControllerImpl {
return .single(.done)
}
switch state {
case .preparing:
return .single(.preparing)
case let .preparing(long):
return .single(.preparing(long))
case let .progress(value):
return .single(.progress(value))
case let .userInteractionRequired(value):

View File

@ -15,6 +15,7 @@ import AppBundle
import DatePickerNode
import DebugSettingsUI
import TabBarUI
import PremiumUI
public final class TelegramRootController: NavigationController {
private let context: AccountContext
@ -129,8 +130,12 @@ public final class TelegramRootController: NavigationController {
self.accountSettingsController = accountSettingsController
self.rootTabController = tabBarController
self.pushViewController(tabBarController, animated: false)
let _ = getUserLimits(postbox: self.context.account.postbox).start()
Queue.mainQueue().after(1.0) {
// let screen = PremiumLimitScreen(context: self.context, subject: .pins, action: {})
let screen = PremiumIntroScreen(context: self.context, action: {})
self.chatListController?.push(screen)
}
}
public func updateRootControllers(showCallsTab: Bool) {