Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios

This commit is contained in:
Ali 2023-04-22 15:00:04 +04:00
commit 58d63e309f
7 changed files with 35 additions and 9 deletions

View File

@ -9340,3 +9340,6 @@ Sorry for the inconvenience.";
"Notification.LockScreenReactionPlaceholder" = "Reaction"; "Notification.LockScreenReactionPlaceholder" = "Reaction";
"UserInfo.BotNamePlaceholder" = "Bot Name"; "UserInfo.BotNamePlaceholder" = "Bot Name";
"ChatList.PremiumRestoreDiscountTitle" = "Get Premium back with up to %@ off";
"ChatList.PremiumRestoreDiscountText" = "Your Telegram Premium has recently expired. Tap here to extend it.";

View File

@ -718,6 +718,7 @@ private final class MainButtonNode: HighlightTrackingButtonNode {
let statusSize = CGSize(width: 20.0, height: 20.0) let statusSize = CGSize(width: 20.0, height: 20.0)
transition.updateFrame(node: self.statusNode, frame: CGRect(origin: CGPoint(x: size.width - statusSize.width - 15.0, y: floorToScreenPixels((size.height - statusSize.height) / 2.0)), size: statusSize)) transition.updateFrame(node: self.statusNode, frame: CGRect(origin: CGPoint(x: size.width - statusSize.width - 15.0, y: floorToScreenPixels((size.height - statusSize.height) / 2.0)), size: statusSize))
self.statusNode.foregroundNodeColor = state.textColor
self.statusNode.transitionToState(state.progress == .side ? .progress(value: nil, cancelEnabled: false, appearance: SemanticStatusNodeState.ProgressAppearance(inset: 0.0, lineWidth: 2.0)) : .none) self.statusNode.transitionToState(state.progress == .side ? .progress(value: nil, cancelEnabled: false, appearance: SemanticStatusNodeState.ProgressAppearance(inset: 0.0, lineWidth: 2.0)) : .none)
} }
} }

View File

