Various improvements

This commit is contained in:
Ilya Laktyushin 2023-09-05 16:56:48 +04:00
parent 04ca831081
commit 266f9e5ad8
12 changed files with 187 additions and 60 deletions

View File

@ -46,6 +46,7 @@ swift_library(
"//submodules/TelegramUI/Components/AnimationCache:AnimationCache", "//submodules/TelegramUI/Components/AnimationCache:AnimationCache",
"//submodules/TelegramUI/Components/MultiAnimationRenderer:MultiAnimationRenderer", "//submodules/TelegramUI/Components/MultiAnimationRenderer:MultiAnimationRenderer",
"//submodules/TelegramUI/Components/SliderContextItem:SliderContextItem", "//submodules/TelegramUI/Components/SliderContextItem:SliderContextItem",
"//submodules/TooltipUI",
], ],
visibility = [ visibility = [
"//visibility:public", "//visibility:public",

View File

@ -11,6 +11,7 @@ import RadialStatusNode
import ScreenCaptureDetection import ScreenCaptureDetection
import AppBundle import AppBundle
import LocalizedPeerData import LocalizedPeerData
import TooltipUI
private func galleryMediaForMedia(media: Media) -> Media? { private func galleryMediaForMedia(media: Media) -> Media? {
if let media = media as? TelegramMediaImage { if let media = media as? TelegramMediaImage {
@ -57,23 +58,54 @@ private final class SecretMediaPreviewControllerNode: GalleryControllerNode {
private var timeoutNode: RadialStatusNode? private var timeoutNode: RadialStatusNode?
private var validLayout: (ContainerViewLayout, CGFloat)? private var validLayout: (ContainerViewLayout, CGFloat)?
var beginTimeAndTimeout: (Double, Double)? { var beginTimeAndTimeout: (Double, Double)? {
didSet { didSet {
if let (beginTime, timeout) = self.beginTimeAndTimeout, Int32(timeout) != viewOnceTimeout { if let (beginTime, timeout) = self.beginTimeAndTimeout {
var beginTime = beginTime
if self.timeoutNode == nil { if self.timeoutNode == nil {
let timeoutNode = RadialStatusNode(backgroundNodeColor: UIColor(white: 0.0, alpha: 0.5)) let timeoutNode = RadialStatusNode(backgroundNodeColor: UIColor(white: 0.0, alpha: 0.5))
self.timeoutNode = timeoutNode self.timeoutNode = timeoutNode
var iconImage: UIImage? let icon: RadialStatusNodeState.SecretTimeoutIcon
if let image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/SecretMediaIcon"), color: .white) { let timeoutValue = Int32(timeout)
let factor: CGFloat = 0.48 if timeoutValue == viewOnceTimeout || "".isEmpty {
iconImage = generateImage(CGSize(width: floor(image.size.width * factor), height: floor(image.size.height * factor)), contextGenerator: { size, context in beginTime = CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970
context.clear(CGRect(origin: CGPoint(), size: size))
context.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: size)) 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) 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 { if let (layout, navigationHeight) = self.validLayout {
self.layoutTimeoutNode(layout, navigationBarHeight: navigationHeight, transition: .immediate) 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) { override func animateIn(animateContent: Bool, useSimpleAnimation: Bool) {
super.animateIn(animateContent: animateContent, useSimpleAnimation: useSimpleAnimation) super.animateIn(animateContent: animateContent, useSimpleAnimation: useSimpleAnimation)
@ -149,6 +188,8 @@ public final class SecretMediaPreviewController: ViewController {
private var screenCaptureEventsDisposable: Disposable? private var screenCaptureEventsDisposable: Disposable?
private weak var tooltipController: TooltipScreen?
public init(context: AccountContext, messageId: MessageId) { public init(context: AccountContext, messageId: MessageId) {
self.context = context self.context = context
self.messageId = messageId self.messageId = messageId
@ -214,6 +255,12 @@ public final class SecretMediaPreviewController: ViewController {
self.displayNode = SecretMediaPreviewControllerNode(controllerInteraction: controllerInteraction) self.displayNode = SecretMediaPreviewControllerNode(controllerInteraction: controllerInteraction)
self.displayNodeDidLoad() self.displayNodeDidLoad()
self.controllerNode.statusPressed = { [weak self] sourceView in
if let self {
self.presentViewOnceTooltip(sourceView: sourceView)
}
}
self.controllerNode.statusBar = self.statusBar self.controllerNode.statusBar = self.statusBar
self.controllerNode.navigationBar = self.navigationBar 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) { public override func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
super.containerLayoutUpdated(layout, transition: transition) super.containerLayoutUpdated(layout, transition: transition)

View File

@ -4,6 +4,35 @@ import AsyncDisplayKit
import Display import Display
public enum RadialStatusNodeState: Equatable { 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 none
case download(UIColor) case download(UIColor)
case play(UIColor) case play(UIColor)
@ -13,7 +42,7 @@ public enum RadialStatusNodeState: Equatable {
case check(UIColor) case check(UIColor)
case customIcon(UIImage) case customIcon(UIImage)
case staticTimeout 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 { public static func ==(lhs: RadialStatusNodeState, rhs: RadialStatusNodeState) -> Bool {
switch lhs { switch lhs {
@ -72,7 +101,7 @@ public enum RadialStatusNodeState: Equatable {
return false return false
} }
case let .secretTimeout(lhsColor, lhsIcon, lhsBeginTime, lhsTimeout, lhsSparks): 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 return true
} else { } else {
return false return false
@ -137,7 +166,7 @@ public enum RadialStatusNodeState: Equatable {
return false return false
} }
case let .secretTimeout(lhsColor, lhsIcon, lhsBeginTime, lhsTimeout, lhsSparks): 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 return true
} else { } else {
return false return false

View File

@ -25,12 +25,12 @@ private struct ContentParticle {
private final class RadialStatusSecretTimeoutContentNodeParameters: NSObject { private final class RadialStatusSecretTimeoutContentNodeParameters: NSObject {
let color: UIColor let color: UIColor
let icon: UIImage? let icon: RadialStatusNodeState.SecretTimeoutIcon
let progress: CGFloat let progress: CGFloat
let sparks: Bool let sparks: Bool
let particles: [ContentParticle] 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.color = color
self.icon = icon self.icon = icon
self.progress = progress self.progress = progress
@ -48,7 +48,7 @@ final class RadialStatusSecretTimeoutContentNode: RadialStatusContentNode {
private let beginTime: Double private let beginTime: Double
private let timeout: Double private let timeout: Double
private let icon: UIImage? private let icon: RadialStatusNodeState.SecretTimeoutIcon
private let sparks: Bool private let sparks: Bool
private var progress: CGFloat = 0.0 private var progress: CGFloat = 0.0
@ -58,7 +58,7 @@ final class RadialStatusSecretTimeoutContentNode: RadialStatusContentNode {
private var displayLink: CADisplayLink? 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.color = color
self.beginTime = beginTime self.beginTime = beginTime
self.timeout = timeout self.timeout = timeout
@ -84,7 +84,7 @@ final class RadialStatusSecretTimeoutContentNode: RadialStatusContentNode {
self.displayLink?.isPaused = true self.displayLink?.isPaused = true
self.displayLink?.add(to: RunLoop.main, forMode: .common) self.displayLink?.add(to: RunLoop.main, forMode: .common)
if icon != nil { if case .flame = icon {
self.addSubnode(self.animationNode) self.addSubnode(self.animationNode)
} }
} }
@ -202,15 +202,15 @@ final class RadialStatusSecretTimeoutContentNode: RadialStatusContentNode {
} }
if let parameters = parameters as? RadialStatusSecretTimeoutContentNodeParameters { if let parameters = parameters as? RadialStatusSecretTimeoutContentNodeParameters {
// if let icon = parameters.icon, let _ = icon.cgImage { 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) 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.saveGState()
// context.translateBy(x: imageRect.midX, y: imageRect.midY) context.translateBy(x: imageRect.midX, y: imageRect.midY)
// context.scaleBy(x: 1.0, y: -1.0) context.scaleBy(x: 1.0, y: -1.0)
// context.translateBy(x: -imageRect.midX, y: -imageRect.midY) context.translateBy(x: -imageRect.midX, y: -imageRect.midY)
// context.draw(iconImage, in: imageRect) context.draw(iconImage, in: imageRect)
// context.restoreGState() context.restoreGState()
// } }
let lineWidth: CGFloat let lineWidth: CGFloat
if parameters.sparks { if parameters.sparks {

View File

@ -95,7 +95,7 @@ enum AccountStateMutationOperation {
case UpdatePinnedItemIds(PeerGroupId, AccountStateUpdatePinnedItemIdsOperation) case UpdatePinnedItemIds(PeerGroupId, AccountStateUpdatePinnedItemIdsOperation)
case UpdatePinnedTopic(peerId: PeerId, threadId: Int64, isPinned: Bool) case UpdatePinnedTopic(peerId: PeerId, threadId: Int64, isPinned: Bool)
case UpdatePinnedTopicOrder(peerId: PeerId, threadIds: [Int64]) case UpdatePinnedTopicOrder(peerId: PeerId, threadIds: [Int64])
case ReadMessageContents((PeerId?, [Int32])) case ReadMessageContents(peerIdsAndMessageIds: (PeerId?, [Int32]), date: Int32?)
case UpdateMessageImpressionCount(MessageId, Int32) case UpdateMessageImpressionCount(MessageId, Int32)
case UpdateMessageForwardsCount(MessageId, Int32) case UpdateMessageForwardsCount(MessageId, Int32)
case UpdateInstalledStickerPacks(AccountStateUpdateStickerPacksOperation) case UpdateInstalledStickerPacks(AccountStateUpdateStickerPacksOperation)
@ -574,8 +574,8 @@ struct AccountMutableState {
self.addOperation(.UpdatePinnedTopicOrder(peerId: peerId, threadIds: threadIds)) self.addOperation(.UpdatePinnedTopicOrder(peerId: peerId, threadIds: threadIds))
} }
mutating func addReadMessagesContents(_ peerIdsAndMessageIds: (PeerId?, [Int32])) { mutating func addReadMessagesContents(_ peerIdsAndMessageIds: (PeerId?, [Int32]), date: Int32?) {
self.addOperation(.ReadMessageContents(peerIdsAndMessageIds)) self.addOperation(.ReadMessageContents(peerIdsAndMessageIds: peerIdsAndMessageIds, date: date))
} }
mutating func addUpdateMessageImpressionCount(id: MessageId, count: Int32) { mutating func addUpdateMessageImpressionCount(id: MessageId, count: Int32) {

View File

@ -1479,11 +1479,11 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox:
case let .updateChannelPinnedTopic(flags, channelId, topicId): case let .updateChannelPinnedTopic(flags, channelId, topicId):
let isPinned = (flags & (1 << 0)) != 0 let isPinned = (flags & (1 << 0)) != 0
updatedState.addUpdatePinnedTopic(peerId: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId)), threadId: Int64(topicId), isPinned: isPinned) updatedState.addUpdatePinnedTopic(peerId: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId)), threadId: Int64(topicId), isPinned: isPinned)
case let .updateReadMessagesContents(_, messages, _, _, _): case let .updateReadMessagesContents(_, messages, _, _, date):
updatedState.addReadMessagesContents((nil, messages)) updatedState.addReadMessagesContents((nil, messages), date: date)
case let .updateChannelReadMessagesContents(_, channelId, topMsgId, messages): case let .updateChannelReadMessagesContents(_, channelId, topMsgId, messages):
let _ = topMsgId 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): 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) 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): /*case let .updateChannelMessageForwards(channelId, id, forwards):
@ -2929,7 +2929,7 @@ private func pollChannel(accountPeerId: PeerId, postbox: Postbox, network: Netwo
}, pinned: (flags & (1 << 0)) != 0) }, pinned: (flags & (1 << 0)) != 0)
case let .updateChannelReadMessagesContents(_, _, topMsgId, messages): case let .updateChannelReadMessagesContents(_, _, topMsgId, messages):
let _ = topMsgId let _ = topMsgId
updatedState.addReadMessagesContents((peer.id, messages)) updatedState.addReadMessagesContents((peer.id, messages), date: nil)
case let .updateChannelMessageViews(_, id, views): case let .updateChannelMessageViews(_, id, views):
updatedState.addUpdateMessageImpressionCount(id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: id), count: views) updatedState.addUpdateMessageImpressionCount(id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: id), count: views)
case let .updateChannelWebPage(_, apiWebpage, _, _): case let .updateChannelWebPage(_, apiWebpage, _, _):
@ -4181,16 +4181,16 @@ func replayFinalState(
transaction.setPeerPinnedThreads(peerId: peerId, threadIds: currentThreadIds) transaction.setPeerPinnedThreads(peerId: peerId, threadIds: currentThreadIds)
case let .UpdatePinnedTopicOrder(peerId, threadIds): case let .UpdatePinnedTopicOrder(peerId, threadIds):
transaction.setPeerPinnedThreads(peerId: peerId, threadIds: threadIds) transaction.setPeerPinnedThreads(peerId: peerId, threadIds: threadIds)
case let .ReadMessageContents(peerIdAndMessageIds): case let .ReadMessageContents(peerIdAndMessageIds, date):
let (peerId, messageIds) = peerIdAndMessageIds let (peerId, messageIds) = peerIdAndMessageIds
if let peerId = peerId { if let peerId = peerId {
for id in messageIds { 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 { } else {
for messageId in transaction.messageIdsForGlobalIds(messageIds) { for messageId in transaction.messageIdsForGlobalIds(messageIds) {
markMessageContentAsConsumedRemotely(transaction: transaction, messageId: messageId) markMessageContentAsConsumedRemotely(transaction: transaction, messageId: messageId, consumeDate: date)
} }
} }
case let .UpdateMessageImpressionCount(id, count): case let .UpdateMessageImpressionCount(id, count):

View File

@ -307,7 +307,7 @@ func processSecretChatIncomingDecryptedOperations(encryptionProvider: Encryption
} }
} }
for messageId in messageIds { for messageId in messageIds {
markMessageContentAsConsumedRemotely(transaction: transaction, messageId: messageId) markMessageContentAsConsumedRemotely(transaction: transaction, messageId: messageId, consumeDate: nil)
} }
default: default:
break break

View File

@ -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) { if let message = transaction.getMessage(messageId) {
var updateMessage = false var updateMessage = false
var updatedAttributes = message.attributes var updatedAttributes = message.attributes
@ -184,35 +184,41 @@ func markMessageContentAsConsumedRemotely(transaction: Transaction, messageId: M
} }
let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
let countdownBeginTime = consumeDate ?? timestamp
for i in 0 ..< updatedAttributes.count { for i in 0 ..< updatedAttributes.count {
if let attribute = updatedAttributes[i] as? AutoremoveTimeoutMessageAttribute { if let attribute = updatedAttributes[i] as? AutoremoveTimeoutMessageAttribute {
if (attribute.countdownBeginTime == nil || attribute.countdownBeginTime == 0) && message.containsSecretMedia { 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 updateMessage = true
if message.id.peerId.namespace == Namespaces.Peer.SecretChat { if message.id.peerId.namespace == Namespaces.Peer.SecretChat {
} else { } else {
for i in 0 ..< updatedMedia.count { if attribute.timeout == viewOnceTimeout || timestamp >= countdownBeginTime + attribute.timeout {
if let _ = updatedMedia[i] as? TelegramMediaImage { for i in 0 ..< updatedMedia.count {
updatedMedia[i] = TelegramMediaExpiredContent(data: .image) if let _ = updatedMedia[i] as? TelegramMediaImage {
} else if let _ = updatedMedia[i] as? TelegramMediaFile { updatedMedia[i] = TelegramMediaExpiredContent(data: .image)
updatedMedia[i] = TelegramMediaExpiredContent(data: .file) } else if let _ = updatedMedia[i] as? TelegramMediaFile {
updatedMedia[i] = TelegramMediaExpiredContent(data: .file)
}
} }
} }
} }
} }
} else if let attribute = updatedAttributes[i] as? AutoclearTimeoutMessageAttribute { } else if let attribute = updatedAttributes[i] as? AutoclearTimeoutMessageAttribute {
if (attribute.countdownBeginTime == nil || attribute.countdownBeginTime == 0) && message.containsSecretMedia { 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 updateMessage = true
if message.id.peerId.namespace == Namespaces.Peer.SecretChat { if message.id.peerId.namespace == Namespaces.Peer.SecretChat {
} else { } else {
for i in 0 ..< updatedMedia.count { for i in 0 ..< updatedMedia.count {
if let _ = updatedMedia[i] as? TelegramMediaImage { if attribute.timeout == viewOnceTimeout || timestamp >= countdownBeginTime + attribute.timeout {
updatedMedia[i] = TelegramMediaExpiredContent(data: .image) if let _ = updatedMedia[i] as? TelegramMediaImage {
} else if let _ = updatedMedia[i] as? TelegramMediaFile { updatedMedia[i] = TelegramMediaExpiredContent(data: .image)
updatedMedia[i] = TelegramMediaExpiredContent(data: .file) } else if let _ = updatedMedia[i] as? TelegramMediaFile {
updatedMedia[i] = TelegramMediaExpiredContent(data: .file)
}
} }
} }
} }

View File

@ -4111,6 +4111,8 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
} }
fileprivate func presentReactionPremiumSuggestion() { fileprivate func presentReactionPremiumSuggestion() {
self.hapticFeedback.impact(.light)
self.dismissAllTooltips() self.dismissAllTooltips()
let context = self.context 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 case .info = action, let self {
if let stickerScreen = self.node.stickerScreen { if let stickerScreen = self.node.stickerScreen {
self.node.stickerScreen = nil self.node.stickerScreen = nil

View File

@ -1157,9 +1157,9 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
case .Local: case .Local:
if isSecretMedia && self.secretProgressIcon != nil { if isSecretMedia && self.secretProgressIcon != nil {
if let (beginTime, timeout) = secretBeginTimeAndTimeout { 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 { } else {
state = .customIcon(secretProgressIcon!) state = .staticTimeout
} }
} else { } else {
state = .none state = .none

View File

@ -1796,6 +1796,10 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
var badgeContent: ChatMessageInteractiveMediaBadgeContent? var badgeContent: ChatMessageInteractiveMediaBadgeContent?
var mediaDownloadState: ChatMessageInteractiveMediaDownloadState? var mediaDownloadState: ChatMessageInteractiveMediaDownloadState?
if isSecretMedia {
backgroundColor = messageTheme.mediaDateAndStatusFillColor
}
if let invoice = invoice { if let invoice = invoice {
if let extendedMedia = invoice.extendedMedia { if let extendedMedia = invoice.extendedMedia {
if case let .preview(_, _, maybeVideoDuration) = extendedMedia, let videoDuration = maybeVideoDuration { if case let .preview(_, _, maybeVideoDuration) = extendedMedia, let videoDuration = maybeVideoDuration {
@ -1989,11 +1993,9 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
secretProgressIcon = PresentationResourcesChat.chatBubbleSecretMediaCompactIcon(theme) secretProgressIcon = PresentationResourcesChat.chatBubbleSecretMediaCompactIcon(theme)
} }
if isSecretMedia, let (maybeBeginTime, timeout) = secretBeginTimeAndTimeout, let beginTime = maybeBeginTime, Int32(timeout) != viewOnceTimeout { 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) state = .secretTimeout(color: messageTheme.mediaOverlayControlColors.foregroundColor, icon: .flame, beginTime: beginTime, timeout: timeout, sparks: true)
backgroundColor = messageTheme.mediaDateAndStatusFillColor
} else if isSecretMedia, let _ = secretProgressIcon { } else if isSecretMedia, let _ = secretProgressIcon {
state = .staticTimeout state = .staticTimeout
backgroundColor = messageTheme.mediaDateAndStatusFillColor
} else if let file = media as? TelegramMediaFile, !file.isVideoSticker { } else if let file = media as? TelegramMediaFile, !file.isVideoSticker {
let isInlinePlayableVideo = file.isVideo && !isSecretMedia && (self.automaticPlayback ?? false) let isInlinePlayableVideo = file.isVideo && !isSecretMedia && (self.automaticPlayback ?? false)
if (!isInlinePlayableVideo || isStory) && file.isVideo { if (!isInlinePlayableVideo || isStory) && file.isVideo {

View File

@ -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)) 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 { if case .top = self.placementPosition {
panelFrame.origin.y = insets.top + margin var topInset = insets.top
panelWrapperFrame.origin.y = insets.top + margin 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) transition.updateFrame(node: self.panelNode, frame: panelFrame)
@ -1441,7 +1445,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
let statusSize: CGFloat = 30.0 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))) 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 { 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: {})
} }
} }