mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-07 09:20:08 +00:00
UI improvements
This commit is contained in:
parent
19967dd331
commit
00ec1a2919
@ -853,8 +853,10 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
self.emojiStatusSelectionController?.dismiss()
|
self.emojiStatusSelectionController?.dismiss()
|
||||||
var selectedItems = Set<MediaId>()
|
var selectedItems = Set<MediaId>()
|
||||||
var topStatusTitle = self.presentationData.strings.PeerStatusSetup_NoTimerTitle
|
var topStatusTitle = self.presentationData.strings.PeerStatusSetup_NoTimerTitle
|
||||||
|
var currentSelection: Int64?
|
||||||
if let peerStatus = self.titleView.title.peerStatus, case let .emoji(emojiStatus) = peerStatus {
|
if let peerStatus = self.titleView.title.peerStatus, case let .emoji(emojiStatus) = peerStatus {
|
||||||
selectedItems.insert(MediaId(namespace: Namespaces.Media.CloudFile, id: emojiStatus.fileId))
|
selectedItems.insert(MediaId(namespace: Namespaces.Media.CloudFile, id: emojiStatus.fileId))
|
||||||
|
currentSelection = emojiStatus.fileId
|
||||||
|
|
||||||
if let timestamp = emojiStatus.expirationDate {
|
if let timestamp = emojiStatus.expirationDate {
|
||||||
topStatusTitle = peerStatusExpirationString(statusTimestamp: timestamp, relativeTo: Int32(Date().timeIntervalSince1970), strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat)
|
topStatusTitle = peerStatusExpirationString(statusTimestamp: timestamp, relativeTo: Int32(Date().timeIntervalSince1970), strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat)
|
||||||
@ -878,6 +880,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
selectedItems: selectedItems,
|
selectedItems: selectedItems,
|
||||||
topStatusTitle: topStatusTitle
|
topStatusTitle: topStatusTitle
|
||||||
),
|
),
|
||||||
|
currentSelection: currentSelection,
|
||||||
destinationItemView: { [weak sourceView] in
|
destinationItemView: { [weak sourceView] in
|
||||||
return sourceView
|
return sourceView
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,6 +25,7 @@ swift_library(
|
|||||||
"//submodules/TelegramUI/Components/AnimationCache:AnimationCache",
|
"//submodules/TelegramUI/Components/AnimationCache:AnimationCache",
|
||||||
"//submodules/TelegramUI/Components/MultiAnimationRenderer:MultiAnimationRenderer",
|
"//submodules/TelegramUI/Components/MultiAnimationRenderer:MultiAnimationRenderer",
|
||||||
"//submodules/TelegramUI/Components/EmojiTextAttachmentView:EmojiTextAttachmentView",
|
"//submodules/TelegramUI/Components/EmojiTextAttachmentView:EmojiTextAttachmentView",
|
||||||
|
"//submodules/TelegramUI/Components/EmojiStatusComponent:EmojiStatusComponent",
|
||||||
"//submodules/TextFormat:TextFormat",
|
"//submodules/TextFormat:TextFormat",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import AnimationCache
|
|||||||
import MultiAnimationRenderer
|
import MultiAnimationRenderer
|
||||||
import EmojiTextAttachmentView
|
import EmojiTextAttachmentView
|
||||||
import TextFormat
|
import TextFormat
|
||||||
|
import EmojiStatusComponent
|
||||||
|
|
||||||
private let avatarFont = avatarPlaceholderFont(size: 16.0)
|
private let avatarFont = avatarPlaceholderFont(size: 16.0)
|
||||||
|
|
||||||
@ -357,7 +358,7 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
|||||||
let highlightBackgroundNode: ASDisplayNode
|
let highlightBackgroundNode: ASDisplayNode
|
||||||
let avatarNode: AvatarNode
|
let avatarNode: AvatarNode
|
||||||
let titleLabelNode: ImmediateTextNode
|
let titleLabelNode: ImmediateTextNode
|
||||||
var credibilityIconNode: ASImageNode?
|
var credibilityIconView: ComponentView<Empty>?
|
||||||
let separatorNode: ASDisplayNode
|
let separatorNode: ASDisplayNode
|
||||||
|
|
||||||
private var reactionLayer: InlineStickerItemLayer?
|
private var reactionLayer: InlineStickerItemLayer?
|
||||||
@ -519,20 +520,46 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
|||||||
}
|
}
|
||||||
|
|
||||||
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: self.context.currentAppConfiguration.with { $0 })
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: self.context.currentAppConfiguration.with { $0 })
|
||||||
var currentCredibilityIconImage: UIImage?
|
var currentCredibilityIcon: EmojiStatusComponent.Content?
|
||||||
if item.peer.isScam {
|
if item.peer.isScam {
|
||||||
currentCredibilityIconImage = PresentationResourcesChatList.scamIcon(presentationData.theme, strings: presentationData.strings, type: .regular)
|
currentCredibilityIcon = .text(color: presentationData.theme.chat.message.incoming.scamColor, string: presentationData.strings.Message_ScamAccount.uppercased())
|
||||||
} else if item.peer.isFake {
|
} else if item.peer.isFake {
|
||||||
currentCredibilityIconImage = PresentationResourcesChatList.fakeIcon(presentationData.theme, strings: presentationData.strings, type: .regular)
|
currentCredibilityIcon = .text(color: presentationData.theme.chat.message.incoming.scamColor, string: presentationData.strings.Message_FakeAccount.uppercased())
|
||||||
|
} else if case let .user(user) = item.peer, let emojiStatus = user.emojiStatus {
|
||||||
|
currentCredibilityIcon = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 32.0, height: 32.0), placeholderColor: UIColor(white: 0.0, alpha: 0.1), themeColor: presentationData.theme.list.itemAccentColor, loopMode: .count(2))
|
||||||
} else if item.peer.isVerified {
|
} else if item.peer.isVerified {
|
||||||
currentCredibilityIconImage = PresentationResourcesChatList.verifiedIcon(presentationData.theme)
|
currentCredibilityIcon = .verified(fillColor: presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact)
|
||||||
} else if item.peer.isPremium && !premiumConfiguration.isPremiumDisabled {
|
} else if item.peer.isPremium && !premiumConfiguration.isPremiumDisabled {
|
||||||
currentCredibilityIconImage = PresentationResourcesChatList.premiumIcon(presentationData.theme)
|
currentCredibilityIcon = .premium(color: presentationData.theme.list.itemCheckColors.fillColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
var credibilityIconSize: CGSize?
|
||||||
|
if let currentCredibilityIcon = currentCredibilityIcon {
|
||||||
|
let credibilityIconView: ComponentView<Empty>
|
||||||
|
if let current = self.credibilityIconView {
|
||||||
|
credibilityIconView = current
|
||||||
|
} else {
|
||||||
|
credibilityIconView = ComponentView<Empty>()
|
||||||
|
self.credibilityIconView = credibilityIconView
|
||||||
|
}
|
||||||
|
credibilityIconSize = credibilityIconView.update(
|
||||||
|
transition: .immediate,
|
||||||
|
component: AnyComponent(EmojiStatusComponent(
|
||||||
|
context: self.context,
|
||||||
|
animationCache: self.context.animationCache,
|
||||||
|
animationRenderer: self.context.animationRenderer,
|
||||||
|
content: currentCredibilityIcon,
|
||||||
|
isVisibleForAnimations: true,
|
||||||
|
action: nil
|
||||||
|
)),
|
||||||
|
environment: {},
|
||||||
|
containerSize: CGSize(width: 24.0, height: 24.0)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
var additionalTitleInset: CGFloat = 0.0
|
var additionalTitleInset: CGFloat = 0.0
|
||||||
if let currentCredibilityIconImage = currentCredibilityIconImage {
|
if let credibilityIconSize = credibilityIconSize {
|
||||||
additionalTitleInset += 3.0 + currentCredibilityIconImage.size.width
|
additionalTitleInset += 3.0 + credibilityIconSize.width
|
||||||
}
|
}
|
||||||
|
|
||||||
self.highlightBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor
|
self.highlightBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor
|
||||||
@ -552,23 +579,16 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
|||||||
let titleFrame = CGRect(origin: CGPoint(x: avatarInset + avatarSize + avatarSpacing, y: floor((size.height - titleSize.height) / 2.0)), size: titleSize)
|
let titleFrame = CGRect(origin: CGPoint(x: avatarInset + avatarSize + avatarSpacing, y: floor((size.height - titleSize.height) / 2.0)), size: titleSize)
|
||||||
self.titleLabelNode.frame = titleFrame
|
self.titleLabelNode.frame = titleFrame
|
||||||
|
|
||||||
if let currentCredibilityIconImage = currentCredibilityIconImage {
|
if let credibilityIconView = self.credibilityIconView, let credibilityIconSize = credibilityIconSize {
|
||||||
let iconNode: ASImageNode
|
if let credibilityIconComponentView = credibilityIconView.view {
|
||||||
if let current = self.credibilityIconNode {
|
if credibilityIconComponentView.superview == nil {
|
||||||
iconNode = current
|
self.view.addSubview(credibilityIconComponentView)
|
||||||
} else {
|
}
|
||||||
iconNode = ASImageNode()
|
credibilityIconComponentView.frame = CGRect(origin: CGPoint(x: titleFrame.maxX + 4.0, y: floorToScreenPixels(titleFrame.midY - credibilityIconSize.height / 2.0) + 1.0 - UIScreenPixel), size: credibilityIconSize)
|
||||||
iconNode.isLayerBacked = true
|
|
||||||
iconNode.displaysAsynchronously = false
|
|
||||||
iconNode.displayWithoutProcessing = true
|
|
||||||
self.addSubnode(iconNode)
|
|
||||||
self.credibilityIconNode = iconNode
|
|
||||||
}
|
}
|
||||||
iconNode.image = currentCredibilityIconImage
|
} else if let credibilityIconView = self.credibilityIconView {
|
||||||
iconNode.frame = CGRect(origin: CGPoint(x: titleFrame.maxX + 4.0, y: floorToScreenPixels(titleFrame.midY - currentCredibilityIconImage.size.height / 2.0) + 1.0 - UIScreenPixel), size: currentCredibilityIconImage.size)
|
self.credibilityIconView = nil
|
||||||
} else if let credibilityIconNode = self.credibilityIconNode {
|
credibilityIconView.view?.removeFromSuperview()
|
||||||
self.credibilityIconNode = nil
|
|
||||||
credibilityIconNode.removeFromSupernode()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let reactionSize = CGSize(width: 22.0, height: 22.0)
|
let reactionSize = CGSize(width: 22.0, height: 22.0)
|
||||||
|
|||||||
@ -232,8 +232,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||||||
private var customPosition: CGPoint?
|
private var customPosition: CGPoint?
|
||||||
private let contentContainerNode: ContextContentContainerNode
|
private let contentContainerNode: ContextContentContainerNode
|
||||||
private var actionsContainerNode: ContextActionsContainerNode
|
private var actionsContainerNode: ContextActionsContainerNode
|
||||||
private var reactionContextNode: ReactionContextNode?
|
|
||||||
private var reactionContextNodeIsAnimatingOut = false
|
|
||||||
|
|
||||||
private var didCompleteAnimationIn = false
|
private var didCompleteAnimationIn = false
|
||||||
private var initialContinueGesturePoint: CGPoint?
|
private var initialContinueGesturePoint: CGPoint?
|
||||||
@ -401,15 +399,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||||||
strongSelf.hapticFeedback.tap()
|
strongSelf.hapticFeedback.tap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let reactionContextNode = strongSelf.reactionContextNode {
|
|
||||||
let reactionPoint = strongSelf.view.convert(localPoint, to: reactionContextNode.view)
|
|
||||||
let highlightedReaction = reactionContextNode.reaction(at: reactionPoint)?.reaction
|
|
||||||
if strongSelf.highlightedReaction?.rawValue != highlightedReaction?.rawValue {
|
|
||||||
strongSelf.highlightedReaction = highlightedReaction
|
|
||||||
strongSelf.hapticFeedback.tap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -428,9 +417,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||||||
strongSelf.highlightedActionNode = nil
|
strongSelf.highlightedActionNode = nil
|
||||||
highlightedActionNode.performAction()
|
highlightedActionNode.performAction()
|
||||||
}
|
}
|
||||||
if let highlightedReaction = strongSelf.highlightedReaction {
|
|
||||||
strongSelf.reactionContextNode?.performReactionSelection(reaction: highlightedReaction, isLarge: false)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if let highlightedActionNode = strongSelf.highlightedActionNode {
|
if let highlightedActionNode = strongSelf.highlightedActionNode {
|
||||||
strongSelf.highlightedActionNode = nil
|
strongSelf.highlightedActionNode = nil
|
||||||
@ -479,15 +465,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||||||
strongSelf.hapticFeedback.tap()
|
strongSelf.hapticFeedback.tap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let reactionContextNode = strongSelf.reactionContextNode {
|
|
||||||
let reactionPoint = strongSelf.view.convert(localPoint, to: reactionContextNode.view)
|
|
||||||
let highlightedReaction = reactionContextNode.reaction(at: reactionPoint)?.reaction
|
|
||||||
if strongSelf.highlightedReaction?.rawValue != highlightedReaction?.rawValue {
|
|
||||||
strongSelf.highlightedReaction = highlightedReaction
|
|
||||||
strongSelf.hapticFeedback.tap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -506,10 +483,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||||||
strongSelf.highlightedActionNode = nil
|
strongSelf.highlightedActionNode = nil
|
||||||
highlightedActionNode.performAction()
|
highlightedActionNode.performAction()
|
||||||
}
|
}
|
||||||
|
|
||||||
if let highlightedReaction = strongSelf.highlightedReaction {
|
|
||||||
strongSelf.reactionContextNode?.performReactionSelection(reaction: highlightedReaction, isLarge: false)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if let highlightedActionNode = strongSelf.highlightedActionNode {
|
if let highlightedActionNode = strongSelf.highlightedActionNode {
|
||||||
strongSelf.highlightedActionNode = nil
|
strongSelf.highlightedActionNode = nil
|
||||||
@ -593,15 +566,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||||||
actionNode.setIsHighlighted(true)
|
actionNode.setIsHighlighted(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let reactionContextNode = self.reactionContextNode {
|
|
||||||
let reactionPoint = self.view.convert(localPoint, to: reactionContextNode.view)
|
|
||||||
let highlightedReaction = reactionContextNode.reaction(at: reactionPoint)?.reaction
|
|
||||||
if self.highlightedReaction?.rawValue != highlightedReaction?.rawValue {
|
|
||||||
self.highlightedReaction = highlightedReaction
|
|
||||||
self.hapticFeedback.tap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case .ended, .cancelled:
|
case .ended, .cancelled:
|
||||||
if let presentationNode = self.presentationNode {
|
if let presentationNode = self.presentationNode {
|
||||||
@ -611,10 +575,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||||||
self.highlightedActionNode = nil
|
self.highlightedActionNode = nil
|
||||||
highlightedActionNode.setIsHighlighted(false)
|
highlightedActionNode.setIsHighlighted(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let _ = self.reactionContextNode {
|
|
||||||
self.highlightedReaction = nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
@ -949,10 +909,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||||||
localContentSourceFrame = localSourceFrame
|
localContentSourceFrame = localSourceFrame
|
||||||
}
|
}
|
||||||
|
|
||||||
if let reactionContextNode = self.reactionContextNode {
|
|
||||||
reactionContextNode.animateIn(from: CGRect(origin: CGPoint(x: originalProjectedContentViewFrame.1.minX, y: originalProjectedContentViewFrame.1.minY), size: contentParentNode.contentRect.size))
|
|
||||||
}
|
|
||||||
|
|
||||||
self.actionsContainerNode.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: localSourceFrame.center.x - self.actionsContainerNode.position.x, y: localSourceFrame.center.y - self.actionsContainerNode.position.y + actionsOffset)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: actionsDuration, initialVelocity: 0.0, damping: springDamping, additive: true)
|
self.actionsContainerNode.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: localSourceFrame.center.x - self.actionsContainerNode.position.x, y: localSourceFrame.center.y - self.actionsContainerNode.position.y + actionsOffset)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: actionsDuration, initialVelocity: 0.0, damping: springDamping, additive: true)
|
||||||
let contentContainerOffset = CGPoint(x: localContentSourceFrame.center.x - self.contentContainerNode.frame.center.x - contentParentNode.contentRect.minX, y: localContentSourceFrame.center.y - self.contentContainerNode.frame.center.y - contentParentNode.contentRect.minY)
|
let contentContainerOffset = CGPoint(x: localContentSourceFrame.center.x - self.contentContainerNode.frame.center.x - contentParentNode.contentRect.minX, y: localContentSourceFrame.center.y - self.contentContainerNode.frame.center.y - contentParentNode.contentRect.minY)
|
||||||
self.contentContainerNode.layer.animateSpring(from: NSValue(cgPoint: contentContainerOffset), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: contentDuration, initialVelocity: 0.0, damping: springDamping, additive: true, completion: { [weak self] _ in
|
self.contentContainerNode.layer.animateSpring(from: NSValue(cgPoint: contentContainerOffset), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: contentDuration, initialVelocity: 0.0, damping: springDamping, additive: true, completion: { [weak self] _ in
|
||||||
@ -1286,10 +1242,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||||||
contentParentNode.updateAbsoluteRect?(self.contentContainerNode.frame.offsetBy(dx: 0.0, dy: -self.scrollNode.view.contentOffset.y + contentContainerOffset.y), self.bounds.size)
|
contentParentNode.updateAbsoluteRect?(self.contentContainerNode.frame.offsetBy(dx: 0.0, dy: -self.scrollNode.view.contentOffset.y + contentContainerOffset.y), self.bounds.size)
|
||||||
contentParentNode.applyAbsoluteOffset?(CGPoint(x: 0.0, y: -contentContainerOffset.y), transitionCurve, transitionDuration)
|
contentParentNode.applyAbsoluteOffset?(CGPoint(x: 0.0, y: -contentContainerOffset.y), transitionCurve, transitionDuration)
|
||||||
|
|
||||||
if let reactionContextNode = self.reactionContextNode {
|
|
||||||
reactionContextNode.animateOut(to: CGRect(origin: CGPoint(x: originalProjectedContentViewFrame.1.minX, y: originalProjectedContentViewFrame.1.minY), size: contentParentNode.contentRect.size), animatingOutToReaction: self.reactionContextNodeIsAnimatingOut)
|
|
||||||
}
|
|
||||||
|
|
||||||
contentParentNode.willUpdateIsExtractedToContextPreview?(false, .animated(duration: 0.2, curve: .easeInOut))
|
contentParentNode.willUpdateIsExtractedToContextPreview?(false, .animated(duration: 0.2, curve: .easeInOut))
|
||||||
} else {
|
} else {
|
||||||
if let snapshotView = contentParentNode.contentNode.view.snapshotContentTree(keepTransform: true) {
|
if let snapshotView = contentParentNode.contentNode.view.snapshotContentTree(keepTransform: true) {
|
||||||
@ -1308,10 +1260,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||||||
|
|
||||||
contentParentNode.contentNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
contentParentNode.contentNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||||
contentParentNode.willUpdateIsExtractedToContextPreview?(false, .animated(duration: 0.2, curve: .easeInOut))
|
contentParentNode.willUpdateIsExtractedToContextPreview?(false, .animated(duration: 0.2, curve: .easeInOut))
|
||||||
|
|
||||||
if let reactionContextNode = self.reactionContextNode {
|
|
||||||
reactionContextNode.animateOut(to: nil, animatingOutToReaction: self.reactionContextNodeIsAnimatingOut)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case let .controller(source):
|
case let .controller(source):
|
||||||
guard let maybeContentNode = self.contentContainerNode.contentNode, case let .controller(controller) = maybeContentNode else {
|
guard let maybeContentNode = self.contentContainerNode.contentNode, case let .controller(controller) = maybeContentNode else {
|
||||||
@ -1449,10 +1397,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||||||
completedContentNode = true
|
completedContentNode = true
|
||||||
intermediateCompletion()
|
intermediateCompletion()
|
||||||
})
|
})
|
||||||
|
|
||||||
if let reactionContextNode = self.reactionContextNode {
|
|
||||||
reactionContextNode.animateOut(to: nil, animatingOutToReaction: self.reactionContextNodeIsAnimatingOut)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1461,10 +1405,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||||||
if let presentationNode = self.presentationNode {
|
if let presentationNode = self.presentationNode {
|
||||||
presentationNode.addRelativeContentOffset(offset, transition: transition)
|
presentationNode.addRelativeContentOffset(offset, transition: transition)
|
||||||
}
|
}
|
||||||
if self.reactionContextNodeIsAnimatingOut, let reactionContextNode = self.reactionContextNode {
|
|
||||||
reactionContextNode.bounds = reactionContextNode.bounds.offsetBy(dx: 0.0, dy: offset.y)
|
|
||||||
transition.animateOffsetAdditive(node: reactionContextNode, offset: -offset.y)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func cancelReactionAnimation() {
|
func cancelReactionAnimation() {
|
||||||
@ -1520,13 +1460,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||||||
|
|
||||||
self.currentItems = items
|
self.currentItems = items
|
||||||
self.currentActionsMinHeight = minHeight
|
self.currentActionsMinHeight = minHeight
|
||||||
|
|
||||||
if let reactionContextNode = self.reactionContextNode {
|
|
||||||
self.reactionContextNode = nil
|
|
||||||
reactionContextNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak reactionContextNode] _ in
|
|
||||||
reactionContextNode?.removeFromSupernode()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
let previousActionsContainerNode = self.actionsContainerNode
|
let previousActionsContainerNode = self.actionsContainerNode
|
||||||
let previousActionsContainerFrame = previousActionsContainerNode.view.convert(previousActionsContainerNode.bounds, to: self.view)
|
let previousActionsContainerFrame = previousActionsContainerNode.view.convert(previousActionsContainerNode.bounds, to: self.view)
|
||||||
@ -1648,11 +1581,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||||||
transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||||
|
|
||||||
let actionsSideInset: CGFloat = layout.safeInsets.left + 12.0
|
let actionsSideInset: CGFloat = layout.safeInsets.left + 12.0
|
||||||
var contentTopInset: CGFloat = max(11.0, layout.statusBarHeight ?? 0.0)
|
let contentTopInset: CGFloat = max(11.0, layout.statusBarHeight ?? 0.0)
|
||||||
|
|
||||||
if let _ = self.reactionContextNode {
|
|
||||||
contentTopInset += 34.0
|
|
||||||
}
|
|
||||||
|
|
||||||
let actionsBottomInset: CGFloat = 11.0
|
let actionsBottomInset: CGFloat = 11.0
|
||||||
|
|
||||||
@ -1897,12 +1826,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||||||
let absoluteContentRect = contentContainerFrame.offsetBy(dx: 0.0, dy: -self.scrollNode.view.contentOffset.y)
|
let absoluteContentRect = contentContainerFrame.offsetBy(dx: 0.0, dy: -self.scrollNode.view.contentOffset.y)
|
||||||
|
|
||||||
contentParentNode.updateAbsoluteRect?(absoluteContentRect, layout.size)
|
contentParentNode.updateAbsoluteRect?(absoluteContentRect, layout.size)
|
||||||
|
|
||||||
if let reactionContextNode = self.reactionContextNode {
|
|
||||||
let insets = layout.insets(options: [.statusBar])
|
|
||||||
transition.updateFrame(node: reactionContextNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
|
||||||
reactionContextNode.updateLayout(size: layout.size, insets: insets, anchorRect: CGRect(origin: CGPoint(x: absoluteContentRect.minX + contentParentNode.contentRect.minX, y: absoluteContentRect.minY + contentParentNode.contentRect.minY), size: contentParentNode.contentRect.size), isAnimatingOut: false, transition: transition)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case let .controller(contentParentNode):
|
case let .controller(contentParentNode):
|
||||||
var projectedFrame: CGRect = convertFrame(contentParentNode.sourceView.bounds, from: contentParentNode.sourceView, to: self.view)
|
var projectedFrame: CGRect = convertFrame(contentParentNode.sourceView.bounds, from: contentParentNode.sourceView, to: self.view)
|
||||||
@ -2033,14 +1956,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||||||
transition.animateOffsetAdditive(node: self.scrollNode, offset: currentContainerFrame.minY - previousContainerFrame.minY)
|
transition.animateOffsetAdditive(node: self.scrollNode, offset: currentContainerFrame.minY - previousContainerFrame.minY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let absoluteContentRect = contentContainerFrame.offsetBy(dx: 0.0, dy: -self.scrollNode.view.contentOffset.y)
|
|
||||||
|
|
||||||
if let reactionContextNode = self.reactionContextNode {
|
|
||||||
let insets = layout.insets(options: [.statusBar])
|
|
||||||
transition.updateFrame(node: reactionContextNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
|
||||||
reactionContextNode.updateLayout(size: layout.size, insets: insets, anchorRect: CGRect(origin: CGPoint(x: absoluteContentRect.minX, y: absoluteContentRect.minY), size: contentSize), isAnimatingOut: false, transition: transition)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2149,12 +2064,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||||||
return presentationNode.hitTest(self.view.convert(point, to: presentationNode.view), with: event)
|
return presentationNode.hitTest(self.view.convert(point, to: presentationNode.view), with: event)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let reactionContextNode = self.reactionContextNode {
|
|
||||||
if let result = reactionContextNode.hitTest(self.view.convert(point, to: reactionContextNode.view), with: event) {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mappedPoint = self.view.convert(point, to: self.scrollNode.view)
|
let mappedPoint = self.view.convert(point, to: self.scrollNode.view)
|
||||||
var maybePassthrough: ContextController.HandledTouchEvent?
|
var maybePassthrough: ContextController.HandledTouchEvent?
|
||||||
if let maybeContentNode = self.contentContainerNode.contentNode {
|
if let maybeContentNode = self.contentContainerNode.contentNode {
|
||||||
|
|||||||
@ -724,11 +724,13 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
|||||||
var reactionAnchorRect = contentRect.offsetBy(dx: contentParentGlobalFrame.minX, dy: 0.0)
|
var reactionAnchorRect = contentRect.offsetBy(dx: contentParentGlobalFrame.minX, dy: 0.0)
|
||||||
|
|
||||||
let bottomInset = layout.insets(options: [.input]).bottom
|
let bottomInset = layout.insets(options: [.input]).bottom
|
||||||
|
var isCoveredByInput = false
|
||||||
if reactionAnchorRect.minY > layout.size.height - bottomInset {
|
if reactionAnchorRect.minY > layout.size.height - bottomInset {
|
||||||
reactionAnchorRect.origin.y = layout.size.height - bottomInset
|
reactionAnchorRect.origin.y = layout.size.height - bottomInset
|
||||||
|
isCoveredByInput = true
|
||||||
}
|
}
|
||||||
|
|
||||||
reactionContextNode.updateLayout(size: layout.size, insets: UIEdgeInsets(top: topInset, left: layout.safeInsets.left, bottom: 0.0, right: layout.safeInsets.right), anchorRect: reactionAnchorRect, isAnimatingOut: isAnimatingOut, transition: reactionContextNodeTransition)
|
reactionContextNode.updateLayout(size: layout.size, insets: UIEdgeInsets(top: topInset, left: layout.safeInsets.left, bottom: 0.0, right: layout.safeInsets.right), anchorRect: reactionAnchorRect, isCoveredByInput: isCoveredByInput, isAnimatingOut: isAnimatingOut, transition: reactionContextNodeTransition)
|
||||||
|
|
||||||
self.proposedReactionsPositionLock = contentRect.minY - 18.0 - reactionContextNode.contentHeight - 46.0
|
self.proposedReactionsPositionLock = contentRect.minY - 18.0 - reactionContextNode.contentHeight - 46.0
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -118,6 +118,7 @@ final class ReactionContextBackgroundNode: ASDisplayNode {
|
|||||||
cloudSourcePoint: CGFloat,
|
cloudSourcePoint: CGFloat,
|
||||||
isLeftAligned: Bool,
|
isLeftAligned: Bool,
|
||||||
isMinimized: Bool,
|
isMinimized: Bool,
|
||||||
|
isCoveredByInput: Bool,
|
||||||
transition: ContainedViewLayoutTransition
|
transition: ContainedViewLayoutTransition
|
||||||
) {
|
) {
|
||||||
let shadowInset: CGFloat = 15.0
|
let shadowInset: CGFloat = 15.0
|
||||||
@ -186,6 +187,12 @@ final class ReactionContextBackgroundNode: ASDisplayNode {
|
|||||||
|
|
||||||
transition.updateFrame(layer: self.backgroundShadowLayer, frame: backgroundFrame.insetBy(dx: -shadowInset, dy: -shadowInset), beginWithCurrentState: true)
|
transition.updateFrame(layer: self.backgroundShadowLayer, frame: backgroundFrame.insetBy(dx: -shadowInset, dy: -shadowInset), beginWithCurrentState: true)
|
||||||
transition.updateFrame(layer: self.largeCircleShadowLayer, frame: largeCircleFrame.insetBy(dx: -shadowInset, dy: -shadowInset), beginWithCurrentState: true)
|
transition.updateFrame(layer: self.largeCircleShadowLayer, frame: largeCircleFrame.insetBy(dx: -shadowInset, dy: -shadowInset), beginWithCurrentState: true)
|
||||||
|
|
||||||
|
transition.updateAlpha(layer: self.largeCircleLayer, alpha: isCoveredByInput ? 0.0 : 1.0)
|
||||||
|
transition.updateAlpha(layer: self.largeCircleShadowLayer, alpha: isCoveredByInput ? 0.0 : 1.0)
|
||||||
|
transition.updateAlpha(layer: self.smallCircleLayer, alpha: isCoveredByInput ? 0.0 : 1.0)
|
||||||
|
transition.updateAlpha(layer: self.smallCircleShadowLayer, alpha: isCoveredByInput ? 0.0 : 1.0)
|
||||||
|
|
||||||
transition.updateFrame(layer: self.smallCircleShadowLayer, frame: smallCircleFrame.insetBy(dx: -shadowInset, dy: -shadowInset), beginWithCurrentState: true)
|
transition.updateFrame(layer: self.smallCircleShadowLayer, frame: smallCircleFrame.insetBy(dx: -shadowInset, dy: -shadowInset), beginWithCurrentState: true)
|
||||||
|
|
||||||
transition.updateFrame(view: self.backgroundView, frame: contentBounds, beginWithCurrentState: true)
|
transition.updateFrame(view: self.backgroundView, frame: contentBounds, beginWithCurrentState: true)
|
||||||
|
|||||||
@ -200,7 +200,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
private var highlightedByHover = false
|
private var highlightedByHover = false
|
||||||
private var didTriggerExpandedReaction: Bool = false
|
private var didTriggerExpandedReaction: Bool = false
|
||||||
private var continuousHaptic: Any?
|
private var continuousHaptic: Any?
|
||||||
private var validLayout: (CGSize, UIEdgeInsets, CGRect)?
|
private var validLayout: (CGSize, UIEdgeInsets, CGRect, Bool)?
|
||||||
private var isLeftAligned: Bool = true
|
private var isLeftAligned: Bool = true
|
||||||
private var itemLayout: ItemLayout?
|
private var itemLayout: ItemLayout?
|
||||||
|
|
||||||
@ -532,8 +532,8 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func updateLayout(size: CGSize, insets: UIEdgeInsets, anchorRect: CGRect, isAnimatingOut: Bool, transition: ContainedViewLayoutTransition) {
|
public func updateLayout(size: CGSize, insets: UIEdgeInsets, anchorRect: CGRect, isCoveredByInput: Bool, isAnimatingOut: Bool, transition: ContainedViewLayoutTransition) {
|
||||||
self.updateLayout(size: size, insets: insets, anchorRect: anchorRect, isAnimatingOut: isAnimatingOut, transition: transition, animateInFromAnchorRect: nil, animateOutToAnchorRect: nil)
|
self.updateLayout(size: size, insets: insets, anchorRect: anchorRect, isCoveredByInput: isCoveredByInput, isAnimatingOut: isAnimatingOut, transition: transition, animateInFromAnchorRect: nil, animateOutToAnchorRect: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func updateIsIntersectingContent(isIntersectingContent: Bool, transition: ContainedViewLayoutTransition) {
|
public func updateIsIntersectingContent(isIntersectingContent: Bool, transition: ContainedViewLayoutTransition) {
|
||||||
@ -544,8 +544,8 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
if self.extensionDistance != distance {
|
if self.extensionDistance != distance {
|
||||||
self.extensionDistance = distance
|
self.extensionDistance = distance
|
||||||
|
|
||||||
if let (size, insets, anchorRect) = self.validLayout {
|
if let (size, insets, anchorRect, isCoveredByInput) = self.validLayout {
|
||||||
self.updateLayout(size: size, insets: insets, anchorRect: anchorRect, isAnimatingOut: false, transition: .immediate, animateInFromAnchorRect: nil, animateOutToAnchorRect: nil)
|
self.updateLayout(size: size, insets: insets, anchorRect: anchorRect, isCoveredByInput: isCoveredByInput, isAnimatingOut: false, transition: .immediate, animateInFromAnchorRect: nil, animateOutToAnchorRect: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -886,12 +886,12 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateLayout(size: CGSize, insets: UIEdgeInsets, anchorRect: CGRect, isAnimatingOut: Bool, transition: ContainedViewLayoutTransition, animateInFromAnchorRect: CGRect?, animateOutToAnchorRect: CGRect?, animateReactionHighlight: Bool = false) {
|
private func updateLayout(size: CGSize, insets: UIEdgeInsets, anchorRect: CGRect, isCoveredByInput: Bool, isAnimatingOut: Bool, transition: ContainedViewLayoutTransition, animateInFromAnchorRect: CGRect?, animateOutToAnchorRect: CGRect?, animateReactionHighlight: Bool = false) {
|
||||||
if let expandItemView = self.expandItemView {
|
if let expandItemView = self.expandItemView {
|
||||||
expandItemView.updateTheme(theme: self.presentationData.theme)
|
expandItemView.updateTheme(theme: self.presentationData.theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.validLayout = (size, insets, anchorRect)
|
self.validLayout = (size, insets, anchorRect, isCoveredByInput)
|
||||||
|
|
||||||
let externalSideInset: CGFloat = 4.0
|
let externalSideInset: CGFloat = 4.0
|
||||||
let sideInset: CGFloat = 6.0
|
let sideInset: CGFloat = 6.0
|
||||||
@ -1095,6 +1095,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
cloudSourcePoint: cloudSourcePoint - visualBackgroundFrame.minX,
|
cloudSourcePoint: cloudSourcePoint - visualBackgroundFrame.minX,
|
||||||
isLeftAligned: isLeftAligned,
|
isLeftAligned: isLeftAligned,
|
||||||
isMinimized: self.highlightedReaction != nil && !self.highlightedByHover,
|
isMinimized: self.highlightedReaction != nil && !self.highlightedByHover,
|
||||||
|
isCoveredByInput: isCoveredByInput,
|
||||||
transition: transition
|
transition: transition
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1276,11 +1277,13 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
}
|
}
|
||||||
strongSelf.requestUpdateOverlayWantsToBeBelowKeyboard(transition.containedViewLayoutTransition)
|
strongSelf.requestUpdateOverlayWantsToBeBelowKeyboard(transition.containedViewLayoutTransition)
|
||||||
},
|
},
|
||||||
updateSearchQuery: { [weak self] query in
|
updateSearchQuery: { [weak self] rawQuery in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let query = rawQuery.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
|
||||||
if query.isEmpty {
|
if query.isEmpty {
|
||||||
strongSelf.emojiSearchDisposable.set(nil)
|
strongSelf.emojiSearchDisposable.set(nil)
|
||||||
strongSelf.emojiSearchResult.set(.single(nil))
|
strongSelf.emojiSearchResult.set(.single(nil))
|
||||||
@ -1288,7 +1291,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
let context = strongSelf.context
|
let context = strongSelf.context
|
||||||
|
|
||||||
let languageCode = "en"
|
let languageCode = "en"
|
||||||
var signal = context.engine.stickers.searchEmojiKeywords(inputLanguageCode: languageCode, query: query, completeMatch: query.count < 2)
|
var signal = context.engine.stickers.searchEmojiKeywords(inputLanguageCode: languageCode, query: query, completeMatch: false)
|
||||||
if !languageCode.lowercased().hasPrefix("en") {
|
if !languageCode.lowercased().hasPrefix("en") {
|
||||||
signal = signal
|
signal = signal
|
||||||
|> mapToSignal { keywords in
|
|> mapToSignal { keywords in
|
||||||
@ -1374,6 +1377,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
strongSelf.emojiSearchDisposable.set((resultSignal
|
strongSelf.emojiSearchDisposable.set((resultSignal
|
||||||
|
|> delay(0.15, queue: .mainQueue())
|
||||||
|> deliverOnMainQueue).start(next: { result in
|
|> deliverOnMainQueue).start(next: { result in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
@ -1396,8 +1400,8 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
public func animateIn(from sourceAnchorRect: CGRect) {
|
public func animateIn(from sourceAnchorRect: CGRect) {
|
||||||
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
|
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
|
||||||
|
|
||||||
if let (size, insets, anchorRect) = self.validLayout {
|
if let (size, insets, anchorRect, isCoveredByInput) = self.validLayout {
|
||||||
self.updateLayout(size: size, insets: insets, anchorRect: anchorRect, isAnimatingOut: false, transition: .immediate, animateInFromAnchorRect: sourceAnchorRect, animateOutToAnchorRect: nil)
|
self.updateLayout(size: size, insets: insets, anchorRect: anchorRect, isCoveredByInput: isCoveredByInput, isAnimatingOut: false, transition: .immediate, animateInFromAnchorRect: sourceAnchorRect, animateOutToAnchorRect: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
let mainCircleDelay: Double = 0.01
|
let mainCircleDelay: Double = 0.01
|
||||||
@ -1475,8 +1479,8 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
expandItemView.tintView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2)
|
expandItemView.tintView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let targetAnchorRect = targetAnchorRect, let (size, insets, anchorRect) = self.validLayout {
|
if let targetAnchorRect = targetAnchorRect, let (size, insets, anchorRect, isCoveredByInput) = self.validLayout {
|
||||||
self.updateLayout(size: size, insets: insets, anchorRect: anchorRect, isAnimatingOut: false, transition: .immediate, animateInFromAnchorRect: nil, animateOutToAnchorRect: targetAnchorRect)
|
self.updateLayout(size: size, insets: insets, anchorRect: anchorRect, isCoveredByInput: isCoveredByInput, isAnimatingOut: false, transition: .immediate, animateInFromAnchorRect: nil, animateOutToAnchorRect: targetAnchorRect)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1919,8 +1923,8 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
self.hapticFeedback = HapticFeedback()
|
self.hapticFeedback = HapticFeedback()
|
||||||
}
|
}
|
||||||
|
|
||||||
if let (size, insets, anchorRect) = self.validLayout {
|
if let (size, insets, anchorRect, isCoveredByInput) = self.validLayout {
|
||||||
self.updateLayout(size: size, insets: insets, anchorRect: anchorRect, isAnimatingOut: false, transition: .animated(duration: longPressDuration, curve: .linear), animateInFromAnchorRect: nil, animateOutToAnchorRect: nil, animateReactionHighlight: true)
|
self.updateLayout(size: size, insets: insets, anchorRect: anchorRect, isCoveredByInput: isCoveredByInput, isAnimatingOut: false, transition: .animated(duration: longPressDuration, curve: .linear), animateInFromAnchorRect: nil, animateOutToAnchorRect: nil, animateReactionHighlight: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.longPressTimer?.invalidate()
|
self.longPressTimer?.invalidate()
|
||||||
@ -1950,8 +1954,8 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
self.continuousHaptic = nil
|
self.continuousHaptic = nil
|
||||||
|
|
||||||
self.highlightedReaction = nil
|
self.highlightedReaction = nil
|
||||||
if let (size, insets, anchorRect) = self.validLayout {
|
if let (size, insets, anchorRect, isCoveredByInput) = self.validLayout {
|
||||||
self.updateLayout(size: size, insets: insets, anchorRect: anchorRect, isAnimatingOut: false, transition: .animated(duration: 0.3, curve: .spring), animateInFromAnchorRect: nil, animateOutToAnchorRect: nil, animateReactionHighlight: true)
|
self.updateLayout(size: size, insets: insets, anchorRect: anchorRect, isCoveredByInput: isCoveredByInput, isAnimatingOut: false, transition: .animated(duration: 0.3, curve: .spring), animateInFromAnchorRect: nil, animateOutToAnchorRect: nil, animateReactionHighlight: true)
|
||||||
}
|
}
|
||||||
case .ended:
|
case .ended:
|
||||||
self.longPressTimer?.invalidate()
|
self.longPressTimer?.invalidate()
|
||||||
@ -2027,8 +2031,8 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
self.hapticFeedback?.tap()
|
self.hapticFeedback?.tap()
|
||||||
}
|
}
|
||||||
|
|
||||||
if let (size, insets, anchorRect) = self.validLayout {
|
if let (size, insets, anchorRect, isCoveredByInput) = self.validLayout {
|
||||||
self.updateLayout(size: size, insets: insets, anchorRect: anchorRect, isAnimatingOut: false, transition: .animated(duration: 0.18, curve: .easeInOut), animateInFromAnchorRect: nil, animateOutToAnchorRect: nil, animateReactionHighlight: true)
|
self.updateLayout(size: size, insets: insets, anchorRect: anchorRect, isCoveredByInput: isCoveredByInput, isAnimatingOut: false, transition: .animated(duration: 0.18, curve: .easeInOut), animateInFromAnchorRect: nil, animateOutToAnchorRect: nil, animateReactionHighlight: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2043,8 +2047,8 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
if performAction {
|
if performAction {
|
||||||
self.performReactionSelection(reaction: highlightedReaction, isLarge: isLarge)
|
self.performReactionSelection(reaction: highlightedReaction, isLarge: isLarge)
|
||||||
} else {
|
} else {
|
||||||
if let (size, insets, anchorRect) = self.validLayout {
|
if let (size, insets, anchorRect, isCoveredByInput) = self.validLayout {
|
||||||
self.updateLayout(size: size, insets: insets, anchorRect: anchorRect, isAnimatingOut: false, transition: .animated(duration: 0.18, curve: .easeInOut), animateInFromAnchorRect: nil, animateOutToAnchorRect: nil, animateReactionHighlight: true)
|
self.updateLayout(size: size, insets: insets, anchorRect: anchorRect, isCoveredByInput: isCoveredByInput, isAnimatingOut: false, transition: .animated(duration: 0.18, curve: .easeInOut), animateInFromAnchorRect: nil, animateOutToAnchorRect: nil, animateReactionHighlight: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2138,8 +2142,8 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
|
|
||||||
public func setHighlightedReaction(_ value: ReactionItem.Reaction?) {
|
public func setHighlightedReaction(_ value: ReactionItem.Reaction?) {
|
||||||
self.highlightedReaction = value
|
self.highlightedReaction = value
|
||||||
if let (size, insets, anchorRect) = self.validLayout {
|
if let (size, insets, anchorRect, isCoveredByInput) = self.validLayout {
|
||||||
self.updateLayout(size: size, insets: insets, anchorRect: anchorRect, isAnimatingOut: false, transition: .animated(duration: 0.18, curve: .easeInOut), animateInFromAnchorRect: nil, animateOutToAnchorRect: nil, animateReactionHighlight: true)
|
self.updateLayout(size: size, insets: insets, anchorRect: anchorRect, isCoveredByInput: isCoveredByInput, isAnimatingOut: false, transition: .animated(duration: 0.18, curve: .easeInOut), animateInFromAnchorRect: nil, animateOutToAnchorRect: nil, animateReactionHighlight: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -185,7 +185,7 @@ private func quickReactionSetupControllerEntries(
|
|||||||
dateTimeFormat: presentationData.dateTimeFormat,
|
dateTimeFormat: presentationData.dateTimeFormat,
|
||||||
nameDisplayOrder: presentationData.nameDisplayOrder,
|
nameDisplayOrder: presentationData.nameDisplayOrder,
|
||||||
availableReactions: availableReactions,
|
availableReactions: availableReactions,
|
||||||
reaction: state.hasReaction ? reactionSettings.quickReaction : nil
|
reaction: state.hasReaction ? reactionSettings.effectiveQuickReaction(hasPremium: isPremium) : nil
|
||||||
))
|
))
|
||||||
entries.append(.demoDescription(presentationData.strings.Settings_QuickReactionSetup_DemoInfo))
|
entries.append(.demoDescription(presentationData.strings.Settings_QuickReactionSetup_DemoInfo))
|
||||||
|
|
||||||
@ -354,6 +354,7 @@ public func quickReactionSetupController(
|
|||||||
chatPeerId: context.account.peerId,
|
chatPeerId: context.account.peerId,
|
||||||
selectedItems: selectedItems
|
selectedItems: selectedItems
|
||||||
),
|
),
|
||||||
|
currentSelection: nil,
|
||||||
destinationItemView: { [weak sourceItemNode] in
|
destinationItemView: { [weak sourceItemNode] in
|
||||||
return sourceItemNode?.iconView
|
return sourceItemNode?.iconView
|
||||||
}
|
}
|
||||||
|
|||||||
@ -927,16 +927,22 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
|
|||||||
case .general, .modal:
|
case .general, .modal:
|
||||||
featured.set(context.account.viewTracker.featuredStickerPacks())
|
featured.set(context.account.viewTracker.featuredStickerPacks())
|
||||||
archivedPromise.set(.single(archivedPacks) |> then(context.engine.stickers.archivedStickerPacks() |> map(Optional.init)))
|
archivedPromise.set(.single(archivedPacks) |> then(context.engine.stickers.archivedStickerPacks() |> map(Optional.init)))
|
||||||
quickReaction = context.account.postbox.preferencesView(keys: [PreferencesKeys.reactionSettings])
|
quickReaction = combineLatest(
|
||||||
|> map { preferencesView -> MessageReaction.Reaction? in
|
context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId)),
|
||||||
|
context.account.postbox.preferencesView(keys: [PreferencesKeys.reactionSettings])
|
||||||
|
)
|
||||||
|
|> map { peer, preferencesView -> MessageReaction.Reaction? in
|
||||||
let reactionSettings: ReactionSettings
|
let reactionSettings: ReactionSettings
|
||||||
if let entry = preferencesView.values[PreferencesKeys.reactionSettings], let value = entry.get(ReactionSettings.self) {
|
if let entry = preferencesView.values[PreferencesKeys.reactionSettings], let value = entry.get(ReactionSettings.self) {
|
||||||
reactionSettings = value
|
reactionSettings = value
|
||||||
} else {
|
} else {
|
||||||
reactionSettings = .default
|
reactionSettings = .default
|
||||||
}
|
}
|
||||||
|
var hasPremium = false
|
||||||
return reactionSettings.quickReaction
|
if case let .user(user) = peer {
|
||||||
|
hasPremium = user.isPremium
|
||||||
|
}
|
||||||
|
return reactionSettings.effectiveQuickReaction(hasPremium: hasPremium)
|
||||||
}
|
}
|
||||||
|> distinctUntilChanged
|
|> distinctUntilChanged
|
||||||
case .masks:
|
case .masks:
|
||||||
|
|||||||
@ -11,6 +11,21 @@ public struct ReactionSettings: Equatable, Codable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public extension ReactionSettings {
|
||||||
|
func effectiveQuickReaction(hasPremium: Bool) -> MessageReaction.Reaction {
|
||||||
|
switch self.quickReaction {
|
||||||
|
case .builtin:
|
||||||
|
return self.quickReaction
|
||||||
|
case .custom:
|
||||||
|
if hasPremium {
|
||||||
|
return self.quickReaction
|
||||||
|
} else {
|
||||||
|
return ReactionSettings.default.quickReaction
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func updateReactionSettings(transaction: Transaction, _ f: (ReactionSettings) -> ReactionSettings) {
|
func updateReactionSettings(transaction: Transaction, _ f: (ReactionSettings) -> ReactionSettings) {
|
||||||
transaction.updatePreferencesEntry(key: PreferencesKeys.reactionSettings, { current in
|
transaction.updatePreferencesEntry(key: PreferencesKeys.reactionSettings, { current in
|
||||||
let previous = current?.get(ReactionSettings.self) ?? ReactionSettings.default
|
let previous = current?.get(ReactionSettings.self) ?? ReactionSettings.default
|
||||||
|
|||||||
@ -243,6 +243,8 @@ public final class EmojiStatusSelectionController: ViewController {
|
|||||||
private var presentationData: PresentationData
|
private var presentationData: PresentationData
|
||||||
private var validLayout: ContainerViewLayout?
|
private var validLayout: ContainerViewLayout?
|
||||||
|
|
||||||
|
private let currentSelection: Int64?
|
||||||
|
|
||||||
private var emojiContentDisposable: Disposable?
|
private var emojiContentDisposable: Disposable?
|
||||||
private var emojiContent: EmojiPagerContentComponent?
|
private var emojiContent: EmojiPagerContentComponent?
|
||||||
private var freezeUpdates: Bool = false
|
private var freezeUpdates: Bool = false
|
||||||
@ -263,9 +265,10 @@ public final class EmojiStatusSelectionController: ViewController {
|
|||||||
private var isAnimatingOut: Bool = false
|
private var isAnimatingOut: Bool = false
|
||||||
private var isDismissed: Bool = false
|
private var isDismissed: Bool = false
|
||||||
|
|
||||||
init(controller: EmojiStatusSelectionController, context: AccountContext, sourceView: UIView?, emojiContent: Signal<EmojiPagerContentComponent, NoError>) {
|
init(controller: EmojiStatusSelectionController, context: AccountContext, sourceView: UIView?, emojiContent: Signal<EmojiPagerContentComponent, NoError>, currentSelection: Int64?) {
|
||||||
self.controller = controller
|
self.controller = controller
|
||||||
self.context = context
|
self.context = context
|
||||||
|
self.currentSelection = currentSelection
|
||||||
|
|
||||||
if let sourceView = sourceView {
|
if let sourceView = sourceView {
|
||||||
self.globalSourceRect = sourceView.convert(sourceView.bounds, to: nil)
|
self.globalSourceRect = sourceView.convert(sourceView.bounds, to: nil)
|
||||||
@ -920,6 +923,13 @@ public final class EmojiStatusSelectionController: ViewController {
|
|||||||
} else {
|
} else {
|
||||||
self.freezeUpdates = true
|
self.freezeUpdates = true
|
||||||
|
|
||||||
|
if case .statusSelection = controller.mode, let item = item, let currentSelection = self.currentSelection, item.itemFile?.fileId.id == currentSelection {
|
||||||
|
let _ = (self.context.engine.accountData.setEmojiStatus(file: nil, expirationDate: nil)
|
||||||
|
|> deliverOnMainQueue).start()
|
||||||
|
controller.dismiss()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if let _ = item, let destinationView = controller.destinationItemView() {
|
if let _ = item, let destinationView = controller.destinationItemView() {
|
||||||
if let snapshotView = destinationView.snapshotView(afterScreenUpdates: false) {
|
if let snapshotView = destinationView.snapshotView(afterScreenUpdates: false) {
|
||||||
snapshotView.frame = destinationView.frame
|
snapshotView.frame = destinationView.frame
|
||||||
@ -1026,6 +1036,7 @@ public final class EmojiStatusSelectionController: ViewController {
|
|||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
private weak var sourceView: UIView?
|
private weak var sourceView: UIView?
|
||||||
private let emojiContent: Signal<EmojiPagerContentComponent, NoError>
|
private let emojiContent: Signal<EmojiPagerContentComponent, NoError>
|
||||||
|
private let currentSelection: Int64?
|
||||||
private let mode: Mode
|
private let mode: Mode
|
||||||
private let destinationItemView: () -> UIView?
|
private let destinationItemView: () -> UIView?
|
||||||
|
|
||||||
@ -1034,11 +1045,12 @@ public final class EmojiStatusSelectionController: ViewController {
|
|||||||
return self._ready
|
return self._ready
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(context: AccountContext, mode: Mode, sourceView: UIView, emojiContent: Signal<EmojiPagerContentComponent, NoError>, destinationItemView: @escaping () -> UIView?) {
|
public init(context: AccountContext, mode: Mode, sourceView: UIView, emojiContent: Signal<EmojiPagerContentComponent, NoError>, currentSelection: Int64?, destinationItemView: @escaping () -> UIView?) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
self.sourceView = sourceView
|
self.sourceView = sourceView
|
||||||
self.emojiContent = emojiContent
|
self.emojiContent = emojiContent
|
||||||
|
self.currentSelection = currentSelection
|
||||||
self.destinationItemView = destinationItemView
|
self.destinationItemView = destinationItemView
|
||||||
|
|
||||||
super.init(navigationBarPresentationData: nil)
|
super.init(navigationBarPresentationData: nil)
|
||||||
@ -1068,7 +1080,7 @@ public final class EmojiStatusSelectionController: ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override public func loadDisplayNode() {
|
override public func loadDisplayNode() {
|
||||||
self.displayNode = Node(controller: self, context: self.context, sourceView: self.sourceView, emojiContent: self.emojiContent)
|
self.displayNode = Node(controller: self, context: self.context, sourceView: self.sourceView, emojiContent: self.emojiContent, currentSelection: self.currentSelection)
|
||||||
|
|
||||||
super.displayNodeDidLoad()
|
super.displayNodeDidLoad()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1551,6 +1551,11 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate {
|
|||||||
|
|
||||||
private let searchIconView: UIImageView
|
private let searchIconView: UIImageView
|
||||||
private let searchIconTintView: UIImageView
|
private let searchIconTintView: UIImageView
|
||||||
|
|
||||||
|
private let clearIconView: UIImageView
|
||||||
|
private let clearIconTintView: UIImageView
|
||||||
|
private let clearIconButton: HighlightTrackingButton
|
||||||
|
|
||||||
private let tintTextView: ComponentView<Empty>
|
private let tintTextView: ComponentView<Empty>
|
||||||
private let textView: ComponentView<Empty>
|
private let textView: ComponentView<Empty>
|
||||||
private let cancelButtonTintTitle: ComponentView<Empty>
|
private let cancelButtonTintTitle: ComponentView<Empty>
|
||||||
@ -1580,6 +1585,13 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate {
|
|||||||
self.searchIconView = UIImageView()
|
self.searchIconView = UIImageView()
|
||||||
self.searchIconTintView = UIImageView()
|
self.searchIconTintView = UIImageView()
|
||||||
|
|
||||||
|
self.clearIconView = UIImageView()
|
||||||
|
self.clearIconTintView = UIImageView()
|
||||||
|
self.clearIconButton = HighlightableButton()
|
||||||
|
self.clearIconView.isHidden = true
|
||||||
|
self.clearIconTintView.isHidden = true
|
||||||
|
self.clearIconButton.isHidden = true
|
||||||
|
|
||||||
self.tintTextView = ComponentView()
|
self.tintTextView = ComponentView()
|
||||||
self.textView = ComponentView()
|
self.textView = ComponentView()
|
||||||
self.cancelButtonTintTitle = ComponentView()
|
self.cancelButtonTintTitle = ComponentView()
|
||||||
@ -1594,6 +1606,10 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate {
|
|||||||
self.addSubview(self.searchIconView)
|
self.addSubview(self.searchIconView)
|
||||||
self.tintContainerView.addSubview(self.searchIconTintView)
|
self.tintContainerView.addSubview(self.searchIconTintView)
|
||||||
|
|
||||||
|
self.addSubview(self.clearIconView)
|
||||||
|
self.tintContainerView.addSubview(self.clearIconTintView)
|
||||||
|
self.addSubview(self.clearIconButton)
|
||||||
|
|
||||||
self.addSubview(self.cancelButton)
|
self.addSubview(self.cancelButton)
|
||||||
self.clipsToBounds = true
|
self.clipsToBounds = true
|
||||||
|
|
||||||
@ -1627,6 +1643,23 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.cancelButton.addTarget(self, action: #selector(self.cancelPressed), for: .touchUpInside)
|
self.cancelButton.addTarget(self, action: #selector(self.cancelPressed), for: .touchUpInside)
|
||||||
|
|
||||||
|
self.clearIconButton.highligthedChanged = { [weak self] highlighted in
|
||||||
|
if let strongSelf = self {
|
||||||
|
if highlighted {
|
||||||
|
strongSelf.clearIconView.layer.removeAnimation(forKey: "opacity")
|
||||||
|
strongSelf.clearIconView.alpha = 0.4
|
||||||
|
strongSelf.clearIconTintView.layer.removeAnimation(forKey: "opacity")
|
||||||
|
strongSelf.clearIconTintView.alpha = 0.4
|
||||||
|
} else {
|
||||||
|
strongSelf.clearIconView.alpha = 1.0
|
||||||
|
strongSelf.clearIconView.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
|
||||||
|
strongSelf.clearIconTintView.alpha = 1.0
|
||||||
|
strongSelf.clearIconTintView.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.clearIconButton.addTarget(self, action: #selector(self.clearPressed), for: .touchUpInside)
|
||||||
}
|
}
|
||||||
|
|
||||||
required public init?(coder: NSCoder) {
|
required public init?(coder: NSCoder) {
|
||||||
@ -1641,7 +1674,7 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate {
|
|||||||
|
|
||||||
let textField = EmojiSearchTextField(frame: textFieldFrame)
|
let textField = EmojiSearchTextField(frame: textFieldFrame)
|
||||||
self.textField = textField
|
self.textField = textField
|
||||||
self.addSubview(textField)
|
self.insertSubview(textField, belowSubview: self.clearIconView)
|
||||||
textField.delegate = self
|
textField.delegate = self
|
||||||
textField.addTarget(self, action: #selector(self.textFieldChanged(_:)), for: .editingChanged)
|
textField.addTarget(self, action: #selector(self.textFieldChanged(_:)), for: .editingChanged)
|
||||||
}
|
}
|
||||||
@ -1655,6 +1688,10 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate {
|
|||||||
@objc private func cancelPressed() {
|
@objc private func cancelPressed() {
|
||||||
self.updateQuery("")
|
self.updateQuery("")
|
||||||
|
|
||||||
|
self.clearIconView.isHidden = true
|
||||||
|
self.clearIconTintView.isHidden = true
|
||||||
|
self.clearIconButton.isHidden = true
|
||||||
|
|
||||||
if let textField = self.textField {
|
if let textField = self.textField {
|
||||||
self.textField = nil
|
self.textField = nil
|
||||||
|
|
||||||
@ -1664,6 +1701,15 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate {
|
|||||||
self.deactivated()
|
self.deactivated()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc private func clearPressed() {
|
||||||
|
self.updateQuery("")
|
||||||
|
self.textField?.text = ""
|
||||||
|
|
||||||
|
self.clearIconView.isHidden = true
|
||||||
|
self.clearIconTintView.isHidden = true
|
||||||
|
self.clearIconButton.isHidden = true
|
||||||
|
}
|
||||||
|
|
||||||
public func textFieldDidBeginEditing(_ textField: UITextField) {
|
public func textFieldDidBeginEditing(_ textField: UITextField) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1673,7 +1719,13 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate {
|
|||||||
@objc private func textFieldChanged(_ textField: UITextField) {
|
@objc private func textFieldChanged(_ textField: UITextField) {
|
||||||
self.update(transition: .immediate)
|
self.update(transition: .immediate)
|
||||||
|
|
||||||
self.updateQuery(textField.text ?? "")
|
let text = textField.text ?? ""
|
||||||
|
|
||||||
|
self.clearIconView.isHidden = text.isEmpty
|
||||||
|
self.clearIconTintView.isHidden = text.isEmpty
|
||||||
|
self.clearIconButton.isHidden = text.isEmpty
|
||||||
|
|
||||||
|
self.updateQuery(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func update(transition: Transition) {
|
private func update(transition: Transition) {
|
||||||
@ -1700,6 +1752,9 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate {
|
|||||||
if self.params?.theme !== theme {
|
if self.params?.theme !== theme {
|
||||||
self.searchIconView.image = generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Loupe"), color: theme.chat.inputMediaPanel.panelContentVibrantOverlayColor)
|
self.searchIconView.image = generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Loupe"), color: theme.chat.inputMediaPanel.panelContentVibrantOverlayColor)
|
||||||
self.searchIconTintView.image = generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Loupe"), color: .white)
|
self.searchIconTintView.image = generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Loupe"), color: .white)
|
||||||
|
|
||||||
|
self.clearIconView.image = generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Clear"), color: theme.chat.inputMediaPanel.panelContentVibrantOverlayColor)
|
||||||
|
self.clearIconTintView.image = generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Clear"), color: .white)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.params = params
|
self.params = params
|
||||||
@ -1785,6 +1840,13 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate {
|
|||||||
transition.setFrame(view: self.searchIconTintView, frame: iconFrame)
|
transition.setFrame(view: self.searchIconTintView, frame: iconFrame)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let image = self.clearIconView.image {
|
||||||
|
let iconFrame = CGRect(origin: CGPoint(x: backgroundFrame.maxX - image.size.width - 4.0, y: backgroundFrame.minY + floor((backgroundFrame.height - image.size.height) / 2.0)), size: image.size)
|
||||||
|
transition.setFrame(view: self.clearIconView, frame: iconFrame)
|
||||||
|
transition.setFrame(view: self.clearIconTintView, frame: iconFrame)
|
||||||
|
transition.setFrame(view: self.clearIconButton, frame: iconFrame.insetBy(dx: -8.0, dy: -10.0))
|
||||||
|
}
|
||||||
|
|
||||||
if let textComponentView = self.textView.view {
|
if let textComponentView = self.textView.view {
|
||||||
if textComponentView.superview == nil {
|
if textComponentView.superview == nil {
|
||||||
self.addSubview(textComponentView)
|
self.addSubview(textComponentView)
|
||||||
@ -3856,7 +3918,7 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
for (id, layer) in self.visibleItemLayers {
|
for (id, layer) in self.visibleItemLayers {
|
||||||
previousVisibleLayers[id] = (layer, layer.frame.offsetBy(dx: 0.0, dy: -self.scrollView.bounds.minY))
|
previousVisibleLayers[id] = (layer, layer.frame.offsetBy(dx: 0.0, dy: -self.scrollView.bounds.minY))
|
||||||
}
|
}
|
||||||
var previousVisibleItemSelectionLayers: [ItemLayer.Key: (CALayer, CGRect)] = [:]
|
var previousVisibleItemSelectionLayers: [ItemLayer.Key: (ItemSelectionLayer, CGRect)] = [:]
|
||||||
for (id, layer) in self.visibleItemSelectionLayers {
|
for (id, layer) in self.visibleItemSelectionLayers {
|
||||||
previousVisibleItemSelectionLayers[id] = (layer, layer.frame.offsetBy(dx: 0.0, dy: -self.scrollView.bounds.minY))
|
previousVisibleItemSelectionLayers[id] = (layer, layer.frame.offsetBy(dx: 0.0, dy: -self.scrollView.bounds.minY))
|
||||||
}
|
}
|
||||||
@ -3980,6 +4042,10 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
for (_, layer) in self.visibleItemLayers {
|
for (_, layer) in self.visibleItemLayers {
|
||||||
layer.animatePosition(from: CGPoint(x: 0.0, y: commonItemOffset), to: CGPoint(), duration: duration, timingFunction: timingFunction, additive: true)
|
layer.animatePosition(from: CGPoint(x: 0.0, y: commonItemOffset), to: CGPoint(), duration: duration, timingFunction: timingFunction, additive: true)
|
||||||
}
|
}
|
||||||
|
for (_, layer) in self.visibleItemSelectionLayers {
|
||||||
|
layer.animatePosition(from: CGPoint(x: 0.0, y: commonItemOffset), to: CGPoint(), duration: duration, timingFunction: timingFunction, additive: true)
|
||||||
|
}
|
||||||
|
|
||||||
for (id, layerAndFrame) in previousVisibleLayers {
|
for (id, layerAndFrame) in previousVisibleLayers {
|
||||||
if self.visibleItemLayers[id] != nil {
|
if self.visibleItemLayers[id] != nil {
|
||||||
continue
|
continue
|
||||||
@ -3990,6 +4056,19 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
layer?.removeFromSuperlayer()
|
layer?.removeFromSuperlayer()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
for (id, layerAndFrame) in previousVisibleItemSelectionLayers {
|
||||||
|
if self.visibleItemSelectionLayers[id] != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
let layer = layerAndFrame.0
|
||||||
|
self.scrollView.layer.addSublayer(layer)
|
||||||
|
let tintContainerLayer = layer.tintContainerLayer
|
||||||
|
self.mirrorContentScrollView.layer.addSublayer(tintContainerLayer)
|
||||||
|
layer.animatePosition(from: CGPoint(x: 0.0, y: commonItemOffset), to: CGPoint(), duration: duration, timingFunction: timingFunction, removeOnCompletion: false, additive: true, completion: { [weak layer, weak tintContainerLayer] _ in
|
||||||
|
layer?.removeFromSuperlayer()
|
||||||
|
tintContainerLayer?.removeFromSuperlayer()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
for (_, view) in self.visibleItemPlaceholderViews {
|
for (_, view) in self.visibleItemPlaceholderViews {
|
||||||
view.layer.animatePosition(from: CGPoint(x: 0.0, y: commonItemOffset), to: CGPoint(), duration: duration, timingFunction: timingFunction, additive: true)
|
view.layer.animatePosition(from: CGPoint(x: 0.0, y: commonItemOffset), to: CGPoint(), duration: duration, timingFunction: timingFunction, additive: true)
|
||||||
@ -4163,8 +4242,11 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
let layer = layerAndFrame.0
|
let layer = layerAndFrame.0
|
||||||
layer.frame = layerAndFrame.1.offsetBy(dx: 0.0, dy: self.scrollView.bounds.minY)
|
layer.frame = layerAndFrame.1.offsetBy(dx: 0.0, dy: self.scrollView.bounds.minY)
|
||||||
self.scrollView.layer.addSublayer(layer)
|
self.scrollView.layer.addSublayer(layer)
|
||||||
layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -commonItemOffset), duration: duration, timingFunction: timingFunction, removeOnCompletion: false, additive: true, completion: { [weak layer] _ in
|
let tintContainerLayer = layer.tintContainerLayer
|
||||||
|
self.mirrorContentScrollView.layer.addSublayer(tintContainerLayer)
|
||||||
|
layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -commonItemOffset), duration: duration, timingFunction: timingFunction, removeOnCompletion: false, additive: true, completion: { [weak layer, weak tintContainerLayer] _ in
|
||||||
layer?.removeFromSuperlayer()
|
layer?.removeFromSuperlayer()
|
||||||
|
tintContainerLayer?.removeFromSuperlayer()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5073,7 +5155,7 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
itemSelectionLayer.frame = baseItemFrame
|
itemTransition.setFrame(layer: itemSelectionLayer, frame: baseItemFrame)
|
||||||
|
|
||||||
itemLayer.transform = CATransform3DMakeScale(0.8, 0.8, 1.0)
|
itemLayer.transform = CATransform3DMakeScale(0.8, 0.8, 1.0)
|
||||||
} else {
|
} else {
|
||||||
@ -5455,10 +5537,10 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
longTapRecognizer.isEnabled = component.enableLongPress
|
longTapRecognizer.isEnabled = component.enableLongPress
|
||||||
}
|
}
|
||||||
if let tapRecognizer = self.tapRecognizer {
|
if let tapRecognizer = self.tapRecognizer {
|
||||||
tapRecognizer.isEnabled = component.enableLongPress
|
tapRecognizer.isEnabled = component.enableLongPress || component.inputInteractionHolder.inputInteraction?.peekBehavior != nil
|
||||||
}
|
}
|
||||||
if let contextGesture = self.contextGesture {
|
if let contextGesture = self.contextGesture {
|
||||||
contextGesture.isEnabled = !component.enableLongPress
|
contextGesture.isEnabled = !component.enableLongPress && component.inputInteractionHolder.inputInteraction?.peekBehavior == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if let shimmerHostView = self.shimmerHostView {
|
if let shimmerHostView = self.shimmerHostView {
|
||||||
@ -6085,6 +6167,10 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if let groupIndex = itemGroupIndexById[groupId] {
|
if let groupIndex = itemGroupIndexById[groupId] {
|
||||||
|
if itemGroups[groupIndex].items.count >= (5 + 8) * 8 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
itemGroups[groupIndex].items.append(resultItem)
|
itemGroups[groupIndex].items.append(resultItem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -6128,6 +6214,10 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if let groupIndex = itemGroupIndexById[groupId] {
|
if let groupIndex = itemGroupIndexById[groupId] {
|
||||||
|
if itemGroups[groupIndex].items.count >= (5 + 8) * 8 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
itemGroups[groupIndex].items.append(resultItem)
|
itemGroups[groupIndex].items.append(resultItem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1004,15 +1004,22 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||||||
|
|
||||||
let availableReactions = context.engine.stickers.availableReactions()
|
let availableReactions = context.engine.stickers.availableReactions()
|
||||||
|
|
||||||
let defaultReaction = context.account.postbox.preferencesView(keys: [PreferencesKeys.reactionSettings])
|
let defaultReaction = combineLatest(
|
||||||
|> map { preferencesView -> MessageReaction.Reaction? in
|
context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId)),
|
||||||
|
context.account.postbox.preferencesView(keys: [PreferencesKeys.reactionSettings])
|
||||||
|
)
|
||||||
|
|> map { peer, preferencesView -> MessageReaction.Reaction? in
|
||||||
let reactionSettings: ReactionSettings
|
let reactionSettings: ReactionSettings
|
||||||
if let entry = preferencesView.values[PreferencesKeys.reactionSettings], let value = entry.get(ReactionSettings.self) {
|
if let entry = preferencesView.values[PreferencesKeys.reactionSettings], let value = entry.get(ReactionSettings.self) {
|
||||||
reactionSettings = value
|
reactionSettings = value
|
||||||
} else {
|
} else {
|
||||||
reactionSettings = .default
|
reactionSettings = .default
|
||||||
}
|
}
|
||||||
return reactionSettings.quickReaction
|
var hasPremium = false
|
||||||
|
if case let .user(user) = peer {
|
||||||
|
hasPremium = user.isPremium
|
||||||
|
}
|
||||||
|
return reactionSettings.effectiveQuickReaction(hasPremium: hasPremium)
|
||||||
}
|
}
|
||||||
|> distinctUntilChanged
|
|> distinctUntilChanged
|
||||||
|
|
||||||
|
|||||||
@ -3103,10 +3103,12 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
|
|
||||||
strongSelf.emojiStatusSelectionController?.dismiss()
|
strongSelf.emojiStatusSelectionController?.dismiss()
|
||||||
var selectedItems = Set<MediaId>()
|
var selectedItems = Set<MediaId>()
|
||||||
var topStatusTitle = "Long tap to set a timer"
|
var currentSelectedFileId: Int64?
|
||||||
|
var topStatusTitle = strongSelf.presentationData.strings.PeerStatusSetup_NoTimerTitle
|
||||||
if let peer = strongSelf.data?.peer {
|
if let peer = strongSelf.data?.peer {
|
||||||
if let user = peer as? TelegramUser, let emojiStatus = user.emojiStatus {
|
if let user = peer as? TelegramUser, let emojiStatus = user.emojiStatus {
|
||||||
selectedItems.insert(MediaId(namespace: Namespaces.Media.CloudFile, id: emojiStatus.fileId))
|
selectedItems.insert(MediaId(namespace: Namespaces.Media.CloudFile, id: emojiStatus.fileId))
|
||||||
|
currentSelectedFileId = emojiStatus.fileId
|
||||||
|
|
||||||
if let timestamp = emojiStatus.expirationDate {
|
if let timestamp = emojiStatus.expirationDate {
|
||||||
topStatusTitle = peerStatusExpirationString(statusTimestamp: timestamp, relativeTo: Int32(Date().timeIntervalSince1970), strings: strongSelf.presentationData.strings, dateTimeFormat: strongSelf.presentationData.dateTimeFormat)
|
topStatusTitle = peerStatusExpirationString(statusTimestamp: timestamp, relativeTo: Int32(Date().timeIntervalSince1970), strings: strongSelf.presentationData.strings, dateTimeFormat: strongSelf.presentationData.dateTimeFormat)
|
||||||
@ -3132,6 +3134,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
selectedItems: selectedItems,
|
selectedItems: selectedItems,
|
||||||
topStatusTitle: topStatusTitle
|
topStatusTitle: topStatusTitle
|
||||||
),
|
),
|
||||||
|
currentSelection: currentSelectedFileId,
|
||||||
destinationItemView: { [weak sourceView] in
|
destinationItemView: { [weak sourceView] in
|
||||||
return sourceView
|
return sourceView
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user