From fbccdd47dfebdaaba8121564cfb3c13edd53aea5 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Sat, 11 Jun 2022 09:10:01 +0400 Subject: [PATCH 01/10] Various improvements --- .../Telegram-iOS/en.lproj/Localizable.strings | 1 + .../Sources/Node/ChatListItem.swift | 6 +- .../Sources/InAppPurchaseManager.swift | 48 +++-- .../Sources/StoredTransactionState.swift | 57 +++++ submodules/PremiumUI/BUILD | 43 ++++ submodules/PremiumUI/MetalResources/chars.png | Bin 0 -> 11102 bytes .../PremiumUI/MetalResources/matrix.metal | 79 +++++++ .../PremiumUI/MetalResources/random.jpg | Bin 0 -> 37539 bytes submodules/PremiumUI/Resources/badge.scn | Bin 20239 -> 27968 bytes submodules/PremiumUI/Resources/lightspeed.scn | Bin 14641 -> 14629 bytes submodules/PremiumUI/Resources/swirl.scn | Bin 0 -> 84910 bytes .../PremiumUI/Sources/BadgeStarsView.swift | 59 +++++ .../PremiumUI/Sources/DataRainView.swift | 198 +++++++++++++++++ .../PremiumUI/Sources/FasterStarsView.swift | 107 +++++++++ .../Sources/PhoneDemoComponent.swift | 199 +++++------------ .../PremiumUI/Sources/PremiumDemoScreen.swift | 15 +- .../Sources/PremiumIntroScreen.swift | 25 ++- .../PremiumUI/Sources/SwirlStarsView.swift | 204 ++++++++++++++++++ .../Sources/ReactionSelectionNode.swift | 146 +++++++++---- .../TelegramEngine/Payments/AppStore.swift | 1 + .../ReactionsStar.imageset/Contents.json | 21 ++ .../ReactionsStar.imageset/ministar@3x.png | Bin 0 -> 341 bytes 22 files changed, 992 insertions(+), 217 deletions(-) create mode 100644 submodules/InAppPurchaseManager/Sources/StoredTransactionState.swift create mode 100644 submodules/PremiumUI/MetalResources/chars.png create mode 100644 submodules/PremiumUI/MetalResources/matrix.metal create mode 100644 submodules/PremiumUI/MetalResources/random.jpg create mode 100644 submodules/PremiumUI/Resources/swirl.scn create mode 100644 submodules/PremiumUI/Sources/BadgeStarsView.swift create mode 100644 submodules/PremiumUI/Sources/DataRainView.swift create mode 100644 submodules/PremiumUI/Sources/FasterStarsView.swift create mode 100644 submodules/PremiumUI/Sources/SwirlStarsView.swift create mode 100644 submodules/TelegramUI/Images.xcassets/Premium/ReactionsStar.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Premium/ReactionsStar.imageset/ministar@3x.png diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 0f820d369f..cc0aa1e301 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -7711,5 +7711,6 @@ Sorry for the inconvenience."; "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."; +"Premium.Purchase.ErrorCantMakePayments" = "In-app purchases are not allowed on this device."; "Settings.Premium" = "Telegram Premium"; diff --git a/submodules/ChatListUI/Sources/Node/ChatListItem.swift b/submodules/ChatListUI/Sources/Node/ChatListItem.swift index 2c2f9345b2..7aff6992c1 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift @@ -2241,7 +2241,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { transition.updateFrameAdditive(node: self.titleNode, frame: CGRect(origin: CGPoint(x: contentRect.origin.x + titleOffset, y: titleFrame.origin.y), size: titleFrame.size)) let authorFrame = self.authorNode.frame - transition.updateFrame(node: self.authorNode, frame: CGRect(origin: CGPoint(x: contentRect.origin.x, y: authorFrame.origin.y), size: authorFrame.size)) + transition.updateFrame(node: self.authorNode, frame: CGRect(origin: CGPoint(x: contentRect.origin.x - 1.0, y: authorFrame.origin.y), size: authorFrame.size)) transition.updateFrame(node: self.inputActivitiesNode, frame: CGRect(origin: CGPoint(x: contentRect.origin.x, y: self.inputActivitiesNode.frame.minY), size: self.inputActivitiesNode.bounds.size)) @@ -2253,7 +2253,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { transition.updateFrameAdditive(node: dustNode, frame: textFrame.insetBy(dx: -3.0, dy: -3.0).offsetBy(dx: 0.0, dy: 3.0)) } - var mediaPreviewOffsetX = textFrame.origin.x + 1.0 + var mediaPreviewOffsetX = textFrame.origin.x let contentImageSpacing: CGFloat = 2.0 for (_, media, mediaSize) in self.currentMediaPreviewSpecs { guard let mediaId = media.id else { @@ -2279,7 +2279,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { } let mutedIconFrame = self.mutedIconNode.frame - transition.updateFrame(node: self.mutedIconNode, frame: CGRect(origin: CGPoint(x: nextTitleIconOrigin - 4.0, y: contentRect.origin.y - 2.0), size: mutedIconFrame.size)) + transition.updateFrame(node: self.mutedIconNode, frame: CGRect(origin: CGPoint(x: nextTitleIconOrigin - 5.0, y: mutedIconFrame.minY), size: mutedIconFrame.size)) nextTitleIconOrigin += mutedIconFrame.size.width + 3.0 let badgeFrame = self.badgeNode.frame diff --git a/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift b/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift index 08bda86473..b1df3d7b7a 100644 --- a/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift +++ b/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift @@ -30,6 +30,8 @@ public final class InAppPurchaseManager: NSObject { case cancelled case network case notAllowed + case cantMakePayments + case assignFailed } public enum RestoreState { @@ -51,6 +53,7 @@ public final class InAppPurchaseManager: NSObject { case restored(transactionId: String?) case purchasing case failed(error: SKError?) + case assignFailed case deferred } @@ -63,7 +66,7 @@ public final class InAppPurchaseManager: NSObject { private let stateQueue = Queue() private var paymentContexts: [String: PaymentTransactionContext] = [:] - + private var onRestoreCompletion: ((RestoreState) -> Void)? private let disposableSet = DisposableDict() @@ -82,6 +85,10 @@ public final class InAppPurchaseManager: NSObject { SKPaymentQueue.default().remove(self) } + var canMakePayments: Bool { + return SKPaymentQueue.canMakePayments() + } + private func requestProducts() { guard !self.premiumProductId.isEmpty else { return @@ -119,10 +126,16 @@ public final class InAppPurchaseManager: NSObject { } } - public func buyProduct(_ product: Product, account: Account) -> Signal { - Logger.shared.log("InAppPurchaseManager", "Buying product: \(product.skProduct.productIdentifier), price \(product.price)") + public func buyProduct(_ product: Product) -> Signal { + if !self.canMakePayments { + return .fail(.cantMakePayments) + } + let accountPeerId = "\(self.engine.account.peerId.toInt64())" - let payment = SKPayment(product: product.skProduct) + Logger.shared.log("InAppPurchaseManager", "Buying: account \(accountPeerId), product \(product.skProduct.productIdentifier), price \(product.price)") + + let payment = SKMutablePayment(product: product.skProduct) + payment.applicationUsername = accountPeerId SKPaymentQueue.default().add(payment) let productIdentifier = payment.productIdentifier @@ -156,6 +169,8 @@ public final class InAppPurchaseManager: NSObject { } else { subscriber.putError(.generic) } + case .assignFailed: + subscriber.putError(.assignFailed) case .deferred, .purchasing: break } @@ -205,39 +220,48 @@ private func getReceiptData() -> Data? { extension InAppPurchaseManager: SKPaymentTransactionObserver { public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { for transaction in transactions { + let accountPeerId = "\(self.engine.account.peerId.toInt64())" + if let applicationUsername = transaction.payment.applicationUsername, applicationUsername != accountPeerId { + continue + } + let productIdentifier = transaction.payment.productIdentifier self.stateQueue.async { let transactionState: TransactionState? switch transaction.transactionState { case .purchased: - Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? ""), original transaction \(transaction.original?.transactionIdentifier ?? "none") purchased") + Logger.shared.log("InAppPurchaseManager", "Account \(accountPeerId), transaction \(transaction.transactionIdentifier ?? ""), original transaction \(transaction.original?.transactionIdentifier ?? "none") purchased") let transactionIdentifier = transaction.transactionIdentifier transactionState = .purchased(transactionId: transactionIdentifier) if let transactionIdentifier = transactionIdentifier { self.disposableSet.set( - self.engine.payments.sendAppStoreReceipt(receipt: getReceiptData() ?? Data(), restore: false).start(error: { _ in - Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? "") failed to assign AppStore transaction") + self.engine.payments.sendAppStoreReceipt(receipt: getReceiptData() ?? Data(), restore: false).start(error: { [weak self] _ in + Logger.shared.log("InAppPurchaseManager", "Account \(accountPeerId), transaction \(transaction.transactionIdentifier ?? "") failed to assign AppStore transaction") queue.finishTransaction(transaction) + + if let strongSelf = self, let context = strongSelf.paymentContexts[productIdentifier] { + context.subscriber(.assignFailed) + } }, completed: { - Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? "") successfully assigned AppStore transaction") + Logger.shared.log("InAppPurchaseManager", "Account \(accountPeerId), transaction \(transaction.transactionIdentifier ?? "") successfully assigned AppStore transaction") queue.finishTransaction(transaction) }), forKey: transactionIdentifier ) } case .restored: - Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? ""), original transaction \(transaction.original?.transactionIdentifier ?? "") restroring") + Logger.shared.log("InAppPurchaseManager", "Account \(accountPeerId), transaction \(transaction.transactionIdentifier ?? ""), original transaction \(transaction.original?.transactionIdentifier ?? "") restroring") let transactionIdentifier = transaction.transactionIdentifier transactionState = .restored(transactionId: transactionIdentifier) case .failed: - Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? "") failed \((transaction.error as? SKError)?.localizedDescription ?? "")") + Logger.shared.log("InAppPurchaseManager", "Account \(accountPeerId), transaction \(transaction.transactionIdentifier ?? "") failed \((transaction.error as? SKError)?.localizedDescription ?? "")") transactionState = .failed(error: transaction.error as? SKError) queue.finishTransaction(transaction) case .purchasing: - Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? "") purchasing") + Logger.shared.log("InAppPurchaseManager", "Account \(accountPeerId), transaction \(transaction.transactionIdentifier ?? "") purchasing") transactionState = .purchasing case .deferred: - Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? "") deferred") + Logger.shared.log("InAppPurchaseManager", "Account \(accountPeerId), transaction \(transaction.transactionIdentifier ?? "") deferred") transactionState = .deferred default: transactionState = nil diff --git a/submodules/InAppPurchaseManager/Sources/StoredTransactionState.swift b/submodules/InAppPurchaseManager/Sources/StoredTransactionState.swift new file mode 100644 index 0000000000..3f0f328d81 --- /dev/null +++ b/submodules/InAppPurchaseManager/Sources/StoredTransactionState.swift @@ -0,0 +1,57 @@ +//import Foundation +//import UIKit +//import SwiftSignalKit +//import Postbox +//import TelegramCore +//import TelegramUIPreferences +// +//final class StoredTransactionState: Codable { +// let timestamp: Double +// let playbackRate: AudioPlaybackRate +// +// init(timestamp: Double, playbackRate: AudioPlaybackRate) { +// self.timestamp = timestamp +// self.playbackRate = playbackRate +// } +// +// public init(from decoder: Decoder) throws { +// let container = try decoder.container(keyedBy: StringCodingKey.self) +// +// self.timestamp = try container.decode(Double.self, forKey: "timestamp") +// self.playbackRate = AudioPlaybackRate(rawValue: try container.decode(Int32.self, forKey: "playbackRate")) ?? .x1 +// } +// +// public func encode(to encoder: Encoder) throws { +// var container = encoder.container(keyedBy: StringCodingKey.self) +// +// try container.encode(self.timestamp, forKey: "timestamp") +// try container.encode(self.playbackRate.rawValue, forKey: "playbackRate") +// } +//} +// +//public func storedState(engine: TelegramEngine, : MessageId) -> Signal { +// let key = ValueBoxKey(length: 20) +// key.setInt32(0, value: messageId.namespace) +// key.setInt32(4, value: messageId.peerId.namespace._internalGetInt32Value()) +// key.setInt64(8, value: messageId.peerId.id._internalGetInt64Value()) +// key.setInt32(16, value: messageId.id) +// +// return engine.data.get(TelegramEngine.EngineData.Item.ItemCache.Item(collectionId: ApplicationSpecificItemCacheCollectionId.mediaPlaybackStoredState, id: key)) +// |> map { entry -> MediaPlaybackStoredState? in +// return entry?.get(MediaPlaybackStoredState.self) +// } +//} +// +//public func updateMediaPlaybackStoredStateInteractively(engine: TelegramEngine, messageId: MessageId, state: MediaPlaybackStoredState?) -> Signal { +// let key = ValueBoxKey(length: 20) +// key.setInt32(0, value: messageId.namespace) +// key.setInt32(4, value: messageId.peerId.namespace._internalGetInt32Value()) +// key.setInt64(8, value: messageId.peerId.id._internalGetInt64Value()) +// key.setInt32(16, value: messageId.id) +// +// if let state = state { +// return engine.itemCache.put(collectionId: ApplicationSpecificItemCacheCollectionId.mediaPlaybackStoredState, id: key, item: state) +// } else { +// return engine.itemCache.remove(collectionId: ApplicationSpecificItemCacheCollectionId.mediaPlaybackStoredState, id: key) +// } +//} diff --git a/submodules/PremiumUI/BUILD b/submodules/PremiumUI/BUILD index 7cf7d94251..12c178b056 100644 --- a/submodules/PremiumUI/BUILD +++ b/submodules/PremiumUI/BUILD @@ -1,4 +1,44 @@ load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") +load( + "@build_bazel_rules_apple//apple:resources.bzl", + "apple_resource_bundle", + "apple_resource_group", +) +load("//build-system/bazel-utils:plist_fragment.bzl", + "plist_fragment", +) + +filegroup( + name = "PremiumUIMetalResources", + srcs = glob([ + "MetalResources/**/*.*", + ]), + visibility = ["//visibility:public"], +) + +plist_fragment( + name = "PremiumUIBundleInfoPlist", + extension = "plist", + template = + """ + CFBundleIdentifier + org.telegram.PremiumUI + CFBundleDevelopmentRegion + en + CFBundleName + PremiumUI + """ +) + +apple_resource_bundle( + name = "PremiumUIBundle", + infoplists = [ + ":PremiumUIBundleInfoPlist", + ], + resources = [ + ":PremiumUIMetalResources", + ], +) filegroup( name = "PremiumUIResources", @@ -17,6 +57,9 @@ swift_library( copts = [ "-warnings-as-errors", ], + data = [ + ":PremiumUIBundle", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/PremiumUI/MetalResources/chars.png b/submodules/PremiumUI/MetalResources/chars.png new file mode 100644 index 0000000000000000000000000000000000000000..1a00dd5f4e3a451e8ce2c0ae695654da80a4e9b2 GIT binary patch literal 11102 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 b)(bjYA@=wL`OZ_TztP}&rnjnYIz9bAqJ$4x literal 0 HcmV?d00001 diff --git a/submodules/PremiumUI/MetalResources/matrix.metal b/submodules/PremiumUI/MetalResources/matrix.metal new file mode 100644 index 0000000000..9cbfd8e745 --- /dev/null +++ b/submodules/PremiumUI/MetalResources/matrix.metal @@ -0,0 +1,79 @@ +#include + +using namespace metal; + +typedef struct { + packed_float2 position; +} Vertex; + +struct RasterizerData +{ + float4 position [[position]]; +}; + +vertex RasterizerData matrixVertex +( + constant Vertex *vertexArray[[buffer(0)]], + uint vertexID [[ vertex_id ]] +) { + RasterizerData out; + + out.position = vector_float4(vertexArray[vertexID].position[0], vertexArray[vertexID].position[1], 0.0, 1.0); + + return out; +} + +float text(float2 uvIn, + texture2d symbolTexture, + texture2d noiseTexture, + float time) +{ + constexpr sampler textureSampler(min_filter::linear, mag_filter::linear, mip_filter::linear, address::repeat); + + float count = 32.0; + + float2 noiseResolution = float2(256.0, 256.0); + + float2 uv = fmod(uvIn, 1.0 / count) * count; + float2 block = uvIn * count - uv; + uv = uv * 0.8 + 0.1; + uv += floor(noiseTexture.sample(textureSampler, block / noiseResolution + time * .00025).xy * 256.); + uv *= -1.0; + + uv *= 0.25; + + return symbolTexture.sample(textureSampler, uv).g; +} + +float4 rain(float2 uvIn, + uint2 resolution, + float time) +{ + float count = 32.0; + uvIn.x -= fmod(uvIn.x, 1.0 / count); + uvIn.y -= fmod(uvIn.y, 1.0 / count); + + float2 fragCoord = uvIn * float2(resolution); + + float offset = sin(fragCoord.x * 15.0); + float speed = cos(fragCoord.x * 3.0) * 0.3 + 0.7; + + float y = fract(fragCoord.y / resolution.y + time * speed + offset); + + return float4(1.0, 1.0, 1.0, 1.0 / (y * 30.0) - 0.02); +} + +fragment half4 matrixFragment(RasterizerData in[[stage_in]], + texture2d symbolTexture [[ texture(0) ]], + texture2d noiseTexture [[ texture(1) ]], + constant uint2 &resolution[[buffer(0)]], + constant float &time[[buffer(1)]]) +{ + float2 uv = (in.position.xy / float2(resolution.xy) - float2(0.5, 0.5)); + uv.y -= 0.1; + + float2 lookup = float2(0.08 / (uv.x), (0.9 - abs(uv.x)) * uv.y * -1.0) * 2.0; + + float4 out = text(lookup, symbolTexture, noiseTexture, time) * rain(lookup, resolution, time); + return half4(out); +} diff --git a/submodules/PremiumUI/MetalResources/random.jpg b/submodules/PremiumUI/MetalResources/random.jpg new file mode 100644 index 0000000000000000000000000000000000000000..708398ba99f86895dfa8b4d7f819758c382b1f0a GIT binary patch literal 37539 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 y7%G=WKUoTwC;!>kv*OGE literal 0 HcmV?d00001 diff --git a/submodules/PremiumUI/Resources/badge.scn b/submodules/PremiumUI/Resources/badge.scn index 48992cda49a4fb56ae3c82a0eb4263f5a4b5f8f7..40aa6f0b0df5b70f4502fed247337c1c3be19814 100644 GIT binary patch delta 10048 zcmb_h2UHZv+U@G@YSJJBjJj)D1SV$~bzNOhC-fvn1p_X_0OBaD2reLIr_6CpW6omE z0Z}pMoO8}OyRKn(pZeE;i1Xe)+4IhS`czkUb#;BWzVCL`?W!@ez>Z5`Y%2I;Tv}CE zSHeY{AWjlzi1WlH;wtfocn0D?OVAo50Xs+rd7wKO4aNXBm;|PQLNE`^2g^VaSPa&I zVxm5{2rhxk;0m}3u7T^|2Dk}sf!p8?xC`!q`-F%n1`oiW;4knm@E&{sA4!4)BuP?a z0LdY_WVnlrAR|dWDIkTUh!m3&QcB8*-K3mUkV>)v*@%oIZDeyoPPQOhk%?pynNAKR zSCA{oRpe@N4Y`&qBG-|{X7THHI2XjiV+~ld0*{3~Dj8oZ3qrppH?;sk78Y>K65gdQ3f` zo>On>nzWo&(DmsbXe%8@x2D_Co#`Apm+nQ+qW92y>7(>X`ZoQTehvX7p+Bqw!(d$~ zhH@ASn?NJ9LI+HNonU9!7Y>HQ;dD3y&P;`KVIeGr+u$C!7aoL1;0bsRUW3=+4TfYW zhGrncFg}bgAOmCYou; z{J{KJ^b`lI)~WEO1VQ+Q>v|CcA|qm7;5$zNI)vW2S5P&p{2tN7R#t{iqSr;|sBkha zXC9G7bRn{lKMF=;P|$U55Wyu_!bto=n23gim9P-ih+HC$$Rlh-bD{-C6i;*`3W)AR z4D*h*}}Pl}1{#14G73)?Kl<-1GI1*PY` z_*{%j;&DnO_7VGu12{j3@A$-F{NzaK^P~8Dj5uD@+vgeWIz^mrm!P$2)4Diw(+Y`G zg#Hxaz}-7bM*>&q92IYj{d86Ia^eH*Gl`oQ3#C|L<