@ -663,7 +663,7 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
nodeInteraction?.openStorageManagement() nodeInteraction?.openStorageManagement()
case .setupPassword: case .setupPassword:
nodeInteraction?.openPasswordSetup() nodeInteraction?.openPasswordSetup()
case .premiumUpgrade, .premiumAnnualDiscount: case .premiumUpgrade, .premiumAnnualDiscount, .premiumRestore:
nodeInteraction?.openPremiumIntro() nodeInteraction?.openPremiumIntro()
} }
case .hide: case .hide:
@ -960,7 +960,7 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
nodeInteraction?.openStorageManagement() nodeInteraction?.openStorageManagement()
case .setupPassword: case .setupPassword:
nodeInteraction?.openPasswordSetup() nodeInteraction?.openPasswordSetup()
case .premiumUpgrade, .premiumAnnualDiscount: case .premiumUpgrade, .premiumAnnualDiscount, .premiumRestore:
nodeInteraction?.openPremiumIntro() nodeInteraction?.openPremiumIntro()
} }
case .hide: case .hide:
@ -1484,6 +1484,7 @@ public final class ChatListNode: ListView {
if let self { if let self {
let _ = dismissServerProvidedSuggestion(account: self.context.account, suggestion: .annualPremium).start() let _ = dismissServerProvidedSuggestion(account: self.context.account, suggestion: .annualPremium).start()
let _ = dismissServerProvidedSuggestion(account: self.context.account, suggestion: .upgradePremium).start() let _ = dismissServerProvidedSuggestion(account: self.context.account, suggestion: .upgradePremium).start()
let _ = dismissServerProvidedSuggestion(account: self.context.account, suggestion: .restorePremium).start()
} }
} }
let controller = self.context.sharedContext.makePremiumIntroController(context: self.context, source: .ads) let controller = self.context.sharedContext.makePremiumIntroController(context: self.context, source: .ads)
@ -1604,7 +1605,7 @@ public final class ChatListNode: ListView {
return .single(.setupPassword) return .single(.setupPassword)
} }
} }
if suggestions.contains(.annualPremium) || suggestions.contains(.upgradePremium), let inAppPurchaseManager = context.inAppPurchaseManager { if suggestions.contains(.annualPremium) || suggestions.contains(.upgradePremium) || suggestions.contains(.restorePremium), let inAppPurchaseManager = context.inAppPurchaseManager {
return inAppPurchaseManager.availableProducts return inAppPurchaseManager.availableProducts
|> map { products -> ChatListNotice? in |> map { products -> ChatListNotice? in
if products.count > 1 { if products.count > 1 {
@ -1619,7 +1620,9 @@ public final class ChatListNode: ListView {
let fraction = Float(product.priceCurrencyAndAmount.amount) / Float(12) / Float(shortestOptionPrice.0) let fraction = Float(product.priceCurrencyAndAmount.amount) / Float(12) / Float(shortestOptionPrice.0)
let discount = Int32(round((1.0 - fraction) * 20.0) * 5.0) let discount = Int32(round((1.0 - fraction) * 20.0) * 5.0)
if discount > 0 { if discount > 0 {
if suggestions.contains(.annualPremium) { if suggestions.contains(.restorePremium) {
return .premiumRestore(discount: discount)
} else if suggestions.contains(.annualPremium) {
return .premiumAnnualDiscount(discount: discount) return .premiumAnnualDiscount(discount: discount)
} else if suggestions.contains(.upgradePremium) { } else if suggestions.contains(.upgradePremium) {
return .premiumUpgrade(discount: discount) return .premiumUpgrade(discount: discount)

View File

@ -84,6 +84,7 @@ enum ChatListNotice: Equatable {
case setupPassword case setupPassword
case premiumUpgrade(discount: Int32) case premiumUpgrade(discount: Int32)
case premiumAnnualDiscount(discount: Int32) case premiumAnnualDiscount(discount: Int32)
case premiumRestore(discount: Int32)
} }
enum ChatListNodeEntry: Comparable, Identifiable { enum ChatListNodeEntry: Comparable, Identifiable {

View File

@ -172,6 +172,16 @@ class ChatListStorageInfoItemNode: ItemListRevealOptionsItemNode {
titleString = titleStringValue titleString = titleStringValue
textString = NSAttributedString(string: item.strings.ChatList_PremiumAnnualDiscountText, font: textFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor) textString = NSAttributedString(string: item.strings.ChatList_PremiumAnnualDiscountText, font: textFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor)
case let .premiumRestore(discount):
let discountString = "\(discount)%"
let rawTitleString = item.strings.ChatList_PremiumRestoreDiscountTitle(discountString)
let titleStringValue = NSMutableAttributedString(attributedString: NSAttributedString(string: rawTitleString.string, font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor))
if let range = rawTitleString.ranges.first {
titleStringValue.addAttribute(.foregroundColor, value: item.theme.rootController.navigationBar.accentTextColor, range: range.range)
}
titleString = titleStringValue
textString = NSAttributedString(string: item.strings.ChatList_PremiumRestoreDiscountText, font: textFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor)
} }
let titleLayout = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleString, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - sideInset - rightInset, height: 100.0))) let titleLayout = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleString, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - sideInset - rightInset, height: 100.0)))

View File

@ -11,6 +11,7 @@ public enum ServerProvidedSuggestion: String {
case setupPassword = "SETUP_PASSWORD" case setupPassword = "SETUP_PASSWORD"
case upgradePremium = "PREMIUM_UPGRADE" case upgradePremium = "PREMIUM_UPGRADE"
case annualPremium = "PREMIUM_ANNUAL" case annualPremium = "PREMIUM_ANNUAL"
case restorePremium = "PREMIUM_RESTORE"
} }
private var dismissedSuggestionsPromise = ValuePromise<[AccountRecordId: Set<ServerProvidedSuggestion>]>([:]) private var dismissedSuggestionsPromise = ValuePromise<[AccountRecordId: Set<ServerProvidedSuggestion>]>([:])

View File

@ -2607,6 +2607,8 @@ final class PeerInfoHeaderNode: ASDisplayNode {
} }
} }
let isLandscape = containerInset > 16.0
let themeUpdated = self.presentationData?.theme !== presentationData.theme let themeUpdated = self.presentationData?.theme !== presentationData.theme
self.presentationData = presentationData self.presentationData = presentationData
@ -2778,7 +2780,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
transitionSourceAvatarFrame = avatarNavigationNode.avatarNode.view.convert(avatarNavigationNode.avatarNode.view.bounds, to: navigationTransition.sourceNavigationBar.view) transitionSourceAvatarFrame = avatarNavigationNode.avatarNode.view.convert(avatarNavigationNode.avatarNode.view.bounds, to: navigationTransition.sourceNavigationBar.view)
} }
} else { } else {
if deviceMetrics.hasDynamicIsland { if deviceMetrics.hasDynamicIsland && !isLandscape {
transitionSourceAvatarFrame = CGRect(origin: CGPoint(x: avatarFrame.minX, y: -20.0), size: avatarFrame.size).insetBy(dx: avatarSize * 0.4, dy: avatarSize * 0.4) transitionSourceAvatarFrame = CGRect(origin: CGPoint(x: avatarFrame.minX, y: -20.0), size: avatarFrame.size).insetBy(dx: avatarSize * 0.4, dy: avatarSize * 0.4)
} else { } else {
transitionSourceAvatarFrame = avatarFrame.offsetBy(dx: 0.0, dy: -avatarFrame.maxY).insetBy(dx: avatarSize * 0.4, dy: avatarSize * 0.4) transitionSourceAvatarFrame = avatarFrame.offsetBy(dx: 0.0, dy: -avatarFrame.maxY).insetBy(dx: avatarSize * 0.4, dy: avatarSize * 0.4)
@ -3304,7 +3306,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
controlsClippingFrame = apparentAvatarFrame controlsClippingFrame = apparentAvatarFrame
} }
let avatarClipOffset: CGFloat = !self.isAvatarExpanded && deviceMetrics.hasDynamicIsland && self.avatarClippingNode.clipsToBounds ? 48.0 : 0.0 let avatarClipOffset: CGFloat = !self.isAvatarExpanded && deviceMetrics.hasDynamicIsland && self.avatarClippingNode.clipsToBounds && !isLandscape ? 48.0 : 0.0
let clippingNodeTransition = ContainedViewLayoutTransition.immediate let clippingNodeTransition = ContainedViewLayoutTransition.immediate
clippingNodeTransition.updateFrame(layer: self.avatarClippingNode.layer, frame: CGRect(origin: CGPoint(x: 0.0, y: avatarClipOffset), size: CGSize(width: width, height: 1000.0))) clippingNodeTransition.updateFrame(layer: self.avatarClippingNode.layer, frame: CGRect(origin: CGPoint(x: 0.0, y: avatarClipOffset), size: CGSize(width: width, height: 1000.0)))
clippingNodeTransition.updateSublayerTransformOffset(layer: self.avatarClippingNode.layer, offset: CGPoint(x: 0.0, y: -avatarClipOffset)) clippingNodeTransition.updateSublayerTransformOffset(layer: self.avatarClippingNode.layer, offset: CGPoint(x: 0.0, y: -avatarClipOffset))
@ -3351,7 +3353,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
transition.updateSublayerTransformScale(node: self.avatarListNode.listContainerTransformNode, scale: avatarListContainerScale) transition.updateSublayerTransformScale(node: self.avatarListNode.listContainerTransformNode, scale: avatarListContainerScale)
} }
if deviceMetrics.hasDynamicIsland && self.forumTopicThreadId == nil && self.navigationTransition == nil { if deviceMetrics.hasDynamicIsland && self.forumTopicThreadId == nil && self.navigationTransition == nil && !isLandscape {
let maskValue = max(0.0, min(1.0, contentOffset / 120.0)) let maskValue = max(0.0, min(1.0, contentOffset / 120.0))
self.avatarListNode.containerNode.view.mask = self.avatarListNode.maskNode.view self.avatarListNode.containerNode.view.mask = self.avatarListNode.maskNode.view
if maskValue > 0.03 { if maskValue > 0.03 {
@ -3368,7 +3370,12 @@ final class PeerInfoHeaderNode: ASDisplayNode {
self.avatarListNode.listContainerNode.topShadowNode.isHidden = !self.isAvatarExpanded self.avatarListNode.listContainerNode.topShadowNode.isHidden = !self.isAvatarExpanded
self.avatarListNode.maskNode.position = CGPoint(x: 0.0, y: -self.avatarListNode.frame.minY + 48.0 + 85.5) var avatarMaskOffset: CGFloat = 0.0
if contentOffset < 0.0 {
avatarMaskOffset -= contentOffset
}
self.avatarListNode.maskNode.position = CGPoint(x: 0.0, y: -self.avatarListNode.frame.minY + 48.0 + 85.5 + avatarMaskOffset)
self.avatarListNode.maskNode.bounds = CGRect(origin: .zero, size: CGSize(width: 171.0, height: 171.0)) self.avatarListNode.maskNode.bounds = CGRect(origin: .zero, size: CGSize(width: 171.0, height: 171.0))
self.avatarListNode.bottomCoverNode.position = self.avatarListNode.maskNode.position self.avatarListNode.bottomCoverNode.position = self.avatarListNode.maskNode.position