diff --git a/submodules/BrowserUI/Sources/BrowserBookmarksScreen.swift b/submodules/BrowserUI/Sources/BrowserBookmarksScreen.swift index b77e831e36..cef645b788 100644 --- a/submodules/BrowserUI/Sources/BrowserBookmarksScreen.swift +++ b/submodules/BrowserUI/Sources/BrowserBookmarksScreen.swift @@ -166,6 +166,7 @@ public final class BrowserBookmarksScreen: ViewController { }, scrollToMessageId: { _ in }, navigateToStory: { _, _ in }, attemptedNavigationToPrivateQuote: { _ in + }, forceUpdateWarpContents: { }, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(), presentationContext: ChatPresentationContext(context: context, backgroundNode: nil)) diff --git a/submodules/Components/ReactionButtonListComponent/Sources/ReactionButtonListComponent.swift b/submodules/Components/ReactionButtonListComponent/Sources/ReactionButtonListComponent.swift index 61746938e2..3efc953198 100644 --- a/submodules/Components/ReactionButtonListComponent/Sources/ReactionButtonListComponent.swift +++ b/submodules/Components/ReactionButtonListComponent/Sources/ReactionButtonListComponent.swift @@ -176,7 +176,7 @@ public final class ReactionIconView: PortalSourceView { animationLayer.masksToBounds = true animationLayer.cornerRadius = floor(size.width * 0.2) case .stars: - iconSize = CGSize(width: floor(size.width * 2.0), height: floor(size.height * 2.0)) + iconSize = CGSize(width: floor(size.width * 1.25), height: floor(size.height * 1.25)) animationLayer.masksToBounds = false animationLayer.cornerRadius = 0.0 } @@ -212,7 +212,7 @@ public final class ReactionIconView: PortalSourceView { case .custom: iconSize = CGSize(width: floor(size.width * 1.25), height: floor(size.height * 1.25)) case .stars: - iconSize = CGSize(width: floor(size.width * 2.0), height: floor(size.height * 2.0)) + iconSize = CGSize(width: floor(size.width * 1.25), height: floor(size.height * 1.25)) } let animationLayer = InlineStickerItemLayer( diff --git a/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift b/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift index 7c147f45af..f58c2993ad 100644 --- a/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift +++ b/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift @@ -1535,8 +1535,6 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo additive: true, completion: { [weak self] _ in Queue.mainQueue().after(reactionContextNodeIsAnimatingOut ? 0.2 * UIView.animationDurationFactor() : 0.0, { - contentNode.containingItem.isExtractedToContextPreview = false - contentNode.containingItem.isExtractedToContextPreviewUpdated?(false) if let strongSelf = self, let contentNode = strongSelf.itemContentNode { switch contentNode.containingItem { @@ -1547,6 +1545,9 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo } } + contentNode.containingItem.isExtractedToContextPreview = false + contentNode.containingItem.isExtractedToContextPreviewUpdated?(false) + completion() }) } diff --git a/submodules/Display/Source/ContainedViewLayoutTransition.swift b/submodules/Display/Source/ContainedViewLayoutTransition.swift index eea065a5f1..4ec3efdd87 100644 --- a/submodules/Display/Source/ContainedViewLayoutTransition.swift +++ b/submodules/Display/Source/ContainedViewLayoutTransition.swift @@ -538,6 +538,17 @@ public extension ContainedViewLayoutTransition { } } + func animateScaleWithKeyframes(layer: CALayer, keyframes: [CGFloat], removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) { + switch self { + case .immediate: + completion?(true) + case let .animated(duration, curve): + layer.animateKeyframes(values: keyframes.map { NSNumber(value: Float($0)) }, duration: duration, keyPath: "transform.scale", timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: removeOnCompletion, completion: { value in + completion?(value) + }) + } + } + func animateFrame(node: ASDisplayNode, from frame: CGRect, to toFrame: CGRect? = nil, removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) { switch self { case .immediate: diff --git a/submodules/PeerInfoUI/Sources/ChannelAdminsController.swift b/submodules/PeerInfoUI/Sources/ChannelAdminsController.swift index 651f17e7fa..429784f087 100644 --- a/submodules/PeerInfoUI/Sources/ChannelAdminsController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelAdminsController.swift @@ -25,8 +25,9 @@ private final class ChannelAdminsControllerArguments { let openAdmin: (ChannelParticipant) -> Void let updateAntiSpamEnabled: (Bool) -> Void let updateSignMessagesEnabled: (Bool) -> Void + let updateShowAuthorProfilesEnabled: (Bool) -> Void - init(context: AccountContext, openRecentActions: @escaping () -> Void, setPeerIdWithRevealedOptions: @escaping (EnginePeer.Id?, EnginePeer.Id?) -> Void, removeAdmin: @escaping (EnginePeer.Id) -> Void, addAdmin: @escaping () -> Void, openAdmin: @escaping (ChannelParticipant) -> Void, updateAntiSpamEnabled: @escaping (Bool) -> Void, updateSignMessagesEnabled: @escaping (Bool) -> Void) { + init(context: AccountContext, openRecentActions: @escaping () -> Void, setPeerIdWithRevealedOptions: @escaping (EnginePeer.Id?, EnginePeer.Id?) -> Void, removeAdmin: @escaping (EnginePeer.Id) -> Void, addAdmin: @escaping () -> Void, openAdmin: @escaping (ChannelParticipant) -> Void, updateAntiSpamEnabled: @escaping (Bool) -> Void, updateSignMessagesEnabled: @escaping (Bool) -> Void, updateShowAuthorProfilesEnabled: @escaping (Bool) -> Void) { self.context = context self.openRecentActions = openRecentActions self.setPeerIdWithRevealedOptions = setPeerIdWithRevealedOptions @@ -35,6 +36,7 @@ private final class ChannelAdminsControllerArguments { self.openAdmin = openAdmin self.updateAntiSpamEnabled = updateAntiSpamEnabled self.updateSignMessagesEnabled = updateSignMessagesEnabled + self.updateShowAuthorProfilesEnabled = updateShowAuthorProfilesEnabled } } @@ -60,6 +62,7 @@ private enum ChannelAdminsEntry: ItemListNodeEntry { case adminsInfo(PresentationTheme, String) case signMessages(PresentationTheme, String, Bool) + case showAuthorProfiles(PresentationTheme, String, Bool) case signMessagesInfo(PresentationTheme, String) var section: ItemListSectionId { @@ -68,7 +71,7 @@ private enum ChannelAdminsEntry: ItemListNodeEntry { return ChannelAdminsSection.administration.rawValue case .adminsHeader, .adminPeerItem, .addAdmin, .adminsInfo: return ChannelAdminsSection.admins.rawValue - case .signMessages, .signMessagesInfo: + case .signMessages, .showAuthorProfiles, .signMessagesInfo: return ChannelAdminsSection.signMessages.rawValue } } @@ -91,8 +94,10 @@ private enum ChannelAdminsEntry: ItemListNodeEntry { return .peer(participant.peer.id) case .signMessages: return .index(6) - case .signMessagesInfo: + case .showAuthorProfiles: return .index(7) + case .signMessagesInfo: + return .index(9) } } @@ -176,6 +181,12 @@ private enum ChannelAdminsEntry: ItemListNodeEntry { } else { return false } + case let .showAuthorProfiles(lhsTheme, lhsText, lhsValue): + if case let .showAuthorProfiles(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue { + return true + } else { + return false + } case let .signMessagesInfo(lhsTheme, lhsText): if case let .signMessagesInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { return true @@ -240,6 +251,13 @@ private enum ChannelAdminsEntry: ItemListNodeEntry { default: return true } + case .showAuthorProfiles: + switch rhs { + case .recentActions, .antiSpam, .antiSpamInfo, .adminsHeader, .addAdmin, .adminPeerItem, .adminsInfo, .signMessages, .showAuthorProfiles: + return false + default: + return true + } case .signMessagesInfo: return false } @@ -301,6 +319,10 @@ private enum ChannelAdminsEntry: ItemListNodeEntry { return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in arguments.updateSignMessagesEnabled(value) }) + case let .showAuthorProfiles(_, text, value): + return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.updateShowAuthorProfilesEnabled(value) + }) case let .signMessagesInfo(_, text): return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section) } @@ -381,7 +403,7 @@ private struct ChannelAdminsControllerState: Equatable { } } -private func channelAdminsControllerEntries(presentationData: PresentationData, accountPeerId: EnginePeer.Id, peer: EnginePeer?, state: ChannelAdminsControllerState, participants: [RenderedChannelParticipant]?, antiSpamAvailable: Bool, antiSpamEnabled: Bool, signMessagesEnabled: Bool) -> [ChannelAdminsEntry] { +private func channelAdminsControllerEntries(presentationData: PresentationData, accountPeerId: EnginePeer.Id, peer: EnginePeer?, state: ChannelAdminsControllerState, participants: [RenderedChannelParticipant]?, antiSpamAvailable: Bool, antiSpamEnabled: Bool, signMessagesEnabled: Bool, showAuthorProfilesEnabled: Bool) -> [ChannelAdminsEntry] { if participants == nil || participants?.count == nil { return [] } @@ -476,7 +498,9 @@ private func channelAdminsControllerEntries(presentationData: PresentationData, if !isGroup && peer.hasPermission(.sendSomething) { entries.append(.signMessages(presentationData.theme, presentationData.strings.Channel_SignMessages, signMessagesEnabled)) - entries.append(.signMessagesInfo(presentationData.theme, presentationData.strings.Channel_SignMessages_Help)) + //TODO:localize + entries.append(.showAuthorProfiles(presentationData.theme, "Show Authors' Profiles", showAuthorProfilesEnabled)) + entries.append(.signMessagesInfo(presentationData.theme, "Add names and photos of admins to the messages they post, linking to their profiles.")) } } } else if case let .legacyGroup(peer) = peer { @@ -588,6 +612,9 @@ public func channelAdminsController(context: AccountContext, updatedPresentation let updateSignMessagesDisposable = MetaDisposable() actionsDisposable.add(updateSignMessagesDisposable) + let updateShowAuthorProfilesDisposable = MetaDisposable() + actionsDisposable.add(updateShowAuthorProfilesDisposable) + let adminsPromise = Promise<[RenderedChannelParticipant]?>(nil) let antiSpamConfiguration = AntiSpamBotConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) @@ -790,6 +817,12 @@ public func channelAdminsController(context: AccountContext, updatedPresentation |> deliverOnMainQueue).start(next: { peerId in updateSignMessagesDisposable.set(context.engine.peers.toggleShouldChannelMessagesSignatures(peerId: peerId, enabled: value).start()) }) + }, updateShowAuthorProfilesEnabled: { value in + let _ = (currentPeerId.get() + |> take(1) + |> deliverOnMainQueue).start(next: { peerId in + updateSignMessagesDisposable.set(context.engine.peers.toggleShouldChannelMessagesSignatures(peerId: peerId, enabled: value).start()) + }) }) let membersAndLoadMoreControlValue = Atomic<(Disposable, PeerChannelMemberCategoryControl?)?>(value: nil) @@ -911,8 +944,10 @@ public func channelAdminsController(context: AccountContext, updatedPresentation } var signMessagesEnabled = false + var showAuthorProfilesEnabled = false if case let .channel(channel) = view.peer, case let .broadcast(info) = channel.info { signMessagesEnabled = info.flags.contains(.messagesShouldHaveSignatures) + showAuthorProfilesEnabled = info.flags.contains(.messagesShouldHaveSignatures) } var rightNavigationButton: ItemListNavigationButton? @@ -986,7 +1021,7 @@ public func channelAdminsController(context: AccountContext, updatedPresentation } let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(isGroup ? presentationData.strings.ChatAdmins_Title : presentationData.strings.Channel_Management_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, secondaryRightNavigationButton: secondaryRightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true) - let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: channelAdminsControllerEntries(presentationData: presentationData, accountPeerId: context.account.peerId, peer: view.peer, state: state, participants: admins, antiSpamAvailable: antiSpamAvailable, antiSpamEnabled: antiSpamEnabled, signMessagesEnabled: signMessagesEnabled), style: .blocks, emptyStateItem: emptyStateItem, searchItem: searchItem, animateChanges: previous != nil && admins != nil && previous!.count >= admins!.count) + let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: channelAdminsControllerEntries(presentationData: presentationData, accountPeerId: context.account.peerId, peer: view.peer, state: state, participants: admins, antiSpamAvailable: antiSpamAvailable, antiSpamEnabled: antiSpamEnabled, signMessagesEnabled: signMessagesEnabled, showAuthorProfilesEnabled: showAuthorProfilesEnabled), style: .blocks, emptyStateItem: emptyStateItem, searchItem: searchItem, animateChanges: previous != nil && admins != nil && previous!.count >= admins!.count) return (controllerState, (listState, arguments)) } |> afterDisposed { diff --git a/submodules/ReactionSelectionNode/BUILD b/submodules/ReactionSelectionNode/BUILD index b94955dc38..fd7a395c21 100644 --- a/submodules/ReactionSelectionNode/BUILD +++ b/submodules/ReactionSelectionNode/BUILD @@ -38,6 +38,7 @@ swift_library( "//submodules/TelegramUI/Components/Utils/GenerateStickerPlaceholderImage", "//submodules/Components/BalancedTextComponent", "//submodules/Markdown", + "//submodules/TelegramUI/Components/Premium/PremiumStarComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift index 73675e12e4..17a69a4228 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift @@ -24,6 +24,7 @@ import TextFormat import GZip import BalancedTextComponent import Markdown +import PremiumStarComponent public final class ReactionItem { public struct Reaction: Equatable { @@ -2672,7 +2673,7 @@ public final class ReactionContextNode: ASDisplayNode, ASScrollViewDelegate { if case .builtin = itemNode.item.reaction.rawValue { selfTargetBounds = selfTargetBounds.insetBy(dx: -selfTargetBounds.width * 0.5, dy: -selfTargetBounds.height * 0.5) } else if case .stars = itemNode.item.reaction.rawValue { - selfTargetBounds = selfTargetBounds.insetBy(dx: -selfTargetBounds.width * 0.5, dy: -selfTargetBounds.height * 0.5) + selfTargetBounds = selfTargetBounds.insetBy(dx: selfTargetBounds.width * 0.0, dy: selfTargetBounds.height * 0.0) } let selfTargetRect = self.view.convert(selfTargetBounds, from: targetView) @@ -2863,7 +2864,6 @@ public final class ReactionContextNode: ASDisplayNode, ASScrollViewDelegate { strongSelf.hapticFeedback = HapticFeedback() } strongSelf.hapticFeedback?.tap() - onHit?() if let targetView = targetView as? ReactionIconView { if switchToInlineImmediately { @@ -2886,6 +2886,8 @@ public final class ReactionContextNode: ASDisplayNode, ASScrollViewDelegate { } } + onHit?() + if switchToInlineImmediately { afterCompletion() } else { @@ -3723,7 +3725,7 @@ public final class StandaloneReactionAnimation: ASDisplayNode { if let itemNode = self.itemNode, case .builtin = itemNode.item.reaction.rawValue { selfTargetBounds = selfTargetBounds.insetBy(dx: -selfTargetBounds.width * 0.5, dy: -selfTargetBounds.height * 0.5) } else if let itemNode = self.itemNode, case .stars = itemNode.item.reaction.rawValue { - selfTargetBounds = selfTargetBounds.insetBy(dx: -selfTargetBounds.width * 0.5, dy: -selfTargetBounds.height * 0.5) + selfTargetBounds = selfTargetBounds.insetBy(dx: -selfTargetBounds.width * 0.0, dy: -selfTargetBounds.height * 0.0) } var targetFrame = self.view.convert(targetView.convert(selfTargetBounds, to: nil), from: nil) @@ -3731,7 +3733,7 @@ public final class StandaloneReactionAnimation: ASDisplayNode { if let itemNode = self.itemNode, case .builtin = itemNode.item.reaction.rawValue { targetFrame = targetFrame.insetBy(dx: -targetFrame.width * 0.5, dy: -targetFrame.height * 0.5) } else if let itemNode = self.itemNode, case .stars = itemNode.item.reaction.rawValue { - targetFrame = targetFrame.insetBy(dx: -targetFrame.width * 0.5, dy: -targetFrame.height * 0.5) + targetFrame = targetFrame.insetBy(dx: -targetFrame.width * 0.0, dy: -targetFrame.height * 0.0) } targetSnapshotView.frame = targetFrame @@ -3778,30 +3780,43 @@ public final class StandaloneReactionAnimation: ASDisplayNode { } public func animateOutToReaction(context: AccountContext, theme: PresentationTheme, item: ReactionItem, value: MessageReaction.Reaction, sourceView: UIView, targetView: UIView, hideNode: Bool, forceSwitchToInlineImmediately: Bool = false, animateTargetContainer: UIView?, addStandaloneReactionAnimation: ((StandaloneReactionAnimation) -> Void)?, onHit: (() -> Void)?, completion: @escaping () -> Void) { - let didTriggerExpandedReaction = !"".isEmpty - - let itemNode = ReactionNode(context: context, theme: theme, item: item, icon: .none, animationCache: context.animationCache, animationRenderer: context.animationRenderer, loopIdle: false, isLocked: false, useDirectRendering: true) - if let contents = sourceView.layer.contents { - itemNode.setCustomContents(contents: contents) + let star = ComponentView() + let starSize = star.update( + transition: .immediate, + component: AnyComponent(StandalonePremiumStarComponent( + theme: theme, + colors: [ + UIColor(rgb: 0xe57d02), + UIColor(rgb: 0xf09903), + UIColor(rgb: 0xf9b004), + UIColor(rgb: 0xfdd219) + ] + )), + environment: {}, + containerSize: CGSize(width: 200.0, height: 200.0) + ) + guard let starView = star.view else { + return } - self.addSubnode(itemNode) - itemNode.frame = sourceView.convert(sourceView.bounds, to: self.view) - itemNode.updateLayout(size: itemNode.frame.size, isExpanded: false, largeExpanded: false, isPreviewing: false, transition: .immediate) + + guard let sourceCloneView = sourceView.snapshotContentTree() else { + return + } + + self.view.addSubview(sourceCloneView) + self.view.addSubview(starView) + + sourceCloneView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.08, removeOnCompletion: false) + starView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.12) + + let didTriggerExpandedReaction = "".isEmpty + + let sourceFrame = sourceView.convert(sourceView.bounds, to: self.view) + starView.bounds = CGRect(origin: CGPoint(), size: starSize) + sourceView.layer.isHidden = true - let switchToInlineImmediately: Bool - if itemNode.item.listAnimation.isVideoEmoji || itemNode.item.listAnimation.isVideoSticker || itemNode.item.listAnimation.isAnimatedSticker || itemNode.item.listAnimation.isStaticEmoji { - switch itemNode.item.reaction.rawValue { - case .builtin: - switchToInlineImmediately = forceSwitchToInlineImmediately - case .custom: - switchToInlineImmediately = !didTriggerExpandedReaction - case .stars: - switchToInlineImmediately = forceSwitchToInlineImmediately - } - } else { - switchToInlineImmediately = !didTriggerExpandedReaction - } + let switchToInlineImmediately: Bool = "".isEmpty if hideNode { if let animateTargetContainer = animateTargetContainer { @@ -3812,26 +3827,20 @@ public final class StandaloneReactionAnimation: ASDisplayNode { targetView.layer.animateAlpha(from: targetView.alpha, to: 0.0, duration: 0.2) } } + if let targetView = targetView as? ReactionIconView { + targetView.updateIsAnimationHidden(isAnimationHidden: true, transition: .immediate) + } - itemNode.isExtracted = true - let selfSourceRect = itemNode.view.convert(itemNode.view.bounds, to: self.view) + let selfSourceRect = sourceFrame var selfTargetBounds = targetView.bounds - if case .builtin = itemNode.item.reaction.rawValue { - selfTargetBounds = selfTargetBounds.insetBy(dx: -selfTargetBounds.width * 0.5, dy: -selfTargetBounds.height * 0.5) - } else if case .stars = itemNode.item.reaction.rawValue { - selfTargetBounds = selfTargetBounds.insetBy(dx: -selfTargetBounds.width * 0.5, dy: -selfTargetBounds.height * 0.5) - } + selfTargetBounds = selfTargetBounds.insetBy(dx: -selfTargetBounds.width * 0.5, dy: -selfTargetBounds.height * 0.5) let selfTargetRect = self.view.convert(selfTargetBounds, from: targetView) var expandedSize: CGSize = selfTargetRect.size if didTriggerExpandedReaction { - if itemNode.item.listAnimation.isVideoEmoji || itemNode.item.listAnimation.isVideoSticker || itemNode.item.listAnimation.isStaticEmoji { - expandedSize = CGSize(width: 80.0, height: 80.0) - } else { - expandedSize = CGSize(width: 120.0, height: 120.0) - } + expandedSize = CGSize(width: 120.0, height: 120.0) } let expandedFrame = CGRect(origin: CGPoint(x: selfTargetRect.midX - expandedSize.width / 2.0, y: selfTargetRect.midY - expandedSize.height / 2.0), size: expandedSize) @@ -3840,29 +3849,24 @@ public final class StandaloneReactionAnimation: ASDisplayNode { let incomingMessage: Bool = expandedFrame.midX < self.bounds.width / 2.0 if didTriggerExpandedReaction { let expandFactor: CGFloat = 0.5 - effectFrame = expandedFrame.insetBy(dx: -expandedFrame.width * expandFactor, dy: -expandedFrame.height * expandFactor).offsetBy(dx: incomingMessage ? (expandedFrame.width - 50.0) : (-expandedFrame.width + 50.0), dy: 0.0) + effectFrame = expandedFrame.insetBy(dx: -expandedFrame.width * expandFactor, dy: -expandedFrame.height * expandFactor)//.offsetBy(dx: incomingMessage ? (expandedFrame.width - 50.0) : (-expandedFrame.width + 50.0), dy: 0.0) } else { effectFrame = expandedFrame.insetBy(dx: -expandedSize.width, dy: -expandedSize.height) - if itemNode.item.isCustom { - effectFrame = effectFrame.insetBy(dx: -expandedSize.width, dy: -expandedSize.height) - } } - let transition: ContainedViewLayoutTransition = .animated(duration: 0.2, curve: .linear) + let transition: ContainedViewLayoutTransition = .animated(duration: 0.4, curve: .linear) - self.addSubnode(itemNode) - itemNode.position = expandedFrame.center - transition.updateBounds(node: itemNode, bounds: CGRect(origin: CGPoint(), size: expandedFrame.size)) - itemNode.updateLayout(size: expandedFrame.size, isExpanded: true, largeExpanded: didTriggerExpandedReaction, isPreviewing: false, transition: transition) + starView.center = expandedFrame.center + sourceCloneView.frame = sourceFrame let additionalAnimationNode: DefaultAnimatedStickerNodeImpl? var genericAnimationView: AnimationView? var additionalAnimation: TelegramMediaFile? if didTriggerExpandedReaction { - additionalAnimation = itemNode.item.largeApplicationAnimation + additionalAnimation = item.largeApplicationAnimation } else { - additionalAnimation = itemNode.item.applicationAnimation + additionalAnimation = item.applicationAnimation } if let additionalAnimation = additionalAnimation { @@ -3874,11 +3878,11 @@ public final class StandaloneReactionAnimation: ASDisplayNode { } } - additionalAnimationNodeValue.setup(source: AnimatedStickerResourceSource(account: itemNode.context.account, resource: additionalAnimation.resource), width: Int(effectFrame.width * 2.0), height: Int(effectFrame.height * 2.0), playbackMode: .once, mode: .direct(cachePathPrefix: context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(additionalAnimation.resource.id))) + additionalAnimationNodeValue.setup(source: AnimatedStickerResourceSource(account: context.account, resource: additionalAnimation.resource), width: Int(effectFrame.width * 2.0), height: Int(effectFrame.height * 2.0), playbackMode: .once, mode: .direct(cachePathPrefix: context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(additionalAnimation.resource.id))) additionalAnimationNodeValue.frame = effectFrame additionalAnimationNodeValue.updateLayout(size: effectFrame.size) self.addSubnode(additionalAnimationNodeValue) - } else if itemNode.item.isCustom { + } else if item.isCustom { additionalAnimationNode = nil var effectData: Data? @@ -3906,36 +3910,6 @@ public final class StandaloneReactionAnimation: ASDisplayNode { genericAnimationView = view - let animationCache = itemNode.context.animationCache - let animationRenderer = itemNode.context.animationRenderer - - for i in 1 ... 32 { - let allLayers = view.allLayers(forKeypath: AnimationKeypath(keypath: "placeholder_\(i)")) - for animationLayer in allLayers { - let baseItemLayer = InlineStickerItemLayer( - context: itemNode.context, - userLocation: .other, - attemptSynchronousLoad: false, - emoji: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: itemNode.item.listAnimation.fileId.id, file: itemNode.item.listAnimation), - file: itemNode.item.listAnimation, - cache: animationCache, - renderer: animationRenderer, - placeholderColor: UIColor(white: 0.0, alpha: 0.0), - pointSize: CGSize(width: didTriggerExpandedReaction ? 64.0 : 32.0, height: didTriggerExpandedReaction ? 64.0 : 32.0) - ) - - if let sublayers = animationLayer.sublayers { - for sublayer in sublayers { - sublayer.isHidden = true - } - } - - baseItemLayer.isVisibleForAnimations = true - baseItemLayer.frame = CGRect(origin: CGPoint(x: -0.0, y: -0.0), size: CGSize(width: 500.0, height: 500.0)) - animationLayer.addSublayer(baseItemLayer) - } - } - if didTriggerExpandedReaction { view.frame = effectFrame.insetBy(dx: -10.0, dy: -10.0).offsetBy(dx: incomingMessage ? 22.0 : -22.0, dy: 0.0) } else { @@ -3969,12 +3943,19 @@ public final class StandaloneReactionAnimation: ASDisplayNode { additionalAnimationCompleted = true } - transition.animatePositionWithKeyframes(node: itemNode, keyframes: generateParabollicMotionKeyframes(from: selfSourceRect.center, to: expandedFrame.center, elevation: 30.0), completion: { [weak itemNode, weak targetView, weak animateTargetContainer] _ in + starView.center = selfTargetRect.center + sourceCloneView.center = selfTargetRect.center + + let starSourceScale = sourceFrame.width / starSize.width + let starDestinationScale = selfTargetRect.width / starSize.width + + let keyframes = generateParabollicMotionKeyframes(from: selfSourceRect.center, to: expandedFrame.center, elevation: 40.0) + let scaleKeyframes = generateScaleKeyframes(from: starSourceScale, center: 1.0, to: starDestinationScale) + starView.layer.transform = CATransform3DMakeScale(starDestinationScale, starDestinationScale, 1.0) + transition.animateScaleWithKeyframes(layer: starView.layer, keyframes: scaleKeyframes) + transition.animatePositionWithKeyframes(layer: starView.layer, keyframes: keyframes, completion: { [weak starView, weak targetView, weak animateTargetContainer] _ in let afterCompletion: () -> Void = { - if didTriggerExpandedReaction { - return - } - guard let itemNode = itemNode else { + guard let starView else { return } if let animateTargetContainer = animateTargetContainer { @@ -3988,19 +3969,18 @@ public final class StandaloneReactionAnimation: ASDisplayNode { } HapticFeedback().tap() - onHit?() if let targetView = targetView as? ReactionIconView { if switchToInlineImmediately { targetView.updateIsAnimationHidden(isAnimationHidden: false, transition: .immediate) - itemNode.isHidden = true + starView.isHidden = true } else { targetView.updateIsAnimationHidden(isAnimationHidden: true, transition: .immediate) - targetView.addSubnode(itemNode) - itemNode.frame = selfTargetBounds + //TODO:release + //targetView.addSubnode(itemNode) } } else if let targetView = targetView as? UIImageView { - itemNode.isHidden = true + starView.isHidden = true targetView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.12) targetView.layer.animateScale(from: 0.2, to: 1.0, duration: 0.12) } @@ -4011,14 +3991,17 @@ public final class StandaloneReactionAnimation: ASDisplayNode { } } + onHit?() + if switchToInlineImmediately { afterCompletion() } else { DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1, execute: afterCompletion) } }) + transition.animatePositionWithKeyframes(layer: sourceCloneView.layer, keyframes: keyframes) - DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.15 * UIView.animationDurationFactor(), execute: { + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.3 * UIView.animationDurationFactor(), execute: { additionalAnimationNode?.visibility = true if let animateTargetContainer = animateTargetContainer { animateTargetContainer.isHidden = false @@ -4029,52 +4012,15 @@ public final class StandaloneReactionAnimation: ASDisplayNode { if !switchToInlineImmediately { DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + min(5.0, 2.0 * UIView.animationDurationFactor()), execute: { - if didTriggerExpandedReaction { - self.animateFromItemNodeToReaction(itemNode: itemNode, targetView: targetView, hideNode: hideNode, completion: { [weak self] in - if let strongSelf = self, didTriggerExpandedReaction, let addStandaloneReactionAnimation = addStandaloneReactionAnimation { - let standaloneReactionAnimation = StandaloneReactionAnimation(genericReactionEffect: strongSelf.genericReactionEffect) - - addStandaloneReactionAnimation(standaloneReactionAnimation) - - standaloneReactionAnimation.animateReactionSelection( - context: context, - theme: theme, - animationCache: context.animationCache, - reaction: itemNode.item, - avatarPeers: [], - playHaptic: false, - isLarge: false, - targetView: targetView, - addStandaloneReactionAnimation: nil, - completion: { [weak standaloneReactionAnimation] in - if let _ = standaloneReactionAnimation?.supernode { - standaloneReactionAnimation?.removeFromSupernode() - } else { - standaloneReactionAnimation?.view.removeFromSuperview() - } - } - ) - } - - mainAnimationCompleted = true - intermediateCompletion() - }) - } else { - if hideNode { - targetView.alpha = 1.0 - targetView.isHidden = false - if let targetView = targetView as? ReactionIconView { - targetView.updateIsAnimationHidden(isAnimationHidden: false, transition: .immediate) - if let _ = itemNode.supernode { - itemNode.removeFromSupernode() - } else { - itemNode.view.removeFromSuperview() - } - } + if hideNode { + targetView.alpha = 1.0 + targetView.isHidden = false + if let targetView = targetView as? ReactionIconView { + targetView.updateIsAnimationHidden(isAnimationHidden: false, transition: .immediate) } - mainAnimationCompleted = true - intermediateCompletion() } + mainAnimationCompleted = true + intermediateCompletion() }) } } @@ -4182,3 +4128,24 @@ private func generateParabollicMotionKeyframes(from sourcePoint: CGPoint, to tar return keyframes } + +private func generateScaleKeyframes(from: CGFloat, center: CGFloat, to: CGFloat) -> [CGFloat] { + var keyframes: [CGFloat] = [] + for i in 0 ..< 10 { + var k = CGFloat(i) / CGFloat(10 - 1) + let valueFrom: CGFloat + let valueTo: CGFloat + if k <= 0.5 { + k /= 0.5 + valueFrom = from + valueTo = center + } else { + k = (k - 0.5) / 0.5 + valueFrom = center + valueTo = to + } + let value = valueFrom * (1.0 - k) + valueTo * k + keyframes.append(value) + } + return keyframes +} diff --git a/submodules/TelegramCore/Sources/State/AvailableReactions.swift b/submodules/TelegramCore/Sources/State/AvailableReactions.swift index 0764d636f1..1fe767cb95 100644 --- a/submodules/TelegramCore/Sources/State/AvailableReactions.swift +++ b/submodules/TelegramCore/Sources/State/AvailableReactions.swift @@ -3,6 +3,45 @@ import TelegramApi import Postbox import SwiftSignalKit +private func generateStarsReactionFile(kind: Int, isAnimatedSticker: Bool) -> TelegramMediaFile { + let baseId: Int64 = 52343278047832950 + let fileId = baseId + Int64(kind) + + var attributes: [TelegramMediaFileAttribute] = [] + attributes.append(TelegramMediaFileAttribute.FileName(fileName: isAnimatedSticker ? "sticker.tgs" : "sticker.webp")) + if !isAnimatedSticker { + attributes.append(.CustomEmoji(isPremium: false, isSingleColor: false, alt: ".", packReference: nil)) + } + + return TelegramMediaFile( + fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: fileId), + partialReference: nil, + resource: LocalFileMediaResource(fileId: fileId), + previewRepresentations: [], + videoThumbnails: [], + immediateThumbnailData: nil, + mimeType: isAnimatedSticker ? "application/x-tgsticker" : "image/webp", + size: nil, + attributes: attributes + ) +} + +private func generateStarsReaction() -> AvailableReactions.Reaction { + return AvailableReactions.Reaction( + isEnabled: false, + isPremium: false, + value: .stars, + title: "Star", + staticIcon: generateStarsReactionFile(kind: 0, isAnimatedSticker: true), + appearAnimation: generateStarsReactionFile(kind: 1, isAnimatedSticker: true), + selectAnimation: generateStarsReactionFile(kind: 2, isAnimatedSticker: true), + activateAnimation: generateStarsReactionFile(kind: 3, isAnimatedSticker: true), + effectAnimation: generateStarsReactionFile(kind: 4, isAnimatedSticker: true), + aroundAnimation: generateStarsReactionFile(kind: 5, isAnimatedSticker: true), + centerAnimation: generateStarsReactionFile(kind: 6, isAnimatedSticker: true) + ) +} + public final class AvailableReactions: Equatable, Codable { public final class Reaction: Equatable, Codable { private enum CodingKeys: String, CodingKey { @@ -181,24 +220,10 @@ public final class AvailableReactions: Equatable, Codable { ) { self.hash = hash - //TODO:release var reactions = reactions reactions.removeAll(where: { if case .stars = $0.value { return true } else { return false } }) - if let item = reactions.first(where: { if case .builtin("🤩") = $0.value { return true } else { return false } }) { - reactions.append(Reaction( - isEnabled: false, - isPremium: false, - value: .stars, - title: "Star", - staticIcon: item.staticIcon, - appearAnimation: item.appearAnimation, - selectAnimation: item.selectAnimation, - activateAnimation: item.activateAnimation, - effectAnimation: item.effectAnimation, - aroundAnimation: item.aroundAnimation, - centerAnimation: item.centerAnimation - )) - } + //TODO:release + reactions.append(generateStarsReaction()) self.reactions = reactions } @@ -220,21 +245,7 @@ public final class AvailableReactions: Equatable, Codable { //TODO:release var reactions = try container.decode([Reaction].self, forKey: .reactions) reactions.removeAll(where: { if case .stars = $0.value { return true } else { return false } }) - if let item = reactions.first(where: { if case .builtin("🤩") = $0.value { return true } else { return false } }) { - reactions.append(Reaction( - isEnabled: false, - isPremium: false, - value: .stars, - title: "Star", - staticIcon: item.staticIcon, - appearAnimation: item.appearAnimation, - selectAnimation: item.selectAnimation, - activateAnimation: item.activateAnimation, - effectAnimation: item.effectAnimation, - aroundAnimation: item.aroundAnimation, - centerAnimation: item.centerAnimation - )) - } + reactions.append(generateStarsReaction()) self.reactions = reactions } @@ -314,6 +325,31 @@ func _internal_setCachedAvailableReactions(transaction: Transaction, availableRe } func managedSynchronizeAvailableReactions(postbox: Postbox, network: Network) -> Signal { + let starsReaction = generateStarsReaction() + let mapping: [String: KeyPath] = [ + "star_reaction_activate.tgs": \.activateAnimation, + "star_reaction_appear.tgs": \.appearAnimation, + "star_reaction_effect.tgs": \.effectAnimation, + "star_reaction_select.tgs": \.selectAnimation, + "star_reaction_static_icon.webp": \.staticIcon + ] + let optionalMapping: [String: KeyPath] = [ + "star_reaction_center.tgs": \.centerAnimation, + "star_reaction_effect.tgs": \.aroundAnimation + ] + for (key, path) in mapping { + if let filePath = Bundle.main.path(forResource: key, ofType: nil), let data = try? Data(contentsOf: URL(fileURLWithPath: filePath)) { + postbox.mediaBox.storeResourceData(starsReaction[keyPath: path].resource.id, data: data) + } + } + for (key, path) in optionalMapping { + if let filePath = Bundle.main.path(forResource: key, ofType: nil), let data = try? Data(contentsOf: URL(fileURLWithPath: filePath)) { + if let file = starsReaction[keyPath: path] { + postbox.mediaBox.storeResourceData(file.resource.id, data: data) + } + } + } + let poll = Signal { subscriber in let signal: Signal = _internal_cachedAvailableReactions(postbox: postbox) |> mapToSignal { current in @@ -327,6 +363,7 @@ func managedSynchronizeAvailableReactions(postbox: Postbox, network: Network) -> guard let result = result else { return .complete() } + switch result { case let .availableReactions(hash, reactions): let availableReactions = AvailableReactions( diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift index 4a8b972ee1..119177fab8 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift @@ -696,7 +696,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee switch participantResult { case let .channelParticipant(participant, _, _): switch participant { - case let .channelParticipantSelf(_, _, inviterId, invitedDate, _): + case let .channelParticipantSelf(flags, _, inviterId, invitedDate, _): invitedBy = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(inviterId)) if (flags & (1 << 0)) != 0 { invitedOn = invitedDate diff --git a/submodules/TelegramCore/Sources/Utils/MessageUtils.swift b/submodules/TelegramCore/Sources/Utils/MessageUtils.swift index e266dc6d11..9cbd7eaa73 100644 --- a/submodules/TelegramCore/Sources/Utils/MessageUtils.swift +++ b/submodules/TelegramCore/Sources/Utils/MessageUtils.swift @@ -530,6 +530,15 @@ public extension Message { var paidContent: TelegramMediaPaidContent? { return self.media.first(where: { $0 is TelegramMediaPaidContent }) as? TelegramMediaPaidContent } + + var authorSignatureAttribute: AuthorSignatureMessageAttribute? { + for attribute in self.attributes { + if let attribute = attribute as? AuthorSignatureMessageAttribute { + return attribute + } + } + return nil + } } public extension Message { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift index 3945bc6377..0600467200 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift @@ -849,6 +849,12 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI for contentNode in self.contentNodes { contentNode.updateIsExtractedToContextPreview(isExtractedToContextPreview) } + + if !isExtractedToContextPreview { + if let item = self.item { + item.controllerInteraction.forceUpdateWarpContents() + } + } } self.mainContextSourceNode.updateAbsoluteRect = { [weak self] rect, size in @@ -1474,6 +1480,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI } var effectiveAuthor: Peer? + var overrideEffectiveAuthor = false var ignoreForward = false var displayAuthorInfo: Bool var ignoreNameHiding = false @@ -1542,6 +1549,31 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI displayAuthorInfo = false } } + + //TODO:release + if let channel = firstMessage.peers[firstMessage.id.peerId] as? TelegramChannel, case let .broadcast(info) = channel.info, info.flags.contains(.messagesShouldHaveSignatures) { + hasAvatar = true + if let authorSignatureAttribute = firstMessage.authorSignatureAttribute { + effectiveAuthor = TelegramUser(id: PeerId(namespace: Namespaces.Peer.Empty, id: PeerId.Id._internalFromInt64Value(Int64(authorSignatureAttribute.signature.persistentHashValue % 32))), accessHash: nil, firstName: authorSignatureAttribute.signature, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, subscriberCount: nil) + overrideEffectiveAuthor = true + + var allowAuthor = incoming + + if let author = firstMessage.author, author is TelegramChannel, !incoming || item.presentationData.isPreview { + allowAuthor = true + ignoreNameHiding = true + } + + if let subject = item.associatedData.subject, case let .customChatContents(contents) = subject, case .hashTagSearch = contents.kind { + ignoreNameHiding = true + } + + displayAuthorInfo = !mergedTop.merged && allowAuthor && peerId.isGroupOrChannel && effectiveAuthor != nil + if let forwardInfo = firstMessage.forwardInfo, forwardInfo.psaType != nil { + displayAuthorInfo = false + } + } + } if !peerId.isRepliesOrSavedMessages(accountPeerId: item.context.account.peerId) { if peerId.isGroupOrChannel && effectiveAuthor != nil { @@ -2089,7 +2121,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI } if initialDisplayHeader && displayAuthorInfo { - if let peer = firstMessage.peers[firstMessage.id.peerId] as? TelegramChannel, case .broadcast = peer.info, item.content.firstMessage.adAttribute == nil { + if let peer = firstMessage.peers[firstMessage.id.peerId] as? TelegramChannel, case .broadcast = peer.info, item.content.firstMessage.adAttribute == nil, !overrideEffectiveAuthor { authorNameString = EnginePeer(peer).displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder) let peer = (peer as Peer) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageItemImpl.swift b/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageItemImpl.swift index d54a68b500..ae81587ac4 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageItemImpl.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageItemImpl.swift @@ -341,8 +341,16 @@ public final class ChatMessageItemImpl: ChatMessageItem, CustomStringConvertible } var hasAvatar = false - if !hasActionMedia && !isBroadcastChannel { - hasAvatar = true + if !hasActionMedia { + if !isBroadcastChannel { + hasAvatar = true + } else if let channel = message.peers[message.id.peerId] as? TelegramChannel, case let .broadcast(info) = channel.info, info.flags.contains(.messagesShouldHaveSignatures) { + //TODO:release + hasAvatar = true + if let authorSignatureAttribute = message.authorSignatureAttribute { + effectiveAuthor = TelegramUser(id: PeerId(namespace: Namespaces.Peer.Empty, id: PeerId.Id._internalFromInt64Value(Int64(authorSignatureAttribute.signature.persistentHashValue % 32))), accessHash: nil, firstName: authorSignatureAttribute.signature, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, subscriberCount: nil) + } + } } if hasAvatar { diff --git a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsControllerNode.swift b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsControllerNode.swift index 32d3549ad1..bbe912994c 100644 --- a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsControllerNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsControllerNode.swift @@ -628,6 +628,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { }, scrollToMessageId: { _ in }, navigateToStory: { _, _ in }, attemptedNavigationToPrivateQuote: { _ in + }, forceUpdateWarpContents: { }, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(), presentationContext: ChatPresentationContext(context: context, backgroundNode: self.backgroundNode)) self.controllerInteraction = controllerInteraction diff --git a/submodules/TelegramUI/Components/Chat/ChatSendAudioMessageContextPreview/Sources/ChatSendAudioMessageContextPreview.swift b/submodules/TelegramUI/Components/Chat/ChatSendAudioMessageContextPreview/Sources/ChatSendAudioMessageContextPreview.swift index e07264e9c3..714ce82b87 100644 --- a/submodules/TelegramUI/Components/Chat/ChatSendAudioMessageContextPreview/Sources/ChatSendAudioMessageContextPreview.swift +++ b/submodules/TelegramUI/Components/Chat/ChatSendAudioMessageContextPreview/Sources/ChatSendAudioMessageContextPreview.swift @@ -492,6 +492,7 @@ public final class ChatSendGroupMediaMessageContextPreview: UIView, ChatSendMess }, scrollToMessageId: { _ in }, navigateToStory: { _, _ in }, attemptedNavigationToPrivateQuote: { _ in + }, forceUpdateWarpContents: { }, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(), presentationContext: ChatPresentationContext(context: self.context, backgroundNode: self.wallpaperBackgroundNode)) diff --git a/submodules/TelegramUI/Components/Chat/ChatSendStarsScreen/Sources/ChatSendStarsScreen.swift b/submodules/TelegramUI/Components/Chat/ChatSendStarsScreen/Sources/ChatSendStarsScreen.swift index 8b1be4cc5a..37000034f2 100644 --- a/submodules/TelegramUI/Components/Chat/ChatSendStarsScreen/Sources/ChatSendStarsScreen.swift +++ b/submodules/TelegramUI/Components/Chat/ChatSendStarsScreen/Sources/ChatSendStarsScreen.swift @@ -745,14 +745,14 @@ private final class ChatSendStarsScreenComponent: Component { let peer: EnginePeer let balance: Int64? let topPeers: [ChatSendStarsScreen.TopPeer] - let completion: (Int64, ChatSendStarsScreen.TransitionOut) -> Void + let completion: (Int64, Bool, ChatSendStarsScreen.TransitionOut) -> Void init( context: AccountContext, peer: EnginePeer, balance: Int64?, topPeers: [ChatSendStarsScreen.TopPeer], - completion: @escaping (Int64, ChatSendStarsScreen.TransitionOut) -> Void + completion: @escaping (Int64, Bool, ChatSendStarsScreen.TransitionOut) -> Void ) { self.context = context self.peer = peer @@ -1104,9 +1104,11 @@ private final class ChatSendStarsScreenComponent: Component { let progressFraction: CGFloat = CGFloat(self.amount) / CGFloat(1000 - 1) + let topCount = component.topPeers.max(by: { $0.count < $1.count })?.count + var topCutoffFraction: CGFloat? - if let maxCount = component.topPeers.max(by: { $0.count < $1.count })?.count { - let topCutoffFractionValue = CGFloat(maxCount) / CGFloat(1000 - 1) + if let topCount { + let topCutoffFractionValue = CGFloat(topCount) / CGFloat(1000 - 1) topCutoffFraction = topCutoffFractionValue let isPastCutoff = progressFraction >= topCutoffFractionValue @@ -1470,8 +1472,15 @@ private final class ChatSendStarsScreenComponent: Component { guard let badgeView = self.badge.view as? BadgeComponent.View else { return } + let isBecomingTop: Bool + if let topCount { + isBecomingTop = self.amount > topCount + } else { + isBecomingTop = true + } component.completion( self.amount, + isBecomingTop, ChatSendStarsScreen.TransitionOut( sourceView: badgeView.badgeIcon ) @@ -1617,7 +1626,7 @@ public class ChatSendStarsScreen: ViewControllerComponentContainer { private var presenceDisposable: Disposable? - public init(context: AccountContext, initialData: InitialData, completion: @escaping (Int64, TransitionOut) -> Void) { + public init(context: AccountContext, initialData: InitialData, completion: @escaping (Int64, Bool, TransitionOut) -> Void) { self.context = context super.init(context: context, component: ChatSendStarsScreenComponent( diff --git a/submodules/TelegramUI/Components/ChatControllerInteraction/Sources/ChatControllerInteraction.swift b/submodules/TelegramUI/Components/ChatControllerInteraction/Sources/ChatControllerInteraction.swift index 3fd747901f..1d555712bf 100644 --- a/submodules/TelegramUI/Components/ChatControllerInteraction/Sources/ChatControllerInteraction.swift +++ b/submodules/TelegramUI/Components/ChatControllerInteraction/Sources/ChatControllerInteraction.swift @@ -272,6 +272,7 @@ public final class ChatControllerInteraction: ChatControllerInteractionProtocol public let scrollToMessageId: (MessageIndex) -> Void public let navigateToStory: (Message, StoryId) -> Void public let attemptedNavigationToPrivateQuote: (Peer?) -> Void + public let forceUpdateWarpContents: () -> Void public var canPlayMedia: Bool = false public var hiddenMedia: [MessageId: [Media]] = [:] @@ -400,6 +401,7 @@ public final class ChatControllerInteraction: ChatControllerInteractionProtocol scrollToMessageId: @escaping (MessageIndex) -> Void, navigateToStory: @escaping (Message, StoryId) -> Void, attemptedNavigationToPrivateQuote: @escaping (Peer?) -> Void, + forceUpdateWarpContents: @escaping () -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, pollActionState: ChatInterfacePollActionState, stickerSettings: ChatInterfaceStickerSettings, @@ -509,6 +511,7 @@ public final class ChatControllerInteraction: ChatControllerInteractionProtocol self.scrollToMessageId = scrollToMessageId self.navigateToStory = navigateToStory self.attemptedNavigationToPrivateQuote = attemptedNavigationToPrivateQuote + self.forceUpdateWarpContents = forceUpdateWarpContents self.automaticMediaDownloadSettings = automaticMediaDownloadSettings diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index 98b14e1ed8..b6a60d4137 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -3485,6 +3485,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro }, scrollToMessageId: { _ in }, navigateToStory: { _, _ in }, attemptedNavigationToPrivateQuote: { _ in + }, forceUpdateWarpContents: { }, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(), presentationContext: ChatPresentationContext(context: context, backgroundNode: nil)) self.hiddenMediaDisposable = context.sharedContext.mediaManager.galleryHiddenMediaManager.hiddenIds().startStrict(next: { [weak self] ids in diff --git a/submodules/TelegramUI/Components/Premium/PremiumStarComponent/Sources/PremiumStarComponent.swift b/submodules/TelegramUI/Components/Premium/PremiumStarComponent/Sources/PremiumStarComponent.swift index 495541776b..9368f7daba 100644 --- a/submodules/TelegramUI/Components/Premium/PremiumStarComponent/Sources/PremiumStarComponent.swift +++ b/submodules/TelegramUI/Components/Premium/PremiumStarComponent/Sources/PremiumStarComponent.swift @@ -684,3 +684,208 @@ public final class PremiumStarComponent: Component { return view.update(component: self, availableSize: availableSize, transition: transition) } } + +public final class StandalonePremiumStarComponent: Component { + let theme: PresentationTheme + let colors: [UIColor]? + + public init( + theme: PresentationTheme, + colors: [UIColor]? = nil + ) { + self.theme = theme + self.colors = colors + } + + public static func ==(lhs: StandalonePremiumStarComponent, rhs: StandalonePremiumStarComponent) -> Bool { + return lhs.theme === rhs.theme && lhs.colors == rhs.colors + } + + public final class View: UIView, SCNSceneRendererDelegate, ComponentTaggedView { + public final class Tag { + public init() { + } + } + + public func matches(tag: Any) -> Bool { + if let _ = tag as? Tag { + return true + } + return false + } + + private var component: StandalonePremiumStarComponent? + + private var _ready = Promise() + public var ready: Signal { + return self._ready.get() + } + + private let sceneView: SCNView + + private var timer: SwiftSignalKit.Timer? + + override init(frame: CGRect) { + self.sceneView = SCNView(frame: CGRect(origin: .zero, size: CGSize(width: 64.0, height: 64.0))) + self.sceneView.backgroundColor = .clear + self.sceneView.transform = CGAffineTransform(scaleX: 0.5, y: 0.5) + self.sceneView.isUserInteractionEnabled = false + self.sceneView.preferredFramesPerSecond = 60 + self.sceneView.isJitteringEnabled = true + + super.init(frame: frame) + + self.addSubview(self.sceneView) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + self.timer?.invalidate() + } + + private var didSetup = false + private func setup() { + guard !self.didSetup, let scene = loadCompressedScene(name: "star2", version: sceneVersion) else { + return + } + + self.didSetup = true + self.sceneView.scene = scene + self.sceneView.delegate = self + + if let component = self.component, let node = scene.rootNode.childNode(withName: "star", recursively: false), let colors = + component.colors { + node.geometry?.materials.first?.diffuse.contents = generateDiffuseTexture(colors: colors) + } + + for node in scene.rootNode.childNodes { + if let name = node.name, name.hasPrefix("particles") { + node.isHidden = true + } + } + + self.didSetReady = true + self._ready.set(.single(true)) + self.onReady() + } + + private var didSetReady = false + public func renderer(_ renderer: SCNSceneRenderer, didRenderScene scene: SCNScene, atTime time: TimeInterval) { + if !self.didSetReady { + self.didSetReady = true + + Queue.mainQueue().justDispatch { + self._ready.set(.single(true)) + self.onReady() + } + } + } + + private func onReady() { + //self.setupScaleAnimation() + //self.setupGradientAnimation() + + self.playAppearanceAnimation(mirror: true) + } + + private func setupScaleAnimation() { + guard let scene = self.sceneView.scene, let node = scene.rootNode.childNode(withName: "star", recursively: false) else { + return + } + + let fromScale: Float = 0.1 + let toScale: Float = 0.092 + + let animation = CABasicAnimation(keyPath: "scale") + animation.duration = 2.0 + animation.fromValue = NSValue(scnVector3: SCNVector3(x: fromScale, y: fromScale, z: fromScale)) + animation.toValue = NSValue(scnVector3: SCNVector3(x: toScale, y: toScale, z: toScale)) + animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) + animation.autoreverses = true + animation.repeatCount = .infinity + + node.addAnimation(animation, forKey: "scale") + } + + private func setupGradientAnimation() { + guard let scene = self.sceneView.scene, let node = scene.rootNode.childNode(withName: "star", recursively: false) else { + return + } + guard let initial = node.geometry?.materials.first?.diffuse.contentsTransform else { + return + } + + let animation = CABasicAnimation(keyPath: "contentsTransform") + animation.duration = 4.5 + animation.fromValue = NSValue(scnMatrix4: initial) + animation.toValue = NSValue(scnMatrix4: SCNMatrix4Translate(initial, -0.35, 0.35, 0)) + animation.timingFunction = CAMediaTimingFunction(name: .linear) + animation.autoreverses = true + animation.repeatCount = .infinity + + node.geometry?.materials.first?.diffuse.addAnimation(animation, forKey: "gradient") + } + + private func playAppearanceAnimation(velocity: CGFloat? = nil, smallAngle: Bool = false, mirror: Bool = false) { + guard let scene = self.sceneView.scene, let node = scene.rootNode.childNode(withName: "star", recursively: false) else { + return + } + + var from = node.presentation.eulerAngles + if abs(from.y - .pi * 2.0) < 0.001 { + from.y = 0.0 + } + node.removeAnimation(forKey: "tapRotate") + + var toValue: Float = smallAngle ? 0.0 : .pi * 2.0 + if let velocity = velocity, !smallAngle && abs(velocity) > 200 && velocity < 0.0 { + toValue *= -1 + } + if mirror { + toValue *= -1 + } + let to = SCNVector3(x: 0.0, y: toValue, z: 0.0) + let distance = rad2deg(to.y - from.y) + + guard !distance.isZero else { + return + } + + let animation = CABasicAnimation(keyPath: "eulerAngles") + animation.fromValue = NSValue(scnVector3: from) + animation.toValue = NSValue(scnVector3: to) + animation.duration = 0.4 * UIView.animationDurationFactor() + animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) + animation.completion = { [weak node] finished in + if finished { + node?.eulerAngles = SCNVector3(x: 0.0, y: 0.0, z: 0.0) + } + } + node.addAnimation(animation, forKey: "rotate") + } + + func update(component: StandalonePremiumStarComponent, availableSize: CGSize, transition: ComponentTransition) -> CGSize { + self.component = component + + self.setup() + + self.sceneView.bounds = CGRect(origin: .zero, size: CGSize(width: availableSize.width * 2.0, height: availableSize.height * 2.0)) + if self.sceneView.superview == self { + self.sceneView.center = CGPoint(x: availableSize.width / 2.0, y: availableSize.height / 2.0) + } + + return availableSize + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/SpaceWarpView/Sources/MeshLayer.swift b/submodules/TelegramUI/Components/SpaceWarpView/Sources/MeshLayer.swift new file mode 100644 index 0000000000..874704418a --- /dev/null +++ b/submodules/TelegramUI/Components/SpaceWarpView/Sources/MeshLayer.swift @@ -0,0 +1,7 @@ +import Foundation +import UIKit +import Display + +final class MeshLayer: CALayer { + +} diff --git a/submodules/TelegramUI/Components/SpaceWarpView/Sources/SpaceWarpView.swift b/submodules/TelegramUI/Components/SpaceWarpView/Sources/SpaceWarpView.swift index 99c074523e..48f18d5f66 100644 --- a/submodules/TelegramUI/Components/SpaceWarpView/Sources/SpaceWarpView.swift +++ b/submodules/TelegramUI/Components/SpaceWarpView/Sources/SpaceWarpView.swift @@ -255,7 +255,7 @@ private final class MaskGridLayer: SimpleLayer { private var resolution: (x: Int, y: Int)? - func updateGrid(resolutionX: Int, resolutionY: Int) { + func updateGrid(size: CGSize, resolutionX: Int, resolutionY: Int, cornerRadius: CGFloat) { if let resolution = self.resolution, resolution.x == resolutionX, resolution.y == resolutionY { return } @@ -266,13 +266,52 @@ private final class MaskGridLayer: SimpleLayer { } self.itemLayers.removeAll() - for _ in 0 ..< resolutionX * resolutionY { - let itemLayer = SimpleLayer() - itemLayer.backgroundColor = UIColor.black.cgColor - itemLayer.opacity = 1.0 - itemLayer.anchorPoint = CGPoint() - self.addSublayer(itemLayer) - self.itemLayers.append(itemLayer) + let itemSize = CGSize(width: size.width / CGFloat(resolutionX), height: size.height / CGFloat(resolutionY)) + + let topLeftCorner = CGRect(origin: CGPoint(), size: CGSize(width: cornerRadius, height: cornerRadius)) + let topRightCorner = CGRect(origin: CGPoint(x: size.width - cornerRadius, y: 0.0), size: CGSize(width: cornerRadius, height: cornerRadius)) + let bottomLeftCorner = CGRect(origin: CGPoint(x: 0.0, y: size.height - cornerRadius), size: CGSize(width: cornerRadius, height: cornerRadius)) + let bottomRightCorner = CGRect(origin: CGPoint(x: size.width - cornerRadius, y: size.height - cornerRadius), size: CGSize(width: cornerRadius, height: cornerRadius)) + + var cornersImage: UIImage? + if cornerRadius > 0.0 { + cornersImage = generateImage(CGSize(width: cornerRadius * 2.0 + 200.0, height: cornerRadius * 2.0 + 200.0), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setFillColor(UIColor.black.cgColor) + context.addPath(UIBezierPath(roundedRect: CGRect(origin: CGPoint(), size: size), cornerRadius: cornerRadius).cgPath) + context.fillPath() + }) + } + + for y in 0 ..< resolutionY { + for x in 0 ..< resolutionX { + let itemLayer = SimpleLayer() + itemLayer.backgroundColor = UIColor.black.cgColor + itemLayer.isOpaque = true + itemLayer.opacity = 1.0 + itemLayer.anchorPoint = CGPoint() + self.addSublayer(itemLayer) + self.itemLayers.append(itemLayer) + + if cornerRadius > 0.0, let cornersImage { + let gridPosition = CGPoint(x: CGFloat(x) / CGFloat(resolutionX), y: CGFloat(y) / CGFloat(resolutionY)) + let sourceRect = CGRect(origin: CGPoint(x: gridPosition.x * (size.width), y: gridPosition.y * (size.height)), size: itemSize) + if sourceRect.intersects(topLeftCorner) || sourceRect.intersects(topRightCorner) || sourceRect.intersects(bottomLeftCorner) || sourceRect.intersects(bottomRightCorner) { + var clippedCornersRect = sourceRect + if clippedCornersRect.maxX > cornersImage.size.width { + clippedCornersRect.origin.x -= size.width - cornersImage.size.width + } + if clippedCornersRect.maxY > cornersImage.size.height { + clippedCornersRect.origin.y -= size.height - cornersImage.size.height + } + + itemLayer.contents = cornersImage.cgImage + itemLayer.contentsRect = CGRect(origin: CGPoint(x: clippedCornersRect.minX / cornersImage.size.width, y: clippedCornersRect.minY / cornersImage.size.height), size: CGSize(width: clippedCornersRect.width / cornersImage.size.width, height: clippedCornersRect.height / cornersImage.size.height)) + itemLayer.backgroundColor = nil + itemLayer.isOpaque = false + } + } + } } } @@ -503,7 +542,7 @@ open class SpaceWarpNodeImpl: ASDisplayNode, SpaceWarpNode { let endEndPoint = CGPoint(x: (gradientLayer.startPoint.x + radius.width), y: (gradientLayer.startPoint.y + radius.height)) gradientLayer.endPoint = endEndPoint - let maxWavefrontNorm: CGFloat = 0.3 + let maxWavefrontNorm: CGFloat = 0.4 let normProgress = max(0.0, min(1.0, progress)) let interpolatedNorm: CGFloat = 1.0 * (1.0 - normProgress) + maxWavefrontNorm * normProgress @@ -587,7 +626,7 @@ open class SpaceWarpNodeImpl: ASDisplayNode, SpaceWarpNode { } if let gradientMaskLayer = self.gradientMaskLayer { - gradientMaskLayer.updateGrid(resolutionX: resolutionX, resolutionY: resolutionY) + gradientMaskLayer.updateGrid(size: size, resolutionX: resolutionX, resolutionY: resolutionY, cornerRadius: cornerRadius) gradientMaskLayer.update(positions: instancePositions, bounds: instanceBounds, transforms: instanceTransforms) } } diff --git a/submodules/TelegramUI/Resources/Animations/star_up/star_reaction_activate.tgs b/submodules/TelegramUI/Resources/Animations/star_up/star_reaction_activate.tgs new file mode 100644 index 0000000000..61f1538549 Binary files /dev/null and b/submodules/TelegramUI/Resources/Animations/star_up/star_reaction_activate.tgs differ diff --git a/submodules/TelegramUI/Resources/Animations/star_up/star_reaction_appear.tgs b/submodules/TelegramUI/Resources/Animations/star_up/star_reaction_appear.tgs new file mode 100644 index 0000000000..3d4e32a67e Binary files /dev/null and b/submodules/TelegramUI/Resources/Animations/star_up/star_reaction_appear.tgs differ diff --git a/submodules/TelegramUI/Resources/Animations/star_up/star_reaction_center.tgs b/submodules/TelegramUI/Resources/Animations/star_up/star_reaction_center.tgs new file mode 100644 index 0000000000..61f1538549 Binary files /dev/null and b/submodules/TelegramUI/Resources/Animations/star_up/star_reaction_center.tgs differ diff --git a/submodules/TelegramUI/Resources/Animations/star_up/star_reaction_effect.tgs b/submodules/TelegramUI/Resources/Animations/star_up/star_reaction_effect.tgs new file mode 100644 index 0000000000..f31897de51 Binary files /dev/null and b/submodules/TelegramUI/Resources/Animations/star_up/star_reaction_effect.tgs differ diff --git a/submodules/TelegramUI/Resources/Animations/star_up/star_reaction_select.tgs b/submodules/TelegramUI/Resources/Animations/star_up/star_reaction_select.tgs new file mode 100644 index 0000000000..61f1538549 Binary files /dev/null and b/submodules/TelegramUI/Resources/Animations/star_up/star_reaction_select.tgs differ diff --git a/submodules/TelegramUI/Resources/Animations/star_up/star_reaction_static_icon.webp b/submodules/TelegramUI/Resources/Animations/star_up/star_reaction_static_icon.webp new file mode 100644 index 0000000000..9c9b83471d Binary files /dev/null and b/submodules/TelegramUI/Resources/Animations/star_up/star_reaction_static_icon.webp differ diff --git a/submodules/TelegramUI/Sources/Chat/PeerMessageSelectedReactions.swift b/submodules/TelegramUI/Sources/Chat/PeerMessageSelectedReactions.swift index 0d41a830af..7a30ad19f8 100644 --- a/submodules/TelegramUI/Sources/Chat/PeerMessageSelectedReactions.swift +++ b/submodules/TelegramUI/Sources/Chat/PeerMessageSelectedReactions.swift @@ -17,6 +17,9 @@ func peerMessageSelectedReactions(context: AccountContext, message: Message) -> if !reaction.isSelected { continue } + if case .stars = reaction.value { + continue + } reactions.insert(reaction.value) switch reaction.value { case .builtin, .stars: diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 1722b18872..b594c88667 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -4525,6 +4525,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G text = self.presentationData.strings.Chat_ToastQuoteChatUnavailbalePrivateChat } self.controllerInteraction?.displayUndo(.info(title: nil, text: text, timeout: nil, customUndoText: nil)) + }, forceUpdateWarpContents: { [weak self] in + guard let self else { + return + } + self.chatDisplayNode.forceUpdateWarpContents() }, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: self.stickerSettings, presentationContext: ChatPresentationContext(context: context, backgroundNode: self.chatBackgroundNode)) controllerInteraction.enableFullTranslucency = context.sharedContext.energyUsageSettings.fullTranslucency diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index 4ca47e5657..a9bee54363 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -1076,6 +1076,13 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { return CGSize(width: layout.size.width, height: height) } + func forceUpdateWarpContents() { + guard let (layout, _) = self.validLayout else { + return + } + self.wrappingNode.update(size: layout.size, cornerRadius: layout.deviceMetrics.screenCornerRadius, transition: .immediate) + } + func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition protoTransition: ContainedViewLayoutTransition, listViewTransaction: (ListViewUpdateSizeAndInsets, CGFloat, Bool, @escaping () -> Void) -> Void, updateExtraNavigationBarBackgroundHeight: (CGFloat, CGFloat, ContainedViewLayoutTransition) -> Void) { let transition: ContainedViewLayoutTransition if let _ = self.scheduledAnimateInAsOverlayFromNode { diff --git a/submodules/TelegramUI/Sources/ChatControllerOpenMessageReactionContextMenu.swift b/submodules/TelegramUI/Sources/ChatControllerOpenMessageReactionContextMenu.swift index fc8bd905eb..090fa6bdfc 100644 --- a/submodules/TelegramUI/Sources/ChatControllerOpenMessageReactionContextMenu.swift +++ b/submodules/TelegramUI/Sources/ChatControllerOpenMessageReactionContextMenu.swift @@ -171,7 +171,7 @@ extension ChatControllerImpl { guard let self, let initialData else { return } - self.push(ChatSendStarsScreen(context: self.context, initialData: initialData, completion: { [weak self] amount, transitionOut in + self.push(ChatSendStarsScreen(context: self.context, initialData: initialData, completion: { [weak self] amount, isBecomingTop, transitionOut in guard let self, amount > 0 else { return } @@ -216,7 +216,7 @@ extension ChatControllerImpl { self.chatDisplayNode.messageTransitionNode.addMessageStandaloneReactionAnimation(messageId: item.message.id, standaloneReactionAnimation: standaloneReactionAnimation) - self.chatDisplayNode.addSubnode(standaloneReactionAnimation) + self.view.window?.addSubview(standaloneReactionAnimation.view) standaloneReactionAnimation.frame = self.chatDisplayNode.bounds standaloneReactionAnimation.animateOutToReaction( context: self.context, @@ -240,47 +240,23 @@ extension ChatControllerImpl { guard let self else { return } + + if isBecomingTop { + self.chatDisplayNode.animateQuizCorrectOptionSelected() + } + if let itemNode, let targetView = itemNode.targetReactionView(value: .stars) { self.chatDisplayNode.wrappingNode.triggerRipple(at: targetView.convert(targetView.bounds.center, to: self.chatDisplayNode.view)) } }, completion: { [weak standaloneReactionAnimation] in - standaloneReactionAnimation?.removeFromSupernode() + standaloneReactionAnimation?.view.removeFromSuperview() } ) - /*standaloneReactionAnimation.animateReactionSelection( - context: strongSelf.context, - theme: strongSelf.presentationData.theme, - animationCache: strongSelf.controllerInteraction!.presentationContext.animationCache, - reaction: reactionItem, - avatarPeers: [], - playHaptic: false, - isLarge: false, - targetView: targetView, - addStandaloneReactionAnimation: { standaloneReactionAnimation in - guard let strongSelf = self else { - return - } - strongSelf.chatDisplayNode.messageTransitionNode.addMessageStandaloneReactionAnimation(messageId: item.message.id, standaloneReactionAnimation: standaloneReactionAnimation) - standaloneReactionAnimation.frame = strongSelf.chatDisplayNode.bounds - strongSelf.chatDisplayNode.addSubnode(standaloneReactionAnimation) - }, - onHit: { [weak itemNode] in - guard let strongSelf = self else { - return - } - if let itemNode = itemNode, let targetView = itemNode.targetReactionView(value: chosenReaction) { - strongSelf.chatDisplayNode.wrappingNode.triggerRipple(at: targetView.convert(targetView.bounds.center, to: strongSelf.chatDisplayNode.view)) - } - }, - completion: { [weak standaloneReactionAnimation] in - standaloneReactionAnimation?.removeFromSupernode() - } - )*/ } } - let _ = self.context.engine.messages.sendStarsReaction(id: message.id, count: Int(amount)) + //let _ = self.context.engine.messages.sendStarsReaction(id: message.id, count: Int(amount)) let _ = (self.context.engine.stickers.resolveInlineStickers(fileIds: [MessageReaction.starsReactionId]) |> deliverOnMainQueue).start(next: { [weak self] files in diff --git a/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift b/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift index c731e46943..98bd6c348e 100644 --- a/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift +++ b/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift @@ -184,6 +184,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu }, scrollToMessageId: { _ in }, navigateToStory: { _, _ in }, attemptedNavigationToPrivateQuote: { _ in + }, forceUpdateWarpContents: { }, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(), presentationContext: ChatPresentationContext(context: context, backgroundNode: nil)) self.dimNode = ASDisplayNode() diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index d040458dc2..01749d1680 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -1787,6 +1787,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { }, scrollToMessageId: { _ in }, navigateToStory: { _, _ in }, attemptedNavigationToPrivateQuote: { _ in + }, forceUpdateWarpContents: { }, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(), presentationContext: ChatPresentationContext(context: context, backgroundNode: backgroundNode as? WallpaperBackgroundNode))