From 24533df255f6d8934d01b86d8a5720ec8bcf5642 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 21 Jan 2025 03:41:22 +0400 Subject: [PATCH] Various improvements --- .../Sources/AccountContext.swift | 1 + .../Sources/ChatListController.swift | 3 + .../Resources/PresentationResourceKey.swift | 1 + .../PresentationResourcesRootController.swift | 2 +- .../EmojiStatusSelectionComponent/BUILD | 1 + .../EmojiStatusSelectionComponent.swift | 46 ++++++++---- .../Sources/GiftViewScreen.swift | 75 ++++++++++++------- .../Sources/PeerInfoHeaderNode.swift | 10 ++- .../Sources/PeerInfoScreen.swift | 9 ++- .../Sources/SharedAccountContext.swift | 5 ++ 10 files changed, 105 insertions(+), 48 deletions(-) diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index 3d1200c9ce..e3ecfa8b86 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -1103,6 +1103,7 @@ public protocol SharedAccountContext: AnyObject { func makeStarsIntroScreen(context: AccountContext) -> ViewController func makeGiftViewScreen(context: AccountContext, message: EngineMessage, shareStory: (() -> Void)?) -> ViewController func makeGiftViewScreen(context: AccountContext, gift: StarGift.UniqueGift, shareStory: (() -> Void)?, dismissed: (() -> Void)?) -> ViewController + func makeGiftWearPreviewScreen(context: AccountContext, gift: StarGift.UniqueGift) -> ViewController func makeStorySharingScreen(context: AccountContext, subject: StorySharingSubject, parentController: ViewController) -> ViewController diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 90d16017c1..c38c158370 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -880,6 +880,9 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController return sourceView } ) + controller.pushController = { [weak self] c in + self?.push(c) + } self.emojiStatusSelectionController = controller self.present(controller, in: .window(.root)) } diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift index 8cf36e5679..5498b805bd 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift @@ -304,6 +304,7 @@ public enum PresentationResourceKey: Int32 { case storyViewListLikeIcon case navigationPostStoryIcon + case navigationSortIcon case chatReplyBackgroundTemplateIncomingImage case chatReplyBackgroundTemplateOutgoingDashedImage diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesRootController.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesRootController.swift index 5e3428a26b..0e4915686b 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesRootController.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesRootController.swift @@ -195,7 +195,7 @@ public struct PresentationResourcesRootController { } public static func navigationSortIcon(_ theme: PresentationTheme) -> UIImage? { - return theme.image(PresentationResourceKey.navigationPostStoryIcon.rawValue, { theme in + return theme.image(PresentationResourceKey.navigationSortIcon.rawValue, { theme in return generateTintedImage(image: UIImage(bundleImageName: "Peer Info/SortIcon"), color: .white) }) } diff --git a/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/BUILD b/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/BUILD index bb7ae0a1d5..926aa3334d 100644 --- a/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/BUILD +++ b/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/BUILD @@ -16,6 +16,7 @@ swift_library( "//submodules/TelegramCore:TelegramCore", "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/ComponentFlow:ComponentFlow", + "//submodules/TelegramNotices", "//submodules/TelegramUI/Components/AnimationCache:AnimationCache", "//submodules/TelegramUI/Components/MultiAnimationRenderer:MultiAnimationRenderer", "//submodules/TelegramUI/Components/EntityKeyboard:EntityKeyboard", diff --git a/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusSelectionComponent.swift b/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusSelectionComponent.swift index d08905b75b..79bd0a020c 100644 --- a/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusSelectionComponent.swift +++ b/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusSelectionComponent.swift @@ -19,6 +19,7 @@ import AppBundle import GZip import EmojiStatusComponent import Postbox +import TelegramNotices private func randomGenericReactionEffect(context: AccountContext) -> Signal { return context.engine.stickers.loadedStickerPack(reference: .emojiGenericAnimations, forceActualized: false) @@ -1322,23 +1323,25 @@ public final class EmojiStatusSelectionController: ViewController { case .statusSelection: animateOutToView = true } - - if animateOutToView, item != nil, let destinationView = controller.destinationItemView() { - if let snapshotView = destinationView.snapshotView(afterScreenUpdates: false) { - snapshotView.frame = destinationView.frame - destinationView.superview?.insertSubview(snapshotView, belowSubview: destinationView) - snapshotView.layer.animateScale(from: 1.0, to: 0.001, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in - snapshotView?.removeFromSuperview() - }) - } - destinationView.isHidden = true - } - + switch controller.mode { case .statusSelection: if let gift = item?.itemGift { - let _ = (self.context.engine.accountData.setStarGiftStatus(starGift: gift, expirationDate: nil) - |> deliverOnMainQueue).start() + animateOutToView = false + + let _ = (ApplicationSpecificNotice.getStarGiftWearTips(accountManager: self.context.sharedContext.accountManager) + |> deliverOnMainQueue).start(next: { [weak self] count in + guard let self else { + return + } + if !self.context.isPremium || count < 3, let pushController = controller.pushController { + let controller = self.context.sharedContext.makeGiftWearPreviewScreen(context: self.context, gift: gift) + pushController(controller) + } else { + let _ = (self.context.engine.accountData.setStarGiftStatus(starGift: gift, expirationDate: nil) + |> deliverOnMainQueue).start() + } + }) } else { let _ = (self.context.engine.accountData.setEmojiStatus(file: item?.itemFile, expirationDate: nil) |> deliverOnMainQueue).start() @@ -1372,6 +1375,17 @@ public final class EmojiStatusSelectionController: ViewController { completion() } + if animateOutToView, item != nil, let destinationView = controller.destinationItemView() { + if let snapshotView = destinationView.snapshotView(afterScreenUpdates: false) { + snapshotView.frame = destinationView.frame + destinationView.superview?.insertSubview(snapshotView, belowSubview: destinationView) + snapshotView.layer.animateScale(from: 1.0, to: 0.001, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + } + destinationView.isHidden = true + } + if animateOutToView, let item = item, let destinationView = controller.destinationItemView() { var emojiString: String? if let itemFile = item.itemFile { @@ -1444,7 +1458,7 @@ public final class EmojiStatusSelectionController: ViewController { private let color: UIColor? private let mode: Mode private let destinationItemView: () -> UIView? - + fileprivate let _ready = Promise() override public var ready: Promise { return self._ready @@ -1454,6 +1468,8 @@ public final class EmojiStatusSelectionController: ViewController { return true } + public var pushController: ((ViewController) -> Void)? + public init(context: AccountContext, mode: Mode, sourceView: UIView, emojiContent: Signal, currentSelection: Int64?, color: UIColor? = nil, destinationItemView: @escaping () -> UIView?) { self.context = context self.mode = mode diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift index 316321a93e..1c05403951 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift @@ -498,12 +498,19 @@ private final class GiftViewSheetContent: CombinedComponent { showUpgradePreview = true } + var showWearPreview = false + if state.inWearPreview { + showWearPreview = true + } else if case .wearPreview = component.subject { + showWearPreview = true + } + let cancel = component.cancel let buttons = buttons.update( component: ButtonsComponent( theme: theme, isOverlay: showUpgradePreview || uniqueGift != nil, - showMoreButton: uniqueGift != nil && !state.inWearPreview, + showMoreButton: uniqueGift != nil && !showWearPreview, closePressed: { [weak state] in guard let state else { return @@ -528,13 +535,13 @@ private final class GiftViewSheetContent: CombinedComponent { availableSize: CGSize(width: 30.0, height: 30.0), transition: context.transition ) - + var originY: CGFloat = 0.0 let headerHeight: CGFloat let headerSubject: GiftCompositionComponent.Subject? if let uniqueGift { - if state.inWearPreview { + if showWearPreview { headerHeight = 200.0 } else if case let .peerId(peerId) = uniqueGift.owner, peerId == component.context.account.peerId { headerHeight = 314.0 @@ -557,7 +564,7 @@ private final class GiftViewSheetContent: CombinedComponent { } var wearPeerNameChild: _UpdatedChildComponent? - if state.inWearPreview, let uniqueGift { + if showWearPreview, let uniqueGift { var peerName = "" if let accountPeer = state.peerMap[component.context.account.peerId] { peerName = accountPeer.displayTitle(strings: strings, displayOrder: nameDisplayOrder) @@ -577,10 +584,17 @@ private final class GiftViewSheetContent: CombinedComponent { transition: .immediate ) + let giftTitle: String + if case .wearPreview = component.subject { + giftTitle = uniqueGift.title + } else { + giftTitle = "\(uniqueGift.title) #\(uniqueGift.number)" + } + let wearTitle = wearTitle.update( component: MultilineTextComponent( text: .plain(NSAttributedString( - string: strings.Gift_Wear_Wear("\(uniqueGift.title) #\(uniqueGift.number)").string, + string: strings.Gift_Wear_Wear(giftTitle).string, font: Font.bold(24.0), textColor: theme.actionSheet.primaryTextColor, paragraphAlignment: .center @@ -638,7 +652,7 @@ private final class GiftViewSheetContent: CombinedComponent { subject: headerSubject, animationOffset: animationOffset, animationScale: animationScale, - displayAnimationStars: state.inWearPreview, + displayAnimationStars: showWearPreview, externalState: giftCompositionExternalState, requestUpdate: { [weak state] in state?.updated() @@ -660,22 +674,24 @@ private final class GiftViewSheetContent: CombinedComponent { vibrantColor = UIColor.white.withAlphaComponent(0.6) } - if let wearPeerNameChild, let accountPeer = state.peerMap[component.context.account.peerId] { - let wearAvatar = wearAvatar.update( - component: AvatarComponent( - context: component.context, - theme: theme, - peer: accountPeer - ), - environment: {}, - availableSize: CGSize(width: 100.0, height: 100.0), - transition: context.transition - ) - context.add(wearAvatar - .position(CGPoint(x: context.availableSize.width / 2.0, y: 67.0)) - .appear(.default(scale: true, alpha: true)) - .disappear(.default(scale: true, alpha: true)) - ) + if let wearPeerNameChild { + if let accountPeer = state.peerMap[component.context.account.peerId] { + let wearAvatar = wearAvatar.update( + component: AvatarComponent( + context: component.context, + theme: theme, + peer: accountPeer + ), + environment: {}, + availableSize: CGSize(width: 100.0, height: 100.0), + transition: context.transition + ) + context.add(wearAvatar + .position(CGPoint(x: context.availableSize.width / 2.0, y: 67.0)) + .appear(.default(scale: true, alpha: true)) + .disappear(.default(scale: true, alpha: true)) + ) + } let wearPeerStatus = wearPeerStatus.update( component: MultilineTextComponent( @@ -1772,7 +1788,7 @@ private final class GiftViewSheetContent: CombinedComponent { originY += table.size.height + 23.0 } - if incoming && !converted && !upgraded && !showUpgradePreview && !state.inWearPreview { + if incoming && !converted && !upgraded && !showUpgradePreview && !showWearPreview { let linkColor = theme.actionSheet.controlAccentColor if state.cachedSmallChevronImage == nil || state.cachedSmallChevronImage?.1 !== environment.theme { state.cachedSmallChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Item List/InlineTextRightArrow"), color: linkColor)!, theme) @@ -1838,7 +1854,7 @@ private final class GiftViewSheetContent: CombinedComponent { pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9) ) let buttonChild: _UpdatedChildComponent - if state.inWearPreview, let uniqueGift { + if showWearPreview, let uniqueGift { let buttonContent: AnyComponentWithIdentity if !component.context.isPremium { buttonContent = AnyComponentWithIdentity( @@ -1898,8 +1914,12 @@ private final class GiftViewSheetContent: CombinedComponent { controller.present(tooltipController, in: .window(.root)) } else { state.commitWear(uniqueGift) - Queue.mainQueue().after(0.2) { - component.showAttributeInfo(statusTag, strings.Gift_View_PutOn("\(uniqueGift.title) #\(uniqueGift.number)").string) + if case .wearPreview = component.subject { + component.cancel(true) + } else { + Queue.mainQueue().after(0.2) { + component.showAttributeInfo(statusTag, strings.Gift_View_PutOn("\(uniqueGift.title) #\(uniqueGift.number)").string) + } } } } @@ -2212,6 +2232,7 @@ public class GiftViewScreen: ViewControllerComponentContainer { case profileGift(EnginePeer.Id, ProfileGiftsContext.State.StarGift) case soldOutGift(StarGift.Gift) case upgradePreview([StarGift.UniqueGift.Attribute], String) + case wearPreview(StarGift.UniqueGift) var arguments: (peerId: EnginePeer.Id?, fromPeerId: EnginePeer.Id?, fromPeerName: String?, messageId: EngineMessage.Id?, reference: StarGiftReference?, incoming: Bool, gift: StarGift, date: Int32, convertStars: Int64?, text: String?, entities: [MessageTextEntity]?, nameHidden: Bool, savedToProfile: Bool, converted: Bool, upgraded: Bool, canUpgrade: Bool, upgradeStars: Int64?, transferStars: Int64?, canExportDate: Int32?, upgradeMessageId: Int32?)? { switch self { @@ -2250,7 +2271,7 @@ public class GiftViewScreen: ViewControllerComponentContainer { return nil } } - case let .uniqueGift(gift): + case let .uniqueGift(gift), let .wearPreview(gift): return (nil, nil, nil, nil, nil, false, .unique(gift), 0, nil, nil, nil, false, false, false, false, false, nil, nil, nil, nil) case let .profileGift(peerId, gift): var messageId: EngineMessage.Id? diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift index 0d8b4904fe..2b4a03ac03 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift @@ -168,7 +168,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { var displayPremiumIntro: ((UIView, PeerEmojiStatus?, Signal<(TelegramMediaFile, LoadedStickerPack)?, NoError>, Bool) -> Void)? var displayStatusPremiumIntro: (() -> Void)? var displayUniqueGiftInfo: ((UIView, String) -> Void)? - var openUniqueGift: ((String) -> Void)? + var openUniqueGift: ((UIView, String) -> Void)? var navigateToForum: (() -> Void)? @@ -918,7 +918,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { return } if let uniqueGiftSlug { - strongSelf.openUniqueGift?(uniqueGiftSlug) + strongSelf.openUniqueGift?(strongSelf.titleStatusIconView, uniqueGiftSlug) } else { strongSelf.displayPremiumIntro?(strongSelf.titleStatusIconView, currentEmojiStatus, strongSelf.emojiStatusFileAndPackTitle.get(), false) } @@ -979,7 +979,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { return } if let uniqueGiftSlug { - strongSelf.openUniqueGift?(uniqueGiftSlug) + strongSelf.openUniqueGift?(strongSelf.titleExpandedStatusIconView, uniqueGiftSlug) } else { strongSelf.displayPremiumIntro?(strongSelf.titleExpandedStatusIconView, currentEmojiStatus, strongSelf.emojiStatusFileAndPackTitle.get(), true) } @@ -2266,7 +2266,9 @@ final class PeerInfoHeaderNode: ASDisplayNode { if let status = peer?.emojiStatus, case .starGift = status.content { backgroundCoverSubject = .status(status) if !self.didSetupBackgroundCover { - backgroundCoverAnimateIn = true + if !self.isSettings { + backgroundCoverAnimateIn = true + } self.didSetupBackgroundCover = true } } else if let peer { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index 2e43048545..e987e40834 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -4587,9 +4587,16 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro return sourceView } ) + emojiStatusSelectionController.pushController = { [weak self] c in + self?.controller?.push(c) + } strongSelf.emojiStatusSelectionController = emojiStatusSelectionController strongSelf.controller?.present(emojiStatusSelectionController, in: .window(.root)) } + + self.headerNode.openUniqueGift = { [weak self] sourceView, _ in + self?.headerNode.displayPremiumIntro?(sourceView, nil, .single(nil), false) + } } else { if peerId == context.account.peerId { self.privacySettings.set(.single(nil) |> then(context.engine.privacy.requestAccountPrivacySettings() |> map(Optional.init))) @@ -4673,7 +4680,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro controller.present(tooltipController, in: .current) } - self.headerNode.openUniqueGift = { [weak self] slug in + self.headerNode.openUniqueGift = { [weak self] _, slug in guard let self else { return } diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index ab45b8f91b..6379d6ebce 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -2956,6 +2956,11 @@ public final class SharedAccountContextImpl: SharedAccountContext { return controller } + public func makeGiftWearPreviewScreen(context: AccountContext, gift: StarGift.UniqueGift) -> ViewController { + let controller = GiftViewScreen(context: context, subject: .wearPreview(gift)) + return controller + } + public func makeStorySharingScreen(context: AccountContext, subject: StorySharingSubject, parentController: ViewController) -> ViewController { let editorSubject: Signal switch subject {