mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-08 19:10:53 +00:00
Merge commit 'debbe67e5aa0bf448b0d17d92f0a06c4541c31cf'
This commit is contained in:
commit
6541feab69
@ -7653,8 +7653,6 @@ Sorry for the inconvenience.";
|
||||
|
||||
"Premium.Terms" = "By purchasing a Premium subscription, you agree to our [Terms of Service](terms) and [Privacy Policy](privacy).";
|
||||
|
||||
"Premium.ChargeInfo" = "Next charge: %1$@ on %2$@. [Cancel](cancel).";
|
||||
|
||||
"Conversation.CopyProtectionSavingDisabledSecret" = "Saving is restricted";
|
||||
"Conversation.CopyProtectionForwardingDisabledSecret" = "Forwards are restricted";
|
||||
|
||||
@ -7668,8 +7666,6 @@ Sorry for the inconvenience.";
|
||||
|
||||
"SponsoredMessageMenu.Hide" = "Hide";
|
||||
|
||||
"ChatListFolder.MaxChatsInFolder" = "Sorry, you can't add more than %d chats to a folder.";
|
||||
|
||||
"Conversation.SaveGif" = "Save GIF";
|
||||
|
||||
"Premium.Limits.Title" = "Doubled Limits";
|
||||
@ -7705,3 +7701,7 @@ Sorry for the inconvenience.";
|
||||
"Message.AudioTranscription.ErrorTooLong" = "The audio is too long";
|
||||
|
||||
"WebApp.SelectChat" = "Select Chat";
|
||||
|
||||
"Premium.Purchase.ErrorUnknown" = "An error occurred. Please try again.";
|
||||
"Premium.Purchase.ErrorNetwork" = "Please check your internet connection and try again.";
|
||||
"Premium.Purchase.ErrorNotAllowed" = "The device is not not allowed to make the payment.";
|
||||
|
@ -680,7 +680,6 @@ public protocol SharedAccountContext: AnyObject {
|
||||
var locationManager: DeviceLocationManager? { get }
|
||||
var callManager: PresentationCallManager? { get }
|
||||
var contactDataManager: DeviceContactDataManager? { get }
|
||||
var inAppPurchaseManager: InAppPurchaseManager? { get }
|
||||
|
||||
var activeAccountContexts: Signal<(primary: AccountContext?, accounts: [(AccountRecordId, AccountContext, Int32)], currentAuth: UnauthorizedAccount?), NoError> { get }
|
||||
var activeAccountsWithInfo: Signal<(primary: AccountRecordId?, accounts: [AccountWithInfo]), NoError> { get }
|
||||
@ -861,6 +860,7 @@ public protocol AccountContext: AnyObject {
|
||||
var peerChannelMemberCategoriesContextsManager: PeerChannelMemberCategoriesContextsManager { get }
|
||||
var wallpaperUploadManager: WallpaperUploadManager? { get }
|
||||
var watchManager: WatchManager? { get }
|
||||
var inAppPurchaseManager: InAppPurchaseManager? { get }
|
||||
|
||||
var currentLimitsConfiguration: Atomic<LimitsConfiguration> { get }
|
||||
var currentContentSettings: Atomic<ContentSettings> { get }
|
||||
|
@ -114,7 +114,7 @@ public final class MultilineTextComponent: Component {
|
||||
}
|
||||
|
||||
public final class View: ImmediateTextView {
|
||||
public func update(component: MultilineTextComponent, availableSize: CGSize) -> CGSize {
|
||||
public func update(component: MultilineTextComponent, availableSize: CGSize, transition: Transition) -> CGSize {
|
||||
let attributedString: NSAttributedString
|
||||
switch component.text {
|
||||
case let .plain(string):
|
||||
@ -122,6 +122,8 @@ public final class MultilineTextComponent: Component {
|
||||
case let .markdown(text, attributes):
|
||||
attributedString = parseMarkdownIntoAttributedString(text, attributes: attributes)
|
||||
}
|
||||
|
||||
let previousText = self.attributedText?.string
|
||||
|
||||
self.attributedText = attributedString
|
||||
self.maximumNumberOfLines = component.maximumNumberOfLines
|
||||
@ -137,6 +139,18 @@ public final class MultilineTextComponent: Component {
|
||||
self.highlightAttributeAction = component.highlightAction
|
||||
self.tapAttributeAction = component.tapAction
|
||||
self.longTapAttributeAction = component.longTapAction
|
||||
|
||||
if case let .curve(duration, _) = transition.animation, let previousText = previousText, previousText != attributedString.string {
|
||||
if let snapshotView = self.snapshotView(afterScreenUpdates: false) {
|
||||
snapshotView.center = self.center
|
||||
self.superview?.addSubview(snapshotView)
|
||||
|
||||
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration, removeOnCompletion: false, completion: { [weak snapshotView] _ in
|
||||
snapshotView?.removeFromSuperview()
|
||||
})
|
||||
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration)
|
||||
}
|
||||
}
|
||||
|
||||
let size = self.updateLayout(availableSize)
|
||||
|
||||
@ -149,6 +163,6 @@ public final class MultilineTextComponent: Component {
|
||||
}
|
||||
|
||||
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize)
|
||||
return view.update(component: self, availableSize: availableSize, transition: transition)
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,8 @@ public final class InAppPurchaseManager: NSObject {
|
||||
public enum PurchaseError {
|
||||
case generic
|
||||
case cancelled
|
||||
case network
|
||||
case notAllowed
|
||||
}
|
||||
|
||||
private final class PaymentTransactionContext {
|
||||
@ -43,10 +45,11 @@ public final class InAppPurchaseManager: NSObject {
|
||||
case purchased(transactionId: String?)
|
||||
case restored(transactionId: String?)
|
||||
case purchasing
|
||||
case failed
|
||||
case failed(error: SKError?)
|
||||
case deferred
|
||||
}
|
||||
|
||||
private let engine: TelegramEngine
|
||||
private let premiumProductId: String
|
||||
|
||||
private var products: [Product] = []
|
||||
@ -56,7 +59,10 @@ public final class InAppPurchaseManager: NSObject {
|
||||
private let stateQueue = Queue()
|
||||
private var paymentContexts: [String: PaymentTransactionContext] = [:]
|
||||
|
||||
public init(premiumProductId: String) {
|
||||
private let disposableSet = DisposableDict<String>()
|
||||
|
||||
public init(engine: TelegramEngine, premiumProductId: String) {
|
||||
self.engine = engine
|
||||
self.premiumProductId = premiumProductId
|
||||
|
||||
super.init()
|
||||
@ -88,7 +94,7 @@ public final class InAppPurchaseManager: NSObject {
|
||||
}
|
||||
|
||||
public func buyProduct(_ product: Product, account: Account) -> Signal<PurchaseState, PurchaseError> {
|
||||
let payment = SKMutablePayment(product: product.skProduct)
|
||||
let payment = SKPayment(product: product.skProduct)
|
||||
SKPaymentQueue.default().add(payment)
|
||||
|
||||
let productIdentifier = payment.productIdentifier
|
||||
@ -105,8 +111,23 @@ public final class InAppPurchaseManager: NSObject {
|
||||
} else {
|
||||
subscriber.putError(.generic)
|
||||
}
|
||||
case .failed:
|
||||
subscriber.putError(.generic)
|
||||
case let .failed(error):
|
||||
if let error = error {
|
||||
let mappedError: PurchaseError
|
||||
switch error.code {
|
||||
case .paymentCancelled:
|
||||
mappedError = .cancelled
|
||||
case .cloudServiceNetworkConnectionFailed, .cloudServicePermissionDenied:
|
||||
mappedError = .network
|
||||
case .paymentNotAllowed, .clientInvalid:
|
||||
mappedError = .notAllowed
|
||||
default:
|
||||
mappedError = .generic
|
||||
}
|
||||
subscriber.putError(mappedError)
|
||||
} else {
|
||||
subscriber.putError(.generic)
|
||||
}
|
||||
case .deferred, .purchasing:
|
||||
break
|
||||
}
|
||||
@ -140,26 +161,47 @@ extension InAppPurchaseManager: SKProductsRequestDelegate {
|
||||
|
||||
extension InAppPurchaseManager: SKPaymentTransactionObserver {
|
||||
public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
|
||||
if let transaction = transactions.first {
|
||||
for transaction in transactions {
|
||||
let productIdentifier = transaction.payment.productIdentifier
|
||||
self.stateQueue.async {
|
||||
if let context = self.paymentContexts[productIdentifier] {
|
||||
let transactionState: TransactionState?
|
||||
switch transaction.transactionState {
|
||||
case .purchased:
|
||||
transactionState = .purchased(transactionId: transaction.transactionIdentifier)
|
||||
case .restored:
|
||||
transactionState = .restored(transactionId: transaction.transactionIdentifier)
|
||||
case .failed:
|
||||
transactionState = .failed
|
||||
case .purchasing:
|
||||
transactionState = .purchasing
|
||||
case .deferred:
|
||||
transactionState = .deferred
|
||||
default:
|
||||
transactionState = nil
|
||||
}
|
||||
if let transactionState = transactionState {
|
||||
let transactionState: TransactionState?
|
||||
switch transaction.transactionState {
|
||||
case .purchased:
|
||||
transactionState = .purchased(transactionId: transaction.transactionIdentifier)
|
||||
if let transactionIdentifier = transaction.transactionIdentifier {
|
||||
self.disposableSet.set(
|
||||
self.engine.payments.assignAppStoreTransaction(transactionId: transactionIdentifier).start(error: { error in
|
||||
|
||||
}, completed: {
|
||||
queue.finishTransaction(transaction)
|
||||
}),
|
||||
forKey: transaction.transactionIdentifier ?? ""
|
||||
)
|
||||
}
|
||||
case .restored:
|
||||
transactionState = .restored(transactionId: transaction.transactionIdentifier)
|
||||
if let transactionIdentifier = transaction.transactionIdentifier {
|
||||
self.disposableSet.set(
|
||||
self.engine.payments.assignAppStoreTransaction(transactionId: transactionIdentifier).start(error: { error in
|
||||
|
||||
}, completed: {
|
||||
queue.finishTransaction(transaction)
|
||||
}),
|
||||
forKey: transaction.transactionIdentifier ?? ""
|
||||
)
|
||||
}
|
||||
case .failed:
|
||||
transactionState = .failed(error: transaction.error as? SKError)
|
||||
queue.finishTransaction(transaction)
|
||||
case .purchasing:
|
||||
transactionState = .purchasing
|
||||
case .deferred:
|
||||
transactionState = .deferred
|
||||
default:
|
||||
transactionState = nil
|
||||
}
|
||||
if let transactionState = transactionState {
|
||||
if let context = self.paymentContexts[productIdentifier] {
|
||||
context.subscriber(transactionState)
|
||||
}
|
||||
}
|
||||
|
@ -894,7 +894,7 @@ private final class DemoSheetContent: CombinedComponent {
|
||||
font: .bold,
|
||||
fontSize: 17.0,
|
||||
height: 50.0,
|
||||
cornerRadius: 10.0,
|
||||
cornerRadius: 11.0,
|
||||
gloss: state.isPremium != true,
|
||||
animationName: isStandalone && component.subject == .uniqueReactions ? "premium_unlock" : nil,
|
||||
iconPosition: .right,
|
||||
|
@ -793,6 +793,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
||||
private let context: AccountContext
|
||||
|
||||
var price: String?
|
||||
var isPremium: Bool?
|
||||
|
||||
private var disposable: Disposable?
|
||||
private(set) var configuration = PremiumIntroConfiguration.defaultValue
|
||||
@ -867,6 +868,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
||||
let environment = context.environment[ViewControllerComponentContainer.Environment.self].value
|
||||
let state = context.state
|
||||
state.price = context.component.price
|
||||
state.isPremium = context.component.isPremium
|
||||
|
||||
let theme = environment.theme
|
||||
let strings = environment.strings
|
||||
@ -963,8 +965,6 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
||||
let buy = context.component.buy
|
||||
let updateIsFocused = context.component.updateIsFocused
|
||||
|
||||
let isPremium = context.component.isPremium ?? false
|
||||
|
||||
var i = 0
|
||||
for perk in state.configuration.perks {
|
||||
let iconBackgroundColors = gradientColors[i]
|
||||
@ -989,11 +989,21 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
||||
var demoSubject: PremiumDemoScreen.Subject
|
||||
switch perk {
|
||||
case .doubleLimits:
|
||||
let controller = PremimLimitsListScreen(context: accountContext, buttonText: isPremium ? strings.Common_OK : strings.Premium_SubscribeFor(state?.price ?? "–").string, isPremium: isPremium)
|
||||
var dismissImpl: (() -> Void)?
|
||||
let controller = PremimLimitsListScreen(context: accountContext, buttonText: state?.isPremium == true ? strings.Common_OK : strings.Premium_SubscribeFor(state?.price ?? "–").string, isPremium: state?.isPremium == true)
|
||||
controller.action = { [weak state] in
|
||||
dismissImpl?()
|
||||
if state?.isPremium == false {
|
||||
buy()
|
||||
}
|
||||
}
|
||||
controller.disposed = {
|
||||
updateIsFocused(false)
|
||||
}
|
||||
present(controller)
|
||||
dismissImpl = { [weak controller] in
|
||||
controller?.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
updateIsFocused(true)
|
||||
return
|
||||
case .moreUpload:
|
||||
@ -1018,15 +1028,13 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
||||
demoSubject = .appIcons
|
||||
}
|
||||
|
||||
var dismissImpl: (() -> Void)?
|
||||
let controller = PremiumDemoScreen(
|
||||
context: accountContext,
|
||||
subject: demoSubject,
|
||||
source: .intro(state?.price),
|
||||
order: state?.configuration.perks,
|
||||
action: {
|
||||
dismissImpl?()
|
||||
if !isPremium {
|
||||
if state?.isPremium == false {
|
||||
buy()
|
||||
}
|
||||
}
|
||||
@ -1035,9 +1043,6 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
||||
updateIsFocused(false)
|
||||
}
|
||||
present(controller)
|
||||
dismissImpl = { [weak controller] in
|
||||
controller?.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
updateIsFocused(true)
|
||||
|
||||
addAppLogEvent(postbox: accountContext.account.postbox, type: "premium.promo_screen_tap", data: ["item": perk.identifier])
|
||||
@ -1315,7 +1320,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
||||
|
||||
super.init()
|
||||
|
||||
if let inAppPurchaseManager = context.sharedContext.inAppPurchaseManager {
|
||||
if let inAppPurchaseManager = context.inAppPurchaseManager {
|
||||
let otherPeerName: Signal<String?, NoError>
|
||||
if case let .profile(peerId) = source {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
@ -1353,27 +1358,38 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
||||
}
|
||||
|
||||
func buy() {
|
||||
guard let inAppPurchaseManager = self.context.sharedContext.inAppPurchaseManager,
|
||||
guard let inAppPurchaseManager = self.context.inAppPurchaseManager,
|
||||
let premiumProduct = self.premiumProduct, !self.inProgress else {
|
||||
return
|
||||
}
|
||||
|
||||
addAppLogEvent(postbox: self.context.account.postbox, type: "premium.promo_screen_accept")
|
||||
|
||||
|
||||
self.inProgress = true
|
||||
self.updateInProgress(true)
|
||||
self.updated(transition: .immediate)
|
||||
|
||||
|
||||
let _ = (self.context.engine.payments.canPurchasePremium()
|
||||
|> deliverOnMainQueue).start(next: { [weak self] available in
|
||||
if let strongSelf = self {
|
||||
if available {
|
||||
strongSelf.paymentDisposable.set((inAppPurchaseManager.buyProduct(premiumProduct, account: strongSelf.context.account)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] status in
|
||||
if let strongSelf = self, case let .purchased(transactionId) = status {
|
||||
strongSelf.activationDisposable.set((strongSelf.context.engine.payments.assignAppStoreTransaction(transactionId: transactionId)
|
||||
if let strongSelf = self, case .purchased = status {
|
||||
strongSelf.activationDisposable.set((strongSelf.context.account.postbox.peerView(id: strongSelf.context.account.peerId)
|
||||
|> castError(AssignAppStoreTransactionError.self)
|
||||
|> take(until: { view in
|
||||
if let peer = view.peers[view.peerId], peer.isPremium {
|
||||
return SignalTakeAction(passthrough: false, complete: true)
|
||||
} else {
|
||||
return SignalTakeAction(passthrough: false, complete: false)
|
||||
}
|
||||
})
|
||||
|> mapToSignal { _ -> Signal<Never, AssignAppStoreTransactionError> in
|
||||
return .never()
|
||||
}
|
||||
|> deliverOnMainQueue).start(error: { _ in
|
||||
|
||||
|
||||
}, completed: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.isPremium = true
|
||||
@ -1387,13 +1403,26 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
||||
strongSelf.inProgress = false
|
||||
strongSelf.updateInProgress(false)
|
||||
strongSelf.updated(transition: .immediate)
|
||||
|
||||
|
||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||
var errorText: String?
|
||||
switch error {
|
||||
case .generic:
|
||||
addAppLogEvent(postbox: strongSelf.context.account.postbox, type: "premium.promo_screen_fail")
|
||||
errorText = presentationData.strings.Premium_Purchase_ErrorUnknown
|
||||
case .network:
|
||||
errorText = presentationData.strings.Premium_Purchase_ErrorNetwork
|
||||
case .notAllowed:
|
||||
errorText = presentationData.strings.Premium_Purchase_ErrorNotAllowed
|
||||
case .cancelled:
|
||||
break
|
||||
}
|
||||
|
||||
if let errorText = errorText {
|
||||
addAppLogEvent(postbox: strongSelf.context.account.postbox, type: "premium.promo_screen_fail")
|
||||
|
||||
let alertController = textAlertController(context: strongSelf.context, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])
|
||||
strongSelf.present(alertController)
|
||||
}
|
||||
}
|
||||
}))
|
||||
} else {
|
||||
@ -1421,7 +1450,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
||||
let star = Child(PremiumStarComponent.self)
|
||||
let topPanel = Child(BlurredRectangle.self)
|
||||
let topSeparator = Child(Rectangle.self)
|
||||
let title = Child(Text.self)
|
||||
let title = Child(MultilineTextComponent.self)
|
||||
let secondaryTitle = Child(MultilineTextComponent.self)
|
||||
let bottomPanel = Child(BlurredRectangle.self)
|
||||
let bottomSeparator = Child(Rectangle.self)
|
||||
@ -1468,10 +1497,11 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
||||
}
|
||||
|
||||
let title = title.update(
|
||||
component: Text(
|
||||
text: titleString,
|
||||
font: Font.bold(28.0),
|
||||
color: environment.theme.rootController.navigationBar.primaryTextColor
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: titleString, font: Font.bold(28.0), textColor: environment.theme.rootController.navigationBar.primaryTextColor)),
|
||||
horizontalAlignment: .center,
|
||||
truncationType: .end,
|
||||
maximumNumberOfLines: 1
|
||||
),
|
||||
availableSize: context.availableSize,
|
||||
transition: context.transition
|
||||
@ -1618,7 +1648,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
||||
foregroundColor: .white
|
||||
),
|
||||
height: 50.0,
|
||||
cornerRadius: 10.0,
|
||||
cornerRadius: 11.0,
|
||||
gloss: true,
|
||||
isLoading: state.inProgress,
|
||||
action: {
|
||||
|
@ -388,7 +388,7 @@ private final class PremimLimitsListScreenComponent: CombinedComponent {
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: environment.navigationHeight + list.size.height / 2.0))
|
||||
)
|
||||
|
||||
return CGSize(width: context.availableSize.width, height: environment.navigationHeight + list.size.height + environment.safeInsets.bottom - 16.0)
|
||||
return CGSize(width: context.availableSize.width, height: environment.navigationHeight + list.size.height + environment.safeInsets.bottom)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -452,6 +452,10 @@ public class PremimLimitsListScreen: ViewController {
|
||||
self.containerView.addSubview(self.scrollView)
|
||||
self.containerView.addSubnode(self.footerNode)
|
||||
self.scrollView.addSubview(self.hostView)
|
||||
|
||||
self.footerNode.action = { [weak self] in
|
||||
self?.controller?.action()
|
||||
}
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
@ -488,7 +492,7 @@ public class PremimLimitsListScreen: ViewController {
|
||||
let contentOffset = self.scrollView.contentOffset.y
|
||||
self.controller?.navigationBar?.updateBackgroundAlpha(min(30.0, contentOffset) / 30.0, transition: .immediate)
|
||||
|
||||
let bottomOffsetY = max(0.0, self.scrollView.contentSize.height + 20.0 - contentOffset - self.scrollView.frame.height)
|
||||
let bottomOffsetY = max(0.0, self.scrollView.contentSize.height - contentOffset - self.scrollView.frame.height)
|
||||
let backgroundAlpha: CGFloat = min(30.0, bottomOffsetY) / 30.0
|
||||
|
||||
self.footerNode.updateBackgroundAlpha(backgroundAlpha, transition: .immediate)
|
||||
@ -891,6 +895,7 @@ public class PremimLimitsListScreen: ViewController {
|
||||
private let buttonText: String
|
||||
private let buttonGloss: Bool
|
||||
|
||||
var action: () -> Void = {}
|
||||
var disposed: () -> Void = {}
|
||||
|
||||
public convenience init(context: AccountContext, buttonText: String, isPremium: Bool) {
|
||||
@ -1072,28 +1077,19 @@ private class FooterNode: ASDisplayNode {
|
||||
let buttonInset: CGFloat = 16.0
|
||||
let buttonWidth = layout.size.width - layout.safeInsets.left - layout.safeInsets.right - buttonInset * 2.0
|
||||
let buttonHeight = self.buttonNode.updateLayout(width: buttonWidth, transition: transition)
|
||||
let inset: CGFloat = 9.0
|
||||
|
||||
let insets = layout.insets(options: [.input])
|
||||
|
||||
var panelHeight: CGFloat = buttonHeight + inset * 2.0
|
||||
let totalPanelHeight: CGFloat
|
||||
if let inputHeight = layout.inputHeight, inputHeight > 0.0 {
|
||||
totalPanelHeight = panelHeight + insets.bottom
|
||||
} else {
|
||||
panelHeight += insets.bottom
|
||||
totalPanelHeight = panelHeight
|
||||
}
|
||||
|
||||
let bottomPanelPadding: CGFloat = 12.0
|
||||
let bottomInset: CGFloat = layout.intrinsicInsets.bottom > 0.0 ? layout.intrinsicInsets.bottom + 5.0 : bottomPanelPadding
|
||||
|
||||
let panelHeight: CGFloat = bottomPanelPadding + 50.0 + bottomInset
|
||||
let panelFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: panelHeight))
|
||||
transition.updateFrame(node: self.buttonNode, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left + buttonInset, y: panelFrame.minY + inset), size: CGSize(width: buttonWidth, height: buttonHeight)))
|
||||
transition.updateFrame(node: self.buttonNode, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left + buttonInset, y: bottomPanelPadding), size: CGSize(width: buttonWidth, height: buttonHeight)))
|
||||
|
||||
transition.updateFrame(node: self.backgroundNode, frame: panelFrame)
|
||||
self.backgroundNode.update(size: panelFrame.size, transition: transition)
|
||||
|
||||
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: panelFrame.origin, size: CGSize(width: panelFrame.width, height: UIScreenPixel)))
|
||||
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: panelFrame.width, height: UIScreenPixel)))
|
||||
|
||||
return totalPanelHeight
|
||||
return panelHeight
|
||||
}
|
||||
|
||||
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
|
||||
|
@ -649,6 +649,9 @@ public final class SolidRoundedButtonView: UIView {
|
||||
self.buttonBackgroundNode = UIImageView()
|
||||
self.buttonBackgroundNode.clipsToBounds = true
|
||||
self.buttonBackgroundNode.layer.cornerRadius = cornerRadius
|
||||
if #available(iOS 13.0, *) {
|
||||
self.buttonBackgroundNode.layer.cornerCurve = .continuous
|
||||
}
|
||||
|
||||
self.buttonBackgroundNode.backgroundColor = theme.backgroundColor
|
||||
if theme.backgroundColors.count > 1 {
|
||||
|
@ -18,6 +18,7 @@ import AsyncDisplayKit
|
||||
import PresentationDataUtils
|
||||
import MeshAnimationCache
|
||||
import FetchManagerImpl
|
||||
import InAppPurchaseManager
|
||||
|
||||
private final class DeviceSpecificContactImportContext {
|
||||
let disposable = MetaDisposable()
|
||||
@ -122,6 +123,7 @@ public final class AccountContextImpl: AccountContext {
|
||||
public let peersNearbyManager: PeersNearbyManager?
|
||||
public let wallpaperUploadManager: WallpaperUploadManager?
|
||||
private let themeUpdateManager: ThemeUpdateManager?
|
||||
public let inAppPurchaseManager: InAppPurchaseManager?
|
||||
|
||||
public let peerChannelMemberCategoriesContextsManager = PeerChannelMemberCategoriesContextsManager()
|
||||
|
||||
@ -184,10 +186,16 @@ public final class AccountContextImpl: AccountContext {
|
||||
self.prefetchManager = PrefetchManagerImpl(sharedContext: sharedContext, account: account, engine: self.engine, fetchManager: self.fetchManager)
|
||||
self.wallpaperUploadManager = WallpaperUploadManagerImpl(sharedContext: sharedContext, account: account, presentationData: sharedContext.presentationData)
|
||||
self.themeUpdateManager = ThemeUpdateManagerImpl(sharedContext: sharedContext, account: account)
|
||||
if let premiumProductId = sharedContext.premiumProductId {
|
||||
self.inAppPurchaseManager = InAppPurchaseManager(engine: self.engine, premiumProductId: premiumProductId)
|
||||
} else {
|
||||
self.inAppPurchaseManager = nil
|
||||
}
|
||||
} else {
|
||||
self.prefetchManager = nil
|
||||
self.wallpaperUploadManager = nil
|
||||
self.themeUpdateManager = nil
|
||||
self.inAppPurchaseManager = nil
|
||||
}
|
||||
|
||||
if let locationManager = self.sharedContextImpl.locationManager, sharedContext.applicationBindings.isMainApp && !temp {
|
||||
|
@ -33,7 +33,6 @@ import TelegramAudio
|
||||
import DebugSettingsUI
|
||||
import BackgroundTasks
|
||||
import UIKitRuntimeUtils
|
||||
import InAppPurchaseManager
|
||||
|
||||
#if canImport(AppCenter)
|
||||
import AppCenter
|
||||
@ -711,8 +710,6 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
||||
UINavigationController.attemptRotationToDeviceOrientation()
|
||||
})
|
||||
|
||||
let inAppPurchaseManager = InAppPurchaseManager(premiumProductId: buildConfig.premiumIAPProductId)
|
||||
|
||||
let accountManager = AccountManager<TelegramAccountManagerTypes>(basePath: rootPath + "/accounts-metadata", isTemporary: false, isReadOnly: false, useCaches: true, removeDatabaseOnError: true)
|
||||
self.accountManager = accountManager
|
||||
|
||||
@ -755,7 +752,7 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
||||
})
|
||||
|
||||
var setPresentationCall: ((PresentationCall?) -> Void)?
|
||||
let sharedContext = SharedAccountContextImpl(mainWindow: self.mainWindow, sharedContainerPath: legacyBasePath, basePath: rootPath, encryptionParameters: encryptionParameters, accountManager: accountManager, appLockContext: appLockContext, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings, networkArguments: networkArguments, inAppPurchaseManager: inAppPurchaseManager, rootPath: rootPath, legacyBasePath: legacyBasePath, apsNotificationToken: self.notificationTokenPromise.get() |> map(Optional.init), voipNotificationToken: self.voipTokenPromise.get() |> map(Optional.init), setNotificationCall: { call in
|
||||
let sharedContext = SharedAccountContextImpl(mainWindow: self.mainWindow, sharedContainerPath: legacyBasePath, basePath: rootPath, encryptionParameters: encryptionParameters, accountManager: accountManager, appLockContext: appLockContext, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings, networkArguments: networkArguments, premiumProductId: buildConfig.premiumIAPProductId, rootPath: rootPath, legacyBasePath: legacyBasePath, apsNotificationToken: self.notificationTokenPromise.get() |> map(Optional.init), voipNotificationToken: self.voipTokenPromise.get() |> map(Optional.init), setNotificationCall: { call in
|
||||
setPresentationCall?(call)
|
||||
}, navigateToChat: { accountId, peerId, messageId in
|
||||
self.openChatWhenReady(accountId: accountId, peerId: peerId, messageId: messageId)
|
||||
|
@ -138,7 +138,7 @@ public final class NotificationViewControllerImpl {
|
||||
return nil
|
||||
})
|
||||
|
||||
sharedAccountContext = SharedAccountContextImpl(mainWindow: nil, sharedContainerPath: self.initializationData.appGroupPath, basePath: rootPath, encryptionParameters: ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: self.initializationData.encryptionParameters.0)!, salt: ValueBoxEncryptionParameters.Salt(data: self.initializationData.encryptionParameters.1)!), accountManager: accountManager, appLockContext: appLockContext, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: NetworkInitializationArguments(apiId: self.initializationData.apiId, apiHash: self.initializationData.apiHash, languagesCategory: self.initializationData.languagesCategory, appVersion: self.initializationData.appVersion, voipMaxLayer: 0, voipVersions: [], appData: .single(self.initializationData.bundleData), autolockDeadine: .single(nil), encryptionProvider: OpenSSLEncryptionProvider(), resolvedDeviceName: nil), inAppPurchaseManager: nil, rootPath: rootPath, legacyBasePath: nil, apsNotificationToken: .never(), voipNotificationToken: .never(), setNotificationCall: { _ in }, navigateToChat: { _, _, _ in })
|
||||
sharedAccountContext = SharedAccountContextImpl(mainWindow: nil, sharedContainerPath: self.initializationData.appGroupPath, basePath: rootPath, encryptionParameters: ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: self.initializationData.encryptionParameters.0)!, salt: ValueBoxEncryptionParameters.Salt(data: self.initializationData.encryptionParameters.1)!), accountManager: accountManager, appLockContext: appLockContext, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: NetworkInitializationArguments(apiId: self.initializationData.apiId, apiHash: self.initializationData.apiHash, languagesCategory: self.initializationData.languagesCategory, appVersion: self.initializationData.appVersion, voipMaxLayer: 0, voipVersions: [], appData: .single(self.initializationData.bundleData), autolockDeadine: .single(nil), encryptionProvider: OpenSSLEncryptionProvider(), resolvedDeviceName: nil), premiumProductId: nil, rootPath: rootPath, legacyBasePath: nil, apsNotificationToken: .never(), voipNotificationToken: .never(), setNotificationCall: { _ in }, navigateToChat: { _, _, _ in })
|
||||
|
||||
presentationDataPromise.set(sharedAccountContext!.presentationData)
|
||||
}
|
||||
|
@ -234,7 +234,7 @@ public class ShareRootControllerImpl {
|
||||
return nil
|
||||
})
|
||||
|
||||
let sharedContext = SharedAccountContextImpl(mainWindow: nil, sharedContainerPath: self.initializationData.appGroupPath, basePath: rootPath, encryptionParameters: ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: self.initializationData.encryptionParameters.0)!, salt: ValueBoxEncryptionParameters.Salt(data: self.initializationData.encryptionParameters.1)!), accountManager: accountManager, appLockContext: appLockContext, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: NetworkInitializationArguments(apiId: self.initializationData.apiId, apiHash: self.initializationData.apiHash, languagesCategory: self.initializationData.languagesCategory, appVersion: self.initializationData.appVersion, voipMaxLayer: 0, voipVersions: [], appData: .single(self.initializationData.bundleData), autolockDeadine: .single(nil), encryptionProvider: OpenSSLEncryptionProvider(), resolvedDeviceName: nil), inAppPurchaseManager: nil, rootPath: rootPath, legacyBasePath: nil, apsNotificationToken: .never(), voipNotificationToken: .never(), setNotificationCall: { _ in }, navigateToChat: { _, _, _ in })
|
||||
let sharedContext = SharedAccountContextImpl(mainWindow: nil, sharedContainerPath: self.initializationData.appGroupPath, basePath: rootPath, encryptionParameters: ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: self.initializationData.encryptionParameters.0)!, salt: ValueBoxEncryptionParameters.Salt(data: self.initializationData.encryptionParameters.1)!), accountManager: accountManager, appLockContext: appLockContext, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: NetworkInitializationArguments(apiId: self.initializationData.apiId, apiHash: self.initializationData.apiHash, languagesCategory: self.initializationData.languagesCategory, appVersion: self.initializationData.appVersion, voipMaxLayer: 0, voipVersions: [], appData: .single(self.initializationData.bundleData), autolockDeadine: .single(nil), encryptionProvider: OpenSSLEncryptionProvider(), resolvedDeviceName: nil), premiumProductId: nil, rootPath: rootPath, legacyBasePath: nil, apsNotificationToken: .never(), voipNotificationToken: .never(), setNotificationCall: { _ in }, navigateToChat: { _, _, _ in })
|
||||
presentationDataPromise.set(sharedContext.presentationData)
|
||||
internalContext = InternalContext(sharedContext: sharedContext)
|
||||
globalInternalContext = internalContext
|
||||
|
@ -90,7 +90,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
public let contactDataManager: DeviceContactDataManager?
|
||||
public let locationManager: DeviceLocationManager?
|
||||
public var callManager: PresentationCallManager?
|
||||
public var inAppPurchaseManager: InAppPurchaseManager?
|
||||
let premiumProductId: String?
|
||||
|
||||
private var callDisposable: Disposable?
|
||||
private var callStateDisposable: Disposable?
|
||||
@ -164,7 +164,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
private var spotlightDataContext: SpotlightDataContext?
|
||||
private var widgetDataContext: WidgetDataContext?
|
||||
|
||||
public init(mainWindow: Window1?, sharedContainerPath: String, basePath: String, encryptionParameters: ValueBoxEncryptionParameters, accountManager: AccountManager<TelegramAccountManagerTypes>, appLockContext: AppLockContext, applicationBindings: TelegramApplicationBindings, initialPresentationDataAndSettings: InitialPresentationDataAndSettings, networkArguments: NetworkInitializationArguments, inAppPurchaseManager: InAppPurchaseManager?, rootPath: String, legacyBasePath: String?, apsNotificationToken: Signal<Data?, NoError>, voipNotificationToken: Signal<Data?, NoError>, setNotificationCall: @escaping (PresentationCall?) -> Void, navigateToChat: @escaping (AccountRecordId, PeerId, MessageId?) -> Void, displayUpgradeProgress: @escaping (Float?) -> Void = { _ in }) {
|
||||
public init(mainWindow: Window1?, sharedContainerPath: String, basePath: String, encryptionParameters: ValueBoxEncryptionParameters, accountManager: AccountManager<TelegramAccountManagerTypes>, appLockContext: AppLockContext, applicationBindings: TelegramApplicationBindings, initialPresentationDataAndSettings: InitialPresentationDataAndSettings, networkArguments: NetworkInitializationArguments, premiumProductId: String?, rootPath: String, legacyBasePath: String?, apsNotificationToken: Signal<Data?, NoError>, voipNotificationToken: Signal<Data?, NoError>, setNotificationCall: @escaping (PresentationCall?) -> Void, navigateToChat: @escaping (AccountRecordId, PeerId, MessageId?) -> Void, displayUpgradeProgress: @escaping (Float?) -> Void = { _ in }) {
|
||||
assert(Queue.mainQueue().isCurrent())
|
||||
|
||||
precondition(!testHasInstance)
|
||||
@ -178,7 +178,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
self.navigateToChatImpl = navigateToChat
|
||||
self.displayUpgradeProgress = displayUpgradeProgress
|
||||
self.appLockContext = appLockContext
|
||||
self.inAppPurchaseManager = inAppPurchaseManager
|
||||
self.premiumProductId = premiumProductId
|
||||
|
||||
self.accountManager.mediaBox.fetchCachedResourceRepresentation = { (resource, representation) -> Signal<CachedMediaResourceRepresentationResult, NoError> in
|
||||
return fetchCachedSharedResourceRepresentation(accountManager: accountManager, resource: resource, representation: representation)
|
||||
|
Loading…
x
Reference in New Issue
Block a user