mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various improvements
This commit is contained in:
parent
b864b88935
commit
2ecbaf4507
@ -1129,6 +1129,8 @@ public protocol SharedAccountContext: AnyObject {
|
||||
|
||||
func makeGalleryController(context: AccountContext, source: GalleryControllerItemSource, streamSingleVideo: Bool, isPreview: Bool) -> ViewController
|
||||
|
||||
func makeAccountFreezeInfoScreen(context: AccountContext) -> ViewController
|
||||
|
||||
func makeDebugSettingsController(context: AccountContext?) -> ViewController?
|
||||
|
||||
func navigateToCurrentCall()
|
||||
|
@ -294,6 +294,44 @@ public struct PremiumConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
public struct AccountFreezeConfiguration {
|
||||
public static var defaultValue: AccountFreezeConfiguration {
|
||||
return AccountFreezeConfiguration(
|
||||
freezeSinceDate: nil,
|
||||
freezeUntilDate: nil,
|
||||
freezeAppealUrl: nil
|
||||
)
|
||||
}
|
||||
|
||||
public let freezeSinceDate: Int32?
|
||||
public let freezeUntilDate: Int32?
|
||||
public let freezeAppealUrl: String?
|
||||
|
||||
fileprivate init(
|
||||
freezeSinceDate: Int32?,
|
||||
freezeUntilDate: Int32?,
|
||||
freezeAppealUrl: String?
|
||||
) {
|
||||
self.freezeSinceDate = freezeSinceDate
|
||||
self.freezeUntilDate = freezeUntilDate
|
||||
self.freezeAppealUrl = freezeAppealUrl
|
||||
}
|
||||
|
||||
public static func with(appConfiguration: AppConfiguration) -> AccountFreezeConfiguration {
|
||||
let defaultValue = self.defaultValue
|
||||
if let data = appConfiguration.data {
|
||||
return AccountFreezeConfiguration(
|
||||
freezeSinceDate: (data["freeze_since_date"] as? Double).flatMap(Int32.init) ?? defaultValue.freezeSinceDate,
|
||||
freezeUntilDate: (data["freeze_until_date"] as? Double).flatMap(Int32.init) ?? defaultValue.freezeUntilDate,
|
||||
freezeAppealUrl: data["freeze_appeal_url"] as? String ?? defaultValue.freezeAppealUrl
|
||||
)
|
||||
} else {
|
||||
return defaultValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public protocol GiftOptionsScreenProtocol {
|
||||
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ swift_library(
|
||||
"//submodules/TelegramUI/Components/TextNodeWithEntities:TextNodeWithEntities",
|
||||
"//submodules/MoreButtonNode:MoreButtonNode",
|
||||
"//submodules/ContextUI:ContextUI",
|
||||
"//submodules/InAppPurchaseManager",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -20,6 +20,7 @@ import TelegramNotices
|
||||
import AuthenticationServices
|
||||
import Markdown
|
||||
import AlertUI
|
||||
import InAppPurchaseManager
|
||||
import ObjectiveC
|
||||
|
||||
private var ObjCKey_Delegate: Int?
|
||||
@ -59,6 +60,8 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
|
||||
return TelegramEngineUnauthorized(account: self.account)
|
||||
}
|
||||
|
||||
private var inAppPurchaseManager: InAppPurchaseManager!
|
||||
|
||||
public init(sharedContext: SharedAccountContext, account: UnauthorizedAccount, otherAccountPhoneNumbers: ((String, AccountRecordId, Bool)?, [(String, AccountRecordId, Bool)]), presentationData: PresentationData, openUrl: @escaping (String) -> Void, apiId: Int32, apiHash: String, authorizationCompleted: @escaping () -> Void) {
|
||||
self.sharedContext = sharedContext
|
||||
self.account = account
|
||||
@ -79,6 +82,8 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
|
||||
|
||||
super.init(mode: .single, theme: NavigationControllerTheme(statusBar: navigationStatusBar, navigationBar: AuthorizationSequenceController.navigationBarTheme(presentationData.theme), emptyAreaColor: .black), isFlat: true)
|
||||
|
||||
self.inAppPurchaseManager = InAppPurchaseManager(engine: .unauthorized(self.engine))
|
||||
|
||||
self.stateDisposable = (self.engine.auth.state()
|
||||
|> map { state -> InnerState in
|
||||
if case .authorized = state {
|
||||
@ -758,6 +763,18 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
|
||||
return controller
|
||||
}
|
||||
|
||||
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
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
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()
|
||||
})
|
||||
return controller
|
||||
}
|
||||
|
||||
@available(iOS 13.0, *)
|
||||
public func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
|
||||
let lastController = self.viewControllers.last as? ViewController
|
||||
@ -1285,6 +1302,13 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
|
||||
}
|
||||
controllers.append(self.signUpController(firstName: firstName, lastName: lastName, termsOfService: termsOfService, displayCancel: displayCancel))
|
||||
self.setViewControllers(controllers, animated: !self.viewControllers.isEmpty)
|
||||
case let .payment(number, codeHash, storeProduct):
|
||||
var controllers: [ViewController] = []
|
||||
if !self.otherAccountPhoneNumbers.1.isEmpty {
|
||||
controllers.append(self.splashController())
|
||||
}
|
||||
controllers.append(self.paymentController(number: number, phoneCodeHash: codeHash, storeProduct: storeProduct))
|
||||
self.setViewControllers(controllers, animated: !self.viewControllers.isEmpty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,287 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import AsyncDisplayKit
|
||||
import SwiftSignalKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import TelegramPresentationData
|
||||
import TelegramUIPreferences
|
||||
import TelegramStringFormatting
|
||||
import PresentationDataUtils
|
||||
import ComponentFlow
|
||||
import ViewControllerComponent
|
||||
import MultilineTextComponent
|
||||
import BalancedTextComponent
|
||||
import BundleIconComponent
|
||||
import LottieComponent
|
||||
import ButtonComponent
|
||||
import TextFormat
|
||||
import InAppPurchaseManager
|
||||
import ConfettiEffect
|
||||
|
||||
final class AuthorizationSequencePaymentScreenComponent: Component {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
||||
let engine: TelegramEngineUnauthorized
|
||||
let inAppPurchaseManager: InAppPurchaseManager
|
||||
let presentationData: PresentationData
|
||||
let phoneNumber: String
|
||||
let phoneCodeHash: String
|
||||
let storeProduct: String
|
||||
|
||||
init(
|
||||
engine: TelegramEngineUnauthorized,
|
||||
inAppPurchaseManager: InAppPurchaseManager,
|
||||
presentationData: PresentationData,
|
||||
phoneNumber: String,
|
||||
phoneCodeHash: String,
|
||||
storeProduct: String
|
||||
) {
|
||||
self.engine = engine
|
||||
self.inAppPurchaseManager = inAppPurchaseManager
|
||||
self.presentationData = presentationData
|
||||
self.phoneNumber = phoneNumber
|
||||
self.phoneCodeHash = phoneCodeHash
|
||||
self.storeProduct = storeProduct
|
||||
}
|
||||
|
||||
static func ==(lhs: AuthorizationSequencePaymentScreenComponent, rhs: AuthorizationSequencePaymentScreenComponent) -> Bool {
|
||||
if lhs.storeProduct != rhs.storeProduct {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class View: UIView {
|
||||
private let animation = ComponentView<Empty>()
|
||||
private let title = ComponentView<Empty>()
|
||||
private let list = ComponentView<Empty>()
|
||||
private let check = ComponentView<Empty>()
|
||||
private let button = ComponentView<Empty>()
|
||||
|
||||
private var isUpdating: Bool = false
|
||||
|
||||
private var component: AuthorizationSequencePaymentScreenComponent?
|
||||
private(set) weak var state: EmptyComponentState?
|
||||
private var environment: EnvironmentType?
|
||||
|
||||
private var products: [InAppPurchaseManager.Product] = []
|
||||
private var productsDisposable: Disposable?
|
||||
private var inProgress = false
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
self.disablesInteractiveKeyboardGestureRecognizer = true
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.productsDisposable?.dispose()
|
||||
}
|
||||
|
||||
private func proceed() {
|
||||
guard let component = self.component, let storeProduct = self.products.first(where: { $0.id == component.storeProduct }) else {
|
||||
return
|
||||
}
|
||||
|
||||
self.inProgress = true
|
||||
self.state?.updated()
|
||||
|
||||
let (currency, amount) = storeProduct.priceCurrencyAndAmount
|
||||
|
||||
let purpose: AppStoreTransactionPurpose = .authCode(restore: false, phoneNumber: component.phoneNumber, phoneCodeHash: component.phoneCodeHash, currency: currency, amount: amount)
|
||||
|
||||
let _ = (component.engine.payments.canPurchasePremium(purpose: purpose)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] available in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let presentationData = component.presentationData
|
||||
if available {
|
||||
let _ = (component.inAppPurchaseManager.buyProduct(storeProduct, quantity: 1, purpose: purpose)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] status in
|
||||
let _ = status
|
||||
let _ = self
|
||||
}, error: { [weak self] error in
|
||||
guard let self, let controller = self.environment?.controller() else {
|
||||
return
|
||||
}
|
||||
self.state?.updated(transition: .immediate)
|
||||
|
||||
var errorText: String?
|
||||
switch error {
|
||||
case .generic:
|
||||
errorText = presentationData.strings.Premium_Purchase_ErrorUnknown
|
||||
case .network:
|
||||
errorText = presentationData.strings.Premium_Purchase_ErrorNetwork
|
||||
case .notAllowed:
|
||||
errorText = presentationData.strings.Premium_Purchase_ErrorNotAllowed
|
||||
case .cantMakePayments:
|
||||
errorText = presentationData.strings.Premium_Purchase_ErrorCantMakePayments
|
||||
case .assignFailed:
|
||||
errorText = presentationData.strings.Premium_Purchase_ErrorUnknown
|
||||
case .tryLater:
|
||||
errorText = presentationData.strings.Premium_Purchase_ErrorUnknown
|
||||
case .cancelled:
|
||||
break
|
||||
}
|
||||
|
||||
if let errorText {
|
||||
//addAppLogEvent(postbox: component.engine.account.postbox, type: "premium_gift.promo_screen_fail")
|
||||
|
||||
let _ = errorText
|
||||
let _ = controller
|
||||
//let alertController = textAlertController(context: component.context, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])
|
||||
//controller.present(alertController, in: .window(.root))
|
||||
}
|
||||
})
|
||||
} else {
|
||||
self.inProgress = false
|
||||
self.state?.updated(transition: .immediate)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func update(component: AuthorizationSequencePaymentScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize {
|
||||
self.isUpdating = true
|
||||
defer {
|
||||
self.isUpdating = false
|
||||
}
|
||||
|
||||
let environment = environment[EnvironmentType.self].value
|
||||
let themeUpdated = self.environment?.theme !== environment.theme
|
||||
self.environment = environment
|
||||
self.component = component
|
||||
self.state = state
|
||||
|
||||
if self.component == nil {
|
||||
self.productsDisposable = (component.inAppPurchaseManager.availableProducts
|
||||
|> deliverOnMainQueue).start(next: { [weak self] products in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.products = products
|
||||
self.state?.updated()
|
||||
})
|
||||
}
|
||||
|
||||
if themeUpdated {
|
||||
self.backgroundColor = environment.theme.list.plainBackgroundColor
|
||||
}
|
||||
|
||||
let animationHeight: CGFloat = 120.0
|
||||
let animationSize = self.animation.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(LottieComponent(
|
||||
content: LottieComponent.AppBundleContent(name: "Coin"),
|
||||
startingPosition: .begin
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: animationHeight, height: animationHeight)
|
||||
)
|
||||
if let animationView = self.animation.view {
|
||||
if animationView.superview == nil {
|
||||
self.addSubview(animationView)
|
||||
}
|
||||
animationView.frame = CGRect(origin: CGPoint(x: floor((availableSize.width - animationSize.width) / 2.0), y: 156.0), size: animationSize)
|
||||
}
|
||||
|
||||
let buttonHeight: CGFloat = 50.0
|
||||
let bottomPanelPadding: CGFloat = 12.0
|
||||
let bottomInset: CGFloat = environment.safeInsets.bottom > 0.0 ? environment.safeInsets.bottom + 5.0 : bottomPanelPadding
|
||||
let bottomPanelHeight = bottomPanelPadding + buttonHeight + bottomInset
|
||||
|
||||
let sideInset: CGFloat = 16.0
|
||||
let buttonString = "Sign up for $1"
|
||||
let buttonAttributedString = NSMutableAttributedString(string: buttonString, font: Font.semibold(17.0), textColor: environment.theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center)
|
||||
let buttonSize = self.button.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(ButtonComponent(
|
||||
background: ButtonComponent.Background(
|
||||
color: environment.theme.list.itemCheckColors.fillColor,
|
||||
foreground: environment.theme.list.itemCheckColors.foregroundColor,
|
||||
pressedColor: environment.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9),
|
||||
cornerRadius: 10.0
|
||||
),
|
||||
content: AnyComponentWithIdentity(
|
||||
id: AnyHashable(buttonString),
|
||||
component: AnyComponent(MultilineTextComponent(text: .plain(buttonAttributedString)))
|
||||
),
|
||||
isEnabled: true,
|
||||
displaysProgress: self.inProgress,
|
||||
action: { [weak self] in
|
||||
self?.proceed()
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: buttonHeight)
|
||||
)
|
||||
if let buttonView = self.button.view {
|
||||
if buttonView.superview == nil {
|
||||
self.addSubview(buttonView)
|
||||
}
|
||||
buttonView.frame = CGRect(origin: CGPoint(x: floor((availableSize.width - buttonSize.width) / 2.0), y: availableSize.height - bottomPanelHeight + bottomPanelPadding), size: buttonSize)
|
||||
}
|
||||
|
||||
return availableSize
|
||||
}
|
||||
}
|
||||
|
||||
func makeView() -> View {
|
||||
return View()
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
public final class AuthorizationSequencePaymentScreen: ViewControllerComponentContainer {
|
||||
public init(
|
||||
engine: TelegramEngineUnauthorized,
|
||||
presentationData: PresentationData,
|
||||
inAppPurchaseManager: InAppPurchaseManager,
|
||||
phoneNumber: String,
|
||||
phoneCodeHash: String,
|
||||
storeProduct: String,
|
||||
back: @escaping () -> Void
|
||||
) {
|
||||
super.init(component: AuthorizationSequencePaymentScreenComponent(
|
||||
engine: engine,
|
||||
inAppPurchaseManager: inAppPurchaseManager,
|
||||
presentationData: presentationData,
|
||||
phoneNumber: phoneNumber,
|
||||
phoneCodeHash: phoneCodeHash,
|
||||
storeProduct: storeProduct
|
||||
), navigationBarAppearance: .transparent, theme: .default, updatedPresentationData: (initial: presentationData, signal: .single(presentationData)))
|
||||
|
||||
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
||||
|
||||
|
||||
self.attemptNavigation = { _ in
|
||||
return false
|
||||
}
|
||||
self.navigationBar?.backPressed = {
|
||||
back()
|
||||
}
|
||||
}
|
||||
|
||||
public override func loadDisplayNode() {
|
||||
super.loadDisplayNode()
|
||||
|
||||
self.displayNode.view.disableAutomaticKeyboardHandling = [.forward, .backward]
|
||||
}
|
||||
|
||||
required public init(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
@objc private func cancelPressed() {
|
||||
self.dismiss()
|
||||
}
|
||||
}
|
@ -1208,6 +1208,14 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
)
|
||||
}
|
||||
|
||||
self.chatListDisplayNode.mainContainerNode.openAccountFreezeInfo = { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let controller = self.context.sharedContext.makeAccountFreezeInfoScreen(context: self.context)
|
||||
self.push(controller)
|
||||
}
|
||||
|
||||
self.chatListDisplayNode.mainContainerNode.openPhotoSetup = { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
|
@ -357,6 +357,9 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele
|
||||
itemNode.listNode.openPhotoSetup = { [weak self] in
|
||||
self?.openPhotoSetup?()
|
||||
}
|
||||
itemNode.listNode.openAccountFreezeInfo = { [weak self] in
|
||||
self?.openAccountFreezeInfo?()
|
||||
}
|
||||
|
||||
self.currentItemStateValue.set(itemNode.listNode.state |> map { state in
|
||||
let filterId: Int32?
|
||||
@ -425,6 +428,7 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele
|
||||
var openStarsTopup: ((Int64?) -> Void)?
|
||||
var openWebApp: ((TelegramUser) -> Void)?
|
||||
var openPhotoSetup: (() -> Void)?
|
||||
var openAccountFreezeInfo: (() -> Void)?
|
||||
var addedVisibleChatsWithPeerIds: (([EnginePeer.Id]) -> Void)?
|
||||
var didBeginSelectingChats: (() -> Void)?
|
||||
var canExpandHiddenItems: (() -> Bool)?
|
||||
|
@ -3261,6 +3261,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
}, openPhotoSetup: {
|
||||
}, openAdInfo: { node in
|
||||
interaction.openAdInfo(node)
|
||||
}, openAccountFreezeInfo: {
|
||||
})
|
||||
chatListInteraction.isSearchMode = true
|
||||
|
||||
@ -5243,6 +5244,7 @@ public final class ChatListSearchShimmerNode: ASDisplayNode {
|
||||
}, openWebApp: { _ in
|
||||
}, openPhotoSetup: {
|
||||
}, openAdInfo: { _ in
|
||||
}, openAccountFreezeInfo: {
|
||||
})
|
||||
var isInlineMode = false
|
||||
if case .topics = key {
|
||||
|
@ -162,6 +162,7 @@ public final class ChatListShimmerNode: ASDisplayNode {
|
||||
}, openWebApp: { _ in
|
||||
}, openPhotoSetup: {
|
||||
}, openAdInfo: { _ in
|
||||
}, openAccountFreezeInfo: {
|
||||
})
|
||||
interaction.isInlineMode = isInlineMode
|
||||
|
||||
|
@ -115,6 +115,7 @@ public final class ChatListNodeInteraction {
|
||||
let openWebApp: (TelegramUser) -> Void
|
||||
let openPhotoSetup: () -> Void
|
||||
let openAdInfo: (ASDisplayNode) -> Void
|
||||
let openAccountFreezeInfo: () -> Void
|
||||
|
||||
public var searchTextHighightState: String?
|
||||
var highlightedChatLocation: ChatListHighlightedLocation?
|
||||
@ -173,7 +174,8 @@ public final class ChatListNodeInteraction {
|
||||
editPeer: @escaping (ChatListItem) -> Void,
|
||||
openWebApp: @escaping (TelegramUser) -> Void,
|
||||
openPhotoSetup: @escaping () -> Void,
|
||||
openAdInfo: @escaping (ASDisplayNode) -> Void
|
||||
openAdInfo: @escaping (ASDisplayNode) -> Void,
|
||||
openAccountFreezeInfo: @escaping () -> Void
|
||||
) {
|
||||
self.activateSearch = activateSearch
|
||||
self.peerSelected = peerSelected
|
||||
@ -220,6 +222,7 @@ public final class ChatListNodeInteraction {
|
||||
self.openWebApp = openWebApp
|
||||
self.openPhotoSetup = openPhotoSetup
|
||||
self.openAdInfo = openAdInfo
|
||||
self.openAccountFreezeInfo = openAccountFreezeInfo
|
||||
}
|
||||
}
|
||||
|
||||
@ -770,6 +773,8 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
|
||||
nodeInteraction?.openStarsTopup(amount.value)
|
||||
case .setupPhoto:
|
||||
nodeInteraction?.openPhotoSetup()
|
||||
case .accountFreeze:
|
||||
nodeInteraction?.openAccountFreezeInfo()
|
||||
}
|
||||
case .hide:
|
||||
nodeInteraction?.dismissNotice(notice)
|
||||
@ -1116,6 +1121,8 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
|
||||
nodeInteraction?.openStarsTopup(amount.value)
|
||||
case .setupPhoto:
|
||||
nodeInteraction?.openPhotoSetup()
|
||||
case .accountFreeze:
|
||||
nodeInteraction?.openAccountFreezeInfo()
|
||||
}
|
||||
case .hide:
|
||||
nodeInteraction?.dismissNotice(notice)
|
||||
@ -1239,6 +1246,7 @@ public final class ChatListNode: ListView {
|
||||
public var openWebApp: ((TelegramUser) -> Void)?
|
||||
public var openPhotoSetup: (() -> Void)?
|
||||
public var openAdInfo: ((ASDisplayNode) -> Void)?
|
||||
public var openAccountFreezeInfo: (() -> Void)?
|
||||
|
||||
private var theme: PresentationTheme
|
||||
|
||||
@ -1899,6 +1907,8 @@ public final class ChatListNode: ListView {
|
||||
self.openPhotoSetup?()
|
||||
}, openAdInfo: { [weak self] node in
|
||||
self?.openAdInfo?(node)
|
||||
}, openAccountFreezeInfo: { [weak self] in
|
||||
self?.openAccountFreezeInfo?()
|
||||
})
|
||||
nodeInteraction.isInlineMode = isInlineMode
|
||||
|
||||
@ -1988,6 +1998,16 @@ public final class ChatListNode: ListView {
|
||||
|
||||
let twoStepData: Signal<TwoStepVerificationConfiguration?, NoError> = .single(nil) |> then(context.engine.auth.twoStepVerificationConfiguration() |> map(Optional.init))
|
||||
|
||||
let accountFreezeConfiguration = (context.account.postbox.preferencesView(keys: [PreferencesKeys.appConfiguration])
|
||||
|> map { view -> AppConfiguration in
|
||||
let appConfiguration: AppConfiguration = view.values[PreferencesKeys.appConfiguration]?.get(AppConfiguration.self) ?? AppConfiguration.defaultValue
|
||||
return appConfiguration
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
|> map { appConfiguration -> AccountFreezeConfiguration in
|
||||
return AccountFreezeConfiguration.with(appConfiguration: appConfiguration)
|
||||
})
|
||||
|
||||
let suggestedChatListNoticeSignal: Signal<ChatListNotice?, NoError> = combineLatest(
|
||||
context.engine.notices.getServerProvidedSuggestions(),
|
||||
context.engine.notices.getServerDismissedSuggestions(),
|
||||
@ -1998,11 +2018,12 @@ public final class ChatListNode: ListView {
|
||||
TelegramEngine.EngineData.Item.Peer.Birthday(id: context.account.peerId)
|
||||
),
|
||||
context.account.stateManager.contactBirthdays,
|
||||
starsSubscriptionsContextPromise.get()
|
||||
starsSubscriptionsContextPromise.get(),
|
||||
accountFreezeConfiguration
|
||||
)
|
||||
|> mapToSignal { suggestions, dismissedSuggestions, configuration, newSessionReviews, data, birthdays, starsSubscriptionsContext -> Signal<ChatListNotice?, NoError> in
|
||||
|> mapToSignal { suggestions, dismissedSuggestions, configuration, newSessionReviews, data, birthdays, starsSubscriptionsContext, accountFreezeConfiguration -> Signal<ChatListNotice?, NoError> in
|
||||
let (accountPeer, birthday) = data
|
||||
|
||||
|
||||
if let newSessionReview = newSessionReviews.first {
|
||||
return .single(.reviewLogin(newSessionReview: newSessionReview, totalCount: newSessionReviews.count))
|
||||
}
|
||||
@ -2035,8 +2056,10 @@ public final class ChatListNode: ListView {
|
||||
if dismissedSuggestions.contains(.todayBirthdays) {
|
||||
todayBirthdayPeerIds = []
|
||||
}
|
||||
|
||||
if suggestions.contains(.starsSubscriptionLowBalance) {
|
||||
|
||||
if let _ = accountFreezeConfiguration.freezeUntilDate {
|
||||
return .single(.accountFreeze)
|
||||
} else if suggestions.contains(.starsSubscriptionLowBalance) {
|
||||
if let starsSubscriptionsContext {
|
||||
return starsSubscriptionsContext.state
|
||||
|> map { state in
|
||||
|
@ -92,6 +92,7 @@ public enum ChatListNotice: Equatable {
|
||||
case premiumGrace
|
||||
case starsSubscriptionLowBalance(amount: StarsAmount, peers: [EnginePeer])
|
||||
case setupPhoto(EnginePeer)
|
||||
case accountFreeze
|
||||
}
|
||||
|
||||
enum ChatListNodeEntry: Comparable, Identifiable {
|
||||
|
@ -288,6 +288,10 @@ final class ChatListNoticeItemNode: ItemListRevealOptionsItemNode {
|
||||
titleString = NSAttributedString(string: item.strings.ChatList_AddPhoto_Title, font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor)
|
||||
textString = NSAttributedString(string: item.strings.ChatList_AddPhoto_Text, font: smallTextFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor)
|
||||
avatarPeer = accountPeer
|
||||
case .accountFreeze:
|
||||
//TODO:localize
|
||||
titleString = NSAttributedString(string: "Your account is frozen", font: titleFont, textColor: item.theme.list.itemDestructiveColor)
|
||||
textString = NSAttributedString(string: "Tap to view details and submit an appeal.", font: smallTextFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor)
|
||||
}
|
||||
|
||||
var leftInset: CGFloat = sideInset
|
||||
|
@ -151,8 +151,8 @@ open class ViewControllerComponentContainer: ViewController {
|
||||
private var currentIsVisible: Bool = false
|
||||
private var currentLayout: (layout: ContainerViewLayout, navigationHeight: CGFloat)?
|
||||
|
||||
init(context: AccountContext, controller: ViewControllerComponentContainer, component: AnyComponent<ViewControllerComponentContainer.Environment>, theme: Theme) {
|
||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
init(presentationData: PresentationData, controller: ViewControllerComponentContainer, component: AnyComponent<ViewControllerComponentContainer.Environment>, theme: Theme) {
|
||||
self.presentationData = presentationData
|
||||
|
||||
self.controller = controller
|
||||
|
||||
@ -234,7 +234,7 @@ open class ViewControllerComponentContainer: ViewController {
|
||||
return self.displayNode as! Node
|
||||
}
|
||||
|
||||
private let context: AccountContext
|
||||
private var presentationData: PresentationData
|
||||
private var theme: Theme
|
||||
public private(set) var component: AnyComponent<ViewControllerComponentContainer.Environment>
|
||||
|
||||
@ -252,17 +252,19 @@ open class ViewControllerComponentContainer: ViewController {
|
||||
theme: Theme = .default,
|
||||
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil
|
||||
) where C.EnvironmentType == ViewControllerComponentContainer.Environment {
|
||||
self.context = context
|
||||
self.component = AnyComponent(component)
|
||||
self.theme = theme
|
||||
|
||||
let presentationData: PresentationData
|
||||
var effectiveUpdatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)
|
||||
if let updatedPresentationData {
|
||||
presentationData = updatedPresentationData.initial
|
||||
effectiveUpdatedPresentationData = updatedPresentationData
|
||||
} else {
|
||||
presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
effectiveUpdatedPresentationData = (initial: context.sharedContext.currentPresentationData.with { $0 }, signal: context.sharedContext.presentationData)
|
||||
}
|
||||
|
||||
let presentationData = effectiveUpdatedPresentationData.initial
|
||||
self.presentationData = presentationData
|
||||
|
||||
let navigationBarPresentationData: NavigationBarPresentationData?
|
||||
switch navigationBarAppearance {
|
||||
case .none:
|
||||
@ -274,7 +276,47 @@ open class ViewControllerComponentContainer: ViewController {
|
||||
}
|
||||
super.init(navigationBarPresentationData: navigationBarPresentationData)
|
||||
|
||||
self.presentationDataDisposable = ((updatedPresentationData?.signal ?? self.context.sharedContext.presentationData)
|
||||
self.setupPresentationData(effectiveUpdatedPresentationData, navigationBarAppearance: navigationBarAppearance, statusBarStyle: statusBarStyle, presentationMode: presentationMode)
|
||||
}
|
||||
|
||||
public init<C: Component>(
|
||||
component: C,
|
||||
navigationBarAppearance: NavigationBarAppearance,
|
||||
statusBarStyle: StatusBarStyle = .default,
|
||||
presentationMode: PresentationMode = .default,
|
||||
theme: Theme = .default,
|
||||
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)
|
||||
) where C.EnvironmentType == ViewControllerComponentContainer.Environment {
|
||||
self.component = AnyComponent(component)
|
||||
self.theme = theme
|
||||
|
||||
let presentationData = updatedPresentationData.initial
|
||||
self.presentationData = presentationData
|
||||
|
||||
let navigationBarPresentationData: NavigationBarPresentationData?
|
||||
switch navigationBarAppearance {
|
||||
case .none:
|
||||
navigationBarPresentationData = nil
|
||||
case .transparent:
|
||||
navigationBarPresentationData = NavigationBarPresentationData(presentationData: presentationData, hideBackground: true, hideBadge: false, hideSeparator: true)
|
||||
case .default:
|
||||
navigationBarPresentationData = NavigationBarPresentationData(presentationData: presentationData)
|
||||
}
|
||||
super.init(navigationBarPresentationData: navigationBarPresentationData)
|
||||
|
||||
self.setupPresentationData(updatedPresentationData, navigationBarAppearance: navigationBarAppearance, statusBarStyle: statusBarStyle, presentationMode: presentationMode)
|
||||
}
|
||||
|
||||
required public init(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.presentationDataDisposable?.dispose()
|
||||
}
|
||||
|
||||
private func setupPresentationData(_ updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>), navigationBarAppearance: NavigationBarAppearance, statusBarStyle: StatusBarStyle, presentationMode: PresentationMode) {
|
||||
self.presentationDataDisposable = (updatedPresentationData.signal
|
||||
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
|
||||
if let strongSelf = self {
|
||||
var theme = presentationData.theme
|
||||
@ -329,16 +371,8 @@ open class ViewControllerComponentContainer: ViewController {
|
||||
}
|
||||
}
|
||||
|
||||
required public init(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.presentationDataDisposable?.dispose()
|
||||
}
|
||||
|
||||
override open func loadDisplayNode() {
|
||||
self.displayNode = Node(context: self.context, controller: self, component: self.component, theme: self.theme)
|
||||
self.displayNode = Node(presentationData: self.presentationData, controller: self, component: self.component, theme: self.theme)
|
||||
|
||||
self.displayNodeDidLoad()
|
||||
}
|
||||
|
@ -591,7 +591,7 @@ private func contactListNodeEntries(accountPeer: EnginePeer?, peers: [ContactLis
|
||||
allSelected = false
|
||||
}
|
||||
var actionTitle: String?
|
||||
if peerIds.count > 1 {
|
||||
if !"".isEmpty, peerIds.count > 1 {
|
||||
actionTitle = allSelected ? strings.Premium_Gift_ContactSelection_DeselectAll.uppercased() : strings.Premium_Gift_ContactSelection_SelectAll.uppercased()
|
||||
}
|
||||
let header: ListViewItemHeader? = ChatListSearchItemHeader(type: .text(title.uppercased(), AnyHashable(10 * sectionId + (allSelected ? 1 : 0))), theme: theme, strings: strings, actionTitle: actionTitle, action: { _ in
|
||||
|
@ -216,7 +216,7 @@ public final class InAppPurchaseManager: NSObject {
|
||||
case deferred
|
||||
}
|
||||
|
||||
private let engine: TelegramEngine
|
||||
private let engine: SomeTelegramEngine
|
||||
|
||||
private var products: [Product] = []
|
||||
private var productsPromise = Promise<[Product]>([])
|
||||
@ -231,7 +231,9 @@ public final class InAppPurchaseManager: NSObject {
|
||||
|
||||
private let disposableSet = DisposableDict<String>()
|
||||
|
||||
public init(engine: TelegramEngine) {
|
||||
private var lastRequestTimestamp: Double?
|
||||
|
||||
public init(engine: SomeTelegramEngine) {
|
||||
self.engine = engine
|
||||
|
||||
super.init()
|
||||
@ -255,11 +257,15 @@ public final class InAppPurchaseManager: NSObject {
|
||||
productRequest.start()
|
||||
|
||||
self.productRequest = productRequest
|
||||
self.lastRequestTimestamp = CFAbsoluteTimeGetCurrent()
|
||||
}
|
||||
|
||||
public var availableProducts: Signal<[Product], NoError> {
|
||||
if self.products.isEmpty && self.productRequest == nil {
|
||||
self.requestProducts()
|
||||
if self.products.isEmpty {
|
||||
if let lastRequestTimestamp, CFAbsoluteTimeGetCurrent() - lastRequestTimestamp > 10.0 {
|
||||
Logger.shared.log("InAppPurchaseManager", "No available products, rerequest")
|
||||
self.requestProducts()
|
||||
}
|
||||
}
|
||||
return self.productsPromise.get()
|
||||
}
|
||||
@ -287,7 +293,13 @@ public final class InAppPurchaseManager: NSObject {
|
||||
return .fail(.cantMakePayments)
|
||||
}
|
||||
|
||||
let accountPeerId = "\(self.engine.account.peerId.toInt64())"
|
||||
let accountPeerId: String
|
||||
switch self.engine {
|
||||
case let .authorized(engine):
|
||||
accountPeerId = "\(engine.account.peerId.toInt64())"
|
||||
case let .unauthorized(engine):
|
||||
accountPeerId = "\(engine.account.id.int64)"
|
||||
}
|
||||
|
||||
Logger.shared.log("InAppPurchaseManager", "Buying: account \(accountPeerId), product \(product.skProduct.productIdentifier), price \(product.price)")
|
||||
|
||||
@ -399,7 +411,13 @@ private func getReceiptData() -> Data? {
|
||||
extension InAppPurchaseManager: SKPaymentTransactionObserver {
|
||||
public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
|
||||
self.stateQueue.async {
|
||||
let accountPeerId = "\(self.engine.account.peerId.toInt64())"
|
||||
let accountPeerId: String
|
||||
switch self.engine {
|
||||
case let .authorized(engine):
|
||||
accountPeerId = "\(engine.account.peerId.toInt64())"
|
||||
case let .unauthorized(engine):
|
||||
accountPeerId = "\(engine.account.id.int64)"
|
||||
}
|
||||
|
||||
let paymentContexts = self.paymentContexts
|
||||
|
||||
@ -519,7 +537,12 @@ extension InAppPurchaseManager: SKPaymentTransactionObserver {
|
||||
(purpose
|
||||
|> castError(AssignAppStoreTransactionError.self)
|
||||
|> mapToSignal { purpose -> Signal<Never, AssignAppStoreTransactionError> in
|
||||
return self.engine.payments.sendAppStoreReceipt(receipt: receiptData, purpose: purpose)
|
||||
switch self.engine {
|
||||
case let .authorized(engine):
|
||||
return engine.payments.sendAppStoreReceipt(receipt: receiptData, purpose: purpose)
|
||||
case let .unauthorized(engine):
|
||||
return engine.payments.sendAppStoreReceipt(receipt: receiptData, purpose: purpose)
|
||||
}
|
||||
}).start(error: { [weak self] _ in
|
||||
Logger.shared.log("InAppPurchaseManager", "Account \(accountPeerId), transactions [\(transactionIds)] failed to assign")
|
||||
for transaction in transactions {
|
||||
@ -551,8 +574,15 @@ extension InAppPurchaseManager: SKPaymentTransactionObserver {
|
||||
self.onRestoreCompletion = nil
|
||||
|
||||
if let receiptData = getReceiptData() {
|
||||
let signal: Signal<Never, AssignAppStoreTransactionError>
|
||||
switch self.engine {
|
||||
case let .authorized(engine):
|
||||
signal = engine.payments.sendAppStoreReceipt(receipt: receiptData, purpose: .restore)
|
||||
case let .unauthorized(engine):
|
||||
signal = engine.payments.sendAppStoreReceipt(receipt: receiptData, purpose: .restore)
|
||||
}
|
||||
self.disposableSet.set(
|
||||
self.engine.payments.sendAppStoreReceipt(receipt: receiptData, purpose: .restore).start(error: { error in
|
||||
signal.start(error: { error in
|
||||
Queue.mainQueue().async {
|
||||
if case .serverProvided = error {
|
||||
onRestoreCompletion(.succeed(true))
|
||||
@ -586,14 +616,17 @@ extension InAppPurchaseManager: SKPaymentTransactionObserver {
|
||||
}
|
||||
|
||||
private func debugSaveReceipt(receiptData: Data) {
|
||||
guard case let .authorized(engine) = self.engine else {
|
||||
return
|
||||
}
|
||||
let id = Int64.random(in: Int64.min ... Int64.max)
|
||||
let fileResource = LocalFileMediaResource(fileId: id, size: Int64(receiptData.count), isSecretRelated: false)
|
||||
self.engine.account.postbox.mediaBox.storeResourceData(fileResource.id, data: receiptData)
|
||||
engine.account.postbox.mediaBox.storeResourceData(fileResource.id, data: receiptData)
|
||||
|
||||
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: fileResource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: Int64(receiptData.count), attributes: [.FileName(fileName: "Receipt.dat")], alternativeRepresentations: [])
|
||||
let message: EnqueueMessage = .message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: file), threadId: nil, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])
|
||||
|
||||
let _ = enqueueMessages(account: self.engine.account, peerId: self.engine.account.peerId, messages: [message]).start()
|
||||
let _ = enqueueMessages(account: engine.account, peerId: engine.account.peerId, messages: [message]).start()
|
||||
}
|
||||
}
|
||||
|
||||
@ -625,6 +658,9 @@ private final class PendingInAppPurchaseState: Codable {
|
||||
case users
|
||||
case text
|
||||
case entities
|
||||
case restore
|
||||
case phoneNumber
|
||||
case phoneCodeHash
|
||||
}
|
||||
|
||||
enum PurposeType: Int32 {
|
||||
@ -637,6 +673,7 @@ private final class PendingInAppPurchaseState: Codable {
|
||||
case stars
|
||||
case starsGift
|
||||
case starsGiveaway
|
||||
case authCode
|
||||
}
|
||||
|
||||
case subscription
|
||||
@ -648,6 +685,7 @@ private final class PendingInAppPurchaseState: Codable {
|
||||
case stars(count: Int64)
|
||||
case starsGift(peerId: EnginePeer.Id, count: Int64)
|
||||
case starsGiveaway(stars: Int64, boostPeer: EnginePeer.Id, additionalPeerIds: [EnginePeer.Id], countries: [String], onlyNewSubscribers: Bool, showWinners: Bool, prizeDescription: String?, randomId: Int64, untilDate: Int32, users: Int32)
|
||||
case authCode(restore: Bool, phoneNumber: String, phoneCodeHash: String)
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
@ -704,6 +742,12 @@ private final class PendingInAppPurchaseState: Codable {
|
||||
untilDate: try container.decode(Int32.self, forKey: .untilDate),
|
||||
users: try container.decode(Int32.self, forKey: .users)
|
||||
)
|
||||
case .authCode:
|
||||
self = .authCode(
|
||||
restore: try container.decode(Bool.self, forKey: .restore),
|
||||
phoneNumber: try container.decode(String.self, forKey: .phoneNumber),
|
||||
phoneCodeHash: try container.decode(String.self, forKey: .phoneCodeHash)
|
||||
)
|
||||
default:
|
||||
throw DecodingError.generic
|
||||
}
|
||||
@ -757,6 +801,11 @@ private final class PendingInAppPurchaseState: Codable {
|
||||
try container.encode(randomId, forKey: .randomId)
|
||||
try container.encode(untilDate, forKey: .untilDate)
|
||||
try container.encode(users, forKey: .users)
|
||||
case let .authCode(restore, phoneNumber, phoneCodeHash):
|
||||
try container.encode(PurposeType.authCode.rawValue, forKey: .type)
|
||||
try container.encode(restore, forKey: .restore)
|
||||
try container.encode(phoneNumber, forKey: .phoneNumber)
|
||||
try container.encode(phoneCodeHash, forKey: .phoneCodeHash)
|
||||
}
|
||||
}
|
||||
|
||||
@ -780,6 +829,8 @@ private final class PendingInAppPurchaseState: Codable {
|
||||
self = .starsGift(peerId: peerId, count: count)
|
||||
case let .starsGiveaway(stars, boostPeer, additionalPeerIds, countries, onlyNewSubscribers, showWinners, prizeDescription, randomId, untilDate, _, _, users):
|
||||
self = .starsGiveaway(stars: stars, boostPeer: boostPeer, additionalPeerIds: additionalPeerIds, countries: countries, onlyNewSubscribers: onlyNewSubscribers, showWinners: showWinners, prizeDescription: prizeDescription, randomId: randomId, untilDate: untilDate, users: users)
|
||||
case let .authCode(restore, phoneNumber, phoneCodeHash, _, _):
|
||||
self = .authCode(restore: restore, phoneNumber: phoneNumber, phoneCodeHash: phoneCodeHash)
|
||||
}
|
||||
}
|
||||
|
||||
@ -804,6 +855,8 @@ private final class PendingInAppPurchaseState: Codable {
|
||||
return .starsGift(peerId: peerId, count: count, currency: currency, amount: amount)
|
||||
case let .starsGiveaway(stars, boostPeer, additionalPeerIds, countries, onlyNewSubscribers, showWinners, prizeDescription, randomId, untilDate, users):
|
||||
return .starsGiveaway(stars: stars, boostPeer: boostPeer, additionalPeerIds: additionalPeerIds, countries: countries, onlyNewSubscribers: onlyNewSubscribers, showWinners: showWinners, prizeDescription: prizeDescription, randomId: randomId, untilDate: untilDate, currency: currency, amount: amount, users: users)
|
||||
case let .authCode(restore, phoneNumber, phoneCodeHash):
|
||||
return .authCode(restore: restore, phoneNumber: phoneNumber, phoneCodeHash: phoneCodeHash, currency: currency, amount: amount)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -831,23 +884,41 @@ private final class PendingInAppPurchaseState: Codable {
|
||||
}
|
||||
}
|
||||
|
||||
private func pendingInAppPurchaseState(engine: TelegramEngine, productId: String) -> Signal<PendingInAppPurchaseState?, NoError> {
|
||||
private func pendingInAppPurchaseState(engine: SomeTelegramEngine, productId: String) -> Signal<PendingInAppPurchaseState?, NoError> {
|
||||
let key = EngineDataBuffer(length: 8)
|
||||
key.setInt64(0, value: Int64(bitPattern: productId.persistentHashValue))
|
||||
|
||||
return engine.data.get(TelegramEngine.EngineData.Item.ItemCache.Item(collectionId: ApplicationSpecificItemCacheCollectionId.pendingInAppPurchaseState, id: key))
|
||||
|> map { entry -> PendingInAppPurchaseState? in
|
||||
return entry?.get(PendingInAppPurchaseState.self)
|
||||
switch engine {
|
||||
case let .authorized(engine):
|
||||
return engine.data.get(TelegramEngine.EngineData.Item.ItemCache.Item(collectionId: ApplicationSpecificItemCacheCollectionId.pendingInAppPurchaseState, id: key))
|
||||
|> map { entry -> PendingInAppPurchaseState? in
|
||||
return entry?.get(PendingInAppPurchaseState.self)
|
||||
}
|
||||
case let .unauthorized(engine):
|
||||
return engine.itemCache.get(collectionId: ApplicationSpecificItemCacheCollectionId.pendingInAppPurchaseState, id: key)
|
||||
|> map { entry -> PendingInAppPurchaseState? in
|
||||
return entry?.get(PendingInAppPurchaseState.self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func updatePendingInAppPurchaseState(engine: TelegramEngine, productId: String, content: PendingInAppPurchaseState?) -> Signal<Never, NoError> {
|
||||
private func updatePendingInAppPurchaseState(engine: SomeTelegramEngine, productId: String, content: PendingInAppPurchaseState?) -> Signal<Never, NoError> {
|
||||
let key = EngineDataBuffer(length: 8)
|
||||
key.setInt64(0, value: Int64(bitPattern: productId.persistentHashValue))
|
||||
|
||||
if let content = content {
|
||||
return engine.itemCache.put(collectionId: ApplicationSpecificItemCacheCollectionId.pendingInAppPurchaseState, id: key, item: content)
|
||||
} else {
|
||||
return engine.itemCache.remove(collectionId: ApplicationSpecificItemCacheCollectionId.pendingInAppPurchaseState, id: key)
|
||||
|
||||
switch engine {
|
||||
case let .authorized(engine):
|
||||
if let content = content {
|
||||
return engine.itemCache.put(collectionId: ApplicationSpecificItemCacheCollectionId.pendingInAppPurchaseState, id: key, item: content)
|
||||
} else {
|
||||
return engine.itemCache.remove(collectionId: ApplicationSpecificItemCacheCollectionId.pendingInAppPurchaseState, id: key)
|
||||
}
|
||||
case let .unauthorized(engine):
|
||||
if let content = content {
|
||||
return engine.itemCache.put(collectionId: ApplicationSpecificItemCacheCollectionId.pendingInAppPurchaseState, id: key, item: content)
|
||||
} else {
|
||||
return engine.itemCache.remove(collectionId: ApplicationSpecificItemCacheCollectionId.pendingInAppPurchaseState, id: key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -898,7 +898,7 @@
|
||||
}
|
||||
restartRequest = true;
|
||||
}
|
||||
else if (rpcError.errorCode == 420 || [rpcError.errorDescription rangeOfString:@"FLOOD_WAIT_"].location != NSNotFound || [rpcError.errorDescription rangeOfString:@"FLOOD_PREMIUM_WAIT_"].location != NSNotFound) {
|
||||
else if ((rpcError.errorCode == 420 && [rpcError.errorDescription rangeOfString:@"FROZEN_METHOD_INVALID"].location == NSNotFound) || [rpcError.errorDescription rangeOfString:@"FLOOD_WAIT_"].location != NSNotFound || [rpcError.errorDescription rangeOfString:@"FLOOD_PREMIUM_WAIT_"].location != NSNotFound) {
|
||||
if (request.errorContext == nil)
|
||||
request.errorContext = [[MTRequestErrorContext alloc] init];
|
||||
|
||||
|
@ -233,6 +233,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, ASScrollView
|
||||
}, openWebApp: { _ in
|
||||
}, openPhotoSetup: {
|
||||
}, openAdInfo: { _ in
|
||||
}, openAccountFreezeInfo: {
|
||||
})
|
||||
|
||||
let chatListPresentationData = ChatListPresentationData(theme: self.presentationData.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true)
|
||||
|
@ -382,6 +382,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
||||
}, openWebApp: { _ in
|
||||
}, openPhotoSetup: {
|
||||
}, openAdInfo: { _ in
|
||||
}, openAccountFreezeInfo: {
|
||||
})
|
||||
|
||||
func makeChatListItem(
|
||||
|
@ -84,7 +84,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-1778593322] = { return Api.BotApp.parse_botApp($0) }
|
||||
dict[1571189943] = { return Api.BotApp.parse_botAppNotModified($0) }
|
||||
dict[-912582320] = { return Api.BotAppSettings.parse_botAppSettings($0) }
|
||||
dict[-1989921868] = { return Api.BotBusinessConnection.parse_botBusinessConnection($0) }
|
||||
dict[-1892371723] = { return Api.BotBusinessConnection.parse_botBusinessConnection($0) }
|
||||
dict[-1032140601] = { return Api.BotCommand.parse_botCommand($0) }
|
||||
dict[-1180016534] = { return Api.BotCommandScope.parse_botCommandScopeChatAdmins($0) }
|
||||
dict[1877059713] = { return Api.BotCommandScope.parse_botCommandScopeChats($0) }
|
||||
@ -118,6 +118,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-867328308] = { return Api.BusinessAwayMessageSchedule.parse_businessAwayMessageScheduleCustom($0) }
|
||||
dict[-1007487743] = { return Api.BusinessAwayMessageSchedule.parse_businessAwayMessageScheduleOutsideWorkHours($0) }
|
||||
dict[-1198722189] = { return Api.BusinessBotRecipients.parse_businessBotRecipients($0) }
|
||||
dict[-1604170505] = { return Api.BusinessBotRights.parse_businessBotRights($0) }
|
||||
dict[-1263638929] = { return Api.BusinessChatLink.parse_businessChatLink($0) }
|
||||
dict[-451302485] = { return Api.BusinessGreetingMessage.parse_businessGreetingMessage($0) }
|
||||
dict[1510606445] = { return Api.BusinessIntro.parse_businessIntro($0) }
|
||||
@ -224,7 +225,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[1713193015] = { return Api.ChatReactions.parse_chatReactionsSome($0) }
|
||||
dict[-1390068360] = { return Api.CodeSettings.parse_codeSettings($0) }
|
||||
dict[-870702050] = { return Api.Config.parse_config($0) }
|
||||
dict[-1123645951] = { return Api.ConnectedBot.parse_connectedBot($0) }
|
||||
dict[-849058964] = { return Api.ConnectedBot.parse_connectedBot($0) }
|
||||
dict[429997937] = { return Api.ConnectedBotStarRef.parse_connectedBotStarRef($0) }
|
||||
dict[341499403] = { return Api.Contact.parse_contact($0) }
|
||||
dict[496600883] = { return Api.ContactBirthday.parse_contactBirthday($0) }
|
||||
@ -480,6 +481,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[853188252] = { return Api.InputStickerSetItem.parse_inputStickerSetItem($0) }
|
||||
dict[70813275] = { return Api.InputStickeredMedia.parse_inputStickeredMediaDocument($0) }
|
||||
dict[1251549527] = { return Api.InputStickeredMedia.parse_inputStickeredMediaPhoto($0) }
|
||||
dict[-1682807955] = { return Api.InputStorePaymentPurpose.parse_inputStorePaymentAuthCode($0) }
|
||||
dict[1634697192] = { return Api.InputStorePaymentPurpose.parse_inputStorePaymentGiftPremium($0) }
|
||||
dict[-75955309] = { return Api.InputStorePaymentPurpose.parse_inputStorePaymentPremiumGiftCode($0) }
|
||||
dict[369444042] = { return Api.InputStorePaymentPurpose.parse_inputStorePaymentPremiumGiveaway($0) }
|
||||
@ -1110,6 +1112,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-1821035490] = { return Api.Update.parse_updateSavedGifs($0) }
|
||||
dict[969307186] = { return Api.Update.parse_updateSavedReactionTags($0) }
|
||||
dict[1960361625] = { return Api.Update.parse_updateSavedRingtones($0) }
|
||||
dict[1347068303] = { return Api.Update.parse_updateSentPhoneCode($0) }
|
||||
dict[2103604867] = { return Api.Update.parse_updateSentStoryReaction($0) }
|
||||
dict[-337352679] = { return Api.Update.parse_updateServiceNotification($0) }
|
||||
dict[-245208620] = { return Api.Update.parse_updateSmsJob($0) }
|
||||
@ -1217,6 +1220,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[957176926] = { return Api.auth.LoginToken.parse_loginTokenSuccess($0) }
|
||||
dict[326715557] = { return Api.auth.PasswordRecovery.parse_passwordRecovery($0) }
|
||||
dict[1577067778] = { return Api.auth.SentCode.parse_sentCode($0) }
|
||||
dict[304435204] = { return Api.auth.SentCode.parse_sentCodePaymentRequired($0) }
|
||||
dict[596704836] = { return Api.auth.SentCode.parse_sentCodeSuccess($0) }
|
||||
dict[1035688326] = { return Api.auth.SentCodeType.parse_sentCodeTypeApp($0) }
|
||||
dict[1398007207] = { return Api.auth.SentCodeType.parse_sentCodeTypeCall($0) }
|
||||
@ -1589,6 +1593,8 @@ public extension Api {
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.BusinessBotRecipients:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.BusinessBotRights:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.BusinessChatLink:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.BusinessGreetingMessage:
|
||||
|
@ -1170,27 +1170,28 @@ public extension Api {
|
||||
}
|
||||
public extension Api {
|
||||
enum BotBusinessConnection: TypeConstructorDescription {
|
||||
case botBusinessConnection(flags: Int32, connectionId: String, userId: Int64, dcId: Int32, date: Int32)
|
||||
case botBusinessConnection(flags: Int32, connectionId: String, userId: Int64, dcId: Int32, date: Int32, rights: Api.BusinessBotRights?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .botBusinessConnection(let flags, let connectionId, let userId, let dcId, let date):
|
||||
case .botBusinessConnection(let flags, let connectionId, let userId, let dcId, let date, let rights):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1989921868)
|
||||
buffer.appendInt32(-1892371723)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeString(connectionId, buffer: buffer, boxed: false)
|
||||
serializeInt64(userId, buffer: buffer, boxed: false)
|
||||
serializeInt32(dcId, buffer: buffer, boxed: false)
|
||||
serializeInt32(date, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 2) != 0 {rights!.serialize(buffer, true)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .botBusinessConnection(let flags, let connectionId, let userId, let dcId, let date):
|
||||
return ("botBusinessConnection", [("flags", flags as Any), ("connectionId", connectionId as Any), ("userId", userId as Any), ("dcId", dcId as Any), ("date", date as Any)])
|
||||
case .botBusinessConnection(let flags, let connectionId, let userId, let dcId, let date, let rights):
|
||||
return ("botBusinessConnection", [("flags", flags as Any), ("connectionId", connectionId as Any), ("userId", userId as Any), ("dcId", dcId as Any), ("date", date as Any), ("rights", rights as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
@ -1205,13 +1206,18 @@ public extension Api {
|
||||
_4 = reader.readInt32()
|
||||
var _5: Int32?
|
||||
_5 = reader.readInt32()
|
||||
var _6: Api.BusinessBotRights?
|
||||
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
|
||||
_6 = Api.parse(reader, signature: signature) as? Api.BusinessBotRights
|
||||
} }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 {
|
||||
return Api.BotBusinessConnection.botBusinessConnection(flags: _1!, connectionId: _2!, userId: _3!, dcId: _4!, date: _5!)
|
||||
let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
||||
return Api.BotBusinessConnection.botBusinessConnection(flags: _1!, connectionId: _2!, userId: _3!, dcId: _4!, date: _5!, rights: _6)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
@ -116,6 +116,7 @@ public extension Api {
|
||||
}
|
||||
public extension Api {
|
||||
indirect enum InputStorePaymentPurpose: TypeConstructorDescription {
|
||||
case inputStorePaymentAuthCode(flags: Int32, phoneNumber: String, phoneCodeHash: String, currency: String, amount: Int64)
|
||||
case inputStorePaymentGiftPremium(userId: Api.InputUser, currency: String, amount: Int64)
|
||||
case inputStorePaymentPremiumGiftCode(flags: Int32, users: [Api.InputUser], boostPeer: Api.InputPeer?, currency: String, amount: Int64, message: Api.TextWithEntities?)
|
||||
case inputStorePaymentPremiumGiveaway(flags: Int32, boostPeer: Api.InputPeer, additionalPeers: [Api.InputPeer]?, countriesIso2: [String]?, prizeDescription: String?, randomId: Int64, untilDate: Int32, currency: String, amount: Int64)
|
||||
@ -126,6 +127,16 @@ public extension Api {
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputStorePaymentAuthCode(let flags, let phoneNumber, let phoneCodeHash, let currency, let amount):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1682807955)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeString(phoneNumber, buffer: buffer, boxed: false)
|
||||
serializeString(phoneCodeHash, buffer: buffer, boxed: false)
|
||||
serializeString(currency, buffer: buffer, boxed: false)
|
||||
serializeInt64(amount, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputStorePaymentGiftPremium(let userId, let currency, let amount):
|
||||
if boxed {
|
||||
buffer.appendInt32(1634697192)
|
||||
@ -223,6 +234,8 @@ public extension Api {
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputStorePaymentAuthCode(let flags, let phoneNumber, let phoneCodeHash, let currency, let amount):
|
||||
return ("inputStorePaymentAuthCode", [("flags", flags as Any), ("phoneNumber", phoneNumber as Any), ("phoneCodeHash", phoneCodeHash as Any), ("currency", currency as Any), ("amount", amount as Any)])
|
||||
case .inputStorePaymentGiftPremium(let userId, let currency, let amount):
|
||||
return ("inputStorePaymentGiftPremium", [("userId", userId as Any), ("currency", currency as Any), ("amount", amount as Any)])
|
||||
case .inputStorePaymentPremiumGiftCode(let flags, let users, let boostPeer, let currency, let amount, let message):
|
||||
@ -240,6 +253,29 @@ public extension Api {
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputStorePaymentAuthCode(_ reader: BufferReader) -> InputStorePaymentPurpose? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: String?
|
||||
_2 = parseString(reader)
|
||||
var _3: String?
|
||||
_3 = parseString(reader)
|
||||
var _4: String?
|
||||
_4 = parseString(reader)
|
||||
var _5: Int64?
|
||||
_5 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 {
|
||||
return Api.InputStorePaymentPurpose.inputStorePaymentAuthCode(flags: _1!, phoneNumber: _2!, phoneCodeHash: _3!, currency: _4!, amount: _5!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputStorePaymentGiftPremium(_ reader: BufferReader) -> InputStorePaymentPurpose? {
|
||||
var _1: Api.InputUser?
|
||||
if let signature = reader.readInt32() {
|
||||
|
@ -1103,6 +1103,7 @@ public extension Api {
|
||||
case updateSavedGifs
|
||||
case updateSavedReactionTags
|
||||
case updateSavedRingtones
|
||||
case updateSentPhoneCode(sentCode: Api.auth.SentCode)
|
||||
case updateSentStoryReaction(peer: Api.Peer, storyId: Int32, reaction: Api.Reaction)
|
||||
case updateServiceNotification(flags: Int32, inboxDate: Int32?, type: String, message: String, media: Api.MessageMedia, entities: [Api.MessageEntity])
|
||||
case updateSmsJob(jobId: String)
|
||||
@ -2198,6 +2199,12 @@ public extension Api {
|
||||
buffer.appendInt32(1960361625)
|
||||
}
|
||||
|
||||
break
|
||||
case .updateSentPhoneCode(let sentCode):
|
||||
if boxed {
|
||||
buffer.appendInt32(1347068303)
|
||||
}
|
||||
sentCode.serialize(buffer, true)
|
||||
break
|
||||
case .updateSentStoryReaction(let peer, let storyId, let reaction):
|
||||
if boxed {
|
||||
@ -2602,6 +2609,8 @@ public extension Api {
|
||||
return ("updateSavedReactionTags", [])
|
||||
case .updateSavedRingtones:
|
||||
return ("updateSavedRingtones", [])
|
||||
case .updateSentPhoneCode(let sentCode):
|
||||
return ("updateSentPhoneCode", [("sentCode", sentCode as Any)])
|
||||
case .updateSentStoryReaction(let peer, let storyId, let reaction):
|
||||
return ("updateSentStoryReaction", [("peer", peer as Any), ("storyId", storyId as Any), ("reaction", reaction as Any)])
|
||||
case .updateServiceNotification(let flags, let inboxDate, let type, let message, let media, let entities):
|
||||
@ -4803,6 +4812,19 @@ public extension Api {
|
||||
public static func parse_updateSavedRingtones(_ reader: BufferReader) -> Update? {
|
||||
return Api.Update.updateSavedRingtones
|
||||
}
|
||||
public static func parse_updateSentPhoneCode(_ reader: BufferReader) -> Update? {
|
||||
var _1: Api.auth.SentCode?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.auth.SentCode
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.Update.updateSentPhoneCode(sentCode: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_updateSentStoryReaction(_ reader: BufferReader) -> Update? {
|
||||
var _1: Api.Peer?
|
||||
if let signature = reader.readInt32() {
|
||||
|
@ -567,6 +567,7 @@ public extension Api.auth {
|
||||
public extension Api.auth {
|
||||
enum SentCode: TypeConstructorDescription {
|
||||
case sentCode(flags: Int32, type: Api.auth.SentCodeType, phoneCodeHash: String, nextType: Api.auth.CodeType?, timeout: Int32?)
|
||||
case sentCodePaymentRequired(storeProduct: String)
|
||||
case sentCodeSuccess(authorization: Api.auth.Authorization)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
@ -581,6 +582,12 @@ public extension Api.auth {
|
||||
if Int(flags) & Int(1 << 1) != 0 {nextType!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 2) != 0 {serializeInt32(timeout!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
case .sentCodePaymentRequired(let storeProduct):
|
||||
if boxed {
|
||||
buffer.appendInt32(304435204)
|
||||
}
|
||||
serializeString(storeProduct, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .sentCodeSuccess(let authorization):
|
||||
if boxed {
|
||||
buffer.appendInt32(596704836)
|
||||
@ -594,6 +601,8 @@ public extension Api.auth {
|
||||
switch self {
|
||||
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)])
|
||||
case .sentCodePaymentRequired(let storeProduct):
|
||||
return ("sentCodePaymentRequired", [("storeProduct", storeProduct as Any)])
|
||||
case .sentCodeSuccess(let authorization):
|
||||
return ("sentCodeSuccess", [("authorization", authorization as Any)])
|
||||
}
|
||||
@ -626,6 +635,17 @@ public extension Api.auth {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_sentCodePaymentRequired(_ reader: BufferReader) -> SentCode? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.auth.SentCode.sentCodePaymentRequired(storeProduct: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_sentCodeSuccess(_ reader: BufferReader) -> SentCode? {
|
||||
var _1: Api.auth.Authorization?
|
||||
if let signature = reader.readInt32() {
|
||||
|
@ -54,6 +54,42 @@ public extension Api {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum BusinessBotRights: TypeConstructorDescription {
|
||||
case businessBotRights(flags: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .businessBotRights(let flags):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1604170505)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .businessBotRights(let flags):
|
||||
return ("businessBotRights", [("flags", flags as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_businessBotRights(_ reader: BufferReader) -> BusinessBotRights? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.BusinessBotRights.businessBotRights(flags: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum BusinessChatLink: TypeConstructorDescription {
|
||||
case businessChatLink(flags: Int32, link: String, message: String, entities: [Api.MessageEntity]?, title: String?, views: Int32)
|
||||
|
@ -1604,13 +1604,14 @@ public extension Api.functions.account {
|
||||
}
|
||||
}
|
||||
public extension Api.functions.account {
|
||||
static func updateConnectedBot(flags: Int32, bot: Api.InputUser, recipients: Api.InputBusinessBotRecipients) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
static func updateConnectedBot(flags: Int32, rights: Api.BusinessBotRights?, bot: Api.InputUser, recipients: Api.InputBusinessBotRecipients) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(1138250269)
|
||||
buffer.appendInt32(1721797758)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {rights!.serialize(buffer, true)}
|
||||
bot.serialize(buffer, true)
|
||||
recipients.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "account.updateConnectedBot", parameters: [("flags", String(describing: flags)), ("bot", String(describing: bot)), ("recipients", String(describing: recipients))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
return (FunctionDescription(name: "account.updateConnectedBot", parameters: [("flags", String(describing: flags)), ("rights", String(describing: rights)), ("bot", String(describing: bot)), ("recipients", String(describing: recipients))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Updates?
|
||||
if let signature = reader.readInt32() {
|
||||
@ -9063,11 +9064,11 @@ public extension Api.functions.payments {
|
||||
}
|
||||
}
|
||||
public extension Api.functions.payments {
|
||||
static func canPurchasePremium(purpose: Api.InputStorePaymentPurpose) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
static func canPurchaseStore(purpose: Api.InputStorePaymentPurpose) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-1614700874)
|
||||
buffer.appendInt32(1339842215)
|
||||
purpose.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "payments.canPurchasePremium", parameters: [("purpose", String(describing: purpose))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
|
||||
return (FunctionDescription(name: "payments.canPurchaseStore", parameters: [("purpose", String(describing: purpose))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Bool?
|
||||
if let signature = reader.readInt32() {
|
||||
|
@ -664,25 +664,26 @@ public extension Api {
|
||||
}
|
||||
public extension Api {
|
||||
enum ConnectedBot: TypeConstructorDescription {
|
||||
case connectedBot(flags: Int32, botId: Int64, recipients: Api.BusinessBotRecipients)
|
||||
case connectedBot(flags: Int32, botId: Int64, recipients: Api.BusinessBotRecipients, rights: Api.BusinessBotRights)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .connectedBot(let flags, let botId, let recipients):
|
||||
case .connectedBot(let flags, let botId, let recipients, let rights):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1123645951)
|
||||
buffer.appendInt32(-849058964)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt64(botId, buffer: buffer, boxed: false)
|
||||
recipients.serialize(buffer, true)
|
||||
rights.serialize(buffer, true)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .connectedBot(let flags, let botId, let recipients):
|
||||
return ("connectedBot", [("flags", flags as Any), ("botId", botId as Any), ("recipients", recipients as Any)])
|
||||
case .connectedBot(let flags, let botId, let recipients, let rights):
|
||||
return ("connectedBot", [("flags", flags as Any), ("botId", botId as Any), ("recipients", recipients as Any), ("rights", rights as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
@ -695,11 +696,16 @@ public extension Api {
|
||||
if let signature = reader.readInt32() {
|
||||
_3 = Api.parse(reader, signature: signature) as? Api.BusinessBotRecipients
|
||||
}
|
||||
var _4: Api.BusinessBotRights?
|
||||
if let signature = reader.readInt32() {
|
||||
_4 = Api.parse(reader, signature: signature) as? Api.BusinessBotRights
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.ConnectedBot.connectedBot(flags: _1!, botId: _2!, recipients: _3!)
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.ConnectedBot.connectedBot(flags: _1!, botId: _2!, recipients: _3!, rights: _4!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
@ -356,6 +356,9 @@ 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)))
|
||||
case let .sentCodePaymentRequired(storeProduct):
|
||||
//TODO:release
|
||||
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .payment(number: phoneNumber, codeHash: "", storeProduct: storeProduct)))
|
||||
case let .sentCodeSuccess(authorization):
|
||||
switch authorization {
|
||||
case let .authorization(_, otherwiseReloginDays, _, futureAuthToken, user):
|
||||
@ -515,6 +518,10 @@ 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)))
|
||||
|
||||
return .single(.sentCode(account))
|
||||
case let .sentCodePaymentRequired(storeProduct):
|
||||
//TODO:release
|
||||
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .payment(number: number, codeHash: "", storeProduct: storeProduct)))
|
||||
return .single(.sentCode(account))
|
||||
case .sentCodeSuccess:
|
||||
return .single(.loggedIn)
|
||||
@ -621,6 +628,9 @@ 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)))
|
||||
case let .sentCodePaymentRequired(storeProduct):
|
||||
//TODO:release
|
||||
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .payment(number: number, codeHash: "", storeProduct: storeProduct)))
|
||||
case .sentCodeSuccess:
|
||||
break
|
||||
}
|
||||
@ -898,6 +908,9 @@ 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)))
|
||||
case let .sentCodePaymentRequired(storeProduct):
|
||||
//TODO:release
|
||||
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .payment(number: phoneNumber, codeHash: phoneCodeHash, storeProduct: storeProduct)))
|
||||
case .sentCodeSuccess:
|
||||
break
|
||||
}
|
||||
@ -960,6 +973,10 @@ 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)))
|
||||
|
||||
return .complete()
|
||||
case let .sentCodePaymentRequired(storeProduct):
|
||||
//TODO:release
|
||||
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .payment(number: phoneNumber, codeHash: phoneCodeHash, storeProduct: storeProduct)))
|
||||
return .complete()
|
||||
case .sentCodeSuccess:
|
||||
return .complete()
|
||||
|
@ -210,7 +210,7 @@ public class BoxedMessage: NSObject {
|
||||
|
||||
public class Serialization: NSObject, MTSerialization {
|
||||
public func currentLayer() -> UInt {
|
||||
return 200
|
||||
return 201
|
||||
}
|
||||
|
||||
public func parseMessage(_ data: Data!) -> Any! {
|
||||
|
@ -139,6 +139,7 @@ private enum UnauthorizedAccountStateContentsValue: Int32 {
|
||||
case signUp = 5
|
||||
case passwordRecovery = 6
|
||||
case awaitingAccountReset = 7
|
||||
case payment = 8
|
||||
}
|
||||
|
||||
public struct UnauthorizedAccountTermsOfService: PostboxCoding, Equatable {
|
||||
@ -181,6 +182,7 @@ public indirect enum UnauthorizedAccountStateContents: PostboxCoding, Equatable
|
||||
case passwordRecovery(hint: String, number: String?, code: AuthorizationCode?, emailPattern: 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 payment(number: String, codeHash: String, storeProduct: String)
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
switch decoder.decodeInt32ForKey("v", orElse: 0) {
|
||||
@ -214,6 +216,9 @@ 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)
|
||||
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)
|
||||
|
||||
case UnauthorizedAccountStateContentsValue.payment.rawValue:
|
||||
self = .payment(number: decoder.decodeStringForKey("n", orElse: ""), codeHash: decoder.decodeStringForKey("h", orElse: ""), storeProduct: decoder.decodeStringForKey("storeProduct", orElse: ""))
|
||||
default:
|
||||
assertionFailure()
|
||||
self = .empty
|
||||
@ -303,6 +308,11 @@ public indirect enum UnauthorizedAccountStateContents: PostboxCoding, Equatable
|
||||
encoder.encodeNil(forKey: "tos")
|
||||
}
|
||||
encoder.encodeInt32(syncContacts ? 1 : 0, forKey: "syncContacts")
|
||||
case let .payment(number, codeHash, storeProduct):
|
||||
encoder.encodeInt32(UnauthorizedAccountStateContentsValue.payment.rawValue, forKey: "v")
|
||||
encoder.encodeString(number, forKey: "n")
|
||||
encoder.encodeString(codeHash, forKey: "h")
|
||||
encoder.encodeString(storeProduct, forKey: "storeProduct")
|
||||
}
|
||||
}
|
||||
|
||||
@ -374,6 +384,12 @@ public indirect enum UnauthorizedAccountStateContents: PostboxCoding, Equatable
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .payment(number, codeHash, storeProduct):
|
||||
if case .payment(number, codeHash, storeProduct) = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ func _internal_requestChangeAccountPhoneNumberVerification(account: Account, api
|
||||
} else {
|
||||
return .single(ChangeAccountPhoneNumberData(type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: codeTimeout, nextType: parsedNextType))
|
||||
}
|
||||
case .sentCodeSuccess:
|
||||
case .sentCodeSuccess, .sentCodePaymentRequired:
|
||||
return .never()
|
||||
}
|
||||
}
|
||||
@ -188,7 +188,7 @@ private func internalResendChangeAccountPhoneNumberVerification(account: Account
|
||||
} else {
|
||||
return .single(ChangeAccountPhoneNumberData(type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: codeTimeout, nextType: parsedNextType))
|
||||
}
|
||||
case .sentCodeSuccess:
|
||||
case .sentCodeSuccess, .sentCodePaymentRequired:
|
||||
return .never()
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ func _internal_requestCancelAccountResetData(network: Network, hash: String) ->
|
||||
parsedNextType = AuthorizationCodeNextType(apiType: nextType)
|
||||
}
|
||||
return .single(CancelAccountResetData(type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: timeout, nextType: parsedNextType))
|
||||
case .sentCodeSuccess:
|
||||
case .sentCodeSuccess, .sentCodePaymentRequired:
|
||||
return .never()
|
||||
}
|
||||
}
|
||||
@ -57,7 +57,7 @@ func _internal_requestNextCancelAccountResetOption(network: Network, phoneNumber
|
||||
parsedNextType = AuthorizationCodeNextType(apiType: nextType)
|
||||
}
|
||||
return .single(CancelAccountResetData(type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: timeout, nextType: parsedNextType))
|
||||
case .sentCodeSuccess:
|
||||
case .sentCodeSuccess, .sentCodePaymentRequired:
|
||||
return .never()
|
||||
}
|
||||
}
|
||||
|
@ -38,3 +38,44 @@ public extension TelegramEngine {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension TelegramEngineUnauthorized {
|
||||
final class ItemCache {
|
||||
private let account: UnauthorizedAccount
|
||||
|
||||
init(account: UnauthorizedAccount) {
|
||||
self.account = account
|
||||
}
|
||||
|
||||
public func put<T: Codable>(collectionId: Int8, id: EngineDataBuffer, item: T) -> Signal<Never, NoError> {
|
||||
return self.account.postbox.transaction { transaction -> Void in
|
||||
if let entry = CodableEntry(item) {
|
||||
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: collectionId, key: id), entry: entry)
|
||||
}
|
||||
}
|
||||
|> ignoreValues
|
||||
}
|
||||
|
||||
public func get(collectionId: Int8, id: EngineDataBuffer) -> Signal<CodableEntry?, NoError> {
|
||||
return self.account.postbox.transaction { transaction -> CodableEntry? in
|
||||
return transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: collectionId, key: id))
|
||||
}
|
||||
}
|
||||
|
||||
public func remove(collectionId: Int8, id: EngineDataBuffer) -> Signal<Never, NoError> {
|
||||
return self.account.postbox.transaction { transaction -> Void in
|
||||
transaction.removeItemCacheEntry(id: ItemCacheEntryId(collectionId: collectionId, key: id))
|
||||
}
|
||||
|> ignoreValues
|
||||
}
|
||||
|
||||
public func clear(collectionIds: [Int8]) -> Signal<Never, NoError> {
|
||||
return self.account.postbox.transaction { transaction -> Void in
|
||||
for id in collectionIds {
|
||||
transaction.clearItemCacheCollection(collectionId: id)
|
||||
}
|
||||
}
|
||||
|> ignoreValues
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1072,7 +1072,7 @@ public func _internal_setAccountConnectedBot(account: Account, bot: TelegramAcco
|
||||
flags |= 1 << 1
|
||||
}
|
||||
|
||||
return account.network.request(Api.functions.account.updateConnectedBot(flags: flags, bot: mappedBot, recipients: mappedRecipients))
|
||||
return account.network.request(Api.functions.account.updateConnectedBot(flags: flags, rights: nil, bot: mappedBot, recipients: mappedRecipients))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
|
||||
return .single(nil)
|
||||
|
@ -20,9 +20,10 @@ public enum AppStoreTransactionPurpose {
|
||||
case stars(count: Int64, currency: String, amount: Int64)
|
||||
case starsGift(peerId: EnginePeer.Id, count: Int64, currency: String, amount: Int64)
|
||||
case starsGiveaway(stars: Int64, boostPeer: EnginePeer.Id, additionalPeerIds: [EnginePeer.Id], countries: [String], onlyNewSubscribers: Bool, showWinners: Bool, prizeDescription: String?, randomId: Int64, untilDate: Int32, currency: String, amount: Int64, users: Int32)
|
||||
case authCode(restore: Bool, phoneNumber: String, phoneCodeHash: String, currency: String, amount: Int64)
|
||||
}
|
||||
|
||||
private func apiInputStorePaymentPurpose(account: Account, purpose: AppStoreTransactionPurpose) -> Signal<Api.InputStorePaymentPurpose, NoError> {
|
||||
private func apiInputStorePaymentPurpose(postbox: Postbox, purpose: AppStoreTransactionPurpose) -> Signal<Api.InputStorePaymentPurpose, NoError> {
|
||||
switch purpose {
|
||||
case .subscription, .upgrade, .restore:
|
||||
var flags: Int32 = 0
|
||||
@ -36,7 +37,7 @@ private func apiInputStorePaymentPurpose(account: Account, purpose: AppStoreTran
|
||||
}
|
||||
return .single(.inputStorePaymentPremiumSubscription(flags: flags))
|
||||
case let .gift(peerId, currency, amount):
|
||||
return account.postbox.loadedPeerWithId(peerId)
|
||||
return postbox.loadedPeerWithId(peerId)
|
||||
|> mapToSignal { peer -> Signal<Api.InputStorePaymentPurpose, NoError> in
|
||||
guard let inputUser = apiInputUser(peer) else {
|
||||
return .complete()
|
||||
@ -44,7 +45,7 @@ private func apiInputStorePaymentPurpose(account: Account, purpose: AppStoreTran
|
||||
return .single(.inputStorePaymentGiftPremium(userId: inputUser, currency: currency, amount: amount))
|
||||
}
|
||||
case let .giftCode(peerIds, boostPeerId, currency, amount, text, entities):
|
||||
return account.postbox.transaction { transaction -> Api.InputStorePaymentPurpose in
|
||||
return postbox.transaction { transaction -> Api.InputStorePaymentPurpose in
|
||||
var flags: Int32 = 0
|
||||
var apiBoostPeer: Api.InputPeer?
|
||||
var apiInputUsers: [Api.InputUser] = []
|
||||
@ -69,7 +70,7 @@ private func apiInputStorePaymentPurpose(account: Account, purpose: AppStoreTran
|
||||
return .inputStorePaymentPremiumGiftCode(flags: flags, users: apiInputUsers, boostPeer: apiBoostPeer, currency: currency, amount: amount, message: message)
|
||||
}
|
||||
case let .giveaway(boostPeerId, additionalPeerIds, countries, onlyNewSubscribers, showWinners, prizeDescription, randomId, untilDate, currency, amount):
|
||||
return account.postbox.transaction { transaction -> Signal<Api.InputStorePaymentPurpose, NoError> in
|
||||
return postbox.transaction { transaction -> Signal<Api.InputStorePaymentPurpose, NoError> in
|
||||
guard let peer = transaction.getPeer(boostPeerId), let apiBoostPeer = apiInputPeer(peer) else {
|
||||
return .complete()
|
||||
}
|
||||
@ -101,7 +102,7 @@ private func apiInputStorePaymentPurpose(account: Account, purpose: AppStoreTran
|
||||
case let .stars(count, currency, amount):
|
||||
return .single(.inputStorePaymentStarsTopup(stars: count, currency: currency, amount: amount))
|
||||
case let .starsGift(peerId, count, currency, amount):
|
||||
return account.postbox.loadedPeerWithId(peerId)
|
||||
return postbox.loadedPeerWithId(peerId)
|
||||
|> mapToSignal { peer -> Signal<Api.InputStorePaymentPurpose, NoError> in
|
||||
guard let inputUser = apiInputUser(peer) else {
|
||||
return .complete()
|
||||
@ -109,7 +110,7 @@ private func apiInputStorePaymentPurpose(account: Account, purpose: AppStoreTran
|
||||
return .single(.inputStorePaymentStarsGift(userId: inputUser, stars: count, currency: currency, amount: amount))
|
||||
}
|
||||
case let .starsGiveaway(stars, boostPeerId, additionalPeerIds, countries, onlyNewSubscribers, showWinners, prizeDescription, randomId, untilDate, currency, amount, users):
|
||||
return account.postbox.transaction { transaction -> Signal<Api.InputStorePaymentPurpose, NoError> in
|
||||
return postbox.transaction { transaction -> Signal<Api.InputStorePaymentPurpose, NoError> in
|
||||
guard let peer = transaction.getPeer(boostPeerId), let apiBoostPeer = apiInputPeer(peer) else {
|
||||
return .complete()
|
||||
}
|
||||
@ -138,14 +139,20 @@ private func apiInputStorePaymentPurpose(account: Account, purpose: AppStoreTran
|
||||
return .single(.inputStorePaymentStarsGiveaway(flags: flags, stars: stars, boostPeer: apiBoostPeer, additionalPeers: additionalPeers, countriesIso2: countries, prizeDescription: prizeDescription, randomId: randomId, untilDate: untilDate, currency: currency, amount: amount, users: users))
|
||||
}
|
||||
|> switchToLatest
|
||||
case let .authCode(restore, phoneNumber, phoneCodeHash, currency, amount):
|
||||
var flags: Int32 = 0
|
||||
if restore {
|
||||
flags |= (1 << 0)
|
||||
}
|
||||
return .single(.inputStorePaymentAuthCode(flags: flags, phoneNumber: phoneNumber, phoneCodeHash: phoneCodeHash, currency: currency, amount: amount))
|
||||
}
|
||||
}
|
||||
|
||||
func _internal_sendAppStoreReceipt(account: Account, receipt: Data, purpose: AppStoreTransactionPurpose) -> Signal<Never, AssignAppStoreTransactionError> {
|
||||
return apiInputStorePaymentPurpose(account: account, purpose: purpose)
|
||||
func _internal_sendAppStoreReceipt(postbox: Postbox, network: Network, stateManager: AccountStateManager?, receipt: Data, purpose: AppStoreTransactionPurpose) -> Signal<Never, AssignAppStoreTransactionError> {
|
||||
return apiInputStorePaymentPurpose(postbox: postbox, purpose: purpose)
|
||||
|> castError(AssignAppStoreTransactionError.self)
|
||||
|> mapToSignal { purpose -> Signal<Never, AssignAppStoreTransactionError> in
|
||||
return account.network.request(Api.functions.payments.assignAppStoreTransaction(receipt: Buffer(data: receipt), purpose: purpose))
|
||||
return network.request(Api.functions.payments.assignAppStoreTransaction(receipt: Buffer(data: receipt), purpose: purpose))
|
||||
|> mapError { error -> AssignAppStoreTransactionError in
|
||||
if error.errorCode == 406 {
|
||||
return .serverProvided
|
||||
@ -154,7 +161,7 @@ func _internal_sendAppStoreReceipt(account: Account, receipt: Data, purpose: App
|
||||
}
|
||||
}
|
||||
|> mapToSignal { updates -> Signal<Never, AssignAppStoreTransactionError> in
|
||||
account.stateManager.addUpdates(updates)
|
||||
stateManager?.addUpdates(updates)
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
@ -164,10 +171,10 @@ public enum RestoreAppStoreReceiptError {
|
||||
case generic
|
||||
}
|
||||
|
||||
func _internal_canPurchasePremium(account: Account, purpose: AppStoreTransactionPurpose) -> Signal<Bool, NoError> {
|
||||
return apiInputStorePaymentPurpose(account: account, purpose: purpose)
|
||||
func _internal_canPurchasePremium(postbox: Postbox, network: Network, purpose: AppStoreTransactionPurpose) -> Signal<Bool, NoError> {
|
||||
return apiInputStorePaymentPurpose(postbox: postbox, purpose: purpose)
|
||||
|> mapToSignal { purpose -> Signal<Bool, NoError> in
|
||||
return account.network.request(Api.functions.payments.canPurchasePremium(purpose: purpose))
|
||||
return network.request(Api.functions.payments.canPurchaseStore(purpose: purpose))
|
||||
|> map { result -> Bool in
|
||||
switch result {
|
||||
case .boolTrue:
|
||||
|
@ -568,6 +568,21 @@ private final class StarsContextImpl {
|
||||
self._state = state
|
||||
self._statePromise.set(.single(state))
|
||||
}
|
||||
|
||||
var onUpdate: Signal<Void, NoError> {
|
||||
return self._statePromise.get()
|
||||
|> take(until: { value in
|
||||
if let value {
|
||||
if !value.flags.contains(.isPendingBalance) {
|
||||
return SignalTakeAction(passthrough: true, complete: true)
|
||||
}
|
||||
}
|
||||
return SignalTakeAction(passthrough: false, complete: false)
|
||||
})
|
||||
|> map { _ in
|
||||
return Void()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension StarsContext.State.Transaction {
|
||||
@ -1011,6 +1026,18 @@ public final class StarsContext {
|
||||
}
|
||||
}
|
||||
|
||||
public var onUpdate: Signal<Void, NoError> {
|
||||
return Signal { subscriber in
|
||||
let disposable = MetaDisposable()
|
||||
self.impl.with { impl in
|
||||
disposable.set(impl.onUpdate.start(next: { value in
|
||||
subscriber.putNext(value)
|
||||
}))
|
||||
}
|
||||
return disposable
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
init(account: Account) {
|
||||
self.impl = QueueLocalObject(queue: Queue.mainQueue(), generate: {
|
||||
|
@ -39,11 +39,11 @@ public extension TelegramEngine {
|
||||
}
|
||||
|
||||
public func sendAppStoreReceipt(receipt: Data, purpose: AppStoreTransactionPurpose) -> Signal<Never, AssignAppStoreTransactionError> {
|
||||
return _internal_sendAppStoreReceipt(account: self.account, receipt: receipt, purpose: purpose)
|
||||
return _internal_sendAppStoreReceipt(postbox: self.account.postbox, network: self.account.network, stateManager: self.account.stateManager, receipt: receipt, purpose: purpose)
|
||||
}
|
||||
|
||||
public func canPurchasePremium(purpose: AppStoreTransactionPurpose) -> Signal<Bool, NoError> {
|
||||
return _internal_canPurchasePremium(account: self.account, purpose: purpose)
|
||||
return _internal_canPurchasePremium(postbox: self.account.postbox, network: self.account.network, purpose: purpose)
|
||||
}
|
||||
|
||||
public func checkPremiumGiftCode(slug: String) -> Signal<PremiumGiftCodeInfo?, NoError> {
|
||||
@ -150,3 +150,21 @@ public extension TelegramEngine {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension TelegramEngineUnauthorized {
|
||||
final class Payments {
|
||||
private let account: UnauthorizedAccount
|
||||
|
||||
init(account: UnauthorizedAccount) {
|
||||
self.account = account
|
||||
}
|
||||
|
||||
public func canPurchasePremium(purpose: AppStoreTransactionPurpose) -> Signal<Bool, NoError> {
|
||||
return _internal_canPurchasePremium(postbox: self.account.postbox, network: self.account.network, purpose: purpose)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -246,7 +246,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee
|
||||
|
||||
if let apiBot = connectedBots.first {
|
||||
switch apiBot {
|
||||
case let .connectedBot(flags, botId, recipients):
|
||||
case let .connectedBot(flags, botId, recipients, _):
|
||||
mappedConnectedBot = TelegramAccountConnectedBot(
|
||||
id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(botId)),
|
||||
recipients: TelegramBusinessRecipients(apiValue: recipients),
|
||||
|
@ -30,7 +30,7 @@ public func secureIdPreparePhoneVerification(network: Network, value: SecureIdPh
|
||||
switch sentCode {
|
||||
case let .sentCode(_, type, phoneCodeHash, nextType, timeout):
|
||||
return .single(SecureIdPreparePhoneVerificationPayload(type: SentAuthorizationCodeType(apiType: type), nextType: nextType.flatMap(AuthorizationCodeNextType.init), timeout: timeout, phone: value.phone, phoneCodeHash: phoneCodeHash))
|
||||
case .sentCodeSuccess:
|
||||
case .sentCodeSuccess, .sentCodePaymentRequired:
|
||||
return .never()
|
||||
}
|
||||
}
|
||||
|
@ -107,6 +107,14 @@ public final class TelegramEngineUnauthorized {
|
||||
public lazy var localization: Localization = {
|
||||
return Localization(account: self.account)
|
||||
}()
|
||||
|
||||
public lazy var payments: Payments = {
|
||||
return Payments(account: self.account)
|
||||
}()
|
||||
|
||||
public lazy var itemCache: ItemCache = {
|
||||
return ItemCache(account: self.account)
|
||||
}()
|
||||
}
|
||||
|
||||
public enum SomeTelegramEngine {
|
||||
|
@ -81,7 +81,7 @@ public func formatTonAmountText(_ value: Int64, dateTimeFormat: PresentationDate
|
||||
|
||||
public func formatStarsAmountText(_ amount: StarsAmount, dateTimeFormat: PresentationDateTimeFormat, showPlus: Bool = false) -> String {
|
||||
var balanceText = presentationStringsFormattedNumber(Int32(amount.value), dateTimeFormat.groupingSeparator)
|
||||
let fraction = Double(amount.nanos) / 10e6
|
||||
let fraction = abs(Double(amount.nanos)) / 10e6
|
||||
if fraction > 0.0 {
|
||||
balanceText.append(dateTimeFormat.decimalSeparator)
|
||||
balanceText.append("\(Int32(fraction))")
|
||||
|
@ -466,6 +466,7 @@ swift_library(
|
||||
"//submodules/TelegramUI/Components/ContentReportScreen",
|
||||
"//submodules/TelegramUI/Components/PeerInfo/AffiliateProgramSetupScreen",
|
||||
"//submodules/TelegramUI/Components/Stars/StarsBalanceOverlayComponent",
|
||||
"//submodules/TelegramUI/Components/Settings/AccountFreezeInfoScreen",
|
||||
"//third-party/recaptcha:RecaptchaEnterprise",
|
||||
] + select({
|
||||
"@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets,
|
||||
|
@ -193,6 +193,7 @@ private final class ScrollContent: CombinedComponent {
|
||||
adsText = strings.AdsInfo_Bot_Ads_Text
|
||||
infoRawText = strings.AdsInfo_Bot_Launch_Text
|
||||
case .search:
|
||||
//TODO:localize
|
||||
respectText = "Ads like this do not use your personal information and are based on the search query you entered."
|
||||
adsText = strings.AdsInfo_Bot_Ads_Text
|
||||
infoRawText = "Anyone can create an ad to display in search results for any query. Check out the Telegram Ad Platform for details. [Learn More >]()"
|
||||
|
@ -1787,7 +1787,7 @@ public final class ChatEmptyNode: ASDisplayNode {
|
||||
isScheduledMessages = true
|
||||
}
|
||||
|
||||
let contentType: ChatEmptyNodeContentType
|
||||
var contentType: ChatEmptyNodeContentType
|
||||
var displayAttachedDescription = false
|
||||
switch subject {
|
||||
case .detailsPlaceholder:
|
||||
@ -1815,7 +1815,7 @@ public final class ChatEmptyNode: ASDisplayNode {
|
||||
} else if let _ = interfaceState.peerNearbyData {
|
||||
contentType = .peerNearby
|
||||
} else if let peer = peer as? TelegramUser {
|
||||
if let _ = interfaceState.sendPaidMessageStars {
|
||||
if let _ = interfaceState.sendPaidMessageStars, interfaceState.businessIntro == nil {
|
||||
contentType = .starsRequired
|
||||
} else if interfaceState.isPremiumRequiredForMessaging {
|
||||
contentType = .premiumRequired
|
||||
|
@ -687,6 +687,8 @@ public final class ChatInlineSearchResultsListComponent: Component {
|
||||
openPhotoSetup: {
|
||||
},
|
||||
openAdInfo: { _ in
|
||||
},
|
||||
openAccountFreezeInfo: {
|
||||
}
|
||||
)
|
||||
self.chatListNodeInteraction = chatListNodeInteraction
|
||||
|
@ -299,7 +299,7 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([
|
||||
}
|
||||
|
||||
if isMediaInverted {
|
||||
result.insert((message, ChatMessageTextBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: isFile ? .condensed : .default)), at: 0)
|
||||
result.insert((message, ChatMessageTextBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: isFile ? .condensed : .default)), at: addedPriceInfo ? 1 : 0)
|
||||
} else {
|
||||
result.append((message, ChatMessageTextBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: isFile ? .condensed : .default)))
|
||||
needReactions = false
|
||||
@ -327,7 +327,7 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([
|
||||
}
|
||||
|
||||
if let attribute = message.attributes.first(where: { $0 is WebpagePreviewMessageAttribute }) as? WebpagePreviewMessageAttribute, attribute.leadingPreview {
|
||||
result.insert((message, ChatMessageWebpageBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default)), at: 0)
|
||||
result.insert((message, ChatMessageWebpageBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default)), at: addedPriceInfo ? 1 : 0)
|
||||
} else {
|
||||
result.append((message, ChatMessageWebpageBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default)))
|
||||
}
|
||||
@ -362,7 +362,7 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([
|
||||
if result.isEmpty {
|
||||
needReactions = false
|
||||
}
|
||||
result.insert((messageWithCaptionToAdd, ChatMessageTextBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default)), at: 0)
|
||||
result.insert((messageWithCaptionToAdd, ChatMessageTextBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default)), at: addedPriceInfo ? 1 : 0)
|
||||
} else {
|
||||
result.append((messageWithCaptionToAdd, ChatMessageTextBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default)))
|
||||
needReactions = false
|
||||
|
@ -393,7 +393,7 @@ public final class ChatUserInfoItemNode: ListViewItemNode, ASGestureRecognizerDe
|
||||
|
||||
let disclaimerText: NSMutableAttributedString
|
||||
if let verification = item.verification {
|
||||
disclaimerText = NSMutableAttributedString(string: " # \(verification.description)", font: Font.regular(13.0), textColor: subtitleColor)
|
||||
disclaimerText = NSMutableAttributedString(string: " # \(verification.description)", font: Font.regular(13.0), textColor: subtitleColor)
|
||||
if let range = disclaimerText.string.range(of: "#") {
|
||||
disclaimerText.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: verification.iconFileId, file: nil), range: NSRange(range, in: disclaimerText.string))
|
||||
disclaimerText.addAttribute(.foregroundColor, value: subtitleColor, range: NSRange(range, in: disclaimerText.string))
|
||||
|
@ -1237,6 +1237,11 @@ final class GiftOptionsScreenComponent: Component {
|
||||
if availableProducts.isEmpty {
|
||||
var premiumProducts: [PremiumGiftProduct] = []
|
||||
for option in premiumOptions {
|
||||
if option.currency == "XTR" {
|
||||
continue
|
||||
}
|
||||
let starsGiftOption = premiumOptions.first(where: { $0.currency == "XTR" && $0.months == option.months })
|
||||
|
||||
premiumProducts.append(
|
||||
PremiumGiftProduct(
|
||||
giftOption: CachedPremiumGiftOption(
|
||||
@ -1246,7 +1251,7 @@ final class GiftOptionsScreenComponent: Component {
|
||||
botUrl: "",
|
||||
storeProductId: option.storeProductId
|
||||
),
|
||||
starsGiftOption: nil,
|
||||
starsGiftOption: starsGiftOption,
|
||||
storeProduct: nil,
|
||||
discount: nil
|
||||
)
|
||||
|
@ -498,16 +498,8 @@ final class GiftSetupScreenComponent: Component {
|
||||
|
||||
starsContext.add(balance: StarsAmount(value: stars, nanos: 0))
|
||||
|
||||
let _ = (starsContext.state
|
||||
|> take(until: { value in
|
||||
if let value {
|
||||
if !value.flags.contains(.isPendingBalance) {
|
||||
return SignalTakeAction(passthrough: true, complete: true)
|
||||
}
|
||||
}
|
||||
return SignalTakeAction(passthrough: false, complete: false)
|
||||
})
|
||||
|> deliverOnMainQueue).start(next: { _ in
|
||||
let _ = (starsContext.onUpdate
|
||||
|> deliverOnMainQueue).start(next: {
|
||||
proceed()
|
||||
})
|
||||
}
|
||||
|
@ -383,10 +383,14 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
options: options ?? [],
|
||||
purpose: .upgradeStarGift(requiredStars: price),
|
||||
completion: { [weak starsContext] stars in
|
||||
starsContext?.add(balance: StarsAmount(value: stars, nanos: 0))
|
||||
Queue.mainQueue().after(2.0) {
|
||||
proceed(upgradeForm.id)
|
||||
guard let starsContext else {
|
||||
return
|
||||
}
|
||||
starsContext.add(balance: StarsAmount(value: stars, nanos: 0))
|
||||
let _ = (starsContext.onUpdate
|
||||
|> deliverOnMainQueue).start(next: {
|
||||
proceed(upgradeForm.id)
|
||||
})
|
||||
}
|
||||
)
|
||||
controller.push(purchaseController)
|
||||
|
@ -188,6 +188,7 @@ public final class LoadingOverlayNode: ASDisplayNode {
|
||||
}, openWebApp: { _ in
|
||||
}, openPhotoSetup: {
|
||||
}, openAdInfo: { _ in
|
||||
}, openAccountFreezeInfo: {
|
||||
})
|
||||
|
||||
let items = (0 ..< 1).map { _ -> ChatListItem in
|
||||
@ -548,6 +549,8 @@ private final class PeerInfoScreenPersonalChannelItemNode: PeerInfoScreenItemNod
|
||||
openPhotoSetup: {
|
||||
},
|
||||
openAdInfo: { _ in
|
||||
},
|
||||
openAccountFreezeInfo: {
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -998,7 +998,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
|
||||
})))
|
||||
}
|
||||
|
||||
if canReorder {
|
||||
if case .unique = gift.gift, canReorder {
|
||||
items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Context_Reorder, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReorderItems"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, f in
|
||||
c?.dismiss(completion: { [weak self] in
|
||||
guard let self else {
|
||||
|
@ -0,0 +1,36 @@
|
||||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "AccountFreezeInfoScreen",
|
||||
module_name = "AccountFreezeInfoScreen",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
copts = [
|
||||
"-warnings-as-errors",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/AsyncDisplayKit",
|
||||
"//submodules/Display",
|
||||
"//submodules/Postbox",
|
||||
"//submodules/TelegramCore",
|
||||
"//submodules/SSignalKit/SwiftSignalKit",
|
||||
"//submodules/ComponentFlow",
|
||||
"//submodules/Components/ViewControllerComponent",
|
||||
"//submodules/Components/ComponentDisplayAdapters",
|
||||
"//submodules/Components/MultilineTextComponent",
|
||||
"//submodules/TelegramPresentationData",
|
||||
"//submodules/AccountContext",
|
||||
"//submodules/AppBundle",
|
||||
"//submodules/Components/SheetComponent",
|
||||
"//submodules/PresentationDataUtils",
|
||||
"//submodules/Components/BundleIconComponent",
|
||||
"//submodules/TelegramUI/Components/ButtonComponent",
|
||||
"//submodules/Components/BalancedTextComponent",
|
||||
"//submodules/TelegramUI/Components/LottieComponent",
|
||||
"//submodules/Markdown",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
@ -0,0 +1,578 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import ComponentFlow
|
||||
import SwiftSignalKit
|
||||
import TelegramCore
|
||||
import Markdown
|
||||
import TextFormat
|
||||
import TelegramPresentationData
|
||||
import TelegramStringFormatting
|
||||
import ViewControllerComponent
|
||||
import SheetComponent
|
||||
import BundleIconComponent
|
||||
import BalancedTextComponent
|
||||
import MultilineTextComponent
|
||||
import LottieComponent
|
||||
import ButtonComponent
|
||||
import AccountContext
|
||||
|
||||
private final class SheetContent: CombinedComponent {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
||||
let context: AccountContext
|
||||
let configuration: AccountFreezeConfiguration
|
||||
let submitAppeal: () -> Void
|
||||
let dismiss: () -> Void
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
configuration: AccountFreezeConfiguration,
|
||||
submitAppeal: @escaping () -> Void,
|
||||
dismiss: @escaping () -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.configuration = configuration
|
||||
self.submitAppeal = submitAppeal
|
||||
self.dismiss = dismiss
|
||||
}
|
||||
|
||||
static func ==(lhs: SheetContent, rhs: SheetContent) -> Bool {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class State: ComponentState {
|
||||
var cachedChevronImage: (UIImage, PresentationTheme)?
|
||||
var cachedCloseImage: (UIImage, PresentationTheme)?
|
||||
|
||||
let playOnce = ActionSlot<Void>()
|
||||
private var didPlayAnimation = false
|
||||
|
||||
func playAnimationIfNeeded() {
|
||||
guard !self.didPlayAnimation else {
|
||||
return
|
||||
}
|
||||
self.didPlayAnimation = true
|
||||
self.playOnce.invoke(Void())
|
||||
}
|
||||
}
|
||||
|
||||
func makeState() -> State {
|
||||
return State()
|
||||
}
|
||||
|
||||
static var body: Body {
|
||||
let animation = Child(LottieComponent.self)
|
||||
|
||||
let title = Child(BalancedTextComponent.self)
|
||||
let list = Child(List<Empty>.self)
|
||||
let actionButton = Child(ButtonComponent.self)
|
||||
let closeButton = Child(ButtonComponent.self)
|
||||
|
||||
return { context in
|
||||
let environment = context.environment[EnvironmentType.self]
|
||||
let component = context.component
|
||||
let state = context.state
|
||||
|
||||
let theme = environment.theme
|
||||
let strings = environment.strings
|
||||
|
||||
let sideInset: CGFloat = 16.0 + environment.safeInsets.left
|
||||
let textSideInset: CGFloat = 30.0 + environment.safeInsets.left
|
||||
|
||||
let titleFont = Font.semibold(20.0)
|
||||
|
||||
let textColor = theme.actionSheet.primaryTextColor
|
||||
let secondaryTextColor = theme.actionSheet.secondaryTextColor
|
||||
let linkColor = theme.actionSheet.controlAccentColor
|
||||
|
||||
let spacing: CGFloat = 16.0
|
||||
var contentSize = CGSize(width: context.availableSize.width, height: 32.0)
|
||||
|
||||
let animationHeight: CGFloat = 120.0
|
||||
let animation = animation.update(
|
||||
component: LottieComponent(
|
||||
content: LottieComponent.AppBundleContent(name: "Banned"),
|
||||
startingPosition: .begin,
|
||||
playOnce: state.playOnce
|
||||
),
|
||||
environment: {},
|
||||
availableSize: CGSize(width: animationHeight, height: animationHeight),
|
||||
transition: .immediate
|
||||
)
|
||||
context.add(animation
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + animation.size.height / 2.0))
|
||||
)
|
||||
contentSize.height += animation.size.height
|
||||
contentSize.height += spacing + 5.0
|
||||
|
||||
let title = title.update(
|
||||
component: BalancedTextComponent(
|
||||
text: .plain(NSAttributedString(string: "Your Account is Frozen", font: titleFont, textColor: textColor)),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 0,
|
||||
lineSpacing: 0.1
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - textSideInset * 2.0, height: context.availableSize.height),
|
||||
transition: .immediate
|
||||
)
|
||||
context.add(title
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + title.size.height / 2.0))
|
||||
)
|
||||
contentSize.height += title.size.height
|
||||
contentSize.height += spacing - 2.0
|
||||
|
||||
//TODO:localize
|
||||
var items: [AnyComponentWithIdentity<Empty>] = []
|
||||
items.append(
|
||||
AnyComponentWithIdentity(
|
||||
id: "ads",
|
||||
component: AnyComponent(ParagraphComponent(
|
||||
title: "Violation of Terms",
|
||||
titleColor: textColor,
|
||||
text: "Your account was frozen for breaking Telegram's Terms and Conditions.",
|
||||
textColor: secondaryTextColor,
|
||||
iconName: "Account Freeze/Violation",
|
||||
iconColor: linkColor
|
||||
))
|
||||
)
|
||||
)
|
||||
items.append(
|
||||
AnyComponentWithIdentity(
|
||||
id: "split",
|
||||
component: AnyComponent(ParagraphComponent(
|
||||
title: "Read-Only Mode",
|
||||
titleColor: textColor,
|
||||
text: "You can access your account but can't send messages or take actions.",
|
||||
textColor: secondaryTextColor,
|
||||
iconName: "Ads/Privacy",
|
||||
iconColor: linkColor
|
||||
))
|
||||
)
|
||||
)
|
||||
let dateString = stringForFullDate(timestamp: component.configuration.freezeUntilDate ?? 0, strings: strings, dateTimeFormat: environment.dateTimeFormat)
|
||||
items.append(
|
||||
AnyComponentWithIdentity(
|
||||
id: "withdrawal",
|
||||
component: AnyComponent(ParagraphComponent(
|
||||
title: "Appeal Before Deactivation",
|
||||
titleColor: textColor,
|
||||
text: "Appeal via [@SpamBot]() before \(dateString), or your account will be deleted.",
|
||||
textColor: secondaryTextColor,
|
||||
iconName: "Account Freeze/Appeal",
|
||||
iconColor: linkColor,
|
||||
action: {
|
||||
component.submitAppeal()
|
||||
Queue.mainQueue().after(1.0) {
|
||||
component.dismiss()
|
||||
}
|
||||
}
|
||||
))
|
||||
)
|
||||
)
|
||||
|
||||
let list = list.update(
|
||||
component: List(items),
|
||||
availableSize: CGSize(width: context.availableSize.width - sideInset, height: 10000.0),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(list
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + list.size.height / 2.0))
|
||||
)
|
||||
contentSize.height += list.size.height
|
||||
contentSize.height += spacing + 2.0
|
||||
|
||||
let buttonAttributedString = NSMutableAttributedString(string: "Submit an Appeal", font: Font.semibold(17.0), textColor: environment.theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center)
|
||||
let actionButton = actionButton.update(
|
||||
component: ButtonComponent(
|
||||
background: ButtonComponent.Background(
|
||||
color: environment.theme.list.itemCheckColors.fillColor,
|
||||
foreground: environment.theme.list.itemCheckColors.foregroundColor,
|
||||
pressedColor: environment.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9),
|
||||
cornerRadius: 10.0
|
||||
),
|
||||
content: AnyComponentWithIdentity(
|
||||
id: AnyHashable(0),
|
||||
component: AnyComponent(MultilineTextComponent(text: .plain(buttonAttributedString)))
|
||||
),
|
||||
isEnabled: true,
|
||||
displaysProgress: false,
|
||||
action: {
|
||||
component.submitAppeal()
|
||||
Queue.mainQueue().after(1.0) {
|
||||
component.dismiss()
|
||||
}
|
||||
}
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(actionButton
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + actionButton.size.height / 2.0))
|
||||
.cornerRadius(10.0)
|
||||
)
|
||||
contentSize.height += actionButton.size.height
|
||||
contentSize.height += 8.0
|
||||
|
||||
let closeAttributedString = NSMutableAttributedString(string: "Understood", font: Font.regular(17.0), textColor: environment.theme.list.itemCheckColors.fillColor, paragraphAlignment: .center)
|
||||
let closeButton = closeButton.update(
|
||||
component: ButtonComponent(
|
||||
background: ButtonComponent.Background(
|
||||
color: .clear,
|
||||
foreground: .clear,
|
||||
pressedColor: .clear,
|
||||
cornerRadius: 10.0
|
||||
),
|
||||
content: AnyComponentWithIdentity(
|
||||
id: AnyHashable(1),
|
||||
component: AnyComponent(MultilineTextComponent(text: .plain(closeAttributedString)))
|
||||
),
|
||||
isEnabled: true,
|
||||
displaysProgress: false,
|
||||
action: {
|
||||
component.dismiss()
|
||||
}
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(closeButton
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + actionButton.size.height / 2.0))
|
||||
)
|
||||
contentSize.height += closeButton.size.height
|
||||
|
||||
if environment.safeInsets.bottom > 0 {
|
||||
contentSize.height += environment.safeInsets.bottom + 5.0
|
||||
} else {
|
||||
contentSize.height += 12.0
|
||||
}
|
||||
|
||||
state.playAnimationIfNeeded()
|
||||
|
||||
return contentSize
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class SheetContainerComponent: CombinedComponent {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
||||
let context: AccountContext
|
||||
let configuration: AccountFreezeConfiguration
|
||||
let submitAppeal: () -> Void
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
configuration: AccountFreezeConfiguration,
|
||||
submitAppeal: @escaping () -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.configuration = configuration
|
||||
self.submitAppeal = submitAppeal
|
||||
}
|
||||
|
||||
static func ==(lhs: SheetContainerComponent, rhs: SheetContainerComponent) -> Bool {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
static var body: Body {
|
||||
let sheet = Child(SheetComponent<EnvironmentType>.self)
|
||||
let animateOut = StoredActionSlot(Action<Void>.self)
|
||||
|
||||
let sheetExternalState = SheetComponent<EnvironmentType>.ExternalState()
|
||||
|
||||
return { context in
|
||||
let environment = context.environment[EnvironmentType.self]
|
||||
|
||||
let controller = environment.controller
|
||||
|
||||
let sheet = sheet.update(
|
||||
component: SheetComponent<EnvironmentType>(
|
||||
content: AnyComponent<EnvironmentType>(SheetContent(
|
||||
context: context.component.context,
|
||||
configuration: context.component.configuration,
|
||||
submitAppeal: context.component.submitAppeal,
|
||||
dismiss: {
|
||||
animateOut.invoke(Action { _ in
|
||||
if let controller = controller() {
|
||||
controller.dismiss(completion: nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
)),
|
||||
backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor),
|
||||
followContentSizeChanges: true,
|
||||
externalState: sheetExternalState,
|
||||
animateOut: animateOut
|
||||
),
|
||||
environment: {
|
||||
environment
|
||||
SheetComponentEnvironment(
|
||||
isDisplaying: environment.value.isVisible,
|
||||
isCentered: environment.metrics.widthClass == .regular,
|
||||
hasInputHeight: !environment.inputHeight.isZero,
|
||||
regularMetricsSize: CGSize(width: 430.0, height: 900.0),
|
||||
dismiss: { animated in
|
||||
if animated {
|
||||
animateOut.invoke(Action { _ in
|
||||
if let controller = controller() {
|
||||
controller.dismiss(completion: nil)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
if let controller = controller() {
|
||||
controller.dismiss(completion: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
availableSize: context.availableSize,
|
||||
transition: context.transition
|
||||
)
|
||||
|
||||
context.add(sheet
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0))
|
||||
)
|
||||
|
||||
if let controller = controller(), !controller.automaticallyControlPresentationContextLayout {
|
||||
let layout = ContainerViewLayout(
|
||||
size: context.availableSize,
|
||||
metrics: environment.metrics,
|
||||
deviceMetrics: environment.deviceMetrics,
|
||||
intrinsicInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: max(environment.safeInsets.bottom, sheetExternalState.contentHeight), right: 0.0),
|
||||
safeInsets: UIEdgeInsets(top: 0.0, left: environment.safeInsets.left, bottom: 0.0, right: environment.safeInsets.right),
|
||||
additionalInsets: .zero,
|
||||
statusBarHeight: environment.statusBarHeight,
|
||||
inputHeight: nil,
|
||||
inputHeightIsInteractivellyChanging: false,
|
||||
inVoiceOver: false
|
||||
)
|
||||
controller.presentationContext.containerLayoutUpdated(layout, transition: context.transition.containedViewLayoutTransition)
|
||||
}
|
||||
|
||||
return context.availableSize
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public final class AccountFreezeInfoScreen: ViewControllerComponentContainer {
|
||||
private let context: AccountContext
|
||||
|
||||
public init(
|
||||
context: AccountContext
|
||||
) {
|
||||
self.context = context
|
||||
|
||||
let configuration = AccountFreezeConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
||||
var submitAppealImpl: (() -> Void)?
|
||||
super.init(
|
||||
context: context,
|
||||
component: SheetContainerComponent(
|
||||
context: context,
|
||||
configuration: configuration,
|
||||
submitAppeal: {
|
||||
submitAppealImpl?()
|
||||
}
|
||||
),
|
||||
navigationBarAppearance: .none,
|
||||
statusBarStyle: .ignore,
|
||||
theme: .default
|
||||
)
|
||||
|
||||
self.navigationPresentation = .flatModal
|
||||
|
||||
submitAppealImpl = { [weak self] in
|
||||
guard let self, let url = configuration.freezeAppealUrl else {
|
||||
return
|
||||
}
|
||||
context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: url, forceExternal: false, presentationData: context.sharedContext.currentPresentationData.with { $0 }, navigationController: self.navigationController as? NavigationController, dismissInput: {})
|
||||
}
|
||||
}
|
||||
|
||||
required public init(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override public func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
self.view.disablesInteractiveModalDismiss = true
|
||||
}
|
||||
|
||||
public func dismissAnimated() {
|
||||
if let view = self.node.hostView.findTaggedView(tag: SheetComponent<ViewControllerComponentContainer.Environment>.View.Tag()) as? SheetComponent<ViewControllerComponentContainer.Environment>.View {
|
||||
view.dismissAnimated()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class ParagraphComponent: CombinedComponent {
|
||||
let title: String
|
||||
let titleColor: UIColor
|
||||
let text: String
|
||||
let textColor: UIColor
|
||||
let iconName: String
|
||||
let iconColor: UIColor
|
||||
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
|
||||
}
|
||||
|
||||
static var body: Body {
|
||||
let title = Child(MultilineTextComponent.self)
|
||||
let text = Child(MultilineTextComponent.self)
|
||||
let icon = Child(BundleIconComponent.self)
|
||||
|
||||
return { context in
|
||||
let component = context.component
|
||||
|
||||
let leftInset: CGFloat = 64.0
|
||||
let rightInset: CGFloat = 32.0
|
||||
let textSideInset: CGFloat = leftInset + 8.0
|
||||
let spacing: CGFloat = 5.0
|
||||
|
||||
let textTopInset: CGFloat = 9.0
|
||||
|
||||
let title = title.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: component.title,
|
||||
font: Font.semibold(15.0),
|
||||
textColor: component.titleColor,
|
||||
paragraphAlignment: .natural
|
||||
)),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 1
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - leftInset - rightInset, height: CGFloat.greatestFiniteMagnitude),
|
||||
transition: .immediate
|
||||
)
|
||||
|
||||
let textFont = Font.regular(15.0)
|
||||
let boldTextFont = Font.semibold(15.0)
|
||||
let textColor = component.textColor
|
||||
let linkColor = component.iconColor
|
||||
let markdownAttributes = 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: markdownAttributes),
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func generateCloseButtonImage(backgroundColor: UIColor, foregroundColor: UIColor) -> UIImage? {
|
||||
return generateImage(CGSize(width: 30.0, height: 30.0), contextGenerator: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
context.setFillColor(backgroundColor.cgColor)
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
context.setLineWidth(2.0)
|
||||
context.setLineCap(.round)
|
||||
context.setStrokeColor(foregroundColor.cgColor)
|
||||
|
||||
context.move(to: CGPoint(x: 10.0, y: 10.0))
|
||||
context.addLine(to: CGPoint(x: 20.0, y: 20.0))
|
||||
context.strokePath()
|
||||
|
||||
context.move(to: CGPoint(x: 20.0, y: 10.0))
|
||||
context.addLine(to: CGPoint(x: 10.0, y: 20.0))
|
||||
context.strokePath()
|
||||
})
|
||||
}
|
@ -213,6 +213,8 @@ final class GreetingMessageListItemComponent: Component {
|
||||
openPhotoSetup: {
|
||||
},
|
||||
openAdInfo: { _ in
|
||||
},
|
||||
openAccountFreezeInfo: {
|
||||
}
|
||||
)
|
||||
self.chatListNodeInteraction = chatListNodeInteraction
|
||||
|
@ -234,6 +234,8 @@ final class QuickReplySetupScreenComponent: Component {
|
||||
openPhotoSetup: {
|
||||
},
|
||||
openAdInfo: { _ in
|
||||
},
|
||||
openAccountFreezeInfo: {
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -876,6 +876,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, ASScrollViewDelegate
|
||||
}, openWebApp: { _ in
|
||||
}, openPhotoSetup: {
|
||||
}, openAdInfo: { _ in
|
||||
}, openAccountFreezeInfo: {
|
||||
})
|
||||
let chatListPresentationData = ChatListPresentationData(theme: self.presentationData.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true)
|
||||
|
||||
|
@ -589,10 +589,15 @@ private final class SheetContent: CombinedComponent {
|
||||
options: state?.options ?? [],
|
||||
purpose: purpose,
|
||||
completion: { [weak starsContext] stars in
|
||||
starsContext?.add(balance: StarsAmount(value: stars, nanos: 0))
|
||||
Queue.mainQueue().after(0.1) {
|
||||
completion()
|
||||
guard let starsContext else {
|
||||
return
|
||||
}
|
||||
starsContext.add(balance: StarsAmount(value: stars, nanos: 0))
|
||||
|
||||
let _ = (starsContext.onUpdate
|
||||
|> deliverOnMainQueue).start(next: {
|
||||
completion()
|
||||
})
|
||||
}
|
||||
)
|
||||
controller?.push(purchaseController)
|
||||
|
12
submodules/TelegramUI/Images.xcassets/Account Freeze/Appeal.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Account Freeze/Appeal.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "hand_30.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
submodules/TelegramUI/Images.xcassets/Account Freeze/Appeal.imageset/hand_30.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Account Freeze/Appeal.imageset/hand_30.pdf
vendored
Normal file
Binary file not shown.
@ -0,0 +1,9 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"provides-namespace" : true
|
||||
}
|
||||
}
|
12
submodules/TelegramUI/Images.xcassets/Account Freeze/Violation.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Account Freeze/Violation.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "sandtimer_30.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
submodules/TelegramUI/Images.xcassets/Account Freeze/Violation.imageset/sandtimer_30.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Account Freeze/Violation.imageset/sandtimer_30.pdf
vendored
Normal file
Binary file not shown.
21
submodules/TelegramUI/Images.xcassets/Components/AdMock.imageset/Contents.json
vendored
Normal file
21
submodules/TelegramUI/Images.xcassets/Components/AdMock.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "admock.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
submodules/TelegramUI/Images.xcassets/Components/AdMock.imageset/admock.png
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Components/AdMock.imageset/admock.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.4 KiB |
12
submodules/TelegramUI/Images.xcassets/Components/PayMock.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Components/PayMock.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "MockSMS.png",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
submodules/TelegramUI/Images.xcassets/Components/PayMock.imageset/MockSMS.png
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Components/PayMock.imageset/MockSMS.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 86 KiB |
@ -294,7 +294,7 @@ public final class AccountContextImpl: AccountContext {
|
||||
self.wallpaperUploadManager = WallpaperUploadManagerImpl(sharedContext: sharedContext, account: account, presentationData: sharedContext.presentationData)
|
||||
self.themeUpdateManager = ThemeUpdateManagerImpl(sharedContext: sharedContext, account: account)
|
||||
|
||||
self.inAppPurchaseManager = InAppPurchaseManager(engine: self.engine)
|
||||
self.inAppPurchaseManager = InAppPurchaseManager(engine: .authorized(self.engine))
|
||||
self.starsContext = self.engine.payments.peerStarsContext()
|
||||
} else {
|
||||
self.prefetchManager = nil
|
||||
@ -356,8 +356,9 @@ public final class AccountContextImpl: AccountContext {
|
||||
|
||||
self.currentCountriesConfiguration = Atomic(value: CountriesConfiguration(countries: loadCountryCodes()))
|
||||
if !temp {
|
||||
let langCode = sharedContext.currentPresentationData.with { $0 }.strings.baseLanguageCode
|
||||
let currentCountriesConfiguration = self.currentCountriesConfiguration
|
||||
self.countriesConfigurationDisposable = (self.engine.localization.getCountriesList(accountManager: sharedContext.accountManager, langCode: nil)
|
||||
self.countriesConfigurationDisposable = (self.engine.localization.getCountriesList(accountManager: sharedContext.accountManager, langCode: langCode)
|
||||
|> deliverOnMainQueue).start(next: { value in
|
||||
let _ = currentCountriesConfiguration.swap(CountriesConfiguration(countries: value))
|
||||
})
|
||||
|
@ -4412,7 +4412,18 @@ extension ChatControllerImpl {
|
||||
}, openMessagePayment: {
|
||||
|
||||
}, openBoostToUnrestrict: { [weak self] in
|
||||
guard let self, let peerId = self.chatLocation.peerId, let cachedData = self.peerView?.cachedData as? CachedChannelData, let boostToUnrestrict = cachedData.boostsToUnrestrict else {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
let accountFreezeConfiguration = AccountFreezeConfiguration.with(appConfiguration: self.context.currentAppConfiguration.with { $0 })
|
||||
if let _ = accountFreezeConfiguration.freezeUntilDate {
|
||||
let controller = self.context.sharedContext.makeAccountFreezeInfoScreen(context: self.context)
|
||||
self.push(controller)
|
||||
return
|
||||
}
|
||||
|
||||
guard let peerId = self.chatLocation.peerId, let cachedData = self.peerView?.cachedData as? CachedChannelData, let boostToUnrestrict = cachedData.boostsToUnrestrict else {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -72,7 +72,10 @@ extension ChatControllerImpl {
|
||||
return
|
||||
}
|
||||
let controller = self.context.sharedContext.makeStarsPurchaseScreen(context: self.context, starsContext: starsContext, options: options, purpose: .sendMessage(peerId: peer.id, requiredStars: totalAmount), completion: { _ in
|
||||
completion(false)
|
||||
let _ = (starsContext.onUpdate
|
||||
|> deliverOnMainQueue).start(next: {
|
||||
completion(false)
|
||||
})
|
||||
})
|
||||
self.push(controller)
|
||||
})
|
||||
|
@ -21,6 +21,18 @@ func inputPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceState
|
||||
return (nil, nil)
|
||||
}
|
||||
|
||||
let accountFreezeConfiguration = AccountFreezeConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
||||
if let _ = accountFreezeConfiguration.freezeUntilDate {
|
||||
if let currentPanel = (currentPanel as? ChatRestrictedInputPanelNode) ?? (currentSecondaryPanel as? ChatRestrictedInputPanelNode) {
|
||||
return (currentPanel, nil)
|
||||
} else {
|
||||
let panel = ChatRestrictedInputPanelNode()
|
||||
panel.context = context
|
||||
panel.interfaceInteraction = interfaceInteraction
|
||||
return (panel, nil)
|
||||
}
|
||||
}
|
||||
|
||||
if let _ = chatPresentationInterfaceState.search {
|
||||
var selectionPanel: ChatMessageSelectionInputPanelNode?
|
||||
if let selectionState = chatPresentationInterfaceState.interfaceState.selectionState {
|
||||
|
@ -583,11 +583,7 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
}
|
||||
}
|
||||
|
||||
/*#if DEBUG
|
||||
emojiStatus = PeerEmojiStatus(fileId: 5062172592505356289, expirationDate: nil)
|
||||
#endif*/
|
||||
|
||||
if let emojiStatus = emojiStatus {
|
||||
if let emojiStatus = emojiStatus, case .emoji = emojiStatus.content {
|
||||
self.emojiSeparatorNode.isHidden = false
|
||||
|
||||
transition.updateFrame(node: self.emojiSeparatorNode, frame: CGRect(origin: CGPoint(x: leftInset + 12.0, y: 40.0), size: CGSize(width: width - leftInset - rightInset - 24.0, height: UIScreenPixel)))
|
||||
|
@ -9,10 +9,12 @@ import TelegramStringFormatting
|
||||
import ChatPresentationInterfaceState
|
||||
import TelegramPresentationData
|
||||
import ChatInputPanelNode
|
||||
import AccountContext
|
||||
|
||||
final class ChatRestrictedInputPanelNode: ChatInputPanelNode {
|
||||
private let buttonNode: HighlightTrackingButtonNode
|
||||
private let textNode: ImmediateTextNode
|
||||
private let subtitleNode: ImmediateTextNode
|
||||
private var iconView: UIImageView?
|
||||
|
||||
private var presentationInterfaceState: ChatPresentationInterfaceState?
|
||||
@ -22,12 +24,17 @@ final class ChatRestrictedInputPanelNode: ChatInputPanelNode {
|
||||
self.textNode.maximumNumberOfLines = 2
|
||||
self.textNode.textAlignment = .center
|
||||
|
||||
self.subtitleNode = ImmediateTextNode()
|
||||
self.subtitleNode.maximumNumberOfLines = 1
|
||||
self.subtitleNode.textAlignment = .center
|
||||
|
||||
self.buttonNode = HighlightTrackingButtonNode()
|
||||
self.buttonNode.isUserInteractionEnabled = false
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.textNode)
|
||||
self.addSubnode(self.subtitleNode)
|
||||
self.addSubnode(self.buttonNode)
|
||||
|
||||
self.buttonNode.highligthedChanged = { [weak self] highlighted in
|
||||
@ -37,11 +44,15 @@ final class ChatRestrictedInputPanelNode: ChatInputPanelNode {
|
||||
self.iconView?.alpha = 0.4
|
||||
self.textNode.layer.removeAnimation(forKey: "opacity")
|
||||
self.textNode.alpha = 0.4
|
||||
self.subtitleNode.layer.removeAnimation(forKey: "opacity")
|
||||
self.subtitleNode.alpha = 0.4
|
||||
} else {
|
||||
self.iconView?.alpha = 1.0
|
||||
self.iconView?.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
|
||||
self.textNode.alpha = 1.0
|
||||
self.textNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
|
||||
self.subtitleNode.alpha = 1.0
|
||||
self.subtitleNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -74,7 +85,16 @@ final class ChatRestrictedInputPanelNode: ChatInputPanelNode {
|
||||
var iconSpacing: CGFloat = 4.0
|
||||
var isUserInteractionEnabled = false
|
||||
|
||||
if case let .replyThread(message) = interfaceState.chatLocation, message.peerId == self.context?.account.peerId {
|
||||
var accountFreezeConfiguration: AccountFreezeConfiguration?
|
||||
if let context = self.context {
|
||||
accountFreezeConfiguration = AccountFreezeConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
||||
}
|
||||
//TODO:localize
|
||||
if let _ = accountFreezeConfiguration?.freezeUntilDate {
|
||||
self.textNode.attributedText = NSAttributedString(string: "You account is frozen", font: Font.semibold(15.0), textColor: interfaceState.theme.list.itemDestructiveColor)
|
||||
self.subtitleNode.attributedText = NSAttributedString(string: "Tap to view details", font: Font.regular(13.0), textColor: interfaceState.theme.chat.inputPanel.secondaryTextColor)
|
||||
isUserInteractionEnabled = true
|
||||
} else if case let .replyThread(message) = interfaceState.chatLocation, message.peerId == self.context?.account.peerId {
|
||||
self.textNode.attributedText = NSAttributedString(string: interfaceState.strings.Chat_PanelStatusAuthorHidden, font: Font.regular(13.0), textColor: interfaceState.theme.chat.inputPanel.secondaryTextColor)
|
||||
} else if let threadData = interfaceState.threadData, threadData.isClosed {
|
||||
iconImage = PresentationResourcesChat.chatPanelLockIcon(interfaceState.theme)
|
||||
@ -116,6 +136,7 @@ final class ChatRestrictedInputPanelNode: ChatInputPanelNode {
|
||||
|
||||
let panelHeight = defaultHeight(metrics: metrics)
|
||||
let textSize = self.textNode.updateLayout(CGSize(width: width - leftInset - rightInset - 8.0 * 2.0, height: panelHeight))
|
||||
let subtitleSize = self.subtitleNode.updateLayout(CGSize(width: width - leftInset - rightInset - 8.0 * 2.0, height: panelHeight))
|
||||
|
||||
var originX: CGFloat = leftInset + floor((width - leftInset - rightInset - textSize.width) / 2.0)
|
||||
|
||||
@ -139,10 +160,18 @@ final class ChatRestrictedInputPanelNode: ChatInputPanelNode {
|
||||
iconView.removeFromSuperview()
|
||||
}
|
||||
|
||||
let textFrame = CGRect(origin: CGPoint(x: originX, y: floor((panelHeight - textSize.height) / 2.0)), size: textSize)
|
||||
var combinedHeight: CGFloat = textSize.height
|
||||
if subtitleSize.height > 0.0 {
|
||||
combinedHeight += subtitleSize.height + 2.0
|
||||
}
|
||||
let textFrame = CGRect(origin: CGPoint(x: originX, y: floor((panelHeight - combinedHeight) / 2.0)), size: textSize)
|
||||
self.textNode.frame = textFrame
|
||||
|
||||
self.buttonNode.frame = textFrame.insetBy(dx: -8.0, dy: -12.0)
|
||||
let subtitleFrame = CGRect(origin: CGPoint(x: leftInset + floor((width - leftInset - rightInset - subtitleSize.width) / 2.0), y: floor((panelHeight + combinedHeight) / 2.0) - subtitleSize.height), size: subtitleSize)
|
||||
self.subtitleNode.frame = subtitleFrame
|
||||
|
||||
let combinedFrame = textFrame.union(subtitleFrame)
|
||||
self.buttonNode.frame = combinedFrame.insetBy(dx: -8.0, dy: -12.0)
|
||||
|
||||
return panelHeight
|
||||
}
|
||||
|
@ -296,6 +296,7 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, ASScrollViewDe
|
||||
}, openWebApp: { _ in
|
||||
}, openPhotoSetup: {
|
||||
}, openAdInfo: { _ in
|
||||
}, openAccountFreezeInfo: {
|
||||
})
|
||||
interaction.searchTextHighightState = searchQuery
|
||||
self.interaction = interaction
|
||||
|
@ -183,6 +183,8 @@ private struct CommandChatInputContextPanelEntry: Comparable, Identifiable {
|
||||
openPhotoSetup: {
|
||||
},
|
||||
openAdInfo: { _ in
|
||||
},
|
||||
openAccountFreezeInfo: {
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -77,6 +77,7 @@ import ContentReportScreen
|
||||
import AffiliateProgramSetupScreen
|
||||
import GalleryUI
|
||||
import ShareController
|
||||
import AccountFreezeInfoScreen
|
||||
|
||||
private final class AccountUserInterfaceInUseContext {
|
||||
let subscribers = Bag<(Bool) -> Void>()
|
||||
@ -3469,6 +3470,10 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
}
|
||||
return controller
|
||||
}
|
||||
|
||||
public func makeAccountFreezeInfoScreen(context: AccountContext) -> ViewController {
|
||||
return AccountFreezeInfoScreen(context: context)
|
||||
}
|
||||
}
|
||||
|
||||
private func peerInfoControllerImpl(context: AccountContext, updatedPresentationData: (PresentationData, Signal<PresentationData, NoError>)?, peer: Peer, mode: PeerInfoControllerMode, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, requestsContext: PeerInvitationImportersContext? = nil) -> ViewController? {
|
||||
|
Loading…
x
Reference in New Issue
Block a user