diff --git a/submodules/Components/ReactionButtonListComponent/Sources/ReactionButtonListComponent.swift b/submodules/Components/ReactionButtonListComponent/Sources/ReactionButtonListComponent.swift index 6cf3f9b459..632084e1ff 100644 --- a/submodules/Components/ReactionButtonListComponent/Sources/ReactionButtonListComponent.swift +++ b/submodules/Components/ReactionButtonListComponent/Sources/ReactionButtonListComponent.swift @@ -23,6 +23,7 @@ public final class ReactionIconView: PortalSourceView { private var file: TelegramMediaFile? private var animationCache: AnimationCache? private var animationRenderer: MultiAnimationRenderer? + private var contentTintColor: UIColor? private var placeholderColor: UIColor? private var size: CGSize? private var animateIdle: Bool? @@ -58,6 +59,7 @@ public final class ReactionIconView: PortalSourceView { fileId: Int64, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, + tintColor: UIColor?, placeholderColor: UIColor, animateIdle: Bool, reaction: MessageReaction.Reaction, @@ -66,6 +68,7 @@ public final class ReactionIconView: PortalSourceView { self.context = context self.animationCache = animationCache self.animationRenderer = animationRenderer + self.contentTintColor = tintColor self.placeholderColor = placeholderColor self.size = size self.animateIdle = animateIdle @@ -112,6 +115,8 @@ public final class ReactionIconView: PortalSourceView { transition.updateFrame(layer: animationLayer, frame: CGRect(origin: CGPoint(x: floor((size.width - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize)) } + + self.updateTintColor() } public func updateIsAnimationHidden(isAnimationHidden: Bool, transition: ContainedViewLayoutTransition) { @@ -170,6 +175,36 @@ public final class ReactionIconView: PortalSourceView { animationLayer.frame = CGRect(origin: CGPoint(x: floor((size.width - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize) animationLayer.isVisibleForAnimations = animateIdle && context.sharedContext.energyUsageSettings.loopEmoji + self.updateTintColor() + } + + private func updateTintColor() { + guard let file = self.file, let animationLayer = self.animationLayer else { + return + } + var accentTint = false + if file.isCustomTemplateEmoji { + accentTint = true + } + for attribute in file.attributes { + if case let .CustomEmoji(_, _, _, packReference) = attribute { + switch packReference { + case let .id(id, _): + if id == 773947703670341676 || id == 2964141614563343 { + accentTint = true + } + default: + break + } + } + } + if accentTint, let tintColor = self.contentTintColor { + animationLayer.contentTintColor = tintColor + animationLayer.dynamicColor = tintColor + } else { + animationLayer.contentTintColor = nil + animationLayer.dynamicColor = nil + } } func reset() { @@ -769,6 +804,17 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceView { animateIdle = false } + let tintColor: UIColor + if layout.backgroundLayout.colors.isSelected { + if layout.spec.component.colors.selectedForeground != 0 { + tintColor = UIColor(argb: layout.spec.component.colors.selectedForeground) + } else { + tintColor = .white + } + } else { + tintColor = UIColor(argb: layout.spec.component.colors.deselectedForeground) + } + iconView.update( size: layout.imageFrame.size, context: layout.spec.component.context, @@ -776,6 +822,7 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceView { fileId: fileId, animationCache: arguments.animationCache, animationRenderer: arguments.animationRenderer, + tintColor: tintColor, placeholderColor: layout.spec.component.chosenOrder != nil ? UIColor(argb: layout.spec.component.colors.selectedMediaPlaceholder) : UIColor(argb: layout.spec.component.colors.deselectedMediaPlaceholder), animateIdle: animateIdle, reaction: layout.spec.component.reaction.value, diff --git a/submodules/Components/ReactionListContextMenuContent/Sources/ReactionListContextMenuContent.swift b/submodules/Components/ReactionListContextMenuContent/Sources/ReactionListContextMenuContent.swift index 777a849ebc..6c1e332766 100644 --- a/submodules/Components/ReactionListContextMenuContent/Sources/ReactionListContextMenuContent.swift +++ b/submodules/Components/ReactionListContextMenuContent/Sources/ReactionListContextMenuContent.swift @@ -218,6 +218,37 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent } reactionLayer.frame = iconFrame } + + self.updateReactionAccentColor() + } + + func updateReactionAccentColor() { + guard let file = self.file, let reactionLayer = self.reactionLayer, let theme = self.theme else { + return + } + var accentTint = false + if file.isCustomTemplateEmoji { + accentTint = true + } + for attribute in file.attributes { + if case let .CustomEmoji(_, _, _, packReference) = attribute { + switch packReference { + case let .id(id, _): + if id == 773947703670341676 || id == 2964141614563343 { + accentTint = true + } + default: + break + } + } + } + if accentTint { + reactionLayer.contentTintColor = theme.contextMenu.badgeFillColor + reactionLayer.dynamicColor = theme.contextMenu.badgeFillColor + } else { + reactionLayer.contentTintColor = nil + reactionLayer.dynamicColor = nil + } } func update(presentationData: PresentationData, constrainedSize: CGSize, isSelected: Bool) -> CGSize { @@ -227,6 +258,8 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent if let iconNode = self.iconNode { iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Reactions"), color: presentationData.theme.contextMenu.primaryColor) } + + self.updateReactionLayer() } let sideInset: CGFloat = 12.0 @@ -478,6 +511,35 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent } } + func updateReactionAccentColor(theme: PresentationTheme) { + guard let file = self.file, let reactionLayer = self.reactionLayer else { + return + } + var accentTint = false + if file.isCustomTemplateEmoji { + accentTint = true + } + for attribute in file.attributes { + if case let .CustomEmoji(_, _, _, packReference) = attribute { + switch packReference { + case let .id(id, _): + if id == 773947703670341676 || id == 2964141614563343 { + accentTint = true + } + default: + break + } + } + } + if accentTint { + reactionLayer.contentTintColor = theme.contextMenu.badgeFillColor + reactionLayer.dynamicColor = theme.contextMenu.badgeFillColor + } else { + reactionLayer.contentTintColor = nil + reactionLayer.dynamicColor = nil + } + } + func update(size: CGSize, presentationData: PresentationData, item: EngineMessageReactionListContext.Item, isLast: Bool, syncronousLoad: Bool) { let avatarInset: CGFloat = 12.0 let avatarSpacing: CGFloat = 8.0 @@ -495,6 +557,7 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent if availableReaction.value == reaction { self.file = availableReaction.centerAnimation self.updateReactionLayer() + self.updateReactionAccentColor(theme: presentationData.theme) break } } @@ -507,6 +570,7 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent } strongSelf.file = file strongSelf.updateReactionLayer() + strongSelf.updateReactionAccentColor(theme: presentationData.theme) }).strict() } } else { @@ -520,6 +584,7 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent } } } + self.updateReactionAccentColor(theme: presentationData.theme) if self.item != item { self.item = item diff --git a/submodules/Components/SheetComponent/Sources/SheetComponent.swift b/submodules/Components/SheetComponent/Sources/SheetComponent.swift index 7ea3f24fe2..9214a41be6 100644 --- a/submodules/Components/SheetComponent/Sources/SheetComponent.swift +++ b/submodules/Components/SheetComponent/Sources/SheetComponent.swift @@ -39,7 +39,6 @@ public final class SheetComponentEnvironment: Equatable { public let sheetComponentTag = GenericComponentViewTag() public final class SheetComponent: Component { - public typealias EnvironmentType = (ChildEnvironmentType, SheetComponentEnvironment) public enum BackgroundColor: Equatable { @@ -54,15 +53,18 @@ public final class SheetComponent: Component { public let content: AnyComponent public let backgroundColor: BackgroundColor + public let followContentSizeChanges: Bool public let animateOut: ActionSlot> public init( content: AnyComponent, backgroundColor: BackgroundColor, + followContentSizeChanges: Bool = false, animateOut: ActionSlot> ) { self.content = content self.backgroundColor = backgroundColor + self.followContentSizeChanges = followContentSizeChanges self.animateOut = animateOut } @@ -73,6 +75,9 @@ public final class SheetComponent: Component { if lhs.backgroundColor != rhs.backgroundColor { return false } + if lhs.followContentSizeChanges != rhs.followContentSizeChanges { + return false + } if lhs.animateOut != rhs.animateOut { return false } @@ -338,12 +343,15 @@ public final class SheetComponent: Component { } transition.setFrame(view: self.scrollView, frame: CGRect(origin: CGPoint(), size: availableSize), completion: nil) + let previousContentSize = self.scrollView.contentSize self.scrollView.contentSize = contentSize self.scrollView.contentInset = UIEdgeInsets(top: max(0.0, availableSize.height - contentSize.height) + contentSize.height, left: 0.0, bottom: 0.0, right: 0.0) self.ignoreScrolling = false if let currentAvailableSize = self.currentAvailableSize, currentAvailableSize.height != availableSize.height { self.scrollView.contentOffset = CGPoint(x: 0.0, y: -(availableSize.height - contentSize.height)) + } else if component.followContentSizeChanges, !previousContentSize.height.isZero, previousContentSize != contentSize { + transition.setBounds(view: self.scrollView, bounds: CGRect(origin: CGPoint(x: 0.0, y: -(availableSize.height - contentSize.height)), size: availableSize)) } if self.currentHasInputHeight != previousHasInputHeight { transition.setBounds(view: self.scrollView, bounds: CGRect(origin: CGPoint(x: 0.0, y: -(availableSize.height - contentSize.height)), size: self.scrollView.bounds.size)) diff --git a/submodules/PremiumUI/Sources/PremiumBoostScreen.swift b/submodules/PremiumUI/Sources/PremiumBoostScreen.swift new file mode 100644 index 0000000000..64f3368a13 --- /dev/null +++ b/submodules/PremiumUI/Sources/PremiumBoostScreen.swift @@ -0,0 +1,123 @@ +import Foundation +import UIKit +import Display +import SwiftSignalKit +import TelegramCore +import AccountContext +import TelegramPresentationData +import UndoUI +import PresentationDataUtils + +//TODO:localize + +public func PremiumBoostScreen( + context: AccountContext, + contentContext: Any?, + peerId: EnginePeer.Id, + isCurrent: Bool, + status: ChannelBoostStatus?, + myBoostStatus: MyBoostStatus?, + forceDark: Bool, + openPeer: @escaping (EnginePeer) -> Void, + presentController: @escaping (ViewController) -> Void, + pushController: @escaping (ViewController) -> Void, + dismissed: @escaping () -> Void +) { + let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) + |> deliverOnMainQueue).startStandalone(next: { peer in + guard let peer, let status else { + return + } + + var myBoostCount: Int32 = 0 + var availableBoosts: [MyBoostStatus.Boost] = [] + var occupiedBoosts: [MyBoostStatus.Boost] = [] + if let myBoostStatus { + for boost in myBoostStatus.boosts { + if let boostPeer = boost.peer { + if boostPeer.id == peer.id { + myBoostCount += 1 + } else { + occupiedBoosts.append(boost) + } + } else { + availableBoosts.append(boost) + } + } + } + + var currentLevel = Int32(status.level) + var currentLevelBoosts = Int32(status.currentLevelBoosts) + var nextLevelBoosts = status.nextLevelBoosts.flatMap(Int32.init) + + if myBoostCount > 0 && status.boosts == currentLevelBoosts { + currentLevel = max(0, currentLevel - 1) + nextLevelBoosts = currentLevelBoosts + currentLevelBoosts = max(0, currentLevelBoosts - 1) + } + + let subject: PremiumLimitScreen.Subject = .storiesChannelBoost(peer: peer, isCurrent: isCurrent, level: currentLevel, currentLevelBoosts: currentLevelBoosts, nextLevelBoosts: nextLevelBoosts, link: nil, myBoostCount: myBoostCount) + let nextSubject: PremiumLimitScreen.Subject = .storiesChannelBoost(peer: peer, isCurrent: isCurrent, level: currentLevel, currentLevelBoosts: currentLevelBoosts, nextLevelBoosts: nextLevelBoosts, link: nil, myBoostCount: myBoostCount + 1) + var nextCount = Int32(status.boosts + 1) + + var updateImpl: (() -> Void)? + var dismissImpl: (() -> Void)? + let controller = PremiumLimitScreen(context: context, subject: subject, count: Int32(status.boosts), forceDark: forceDark, action: { + let dismiss = false + updateImpl?() + return dismiss + }, + openPeer: { peer in + openPeer(peer) + }) + pushController(controller) + + controller.disposed = { + dismissed() + } + + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + updateImpl = { [weak controller] in + if let _ = status.nextLevelBoosts { + if let availableBoost = availableBoosts.first { + let _ = context.engine.peers.applyChannelBoost(peerId: peerId, slots: [availableBoost.slot]).startStandalone() + controller?.updateSubject(nextSubject, count: nextCount) + + availableBoosts.removeFirst() + nextCount += 1 + } else if !occupiedBoosts.isEmpty, let myBoostStatus { + let replaceController = ReplaceBoostScreen(context: context, peerId: peerId, myBoostStatus: myBoostStatus, replaceBoosts: { slots in + let _ = context.engine.peers.applyChannelBoost(peerId: peerId, slots: slots).startStandalone() + + let undoController = UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: "\(slots.count) boosts are reassigned from 1 other channel.", timeout: nil, customUndoText: nil), elevatedLayout: true, position: .bottom, action: { _ in return true }) + presentController(undoController) + }) + dismissImpl?() + pushController(replaceController) + } else { + let controller = textAlertController( + sharedContext: context.sharedContext, + updatedPresentationData: nil, + title: presentationData.strings.ChannelBoost_Error_PremiumNeededTitle, + text: presentationData.strings.ChannelBoost_Error_PremiumNeededText, + actions: [ + TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), + TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Yes, action: { + dismissImpl?() + let controller = context.sharedContext.makePremiumIntroController(context: context, source: .channelBoost(peerId), forceDark: forceDark, dismissed: nil) + pushController(controller) + }) + ], + parseMarkdown: true + ) + presentController(controller) + } + } else { + dismissImpl?() + } + } + dismissImpl = { [weak controller] in + controller?.dismissAnimated() + } + }) +} diff --git a/submodules/PremiumUI/Sources/PremiumDemoScreen.swift b/submodules/PremiumUI/Sources/PremiumDemoScreen.swift index 7166917916..691c500d6d 100644 --- a/submodules/PremiumUI/Sources/PremiumDemoScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumDemoScreen.swift @@ -1262,11 +1262,6 @@ public class PremiumDemoScreen: ViewControllerComponentContainer { self.view.disablesInteractiveModalDismiss = true } - public override func replace(with controller: ViewController) { - self.dismissAnimated() - super.replace(with: controller) - } - public func dismissAnimated() { if let view = self.node.hostView.findTaggedView(tag: SheetComponent.View.Tag()) as? SheetComponent.View { view.dismissAnimated() diff --git a/submodules/PremiumUI/Sources/PremiumLimitScreen.swift b/submodules/PremiumUI/Sources/PremiumLimitScreen.swift index 8ca220b408..f3d7c5bf3b 100644 --- a/submodules/PremiumUI/Sources/PremiumLimitScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumLimitScreen.swift @@ -162,6 +162,9 @@ public class PremiumLimitDisplayComponent: Component { private let badgeLabel: BadgeLabelView private let badgeLabelMaskView = UIImageView() + private var badgeTailPosition: CGFloat = 0.0 + private var badgeShapeArguments: (Double, Double, CGSize, CGFloat, CGFloat)? + private let hapticFeedback = HapticFeedback() override init(frame: CGRect) { @@ -232,6 +235,10 @@ public class PremiumLimitDisplayComponent: Component { fatalError("init(coder:) has not been implemented") } + deinit { + self.badgeShapeAnimator?.invalidate() + } + private var didPlayAppearanceAnimation = false func playAppearanceAnimation(component: PremiumLimitDisplayComponent, badgeFullSize: CGSize, from: CGFloat? = nil) { if from == nil { @@ -534,17 +541,30 @@ public class PremiumLimitDisplayComponent: Component { if badgePosition > 1.0 - 0.15 { progressTransition.setAnchorPoint(layer: self.badgeView.layer, anchorPoint: CGPoint(x: 1.0, y: 1.0)) - progressTransition.setShapeLayerPath(layer: self.badgeShapeLayer, path: generateRoundedRectWithTailPath(rectSize: badgeSize, tailPosition: component.isPremiumDisabled ? nil : 1.0).cgPath) + + if component.isPremiumDisabled || progressTransition.animation.isImmediate { + self.badgeShapeLayer.path = generateRoundedRectWithTailPath(rectSize: badgeSize, tailPosition: component.isPremiumDisabled ? nil : 1.0).cgPath + } else { + self.badgeShapeArguments = (CACurrentMediaTime(), 0.5, badgeSize, self.badgeTailPosition, 1.0) + self.animateBadgeTailPositionChange() + } + self.badgeTailPosition = 1.0 if let _ = self.badgeView.layer.animation(forKey: "appearance1") { - } else { self.badgeView.center = CGPoint(x: 3.0 + (size.width - 6.0) * badgePosition + 3.0, y: 82.0) } } else if badgePosition < 0.15 { progressTransition.setAnchorPoint(layer: self.badgeView.layer, anchorPoint: CGPoint(x: 0.0, y: 1.0)) - progressTransition.setShapeLayerPath(layer: self.badgeShapeLayer, path: generateRoundedRectWithTailPath(rectSize: badgeSize, tailPosition: component.isPremiumDisabled ? nil : 0.0).cgPath) - + + if component.isPremiumDisabled || progressTransition.animation.isImmediate { + self.badgeShapeLayer.path = generateRoundedRectWithTailPath(rectSize: badgeSize, tailPosition: component.isPremiumDisabled ? nil : 0.0).cgPath + } else { + self.badgeShapeArguments = (CACurrentMediaTime(), 0.5, badgeSize, self.badgeTailPosition, 0.0) + self.animateBadgeTailPositionChange() + } + self.badgeTailPosition = 0.0 + if let _ = self.badgeView.layer.animation(forKey: "appearance1") { } else { @@ -552,8 +572,15 @@ public class PremiumLimitDisplayComponent: Component { } } else { progressTransition.setAnchorPoint(layer: self.badgeView.layer, anchorPoint: CGPoint(x: 0.5, y: 1.0)) - progressTransition.setShapeLayerPath(layer: self.badgeShapeLayer, path: generateRoundedRectWithTailPath(rectSize: badgeSize, tailPosition: component.isPremiumDisabled ? nil : 0.5).cgPath) - + + if component.isPremiumDisabled || progressTransition.animation.isImmediate { + self.badgeShapeLayer.path = generateRoundedRectWithTailPath(rectSize: badgeSize, tailPosition: component.isPremiumDisabled ? nil : 0.5).cgPath + } else { + self.badgeShapeArguments = (CACurrentMediaTime(), 0.5, badgeSize, self.badgeTailPosition, 0.5) + self.animateBadgeTailPositionChange() + } + self.badgeTailPosition = 0.5 + if let _ = self.badgeView.layer.animation(forKey: "appearance1") { } else { @@ -617,6 +644,36 @@ public class PremiumLimitDisplayComponent: Component { return size } + private var badgeShapeAnimator: ConstantDisplayLinkAnimator? + private func animateBadgeTailPositionChange() { + if self.badgeShapeAnimator == nil { + self.badgeShapeAnimator = ConstantDisplayLinkAnimator(update: { [weak self] in + self?.animateBadgeTailPositionChange() + }) + self.badgeShapeAnimator?.isPaused = true + } + + if let (startTime, duration, badgeSize, initial, target) = self.badgeShapeArguments { + self.badgeShapeAnimator?.isPaused = false + + let t = CGFloat(max(0.0, min(1.0, (CACurrentMediaTime() - startTime) / duration))) + let value = initial + (target - initial) * t + + self.badgeShapeLayer.path = generateRoundedRectWithTailPath(rectSize: badgeSize, tailPosition: value).cgPath + + if t >= 1.0 { + self.badgeShapeArguments = nil + self.badgeShapeAnimator?.isPaused = true + self.badgeShapeAnimator?.invalidate() + self.badgeShapeAnimator = nil + } + } else { + self.badgeShapeAnimator?.isPaused = true + self.badgeShapeAnimator?.invalidate() + self.badgeShapeAnimator = nil + } + } + private func setupGradientAnimations() { guard let _ = self.component else { return @@ -1143,12 +1200,14 @@ private final class LimitSheetContent: CombinedComponent { string = strings.ChannelBoost_HelpUpgradeChannelText(peer.compactDisplayTitle, boostsString, storiesString).string } actionButtonText = strings.ChannelBoost_BoostChannel + buttonIconName = "Premium/BoostChannel" } else { titleText = strings.ChannelBoost_MaxLevelReached string = strings.ChannelBoost_BoostedChannelReachedLevel("\(level)", storiesString).string actionButtonText = strings.Common_OK + buttonIconName = nil + actionButtonHasGloss = false } - buttonIconName = "Premium/BoostChannel" } buttonAnimationName = nil defaultTitle = strings.ChannelBoost_Level("\(level)").string @@ -1161,11 +1220,13 @@ private final class LimitSheetContent: CombinedComponent { let prefixString = isCurrent ? strings.ChannelBoost_YouBoostedChannelText(peer.compactDisplayTitle).string : strings.ChannelBoost_YouBoostedOtherChannelText let storiesString = strings.ChannelBoost_StoriesPerDay(level + 1) - -// buttonIconName = nil -// actionButtonText = environment.strings.Common_OK - actionButtonText = "Boost Again" - + if let _ = remaining { + actionButtonText = "Boost Again" + } else { + buttonIconName = nil + actionButtonText = environment.strings.Common_OK + actionButtonHasGloss = false + } if let remaining { let boostsString = strings.ChannelBoost_MoreBoosts(remaining) if level == 0 { @@ -1209,7 +1270,6 @@ private final class LimitSheetContent: CombinedComponent { reachedMaximumLimit = false } - let contentSize: CGSize if state.initialized { let title = title.update( @@ -1391,6 +1451,7 @@ private final class LimitSheetContent: CombinedComponent { })) ) } else if let alternateTextChild { + textOffset += 9.0 textSize = alternateTextChild.size context.add(alternateTextChild .position(CGPoint(x: context.availableSize.width / 2.0, y: textOffset)) @@ -1498,7 +1559,7 @@ private final class LimitSheetContent: CombinedComponent { .position(CGPoint(x: context.availableSize.width / 2.0, y: buttonFrame.maxY + 50.0 + giftText.size.height / 2.0)) ) - additionalContentHeight += giftText.size.height + 50.0 + additionalContentHeight += giftText.size.height + 30.0 } contentSize = CGSize(width: context.availableSize.width, height: buttonFrame.maxY + additionalContentHeight + 5.0 + environment.safeInsets.bottom) @@ -1598,6 +1659,7 @@ private final class LimitSheetComponent: CombinedComponent { openGift: context.component.openGift )), backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor), + followContentSizeChanges: true, animateOut: animateOut ), environment: { diff --git a/submodules/PremiumUI/Sources/ReplaceBoostScreen.swift b/submodules/PremiumUI/Sources/ReplaceBoostScreen.swift index 7a3ba67ad7..a35783909f 100644 --- a/submodules/PremiumUI/Sources/ReplaceBoostScreen.swift +++ b/submodules/PremiumUI/Sources/ReplaceBoostScreen.swift @@ -19,6 +19,8 @@ import PeerListItemComponent import TelegramStringFormatting import AvatarNode +//TODO:localize + private final class ReplaceBoostScreenComponent: CombinedComponent { typealias EnvironmentType = ViewControllerComponentContainer.Environment @@ -799,6 +801,7 @@ public class ReplaceBoostScreen: ViewController { self?.node.selectedSlots = selectedSlots } presentControllerImpl = { [weak self] c in + self?.dismissAllTooltips() self?.present(c, in: .window(.root)) } @@ -819,15 +822,29 @@ public class ReplaceBoostScreen: ViewController { fatalError("init(coder:) has not been implemented") } - @objc private func cancelPressed() { - self.dismiss(animated: true, completion: nil) - } - override open func loadDisplayNode() { self.displayNode = Node(context: self.context, controller: self, component: self.component) self.displayNodeDidLoad() } + fileprivate func dismissAllTooltips() { + self.window?.forEachController({ controller in + if let controller = controller as? UndoOverlayController { + controller.dismiss() + } + }) + self.forEachController({ controller in + if let controller = controller as? UndoOverlayController { + controller.dismiss() + } + return true + }) + } + + @objc private func cancelPressed() { + self.dismiss(animated: true, completion: nil) + } + public override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) { self.view.endEditing(true) diff --git a/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift b/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift index 80db8ad146..36c96b3cc1 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift @@ -133,9 +133,9 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode { return { item, params, neighbors in if currentBackgroundNode == nil { currentBackgroundNode = createWallpaperBackgroundNode(context: item.context, forChatDisplay: false) + currentBackgroundNode?.update(wallpaper: item.wallpaper) + currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners) } - currentBackgroundNode?.update(wallpaper: item.wallpaper) - currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners) let insets: UIEdgeInsets let separatorHeight = UIScreenPixel @@ -189,6 +189,11 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode { if let strongSelf = self { strongSelf.item = item + if let currentBackgroundNode { + currentBackgroundNode.update(wallpaper: item.wallpaper) + currentBackgroundNode.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners) + } + strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: contentSize) var topOffset: CGFloat = 8.0 diff --git a/submodules/SettingsUI/Sources/Reactions/ReactionChatPreviewItem.swift b/submodules/SettingsUI/Sources/Reactions/ReactionChatPreviewItem.swift index 505feacc47..33ffc6a2fc 100644 --- a/submodules/SettingsUI/Sources/Reactions/ReactionChatPreviewItem.swift +++ b/submodules/SettingsUI/Sources/Reactions/ReactionChatPreviewItem.swift @@ -261,10 +261,10 @@ class ReactionChatPreviewItemNode: ListViewItemNode { return { item, params, neighbors in if currentBackgroundNode == nil { currentBackgroundNode = createWallpaperBackgroundNode(context: item.context, forChatDisplay: false) + currentBackgroundNode?.update(wallpaper: item.wallpaper) + currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners) } - currentBackgroundNode?.update(wallpaper: item.wallpaper) - currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners) - + let insets: UIEdgeInsets let separatorHeight = UIScreenPixel @@ -330,6 +330,11 @@ class ReactionChatPreviewItemNode: ListViewItemNode { strongSelf.item = item + if let currentBackgroundNode { + currentBackgroundNode.update(wallpaper: item.wallpaper) + currentBackgroundNode.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners) + } + if strongSelf.genericReactionEffectDisposable == nil { strongSelf.loadNextGenericReactionEffect(context: item.context) } diff --git a/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift b/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift index dc4b23622e..529833142e 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift @@ -128,7 +128,7 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe case let .messageActionSetSameChatWallPaper(wallpaper): return TelegramMediaAction(action: .setSameChatWallpaper(wallpaper: TelegramWallpaper(apiWallpaper: wallpaper))) case let .messageActionGiftCode(flags, boostPeer, months, slug): - return TelegramMediaAction(action: .giftCode(slug: slug, fromGiveaway: (flags & (1 << 0)) != 0, boostPeerId: boostPeer?.peerId, months: months)) + return TelegramMediaAction(action: .giftCode(slug: slug, fromGiveaway: (flags & (1 << 0)) != 0, isUnclaimed: (flags & (1 << 1)) != 0, boostPeerId: boostPeer?.peerId, months: months)) case .messageActionGiveawayLaunch: return TelegramMediaAction(action: .giveawayLaunched) } diff --git a/submodules/TelegramCore/Sources/State/ChannelBoost.swift b/submodules/TelegramCore/Sources/State/ChannelBoost.swift index caa5ded53e..3a6295d86f 100644 --- a/submodules/TelegramCore/Sources/State/ChannelBoost.swift +++ b/submodules/TelegramCore/Sources/State/ChannelBoost.swift @@ -113,7 +113,7 @@ func _internal_applyChannelBoost(account: Account, peerId: PeerId, slots: [Int32 |> map (Optional.init) |> `catch` { error -> Signal in return .complete() - } + } |> mapToSignal { result -> Signal in if let result = result { return account.postbox.transaction { transaction -> MyBoostStatus? in diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift index 7bf8020534..23ed143d9e 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift @@ -109,7 +109,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { case requestedPeer(buttonId: Int32, peerId: PeerId) case setChatWallpaper(wallpaper: TelegramWallpaper) case setSameChatWallpaper(wallpaper: TelegramWallpaper) - case giftCode(slug: String, fromGiveaway: Bool, boostPeerId: PeerId?, months: Int32) + case giftCode(slug: String, fromGiveaway: Bool, isUnclaimed: Bool, boostPeerId: PeerId?, months: Int32) case giveawayLaunched public init(decoder: PostboxDecoder) { @@ -206,7 +206,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { case 35: self = .botAppAccessGranted(appName: decoder.decodeOptionalStringForKey("app"), type: decoder.decodeOptionalInt32ForKey("atp").flatMap { BotSendMessageAccessGrantedType(rawValue: $0) }) case 36: - self = .giftCode(slug: decoder.decodeStringForKey("slug", orElse: ""), fromGiveaway: decoder.decodeBoolForKey("give", orElse: false), boostPeerId: PeerId(decoder.decodeInt64ForKey("pi", orElse: 0)), months: decoder.decodeInt32ForKey("months", orElse: 0)) + self = .giftCode(slug: decoder.decodeStringForKey("slug", orElse: ""), fromGiveaway: decoder.decodeBoolForKey("give", orElse: false), isUnclaimed: decoder.decodeBoolForKey("unclaimed", orElse: false), boostPeerId: PeerId(decoder.decodeInt64ForKey("pi", orElse: 0)), months: decoder.decodeInt32ForKey("months", orElse: 0)) case 37: self = .giveawayLaunched default: @@ -388,10 +388,11 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { } else { encoder.encodeNil(forKey: "atp") } - case let .giftCode(slug, fromGiveaway, boostPeerId, months): + case let .giftCode(slug, fromGiveaway, unclaimed, boostPeerId, months): encoder.encodeInt32(36, forKey: "_rawValue") encoder.encodeString(slug, forKey: "slug") encoder.encodeBool(fromGiveaway, forKey: "give") + encoder.encodeBool(unclaimed, forKey: "unclaimed") if let boostPeerId = boostPeerId { encoder.encodeInt64(boostPeerId.toInt64(), forKey: "pi") } else { @@ -421,7 +422,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { return peerIds case let .requestedPeer(_, peerId): return [peerId] - case let .giftCode(_, _, boostPeerId, _): + case let .giftCode(_, _, _, boostPeerId, _): return boostPeerId.flatMap { [$0] } ?? [] default: return [] diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift index ed6f392185..717ae063e9 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift @@ -166,6 +166,7 @@ private final class StatusReactionNode: ASDisplayNode { fileId: fileId, animationCache: animationCache, animationRenderer: animationRenderer, + tintColor: nil, placeholderColor: placeholderColor, animateIdle: animateIdle, reaction: value, diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode/Sources/ChatMessageGiftBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode/Sources/ChatMessageGiftBubbleContentNode.swift index 0e266f0e6a..c1adef884e 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode/Sources/ChatMessageGiftBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode/Sources/ChatMessageGiftBubbleContentNode.swift @@ -188,7 +188,8 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode { }) } } - + + //TODO:localize override public func asyncLayoutContent() -> (_ item: ChatMessageBubbleContentItem, _ layoutConstants: ChatMessageItemLayoutConstants, _ preparePosition: ChatMessageBubblePreparePosition, _ messageSelection: Bool?, _ constrainedSize: CGSize, _ avatarInset: CGFloat) -> (ChatMessageBubbleContentProperties, unboundSize: CGSize?, maxWidth: CGFloat, layout: (CGSize, ChatMessageBubbleContentPosition) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool, ListViewItemApply?) -> Void))) { let makeLabelLayout = TextNode.asyncLayout(self.labelNode) let makeTitleLayout = TextNode.asyncLayout(self.titleNode) @@ -220,17 +221,22 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode { case let .giftPremium(_, _, monthsValue, _, _): months = monthsValue text = item.presentationData.strings.Notification_PremiumGift_Subtitle(item.presentationData.strings.Notification_PremiumGift_Months(months)).string - case let .giftCode(_, fromGiveaway, channelId, monthsValue): + case let .giftCode(_, fromGiveaway, unclaimed, channelId, monthsValue): giftSize.width += 34.0 - giftSize.height += 84.0 textSpacing += 13.0 - title = "Congratulations!" + if unclaimed { + title = "Unclaimed Prize" + } else { + title = "Congratulations!" + } var peerName = "" if let channelId, let channel = item.message.peers[channelId] { peerName = EnginePeer(channel).compactDisplayTitle } - if fromGiveaway { + if unclaimed { + text = "You have an unclaimed prize from a giveaway by **\(peerName)**.\n\nThis prize is a **Telegram Premium** subscription for **\(monthsValue)** months." + } else if fromGiveaway { text = "You won a prize in a giveaway organized by **\(peerName)**.\n\nYour prize is a **Telegram Premium** subscription for **\(monthsValue)** months." } else { text = "You've received a gift from **\(peerName)**.\n\nYour gift is a **Telegram Premium** subscription for **\(monthsValue)** months." @@ -273,6 +279,8 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode { let (buttonTitleLayout, buttonTitleApply) = makeButtonTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: buttonTitle, font: Font.semibold(15.0), textColor: primaryTextColor, paragraphAlignment: .center), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: giftSize.width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets())) + giftSize.height = titleLayout.size.height + subtitleLayout.size.height + 225.0 + var labelRects = labelLayout.linesRects() if labelRects.count > 1 { let sortedIndices = (0 ..< labelRects.count).sorted(by: { labelRects[$0].width > labelRects[$1].width }) diff --git a/submodules/TelegramUI/Components/MessageInputActionButtonComponent/Sources/MessageInputActionButtonComponent.swift b/submodules/TelegramUI/Components/MessageInputActionButtonComponent/Sources/MessageInputActionButtonComponent.swift index 4b17e2b9aa..e368bdfdc3 100644 --- a/submodules/TelegramUI/Components/MessageInputActionButtonComponent/Sources/MessageInputActionButtonComponent.swift +++ b/submodules/TelegramUI/Components/MessageInputActionButtonComponent/Sources/MessageInputActionButtonComponent.swift @@ -449,6 +449,7 @@ public final class MessageInputActionButtonComponent: Component { fileId: animationFileId ?? reactionFile?.fileId.id ?? 0, animationCache: component.context.animationCache, animationRenderer: component.context.animationRenderer, + tintColor: nil, placeholderColor: UIColor(white: 1.0, alpha: 0.2), animateIdle: false, reaction: reaction, diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorChatPreviewItem.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorChatPreviewItem.swift index a5cce6d834..8a0f46bade 100644 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorChatPreviewItem.swift +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorChatPreviewItem.swift @@ -168,9 +168,9 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode { return { item, params, neighbors in if currentBackgroundNode == nil { currentBackgroundNode = createWallpaperBackgroundNode(context: item.context, forChatDisplay: false) + currentBackgroundNode?.update(wallpaper: item.wallpaper) + currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.componentTheme, bubbleCorners: item.chatBubbleCorners) } - currentBackgroundNode?.update(wallpaper: item.wallpaper) - currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.componentTheme, bubbleCorners: item.chatBubbleCorners) let insets: UIEdgeInsets let separatorHeight = UIScreenPixel @@ -250,6 +250,11 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode { if let strongSelf = self { strongSelf.item = item + if let currentBackgroundNode { + currentBackgroundNode.update(wallpaper: item.wallpaper) + currentBackgroundNode.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners) + } + strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: contentSize) if let currentItem, currentItem.messageItems.first?.nameColor != item.messageItems.first?.nameColor || currentItem.messageItems.first?.backgroundEmojiId != item.messageItems.first?.backgroundEmojiId { diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorScreen.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorScreen.swift index 60b31d61f6..b752772d10 100644 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorScreen.swift +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorScreen.swift @@ -412,7 +412,8 @@ public func PeerNameColorScreen( updatedState.inProgress = true return updatedState } - let _ = context.engine.peers.updatePeerNameColorAndEmoji(peerId: peerId, nameColor: nameColor ?? .blue, backgroundEmojiId: backgroundEmojiId ?? 0).startStandalone(next: { + let _ = (context.engine.peers.updatePeerNameColorAndEmoji(peerId: peerId, nameColor: nameColor ?? .blue, backgroundEmojiId: backgroundEmojiId ?? 0) + |> deliverOnMainQueue).startStandalone(next: { }, error: { error in if case .channelBoostRequired = error { let _ = combineLatest( diff --git a/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift b/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift index 28e88fc1a7..c16977a4f9 100644 --- a/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift +++ b/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift @@ -25,6 +25,7 @@ import TelegramUIPreferences import UndoUI import TelegramStringFormatting +//TODO:localize final class ShareWithPeersScreenComponent: Component { typealias EnvironmentType = ViewControllerComponentContainer.Environment diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Info/NameColorIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Info/NameColorIcon.imageset/Contents.json new file mode 100644 index 0000000000..c581600204 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Info/NameColorIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "brush_30.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Info/NameColorIcon.imageset/brush_30.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Info/NameColorIcon.imageset/brush_30.pdf new file mode 100644 index 0000000000..309850c590 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Info/NameColorIcon.imageset/brush_30.pdf @@ -0,0 +1,193 @@ +%PDF-1.7 + +1 0 obj + << /Type /XObject + /Length 2 0 R + /Group << /Type /Group + /S /Transparency + >> + /Subtype /Form + /Resources << >> + /BBox [ 0.000000 0.000000 30.000000 30.000000 ] + >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm +1.000000 0.584314 0.000000 scn +0.000000 18.799999 m +0.000000 22.720367 0.000000 24.680552 0.762954 26.177933 c +1.434068 27.495068 2.504932 28.565931 3.822066 29.237045 c +5.319448 30.000000 7.279633 30.000000 11.200000 30.000000 c +18.799999 30.000000 l +22.720367 30.000000 24.680552 30.000000 26.177933 29.237045 c +27.495068 28.565931 28.565931 27.495068 29.237045 26.177933 c +30.000000 24.680552 30.000000 22.720367 30.000000 18.799999 c +30.000000 11.200001 l +30.000000 7.279633 30.000000 5.319448 29.237045 3.822067 c +28.565931 2.504932 27.495068 1.434069 26.177933 0.762955 c +24.680552 0.000000 22.720367 0.000000 18.799999 0.000000 c +11.200000 0.000000 l +7.279633 0.000000 5.319448 0.000000 3.822066 0.762955 c +2.504932 1.434069 1.434068 2.504932 0.762954 3.822067 c +0.000000 5.319448 0.000000 7.279633 0.000000 11.200001 c +0.000000 18.799999 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 5.000000 3.864258 cm +1.000000 1.000000 1.000000 scn +17.269485 19.786890 m +18.869095 20.947046 20.890579 18.978296 19.730423 17.325953 c +19.405228 16.877710 16.109329 12.034937 13.402297 8.967554 c +12.690383 11.103296 11.038039 12.755640 8.911086 13.458765 c +11.978469 16.165796 16.821243 19.461695 17.269485 19.786890 c +h +8.867141 8.932398 m +8.164016 9.635523 7.267532 10.136499 6.476516 10.285913 c +6.590774 10.927515 6.889602 11.481226 7.408157 12.034937 c +7.496047 12.131617 7.583938 12.219507 7.680618 12.307398 c +9.965775 11.982203 11.908156 10.022242 12.242141 7.737085 c +12.154250 7.640406 12.066360 7.552515 11.960891 7.464624 c +11.415969 6.954859 10.879836 6.664820 10.229445 6.541773 c +10.071242 7.332788 9.570266 8.229273 8.867141 8.932398 c +h +0.385696 3.527124 m +-0.062546 4.072046 -0.141648 4.564234 0.262649 4.889429 c +0.746047 5.267359 1.378860 4.915796 2.081985 5.460718 c +2.864211 6.058374 2.310500 7.394312 3.690383 8.466578 c +5.043899 9.565210 7.074172 9.204859 8.287063 7.666773 c +9.623000 5.961695 9.368118 3.869898 7.592727 2.481226 c +5.465774 0.811304 2.064407 1.400171 0.385696 3.527124 c +h +f +n +Q + +endstream +endobj + +2 0 obj + 2104 +endobj + +3 0 obj + << /Type /XObject + /Length 4 0 R + /Group << /Type /Group + /S /Transparency + >> + /Subtype /Form + /Resources << >> + /BBox [ 0.000000 0.000000 30.000000 30.000000 ] + >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm +0.000000 0.000000 0.000000 scn +0.000000 18.799999 m +0.000000 22.720367 0.000000 24.680552 0.762954 26.177933 c +1.434068 27.495068 2.504932 28.565931 3.822066 29.237045 c +5.319448 30.000000 7.279633 30.000000 11.200000 30.000000 c +18.799999 30.000000 l +22.720367 30.000000 24.680552 30.000000 26.177933 29.237045 c +27.495068 28.565931 28.565931 27.495068 29.237045 26.177933 c +30.000000 24.680552 30.000000 22.720367 30.000000 18.799999 c +30.000000 11.200001 l +30.000000 7.279633 30.000000 5.319448 29.237045 3.822067 c +28.565931 2.504932 27.495068 1.434069 26.177933 0.762955 c +24.680552 0.000000 22.720367 0.000000 18.799999 0.000000 c +11.200000 0.000000 l +7.279633 0.000000 5.319448 0.000000 3.822066 0.762955 c +2.504932 1.434069 1.434068 2.504932 0.762954 3.822067 c +0.000000 5.319448 0.000000 7.279633 0.000000 11.200001 c +0.000000 18.799999 l +h +f +n +Q + +endstream +endobj + +4 0 obj + 944 +endobj + +5 0 obj + << /XObject << /X1 1 0 R >> + /ExtGState << /E1 << /SMask << /Type /Mask + /G 3 0 R + /S /Alpha + >> + /Type /ExtGState + >> >> + >> +endobj + +6 0 obj + << /Length 7 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +/E1 gs +/X1 Do +Q + +endstream +endobj + +7 0 obj + 46 +endobj + +8 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 30.000000 30.000000 ] + /Resources 5 0 R + /Contents 6 0 R + /Parent 9 0 R + >> +endobj + +9 0 obj + << /Kids [ 8 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +10 0 obj + << /Pages 9 0 R + /Type /Catalog + >> +endobj + +xref +0 11 +0000000000 65535 f +0000000010 00000 n +0000002362 00000 n +0000002385 00000 n +0000003577 00000 n +0000003599 00000 n +0000003897 00000 n +0000003999 00000 n +0000004020 00000 n +0000004193 00000 n +0000004267 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 10 0 R + /Size 11 +>> +startxref +4327 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 16463ef115..e92b475d05 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -948,7 +948,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let controller = PremiumIntroScreen(context: strongSelf.context, source: .gift(from: fromPeerId, to: toPeerId, duration: duration)) strongSelf.push(controller) return true - case let .giftCode(slug, _, _, _): + case let .giftCode(slug, _, _, _, _): strongSelf.openResolved(result: .premiumGiftCode(slug: slug), sourceMessageId: message.id) return true case let .suggestedProfilePhoto(image): diff --git a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift index e173439098..e9f59997e2 100644 --- a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift +++ b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift @@ -842,101 +842,49 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur } }) case let .boost(peerId, status, myBoostStatus): - let _ = myBoostStatus + var isCurrent = false + if case let .chat(chatPeerId, _) = urlContext, chatPeerId == peerId { + isCurrent = true + } var forceDark = false if let updatedPresentationData, updatedPresentationData.initial.theme.overallDarkAppearance { forceDark = true } - let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) - |> deliverOnMainQueue).startStandalone(next: { peer in - guard let peer, let status else { - return + + var dismissedImpl: (() -> Void)? + if let storyProgressPauseContext = contentContext as? StoryProgressPauseContext { + let updateExternalController = storyProgressPauseContext.update + dismissedImpl = { + updateExternalController(nil) } - - var myBoostCount: Int32 = 0 - var availableBoosts: [MyBoostStatus.Boost] = [] - var occupiedBoosts: [MyBoostStatus.Boost] = [] - if let myBoostStatus { - for boost in myBoostStatus.boosts { - if let boostPeer = boost.peer { - if boostPeer.id == peer.id { - myBoostCount += 1 - } else { - occupiedBoosts.append(boost) - } - } else { - availableBoosts.append(boost) - } - } - } - - var isCurrent = false - if case let .chat(chatPeerId, _) = urlContext, chatPeerId == peerId { - isCurrent = true - } - - var currentLevel = Int32(status.level) - var currentLevelBoosts = Int32(status.currentLevelBoosts) - var nextLevelBoosts = status.nextLevelBoosts.flatMap(Int32.init) - - if myBoostCount > 0 && status.boosts == currentLevelBoosts { - currentLevel = max(0, currentLevel - 1) - nextLevelBoosts = currentLevelBoosts - currentLevelBoosts = max(0, currentLevelBoosts - 1) - } - - let subject: PremiumLimitScreen.Subject = .storiesChannelBoost(peer: peer, isCurrent: isCurrent, level: currentLevel, currentLevelBoosts: currentLevelBoosts, nextLevelBoosts: nextLevelBoosts, link: nil, myBoostCount: myBoostCount) - let nextSubject: PremiumLimitScreen.Subject = .storiesChannelBoost(peer: peer, isCurrent: isCurrent, level: currentLevel, currentLevelBoosts: currentLevelBoosts, nextLevelBoosts: nextLevelBoosts, link: nil, myBoostCount: myBoostCount + 1) - var nextCount = Int32(status.boosts + 1) - - var updateImpl: (() -> Void)? - var dismissImpl: (() -> Void)? - let controller = PremiumLimitScreen(context: context, subject: subject, count: Int32(status.boosts), forceDark: forceDark, action: { - let dismiss = false - updateImpl?() - return dismiss - }, + } + + PremiumBoostScreen( + context: context, + contentContext: contentContext, + peerId: peerId, + isCurrent: isCurrent, + status: status, + myBoostStatus: myBoostStatus, + forceDark: forceDark, openPeer: { peer in openPeer(peer, .chat(textInputState: nil, subject: nil, peekData: nil)) - }) - navigationController?.pushViewController(controller) - - if let storyProgressPauseContext = contentContext as? StoryProgressPauseContext { - storyProgressPauseContext.update(controller) + }, + presentController: { [weak navigationController] c in + (navigationController?.viewControllers.last as? ViewController)?.present(c, in: .window(.root)) + }, + pushController: { [weak navigationController] c in + navigationController?.pushViewController(c) - let updateExternalController = storyProgressPauseContext.update - controller.disposed = { - updateExternalController(nil) - } - } - - updateImpl = { [weak controller] in - if let _ = status.nextLevelBoosts { - if let availableBoost = availableBoosts.first { - let _ = context.engine.peers.applyChannelBoost(peerId: peerId, slots: [availableBoost.slot]).startStandalone() - controller?.updateSubject(nextSubject, count: nextCount) - - availableBoosts.removeFirst() - nextCount += 1 - } else if !occupiedBoosts.isEmpty, let myBoostStatus { - let replaceController = ReplaceBoostScreen(context: context, peerId: peerId, myBoostStatus: myBoostStatus, replaceBoosts: { slots in - let _ = context.engine.peers.applyChannelBoost(peerId: peerId, slots: slots).startStandalone() - - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let undoController = UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: "\(slots.count) boosts are reassigned from 1 other channel.", timeout: nil, customUndoText: nil), elevatedLayout: true, position: .bottom, action: { _ in return true }) - (navigationController?.viewControllers.last as? ViewController)?.present(undoController, in: .window(.root)) - }) - dismissImpl?() - navigationController?.pushViewController(replaceController) + if c is PremiumLimitScreen { + if let storyProgressPauseContext = contentContext as? StoryProgressPauseContext { + storyProgressPauseContext.update(c) } - } else { - dismissImpl?() } + }, dismissed: { + dismissedImpl?() } - dismissImpl = { [weak controller] in - controller?.dismissAnimated() - } - }) + ) case let .premiumGiftCode(slug): var forceDark = false if let updatedPresentationData, updatedPresentationData.initial.theme.overallDarkAppearance { diff --git a/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenDisclosureItem.swift b/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenDisclosureItem.swift index 09e9ab45e4..760f6f41d1 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenDisclosureItem.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenDisclosureItem.swift @@ -156,7 +156,11 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode { self.textNode.attributedText = NSAttributedString(string: item.text, font: titleFont, textColor: textColorValue) let textSize = self.textNode.updateLayout(CGSize(width: width - (leftInset + rightInset), height: .greatestFiniteMagnitude)) - let labelSize = self.labelNode.updateLayout(CGSize(width: width - textSize.width - (leftInset + rightInset), height: .greatestFiniteMagnitude)) + var labelConstrainWidth = width - textSize.width - (leftInset + rightInset) + if case .semitransparentBadge = item.label { + labelConstrainWidth -= 16.0 + } + let labelSize = self.labelNode.updateLayout(CGSize(width: labelConstrainWidth, height: .greatestFiniteMagnitude)) let textFrame = CGRect(origin: CGPoint(x: leftInset, y: 12.0), size: textSize) diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index d9c9c6e5c5..8c581220f8 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -1568,12 +1568,6 @@ private func editingItems(data: PeerInfoScreenData?, state: PeerInfoState, chatL })) } - if isCreator || (channel.adminRights?.rights.contains(.canChangeInfo) == true) { - items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemNameColor, label: .semitransparentBadge(EnginePeer(channel).compactDisplayTitle, (data.peer?.nameColor ?? .blue).color), text: "Channel Color", icon: UIImage(bundleImageName: "Settings/Menu/Appearance"), action: { - interaction.editingOpenNameColorSetup() - })) - } - if (isCreator && (channel.addressName?.isEmpty ?? true)) || (!channel.flags.contains(.isCreator) && channel.adminRights?.rights.contains(.canInviteUsers) == true) { let invitesText: String if let count = data.invitations?.count, count > 0 { @@ -1626,6 +1620,12 @@ private func editingItems(data: PeerInfoScreenData?, state: PeerInfoState, chatL })) } + if isCreator || (channel.adminRights?.rights.contains(.canChangeInfo) == true) { + items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemNameColor, label: .semitransparentBadge(EnginePeer(channel).compactDisplayTitle, (data.peer?.nameColor ?? .blue).color), text: "Channel Color", icon: UIImage(bundleImageName: "Chat/Info/NameColorIcon"), action: { + interaction.editingOpenNameColorSetup() + })) + } + if isCreator || (channel.adminRights != nil && channel.hasPermission(.sendSomething)) { let messagesShouldHaveSignatures: Bool switch channel.info {