mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-07 09:20:08 +00:00
Various improvements
This commit is contained in:
parent
ebfa8f08a1
commit
80cd8f7b32
@ -1053,6 +1053,8 @@ public protocol SharedAccountContext: AnyObject {
|
|||||||
func makeChatQrCodeScreen(context: AccountContext, peer: Peer, threadId: Int64?, temporary: Bool) -> ViewController
|
func makeChatQrCodeScreen(context: AccountContext, peer: Peer, threadId: Int64?, temporary: Bool) -> ViewController
|
||||||
|
|
||||||
func makePremiumIntroController(context: AccountContext, source: PremiumIntroSource, forceDark: Bool, dismissed: (() -> Void)?) -> ViewController
|
func makePremiumIntroController(context: AccountContext, source: PremiumIntroSource, forceDark: Bool, dismissed: (() -> Void)?) -> ViewController
|
||||||
|
func makePremiumIntroController(sharedContext: SharedAccountContext, engine: TelegramEngineUnauthorized, inAppPurchaseManager: InAppPurchaseManager, source: PremiumIntroSource, dismissed: (() -> Void)?) -> ViewController
|
||||||
|
|
||||||
func makePremiumDemoController(context: AccountContext, subject: PremiumDemoSubject, forceDark: Bool, action: @escaping () -> Void, dismissed: (() -> Void)?) -> ViewController
|
func makePremiumDemoController(context: AccountContext, subject: PremiumDemoSubject, forceDark: Bool, action: @escaping () -> Void, dismissed: (() -> Void)?) -> ViewController
|
||||||
func makePremiumLimitController(context: AccountContext, subject: PremiumLimitSubject, count: Int32, forceDark: Bool, cancel: @escaping () -> Void, action: @escaping () -> Bool) -> ViewController
|
func makePremiumLimitController(context: AccountContext, subject: PremiumLimitSubject, count: Int32, forceDark: Bool, cancel: @escaping () -> Void, action: @escaping () -> Bool) -> ViewController
|
||||||
|
|
||||||
|
|||||||
@ -43,6 +43,7 @@ swift_library(
|
|||||||
"//submodules/MoreButtonNode:MoreButtonNode",
|
"//submodules/MoreButtonNode:MoreButtonNode",
|
||||||
"//submodules/ContextUI:ContextUI",
|
"//submodules/ContextUI:ContextUI",
|
||||||
"//submodules/InAppPurchaseManager",
|
"//submodules/InAppPurchaseManager",
|
||||||
|
"//submodules/TelegramUI/Components/Premium/PremiumCoinComponent",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
|||||||
@ -764,12 +764,11 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func paymentController(number: String, phoneCodeHash: String, storeProduct: String) -> AuthorizationSequencePaymentScreen {
|
private func paymentController(number: String, phoneCodeHash: String, storeProduct: String) -> AuthorizationSequencePaymentScreen {
|
||||||
let controller = AuthorizationSequencePaymentScreen(engine: self.engine, presentationData: self.presentationData, inAppPurchaseManager: self.inAppPurchaseManager, phoneNumber: number, phoneCodeHash: phoneCodeHash, storeProduct: storeProduct, back: { [weak self] in
|
let controller = AuthorizationSequencePaymentScreen(sharedContext: self.sharedContext, engine: self.engine, presentationData: self.presentationData, inAppPurchaseManager: self.inAppPurchaseManager, phoneNumber: number, phoneCodeHash: phoneCodeHash, storeProduct: storeProduct, back: { [weak self] in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let countryCode = AuthorizationSequenceController.defaultCountryCode()
|
let countryCode = AuthorizationSequenceController.defaultCountryCode()
|
||||||
|
|
||||||
let _ = self.engine.auth.setState(state: UnauthorizedAccountState(isTestingEnvironment: self.account.testingEnvironment, masterDatacenterId: self.account.masterDatacenterId, contents: .phoneEntry(countryCode: countryCode, number: ""))).startStandalone()
|
let _ = self.engine.auth.setState(state: UnauthorizedAccountState(isTestingEnvironment: self.account.testingEnvironment, masterDatacenterId: self.account.masterDatacenterId, contents: .phoneEntry(countryCode: countryCode, number: ""))).startStandalone()
|
||||||
})
|
})
|
||||||
return controller
|
return controller
|
||||||
@ -1302,7 +1301,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
|
|||||||
}
|
}
|
||||||
controllers.append(self.signUpController(firstName: firstName, lastName: lastName, termsOfService: termsOfService, displayCancel: displayCancel))
|
controllers.append(self.signUpController(firstName: firstName, lastName: lastName, termsOfService: termsOfService, displayCancel: displayCancel))
|
||||||
self.setViewControllers(controllers, animated: !self.viewControllers.isEmpty)
|
self.setViewControllers(controllers, animated: !self.viewControllers.isEmpty)
|
||||||
case let .payment(number, codeHash, storeProduct):
|
case let .payment(number, codeHash, storeProduct, _):
|
||||||
var controllers: [ViewController] = []
|
var controllers: [ViewController] = []
|
||||||
if !self.otherAccountPhoneNumbers.1.isEmpty {
|
if !self.otherAccountPhoneNumbers.1.isEmpty {
|
||||||
controllers.append(self.splashController())
|
controllers.append(self.splashController())
|
||||||
|
|||||||
@ -14,15 +14,19 @@ import ViewControllerComponent
|
|||||||
import MultilineTextComponent
|
import MultilineTextComponent
|
||||||
import BalancedTextComponent
|
import BalancedTextComponent
|
||||||
import BundleIconComponent
|
import BundleIconComponent
|
||||||
import LottieComponent
|
|
||||||
import ButtonComponent
|
import ButtonComponent
|
||||||
import TextFormat
|
import TextFormat
|
||||||
import InAppPurchaseManager
|
import InAppPurchaseManager
|
||||||
import ConfettiEffect
|
import ConfettiEffect
|
||||||
|
import PremiumCoinComponent
|
||||||
|
import Markdown
|
||||||
|
import CountrySelectionUI
|
||||||
|
import AccountContext
|
||||||
|
|
||||||
final class AuthorizationSequencePaymentScreenComponent: Component {
|
final class AuthorizationSequencePaymentScreenComponent: Component {
|
||||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||||
|
|
||||||
|
let sharedContext: SharedAccountContext
|
||||||
let engine: TelegramEngineUnauthorized
|
let engine: TelegramEngineUnauthorized
|
||||||
let inAppPurchaseManager: InAppPurchaseManager
|
let inAppPurchaseManager: InAppPurchaseManager
|
||||||
let presentationData: PresentationData
|
let presentationData: PresentationData
|
||||||
@ -31,6 +35,7 @@ final class AuthorizationSequencePaymentScreenComponent: Component {
|
|||||||
let storeProduct: String
|
let storeProduct: String
|
||||||
|
|
||||||
init(
|
init(
|
||||||
|
sharedContext: SharedAccountContext,
|
||||||
engine: TelegramEngineUnauthorized,
|
engine: TelegramEngineUnauthorized,
|
||||||
inAppPurchaseManager: InAppPurchaseManager,
|
inAppPurchaseManager: InAppPurchaseManager,
|
||||||
presentationData: PresentationData,
|
presentationData: PresentationData,
|
||||||
@ -38,6 +43,7 @@ final class AuthorizationSequencePaymentScreenComponent: Component {
|
|||||||
phoneCodeHash: String,
|
phoneCodeHash: String,
|
||||||
storeProduct: String
|
storeProduct: String
|
||||||
) {
|
) {
|
||||||
|
self.sharedContext = sharedContext
|
||||||
self.engine = engine
|
self.engine = engine
|
||||||
self.inAppPurchaseManager = inAppPurchaseManager
|
self.inAppPurchaseManager = inAppPurchaseManager
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
@ -93,9 +99,7 @@ final class AuthorizationSequencePaymentScreenComponent: Component {
|
|||||||
self.state?.updated()
|
self.state?.updated()
|
||||||
|
|
||||||
let (currency, amount) = storeProduct.priceCurrencyAndAmount
|
let (currency, amount) = storeProduct.priceCurrencyAndAmount
|
||||||
|
|
||||||
let purpose: AppStoreTransactionPurpose = .authCode(restore: false, phoneNumber: component.phoneNumber, phoneCodeHash: component.phoneCodeHash, currency: currency, amount: amount)
|
let purpose: AppStoreTransactionPurpose = .authCode(restore: false, phoneNumber: component.phoneNumber, phoneCodeHash: component.phoneCodeHash, currency: currency, amount: amount)
|
||||||
|
|
||||||
let _ = (component.engine.payments.canPurchasePremium(purpose: purpose)
|
let _ = (component.engine.payments.canPurchasePremium(purpose: purpose)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] available in
|
|> deliverOnMainQueue).start(next: { [weak self] available in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
@ -111,6 +115,7 @@ final class AuthorizationSequencePaymentScreenComponent: Component {
|
|||||||
guard let self, let controller = self.environment?.controller() else {
|
guard let self, let controller = self.environment?.controller() else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
self.inProgress = false
|
||||||
self.state?.updated(transition: .immediate)
|
self.state?.updated(transition: .immediate)
|
||||||
|
|
||||||
var errorText: String?
|
var errorText: String?
|
||||||
@ -156,7 +161,6 @@ final class AuthorizationSequencePaymentScreenComponent: Component {
|
|||||||
let environment = environment[EnvironmentType.self].value
|
let environment = environment[EnvironmentType.self].value
|
||||||
let themeUpdated = self.environment?.theme !== environment.theme
|
let themeUpdated = self.environment?.theme !== environment.theme
|
||||||
self.environment = environment
|
self.environment = environment
|
||||||
self.component = component
|
|
||||||
self.state = state
|
self.state = state
|
||||||
|
|
||||||
if self.component == nil {
|
if self.component == nil {
|
||||||
@ -166,29 +170,136 @@ final class AuthorizationSequencePaymentScreenComponent: Component {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.products = products
|
self.products = products
|
||||||
|
if !self.isUpdating {
|
||||||
self.state?.updated()
|
self.state?.updated()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.component = component
|
||||||
|
|
||||||
if themeUpdated {
|
if themeUpdated {
|
||||||
self.backgroundColor = environment.theme.list.plainBackgroundColor
|
self.backgroundColor = environment.theme.list.plainBackgroundColor
|
||||||
}
|
}
|
||||||
|
|
||||||
let animationHeight: CGFloat = 120.0
|
let sideInset: CGFloat = 16.0 + environment.safeInsets.left
|
||||||
|
|
||||||
let animationSize = self.animation.update(
|
let animationSize = self.animation.update(
|
||||||
transition: transition,
|
transition: transition,
|
||||||
component: AnyComponent(LottieComponent(
|
component: AnyComponent(PremiumCoinComponent(
|
||||||
content: LottieComponent.AppBundleContent(name: "Coin"),
|
mode: .business,
|
||||||
startingPosition: .begin
|
isIntro: true,
|
||||||
|
isVisible: true,
|
||||||
|
hasIdleAnimations: true
|
||||||
)),
|
)),
|
||||||
environment: {},
|
environment: {},
|
||||||
containerSize: CGSize(width: animationHeight, height: animationHeight)
|
containerSize: CGSize(width: min(414.0, availableSize.width), height: 184.0)
|
||||||
)
|
)
|
||||||
|
let titleSize = self.title.update(
|
||||||
|
transition: transition,
|
||||||
|
component: AnyComponent(
|
||||||
|
MultilineTextComponent(text: .plain(NSAttributedString(string: "SMS Fee", font: Font.bold(28.0), textColor: environment.theme.rootController.navigationBar.primaryTextColor)))
|
||||||
|
),
|
||||||
|
environment: {},
|
||||||
|
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 100.0)
|
||||||
|
)
|
||||||
|
|
||||||
|
let textColor = environment.theme.list.itemPrimaryTextColor
|
||||||
|
let secondaryTextColor = environment.theme.list.itemSecondaryTextColor
|
||||||
|
let linkColor = environment.theme.list.itemAccentColor
|
||||||
|
|
||||||
|
var countryName: String = ""
|
||||||
|
if let (country, _) = AuthorizationSequenceCountrySelectionController.lookupCountryIdByNumber(component.phoneNumber, preferredCountries: [:]) {
|
||||||
|
countryName = AuthorizationSequenceCountrySelectionController.lookupCountryNameById(country.id, strings: environment.strings) ?? country.name
|
||||||
|
}
|
||||||
|
|
||||||
|
var items: [AnyComponentWithIdentity<Empty>] = []
|
||||||
|
items.append(
|
||||||
|
AnyComponentWithIdentity(
|
||||||
|
id: "cost",
|
||||||
|
component: AnyComponent(ParagraphComponent(
|
||||||
|
title: "High SMS Costs",
|
||||||
|
titleColor: textColor,
|
||||||
|
text: "Telecom providers in your country (\(countryName)) charge Telegram very high prices for SMS.",
|
||||||
|
textColor: secondaryTextColor,
|
||||||
|
iconName: "Premium/Authorization/Cost",
|
||||||
|
iconColor: linkColor
|
||||||
|
))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
items.append(
|
||||||
|
AnyComponentWithIdentity(
|
||||||
|
id: "verification",
|
||||||
|
component: AnyComponent(ParagraphComponent(
|
||||||
|
title: "Verification Required",
|
||||||
|
titleColor: textColor,
|
||||||
|
text: "Telegram needs to send you an SMS with a verification code to confirm your phone number.",
|
||||||
|
textColor: secondaryTextColor,
|
||||||
|
iconName: "Premium/Authorization/Verification",
|
||||||
|
iconColor: linkColor
|
||||||
|
))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
items.append(
|
||||||
|
AnyComponentWithIdentity(
|
||||||
|
id: "withdrawal",
|
||||||
|
component: AnyComponent(ParagraphComponent(
|
||||||
|
title: "Support via [Telegram Premium >]()",
|
||||||
|
titleColor: textColor,
|
||||||
|
text: "Sign up for a 1-week Telegram Premium subscription to help cover the SMS costs.",
|
||||||
|
textColor: secondaryTextColor,
|
||||||
|
iconName: "Premium/Authorization/Support",
|
||||||
|
iconColor: linkColor,
|
||||||
|
action: { [weak self] in
|
||||||
|
guard let self, let controller = self.environment?.controller() else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let introController = component.sharedContext.makePremiumIntroController(
|
||||||
|
sharedContext: component.sharedContext,
|
||||||
|
engine: component.engine,
|
||||||
|
inAppPurchaseManager: component.inAppPurchaseManager,
|
||||||
|
source: .about,
|
||||||
|
dismissed: nil
|
||||||
|
)
|
||||||
|
controller.push(introController)
|
||||||
|
}
|
||||||
|
))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
let listSize = self.list.update(
|
||||||
|
transition: transition,
|
||||||
|
component: AnyComponent(List(items)),
|
||||||
|
environment: {},
|
||||||
|
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: availableSize.height)
|
||||||
|
)
|
||||||
|
|
||||||
|
let titleSpacing: CGFloat = -24.0
|
||||||
|
let listSpacing: CGFloat = 12.0
|
||||||
|
let totalHeight = animationSize.height + titleSpacing + titleSize.height + listSpacing + listSize.height
|
||||||
|
|
||||||
|
var originY = floor((availableSize.height - totalHeight) / 2.0)
|
||||||
|
|
||||||
if let animationView = self.animation.view {
|
if let animationView = self.animation.view {
|
||||||
if animationView.superview == nil {
|
if animationView.superview == nil {
|
||||||
self.addSubview(animationView)
|
self.addSubview(animationView)
|
||||||
}
|
}
|
||||||
animationView.frame = CGRect(origin: CGPoint(x: floor((availableSize.width - animationSize.width) / 2.0), y: 156.0), size: animationSize)
|
animationView.frame = CGRect(origin: CGPoint(x: floor((availableSize.width - animationSize.width) / 2.0), y: originY), size: animationSize)
|
||||||
|
originY += animationSize.height + titleSpacing
|
||||||
|
}
|
||||||
|
if let titleView = self.title.view {
|
||||||
|
if titleView.superview == nil {
|
||||||
|
self.addSubview(titleView)
|
||||||
|
}
|
||||||
|
titleView.frame = CGRect(origin: CGPoint(x: floor((availableSize.width - titleSize.width) / 2.0), y: originY), size: titleSize)
|
||||||
|
originY += titleSize.height + listSpacing
|
||||||
|
}
|
||||||
|
|
||||||
|
if let listView = self.list.view {
|
||||||
|
if listView.superview == nil {
|
||||||
|
self.addSubview(listView)
|
||||||
|
}
|
||||||
|
listView.frame = CGRect(origin: CGPoint(x: floor((availableSize.width - listSize.width) / 2.0), y: originY), size: listSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
let buttonHeight: CGFloat = 50.0
|
let buttonHeight: CGFloat = 50.0
|
||||||
@ -196,8 +307,13 @@ final class AuthorizationSequencePaymentScreenComponent: Component {
|
|||||||
let bottomInset: CGFloat = environment.safeInsets.bottom > 0.0 ? environment.safeInsets.bottom + 5.0 : bottomPanelPadding
|
let bottomInset: CGFloat = environment.safeInsets.bottom > 0.0 ? environment.safeInsets.bottom + 5.0 : bottomPanelPadding
|
||||||
let bottomPanelHeight = bottomPanelPadding + buttonHeight + bottomInset
|
let bottomPanelHeight = bottomPanelPadding + buttonHeight + bottomInset
|
||||||
|
|
||||||
let sideInset: CGFloat = 16.0
|
let priceString: String
|
||||||
let buttonString = "Sign up for $1"
|
if let product = self.products.first(where: { $0.id == component.storeProduct }) {
|
||||||
|
priceString = product.price
|
||||||
|
} else {
|
||||||
|
priceString = "–"
|
||||||
|
}
|
||||||
|
let buttonString = "Sign up for \(priceString)"
|
||||||
let buttonAttributedString = NSMutableAttributedString(string: buttonString, font: Font.semibold(17.0), textColor: environment.theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center)
|
let buttonAttributedString = NSMutableAttributedString(string: buttonString, font: Font.semibold(17.0), textColor: environment.theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center)
|
||||||
let buttonSize = self.button.update(
|
let buttonSize = self.button.update(
|
||||||
transition: transition,
|
transition: transition,
|
||||||
@ -210,7 +326,12 @@ final class AuthorizationSequencePaymentScreenComponent: Component {
|
|||||||
),
|
),
|
||||||
content: AnyComponentWithIdentity(
|
content: AnyComponentWithIdentity(
|
||||||
id: AnyHashable(buttonString),
|
id: AnyHashable(buttonString),
|
||||||
component: AnyComponent(MultilineTextComponent(text: .plain(buttonAttributedString)))
|
component: AnyComponent(
|
||||||
|
VStack([
|
||||||
|
AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent(text: .plain(buttonAttributedString)))),
|
||||||
|
AnyComponentWithIdentity(id: AnyHashable(1), component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: "Get Telegram Premium for 1 week", font: Font.regular(11.0), textColor: environment.theme.list.itemCheckColors.foregroundColor.withAlphaComponent(0.7), paragraphAlignment: .center)))))
|
||||||
|
], spacing: 1.0)
|
||||||
|
)
|
||||||
),
|
),
|
||||||
isEnabled: true,
|
isEnabled: true,
|
||||||
displaysProgress: self.inProgress,
|
displaysProgress: self.inProgress,
|
||||||
@ -243,6 +364,7 @@ final class AuthorizationSequencePaymentScreenComponent: Component {
|
|||||||
|
|
||||||
public final class AuthorizationSequencePaymentScreen: ViewControllerComponentContainer {
|
public final class AuthorizationSequencePaymentScreen: ViewControllerComponentContainer {
|
||||||
public init(
|
public init(
|
||||||
|
sharedContext: SharedAccountContext,
|
||||||
engine: TelegramEngineUnauthorized,
|
engine: TelegramEngineUnauthorized,
|
||||||
presentationData: PresentationData,
|
presentationData: PresentationData,
|
||||||
inAppPurchaseManager: InAppPurchaseManager,
|
inAppPurchaseManager: InAppPurchaseManager,
|
||||||
@ -252,6 +374,7 @@ public final class AuthorizationSequencePaymentScreen: ViewControllerComponentCo
|
|||||||
back: @escaping () -> Void
|
back: @escaping () -> Void
|
||||||
) {
|
) {
|
||||||
super.init(component: AuthorizationSequencePaymentScreenComponent(
|
super.init(component: AuthorizationSequencePaymentScreenComponent(
|
||||||
|
sharedContext: sharedContext,
|
||||||
engine: engine,
|
engine: engine,
|
||||||
inAppPurchaseManager: inAppPurchaseManager,
|
inAppPurchaseManager: inAppPurchaseManager,
|
||||||
presentationData: presentationData,
|
presentationData: presentationData,
|
||||||
@ -260,6 +383,12 @@ public final class AuthorizationSequencePaymentScreen: ViewControllerComponentCo
|
|||||||
storeProduct: storeProduct
|
storeProduct: storeProduct
|
||||||
), navigationBarAppearance: .transparent, theme: .default, updatedPresentationData: (initial: presentationData, signal: .single(presentationData)))
|
), navigationBarAppearance: .transparent, theme: .default, updatedPresentationData: (initial: presentationData, signal: .single(presentationData)))
|
||||||
|
|
||||||
|
loadServerCountryCodes(accountManager: sharedContext.accountManager, engine: engine, completion: { [weak self] in
|
||||||
|
if let strongSelf = self {
|
||||||
|
strongSelf.requestLayout(forceUpdate: true, transition: .immediate)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
||||||
|
|
||||||
|
|
||||||
@ -285,3 +414,183 @@ public final class AuthorizationSequencePaymentScreen: ViewControllerComponentCo
|
|||||||
self.dismiss()
|
self.dismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final class ParagraphComponent: CombinedComponent {
|
||||||
|
let title: String
|
||||||
|
let titleColor: UIColor
|
||||||
|
let text: String
|
||||||
|
let textColor: UIColor
|
||||||
|
let iconName: String
|
||||||
|
let iconColor: UIColor
|
||||||
|
let action: (() -> Void)?
|
||||||
|
|
||||||
|
public init(
|
||||||
|
title: String,
|
||||||
|
titleColor: UIColor,
|
||||||
|
text: String,
|
||||||
|
textColor: UIColor,
|
||||||
|
iconName: String,
|
||||||
|
iconColor: UIColor,
|
||||||
|
action: (() -> Void)? = nil
|
||||||
|
) {
|
||||||
|
self.title = title
|
||||||
|
self.titleColor = titleColor
|
||||||
|
self.text = text
|
||||||
|
self.textColor = textColor
|
||||||
|
self.iconName = iconName
|
||||||
|
self.iconColor = iconColor
|
||||||
|
self.action = action
|
||||||
|
}
|
||||||
|
|
||||||
|
static func ==(lhs: ParagraphComponent, rhs: ParagraphComponent) -> Bool {
|
||||||
|
if lhs.title != rhs.title {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.titleColor != rhs.titleColor {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.text != rhs.text {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.textColor != rhs.textColor {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.iconName != rhs.iconName {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.iconColor != rhs.iconColor {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
final class State: ComponentState {
|
||||||
|
var cachedChevronImage: (UIImage, UIColor)?
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeState() -> State {
|
||||||
|
return State()
|
||||||
|
}
|
||||||
|
|
||||||
|
static var body: Body {
|
||||||
|
let title = Child(MultilineTextComponent.self)
|
||||||
|
let text = Child(MultilineTextComponent.self)
|
||||||
|
let icon = Child(BundleIconComponent.self)
|
||||||
|
|
||||||
|
return { context in
|
||||||
|
let component = context.component
|
||||||
|
let state = context.state
|
||||||
|
|
||||||
|
let leftInset: CGFloat = 64.0
|
||||||
|
let rightInset: CGFloat = 32.0
|
||||||
|
let textSideInset: CGFloat = leftInset + 8.0
|
||||||
|
let spacing: CGFloat = 5.0
|
||||||
|
|
||||||
|
let textTopInset: CGFloat = 9.0
|
||||||
|
|
||||||
|
let textFont = Font.regular(15.0)
|
||||||
|
let boldTextFont = Font.semibold(15.0)
|
||||||
|
let titleColor = component.titleColor
|
||||||
|
let textColor = component.textColor
|
||||||
|
let linkColor = component.iconColor
|
||||||
|
let titleMarkdownAttributes = MarkdownAttributes(
|
||||||
|
body: MarkdownAttributeSet(font: boldTextFont, textColor: titleColor),
|
||||||
|
bold: MarkdownAttributeSet(font: boldTextFont, textColor: titleColor),
|
||||||
|
link: MarkdownAttributeSet(font: boldTextFont, textColor: linkColor),
|
||||||
|
linkAttribute: { contents in
|
||||||
|
return (TelegramTextAttributes.URL, contents)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if state.cachedChevronImage == nil || state.cachedChevronImage?.1 !== linkColor {
|
||||||
|
state.cachedChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Item List/InlineTextRightArrow"), color: linkColor)!, linkColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
let titleAttributedString = parseMarkdownIntoAttributedString(component.title, attributes: titleMarkdownAttributes).mutableCopy() as! NSMutableAttributedString
|
||||||
|
if let range = titleAttributedString.string.range(of: ">"), let chevronImage = state.cachedChevronImage?.0 {
|
||||||
|
titleAttributedString.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: titleAttributedString.string))
|
||||||
|
}
|
||||||
|
|
||||||
|
let title = title.update(
|
||||||
|
component: MultilineTextComponent(
|
||||||
|
text: .plain(titleAttributedString),
|
||||||
|
horizontalAlignment: .center,
|
||||||
|
maximumNumberOfLines: 1,
|
||||||
|
highlightColor: linkColor.withAlphaComponent(0.1),
|
||||||
|
highlightInset: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: -8.0),
|
||||||
|
highlightAction: { attributes in
|
||||||
|
if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] {
|
||||||
|
return NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tapAction: { attributes, _ in
|
||||||
|
if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String {
|
||||||
|
component.action?()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
availableSize: CGSize(width: context.availableSize.width - leftInset - rightInset, height: CGFloat.greatestFiniteMagnitude),
|
||||||
|
transition: .immediate
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
let textMarkdownAttributes = MarkdownAttributes(
|
||||||
|
body: MarkdownAttributeSet(font: textFont, textColor: textColor),
|
||||||
|
bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor),
|
||||||
|
link: MarkdownAttributeSet(font: textFont, textColor: linkColor),
|
||||||
|
linkAttribute: { contents in
|
||||||
|
return (TelegramTextAttributes.URL, contents)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
let text = text.update(
|
||||||
|
component: MultilineTextComponent(
|
||||||
|
text: .markdown(text: component.text, attributes: textMarkdownAttributes),
|
||||||
|
horizontalAlignment: .natural,
|
||||||
|
maximumNumberOfLines: 0,
|
||||||
|
lineSpacing: 0.2,
|
||||||
|
highlightColor: linkColor.withAlphaComponent(0.1),
|
||||||
|
highlightAction: { attributes in
|
||||||
|
if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] {
|
||||||
|
return NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tapAction: { attributes, _ in
|
||||||
|
if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String {
|
||||||
|
component.action?()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
availableSize: CGSize(width: context.availableSize.width - leftInset - rightInset, height: context.availableSize.height),
|
||||||
|
transition: .immediate
|
||||||
|
)
|
||||||
|
|
||||||
|
let icon = icon.update(
|
||||||
|
component: BundleIconComponent(
|
||||||
|
name: component.iconName,
|
||||||
|
tintColor: component.iconColor
|
||||||
|
),
|
||||||
|
availableSize: CGSize(width: context.availableSize.width, height: context.availableSize.height),
|
||||||
|
transition: .immediate
|
||||||
|
)
|
||||||
|
|
||||||
|
context.add(title
|
||||||
|
.position(CGPoint(x: textSideInset + title.size.width / 2.0, y: textTopInset + title.size.height / 2.0))
|
||||||
|
)
|
||||||
|
|
||||||
|
context.add(text
|
||||||
|
.position(CGPoint(x: textSideInset + text.size.width / 2.0, y: textTopInset + title.size.height + spacing + text.size.height / 2.0))
|
||||||
|
)
|
||||||
|
|
||||||
|
context.add(icon
|
||||||
|
.position(CGPoint(x: 47.0, y: textTopInset + 18.0))
|
||||||
|
)
|
||||||
|
|
||||||
|
return CGSize(width: context.availableSize.width, height: textTopInset + title.size.height + text.size.height + 18.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -28,6 +28,8 @@ private let productIdentifiers = [
|
|||||||
"org.telegram.telegramPremium.sixMonths.code_x10",
|
"org.telegram.telegramPremium.sixMonths.code_x10",
|
||||||
"org.telegram.telegramPremium.twelveMonths.code_x10",
|
"org.telegram.telegramPremium.twelveMonths.code_x10",
|
||||||
|
|
||||||
|
"org.telegram.telegramPremium.oneWeek.auth",
|
||||||
|
|
||||||
"org.telegram.telegramStars.topup.x15",
|
"org.telegram.telegramStars.topup.x15",
|
||||||
"org.telegram.telegramStars.topup.x25",
|
"org.telegram.telegramStars.topup.x25",
|
||||||
"org.telegram.telegramStars.topup.x50",
|
"org.telegram.telegramStars.topup.x50",
|
||||||
|
|||||||
@ -120,6 +120,7 @@ swift_library(
|
|||||||
"//submodules/TelegramUI/Components/EmojiActionIconComponent",
|
"//submodules/TelegramUI/Components/EmojiActionIconComponent",
|
||||||
"//submodules/TelegramUI/Components/ScrollComponent",
|
"//submodules/TelegramUI/Components/ScrollComponent",
|
||||||
"//submodules/TelegramUI/Components/Premium/PremiumStarComponent",
|
"//submodules/TelegramUI/Components/Premium/PremiumStarComponent",
|
||||||
|
"//submodules/TelegramUI/Components/Premium/PremiumCoinComponent",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import Markdown
|
|||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
import BundleIconComponent
|
import BundleIconComponent
|
||||||
import ScrollComponent
|
import ScrollComponent
|
||||||
|
import PremiumCoinComponent
|
||||||
|
|
||||||
private final class HeaderComponent: Component {
|
private final class HeaderComponent: Component {
|
||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
|
|||||||
@ -34,6 +34,7 @@ import EntityKeyboard
|
|||||||
import EmojiActionIconComponent
|
import EmojiActionIconComponent
|
||||||
import ScrollComponent
|
import ScrollComponent
|
||||||
import PremiumStarComponent
|
import PremiumStarComponent
|
||||||
|
import PremiumCoinComponent
|
||||||
|
|
||||||
public enum PremiumSource: Equatable {
|
public enum PremiumSource: Equatable {
|
||||||
public static func == (lhs: PremiumSource, rhs: PremiumSource) -> Bool {
|
public static func == (lhs: PremiumSource, rhs: PremiumSource) -> Bool {
|
||||||
@ -1403,7 +1404,7 @@ final class PerkComponent: CombinedComponent {
|
|||||||
private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
||||||
typealias EnvironmentType = (ViewControllerComponentContainer.Environment, ScrollChildEnvironment)
|
typealias EnvironmentType = (ViewControllerComponentContainer.Environment, ScrollChildEnvironment)
|
||||||
|
|
||||||
let context: AccountContext
|
let screenContext: PremiumIntroScreen.ScreenContext
|
||||||
let mode: PremiumIntroScreen.Mode
|
let mode: PremiumIntroScreen.Mode
|
||||||
let source: PremiumSource
|
let source: PremiumSource
|
||||||
let forceDark: Bool
|
let forceDark: Bool
|
||||||
@ -1423,7 +1424,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
let shareLink: (String) -> Void
|
let shareLink: (String) -> Void
|
||||||
|
|
||||||
init(
|
init(
|
||||||
context: AccountContext,
|
screenContext: PremiumIntroScreen.ScreenContext,
|
||||||
mode: PremiumIntroScreen.Mode,
|
mode: PremiumIntroScreen.Mode,
|
||||||
source: PremiumSource,
|
source: PremiumSource,
|
||||||
forceDark: Bool,
|
forceDark: Bool,
|
||||||
@ -1442,7 +1443,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
copyLink: @escaping (String) -> Void,
|
copyLink: @escaping (String) -> Void,
|
||||||
shareLink: @escaping (String) -> Void
|
shareLink: @escaping (String) -> Void
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.screenContext = screenContext
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
self.source = source
|
self.source = source
|
||||||
self.forceDark = forceDark
|
self.forceDark = forceDark
|
||||||
@ -1463,9 +1464,6 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static func ==(lhs: PremiumIntroScreenContentComponent, rhs: PremiumIntroScreenContentComponent) -> Bool {
|
static func ==(lhs: PremiumIntroScreenContentComponent, rhs: PremiumIntroScreenContentComponent) -> Bool {
|
||||||
if lhs.context !== rhs.context {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if lhs.source != rhs.source {
|
if lhs.source != rhs.source {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -1498,7 +1496,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final class State: ComponentState {
|
final class State: ComponentState {
|
||||||
private let context: AccountContext
|
private let screenContext: PremiumIntroScreen.ScreenContext
|
||||||
private let present: (ViewController) -> Void
|
private let present: (ViewController) -> Void
|
||||||
|
|
||||||
var products: [PremiumProduct]?
|
var products: [PremiumProduct]?
|
||||||
@ -1542,26 +1540,42 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
var cachedChevronImage: (UIImage, PresentationTheme)?
|
var cachedChevronImage: (UIImage, PresentationTheme)?
|
||||||
|
|
||||||
init(
|
init(
|
||||||
context: AccountContext,
|
screenContext: PremiumIntroScreen.ScreenContext,
|
||||||
source: PremiumSource,
|
source: PremiumSource,
|
||||||
present: @escaping (ViewController) -> Void
|
present: @escaping (ViewController) -> Void
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.screenContext = screenContext
|
||||||
self.present = present
|
self.present = present
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
self.disposable = (context.engine.data.subscribe(
|
let premiumIntroConfiguration: Signal<PremiumIntroConfiguration, NoError>
|
||||||
TelegramEngine.EngineData.Item.Configuration.App(),
|
let accountPeer: Signal<EnginePeer?, NoError>
|
||||||
TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId)
|
switch screenContext {
|
||||||
)
|
case let .accountContext(context):
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] appConfiguration, accountPeer in
|
premiumIntroConfiguration = context.engine.data.subscribe(TelegramEngine.EngineData.Item.Configuration.App())
|
||||||
if let strongSelf = self {
|
|> map { appConfiguration in
|
||||||
let isFirstTime = strongSelf.peer == nil
|
return PremiumIntroConfiguration.with(appConfiguration: appConfiguration)
|
||||||
|
}
|
||||||
|
accountPeer = context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId))
|
||||||
|
case .sharedContext:
|
||||||
|
premiumIntroConfiguration = .single(PremiumIntroConfiguration.defaultValue)
|
||||||
|
accountPeer = .single(nil)
|
||||||
|
}
|
||||||
|
|
||||||
strongSelf.configuration = PremiumIntroConfiguration.with(appConfiguration: appConfiguration)
|
self.disposable = combineLatest(
|
||||||
strongSelf.peer = accountPeer
|
queue: Queue.mainQueue(),
|
||||||
strongSelf.updated(transition: .immediate)
|
premiumIntroConfiguration,
|
||||||
|
accountPeer
|
||||||
|
).start(next: { [weak self] premiumIntroConfiguration, accountPeer in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let isFirstTime = self.peer == nil
|
||||||
|
|
||||||
|
self.configuration = premiumIntroConfiguration
|
||||||
|
self.peer = accountPeer
|
||||||
|
self.updated(transition: .immediate)
|
||||||
|
|
||||||
if let identifier = source.identifier, isFirstTime {
|
if let identifier = source.identifier, isFirstTime {
|
||||||
var jsonString: String = "{"
|
var jsonString: String = "{"
|
||||||
@ -1569,7 +1583,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
|
|
||||||
jsonString += "\"data\": {\"premium_promo_order\":["
|
jsonString += "\"data\": {\"premium_promo_order\":["
|
||||||
var isFirst = true
|
var isFirst = true
|
||||||
for perk in strongSelf.configuration.perks {
|
for perk in premiumIntroConfiguration.perks {
|
||||||
if !isFirst {
|
if !isFirst {
|
||||||
jsonString += ","
|
jsonString += ","
|
||||||
}
|
}
|
||||||
@ -1578,35 +1592,36 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
jsonString += "]}}"
|
jsonString += "]}}"
|
||||||
|
|
||||||
if let data = jsonString.data(using: .utf8), let json = JSON(data: data) {
|
if let context = screenContext.context, let data = jsonString.data(using: .utf8), let json = JSON(data: data) {
|
||||||
addAppLogEvent(postbox: strongSelf.context.account.postbox, type: "premium.promo_screen_show", data: json)
|
addAppLogEvent(postbox: context.account.postbox, type: "premium.promo_screen_show", data: json)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if let context = screenContext.context {
|
||||||
let _ = updatePremiumPromoConfigurationOnce(account: context.account).start()
|
let _ = updatePremiumPromoConfigurationOnce(account: context.account).start()
|
||||||
|
|
||||||
let stickersKey: PostboxViewKey = .orderedItemList(id: Namespaces.OrderedItemList.CloudPremiumStickers)
|
let stickersKey: PostboxViewKey = .orderedItemList(id: Namespaces.OrderedItemList.CloudPremiumStickers)
|
||||||
self.stickersDisposable = (self.context.account.postbox.combinedView(keys: [stickersKey])
|
self.stickersDisposable = (context.account.postbox.combinedView(keys: [stickersKey])
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] views in
|
|> deliverOnMainQueue).start(next: { [weak self] views in
|
||||||
guard let strongSelf = self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if let view = views.views[stickersKey] as? OrderedItemListView {
|
if let view = views.views[stickersKey] as? OrderedItemListView {
|
||||||
for item in view.items {
|
for item in view.items {
|
||||||
if let mediaItem = item.contents.get(RecentMediaItem.self) {
|
if let mediaItem = item.contents.get(RecentMediaItem.self) {
|
||||||
let file = mediaItem.media._parse()
|
let file = mediaItem.media._parse()
|
||||||
strongSelf.preloadDisposableSet.add(freeMediaFileResourceInteractiveFetched(account: context.account, userLocation: .other, fileReference: .standalone(media: file), resource: file.resource).start())
|
self.preloadDisposableSet.add(freeMediaFileResourceInteractiveFetched(account: context.account, userLocation: .other, fileReference: .standalone(media: file), resource: file.resource).start())
|
||||||
if let effect = file.videoThumbnails.first {
|
if let effect = file.videoThumbnails.first {
|
||||||
strongSelf.preloadDisposableSet.add(freeMediaFileResourceInteractiveFetched(account: context.account, userLocation: .other, fileReference: .standalone(media: file), resource: effect.resource).start())
|
self.preloadDisposableSet.add(freeMediaFileResourceInteractiveFetched(account: context.account, userLocation: .other, fileReference: .standalone(media: file), resource: effect.resource).start())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
self.newPerksDisposable = combineLatest(queue: Queue.mainQueue(),
|
self.newPerksDisposable = combineLatest(
|
||||||
|
queue: Queue.mainQueue(),
|
||||||
ApplicationSpecificNotice.dismissedBusinessBadge(accountManager: context.sharedContext.accountManager),
|
ApplicationSpecificNotice.dismissedBusinessBadge(accountManager: context.sharedContext.accountManager),
|
||||||
ApplicationSpecificNotice.dismissedBusinessLinksBadge(accountManager: context.sharedContext.accountManager),
|
ApplicationSpecificNotice.dismissedBusinessLinksBadge(accountManager: context.sharedContext.accountManager),
|
||||||
ApplicationSpecificNotice.dismissedBusinessIntroBadge(accountManager: context.sharedContext.accountManager),
|
ApplicationSpecificNotice.dismissedBusinessIntroBadge(accountManager: context.sharedContext.accountManager),
|
||||||
@ -1641,6 +1656,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
self.updated()
|
self.updated()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
self.disposable?.dispose()
|
self.disposable?.dispose()
|
||||||
@ -1655,6 +1671,9 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
private weak var emojiStatusSelectionController: ViewController?
|
private weak var emojiStatusSelectionController: ViewController?
|
||||||
private var previousEmojiSetupTimestamp: Double?
|
private var previousEmojiSetupTimestamp: Double?
|
||||||
func openEmojiSetup(sourceView: UIView, currentFileId: Int64?, color: UIColor?) {
|
func openEmojiSetup(sourceView: UIView, currentFileId: Int64?, color: UIColor?) {
|
||||||
|
guard let context = self.screenContext.context else {
|
||||||
|
return
|
||||||
|
}
|
||||||
let currentTimestamp = CACurrentMediaTime()
|
let currentTimestamp = CACurrentMediaTime()
|
||||||
if let previousTimestamp = self.previousEmojiSetupTimestamp, currentTimestamp < previousTimestamp + 1.0 {
|
if let previousTimestamp = self.previousEmojiSetupTimestamp, currentTimestamp < previousTimestamp + 1.0 {
|
||||||
return
|
return
|
||||||
@ -1668,20 +1687,20 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let controller = EmojiStatusSelectionController(
|
let controller = EmojiStatusSelectionController(
|
||||||
context: self.context,
|
context: context,
|
||||||
mode: .statusSelection,
|
mode: .statusSelection,
|
||||||
sourceView: sourceView,
|
sourceView: sourceView,
|
||||||
emojiContent: EmojiPagerContentComponent.emojiInputData(
|
emojiContent: EmojiPagerContentComponent.emojiInputData(
|
||||||
context: self.context,
|
context: context,
|
||||||
animationCache: self.context.animationCache,
|
animationCache: context.animationCache,
|
||||||
animationRenderer: self.context.animationRenderer,
|
animationRenderer: context.animationRenderer,
|
||||||
isStandalone: false,
|
isStandalone: false,
|
||||||
subject: .status,
|
subject: .status,
|
||||||
hasTrending: false,
|
hasTrending: false,
|
||||||
topReactionItems: [],
|
topReactionItems: [],
|
||||||
areUnicodeEmojiEnabled: false,
|
areUnicodeEmojiEnabled: false,
|
||||||
areCustomEmojiEnabled: true,
|
areCustomEmojiEnabled: true,
|
||||||
chatPeerId: self.context.account.peerId,
|
chatPeerId: context.account.peerId,
|
||||||
selectedItems: selectedItems,
|
selectedItems: selectedItems,
|
||||||
topStatusTitle: nil,
|
topStatusTitle: nil,
|
||||||
backgroundIconColor: color
|
backgroundIconColor: color
|
||||||
@ -1701,7 +1720,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func makeState() -> State {
|
func makeState() -> State {
|
||||||
return State(context: self.context, source: self.source, present: self.present)
|
return State(screenContext: self.screenContext, source: self.source, present: self.present)
|
||||||
}
|
}
|
||||||
|
|
||||||
static var body: Body {
|
static var body: Body {
|
||||||
@ -1733,7 +1752,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
|
|
||||||
let theme = environment.theme
|
let theme = environment.theme
|
||||||
let strings = environment.strings
|
let strings = environment.strings
|
||||||
let presentationData = context.component.context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = context.component.screenContext.presentationData
|
||||||
|
|
||||||
let availableWidth = context.availableSize.width
|
let availableWidth = context.availableSize.width
|
||||||
let sideInsets = sideInset * 2.0 + environment.safeInsets.left + environment.safeInsets.right
|
let sideInsets = sideInset * 2.0 + environment.safeInsets.left + environment.safeInsets.right
|
||||||
@ -1786,8 +1805,8 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
} else if case .giftTerms = context.component.source {
|
} else if case .giftTerms = context.component.source {
|
||||||
textString = strings.Premium_PersonalDescription
|
textString = strings.Premium_PersonalDescription
|
||||||
} else if let _ = context.component.otherPeerName {
|
} else if let _ = context.component.otherPeerName {
|
||||||
if case let .gift(fromId, _, _, giftCode) = context.component.source {
|
if case let .gift(fromId, _, _, giftCode) = context.component.source, let accountContext = context.component.screenContext.context {
|
||||||
if fromId == context.component.context.account.peerId {
|
if fromId == accountContext.account.peerId {
|
||||||
textString = strings.Premium_GiftedDescriptionYou
|
textString = strings.Premium_GiftedDescriptionYou
|
||||||
} else {
|
} else {
|
||||||
if let giftCode {
|
if let giftCode {
|
||||||
@ -1895,7 +1914,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
UIColor(rgb: 0x3dbd4a)
|
UIColor(rgb: 0x3dbd4a)
|
||||||
]
|
]
|
||||||
|
|
||||||
let accountContext = context.component.context
|
let accountContext = context.component.screenContext.context
|
||||||
let present = context.component.present
|
let present = context.component.present
|
||||||
let push = context.component.push
|
let push = context.component.push
|
||||||
let selectProduct = context.component.selectProduct
|
let selectProduct = context.component.selectProduct
|
||||||
@ -2068,6 +2087,9 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
iconName: perk.iconName
|
iconName: perk.iconName
|
||||||
))), false),
|
))), false),
|
||||||
action: { [weak state] _ in
|
action: { [weak state] _ in
|
||||||
|
guard let accountContext else {
|
||||||
|
return
|
||||||
|
}
|
||||||
var demoSubject: PremiumDemoScreen.Subject
|
var demoSubject: PremiumDemoScreen.Subject
|
||||||
switch perk {
|
switch perk {
|
||||||
case .doubleLimits:
|
case .doubleLimits:
|
||||||
@ -2239,6 +2261,10 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
iconName: perk.iconName
|
iconName: perk.iconName
|
||||||
))), false),
|
))), false),
|
||||||
action: { [weak state] _ in
|
action: { [weak state] _ in
|
||||||
|
guard let accountContext else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let isPremium = state?.isPremium == true
|
let isPremium = state?.isPremium == true
|
||||||
if isPremium {
|
if isPremium {
|
||||||
switch perk {
|
switch perk {
|
||||||
@ -2396,6 +2422,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
|
|
||||||
let accentColor = environment.theme.list.itemAccentColor
|
let accentColor = environment.theme.list.itemAccentColor
|
||||||
var perksItems: [AnyComponentWithIdentity<Empty>] = []
|
var perksItems: [AnyComponentWithIdentity<Empty>] = []
|
||||||
|
if let accountContext = context.component.screenContext.context {
|
||||||
perksItems.append(AnyComponentWithIdentity(id: perksItems.count, component: AnyComponent(ListActionItemComponent(
|
perksItems.append(AnyComponentWithIdentity(id: perksItems.count, component: AnyComponent(ListActionItemComponent(
|
||||||
theme: environment.theme,
|
theme: environment.theme,
|
||||||
title: AnyComponent(VStack([
|
title: AnyComponent(VStack([
|
||||||
@ -2423,7 +2450,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
iconName: "Premium/BusinessPerk/Status"
|
iconName: "Premium/BusinessPerk/Status"
|
||||||
))), false),
|
))), false),
|
||||||
icon: ListActionItemComponent.Icon(component: AnyComponentWithIdentity(id: 0, component: AnyComponent(EmojiActionIconComponent(
|
icon: ListActionItemComponent.Icon(component: AnyComponentWithIdentity(id: 0, component: AnyComponent(EmojiActionIconComponent(
|
||||||
context: context.component.context,
|
context: accountContext,
|
||||||
color: accentColor,
|
color: accentColor,
|
||||||
fileId: status?.fileId,
|
fileId: status?.fileId,
|
||||||
file: nil
|
file: nil
|
||||||
@ -2436,6 +2463,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
state?.openEmojiSetup(sourceView: iconView, currentFileId: nil, color: accentColor)
|
state?.openEmojiSetup(sourceView: iconView, currentFileId: nil, color: accentColor)
|
||||||
}
|
}
|
||||||
))))
|
))))
|
||||||
|
}
|
||||||
|
|
||||||
perksItems.append(AnyComponentWithIdentity(id: perksItems.count, component: AnyComponent(ListActionItemComponent(
|
perksItems.append(AnyComponentWithIdentity(id: perksItems.count, component: AnyComponent(ListActionItemComponent(
|
||||||
theme: environment.theme,
|
theme: environment.theme,
|
||||||
@ -2464,6 +2492,9 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
iconName: "Premium/BusinessPerk/Tag"
|
iconName: "Premium/BusinessPerk/Tag"
|
||||||
))), false),
|
))), false),
|
||||||
action: { _ in
|
action: { _ in
|
||||||
|
guard let accountContext else {
|
||||||
|
return
|
||||||
|
}
|
||||||
push(accountContext.sharedContext.makeFilterSettingsController(context: accountContext, modal: false, scrollToTags: true, dismissed: nil))
|
push(accountContext.sharedContext.makeFilterSettingsController(context: accountContext, modal: false, scrollToTags: true, dismissed: nil))
|
||||||
}
|
}
|
||||||
))))
|
))))
|
||||||
@ -2495,6 +2526,9 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
iconName: "Premium/Perk/Stories"
|
iconName: "Premium/Perk/Stories"
|
||||||
))), false),
|
))), false),
|
||||||
action: { _ in
|
action: { _ in
|
||||||
|
guard let accountContext else {
|
||||||
|
return
|
||||||
|
}
|
||||||
push(accountContext.sharedContext.makeMyStoriesController(context: accountContext, isArchive: false))
|
push(accountContext.sharedContext.makeMyStoriesController(context: accountContext, isArchive: false))
|
||||||
}
|
}
|
||||||
))))
|
))))
|
||||||
@ -2560,6 +2594,9 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
))),
|
))),
|
||||||
], alignment: .left, spacing: 2.0)),
|
], alignment: .left, spacing: 2.0)),
|
||||||
accessory: .toggle(ListActionItemComponent.Toggle(style: .regular, isOn: state.adsEnabled, action: { [weak state] value in
|
accessory: .toggle(ListActionItemComponent.Toggle(style: .regular, isOn: state.adsEnabled, action: { [weak state] value in
|
||||||
|
guard let accountContext else {
|
||||||
|
return
|
||||||
|
}
|
||||||
let _ = accountContext.engine.accountData.updateAdMessagesEnabled(enabled: value).startStandalone()
|
let _ = accountContext.engine.accountData.updateAdMessagesEnabled(enabled: value).startStandalone()
|
||||||
state?.updated(transition: .immediate)
|
state?.updated(transition: .immediate)
|
||||||
})),
|
})),
|
||||||
@ -2576,8 +2613,8 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
let controller = environment.controller
|
let controller = environment.controller
|
||||||
let adsInfoTapActionImpl: ([NSAttributedString.Key: Any]) -> Void = { _ in
|
let adsInfoTapActionImpl: ([NSAttributedString.Key: Any]) -> Void = { _ in
|
||||||
if let controller = controller() as? PremiumIntroScreen {
|
if let controller = controller() as? PremiumIntroScreen, let context = controller.context {
|
||||||
controller.context.sharedContext.openExternalUrl(context: controller.context, urlContext: .generic, url: environment.strings.Business_AdsInfo_URL, forceExternal: true, presentationData: controller.context.sharedContext.currentPresentationData.with({$0}), navigationController: nil, dismissInput: {})
|
context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: environment.strings.Business_AdsInfo_URL, forceExternal: true, presentationData: presentationData, navigationController: nil, dismissInput: {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let adsSettingsSection = adsSettingsSection.update(
|
let adsSettingsSection = adsSettingsSection.update(
|
||||||
@ -2627,7 +2664,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
layoutPerks()
|
layoutPerks()
|
||||||
layoutOptions()
|
layoutOptions()
|
||||||
} else if case let .gift(fromPeerId, _, _, giftCode) = context.component.source {
|
} else if case let .gift(fromPeerId, _, _, giftCode) = context.component.source {
|
||||||
if let giftCode, fromPeerId != context.component.context.account.peerId, !context.component.justBought {
|
if let giftCode, let accountContext = context.component.screenContext.context, fromPeerId != accountContext.account.peerId, !context.component.justBought {
|
||||||
let link = "https://t.me/giftcode/\(giftCode.slug)"
|
let link = "https://t.me/giftcode/\(giftCode.slug)"
|
||||||
let linkButton = linkButton.update(
|
let linkButton = linkButton.update(
|
||||||
component: Button(
|
component: Button(
|
||||||
@ -2718,7 +2755,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
|
|
||||||
var isGiftView = false
|
var isGiftView = false
|
||||||
if case let .gift(fromId, _, _, _) = context.component.source {
|
if case let .gift(fromId, _, _, _) = context.component.source {
|
||||||
if fromId == context.component.context.account.peerId {
|
if let accountContext = context.component.screenContext.context, fromId == accountContext.account.peerId {
|
||||||
isGiftView = true
|
isGiftView = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2738,14 +2775,12 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
|
|
||||||
let controller = environment.controller
|
let controller = environment.controller
|
||||||
let termsTapActionImpl: ([NSAttributedString.Key: Any]) -> Void = { attributes in
|
let termsTapActionImpl: ([NSAttributedString.Key: Any]) -> Void = { attributes in
|
||||||
if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String,
|
if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String, let controller = controller() as? PremiumIntroScreen, let context = controller.context, let navigationController = controller.navigationController as? NavigationController {
|
||||||
let controller = controller() as? PremiumIntroScreen, let navigationController = controller.navigationController as? NavigationController {
|
|
||||||
if url.hasPrefix("https://apps.apple.com/account/subscriptions") {
|
if url.hasPrefix("https://apps.apple.com/account/subscriptions") {
|
||||||
controller.context.sharedContext.applicationBindings.openSubscriptions()
|
context.sharedContext.applicationBindings.openSubscriptions()
|
||||||
} else if url.hasPrefix("https://") || url.hasPrefix("tg://") {
|
} else if url.hasPrefix("https://") || url.hasPrefix("tg://") {
|
||||||
controller.context.sharedContext.openExternalUrl(context: controller.context, urlContext: .generic, url: url, forceExternal: false, presentationData: controller.context.sharedContext.currentPresentationData.with({$0}), navigationController: navigationController, dismissInput: {})
|
context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: url, forceExternal: false, presentationData: presentationData, navigationController: navigationController, dismissInput: {})
|
||||||
} else {
|
} else {
|
||||||
let context = controller.context
|
|
||||||
let signal: Signal<ResolvedUrl, NoError>?
|
let signal: Signal<ResolvedUrl, NoError>?
|
||||||
switch url {
|
switch url {
|
||||||
case "terms":
|
case "terms":
|
||||||
@ -2815,7 +2850,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
private final class PremiumIntroScreenComponent: CombinedComponent {
|
private final class PremiumIntroScreenComponent: CombinedComponent {
|
||||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||||
|
|
||||||
let context: AccountContext
|
let screenContext: PremiumIntroScreen.ScreenContext
|
||||||
let mode: PremiumIntroScreen.Mode
|
let mode: PremiumIntroScreen.Mode
|
||||||
let source: PremiumSource
|
let source: PremiumSource
|
||||||
let forceDark: Bool
|
let forceDark: Bool
|
||||||
@ -2827,8 +2862,8 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
|||||||
let copyLink: (String) -> Void
|
let copyLink: (String) -> Void
|
||||||
let shareLink: (String) -> Void
|
let shareLink: (String) -> Void
|
||||||
|
|
||||||
init(context: AccountContext, mode: PremiumIntroScreen.Mode, source: PremiumSource, forceDark: Bool, forceHasPremium: Bool, updateInProgress: @escaping (Bool) -> Void, present: @escaping (ViewController) -> Void, push: @escaping (ViewController) -> Void, completion: @escaping () -> Void, copyLink: @escaping (String) -> Void, shareLink: @escaping (String) -> Void) {
|
init(screenContext: PremiumIntroScreen.ScreenContext, mode: PremiumIntroScreen.Mode, source: PremiumSource, forceDark: Bool, forceHasPremium: Bool, updateInProgress: @escaping (Bool) -> Void, present: @escaping (ViewController) -> Void, push: @escaping (ViewController) -> Void, completion: @escaping () -> Void, copyLink: @escaping (String) -> Void, shareLink: @escaping (String) -> Void) {
|
||||||
self.context = context
|
self.screenContext = screenContext
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
self.source = source
|
self.source = source
|
||||||
self.forceDark = forceDark
|
self.forceDark = forceDark
|
||||||
@ -2842,9 +2877,6 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static func ==(lhs: PremiumIntroScreenComponent, rhs: PremiumIntroScreenComponent) -> Bool {
|
static func ==(lhs: PremiumIntroScreenComponent, rhs: PremiumIntroScreenComponent) -> Bool {
|
||||||
if lhs.context !== rhs.context {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if lhs.mode != rhs.mode {
|
if lhs.mode != rhs.mode {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -2861,7 +2893,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final class State: ComponentState {
|
final class State: ComponentState {
|
||||||
private let context: AccountContext
|
private let screenContext: PremiumIntroScreen.ScreenContext
|
||||||
private let source: PremiumSource
|
private let source: PremiumSource
|
||||||
private let updateInProgress: (Bool) -> Void
|
private let updateInProgress: (Bool) -> Void
|
||||||
private let present: (ViewController) -> Void
|
private let present: (ViewController) -> Void
|
||||||
@ -2884,9 +2916,6 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
|||||||
var otherPeerName: String?
|
var otherPeerName: String?
|
||||||
var justBought = false
|
var justBought = false
|
||||||
|
|
||||||
let animationCache: AnimationCache
|
|
||||||
let animationRenderer: MultiAnimationRenderer
|
|
||||||
|
|
||||||
var emojiFile: TelegramMediaFile?
|
var emojiFile: TelegramMediaFile?
|
||||||
var emojiPackTitle: String?
|
var emojiPackTitle: String?
|
||||||
private var emojiFileDisposable: Disposable?
|
private var emojiFileDisposable: Disposable?
|
||||||
@ -2917,28 +2946,26 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init(context: AccountContext, source: PremiumSource, forceHasPremium: Bool, updateInProgress: @escaping (Bool) -> Void, present: @escaping (ViewController) -> Void, completion: @escaping () -> Void) {
|
init(screenContext: PremiumIntroScreen.ScreenContext, source: PremiumSource, forceHasPremium: Bool, updateInProgress: @escaping (Bool) -> Void, present: @escaping (ViewController) -> Void, completion: @escaping () -> Void) {
|
||||||
self.context = context
|
self.screenContext = screenContext
|
||||||
self.source = source
|
self.source = source
|
||||||
self.updateInProgress = updateInProgress
|
self.updateInProgress = updateInProgress
|
||||||
self.present = present
|
self.present = present
|
||||||
self.completion = completion
|
self.completion = completion
|
||||||
|
|
||||||
self.animationCache = context.animationCache
|
|
||||||
self.animationRenderer = context.animationRenderer
|
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
self.validPurchases = context.inAppPurchaseManager?.getReceiptPurchases() ?? []
|
self.validPurchases = screenContext.inAppPurchaseManager?.getReceiptPurchases() ?? []
|
||||||
|
|
||||||
let availableProducts: Signal<[InAppPurchaseManager.Product], NoError>
|
let availableProducts: Signal<[InAppPurchaseManager.Product], NoError>
|
||||||
if let inAppPurchaseManager = context.inAppPurchaseManager {
|
if let inAppPurchaseManager = screenContext.inAppPurchaseManager {
|
||||||
availableProducts = inAppPurchaseManager.availableProducts
|
availableProducts = inAppPurchaseManager.availableProducts
|
||||||
} else {
|
} else {
|
||||||
availableProducts = .single([])
|
availableProducts = .single([])
|
||||||
}
|
}
|
||||||
|
|
||||||
let otherPeerName: Signal<String?, NoError>
|
let otherPeerName: Signal<String?, NoError>
|
||||||
|
if let context = screenContext.context {
|
||||||
if case let .gift(fromPeerId, toPeerId, _, _) = source {
|
if case let .gift(fromPeerId, toPeerId, _, _) = source {
|
||||||
let otherPeerId = fromPeerId != context.account.peerId ? fromPeerId : toPeerId
|
let otherPeerId = fromPeerId != context.account.peerId ? fromPeerId : toPeerId
|
||||||
otherPeerName = context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: otherPeerId))
|
otherPeerName = context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: otherPeerId))
|
||||||
@ -2958,26 +2985,39 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
|||||||
} else {
|
} else {
|
||||||
otherPeerName = .single(nil)
|
otherPeerName = .single(nil)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
otherPeerName = .single(nil)
|
||||||
|
}
|
||||||
|
|
||||||
if forceHasPremium {
|
if forceHasPremium {
|
||||||
self.isPremium = true
|
self.isPremium = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let isPremium: Signal<Bool, NoError>
|
||||||
|
let promoConfiguration: Signal<PremiumPromoConfiguration, NoError>
|
||||||
|
switch screenContext {
|
||||||
|
case let .accountContext(context):
|
||||||
|
isPremium = context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId))
|
||||||
|
|> map { peer -> Bool in
|
||||||
|
return peer?.isPremium ?? false
|
||||||
|
}
|
||||||
|
promoConfiguration = context.engine.data.subscribe(TelegramEngine.EngineData.Item.Configuration.PremiumPromo())
|
||||||
|
case .sharedContext:
|
||||||
|
isPremium = .single(false)
|
||||||
|
promoConfiguration = .single(PremiumPromoConfiguration.defaultValue)
|
||||||
|
}
|
||||||
|
|
||||||
self.disposable = combineLatest(
|
self.disposable = combineLatest(
|
||||||
queue: Queue.mainQueue(),
|
queue: Queue.mainQueue(),
|
||||||
availableProducts,
|
availableProducts,
|
||||||
context.engine.data.subscribe(TelegramEngine.EngineData.Item.Configuration.PremiumPromo()),
|
promoConfiguration,
|
||||||
context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId))
|
isPremium,
|
||||||
|> map { peer -> Bool in
|
|
||||||
return peer?.isPremium ?? false
|
|
||||||
},
|
|
||||||
otherPeerName
|
otherPeerName
|
||||||
).start(next: { [weak self] availableProducts, promoConfiguration, isPremium, otherPeerName in
|
).start(next: { [weak self] availableProducts, promoConfiguration, isPremium, otherPeerName in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.promoConfiguration = promoConfiguration
|
strongSelf.promoConfiguration = promoConfiguration
|
||||||
|
|
||||||
let hadProducts = strongSelf.products != nil
|
let hadProducts = strongSelf.products != nil
|
||||||
|
|
||||||
var products: [PremiumProduct] = []
|
var products: [PremiumProduct] = []
|
||||||
for option in promoConfiguration.premiumProductOptions {
|
for option in promoConfiguration.premiumProductOptions {
|
||||||
if let product = availableProducts.first(where: { $0.id == option.storeProductId }), product.isSubscription {
|
if let product = availableProducts.first(where: { $0.id == option.storeProductId }), product.isSubscription {
|
||||||
@ -2992,10 +3032,12 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
|||||||
if !hadProducts {
|
if !hadProducts {
|
||||||
strongSelf.selectedProductId = strongSelf.products?.first?.id
|
strongSelf.selectedProductId = strongSelf.products?.first?.id
|
||||||
|
|
||||||
|
if let context = screenContext.context {
|
||||||
for (_, video) in promoConfiguration.videos {
|
for (_, video) in promoConfiguration.videos {
|
||||||
strongSelf.preloadDisposableSet.add(preloadVideoResource(postbox: context.account.postbox, userLocation: .other, userContentType: .video, resourceReference: .standalone(resource: video.resource), duration: 3.0).start())
|
strongSelf.preloadDisposableSet.add(preloadVideoResource(postbox: context.account.postbox, userLocation: .other, userContentType: .video, resourceReference: .standalone(resource: video.resource), duration: 3.0).start())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
strongSelf.updated(transition: .immediate)
|
strongSelf.updated(transition: .immediate)
|
||||||
}
|
}
|
||||||
@ -3007,17 +3049,19 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
|||||||
self.emojiPackTitle = info.title
|
self.emojiPackTitle = info.title
|
||||||
self.updated(transition: .immediate)
|
self.updated(transition: .immediate)
|
||||||
} else {
|
} else {
|
||||||
|
if let context = screenContext.context {
|
||||||
self.emojiFileDisposable = (context.engine.stickers.resolveInlineStickers(fileIds: [emojiFileId])
|
self.emojiFileDisposable = (context.engine.stickers.resolveInlineStickers(fileIds: [emojiFileId])
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] result in
|
|> deliverOnMainQueue).start(next: { [weak self] result in
|
||||||
guard let strongSelf = self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.emojiFile = result[emojiFileId]
|
self.emojiFile = result[emojiFileId]
|
||||||
strongSelf.updated(transition: .immediate)
|
self.updated(transition: .immediate)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
self.disposable?.dispose()
|
self.disposable?.dispose()
|
||||||
@ -3032,12 +3076,17 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let presentationData = self.screenContext.presentationData
|
||||||
|
|
||||||
if case let .gift(_, _, _, giftCode) = self.source, let giftCode, giftCode.usedDate == nil {
|
if case let .gift(_, _, _, giftCode) = self.source, let giftCode, giftCode.usedDate == nil {
|
||||||
|
guard let context = self.screenContext.context else {
|
||||||
|
return
|
||||||
|
}
|
||||||
self.inProgress = true
|
self.inProgress = true
|
||||||
self.updateInProgress(true)
|
self.updateInProgress(true)
|
||||||
self.updated(transition: .immediate)
|
self.updated(transition: .immediate)
|
||||||
|
|
||||||
self.paymentDisposable.set((self.context.engine.payments.applyPremiumGiftCode(slug: giftCode.slug)
|
self.paymentDisposable.set((context.engine.payments.applyPremiumGiftCode(slug: giftCode.slug)
|
||||||
|> deliverOnMainQueue).start(error: { [weak self] error in
|
|> deliverOnMainQueue).start(error: { [weak self] error in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
@ -3048,8 +3097,6 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
|||||||
self.updated(transition: .immediate)
|
self.updated(transition: .immediate)
|
||||||
|
|
||||||
if case let .waitForExpiration(date) = error {
|
if case let .waitForExpiration(date) = error {
|
||||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
|
||||||
|
|
||||||
let dateText = stringForMediumDate(timestamp: date, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat)
|
let dateText = stringForMediumDate(timestamp: date, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat)
|
||||||
self.present(UndoOverlayController(presentationData: presentationData, content: .info(title: presentationData.strings.Premium_Gift_ApplyLink_AlreadyHasPremium_Title, text: presentationData.strings.Premium_Gift_ApplyLink_AlreadyHasPremium_Text(dateText).string, timeout: nil, customUndoText: nil), elevatedLayout: true, position: .bottom, action: { _ in return true }))
|
self.present(UndoOverlayController(presentationData: presentationData, content: .info(title: presentationData.strings.Premium_Gift_ApplyLink_AlreadyHasPremium_Title, text: presentationData.strings.Premium_Gift_ApplyLink_AlreadyHasPremium_Text(dateText).string, timeout: nil, customUndoText: nil), elevatedLayout: true, position: .bottom, action: { _ in return true }))
|
||||||
}
|
}
|
||||||
@ -3067,16 +3114,15 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let inAppPurchaseManager = self.context.inAppPurchaseManager,
|
guard let inAppPurchaseManager = self.screenContext.inAppPurchaseManager,
|
||||||
let premiumProduct = self.products?.first(where: { $0.id == self.selectedProductId }) else {
|
let premiumProduct = self.products?.first(where: { $0.id == self.selectedProductId }) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
|
||||||
|
|
||||||
let isUpgrade = self.products?.first(where: { $0.isCurrent }) != nil
|
let isUpgrade = self.products?.first(where: { $0.isCurrent }) != nil
|
||||||
|
|
||||||
var hasActiveSubsciption = false
|
var hasActiveSubsciption = false
|
||||||
if let data = self.context.currentAppConfiguration.with({ $0 }).data, let _ = data["ios_killswitch_disable_receipt_check"] {
|
if let context = self.screenContext.context, let data = context.currentAppConfiguration.with({ $0 }).data, let _ = data["ios_killswitch_disable_receipt_check"] {
|
||||||
|
|
||||||
} else if !self.validPurchases.isEmpty && !isUpgrade {
|
} else if !self.validPurchases.isEmpty && !isUpgrade {
|
||||||
let now = Date()
|
let now = Date()
|
||||||
@ -3089,26 +3135,40 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
|||||||
|
|
||||||
if hasActiveSubsciption {
|
if hasActiveSubsciption {
|
||||||
let errorText = presentationData.strings.Premium_Purchase_OnlyOneSubscriptionAllowed
|
let errorText = presentationData.strings.Premium_Purchase_OnlyOneSubscriptionAllowed
|
||||||
let alertController = textAlertController(context: self.context, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])
|
let alertController = textAlertController(sharedContext: self.screenContext.sharedContext, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])
|
||||||
self.present(alertController)
|
self.present(alertController)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
addAppLogEvent(postbox: self.context.account.postbox, type: "premium.promo_screen_accept")
|
if let context = self.screenContext.context {
|
||||||
|
addAppLogEvent(postbox: context.account.postbox, type: "premium.promo_screen_accept")
|
||||||
|
}
|
||||||
|
|
||||||
self.inProgress = true
|
self.inProgress = true
|
||||||
self.updateInProgress(true)
|
self.updateInProgress(true)
|
||||||
self.updated(transition: .immediate)
|
self.updated(transition: .immediate)
|
||||||
|
|
||||||
let purpose: AppStoreTransactionPurpose = isUpgrade ? .upgrade : .subscription
|
let purpose: AppStoreTransactionPurpose = isUpgrade ? .upgrade : .subscription
|
||||||
let _ = (self.context.engine.payments.canPurchasePremium(purpose: purpose)
|
|
||||||
|
let canPurchasePremium: Signal<Bool, NoError>
|
||||||
|
switch self.screenContext {
|
||||||
|
case let .accountContext(context):
|
||||||
|
canPurchasePremium = context.engine.payments.canPurchasePremium(purpose: purpose)
|
||||||
|
case let .sharedContext(_, engine, _):
|
||||||
|
canPurchasePremium = engine.payments.canPurchasePremium(purpose: purpose)
|
||||||
|
}
|
||||||
|
let _ = (canPurchasePremium
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] available in
|
|> deliverOnMainQueue).start(next: { [weak self] available in
|
||||||
if let strongSelf = self {
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
if available {
|
if available {
|
||||||
strongSelf.paymentDisposable.set((inAppPurchaseManager.buyProduct(premiumProduct.storeProduct, purpose: purpose)
|
self.paymentDisposable.set((inAppPurchaseManager.buyProduct(premiumProduct.storeProduct, purpose: purpose)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] status in
|
|> deliverOnMainQueue).start(next: { [weak self] status in
|
||||||
if let strongSelf = self, case .purchased = status {
|
if let self, case .purchased = status {
|
||||||
strongSelf.activationDisposable.set((strongSelf.context.account.postbox.peerView(id: strongSelf.context.account.peerId)
|
let activation: Signal<Never, AssignAppStoreTransactionError>
|
||||||
|
if let context = self.screenContext.context {
|
||||||
|
activation = context.account.postbox.peerView(id: context.account.peerId)
|
||||||
|> castError(AssignAppStoreTransactionError.self)
|
|> castError(AssignAppStoreTransactionError.self)
|
||||||
|> take(until: { view in
|
|> take(until: { view in
|
||||||
if let peer = view.peers[view.peerId], peer.isPremium {
|
if let peer = view.peers[view.peerId], peer.isPremium {
|
||||||
@ -3121,38 +3181,50 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
|||||||
return .never()
|
return .never()
|
||||||
}
|
}
|
||||||
|> timeout(15.0, queue: Queue.mainQueue(), alternate: .fail(.timeout))
|
|> timeout(15.0, queue: Queue.mainQueue(), alternate: .fail(.timeout))
|
||||||
|
} else {
|
||||||
|
activation = .complete()
|
||||||
|
}
|
||||||
|
|
||||||
|
self.activationDisposable.set((activation
|
||||||
|> deliverOnMainQueue).start(error: { [weak self] _ in
|
|> deliverOnMainQueue).start(error: { [weak self] _ in
|
||||||
if let strongSelf = self {
|
if let self {
|
||||||
strongSelf.inProgress = false
|
self.inProgress = false
|
||||||
strongSelf.updateInProgress(false)
|
self.updateInProgress(false)
|
||||||
|
|
||||||
strongSelf.updated(transition: .immediate)
|
self.updated(transition: .immediate)
|
||||||
|
|
||||||
addAppLogEvent(postbox: strongSelf.context.account.postbox, type: "premium.promo_screen_fail")
|
if let context = self.screenContext.context {
|
||||||
|
addAppLogEvent(postbox: context.account.postbox, type: "premium.promo_screen_fail")
|
||||||
|
}
|
||||||
|
|
||||||
let errorText = presentationData.strings.Premium_Purchase_ErrorUnknown
|
let errorText = presentationData.strings.Premium_Purchase_ErrorUnknown
|
||||||
let alertController = textAlertController(context: strongSelf.context, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])
|
let alertController = textAlertController(sharedContext: self.screenContext.sharedContext, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])
|
||||||
strongSelf.present(alertController)
|
self.present(alertController)
|
||||||
}
|
}
|
||||||
}, completed: { [weak self] in
|
}, completed: { [weak self] in
|
||||||
if let strongSelf = self {
|
guard let self else {
|
||||||
let _ = updatePremiumPromoConfigurationOnce(account: strongSelf.context.account).start()
|
return
|
||||||
strongSelf.inProgress = false
|
|
||||||
strongSelf.updateInProgress(false)
|
|
||||||
|
|
||||||
strongSelf.isPremium = true
|
|
||||||
strongSelf.justBought = true
|
|
||||||
|
|
||||||
strongSelf.updated(transition: .easeInOut(duration: 0.25))
|
|
||||||
strongSelf.completion()
|
|
||||||
}
|
}
|
||||||
|
if let context = self.screenContext.context {
|
||||||
|
let _ = updatePremiumPromoConfigurationOnce(account: context.account).start()
|
||||||
|
}
|
||||||
|
self.inProgress = false
|
||||||
|
self.updateInProgress(false)
|
||||||
|
|
||||||
|
self.isPremium = true
|
||||||
|
self.justBought = true
|
||||||
|
|
||||||
|
self.updated(transition: .easeInOut(duration: 0.25))
|
||||||
|
self.completion()
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}, error: { [weak self] error in
|
}, error: { [weak self] error in
|
||||||
if let strongSelf = self {
|
guard let self else {
|
||||||
strongSelf.inProgress = false
|
return
|
||||||
strongSelf.updateInProgress(false)
|
}
|
||||||
strongSelf.updated(transition: .immediate)
|
self.inProgress = false
|
||||||
|
self.updateInProgress(false)
|
||||||
|
self.updated(transition: .immediate)
|
||||||
|
|
||||||
var errorText: String?
|
var errorText: String?
|
||||||
switch error {
|
switch error {
|
||||||
@ -3173,18 +3245,18 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let errorText = errorText {
|
if let errorText = errorText {
|
||||||
addAppLogEvent(postbox: strongSelf.context.account.postbox, type: "premium.promo_screen_fail")
|
if let context = self.screenContext.context {
|
||||||
|
addAppLogEvent(postbox: context.account.postbox, type: "premium.promo_screen_fail")
|
||||||
let alertController = textAlertController(context: strongSelf.context, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])
|
|
||||||
strongSelf.present(alertController)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let alertController = textAlertController(sharedContext: self.screenContext.sharedContext, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])
|
||||||
|
self.present(alertController)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
strongSelf.inProgress = false
|
self.inProgress = false
|
||||||
strongSelf.updateInProgress(false)
|
self.updateInProgress(false)
|
||||||
strongSelf.updated(transition: .immediate)
|
self.updated(transition: .immediate)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -3201,7 +3273,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func makeState() -> State {
|
func makeState() -> State {
|
||||||
return State(context: self.context, source: self.source, forceHasPremium: self.forceHasPremium, updateInProgress: self.updateInProgress, present: self.present, completion: self.completion)
|
return State(screenContext: self.screenContext, source: self.source, forceHasPremium: self.forceHasPremium, updateInProgress: self.updateInProgress, present: self.present, completion: self.completion)
|
||||||
}
|
}
|
||||||
|
|
||||||
static var body: Body {
|
static var body: Body {
|
||||||
@ -3248,12 +3320,12 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
|||||||
availableSize: CGSize(width: min(414.0, context.availableSize.width), height: 220.0),
|
availableSize: CGSize(width: min(414.0, context.availableSize.width), height: 220.0),
|
||||||
transition: context.transition
|
transition: context.transition
|
||||||
)
|
)
|
||||||
} else if case let .emojiStatus(_, fileId, _, _) = context.component.source {
|
} else if case let .emojiStatus(_, fileId, _, _) = context.component.source, case let .accountContext(accountContext) = context.component.screenContext {
|
||||||
header = emoji.update(
|
header = emoji.update(
|
||||||
component: EmojiHeaderComponent(
|
component: EmojiHeaderComponent(
|
||||||
context: context.component.context,
|
context: accountContext,
|
||||||
animationCache: state.animationCache,
|
animationCache: accountContext.animationCache,
|
||||||
animationRenderer: state.animationRenderer,
|
animationRenderer: accountContext.animationRenderer,
|
||||||
placeholderColor: environment.theme.list.mediaPlaceholderColor,
|
placeholderColor: environment.theme.list.mediaPlaceholderColor,
|
||||||
accentColor: environment.theme.list.itemAccentColor,
|
accentColor: environment.theme.list.itemAccentColor,
|
||||||
fileId: fileId,
|
fileId: fileId,
|
||||||
@ -3356,7 +3428,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
|||||||
} else if case .profile = context.component.source {
|
} else if case .profile = context.component.source {
|
||||||
secondaryTitleText = environment.strings.Premium_PersonalTitle(otherPeerName).string
|
secondaryTitleText = environment.strings.Premium_PersonalTitle(otherPeerName).string
|
||||||
} else if case let .gift(fromPeerId, _, duration, _) = context.component.source {
|
} else if case let .gift(fromPeerId, _, duration, _) = context.component.source {
|
||||||
if fromPeerId == context.component.context.account.peerId {
|
if case let .accountContext(accountContext) = context.component.screenContext, fromPeerId == accountContext.account.peerId {
|
||||||
if duration == 12 {
|
if duration == 12 {
|
||||||
secondaryTitleText = environment.strings.Premium_GiftedTitleYou_12Month(otherPeerName).string
|
secondaryTitleText = environment.strings.Premium_GiftedTitleYou_12Month(otherPeerName).string
|
||||||
} else if duration == 6 {
|
} else if duration == 6 {
|
||||||
@ -3409,14 +3481,13 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
|||||||
secondaryAttributedText.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: emojiFile.fileId.id, file: emojiFile), range: range)
|
secondaryAttributedText.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: emojiFile.fileId.id, file: emojiFile), range: range)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let accountContext = context.component.context
|
|
||||||
let presentController = context.component.present
|
|
||||||
|
|
||||||
|
let presentController = context.component.present
|
||||||
let secondaryTitle = secondaryTitle.update(
|
let secondaryTitle = secondaryTitle.update(
|
||||||
component: MultilineTextWithEntitiesComponent(
|
component: MultilineTextWithEntitiesComponent(
|
||||||
context: context.component.context,
|
context: context.component.screenContext.context,
|
||||||
animationCache: context.state.animationCache,
|
animationCache: context.component.screenContext.context?.animationCache,
|
||||||
animationRenderer: context.state.animationRenderer,
|
animationRenderer: context.component.screenContext.context?.animationRenderer,
|
||||||
placeholderColor: environment.theme.list.mediaPlaceholderColor,
|
placeholderColor: environment.theme.list.mediaPlaceholderColor,
|
||||||
text: .plain(secondaryAttributedText),
|
text: .plain(secondaryAttributedText),
|
||||||
horizontalAlignment: .center,
|
horizontalAlignment: .center,
|
||||||
@ -3431,7 +3502,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
} : nil,
|
} : nil,
|
||||||
tapAction: { [weak state, weak environment] _, _ in
|
tapAction: { [weak state, weak environment] _, _ in
|
||||||
if let emojiFile = state?.emojiFile, let controller = environment?.controller() as? PremiumIntroScreen, let navigationController = controller.navigationController as? NavigationController {
|
if let emojiFile = state?.emojiFile, let controller = environment?.controller() as? PremiumIntroScreen, let context = controller.context, let navigationController = controller.navigationController as? NavigationController {
|
||||||
for attribute in emojiFile.attributes {
|
for attribute in emojiFile.attributes {
|
||||||
if case let .CustomEmoji(_, _, _, packReference) = attribute, let packReference = packReference {
|
if case let .CustomEmoji(_, _, _, packReference) = attribute, let packReference = packReference {
|
||||||
var loadedPack: LoadedStickerPack?
|
var loadedPack: LoadedStickerPack?
|
||||||
@ -3439,7 +3510,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
|||||||
loadedPack = .result(info: info, items: items, installed: updatedInstalled ?? installed)
|
loadedPack = .result(info: info, items: items, installed: updatedInstalled ?? installed)
|
||||||
}
|
}
|
||||||
|
|
||||||
let controller = accountContext.sharedContext.makeStickerPackScreen(context: accountContext, updatedPresentationData: nil, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: loadedPack.flatMap { [$0] } ?? [], actionTitle: nil, isEditing: false, expandIfNeeded: false, parentNavigationController: navigationController, sendSticker: { _, _, _ in
|
let controller = context.sharedContext.makeStickerPackScreen(context: context, updatedPresentationData: nil, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: loadedPack.flatMap { [$0] } ?? [], actionTitle: nil, isEditing: false, expandIfNeeded: false, parentNavigationController: navigationController, sendSticker: { _, _, _ in
|
||||||
return false
|
return false
|
||||||
}, actionPerformed: { added in
|
}, actionPerformed: { added in
|
||||||
updatedInstalled = added
|
updatedInstalled = added
|
||||||
@ -3462,7 +3533,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
|||||||
let scrollContent = scrollContent.update(
|
let scrollContent = scrollContent.update(
|
||||||
component: ScrollComponent<EnvironmentType>(
|
component: ScrollComponent<EnvironmentType>(
|
||||||
content: AnyComponent(PremiumIntroScreenContentComponent(
|
content: AnyComponent(PremiumIntroScreenContentComponent(
|
||||||
context: context.component.context,
|
screenContext: context.component.screenContext,
|
||||||
mode: context.component.mode,
|
mode: context.component.mode,
|
||||||
source: context.component.source,
|
source: context.component.source,
|
||||||
forceDark: context.component.forceDark,
|
forceDark: context.component.forceDark,
|
||||||
@ -3570,8 +3641,8 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
|||||||
)
|
)
|
||||||
|
|
||||||
var isUnusedGift = false
|
var isUnusedGift = false
|
||||||
if case let .gift(fromId, _, _, giftCode) = context.component.source {
|
if case let .gift(fromId, _, _, giftCode) = context.component.source, let accountContext = context.component.screenContext.context {
|
||||||
if let giftCode, giftCode.usedDate == nil, fromId != context.component.context.account.peerId {
|
if let giftCode, giftCode.usedDate == nil, fromId != accountContext.account.peerId {
|
||||||
isUnusedGift = true
|
isUnusedGift = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3691,12 +3762,70 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final class PremiumIntroScreen: ViewControllerComponentContainer {
|
public final class PremiumIntroScreen: ViewControllerComponentContainer {
|
||||||
|
public enum ScreenContext {
|
||||||
|
case accountContext(AccountContext)
|
||||||
|
case sharedContext(SharedAccountContext, TelegramEngineUnauthorized, InAppPurchaseManager)
|
||||||
|
|
||||||
|
var context: AccountContext? {
|
||||||
|
switch self {
|
||||||
|
case let .accountContext(context):
|
||||||
|
return context
|
||||||
|
case .sharedContext:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var sharedContext: SharedAccountContext {
|
||||||
|
switch self {
|
||||||
|
case let .accountContext(context):
|
||||||
|
return context.sharedContext
|
||||||
|
case let .sharedContext(sharedContext, _, _):
|
||||||
|
return sharedContext
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var inAppPurchaseManager: InAppPurchaseManager? {
|
||||||
|
switch self {
|
||||||
|
case let .accountContext(context):
|
||||||
|
return context.inAppPurchaseManager
|
||||||
|
case let .sharedContext(_, _, inAppPurchaseManager):
|
||||||
|
return inAppPurchaseManager
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var presentationData: PresentationData {
|
||||||
|
switch self {
|
||||||
|
case let .accountContext(context):
|
||||||
|
return context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
case let .sharedContext(sharedContext, _, _):
|
||||||
|
return sharedContext.currentPresentationData.with { $0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>) {
|
||||||
|
switch self {
|
||||||
|
case let .accountContext(context):
|
||||||
|
return (initial: context.sharedContext.currentPresentationData.with { $0 }, signal: context.sharedContext.presentationData)
|
||||||
|
case let .sharedContext(sharedContext, _, _):
|
||||||
|
return (initial: sharedContext.currentPresentationData.with { $0 }, signal: sharedContext.presentationData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public enum Mode {
|
public enum Mode {
|
||||||
case premium
|
case premium
|
||||||
case business
|
case business
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate let context: AccountContext
|
fileprivate var context: AccountContext? {
|
||||||
|
switch self.screenContext {
|
||||||
|
case let .accountContext(context):
|
||||||
|
return context
|
||||||
|
case .sharedContext:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private let screenContext: ScreenContext
|
||||||
fileprivate let mode: Mode
|
fileprivate let mode: Mode
|
||||||
|
|
||||||
private var didSetReady = false
|
private var didSetReady = false
|
||||||
@ -3709,18 +3838,24 @@ public final class PremiumIntroScreen: ViewControllerComponentContainer {
|
|||||||
public weak var containerView: UIView?
|
public weak var containerView: UIView?
|
||||||
public var animationColor: UIColor?
|
public var animationColor: UIColor?
|
||||||
|
|
||||||
public init(context: AccountContext, mode: Mode = .premium, source: PremiumSource, modal: Bool = true, forceDark: Bool = false, forceHasPremium: Bool = false) {
|
public convenience init(context: AccountContext, mode: Mode = .premium, source: PremiumSource, modal: Bool = true, forceDark: Bool = false, forceHasPremium: Bool = false) {
|
||||||
self.context = context
|
self.init(screenContext: .accountContext(context), mode: mode, source: source, modal: modal, forceDark: forceDark, forceHasPremium: forceHasPremium)
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(screenContext: ScreenContext, mode: Mode = .premium, source: PremiumSource, modal: Bool = true, forceDark: Bool = false, forceHasPremium: Bool = false) {
|
||||||
|
self.screenContext = screenContext
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
|
|
||||||
|
let presentationData = screenContext.presentationData
|
||||||
|
|
||||||
var updateInProgressImpl: ((Bool) -> Void)?
|
var updateInProgressImpl: ((Bool) -> Void)?
|
||||||
var pushImpl: ((ViewController) -> Void)?
|
var pushImpl: ((ViewController) -> Void)?
|
||||||
var presentImpl: ((ViewController) -> Void)?
|
var presentImpl: ((ViewController) -> Void)?
|
||||||
var completionImpl: (() -> Void)?
|
var completionImpl: (() -> Void)?
|
||||||
var copyLinkImpl: ((String) -> Void)?
|
var copyLinkImpl: ((String) -> Void)?
|
||||||
var shareLinkImpl: ((String) -> Void)?
|
var shareLinkImpl: ((String) -> Void)?
|
||||||
super.init(context: context, component: PremiumIntroScreenComponent(
|
super.init(component: PremiumIntroScreenComponent(
|
||||||
context: context,
|
screenContext: screenContext,
|
||||||
mode: mode,
|
mode: mode,
|
||||||
source: source,
|
source: source,
|
||||||
forceDark: forceDark,
|
forceDark: forceDark,
|
||||||
@ -3743,9 +3878,7 @@ public final class PremiumIntroScreen: ViewControllerComponentContainer {
|
|||||||
shareLink: { link in
|
shareLink: { link in
|
||||||
shareLinkImpl?(link)
|
shareLinkImpl?(link)
|
||||||
}
|
}
|
||||||
), navigationBarAppearance: .transparent, presentationMode: modal ? .modal : .default, theme: forceDark ? .dark : .default)
|
), navigationBarAppearance: .transparent, presentationMode: modal ? .modal : .default, theme: forceDark ? .dark : .default, updatedPresentationData: screenContext.updatedPresentationData)
|
||||||
|
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
||||||
|
|
||||||
if modal {
|
if modal {
|
||||||
let cancelItem = UIBarButtonItem(title: presentationData.strings.Common_Close, style: .plain, target: self, action: #selector(self.cancelPressed))
|
let cancelItem = UIBarButtonItem(title: presentationData.strings.Common_Close, style: .plain, target: self, action: #selector(self.cancelPressed))
|
||||||
@ -3756,11 +3889,12 @@ public final class PremiumIntroScreen: ViewControllerComponentContainer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateInProgressImpl = { [weak self] inProgress in
|
updateInProgressImpl = { [weak self] inProgress in
|
||||||
if let strongSelf = self {
|
guard let self else {
|
||||||
strongSelf.navigationItem.leftBarButtonItem?.isEnabled = !inProgress
|
return
|
||||||
strongSelf.view.disablesInteractiveTransitionGestureRecognizer = inProgress
|
|
||||||
strongSelf.view.disablesInteractiveModalDismiss = inProgress
|
|
||||||
}
|
}
|
||||||
|
self.navigationItem.leftBarButtonItem?.isEnabled = !inProgress
|
||||||
|
self.view.disablesInteractiveTransitionGestureRecognizer = inProgress
|
||||||
|
self.view.disablesInteractiveModalDismiss = inProgress
|
||||||
}
|
}
|
||||||
|
|
||||||
presentImpl = { [weak self] c in
|
presentImpl = { [weak self] c in
|
||||||
@ -3789,12 +3923,11 @@ public final class PremiumIntroScreen: ViewControllerComponentContainer {
|
|||||||
}
|
}
|
||||||
self.dismissAllTooltips()
|
self.dismissAllTooltips()
|
||||||
|
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
||||||
self.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, position: .top, action: { _ in return true }), in: .current)
|
self.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, position: .top, action: { _ in return true }), in: .current)
|
||||||
}
|
}
|
||||||
|
|
||||||
shareLinkImpl = { [weak self] link in
|
shareLinkImpl = { [weak self] link in
|
||||||
guard let self, let navigationController = self.navigationController as? NavigationController else {
|
guard let self, case let .accountContext(context) = screenContext, let navigationController = self.navigationController as? NavigationController else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3807,7 +3940,6 @@ public final class PremiumIntroScreen: ViewControllerComponentContainer {
|
|||||||
HapticFeedback().success()
|
HapticFeedback().success()
|
||||||
}
|
}
|
||||||
|
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
||||||
(navigationController?.topViewController as? ViewController)?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: true, text: peer.id == context.account.peerId ? presentationData.strings.GiftLink_LinkSharedToSavedMessages : presentationData.strings.GiftLink_LinkSharedToChat(peer.compactDisplayTitle).string), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .window(.root))
|
(navigationController?.topViewController as? ViewController)?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: true, text: peer.id == context.account.peerId ? presentationData.strings.GiftLink_LinkSharedToSavedMessages : presentationData.strings.GiftLink_LinkSharedToChat(peer.compactDisplayTitle).string), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .window(.root))
|
||||||
|
|
||||||
let _ = (enqueueMessages(account: context.account, peerId: peer.id, messages: messages)
|
let _ = (enqueueMessages(account: context.account, peerId: peer.id, messages: messages)
|
||||||
@ -3820,7 +3952,7 @@ public final class PremiumIntroScreen: ViewControllerComponentContainer {
|
|||||||
navigationController.pushViewController(peerSelectionController)
|
navigationController.pushViewController(peerSelectionController)
|
||||||
}
|
}
|
||||||
|
|
||||||
if case .business = mode {
|
if case .business = mode, case let .accountContext(context) = screenContext {
|
||||||
context.account.viewTracker.keepQuickRepliesApproximatelyUpdated()
|
context.account.viewTracker.keepQuickRepliesApproximatelyUpdated()
|
||||||
context.account.viewTracker.keepBusinessLinksApproximatelyUpdated()
|
context.account.viewTracker.keepBusinessLinksApproximatelyUpdated()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import BundleIconComponent
|
|||||||
import Markdown
|
import Markdown
|
||||||
import SolidRoundedButtonNode
|
import SolidRoundedButtonNode
|
||||||
import BlurredBackgroundComponent
|
import BlurredBackgroundComponent
|
||||||
|
import PremiumCoinComponent
|
||||||
|
|
||||||
public class PremiumLimitsListScreen: ViewController {
|
public class PremiumLimitsListScreen: ViewController {
|
||||||
final class Node: ViewControllerTracingNode, ASScrollViewDelegate, ASGestureRecognizerDelegate {
|
final class Node: ViewControllerTracingNode, ASScrollViewDelegate, ASGestureRecognizerDelegate {
|
||||||
|
|||||||
@ -1220,7 +1220,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||||||
dict[957176926] = { return Api.auth.LoginToken.parse_loginTokenSuccess($0) }
|
dict[957176926] = { return Api.auth.LoginToken.parse_loginTokenSuccess($0) }
|
||||||
dict[326715557] = { return Api.auth.PasswordRecovery.parse_passwordRecovery($0) }
|
dict[326715557] = { return Api.auth.PasswordRecovery.parse_passwordRecovery($0) }
|
||||||
dict[1577067778] = { return Api.auth.SentCode.parse_sentCode($0) }
|
dict[1577067778] = { return Api.auth.SentCode.parse_sentCode($0) }
|
||||||
dict[304435204] = { return Api.auth.SentCode.parse_sentCodePaymentRequired($0) }
|
dict[-674301568] = { return Api.auth.SentCode.parse_sentCodePaymentRequired($0) }
|
||||||
dict[596704836] = { return Api.auth.SentCode.parse_sentCodeSuccess($0) }
|
dict[596704836] = { return Api.auth.SentCode.parse_sentCodeSuccess($0) }
|
||||||
dict[1035688326] = { return Api.auth.SentCodeType.parse_sentCodeTypeApp($0) }
|
dict[1035688326] = { return Api.auth.SentCodeType.parse_sentCodeTypeApp($0) }
|
||||||
dict[1398007207] = { return Api.auth.SentCodeType.parse_sentCodeTypeCall($0) }
|
dict[1398007207] = { return Api.auth.SentCodeType.parse_sentCodeTypeCall($0) }
|
||||||
|
|||||||
@ -567,7 +567,7 @@ public extension Api.auth {
|
|||||||
public extension Api.auth {
|
public extension Api.auth {
|
||||||
enum SentCode: TypeConstructorDescription {
|
enum SentCode: TypeConstructorDescription {
|
||||||
case sentCode(flags: Int32, type: Api.auth.SentCodeType, phoneCodeHash: String, nextType: Api.auth.CodeType?, timeout: Int32?)
|
case sentCode(flags: Int32, type: Api.auth.SentCodeType, phoneCodeHash: String, nextType: Api.auth.CodeType?, timeout: Int32?)
|
||||||
case sentCodePaymentRequired(storeProduct: String)
|
case sentCodePaymentRequired(storeProduct: String, phoneCodeHash: String)
|
||||||
case sentCodeSuccess(authorization: Api.auth.Authorization)
|
case sentCodeSuccess(authorization: Api.auth.Authorization)
|
||||||
|
|
||||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||||
@ -582,11 +582,12 @@ public extension Api.auth {
|
|||||||
if Int(flags) & Int(1 << 1) != 0 {nextType!.serialize(buffer, true)}
|
if Int(flags) & Int(1 << 1) != 0 {nextType!.serialize(buffer, true)}
|
||||||
if Int(flags) & Int(1 << 2) != 0 {serializeInt32(timeout!, buffer: buffer, boxed: false)}
|
if Int(flags) & Int(1 << 2) != 0 {serializeInt32(timeout!, buffer: buffer, boxed: false)}
|
||||||
break
|
break
|
||||||
case .sentCodePaymentRequired(let storeProduct):
|
case .sentCodePaymentRequired(let storeProduct, let phoneCodeHash):
|
||||||
if boxed {
|
if boxed {
|
||||||
buffer.appendInt32(304435204)
|
buffer.appendInt32(-674301568)
|
||||||
}
|
}
|
||||||
serializeString(storeProduct, buffer: buffer, boxed: false)
|
serializeString(storeProduct, buffer: buffer, boxed: false)
|
||||||
|
serializeString(phoneCodeHash, buffer: buffer, boxed: false)
|
||||||
break
|
break
|
||||||
case .sentCodeSuccess(let authorization):
|
case .sentCodeSuccess(let authorization):
|
||||||
if boxed {
|
if boxed {
|
||||||
@ -601,8 +602,8 @@ public extension Api.auth {
|
|||||||
switch self {
|
switch self {
|
||||||
case .sentCode(let flags, let type, let phoneCodeHash, let nextType, let timeout):
|
case .sentCode(let flags, let type, let phoneCodeHash, let nextType, let timeout):
|
||||||
return ("sentCode", [("flags", flags as Any), ("type", type as Any), ("phoneCodeHash", phoneCodeHash as Any), ("nextType", nextType as Any), ("timeout", timeout as Any)])
|
return ("sentCode", [("flags", flags as Any), ("type", type as Any), ("phoneCodeHash", phoneCodeHash as Any), ("nextType", nextType as Any), ("timeout", timeout as Any)])
|
||||||
case .sentCodePaymentRequired(let storeProduct):
|
case .sentCodePaymentRequired(let storeProduct, let phoneCodeHash):
|
||||||
return ("sentCodePaymentRequired", [("storeProduct", storeProduct as Any)])
|
return ("sentCodePaymentRequired", [("storeProduct", storeProduct as Any), ("phoneCodeHash", phoneCodeHash as Any)])
|
||||||
case .sentCodeSuccess(let authorization):
|
case .sentCodeSuccess(let authorization):
|
||||||
return ("sentCodeSuccess", [("authorization", authorization as Any)])
|
return ("sentCodeSuccess", [("authorization", authorization as Any)])
|
||||||
}
|
}
|
||||||
@ -638,9 +639,12 @@ public extension Api.auth {
|
|||||||
public static func parse_sentCodePaymentRequired(_ reader: BufferReader) -> SentCode? {
|
public static func parse_sentCodePaymentRequired(_ reader: BufferReader) -> SentCode? {
|
||||||
var _1: String?
|
var _1: String?
|
||||||
_1 = parseString(reader)
|
_1 = parseString(reader)
|
||||||
|
var _2: String?
|
||||||
|
_2 = parseString(reader)
|
||||||
let _c1 = _1 != nil
|
let _c1 = _1 != nil
|
||||||
if _c1 {
|
let _c2 = _2 != nil
|
||||||
return Api.auth.SentCode.sentCodePaymentRequired(storeProduct: _1!)
|
if _c1 && _c2 {
|
||||||
|
return Api.auth.SentCode.sentCodePaymentRequired(storeProduct: _1!, phoneCodeHash: _2!)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@ -73,7 +73,7 @@ public class UnauthorizedAccount {
|
|||||||
public let testingEnvironment: Bool
|
public let testingEnvironment: Bool
|
||||||
public let postbox: Postbox
|
public let postbox: Postbox
|
||||||
public let network: Network
|
public let network: Network
|
||||||
private let stateManager: UnauthorizedAccountStateManager
|
let stateManager: UnauthorizedAccountStateManager
|
||||||
|
|
||||||
private let updateLoginTokenPipe = ValuePipe<Void>()
|
private let updateLoginTokenPipe = ValuePipe<Void>()
|
||||||
public var updateLoginTokenEvents: Signal<Void, NoError> {
|
public var updateLoginTokenEvents: Signal<Void, NoError> {
|
||||||
@ -91,7 +91,7 @@ public class UnauthorizedAccount {
|
|||||||
|
|
||||||
public let shouldBeServiceTaskMaster = Promise<AccountServiceTaskMasterMode>()
|
public let shouldBeServiceTaskMaster = Promise<AccountServiceTaskMasterMode>()
|
||||||
|
|
||||||
init(networkArguments: NetworkInitializationArguments, id: AccountRecordId, rootPath: String, basePath: String, testingEnvironment: Bool, postbox: Postbox, network: Network, shouldKeepAutoConnection: Bool = true) {
|
init(accountManager: AccountManager<TelegramAccountManagerTypes>, networkArguments: NetworkInitializationArguments, id: AccountRecordId, rootPath: String, basePath: String, testingEnvironment: Bool, postbox: Postbox, network: Network, shouldKeepAutoConnection: Bool = true) {
|
||||||
self.networkArguments = networkArguments
|
self.networkArguments = networkArguments
|
||||||
self.id = id
|
self.id = id
|
||||||
self.rootPath = rootPath
|
self.rootPath = rootPath
|
||||||
@ -101,11 +101,84 @@ public class UnauthorizedAccount {
|
|||||||
self.network = network
|
self.network = network
|
||||||
let updateLoginTokenPipe = self.updateLoginTokenPipe
|
let updateLoginTokenPipe = self.updateLoginTokenPipe
|
||||||
let serviceNotificationPipe = self.serviceNotificationPipe
|
let serviceNotificationPipe = self.serviceNotificationPipe
|
||||||
self.stateManager = UnauthorizedAccountStateManager(network: network, updateLoginToken: {
|
let masterDatacenterId = Int32(network.mtProto.datacenterId)
|
||||||
|
|
||||||
|
var updateSentCodeImpl: ((Api.auth.SentCode) -> Void)?
|
||||||
|
self.stateManager = UnauthorizedAccountStateManager(
|
||||||
|
network: network,
|
||||||
|
updateLoginToken: {
|
||||||
updateLoginTokenPipe.putNext(Void())
|
updateLoginTokenPipe.putNext(Void())
|
||||||
}, displayServiceNotification: { text in
|
},
|
||||||
|
updateSentCode: { sentCode in
|
||||||
|
updateSentCodeImpl?(sentCode)
|
||||||
|
},
|
||||||
|
displayServiceNotification: { text in
|
||||||
serviceNotificationPipe.putNext(text)
|
serviceNotificationPipe.putNext(text)
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
updateSentCodeImpl = { [weak self] sentCode in
|
||||||
|
switch sentCode {
|
||||||
|
case .sentCodePaymentRequired:
|
||||||
|
break
|
||||||
|
case let .sentCode(_, type, phoneCodeHash, nextType, codeTimeout):
|
||||||
|
let _ = postbox.transaction({ transaction in
|
||||||
|
var parsedNextType: AuthorizationCodeNextType?
|
||||||
|
if let nextType = nextType {
|
||||||
|
parsedNextType = AuthorizationCodeNextType(apiType: nextType)
|
||||||
|
}
|
||||||
|
if let state = transaction.getState() as? UnauthorizedAccountState, case let .payment(phoneNumber, _, _, syncContacts) = state.contents {
|
||||||
|
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: testingEnvironment, masterDatacenterId: masterDatacenterId, contents: .confirmationCodeEntry(number: phoneNumber, type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: codeTimeout, nextType: parsedNextType, syncContacts: syncContacts, previousCodeEntry: nil, usePrevious: false)))
|
||||||
|
}
|
||||||
|
}).start()
|
||||||
|
case let .sentCodeSuccess(authorization):
|
||||||
|
switch authorization {
|
||||||
|
case let .authorization(_, _, _, futureAuthToken, user):
|
||||||
|
let _ = postbox.transaction({ [weak self] transaction in
|
||||||
|
var syncContacts = true
|
||||||
|
if let state = transaction.getState() as? UnauthorizedAccountState, case let .payment(_, _, _, syncContactsValue) = state.contents {
|
||||||
|
syncContacts = syncContactsValue
|
||||||
|
}
|
||||||
|
|
||||||
|
if let futureAuthToken = futureAuthToken {
|
||||||
|
storeFutureLoginToken(accountManager: accountManager, token: futureAuthToken.makeData())
|
||||||
|
}
|
||||||
|
|
||||||
|
let user = TelegramUser(user: user)
|
||||||
|
var isSupportUser = false
|
||||||
|
if let phone = user.phone, phone.hasPrefix("42"), phone.count <= 5 {
|
||||||
|
isSupportUser = true
|
||||||
|
}
|
||||||
|
let state = AuthorizedAccountState(isTestingEnvironment: testingEnvironment, masterDatacenterId: masterDatacenterId, peerId: user.id, state: nil, invalidatedChannels: [])
|
||||||
|
initializedAppSettingsAfterLogin(transaction: transaction, appVersion: networkArguments.appVersion, syncContacts: syncContacts)
|
||||||
|
transaction.setState(state)
|
||||||
|
return accountManager.transaction { [weak self] transaction -> SendAuthorizationCodeResult in
|
||||||
|
if let self {
|
||||||
|
switchToAuthorizedAccount(transaction: transaction, account: self, isSupportUser: isSupportUser)
|
||||||
|
}
|
||||||
|
return .loggedIn
|
||||||
|
}
|
||||||
|
}).start()
|
||||||
|
case let .authorizationSignUpRequired(_, termsOfService):
|
||||||
|
let _ = postbox.transaction({ [weak self] transaction in
|
||||||
|
if let self {
|
||||||
|
if let state = transaction.getState() as? UnauthorizedAccountState, case let .payment(number, codeHash, _, syncContacts) = state.contents {
|
||||||
|
let _ = beginSignUp(
|
||||||
|
account: self,
|
||||||
|
data: AuthorizationSignUpData(
|
||||||
|
number: number,
|
||||||
|
codeHash: codeHash,
|
||||||
|
code: .phoneCode(""),
|
||||||
|
termsOfService: termsOfService.flatMap(UnauthorizedAccountTermsOfService.init(apiTermsOfService:)),
|
||||||
|
syncContacts: syncContacts
|
||||||
|
)
|
||||||
|
).start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
network.shouldKeepConnection.set(self.shouldBeServiceTaskMaster.get()
|
network.shouldKeepConnection.set(self.shouldBeServiceTaskMaster.get()
|
||||||
|> map { mode -> Bool in
|
|> map { mode -> Bool in
|
||||||
@ -152,7 +225,7 @@ public class UnauthorizedAccount {
|
|||||||
|> mapToSignal { localizationSettings, proxySettings, networkSettings, appConfiguration -> Signal<UnauthorizedAccount, NoError> in
|
|> mapToSignal { localizationSettings, proxySettings, networkSettings, appConfiguration -> Signal<UnauthorizedAccount, NoError> in
|
||||||
return initializedNetwork(accountId: self.id, arguments: self.networkArguments, supplementary: false, datacenterId: Int(masterDatacenterId), keychain: keychain, basePath: self.basePath, testingEnvironment: self.testingEnvironment, languageCode: localizationSettings?.primaryComponent.languageCode, proxySettings: proxySettings, networkSettings: networkSettings, phoneNumber: nil, useRequestTimeoutTimers: false, appConfiguration: appConfiguration)
|
return initializedNetwork(accountId: self.id, arguments: self.networkArguments, supplementary: false, datacenterId: Int(masterDatacenterId), keychain: keychain, basePath: self.basePath, testingEnvironment: self.testingEnvironment, languageCode: localizationSettings?.primaryComponent.languageCode, proxySettings: proxySettings, networkSettings: networkSettings, phoneNumber: nil, useRequestTimeoutTimers: false, appConfiguration: appConfiguration)
|
||||||
|> map { network in
|
|> map { network in
|
||||||
let updated = UnauthorizedAccount(networkArguments: self.networkArguments, id: self.id, rootPath: self.rootPath, basePath: self.basePath, testingEnvironment: self.testingEnvironment, postbox: self.postbox, network: network)
|
let updated = UnauthorizedAccount(accountManager: accountManager, networkArguments: self.networkArguments, id: self.id, rootPath: self.rootPath, basePath: self.basePath, testingEnvironment: self.testingEnvironment, postbox: self.postbox, network: network)
|
||||||
updated.shouldBeServiceTaskMaster.set(self.shouldBeServiceTaskMaster.get())
|
updated.shouldBeServiceTaskMaster.set(self.shouldBeServiceTaskMaster.get())
|
||||||
return updated
|
return updated
|
||||||
}
|
}
|
||||||
@ -250,7 +323,7 @@ public func accountWithId(accountManager: AccountManager<TelegramAccountManagerT
|
|||||||
case let unauthorizedState as UnauthorizedAccountState:
|
case let unauthorizedState as UnauthorizedAccountState:
|
||||||
return initializedNetwork(accountId: id, arguments: networkArguments, supplementary: supplementary, datacenterId: Int(unauthorizedState.masterDatacenterId), keychain: keychain, basePath: path, testingEnvironment: unauthorizedState.isTestingEnvironment, languageCode: localizationSettings?.primaryComponent.languageCode, proxySettings: proxySettings, networkSettings: networkSettings, phoneNumber: nil, useRequestTimeoutTimers: useRequestTimeoutTimers, appConfiguration: appConfig)
|
return initializedNetwork(accountId: id, arguments: networkArguments, supplementary: supplementary, datacenterId: Int(unauthorizedState.masterDatacenterId), keychain: keychain, basePath: path, testingEnvironment: unauthorizedState.isTestingEnvironment, languageCode: localizationSettings?.primaryComponent.languageCode, proxySettings: proxySettings, networkSettings: networkSettings, phoneNumber: nil, useRequestTimeoutTimers: useRequestTimeoutTimers, appConfiguration: appConfig)
|
||||||
|> map { network -> AccountResult in
|
|> map { network -> AccountResult in
|
||||||
return .unauthorized(UnauthorizedAccount(networkArguments: networkArguments, id: id, rootPath: rootPath, basePath: path, testingEnvironment: unauthorizedState.isTestingEnvironment, postbox: postbox, network: network, shouldKeepAutoConnection: shouldKeepAutoConnection))
|
return .unauthorized(UnauthorizedAccount(accountManager: accountManager, networkArguments: networkArguments, id: id, rootPath: rootPath, basePath: path, testingEnvironment: unauthorizedState.isTestingEnvironment, postbox: postbox, network: network, shouldKeepAutoConnection: shouldKeepAutoConnection))
|
||||||
}
|
}
|
||||||
case let authorizedState as AuthorizedAccountState:
|
case let authorizedState as AuthorizedAccountState:
|
||||||
return postbox.transaction { transaction -> String? in
|
return postbox.transaction { transaction -> String? in
|
||||||
@ -269,7 +342,7 @@ public func accountWithId(accountManager: AccountManager<TelegramAccountManagerT
|
|||||||
|
|
||||||
return initializedNetwork(accountId: id, arguments: networkArguments, supplementary: supplementary, datacenterId: 2, keychain: keychain, basePath: path, testingEnvironment: beginWithTestingEnvironment, languageCode: localizationSettings?.primaryComponent.languageCode, proxySettings: proxySettings, networkSettings: networkSettings, phoneNumber: nil, useRequestTimeoutTimers: useRequestTimeoutTimers, appConfiguration: appConfig)
|
return initializedNetwork(accountId: id, arguments: networkArguments, supplementary: supplementary, datacenterId: 2, keychain: keychain, basePath: path, testingEnvironment: beginWithTestingEnvironment, languageCode: localizationSettings?.primaryComponent.languageCode, proxySettings: proxySettings, networkSettings: networkSettings, phoneNumber: nil, useRequestTimeoutTimers: useRequestTimeoutTimers, appConfiguration: appConfig)
|
||||||
|> map { network -> AccountResult in
|
|> map { network -> AccountResult in
|
||||||
return .unauthorized(UnauthorizedAccount(networkArguments: networkArguments, id: id, rootPath: rootPath, basePath: path, testingEnvironment: beginWithTestingEnvironment, postbox: postbox, network: network, shouldKeepAutoConnection: shouldKeepAutoConnection))
|
return .unauthorized(UnauthorizedAccount(accountManager: accountManager, networkArguments: networkArguments, id: id, rootPath: rootPath, basePath: path, testingEnvironment: beginWithTestingEnvironment, postbox: postbox, network: network, shouldKeepAutoConnection: shouldKeepAutoConnection))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -356,9 +356,8 @@ public func sendAuthorizationCode(accountManager: AccountManager<TelegramAccount
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: phoneNumber, type: parsedType, hash: phoneCodeHash, timeout: codeTimeout, nextType: parsedNextType, syncContacts: syncContacts, previousCodeEntry: previousCodeEntry, usePrevious: false)))
|
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: phoneNumber, type: parsedType, hash: phoneCodeHash, timeout: codeTimeout, nextType: parsedNextType, syncContacts: syncContacts, previousCodeEntry: previousCodeEntry, usePrevious: false)))
|
||||||
case let .sentCodePaymentRequired(storeProduct):
|
case .sentCodePaymentRequired:
|
||||||
//TODO:release
|
return .never()
|
||||||
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .payment(number: phoneNumber, codeHash: "", storeProduct: storeProduct)))
|
|
||||||
case let .sentCodeSuccess(authorization):
|
case let .sentCodeSuccess(authorization):
|
||||||
switch authorization {
|
switch authorization {
|
||||||
case let .authorization(_, otherwiseReloginDays, _, futureAuthToken, user):
|
case let .authorization(_, otherwiseReloginDays, _, futureAuthToken, user):
|
||||||
@ -519,9 +518,8 @@ private func internalResendAuthorizationCode(accountManager: AccountManager<Tele
|
|||||||
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: number, type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: codeTimeout, nextType: parsedNextType, syncContacts: syncContacts, previousCodeEntry: previousCodeEntry, usePrevious: false)))
|
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: number, type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: codeTimeout, nextType: parsedNextType, syncContacts: syncContacts, previousCodeEntry: previousCodeEntry, usePrevious: false)))
|
||||||
|
|
||||||
return .single(.sentCode(account))
|
return .single(.sentCode(account))
|
||||||
case let .sentCodePaymentRequired(storeProduct):
|
case let .sentCodePaymentRequired(storeProduct, codeHash):
|
||||||
//TODO:release
|
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .payment(number: number, codeHash: codeHash, storeProduct: storeProduct, syncContacts: syncContacts)))
|
||||||
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .payment(number: number, codeHash: "", storeProduct: storeProduct)))
|
|
||||||
return .single(.sentCode(account))
|
return .single(.sentCode(account))
|
||||||
case .sentCodeSuccess:
|
case .sentCodeSuccess:
|
||||||
return .single(.loggedIn)
|
return .single(.loggedIn)
|
||||||
@ -628,9 +626,8 @@ public func resendAuthorizationCode(accountManager: AccountManager<TelegramAccou
|
|||||||
}
|
}
|
||||||
|
|
||||||
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: number, type: parsedType, hash: phoneCodeHash, timeout: codeTimeout, nextType: parsedNextType, syncContacts: syncContacts, previousCodeEntry: previousCodeEntry, usePrevious: false)))
|
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: number, type: parsedType, hash: phoneCodeHash, timeout: codeTimeout, nextType: parsedNextType, syncContacts: syncContacts, previousCodeEntry: previousCodeEntry, usePrevious: false)))
|
||||||
case let .sentCodePaymentRequired(storeProduct):
|
case let .sentCodePaymentRequired(storeProduct, codeHash):
|
||||||
//TODO:release
|
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .payment(number: number, codeHash: codeHash, storeProduct: storeProduct, syncContacts: syncContacts)))
|
||||||
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .payment(number: number, codeHash: "", storeProduct: storeProduct)))
|
|
||||||
case .sentCodeSuccess:
|
case .sentCodeSuccess:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -908,9 +905,8 @@ public func verifyLoginEmailSetup(account: UnauthorizedAccount, code: Authorizat
|
|||||||
}
|
}
|
||||||
|
|
||||||
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: phoneNumber, type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: timeout, nextType: parsedNextType, syncContacts: syncContacts, previousCodeEntry: nil, usePrevious: false)))
|
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: phoneNumber, type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: timeout, nextType: parsedNextType, syncContacts: syncContacts, previousCodeEntry: nil, usePrevious: false)))
|
||||||
case let .sentCodePaymentRequired(storeProduct):
|
case let .sentCodePaymentRequired(storeProduct, codeHash):
|
||||||
//TODO:release
|
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .payment(number: phoneNumber, codeHash: codeHash, storeProduct: storeProduct, syncContacts: syncContacts)))
|
||||||
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .payment(number: phoneNumber, codeHash: phoneCodeHash, storeProduct: storeProduct)))
|
|
||||||
case .sentCodeSuccess:
|
case .sentCodeSuccess:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -974,9 +970,8 @@ public func resetLoginEmail(account: UnauthorizedAccount, phoneNumber: String, p
|
|||||||
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: phoneNumber, type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: codeTimeout, nextType: parsedNextType, syncContacts: syncContacts, previousCodeEntry: nil, usePrevious: false)))
|
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: phoneNumber, type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: codeTimeout, nextType: parsedNextType, syncContacts: syncContacts, previousCodeEntry: nil, usePrevious: false)))
|
||||||
|
|
||||||
return .complete()
|
return .complete()
|
||||||
case let .sentCodePaymentRequired(storeProduct):
|
case let .sentCodePaymentRequired(storeProduct, codeHash):
|
||||||
//TODO:release
|
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .payment(number: phoneNumber, codeHash: codeHash, storeProduct: storeProduct, syncContacts: syncContacts)))
|
||||||
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .payment(number: phoneNumber, codeHash: phoneCodeHash, storeProduct: storeProduct)))
|
|
||||||
return .complete()
|
return .complete()
|
||||||
case .sentCodeSuccess:
|
case .sentCodeSuccess:
|
||||||
return .complete()
|
return .complete()
|
||||||
|
|||||||
@ -53,11 +53,18 @@ final class UnauthorizedAccountStateManager {
|
|||||||
private var updateService: UnauthorizedUpdateMessageService?
|
private var updateService: UnauthorizedUpdateMessageService?
|
||||||
private let updateServiceDisposable = MetaDisposable()
|
private let updateServiceDisposable = MetaDisposable()
|
||||||
private let updateLoginToken: () -> Void
|
private let updateLoginToken: () -> Void
|
||||||
|
private let updateSentCode: (Api.auth.SentCode) -> Void
|
||||||
private let displayServiceNotification: (String) -> Void
|
private let displayServiceNotification: (String) -> Void
|
||||||
|
|
||||||
init(network: Network, updateLoginToken: @escaping () -> Void, displayServiceNotification: @escaping (String) -> Void) {
|
init(
|
||||||
|
network: Network,
|
||||||
|
updateLoginToken: @escaping () -> Void,
|
||||||
|
updateSentCode: @escaping (Api.auth.SentCode) -> Void,
|
||||||
|
displayServiceNotification: @escaping (String) -> Void
|
||||||
|
) {
|
||||||
self.network = network
|
self.network = network
|
||||||
self.updateLoginToken = updateLoginToken
|
self.updateLoginToken = updateLoginToken
|
||||||
|
self.updateSentCode = updateSentCode
|
||||||
self.displayServiceNotification = displayServiceNotification
|
self.displayServiceNotification = displayServiceNotification
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,11 +72,18 @@ final class UnauthorizedAccountStateManager {
|
|||||||
self.updateServiceDisposable.dispose()
|
self.updateServiceDisposable.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func addUpdates(_ updates: Api.Updates) {
|
||||||
|
self.queue.async {
|
||||||
|
self.updateService?.addUpdates(updates)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func reset() {
|
func reset() {
|
||||||
self.queue.async {
|
self.queue.async {
|
||||||
if self.updateService == nil {
|
if self.updateService == nil {
|
||||||
self.updateService = UnauthorizedUpdateMessageService()
|
self.updateService = UnauthorizedUpdateMessageService()
|
||||||
let updateLoginToken = self.updateLoginToken
|
let updateLoginToken = self.updateLoginToken
|
||||||
|
let updateSentCode = self.updateSentCode
|
||||||
let displayServiceNotification = self.displayServiceNotification
|
let displayServiceNotification = self.displayServiceNotification
|
||||||
self.updateServiceDisposable.set(self.updateService!.pipe.signal().start(next: { updates in
|
self.updateServiceDisposable.set(self.updateService!.pipe.signal().start(next: { updates in
|
||||||
for update in updates {
|
for update in updates {
|
||||||
@ -81,6 +95,8 @@ final class UnauthorizedAccountStateManager {
|
|||||||
if popup {
|
if popup {
|
||||||
displayServiceNotification(message)
|
displayServiceNotification(message)
|
||||||
}
|
}
|
||||||
|
case let .updateSentPhoneCode(sentCode):
|
||||||
|
updateSentCode(sentCode)
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
@ -182,7 +182,7 @@ public indirect enum UnauthorizedAccountStateContents: PostboxCoding, Equatable
|
|||||||
case passwordRecovery(hint: String, number: String?, code: AuthorizationCode?, emailPattern: String, syncContacts: Bool)
|
case passwordRecovery(hint: String, number: String?, code: AuthorizationCode?, emailPattern: String, syncContacts: Bool)
|
||||||
case awaitingAccountReset(protectedUntil: Int32, number: String?, syncContacts: Bool)
|
case awaitingAccountReset(protectedUntil: Int32, number: String?, syncContacts: Bool)
|
||||||
case signUp(number: String, codeHash: String, firstName: String, lastName: String, termsOfService: UnauthorizedAccountTermsOfService?, syncContacts: Bool)
|
case signUp(number: String, codeHash: String, firstName: String, lastName: String, termsOfService: UnauthorizedAccountTermsOfService?, syncContacts: Bool)
|
||||||
case payment(number: String, codeHash: String, storeProduct: String)
|
case payment(number: String, codeHash: String, storeProduct: String, syncContacts: Bool)
|
||||||
|
|
||||||
public init(decoder: PostboxDecoder) {
|
public init(decoder: PostboxDecoder) {
|
||||||
switch decoder.decodeInt32ForKey("v", orElse: 0) {
|
switch decoder.decodeInt32ForKey("v", orElse: 0) {
|
||||||
@ -216,9 +216,8 @@ public indirect enum UnauthorizedAccountStateContents: PostboxCoding, Equatable
|
|||||||
self = .awaitingAccountReset(protectedUntil: decoder.decodeInt32ForKey("protectedUntil", orElse: 0), number: decoder.decodeOptionalStringForKey("number"), syncContacts: decoder.decodeInt32ForKey("syncContacts", orElse: 1) != 0)
|
self = .awaitingAccountReset(protectedUntil: decoder.decodeInt32ForKey("protectedUntil", orElse: 0), number: decoder.decodeOptionalStringForKey("number"), syncContacts: decoder.decodeInt32ForKey("syncContacts", orElse: 1) != 0)
|
||||||
case UnauthorizedAccountStateContentsValue.signUp.rawValue:
|
case UnauthorizedAccountStateContentsValue.signUp.rawValue:
|
||||||
self = .signUp(number: decoder.decodeStringForKey("n", orElse: ""), codeHash: decoder.decodeStringForKey("h", orElse: ""), firstName: decoder.decodeStringForKey("f", orElse: ""), lastName: decoder.decodeStringForKey("l", orElse: ""), termsOfService: decoder.decodeObjectForKey("tos", decoder: { UnauthorizedAccountTermsOfService(decoder: $0) }) as? UnauthorizedAccountTermsOfService, syncContacts: decoder.decodeInt32ForKey("syncContacts", orElse: 1) != 0)
|
self = .signUp(number: decoder.decodeStringForKey("n", orElse: ""), codeHash: decoder.decodeStringForKey("h", orElse: ""), firstName: decoder.decodeStringForKey("f", orElse: ""), lastName: decoder.decodeStringForKey("l", orElse: ""), termsOfService: decoder.decodeObjectForKey("tos", decoder: { UnauthorizedAccountTermsOfService(decoder: $0) }) as? UnauthorizedAccountTermsOfService, syncContacts: decoder.decodeInt32ForKey("syncContacts", orElse: 1) != 0)
|
||||||
|
|
||||||
case UnauthorizedAccountStateContentsValue.payment.rawValue:
|
case UnauthorizedAccountStateContentsValue.payment.rawValue:
|
||||||
self = .payment(number: decoder.decodeStringForKey("n", orElse: ""), codeHash: decoder.decodeStringForKey("h", orElse: ""), storeProduct: decoder.decodeStringForKey("storeProduct", orElse: ""))
|
self = .payment(number: decoder.decodeStringForKey("n", orElse: ""), codeHash: decoder.decodeStringForKey("h", orElse: ""), storeProduct: decoder.decodeStringForKey("storeProduct", orElse: ""), syncContacts: decoder.decodeInt32ForKey("syncContacts", orElse: 1) != 0)
|
||||||
default:
|
default:
|
||||||
assertionFailure()
|
assertionFailure()
|
||||||
self = .empty
|
self = .empty
|
||||||
@ -308,11 +307,12 @@ public indirect enum UnauthorizedAccountStateContents: PostboxCoding, Equatable
|
|||||||
encoder.encodeNil(forKey: "tos")
|
encoder.encodeNil(forKey: "tos")
|
||||||
}
|
}
|
||||||
encoder.encodeInt32(syncContacts ? 1 : 0, forKey: "syncContacts")
|
encoder.encodeInt32(syncContacts ? 1 : 0, forKey: "syncContacts")
|
||||||
case let .payment(number, codeHash, storeProduct):
|
case let .payment(number, codeHash, storeProduct, syncContacts):
|
||||||
encoder.encodeInt32(UnauthorizedAccountStateContentsValue.payment.rawValue, forKey: "v")
|
encoder.encodeInt32(UnauthorizedAccountStateContentsValue.payment.rawValue, forKey: "v")
|
||||||
encoder.encodeString(number, forKey: "n")
|
encoder.encodeString(number, forKey: "n")
|
||||||
encoder.encodeString(codeHash, forKey: "h")
|
encoder.encodeString(codeHash, forKey: "h")
|
||||||
encoder.encodeString(storeProduct, forKey: "storeProduct")
|
encoder.encodeString(storeProduct, forKey: "storeProduct")
|
||||||
|
encoder.encodeInt32(syncContacts ? 1 : 0, forKey: "syncContacts")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -384,8 +384,8 @@ public indirect enum UnauthorizedAccountStateContents: PostboxCoding, Equatable
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .payment(number, codeHash, storeProduct):
|
case let .payment(number, codeHash, storeProduct, syncContacts):
|
||||||
if case .payment(number, codeHash, storeProduct) = rhs {
|
if case .payment(number, codeHash, storeProduct, syncContacts) = rhs {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
|
|||||||
@ -148,7 +148,7 @@ private func apiInputStorePaymentPurpose(postbox: Postbox, purpose: AppStoreTran
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func _internal_sendAppStoreReceipt(postbox: Postbox, network: Network, stateManager: AccountStateManager?, receipt: Data, purpose: AppStoreTransactionPurpose) -> Signal<Never, AssignAppStoreTransactionError> {
|
func _internal_sendAppStoreReceipt(postbox: Postbox, network: Network, stateManager: AccountStateManager, receipt: Data, purpose: AppStoreTransactionPurpose) -> Signal<Never, AssignAppStoreTransactionError> {
|
||||||
return apiInputStorePaymentPurpose(postbox: postbox, purpose: purpose)
|
return apiInputStorePaymentPurpose(postbox: postbox, purpose: purpose)
|
||||||
|> castError(AssignAppStoreTransactionError.self)
|
|> castError(AssignAppStoreTransactionError.self)
|
||||||
|> mapToSignal { purpose -> Signal<Never, AssignAppStoreTransactionError> in
|
|> mapToSignal { purpose -> Signal<Never, AssignAppStoreTransactionError> in
|
||||||
@ -161,7 +161,26 @@ func _internal_sendAppStoreReceipt(postbox: Postbox, network: Network, stateMana
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|> mapToSignal { updates -> Signal<Never, AssignAppStoreTransactionError> in
|
|> mapToSignal { updates -> Signal<Never, AssignAppStoreTransactionError> in
|
||||||
stateManager?.addUpdates(updates)
|
stateManager.addUpdates(updates)
|
||||||
|
return .complete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _internal_sendAppStoreReceipt(postbox: Postbox, network: Network, stateManager: UnauthorizedAccountStateManager, receipt: Data, purpose: AppStoreTransactionPurpose) -> Signal<Never, AssignAppStoreTransactionError> {
|
||||||
|
return apiInputStorePaymentPurpose(postbox: postbox, purpose: purpose)
|
||||||
|
|> castError(AssignAppStoreTransactionError.self)
|
||||||
|
|> mapToSignal { purpose -> Signal<Never, AssignAppStoreTransactionError> in
|
||||||
|
return network.request(Api.functions.payments.assignAppStoreTransaction(receipt: Buffer(data: receipt), purpose: purpose))
|
||||||
|
|> mapError { error -> AssignAppStoreTransactionError in
|
||||||
|
if error.errorCode == 406 {
|
||||||
|
return .serverProvided
|
||||||
|
} else {
|
||||||
|
return .generic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|> mapToSignal { updates -> Signal<Never, AssignAppStoreTransactionError> in
|
||||||
|
stateManager.addUpdates(updates)
|
||||||
return .complete()
|
return .complete()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -164,7 +164,7 @@ public extension TelegramEngineUnauthorized {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func sendAppStoreReceipt(receipt: Data, purpose: AppStoreTransactionPurpose) -> Signal<Never, AssignAppStoreTransactionError> {
|
public func sendAppStoreReceipt(receipt: Data, purpose: AppStoreTransactionPurpose) -> Signal<Never, AssignAppStoreTransactionError> {
|
||||||
return _internal_sendAppStoreReceipt(postbox: self.account.postbox, network: self.account.network, stateManager: nil, receipt: receipt, purpose: purpose)
|
return _internal_sendAppStoreReceipt(postbox: self.account.postbox, network: self.account.network, stateManager: self.account.stateManager, receipt: receipt, purpose: purpose)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1213,9 +1213,9 @@ public final class ChatEmptyNodePremiumRequiredChatContent: ASDisplayNode, ChatE
|
|||||||
private var currentTheme: PresentationTheme?
|
private var currentTheme: PresentationTheme?
|
||||||
private var currentStrings: PresentationStrings?
|
private var currentStrings: PresentationStrings?
|
||||||
|
|
||||||
private let stars: StarsAmount?
|
private let stars: Int64?
|
||||||
|
|
||||||
public init(context: AccountContext, interaction: ChatPanelInterfaceInteraction?, stars: StarsAmount?) {
|
public init(context: AccountContext, interaction: ChatPanelInterfaceInteraction?, stars: Int64?) {
|
||||||
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
||||||
self.isPremiumDisabled = premiumConfiguration.isPremiumDisabled
|
self.isPremiumDisabled = premiumConfiguration.isPremiumDisabled
|
||||||
self.stars = stars
|
self.stars = stars
|
||||||
@ -1295,7 +1295,7 @@ public final class ChatEmptyNodePremiumRequiredChatContent: ASDisplayNode, ChatE
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
if let amount = self.stars {
|
if let amount = self.stars {
|
||||||
let starsString = presentationStringsFormattedNumber(Int32(amount.value), interfaceState.dateTimeFormat.groupingSeparator)
|
let starsString = presentationStringsFormattedNumber(Int32(amount), interfaceState.dateTimeFormat.groupingSeparator)
|
||||||
let rawText: String
|
let rawText: String
|
||||||
if self.isPremiumDisabled {
|
if self.isPremiumDisabled {
|
||||||
rawText = interfaceState.strings.Chat_EmptyStatePaidMessagingDisabled_Text(peerTitle, " $ \(starsString)").string
|
rawText = interfaceState.strings.Chat_EmptyStatePaidMessagingDisabled_Text(peerTitle, " $ \(starsString)").string
|
||||||
@ -1426,7 +1426,7 @@ private enum ChatEmptyNodeContentType: Equatable {
|
|||||||
case greeting
|
case greeting
|
||||||
case topic
|
case topic
|
||||||
case premiumRequired
|
case premiumRequired
|
||||||
case starsRequired
|
case starsRequired(Int64)
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class EmptyAttachedDescriptionNode: HighlightTrackingButtonNode {
|
private final class EmptyAttachedDescriptionNode: HighlightTrackingButtonNode {
|
||||||
@ -1815,8 +1815,8 @@ public final class ChatEmptyNode: ASDisplayNode {
|
|||||||
} else if let _ = interfaceState.peerNearbyData {
|
} else if let _ = interfaceState.peerNearbyData {
|
||||||
contentType = .peerNearby
|
contentType = .peerNearby
|
||||||
} else if let peer = peer as? TelegramUser {
|
} else if let peer = peer as? TelegramUser {
|
||||||
if let _ = interfaceState.sendPaidMessageStars, interfaceState.businessIntro == nil {
|
if let sendPaidMessageStars = interfaceState.sendPaidMessageStars, interfaceState.businessIntro == nil {
|
||||||
contentType = .starsRequired
|
contentType = .starsRequired(sendPaidMessageStars.value)
|
||||||
} else if interfaceState.isPremiumRequiredForMessaging {
|
} else if interfaceState.isPremiumRequiredForMessaging {
|
||||||
contentType = .premiumRequired
|
contentType = .premiumRequired
|
||||||
} else {
|
} else {
|
||||||
@ -1881,8 +1881,8 @@ public final class ChatEmptyNode: ASDisplayNode {
|
|||||||
node = ChatEmptyNodeTopicChatContent(context: self.context)
|
node = ChatEmptyNodeTopicChatContent(context: self.context)
|
||||||
case .premiumRequired:
|
case .premiumRequired:
|
||||||
node = ChatEmptyNodePremiumRequiredChatContent(context: self.context, interaction: self.interaction, stars: nil)
|
node = ChatEmptyNodePremiumRequiredChatContent(context: self.context, interaction: self.interaction, stars: nil)
|
||||||
case .starsRequired:
|
case let .starsRequired(stars):
|
||||||
node = ChatEmptyNodePremiumRequiredChatContent(context: self.context, interaction: self.interaction, stars: interfaceState.sendPaidMessageStars)
|
node = ChatEmptyNodePremiumRequiredChatContent(context: self.context, interaction: self.interaction, stars: stars)
|
||||||
}
|
}
|
||||||
self.content = (contentType, node)
|
self.content = (contentType, node)
|
||||||
self.addSubnode(node)
|
self.addSubnode(node)
|
||||||
@ -1893,7 +1893,12 @@ public final class ChatEmptyNode: ASDisplayNode {
|
|||||||
node.layer.animateScale(from: 0.0, to: 1.0, duration: duration, timingFunction: curve.timingFunction)
|
node.layer.animateScale(from: 0.0, to: 1.0, duration: duration, timingFunction: curve.timingFunction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.isUserInteractionEnabled = [.peerNearby, .greeting, .premiumRequired, .starsRequired, .cloud].contains(contentType)
|
switch contentType {
|
||||||
|
case .peerNearby, .greeting, .premiumRequired, .starsRequired, .cloud:
|
||||||
|
self.isUserInteractionEnabled = true
|
||||||
|
default:
|
||||||
|
self.isUserInteractionEnabled = false
|
||||||
|
}
|
||||||
|
|
||||||
let displayRect = CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: size.width, height: size.height - insets.top - insets.bottom))
|
let displayRect = CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: size.width, height: size.height - insets.top - insets.bottom))
|
||||||
|
|
||||||
|
|||||||
@ -1044,6 +1044,7 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
var edited = false
|
var edited = false
|
||||||
var viewCount: Int? = nil
|
var viewCount: Int? = nil
|
||||||
var dateReplies = 0
|
var dateReplies = 0
|
||||||
|
var starsCount: Int64?
|
||||||
var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.message)
|
var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.message)
|
||||||
if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) {
|
if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) {
|
||||||
dateReactionsAndPeers = ([], [])
|
dateReactionsAndPeers = ([], [])
|
||||||
@ -1057,6 +1058,8 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info {
|
if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info {
|
||||||
dateReplies = Int(attribute.count)
|
dateReplies = Int(attribute.count)
|
||||||
}
|
}
|
||||||
|
} else if let attribute = attribute as? PaidStarsMessageAttribute, item.message.id.peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||||
|
starsCount = attribute.stars.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1086,6 +1089,7 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
areReactionsTags: item.message.areReactionsTags(accountPeerId: item.context.account.peerId),
|
areReactionsTags: item.message.areReactionsTags(accountPeerId: item.context.account.peerId),
|
||||||
messageEffect: messageEffect,
|
messageEffect: messageEffect,
|
||||||
replyCount: dateReplies,
|
replyCount: dateReplies,
|
||||||
|
starsCount: starsCount,
|
||||||
isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread,
|
isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread,
|
||||||
hasAutoremove: item.message.isSelfExpiring,
|
hasAutoremove: item.message.isSelfExpiring,
|
||||||
canViewReactionList: canViewMessageReactionList(message: item.message),
|
canViewReactionList: canViewMessageReactionList(message: item.message),
|
||||||
|
|||||||
@ -675,6 +675,7 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
var viewCount: Int?
|
var viewCount: Int?
|
||||||
var dateReplies = 0
|
var dateReplies = 0
|
||||||
|
var starsCount: Int64?
|
||||||
var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: context.account.peerId, accountPeer: associatedData.accountPeer, message: message)
|
var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: context.account.peerId, accountPeer: associatedData.accountPeer, message: message)
|
||||||
if message.isRestricted(platform: "ios", contentSettings: context.currentContentSettings.with { $0 }) || presentationData.isPreview {
|
if message.isRestricted(platform: "ios", contentSettings: context.currentContentSettings.with { $0 }) || presentationData.isPreview {
|
||||||
dateReactionsAndPeers = ([], [])
|
dateReactionsAndPeers = ([], [])
|
||||||
@ -688,6 +689,8 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
|
|||||||
if let channel = message.peers[message.id.peerId] as? TelegramChannel, case .group = channel.info {
|
if let channel = message.peers[message.id.peerId] as? TelegramChannel, case .group = channel.info {
|
||||||
dateReplies = Int(attribute.count)
|
dateReplies = Int(attribute.count)
|
||||||
}
|
}
|
||||||
|
} else if let attribute = attribute as? PaidStarsMessageAttribute, message.id.peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||||
|
starsCount = attribute.stars.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -747,6 +750,7 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
|
|||||||
areReactionsTags: message.areReactionsTags(accountPeerId: context.account.peerId),
|
areReactionsTags: message.areReactionsTags(accountPeerId: context.account.peerId),
|
||||||
messageEffect: message.messageEffect(availableMessageEffects: associatedData.availableMessageEffects),
|
messageEffect: message.messageEffect(availableMessageEffects: associatedData.availableMessageEffects),
|
||||||
replyCount: dateReplies,
|
replyCount: dateReplies,
|
||||||
|
starsCount: starsCount,
|
||||||
isPinned: message.tags.contains(.pinned) && !associatedData.isInPinnedListMode && !isReplyThread,
|
isPinned: message.tags.contains(.pinned) && !associatedData.isInPinnedListMode && !isReplyThread,
|
||||||
hasAutoremove: message.isSelfExpiring,
|
hasAutoremove: message.isSelfExpiring,
|
||||||
canViewReactionList: canViewMessageReactionList(message: message),
|
canViewReactionList: canViewMessageReactionList(message: message),
|
||||||
|
|||||||
@ -130,7 +130,7 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([
|
|||||||
result.append((message, ChatMessageRestrictedBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default)))
|
result.append((message, ChatMessageRestrictedBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default)))
|
||||||
needReactions = false
|
needReactions = false
|
||||||
break outer
|
break outer
|
||||||
} else if let _ = attribute as? PaidStarsMessageAttribute, !addedPriceInfo {
|
} else if let _ = attribute as? PaidStarsMessageAttribute, !addedPriceInfo, message.id.peerId.namespace == Namespaces.Peer.CloudUser {
|
||||||
result.append((message, ChatMessageActionBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default)))
|
result.append((message, ChatMessageActionBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default)))
|
||||||
addedPriceInfo = true
|
addedPriceInfo = true
|
||||||
}
|
}
|
||||||
@ -2276,6 +2276,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
}
|
}
|
||||||
var viewCount: Int?
|
var viewCount: Int?
|
||||||
var dateReplies = 0
|
var dateReplies = 0
|
||||||
|
var starsCount: Int64?
|
||||||
var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: message)
|
var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: message)
|
||||||
if message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) {
|
if message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) {
|
||||||
dateReactionsAndPeers = ([], [])
|
dateReactionsAndPeers = ([], [])
|
||||||
@ -2289,6 +2290,8 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
if let channel = message.peers[message.id.peerId] as? TelegramChannel, case .group = channel.info {
|
if let channel = message.peers[message.id.peerId] as? TelegramChannel, case .group = channel.info {
|
||||||
dateReplies = Int(attribute.count)
|
dateReplies = Int(attribute.count)
|
||||||
}
|
}
|
||||||
|
} else if let attribute = attribute as? PaidStarsMessageAttribute, item.message.id.peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||||
|
starsCount = attribute.stars.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2337,6 +2340,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
areReactionsTags: item.message.areReactionsTags(accountPeerId: item.context.account.peerId),
|
areReactionsTags: item.message.areReactionsTags(accountPeerId: item.context.account.peerId),
|
||||||
messageEffect: item.message.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects),
|
messageEffect: item.message.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects),
|
||||||
replyCount: dateReplies,
|
replyCount: dateReplies,
|
||||||
|
starsCount: starsCount,
|
||||||
isPinned: message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread,
|
isPinned: message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread,
|
||||||
hasAutoremove: message.isSelfExpiring,
|
hasAutoremove: message.isSelfExpiring,
|
||||||
canViewReactionList: canViewMessageReactionList(message: message),
|
canViewReactionList: canViewMessageReactionList(message: message),
|
||||||
|
|||||||
@ -233,6 +233,7 @@ public class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
}
|
}
|
||||||
var viewCount: Int?
|
var viewCount: Int?
|
||||||
var dateReplies = 0
|
var dateReplies = 0
|
||||||
|
var starsCount: Int64?
|
||||||
var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.message)
|
var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.message)
|
||||||
if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) {
|
if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) {
|
||||||
dateReactionsAndPeers = ([], [])
|
dateReactionsAndPeers = ([], [])
|
||||||
@ -246,6 +247,8 @@ public class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info {
|
if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info {
|
||||||
dateReplies = Int(attribute.count)
|
dateReplies = Int(attribute.count)
|
||||||
}
|
}
|
||||||
|
} else if let attribute = attribute as? PaidStarsMessageAttribute, item.message.id.peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||||
|
starsCount = attribute.stars.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -300,6 +303,7 @@ public class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
areReactionsTags: item.topMessage.areReactionsTags(accountPeerId: item.context.account.peerId),
|
areReactionsTags: item.topMessage.areReactionsTags(accountPeerId: item.context.account.peerId),
|
||||||
messageEffect: messageEffect,
|
messageEffect: messageEffect,
|
||||||
replyCount: dateReplies,
|
replyCount: dateReplies,
|
||||||
|
starsCount: starsCount,
|
||||||
isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && isReplyThread,
|
isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && isReplyThread,
|
||||||
hasAutoremove: item.message.isSelfExpiring,
|
hasAutoremove: item.message.isSelfExpiring,
|
||||||
canViewReactionList: canViewMessageReactionList(message: item.topMessage),
|
canViewReactionList: canViewMessageReactionList(message: item.topMessage),
|
||||||
|
|||||||
@ -195,6 +195,7 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode {
|
|||||||
var areReactionsTags: Bool
|
var areReactionsTags: Bool
|
||||||
var messageEffect: AvailableMessageEffects.MessageEffect?
|
var messageEffect: AvailableMessageEffects.MessageEffect?
|
||||||
var replyCount: Int
|
var replyCount: Int
|
||||||
|
var starsCount: Int64?
|
||||||
var isPinned: Bool
|
var isPinned: Bool
|
||||||
var hasAutoremove: Bool
|
var hasAutoremove: Bool
|
||||||
var canViewReactionList: Bool
|
var canViewReactionList: Bool
|
||||||
@ -218,6 +219,7 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode {
|
|||||||
areReactionsTags: Bool,
|
areReactionsTags: Bool,
|
||||||
messageEffect: AvailableMessageEffects.MessageEffect?,
|
messageEffect: AvailableMessageEffects.MessageEffect?,
|
||||||
replyCount: Int,
|
replyCount: Int,
|
||||||
|
starsCount: Int64?,
|
||||||
isPinned: Bool,
|
isPinned: Bool,
|
||||||
hasAutoremove: Bool,
|
hasAutoremove: Bool,
|
||||||
canViewReactionList: Bool,
|
canViewReactionList: Bool,
|
||||||
@ -240,6 +242,7 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode {
|
|||||||
self.areReactionsTags = areReactionsTags
|
self.areReactionsTags = areReactionsTags
|
||||||
self.messageEffect = messageEffect
|
self.messageEffect = messageEffect
|
||||||
self.replyCount = replyCount
|
self.replyCount = replyCount
|
||||||
|
self.starsCount = starsCount
|
||||||
self.isPinned = isPinned
|
self.isPinned = isPinned
|
||||||
self.hasAutoremove = hasAutoremove
|
self.hasAutoremove = hasAutoremove
|
||||||
self.canViewReactionList = canViewReactionList
|
self.canViewReactionList = canViewReactionList
|
||||||
@ -262,6 +265,8 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode {
|
|||||||
private var repliesIcon: ASImageNode?
|
private var repliesIcon: ASImageNode?
|
||||||
private var selfExpiringIcon: ASImageNode?
|
private var selfExpiringIcon: ASImageNode?
|
||||||
private var replyCountNode: TextNode?
|
private var replyCountNode: TextNode?
|
||||||
|
private var starsIcon: ASImageNode?
|
||||||
|
private var starsCountNode: TextNode?
|
||||||
|
|
||||||
private var type: ChatMessageDateAndStatusType?
|
private var type: ChatMessageDateAndStatusType?
|
||||||
private var theme: ChatPresentationThemeData?
|
private var theme: ChatPresentationThemeData?
|
||||||
@ -316,11 +321,13 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode {
|
|||||||
var currentBackgroundNode = self.backgroundNode
|
var currentBackgroundNode = self.backgroundNode
|
||||||
var currentImpressionIcon = self.impressionIcon
|
var currentImpressionIcon = self.impressionIcon
|
||||||
var currentRepliesIcon = self.repliesIcon
|
var currentRepliesIcon = self.repliesIcon
|
||||||
|
var currentStarsIcon = self.starsIcon
|
||||||
|
|
||||||
let currentType = self.type
|
let currentType = self.type
|
||||||
let currentTheme = self.theme
|
let currentTheme = self.theme
|
||||||
|
|
||||||
let makeReplyCountLayout = TextNode.asyncLayout(self.replyCountNode)
|
let makeReplyCountLayout = TextNode.asyncLayout(self.replyCountNode)
|
||||||
|
let makeStarsCountLayout = TextNode.asyncLayout(self.starsCountNode)
|
||||||
|
|
||||||
let reactionButtonsContainer = self.reactionButtonsContainer
|
let reactionButtonsContainer = self.reactionButtonsContainer
|
||||||
|
|
||||||
@ -337,6 +344,7 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode {
|
|||||||
let clockMinImage: UIImage?
|
let clockMinImage: UIImage?
|
||||||
var impressionImage: UIImage?
|
var impressionImage: UIImage?
|
||||||
var repliesImage: UIImage?
|
var repliesImage: UIImage?
|
||||||
|
var starsImage: UIImage?
|
||||||
|
|
||||||
let themeUpdated = arguments.presentationData.theme != currentTheme || arguments.type != currentType
|
let themeUpdated = arguments.presentationData.theme != currentTheme || arguments.type != currentType
|
||||||
|
|
||||||
@ -404,6 +412,9 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode {
|
|||||||
} else if arguments.isPinned {
|
} else if arguments.isPinned {
|
||||||
repliesImage = graphics.incomingDateAndStatusPinnedIcon
|
repliesImage = graphics.incomingDateAndStatusPinnedIcon
|
||||||
}
|
}
|
||||||
|
if (arguments.starsCount ?? 0) != 0 {
|
||||||
|
starsImage = graphics.incomingDateAndStatusRepliesIcon
|
||||||
|
}
|
||||||
case let .BubbleOutgoing(status):
|
case let .BubbleOutgoing(status):
|
||||||
dateColor = arguments.presentationData.theme.theme.chat.message.outgoing.secondaryTextColor
|
dateColor = arguments.presentationData.theme.theme.chat.message.outgoing.secondaryTextColor
|
||||||
outgoingStatus = status
|
outgoingStatus = status
|
||||||
@ -420,6 +431,9 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode {
|
|||||||
} else if arguments.isPinned {
|
} else if arguments.isPinned {
|
||||||
repliesImage = graphics.outgoingDateAndStatusPinnedIcon
|
repliesImage = graphics.outgoingDateAndStatusPinnedIcon
|
||||||
}
|
}
|
||||||
|
if (arguments.starsCount ?? 0) != 0 {
|
||||||
|
starsImage = graphics.outgoingDateAndStatusRepliesIcon
|
||||||
|
}
|
||||||
case .ImageIncoming:
|
case .ImageIncoming:
|
||||||
dateColor = arguments.presentationData.theme.theme.chat.message.mediaDateAndStatusTextColor
|
dateColor = arguments.presentationData.theme.theme.chat.message.mediaDateAndStatusTextColor
|
||||||
backgroundImage = graphics.dateAndStatusMediaBackground
|
backgroundImage = graphics.dateAndStatusMediaBackground
|
||||||
@ -436,6 +450,9 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode {
|
|||||||
} else if arguments.isPinned {
|
} else if arguments.isPinned {
|
||||||
repliesImage = graphics.mediaPinnedIcon
|
repliesImage = graphics.mediaPinnedIcon
|
||||||
}
|
}
|
||||||
|
if (arguments.starsCount ?? 0) != 0 {
|
||||||
|
starsImage = graphics.mediaRepliesIcon
|
||||||
|
}
|
||||||
case let .ImageOutgoing(status):
|
case let .ImageOutgoing(status):
|
||||||
dateColor = arguments.presentationData.theme.theme.chat.message.mediaDateAndStatusTextColor
|
dateColor = arguments.presentationData.theme.theme.chat.message.mediaDateAndStatusTextColor
|
||||||
outgoingStatus = status
|
outgoingStatus = status
|
||||||
@ -453,6 +470,9 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode {
|
|||||||
} else if arguments.isPinned {
|
} else if arguments.isPinned {
|
||||||
repliesImage = graphics.mediaPinnedIcon
|
repliesImage = graphics.mediaPinnedIcon
|
||||||
}
|
}
|
||||||
|
if (arguments.starsCount ?? 0) != 0 {
|
||||||
|
starsImage = graphics.mediaRepliesIcon
|
||||||
|
}
|
||||||
case .FreeIncoming:
|
case .FreeIncoming:
|
||||||
let serviceColor = serviceMessageColorComponents(theme: arguments.presentationData.theme.theme, wallpaper: arguments.presentationData.theme.wallpaper)
|
let serviceColor = serviceMessageColorComponents(theme: arguments.presentationData.theme.theme, wallpaper: arguments.presentationData.theme.wallpaper)
|
||||||
dateColor = serviceColor.primaryText
|
dateColor = serviceColor.primaryText
|
||||||
@ -471,6 +491,9 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode {
|
|||||||
} else if arguments.isPinned {
|
} else if arguments.isPinned {
|
||||||
repliesImage = graphics.freePinnedIcon
|
repliesImage = graphics.freePinnedIcon
|
||||||
}
|
}
|
||||||
|
if (arguments.starsCount ?? 0) != 0 {
|
||||||
|
starsImage = graphics.freeRepliesIcon
|
||||||
|
}
|
||||||
case let .FreeOutgoing(status):
|
case let .FreeOutgoing(status):
|
||||||
let serviceColor = serviceMessageColorComponents(theme: arguments.presentationData.theme.theme, wallpaper: arguments.presentationData.theme.wallpaper)
|
let serviceColor = serviceMessageColorComponents(theme: arguments.presentationData.theme.theme, wallpaper: arguments.presentationData.theme.wallpaper)
|
||||||
dateColor = serviceColor.primaryText
|
dateColor = serviceColor.primaryText
|
||||||
@ -489,6 +512,9 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode {
|
|||||||
} else if arguments.isPinned {
|
} else if arguments.isPinned {
|
||||||
repliesImage = graphics.freePinnedIcon
|
repliesImage = graphics.freePinnedIcon
|
||||||
}
|
}
|
||||||
|
if (arguments.starsCount ?? 0) != 0 {
|
||||||
|
starsImage = graphics.freeRepliesIcon
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var updatedDateText = arguments.dateText
|
var updatedDateText = arguments.dateText
|
||||||
@ -541,6 +567,20 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode {
|
|||||||
currentRepliesIcon = nil
|
currentRepliesIcon = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var starsIconSize = CGSize()
|
||||||
|
if let starsImage = starsImage {
|
||||||
|
if currentStarsIcon == nil {
|
||||||
|
let iconNode = ASImageNode()
|
||||||
|
iconNode.isLayerBacked = true
|
||||||
|
iconNode.displayWithoutProcessing = true
|
||||||
|
iconNode.displaysAsynchronously = false
|
||||||
|
currentStarsIcon = iconNode
|
||||||
|
}
|
||||||
|
starsIconSize = starsImage.size
|
||||||
|
} else {
|
||||||
|
currentStarsIcon = nil
|
||||||
|
}
|
||||||
|
|
||||||
if let outgoingStatus = outgoingStatus {
|
if let outgoingStatus = outgoingStatus {
|
||||||
switch outgoingStatus {
|
switch outgoingStatus {
|
||||||
case .Sending:
|
case .Sending:
|
||||||
@ -652,6 +692,7 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var replyCountLayoutAndApply: (TextNodeLayout, () -> TextNode)?
|
var replyCountLayoutAndApply: (TextNodeLayout, () -> TextNode)?
|
||||||
|
var starsCountLayoutAndApply: (TextNodeLayout, () -> TextNode)?
|
||||||
|
|
||||||
let reactionSize: CGFloat = 8.0
|
let reactionSize: CGFloat = 8.0
|
||||||
let reactionSpacing: CGFloat = 2.0
|
let reactionSpacing: CGFloat = 2.0
|
||||||
@ -676,6 +717,21 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode {
|
|||||||
reactionInset += 12.0
|
reactionInset += 12.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let starsCount = arguments.starsCount, starsCount > 0 {
|
||||||
|
let countString: String
|
||||||
|
if starsCount > 1000000 {
|
||||||
|
countString = "\(starsCount / 1000000)M"
|
||||||
|
} else if starsCount > 1000 {
|
||||||
|
countString = "\(starsCount / 1000)K"
|
||||||
|
} else {
|
||||||
|
countString = "\(starsCount)"
|
||||||
|
}
|
||||||
|
|
||||||
|
let layoutAndApply = makeStarsCountLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: countString, font: dateFont, textColor: dateColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 100.0, height: 100.0)))
|
||||||
|
reactionInset += 14.0 + layoutAndApply.0.size.width + 4.0
|
||||||
|
starsCountLayoutAndApply = layoutAndApply
|
||||||
|
}
|
||||||
|
|
||||||
if arguments.messageEffect != nil {
|
if arguments.messageEffect != nil {
|
||||||
reactionInset += 13.0
|
reactionInset += 13.0
|
||||||
}
|
}
|
||||||
@ -1237,6 +1293,56 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode {
|
|||||||
replyCountNode.removeFromSupernode()
|
replyCountNode.removeFromSupernode()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let currentStarsIcon = currentStarsIcon {
|
||||||
|
currentStarsIcon.displaysAsynchronously = false
|
||||||
|
if currentStarsIcon.image !== starsImage {
|
||||||
|
currentStarsIcon.image = starsImage
|
||||||
|
}
|
||||||
|
if currentStarsIcon.supernode == nil {
|
||||||
|
strongSelf.starsIcon = currentStarsIcon
|
||||||
|
strongSelf.addSubnode(currentStarsIcon)
|
||||||
|
if animation.isAnimated {
|
||||||
|
currentStarsIcon.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let starsIconFrame = CGRect(origin: CGPoint(x: reactionOffset - 2.0, y: backgroundInsets.top + offset + verticalInset + floor((date.size.height - starsIconSize.height) / 2.0)), size: starsIconSize)
|
||||||
|
animation.animator.updateFrame(layer: currentStarsIcon.layer, frame: starsIconFrame, completion: nil)
|
||||||
|
reactionOffset += 9.0
|
||||||
|
} else if let starsIcon = strongSelf.starsIcon {
|
||||||
|
strongSelf.starsIcon = nil
|
||||||
|
if animation.isAnimated {
|
||||||
|
starsIcon.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak starsIcon] _ in
|
||||||
|
starsIcon?.removeFromSupernode()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
starsIcon.removeFromSupernode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let (layout, apply) = starsCountLayoutAndApply {
|
||||||
|
let node = apply()
|
||||||
|
if strongSelf.starsCountNode !== node {
|
||||||
|
strongSelf.starsCountNode?.removeFromSupernode()
|
||||||
|
strongSelf.addSubnode(node)
|
||||||
|
strongSelf.starsCountNode = node
|
||||||
|
if animation.isAnimated {
|
||||||
|
node.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let starsCountFrame = CGRect(origin: CGPoint(x: reactionOffset + 4.0, y: backgroundInsets.top + 1.0 + offset + verticalInset), size: layout.size)
|
||||||
|
animation.animator.updateFrame(layer: node.layer, frame: starsCountFrame, completion: nil)
|
||||||
|
reactionOffset += 4.0 + layout.size.width
|
||||||
|
} else if let starsCountNode = strongSelf.starsCountNode {
|
||||||
|
strongSelf.starsCountNode = nil
|
||||||
|
if animation.isAnimated {
|
||||||
|
starsCountNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak starsCountNode] _ in
|
||||||
|
starsCountNode?.removeFromSupernode()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
starsCountNode.removeFromSupernode()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -295,6 +295,7 @@ public class ChatMessageFactCheckBubbleContentNode: ChatMessageBubbleContentNode
|
|||||||
var rawText = ""
|
var rawText = ""
|
||||||
var rawEntities: [MessageTextEntity] = []
|
var rawEntities: [MessageTextEntity] = []
|
||||||
var dateReplies = 0
|
var dateReplies = 0
|
||||||
|
var starsCount: Int64?
|
||||||
var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.message)
|
var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.message)
|
||||||
if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) {
|
if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) {
|
||||||
dateReactionsAndPeers = ([], [])
|
dateReactionsAndPeers = ([], [])
|
||||||
@ -311,6 +312,8 @@ public class ChatMessageFactCheckBubbleContentNode: ChatMessageBubbleContentNode
|
|||||||
if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info {
|
if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info {
|
||||||
dateReplies = Int(attribute.count)
|
dateReplies = Int(attribute.count)
|
||||||
}
|
}
|
||||||
|
} else if let attribute = attribute as? PaidStarsMessageAttribute, item.message.id.peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||||
|
starsCount = attribute.stars.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -447,6 +450,7 @@ public class ChatMessageFactCheckBubbleContentNode: ChatMessageBubbleContentNode
|
|||||||
areReactionsTags: item.topMessage.areReactionsTags(accountPeerId: item.context.account.peerId),
|
areReactionsTags: item.topMessage.areReactionsTags(accountPeerId: item.context.account.peerId),
|
||||||
messageEffect: item.topMessage.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects),
|
messageEffect: item.topMessage.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects),
|
||||||
replyCount: dateReplies,
|
replyCount: dateReplies,
|
||||||
|
starsCount: starsCount,
|
||||||
isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && isReplyThread,
|
isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && isReplyThread,
|
||||||
hasAutoremove: item.message.isSelfExpiring,
|
hasAutoremove: item.message.isSelfExpiring,
|
||||||
canViewReactionList: canViewMessageReactionList(message: item.topMessage),
|
canViewReactionList: canViewMessageReactionList(message: item.topMessage),
|
||||||
|
|||||||
@ -578,6 +578,7 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode,
|
|||||||
areReactionsTags: item.topMessage.areReactionsTags(accountPeerId: item.context.account.peerId),
|
areReactionsTags: item.topMessage.areReactionsTags(accountPeerId: item.context.account.peerId),
|
||||||
messageEffect: item.topMessage.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects),
|
messageEffect: item.topMessage.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects),
|
||||||
replyCount: dateReplies,
|
replyCount: dateReplies,
|
||||||
|
starsCount: nil,
|
||||||
isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && isReplyThread,
|
isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && isReplyThread,
|
||||||
hasAutoremove: item.message.isSelfExpiring,
|
hasAutoremove: item.message.isSelfExpiring,
|
||||||
canViewReactionList: canViewMessageReactionList(message: item.topMessage),
|
canViewReactionList: canViewMessageReactionList(message: item.topMessage),
|
||||||
|
|||||||
@ -898,6 +898,7 @@ public final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
var viewCount: Int?
|
var viewCount: Int?
|
||||||
var dateReplies = 0
|
var dateReplies = 0
|
||||||
|
var starsCount: Int64?
|
||||||
var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: arguments.context.account.peerId, accountPeer: arguments.associatedData.accountPeer, message: arguments.topMessage)
|
var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: arguments.context.account.peerId, accountPeer: arguments.associatedData.accountPeer, message: arguments.topMessage)
|
||||||
if arguments.topMessage.isRestricted(platform: "ios", contentSettings: arguments.context.currentContentSettings.with { $0 }) || arguments.presentationData.isPreview {
|
if arguments.topMessage.isRestricted(platform: "ios", contentSettings: arguments.context.currentContentSettings.with { $0 }) || arguments.presentationData.isPreview {
|
||||||
dateReactionsAndPeers = ([], [])
|
dateReactionsAndPeers = ([], [])
|
||||||
@ -911,6 +912,8 @@ public final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
|||||||
if let channel = arguments.message.peers[arguments.message.id.peerId] as? TelegramChannel, case .group = channel.info {
|
if let channel = arguments.message.peers[arguments.message.id.peerId] as? TelegramChannel, case .group = channel.info {
|
||||||
dateReplies = Int(attribute.count)
|
dateReplies = Int(attribute.count)
|
||||||
}
|
}
|
||||||
|
} else if let attribute = attribute as? PaidStarsMessageAttribute, arguments.message.id.peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||||
|
starsCount = attribute.stars.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if arguments.forcedIsEdited {
|
if arguments.forcedIsEdited {
|
||||||
@ -956,6 +959,7 @@ public final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
|||||||
areReactionsTags: arguments.message.areReactionsTags(accountPeerId: arguments.context.account.peerId),
|
areReactionsTags: arguments.message.areReactionsTags(accountPeerId: arguments.context.account.peerId),
|
||||||
messageEffect: arguments.message.messageEffect(availableMessageEffects: arguments.associatedData.availableMessageEffects),
|
messageEffect: arguments.message.messageEffect(availableMessageEffects: arguments.associatedData.availableMessageEffects),
|
||||||
replyCount: dateReplies,
|
replyCount: dateReplies,
|
||||||
|
starsCount: starsCount,
|
||||||
isPinned: arguments.isPinned && !arguments.associatedData.isInPinnedListMode,
|
isPinned: arguments.isPinned && !arguments.associatedData.isInPinnedListMode,
|
||||||
hasAutoremove: arguments.message.isSelfExpiring,
|
hasAutoremove: arguments.message.isSelfExpiring,
|
||||||
canViewReactionList: canViewMessageReactionList(message: arguments.topMessage),
|
canViewReactionList: canViewMessageReactionList(message: arguments.topMessage),
|
||||||
|
|||||||
@ -524,6 +524,7 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
|||||||
let sentViaBot = false
|
let sentViaBot = false
|
||||||
var viewCount: Int? = nil
|
var viewCount: Int? = nil
|
||||||
var dateReplies = 0
|
var dateReplies = 0
|
||||||
|
var starsCount: Int64?
|
||||||
var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.message)
|
var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.message)
|
||||||
if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) {
|
if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) {
|
||||||
dateReactionsAndPeers = ([], [])
|
dateReactionsAndPeers = ([], [])
|
||||||
@ -537,6 +538,8 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
|||||||
if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info {
|
if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info {
|
||||||
dateReplies = Int(attribute.count)
|
dateReplies = Int(attribute.count)
|
||||||
}
|
}
|
||||||
|
} else if let attribute = attribute as? PaidStarsMessageAttribute, item.message.id.peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||||
|
starsCount = attribute.stars.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -583,6 +586,7 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
|||||||
areReactionsTags: item.topMessage.areReactionsTags(accountPeerId: item.context.account.peerId),
|
areReactionsTags: item.topMessage.areReactionsTags(accountPeerId: item.context.account.peerId),
|
||||||
messageEffect: messageEffect,
|
messageEffect: messageEffect,
|
||||||
replyCount: dateReplies,
|
replyCount: dateReplies,
|
||||||
|
starsCount: starsCount,
|
||||||
isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread,
|
isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread,
|
||||||
hasAutoremove: item.message.isSelfExpiring,
|
hasAutoremove: item.message.isSelfExpiring,
|
||||||
canViewReactionList: canViewMessageReactionList(message: item.topMessage),
|
canViewReactionList: canViewMessageReactionList(message: item.topMessage),
|
||||||
|
|||||||
@ -83,6 +83,7 @@ public struct ChatMessageDateAndStatus {
|
|||||||
public var dateReactions: [MessageReaction]
|
public var dateReactions: [MessageReaction]
|
||||||
public var dateReactionPeers: [(MessageReaction.Reaction, EnginePeer)]
|
public var dateReactionPeers: [(MessageReaction.Reaction, EnginePeer)]
|
||||||
public var dateReplies: Int
|
public var dateReplies: Int
|
||||||
|
public var starsCount: Int64?
|
||||||
public var isPinned: Bool
|
public var isPinned: Bool
|
||||||
public var dateText: String
|
public var dateText: String
|
||||||
|
|
||||||
@ -93,6 +94,7 @@ public struct ChatMessageDateAndStatus {
|
|||||||
dateReactions: [MessageReaction],
|
dateReactions: [MessageReaction],
|
||||||
dateReactionPeers: [(MessageReaction.Reaction, EnginePeer)],
|
dateReactionPeers: [(MessageReaction.Reaction, EnginePeer)],
|
||||||
dateReplies: Int,
|
dateReplies: Int,
|
||||||
|
starsCount: Int64?,
|
||||||
isPinned: Bool,
|
isPinned: Bool,
|
||||||
dateText: String
|
dateText: String
|
||||||
) {
|
) {
|
||||||
@ -102,6 +104,7 @@ public struct ChatMessageDateAndStatus {
|
|||||||
self.dateReactions = dateReactions
|
self.dateReactions = dateReactions
|
||||||
self.dateReactionPeers = dateReactionPeers
|
self.dateReactionPeers = dateReactionPeers
|
||||||
self.dateReplies = dateReplies
|
self.dateReplies = dateReplies
|
||||||
|
self.starsCount = starsCount
|
||||||
self.isPinned = isPinned
|
self.isPinned = isPinned
|
||||||
self.dateText = dateText
|
self.dateText = dateText
|
||||||
}
|
}
|
||||||
@ -1118,6 +1121,7 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
|||||||
areReactionsTags: message.areReactionsTags(accountPeerId: context.account.peerId),
|
areReactionsTags: message.areReactionsTags(accountPeerId: context.account.peerId),
|
||||||
messageEffect: messageEffect,
|
messageEffect: messageEffect,
|
||||||
replyCount: dateAndStatus.dateReplies,
|
replyCount: dateAndStatus.dateReplies,
|
||||||
|
starsCount: dateAndStatus.starsCount,
|
||||||
isPinned: dateAndStatus.isPinned,
|
isPinned: dateAndStatus.isPinned,
|
||||||
hasAutoremove: message.isSelfExpiring,
|
hasAutoremove: message.isSelfExpiring,
|
||||||
canViewReactionList: canViewMessageReactionList(message: message),
|
canViewReactionList: canViewMessageReactionList(message: message),
|
||||||
|
|||||||
@ -192,6 +192,7 @@ public class ChatMessageMapBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
}
|
}
|
||||||
var viewCount: Int?
|
var viewCount: Int?
|
||||||
var dateReplies = 0
|
var dateReplies = 0
|
||||||
|
var starsCount: Int64?
|
||||||
var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.message)
|
var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.message)
|
||||||
if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) {
|
if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) {
|
||||||
dateReactionsAndPeers = ([], [])
|
dateReactionsAndPeers = ([], [])
|
||||||
@ -205,6 +206,8 @@ public class ChatMessageMapBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info {
|
if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info {
|
||||||
dateReplies = Int(attribute.count)
|
dateReplies = Int(attribute.count)
|
||||||
}
|
}
|
||||||
|
} else if let attribute = attribute as? PaidStarsMessageAttribute, item.message.id.peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||||
|
starsCount = attribute.stars.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,6 +287,7 @@ public class ChatMessageMapBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
areReactionsTags: item.topMessage.areReactionsTags(accountPeerId: item.context.account.peerId),
|
areReactionsTags: item.topMessage.areReactionsTags(accountPeerId: item.context.account.peerId),
|
||||||
messageEffect: item.topMessage.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects),
|
messageEffect: item.topMessage.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects),
|
||||||
replyCount: dateReplies,
|
replyCount: dateReplies,
|
||||||
|
starsCount: starsCount,
|
||||||
isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread,
|
isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread,
|
||||||
hasAutoremove: item.message.isSelfExpiring,
|
hasAutoremove: item.message.isSelfExpiring,
|
||||||
canViewReactionList: canViewMessageReactionList(message: item.topMessage),
|
canViewReactionList: canViewMessageReactionList(message: item.topMessage),
|
||||||
|
|||||||
@ -307,6 +307,7 @@ public class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
}
|
}
|
||||||
var viewCount: Int?
|
var viewCount: Int?
|
||||||
var dateReplies = 0
|
var dateReplies = 0
|
||||||
|
var starsCount: Int64?
|
||||||
var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.message)
|
var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.message)
|
||||||
if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) {
|
if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) {
|
||||||
dateReactionsAndPeers = ([], [])
|
dateReactionsAndPeers = ([], [])
|
||||||
@ -323,6 +324,8 @@ public class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info {
|
if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info {
|
||||||
dateReplies = Int(attribute.count)
|
dateReplies = Int(attribute.count)
|
||||||
}
|
}
|
||||||
|
} else if let attribute = attribute as? PaidStarsMessageAttribute, item.message.id.peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||||
|
starsCount = attribute.stars.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -373,6 +376,7 @@ public class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
dateReactions: dateReactionsAndPeers.reactions,
|
dateReactions: dateReactionsAndPeers.reactions,
|
||||||
dateReactionPeers: dateReactionsAndPeers.peers,
|
dateReactionPeers: dateReactionsAndPeers.peers,
|
||||||
dateReplies: dateReplies,
|
dateReplies: dateReplies,
|
||||||
|
starsCount: starsCount,
|
||||||
isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread,
|
isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread,
|
||||||
dateText: dateText
|
dateText: dateText
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1054,6 +1054,7 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
}
|
}
|
||||||
var viewCount: Int?
|
var viewCount: Int?
|
||||||
var dateReplies = 0
|
var dateReplies = 0
|
||||||
|
var starsCount: Int64?
|
||||||
var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.message)
|
var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.message)
|
||||||
if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) {
|
if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) {
|
||||||
dateReactionsAndPeers = ([], [])
|
dateReactionsAndPeers = ([], [])
|
||||||
@ -1067,6 +1068,8 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info {
|
if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info {
|
||||||
dateReplies = Int(attribute.count)
|
dateReplies = Int(attribute.count)
|
||||||
}
|
}
|
||||||
|
} else if let attribute = attribute as? PaidStarsMessageAttribute, item.message.id.peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||||
|
starsCount = attribute.stars.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1125,6 +1128,7 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
areReactionsTags: item.topMessage.areReactionsTags(accountPeerId: item.context.account.peerId),
|
areReactionsTags: item.topMessage.areReactionsTags(accountPeerId: item.context.account.peerId),
|
||||||
messageEffect: item.topMessage.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects),
|
messageEffect: item.topMessage.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects),
|
||||||
replyCount: dateReplies,
|
replyCount: dateReplies,
|
||||||
|
starsCount: starsCount,
|
||||||
isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread,
|
isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread,
|
||||||
hasAutoremove: item.message.isSelfExpiring,
|
hasAutoremove: item.message.isSelfExpiring,
|
||||||
canViewReactionList: canViewMessageReactionList(message: item.topMessage),
|
canViewReactionList: canViewMessageReactionList(message: item.topMessage),
|
||||||
|
|||||||
@ -56,6 +56,7 @@ public class ChatMessageRestrictedBubbleContentNode: ChatMessageBubbleContentNod
|
|||||||
var viewCount: Int?
|
var viewCount: Int?
|
||||||
var rawText = ""
|
var rawText = ""
|
||||||
var dateReplies = 0
|
var dateReplies = 0
|
||||||
|
var starsCount: Int64?
|
||||||
var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.message)
|
var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.message)
|
||||||
if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) {
|
if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) {
|
||||||
dateReactionsAndPeers = ([], [])
|
dateReactionsAndPeers = ([], [])
|
||||||
@ -71,6 +72,8 @@ public class ChatMessageRestrictedBubbleContentNode: ChatMessageBubbleContentNod
|
|||||||
if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info {
|
if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info {
|
||||||
dateReplies = Int(attribute.count)
|
dateReplies = Int(attribute.count)
|
||||||
}
|
}
|
||||||
|
} else if let attribute = attribute as? PaidStarsMessageAttribute, item.message.id.peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||||
|
starsCount = attribute.stars.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,6 +143,7 @@ public class ChatMessageRestrictedBubbleContentNode: ChatMessageBubbleContentNod
|
|||||||
areReactionsTags: item.topMessage.areReactionsTags(accountPeerId: item.context.account.peerId),
|
areReactionsTags: item.topMessage.areReactionsTags(accountPeerId: item.context.account.peerId),
|
||||||
messageEffect: item.topMessage.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects),
|
messageEffect: item.topMessage.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects),
|
||||||
replyCount: dateReplies,
|
replyCount: dateReplies,
|
||||||
|
starsCount: starsCount,
|
||||||
isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && isReplyThread,
|
isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && isReplyThread,
|
||||||
hasAutoremove: item.message.isSelfExpiring,
|
hasAutoremove: item.message.isSelfExpiring,
|
||||||
canViewReactionList: canViewMessageReactionList(message: item.topMessage),
|
canViewReactionList: canViewMessageReactionList(message: item.topMessage),
|
||||||
|
|||||||
@ -602,6 +602,7 @@ public class ChatMessageStickerItemNode: ChatMessageItemView {
|
|||||||
var edited = false
|
var edited = false
|
||||||
var viewCount: Int? = nil
|
var viewCount: Int? = nil
|
||||||
var dateReplies = 0
|
var dateReplies = 0
|
||||||
|
var starsCount: Int64?
|
||||||
var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.message)
|
var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.message)
|
||||||
if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) {
|
if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) {
|
||||||
dateReactionsAndPeers = ([], [])
|
dateReactionsAndPeers = ([], [])
|
||||||
@ -615,6 +616,8 @@ public class ChatMessageStickerItemNode: ChatMessageItemView {
|
|||||||
if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info {
|
if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info {
|
||||||
dateReplies = Int(attribute.count)
|
dateReplies = Int(attribute.count)
|
||||||
}
|
}
|
||||||
|
} else if let attribute = attribute as? PaidStarsMessageAttribute, item.message.id.peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||||
|
starsCount = attribute.stars.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -648,6 +651,7 @@ public class ChatMessageStickerItemNode: ChatMessageItemView {
|
|||||||
areReactionsTags: item.message.areReactionsTags(accountPeerId: item.context.account.peerId),
|
areReactionsTags: item.message.areReactionsTags(accountPeerId: item.context.account.peerId),
|
||||||
messageEffect: item.message.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects),
|
messageEffect: item.message.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects),
|
||||||
replyCount: dateReplies,
|
replyCount: dateReplies,
|
||||||
|
starsCount: starsCount,
|
||||||
isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread,
|
isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread,
|
||||||
hasAutoremove: item.message.isSelfExpiring,
|
hasAutoremove: item.message.isSelfExpiring,
|
||||||
canViewReactionList: canViewMessageReactionList(message: item.message),
|
canViewReactionList: canViewMessageReactionList(message: item.message),
|
||||||
|
|||||||
@ -264,6 +264,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
}
|
}
|
||||||
var viewCount: Int?
|
var viewCount: Int?
|
||||||
var dateReplies = 0
|
var dateReplies = 0
|
||||||
|
var starsCount: Int64?
|
||||||
var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.topMessage)
|
var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.topMessage)
|
||||||
if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) {
|
if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) {
|
||||||
dateReactionsAndPeers = ([], [])
|
dateReactionsAndPeers = ([], [])
|
||||||
@ -278,6 +279,8 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info {
|
if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info {
|
||||||
dateReplies = Int(attribute.count)
|
dateReplies = Int(attribute.count)
|
||||||
}
|
}
|
||||||
|
} else if let attribute = attribute as? PaidStarsMessageAttribute, item.message.id.peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||||
|
starsCount = attribute.stars.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -647,6 +650,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
areReactionsTags: item.topMessage.areReactionsTags(accountPeerId: item.context.account.peerId),
|
areReactionsTags: item.topMessage.areReactionsTags(accountPeerId: item.context.account.peerId),
|
||||||
messageEffect: item.topMessage.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects),
|
messageEffect: item.topMessage.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects),
|
||||||
replyCount: dateReplies,
|
replyCount: dateReplies,
|
||||||
|
starsCount: starsCount,
|
||||||
isPinned: item.message.tags.contains(.pinned) && (!item.associatedData.isInPinnedListMode || isReplyThread),
|
isPinned: item.message.tags.contains(.pinned) && (!item.associatedData.isInPinnedListMode || isReplyThread),
|
||||||
hasAutoremove: item.message.isSelfExpiring,
|
hasAutoremove: item.message.isSelfExpiring,
|
||||||
canViewReactionList: canViewMessageReactionList(message: item.topMessage),
|
canViewReactionList: canViewMessageReactionList(message: item.topMessage),
|
||||||
|
|||||||
@ -28,7 +28,6 @@ swift_library(
|
|||||||
"//submodules/TelegramUI/Components/ListSectionComponent",
|
"//submodules/TelegramUI/Components/ListSectionComponent",
|
||||||
"//submodules/TelegramUI/Components/ListItemSliderSelectorComponent",
|
"//submodules/TelegramUI/Components/ListItemSliderSelectorComponent",
|
||||||
"//submodules/TelegramUI/Components/ListActionItemComponent",
|
"//submodules/TelegramUI/Components/ListActionItemComponent",
|
||||||
"//submodules/PremiumUI",
|
|
||||||
"//submodules/Components/BlurredBackgroundComponent",
|
"//submodules/Components/BlurredBackgroundComponent",
|
||||||
"//submodules/Markdown",
|
"//submodules/Markdown",
|
||||||
"//submodules/PresentationDataUtils",
|
"//submodules/PresentationDataUtils",
|
||||||
@ -38,6 +37,7 @@ swift_library(
|
|||||||
"//submodules/TelegramUI/Components/PlainButtonComponent",
|
"//submodules/TelegramUI/Components/PlainButtonComponent",
|
||||||
"//submodules/TelegramUI/Components/ToastComponent",
|
"//submodules/TelegramUI/Components/ToastComponent",
|
||||||
"//submodules/AvatarNode",
|
"//submodules/AvatarNode",
|
||||||
|
"//submodules/TelegramUI/Components/Premium/PremiumCoinComponent",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
|||||||
@ -19,13 +19,13 @@ import ListItemSliderSelectorComponent
|
|||||||
import ListActionItemComponent
|
import ListActionItemComponent
|
||||||
import Markdown
|
import Markdown
|
||||||
import BlurredBackgroundComponent
|
import BlurredBackgroundComponent
|
||||||
import PremiumUI
|
|
||||||
import PresentationDataUtils
|
import PresentationDataUtils
|
||||||
import PeerListItemComponent
|
import PeerListItemComponent
|
||||||
import TelegramStringFormatting
|
import TelegramStringFormatting
|
||||||
import ContextUI
|
import ContextUI
|
||||||
import BalancedTextComponent
|
import BalancedTextComponent
|
||||||
import AlertComponent
|
import AlertComponent
|
||||||
|
import PremiumCoinComponent
|
||||||
|
|
||||||
private func textForTimeout(value: Int32) -> String {
|
private func textForTimeout(value: Int32) -> String {
|
||||||
if value < 3600 {
|
if value < 3600 {
|
||||||
|
|||||||
@ -0,0 +1,27 @@
|
|||||||
|
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||||
|
|
||||||
|
swift_library(
|
||||||
|
name = "PremiumCoinComponent",
|
||||||
|
module_name = "PremiumCoinComponent",
|
||||||
|
srcs = glob([
|
||||||
|
"Sources/**/*.swift",
|
||||||
|
]),
|
||||||
|
copts = [
|
||||||
|
"-warnings-as-errors",
|
||||||
|
],
|
||||||
|
deps = [
|
||||||
|
"//submodules/AsyncDisplayKit",
|
||||||
|
"//submodules/Display",
|
||||||
|
"//submodules/SSignalKit/SwiftSignalKit",
|
||||||
|
"//submodules/ComponentFlow",
|
||||||
|
"//submodules/AccountContext",
|
||||||
|
"//submodules/AppBundle",
|
||||||
|
"//submodules/GZip",
|
||||||
|
"//submodules/LegacyComponents",
|
||||||
|
"//submodules/Components/MultilineTextComponent:MultilineTextComponent",
|
||||||
|
"//submodules/TelegramUI/Components/Premium/PremiumStarComponent",
|
||||||
|
],
|
||||||
|
visibility = [
|
||||||
|
"//visibility:public",
|
||||||
|
],
|
||||||
|
)
|
||||||
@ -48,6 +48,9 @@ public final class PremiumCoinComponent: Component {
|
|||||||
|
|
||||||
public final class View: UIView, SCNSceneRendererDelegate, ComponentTaggedView {
|
public final class View: UIView, SCNSceneRendererDelegate, ComponentTaggedView {
|
||||||
public final class Tag {
|
public final class Tag {
|
||||||
|
public init() {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func matches(tag: Any) -> Bool {
|
public func matches(tag: Any) -> Bool {
|
||||||
@ -58,7 +61,7 @@ public final class PremiumCoinComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var _ready = Promise<Bool>()
|
private var _ready = Promise<Bool>()
|
||||||
var ready: Signal<Bool, NoError> {
|
public var ready: Signal<Bool, NoError> {
|
||||||
return self._ready.get()
|
return self._ready.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"provides-namespace" : true
|
||||||
|
}
|
||||||
|
}
|
||||||
12
submodules/TelegramUI/Images.xcassets/Premium/Authorization/Cost.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Premium/Authorization/Cost.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "highprice_30.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
submodules/TelegramUI/Images.xcassets/Premium/Authorization/Cost.imageset/highprice_30.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Premium/Authorization/Cost.imageset/highprice_30.pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Premium/Authorization/Support.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Premium/Authorization/Support.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "support_30.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
submodules/TelegramUI/Images.xcassets/Premium/Authorization/Support.imageset/support_30.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Premium/Authorization/Support.imageset/support_30.pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Premium/Authorization/Verification.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Premium/Authorization/Verification.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "verificationcode_30.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
@ -2366,13 +2366,11 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func makePremiumIntroController(context: AccountContext, source: PremiumIntroSource, forceDark: Bool, dismissed: (() -> Void)?) -> ViewController {
|
private func mapIntroSource(source: PremiumIntroSource) -> PremiumSource {
|
||||||
var modal = true
|
|
||||||
let mappedSource: PremiumSource
|
let mappedSource: PremiumSource
|
||||||
switch source {
|
switch source {
|
||||||
case .settings:
|
case .settings:
|
||||||
mappedSource = .settings
|
mappedSource = .settings
|
||||||
modal = false
|
|
||||||
case .stickers:
|
case .stickers:
|
||||||
mappedSource = .stickers
|
mappedSource = .stickers
|
||||||
case .reactions:
|
case .reactions:
|
||||||
@ -2454,7 +2452,25 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
case .paidMessages:
|
case .paidMessages:
|
||||||
mappedSource = .paidMessages
|
mappedSource = .paidMessages
|
||||||
}
|
}
|
||||||
let controller = PremiumIntroScreen(context: context, source: mappedSource, modal: modal, forceDark: forceDark)
|
return mappedSource
|
||||||
|
}
|
||||||
|
|
||||||
|
public func makePremiumIntroController(context: AccountContext, source: PremiumIntroSource, forceDark: Bool, dismissed: (() -> Void)?) -> ViewController {
|
||||||
|
var modal = true
|
||||||
|
if case .settings = source {
|
||||||
|
modal = false
|
||||||
|
}
|
||||||
|
let controller = PremiumIntroScreen(context: context, source: self.mapIntroSource(source: source), modal: modal, forceDark: forceDark)
|
||||||
|
controller.wasDismissed = dismissed
|
||||||
|
return controller
|
||||||
|
}
|
||||||
|
|
||||||
|
public func makePremiumIntroController(sharedContext: SharedAccountContext, engine: TelegramEngineUnauthorized, inAppPurchaseManager: InAppPurchaseManager, source: PremiumIntroSource, dismissed: (() -> Void)?) -> ViewController {
|
||||||
|
var modal = true
|
||||||
|
if case .settings = source {
|
||||||
|
modal = false
|
||||||
|
}
|
||||||
|
let controller = PremiumIntroScreen(screenContext: .sharedContext(sharedContext, engine, inAppPurchaseManager), source: self.mapIntroSource(source: source), modal: modal)
|
||||||
controller.wasDismissed = dismissed
|
controller.wasDismissed = dismissed
|
||||||
return controller
|
return controller
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user