Stories improvements

This commit is contained in:
Ilya Laktyushin 2023-06-27 20:53:24 +02:00
parent b10ae1b12a
commit 8ac9121c5e
28 changed files with 402 additions and 148 deletions

View File

@ -1369,7 +1369,7 @@ public final class CalendarMessageScreen: ViewController {
if self.selectionState?.dayRange == nil { if self.selectionState?.dayRange == nil {
if let selectionToolbarNode = self.selectionToolbarNode { if let selectionToolbarNode = self.selectionToolbarNode {
let toolbarFrame = selectionToolbarNode.view.convert(selectionToolbarNode.bounds, to: self.view) let toolbarFrame = selectionToolbarNode.view.convert(selectionToolbarNode.bounds, to: self.view)
self.controller?.present(TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: self.presentationData.strings.MessageCalendar_EmptySelectionTooltip, style: .default, icon: .none, location: .point(toolbarFrame.insetBy(dx: 0.0, dy: 10.0), .bottom), shouldDismissOnTouch: { point in self.controller?.present(TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .plain(text: self.presentationData.strings.MessageCalendar_EmptySelectionTooltip), style: .default, icon: .none, location: .point(toolbarFrame.insetBy(dx: 0.0, dy: 10.0), .bottom), shouldDismissOnTouch: { point in
return .dismiss(consume: false) return .dismiss(consume: false)
}), in: .current) }), in: .current)
} }

View File

@ -165,6 +165,7 @@ private final class VideoRecorderImpl {
if !self.savedTransitionImage, let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) { if !self.savedTransitionImage, let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) {
self.savedTransitionImage = true self.savedTransitionImage = true
Queue.concurrentDefaultQueue().async {
let ciImage = CIImage(cvPixelBuffer: pixelBuffer) let ciImage = CIImage(cvPixelBuffer: pixelBuffer)
if let cgImage = self.imageContext.createCGImage(ciImage, from: ciImage.extent) { if let cgImage = self.imageContext.createCGImage(ciImage, from: ciImage.extent) {
self.transitionImage = UIImage(cgImage: cgImage, scale: 1.0, orientation: .right) self.transitionImage = UIImage(cgImage: cgImage, scale: 1.0, orientation: .right)
@ -172,6 +173,7 @@ private final class VideoRecorderImpl {
self.savedTransitionImage = false self.savedTransitionImage = false
} }
} }
}
if videoInput.append(sampleBuffer) { if videoInput.append(sampleBuffer) {
self.lastVideoSampleTime = presentationTime self.lastVideoSampleTime = presentationTime

View File

@ -2356,7 +2356,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.minY - 8.0), size: CGSize()) let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.minY - 8.0), size: CGSize())
parentController.present(TooltipScreen(account: strongSelf.context.account, sharedContext: strongSelf.context.sharedContext, text: text, icon: .chatListPress, location: .point(location, .bottom), shouldDismissOnTouch: { point in parentController.present(TooltipScreen(account: strongSelf.context.account, sharedContext: strongSelf.context.sharedContext, text: .plain(text: text), icon: .animation(name: "ChatListFoldersTooltip", delay: 0.6), location: .point(location, .bottom), shouldDismissOnTouch: { point in
guard let strongSelf = self, let parentController = strongSelf.parent as? TabBarController else { guard let strongSelf = self, let parentController = strongSelf.parent as? TabBarController else {
return .dismiss(consume: false) return .dismiss(consume: false)
} }
@ -2697,11 +2697,21 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
return return
} }
let location = CGRect(origin: CGPoint(x: sourceFrame.midX, y: sourceFrame.minY - 8.0), size: CGSize()) let location = CGRect(origin: CGPoint(x: sourceFrame.midX, y: sourceFrame.minY + 1.0), size: CGSize())
let tooltipController = TooltipScreen( let tooltipController = TooltipScreen(
context: self.context,
account: self.context.account, account: self.context.account,
sharedContext: self.context.sharedContext, sharedContext: self.context.sharedContext,
text: "Stories from \(peer.compactDisplayTitle) will now be shown in Contacts, not Chats.", text: .markdown(text: "Stories from **\(peer.compactDisplayTitle)** will now be shown in Contacts, not Chats."),
icon: .peer(peer: peer, isStory: true),
action: TooltipScreen.Action(
title: "Undo",
action: { [weak self] in
if let self {
self.context.engine.peers.updatePeerStoriesHidden(id: peer.id, isHidden: false)
}
}
),
location: .point(location, .bottom), location: .point(location, .bottom),
shouldDismissOnTouch: { _ in return .dismiss(consume: false) } shouldDismissOnTouch: { _ in return .dismiss(consume: false) }
) )

View File

@ -96,15 +96,25 @@ func contactContextMenuItems(context: AccountContext, peerId: EnginePeer.Id, con
return return
} }
let location = CGRect(origin: CGPoint(x: sourceFrame.midX, y: sourceFrame.minY - 8.0), size: CGSize()) if let peer {
let location = CGRect(origin: CGPoint(x: sourceFrame.midX, y: sourceFrame.minY + 1.0), size: CGSize())
let tooltipController = TooltipScreen( let tooltipController = TooltipScreen(
context: context,
account: context.account, account: context.account,
sharedContext: context.sharedContext, sharedContext: context.sharedContext,
text: "Stories from \(peer?.compactDisplayTitle ?? "") will now be shown in Chats, not Contacts.", text: .markdown(text: "Stories from \(peer.compactDisplayTitle) will now be shown in Chats, not Contacts."),
icon: .peer(peer: peer, isStory: true),
action: TooltipScreen.Action(
title: "Undo",
action: {
context.engine.peers.updatePeerStoriesHidden(id: peer.id, isHidden: true)
}
),
location: .point(location, .bottom), location: .point(location, .bottom),
shouldDismissOnTouch: { _ in return .dismiss(consume: false) } shouldDismissOnTouch: { _ in return .dismiss(consume: false) }
) )
contactsController?.present(tooltipController, in: .window(.root)) contactsController?.present(tooltipController, in: .window(.root))
}
}))) })))
return items return items

View File

@ -828,7 +828,7 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
text = strongSelf.presentationData.strings.Location_ProximityTip(EnginePeer(peer).compactDisplayTitle).string text = strongSelf.presentationData.strings.Location_ProximityTip(EnginePeer(peer).compactDisplayTitle).string
} }
strongSelf.interaction.present(TooltipScreen(account: strongSelf.context.account, sharedContext: strongSelf.context.sharedContext, text: text, icon: nil, location: .point(location.offsetBy(dx: -9.0, dy: 0.0), .right), displayDuration: .custom(3.0), shouldDismissOnTouch: { _ in strongSelf.interaction.present(TooltipScreen(account: strongSelf.context.account, sharedContext: strongSelf.context.sharedContext, text: .plain(text: text), icon: nil, location: .point(location.offsetBy(dx: -9.0, dy: 0.0), .right), displayDuration: .custom(3.0), shouldDismissOnTouch: { _ in
return .dismiss(consume: false) return .dismiss(consume: false)
})) }))
}) })

View File

