mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various improvements
This commit is contained in:
parent
6b7ed2e6ac
commit
ff7cc72a71
@ -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",
|
||||
|
@ -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";
|
||||
|
@ -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] = []
|
||||
|
||||
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)
|
||||
|
||||
})))
|
||||
let limitScreen = PremiumLimitScreen(context: context, subject: .pins, action: {
|
||||
let premiumScreen = PremiumIntroScreen(context: context, action: {
|
||||
|
||||
c.pushItems(items: .single(ContextController.Items(content: .list(subItems))))
|
||||
})
|
||||
chatListController?.push(premiumScreen)
|
||||
})
|
||||
chatListController?.push(limitScreen)
|
||||
}
|
||||
|
||||
})
|
||||
})))
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)?
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
20
submodules/Components/SheetComponent/BUILD
Normal file
20
submodules/Components/SheetComponent/BUILD
Normal 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",
|
||||
],
|
||||
)
|
@ -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)
|
||||
}
|
||||
}
|
@ -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() }
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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",
|
||||
|
BIN
submodules/PremiumUI/Resources/flecks.png
Normal file
BIN
submodules/PremiumUI/Resources/flecks.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 207 KiB |
BIN
submodules/PremiumUI/Resources/shine.png
Normal file
BIN
submodules/PremiumUI/Resources/shine.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
BIN
submodules/PremiumUI/Resources/speckle.png
Normal file
BIN
submodules/PremiumUI/Resources/speckle.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1002 B |
BIN
submodules/PremiumUI/Resources/star.scn
Normal file
BIN
submodules/PremiumUI/Resources/star.scn
Normal file
Binary file not shown.
BIN
submodules/PremiumUI/Resources/texture.png
Normal file
BIN
submodules/PremiumUI/Resources/texture.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 38 KiB |
@ -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
360
submodules/PremiumUI/Sources/PremiumLimitScreen.swift
Normal file
360
submodules/PremiumUI/Sources/PremiumLimitScreen.swift
Normal 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
|
||||
}
|
||||
}
|
@ -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:
|
||||
|
@ -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,6 +694,15 @@ 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
|
||||
|
@ -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,7 +120,9 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri
|
||||
}
|
||||
}
|
||||
|
||||
return loadValues(asset)
|
||||
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)
|
||||
@ -151,6 +154,7 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
} else if let data = value["data"] as? Data {
|
||||
let fileName = value["fileName"] as? String
|
||||
let mimeType = (value["mimeType"] as? String) ?? "application/octet-stream"
|
||||
@ -194,7 +198,9 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri
|
||||
return disposable
|
||||
}
|
||||
|
||||
return convertedData
|
||||
return .single(.preparing(true))
|
||||
|> then(
|
||||
convertedData
|
||||
|> castError(Void.self)
|
||||
|> mapToSignal { data, dimensions, duration, converted in
|
||||
var attributes: [TelegramMediaFileAttribute] = []
|
||||
@ -217,10 +223,13 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
} 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)))
|
||||
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 {
|
||||
@ -230,6 +239,7 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri
|
||||
return .single(.done(.media(media)))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
} else {
|
||||
var thumbnailData: Data?
|
||||
@ -237,7 +247,10 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri
|
||||
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)
|
||||
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 {
|
||||
@ -247,6 +260,7 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri
|
||||
return .single(.done(.media(media)))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
} else if let url = value["audio"] as? URL {
|
||||
if let audioData = try? Data(contentsOf: url, options: [.mappedIfSafe]) {
|
||||
@ -262,7 +276,10 @@ 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)
|
||||
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 {
|
||||
@ -272,15 +289,19 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri
|
||||
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):
|
||||
|
@ -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
|
||||
@ -516,12 +548,6 @@ public final class SolidRoundedButtonView: UIView {
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
12
submodules/TelegramUI/Images.xcassets/Premium/Perk/Avatar.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Premium/Perk/Avatar.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Profile.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
79
submodules/TelegramUI/Images.xcassets/Premium/Perk/Avatar.imageset/Profile.pdf
vendored
Normal file
79
submodules/TelegramUI/Images.xcassets/Premium/Perk/Avatar.imageset/Profile.pdf
vendored
Normal 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
|
97
submodules/TelegramUI/Images.xcassets/Premium/Perk/Badge.imageset/Badge.pdf
vendored
Normal file
97
submodules/TelegramUI/Images.xcassets/Premium/Perk/Badge.imageset/Badge.pdf
vendored
Normal 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
|
12
submodules/TelegramUI/Images.xcassets/Premium/Perk/Badge.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Premium/Perk/Badge.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Badge.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"provides-namespace" : true
|
||||
}
|
||||
}
|
107
submodules/TelegramUI/Images.xcassets/Premium/Perk/Limits.imageset/2x.pdf
vendored
Normal file
107
submodules/TelegramUI/Images.xcassets/Premium/Perk/Limits.imageset/2x.pdf
vendored
Normal 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
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "2x.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
127
submodules/TelegramUI/Images.xcassets/Premium/Perk/NoAds.imageset/Ads.pdf
vendored
Normal file
127
submodules/TelegramUI/Images.xcassets/Premium/Perk/NoAds.imageset/Ads.pdf
vendored
Normal 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
|
12
submodules/TelegramUI/Images.xcassets/Premium/Perk/NoAds.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Premium/Perk/NoAds.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Ads.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
12
submodules/TelegramUI/Images.xcassets/Premium/Perk/Reactions.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Premium/Perk/Reactions.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Reactions.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
75
submodules/TelegramUI/Images.xcassets/Premium/Perk/Reactions.imageset/Reactions.pdf
vendored
Normal file
75
submodules/TelegramUI/Images.xcassets/Premium/Perk/Reactions.imageset/Reactions.pdf
vendored
Normal 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
|
12
submodules/TelegramUI/Images.xcassets/Premium/Perk/Speed.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Premium/Perk/Speed.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Speed.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
112
submodules/TelegramUI/Images.xcassets/Premium/Perk/Speed.imageset/Speed.pdf
vendored
Normal file
112
submodules/TelegramUI/Images.xcassets/Premium/Perk/Speed.imageset/Speed.pdf
vendored
Normal 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
|
12
submodules/TelegramUI/Images.xcassets/Premium/Perk/Stickers.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Premium/Perk/Stickers.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Stickers.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
149
submodules/TelegramUI/Images.xcassets/Premium/Perk/Stickers.imageset/Stickers.pdf
vendored
Normal file
149
submodules/TelegramUI/Images.xcassets/Premium/Perk/Stickers.imageset/Stickers.pdf
vendored
Normal 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
|
12
submodules/TelegramUI/Images.xcassets/Premium/Perk/Upload.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Premium/Perk/Upload.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Files.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
119
submodules/TelegramUI/Images.xcassets/Premium/Perk/Upload.imageset/Files.pdf
vendored
Normal file
119
submodules/TelegramUI/Images.xcassets/Premium/Perk/Upload.imageset/Files.pdf
vendored
Normal 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
|
@ -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):
|
||||
|
@ -15,6 +15,7 @@ import AppBundle
|
||||
import DatePickerNode
|
||||
import DebugSettingsUI
|
||||
import TabBarUI
|
||||
import PremiumUI
|
||||
|
||||
public final class TelegramRootController: NavigationController {
|
||||
private let context: AccountContext
|
||||
@ -130,7 +131,11 @@ public final class TelegramRootController: NavigationController {
|
||||
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) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user