diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index 91f5a322f9..51de4609a3 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -89,7 +89,7 @@ private enum ChatListRecentEntry: Comparable, Identifiable { } } - func item(context: AccountContext, presentationData: ChatListPresentationData, filter: ChatListNodePeersFilter, peerSelected: @escaping (EnginePeer) -> Void, disabledPeerSelected: @escaping (EnginePeer) -> Void, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, clearRecentlySearchedPeers: @escaping () -> Void, deletePeer: @escaping (EnginePeer.Id) -> Void) -> ListViewItem { + func item(context: AccountContext, presentationData: ChatListPresentationData, filter: ChatListNodePeersFilter, peerSelected: @escaping (EnginePeer) -> Void, disabledPeerSelected: @escaping (EnginePeer) -> Void, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, clearRecentlySearchedPeers: @escaping () -> Void, deletePeer: @escaping (EnginePeer.Id) -> Void, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer) -> ListViewItem { switch self { case let .topPeers(peers, theme, strings): return ChatListRecentPeersListItem(theme: theme, strings: strings, context: context, peers: peers, peerSelected: { peer in @@ -195,25 +195,45 @@ private enum ChatListRecentEntry: Comparable, Identifiable { badge = ContactsPeerItemBadge(count: peer.unreadCount, type: isMuted ? .inactive : .active) } - return ContactsPeerItem(presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings), sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, context: context, peerMode: .generalSearch, peer: .peer(peer: primaryPeer, chatPeer: chatPeer), status: status, badge: badge, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: ChatListSearchItemHeader(type: .recentPeers, theme: theme, strings: strings, actionTitle: strings.WebSearch_RecentSectionClear, action: { - clearRecentlySearchedPeers() - }), action: { _ in - if let chatPeer = peer.peer.peers[peer.peer.peerId] { - peerSelected(EnginePeer(chatPeer)) - } - }, disabledAction: { _ in - if let chatPeer = peer.peer.peers[peer.peer.peerId] { - disabledPeerSelected(EnginePeer(chatPeer)) - } - }, deletePeer: deletePeer, contextAction: peerContextAction.flatMap { peerContextAction in - return { node, gesture, location in + return ContactsPeerItem( + presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings), + sortOrder: nameSortOrder, + displayOrder: nameDisplayOrder, + context: context, + peerMode: .generalSearch, + peer: .peer(peer: primaryPeer, chatPeer: chatPeer), + status: status, + badge: badge, + enabled: enabled, + selection: .none, + editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), + index: nil, + header: ChatListSearchItemHeader(type: .recentPeers, theme: theme, strings: strings, actionTitle: strings.WebSearch_RecentSectionClear, action: { + clearRecentlySearchedPeers() + }), + action: { _ in if let chatPeer = peer.peer.peers[peer.peer.peerId] { - peerContextAction(EnginePeer(chatPeer), .recentSearch, node, gesture, location) - } else { - gesture?.cancel() + peerSelected(EnginePeer(chatPeer)) } - } - }) + }, + disabledAction: { _ in + if let chatPeer = peer.peer.peers[peer.peer.peerId] { + disabledPeerSelected(EnginePeer(chatPeer)) + } + }, + deletePeer: deletePeer, + contextAction: peerContextAction.flatMap { peerContextAction in + return { node, gesture, location in + if let chatPeer = peer.peer.peers[peer.peer.peerId] { + peerContextAction(EnginePeer(chatPeer), .recentSearch, node, gesture, location) + } else { + gesture?.cancel() + } + } + }, + animationCache: animationCache, + animationRenderer: animationRenderer + ) } } } @@ -497,7 +517,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable { gesture?.cancel() } } - }, arrowAction: nil) + }, arrowAction: nil, animationCache: interaction.animationCache, animationRenderer: interaction.animationRenderer) case let .localPeer(peer, associatedPeer, unreadBadge, _, theme, strings, nameSortOrder, nameDisplayOrder, expandType): let primaryPeer: EnginePeer var chatPeer: EnginePeer? @@ -584,7 +604,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable { gesture?.cancel() } } - }, arrowAction: nil) + }, arrowAction: nil, animationCache: interaction.animationCache, animationRenderer: interaction.animationRenderer) case let .globalPeer(peer, unreadBadge, _, theme, strings, nameSortOrder, nameDisplayOrder, expandType): var enabled = true if filter.contains(.onlyWriteable) { @@ -643,7 +663,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable { return { node, gesture, location in peerContextAction(EnginePeer(peer.peer), .search(nil), node, gesture, location) } - }) + }, animationCache: interaction.animationCache, animationRenderer: interaction.animationRenderer) case let .message(message, peer, readState, presentationData, _, selected, displayCustomHeader, orderingKey, _, _, allPaused): let header: ChatListSearchItemHeader switch orderingKey { @@ -712,12 +732,12 @@ public struct ChatListSearchContainerTransition { } } -private func chatListSearchContainerPreparedRecentTransition(from fromEntries: [ChatListRecentEntry], to toEntries: [ChatListRecentEntry], context: AccountContext, presentationData: ChatListPresentationData, filter: ChatListNodePeersFilter, peerSelected: @escaping (EnginePeer) -> Void, disabledPeerSelected: @escaping (EnginePeer) -> Void, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, clearRecentlySearchedPeers: @escaping () -> Void, deletePeer: @escaping (EnginePeer.Id) -> Void) -> ChatListSearchContainerRecentTransition { +private func chatListSearchContainerPreparedRecentTransition(from fromEntries: [ChatListRecentEntry], to toEntries: [ChatListRecentEntry], context: AccountContext, presentationData: ChatListPresentationData, filter: ChatListNodePeersFilter, peerSelected: @escaping (EnginePeer) -> Void, disabledPeerSelected: @escaping (EnginePeer) -> Void, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, clearRecentlySearchedPeers: @escaping () -> Void, deletePeer: @escaping (EnginePeer.Id) -> Void, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer) -> ChatListSearchContainerRecentTransition { let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) } - let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, filter: filter, peerSelected: peerSelected, disabledPeerSelected: disabledPeerSelected, peerContextAction: peerContextAction, clearRecentlySearchedPeers: clearRecentlySearchedPeers, deletePeer: deletePeer), directionHint: nil) } - let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, filter: filter, peerSelected: peerSelected, disabledPeerSelected: disabledPeerSelected, peerContextAction: peerContextAction, clearRecentlySearchedPeers: clearRecentlySearchedPeers, deletePeer: deletePeer), directionHint: nil) } + let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, filter: filter, peerSelected: peerSelected, disabledPeerSelected: disabledPeerSelected, peerContextAction: peerContextAction, clearRecentlySearchedPeers: clearRecentlySearchedPeers, deletePeer: deletePeer, animationCache: animationCache, animationRenderer: animationRenderer), directionHint: nil) } + let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, filter: filter, peerSelected: peerSelected, disabledPeerSelected: disabledPeerSelected, peerContextAction: peerContextAction, clearRecentlySearchedPeers: clearRecentlySearchedPeers, deletePeer: deletePeer, animationCache: animationCache, animationRenderer: animationRenderer), directionHint: nil) } return ChatListSearchContainerRecentTransition(deletions: deletions, insertions: insertions, updates: updates) } @@ -2058,7 +2078,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { interaction.clearRecentSearch() }, deletePeer: { peerId in let _ = context.engine.peers.removeRecentlySearchedPeer(peerId: peerId).start() - }) + }, animationCache: strongSelf.animationCache, animationRenderer: strongSelf.animationRenderer) strongSelf.enqueueRecentTransition(transition, firstTime: firstTime) } })) diff --git a/submodules/ChatListUI/Sources/ChatListTitleView.swift b/submodules/ChatListUI/Sources/ChatListTitleView.swift index c6f9eb02f3..d75df8b91a 100644 --- a/submodules/ChatListUI/Sources/ChatListTitleView.swift +++ b/submodules/ChatListUI/Sources/ChatListTitleView.swift @@ -435,6 +435,14 @@ final class ChatListTitleView: UIView, NavigationBarTitleView, NavigationBarTitl } override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + if let titleCredibilityIconView = self.titleCredibilityIconView, !titleCredibilityIconView.isHidden, titleCredibilityIconView.alpha != 0.0 { + if titleCredibilityIconView.bounds.insetBy(dx: -8.0, dy: -8.0).contains(self.convert(point, to: titleCredibilityIconView)) { + if let result = titleCredibilityIconView.hitTest(titleCredibilityIconView.bounds.center, with: event) { + return result + } + } + } + if !self.proxyButton.isHidden { if let result = self.proxyButton.hitTest(point.offsetBy(dx: -self.proxyButton.frame.minX, dy: -self.proxyButton.frame.minY), with: event) { return result; diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift index cb0635854b..bb61c8a6e7 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNode.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNode.swift @@ -393,7 +393,9 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL if let chatPeer = chatPeer { nodeInteraction.disabledPeerSelected(chatPeer) } - } + }, + animationCache: nodeInteraction.animationCache, + animationRenderer: nodeInteraction.animationRenderer ), directionHint: entry.directionHint) } case let .HoleEntry(_, theme): @@ -533,7 +535,9 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL if let chatPeer = chatPeer { nodeInteraction.disabledPeerSelected(chatPeer) } - } + }, + animationCache: nodeInteraction.animationCache, + animationRenderer: nodeInteraction.animationRenderer ), directionHint: entry.directionHint) } case let .HoleEntry(_, theme): diff --git a/submodules/ComponentFlow/Source/Base/Transition.swift b/submodules/ComponentFlow/Source/Base/Transition.swift index c6a8334e42..cb988e2aea 100644 --- a/submodules/ComponentFlow/Source/Base/Transition.swift +++ b/submodules/ComponentFlow/Source/Base/Transition.swift @@ -390,7 +390,11 @@ public struct Transition { } public func setScale(view: UIView, scale: CGFloat, completion: ((Bool) -> Void)? = nil) { - let t = view.layer.presentation()?.transform ?? view.layer.transform + self.setScale(layer: view.layer, scale: scale, completion: completion) + } + + public func setScale(layer: CALayer, scale: CGFloat, completion: ((Bool) -> Void)? = nil) { + let t = layer.presentation()?.transform ?? layer.transform let currentScale = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13)) if currentScale == scale { completion?(true) @@ -398,12 +402,12 @@ public struct Transition { } switch self.animation { case .none: - view.layer.transform = CATransform3DMakeScale(scale, scale, 1.0) + layer.transform = CATransform3DMakeScale(scale, scale, 1.0) completion?(true) case let .curve(duration, curve): let previousScale = currentScale - view.layer.transform = CATransform3DMakeScale(scale, scale, 1.0) - view.layer.animate( + layer.transform = CATransform3DMakeScale(scale, scale, 1.0) + layer.animate( from: previousScale as NSNumber, to: scale as NSNumber, keyPath: "transform.scale", diff --git a/submodules/Components/ReactionButtonListComponent/Sources/ReactionButtonListComponent.swift b/submodules/Components/ReactionButtonListComponent/Sources/ReactionButtonListComponent.swift index daa8aaf93f..a6b076a22d 100644 --- a/submodules/Components/ReactionButtonListComponent/Sources/ReactionButtonListComponent.swift +++ b/submodules/Components/ReactionButtonListComponent/Sources/ReactionButtonListComponent.swift @@ -746,7 +746,7 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceView { fileId: fileId, animationCache: arguments.animationCache, animationRenderer: arguments.animationRenderer, - placeholderColor: .gray, + placeholderColor: layout.spec.component.chosenOrder != nil ? UIColor(argb: layout.spec.component.colors.selectedForeground).withMultipliedAlpha(0.1) : UIColor(argb: layout.spec.component.colors.deselectedForeground).withMultipliedAlpha(0.1), animateIdle: animateIdle, reaction: layout.spec.component.reaction.value, transition: animation.transition diff --git a/submodules/ContactsPeerItem/BUILD b/submodules/ContactsPeerItem/BUILD index af0de6ee9a..a406bb700c 100644 --- a/submodules/ContactsPeerItem/BUILD +++ b/submodules/ContactsPeerItem/BUILD @@ -26,6 +26,10 @@ swift_library( "//submodules/ListSectionHeaderNode:ListSectionHeaderNode", "//submodules/ContextUI:ContextUI", "//submodules/PresentationDataUtils:PresentationDataUtils", + "//submodules/ComponentFlow", + "//submodules/TelegramUI/Components/AnimationCache", + "//submodules/TelegramUI/Components/MultiAnimationRenderer", + "//submodules/TelegramUI/Components/EmojiStatusComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift b/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift index 050eb5789e..426a909085 100644 --- a/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift +++ b/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift @@ -16,6 +16,10 @@ import PeerPresenceStatusManager import ItemListPeerItem import ContextUI import AccountContext +import ComponentFlow +import AnimationCache +import MultiAnimationRenderer +import EmojiStatusComponent public final class ContactItemHighlighting { public var chatLocation: ChatLocation? @@ -150,6 +154,8 @@ public class ContactsPeerItem: ItemListItem, ListViewItemWithHeader { let itemHighlighting: ContactItemHighlighting? let contextAction: ((ASDisplayNode, ContextGesture?, CGPoint?) -> Void)? let arrowAction: (() -> Void)? + let animationCache: AnimationCache? + let animationRenderer: MultiAnimationRenderer? public let selectable: Bool @@ -157,7 +163,35 @@ public class ContactsPeerItem: ItemListItem, ListViewItemWithHeader { public let header: ListViewItemHeader? - public init(presentationData: ItemListPresentationData, style: ItemListStyle = .plain, sectionId: ItemListSectionId = 0, sortOrder: PresentationPersonNameOrder, displayOrder: PresentationPersonNameOrder, context: AccountContext, peerMode: ContactsPeerItemPeerMode, peer: ContactsPeerItemPeer, status: ContactsPeerItemStatus, badge: ContactsPeerItemBadge? = nil, enabled: Bool, selection: ContactsPeerItemSelection, selectionPosition: ContactsPeerItemSelectionPosition = .right, editing: ContactsPeerItemEditing, options: [ItemListPeerItemRevealOption] = [], additionalActions: [ContactsPeerItemAction] = [], actionIcon: ContactsPeerItemActionIcon = .none, index: SortIndex?, header: ListViewItemHeader?, action: @escaping (ContactsPeerItemPeer) -> Void, disabledAction: ((ContactsPeerItemPeer) -> Void)? = nil, setPeerIdWithRevealedOptions: ((EnginePeer.Id?, EnginePeer.Id?) -> Void)? = nil, deletePeer: ((EnginePeer.Id) -> Void)? = nil, itemHighlighting: ContactItemHighlighting? = nil, contextAction: ((ASDisplayNode, ContextGesture?, CGPoint?) -> Void)? = nil, arrowAction: (() -> Void)? = nil) { + public init( + presentationData: ItemListPresentationData, + style: ItemListStyle = .plain, + sectionId: ItemListSectionId = 0, + sortOrder: PresentationPersonNameOrder, + displayOrder: PresentationPersonNameOrder, + context: AccountContext, + peerMode: ContactsPeerItemPeerMode, + peer: ContactsPeerItemPeer, + status: ContactsPeerItemStatus, + badge: ContactsPeerItemBadge? = nil, + enabled: Bool, + selection: ContactsPeerItemSelection, + selectionPosition: ContactsPeerItemSelectionPosition = .right, + editing: ContactsPeerItemEditing, + options: [ItemListPeerItemRevealOption] = [], + additionalActions: [ContactsPeerItemAction] = [], + actionIcon: ContactsPeerItemActionIcon = .none, + index: SortIndex?, + header: ListViewItemHeader?, + action: @escaping (ContactsPeerItemPeer) -> Void, + disabledAction: ((ContactsPeerItemPeer) -> Void)? = nil, + setPeerIdWithRevealedOptions: ((EnginePeer.Id?, EnginePeer.Id?) -> Void)? = nil, + deletePeer: ((EnginePeer.Id) -> Void)? = nil, + itemHighlighting: ContactItemHighlighting? = nil, + contextAction: ((ASDisplayNode, ContextGesture?, CGPoint?) -> Void)? = nil, arrowAction: (() -> Void)? = nil, + animationCache: AnimationCache? = nil, + animationRenderer: MultiAnimationRenderer? = nil + ) { self.presentationData = presentationData self.style = style self.sectionId = sectionId @@ -184,6 +218,8 @@ public class ContactsPeerItem: ItemListItem, ListViewItemWithHeader { self.selectable = enabled || disabledAction != nil self.contextAction = contextAction self.arrowAction = arrowAction + self.animationCache = animationCache + self.animationRenderer = animationRenderer if let index = index { var letter: String = "#" @@ -333,7 +369,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { private let avatarNode: AvatarNode private let titleNode: TextNode - private var credibilityIconNode: ASImageNode? + private var credibilityIconView: ComponentHostView? private let statusNode: TextNode private var badgeBackgroundNode: ASImageNode? private var badgeTextNode: TextNode? @@ -571,18 +607,20 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { let premiumConfiguration = PremiumConfiguration.with(appConfiguration: item.context.currentAppConfiguration.with { $0 }) - var currentCredibilityIconImage: UIImage? + var credibilityIcon: EmojiStatusComponent.Content? switch item.peer { case let .peer(peer, _): if let peer = peer, peer.id != item.context.account.peerId { if peer.isScam { - currentCredibilityIconImage = PresentationResourcesChatList.scamIcon(item.presentationData.theme, strings: item.presentationData.strings, type: .regular) + credibilityIcon = .scam(color: item.presentationData.theme.chat.message.incoming.scamColor) } else if peer.isFake { - currentCredibilityIconImage = PresentationResourcesChatList.fakeIcon(item.presentationData.theme, strings: item.presentationData.strings, type: .regular) + credibilityIcon = .fake(color: item.presentationData.theme.chat.message.incoming.scamColor) + } else if case let .user(user) = peer, let emojiStatus = user.emojiStatus { + credibilityIcon = .emojiStatus(status: emojiStatus, size: CGSize(width: 20.0, height: 20.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor) } else if peer.isVerified { - currentCredibilityIconImage = PresentationResourcesChatList.verifiedIcon(item.presentationData.theme) + credibilityIcon = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor) } else if peer.isPremium && !premiumConfiguration.isPremiumDisabled { - currentCredibilityIconImage = PresentationResourcesChatList.premiumIcon(item.presentationData.theme) + credibilityIcon = .premium(color: item.presentationData.theme.list.itemAccentColor) } } case .deviceContact: @@ -743,8 +781,14 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { } var additionalTitleInset: CGFloat = 0.0 - if let currentCredibilityIconImage = currentCredibilityIconImage { - additionalTitleInset += 3.0 + currentCredibilityIconImage.size.width + if let credibilityIcon = credibilityIcon { + additionalTitleInset += 3.0 + switch credibilityIcon { + case .scam, .fake: + additionalTitleInset += 30.0 + default: + additionalTitleInset += 16.0 + } } if let actionButtons = actionButtons { additionalTitleInset += 3.0 @@ -930,23 +974,35 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { strongSelf.statusNode.frame = statusFrame transition.animatePositionAdditive(node: strongSelf.statusNode, offset: CGPoint(x: previousStatusFrame.minX - statusFrame.minX, y: 0)) - if let currentCredibilityIconImage = currentCredibilityIconImage { - let iconNode: ASImageNode - if let current = strongSelf.credibilityIconNode { - iconNode = current + if let credibilityIcon = credibilityIcon, let animationCache = item.animationCache, let animationRenderer = item.animationRenderer { + let credibilityIconView: ComponentHostView + if let current = strongSelf.credibilityIconView { + credibilityIconView = current } else { - iconNode = ASImageNode() - iconNode.isLayerBacked = true - iconNode.displaysAsynchronously = false - iconNode.displayWithoutProcessing = true - strongSelf.offsetContainerNode.addSubnode(iconNode) - strongSelf.credibilityIconNode = iconNode + credibilityIconView = ComponentHostView() + strongSelf.offsetContainerNode.view.addSubview(credibilityIconView) + strongSelf.credibilityIconView = credibilityIconView } - iconNode.image = currentCredibilityIconImage - transition.updateFrame(node: iconNode, frame: CGRect(origin: CGPoint(x: titleFrame.maxX + 4.0, y: floorToScreenPixels(titleFrame.midY - currentCredibilityIconImage.size.height / 2.0) - UIScreenPixel), size: currentCredibilityIconImage.size)) - } else if let credibilityIconNode = strongSelf.credibilityIconNode { - strongSelf.credibilityIconNode = nil - credibilityIconNode.removeFromSupernode() + + let iconSize = credibilityIconView.update( + transition: .immediate, + component: AnyComponent(EmojiStatusComponent( + context: item.context, + animationCache: animationCache, + animationRenderer: animationRenderer, + content: credibilityIcon, + action: nil, + longTapAction: nil, + emojiFileUpdated: nil + )), + environment: {}, + containerSize: CGSize(width: 20.0, height: 20.0) + ) + + transition.updateFrame(view: credibilityIconView, frame: CGRect(origin: CGPoint(x: titleFrame.maxX + 4.0, y: floorToScreenPixels(titleFrame.midY - iconSize.height / 2.0)), size: iconSize)) + } else if let credibilityIconView = strongSelf.credibilityIconView { + strongSelf.credibilityIconView = nil + credibilityIconView.removeFromSuperview() } if let actionButtons = actionButtons { @@ -1146,10 +1202,10 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { self.statusNode.frame = statusFrame transition.animatePositionAdditive(node: self.statusNode, offset: CGPoint(x: previousStatusFrame.minX - statusFrame.minX, y: 0)) - if let credibilityIconNode = self.credibilityIconNode { - var iconFrame = credibilityIconNode.frame + if let credibilityIconView = self.credibilityIconView { + var iconFrame = credibilityIconView.frame iconFrame.origin.x = titleFrame.maxX + 4.0 - transition.updateFrame(node: credibilityIconNode, frame: iconFrame) + transition.updateFrame(view: credibilityIconView, frame: iconFrame) } if let badgeBackgroundNode = self.badgeBackgroundNode, let badgeTextNode = self.badgeTextNode { diff --git a/submodules/ReactionSelectionNode/Sources/ReactionContextBackgroundNode.swift b/submodules/ReactionSelectionNode/Sources/ReactionContextBackgroundNode.swift index b998f72ac3..ebbd3ca2c6 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionContextBackgroundNode.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionContextBackgroundNode.swift @@ -215,7 +215,7 @@ final class ReactionContextBackgroundNode: ASDisplayNode { } func animateInFromAnchorRect(size: CGSize, sourceBackgroundFrame: CGRect) { - let springDuration: Double = 0.3 + let springDuration: Double = 0.5 let springDamping: CGFloat = 104.0 let springDelay: Double = 0.05 let shadowInset: CGFloat = 15.0 diff --git a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift index 5f42662e14..6c2d858c07 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift @@ -114,7 +114,7 @@ private final class ExpandItemView: UIView { transition.updateCornerRadius(layer: self.tintView.layer, cornerRadius: size.width / 2.0) if let image = self.arrowView.image { - transition.updateFrame(view: self.arrowView, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - image.size.width) / 2.0), y: floorToScreenPixels(size.height - size.width + (size.width - image.size.height) / 2.0)), size: image.size)) + transition.updateFrame(view: self.arrowView, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - image.size.width) / 2.0), y: floorToScreenPixels(size.height - size.width + (size.width - image.size.height) / 2.0 + 1.0)), size: image.size)) } } } @@ -874,7 +874,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { } if let animateInFromAnchorRect = animateInFromAnchorRect { - let springDuration: Double = 0.3 + let springDuration: Double = 0.5 let springDamping: CGFloat = 104.0 let springDelay: Double = 0.05 @@ -902,11 +902,13 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { } emojiContent.inputInteractionHolder.inputInteraction = EmojiPagerContentComponent.InputInteraction( - performItemAction: { [weak self] groupId, item, sourceView, sourceRect, sourceLayer in + performItemAction: { [weak self] groupId, item, sourceView, sourceRect, sourceLayer, isLongPress in guard let strongSelf = self, let itemFile = item.itemFile else { return } + strongSelf.didTriggerExpandedReaction = isLongPress + var found = false if let groupId = groupId.base as? String, groupId == "recent" { for reactionItem in strongSelf.items { @@ -915,7 +917,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { found = true strongSelf.customReactionSource = (sourceView, sourceRect, sourceLayer, reactionItem) - strongSelf.reactionSelected?(reactionItem.updateMessageReaction, false) + strongSelf.reactionSelected?(reactionItem.updateMessageReaction, isLongPress) break } @@ -934,7 +936,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { isCustom: true ) strongSelf.customReactionSource = (sourceView, sourceRect, sourceLayer, reactionItem) - strongSelf.reactionSelected?(reactionItem.updateMessageReaction, false) + strongSelf.reactionSelected?(reactionItem.updateMessageReaction, isLongPress) } }, deleteBackwards: { @@ -1157,10 +1159,10 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { case .builtin: switchToInlineImmediately = false case .custom: - switchToInlineImmediately = true + switchToInlineImmediately = !self.didTriggerExpandedReaction } } else { - switchToInlineImmediately = true + switchToInlineImmediately = !self.didTriggerExpandedReaction } self.animationTargetView = targetView @@ -1192,7 +1194,11 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { var expandedSize: CGSize = selfTargetRect.size if self.didTriggerExpandedReaction { - expandedSize = CGSize(width: 120.0, height: 120.0) + if itemNode.item.listAnimation.isVideoEmoji || itemNode.item.listAnimation.isVideoSticker { + expandedSize = CGSize(width: 80.0, height: 80.0) + } else { + 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) @@ -1200,7 +1206,8 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { let effectFrame: CGRect let incomingMessage: Bool = expandedFrame.midX < self.bounds.width / 2.0 if self.didTriggerExpandedReaction { - effectFrame = expandedFrame.insetBy(dx: -expandedFrame.width * 0.5, dy: -expandedFrame.height * 0.5).offsetBy(dx: incomingMessage ? (expandedFrame.width - 50.0) : (-expandedFrame.width + 50.0), dy: 0.0) + 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) } else { effectFrame = expandedFrame.insetBy(dx: -expandedSize.width, dy: -expandedSize.height) } @@ -1265,7 +1272,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { cache: animationCache, renderer: animationRenderer, placeholderColor: UIColor(white: 0.0, alpha: 0.0), - pointSize: CGSize(width: 32.0, height: 32.0) + pointSize: CGSize(width: self.didTriggerExpandedReaction ? 64.0 : 32.0, height: self.didTriggerExpandedReaction ? 64.0 : 32.0) ) if let sublayers = animationLayer.sublayers { @@ -1484,6 +1491,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { if let expandItemView = self.expandItemView, expandItemView.bounds.contains(self.view.convert(point, to: self.expandItemView)) { self.currentContentHeight = 300.0 self.isExpanded = true + self.longPressRecognizer?.isEnabled = false self.isExpandedUpdated(.animated(duration: 0.4, curve: .spring)) } else if let reaction = self.reaction(at: point) { switch reaction { @@ -1504,6 +1512,8 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { } self.hapticFeedback?.tap() + self.longPressRecognizer?.isEnabled = false + self.animateFromExtensionDistance = self.extensionDistance self.extensionDistance = 0.0 self.visibleExtensionDistance = 0.0 diff --git a/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusSelectionComponent.swift b/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusSelectionComponent.swift index e4b0ee2797..7bf18914b6 100644 --- a/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusSelectionComponent.swift +++ b/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusSelectionComponent.swift @@ -235,7 +235,7 @@ public final class EmojiStatusSelectionController: ViewController { strongSelf.emojiContent = emojiContent emojiContent.inputInteractionHolder.inputInteraction = EmojiPagerContentComponent.InputInteraction( - performItemAction: { _, item, _, _, _ in + performItemAction: { _, item, _, _, _, _ in guard let strongSelf = self else { return } @@ -416,16 +416,14 @@ public final class EmojiStatusSelectionController: ViewController { transition.setFrame(layer: self.cloudShadowLayer1, frame: cloudFrame1) transition.setCornerRadius(layer: self.cloudLayer1, cornerRadius: cloudFrame1.width / 2.0) - //transition.setFrame(view: componentView, frame: componentFrame) transition.setFrame(view: componentView, frame: CGRect(origin: componentFrame.origin, size: CGSize(width: componentFrame.width, height: componentFrame.height))) if animateIn { - //self.allowsGroupOpacity = true self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1, completion: { [weak self] _ in self?.allowsGroupOpacity = false }) - let contentDuration: Double = 0.5 + let contentDuration: Double = 0.3 let contentDelay: Double = 0.14 let initialContentFrame = CGRect(origin: CGPoint(x: cloudFrame0.midX - 24.0, y: componentFrame.minY), size: CGSize(width: 24.0 * 2.0, height: 24.0 * 2.0)) @@ -439,16 +437,9 @@ public final class EmojiStatusSelectionController: ViewController { componentView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.04, delay: contentDelay) self.componentShadowLayer.animateAlpha(from: 0.0, to: 1.0, duration: 0.04, delay: contentDelay) - //componentView.layer.animateScale(from: 0.5, to: 1.0, duration: contentDuration, delay: contentDelay, timingFunction: kCAMediaTimingFunctionSpring) - //self.componentShadowLayer.animateScale(from: 0.5, to: 1.0, duration: contentDuration, delay: contentDelay, timingFunction: kCAMediaTimingFunctionSpring) - let initialComponentShadowPath = UIBezierPath(roundedRect: CGRect(origin: CGPoint(), size: initialContentFrame.size), cornerRadius: 24.0).cgPath self.componentShadowLayer.animate(from: initialComponentShadowPath, to: self.componentShadowLayer.shadowPath!, keyPath: "shadowPath", timingFunction: kCAMediaTimingFunctionSpring, duration: contentDuration, delay: contentDelay) - //componentView.layer.animateScale(from: (componentView.bounds.width - 10.0) / componentView.bounds.width, to: 1.0, duration: 0.4, delay: 0.1, timingFunction: kCAMediaTimingFunctionSpring) - //componentView.layer.animateSpring(from: 0.01 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.4, delay: contentDelay) - //self.componentShadowLayer.animateSpring(from: 0.01 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.4, delay: contentDelay) - self.cloudLayer0.animateScale(from: 0.01, to: 1.0, duration: 0.4, delay: 0.05, timingFunction: kCAMediaTimingFunctionSpring) self.cloudShadowLayer0.animateScale(from: 0.01, to: 1.0, duration: 0.4, delay: 0.05, timingFunction: kCAMediaTimingFunctionSpring) diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift index 23bf71082f..f07a5d5ebb 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift @@ -1543,7 +1543,7 @@ public final class EmojiPagerContentComponent: Component { } public final class InputInteraction { - public let performItemAction: (AnyHashable, Item, UIView, CGRect, CALayer) -> Void + public let performItemAction: (AnyHashable, Item, UIView, CGRect, CALayer, Bool) -> Void public let deleteBackwards: () -> Void public let openStickerSettings: () -> Void public let openFeatured: () -> Void @@ -1560,7 +1560,7 @@ public final class EmojiPagerContentComponent: Component { public let externalBackground: ExternalBackground? public init( - performItemAction: @escaping (AnyHashable, Item, UIView, CGRect, CALayer) -> Void, + performItemAction: @escaping (AnyHashable, Item, UIView, CGRect, CALayer, Bool) -> Void, deleteBackwards: @escaping () -> Void, openStickerSettings: @escaping () -> Void, openFeatured: @escaping () -> Void, @@ -1777,6 +1777,7 @@ public final class EmojiPagerContentComponent: Component { public let itemGroups: [ItemGroup] public let itemLayoutType: ItemLayoutType public let warpContentsOnEdges: Bool + public let enableLongPress: Bool public init( id: AnyHashable, @@ -1787,7 +1788,8 @@ public final class EmojiPagerContentComponent: Component { inputInteractionHolder: InputInteractionHolder, itemGroups: [ItemGroup], itemLayoutType: ItemLayoutType, - warpContentsOnEdges: Bool + warpContentsOnEdges: Bool, + enableLongPress: Bool ) { self.id = id self.context = context @@ -1798,6 +1800,7 @@ public final class EmojiPagerContentComponent: Component { self.itemGroups = itemGroups self.itemLayoutType = itemLayoutType self.warpContentsOnEdges = warpContentsOnEdges + self.enableLongPress = enableLongPress } public static func ==(lhs: EmojiPagerContentComponent, rhs: EmojiPagerContentComponent) -> Bool { @@ -1831,6 +1834,9 @@ public final class EmojiPagerContentComponent: Component { if lhs.warpContentsOnEdges != rhs.warpContentsOnEdges { return false } + if lhs.enableLongPress != rhs.enableLongPress { + return false + } return true } @@ -2556,6 +2562,8 @@ public final class EmojiPagerContentComponent: Component { private var activeItemUpdated: ActionSlot<(AnyHashable, AnyHashable?, Transition)>? private var itemLayout: ItemLayout? + private var longTapRecognizer: UILongPressGestureRecognizer? + override init(frame: CGRect) { self.backgroundView = BlurredBackgroundView(color: nil) @@ -2607,6 +2615,12 @@ public final class EmojiPagerContentComponent: Component { self.scrollView.addSubview(self.placeholdersContainerView) self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))) + + let longTapRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(self.longPressGesture(_:))) + longTapRecognizer.minimumPressDuration = 0.2 + self.longTapRecognizer = longTapRecognizer + self.addGestureRecognizer(longTapRecognizer) + longTapRecognizer.isEnabled = false } required init?(coder: NSCoder) { @@ -2665,7 +2679,7 @@ public final class EmojiPagerContentComponent: Component { let distance = sqrt(distanceVector.x * distanceVector.x + distanceVector.y * distanceVector.y) let distanceNorm = min(1.0, max(0.0, distance / self.bounds.width)) - let delay = 0.05 + (distanceNorm) * 0.4 + let delay = 0.05 + (distanceNorm) * 0.3 itemLayer.animateScale(from: 0.01, to: 1.0, duration: 0.18, delay: delay, timingFunction: kCAMediaTimingFunctionSpring) } } @@ -2692,7 +2706,7 @@ public final class EmojiPagerContentComponent: Component { let clippedDistance = max(0.0, min(distance, maxDistance)) let distanceNorm = clippedDistance / maxDistance - let delay = listViewAnimationCurveSystem(distanceNorm) * 0.16 + let delay = listViewAnimationCurveSystem(distanceNorm) * 0.1 itemLayer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, delay: delay) itemLayer.animateSpring(from: 0.01 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.6, delay: delay) @@ -3196,7 +3210,7 @@ public final class EmojiPagerContentComponent: Component { foundExactItem = true foundItem = true if !itemLayer.displayPlaceholder { - component.inputInteractionHolder.inputInteraction?.performItemAction(itemKey.groupId, item, self, self.scrollView.convert(itemLayer.frame, to: self), itemLayer) + component.inputInteractionHolder.inputInteraction?.performItemAction(itemKey.groupId, item, self, self.scrollView.convert(itemLayer.frame, to: self), itemLayer, false) } } @@ -3204,7 +3218,7 @@ public final class EmojiPagerContentComponent: Component { if let (item, itemKey) = self.item(atPoint: recognizer.location(in: self), extendedHitRange: true), let itemLayer = self.visibleItemLayers[itemKey] { foundItem = true if !itemLayer.displayPlaceholder { - component.inputInteractionHolder.inputInteraction?.performItemAction(itemKey.groupId, item, self, self.scrollView.convert(itemLayer.frame, to: self), itemLayer) + component.inputInteractionHolder.inputInteraction?.performItemAction(itemKey.groupId, item, self, self.scrollView.convert(itemLayer.frame, to: self), itemLayer, false) } } } @@ -3213,6 +3227,87 @@ public final class EmojiPagerContentComponent: Component { } } + private let longPressDuration: Double = 0.5 + private var longPressItem: EmojiPagerContentComponent.View.ItemLayer.Key? + private var hapticFeedback: HapticFeedback? + private var continuousHaptic: AnyObject? + private var longPressTimer: SwiftSignalKit.Timer? + + @objc private func longPressGesture(_ recognizer: UILongPressGestureRecognizer) { + switch recognizer.state { + case .began: + let point = recognizer.location(in: self) + + guard let item = self.item(atPoint: point), let itemLayer = self.visibleItemLayers[item.1] else { + return + } + switch item.0.content { + case .animation: + break + default: + return + } + self.longPressItem = item.1 + + if #available(iOS 13.0, *) { + self.continuousHaptic = try? ContinuousHaptic(duration: longPressDuration) + } + + if self.hapticFeedback == nil { + self.hapticFeedback = HapticFeedback() + } + + let transition = Transition(animation: .curve(duration: longPressDuration, curve: .easeInOut)) + transition.setScale(layer: itemLayer, scale: 1.3) + + self.longPressTimer?.invalidate() + self.longPressTimer = SwiftSignalKit.Timer(timeout: longPressDuration, repeat: false, completion: { [weak self] in + guard let strongSelf = self else { + return + } + strongSelf.longTapRecognizer?.state = .ended + }, queue: .mainQueue()) + self.longPressTimer?.start() + case .changed: + let point = recognizer.location(in: self) + + if let longPressItem = self.longPressItem, let item = self.item(atPoint: point), longPressItem == item.1 { + } else { + self.longTapRecognizer?.state = .cancelled + } + case .cancelled: + self.longPressTimer?.invalidate() + self.continuousHaptic = nil + + if let itemKey = self.longPressItem { + self.longPressItem = nil + + if let itemLayer = self.visibleItemLayers[itemKey] { + let transition = Transition(animation: .curve(duration: 0.3, curve: .spring)) + transition.setScale(layer: itemLayer, scale: 1.0) + } + } + case .ended: + self.longPressTimer?.invalidate() + self.continuousHaptic = nil + + if let itemKey = self.longPressItem { + self.longPressItem = nil + + if let component = self.component, let itemLayer = self.visibleItemLayers[itemKey] { + component.inputInteractionHolder.inputInteraction?.performItemAction(itemKey.groupId, itemLayer.item, self, self.scrollView.convert(itemLayer.frame, to: self), itemLayer, true) + } else { + if let itemLayer = self.visibleItemLayers[itemKey] { + let transition = Transition(animation: .curve(duration: 0.3, curve: .spring)) + transition.setScale(layer: itemLayer, scale: 1.0) + } + } + } + default: + break + } + } + private func item(atPoint point: CGPoint, extendedHitRange: Bool = false) -> (Item, ItemLayer.Key)? { let localPoint = self.convert(point, to: self.scrollView) @@ -3411,7 +3506,7 @@ public final class EmojiPagerContentComponent: Component { guard let strongSelf = self, let component = strongSelf.component else { return } - component.inputInteractionHolder.inputInteraction?.performItemAction(groupId, item, view, rect, layer) + component.inputInteractionHolder.inputInteraction?.performItemAction(groupId, item, view, rect, layer, false) } ) self.visibleGroupHeaders[itemGroup.groupId] = groupHeaderView @@ -4133,6 +4228,10 @@ public final class EmojiPagerContentComponent: Component { self.updateIsWarpEnabled(isEnabled: component.warpContentsOnEdges) + if let longTapRecognizer = self.longTapRecognizer { + longTapRecognizer.isEnabled = component.enableLongPress + } + if let shimmerHostView = self.shimmerHostView { transition.setFrame(view: shimmerHostView, frame: CGRect(origin: CGPoint(), size: availableSize)) } @@ -4892,7 +4991,8 @@ public final class EmojiPagerContentComponent: Component { ) }, itemLayoutType: .compact, - warpContentsOnEdges: isReactionSelection || isStatusSelection + warpContentsOnEdges: isReactionSelection || isStatusSelection, + enableLongPress: isReactionSelection ) } return emojiItems diff --git a/submodules/TelegramUI/Sources/ChatEntityKeyboardInputNode.swift b/submodules/TelegramUI/Sources/ChatEntityKeyboardInputNode.swift index ee04939bee..988d9d0867 100644 --- a/submodules/TelegramUI/Sources/ChatEntityKeyboardInputNode.swift +++ b/submodules/TelegramUI/Sources/ChatEntityKeyboardInputNode.swift @@ -505,7 +505,8 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { ) }, itemLayoutType: .detailed, - warpContentsOnEdges: false + warpContentsOnEdges: false, + enableLongPress: false ) } @@ -902,7 +903,7 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { var premiumToastCounter = 0 self.emojiInputInteraction = EmojiPagerContentComponent.InputInteraction( - performItemAction: { [weak self, weak interfaceInteraction, weak controllerInteraction] _, item, _, _, _ in + performItemAction: { [weak self, weak interfaceInteraction, weak controllerInteraction] _, item, _, _, _, _ in let _ = (ChatEntityKeyboardInputNode.hasPremium(context: context, chatPeerId: chatPeerId, premiumIfSavedMessages: true) |> take(1) |> deliverOnMainQueue).start(next: { hasPremium in guard let strongSelf = self, let controllerInteraction = controllerInteraction, let interfaceInteraction = interfaceInteraction else { return @@ -1099,7 +1100,7 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { ) } self.stickerInputInteraction = EmojiPagerContentComponent.InputInteraction( - performItemAction: { [weak controllerInteraction, weak interfaceInteraction] groupId, item, view, rect, layer in + performItemAction: { [weak controllerInteraction, weak interfaceInteraction] groupId, item, view, rect, layer, _ in let _ = (ChatEntityKeyboardInputNode.hasPremium(context: context, chatPeerId: chatPeerId, premiumIfSavedMessages: false) |> take(1) |> deliverOnMainQueue).start(next: { hasPremium in guard let controllerInteraction = controllerInteraction, let interfaceInteraction = interfaceInteraction else { return @@ -1918,7 +1919,7 @@ final class EntityInputView: UIView, AttachmentTextInputPanelInputView, UIInputV self.clipsToBounds = true let inputInteraction = EmojiPagerContentComponent.InputInteraction( - performItemAction: { [weak self] _, item, _, _, _ in + performItemAction: { [weak self] _, item, _, _, _, _ in let _ = (ChatEntityKeyboardInputNode.hasPremium(context: context, chatPeerId: nil, premiumIfSavedMessages: false) |> take(1) |> deliverOnMainQueue).start(next: { hasPremium in guard let strongSelf = self else { return diff --git a/submodules/TelegramUI/Sources/ChatMessageDateAndStatusNode.swift b/submodules/TelegramUI/Sources/ChatMessageDateAndStatusNode.swift index d9fd059380..a9323b462a 100644 --- a/submodules/TelegramUI/Sources/ChatMessageDateAndStatusNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageDateAndStatusNode.swift @@ -143,6 +143,22 @@ private final class StatusReactionNode: ASDisplayNode { animateIdle = false } + let placeholderColor: UIColor + switch type { + case .BubbleIncoming: + placeholderColor = theme.chat.message.incoming.mediaPlaceholderColor + case .BubbleOutgoing: + placeholderColor = theme.chat.message.incoming.mediaPlaceholderColor + case .ImageIncoming: + placeholderColor = UIColor(white: 1.0, alpha: 0.1) + case .ImageOutgoing: + placeholderColor = UIColor(white: 1.0, alpha: 0.1) + case .FreeIncoming: + placeholderColor = UIColor(white: 0.0, alpha: 0.1) + case .FreeOutgoing: + placeholderColor = UIColor(white: 0.0, alpha: 0.1) + } + self.iconView.update( size: boundingImageSize, context: context, @@ -150,7 +166,7 @@ private final class StatusReactionNode: ASDisplayNode { fileId: fileId, animationCache: animationCache, animationRenderer: animationRenderer, - placeholderColor: .gray, + placeholderColor: placeholderColor, animateIdle: animateIdle, reaction: value, transition: .immediate