diff --git a/submodules/GalleryUI/BUILD b/submodules/GalleryUI/BUILD index 82b0612031..6443dcc251 100644 --- a/submodules/GalleryUI/BUILD +++ b/submodules/GalleryUI/BUILD @@ -46,6 +46,7 @@ swift_library( "//submodules/TelegramUI/Components/AnimationCache:AnimationCache", "//submodules/TelegramUI/Components/MultiAnimationRenderer:MultiAnimationRenderer", "//submodules/TelegramUI/Components/SliderContextItem:SliderContextItem", + "//submodules/TooltipUI", ], visibility = [ "//visibility:public", diff --git a/submodules/GalleryUI/Sources/SecretMediaPreviewController.swift b/submodules/GalleryUI/Sources/SecretMediaPreviewController.swift index 2a72ee37c4..076c6ad06c 100644 --- a/submodules/GalleryUI/Sources/SecretMediaPreviewController.swift +++ b/submodules/GalleryUI/Sources/SecretMediaPreviewController.swift @@ -11,6 +11,7 @@ import RadialStatusNode import ScreenCaptureDetection import AppBundle import LocalizedPeerData +import TooltipUI private func galleryMediaForMedia(media: Media) -> Media? { if let media = media as? TelegramMediaImage { @@ -57,23 +58,54 @@ private final class SecretMediaPreviewControllerNode: GalleryControllerNode { private var timeoutNode: RadialStatusNode? private var validLayout: (ContainerViewLayout, CGFloat)? - + var beginTimeAndTimeout: (Double, Double)? { didSet { - if let (beginTime, timeout) = self.beginTimeAndTimeout, Int32(timeout) != viewOnceTimeout { + if let (beginTime, timeout) = self.beginTimeAndTimeout { + var beginTime = beginTime if self.timeoutNode == nil { let timeoutNode = RadialStatusNode(backgroundNodeColor: UIColor(white: 0.0, alpha: 0.5)) self.timeoutNode = timeoutNode - var iconImage: UIImage? - if let image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/SecretMediaIcon"), color: .white) { - let factor: CGFloat = 0.48 - iconImage = generateImage(CGSize(width: floor(image.size.width * factor), height: floor(image.size.height * factor)), contextGenerator: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - context.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: size)) - }) + let icon: RadialStatusNodeState.SecretTimeoutIcon + let timeoutValue = Int32(timeout) + if timeoutValue == viewOnceTimeout || "".isEmpty { + beginTime = CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970 + + if let image = generateImage(CGSize(width: 28.0, height: 28.0), rotatedContext: { size, context in + let bounds = CGRect(origin: .zero, size: size) + context.clear(bounds) + + let string = "1" + let attributedString = NSAttributedString(string: string, attributes: [NSAttributedString.Key.font: Font.with(size: 14.0, design: .round), NSAttributedString.Key.foregroundColor: UIColor.white]) + + let line = CTLineCreateWithAttributedString(attributedString) + let lineBounds = CTLineGetBoundsWithOptions(line, .useGlyphPathBounds) + + let lineOffset = CGPoint(x: -1.0, y: 0.0) + let lineOrigin = CGPoint(x: floorToScreenPixels(-lineBounds.origin.x + (bounds.size.width - lineBounds.size.width) / 2.0) + lineOffset.x, y: floorToScreenPixels(-lineBounds.origin.y + (bounds.size.height - lineBounds.size.height) / 2.0)) + + context.translateBy(x: bounds.size.width / 2.0, y: bounds.size.height / 2.0) + context.scaleBy(x: 1.0, y: -1.0) + context.translateBy(x: -bounds.size.width / 2.0, y: -bounds.size.height / 2.0) + + context.translateBy(x: lineOrigin.x, y: lineOrigin.y) + CTLineDraw(line, context) + context.translateBy(x: -lineOrigin.x, y: -lineOrigin.y) + }) { + icon = .image(image) + } else { + icon = .flame + } + } else { + icon = .flame } - timeoutNode.transitionToState(.secretTimeout(color: .white, icon: iconImage, beginTime: beginTime, timeout: timeout, sparks: true), completion: {}) + timeoutNode.transitionToState(.secretTimeout(color: .white, icon: icon, beginTime: beginTime, timeout: timeout, sparks: true), completion: {}) self.addSubnode(timeoutNode) + + timeoutNode.addTarget(self, action: #selector(self.statusTapGesture), forControlEvents: .touchUpInside) + +// let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.statusTapGesture)) +// timeoutNode.view.addGestureRecognizer(tapGesture) if let (layout, navigationHeight) = self.validLayout { self.layoutTimeoutNode(layout, navigationBarHeight: navigationHeight, transition: .immediate) @@ -86,6 +118,13 @@ private final class SecretMediaPreviewControllerNode: GalleryControllerNode { } } + var statusPressed: (UIView) -> Void = { _ in } + @objc private func statusTapGesture() { + if let sourceView = self.timeoutNode?.view { + self.statusPressed(sourceView) + } + } + override func animateIn(animateContent: Bool, useSimpleAnimation: Bool) { super.animateIn(animateContent: animateContent, useSimpleAnimation: useSimpleAnimation) @@ -149,6 +188,8 @@ public final class SecretMediaPreviewController: ViewController { private var screenCaptureEventsDisposable: Disposable? + private weak var tooltipController: TooltipScreen? + public init(context: AccountContext, messageId: MessageId) { self.context = context self.messageId = messageId @@ -214,6 +255,12 @@ public final class SecretMediaPreviewController: ViewController { self.displayNode = SecretMediaPreviewControllerNode(controllerInteraction: controllerInteraction) self.displayNodeDidLoad() + self.controllerNode.statusPressed = { [weak self] sourceView in + if let self { + self.presentViewOnceTooltip(sourceView: sourceView) + } + } + self.controllerNode.statusBar = self.statusBar self.controllerNode.navigationBar = self.navigationBar @@ -497,6 +544,42 @@ public final class SecretMediaPreviewController: ViewController { } } + private func presentViewOnceTooltip(sourceView: UIView) { + if let tooltipController = self.tooltipController { + self.tooltipController = nil + tooltipController.dismiss() + } + + let absoluteFrame = sourceView.convert(sourceView.bounds, to: nil) + let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.maxY + 2.0), size: CGSize()) + + let iconName = "anim_autoremove_on" + let text: String + if self.currentNodeMessageIsVideo { + text = "This video can only be viewed once" + } else { + text = "This photo can only be viewed once" + } + + let tooltipController = TooltipScreen( + account: self.context.account, + sharedContext: self.context.sharedContext, + text: .plain(text: text), + balancedTextLayout: true, + style: .customBlur(UIColor(rgb: 0x18181a), 0.0), + arrowStyle: .small, + icon: .animation(name: iconName, delay: 0.1, tintColor: nil), + location: .point(location, .top), + displayDuration: .default, + inset: 8.0, + shouldDismissOnTouch: { _, _ in + return .ignore + } + ) + self.tooltipController = tooltipController + self.present(tooltipController, in: .window(.root)) + } + public override func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { super.containerLayoutUpdated(layout, transition: transition) diff --git a/submodules/RadialStatusNode/Sources/RadialStatusNode.swift b/submodules/RadialStatusNode/Sources/RadialStatusNode.swift index 3c1a78394d..1b7d5e95dd 100644 --- a/submodules/RadialStatusNode/Sources/RadialStatusNode.swift +++ b/submodules/RadialStatusNode/Sources/RadialStatusNode.swift @@ -4,6 +4,35 @@ import AsyncDisplayKit import Display public enum RadialStatusNodeState: Equatable { + public enum SecretTimeoutIcon: Equatable { + case none + case image(UIImage) + case flame + + public static func ==(lhs: SecretTimeoutIcon, rhs: SecretTimeoutIcon) -> Bool { + switch lhs { + case .none: + if case .none = rhs { + return true + } else { + return false + } + case let .image(lhsImage): + if case let .image(rhsImage) = rhs, lhsImage === rhsImage { + return true + } else { + return false + } + case .flame: + if case .flame = rhs { + return true + } else { + return false + } + } + } + } + case none case download(UIColor) case play(UIColor) @@ -13,7 +42,7 @@ public enum RadialStatusNodeState: Equatable { case check(UIColor) case customIcon(UIImage) case staticTimeout - case secretTimeout(color: UIColor, icon: UIImage?, beginTime: Double, timeout: Double, sparks: Bool) + case secretTimeout(color: UIColor, icon: SecretTimeoutIcon, beginTime: Double, timeout: Double, sparks: Bool) public static func ==(lhs: RadialStatusNodeState, rhs: RadialStatusNodeState) -> Bool { switch lhs { @@ -72,7 +101,7 @@ public enum RadialStatusNodeState: Equatable { return false } case let .secretTimeout(lhsColor, lhsIcon, lhsBeginTime, lhsTimeout, lhsSparks): - if case let .secretTimeout(rhsColor, rhsIcon, rhsBeginTime, rhsTimeout, rhsSparks) = rhs, lhsColor.isEqual(rhsColor), lhsIcon === rhsIcon, lhsBeginTime.isEqual(to: rhsBeginTime), lhsTimeout.isEqual(to: rhsTimeout), lhsSparks == rhsSparks { + if case let .secretTimeout(rhsColor, rhsIcon, rhsBeginTime, rhsTimeout, rhsSparks) = rhs, lhsColor.isEqual(rhsColor), lhsIcon == rhsIcon, lhsBeginTime.isEqual(to: rhsBeginTime), lhsTimeout.isEqual(to: rhsTimeout), lhsSparks == rhsSparks { return true } else { return false @@ -137,7 +166,7 @@ public enum RadialStatusNodeState: Equatable { return false } case let .secretTimeout(lhsColor, lhsIcon, lhsBeginTime, lhsTimeout, lhsSparks): - if case let .secretTimeout(rhsColor, rhsIcon, rhsBeginTime, rhsTimeout, rhsSparks) = rhs, lhsColor.isEqual(rhsColor), lhsIcon === rhsIcon, lhsBeginTime.isEqual(to: rhsBeginTime), lhsTimeout.isEqual(to: rhsTimeout), lhsSparks == rhsSparks { + if case let .secretTimeout(rhsColor, rhsIcon, rhsBeginTime, rhsTimeout, rhsSparks) = rhs, lhsColor.isEqual(rhsColor), lhsIcon == rhsIcon, lhsBeginTime.isEqual(to: rhsBeginTime), lhsTimeout.isEqual(to: rhsTimeout), lhsSparks == rhsSparks { return true } else { return false diff --git a/submodules/RadialStatusNode/Sources/RadialStatusSecretTimeoutContentNode.swift b/submodules/RadialStatusNode/Sources/RadialStatusSecretTimeoutContentNode.swift index 8c424477d7..500eba5206 100644 --- a/submodules/RadialStatusNode/Sources/RadialStatusSecretTimeoutContentNode.swift +++ b/submodules/RadialStatusNode/Sources/RadialStatusSecretTimeoutContentNode.swift @@ -25,12 +25,12 @@ private struct ContentParticle { private final class RadialStatusSecretTimeoutContentNodeParameters: NSObject { let color: UIColor - let icon: UIImage? + let icon: RadialStatusNodeState.SecretTimeoutIcon let progress: CGFloat let sparks: Bool let particles: [ContentParticle] - init(color: UIColor, icon: UIImage?, progress: CGFloat, sparks: Bool, particles: [ContentParticle]) { + init(color: UIColor, icon: RadialStatusNodeState.SecretTimeoutIcon, progress: CGFloat, sparks: Bool, particles: [ContentParticle]) { self.color = color self.icon = icon self.progress = progress @@ -48,7 +48,7 @@ final class RadialStatusSecretTimeoutContentNode: RadialStatusContentNode { private let beginTime: Double private let timeout: Double - private let icon: UIImage? + private let icon: RadialStatusNodeState.SecretTimeoutIcon private let sparks: Bool private var progress: CGFloat = 0.0 @@ -58,7 +58,7 @@ final class RadialStatusSecretTimeoutContentNode: RadialStatusContentNode { private var displayLink: CADisplayLink? - init(color: UIColor, beginTime: Double, timeout: Double, icon: UIImage?, sparks: Bool) { + init(color: UIColor, beginTime: Double, timeout: Double, icon: RadialStatusNodeState.SecretTimeoutIcon, sparks: Bool) { self.color = color self.beginTime = beginTime self.timeout = timeout @@ -84,7 +84,7 @@ final class RadialStatusSecretTimeoutContentNode: RadialStatusContentNode { self.displayLink?.isPaused = true self.displayLink?.add(to: RunLoop.main, forMode: .common) - if icon != nil { + if case .flame = icon { self.addSubnode(self.animationNode) } } @@ -202,15 +202,15 @@ final class RadialStatusSecretTimeoutContentNode: RadialStatusContentNode { } if let parameters = parameters as? RadialStatusSecretTimeoutContentNodeParameters { -// if let icon = parameters.icon, let _ = icon.cgImage { -// let imageRect = CGRect(origin: CGPoint(x: floor((bounds.size.width - icon.size.width) / 2.0), y: floor((bounds.size.height - icon.size.height) / 2.0)), size: icon.size) -// context.saveGState() -// context.translateBy(x: imageRect.midX, y: imageRect.midY) -// context.scaleBy(x: 1.0, y: -1.0) -// context.translateBy(x: -imageRect.midX, y: -imageRect.midY) -// context.draw(iconImage, in: imageRect) -// context.restoreGState() -// } + if case let .image(icon) = parameters.icon, let iconImage = icon.cgImage { + let imageRect = CGRect(origin: CGPoint(x: floor((bounds.size.width - icon.size.width) / 2.0), y: floor((bounds.size.height - icon.size.height) / 2.0)), size: icon.size) + context.saveGState() + context.translateBy(x: imageRect.midX, y: imageRect.midY) + context.scaleBy(x: 1.0, y: -1.0) + context.translateBy(x: -imageRect.midX, y: -imageRect.midY) + context.draw(iconImage, in: imageRect) + context.restoreGState() + } let lineWidth: CGFloat if parameters.sparks { diff --git a/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift b/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift index b3a56674a9..670595df4b 100644 --- a/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift +++ b/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift @@ -95,7 +95,7 @@ enum AccountStateMutationOperation { case UpdatePinnedItemIds(PeerGroupId, AccountStateUpdatePinnedItemIdsOperation) case UpdatePinnedTopic(peerId: PeerId, threadId: Int64, isPinned: Bool) case UpdatePinnedTopicOrder(peerId: PeerId, threadIds: [Int64]) - case ReadMessageContents((PeerId?, [Int32])) + case ReadMessageContents(peerIdsAndMessageIds: (PeerId?, [Int32]), date: Int32?) case UpdateMessageImpressionCount(MessageId, Int32) case UpdateMessageForwardsCount(MessageId, Int32) case UpdateInstalledStickerPacks(AccountStateUpdateStickerPacksOperation) @@ -574,8 +574,8 @@ struct AccountMutableState { self.addOperation(.UpdatePinnedTopicOrder(peerId: peerId, threadIds: threadIds)) } - mutating func addReadMessagesContents(_ peerIdsAndMessageIds: (PeerId?, [Int32])) { - self.addOperation(.ReadMessageContents(peerIdsAndMessageIds)) + mutating func addReadMessagesContents(_ peerIdsAndMessageIds: (PeerId?, [Int32]), date: Int32?) { + self.addOperation(.ReadMessageContents(peerIdsAndMessageIds: peerIdsAndMessageIds, date: date)) } mutating func addUpdateMessageImpressionCount(id: MessageId, count: Int32) { diff --git a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift index 9f9c4b0f0c..5ca53dcafe 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift @@ -1479,11 +1479,11 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox: case let .updateChannelPinnedTopic(flags, channelId, topicId): let isPinned = (flags & (1 << 0)) != 0 updatedState.addUpdatePinnedTopic(peerId: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId)), threadId: Int64(topicId), isPinned: isPinned) - case let .updateReadMessagesContents(_, messages, _, _, _): - updatedState.addReadMessagesContents((nil, messages)) + case let .updateReadMessagesContents(_, messages, _, _, date): + updatedState.addReadMessagesContents((nil, messages), date: date) case let .updateChannelReadMessagesContents(_, channelId, topMsgId, messages): let _ = topMsgId - updatedState.addReadMessagesContents((PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId)), messages)) + updatedState.addReadMessagesContents((PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId)), messages), date: nil) case let .updateChannelMessageViews(channelId, id, views): updatedState.addUpdateMessageImpressionCount(id: MessageId(peerId: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId)), namespace: Namespaces.Message.Cloud, id: id), count: views) /*case let .updateChannelMessageForwards(channelId, id, forwards): @@ -2929,7 +2929,7 @@ private func pollChannel(accountPeerId: PeerId, postbox: Postbox, network: Netwo }, pinned: (flags & (1 << 0)) != 0) case let .updateChannelReadMessagesContents(_, _, topMsgId, messages): let _ = topMsgId - updatedState.addReadMessagesContents((peer.id, messages)) + updatedState.addReadMessagesContents((peer.id, messages), date: nil) case let .updateChannelMessageViews(_, id, views): updatedState.addUpdateMessageImpressionCount(id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: id), count: views) case let .updateChannelWebPage(_, apiWebpage, _, _): @@ -4181,16 +4181,16 @@ func replayFinalState( transaction.setPeerPinnedThreads(peerId: peerId, threadIds: currentThreadIds) case let .UpdatePinnedTopicOrder(peerId, threadIds): transaction.setPeerPinnedThreads(peerId: peerId, threadIds: threadIds) - case let .ReadMessageContents(peerIdAndMessageIds): + case let .ReadMessageContents(peerIdAndMessageIds, date): let (peerId, messageIds) = peerIdAndMessageIds if let peerId = peerId { for id in messageIds { - markMessageContentAsConsumedRemotely(transaction: transaction, messageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: id)) + markMessageContentAsConsumedRemotely(transaction: transaction, messageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: id), consumeDate: date) } } else { for messageId in transaction.messageIdsForGlobalIds(messageIds) { - markMessageContentAsConsumedRemotely(transaction: transaction, messageId: messageId) + markMessageContentAsConsumedRemotely(transaction: transaction, messageId: messageId, consumeDate: date) } } case let .UpdateMessageImpressionCount(id, count): diff --git a/submodules/TelegramCore/Sources/State/ProcessSecretChatIncomingDecryptedOperations.swift b/submodules/TelegramCore/Sources/State/ProcessSecretChatIncomingDecryptedOperations.swift index c6030ec173..1151ec65f2 100644 --- a/submodules/TelegramCore/Sources/State/ProcessSecretChatIncomingDecryptedOperations.swift +++ b/submodules/TelegramCore/Sources/State/ProcessSecretChatIncomingDecryptedOperations.swift @@ -307,7 +307,7 @@ func processSecretChatIncomingDecryptedOperations(encryptionProvider: Encryption } } for messageId in messageIds { - markMessageContentAsConsumedRemotely(transaction: transaction, messageId: messageId) + markMessageContentAsConsumedRemotely(transaction: transaction, messageId: messageId, consumeDate: nil) } default: break diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/MarkMessageContentAsConsumedInteractively.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/MarkMessageContentAsConsumedInteractively.swift index 46be899ce2..76d2aacf95 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/MarkMessageContentAsConsumedInteractively.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/MarkMessageContentAsConsumedInteractively.swift @@ -160,7 +160,7 @@ func _internal_markReactionsAsSeenInteractively(postbox: Postbox, messageId: Mes } } -func markMessageContentAsConsumedRemotely(transaction: Transaction, messageId: MessageId) { +func markMessageContentAsConsumedRemotely(transaction: Transaction, messageId: MessageId, consumeDate: Int32?) { if let message = transaction.getMessage(messageId) { var updateMessage = false var updatedAttributes = message.attributes @@ -184,35 +184,41 @@ func markMessageContentAsConsumedRemotely(transaction: Transaction, messageId: M } let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) + let countdownBeginTime = consumeDate ?? timestamp + for i in 0 ..< updatedAttributes.count { if let attribute = updatedAttributes[i] as? AutoremoveTimeoutMessageAttribute { if (attribute.countdownBeginTime == nil || attribute.countdownBeginTime == 0) && message.containsSecretMedia { - updatedAttributes[i] = AutoremoveTimeoutMessageAttribute(timeout: attribute.timeout, countdownBeginTime: timestamp) + updatedAttributes[i] = AutoremoveTimeoutMessageAttribute(timeout: attribute.timeout, countdownBeginTime: countdownBeginTime) updateMessage = true - + if message.id.peerId.namespace == Namespaces.Peer.SecretChat { } else { - for i in 0 ..< updatedMedia.count { - if let _ = updatedMedia[i] as? TelegramMediaImage { - updatedMedia[i] = TelegramMediaExpiredContent(data: .image) - } else if let _ = updatedMedia[i] as? TelegramMediaFile { - updatedMedia[i] = TelegramMediaExpiredContent(data: .file) + if attribute.timeout == viewOnceTimeout || timestamp >= countdownBeginTime + attribute.timeout { + for i in 0 ..< updatedMedia.count { + if let _ = updatedMedia[i] as? TelegramMediaImage { + updatedMedia[i] = TelegramMediaExpiredContent(data: .image) + } else if let _ = updatedMedia[i] as? TelegramMediaFile { + updatedMedia[i] = TelegramMediaExpiredContent(data: .file) + } } } } } } else if let attribute = updatedAttributes[i] as? AutoclearTimeoutMessageAttribute { if (attribute.countdownBeginTime == nil || attribute.countdownBeginTime == 0) && message.containsSecretMedia { - updatedAttributes[i] = AutoclearTimeoutMessageAttribute(timeout: attribute.timeout, countdownBeginTime: timestamp) + updatedAttributes[i] = AutoclearTimeoutMessageAttribute(timeout: attribute.timeout, countdownBeginTime: countdownBeginTime) updateMessage = true if message.id.peerId.namespace == Namespaces.Peer.SecretChat { } else { for i in 0 ..< updatedMedia.count { - if let _ = updatedMedia[i] as? TelegramMediaImage { - updatedMedia[i] = TelegramMediaExpiredContent(data: .image) - } else if let _ = updatedMedia[i] as? TelegramMediaFile { - updatedMedia[i] = TelegramMediaExpiredContent(data: .file) + if attribute.timeout == viewOnceTimeout || timestamp >= countdownBeginTime + attribute.timeout { + if let _ = updatedMedia[i] as? TelegramMediaImage { + updatedMedia[i] = TelegramMediaExpiredContent(data: .image) + } else if let _ = updatedMedia[i] as? TelegramMediaFile { + updatedMedia[i] = TelegramMediaExpiredContent(data: .file) + } } } } diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index 031e111e19..0c0c2aadec 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -4111,6 +4111,8 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } fileprivate func presentReactionPremiumSuggestion() { + self.hapticFeedback.impact(.light) + self.dismissAllTooltips() let context = self.context @@ -4142,7 +4144,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate ) } - let controller = UndoOverlayController(presentationData: presentationData, content: content, elevatedLayout: true, position: .bottom, animateInAsReplacement: false, action: { [weak self] action in + let controller = UndoOverlayController(presentationData: presentationData, content: content, elevatedLayout: true, position: .top, animateInAsReplacement: false, action: { [weak self] action in if case .info = action, let self { if let stickerScreen = self.node.stickerScreen { self.node.stickerScreen = nil diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift index a731df238b..130a758930 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift @@ -1157,9 +1157,9 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { case .Local: if isSecretMedia && self.secretProgressIcon != nil { if let (beginTime, timeout) = secretBeginTimeAndTimeout { - state = .secretTimeout(color: messageTheme.mediaOverlayControlColors.foregroundColor, icon: secretProgressIcon, beginTime: beginTime, timeout: timeout, sparks: true) + state = .secretTimeout(color: messageTheme.mediaOverlayControlColors.foregroundColor, icon: .flame, beginTime: beginTime, timeout: timeout, sparks: true) } else { - state = .customIcon(secretProgressIcon!) + state = .staticTimeout } } else { state = .none diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift index 899c73a3f1..6dfa7bd625 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift @@ -1796,6 +1796,10 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio var badgeContent: ChatMessageInteractiveMediaBadgeContent? var mediaDownloadState: ChatMessageInteractiveMediaDownloadState? + if isSecretMedia { + backgroundColor = messageTheme.mediaDateAndStatusFillColor + } + if let invoice = invoice { if let extendedMedia = invoice.extendedMedia { if case let .preview(_, _, maybeVideoDuration) = extendedMedia, let videoDuration = maybeVideoDuration { @@ -1989,11 +1993,9 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio secretProgressIcon = PresentationResourcesChat.chatBubbleSecretMediaCompactIcon(theme) } if isSecretMedia, let (maybeBeginTime, timeout) = secretBeginTimeAndTimeout, let beginTime = maybeBeginTime, Int32(timeout) != viewOnceTimeout { - state = .secretTimeout(color: messageTheme.mediaOverlayControlColors.foregroundColor, icon: secretProgressIcon, beginTime: beginTime, timeout: timeout, sparks: true) - backgroundColor = messageTheme.mediaDateAndStatusFillColor + state = .secretTimeout(color: messageTheme.mediaOverlayControlColors.foregroundColor, icon: .flame, beginTime: beginTime, timeout: timeout, sparks: true) } else if isSecretMedia, let _ = secretProgressIcon { state = .staticTimeout - backgroundColor = messageTheme.mediaDateAndStatusFillColor } else if let file = media as? TelegramMediaFile, !file.isVideoSticker { let isInlinePlayableVideo = file.isVideo && !isSecretMedia && (self.automaticPlayback ?? false) if (!isInlinePlayableVideo || isStory) && file.isVideo { diff --git a/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift b/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift index fa6d6e5083..594da666cc 100644 --- a/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift +++ b/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift @@ -1347,8 +1347,12 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { var panelWrapperFrame = CGRect(origin: CGPoint(x: leftMargin + layout.safeInsets.left, y: layout.size.height - contentHeight - insets.bottom - margin), size: CGSize(width: layout.size.width - leftMargin * 2.0 - layout.safeInsets.left - layout.safeInsets.right, height: contentHeight)) if case .top = self.placementPosition { - panelFrame.origin.y = insets.top + margin - panelWrapperFrame.origin.y = insets.top + margin + var topInset = insets.top + if topInset.isZero { + topInset = layout.statusBarHeight ?? 44.0 + } + panelFrame.origin.y = topInset + margin + panelWrapperFrame.origin.y = topInset + margin } transition.updateFrame(node: self.panelNode, frame: panelFrame) @@ -1441,7 +1445,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { let statusSize: CGFloat = 30.0 transition.updateFrame(node: statusNode, frame: CGRect(origin: CGPoint(x: floor((leftInset - statusSize) / 2.0), y: floor((contentHeight - statusSize) / 2.0)), size: CGSize(width: statusSize, height: statusSize))) if firstLayout { - statusNode.transitionToState(.secretTimeout(color: .white, icon: nil, beginTime: CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970, timeout: Double(self.remainingSeconds), sparks: false), completion: {}) + statusNode.transitionToState(.secretTimeout(color: .white, icon: .none, beginTime: CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970, timeout: Double(self.remainingSeconds), sparks: false), completion: {}) } }