mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Reaction improvements
This commit is contained in:
parent
53690ab199
commit
3b76a3cbfa
@ -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,17 +195,34 @@ 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: {
|
||||
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
|
||||
}),
|
||||
action: { _ in
|
||||
if let chatPeer = peer.peer.peers[peer.peer.peerId] {
|
||||
peerSelected(EnginePeer(chatPeer))
|
||||
}
|
||||
}, disabledAction: { _ in
|
||||
},
|
||||
disabledAction: { _ in
|
||||
if let chatPeer = peer.peer.peers[peer.peer.peerId] {
|
||||
disabledPeerSelected(EnginePeer(chatPeer))
|
||||
}
|
||||
}, deletePeer: deletePeer, contextAction: peerContextAction.flatMap { peerContextAction in
|
||||
},
|
||||
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)
|
||||
@ -213,7 +230,10 @@ private enum ChatListRecentEntry: Comparable, Identifiable {
|
||||
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)
|
||||
}
|
||||
}))
|
||||
|
@ -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;
|
||||
|
@ -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):
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
|
@ -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<Empty>?
|
||||
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<Empty>
|
||||
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<Empty>()
|
||||
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 {
|
||||
|
@ -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
|
||||
|
@ -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,15 +1194,20 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
|
||||
var expandedSize: CGSize = selfTargetRect.size
|
||||
if self.didTriggerExpandedReaction {
|
||||
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)
|
||||
|
||||
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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user