mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Move limits page to horizontal scroll
This commit is contained in:
parent
0ffcbf0b06
commit
f8c5bf2a6b
@ -17,7 +17,7 @@ import SolidRoundedButtonComponent
|
|||||||
import Markdown
|
import Markdown
|
||||||
import TelegramUIPreferences
|
import TelegramUIPreferences
|
||||||
|
|
||||||
private final class GradientBackgroundComponent: Component {
|
final class GradientBackgroundComponent: Component {
|
||||||
public let colors: [UIColor]
|
public let colors: [UIColor]
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
@ -153,7 +153,7 @@ final class DemoPageEnvironment: Equatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class PageComponent<ChildEnvironment: Equatable>: CombinedComponent {
|
final class PageComponent<ChildEnvironment: Equatable>: CombinedComponent {
|
||||||
typealias EnvironmentType = ChildEnvironment
|
typealias EnvironmentType = ChildEnvironment
|
||||||
|
|
||||||
private let content: AnyComponent<ChildEnvironment>
|
private let content: AnyComponent<ChildEnvironment>
|
||||||
@ -263,7 +263,7 @@ private final class PageComponent<ChildEnvironment: Equatable>: CombinedComponen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class DemoPagerComponent: Component {
|
final class DemoPagerComponent: Component {
|
||||||
public final class Item: Equatable {
|
public final class Item: Equatable {
|
||||||
public let content: AnyComponentWithIdentity<DemoPageEnvironment>
|
public let content: AnyComponentWithIdentity<DemoPageEnvironment>
|
||||||
|
|
||||||
@ -282,40 +282,29 @@ private final class DemoPagerComponent: Component {
|
|||||||
|
|
||||||
let items: [Item]
|
let items: [Item]
|
||||||
let index: Int
|
let index: Int
|
||||||
let activeColor: UIColor
|
let updated: (CGFloat, Int) -> Void
|
||||||
let inactiveColor: UIColor
|
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
items: [Item],
|
items: [Item],
|
||||||
index: Int = 0,
|
index: Int = 0,
|
||||||
activeColor: UIColor,
|
updated: @escaping (CGFloat, Int) -> Void
|
||||||
inactiveColor: UIColor
|
|
||||||
) {
|
) {
|
||||||
self.items = items
|
self.items = items
|
||||||
self.index = index
|
self.index = index
|
||||||
self.activeColor = activeColor
|
self.updated = updated
|
||||||
self.inactiveColor = inactiveColor
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func ==(lhs: DemoPagerComponent, rhs: DemoPagerComponent) -> Bool {
|
public static func ==(lhs: DemoPagerComponent, rhs: DemoPagerComponent) -> Bool {
|
||||||
if lhs.items != rhs.items {
|
if lhs.items != rhs.items {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if !lhs.activeColor.isEqual(rhs.activeColor) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if !lhs.inactiveColor.isEqual(rhs.inactiveColor) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate final class View: UIView, UIScrollViewDelegate {
|
final class View: UIView, UIScrollViewDelegate {
|
||||||
private let scrollView: UIScrollView
|
private let scrollView: UIScrollView
|
||||||
private var itemViews: [AnyHashable: ComponentHostView<DemoPageEnvironment>] = [:]
|
private var itemViews: [AnyHashable: ComponentHostView<DemoPageEnvironment>] = [:]
|
||||||
|
|
||||||
private let pageIndicatorView: ComponentHostView<Empty>
|
|
||||||
|
|
||||||
private var component: DemoPagerComponent?
|
private var component: DemoPagerComponent?
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
override init(frame: CGRect) {
|
||||||
@ -327,15 +316,11 @@ private final class DemoPagerComponent: Component {
|
|||||||
self.scrollView.bounces = false
|
self.scrollView.bounces = false
|
||||||
self.scrollView.layer.cornerRadius = 10.0
|
self.scrollView.layer.cornerRadius = 10.0
|
||||||
|
|
||||||
self.pageIndicatorView = ComponentHostView<Empty>()
|
|
||||||
self.pageIndicatorView.isUserInteractionEnabled = false
|
|
||||||
|
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
|
|
||||||
self.scrollView.delegate = self
|
self.scrollView.delegate = self
|
||||||
|
|
||||||
self.addSubview(self.scrollView)
|
self.addSubview(self.scrollView)
|
||||||
self.addSubview(self.pageIndicatorView)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder: NSCoder) {
|
||||||
@ -350,6 +335,7 @@ private final class DemoPagerComponent: Component {
|
|||||||
|
|
||||||
self.ignoreContentOffsetChange = true
|
self.ignoreContentOffsetChange = true
|
||||||
let _ = self.update(component: component, availableSize: self.bounds.size, transition: .immediate)
|
let _ = self.update(component: component, availableSize: self.bounds.size, transition: .immediate)
|
||||||
|
component.updated(self.scrollView.contentOffset.x / (self.scrollView.contentSize.width - self.scrollView.frame.width), component.items.count)
|
||||||
self.ignoreContentOffsetChange = false
|
self.ignoreContentOffsetChange = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -369,6 +355,7 @@ private final class DemoPagerComponent: Component {
|
|||||||
|
|
||||||
if firstTime {
|
if firstTime {
|
||||||
self.scrollView.contentOffset = CGPoint(x: CGFloat(component.index) * availableSize.width, y: 0.0)
|
self.scrollView.contentOffset = CGPoint(x: CGFloat(component.index) * availableSize.width, y: 0.0)
|
||||||
|
component.updated(self.scrollView.contentOffset.x / (self.scrollView.contentSize.width - self.scrollView.frame.width), component.items.count)
|
||||||
}
|
}
|
||||||
let viewportCenter = self.scrollView.contentOffset.x + availableSize.width * 0.5
|
let viewportCenter = self.scrollView.contentOffset.x + availableSize.width * 0.5
|
||||||
|
|
||||||
@ -398,7 +385,6 @@ private final class DemoPagerComponent: Component {
|
|||||||
itemView = ComponentHostView<DemoPageEnvironment>()
|
itemView = ComponentHostView<DemoPageEnvironment>()
|
||||||
self.itemViews[item.content.id] = itemView
|
self.itemViews[item.content.id] = itemView
|
||||||
|
|
||||||
|
|
||||||
if item.content.id == (PremiumDemoScreen.Subject.fasterDownload as AnyHashable) {
|
if item.content.id == (PremiumDemoScreen.Subject.fasterDownload as AnyHashable) {
|
||||||
self.scrollView.insertSubview(itemView, at: 0)
|
self.scrollView.insertSubview(itemView, at: 0)
|
||||||
} else {
|
} else {
|
||||||
@ -430,33 +416,15 @@ private final class DemoPagerComponent: Component {
|
|||||||
|
|
||||||
self.component = component
|
self.component = component
|
||||||
|
|
||||||
if component.items.count > 1 {
|
|
||||||
let pageIndicatorComponent = PageIndicatorComponent(
|
|
||||||
pageCount: component.items.count,
|
|
||||||
position: self.scrollView.contentOffset.x / (self.scrollView.contentSize.width - availableSize.width),
|
|
||||||
inactiveColor: component.inactiveColor,
|
|
||||||
activeColor: component.activeColor
|
|
||||||
)
|
|
||||||
let indicatorSize = self.pageIndicatorView.update(
|
|
||||||
transition: .immediate,
|
|
||||||
component: AnyComponent(
|
|
||||||
pageIndicatorComponent
|
|
||||||
),
|
|
||||||
environment: {},
|
|
||||||
containerSize: availableSize
|
|
||||||
)
|
|
||||||
self.pageIndicatorView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - indicatorSize.width) / 2.0), y: availableSize.height - indicatorSize.height - 11.0), size: indicatorSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
return availableSize
|
return availableSize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func makeView() -> View {
|
func makeView() -> View {
|
||||||
return View(frame: CGRect())
|
return View(frame: CGRect())
|
||||||
}
|
}
|
||||||
|
|
||||||
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||||
return view.update(component: self, availableSize: availableSize, transition: transition)
|
return view.update(component: self, availableSize: availableSize, transition: transition)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -949,8 +917,7 @@ private final class DemoSheetContent: CombinedComponent {
|
|||||||
component: DemoPagerComponent(
|
component: DemoPagerComponent(
|
||||||
items: items,
|
items: items,
|
||||||
index: index,
|
index: index,
|
||||||
activeColor: UIColor(rgb: 0x7169ff),
|
updated: { _, _ in }
|
||||||
inactiveColor: theme.list.disclosureArrowColor
|
|
||||||
),
|
),
|
||||||
availableSize: CGSize(width: context.availableSize.width, height: context.availableSize.width + 154.0),
|
availableSize: CGSize(width: context.availableSize.width, height: context.availableSize.width + 154.0),
|
||||||
transition: context.transition
|
transition: context.transition
|
||||||
@ -1177,6 +1144,7 @@ private final class DemoSheetComponent: CombinedComponent {
|
|||||||
|
|
||||||
public class PremiumDemoScreen: ViewControllerComponentContainer {
|
public class PremiumDemoScreen: ViewControllerComponentContainer {
|
||||||
public enum Subject {
|
public enum Subject {
|
||||||
|
case doubleLimits
|
||||||
case moreUpload
|
case moreUpload
|
||||||
case fasterDownload
|
case fasterDownload
|
||||||
case voiceToText
|
case voiceToText
|
||||||
|
@ -361,21 +361,7 @@ private final class PremiumGiftScreenContentComponent: CombinedComponent {
|
|||||||
var demoSubject: PremiumDemoScreen.Subject
|
var demoSubject: PremiumDemoScreen.Subject
|
||||||
switch perk {
|
switch perk {
|
||||||
case .doubleLimits:
|
case .doubleLimits:
|
||||||
var dismissImpl: (() -> Void)?
|
demoSubject = .doubleLimits
|
||||||
let controller = PremimLimitsListScreen(context: accountContext, buttonText: strings.Premium_Gift_GiftSubscription(state?.price ?? "–").string, isPremium: false)
|
|
||||||
controller.action = {
|
|
||||||
dismissImpl?()
|
|
||||||
buy()
|
|
||||||
}
|
|
||||||
controller.disposed = {
|
|
||||||
// updateIsFocused(false)
|
|
||||||
}
|
|
||||||
present(controller)
|
|
||||||
dismissImpl = { [weak controller] in
|
|
||||||
controller?.dismiss(animated: true, completion: nil)
|
|
||||||
}
|
|
||||||
// updateIsFocused(true)
|
|
||||||
return
|
|
||||||
case .moreUpload:
|
case .moreUpload:
|
||||||
demoSubject = .moreUpload
|
demoSubject = .moreUpload
|
||||||
case .fasterDownload:
|
case .fasterDownload:
|
||||||
@ -402,20 +388,34 @@ private final class PremiumGiftScreenContentComponent: CombinedComponent {
|
|||||||
demoSubject = .emojiStatus
|
demoSubject = .emojiStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
let controller = PremiumDemoScreen(
|
var dismissImpl: (() -> Void)?
|
||||||
context: accountContext,
|
let controller = PremiumLimitsListScreen(context: accountContext, subject: demoSubject, source: .gift(state?.price), order: state?.configuration.perks, buttonText: strings.Premium_Gift_GiftSubscription(state?.price ?? "–").string, isPremium: false)
|
||||||
subject: demoSubject,
|
controller.action = {
|
||||||
source: .gift(state?.price),
|
dismissImpl?()
|
||||||
order: state?.configuration.perks,
|
buy()
|
||||||
action: {
|
}
|
||||||
buy()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
controller.disposed = {
|
controller.disposed = {
|
||||||
// updateIsFocused(false)
|
// updateIsFocused(false)
|
||||||
}
|
}
|
||||||
present(controller)
|
present(controller)
|
||||||
// updateIsFocused(true)
|
dismissImpl = { [weak controller] in
|
||||||
|
controller?.dismiss(animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// let controller = PremiumDemoScreen(
|
||||||
|
// context: accountContext,
|
||||||
|
// subject: demoSubject,
|
||||||
|
// source: .gift(state?.price),
|
||||||
|
// order: state?.configuration.perks,
|
||||||
|
// action: {
|
||||||
|
// buy()
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
// controller.disposed = {
|
||||||
|
//// updateIsFocused(false)
|
||||||
|
// }
|
||||||
|
// present(controller)
|
||||||
|
//// updateIsFocused(true)
|
||||||
|
|
||||||
addAppLogEvent(postbox: accountContext.account.postbox, type: "premium.promo_screen_tap", data: ["item": perk.identifier])
|
addAppLogEvent(postbox: accountContext.account.postbox, type: "premium.promo_screen_tap", data: ["item": perk.identifier])
|
||||||
}
|
}
|
||||||
|
@ -1556,25 +1556,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
var demoSubject: PremiumDemoScreen.Subject
|
var demoSubject: PremiumDemoScreen.Subject
|
||||||
switch perk {
|
switch perk {
|
||||||
case .doubleLimits:
|
case .doubleLimits:
|
||||||
let isPremium = state?.isPremium == true
|
demoSubject = .doubleLimits
|
||||||
|
|
||||||
var dismissImpl: (() -> Void)?
|
|
||||||
let controller = PremimLimitsListScreen(context: accountContext, buttonText: isPremium ? strings.Common_OK : (state?.isAnnual == true ? strings.Premium_SubscribeForAnnual(state?.price ?? "–").string : strings.Premium_SubscribeFor(state?.price ?? "–").string), isPremium: isPremium)
|
|
||||||
controller.action = { [weak state] in
|
|
||||||
dismissImpl?()
|
|
||||||
if state?.isPremium == false {
|
|
||||||
buy()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
controller.disposed = {
|
|
||||||
updateIsFocused(false)
|
|
||||||
}
|
|
||||||
present(controller)
|
|
||||||
dismissImpl = { [weak controller] in
|
|
||||||
controller?.dismiss(animated: true, completion: nil)
|
|
||||||
}
|
|
||||||
updateIsFocused(true)
|
|
||||||
return
|
|
||||||
case .moreUpload:
|
case .moreUpload:
|
||||||
demoSubject = .moreUpload
|
demoSubject = .moreUpload
|
||||||
case .fasterDownload:
|
case .fasterDownload:
|
||||||
@ -1601,23 +1583,42 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
demoSubject = .emojiStatus
|
demoSubject = .emojiStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
let controller = PremiumDemoScreen(
|
let isPremium = state?.isPremium == true
|
||||||
context: accountContext,
|
|
||||||
subject: demoSubject,
|
var dismissImpl: (() -> Void)?
|
||||||
source: .intro(state?.price),
|
let controller = PremiumLimitsListScreen(context: accountContext, subject: demoSubject, source: .intro(state?.price), order: state?.configuration.perks, buttonText: isPremium ? strings.Common_OK : (state?.isAnnual == true ? strings.Premium_SubscribeForAnnual(state?.price ?? "–").string : strings.Premium_SubscribeFor(state?.price ?? "–").string), isPremium: isPremium)
|
||||||
order: state?.configuration.perks,
|
controller.action = { [weak state] in
|
||||||
action: {
|
dismissImpl?()
|
||||||
if state?.isPremium == false {
|
if state?.isPremium == false {
|
||||||
buy()
|
buy()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
controller.disposed = {
|
controller.disposed = {
|
||||||
updateIsFocused(false)
|
updateIsFocused(false)
|
||||||
}
|
}
|
||||||
present(controller)
|
present(controller)
|
||||||
|
dismissImpl = { [weak controller] in
|
||||||
|
controller?.dismiss(animated: true, completion: nil)
|
||||||
|
}
|
||||||
updateIsFocused(true)
|
updateIsFocused(true)
|
||||||
|
|
||||||
|
// let controller = PremiumDemoScreen(
|
||||||
|
// context: accountContext,
|
||||||
|
// subject: demoSubject,
|
||||||
|
// source: .intro(state?.price),
|
||||||
|
// order: state?.configuration.perks,
|
||||||
|
// action: {
|
||||||
|
// if state?.isPremium == false {
|
||||||
|
// buy()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
// controller.disposed = {
|
||||||
|
// updateIsFocused(false)
|
||||||
|
// }
|
||||||
|
// present(controller)
|
||||||
|
// updateIsFocused(true)
|
||||||
|
|
||||||
addAppLogEvent(postbox: accountContext.account.postbox, type: "premium.promo_screen_tap", data: ["item": perk.identifier])
|
addAppLogEvent(postbox: accountContext.account.postbox, type: "premium.promo_screen_tap", data: ["item": perk.identifier])
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -20,23 +20,26 @@ final class ScrollChildEnvironment: Equatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final class ScrollComponent<ChildEnvironment: Equatable>: Component {
|
final class ScrollComponent<ChildEnvironment: Equatable>: Component {
|
||||||
public typealias EnvironmentType = ChildEnvironment
|
typealias EnvironmentType = ChildEnvironment
|
||||||
|
|
||||||
public let content: AnyComponent<(ChildEnvironment, ScrollChildEnvironment)>
|
let content: AnyComponent<(ChildEnvironment, ScrollChildEnvironment)>
|
||||||
public let contentInsets: UIEdgeInsets
|
let contentInsets: UIEdgeInsets
|
||||||
public let contentOffsetUpdated: (_ top: CGFloat, _ bottom: CGFloat) -> Void
|
let contentOffsetUpdated: (_ top: CGFloat, _ bottom: CGFloat) -> Void
|
||||||
public let contentOffsetWillCommit: (UnsafeMutablePointer<CGPoint>) -> Void
|
let contentOffsetWillCommit: (UnsafeMutablePointer<CGPoint>) -> Void
|
||||||
|
let resetScroll: ActionSlot<Void>
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
content: AnyComponent<(ChildEnvironment, ScrollChildEnvironment)>,
|
content: AnyComponent<(ChildEnvironment, ScrollChildEnvironment)>,
|
||||||
contentInsets: UIEdgeInsets,
|
contentInsets: UIEdgeInsets,
|
||||||
contentOffsetUpdated: @escaping (_ top: CGFloat, _ bottom: CGFloat) -> Void,
|
contentOffsetUpdated: @escaping (_ top: CGFloat, _ bottom: CGFloat) -> Void,
|
||||||
contentOffsetWillCommit: @escaping (UnsafeMutablePointer<CGPoint>) -> Void
|
contentOffsetWillCommit: @escaping (UnsafeMutablePointer<CGPoint>) -> Void,
|
||||||
|
resetScroll: ActionSlot<Void> = ActionSlot()
|
||||||
) {
|
) {
|
||||||
self.content = content
|
self.content = content
|
||||||
self.contentInsets = contentInsets
|
self.contentInsets = contentInsets
|
||||||
self.contentOffsetUpdated = contentOffsetUpdated
|
self.contentOffsetUpdated = contentOffsetUpdated
|
||||||
self.contentOffsetWillCommit = contentOffsetWillCommit
|
self.contentOffsetWillCommit = contentOffsetWillCommit
|
||||||
|
self.resetScroll = resetScroll
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func ==(lhs: ScrollComponent, rhs: ScrollComponent) -> Bool {
|
public static func ==(lhs: ScrollComponent, rhs: ScrollComponent) -> Bool {
|
||||||
@ -46,7 +49,6 @@ final class ScrollComponent<ChildEnvironment: Equatable>: Component {
|
|||||||
if lhs.contentInsets != rhs.contentInsets {
|
if lhs.contentInsets != rhs.contentInsets {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,6 +109,10 @@ final class ScrollComponent<ChildEnvironment: Equatable>: Component {
|
|||||||
)
|
)
|
||||||
transition.setFrame(view: self.contentView, frame: CGRect(origin: .zero, size: contentSize), completion: nil)
|
transition.setFrame(view: self.contentView, frame: CGRect(origin: .zero, size: contentSize), completion: nil)
|
||||||
|
|
||||||
|
component.resetScroll.connect { [weak self] _ in
|
||||||
|
self?.setContentOffset(.zero, animated: false)
|
||||||
|
}
|
||||||
|
|
||||||
if self.contentSize != contentSize {
|
if self.contentSize != contentSize {
|
||||||
self.ignoreDidScroll = true
|
self.ignoreDidScroll = true
|
||||||
self.contentSize = contentSize
|
self.contentSize = contentSize
|
||||||
|
@ -8,7 +8,8 @@ private let whitelistedHosts: Set<String> = Set([
|
|||||||
"t.me",
|
"t.me",
|
||||||
"telegram.me",
|
"telegram.me",
|
||||||
"telegra.ph",
|
"telegra.ph",
|
||||||
"telesco.pe"
|
"telesco.pe",
|
||||||
|
"fragment.com"
|
||||||
])
|
])
|
||||||
|
|
||||||
private let dataDetector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType([.link]).rawValue)
|
private let dataDetector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType([.link]).rawValue)
|
||||||
|
@ -4,7 +4,8 @@ private let whitelistedHosts: Set<String> = Set([
|
|||||||
"t.me",
|
"t.me",
|
||||||
"telegram.me",
|
"telegram.me",
|
||||||
"telegra.ph",
|
"telegra.ph",
|
||||||
"telesco.pe"
|
"telesco.pe",
|
||||||
|
"fragment.com"
|
||||||
])
|
])
|
||||||
|
|
||||||
public func isConcealedUrlWhitelisted(_ url: URL) -> Bool {
|
public func isConcealedUrlWhitelisted(_ url: URL) -> Bool {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user