mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 14:20:20 +00:00
Tag saved message
This commit is contained in:
@@ -19,6 +19,7 @@ import AccountContext
|
||||
import AnimatedAvatarSetNode
|
||||
import ComponentFlow
|
||||
import EmojiStatusComponent
|
||||
import TextNodeWithEntities
|
||||
|
||||
final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
private let presentationData: PresentationData
|
||||
@@ -40,7 +41,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
private var stickerOffset: CGPoint?
|
||||
private var emojiStatus: ComponentView<Empty>?
|
||||
private let titleNode: ImmediateTextNode
|
||||
private let textNode: ImmediateTextNode
|
||||
private let textNode: ImmediateTextNodeWithEntities
|
||||
private let buttonNode: HighlightTrackingButtonNode
|
||||
private let undoButtonTextNode: ImmediateTextNode
|
||||
private let undoButtonNode: HighlightTrackingButtonNode
|
||||
@@ -52,10 +53,13 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
private var content: UndoOverlayContent
|
||||
private let blurred: Bool
|
||||
|
||||
private let additionalView: UndoOverlayControllerAdditionalView?
|
||||
|
||||
private let effectView: UIView
|
||||
|
||||
private let animationBackgroundColor: UIColor
|
||||
|
||||
private var isTimeoutDisabled: Bool = false
|
||||
private var originalRemainingSeconds: Double
|
||||
private var remainingSeconds: Double
|
||||
private var timer: SwiftSignalKit.Timer?
|
||||
@@ -64,13 +68,15 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
|
||||
private var fetchResourceDisposable: Disposable?
|
||||
|
||||
init(presentationData: PresentationData, content: UndoOverlayContent, elevatedLayout: Bool, placementPosition: UndoOverlayController.Position, blurred: Bool, action: @escaping (UndoOverlayAction) -> Bool, dismiss: @escaping () -> Void) {
|
||||
init(presentationData: PresentationData, content: UndoOverlayContent, elevatedLayout: Bool, placementPosition: UndoOverlayController.Position, blurred: Bool, additionalView: (() -> UndoOverlayControllerAdditionalView?)?, action: @escaping (UndoOverlayAction) -> Bool, dismiss: @escaping () -> Void) {
|
||||
self.presentationData = presentationData
|
||||
self.elevatedLayout = elevatedLayout
|
||||
self.placementPosition = placementPosition
|
||||
self.blurred = blurred
|
||||
self.content = content
|
||||
|
||||
self.additionalView = additionalView?()
|
||||
|
||||
self.action = action
|
||||
self.dismiss = dismiss
|
||||
|
||||
@@ -81,7 +87,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
self.titleNode.displaysAsynchronously = false
|
||||
self.titleNode.maximumNumberOfLines = 0
|
||||
|
||||
self.textNode = ImmediateTextNode()
|
||||
self.textNode = ImmediateTextNodeWithEntities()
|
||||
self.textNode.displaysAsynchronously = false
|
||||
self.textNode.maximumNumberOfLines = 0
|
||||
|
||||
@@ -1153,6 +1159,39 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
} else {
|
||||
displayUndo = false
|
||||
}
|
||||
case let .messageTagged(context, customEmoji, isBuiltinReaction):
|
||||
self.avatarNode = nil
|
||||
self.iconNode = nil
|
||||
self.iconCheckNode = nil
|
||||
self.animationNode = AnimationNode(animation: "anim_savedmessages", colors: [:], scale: 0.066)
|
||||
self.animatedStickerNode = nil
|
||||
|
||||
let rawText = presentationData.strings.Chat_ToastMessageTagged_Text(".")
|
||||
let attributedText = NSMutableAttributedString(string: rawText.string, font: Font.regular(14.0), textColor: .white)
|
||||
for range in rawText.ranges {
|
||||
attributedText.addAttributes([ChatTextInputAttributes.customEmoji: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: customEmoji.fileId.id, file: customEmoji, custom: nil)], range: range.range)
|
||||
}
|
||||
self.textNode.customItemLayout = { size, _ in
|
||||
if isBuiltinReaction {
|
||||
return CGSize(width: size.width * 2.0, height: size.height * 2.0)
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
self.textNode.arguments = TextNodeWithEntities.Arguments(
|
||||
context: context,
|
||||
cache: context.animationCache,
|
||||
renderer: context.animationRenderer,
|
||||
placeholderColor: UIColor(white: 1.0, alpha: 0.1),
|
||||
attemptSynchronous: false
|
||||
)
|
||||
self.textNode.visibility = true
|
||||
|
||||
self.textNode.attributedText = attributedText
|
||||
self.textNode.maximumNumberOfLines = 2
|
||||
|
||||
displayUndo = false
|
||||
self.originalRemainingSeconds = 3
|
||||
}
|
||||
|
||||
self.remainingSeconds = self.originalRemainingSeconds
|
||||
@@ -1181,7 +1220,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
switch content {
|
||||
case .removedChat:
|
||||
self.panelWrapperNode.addSubnode(self.timerTextNode)
|
||||
case .archivedChat, .hidArchive, .revealedArchive, .autoDelete, .succeed, .emoji, .swipeToReply, .actionSucceeded, .stickersModified, .chatAddedToFolder, .chatRemovedFromFolder, .messagesUnpinned, .setProximityAlert, .invitedToVoiceChat, .linkCopied, .banned, .importedMessage, .audioRate, .forward, .gigagroupConversion, .linkRevoked, .voiceChatRecording, .voiceChatFlag, .voiceChatCanSpeak, .copy, .mediaSaved, .paymentSent, .image, .inviteRequestSent, .notificationSoundAdded, .universal, .premiumPaywall, .peers:
|
||||
case .archivedChat, .hidArchive, .revealedArchive, .autoDelete, .succeed, .emoji, .swipeToReply, .actionSucceeded, .stickersModified, .chatAddedToFolder, .chatRemovedFromFolder, .messagesUnpinned, .setProximityAlert, .invitedToVoiceChat, .linkCopied, .banned, .importedMessage, .audioRate, .forward, .gigagroupConversion, .linkRevoked, .voiceChatRecording, .voiceChatFlag, .voiceChatCanSpeak, .copy, .mediaSaved, .paymentSent, .image, .inviteRequestSent, .notificationSoundAdded, .universal, .premiumPaywall, .peers, .messageTagged:
|
||||
if self.textNode.tapAttributeAction != nil || displayUndo {
|
||||
self.isUserInteractionEnabled = true
|
||||
} else {
|
||||
@@ -1251,6 +1290,24 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
self.animatedStickerNode?.started = { [weak self] in
|
||||
self?.stillStickerNode?.isHidden = true
|
||||
}
|
||||
|
||||
if let additionalView = self.additionalView {
|
||||
additionalView.interaction = UndoOverlayControllerAdditionalViewInteraction(disableTimeout: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.isTimeoutDisabled = true
|
||||
self.timer?.invalidate()
|
||||
self.remainingSeconds = self.originalRemainingSeconds
|
||||
self.checkTimer()
|
||||
}, dismiss: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.dismiss()
|
||||
})
|
||||
self.view.addSubview(additionalView)
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
@@ -1265,6 +1322,16 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
}
|
||||
|
||||
self.panelNode.view.addSubview(self.effectView)
|
||||
|
||||
if self.additionalView != nil {
|
||||
self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func tapGesture(_ recognizer: UITapGestureRecognizer) {
|
||||
if case .ended = recognizer.state {
|
||||
self.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func buttonPressed() {
|
||||
@@ -1318,11 +1385,13 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
self.containerLayoutUpdated(layout: validLayout, transition: .immediate)
|
||||
}
|
||||
}
|
||||
let timer = SwiftSignalKit.Timer(timeout: 0.5, repeat: false, completion: { [weak self] in
|
||||
self?.checkTimer()
|
||||
}, queue: .mainQueue())
|
||||
self.timer = timer
|
||||
timer.start()
|
||||
if !self.isTimeoutDisabled {
|
||||
let timer = SwiftSignalKit.Timer(timeout: 0.5, repeat: false, completion: { [weak self] in
|
||||
self?.checkTimer()
|
||||
}, queue: .mainQueue())
|
||||
self.timer = timer
|
||||
timer.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1566,6 +1635,12 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
let avatarsFrame = CGRect(origin: CGPoint(x: 13.0, y: floor((contentHeight - multiAvatarsSize.height) / 2.0) + verticalOffset), size: multiAvatarsSize)
|
||||
transition.updateFrame(node: multiAvatarsNode, frame: avatarsFrame)
|
||||
}
|
||||
|
||||
if let additionalView = self.additionalView {
|
||||
let additionalViewFrame = CGRect(origin: CGPoint(x: 0.0, y: panelWrapperFrame.maxY), size: CGSize(width: layout.size.width, height: layout.size.height - insets.bottom - panelWrapperFrame.maxY))
|
||||
transition.updateFrame(view: additionalView, frame: additionalViewFrame)
|
||||
additionalView.update(size: additionalViewFrame.size, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
func animateIn(asReplacement: Bool) {
|
||||
@@ -1602,6 +1677,10 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
|
||||
self.animatedStickerNode?.visibility = true
|
||||
|
||||
if let additionalView = self.additionalView {
|
||||
additionalView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
|
||||
}
|
||||
|
||||
self.checkTimer()
|
||||
}
|
||||
|
||||
@@ -1617,6 +1696,10 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
}
|
||||
self.panelNode.layer.animateScale(from: 1.0, to: 0.96, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
|
||||
self.panelWrapperNode.layer.animateScale(from: 1.0, to: 0.96, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
|
||||
|
||||
if let additionalView = self.additionalView {
|
||||
additionalView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false)
|
||||
}
|
||||
}
|
||||
|
||||
func animateOutWithReplacement(completion: @escaping () -> Void) {
|
||||
@@ -1631,9 +1714,24 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
}
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
if !self.panelNode.frame.insetBy(dx: -60.0, dy: 0.0).contains(point) {
|
||||
return nil
|
||||
if let additionalView = self.additionalView {
|
||||
if let result = additionalView.hitTest(self.view.convert(point, to: additionalView), with: event) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
return super.hitTest(point, with: event)
|
||||
|
||||
if !self.panelNode.frame.insetBy(dx: -60.0, dy: 0.0).contains(point) {
|
||||
if self.additionalView != nil && self.isTimeoutDisabled {
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
let result = super.hitTest(point, with: event)
|
||||
if result == self {
|
||||
if self.additionalView != nil && self.isTimeoutDisabled {
|
||||
return self.view
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user