AABHsOhQ?pqO0&SnOF)9X)woaQmb^Ez3D`Kv{ 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 D?n-Tf delta 6540 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 Y)ois|9o7a*)?Y_`fF|K}M0H>IAD|DD{r~^~ diff --git a/submodules/PremiumUI/Resources/swirl.scn b/submodules/PremiumUI/Resources/swirl.scn new file mode 100644 index 0000000000000000000000000000000000000000..f5f50794de53cb6a05ca5d10bd0f79606da704bf GIT binary patch literal 84910 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.size * 2, index: 0) + + var time = Float(CACurrentMediaTime() - self.startTimestamp) * 0.75 + renderEncoder.setFragmentBytes(&time, length: 4, index: 1) + + renderEncoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 6, instanceCount: 1) + + renderEncoder.endEncoding() + + commandBuffer.present(drawable) + commandBuffer.commit() + + } +} diff --git a/submodules/PremiumUI/Sources/FasterStarsView.swift b/submodules/PremiumUI/Sources/FasterStarsView.swift new file mode 100644 index 0000000000..f80b621f70 --- /dev/null +++ b/submodules/PremiumUI/Sources/FasterStarsView.swift @@ -0,0 +1,107 @@ +import Foundation +import UIKit +import SceneKit +import Display +import AppBundle + +final class FasterStarsView: UIView, PhoneDemoDecorationView { + private let sceneView: SCNView + + private var particles: SCNNode? + + override init(frame: CGRect) { + self.sceneView = SCNView(frame: CGRect(origin: .zero, size: frame.size)) + self.sceneView.backgroundColor = .clear + if let url = getAppBundle().url(forResource: "lightspeed", withExtension: "scn") { + self.sceneView.scene = try? SCNScene(url: url, options: nil) + } + self.sceneView.isUserInteractionEnabled = false + self.sceneView.preferredFramesPerSecond = 60 + + super.init(frame: frame) + + self.alpha = 0.0 + + self.addSubview(self.sceneView) + + self.particles = self.sceneView.scene?.rootNode.childNode(withName: "particles", recursively: false) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + self.particles = nil + } + + func setVisible(_ visible: Bool) { + if visible, let particles = self.particles, particles.parent == nil { + self.sceneView.scene?.rootNode.addChildNode(particles) + } + + let transition = ContainedViewLayoutTransition.animated(duration: 0.3, curve: .linear) + transition.updateAlpha(layer: self.layer, alpha: visible ? 0.4 : 0.0, completion: { [weak self] finished in + if let strongSelf = self, finished && !visible && strongSelf.particles?.parent != nil { + strongSelf.particles?.removeFromParentNode() + } + }) + } + + private var playing = false + func startAnimation() { + guard !self.playing, let scene = self.sceneView.scene, let node = scene.rootNode.childNode(withName: "particles", recursively: false), let particles = node.particleSystems?.first else { + return + } + self.playing = true + + let speedAnimation = CABasicAnimation(keyPath: "speedFactor") + speedAnimation.fromValue = 1.0 + speedAnimation.toValue = 1.8 + speedAnimation.duration = 0.8 + speedAnimation.fillMode = .forwards + particles.addAnimation(speedAnimation, forKey: "speedFactor") + + particles.speedFactor = 3.0 + + let stretchAnimation = CABasicAnimation(keyPath: "stretchFactor") + stretchAnimation.fromValue = 0.05 + stretchAnimation.toValue = 0.3 + stretchAnimation.duration = 0.8 + stretchAnimation.fillMode = .forwards + particles.addAnimation(stretchAnimation, forKey: "stretchFactor") + + particles.stretchFactor = 0.3 + } + + func resetAnimation() { + guard self.playing, let scene = self.sceneView.scene, let node = scene.rootNode.childNode(withName: "particles", recursively: false), let particles = node.particleSystems?.first else { + return + } + self.playing = false + + let speedAnimation = CABasicAnimation(keyPath: "speedFactor") + speedAnimation.fromValue = 3.0 + speedAnimation.toValue = 1.0 + speedAnimation.duration = 0.35 + speedAnimation.fillMode = .forwards + particles.addAnimation(speedAnimation, forKey: "speedFactor") + + particles.speedFactor = 1.0 + + let stretchAnimation = CABasicAnimation(keyPath: "stretchFactor") + stretchAnimation.fromValue = 0.3 + stretchAnimation.toValue = 0.05 + stretchAnimation.duration = 0.35 + stretchAnimation.fillMode = .forwards + particles.addAnimation(stretchAnimation, forKey: "stretchFactor") + + particles.stretchFactor = 0.05 + } + + override func layoutSubviews() { + super.layoutSubviews() + + self.sceneView.frame = CGRect(origin: .zero, size: frame.size) + } +} diff --git a/submodules/PremiumUI/Sources/PhoneDemoComponent.swift b/submodules/PremiumUI/Sources/PhoneDemoComponent.swift index 9a6515eb8c..e1fb0878cd 100644 --- a/submodules/PremiumUI/Sources/PhoneDemoComponent.swift +++ b/submodules/PremiumUI/Sources/PhoneDemoComponent.swift @@ -280,125 +280,9 @@ private final class PhoneView: UIView { } } -private final class FasterStarsView: UIView { - private let sceneView: SCNView - - override init(frame: CGRect) { - self.sceneView = SCNView(frame: CGRect(origin: .zero, size: frame.size)) - self.sceneView.backgroundColor = .clear - if let url = getAppBundle().url(forResource: "lightspeed", withExtension: "scn") { - self.sceneView.scene = try? SCNScene(url: url, options: nil) - } - self.sceneView.isUserInteractionEnabled = false - self.sceneView.preferredFramesPerSecond = 60 - - super.init(frame: frame) - - self.alpha = 0.0 - - self.addSubview(self.sceneView) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func setVisible(_ visible: Bool) { - let transition = ContainedViewLayoutTransition.animated(duration: 0.3, curve: .linear) - transition.updateAlpha(layer: self.layer, alpha: visible ? 1.0 : 0.0) - } - - private var playing = false - func startAnimation() { - guard !self.playing, let scene = self.sceneView.scene, let node = scene.rootNode.childNode(withName: "particles", recursively: false), let particles = node.particleSystems?.first else { - return - } - self.playing = true - - let speedAnimation = CABasicAnimation(keyPath: "speedFactor") - speedAnimation.fromValue = 1.0 - speedAnimation.toValue = 1.8 - speedAnimation.duration = 0.8 - speedAnimation.fillMode = .forwards - particles.addAnimation(speedAnimation, forKey: "speedFactor") - - particles.speedFactor = 3.0 - - let stretchAnimation = CABasicAnimation(keyPath: "stretchFactor") - stretchAnimation.fromValue = 0.05 - stretchAnimation.toValue = 0.3 - stretchAnimation.duration = 0.8 - stretchAnimation.fillMode = .forwards - particles.addAnimation(stretchAnimation, forKey: "stretchFactor") - - particles.stretchFactor = 0.3 - } - - func stopAnimation() { - guard self.playing, let scene = self.sceneView.scene, let node = scene.rootNode.childNode(withName: "particles", recursively: false), let particles = node.particleSystems?.first else { - return - } - self.playing = false - - let speedAnimation = CABasicAnimation(keyPath: "speedFactor") - speedAnimation.fromValue = 3.0 - speedAnimation.toValue = 1.0 - speedAnimation.duration = 0.35 - speedAnimation.fillMode = .forwards - particles.addAnimation(speedAnimation, forKey: "speedFactor") - - particles.speedFactor = 1.0 - - let stretchAnimation = CABasicAnimation(keyPath: "stretchFactor") - stretchAnimation.fromValue = 0.3 - stretchAnimation.toValue = 0.05 - stretchAnimation.duration = 0.35 - stretchAnimation.fillMode = .forwards - particles.addAnimation(stretchAnimation, forKey: "stretchFactor") - - particles.stretchFactor = 0.05 - } - - override func layoutSubviews() { - super.layoutSubviews() - - self.sceneView.frame = CGRect(origin: .zero, size: frame.size) - } -} - -private final class BadgeStarsView: UIView { - private let sceneView: SCNView - - override init(frame: CGRect) { - self.sceneView = SCNView(frame: CGRect(origin: .zero, size: frame.size)) - self.sceneView.backgroundColor = .clear - if let url = getAppBundle().url(forResource: "badge", withExtension: "scn") { - self.sceneView.scene = try? SCNScene(url: url, options: nil) - } - self.sceneView.isUserInteractionEnabled = false - self.sceneView.preferredFramesPerSecond = 60 - - super.init(frame: frame) - - self.alpha = 0.0 - - self.addSubview(self.sceneView) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func setVisible(_ visible: Bool) { - let transition = ContainedViewLayoutTransition.animated(duration: 0.3, curve: .linear) - transition.updateAlpha(layer: self.layer, alpha: visible ? 0.75 : 0.0) - } - - override func layoutSubviews() { - super.layoutSubviews() - - self.sceneView.frame = CGRect(origin: .zero, size: frame.size) - } +protocol PhoneDemoDecorationView: UIView { + func setVisible(_ visible: Bool) + func resetAnimation() } final class PhoneDemoComponent: Component { @@ -411,6 +295,8 @@ final class PhoneDemoComponent: Component { enum BackgroundDecoration { case none + case dataRain + case swirlStars case fasterStars case badgeStars } @@ -462,14 +348,12 @@ final class PhoneDemoComponent: Component { private var isCentral = false private var component: PhoneDemoComponent? - private let starsContainerView: UIView + private let decorationContainerView: UIView + private var decorationView: PhoneDemoDecorationView? private let containerView: UIView private let phoneView: PhoneView - - private var fasterStarsView: FasterStarsView? - private var badgeStarsView: BadgeStarsView? - - private var starsDisposable: Disposable? + + private var playbackStatusDisposable: Disposable? public var ready: Signal { if let videoNode = self.phoneView.videoNode { @@ -483,8 +367,8 @@ final class PhoneDemoComponent: Component { } public override init(frame: CGRect) { - self.starsContainerView = UIView(frame: frame) - self.starsContainerView.clipsToBounds = true + self.decorationContainerView = UIView(frame: frame) + self.decorationContainerView.clipsToBounds = true self.containerView = UIView(frame: frame) self.containerView.clipsToBounds = true @@ -493,7 +377,7 @@ final class PhoneDemoComponent: Component { super.init(frame: frame) - self.addSubview(self.starsContainerView) + self.addSubview(self.decorationContainerView) self.addSubview(self.containerView) self.containerView.addSubview(self.phoneView) } @@ -502,42 +386,60 @@ final class PhoneDemoComponent: Component { fatalError("init(coder:) has not been implemented") } - deinit { - self.starsDisposable?.dispose() - } +// deinit { +// self.playbackStatusDisposable?.dispose() +// } public func update(component: PhoneDemoComponent, availableSize: CGSize, environment: Environment, transition: Transition) -> CGSize { self.component = component self.containerView.frame = CGRect(origin: .zero, size: availableSize) - self.starsContainerView.frame = CGRect(origin: CGPoint(x: -availableSize.width * 0.5, y: 0.0), size: CGSize(width: availableSize.width * 2.0, height: availableSize.height)) + self.decorationContainerView.frame = CGRect(origin: CGPoint(x: -availableSize.width * 0.5, y: 0.0), size: CGSize(width: availableSize.width * 2.0, height: availableSize.height)) self.phoneView.bounds = CGRect(origin: .zero, size: phoneSize) switch component.decoration { case .none: break + case .dataRain: + if #available(iOS 10.0, *) { + if let _ = self.decorationView as? MatrixView { + } else if let rainView = MatrixView(test: true) { + rainView.frame = self.decorationContainerView.bounds.insetBy(dx: availableSize.width * 0.5, dy: 0.0) + self.decorationView = rainView + self.decorationContainerView.addSubview(rainView) + } + } + case .swirlStars: + if let _ = self.decorationView as? SwirlStarsView { + } else { + let starsView = SwirlStarsView(frame: self.decorationContainerView.bounds) + self.decorationView = starsView + self.decorationContainerView.addSubview(starsView) + } case .fasterStars: - if self.fasterStarsView == nil { - let starsView = FasterStarsView(frame: self.starsContainerView.bounds) - self.fasterStarsView = starsView - self.starsContainerView.addSubview(starsView) + if let _ = self.decorationView as? FasterStarsView { + } else { + let starsView = FasterStarsView(frame: self.decorationContainerView.bounds) + self.decorationView = starsView + self.decorationContainerView.addSubview(starsView) - self.starsDisposable = (self.phoneView.playbackStatus - |> deliverOnMainQueue).start(next: { [weak self] status in - if let strongSelf = self, let status = status { + self.playbackStatusDisposable = (self.phoneView.playbackStatus + |> deliverOnMainQueue).start(next: { [weak starsView] status in + if let starsView = starsView, let status = status { if status.timestamp > 8.0 { - strongSelf.fasterStarsView?.stopAnimation() + starsView.resetAnimation() } else if status.timestamp > 0.85 { - strongSelf.fasterStarsView?.startAnimation() + starsView.startAnimation() } } }) } case .badgeStars: - if self.badgeStarsView == nil { - let starsView = BadgeStarsView(frame: self.starsContainerView.bounds) - self.badgeStarsView = starsView - self.starsContainerView.addSubview(starsView) + if let _ = self.decorationView as? BadgeStarsView { + } else { + let starsView = BadgeStarsView(frame: self.decorationContainerView.bounds) + self.decorationView = starsView + self.decorationContainerView.addSubview(starsView) } } @@ -561,8 +463,9 @@ final class PhoneDemoComponent: Component { let isCentral = environment[DemoPageEnvironment.self].isCentral self.isCentral = isCentral - self.fasterStarsView?.setVisible(isVisible && abs(mappedPosition) < 0.4) - self.badgeStarsView?.setVisible(isVisible && abs(mappedPosition) < 0.4) + if let decorationView = self.decorationView { + decorationView.setVisible(isVisible && abs(mappedPosition) < 0.4) + } self.phoneView.center = CGPoint(x: availableSize.width / 2.0 + phoneX, y: phoneY) self.phoneView.screenRotation = mappedPosition * -0.7 @@ -575,7 +478,7 @@ final class PhoneDemoComponent: Component { self.phoneView.play() } else if !isVisible { self.phoneView.reset() - self.fasterStarsView?.stopAnimation() + self.decorationView?.resetAnimation() } if let _ = transition.userData(DemoAnimateInTransition.self), abs(mappedPosition) < .ulpOfOne { diff --git a/submodules/PremiumUI/Sources/PremiumDemoScreen.swift b/submodules/PremiumUI/Sources/PremiumDemoScreen.swift index 7c2a77decd..327de73c3b 100644 --- a/submodules/PremiumUI/Sources/PremiumDemoScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumDemoScreen.swift @@ -725,7 +725,8 @@ private final class DemoSheetContent: CombinedComponent { content: AnyComponent(PhoneDemoComponent( context: component.context, position: .bottom, - videoFile: configuration.videos["more_upload"] + videoFile: configuration.videos["more_upload"], + decoration: .dataRain )), title: strings.Premium_UploadSize, text: strings.Premium_UploadSizeInfo, @@ -760,7 +761,8 @@ private final class DemoSheetContent: CombinedComponent { content: AnyComponent(PhoneDemoComponent( context: component.context, position: .top, - videoFile: configuration.videos["voice_to_text"] + videoFile: configuration.videos["voice_to_text"], + decoration: .badgeStars )), title: strings.Premium_VoiceToText, text: strings.Premium_VoiceToTextInfo, @@ -777,7 +779,8 @@ private final class DemoSheetContent: CombinedComponent { content: AnyComponent(PhoneDemoComponent( context: component.context, position: .bottom, - videoFile: configuration.videos["no_ads"] + videoFile: configuration.videos["no_ads"], + decoration: .swirlStars )), title: strings.Premium_NoAds, text: isStandalone ? strings.Premium_NoAdsStandaloneInfo : strings.Premium_NoAdsInfo, @@ -831,7 +834,8 @@ private final class DemoSheetContent: CombinedComponent { content: AnyComponent(PhoneDemoComponent( context: component.context, position: .top, - videoFile: configuration.videos["advanced_chat_management"] + videoFile: configuration.videos["advanced_chat_management"], + decoration: .swirlStars )), title: strings.Premium_ChatManagement, text: strings.Premium_ChatManagementInfo, @@ -866,7 +870,8 @@ private final class DemoSheetContent: CombinedComponent { content: AnyComponent(PhoneDemoComponent( context: component.context, position: .top, - videoFile: configuration.videos["animated_userpics"] + videoFile: configuration.videos["animated_userpics"], + decoration: .swirlStars )), title: strings.Premium_Avatar, text: strings.Premium_AvatarInfo, diff --git a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift index 313505e395..cae6c1b59d 100644 --- a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift @@ -1161,13 +1161,10 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { }) let termsString: MultilineTextComponent.TextContent - if context.component.isPremium == true { - if let promoConfiguration = context.state.promoConfiguration { - let attributedString = stringWithAppliedEntities(promoConfiguration.status, entities: promoConfiguration.statusEntities, baseColor: termsTextColor, linkColor: environment.theme.list.itemAccentColor, baseFont: termsFont, linkFont: termsFont, boldFont: boldTermsFont, italicFont: italicTermsFont, boldItalicFont: boldItalicTermsFont, fixedFont: monospaceTermsFont, blockQuoteFont: termsFont) - termsString = .plain(attributedString) - } else { - termsString = .plain(NSAttributedString()) - } +// if context.component.isPremium == true { + if let promoConfiguration = context.state.promoConfiguration { + let attributedString = stringWithAppliedEntities(promoConfiguration.status, entities: promoConfiguration.statusEntities, baseColor: termsTextColor, linkColor: environment.theme.list.itemAccentColor, baseFont: termsFont, linkFont: termsFont, boldFont: boldTermsFont, italicFont: italicTermsFont, boldItalicFont: boldItalicTermsFont, fixedFont: monospaceTermsFont, blockQuoteFont: termsFont) + termsString = .plain(attributedString) } else { termsString = .markdown( text: strings.Premium_Terms, @@ -1403,7 +1400,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent { |> deliverOnMainQueue).start(next: { [weak self] available in if let strongSelf = self { if available { - strongSelf.paymentDisposable.set((inAppPurchaseManager.buyProduct(premiumProduct, account: strongSelf.context.account) + strongSelf.paymentDisposable.set((inAppPurchaseManager.buyProduct(premiumProduct) |> deliverOnMainQueue).start(next: { [weak self] status in if let strongSelf = self, case .purchased = status { strongSelf.activationDisposable.set((strongSelf.context.account.postbox.peerView(id: strongSelf.context.account.peerId) @@ -1418,8 +1415,14 @@ private final class PremiumIntroScreenComponent: CombinedComponent { |> mapToSignal { _ -> Signal in return .never() } + |> timeout(15.0, queue: Queue.mainQueue(), alternate: .fail(.timeout)) |> deliverOnMainQueue).start(error: { _ in - + addAppLogEvent(postbox: strongSelf.context.account.postbox, type: "premium.promo_screen_fail") + + let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } + let errorText = presentationData.strings.Premium_Purchase_ErrorUnknown + let alertController = textAlertController(context: strongSelf.context, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]) + strongSelf.present(alertController) }, completed: { [weak self] in if let strongSelf = self { let _ = updatePremiumPromoConfigurationOnce(account: strongSelf.context.account).start() @@ -1444,6 +1447,10 @@ private final class PremiumIntroScreenComponent: CombinedComponent { 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 .cancelled: break } diff --git a/submodules/PremiumUI/Sources/SwirlStarsView.swift b/submodules/PremiumUI/Sources/SwirlStarsView.swift new file mode 100644 index 0000000000..6b80754777 --- /dev/null +++ b/submodules/PremiumUI/Sources/SwirlStarsView.swift @@ -0,0 +1,204 @@ +import Foundation +import UIKit +import SceneKit +import Display +import AppBundle +import SwiftSignalKit + +final class SwirlStarsView: UIView, PhoneDemoDecorationView { + private let sceneView: SCNView + + private var particles: SCNNode? + + override init(frame: CGRect) { + self.sceneView = SCNView(frame: CGRect(origin: .zero, size: frame.size)) + self.sceneView.backgroundColor = .clear + if let url = getAppBundle().url(forResource: "swirl", withExtension: "scn") { + self.sceneView.scene = try? SCNScene(url: url, options: nil) + } + self.sceneView.isUserInteractionEnabled = false + self.sceneView.preferredFramesPerSecond = 60 + + super.init(frame: frame) + + self.alpha = 0.0 + + self.addSubview(self.sceneView) + + self.particles = self.sceneView.scene?.rootNode.childNode(withName: "particles", recursively: false) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + self.particles = nil + } + + func setVisible(_ visible: Bool) { + if visible, let particles = self.particles, particles.parent == nil { + self.sceneView.scene?.rootNode.addChildNode(particles) + } + self.setupAnimations() + + let transition = ContainedViewLayoutTransition.animated(duration: 0.3, curve: .linear) + transition.updateAlpha(layer: self.layer, alpha: visible ? 0.6 : 0.0, completion: { [weak self] finished in + if let strongSelf = self, finished && !visible && strongSelf.particles?.parent != nil { + strongSelf.particles?.removeFromParentNode() + + if let node = strongSelf.sceneView.scene?.rootNode.childNode(withName: "star", recursively: false) { + node.removeAllAnimations() + } + } + }) + } + + func setupAnimations() { + guard let node = self.sceneView.scene?.rootNode.childNode(withName: "star", recursively: false), node.animationKeys.isEmpty else { + return + } + + let initial = node.eulerAngles + let target = SCNVector3(x: node.eulerAngles.x + .pi * 2.0, y: node.eulerAngles.y, z: node.eulerAngles.z) + + let animation = CABasicAnimation(keyPath: "eulerAngles") + animation.fromValue = NSValue(scnVector3: initial) + animation.toValue = NSValue(scnVector3: target) + animation.duration = 1.5 + animation.timingFunction = CAMediaTimingFunction(name: .linear) + animation.fillMode = .forwards + animation.repeatCount = .infinity + node.addAnimation(animation, forKey: "rotation") + + self.setupMovementAnimation() + } + + func setupMovementAnimation() { + guard let node = self.sceneView.scene?.rootNode.childNode(withName: "star", recursively: false) else { + return + } + + node.position = SCNVector3(3.5, 0.0, -2.0) + let firstPath = UIBezierPath() + firstPath.move(to: CGPoint(x: 3.5, y: -2.0)) + firstPath.addLine(to: CGPoint(x: -15.5, y: 15.5)) + + let firstAction = SCNAction.moveAlong(path: firstPath, duration: 2.0) + + SCNTransaction.begin() + SCNTransaction.animationDuration = 2.0 + node.runAction(firstAction) + SCNTransaction.completionBlock = { [weak self, weak node] in + Queue.mainQueue().after(2.2, { + node?.position = SCNVector3(0.0, 0.0, -3.0) + let secondPath = UIBezierPath() + secondPath.move(to: CGPoint(x: 0.0, y: -3.0)) + secondPath.addLine(to: CGPoint(x: 15.5, y: 20.0)) + + let secondAction = SCNAction.moveAlong(path: secondPath, duration: 2.0) + SCNTransaction.begin() + SCNTransaction.animationDuration = 2.0 + node?.runAction(secondAction) + SCNTransaction.completionBlock = { [weak self] in + Queue.mainQueue().after(2.2, { + self?.setupMovementAnimation() + }) + } + SCNTransaction.commit() + }) + } + SCNTransaction.commit() + } + + func resetAnimation() { + } + + override func layoutSubviews() { + super.layoutSubviews() + + self.sceneView.frame = CGRect(origin: .zero, size: frame.size) + } +} + +extension UIBezierPath { + var elements: [PathElement] { + var pathElements = [PathElement]() + withUnsafeMutablePointer(to: &pathElements) { elementsPointer in + cgPath.apply(info: elementsPointer) { (userInfo, nextElementPointer) in + let nextElement = PathElement(element: nextElementPointer.pointee) + let elementsPointer = userInfo!.assumingMemoryBound(to: [PathElement].self) + elementsPointer.pointee.append(nextElement) + } + } + return pathElements + } +} + +enum PathElement { + case moveToPoint(CGPoint) + case addLineToPoint(CGPoint) + case addQuadCurveToPoint(CGPoint, CGPoint) + case addCurveToPoint(CGPoint, CGPoint, CGPoint) + case closeSubpath + + init(element: CGPathElement) { + switch element.type { + case .moveToPoint: + self = .moveToPoint(element.points[0]) + case .addLineToPoint: + self = .addLineToPoint(element.points[0]) + case .addQuadCurveToPoint: + self = .addQuadCurveToPoint(element.points[0], element.points[1]) + case .addCurveToPoint: + self = .addCurveToPoint(element.points[0], element.points[1], element.points[2]) + case .closeSubpath: + self = .closeSubpath + @unknown default: + self = .closeSubpath + } + } +} + +public extension SCNAction { + class func moveAlong(path: UIBezierPath, duration animationDuration: Double) -> SCNAction { + let points = path.elements + var actions = [SCNAction]() + + for point in points { + switch point { + case .moveToPoint(let a): + let moveAction = SCNAction.move(to: SCNVector3(a.x, 0, a.y), duration: animationDuration) + actions.append(moveAction) + break + + case .addCurveToPoint(let a, let b, let c): + let moveAction1 = SCNAction.move(to: SCNVector3(a.x, 0, a.y), duration: animationDuration) + let moveAction2 = SCNAction.move(to: SCNVector3(b.x, 0, b.y), duration: animationDuration) + let moveAction3 = SCNAction.move(to: SCNVector3(c.x, 0, c.y), duration: animationDuration) + actions.append(moveAction1) + actions.append(moveAction2) + actions.append(moveAction3) + break + + case .addLineToPoint(let a): + let moveAction = SCNAction.move(to: SCNVector3(a.x, 0, a.y), duration: animationDuration) + actions.append(moveAction) + break + + case .addQuadCurveToPoint(let a, let b): + let moveAction1 = SCNAction.move(to: SCNVector3(a.x, 0, a.y), duration: animationDuration) + let moveAction2 = SCNAction.move(to: SCNVector3(b.x, 0, b.y), duration: animationDuration) + actions.append(moveAction1) + actions.append(moveAction2) + break + + default: + let moveAction = SCNAction.move(to: SCNVector3(0, 0, 0), duration: animationDuration) + actions.append(moveAction) + break + } + } + return SCNAction.sequence(actions) + } +} diff --git a/submodules/ReactionSelectionNode/Sources/ReactionSelectionNode.swift b/submodules/ReactionSelectionNode/Sources/ReactionSelectionNode.swift index 62ef7d5b8b..00956b337e 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionSelectionNode.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionSelectionNode.swift @@ -321,46 +321,82 @@ public final class ReactionNode: ASDisplayNode, ReactionItemNode { } } -private func generatePremiumReactionIcon() -> UIImage? { - return generateImage(CGSize(width: 32.0, height: 32.0), contextGenerator: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - if let backgroundImage = UIImage(bundleImageName: "Premium/BackgroundIcon"), let foregroundImage = UIImage(bundleImageName: "Premium/ForegroundIcon") { - context.saveGState() - if let cgImage = backgroundImage.cgImage { - context.clip(to: CGRect(origin: .zero, size: size), mask: cgImage) - } - - let colorsArray: [CGColor] = [ - UIColor(rgb: 0x6B93FF).cgColor, - UIColor(rgb: 0x6B93FF).cgColor, - UIColor(rgb: 0x976FFF).cgColor, - UIColor(rgb: 0xE46ACE).cgColor, - UIColor(rgb: 0xE46ACE).cgColor - ] - var locations: [CGFloat] = [0.0, 0.15, 0.5, 0.85, 1.0] - let gradient = CGGradient(colorsSpace: deviceColorSpace, colors: colorsArray as CFArray, locations: &locations)! - - context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: size.width, y: size.height), options: CGGradientDrawingOptions()) - - context.restoreGState() - - if let cgImage = foregroundImage.cgImage { - context.clip(to: CGRect(origin: .zero, size: size), mask: cgImage) - } - context.setFillColor(UIColor.white.cgColor) - context.fill(CGRect(origin: CGPoint(), size: size)) +private let starsCount = 7 +private final class StarsNode: ASDisplayNode { + private let starNodes: [ASImageNode] + private var timer: SwiftSignalKit.Timer? + + override init() { + let image = UIImage(bundleImageName: "Premium/ReactionsStar") + var starNodes: [ASImageNode] = [] + for _ in 0 ..< starsCount { + let node = ASImageNode() + node.alpha = 0.0 + node.image = image + node.displaysAsynchronously = false + starNodes.append(node) } - }) + self.starNodes = starNodes + + super.init() + + for node in starNodes { + self.addSubnode(node) + } + + self.setup(firstTime: true) + + self.timer = SwiftSignalKit.Timer(timeout: 0.5, repeat: true, completion: { [weak self] in + self?.setup() + }, queue: Queue.mainQueue()) + self.timer?.start() + } + + deinit { + self.timer?.invalidate() + } + + func setup(firstTime: Bool = false) { + let size = CGSize(width: 32.0, height: 32.0) + let starSize = CGSize(width: 6.0, height: 8.0) + + for node in self.starNodes { + if node.layer.animation(forKey: "transform.scale") == nil && node.layer.animation(forKey: "opacity") == nil { + let x = CGFloat.random(in: 0 ..< size.width) + let y = CGFloat.random(in: 0 ..< size.width) + + let randomTargetScale = CGFloat.random(in: 0.8 ..< 1.0) + node.bounds = CGRect(origin: .zero, size: starSize) + node.position = CGPoint(x: x, y: y) + + node.alpha = 1.0 + + let duration = CGFloat.random(in: 0.4 ..< 0.65) + let delay = firstTime ? CGFloat.random(in: 0.0 ..< 0.25) : 0.0 + node.layer.animateScale(from: 0.001, to: randomTargetScale, duration: duration, delay: delay, removeOnCompletion: false, completion: { [weak self, weak node] _ in + let duration = CGFloat.random(in: 0.3 ..< 0.35) + node?.alpha = 0.0 + node?.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration, removeOnCompletion: false, completion: { [weak self, weak node] _ in + node?.layer.removeAllAnimations() + self?.setup() + }) + }) + } + } + } } final class PremiumReactionsNode: ASDisplayNode, ReactionItemNode { var isExtracted: Bool = false - var backgroundView: UIVisualEffectView? - let backgroundMaskNode: ASImageNode - let backgroundOverlayNode: ASImageNode - let imageNode: ASImageNode - let maskImageNode: ASImageNode + private var backgroundView: UIVisualEffectView? + private let backgroundMaskNode: ASImageNode + private let backgroundOverlayNode: ASImageNode + private let imageNode: ASImageNode + private var starsNode: StarsNode? + + private let maskContainerNode: ASDisplayNode + private let maskImageNode: ASImageNode init(theme: PresentationTheme) { self.backgroundMaskNode = ASImageNode() @@ -370,7 +406,7 @@ final class PremiumReactionsNode: ASDisplayNode, ReactionItemNode { self.backgroundMaskNode.image = UIImage(bundleImageName: "Premium/ReactionsBackground") self.backgroundOverlayNode = ASImageNode() - self.backgroundOverlayNode.alpha = 0.05 + self.backgroundOverlayNode.alpha = 0.1 self.backgroundOverlayNode.contentMode = .center self.backgroundOverlayNode.displaysAsynchronously = false self.backgroundOverlayNode.isUserInteractionEnabled = false @@ -382,20 +418,24 @@ final class PremiumReactionsNode: ASDisplayNode, ReactionItemNode { self.imageNode.isUserInteractionEnabled = false self.imageNode.image = UIImage(bundleImageName: "Premium/ReactionsForeground") + self.maskContainerNode = ASDisplayNode() + self.maskImageNode = ASImageNode() if let backgroundImage = UIImage(bundleImageName: "Premium/ReactionsBackground") { - self.maskImageNode.image = generateImage(CGSize(width: 40.0, height: 52.0), contextGenerator: { size, context in + self.maskImageNode.image = generateImage(CGSize(width: 40.0 * 4.0, height: 52.0 * 4.0), contextGenerator: { size, context in context.setFillColor(UIColor.black.cgColor) context.fill(CGRect(origin: .zero, size: size)) if let cgImage = backgroundImage.cgImage { - let maskFrame = CGRect(origin: .zero, size: size).insetBy(dx: 4.0, dy: 10.0) + let maskFrame = CGRect(origin: .zero, size: size).insetBy(dx: 4.0 + 40.0 * 2.0 - 16.0, dy: 10.0 + 52.0 * 2.0 - 16.0) context.clip(to: maskFrame, mask: cgImage) } context.setBlendMode(.clear) context.fill(CGRect(origin: .zero, size: size)) }) } + self.maskImageNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((40.0 - 40.0 * 4.0) / 2.0), y: floorToScreenPixels((52.0 - 52.0 * 4.0) / 2.0)), size: CGSize(width: 40.0 * 4.0, height: 52.0 * 4.0)) + self.maskContainerNode.addSubnode(self.maskImageNode) super.init() @@ -416,10 +456,37 @@ final class PremiumReactionsNode: ASDisplayNode, ReactionItemNode { backgroundView.mask = self.backgroundMaskNode.view self.view.insertSubview(backgroundView, at: 0) self.backgroundView = backgroundView + + let starsNode = StarsNode() + starsNode.frame = CGRect(origin: .zero, size: CGSize(width: 32.0, height: 32.0)) + self.backgroundView?.contentView.addSubview(starsNode.view) + self.starsNode = starsNode } func appear(animated: Bool) { - + if animated { + let delay: Double = 0.1 + let duration: Double = 0.85 + let damping: CGFloat = 60.0 + + let initialScale: CGFloat = 0.25 + self.maskImageNode.layer.animateSpring(from: initialScale as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: duration, delay: delay, damping: damping) + self.backgroundView?.layer.animateSpring(from: initialScale as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: duration, delay: delay, damping: damping) + self.backgroundOverlayNode.layer.animateSpring(from: initialScale as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: duration, delay: delay, damping: damping) + self.imageNode.layer.animateSpring(from: initialScale as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: duration, delay: delay, damping: damping) + + Queue.mainQueue().after(0.25, { + let shimmerNode = ASImageNode() + shimmerNode.displaysAsynchronously = false + shimmerNode.image = generateGradientImage(size: CGSize(width: 32.0, height: 32.0), colors: [UIColor(rgb: 0xffffff, alpha: 0.0), UIColor(rgb: 0xffffff, alpha: 0.24), UIColor(rgb: 0xffffff, alpha: 0.0)], locations: [0.0, 0.5, 1.0], direction: .horizontal) + shimmerNode.frame = CGRect(origin: .zero, size: CGSize(width: 32.0, height: 32.0)) + self.backgroundView?.contentView.addSubview(shimmerNode.view) + + shimmerNode.layer.animatePosition(from: CGPoint(x: -60.0, y: 0.0), to: CGPoint(x: 60.0, y: 0.0), duration: 0.75, removeOnCompletion: false, additive: true, completion: { [weak shimmerNode] _ in + shimmerNode?.view.removeFromSuperview() + }) + }) + } } func updateLayout(size: CGSize, isExpanded: Bool, largeExpanded: Bool, isPreviewing: Bool, transition: ContainedViewLayoutTransition) { @@ -430,8 +497,7 @@ final class PremiumReactionsNode: ASDisplayNode, ReactionItemNode { self.imageNode.frame = bounds } - var maskNode: ASDisplayNode? { - return self.maskImageNode + return self.maskContainerNode } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/AppStore.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/AppStore.swift index 8dd0f98cef..4a8b1c68b0 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/AppStore.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/AppStore.swift @@ -6,6 +6,7 @@ import TelegramApi public enum AssignAppStoreTransactionError { case generic + case timeout } func _internal_sendAppStoreReceipt(account: Account, receipt: Data, restore: Bool) -> Signal { diff --git a/submodules/TelegramUI/Images.xcassets/Premium/ReactionsStar.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Premium/ReactionsStar.imageset/Contents.json new file mode 100644 index 0000000000..18355ee5bf --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Premium/ReactionsStar.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "ministar@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Premium/ReactionsStar.imageset/ministar@3x.png b/submodules/TelegramUI/Images.xcassets/Premium/ReactionsStar.imageset/ministar@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..efd6a2ef5a49cd8b74c63a9567b46c26ca097493 GIT binary patch literal 341 zcmV-b0jmCqP)T)S*tpP!xypuL~o~vIPP&cmxv|f}0}X7X%g!JPHqh z1xS#?Sy5wgK$|qc)%; zV5be}1UPI8Z37eFxG8i7^vTB?N0Vx(2kMugHnaX>dnO6R;UWQlVF%R~JfwK7p(*lyXM> n;`!YaDzBQKSSV$*C{We{%t$oU2G Date: Sat, 11 Jun 2022 11:49:36 +0400 Subject: [PATCH 02/10] Various fixes --- .../Telegram-iOS/en.lproj/Localizable.strings | 11 +++ .../ChatListFilterPresetListItem.swift | 36 +++++----- .../ChatListFilterTabContainerNode.swift | 4 +- .../Sources/ContactsPeerItem.swift | 2 +- .../Sources/OldChannelsController.swift | 27 ++++++- .../Sources/PremiumIntroScreen.swift | 8 ++- .../TelegramUI/Sources/ChatController.swift | 2 +- .../TelegramUI/Sources/ChatTitleView.swift | 22 +++--- .../Sources/PeerInfo/PeerInfoScreen.swift | 72 ++++++++++++------- 9 files changed, 121 insertions(+), 63 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index a93c850ef2..3745e0790a 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -7564,6 +7564,15 @@ Sorry for the inconvenience."; "OldChannels.TooManyCommunitiesText" = "You are a member of **%@** groups and channels. Please leave some before joining a new one or upgrade to **Telegram Premium** to double the limit to **%@** groups and channels."; "OldChannels.TooManyCommunitiesNoPremiumText" = "You are a member of **%@** groups and channels. Please leave some before joining a new one. We are working to let you increase this limit in the future."; "OldChannels.TooManyCommunitiesFinalText" = "You are a member of **%@** groups and channels. Please leave some before joining a new one."; + +"OldChannels.TooManyCommunitiesCreateText" = "You are a member of **%@** groups and channels. Please leave some before creating a new one or upgrade to **Telegram Premium** to double the limit to **%@** groups and channels."; +"OldChannels.TooManyCommunitiesCreateNoPremiumText" = "You are a member of **%@** groups and channels. Please leave some before creating a new one. We are working to let you increase this limit in the future."; +"OldChannels.TooManyCommunitiesCreateFinalText" = "You are a member of **%@** groups and channels. Please leave some before creating a new one."; + +"OldChannels.TooManyCommunitiesUpgradeText" = "You are a member of **%@** groups and channels. For technical reasons, you need to leave some first before changing this setting in your groups or upgrade to **Telegram Premium** to double the limit to **%@** groups and channels."; +"OldChannels.TooManyCommunitiesUpgradeNoPremiumText" = "You are a member of **%@** groups and channels. For technical reasons, you need to leave some first before changing this setting in your groups. We are working to let you increase this limit in the future."; +"OldChannels.TooManyCommunitiesUpgradeFinalText" = "You are a member of **%@** groups and channels. For technical reasons, you need to leave some first before changing this setting in your groups."; + "OldChannels.LeaveCommunities_1" = "Leave %@ Community"; "OldChannels.LeaveCommunities_any" = "Leave %@ Communities"; @@ -7714,3 +7723,5 @@ Sorry for the inconvenience."; "Premium.Purchase.ErrorCantMakePayments" = "In-app purchases are not allowed on this device."; "Settings.Premium" = "Telegram Premium"; + +"Settings.AddAnotherAccount.PremiumHelp" = "You can add up to four accounts with different phone numbers."; diff --git a/submodules/ChatListUI/Sources/ChatListFilterPresetListItem.swift b/submodules/ChatListUI/Sources/ChatListFilterPresetListItem.swift index 431fc37e46..efecbd9832 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterPresetListItem.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterPresetListItem.swift @@ -276,6 +276,23 @@ private final class ChatListFilterPresetListItemNode: ItemListRevealOptionsItemN transition = .immediate } + if let reorderControlSizeAndApply = reorderControlSizeAndApply { + if strongSelf.reorderControlNode == nil { + let reorderControlNode = reorderControlSizeAndApply.1(layout.contentSize.height, false, .immediate) + strongSelf.reorderControlNode = reorderControlNode + strongSelf.controlsContainer.addSubnode(reorderControlNode) + reorderControlNode.alpha = 0.0 + transition.updateAlpha(node: reorderControlNode, alpha: 1.0) + } + let reorderControlFrame = CGRect(origin: CGPoint(x: params.width + revealOffset - params.rightInset - reorderControlSizeAndApply.0, y: 0.0), size: CGSize(width: reorderControlSizeAndApply.0, height: layout.contentSize.height)) + strongSelf.reorderControlNode?.frame = reorderControlFrame + } else if let reorderControlNode = strongSelf.reorderControlNode { + strongSelf.reorderControlNode = nil + transition.updateAlpha(node: reorderControlNode, alpha: 0.0, completion: { [weak reorderControlNode] _ in + reorderControlNode?.removeFromSupernode() + }) + } + if let editableControlSizeAndApply = editableControlSizeAndApply { let editableControlFrame = CGRect(origin: CGPoint(x: params.leftInset + revealOffset, y: 0.0), size: CGSize(width: editableControlSizeAndApply.0, height: layout.contentSize.height)) if strongSelf.editableControlNode == nil { @@ -306,24 +323,7 @@ private final class ChatListFilterPresetListItemNode: ItemListRevealOptionsItemN }) } strongSelf.editableControlNode?.isHidden = !item.canBeDeleted - - if let reorderControlSizeAndApply = reorderControlSizeAndApply { - if strongSelf.reorderControlNode == nil { - let reorderControlNode = reorderControlSizeAndApply.1(layout.contentSize.height, false, .immediate) - strongSelf.reorderControlNode = reorderControlNode - strongSelf.addSubnode(reorderControlNode) - reorderControlNode.alpha = 0.0 - transition.updateAlpha(node: reorderControlNode, alpha: 1.0) - } - let reorderControlFrame = CGRect(origin: CGPoint(x: params.width + revealOffset - params.rightInset - reorderControlSizeAndApply.0, y: 0.0), size: CGSize(width: reorderControlSizeAndApply.0, height: layout.contentSize.height)) - strongSelf.reorderControlNode?.frame = reorderControlFrame - } else if let reorderControlNode = strongSelf.reorderControlNode { - strongSelf.reorderControlNode = nil - transition.updateAlpha(node: reorderControlNode, alpha: 0.0, completion: { [weak reorderControlNode] _ in - reorderControlNode?.removeFromSupernode() - }) - } - + let _ = titleApply() let _ = labelApply() diff --git a/submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift b/submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift index a752b1ec32..b9f8461875 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift @@ -294,7 +294,7 @@ private final class ItemNode: ASDisplayNode { if let deleteButtonNode = self.deleteButtonNode { if let theme = self.theme { let deleteButtonSize = deleteButtonNode.update(theme: theme) - deleteButtonNode.frame = CGRect(origin: CGPoint(x: -deleteButtonSize.width, y: 5.0), size: deleteButtonSize) + deleteButtonNode.frame = CGRect(origin: CGPoint(x: -deleteButtonSize.width + 3.0, y: 5.0), size: deleteButtonSize) } } @@ -788,7 +788,7 @@ final class ChatListFilterTabContainerNode: ASDisplayNode { selectionFraction = 0.0 } - itemNode.updateText(strings: presentationData.strings, title: filter.title(strings: presentationData.strings), shortTitle: filter.shortTitle(strings: presentationData.strings), unreadCount: unreadCount, unreadHasUnmuted: unreadHasUnmuted, isNoFilter: isNoFilter, selectionFraction: selectionFraction, isEditing: isEditing, isReordering: isReordering, canReorderAllChats: canReorderAllChats, isDisabled: isDisabled, presentationData: presentationData, transition: itemNodeTransition) + itemNode.updateText(strings: presentationData.strings, title: filter.title(strings: presentationData.strings), shortTitle: i == 0 ? filter.shortTitle(strings: presentationData.strings) : filter.title(strings: presentationData.strings), unreadCount: unreadCount, unreadHasUnmuted: unreadHasUnmuted, isNoFilter: isNoFilter, selectionFraction: selectionFraction, isEditing: isEditing, isReordering: isReordering, canReorderAllChats: canReorderAllChats, isDisabled: isDisabled, presentationData: presentationData, transition: itemNodeTransition) } var removeKeys: [ChatListFilterTabEntryId] = [] for (id, _) in self.itemNodes { diff --git a/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift b/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift index 70bd21072f..ea842498af 100644 --- a/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift +++ b/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift @@ -932,7 +932,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { iconNode.isLayerBacked = true iconNode.displaysAsynchronously = false iconNode.displayWithoutProcessing = true - strongSelf.containerNode.addSubnode(iconNode) + strongSelf.offsetContainerNode.addSubnode(iconNode) strongSelf.credibilityIconNode = iconNode } iconNode.image = currentCredibilityIconImage diff --git a/submodules/PeerInfoUI/Sources/OldChannelsController.swift b/submodules/PeerInfoUI/Sources/OldChannelsController.swift index f546d714cc..9b01607fbd 100644 --- a/submodules/PeerInfoUI/Sources/OldChannelsController.swift +++ b/submodules/PeerInfoUI/Sources/OldChannelsController.swift @@ -191,12 +191,33 @@ private func oldChannelsEntries(presentationData: PresentationData, state: OldCh let count = max(limit, Int32(peers?.count ?? 0)) var text: String? if count >= premiumLimit { - text = presentationData.strings.OldChannels_TooManyCommunitiesFinalText("\(premiumLimit)").string + switch intent { + case .create: + text = presentationData.strings.OldChannels_TooManyCommunitiesCreateFinalText("\(premiumLimit)").string + case .upgrade: + text = presentationData.strings.OldChannels_TooManyCommunitiesUpgradeFinalText("\(premiumLimit)").string + case .join: + text = presentationData.strings.OldChannels_TooManyCommunitiesFinalText("\(premiumLimit)").string + } } else if count >= limit { if isPremiumDisabled { - text = presentationData.strings.OldChannels_TooManyCommunitiesNoPremiumText("\(count)").string + switch intent { + case .create: + text = presentationData.strings.OldChannels_TooManyCommunitiesCreateNoPremiumText("\(premiumLimit)").string + case .upgrade: + text = presentationData.strings.OldChannels_TooManyCommunitiesUpgradeNoPremiumText("\(premiumLimit)").string + case .join: + text = presentationData.strings.OldChannels_TooManyCommunitiesNoPremiumText("\(count)").string + } } else { - text = presentationData.strings.OldChannels_TooManyCommunitiesText("\(count)", "\(premiumLimit)").string + switch intent { + case .create: + text = presentationData.strings.OldChannels_TooManyCommunitiesCreateText("\(count)", "\(premiumLimit)").string + case .upgrade: + text = presentationData.strings.OldChannels_TooManyCommunitiesUpgradeText("\(count)", "\(premiumLimit)").string + case .join: + text = presentationData.strings.OldChannels_TooManyCommunitiesText("\(count)", "\(premiumLimit)").string + } } } diff --git a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift index cae6c1b59d..aac2278425 100644 --- a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift @@ -1161,7 +1161,6 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { }) let termsString: MultilineTextComponent.TextContent -// if context.component.isPremium == true { if let promoConfiguration = context.state.promoConfiguration { let attributedString = stringWithAppliedEntities(promoConfiguration.status, entities: promoConfiguration.statusEntities, baseColor: termsTextColor, linkColor: environment.theme.list.itemAccentColor, baseFont: termsFont, linkFont: termsFont, boldFont: boldTermsFont, italicFont: italicTermsFont, boldItalicFont: boldItalicTermsFont, fixedFont: monospaceTermsFont, blockQuoteFont: termsFont) termsString = .plain(attributedString) @@ -1390,6 +1389,13 @@ private final class PremiumIntroScreenComponent: CombinedComponent { return } + guard !self.context.account.testingEnvironment else { + let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } + let alertController = textAlertController(context: self.context, title: nil, text: "Telegram Premium purchase is not available in the test environment.", actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]) + self.present(alertController) + return + } + addAppLogEvent(postbox: self.context.account.postbox, type: "premium.promo_screen_accept") self.inProgress = true diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 2a6d52527e..5fd0ef4b1a 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -3602,7 +3602,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return true } - self.chatTitleView = ChatTitleView(account: self.context.account, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder) + self.chatTitleView = ChatTitleView(context: self.context, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder) self.navigationItem.titleView = self.chatTitleView self.chatTitleView?.pressed = { [weak self] in if let strongSelf = self { diff --git a/submodules/TelegramUI/Sources/ChatTitleView.swift b/submodules/TelegramUI/Sources/ChatTitleView.swift index 0feb087087..7261c51428 100644 --- a/submodules/TelegramUI/Sources/ChatTitleView.swift +++ b/submodules/TelegramUI/Sources/ChatTitleView.swift @@ -16,6 +16,7 @@ import LocalizedPeerData import PhoneNumberFormat import ChatTitleActivityNode import AnimatedCountLabelNode +import AccountContext private let titleFont = Font.with(size: 17.0, design: .regular, weight: .semibold, traits: [.monospacedNumbers]) private let subtitleFont = Font.regular(13.0) @@ -46,7 +47,7 @@ private enum ChatTitleCredibilityIcon { } final class ChatTitleView: UIView, NavigationBarTitleView { - private let account: Account + private let context: AccountContext private var theme: PresentationTheme private var hasEmbeddedTitleContent: Bool = false @@ -122,7 +123,7 @@ final class ChatTitleView: UIView, NavigationBarTitleView { segments = [.text(0, NSAttributedString(string: typeText, font: titleFont, textColor: titleTheme.rootController.navigationBar.primaryTextColor))] isEnabled = false } else if isScheduledMessages { - if peerView.peerId == self.account.peerId { + if peerView.peerId == self.context.account.peerId { segments = [.text(0, NSAttributedString(string: self.strings.ScheduledMessages_RemindersTitle, font: titleFont, textColor: titleTheme.rootController.navigationBar.primaryTextColor))] } else { segments = [.text(0, NSAttributedString(string: self.strings.ScheduledMessages_Title, font: titleFont, textColor: titleTheme.rootController.navigationBar.primaryTextColor))] @@ -130,7 +131,7 @@ final class ChatTitleView: UIView, NavigationBarTitleView { isEnabled = false } else { if let peer = peerViewMainPeer(peerView) { - if peerView.peerId == self.account.peerId { + if peerView.peerId == self.context.account.peerId { segments = [.text(0, NSAttributedString(string: self.strings.Conversation_SavedMessages, font: titleFont, textColor: titleTheme.rootController.navigationBar.primaryTextColor))] } else { if !peerView.peerIsContact, let user = peer as? TelegramUser, !user.flags.contains(.isSupport), user.botInfo == nil, let phone = user.phone, !phone.isEmpty { @@ -139,14 +140,15 @@ final class ChatTitleView: UIView, NavigationBarTitleView { segments = [.text(0, NSAttributedString(string: EnginePeer(peer).displayTitle(strings: self.strings, displayOrder: self.nameDisplayOrder), font: titleFont, textColor: titleTheme.rootController.navigationBar.primaryTextColor))] } } - if peer.id != self.account.peerId { + if peer.id != self.context.account.peerId { + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: self.context.currentAppConfiguration.with { $0 }) if peer.isFake { titleCredibilityIcon = .fake } else if peer.isScam { titleCredibilityIcon = .scam } else if peer.isVerified { titleCredibilityIcon = .verified - } else if peer.isPremium { + } else if peer.isPremium && !premiumConfiguration.isPremiumDisabled { titleCredibilityIcon = .premium } } @@ -303,7 +305,7 @@ final class ChatTitleView: UIView, NavigationBarTitleView { switch titleContent { case let .peer(peerView, _, isScheduledMessages): if let peer = peerViewMainPeer(peerView) { - if peer.id == self.account.peerId || isScheduledMessages || peer.id.isReplies { + if peer.id == self.context.account.peerId || isScheduledMessages || peer.id.isReplies { inputActivitiesAllowed = false } } @@ -405,7 +407,7 @@ final class ChatTitleView: UIView, NavigationBarTitleView { case let .peer(peerView, onlineMemberCount, isScheduledMessages): if let peer = peerViewMainPeer(peerView) { let servicePeer = isServicePeer(peer) - if peer.id == self.account.peerId || isScheduledMessages || peer.id.isReplies { + if peer.id == self.context.account.peerId || isScheduledMessages || peer.id.isReplies { let string = NSAttributedString(string: "", font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor) state = .info(string, .generic) } else if let user = peer as? TelegramUser { @@ -541,13 +543,13 @@ final class ChatTitleView: UIView, NavigationBarTitleView { } } - init(account: Account, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder) { - self.account = account + init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder) { + self.context = context self.theme = theme self.strings = strings self.dateTimeFormat = dateTimeFormat self.nameDisplayOrder = nameDisplayOrder - + self.contentContainer = ASDisplayNode() self.titleNode = ImmediateAnimatedCountLabelNode() diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index e8990e4def..e4369d1b43 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -842,7 +842,23 @@ private func settingsEditingItems(data: PeerInfoScreenData?, state: PeerInfoStat items[.account]!.append(PeerInfoScreenActionItem(id: ItemAddAccount, text: presentationData.strings.Settings_AddAnotherAccount, alignment: .center, action: { interaction.openSettings(.addAccount) })) - items[.account]!.append(PeerInfoScreenCommentItem(id: ItemAddAccountHelp, text: presentationData.strings.Settings_AddAnotherAccount_Help)) + + var hasPremiumAccounts = false + if data.peer?.isPremium == true && !context.account.testingEnvironment { + hasPremiumAccounts = true + } + if let settings = data.globalSettings { + for (accountContext, peer, _) in settings.accountsAndPeers { + if !accountContext.account.testingEnvironment { + if peer.isPremium { + hasPremiumAccounts = true + break + } + } + } + } + + items[.account]!.append(PeerInfoScreenCommentItem(id: ItemAddAccountHelp, text: hasPremiumAccounts ? presentationData.strings.Settings_AddAnotherAccount_PremiumHelp : presentationData.strings.Settings_AddAnotherAccount_Help)) items[.logout]!.append(PeerInfoScreenActionItem(id: ItemLogout, text: presentationData.strings.Settings_Logout, color: .destructive, alignment: .center, action: { interaction.openSettings(.logout) @@ -6275,36 +6291,38 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate case .username: push(usernameSetupController(context: self.context)) case .addAccount: - var maximumAvailableAccounts: Int = 3 - if self.data?.peer?.isPremium == true && !self.context.account.testingEnvironment { - maximumAvailableAccounts = 4 - } - var count: Int = 1 - if let settings = self.data?.globalSettings { - for (accountContext, peer, _) in settings.accountsAndPeers { - if !accountContext.account.testingEnvironment { - if peer.isPremium { - maximumAvailableAccounts = 4 + if let data = self.data { + var maximumAvailableAccounts: Int = 3 + if data.peer?.isPremium == true && !self.context.account.testingEnvironment { + maximumAvailableAccounts = 4 + } + var count: Int = 1 + if let settings = data.globalSettings { + for (accountContext, peer, _) in settings.accountsAndPeers { + if !accountContext.account.testingEnvironment { + if peer.isPremium { + maximumAvailableAccounts = 4 + } + count += 1 } - count += 1 } } - } - if count >= maximumAvailableAccounts { - let context = self.context - var replaceImpl: ((ViewController) -> Void)? - let controller = PremiumLimitScreen(context: context, subject: .accounts, count: Int32(count), action: { - let controller = PremiumIntroScreen(context: context, source: .accounts) - replaceImpl?(controller) - }) - replaceImpl = { [weak controller] c in - controller?.replace(with: c) + if count >= maximumAvailableAccounts { + let context = self.context + var replaceImpl: ((ViewController) -> Void)? + let controller = PremiumLimitScreen(context: context, subject: .accounts, count: Int32(count), action: { + let controller = PremiumIntroScreen(context: context, source: .accounts) + replaceImpl?(controller) + }) + replaceImpl = { [weak controller] c in + controller?.replace(with: c) + } + if let navigationController = context.sharedContext.mainWindow?.viewController as? NavigationController { + navigationController.pushViewController(controller) + } + } else { + self.context.sharedContext.beginNewAuth(testingEnvironment: self.context.account.testingEnvironment) } - if let navigationController = context.sharedContext.mainWindow?.viewController as? NavigationController { - navigationController.pushViewController(controller) - } - } else { - self.context.sharedContext.beginNewAuth(testingEnvironment: self.context.account.testingEnvironment) } case .logout: if let user = self.data?.peer as? TelegramUser, let phoneNumber = user.phone { From eaf6a69cfbea60fcecf6bb98b1a031c5717ed6c3 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Sat, 11 Jun 2022 14:38:44 +0400 Subject: [PATCH 03/10] Various fixes --- .../Telegram-iOS/en.lproj/Localizable.strings | 4 +++ .../Sources/InAppPurchaseManager.swift | 36 ++++++++++--------- .../Sources/ChatAvatarNavigationNode.swift | 2 +- .../Sources/ChatMessageDateHeader.swift | 2 +- submodules/TelegramUI/Sources/OpenUrl.swift | 15 ++++++++ 5 files changed, 41 insertions(+), 18 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 3745e0790a..619b58a565 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -7722,6 +7722,10 @@ Sorry for the inconvenience."; "Premium.Purchase.ErrorNotAllowed" = "The device is not not allowed to make the payment."; "Premium.Purchase.ErrorCantMakePayments" = "In-app purchases are not allowed on this device."; +"Premium.Restore.Success" = "Done"; +"Premium.Restore.ErrorUnknown" = "An error occurred. Please try again."; + + "Settings.Premium" = "Telegram Premium"; "Settings.AddAnotherAccount.PremiumHelp" = "You can add up to four accounts with different phone numbers."; diff --git a/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift b/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift index b1df3d7b7a..594272e10b 100644 --- a/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift +++ b/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift @@ -276,27 +276,31 @@ extension InAppPurchaseManager: SKPaymentTransactionObserver { } public func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) { - if let onRestoreCompletion = self.onRestoreCompletion { - Logger.shared.log("InAppPurchaseManager", "Transactions restoration finished") - onRestoreCompletion(.succeed) - self.onRestoreCompletion = nil - - if let receiptData = getReceiptData() { - self.disposableSet.set( - self.engine.payments.sendAppStoreReceipt(receipt: receiptData, restore: true).start(completed: { - Logger.shared.log("InAppPurchaseManager", "Sent restored receipt") - }), - forKey: "restore" - ) + Queue.mainQueue().async { + if let onRestoreCompletion = self.onRestoreCompletion { + Logger.shared.log("InAppPurchaseManager", "Transactions restoration finished") + onRestoreCompletion(.succeed) + self.onRestoreCompletion = nil + + if let receiptData = getReceiptData() { + self.disposableSet.set( + self.engine.payments.sendAppStoreReceipt(receipt: receiptData, restore: true).start(completed: { + Logger.shared.log("InAppPurchaseManager", "Sent restored receipt") + }), + forKey: "restore" + ) + } } } } public func paymentQueue(_ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: Error) { - if let onRestoreCompletion = self.onRestoreCompletion { - Logger.shared.log("InAppPurchaseManager", "Transactions restoration failed with error \((error as? SKError)?.localizedDescription ?? "")") - onRestoreCompletion(.failed) - self.onRestoreCompletion = nil + Queue.mainQueue().async { + if let onRestoreCompletion = self.onRestoreCompletion { + Logger.shared.log("InAppPurchaseManager", "Transactions restoration failed with error \((error as? SKError)?.localizedDescription ?? "")") + onRestoreCompletion(.failed) + self.onRestoreCompletion = nil + } } } } diff --git a/submodules/TelegramUI/Sources/ChatAvatarNavigationNode.swift b/submodules/TelegramUI/Sources/ChatAvatarNavigationNode.swift index 97e5ca823d..90277dc65a 100644 --- a/submodules/TelegramUI/Sources/ChatAvatarNavigationNode.swift +++ b/submodules/TelegramUI/Sources/ChatAvatarNavigationNode.swift @@ -96,7 +96,7 @@ final class ChatAvatarNavigationNode: ASDisplayNode { if let photo = cachedPeerData.photo, let video = smallestVideoRepresentation(photo.videoRepresentations), let peerReference = PeerReference(peer._asPeer()) { let videoId = photo.id?.id ?? peer.id.id._internalGetInt64Value() let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: photo.representations, videoThumbnails: [], immediateThumbnailData: photo.immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [])])) - let videoContent = NativeVideoContent(id: .profileVideo(videoId, nil), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, useLargeThumbnail: true, autoFetchFullSizeThumbnail: true, startTimestamp: video.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear, captureProtected: false) + let videoContent = NativeVideoContent(id: .profileVideo(videoId, "header"), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, useLargeThumbnail: true, autoFetchFullSizeThumbnail: true, startTimestamp: video.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear, captureProtected: false) if videoContent.id != strongSelf.videoContent?.id { strongSelf.videoNode?.removeFromSupernode() strongSelf.videoContent = videoContent diff --git a/submodules/TelegramUI/Sources/ChatMessageDateHeader.swift b/submodules/TelegramUI/Sources/ChatMessageDateHeader.swift index b848d6ace2..52b374d55e 100644 --- a/submodules/TelegramUI/Sources/ChatMessageDateHeader.swift +++ b/submodules/TelegramUI/Sources/ChatMessageDateHeader.swift @@ -483,7 +483,7 @@ final class ChatMessageAvatarHeaderNode: ListViewItemHeaderNode { if let photo = cachedPeerData.photo, let video = photo.videoRepresentations.last, let peerReference = PeerReference(peer) { let videoId = photo.id?.id ?? peer.id.id._internalGetInt64Value() let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: photo.representations, videoThumbnails: [], immediateThumbnailData: photo.immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [])])) - let videoContent = NativeVideoContent(id: .profileVideo(videoId, nil), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, useLargeThumbnail: true, autoFetchFullSizeThumbnail: true, startTimestamp: video.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear, captureProtected: false) + let videoContent = NativeVideoContent(id: .profileVideo(videoId, "\(Int32.random(in: 0 ..< Int32.max))"), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, useLargeThumbnail: true, autoFetchFullSizeThumbnail: true, startTimestamp: video.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear, captureProtected: false) if videoContent.id != strongSelf.videoContent?.id { strongSelf.videoNode?.removeFromSupernode() strongSelf.videoContent = videoContent diff --git a/submodules/TelegramUI/Sources/OpenUrl.swift b/submodules/TelegramUI/Sources/OpenUrl.swift index 310c76af18..e06417714c 100644 --- a/submodules/TelegramUI/Sources/OpenUrl.swift +++ b/submodules/TelegramUI/Sources/OpenUrl.swift @@ -763,6 +763,21 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur } } else if parsedUrl.host == "premium_offer" { handleResolvedUrl(.premiumOffer(reference: nil)) + } else if parsedUrl.host == "restore_purchases" { + context.inAppPurchaseManager?.restorePurchases(completion: { result in + let text: String + switch result { + case .succeed: + text = presentationData.strings.Premium_Restore_Success + case .failed: + text = presentationData.strings.Premium_Restore_ErrorUnknown + } + context.sharedContext.presentGlobalController(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: text, actions: [ + TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: { + }), + ], parseMarkdown: true), nil) + + }) } } From 0788e34625da2ea58de966bf6897582586217842 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Sat, 11 Jun 2022 15:55:01 +0400 Subject: [PATCH 04/10] Various improvements --- .../Sources/InAppPurchaseManager.swift | 61 +++++---- .../Sources/PremiumIntroScreen.swift | 21 ++- .../Sources/PremiumLimitScreen.swift | 129 +++++++++--------- .../Sources/SolidRoundedButtonNode.swift | 6 +- 4 files changed, 121 insertions(+), 96 deletions(-) diff --git a/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift b/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift index 594272e10b..df32f8ba53 100644 --- a/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift +++ b/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift @@ -219,36 +219,22 @@ private func getReceiptData() -> Data? { extension InAppPurchaseManager: SKPaymentTransactionObserver { public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { - for transaction in transactions { + self.stateQueue.async { let accountPeerId = "\(self.engine.account.peerId.toInt64())" - if let applicationUsername = transaction.payment.applicationUsername, applicationUsername != accountPeerId { - continue - } - - let productIdentifier = transaction.payment.productIdentifier - self.stateQueue.async { + var transactionsToAssign: [SKPaymentTransaction] = [] + for transaction in transactions { + if let applicationUsername = transaction.payment.applicationUsername, applicationUsername != accountPeerId { + continue + } + + let productIdentifier = transaction.payment.productIdentifier let transactionState: TransactionState? switch transaction.transactionState { case .purchased: Logger.shared.log("InAppPurchaseManager", "Account \(accountPeerId), transaction \(transaction.transactionIdentifier ?? ""), original transaction \(transaction.original?.transactionIdentifier ?? "none") purchased") - let transactionIdentifier = transaction.transactionIdentifier - transactionState = .purchased(transactionId: transactionIdentifier) - if let transactionIdentifier = transactionIdentifier { - self.disposableSet.set( - self.engine.payments.sendAppStoreReceipt(receipt: getReceiptData() ?? Data(), restore: false).start(error: { [weak self] _ in - Logger.shared.log("InAppPurchaseManager", "Account \(accountPeerId), transaction \(transaction.transactionIdentifier ?? "") failed to assign AppStore transaction") - queue.finishTransaction(transaction) - - if let strongSelf = self, let context = strongSelf.paymentContexts[productIdentifier] { - context.subscriber(.assignFailed) - } - }, completed: { - Logger.shared.log("InAppPurchaseManager", "Account \(accountPeerId), transaction \(transaction.transactionIdentifier ?? "") successfully assigned AppStore transaction") - queue.finishTransaction(transaction) - }), - forKey: transactionIdentifier - ) - } + + transactionState = .purchased(transactionId: transaction.transactionIdentifier) + transactionsToAssign.append(transaction) case .restored: Logger.shared.log("InAppPurchaseManager", "Account \(accountPeerId), transaction \(transaction.transactionIdentifier ?? ""), original transaction \(transaction.original?.transactionIdentifier ?? "") restroring") let transactionIdentifier = transaction.transactionIdentifier @@ -272,6 +258,31 @@ extension InAppPurchaseManager: SKPaymentTransactionObserver { } } } + + if !transactionsToAssign.isEmpty { + let transactionIds = transactionsToAssign.compactMap({ $0.transactionIdentifier }).joined(separator: ", ") + Logger.shared.log("InAppPurchaseManager", "Account \(accountPeerId), sending receipt for transactions [\(transactionIds)]") + + self.disposableSet.set( + self.engine.payments.sendAppStoreReceipt(receipt: getReceiptData() ?? Data(), restore: false).start(error: { [weak self] _ in + Logger.shared.log("InAppPurchaseManager", "Account \(accountPeerId), transactions [\(transactionIds)] failed to assign") + for transaction in transactions { + self?.stateQueue.async { + if let strongSelf = self, let context = strongSelf.paymentContexts[transaction.payment.productIdentifier] { + context.subscriber(.assignFailed) + } + } + queue.finishTransaction(transaction) + } + }, completed: { + Logger.shared.log("InAppPurchaseManager", "Account \(accountPeerId), transactions [\(transactionIds)] successfully assigned") + for transaction in transactions { + queue.finishTransaction(transaction) + } + }), + forKey: transactionIds + ) + } } } diff --git a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift index aac2278425..afcaf06ea6 100644 --- a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift @@ -1422,16 +1422,23 @@ private final class PremiumIntroScreenComponent: CombinedComponent { return .never() } |> timeout(15.0, queue: Queue.mainQueue(), alternate: .fail(.timeout)) - |> deliverOnMainQueue).start(error: { _ in - addAppLogEvent(postbox: strongSelf.context.account.postbox, type: "premium.promo_screen_fail") - - let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } - let errorText = presentationData.strings.Premium_Purchase_ErrorUnknown - let alertController = textAlertController(context: strongSelf.context, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]) - strongSelf.present(alertController) + |> deliverOnMainQueue).start(error: { [weak self] _ in + if let strongSelf = self { + strongSelf.inProgress = false + strongSelf.updated(transition: .immediate) + strongSelf.completion() + + addAppLogEvent(postbox: strongSelf.context.account.postbox, type: "premium.promo_screen_fail") + + let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } + let errorText = presentationData.strings.Premium_Purchase_ErrorUnknown + let alertController = textAlertController(context: strongSelf.context, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]) + strongSelf.present(alertController) + } }, completed: { [weak self] in if let strongSelf = self { let _ = updatePremiumPromoConfigurationOnce(account: strongSelf.context.account).start() + strongSelf.inProgress = false strongSelf.isPremium = true strongSelf.updated(transition: .easeInOut(duration: 0.25)) strongSelf.completion() diff --git a/submodules/PremiumUI/Sources/PremiumLimitScreen.swift b/submodules/PremiumUI/Sources/PremiumLimitScreen.swift index e06998de3c..275b522b93 100644 --- a/submodules/PremiumUI/Sources/PremiumLimitScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumLimitScreen.swift @@ -851,23 +851,24 @@ private final class LimitSheetContent: CombinedComponent { availableSize: CGSize(width: context.availableSize.width - textSideInset * 2.0, height: context.availableSize.height), transition: .immediate ) - - let gradientColors: [UIColor] - if isPremiumDisabled { - gradientColors = [ - UIColor(rgb: 0x007afe), - UIColor(rgb: 0x5494ff) - ] - } else { - gradientColors = [ - UIColor(rgb: 0x0077ff), - UIColor(rgb: 0x6b93ff), - UIColor(rgb: 0x8878ff), - UIColor(rgb: 0xe46ace) - ] - } - + + let contentSize: CGSize if state.initialized { + let gradientColors: [UIColor] + if isPremiumDisabled { + gradientColors = [ + UIColor(rgb: 0x007afe), + UIColor(rgb: 0x5494ff) + ] + } else { + gradientColors = [ + UIColor(rgb: 0x0077ff), + UIColor(rgb: 0x6b93ff), + UIColor(rgb: 0x8878ff), + UIColor(rgb: 0xe46ace) + ] + } + let limit = limit.update( component: PremiumLimitDisplayComponent( inactiveColor: theme.list.itemBlocksSeparatorColor.withAlphaComponent(0.5), @@ -889,58 +890,60 @@ private final class LimitSheetContent: CombinedComponent { context.add(limit .position(CGPoint(x: context.availableSize.width / 2.0, y: limit.size.height / 2.0 + 44.0)) ) - } - let isIncreaseButton = !reachedMaximumLimit && !isPremiumDisabled - let button = button.update( - component: SolidRoundedButtonComponent( - title: isIncreaseButton ? strings.Premium_IncreaseLimit : strings.Common_OK, - - theme: SolidRoundedButtonComponent.Theme( - backgroundColor: .black, - backgroundColors: gradientColors, - foregroundColor: .white + let isIncreaseButton = !reachedMaximumLimit && !isPremiumDisabled + let button = button.update( + component: SolidRoundedButtonComponent( + title: isIncreaseButton ? strings.Premium_IncreaseLimit : strings.Common_OK, + + theme: SolidRoundedButtonComponent.Theme( + backgroundColor: .black, + backgroundColors: gradientColors, + foregroundColor: .white + ), + font: .bold, + fontSize: 17.0, + height: 50.0, + cornerRadius: 10.0, + gloss: isIncreaseButton, + animationName: isIncreaseButton ? buttonAnimationName : nil, + iconPosition: .right, + action: { [weak component] in + guard let component = component else { + return + } + component.dismiss() + if isIncreaseButton { + component.action() + } + } ), - font: .bold, - fontSize: 17.0, - height: 50.0, - cornerRadius: 10.0, - gloss: isIncreaseButton, - animationName: isIncreaseButton ? buttonAnimationName : nil, - iconPosition: .right, - action: { [weak component] in - guard let component = component else { - return - } - component.dismiss() - if isIncreaseButton { - component.action() - } - } - ), - availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0), - transition: context.transition - ) + availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0), + transition: context.transition + ) + + var textOffset: CGFloat = 228.0 + if isPremiumDisabled { + textOffset -= 68.0 + } + + context.add(title + .position(CGPoint(x: context.availableSize.width / 2.0, y: 28.0)) + ) + context.add(text + .position(CGPoint(x: context.availableSize.width / 2.0, y: textOffset)) + ) + + let buttonFrame = CGRect(origin: CGPoint(x: sideInset, y: textOffset + ceil(text.size.height / 2.0) + 38.0), size: button.size) + context.add(button + .position(CGPoint(x: buttonFrame.midX, y: buttonFrame.midY)) + ) - var textOffset: CGFloat = 228.0 - if isPremiumDisabled { - textOffset -= 68.0 + contentSize = CGSize(width: context.availableSize.width, height: buttonFrame.maxY + 5.0 + environment.safeInsets.bottom) + } else { + contentSize = CGSize(width: context.availableSize.width, height: 351.0 + environment.safeInsets.bottom) } - context.add(title - .position(CGPoint(x: context.availableSize.width / 2.0, y: 28.0)) - ) - context.add(text - .position(CGPoint(x: context.availableSize.width / 2.0, y: textOffset)) - ) - - let buttonFrame = CGRect(origin: CGPoint(x: sideInset, y: textOffset + ceil(text.size.height / 2.0) + 38.0), size: button.size) - context.add(button - .position(CGPoint(x: buttonFrame.midX, y: buttonFrame.midY)) - ) - - let contentSize = CGSize(width: context.availableSize.width, height: buttonFrame.maxY + 5.0 + environment.safeInsets.bottom) - return contentSize } } diff --git a/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift b/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift index ae976a8581..04c3773cc8 100644 --- a/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift +++ b/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift @@ -808,6 +808,10 @@ public final class SolidRoundedButtonView: UIView { fatalError("init(coder:) has not been implemented") } + deinit { + self.animationTimer?.invalidate() + } + private func setupGloss() { if self.gloss { if self.shimmerView == nil { @@ -1002,7 +1006,7 @@ public final class SolidRoundedButtonView: UIView { compositingFilter = nil } - let globalTimeOffset = self.icon == nil + let globalTimeOffset = self.icon == nil && self.animation == nil shimmerView.update(backgroundColor: .clear, foregroundColor: color.withAlphaComponent(alpha), gradientSize: 70.0, globalTimeOffset: globalTimeOffset, duration: 3.0, horizontal: true) borderShimmerView.update(backgroundColor: .clear, foregroundColor: color.withAlphaComponent(borderAlpha), gradientSize: 70.0, globalTimeOffset: globalTimeOffset, duration: 3.0, horizontal: true) From bbc2744915b902eff5ae964677ac29811dffd6d7 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Sat, 11 Jun 2022 15:57:10 +0400 Subject: [PATCH 05/10] Various fixes --- submodules/PremiumUI/Resources/swirl.scn | Bin 84910 -> 16713 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/submodules/PremiumUI/Resources/swirl.scn b/submodules/PremiumUI/Resources/swirl.scn index f5f50794de53cb6a05ca5d10bd0f79606da704bf..da92d416404bf96e38079932b401ce2b65a28f74 100644 GIT binary patch delta 3593 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 ibtmd>bYk>`=oh22qw}KYKKu^@(813<KSD literal 84910 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 Date: Sat, 11 Jun 2022 16:38:19 +0400 Subject: [PATCH 06/10] Various fixes --- .../Sources/PhoneDemoComponent.swift | 29 +++++++++++++------ .../Sources/PremiumStarComponent.swift | 4 +-- .../Sources/SolidRoundedButtonNode.swift | 8 ++--- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/submodules/PremiumUI/Sources/PhoneDemoComponent.swift b/submodules/PremiumUI/Sources/PhoneDemoComponent.swift index e1fb0878cd..6005dbd665 100644 --- a/submodules/PremiumUI/Sources/PhoneDemoComponent.swift +++ b/submodules/PremiumUI/Sources/PhoneDemoComponent.swift @@ -111,6 +111,7 @@ private final class PhoneView: UIView { self.shimmerMaskView = UIView() self.shimmerBorderView = UIImageView(image: phoneBorderMaskImage) self.shimmerStarView = UIImageView(image: starMaskImage) + self.shimmerStarView.alpha = 0.7 self.backShimmerView = UIView() self.backShimmerView.alpha = 0.0 @@ -140,10 +141,10 @@ private final class PhoneView: UIView { self.frontShimmerView.mask = self.shimmerMaskView self.frontShimmerView.addSubview(self.shimmerEffectView) - self.backShimmerEffectView.update(backgroundColor: .clear, foregroundColor: UIColor.white.withAlphaComponent(0.35), gradientSize: 70.0, globalTimeOffset: true, duration: 3.0, horizontal: true) + self.backShimmerEffectView.update(backgroundColor: .clear, foregroundColor: UIColor.white.withAlphaComponent(0.35), gradientSize: 60.0, globalTimeOffset: true, duration: 4.0, horizontal: true) self.backShimmerEffectView.layer.compositingFilter = "overlayBlendMode" - self.shimmerEffectView.update(backgroundColor: .clear, foregroundColor: UIColor.white.withAlphaComponent(0.65), gradientSize: 70.0, globalTimeOffset: true, duration: 3.0, horizontal: true) + self.shimmerEffectView.update(backgroundColor: .clear, foregroundColor: UIColor.white.withAlphaComponent(0.5), gradientSize: 16.0, globalTimeOffset: true, duration: 4.0, horizontal: true) self.shimmerEffectView.layer.compositingFilter = "overlayBlendMode" } @@ -180,8 +181,16 @@ private final class PhoneView: UIView { let status = videoNode.status |> mapToSignal { status -> Signal in - if let status = status, case .buffering = status.status { - return .single(status) |> delay(1.0, queue: Queue.mainQueue()) + var isLoading = false + if let status = status { + if case .buffering = status.status { + isLoading = true + } else if status.duration.isZero { + isLoading = true + } + } + if isLoading { + return .single(status) |> delay(0.6, queue: Queue.mainQueue()) } else { return .single(status) } @@ -205,11 +214,13 @@ private final class PhoneView: UIView { private func updatePlaybackStatus() { var isDisplayingProgress = false if let playbackStatus = self.playbackStatusValue { - if case let .buffering(initial, _, progress, _) = playbackStatus.status, initial || !progress.isZero { + if case .buffering = playbackStatus.status { isDisplayingProgress = true } else if playbackStatus.status == .playing { - isDisplayingProgress = false + isDisplayingProgress = playbackStatus.duration.isZero } + } else { + isDisplayingProgress = true } let targetAlpha = isDisplayingProgress ? 1.0 : 0.0 @@ -386,9 +397,9 @@ final class PhoneDemoComponent: Component { fatalError("init(coder:) has not been implemented") } -// deinit { -// self.playbackStatusDisposable?.dispose() -// } + deinit { + self.playbackStatusDisposable?.dispose() + } public func update(component: PhoneDemoComponent, availableSize: CGSize, environment: Environment, transition: Transition) -> CGSize { self.component = component diff --git a/submodules/PremiumUI/Sources/PremiumStarComponent.swift b/submodules/PremiumUI/Sources/PremiumStarComponent.swift index 505fef2f4c..b8a463b502 100644 --- a/submodules/PremiumUI/Sources/PremiumStarComponent.swift +++ b/submodules/PremiumUI/Sources/PremiumStarComponent.swift @@ -386,13 +386,13 @@ class PremiumStarComponent: Component { animation.fromValue = NSValue(scnMatrix4: initial) animation.toValue = NSValue(scnMatrix4: SCNMatrix4Translate(initial, -1.6, 0.0, 0.0)) animation.timingFunction = CAMediaTimingFunction(name: .easeOut) - animation.beginTime = 0.6 + animation.beginTime = 1.1 animation.duration = 0.9 let group = CAAnimationGroup() group.animations = [animation] group.beginTime = 1.0 - group.duration = 3.0 + group.duration = 4.0 group.repeatCount = .infinity node.geometry?.materials.first?.emission.addAnimation(group, forKey: "shimmer") diff --git a/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift b/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift index 04c3773cc8..2588a58efc 100644 --- a/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift +++ b/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift @@ -358,8 +358,8 @@ public final class SolidRoundedButtonNode: ASDisplayNode { compositingFilter = nil } - shimmerView.update(backgroundColor: .clear, foregroundColor: color.withAlphaComponent(alpha), gradientSize: 70.0, globalTimeOffset: false, duration: 3.0, horizontal: true) - borderShimmerView.update(backgroundColor: .clear, foregroundColor: color.withAlphaComponent(borderAlpha), gradientSize: 70.0, globalTimeOffset: false, duration: 3.0, horizontal: true) + shimmerView.update(backgroundColor: .clear, foregroundColor: color.withAlphaComponent(alpha), gradientSize: 70.0, globalTimeOffset: false, duration: 4.0, horizontal: true) + borderShimmerView.update(backgroundColor: .clear, foregroundColor: color.withAlphaComponent(borderAlpha), gradientSize: 70.0, globalTimeOffset: false, duration: 4.0, horizontal: true) shimmerView.layer.compositingFilter = compositingFilter borderShimmerView.layer.compositingFilter = compositingFilter @@ -1008,8 +1008,8 @@ public final class SolidRoundedButtonView: UIView { let globalTimeOffset = self.icon == nil && self.animation == nil - shimmerView.update(backgroundColor: .clear, foregroundColor: color.withAlphaComponent(alpha), gradientSize: 70.0, globalTimeOffset: globalTimeOffset, duration: 3.0, horizontal: true) - borderShimmerView.update(backgroundColor: .clear, foregroundColor: color.withAlphaComponent(borderAlpha), gradientSize: 70.0, globalTimeOffset: globalTimeOffset, duration: 3.0, horizontal: true) + shimmerView.update(backgroundColor: .clear, foregroundColor: color.withAlphaComponent(alpha), gradientSize: 70.0, globalTimeOffset: globalTimeOffset, duration: 4.0, horizontal: true) + borderShimmerView.update(backgroundColor: .clear, foregroundColor: color.withAlphaComponent(borderAlpha), gradientSize: 70.0, globalTimeOffset: globalTimeOffset, duration: 4.0, horizontal: true) shimmerView.layer.compositingFilter = compositingFilter borderShimmerView.layer.compositingFilter = compositingFilter From f68632cb36add9a26b7994f7c2bb6451f07038c1 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Sat, 11 Jun 2022 16:46:07 +0400 Subject: [PATCH 07/10] Fix crash --- .../Sources/PremiumLimitScreen.swift | 66 +++++++++---------- .../Sources/SolidRoundedButtonNode.swift | 8 +-- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/submodules/PremiumUI/Sources/PremiumLimitScreen.swift b/submodules/PremiumUI/Sources/PremiumLimitScreen.swift index 275b522b93..89e3c65778 100644 --- a/submodules/PremiumUI/Sources/PremiumLimitScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumLimitScreen.swift @@ -819,41 +819,41 @@ private final class LimitSheetContent: CombinedComponent { reachedMaximumLimit = false } - let title = title.update( - component: MultilineTextComponent( - text: .plain(NSAttributedString( - string: titleText, - font: Font.semibold(17.0), - textColor: theme.actionSheet.primaryTextColor, - paragraphAlignment: .center - )), - horizontalAlignment: .center, - maximumNumberOfLines: 1 - ), - availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: CGFloat.greatestFiniteMagnitude), - transition: .immediate - ) - - let textFont = Font.regular(17.0) - let boldTextFont = Font.semibold(17.0) - let textColor = theme.actionSheet.primaryTextColor - let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: textColor), linkAttribute: { _ in - return nil - }) - - let text = text.update( - component: MultilineTextComponent( - text: .markdown(text: string, attributes: markdownAttributes), - horizontalAlignment: .center, - maximumNumberOfLines: 0, - lineSpacing: 0.0 - ), - availableSize: CGSize(width: context.availableSize.width - textSideInset * 2.0, height: context.availableSize.height), - transition: .immediate - ) - let contentSize: CGSize if state.initialized { + let title = title.update( + component: MultilineTextComponent( + text: .plain(NSAttributedString( + string: titleText, + font: Font.semibold(17.0), + textColor: theme.actionSheet.primaryTextColor, + paragraphAlignment: .center + )), + horizontalAlignment: .center, + maximumNumberOfLines: 1 + ), + availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: CGFloat.greatestFiniteMagnitude), + transition: .immediate + ) + + let textFont = Font.regular(17.0) + let boldTextFont = Font.semibold(17.0) + let textColor = theme.actionSheet.primaryTextColor + let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: textColor), linkAttribute: { _ in + return nil + }) + + let text = text.update( + component: MultilineTextComponent( + text: .markdown(text: string, attributes: markdownAttributes), + horizontalAlignment: .center, + maximumNumberOfLines: 0, + lineSpacing: 0.0 + ), + availableSize: CGSize(width: context.availableSize.width - textSideInset * 2.0, height: context.availableSize.height), + transition: .immediate + ) + let gradientColors: [UIColor] if isPremiumDisabled { gradientColors = [ diff --git a/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift b/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift index 2588a58efc..859fef30b8 100644 --- a/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift +++ b/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift @@ -128,10 +128,10 @@ public final class SolidRoundedButtonNode: ASDisplayNode { if self.gloss { self.animationTimer?.invalidate() - Queue.mainQueue().after(0.75) { + Queue.mainQueue().after(1.25) { self.animationNode?.play() - let timer = SwiftSignalKit.Timer(timeout: 3.0, repeat: true, completion: { [weak self] in + let timer = SwiftSignalKit.Timer(timeout: 4.0, repeat: true, completion: { [weak self] in self?.animationNode?.play() }, queue: Queue.mainQueue()) self.animationTimer = timer @@ -672,10 +672,10 @@ public final class SolidRoundedButtonView: UIView { if self.gloss { self.animationTimer?.invalidate() - Queue.mainQueue().after(0.75) { + Queue.mainQueue().after(1.25) { self.animationNode?.play() - let timer = SwiftSignalKit.Timer(timeout: 3.0, repeat: true, completion: { [weak self] in + let timer = SwiftSignalKit.Timer(timeout: 4.0, repeat: true, completion: { [weak self] in self?.animationNode?.play() }, queue: Queue.mainQueue()) self.animationTimer = timer From 75f7d3e2fb106348b92086b235a7a7433d95dcd5 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Sat, 11 Jun 2022 17:07:13 +0400 Subject: [PATCH 08/10] Various fixes --- .../ChatListUI/Sources/ChatContextMenus.swift | 28 +++++++++++++------ .../Peers/TogglePeerChatPinned.swift | 12 ++++---- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/submodules/ChatListUI/Sources/ChatContextMenus.swift b/submodules/ChatListUI/Sources/ChatContextMenus.swift index 48189255c3..2c2e2f66e6 100644 --- a/submodules/ChatListUI/Sources/ChatContextMenus.swift +++ b/submodules/ChatListUI/Sources/ChatContextMenus.swift @@ -354,14 +354,26 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch case let .limitExceeded(count, _): f(.default) - var replaceImpl: ((ViewController) -> Void)? - let controller = PremiumLimitScreen(context: context, subject: .pins, count: Int32(count), action: { - let premiumScreen = PremiumIntroScreen(context: context, source: .pinnedChats) - replaceImpl?(premiumScreen) - }) - chatListController?.push(controller) - replaceImpl = { [weak controller] c in - controller?.replace(with: c) + if case .filter = location { + var replaceImpl: ((ViewController) -> Void)? + let controller = PremiumLimitScreen(context: context, subject: .chatsPerFolder, count: Int32(count), action: { + let premiumScreen = PremiumIntroScreen(context: context, source: .pinnedChats) + replaceImpl?(premiumScreen) + }) + chatListController?.push(controller) + replaceImpl = { [weak controller] c in + controller?.replace(with: c) + } + } else { + var replaceImpl: ((ViewController) -> Void)? + let controller = PremiumLimitScreen(context: context, subject: .pins, count: Int32(count), action: { + let premiumScreen = PremiumIntroScreen(context: context, source: .pinnedChats) + replaceImpl?(premiumScreen) + }) + chatListController?.push(controller) + replaceImpl = { [weak controller] c in + controller?.replace(with: c) + } } } }) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TogglePeerChatPinned.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TogglePeerChatPinned.swift index e329d996d3..9b9e204345 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TogglePeerChatPinned.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TogglePeerChatPinned.swift @@ -2,7 +2,6 @@ import Foundation import Postbox import SwiftSignalKit - public enum TogglePeerChatPinnedLocation { case group(PeerGroupId) case filter(Int32) @@ -17,6 +16,10 @@ func _internal_toggleItemPinned(postbox: Postbox, accountPeerId: PeerId, locatio return postbox.transaction { transaction -> TogglePeerChatPinnedResult in let isPremium = transaction.getPeer(accountPeerId)?.isPremium ?? false + let appConfiguration = transaction.getPreferencesEntry(key: PreferencesKeys.appConfiguration)?.get(AppConfiguration.self) ?? .defaultValue + let limitsConfiguration = transaction.getPreferencesEntry(key: PreferencesKeys.limitsConfiguration)?.get(LimitsConfiguration.self) ?? LimitsConfiguration.defaultValue + let userLimitsConfiguration = UserLimitsConfiguration(appConfiguration: appConfiguration, isPremium: isPremium) + switch location { case let .group(groupId): var itemIds = transaction.getPinnedItemIds(groupId: groupId) @@ -39,9 +42,8 @@ func _internal_toggleItemPinned(postbox: Postbox, accountPeerId: PeerId, locatio additionalCount = 1 } - let appConfiguration = transaction.getPreferencesEntry(key: PreferencesKeys.appConfiguration)?.get(AppConfiguration.self) ?? .defaultValue - let limitsConfiguration = transaction.getPreferencesEntry(key: PreferencesKeys.limitsConfiguration)?.get(LimitsConfiguration.self) ?? LimitsConfiguration.defaultValue - let userLimitsConfiguration = UserLimitsConfiguration(appConfiguration: appConfiguration, isPremium: isPremium) + + let limitCount: Int if case .root = groupId { @@ -75,7 +77,7 @@ func _internal_toggleItemPinned(postbox: Postbox, accountPeerId: PeerId, locatio updatedData.includePeers.removePinnedPeer(peerId) } else { if !updatedData.includePeers.addPinnedPeer(peerId) { - result = .limitExceeded(count: updatedData.includePeers.pinnedPeers.count, limit: 100) + result = .limitExceeded(count: updatedData.includePeers.peers.count, limit: Int(userLimitsConfiguration.maxFolderChatsCount)) } } filters[index] = .filter(id: id, title: title, emoticon: emoticon, data: updatedData) From d8f7f18652911ff4a474cd583a11ddb351915b22 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Sat, 11 Jun 2022 18:31:10 +0400 Subject: [PATCH 09/10] Various fixes --- .../PremiumUI/MetalResources/matrix.metal | 2 +- submodules/PremiumUI/Resources/badge.scn | Bin 27968 -> 27949 bytes .../Sources/StickersCarouselComponent.swift | 2 +- .../Sources/PeerInfo/PeerInfoHeaderNode.swift | 2 +- .../Sources/PeerInfo/PeerInfoScreen.swift | 34 ++++++++++-------- 5 files changed, 22 insertions(+), 18 deletions(-) diff --git a/submodules/PremiumUI/MetalResources/matrix.metal b/submodules/PremiumUI/MetalResources/matrix.metal index 9cbfd8e745..64040fc282 100644 --- a/submodules/PremiumUI/MetalResources/matrix.metal +++ b/submodules/PremiumUI/MetalResources/matrix.metal @@ -36,7 +36,7 @@ float text(float2 uvIn, float2 uv = fmod(uvIn, 1.0 / count) * count; float2 block = uvIn * count - uv; - uv = uv * 0.8 + 0.1; + uv = uv * 0.8; uv += floor(noiseTexture.sample(textureSampler, block / noiseResolution + time * .00025).xy * 256.); uv *= -1.0; diff --git a/submodules/PremiumUI/Resources/badge.scn b/submodules/PremiumUI/Resources/badge.scn index 40aa6f0b0df5b70f4502fed247337c1c3be19814..6c59e8ffb0fb648b8d6228965a70c7ffb59f6dc7 100644 GIT binary patch delta 4994 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 take(1) + |> deliverOnMainQueue + ).start(next: { [weak self] accountAndPeer, accountsAndPeers in + guard let strongSelf = self else { + return + } var maximumAvailableAccounts: Int = 3 - if data.peer?.isPremium == true && !self.context.account.testingEnvironment { + if accountAndPeer?.1.isPremium == true && !strongSelf.context.account.testingEnvironment { maximumAvailableAccounts = 4 } var count: Int = 1 - if let settings = data.globalSettings { - for (accountContext, peer, _) in settings.accountsAndPeers { - if !accountContext.account.testingEnvironment { - if peer.isPremium { - maximumAvailableAccounts = 4 - } - count += 1 + for (accountContext, peer, _) in accountsAndPeers { + if !accountContext.account.testingEnvironment { + if peer.isPremium { + maximumAvailableAccounts = 4 } + count += 1 } } + if count >= maximumAvailableAccounts { - let context = self.context var replaceImpl: ((ViewController) -> Void)? - let controller = PremiumLimitScreen(context: context, subject: .accounts, count: Int32(count), action: { - let controller = PremiumIntroScreen(context: context, source: .accounts) + let controller = PremiumLimitScreen(context: strongSelf.context, subject: .accounts, count: Int32(count), action: { + let controller = PremiumIntroScreen(context: strongSelf.context, source: .accounts) replaceImpl?(controller) }) replaceImpl = { [weak controller] c in controller?.replace(with: c) } - if let navigationController = context.sharedContext.mainWindow?.viewController as? NavigationController { + if let navigationController = strongSelf.context.sharedContext.mainWindow?.viewController as? NavigationController { navigationController.pushViewController(controller) } } else { - self.context.sharedContext.beginNewAuth(testingEnvironment: self.context.account.testingEnvironment) + strongSelf.context.sharedContext.beginNewAuth(testingEnvironment: strongSelf.context.account.testingEnvironment) } - } + }) case .logout: if let user = self.data?.peer as? TelegramUser, let phoneNumber = user.phone { if let controller = self.controller, let navigationController = controller.navigationController as? NavigationController { From 3481e44afacbac6bb69023ede58fb0d166a889de Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Sat, 11 Jun 2022 20:00:35 +0400 Subject: [PATCH 10/10] Various fixes --- submodules/PremiumUI/Sources/PremiumIntroScreen.swift | 10 +++++++++- .../Sources/SolidRoundedButtonNode.swift | 10 ++++++++++ .../Sources/PeerInfo/PeerInfoHeaderNode.swift | 2 +- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift index afcaf06ea6..5e56d3d847 100644 --- a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift @@ -1227,6 +1227,10 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { size.height += 10.0 size.height += scrollEnvironment.insets.bottom + if context.component.source != .settings { + size.height += 44.0 + } + return size } } @@ -1391,7 +1395,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent { guard !self.context.account.testingEnvironment else { let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } - let alertController = textAlertController(context: self.context, title: nil, text: "Telegram Premium purchase is not available in the test environment.", actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]) + let alertController = textAlertController(context: self.context, title: nil, text: "Telegram Premium is not available in the test environment.", actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]) self.present(alertController) return } @@ -1425,6 +1429,8 @@ private final class PremiumIntroScreenComponent: CombinedComponent { |> deliverOnMainQueue).start(error: { [weak self] _ in if let strongSelf = self { strongSelf.inProgress = false + strongSelf.updateInProgress(false) + strongSelf.updated(transition: .immediate) strongSelf.completion() @@ -1439,6 +1445,8 @@ private final class PremiumIntroScreenComponent: CombinedComponent { if let strongSelf = self { let _ = updatePremiumPromoConfigurationOnce(account: strongSelf.context.account).start() strongSelf.inProgress = false + strongSelf.updateInProgress(false) + strongSelf.isPremium = true strongSelf.updated(transition: .easeInOut(duration: 0.25)) strongSelf.completion() diff --git a/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift b/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift index 859fef30b8..acee247936 100644 --- a/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift +++ b/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift @@ -118,6 +118,7 @@ public final class SolidRoundedButtonNode: ASDisplayNode { let animationNode = SimpleAnimationNode(animationName: animation, size: CGSize(width: 30.0, height: 30.0)) animationNode.customColor = self.theme.foregroundColor + animationNode.isUserInteractionEnabled = false self.addSubnode(animationNode) self.animationNode = animationNode @@ -227,6 +228,8 @@ public final class SolidRoundedButtonNode: ASDisplayNode { strongSelf.subtitleNode.alpha = 0.55 strongSelf.iconNode.layer.removeAnimation(forKey: "opacity") strongSelf.iconNode.alpha = 0.55 + strongSelf.animationNode?.layer.removeAnimation(forKey: "opacity") + strongSelf.animationNode?.alpha = 0.55 } else { if strongSelf.buttonBackgroundNode.alpha > 0.0 { strongSelf.buttonBackgroundNode.alpha = 1.0 @@ -237,6 +240,8 @@ public final class SolidRoundedButtonNode: ASDisplayNode { strongSelf.subtitleNode.layer.animateAlpha(from: 0.55, to: 1.0, duration: 0.2) strongSelf.iconNode.alpha = 1.0 strongSelf.iconNode.layer.animateAlpha(from: 0.55, to: 1.0, duration: 0.2) + strongSelf.animationNode?.alpha = 1.0 + strongSelf.animationNode?.layer.animateAlpha(from: 0.55, to: 1.0, duration: 0.2) } } } @@ -662,6 +667,7 @@ public final class SolidRoundedButtonView: UIView { let animationNode = SimpleAnimationNode(animationName: animation, size: CGSize(width: 30.0, height: 30.0)) animationNode.customColor = self.theme.foregroundColor + animationNode.isUserInteractionEnabled = false self.addSubview(animationNode.view) self.animationNode = animationNode @@ -784,6 +790,8 @@ public final class SolidRoundedButtonView: UIView { strongSelf.subtitleNode.alpha = 0.55 strongSelf.iconNode.layer.removeAnimation(forKey: "opacity") strongSelf.iconNode.alpha = 0.55 + strongSelf.animationNode?.layer.removeAnimation(forKey: "opacity") + strongSelf.animationNode?.alpha = 0.55 } else { if strongSelf.buttonBackgroundNode.alpha > 0.0 { strongSelf.buttonBackgroundNode.alpha = 1.0 @@ -794,6 +802,8 @@ public final class SolidRoundedButtonView: UIView { strongSelf.subtitleNode.layer.animateAlpha(from: 0.55, to: 1.0, duration: 0.2) strongSelf.iconNode.alpha = 1.0 strongSelf.iconNode.layer.animateAlpha(from: 0.55, to: 1.0, duration: 0.2) + strongSelf.animationNode?.alpha = 1.0 + strongSelf.animationNode?.layer.animateAlpha(from: 0.55, to: 1.0, duration: 0.2) } } } diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift index 933a95b8b6..d6ed8b8dea 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift @@ -2310,7 +2310,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { credibilityIcon = .scam } else if peer.isVerified { credibilityIcon = .verified - } else if peer.isPremium && !premiumConfiguration.isPremiumDisabled && peer.id != self.context.account.peerId { + } else if peer.isPremium && !premiumConfiguration.isPremiumDisabled && (peer.id != self.context.account.peerId || self.isSettings) { credibilityIcon = .premium } else { credibilityIcon = .none