@ -179,7 +179,7 @@ public final class QrCodeScanScreen: ViewController {
return return
} }
if let navigationController = navigationController as? NavigationController { if let navigationController = navigationController as? NavigationController {
self.present(UndoOverlayController(presentationData: self.presentationData, content: .actionSucceeded(title: self.presentationData.strings.AuthSessions_AddedDeviceTitle, text: session?.appName ?? "Telegram for macOS", cancel: self.presentationData.strings.AuthSessions_AddedDeviceTerminate), elevatedLayout: false, animateInAsReplacement: false, action: { value in self.present(UndoOverlayController(presentationData: self.presentationData, content: .actionSucceeded(title: self.presentationData.strings.AuthSessions_AddedDeviceTitle, text: session?.appName ?? "Telegram for macOS", cancel: self.presentationData.strings.AuthSessions_AddedDeviceTerminate, destructive: true), elevatedLayout: false, animateInAsReplacement: false, action: { value in
if value == .undo, let session = session { if value == .undo, let session = session {
let _ = activeSessionsContext.remove(hash: session.hash).start() let _ = activeSessionsContext.remove(hash: session.hash).start()
return true return true

View File

@ -456,7 +456,7 @@ public final class ThemePreviewController: ViewController {
guard let strongSelf = self, !displayed else { guard let strongSelf = self, !displayed else {
return return
} }
strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .actionSucceeded(title: strongSelf.presentationData.strings.Theme_ThemeChanged, text: strongSelf.presentationData.strings.Theme_ThemeChangedText, cancel: strongSelf.presentationData.strings.Undo_Undo), elevatedLayout: true, animateInAsReplacement: false, action: { value in strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .actionSucceeded(title: strongSelf.presentationData.strings.Theme_ThemeChanged, text: strongSelf.presentationData.strings.Theme_ThemeChangedText, cancel: strongSelf.presentationData.strings.Undo_Undo, destructive: false), elevatedLayout: true, animateInAsReplacement: false, action: { value in
if value == .undo { if value == .undo {
Queue.mainQueue().after(0.2) { Queue.mainQueue().after(0.2) {
let _ = updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current -> PresentationThemeSettings in let _ = updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current -> PresentationThemeSettings in

View File

@ -1729,7 +1729,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
if let strongSelf = self, (count < 2 && currentTimestamp > timestamp + 24 * 60 * 60) { if let strongSelf = self, (count < 2 && currentTimestamp > timestamp + 24 * 60 * 60) {
strongSelf.displayedPreviewTooltip = true strongSelf.displayedPreviewTooltip = true
let controller = TooltipScreen(account: strongSelf.context.account, sharedContext: strongSelf.context.sharedContext, text: isDark ? strongSelf.presentationData.strings.WallpaperPreview_PreviewInDayMode : strongSelf.presentationData.strings.WallpaperPreview_PreviewInNightMode, style: .customBlur(UIColor(rgb: 0x333333, alpha: 0.35)), icon: nil, location: .point(frame.offsetBy(dx: 1.0, dy: 6.0), .bottom), displayDuration: .custom(3.0), inset: 3.0, shouldDismissOnTouch: { _ in let controller = TooltipScreen(account: strongSelf.context.account, sharedContext: strongSelf.context.sharedContext, text: .plain(text: isDark ? strongSelf.presentationData.strings.WallpaperPreview_PreviewInDayMode : strongSelf.presentationData.strings.WallpaperPreview_PreviewInNightMode), style: .customBlur(UIColor(rgb: 0x333333, alpha: 0.35)), icon: nil, location: .point(frame.offsetBy(dx: 1.0, dy: 6.0), .bottom), displayDuration: .custom(3.0), inset: 3.0, shouldDismissOnTouch: { _ in
return .dismiss(consume: false) return .dismiss(consume: false)
}) })
strongSelf.galleryController()?.present(controller, in: .current) strongSelf.galleryController()?.present(controller, in: .current)

View File

@ -529,7 +529,7 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi
let _ = (ApplicationSpecificNotice.incrementAudioRateOptionsTip(accountManager: self.context.sharedContext.accountManager) let _ = (ApplicationSpecificNotice.incrementAudioRateOptionsTip(accountManager: self.context.sharedContext.accountManager)
|> deliverOnMainQueue).start(next: { [weak self] value in |> deliverOnMainQueue).start(next: { [weak self] value in
if let strongSelf = self, let controller = strongSelf.getController?(), value == 2 { if let strongSelf = self, let controller = strongSelf.getController?(), value == 2 {
let tooltipController = TooltipScreen(account: strongSelf.context.account, sharedContext: strongSelf.context.sharedContext, text: strongSelf.strings.Conversation_AudioRateOptionsTooltip, style: .default, icon: nil, location: .point(frame.offsetBy(dx: 0.0, dy: 4.0), .bottom), displayDuration: .custom(3.0), inset: 3.0, shouldDismissOnTouch: { _ in let tooltipController = TooltipScreen(account: strongSelf.context.account, sharedContext: strongSelf.context.sharedContext, text: .plain(text: strongSelf.strings.Conversation_AudioRateOptionsTooltip), style: .default, icon: nil, location: .point(frame.offsetBy(dx: 0.0, dy: 4.0), .bottom), displayDuration: .custom(3.0), inset: 3.0, shouldDismissOnTouch: { _ in
return .dismiss(consume: false) return .dismiss(consume: false)
}) })
controller.present(tooltipController, in: .window(.root)) controller.present(tooltipController, in: .window(.root))

View File

@ -761,7 +761,7 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
return return
} }
self.present?(TooltipScreen(account: self.account, sharedContext: self.sharedContext, text: self.presentationData.strings.Call_CameraOrScreenTooltip, style: .light, icon: nil, location: .point(location.offsetBy(dx: 0.0, dy: -14.0), .bottom), displayDuration: .custom(5.0), shouldDismissOnTouch: { _ in self.present?(TooltipScreen(account: self.account, sharedContext: self.sharedContext, text: .plain(text: self.presentationData.strings.Call_CameraOrScreenTooltip), style: .light, icon: nil, location: .point(location.offsetBy(dx: 0.0, dy: -14.0), .bottom), displayDuration: .custom(5.0), shouldDismissOnTouch: { _ in
return .dismiss(consume: false) return .dismiss(consume: false)
})) }))
} }

View File

@ -2308,7 +2308,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController
} else { } else {
text = presentationData.strings.VoiceChat_RecordingInProgress text = presentationData.strings.VoiceChat_RecordingInProgress
} }
strongSelf.controller?.present(TooltipScreen(account: strongSelf.context.account, sharedContext: strongSelf.context.sharedContext, text: text, icon: nil, location: .point(location.offsetBy(dx: 1.0, dy: 0.0), .top), displayDuration: .custom(3.0), shouldDismissOnTouch: { _ in strongSelf.controller?.present(TooltipScreen(account: strongSelf.context.account, sharedContext: strongSelf.context.sharedContext, text: .plain(text: text), icon: nil, location: .point(location.offsetBy(dx: 1.0, dy: 0.0), .top), displayDuration: .custom(3.0), shouldDismissOnTouch: { _ in
return .dismiss(consume: true) return .dismiss(consume: true)
}), in: .window(.root)) }), in: .window(.root))
} }
@ -3505,7 +3505,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController
if !callState.subscribedToScheduled { if !callState.subscribedToScheduled {
let location = self.actionButton.view.convert(self.actionButton.bounds, to: self.view).center let location = self.actionButton.view.convert(self.actionButton.bounds, to: self.view).center
let point = CGRect(origin: CGPoint(x: location.x - 5.0, y: location.y - 5.0 - 68.0), size: CGSize(width: 10.0, height: 10.0)) let point = CGRect(origin: CGPoint(x: location.x - 5.0, y: location.y - 5.0 - 68.0), size: CGSize(width: 10.0, height: 10.0))
self.controller?.present(TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: self.presentationData.strings.VoiceChat_ReminderNotify, style: .gradient(UIColor(rgb: 0x262c5a), UIColor(rgb: 0x5d2835)), icon: nil, location: .point(point, .bottom), displayDuration: .custom(3.0), shouldDismissOnTouch: { _ in self.controller?.present(TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .plain(text: self.presentationData.strings.VoiceChat_ReminderNotify), style: .gradient(UIColor(rgb: 0x262c5a), UIColor(rgb: 0x5d2835)), icon: nil, location: .point(point, .bottom), displayDuration: .custom(3.0), shouldDismissOnTouch: { _ in
return .dismiss(consume: false) return .dismiss(consume: false)
}), in: .window(.root)) }), in: .window(.root))
} }
@ -6409,7 +6409,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController
point.origin.y += 32.0 point.origin.y += 32.0
} }
} }
self.controller?.present(TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: self.presentationData.strings.VoiceChat_UnmuteSuggestion, style: .gradient(UIColor(rgb: 0x1d446c), UIColor(rgb: 0x193e63)), icon: nil, location: .point(point, position), displayDuration: .custom(8.0), shouldDismissOnTouch: { _ in self.controller?.present(TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .plain(text: self.presentationData.strings.VoiceChat_UnmuteSuggestion), style: .gradient(UIColor(rgb: 0x1d446c), UIColor(rgb: 0x193e63)), icon: nil, location: .point(point, position), displayDuration: .custom(8.0), shouldDismissOnTouch: { _ in
return .dismiss(consume: false) return .dismiss(consume: false)
}), in: .window(.root)) }), in: .window(.root))
} }

View File

@ -1589,7 +1589,7 @@ public class CameraScreen: ViewController {
let absoluteFrame = sourceView.convert(sourceView.bounds, to: nil).offsetBy(dx: -parentFrame.minX, dy: 0.0) let absoluteFrame = sourceView.convert(sourceView.bounds, to: nil).offsetBy(dx: -parentFrame.minX, dy: 0.0)
let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.minY - 4.0), size: CGSize()) let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.minY - 4.0), size: CGSize())
let controller = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: "Draft Saved", location: .point(location, .bottom), displayDuration: .default, inset: 16.0, shouldDismissOnTouch: { _ in let controller = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .plain(text: "Draft Saved"), location: .point(location, .bottom), displayDuration: .default, inset: 16.0, shouldDismissOnTouch: { _ in
return .ignore return .ignore
}) })
self.controller?.present(controller, in: .current) self.controller?.present(controller, in: .current)
@ -1604,7 +1604,7 @@ public class CameraScreen: ViewController {
let absoluteFrame = sourceView.convert(sourceView.bounds, to: nil).offsetBy(dx: -parentFrame.minX, dy: 0.0) let absoluteFrame = sourceView.convert(sourceView.bounds, to: nil).offsetBy(dx: -parentFrame.minX, dy: 0.0)
let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.maxY + 3.0), size: CGSize()) let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.maxY + 3.0), size: CGSize())
let tooltipController = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: "Enable Dual Camera Mode", location: .point(location, .top), displayDuration: .manual(false), inset: 16.0, shouldDismissOnTouch: { _ in let tooltipController = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .plain(text: "Enable Dual Camera Mode"), location: .point(location, .top), displayDuration: .manual(false), inset: 16.0, shouldDismissOnTouch: { _ in
return .ignore return .ignore
}) })
self.controller?.present(tooltipController, in: .current) self.controller?.present(tooltipController, in: .current)
@ -1617,9 +1617,9 @@ public class CameraScreen: ViewController {
let parentFrame = self.view.convert(self.bounds, to: nil) let parentFrame = self.view.convert(self.bounds, to: nil)
let absoluteFrame = sourceView.convert(sourceView.bounds, to: nil).offsetBy(dx: -parentFrame.minX, dy: 0.0) let absoluteFrame = sourceView.convert(sourceView.bounds, to: nil).offsetBy(dx: -parentFrame.minX, dy: 0.0)
let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.minY - 3.0), size: CGSize()) let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.minY - 1.0), size: CGSize())
let tooltipController = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: "Take photos or videos to share with all your contacts or close friends at once.", location: .point(location, .bottom), displayDuration: .default, inset: 16.0, shouldDismissOnTouch: { _ in let tooltipController = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .plain(text: "Take photos or videos to share with all\nyour contacts or close friends at once."), textAlignment: .center, location: .point(location, .bottom), displayDuration: .custom(3.0), inset: 16.0, shouldDismissOnTouch: { _ in
return .ignore return .ignore
}) })
self.controller?.present(tooltipController, in: .current) self.controller?.present(tooltipController, in: .current)
@ -2106,6 +2106,11 @@ public class CameraScreen: ViewController {
if let layout = self.validLayout, case .regular = layout.metrics.widthClass { if let layout = self.validLayout, case .regular = layout.metrics.widthClass {
return return
} }
if self.node.hasAppeared {
self.dismissAllTooltips()
}
let transitionFraction = max(0.0, min(1.0, transitionFraction)) let transitionFraction = max(0.0, min(1.0, transitionFraction))
let offsetX = floorToScreenPixels((1.0 - transitionFraction) * self.node.frame.width * -1.0) let offsetX = floorToScreenPixels((1.0 - transitionFraction) * self.node.frame.width * -1.0)
transition.updateTransform(layer: self.node.backgroundView.layer, transform: CGAffineTransform(translationX: offsetX, y: 0.0)) transition.updateTransform(layer: self.node.backgroundView.layer, transform: CGAffineTransform(translationX: offsetX, y: 0.0))

View File

@ -265,6 +265,7 @@ public final class MediaEditor {
videoTrimRange: nil, videoTrimRange: nil,
videoIsMuted: false, videoIsMuted: false,
videoIsFullHd: false, videoIsFullHd: false,
videoIsMirrored: false,
additionalVideoPath: nil, additionalVideoPath: nil,
additionalVideoPosition: nil, additionalVideoPosition: nil,
additionalVideoScale: nil, additionalVideoScale: nil,
@ -582,6 +583,12 @@ public final class MediaEditor {
} }
} }
public func setVideoIsMirrored(_ videoIsMirrored: Bool) {
self.updateValues(mode: .skipRendering) { values in
return values.withUpdatedVideoIsMirrored(videoIsMirrored)
}
}
public func setVideoIsFullHd(_ videoIsFullHd: Bool) { public func setVideoIsFullHd(_ videoIsFullHd: Bool) {
self.updateValues(mode: .skipRendering) { values in self.updateValues(mode: .skipRendering) { values in
return values.withUpdatedVideoIsFullHd(videoIsFullHd) return values.withUpdatedVideoIsFullHd(videoIsFullHd)

View File

@ -84,6 +84,9 @@ public final class MediaEditorValues: Codable, Equatable {
if lhs.videoIsFullHd != rhs.videoIsFullHd { if lhs.videoIsFullHd != rhs.videoIsFullHd {
return false return false
} }
if lhs.videoIsMirrored != rhs.videoIsMirrored {
return false
}
if lhs.additionalVideoPath != rhs.additionalVideoPath { if lhs.additionalVideoPath != rhs.additionalVideoPath {
return false return false
} }
@ -143,6 +146,7 @@ public final class MediaEditorValues: Codable, Equatable {
case videoTrimRange case videoTrimRange
case videoIsMuted case videoIsMuted
case videoIsFullHd case videoIsFullHd
case videoIsMirrored
case additionalVideoPath case additionalVideoPath
case additionalVideoPosition case additionalVideoPosition
@ -167,6 +171,7 @@ public final class MediaEditorValues: Codable, Equatable {
public let videoTrimRange: Range<Double>? public let videoTrimRange: Range<Double>?
public let videoIsMuted: Bool public let videoIsMuted: Bool
public let videoIsFullHd: Bool public let videoIsFullHd: Bool
public let videoIsMirrored: Bool
public let additionalVideoPath: String? public let additionalVideoPath: String?
public let additionalVideoPosition: CGPoint? public let additionalVideoPosition: CGPoint?
@ -189,6 +194,7 @@ public final class MediaEditorValues: Codable, Equatable {
videoTrimRange: Range<Double>?, videoTrimRange: Range<Double>?,
videoIsMuted: Bool, videoIsMuted: Bool,
videoIsFullHd: Bool, videoIsFullHd: Bool,
videoIsMirrored: Bool,
additionalVideoPath: String?, additionalVideoPath: String?,
additionalVideoPosition: CGPoint?, additionalVideoPosition: CGPoint?,
additionalVideoScale: CGFloat?, additionalVideoScale: CGFloat?,
@ -208,6 +214,7 @@ public final class MediaEditorValues: Codable, Equatable {
self.videoTrimRange = videoTrimRange self.videoTrimRange = videoTrimRange
self.videoIsMuted = videoIsMuted self.videoIsMuted = videoIsMuted
self.videoIsFullHd = videoIsFullHd self.videoIsFullHd = videoIsFullHd
self.videoIsMirrored = videoIsMirrored
self.additionalVideoPath = additionalVideoPath self.additionalVideoPath = additionalVideoPath
self.additionalVideoPosition = additionalVideoPosition self.additionalVideoPosition = additionalVideoPosition
self.additionalVideoScale = additionalVideoScale self.additionalVideoScale = additionalVideoScale
@ -240,6 +247,7 @@ public final class MediaEditorValues: Codable, Equatable {
self.videoTrimRange = try container.decodeIfPresent(Range<Double>.self, forKey: .videoTrimRange) self.videoTrimRange = try container.decodeIfPresent(Range<Double>.self, forKey: .videoTrimRange)
self.videoIsMuted = try container.decode(Bool.self, forKey: .videoIsMuted) self.videoIsMuted = try container.decode(Bool.self, forKey: .videoIsMuted)
self.videoIsFullHd = try container.decodeIfPresent(Bool.self, forKey: .videoIsFullHd) ?? false self.videoIsFullHd = try container.decodeIfPresent(Bool.self, forKey: .videoIsFullHd) ?? false
self.videoIsMirrored = try container.decodeIfPresent(Bool.self, forKey: .videoIsMirrored) ?? false
self.additionalVideoPath = try container.decodeIfPresent(String.self, forKey: .additionalVideoPath) self.additionalVideoPath = try container.decodeIfPresent(String.self, forKey: .additionalVideoPath)
self.additionalVideoPosition = try container.decodeIfPresent(CGPoint.self, forKey: .additionalVideoPosition) self.additionalVideoPosition = try container.decodeIfPresent(CGPoint.self, forKey: .additionalVideoPosition)
@ -283,6 +291,7 @@ public final class MediaEditorValues: Codable, Equatable {
try container.encodeIfPresent(self.videoTrimRange, forKey: .videoTrimRange) try container.encodeIfPresent(self.videoTrimRange, forKey: .videoTrimRange)
try container.encode(self.videoIsMuted, forKey: .videoIsMuted) try container.encode(self.videoIsMuted, forKey: .videoIsMuted)
try container.encode(self.videoIsFullHd, forKey: .videoIsFullHd) try container.encode(self.videoIsFullHd, forKey: .videoIsFullHd)
try container.encode(self.videoIsMirrored, forKey: .videoIsMirrored)
try container.encodeIfPresent(self.additionalVideoPath, forKey: .additionalVideoPath) try container.encodeIfPresent(self.additionalVideoPath, forKey: .additionalVideoPath)
try container.encodeIfPresent(self.additionalVideoPosition, forKey: .additionalVideoPosition) try container.encodeIfPresent(self.additionalVideoPosition, forKey: .additionalVideoPosition)
@ -306,43 +315,48 @@ public final class MediaEditorValues: Codable, Equatable {
} }
public func makeCopy() -> MediaEditorValues { public func makeCopy() -> MediaEditorValues {
return MediaEditorValues(originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues) return MediaEditorValues(originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues)
} }
func withUpdatedCrop(offset: CGPoint, scale: CGFloat, rotation: CGFloat, mirroring: Bool) -> MediaEditorValues { func withUpdatedCrop(offset: CGPoint, scale: CGFloat, rotation: CGFloat, mirroring: Bool) -> MediaEditorValues {
return MediaEditorValues(originalDimensions: self.originalDimensions, cropOffset: offset, cropSize: self.cropSize, cropScale: scale, cropRotation: rotation, cropMirroring: mirroring, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues) return MediaEditorValues(originalDimensions: self.originalDimensions, cropOffset: offset, cropSize: self.cropSize, cropScale: scale, cropRotation: rotation, cropMirroring: mirroring, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues)
} }
func withUpdatedGradientColors(gradientColors: [UIColor]) -> MediaEditorValues { func withUpdatedGradientColors(gradientColors: [UIColor]) -> MediaEditorValues {
return MediaEditorValues(originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues) return MediaEditorValues(originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues)
} }
func withUpdatedVideoIsMuted(_ videoIsMuted: Bool) -> MediaEditorValues { func withUpdatedVideoIsMuted(_ videoIsMuted: Bool) -> MediaEditorValues {
return MediaEditorValues(originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: videoIsMuted, videoIsFullHd: self.videoIsFullHd, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues) return MediaEditorValues(originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues)
} }
func withUpdatedVideoIsFullHd(_ videoIsFullHd: Bool) -> MediaEditorValues { func withUpdatedVideoIsFullHd(_ videoIsFullHd: Bool) -> MediaEditorValues {
return MediaEditorValues(originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: videoIsFullHd, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues) return MediaEditorValues(originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues)
}
func withUpdatedVideoIsMirrored(_ videoIsMirrored: Bool) -> MediaEditorValues {
return MediaEditorValues(originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues)
} }
func withUpdatedAdditionalVideo(path: String, positionChanges: [VideoPositionChange]) -> MediaEditorValues { func withUpdatedAdditionalVideo(path: String, positionChanges: [VideoPositionChange]) -> MediaEditorValues {
return MediaEditorValues(originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, additionalVideoPath: path, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: positionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues) return MediaEditorValues(originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: path, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: positionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues)
} }
func withUpdatedAdditionalVideo(position: CGPoint, scale: CGFloat, rotation: CGFloat) -> MediaEditorValues { func withUpdatedAdditionalVideo(position: CGPoint, scale: CGFloat, rotation: CGFloat) -> MediaEditorValues {
return MediaEditorValues(originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: position, additionalVideoScale: scale, additionalVideoRotation: rotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues) return MediaEditorValues(originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: position, additionalVideoScale: scale, additionalVideoRotation: rotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues)
} }
func withUpdatedVideoTrimRange(_ videoTrimRange: Range<Double>) -> MediaEditorValues { func withUpdatedVideoTrimRange(_ videoTrimRange: Range<Double>) -> MediaEditorValues {
return MediaEditorValues(originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues) return MediaEditorValues(originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues)
} }
func withUpdatedDrawingAndEntities(drawing: UIImage?, entities: [CodableDrawingEntity]) -> MediaEditorValues { func withUpdatedDrawingAndEntities(drawing: UIImage?, entities: [CodableDrawingEntity]) -> MediaEditorValues {
return MediaEditorValues(originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: drawing, entities: entities, toolValues: self.toolValues) return MediaEditorValues(originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: drawing, entities: entities, toolValues: self.toolValues)
} }
func withUpdatedToolValues(_ toolValues: [EditorToolKey: Any]) -> MediaEditorValues { func withUpdatedToolValues(_ toolValues: [EditorToolKey: Any]) -> MediaEditorValues {
return MediaEditorValues(originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: toolValues) return MediaEditorValues(originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: toolValues)
} }
public var resultDimensions: PixelDimensions { public var resultDimensions: PixelDimensions {

View File

@ -345,11 +345,17 @@ public final class MediaEditorVideoExport {
private func setupWithAsset(_ asset: AVAsset, additionalAsset: AVAsset?) { private func setupWithAsset(_ asset: AVAsset, additionalAsset: AVAsset?) {
self.reader = try? AVAssetReader(asset: asset) self.reader = try? AVAssetReader(asset: asset)
self.textureRotation = textureRotatonForAVAsset(asset)
var mirror = false
if additionalAsset == nil, self.configuration.values.videoIsMirrored {
mirror = true
}
self.textureRotation = textureRotatonForAVAsset(asset, mirror: mirror)
if let additionalAsset { if let additionalAsset {
self.additionalReader = try? AVAssetReader(asset: additionalAsset) self.additionalReader = try? AVAssetReader(asset: additionalAsset)
self.additionalTextureRotation = textureRotatonForAVAsset(additionalAsset) self.additionalTextureRotation = textureRotatonForAVAsset(additionalAsset, mirror: true)
} }
guard let reader = self.reader else { guard let reader = self.reader else {
return return

View File

@ -696,7 +696,9 @@ final class MediaEditorScreenComponent: Component {
guard let controller = environment.controller() as? MediaEditorScreen else { guard let controller = environment.controller() as? MediaEditorScreen else {
return return
} }
controller.requestCompletion(animated: true) controller.openPrivacySettings(completion: { [weak controller] in
controller?.requestCompletion(animated: true)
})
} }
)), )),
environment: {}, environment: {},
@ -1142,7 +1144,7 @@ final class MediaEditorScreenComponent: Component {
} }
if let privacyButtonView = self.privacyButton.view { if let privacyButtonView = self.privacyButton.view {
if privacyButtonView.superview == nil { if privacyButtonView.superview == nil {
self.addSubview(privacyButtonView) //self.addSubview(privacyButtonView)
} }
transition.setPosition(view: privacyButtonView, position: privacyButtonFrame.center) transition.setPosition(view: privacyButtonView, position: privacyButtonFrame.center)
transition.setBounds(view: privacyButtonView, bounds: CGRect(origin: .zero, size: privacyButtonFrame.size)) transition.setBounds(view: privacyButtonView, bounds: CGRect(origin: .zero, size: privacyButtonFrame.size))
@ -1841,7 +1843,9 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
imageEntity.scale = 1.49 imageEntity.scale = 1.49
imageEntity.position = position.getPosition(storyDimensions) imageEntity.position = position.getPosition(storyDimensions)
self.entitiesView.add(imageEntity, announce: false) self.entitiesView.add(imageEntity, announce: false)
} else if case let .video(_, _, _, additionalVideoPath, _, _, _, changes, position) = subject, let additionalVideoPath { } else if case let .video(_, _, mirror, additionalVideoPath, _, _, _, changes, position) = subject {
mediaEditor.setVideoIsMirrored(mirror)
if let additionalVideoPath {
let videoEntity = DrawingStickerEntity(content: .dualVideoReference) let videoEntity = DrawingStickerEntity(content: .dualVideoReference)
videoEntity.referenceDrawingSize = storyDimensions videoEntity.referenceDrawingSize = storyDimensions
videoEntity.scale = 1.49 videoEntity.scale = 1.49
@ -1857,6 +1861,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
} }
} }
} }
}
} else if case let .asset(asset) = subject, asset.mediaType == .video { } else if case let .asset(asset) = subject, asset.mediaType == .video {
//#if DEBUG //#if DEBUG
// let videoEntity = DrawingStickerEntity(content: .dualVideoReference) // let videoEntity = DrawingStickerEntity(content: .dualVideoReference)
@ -2504,7 +2509,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
let absoluteFrame = sourceView.convert(sourceView.bounds, to: nil).offsetBy(dx: -parentFrame.minX, dy: 0.0) let absoluteFrame = sourceView.convert(sourceView.bounds, to: nil).offsetBy(dx: -parentFrame.minX, dy: 0.0)
let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.maxY + 3.0), size: CGSize()) let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.maxY + 3.0), size: CGSize())
let tooltipController = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: "You can set who can view this story.", location: .point(location, .top), displayDuration: .manual(false), inset: 16.0, shouldDismissOnTouch: { _ in let tooltipController = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .plain(text: "You can set who can view this story."), location: .point(location, .top), displayDuration: .manual(false), inset: 16.0, shouldDismissOnTouch: { _ in
return .ignore return .ignore
}) })
self.controller?.present(tooltipController, in: .current) self.controller?.present(tooltipController, in: .current)
@ -2527,7 +2532,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
let absoluteFrame = sourceView.convert(sourceView.bounds, to: nil).offsetBy(dx: -parentFrame.minX, dy: 0.0) let absoluteFrame = sourceView.convert(sourceView.bounds, to: nil).offsetBy(dx: -parentFrame.minX, dy: 0.0)
let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.maxY + 3.0), size: CGSize()) let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.maxY + 3.0), size: CGSize())
let tooltipController = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: isMuted ? "The story will have no sound." : "The story will have sound." , location: .point(location, .top), displayDuration: .default, inset: 16.0, shouldDismissOnTouch: { _ in let tooltipController = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .plain(text: isMuted ? "The story will have no sound." : "The story will have sound."), location: .point(location, .top), displayDuration: .default, inset: 16.0, shouldDismissOnTouch: { _ in
return .ignore return .ignore
}) })
self.muteTooltip = tooltipController self.muteTooltip = tooltipController
@ -2642,7 +2647,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
text = "Story will disappear in 24 hours." text = "Story will disappear in 24 hours."
} }
let tooltipController = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: text, location: .point(location, .bottom), displayDuration: .default, inset: 7.0, cornerRadius: 9.0, shouldDismissOnTouch: { _ in let tooltipController = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .plain(text: text), location: .point(location, .bottom), displayDuration: .default, inset: 7.0, cornerRadius: 9.0, shouldDismissOnTouch: { _ in
return .ignore return .ignore
}) })
self.storyArchiveTooltip = tooltipController self.storyArchiveTooltip = tooltipController
@ -3133,7 +3138,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
self.displayNode.view.addInteraction(dropInteraction) self.displayNode.view.addInteraction(dropInteraction)
} }
func openPrivacySettings(_ privacy: MediaEditorResultPrivacy? = nil) { func openPrivacySettings(_ privacy: MediaEditorResultPrivacy? = nil, completion: @escaping () -> Void = {}) {
self.hapticFeedback.impact(.light) self.hapticFeedback.impact(.light)
let privacy = privacy ?? self.state.privacy let privacy = privacy ?? self.state.privacy
@ -3157,6 +3162,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
return return
} }
self.state.privacy = MediaEditorResultPrivacy(privacy: privacy, timeout: timeout, archive: archive) self.state.privacy = MediaEditorResultPrivacy(privacy: privacy, timeout: timeout, archive: archive)
completion()
}, },
editCategory: { [weak self] privacy in editCategory: { [weak self] privacy in
guard let self else { guard let self else {
@ -3166,7 +3172,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
guard let self else { guard let self else {
return return
} }
self.openPrivacySettings(MediaEditorResultPrivacy(privacy: privacy, timeout: timeout, archive: archive)) self.openPrivacySettings(MediaEditorResultPrivacy(privacy: privacy, timeout: timeout, archive: archive), completion: completion)
}) })
} }
) )

