diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 9078272510..3b89811ad0 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -14338,3 +14338,6 @@ Sorry for the inconvenience."; "Gift.Buy.ErrorTooEarly.Title" = "Try Later"; "Gift.Buy.ErrorTooEarly.Text" = "You will be able to buy this gift on %@."; + +"Chat.PauseVoiceMessageTooltip" = "Pause to trim or replay."; +"Chat.PauseVideoMessageTooltip" = "Pause to trim or replay."; diff --git a/submodules/AttachmentUI/Sources/AttachmentPanel.swift b/submodules/AttachmentUI/Sources/AttachmentPanel.swift index 21c65ebcdd..6f53f3e534 100644 --- a/submodules/AttachmentUI/Sources/AttachmentPanel.swift +++ b/submodules/AttachmentUI/Sources/AttachmentPanel.swift @@ -1265,6 +1265,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate { }, openMessagePayment: { }, openBoostToUnrestrict: { }, updateRecordingTrimRange: { _, _, _, _ in + }, dismissAllTooltips: { }, updateHistoryFilter: { _ in }, updateChatLocationThread: { _, _ in }, toggleChatSidebarMode: { diff --git a/submodules/Camera/Sources/CameraOutput.swift b/submodules/Camera/Sources/CameraOutput.swift index df644f0da2..bc4b50fa0f 100644 --- a/submodules/Camera/Sources/CameraOutput.swift +++ b/submodules/Camera/Sources/CameraOutput.swift @@ -109,7 +109,7 @@ final class CameraOutput: NSObject { private var videoConnection: AVCaptureConnection? private var previewConnection: AVCaptureConnection? - private var roundVideoFilter: CameraRoundVideoFilter? + private var roundVideoFilter: CameraRoundLegacyVideoFilter? private let semaphore = DispatchSemaphore(value: 1) private let videoQueue = DispatchQueue(label: "", qos: .userInitiated) @@ -577,11 +577,11 @@ final class CameraOutput: NSObject { return nil } - let filter: CameraRoundVideoFilter + let filter: CameraRoundLegacyVideoFilter if let current = self.roundVideoFilter { filter = current } else { - filter = CameraRoundVideoFilter(ciContext: self.ciContext, colorSpace: self.colorSpace, simple: self.exclusive) + filter = CameraRoundLegacyVideoFilter(ciContext: self.ciContext, colorSpace: self.colorSpace, simple: self.exclusive) self.roundVideoFilter = filter } if !filter.isPrepared { diff --git a/submodules/ChatPresentationInterfaceState/Sources/ChatPanelInterfaceInteraction.swift b/submodules/ChatPresentationInterfaceState/Sources/ChatPanelInterfaceInteraction.swift index 48ebb5d6b6..f0566406fa 100644 --- a/submodules/ChatPresentationInterfaceState/Sources/ChatPanelInterfaceInteraction.swift +++ b/submodules/ChatPresentationInterfaceState/Sources/ChatPanelInterfaceInteraction.swift @@ -176,6 +176,7 @@ public final class ChatPanelInterfaceInteraction { public let updateDisplayHistoryFilterAsList: (Bool) -> Void public let openBoostToUnrestrict: () -> Void public let updateRecordingTrimRange: (Double, Double, Bool, Bool) -> Void + public let dismissAllTooltips: () -> Void public let requestLayout: (ContainedViewLayoutTransition) -> Void public let chatController: () -> ViewController? public let statuses: ChatPanelInterfaceInteractionStatuses? @@ -291,6 +292,7 @@ public final class ChatPanelInterfaceInteraction { openMessagePayment: @escaping () -> Void, openBoostToUnrestrict: @escaping () -> Void, updateRecordingTrimRange: @escaping (Double, Double, Bool, Bool) -> Void, + dismissAllTooltips: @escaping () -> Void, updateHistoryFilter: @escaping ((ChatPresentationInterfaceState.HistoryFilter?) -> ChatPresentationInterfaceState.HistoryFilter?) -> Void, updateChatLocationThread: @escaping (Int64?, ChatControllerAnimateInnerChatSwitchDirection?) -> Void, toggleChatSidebarMode: @escaping () -> Void, @@ -409,6 +411,7 @@ public final class ChatPanelInterfaceInteraction { self.openMessagePayment = openMessagePayment self.openBoostToUnrestrict = openBoostToUnrestrict self.updateRecordingTrimRange = updateRecordingTrimRange + self.dismissAllTooltips = dismissAllTooltips self.updateHistoryFilter = updateHistoryFilter self.updateChatLocationThread = updateChatLocationThread self.toggleChatSidebarMode = toggleChatSidebarMode @@ -536,6 +539,7 @@ public final class ChatPanelInterfaceInteraction { }, openMessagePayment: { }, openBoostToUnrestrict: { }, updateRecordingTrimRange: { _, _, _, _ in + }, dismissAllTooltips: { }, updateHistoryFilter: { _ in }, updateChatLocationThread: { _, _ in }, toggleChatSidebarMode: { diff --git a/submodules/TelegramNotices/Sources/Notices.swift b/submodules/TelegramNotices/Sources/Notices.swift index 29a3e4862b..5c7b6162d6 100644 --- a/submodules/TelegramNotices/Sources/Notices.swift +++ b/submodules/TelegramNotices/Sources/Notices.swift @@ -204,6 +204,8 @@ private enum ApplicationSpecificGlobalNotice: Int32 { case starGiftWearTips = 77 case channelSuggestTooltip = 78 case multipleStoriesTooltip = 79 + case voiceMessagesPauseSuggestion = 80 + case videoMessagesPauseSuggestion = 81 var key: ValueBoxKey { let v = ValueBoxKey(length: 4) @@ -569,6 +571,14 @@ private struct ApplicationSpecificNoticeKeys { static func multipleStoriesTooltip() -> NoticeEntryKey { return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.multipleStoriesTooltip.key) } + + static func voiceMessagesPauseSuggestion() -> NoticeEntryKey { + return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.voiceMessagesPauseSuggestion.key) + } + + static func videoMessagesPauseSuggestion() -> NoticeEntryKey { + return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.videoMessagesPauseSuggestion.key) + } } public struct ApplicationSpecificNotice { @@ -2458,4 +2468,58 @@ public struct ApplicationSpecificNotice { return Int(previousValue) } } + + public static func getVoiceMessagesPauseSuggestion(accountManager: AccountManager) -> Signal { + return accountManager.transaction { transaction -> Int32 in + if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.voiceMessagesPauseSuggestion())?.get(ApplicationSpecificCounterNotice.self) { + return value.value + } else { + return 0 + } + } + } + + public static func incrementVoiceMessagesPauseSuggestion(accountManager: AccountManager, count: Int = 1) -> Signal { + return accountManager.transaction { transaction -> Int in + var currentValue: Int32 = 0 + if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.voiceMessagesPauseSuggestion())?.get(ApplicationSpecificCounterNotice.self) { + currentValue = value.value + } + let previousValue = currentValue + currentValue += Int32(count) + + if let entry = CodableEntry(ApplicationSpecificCounterNotice(value: currentValue)) { + transaction.setNotice(ApplicationSpecificNoticeKeys.voiceMessagesPauseSuggestion(), entry) + } + + return Int(previousValue) + } + } + + public static func getVideoMessagesPauseSuggestion(accountManager: AccountManager) -> Signal { + return accountManager.transaction { transaction -> Int32 in + if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.videoMessagesPauseSuggestion())?.get(ApplicationSpecificCounterNotice.self) { + return value.value + } else { + return 0 + } + } + } + + public static func incrementVideoMessagesPauseSuggestion(accountManager: AccountManager, count: Int = 1) -> Signal { + return accountManager.transaction { transaction -> Int in + var currentValue: Int32 = 0 + if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.videoMessagesPauseSuggestion())?.get(ApplicationSpecificCounterNotice.self) { + currentValue = value.value + } + let previousValue = currentValue + currentValue += Int32(count) + + if let entry = CodableEntry(ApplicationSpecificCounterNotice(value: currentValue)) { + transaction.setNotice(ApplicationSpecificNoticeKeys.videoMessagesPauseSuggestion(), entry) + } + + return Int(previousValue) + } + } } diff --git a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsController.swift b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsController.swift index be16736728..f84db49c41 100644 --- a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsController.swift +++ b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsController.swift @@ -169,6 +169,7 @@ public final class ChatRecentActionsController: TelegramBaseController { }, openMessagePayment: { }, openBoostToUnrestrict: { }, updateRecordingTrimRange: { _, _, _, _ in + }, dismissAllTooltips: { }, updateHistoryFilter: { _ in }, updateChatLocationThread: { _, _ in }, toggleChatSidebarMode: { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index 39a5a90b55..654f3dd354 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -434,6 +434,7 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode { }, openMessagePayment: { }, openBoostToUnrestrict: { }, updateRecordingTrimRange: { _, _, _, _ in + }, dismissAllTooltips: { }, updateHistoryFilter: { _ in }, updateChatLocationThread: { _, _ in }, toggleChatSidebarMode: { diff --git a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift index 73b007a191..4c035c1011 100644 --- a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift +++ b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift @@ -783,6 +783,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { }, openMessagePayment: { }, openBoostToUnrestrict: { }, updateRecordingTrimRange: { _, _, _, _ in + }, dismissAllTooltips: { }, updateHistoryFilter: { _ in }, updateChatLocationThread: { _, _ in }, toggleChatSidebarMode: { diff --git a/submodules/TelegramUI/Components/VideoMessageCameraScreen/Sources/VideoMessageCameraScreen.swift b/submodules/TelegramUI/Components/VideoMessageCameraScreen/Sources/VideoMessageCameraScreen.swift index 7101eab38d..053d08a7de 100644 --- a/submodules/TelegramUI/Components/VideoMessageCameraScreen/Sources/VideoMessageCameraScreen.swift +++ b/submodules/TelegramUI/Components/VideoMessageCameraScreen/Sources/VideoMessageCameraScreen.swift @@ -1199,6 +1199,8 @@ public class VideoMessageCameraScreen: ViewController { self.currentLiveUploadData = nil } + let _ = ApplicationSpecificNotice.incrementVideoMessagesPauseSuggestion(accountManager: self.context.sharedContext.accountManager, count: 3).startStandalone() + self.pauseCameraCapture() self.results.append(result) @@ -1251,22 +1253,35 @@ public class VideoMessageCameraScreen: ViewController { return result } - fileprivate func maybePresentViewOnceTooltip() { + fileprivate func maybePresentTooltips() { let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } - let _ = (ApplicationSpecificNotice.getVideoMessagesPlayOnceSuggestion(accountManager: context.sharedContext.accountManager) - |> deliverOnMainQueue).startStandalone(next: { [weak self] counter in + + let _ = (ApplicationSpecificNotice.getVideoMessagesPauseSuggestion(accountManager: self.context.sharedContext.accountManager) + |> deliverOnMainQueue).startStandalone(next: { [weak self] pauseCounter in guard let self else { return } - if counter >= 3 { - return + + if pauseCounter >= 3 { + let _ = (ApplicationSpecificNotice.getVideoMessagesPlayOnceSuggestion(accountManager: self.context.sharedContext.accountManager) + |> deliverOnMainQueue).startStandalone(next: { [weak self] counter in + guard let self else { + return + } + if counter >= 3 { + return + } + Queue.mainQueue().after(0.3) { + self.displayViewOnceTooltip(text: presentationData.strings.Chat_TapToPlayVideoMessageOnceTooltip, hasIcon: true) + } + let _ = ApplicationSpecificNotice.incrementVideoMessagesPlayOnceSuggestion(accountManager: self.context.sharedContext.accountManager).startStandalone() + }) + } else { + Queue.mainQueue().after(0.3) { + self.displayPauseTooltip(text: presentationData.strings.Chat_PauseVideoMessageTooltip) + } + let _ = ApplicationSpecificNotice.incrementVideoMessagesPauseSuggestion(accountManager: self.context.sharedContext.accountManager).startStandalone() } - - Queue.mainQueue().after(0.3) { - self.displayViewOnceTooltip(text: presentationData.strings.Chat_TapToPlayVideoMessageOnceTooltip, hasIcon: true) - } - - let _ = ApplicationSpecificNotice.incrementVideoMessagesPlayOnceSuggestion(accountManager: self.context.sharedContext.accountManager).startStandalone() }) } @@ -1299,6 +1314,36 @@ public class VideoMessageCameraScreen: ViewController { ) controller.present(tooltipController, in: .window(.root)) } + + private func displayPauseTooltip(text: String) { + guard let controller = self.controller, let sourceView = self.componentHost.findTaggedView(tag: viewOnceButtonTag) else { + return + } + + self.dismissAllTooltips() + + let absoluteFrame = sourceView.convert(sourceView.bounds, to: self.view) + let location = CGRect(origin: CGPoint(x: absoluteFrame.midX - 20.0, y: absoluteFrame.midY + 53.0), size: CGSize()) + + let tooltipController = TooltipScreen( + account: context.account, + sharedContext: context.sharedContext, + text: .markdown(text: text), + balancedTextLayout: true, + constrainWidth: 240.0, + style: .customBlur(UIColor(rgb: 0x18181a), 0.0), + arrowStyle: .small, + icon: nil, + location: .point(location, .right), + displayDuration: .default, + inset: 8.0, + cornerRadius: 8.0, + shouldDismissOnTouch: { _, _ in + return .ignore + } + ) + controller.present(tooltipController, in: .window(.root)) + } fileprivate func dismissAllTooltips() { guard let controller = self.controller else { @@ -1934,7 +1979,7 @@ public class VideoMessageCameraScreen: ViewController { self.updateCameraState({ $0.updatedRecording(.handsFree) }, transition: .spring(duration: 0.4)) } - self.node.maybePresentViewOnceTooltip() + self.node.maybePresentTooltips() } public func discardVideo() { diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift index 3bd0a344cf..8d12f0c738 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift @@ -4112,6 +4112,11 @@ extension ChatControllerImpl { return } self.updateTrimRange(start: start, end: end, updatedEnd: updatedEnd, apply: apply) + }, dismissAllTooltips: { [weak self] in + guard let self else { + return + } + self.dismissAllTooltips() }, updateHistoryFilter: { [weak self] update in guard let self else { return diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerMediaRecording.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerMediaRecording.swift index e0965c3c62..22e9cf0160 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerMediaRecording.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerMediaRecording.swift @@ -276,6 +276,8 @@ extension ChatControllerImpl { audioRecorderValue.stop() } + self.dismissAllTooltips() + switch updatedAction { case .dismiss: self.recorderDataDisposable.set(nil) @@ -528,7 +530,11 @@ extension ChatControllerImpl { }) } - self.videoRecorderValue?.lockVideoRecording() + if let _ = self.audioRecorderValue { + self.maybePresentAudioPauseTooltip() + } else if let videoRecorderValue = self.videoRecorderValue { + videoRecorderValue.lockVideoRecording() + } } func deleteMediaRecording() { @@ -544,6 +550,56 @@ extension ChatControllerImpl { $0.updatedInterfaceState { $0.withUpdatedMediaDraftState(nil) } }) self.updateDownButtonVisibility() + + self.dismissAllTooltips() + } + + private func maybePresentAudioPauseTooltip() { + let _ = (ApplicationSpecificNotice.getVoiceMessagesPauseSuggestion(accountManager: self.context.sharedContext.accountManager) + |> deliverOnMainQueue).startStandalone(next: { [weak self] pauseCounter in + guard let self else { + return + } + + if pauseCounter >= 3 { + return + } else { + Queue.mainQueue().after(0.3) { + self.displayPauseTooltip(text: self.presentationData.strings.Chat_PauseVoiceMessageTooltip) + } + let _ = ApplicationSpecificNotice.incrementVoiceMessagesPauseSuggestion(accountManager: self.context.sharedContext.accountManager).startStandalone() + } + }) + } + + private func displayPauseTooltip(text: String) { + guard let layout = self.validLayout else { + return + } + + self.dismissAllTooltips() + + let insets = layout.insets(options: [.input]) + let location = CGRect(origin: CGPoint(x: layout.size.width - layout.safeInsets.right - 42.0 - UIScreenPixel, y: layout.size.height - insets.bottom - 122.0), size: CGSize()) + + let tooltipController = TooltipScreen( + account: self.context.account, + sharedContext: self.context.sharedContext, + text: .markdown(text: text), + balancedTextLayout: true, + constrainWidth: 240.0, + style: .customBlur(UIColor(rgb: 0x18181a), 0.0), + arrowStyle: .small, + icon: nil, + location: .point(location, .right), + displayDuration: .default, + inset: 8.0, + cornerRadius: 8.0, + shouldDismissOnTouch: { _, _ in + return .ignore + } + ) + self.present(tooltipController, in: .window(.root)) } private func withAudioRecorder(_ f: (ManagedAudioRecorder) -> Void) { diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index f0be6dad05..8d073cbc1a 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -9111,6 +9111,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let controller = controller as? QuickShareToastScreen { controller.dismissWithCommitAction() } + if let controller = controller as? TooltipScreen, !controller.alwaysVisible { + controller.dismiss() + } }) self.forEachController({ controller in if let controller = controller as? UndoOverlayController { diff --git a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift index 7aea29de18..b28520a2e3 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift @@ -2924,6 +2924,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch self.tooltipController?.dismiss() if self.viewOnce { + self.interfaceInteraction?.dismissAllTooltips() self.displayViewOnceTooltip(text: interfaceState.strings.Chat_PlayVoiceMessageOnceTooltip) let _ = ApplicationSpecificNotice.incrementVoiceMessagesPlayOnceSuggestion(accountManager: context.sharedContext.accountManager, count: 3).startStandalone()