mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +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()
|
||||
var selectedItems = Set<MediaId>()
|
||||
var topStatusTitle = self.presentationData.strings.PeerStatusSetup_NoTimerTitle
|
||||
var currentSelection: Int64?
|
||||
if let peerStatus = self.titleView.title.peerStatus, case let .emoji(emojiStatus) = peerStatus {
|
||||
selectedItems.insert(MediaId(namespace: Namespaces.Media.CloudFile, id: emojiStatus.fileId))
|
||||
currentSelection = emojiStatus.fileId
|
||||
|
||||
if let timestamp = emojiStatus.expirationDate {
|
||||
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,
|
||||
topStatusTitle: topStatusTitle
|
||||
),
|
||||
currentSelection: currentSelection,
|
||||
destinationItemView: { [weak sourceView] in
|
||||
return sourceView
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ swift_library(
|
||||
"//submodules/TelegramUI/Components/AnimationCache:AnimationCache",
|
||||
"//submodules/TelegramUI/Components/MultiAnimationRenderer:MultiAnimationRenderer",
|
||||
"//submodules/TelegramUI/Components/EmojiTextAttachmentView:EmojiTextAttachmentView",
|
||||
"//submodules/TelegramUI/Components/EmojiStatusComponent:EmojiStatusComponent",
|
||||
"//submodules/TextFormat:TextFormat",
|
||||
],
|
||||
visibility = [
|
||||
|
@ -16,6 +16,7 @@ import AnimationCache
|
||||
import MultiAnimationRenderer
|
||||
import EmojiTextAttachmentView
|
||||
import TextFormat
|
||||
import EmojiStatusComponent
|
||||
|
||||
private let avatarFont = avatarPlaceholderFont(size: 16.0)
|
||||
|
||||
@ -357,7 +358,7 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
||||
let highlightBackgroundNode: ASDisplayNode
|
||||
let avatarNode: AvatarNode
|
||||
let titleLabelNode: ImmediateTextNode
|
||||
var credibilityIconNode: ASImageNode?
|
||||
var credibilityIconView: ComponentView<Empty>?
|
||||
let separatorNode: ASDisplayNode
|
||||
|
||||
private var reactionLayer: InlineStickerItemLayer?
|
||||
@ -519,20 +520,46 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
||||
}
|
||||
|
||||
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: self.context.currentAppConfiguration.with { $0 })
|
||||
var currentCredibilityIconImage: UIImage?
|
||||
var currentCredibilityIcon: EmojiStatusComponent.Content?
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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
|
||||
if let currentCredibilityIconImage = currentCredibilityIconImage {
|
||||
additionalTitleInset += 3.0 + currentCredibilityIconImage.size.width
|
||||
if let credibilityIconSize = credibilityIconSize {
|
||||
additionalTitleInset += 3.0 + credibilityIconSize.width
|
||||
}
|
||||
|
||||
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)
|
||||
self.titleLabelNode.frame = titleFrame
|
||||
|
||||
if let currentCredibilityIconImage = currentCredibilityIconImage {
|
||||
let iconNode: ASImageNode
|
||||
if let current = self.credibilityIconNode {
|
||||
iconNode = current
|
||||
} else {
|
||||
iconNode = ASImageNode()
|
||||
iconNode.isLayerBacked = true
|
||||
iconNode.displaysAsynchronously = false
|
||||
iconNode.displayWithoutProcessing = true
|
||||
self.addSubnode(iconNode)
|
||||
self.credibilityIconNode = iconNode
|
||||
if let credibilityIconView = self.credibilityIconView, let credibilityIconSize = credibilityIconSize {
|
||||
if let credibilityIconComponentView = credibilityIconView.view {
|
||||
if credibilityIconComponentView.superview == nil {
|
||||
self.view.addSubview(credibilityIconComponentView)
|
||||
}
|
||||
credibilityIconComponentView.frame = CGRect(origin: CGPoint(x: titleFrame.maxX + 4.0, y: floorToScreenPixels(titleFrame.midY - credibilityIconSize.height / 2.0) + 1.0 - UIScreenPixel), size: credibilityIconSize)
|
||||
}
|
||||
iconNode.image = currentCredibilityIconImage
|
||||
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)
|
||||
} else if let credibilityIconNode = self.credibilityIconNode {
|
||||
self.credibilityIconNode = nil
|
||||
credibilityIconNode.removeFromSupernode()
|
||||
} else if let credibilityIconView = self.credibilityIconView {
|
||||
self.credibilityIconView = nil
|
||||
credibilityIconView.view?.removeFromSuperview()
|
||||
}
|
||||
|
||||
let reactionSize = CGSize(width: 22.0, height: 22.0)
|
||||
|
@ -232,8 +232,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
private var customPosition: CGPoint?
|
||||
private let contentContainerNode: ContextContentContainerNode
|
||||
private var actionsContainerNode: ContextActionsContainerNode
|
||||
private var reactionContextNode: ReactionContextNode?
|
||||
private var reactionContextNodeIsAnimatingOut = false
|
||||
|
||||
private var didCompleteAnimationIn = false
|
||||
private var initialContinueGesturePoint: CGPoint?
|
||||
@ -401,15 +399,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
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
|
||||
highlightedActionNode.performAction()
|
||||
}
|
||||
if let highlightedReaction = strongSelf.highlightedReaction {
|
||||
strongSelf.reactionContextNode?.performReactionSelection(reaction: highlightedReaction, isLarge: false)
|
||||
}
|
||||
} else {
|
||||
if let highlightedActionNode = strongSelf.highlightedActionNode {
|
||||
strongSelf.highlightedActionNode = nil
|
||||
@ -479,15 +465,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
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
|
||||
highlightedActionNode.performAction()
|
||||
}
|
||||
|
||||
if let highlightedReaction = strongSelf.highlightedReaction {
|
||||
strongSelf.reactionContextNode?.performReactionSelection(reaction: highlightedReaction, isLarge: false)
|
||||
}
|
||||
} else {
|
||||
if let highlightedActionNode = strongSelf.highlightedActionNode {
|
||||
strongSelf.highlightedActionNode = nil
|
||||
@ -593,15 +566,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
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:
|
||||
if let presentationNode = self.presentationNode {
|
||||
@ -611,10 +575,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
self.highlightedActionNode = nil
|
||||
highlightedActionNode.setIsHighlighted(false)
|
||||
}
|
||||
|
||||
if let _ = self.reactionContextNode {
|
||||
self.highlightedReaction = nil
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
@ -949,10 +909,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
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)
|
||||
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
|
||||
@ -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.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))
|
||||
} else {
|
||||
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.willUpdateIsExtractedToContextPreview?(false, .animated(duration: 0.2, curve: .easeInOut))
|
||||
|
||||
if let reactionContextNode = self.reactionContextNode {
|
||||
reactionContextNode.animateOut(to: nil, animatingOutToReaction: self.reactionContextNodeIsAnimatingOut)
|
||||
}
|
||||
}
|
||||
case let .controller(source):
|
||||
guard let maybeContentNode = self.contentContainerNode.contentNode, case let .controller(controller) = maybeContentNode else {
|
||||
@ -1449,10 +1397,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
completedContentNode = true
|
||||
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 {
|
||||
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() {
|
||||
@ -1520,13 +1460,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
|
||||
self.currentItems = items
|
||||
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 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))
|
||||
|
||||
let actionsSideInset: CGFloat = layout.safeInsets.left + 12.0
|
||||
var contentTopInset: CGFloat = max(11.0, layout.statusBarHeight ?? 0.0)
|
||||
|
||||
if let _ = self.reactionContextNode {
|
||||
contentTopInset += 34.0
|
||||
}
|
||||
let contentTopInset: CGFloat = max(11.0, layout.statusBarHeight ?? 0.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)
|
||||
|
||||
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):
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
var maybePassthrough: ContextController.HandledTouchEvent?
|
||||
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)
|
||||
|
||||
let bottomInset = layout.insets(options: [.input]).bottom
|
||||
var isCoveredByInput = false
|
||||
if reactionAnchorRect.minY > 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
|
||||
} else {
|
||||
|
@ -118,6 +118,7 @@ final class ReactionContextBackgroundNode: ASDisplayNode {
|
||||
cloudSourcePoint: CGFloat,
|
||||
isLeftAligned: Bool,
|
||||
isMinimized: Bool,
|
||||
isCoveredByInput: Bool,
|
||||
transition: ContainedViewLayoutTransition
|
||||
) {
|
||||
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.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(view: self.backgroundView, frame: contentBounds, beginWithCurrentState: true)
|
||||
|
@ -200,7 +200,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
private var highlightedByHover = false
|
||||
private var didTriggerExpandedReaction: Bool = false
|
||||
private var continuousHaptic: Any?
|
||||
private var validLayout: (CGSize, UIEdgeInsets, CGRect)?
|
||||
private var validLayout: (CGSize, UIEdgeInsets, CGRect, Bool)?
|
||||
private var isLeftAligned: Bool = true
|
||||
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) {
|
||||
self.updateLayout(size: size, insets: insets, anchorRect: anchorRect, isAnimatingOut: isAnimatingOut, transition: transition, animateInFromAnchorRect: nil, animateOutToAnchorRect: nil)
|
||||
public func updateLayout(size: CGSize, insets: UIEdgeInsets, anchorRect: CGRect, isCoveredByInput: Bool, isAnimatingOut: Bool, transition: ContainedViewLayoutTransition) {
|
||||
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) {
|
||||
@ -544,8 +544,8 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
if self.extensionDistance != distance {
|
||||
self.extensionDistance = distance
|
||||
|
||||
if let (size, insets, anchorRect) = self.validLayout {
|
||||
self.updateLayout(size: size, insets: insets, anchorRect: anchorRect, isAnimatingOut: false, transition: .immediate, animateInFromAnchorRect: nil, animateOutToAnchorRect: nil)
|
||||
if let (size, insets, anchorRect, isCoveredByInput) = self.validLayout {
|
||||
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 {
|
||||
expandItemView.updateTheme(theme: self.presentationData.theme)
|
||||
}
|
||||
|
||||
self.validLayout = (size, insets, anchorRect)
|
||||
self.validLayout = (size, insets, anchorRect, isCoveredByInput)
|
||||
|
||||
let externalSideInset: CGFloat = 4.0
|
||||
let sideInset: CGFloat = 6.0
|
||||
@ -1095,6 +1095,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
cloudSourcePoint: cloudSourcePoint - visualBackgroundFrame.minX,
|
||||
isLeftAligned: isLeftAligned,
|
||||
isMinimized: self.highlightedReaction != nil && !self.highlightedByHover,
|
||||
isCoveredByInput: isCoveredByInput,
|
||||
transition: transition
|
||||
)
|
||||
|
||||
@ -1276,11 +1277,13 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
}
|
||||
strongSelf.requestUpdateOverlayWantsToBeBelowKeyboard(transition.containedViewLayoutTransition)
|
||||
},
|
||||
updateSearchQuery: { [weak self] query in
|
||||
updateSearchQuery: { [weak self] rawQuery in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
let query = rawQuery.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
|
||||
if query.isEmpty {
|
||||
strongSelf.emojiSearchDisposable.set(nil)
|
||||
strongSelf.emojiSearchResult.set(.single(nil))
|
||||
@ -1288,7 +1291,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
let context = strongSelf.context
|
||||
|
||||
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") {
|
||||
signal = signal
|
||||
|> mapToSignal { keywords in
|
||||
@ -1374,6 +1377,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
}
|
||||
|
||||
strongSelf.emojiSearchDisposable.set((resultSignal
|
||||
|> delay(0.15, queue: .mainQueue())
|
||||
|> deliverOnMainQueue).start(next: { result in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
@ -1396,8 +1400,8 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
public func animateIn(from sourceAnchorRect: CGRect) {
|
||||
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
|
||||
|
||||
if let (size, insets, anchorRect) = self.validLayout {
|
||||
self.updateLayout(size: size, insets: insets, anchorRect: anchorRect, isAnimatingOut: false, transition: .immediate, animateInFromAnchorRect: sourceAnchorRect, animateOutToAnchorRect: nil)
|
||||
if let (size, insets, anchorRect, isCoveredByInput) = self.validLayout {
|
||||
self.updateLayout(size: size, insets: insets, anchorRect: anchorRect, isCoveredByInput: isCoveredByInput, isAnimatingOut: false, transition: .immediate, animateInFromAnchorRect: sourceAnchorRect, animateOutToAnchorRect: nil)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
if let targetAnchorRect = targetAnchorRect, let (size, insets, anchorRect) = self.validLayout {
|
||||
self.updateLayout(size: size, insets: insets, anchorRect: anchorRect, isAnimatingOut: false, transition: .immediate, animateInFromAnchorRect: nil, animateOutToAnchorRect: targetAnchorRect)
|
||||
if let targetAnchorRect = targetAnchorRect, let (size, insets, anchorRect, isCoveredByInput) = self.validLayout {
|
||||
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()
|
||||
}
|
||||
|
||||
if let (size, insets, anchorRect) = self.validLayout {
|
||||
self.updateLayout(size: size, insets: insets, anchorRect: anchorRect, isAnimatingOut: false, transition: .animated(duration: longPressDuration, curve: .linear), animateInFromAnchorRect: nil, animateOutToAnchorRect: nil, animateReactionHighlight: true)
|
||||
if let (size, insets, anchorRect, isCoveredByInput) = self.validLayout {
|
||||
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()
|
||||
@ -1950,8 +1954,8 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
self.continuousHaptic = nil
|
||||
|
||||
self.highlightedReaction = nil
|
||||
if let (size, insets, anchorRect) = 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)
|
||||
if let (size, insets, anchorRect, isCoveredByInput) = self.validLayout {
|
||||
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:
|
||||
self.longPressTimer?.invalidate()
|
||||
@ -2027,8 +2031,8 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
self.hapticFeedback?.tap()
|
||||
}
|
||||
|
||||
if let (size, insets, anchorRect) = 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)
|
||||
if let (size, insets, anchorRect, isCoveredByInput) = self.validLayout {
|
||||
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 {
|
||||
self.performReactionSelection(reaction: highlightedReaction, isLarge: isLarge)
|
||||
} else {
|
||||
if let (size, insets, anchorRect) = 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)
|
||||
if let (size, insets, anchorRect, isCoveredByInput) = self.validLayout {
|
||||
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?) {
|
||||
self.highlightedReaction = value
|
||||
if let (size, insets, anchorRect) = 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)
|
||||
if let (size, insets, anchorRect, isCoveredByInput) = self.validLayout {
|
||||
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,
|
||||
nameDisplayOrder: presentationData.nameDisplayOrder,
|
||||
availableReactions: availableReactions,
|
||||
reaction: state.hasReaction ? reactionSettings.quickReaction : nil
|
||||
reaction: state.hasReaction ? reactionSettings.effectiveQuickReaction(hasPremium: isPremium) : nil
|
||||
))
|
||||
entries.append(.demoDescription(presentationData.strings.Settings_QuickReactionSetup_DemoInfo))
|
||||
|
||||
@ -354,6 +354,7 @@ public func quickReactionSetupController(
|
||||
chatPeerId: context.account.peerId,
|
||||
selectedItems: selectedItems
|
||||
),
|
||||
currentSelection: nil,
|
||||
destinationItemView: { [weak sourceItemNode] in
|
||||
return sourceItemNode?.iconView
|
||||
}
|
||||
|
@ -927,16 +927,22 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
|
||||
case .general, .modal:
|
||||
featured.set(context.account.viewTracker.featuredStickerPacks())
|
||||
archivedPromise.set(.single(archivedPacks) |> then(context.engine.stickers.archivedStickerPacks() |> map(Optional.init)))
|
||||
quickReaction = context.account.postbox.preferencesView(keys: [PreferencesKeys.reactionSettings])
|
||||
|> map { preferencesView -> MessageReaction.Reaction? in
|
||||
quickReaction = combineLatest(
|
||||
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
|
||||
if let entry = preferencesView.values[PreferencesKeys.reactionSettings], let value = entry.get(ReactionSettings.self) {
|
||||
reactionSettings = value
|
||||
} else {
|
||||
reactionSettings = .default
|
||||
}
|
||||
|
||||
return reactionSettings.quickReaction
|
||||
var hasPremium = false
|
||||
if case let .user(user) = peer {
|
||||
hasPremium = user.isPremium
|
||||
}
|
||||
return reactionSettings.effectiveQuickReaction(hasPremium: hasPremium)
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
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) {
|
||||
transaction.updatePreferencesEntry(key: PreferencesKeys.reactionSettings, { current in
|
||||
let previous = current?.get(ReactionSettings.self) ?? ReactionSettings.default
|
||||
|
@ -243,6 +243,8 @@ public final class EmojiStatusSelectionController: ViewController {
|
||||
private var presentationData: PresentationData
|
||||
private var validLayout: ContainerViewLayout?
|
||||
|
||||
private let currentSelection: Int64?
|
||||
|
||||
private var emojiContentDisposable: Disposable?
|
||||
private var emojiContent: EmojiPagerContentComponent?
|
||||
private var freezeUpdates: Bool = false
|
||||
@ -263,9 +265,10 @@ public final class EmojiStatusSelectionController: ViewController {
|
||||
private var isAnimatingOut: 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.context = context
|
||||
self.currentSelection = currentSelection
|
||||
|
||||
if let sourceView = sourceView {
|
||||
self.globalSourceRect = sourceView.convert(sourceView.bounds, to: nil)
|
||||
@ -920,6 +923,13 @@ public final class EmojiStatusSelectionController: ViewController {
|
||||
} else {
|
||||
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 snapshotView = destinationView.snapshotView(afterScreenUpdates: false) {
|
||||
snapshotView.frame = destinationView.frame
|
||||
@ -1026,6 +1036,7 @@ public final class EmojiStatusSelectionController: ViewController {
|
||||
private let context: AccountContext
|
||||
private weak var sourceView: UIView?
|
||||
private let emojiContent: Signal<EmojiPagerContentComponent, NoError>
|
||||
private let currentSelection: Int64?
|
||||
private let mode: Mode
|
||||
private let destinationItemView: () -> UIView?
|
||||
|
||||
@ -1034,11 +1045,12 @@ public final class EmojiStatusSelectionController: ViewController {
|
||||
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.mode = mode
|
||||
self.sourceView = sourceView
|
||||
self.emojiContent = emojiContent
|
||||
self.currentSelection = currentSelection
|
||||
self.destinationItemView = destinationItemView
|
||||
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
@ -1068,7 +1080,7 @@ public final class EmojiStatusSelectionController: ViewController {
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
@ -1551,6 +1551,11 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate {
|
||||
|
||||
private let searchIconView: UIImageView
|
||||
private let searchIconTintView: UIImageView
|
||||
|
||||
private let clearIconView: UIImageView
|
||||
private let clearIconTintView: UIImageView
|
||||
private let clearIconButton: HighlightTrackingButton
|
||||
|
||||
private let tintTextView: ComponentView<Empty>
|
||||
private let textView: ComponentView<Empty>
|
||||
private let cancelButtonTintTitle: ComponentView<Empty>
|
||||
@ -1580,6 +1585,13 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate {
|
||||
self.searchIconView = 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.textView = ComponentView()
|
||||
self.cancelButtonTintTitle = ComponentView()
|
||||
@ -1594,6 +1606,10 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate {
|
||||
self.addSubview(self.searchIconView)
|
||||
self.tintContainerView.addSubview(self.searchIconTintView)
|
||||
|
||||
self.addSubview(self.clearIconView)
|
||||
self.tintContainerView.addSubview(self.clearIconTintView)
|
||||
self.addSubview(self.clearIconButton)
|
||||
|
||||
self.addSubview(self.cancelButton)
|
||||
self.clipsToBounds = true
|
||||
|
||||
@ -1627,6 +1643,23 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate {
|
||||
}
|
||||
}
|
||||
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) {
|
||||
@ -1641,7 +1674,7 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate {
|
||||
|
||||
let textField = EmojiSearchTextField(frame: textFieldFrame)
|
||||
self.textField = textField
|
||||
self.addSubview(textField)
|
||||
self.insertSubview(textField, belowSubview: self.clearIconView)
|
||||
textField.delegate = self
|
||||
textField.addTarget(self, action: #selector(self.textFieldChanged(_:)), for: .editingChanged)
|
||||
}
|
||||
@ -1655,6 +1688,10 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate {
|
||||
@objc private func cancelPressed() {
|
||||
self.updateQuery("")
|
||||
|
||||
self.clearIconView.isHidden = true
|
||||
self.clearIconTintView.isHidden = true
|
||||
self.clearIconButton.isHidden = true
|
||||
|
||||
if let textField = self.textField {
|
||||
self.textField = nil
|
||||
|
||||
@ -1664,6 +1701,15 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate {
|
||||
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) {
|
||||
}
|
||||
|
||||
@ -1673,7 +1719,13 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate {
|
||||
@objc private func textFieldChanged(_ textField: UITextField) {
|
||||
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) {
|
||||
@ -1700,6 +1752,9 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate {
|
||||
if self.params?.theme !== theme {
|
||||
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.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
|
||||
@ -1785,6 +1840,13 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate {
|
||||
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 textComponentView.superview == nil {
|
||||
self.addSubview(textComponentView)
|
||||
@ -3856,7 +3918,7 @@ public final class EmojiPagerContentComponent: Component {
|
||||
for (id, layer) in self.visibleItemLayers {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
if self.visibleItemLayers[id] != nil {
|
||||
continue
|
||||
@ -3990,6 +4056,19 @@ public final class EmojiPagerContentComponent: Component {
|
||||
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 {
|
||||
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
|
||||
layer.frame = layerAndFrame.1.offsetBy(dx: 0.0, dy: self.scrollView.bounds.minY)
|
||||
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()
|
||||
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)
|
||||
} else {
|
||||
@ -5455,10 +5537,10 @@ public final class EmojiPagerContentComponent: Component {
|
||||
longTapRecognizer.isEnabled = component.enableLongPress
|
||||
}
|
||||
if let tapRecognizer = self.tapRecognizer {
|
||||
tapRecognizer.isEnabled = component.enableLongPress
|
||||
tapRecognizer.isEnabled = component.enableLongPress || component.inputInteractionHolder.inputInteraction?.peekBehavior != nil
|
||||
}
|
||||
if let contextGesture = self.contextGesture {
|
||||
contextGesture.isEnabled = !component.enableLongPress
|
||||
contextGesture.isEnabled = !component.enableLongPress && component.inputInteractionHolder.inputInteraction?.peekBehavior == nil
|
||||
}
|
||||
|
||||
if let shimmerHostView = self.shimmerHostView {
|
||||
@ -6085,6 +6167,10 @@ public final class EmojiPagerContentComponent: Component {
|
||||
)
|
||||
|
||||
if let groupIndex = itemGroupIndexById[groupId] {
|
||||
if itemGroups[groupIndex].items.count >= (5 + 8) * 8 {
|
||||
break
|
||||
}
|
||||
|
||||
itemGroups[groupIndex].items.append(resultItem)
|
||||
}
|
||||
}
|
||||
@ -6128,6 +6214,10 @@ public final class EmojiPagerContentComponent: Component {
|
||||
)
|
||||
|
||||
if let groupIndex = itemGroupIndexById[groupId] {
|
||||
if itemGroups[groupIndex].items.count >= (5 + 8) * 8 {
|
||||
break
|
||||
}
|
||||
|
||||
itemGroups[groupIndex].items.append(resultItem)
|
||||
}
|
||||
}
|
||||
|
@ -1004,15 +1004,22 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
|
||||
let availableReactions = context.engine.stickers.availableReactions()
|
||||
|
||||
let defaultReaction = context.account.postbox.preferencesView(keys: [PreferencesKeys.reactionSettings])
|
||||
|> map { preferencesView -> MessageReaction.Reaction? in
|
||||
let defaultReaction = combineLatest(
|
||||
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
|
||||
if let entry = preferencesView.values[PreferencesKeys.reactionSettings], let value = entry.get(ReactionSettings.self) {
|
||||
reactionSettings = value
|
||||
} else {
|
||||
reactionSettings = .default
|
||||
}
|
||||
return reactionSettings.quickReaction
|
||||
var hasPremium = false
|
||||
if case let .user(user) = peer {
|
||||
hasPremium = user.isPremium
|
||||
}
|
||||
return reactionSettings.effectiveQuickReaction(hasPremium: hasPremium)
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
|
||||
|
@ -3103,10 +3103,12 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
|
||||
strongSelf.emojiStatusSelectionController?.dismiss()
|
||||
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 user = peer as? TelegramUser, let emojiStatus = user.emojiStatus {
|
||||
selectedItems.insert(MediaId(namespace: Namespaces.Media.CloudFile, id: emojiStatus.fileId))
|
||||
currentSelectedFileId = emojiStatus.fileId
|
||||
|
||||
if let timestamp = emojiStatus.expirationDate {
|
||||
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,
|
||||
topStatusTitle: topStatusTitle
|
||||
),
|
||||
currentSelection: currentSelectedFileId,
|
||||
destinationItemView: { [weak sourceView] in
|
||||
return sourceView
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user