View File

@ -458,8 +458,7 @@ final class ShareWithPeersScreenComponent: Component {
let sectionTitle: String let sectionTitle: String
if section.id == 0 { if section.id == 0 {
let hours = component.timeout / 3600 sectionTitle = "WHO CAN VIEW"
sectionTitle = "WHO CAN VIEW FOR \(hours) HOURS"
} else { } else {
if case .chats = component.stateContext.subject { if case .chats = component.stateContext.subject {
sectionTitle = "CHATS" sectionTitle = "CHATS"
@ -1063,10 +1062,12 @@ final class ShareWithPeersScreenComponent: Component {
} }
navigationButtonsWidth += navigationLeftButtonSize.width + navigationSideInset navigationButtonsWidth += navigationLeftButtonSize.width + navigationSideInset
var actionButtonTitle = "Save Settings"
let title: String let title: String
switch component.stateContext.subject { switch component.stateContext.subject {
case .stories: case .stories:
title = "Share Story" title = "Share Story"
actionButtonTitle = "Post Story"
case .chats: case .chats:
title = "Send as a Message" title = "Send as a Message"
case let .contacts(category): case let .contacts(category):
@ -1125,7 +1126,6 @@ final class ShareWithPeersScreenComponent: Component {
transition.setFrame(layer: self.navigationSeparatorLayer, frame: CGRect(origin: CGPoint(x: containerSideInset, y: navigationHeight), size: CGSize(width: containerWidth, height: UIScreenPixel))) transition.setFrame(layer: self.navigationSeparatorLayer, frame: CGRect(origin: CGPoint(x: containerSideInset, y: navigationHeight), size: CGSize(width: containerWidth, height: UIScreenPixel)))
let actionButtonTitle: String = "Save Settings"
let actionButtonSize = self.actionButton.update( let actionButtonSize = self.actionButton.update(
transition: transition, transition: transition,
component: AnyComponent(ButtonComponent( component: AnyComponent(ButtonComponent(

View File

@ -712,6 +712,9 @@ public final class StoryItemSetContainerComponent: Component {
if self.inputPanelExternalState.isEditing || component.isProgressPaused || self.actionSheet != nil || self.contextController != nil || self.sendMessageContext.audioRecorderValue != nil || self.sendMessageContext.videoRecorderValue != nil || self.displayViewList { if self.inputPanelExternalState.isEditing || component.isProgressPaused || self.actionSheet != nil || self.contextController != nil || self.sendMessageContext.audioRecorderValue != nil || self.sendMessageContext.videoRecorderValue != nil || self.displayViewList {
return true return true
} }
if let reactionContextNode = self.reactionContextNode, reactionContextNode.isReactionSearchActive {
return true
}
if self.privacyController != nil { if self.privacyController != nil {
return true return true
} }
@ -2235,7 +2238,7 @@ public final class StoryItemSetContainerComponent: Component {
let tooltipScreen = TooltipScreen( let tooltipScreen = TooltipScreen(
account: component.context.account, account: component.context.account,
sharedContext: component.context.sharedContext, sharedContext: component.context.sharedContext,
text: "You are seeing this story because you have\nbeen added to \(component.slice.peer.compactDisplayTitle)'s list of close friends.", style: .default, location: TooltipScreen.Location.point(closeFriendIconView.convert(closeFriendIconView.bounds, to: self).offsetBy(dx: 1.0, dy: 6.0), .top), displayDuration: .manual(true), shouldDismissOnTouch: { _ in text: .markdown(text: "You are seeing this story because you have\nbeen added to **\(component.slice.peer.compactDisplayTitle)'s** list of close friends."), style: .default, location: TooltipScreen.Location.point(closeFriendIconView.convert(closeFriendIconView.bounds, to: self).offsetBy(dx: 1.0, dy: 6.0), .top), displayDuration: .manual(true), shouldDismissOnTouch: { _ in
return .dismiss(consume: false) return .dismiss(consume: false)
} }
) )

View File

@ -338,7 +338,7 @@ final class StoryItemSetContainerSendMessage {
Queue.mainQueue().after(0.3) { Queue.mainQueue().after(0.3) {
controller.present(UndoOverlayController( controller.present(UndoOverlayController(
presentationData: presentationData, presentationData: presentationData,
content: .actionSucceeded(title: "", text: "Message Sent", cancel: "View in Chat"), content: .actionSucceeded(title: "", text: "Message Sent", cancel: "View in Chat", destructive: false),
elevatedLayout: false, elevatedLayout: false,
animateInAsReplacement: false, animateInAsReplacement: false,
action: { [weak view] action in action: { [weak view] action in
@ -380,7 +380,7 @@ final class StoryItemSetContainerSendMessage {
Queue.mainQueue().after(0.3) { Queue.mainQueue().after(0.3) {
controller.present(UndoOverlayController( controller.present(UndoOverlayController(
presentationData: presentationData, presentationData: presentationData,
content: .actionSucceeded(title: "", text: "Message Sent", cancel: "View in Chat"), content: .actionSucceeded(title: "", text: "Message Sent", cancel: "View in Chat", destructive: false),
elevatedLayout: false, elevatedLayout: false,
animateInAsReplacement: false, animateInAsReplacement: false,
action: { [weak view] action in action: { [weak view] action in
@ -442,7 +442,7 @@ final class StoryItemSetContainerSendMessage {
Queue.mainQueue().after(0.3) { Queue.mainQueue().after(0.3) {
controller.present(UndoOverlayController( controller.present(UndoOverlayController(
presentationData: presentationData, presentationData: presentationData,
content: .actionSucceeded(title: "", text: "Message Sent", cancel: "View in Chat"), content: .actionSucceeded(title: "", text: "Message Sent", cancel: "View in Chat", destructive: false),
elevatedLayout: false, elevatedLayout: false,
animateInAsReplacement: false, animateInAsReplacement: false,
action: { [weak view] action in action: { [weak view] action in
@ -491,7 +491,7 @@ final class StoryItemSetContainerSendMessage {
if isVideo { if isVideo {
if self.videoRecorderValue == nil { if self.videoRecorderValue == nil {
if let currentInputPanelFrame = view.inputPanel.view?.frame { if let currentInputPanelFrame = view.inputPanel.view?.frame {
self.videoRecorder.set(.single(legacyInstantVideoController(theme: component.theme, panelFrame: view.convert(currentInputPanelFrame, to: nil), context: component.context, peerId: peer.id, slowmodeState: nil, hasSchedule: peer.id.namespace != Namespaces.Peer.SecretChat, send: { [weak self, weak view] videoController, message in self.videoRecorder.set(.single(legacyInstantVideoController(theme: component.theme, panelFrame: view.convert(currentInputPanelFrame, to: nil), context: component.context, peerId: peer.id, slowmodeState: nil, hasSchedule: true, send: { [weak self, weak view] videoController, message in
guard let self, let view, let component = view.component else { guard let self, let view, let component = view.component else {
return return
} }
@ -2057,28 +2057,36 @@ final class StoryItemSetContainerSendMessage {
} }
private func sendMessages(view: StoryItemSetContainerComponent.View, peer: EnginePeer, messages: [EnqueueMessage], media: Bool = false, commit: Bool = false) { private func sendMessages(view: StoryItemSetContainerComponent.View, peer: EnginePeer, messages: [EnqueueMessage], media: Bool = false, commit: Bool = false) {
guard let component = view.component else { guard let component = view.component, let controller = component.controller() else {
return return
} }
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
let _ = (enqueueMessages(account: component.context.account, peerId: peer.id, messages: self.transformEnqueueMessages(view: view, messages: messages, silentPosting: false)) let _ = (enqueueMessages(account: component.context.account, peerId: peer.id, messages: self.transformEnqueueMessages(view: view, messages: messages, silentPosting: false))
|> deliverOnMainQueue).start() |> deliverOnMainQueue).start(next: { [weak controller] messageIds in
if let controller {
Queue.mainQueue().after(0.3) {
controller.present(UndoOverlayController(
presentationData: presentationData,
content: .actionSucceeded(title: "", text: "Message Sent", cancel: "View in Chat", destructive: false),
elevatedLayout: false,
animateInAsReplacement: false,
action: { [weak view] action in
if case .undo = action, let messageId = messageIds.first {
view?.navigateToPeer(peer: peer, messageId: messageId)
}
return false
}
), in: .current)
}
}
})
donateSendMessageIntent(account: component.context.account, sharedContext: component.context.sharedContext, intentContext: .chat, peerIds: [peer.id]) donateSendMessageIntent(account: component.context.account, sharedContext: component.context.sharedContext, intentContext: .chat, peerIds: [peer.id])
if let attachmentController = self.attachmentController { if let attachmentController = self.attachmentController {
attachmentController.dismiss(animated: true) attachmentController.dismiss(animated: true)
} }
if let controller = component.controller() {
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
controller.present(UndoOverlayController(
presentationData: presentationData,
content: .succeed(text: "Message Sent"),
elevatedLayout: false,
animateInAsReplacement: false,
action: { _ in return false }
), in: .current)
}
} }
private func enqueueMediaMessages(view: StoryItemSetContainerComponent.View, peer: EnginePeer, replyToMessageId: EngineMessage.Id?, replyToStoryId: StoryId?, signals: [Any]?, silentPosting: Bool, scheduleTime: Int32? = nil, getAnimatedTransitionSource: ((String) -> UIView?)? = nil, completion: @escaping () -> Void = {}) { private func enqueueMediaMessages(view: StoryItemSetContainerComponent.View, peer: EnginePeer, replyToMessageId: EngineMessage.Id?, replyToStoryId: StoryId?, signals: [Any]?, silentPosting: Bool, scheduleTime: Int32? = nil, getAnimatedTransitionSource: ((String) -> UIView?)? = nil, completion: @escaping () -> Void = {}) {
@ -2139,10 +2147,8 @@ final class StoryItemSetContainerSendMessage {
strongSelf.sendMessages(view: view, peer: peer, messages: messages.map { $0.withUpdatedReplyToMessageId(replyToMessageId).withUpdatedReplyToStoryId(replyToStoryId) }, media: true) strongSelf.sendMessages(view: view, peer: peer, messages: messages.map { $0.withUpdatedReplyToMessageId(replyToMessageId).withUpdatedReplyToStoryId(replyToStoryId) }, media: true)
if let _ = scheduleTime {
completion() completion()
} }
}
})) }))
} }

View File

@ -130,7 +130,7 @@ final class ChatBotStartInputPanelNode: ChatInputPanelNode {
tooltipController.location = .point(location, .bottom) tooltipController.location = .point(location, .bottom)
} }
} else { } else {
let controller = TooltipScreen(account: context.account, sharedContext: context.sharedContext, text: self.strings.Bot_TapToUse, icon: .downArrows, location: .point(location, .bottom), displayDuration: .infinite, shouldDismissOnTouch: { _ in let controller = TooltipScreen(account: context.account, sharedContext: context.sharedContext, text: .plain(text: self.strings.Bot_TapToUse), icon: .downArrows, location: .point(location, .bottom), displayDuration: .infinite, shouldDismissOnTouch: { _ in
return .ignore return .ignore
}) })
controller.alwaysVisible = true controller.alwaysVisible = true

View File

@ -14490,7 +14490,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
var found = false var found = false
self.forEachController({ controller in self.forEachController({ controller in
if let controller = controller as? TooltipScreen { if let controller = controller as? TooltipScreen {
if controller.text == solution.text && controller.textEntities == solution.entities { if controller.text == .entities(text: solution.text, entities: solution.entities) {
found = true found = true
controller.dismiss() controller.dismiss()
return false return false
@ -14502,7 +14502,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return return
} }
let tooltipScreen = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: solution.text, textEntities: solution.entities, icon: .info, location: .top, shouldDismissOnTouch: { point in let tooltipScreen = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .entities(text: solution.text, entities: solution.entities), icon: .animation(name: "anim_infotip", delay: 0.2), location: .top, shouldDismissOnTouch: { point in
return .ignore return .ignore
}, openActiveTextItem: { [weak self] item, action in }, openActiveTextItem: { [weak self] item, action in
guard let strongSelf = self else { guard let strongSelf = self else {
@ -14583,7 +14583,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
var found = false var found = false
self.forEachController({ controller in self.forEachController({ controller in
if let controller = controller as? TooltipScreen { if let controller = controller as? TooltipScreen {
if controller.text == psaText { if controller.text == .plain(text: psaText) {
found = true found = true
controller.dismiss() controller.dismiss()
return false return false
@ -14595,7 +14595,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return return
} }
let tooltipScreen = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: psaText, textEntities: psaEntities, icon: .info, location: .top, displayDuration: .custom(10.0), shouldDismissOnTouch: { point in let tooltipScreen = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .entities(text: psaText, entities: psaEntities), icon: .animation(name: "anim_infotip", delay: 0.2), location: .top, displayDuration: .custom(10.0), shouldDismissOnTouch: { point in
return .ignore return .ignore
}, openActiveTextItem: { [weak self] item, action in }, openActiveTextItem: { [weak self] item, action in
guard let strongSelf = self else { guard let strongSelf = self else {
@ -14683,7 +14683,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
var found = false var found = false
self.forEachController({ controller in self.forEachController({ controller in
if let controller = controller as? TooltipScreen { if let controller = controller as? TooltipScreen {
if controller.text == psaText { if controller.text == .plain(text: psaText) {
found = true found = true
controller.resetDismissTimeout() controller.resetDismissTimeout()
@ -14709,7 +14709,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return return
} }
let tooltipScreen = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: psaText, textEntities: psaEntities, icon: .info, location: .top, displayDuration: .custom(10.0), shouldDismissOnTouch: { point in let tooltipScreen = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .entities(text: psaText, entities: psaEntities), icon: .animation(name: "anim_infotip", delay: 0.2), location: .top, displayDuration: .custom(10.0), shouldDismissOnTouch: { point in
return .ignore return .ignore
}, openActiveTextItem: { [weak self] item, action in }, openActiveTextItem: { [weak self] item, action in
guard let strongSelf = self else { guard let strongSelf = self else {

View File

@ -1484,7 +1484,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
tooltipController.location = .point(location, .bottom) tooltipController.location = .point(location, .bottom)
} }
} else { } else {
let controller = TooltipScreen(account: context.account, sharedContext: context.sharedContext, text: interfaceState.strings.Bot_TapToUse, icon: .downArrows, location: .point(location, .bottom), displayDuration: .infinite, shouldDismissOnTouch: { _ in let controller = TooltipScreen(account: context.account, sharedContext: context.sharedContext, text: .plain(text: interfaceState.strings.Bot_TapToUse), icon: .downArrows, location: .point(location, .bottom), displayDuration: .infinite, shouldDismissOnTouch: { _ in
return .ignore return .ignore
}) })
controller.alwaysVisible = true controller.alwaysVisible = true

View File

@ -1262,7 +1262,7 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
if let strongSelf = self, count < 2 && currentTimestamp > timestamp + 24 * 60 * 60 { if let strongSelf = self, count < 2 && currentTimestamp > timestamp + 24 * 60 * 60 {
strongSelf.displayedPreviewTooltip = true strongSelf.displayedPreviewTooltip = true
strongSelf.present?(TooltipScreen(account: strongSelf.context.account, sharedContext: strongSelf.context.sharedContext, text: isDark ? strongSelf.presentationData.strings.Conversation_Theme_PreviewLightShort : strongSelf.presentationData.strings.Conversation_Theme_PreviewDarkShort, style: .default, icon: nil, location: .point(frame.offsetBy(dx: 3.0, dy: 6.0), .bottom), displayDuration: .custom(3.0), inset: 3.0, shouldDismissOnTouch: { _ in strongSelf.present?(TooltipScreen(account: strongSelf.context.account, sharedContext: strongSelf.context.sharedContext, text: .plain(text: isDark ? strongSelf.presentationData.strings.Conversation_Theme_PreviewLightShort : strongSelf.presentationData.strings.Conversation_Theme_PreviewDarkShort), style: .default, icon: nil, location: .point(frame.offsetBy(dx: 3.0, dy: 6.0), .bottom), displayDuration: .custom(3.0), inset: 3.0, shouldDismissOnTouch: { _ in
return .dismiss(consume: false) return .dismiss(consume: false)
})) }))

View File

@ -8691,7 +8691,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
return return
} }
let buttonFrame = buttonNode.view.convert(buttonNode.bounds, to: self.view) let buttonFrame = buttonNode.view.convert(buttonNode.bounds, to: self.view)
controller.present(TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: self.presentationData.strings.SharedMedia_CalendarTooltip, style: .default, icon: .none, location: .point(buttonFrame.insetBy(dx: 0.0, dy: 5.0), .top), shouldDismissOnTouch: { point in controller.present(TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .plain(text: self.presentationData.strings.SharedMedia_CalendarTooltip), style: .default, icon: .none, location: .point(buttonFrame.insetBy(dx: 0.0, dy: 5.0), .top), shouldDismissOnTouch: { point in
return .dismiss(consume: false) return .dismiss(consume: false)
}), in: .current) }), in: .current)
} }

View File

@ -20,6 +20,10 @@ swift_library(
"//submodules/TextFormat:TextFormat", "//submodules/TextFormat:TextFormat",
"//submodules/UrlEscaping:UrlEscaping", "//submodules/UrlEscaping:UrlEscaping",
"//submodules/AccountContext:AccountContext", "//submodules/AccountContext:AccountContext",
"//submodules/AvatarNode:AvatarNode",
"//submodules/ComponentFlow",
"//submodules/Markdown",
"//submodules/TelegramUI/Components/Stories/AvatarStoryIndicatorComponent",
], ],
visibility = [ visibility = [
"//visibility:public", "//visibility:public",

View File

@ -11,11 +11,11 @@ import TelegramCore
import TextFormat import TextFormat
import UrlEscaping import UrlEscaping
import AccountContext import AccountContext
import AvatarNode
public protocol TooltipCustomContentNode: ASDisplayNode { import ComponentFlow
func animateIn() import AvatarStoryIndicatorComponent
func updateLayout(size: CGSize) -> CGSize import AccountContext
} import Markdown
public enum TooltipActiveTextItem { public enum TooltipActiveTextItem {
case url(String, Bool) case url(String, Bool)
@ -109,7 +109,7 @@ private class DownArrowsIconNode: ASDisplayNode {
private final class TooltipScreenNode: ViewControllerTracingNode { private final class TooltipScreenNode: ViewControllerTracingNode {
private let tooltipStyle: TooltipScreen.Style private let tooltipStyle: TooltipScreen.Style
private let icon: TooltipScreen.Icon? private let icon: TooltipScreen.Icon?
private let customContentNode: TooltipCustomContentNode? private let action: TooltipScreen.Action?
var location: TooltipScreen.Location { var location: TooltipScreen.Location {
didSet { didSet {
if let layout = self.validLayout { if let layout = self.validLayout {
@ -134,8 +134,11 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
private let arrowContainer: ASDisplayNode private let arrowContainer: ASDisplayNode
private let animatedStickerNode: AnimatedStickerNode private let animatedStickerNode: AnimatedStickerNode
private var downArrowsNode: DownArrowsIconNode? private var downArrowsNode: DownArrowsIconNode?
private var avatarNode: AvatarNode?
private var avatarStoryIndicator: ComponentView<Empty>?
private let textNode: ImmediateTextNode private let textNode: ImmediateTextNode
private let closeButtonNode: HighlightableButtonNode private var closeButtonNode: HighlightableButtonNode?
private var actionButtonNode: HighlightableButtonNode?
private var isArrowInverted: Bool = false private var isArrowInverted: Bool = false
@ -143,10 +146,24 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
private var validLayout: ContainerViewLayout? private var validLayout: ContainerViewLayout?
init(account: Account, sharedContext: SharedAccountContext, text: String, textEntities: [MessageTextEntity], style: TooltipScreen.Style, icon: TooltipScreen.Icon? = nil, customContentNode: TooltipCustomContentNode? = nil, location: TooltipScreen.Location, displayDuration: TooltipScreen.DisplayDuration, inset: CGFloat = 13.0, cornerRadius: CGFloat? = nil, shouldDismissOnTouch: @escaping (CGPoint) -> TooltipScreen.DismissOnTouch, requestDismiss: @escaping () -> Void, openActiveTextItem: ((TooltipActiveTextItem, TooltipActiveTextAction) -> Void)?) { init(
context: AccountContext?,
account: Account,
sharedContext: SharedAccountContext,
text: TooltipScreen.Text,
textAlignment: TooltipScreen.Alignment,
style: TooltipScreen.Style,
icon: TooltipScreen.Icon? = nil,
action: TooltipScreen.Action? = nil,
location: TooltipScreen.Location,
displayDuration: TooltipScreen.DisplayDuration,
inset: CGFloat = 13.0,
cornerRadius: CGFloat? = nil,
shouldDismissOnTouch: @escaping (CGPoint) -> TooltipScreen.DismissOnTouch, requestDismiss: @escaping () -> Void, openActiveTextItem: ((TooltipActiveTextItem, TooltipActiveTextAction) -> Void)?)
{
self.tooltipStyle = style self.tooltipStyle = style
self.icon = icon self.icon = icon
self.customContentNode = customContentNode self.action = action
self.location = location self.location = location
self.displayDuration = displayDuration self.displayDuration = displayDuration
self.inset = inset self.inset = inset
@ -164,6 +181,8 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
self.scrollingContainer = ASDisplayNode() self.scrollingContainer = ASDisplayNode()
let theme = sharedContext.currentPresentationData.with { $0 }.theme
func svgPath(_ path: StaticString, scale: CGPoint = CGPoint(x: 1.0, y: 1.0), offset: CGPoint = CGPoint()) throws -> UIBezierPath { func svgPath(_ path: StaticString, scale: CGPoint = CGPoint(x: 1.0, y: 1.0), offset: CGPoint = CGPoint()) throws -> UIBezierPath {
var index: UnsafePointer<UInt8> = path.utf8Start var index: UnsafePointer<UInt8> = path.utf8Start
let end = path.utf8Start.advanced(by: path.utf8CodeUnitCount) let end = path.utf8Start.advanced(by: path.utf8CodeUnitCount)
@ -210,7 +229,6 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
self.arrowContainer = ASDisplayNode() self.arrowContainer = ASDisplayNode()
let theme = sharedContext.currentPresentationData.with { $0 }.theme
let fontSize: CGFloat let fontSize: CGFloat
if case .top = location { if case .top = location {
let backgroundColor: UIColor let backgroundColor: UIColor
@ -311,24 +329,55 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
self.textNode.displaysAsynchronously = false self.textNode.displaysAsynchronously = false
self.textNode.maximumNumberOfLines = 0 self.textNode.maximumNumberOfLines = 0
self.textNode.attributedText = stringWithAppliedEntities(text, entities: textEntities, baseColor: .white, linkColor: .white, baseFont: Font.regular(fontSize), linkFont: Font.regular(fontSize), boldFont: Font.semibold(14.0), italicFont: Font.italic(fontSize), boldItalicFont: Font.semiboldItalic(fontSize), fixedFont: Font.monospace(fontSize), blockQuoteFont: Font.regular(fontSize), underlineLinks: true, external: false, message: nil) let baseFont = Font.regular(fontSize)
let boldFont = Font.semibold(14.0)
let italicFont = Font.italic(fontSize)
let boldItalicFont = Font.semiboldItalic(fontSize)
let fixedFont = Font.monospace(fontSize)
let textColor: UIColor = .white
let attributedText: NSAttributedString
switch text {
case let .plain(text):
attributedText = NSAttributedString(string: text, font: baseFont, textColor: textColor)
case let .entities(text, entities):
attributedText = stringWithAppliedEntities(text, entities: entities, baseColor: textColor, linkColor: textColor, baseFont: baseFont, linkFont: baseFont, boldFont: boldFont, italicFont: italicFont, boldItalicFont: boldItalicFont, fixedFont: fixedFont, blockQuoteFont: baseFont, underlineLinks: true, external: false, message: nil)
case let .markdown(text):
let markdownAttributes = MarkdownAttributes(
body: MarkdownAttributeSet(font: baseFont, textColor: textColor),
bold: MarkdownAttributeSet(font: boldFont, textColor: textColor),
link: MarkdownAttributeSet(font: baseFont, textColor: textColor),
linkAttribute: { _ in
return nil
}
)
attributedText = parseMarkdownIntoAttributedString(text, attributes: markdownAttributes)
}
self.textNode.attributedText = attributedText
self.textNode.textAlignment = textAlignment == .center ? .center : .natural
self.animatedStickerNode = DefaultAnimatedStickerNodeImpl() self.animatedStickerNode = DefaultAnimatedStickerNodeImpl()
switch icon { switch icon {
case .none: case .none:
break break
case .chatListPress: case let .animation(animationName, _):
self.animatedStickerNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "ChatListFoldersTooltip"), width: Int(70 * UIScreenScale), height: Int(70 * UIScreenScale), playbackMode: .once, mode: .direct(cachePathPrefix: nil)) self.animatedStickerNode.setup(source: AnimatedStickerNodeLocalFileSource(name: animationName), width: Int(70 * UIScreenScale), height: Int(70 * UIScreenScale), playbackMode: .once, mode: .direct(cachePathPrefix: nil))
self.animatedStickerNode.automaticallyLoadFirstFrame = true
case .info:
self.animatedStickerNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "anim_infotip"), width: Int(70 * UIScreenScale), height: Int(70 * UIScreenScale), playbackMode: .once, mode: .direct(cachePathPrefix: nil))
self.animatedStickerNode.automaticallyLoadFirstFrame = true self.animatedStickerNode.automaticallyLoadFirstFrame = true
case .downArrows: case .downArrows:
self.downArrowsNode = DownArrowsIconNode() self.downArrowsNode = DownArrowsIconNode()
case let .peer(peer, _):
self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 15.0))
if let context {
self.avatarNode?.setPeer(context: context, theme: defaultDarkPresentationTheme, peer: peer)
}
} }
if case .manual = displayDuration {
self.closeButtonNode = HighlightableButtonNode() self.closeButtonNode = HighlightableButtonNode()
self.closeButtonNode.setImage(UIImage(bundleImageName: "Components/Close"), for: .normal) self.closeButtonNode?.setImage(UIImage(bundleImageName: "Components/Close"), for: .normal)
}
super.init() super.init()
@ -343,16 +392,28 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
self.containerNode.addSubnode(self.textNode) self.containerNode.addSubnode(self.textNode)
self.containerNode.addSubnode(self.animatedStickerNode) self.containerNode.addSubnode(self.animatedStickerNode)
if case .manual = displayDuration { if let closeButtonNode = self.closeButtonNode {
self.containerNode.addSubnode(self.closeButtonNode) self.containerNode.addSubnode(closeButtonNode)
} }
if let downArrowsNode = self.downArrowsNode { if let downArrowsNode = self.downArrowsNode {
self.containerNode.addSubnode(downArrowsNode) self.containerNode.addSubnode(downArrowsNode)
} }
if let avatarNode = self.avatarNode {
self.containerNode.addSubnode(avatarNode)
}
self.scrollingContainer.addSubnode(self.containerNode) self.scrollingContainer.addSubnode(self.containerNode)
self.addSubnode(self.scrollingContainer) self.addSubnode(self.scrollingContainer)
if let action {
let actionColor = theme.list.itemAccentColor.withMultiplied(hue: 1.0, saturation: 0.64, brightness: 1.08)
let actionButtonNode = HighlightableButtonNode()
actionButtonNode.hitTestSlop = UIEdgeInsets(top: -16.0, left: -16.0, bottom: -16.0, right: -16.0)
actionButtonNode.setAttributedTitle(NSAttributedString(string: action.title, font: Font.regular(17.0), textColor: actionColor), for: .normal)
self.containerNode.addSubnode(actionButtonNode)
self.actionButtonNode = actionButtonNode
}
self.textNode.linkHighlightColor = UIColor.white.withAlphaComponent(0.5) self.textNode.linkHighlightColor = UIColor.white.withAlphaComponent(0.5)
self.textNode.highlightAttributeAction = { attributes in self.textNode.highlightAttributeAction = { attributes in
let highlightedAttributes = [ let highlightedAttributes = [
@ -412,7 +473,15 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
} }
} }
self.closeButtonNode.addTarget(self, action: #selector(self.closePressed), forControlEvents: .touchUpInside) self.actionButtonNode?.addTarget(self, action: #selector(self.actionPressed), forControlEvents: .touchUpInside)
self.closeButtonNode?.addTarget(self, action: #selector(self.closePressed), forControlEvents: .touchUpInside)
}
@objc private func actionPressed() {
if let action = self.action {
action.action()
self.requestDismiss()
}
} }
@objc private func closePressed() { @objc private func closePressed() {
@ -441,11 +510,15 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
animationSize = CGSize(width: 24.0, height: 32.0) animationSize = CGSize(width: 24.0, height: 32.0)
animationInset = (40.0 - animationSize.width) / 2.0 animationInset = (40.0 - animationSize.width) / 2.0
animationSpacing = 8.0 animationSpacing = 8.0
case .chatListPress: case let .animation(animationName, _):
animationSize = CGSize(width: 32.0, height: 32.0) animationSize = CGSize(width: 32.0, height: 32.0)
if animationName == "ChatListFoldersTooltip" {
animationInset = (70.0 - animationSize.width) / 2.0 animationInset = (70.0 - animationSize.width) / 2.0
} else {
animationInset = 0.0
}
animationSpacing = 8.0 animationSpacing = 8.0
case .info: case .peer:
animationSize = CGSize(width: 32.0, height: 32.0) animationSize = CGSize(width: 32.0, height: 32.0)
animationInset = 0.0 animationInset = 0.0
animationSpacing = 8.0 animationSpacing = 8.0
@ -453,14 +526,28 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
let containerWidth = max(100.0, min(layout.size.width, 614.0) - (sideInset + layout.safeInsets.left) * 2.0) let containerWidth = max(100.0, min(layout.size.width, 614.0) - (sideInset + layout.safeInsets.left) * 2.0)
let textSize = self.textNode.updateLayout(CGSize(width: containerWidth - contentInset * 2.0 - animationSize.width - animationSpacing, height: .greatestFiniteMagnitude)) var actionSize: CGSize = .zero
var buttonInset: CGFloat = 0.0
if let actionButtonNode = self.actionButtonNode {
actionSize = actionButtonNode.measure(CGSize(width: containerWidth, height: .greatestFiniteMagnitude))
buttonInset += actionSize.width + 32.0
}
if self.closeButtonNode != nil {
buttonInset += 24.0
}
let textSize = self.textNode.updateLayout(CGSize(width: containerWidth - contentInset * 2.0 - animationSize.width - animationSpacing - buttonInset, height: .greatestFiniteMagnitude))
var backgroundFrame: CGRect var backgroundFrame: CGRect
let backgroundHeight: CGFloat var backgroundHeight: CGFloat
switch self.tooltipStyle { switch self.tooltipStyle {
case .default, .gradient, .customBlur: case .default, .gradient, .customBlur:
backgroundHeight = max(animationSize.height, textSize.height) + contentVerticalInset * 2.0 backgroundHeight = max(animationSize.height, textSize.height) + contentVerticalInset * 2.0
if self.actionButtonNode != nil {
backgroundHeight += 2.0
}
case .light: case .light:
backgroundHeight = max(28.0, max(animationSize.height, textSize.height) + 4.0 * 2.0) backgroundHeight = max(28.0, max(animationSize.height, textSize.height) + 4.0 * 2.0)
} }
@ -469,8 +556,11 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
switch self.location { switch self.location {
case let .point(rect, arrowPosition): case let .point(rect, arrowPosition):
var backgroundWidth = textSize.width + contentInset * 2.0 + animationSize.width + animationSpacing var backgroundWidth = textSize.width + contentInset * 2.0 + animationSize.width + animationSpacing
if self.closeButtonNode.supernode != nil { if self.closeButtonNode != nil || self.actionButtonNode != nil {
backgroundWidth += 24.0 backgroundWidth += buttonInset
}
if self.actionButtonNode != nil, case .compact = layout.metrics.widthClass {
backgroundWidth = containerWidth
} }
switch arrowPosition { switch arrowPosition {
case .bottom, .top: case .bottom, .top:
@ -554,8 +644,14 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
let textFrame = CGRect(origin: CGPoint(x: contentInset + animationSize.width + animationSpacing, y: floor((backgroundHeight - textSize.height) / 2.0)), size: textSize) let textFrame = CGRect(origin: CGPoint(x: contentInset + animationSize.width + animationSpacing, y: floor((backgroundHeight - textSize.height) / 2.0)), size: textSize)
transition.updateFrame(node: self.textNode, frame: textFrame) transition.updateFrame(node: self.textNode, frame: textFrame)
if let closeButtonNode = self.closeButtonNode {
let closeSize = CGSize(width: 44.0, height: 44.0) let closeSize = CGSize(width: 44.0, height: 44.0)
transition.updateFrame(node: self.closeButtonNode, frame: CGRect(origin: CGPoint(x: textFrame.maxX - 6.0, y: floor((backgroundHeight - closeSize.height) / 2.0)), size: closeSize)) transition.updateFrame(node: closeButtonNode, frame: CGRect(origin: CGPoint(x: textFrame.maxX - 6.0, y: floor((backgroundHeight - closeSize.height) / 2.0)), size: closeSize))
}
if let actionButtonNode = self.actionButtonNode {
transition.updateFrame(node: actionButtonNode, frame: CGRect(origin: CGPoint(x: backgroundFrame.width - actionSize.width - 16.0, y: floor((backgroundHeight - actionSize.height) / 2.0)), size: actionSize))
}
let animationFrame = CGRect(origin: CGPoint(x: contentInset - animationInset, y: contentVerticalInset - animationInset), size: CGSize(width: animationSize.width + animationInset * 2.0, height: animationSize.height + animationInset * 2.0)) let animationFrame = CGRect(origin: CGPoint(x: contentInset - animationInset, y: contentVerticalInset - animationInset), size: CGSize(width: animationSize.width + animationInset * 2.0, height: animationSize.height + animationInset * 2.0))
transition.updateFrame(node: self.animatedStickerNode, frame: animationFrame) transition.updateFrame(node: self.animatedStickerNode, frame: animationFrame)
@ -566,6 +662,59 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
transition.updateFrame(node: downArrowsNode, frame: CGRect(origin: CGPoint(x: animationFrame.midX - arrowsSize.width / 2.0, y: animationFrame.midY - arrowsSize.height / 2.0), size: arrowsSize)) transition.updateFrame(node: downArrowsNode, frame: CGRect(origin: CGPoint(x: animationFrame.midX - arrowsSize.width / 2.0, y: animationFrame.midY - arrowsSize.height / 2.0), size: arrowsSize))
downArrowsNode.setupAnimations() downArrowsNode.setupAnimations()
} }
if let avatarNode = self.avatarNode {
var avatarFrame = animationFrame
if let icon, case let .peer(_, isStory) = icon, isStory {
let indicatorTransition: Transition = .immediate
let avatarStoryIndicator: ComponentView<Empty>
if let current = self.avatarStoryIndicator {
avatarStoryIndicator = current
} else {
avatarStoryIndicator = ComponentView()
self.avatarStoryIndicator = avatarStoryIndicator
}
let storyIndicatorScale: CGFloat = 1.0
var indicatorFrame = CGRect(origin: CGPoint(x: avatarFrame.minX + 4.0, y: avatarFrame.minY + 4.0), size: CGSize(width: avatarFrame.width - 4.0 - 4.0, height: avatarFrame.height - 4.0 - 4.0))
indicatorFrame.origin.x -= (avatarFrame.width - avatarFrame.width * storyIndicatorScale) * 0.5
let _ = avatarStoryIndicator.update(
transition: indicatorTransition,
component: AnyComponent(AvatarStoryIndicatorComponent(
hasUnseen: true,
hasUnseenCloseFriendsItems: false,
theme: defaultDarkPresentationTheme,
activeLineWidth: 1.0 + UIScreenPixel,
inactiveLineWidth: 1.0 + UIScreenPixel,
counters: nil
)),
environment: {},
containerSize: indicatorFrame.size
)
if let avatarStoryIndicatorView = avatarStoryIndicator.view {
if avatarStoryIndicatorView.superview == nil {
avatarStoryIndicatorView.isUserInteractionEnabled = false
self.containerNode.view.addSubview(avatarStoryIndicatorView)
}
indicatorTransition.setPosition(view: avatarStoryIndicatorView, position: indicatorFrame.center)
indicatorTransition.setBounds(view: avatarStoryIndicatorView, bounds: CGRect(origin: CGPoint(), size: indicatorFrame.size))
indicatorTransition.setScale(view: avatarStoryIndicatorView, scale: storyIndicatorScale)
}
avatarFrame = avatarFrame.insetBy(dx: 4.0, dy: 4.0)
} else {
if let avatarStoryIndicator = self.avatarStoryIndicator {
self.avatarStoryIndicator = nil
avatarStoryIndicator.view?.removeFromSuperview()
}
}
transition.updateFrame(node: avatarNode, frame: avatarFrame)
avatarNode.updateSize(size: avatarFrame.size)
}
} }
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
@ -587,6 +736,9 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
return nil return nil
} }
} }
if let actionButtonNode = self.actionButtonNode, let result = actionButtonNode.hitTest(self.convert(point, to: actionButtonNode), with: event) {
return result
}
switch self.shouldDismissOnTouch(point) { switch self.shouldDismissOnTouch(point) {
case .ignore: case .ignore:
break break
@ -628,12 +780,12 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
let animationDelay: Double let animationDelay: Double
switch self.icon { switch self.icon {
case .chatListPress: case let .animation(_, delay):
animationDelay = 0.6 animationDelay = delay
case .info:
animationDelay = 0.2
case .none, .downArrows: case .none, .downArrows:
animationDelay = 0.0 animationDelay = 0.0
case .peer:
animationDelay = 0.0
} }
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + animationDelay, execute: { [weak self] in DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + animationDelay, execute: { [weak self] in
@ -684,9 +836,28 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
} }
public final class TooltipScreen: ViewController { public final class TooltipScreen: ViewController {
public enum Text: Equatable {
case plain(text: String)
case entities(text: String, entities: [MessageTextEntity])
case markdown(text: String)
}
public class Action {
public let title: String
public let action: () -> Void
public init(
title: String,
action: @escaping () -> Void
) {
self.title = title
self.action = action
}
}
public enum Icon { public enum Icon {
case info case animation(name: String, delay: Double)
case chatListPress case peer(peer: EnginePeer, isStory: Bool)
case downArrows case downArrows
} }
@ -720,13 +891,19 @@ public final class TooltipScreen: ViewController {
case gradient(UIColor, UIColor) case gradient(UIColor, UIColor)
} }
public enum Alignment {
case natural
case center
}
private let context: AccountContext?
private let account: Account private let account: Account
private let sharedContext: SharedAccountContext private let sharedContext: SharedAccountContext
public let text: String public let text: TooltipScreen.Text
public let textEntities: [MessageTextEntity] public let textAlignment: TooltipScreen.Alignment
private let style: TooltipScreen.Style private let style: TooltipScreen.Style
private let icon: TooltipScreen.Icon? private let icon: TooltipScreen.Icon?
private let customContentNode: TooltipCustomContentNode? private let action: TooltipScreen.Action?
public var location: TooltipScreen.Location { public var location: TooltipScreen.Location {
didSet { didSet {
if self.isNodeLoaded { if self.isNodeLoaded {
@ -755,13 +932,14 @@ public final class TooltipScreen: ViewController {
public var alwaysVisible = false public var alwaysVisible = false
public init( public init(
context: AccountContext? = nil,
account: Account, account: Account,
sharedContext: SharedAccountContext, sharedContext: SharedAccountContext,
text: String, text: TooltipScreen.Text,
textEntities: [MessageTextEntity] = [], textAlignment: TooltipScreen.Alignment = .natural,
style: TooltipScreen.Style = .default, style: TooltipScreen.Style = .default,
icon: TooltipScreen.Icon? = nil, icon: TooltipScreen.Icon? = nil,
customContentNode: TooltipCustomContentNode? = nil, action: TooltipScreen.Action? = nil,
location: TooltipScreen.Location, location: TooltipScreen.Location,
displayDuration: DisplayDuration = .default, displayDuration: DisplayDuration = .default,
inset: CGFloat = 13.0, inset: CGFloat = 13.0,
@ -769,13 +947,14 @@ public final class TooltipScreen: ViewController {
shouldDismissOnTouch: @escaping (CGPoint) -> TooltipScreen.DismissOnTouch, shouldDismissOnTouch: @escaping (CGPoint) -> TooltipScreen.DismissOnTouch,
openActiveTextItem: ((TooltipActiveTextItem, TooltipActiveTextAction) -> Void)? = nil openActiveTextItem: ((TooltipActiveTextItem, TooltipActiveTextAction) -> Void)? = nil
) { ) {
self.context = context
self.account = account self.account = account
self.sharedContext = sharedContext self.sharedContext = sharedContext
self.text = text self.text = text
self.textEntities = textEntities self.textAlignment = textAlignment
self.style = style self.style = style
self.icon = icon self.icon = icon
self.customContentNode = customContentNode self.action = action
self.location = location self.location = location
self.displayDuration = displayDuration self.displayDuration = displayDuration
self.inset = inset self.inset = inset
@ -839,7 +1018,7 @@ public final class TooltipScreen: ViewController {
} }
override public func loadDisplayNode() { override public func loadDisplayNode() {
self.displayNode = TooltipScreenNode(account: self.account, sharedContext: self.sharedContext, text: self.text, textEntities: self.textEntities, style: self.style, icon: self.icon, customContentNode: self.customContentNode, location: self.location, displayDuration: self.displayDuration, inset: self.inset, cornerRadius: self.cornerRadius, shouldDismissOnTouch: self.shouldDismissOnTouch, requestDismiss: { [weak self] in self.displayNode = TooltipScreenNode(context: self.context, account: self.account, sharedContext: self.sharedContext, text: self.text, textAlignment: self.textAlignment, style: self.style, icon: self.icon, action: self.action, location: self.location, displayDuration: self.displayDuration, inset: self.inset, cornerRadius: self.cornerRadius, shouldDismissOnTouch: self.shouldDismissOnTouch, requestDismiss: { [weak self] in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }

View File

@ -15,7 +15,7 @@ public enum UndoOverlayContent {
case info(title: String?, text: String, timeout: Double?) case info(title: String?, text: String, timeout: Double?)
case emoji(name: String, text: String) case emoji(name: String, text: String)
case swipeToReply(title: String, text: String) case swipeToReply(title: String, text: String)
case actionSucceeded(title: String, text: String, cancel: String) case actionSucceeded(title: String, text: String, cancel: String, destructive: Bool)
case stickersModified(title: String, text: String, undo: Bool, info: StickerPackCollectionInfo, topItem: StickerPackItem?, context: AccountContext) case stickersModified(title: String, text: String, undo: Bool, info: StickerPackCollectionInfo, topItem: StickerPackItem?, context: AccountContext)
case dice(dice: TelegramMediaDice, context: AccountContext, text: String, action: String?) case dice(dice: TelegramMediaDice, context: AccountContext, text: String, action: String?)
case chatAddedToFolder(chatTitle: String, folderTitle: String) case chatAddedToFolder(chatTitle: String, folderTitle: String)

View File

@ -221,14 +221,16 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
isUserInteractionEnabled = true isUserInteractionEnabled = true
} }
case let .actionSucceeded(title, text, cancel): case let .actionSucceeded(title, text, cancel, destructive):
self.avatarNode = nil self.avatarNode = nil
self.iconNode = nil self.iconNode = nil
self.iconCheckNode = nil self.iconCheckNode = nil
self.animationNode = AnimationNode(animation: "anim_success", colors: ["info1.info1.stroke": self.animationBackgroundColor, "info2.info2.Fill": self.animationBackgroundColor], scale: 1.0) self.animationNode = AnimationNode(animation: "anim_success", colors: ["info1.info1.stroke": self.animationBackgroundColor, "info2.info2.Fill": self.animationBackgroundColor], scale: 1.0)
self.animatedStickerNode = nil self.animatedStickerNode = nil
if destructive {
undoTextColor = UIColor(rgb: 0xff7b74) undoTextColor = UIColor(rgb: 0xff7b74)
}
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white) let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white) let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)