From a690eb803323f809824a33a010f488dc647e8439 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 31 Aug 2023 18:30:33 +0400 Subject: [PATCH 01/13] Various fixes --- .../Telegram-iOS/en.lproj/Localizable.strings | 5 +++++ .../Sources/SecretMediaPreviewController.swift | 7 ++++++- ...Core_AutoremoveTimeoutMessageAttribute.swift | 12 ++++++++++-- ...kMessageContentAsConsumedInteractively.swift | 2 +- .../Sources/MediaEditorScreen.swift | 17 ++++++++++++++++- .../Sources/ShareWithPeersScreen.swift | 13 ++++++++++--- 6 files changed, 48 insertions(+), 8 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 2f3ea5fe2b..150de95c72 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -9870,3 +9870,8 @@ Sorry for the inconvenience."; "Story.Privacy.KeepOnChannelPageInfo" = "Keep this story on channel profile even after it expires in %@."; "Story.Editor.TooltipPremiumReaction" = "Subscribe to [Telegram Premium]() to use this reaction."; + +"Story.Privacy.TooltipStoryArchivedChannel" = "Users will see this story on the channel page even after it expires."; + +"Story.Editor.TooltipMutedWithAudio" = "Original audio will be removed"; +"Story.Editor.TooltipUnmutedWithAudio" = "Original audio will be preserved"; diff --git a/submodules/GalleryUI/Sources/SecretMediaPreviewController.swift b/submodules/GalleryUI/Sources/SecretMediaPreviewController.swift index 26a0fddda9..d0aa31a36b 100644 --- a/submodules/GalleryUI/Sources/SecretMediaPreviewController.swift +++ b/submodules/GalleryUI/Sources/SecretMediaPreviewController.swift @@ -139,6 +139,7 @@ public final class SecretMediaPreviewController: ViewController { private var messageView: MessageView? private var currentNodeMessageId: MessageId? private var currentNodeMessageIsVideo = false + private var currentNodeMessageIsViewOnce = false private var tempFile: TempBoxFile? private let _hiddenMedia = Promise<(MessageId, Media)?>(nil) @@ -263,6 +264,8 @@ public final class SecretMediaPreviewController: ViewController { } if let attribute = message.autoclearAttribute { + strongSelf.currentNodeMessageIsViewOnce = attribute.timeout == viewOnceTimeout + if let countdownBeginTime = attribute.countdownBeginTime { if let videoDuration = videoDuration { beginTimeAndTimeout = (CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970, videoDuration) @@ -271,6 +274,8 @@ public final class SecretMediaPreviewController: ViewController { } } } else if let attribute = message.autoremoveAttribute { + strongSelf.currentNodeMessageIsViewOnce = attribute.timeout == viewOnceTimeout + if let countdownBeginTime = attribute.countdownBeginTime { if let videoDuration = videoDuration { beginTimeAndTimeout = (CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970, videoDuration) @@ -478,7 +483,7 @@ public final class SecretMediaPreviewController: ViewController { if !self.didSetReady { self._ready.set(.single(true)) } - if !self.currentNodeMessageIsVideo { + if !(self.currentNodeMessageIsVideo && !self.currentNodeMessageIsViewOnce) { self.dismiss() } } diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_AutoremoveTimeoutMessageAttribute.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_AutoremoveTimeoutMessageAttribute.swift index 2ec5399c1e..b8c99a0b3f 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_AutoremoveTimeoutMessageAttribute.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_AutoremoveTimeoutMessageAttribute.swift @@ -56,7 +56,11 @@ public class AutoclearTimeoutMessageAttribute: MessageAttribute { self.countdownBeginTime = countdownBeginTime if let countdownBeginTime = countdownBeginTime { - self.automaticTimestampBasedAttribute = (1, countdownBeginTime + timeout) + if self.timeout == viewOnceTimeout { + self.automaticTimestampBasedAttribute = (1, countdownBeginTime) + } else { + self.automaticTimestampBasedAttribute = (1, countdownBeginTime + timeout) + } } else { self.automaticTimestampBasedAttribute = nil } @@ -67,7 +71,11 @@ public class AutoclearTimeoutMessageAttribute: MessageAttribute { self.countdownBeginTime = decoder.decodeOptionalInt32ForKey("c") if let countdownBeginTime = self.countdownBeginTime { - self.automaticTimestampBasedAttribute = (1, countdownBeginTime + self.timeout) + if self.timeout == viewOnceTimeout { + self.automaticTimestampBasedAttribute = (1, countdownBeginTime) + } else { + self.automaticTimestampBasedAttribute = (1, countdownBeginTime + self.timeout) + } } else { self.automaticTimestampBasedAttribute = nil } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/MarkMessageContentAsConsumedInteractively.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/MarkMessageContentAsConsumedInteractively.swift index 1916744a13..46be899ce2 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/MarkMessageContentAsConsumedInteractively.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/MarkMessageContentAsConsumedInteractively.swift @@ -83,7 +83,7 @@ func _internal_markMessageContentAsConsumedInteractively(postbox: Postbox, messa } else if let attribute = updatedAttributes[i] as? AutoclearTimeoutMessageAttribute { if attribute.countdownBeginTime == nil || attribute.countdownBeginTime == 0 { var timeout = attribute.timeout - if let duration = message.secretMediaDuration { + if let duration = message.secretMediaDuration, timeout != viewOnceTimeout { timeout = max(timeout, Int32(duration)) } updatedAttributes[i] = AutoclearTimeoutMessageAttribute(timeout: timeout, countdownBeginTime: timestamp) diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index 8c88fd1dfa..baabfbea55 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -2778,7 +2778,22 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate 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 tooltipController = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .plain(text: isMuted ? self.presentationData.strings.Story_Editor_TooltipMuted : self.presentationData.strings.Story_Editor_TooltipUnmuted), location: .point(location, .top), displayDuration: .default, inset: 16.0, shouldDismissOnTouch: { _, _ in + let text: String + if let _ = self.mediaEditor?.values.audioTrack { + if isMuted { + text = self.presentationData.strings.Story_Editor_TooltipMutedWithAudio + } else { + text = self.presentationData.strings.Story_Editor_TooltipUnmutedWithAudio + } + } else { + if isMuted { + text = self.presentationData.strings.Story_Editor_TooltipMuted + } else { + text = self.presentationData.strings.Story_Editor_TooltipUnmuted + } + } + + let tooltipController = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .plain(text: text), location: .point(location, .top), displayDuration: .default, inset: 16.0, shouldDismissOnTouch: { _, _ in return .ignore }) self.muteTooltip = tooltipController diff --git a/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift b/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift index 56dbc068be..b4c2c05db2 100644 --- a/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift +++ b/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift @@ -345,6 +345,8 @@ final class ShareWithPeersScreenComponent: Component { private var searchStateContext: ShareWithPeersScreen.StateContext? private var searchStateDisposable: Disposable? + private let hapticFeedback = HapticFeedback() + private var effectiveStateValue: ShareWithPeersScreen.State? { return self.searchStateContext?.stateValue ?? self.defaultStateValue } @@ -549,7 +551,11 @@ final class ShareWithPeersScreenComponent: Component { case .pin: if self.selectedOptions.contains(.pin) { animationName = "anim_profileadd" - text = presentationData.strings.Story_Privacy_TooltipStoryArchived + if let peerId = self.sendAsPeerId, peerId.namespace != Namespaces.Peer.CloudUser { + text = presentationData.strings.Story_Privacy_TooltipStoryArchivedChannel + } else { + text = presentationData.strings.Story_Privacy_TooltipStoryArchived + } } else { animationName = "anim_autoremove_on" text = presentationData.strings.Story_Privacy_TooltipStoryExpires @@ -996,9 +1002,9 @@ final class ShareWithPeersScreenComponent: Component { let subtitle: String? if case .user = peer { - subtitle = "personal account" + subtitle = environment.strings.VoiceChat_PersonalAccount } else { - subtitle = "channel" + subtitle = environment.strings.Channel_Status } var isStories = false @@ -1037,6 +1043,7 @@ final class ShareWithPeersScreenComponent: Component { if isStories { let _ = self.presentSendAsPeer() } else { + self.hapticFeedback.tap() self.environment?.controller()?.dismiss() self.component?.peerCompletion(peer.id) } From c4198047daffbe4815fba37032de3f8b2bd17de7 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 31 Aug 2023 20:28:10 +0400 Subject: [PATCH 02/13] Various fixes --- .../SecretMediaPreviewController.swift | 2 +- ...RadialStatusSecretTimeoutContentNode.swift | 27 ++++++++++++------- .../Sources/MediaEditorVideoExport.swift | 4 +-- .../ChatMessageInteractiveMediaNode.swift | 10 ++++--- 4 files changed, 26 insertions(+), 17 deletions(-) diff --git a/submodules/GalleryUI/Sources/SecretMediaPreviewController.swift b/submodules/GalleryUI/Sources/SecretMediaPreviewController.swift index d0aa31a36b..8a0c90bf06 100644 --- a/submodules/GalleryUI/Sources/SecretMediaPreviewController.swift +++ b/submodules/GalleryUI/Sources/SecretMediaPreviewController.swift @@ -483,7 +483,7 @@ public final class SecretMediaPreviewController: ViewController { if !self.didSetReady { self._ready.set(.single(true)) } - if !(self.currentNodeMessageIsVideo && !self.currentNodeMessageIsViewOnce) { + if !(self.currentNodeMessageIsVideo || self.currentNodeMessageIsViewOnce) { self.dismiss() } } diff --git a/submodules/RadialStatusNode/Sources/RadialStatusSecretTimeoutContentNode.swift b/submodules/RadialStatusNode/Sources/RadialStatusSecretTimeoutContentNode.swift index f790cf06d9..0305e5b912 100644 --- a/submodules/RadialStatusNode/Sources/RadialStatusSecretTimeoutContentNode.swift +++ b/submodules/RadialStatusNode/Sources/RadialStatusSecretTimeoutContentNode.swift @@ -95,7 +95,14 @@ final class RadialStatusSecretTimeoutContentNode: RadialStatusContentNode { override func layout() { super.layout() - self.animationNode.frame = CGRect(x: 6.0, y: 2.0, width: 36.0, height: 36.0) + var factor: CGFloat = 0.75 + var offset: CGFloat = 0.0415 + if self.bounds.width < 30.0 { + factor = 0.66 + offset = 0.08 + } + let size = floorToScreenPixels(self.bounds.width * factor) + self.animationNode.frame = CGRect(x: floorToScreenPixels((self.bounds.width - size) / 2.0), y: ceil(self.bounds.height * offset), width: size, height: size) } override func animateOut(to: RadialStatusNodeState, completion: @escaping () -> Void) { @@ -194,15 +201,15 @@ final class RadialStatusSecretTimeoutContentNode: RadialStatusContentNode { } if let parameters = parameters as? RadialStatusSecretTimeoutContentNodeParameters { - if let icon = parameters.icon, let iconImage = icon.cgImage { - let imageRect = CGRect(origin: CGPoint(x: floor((bounds.size.width - icon.size.width) / 2.0), y: floor((bounds.size.height - icon.size.height) / 2.0)), size: icon.size) - context.saveGState() - context.translateBy(x: imageRect.midX, y: imageRect.midY) - context.scaleBy(x: 1.0, y: -1.0) - context.translateBy(x: -imageRect.midX, y: -imageRect.midY) - context.draw(iconImage, in: imageRect) - context.restoreGState() - } +// if let icon = parameters.icon, let _ = icon.cgImage { +// let imageRect = CGRect(origin: CGPoint(x: floor((bounds.size.width - icon.size.width) / 2.0), y: floor((bounds.size.height - icon.size.height) / 2.0)), size: icon.size) +// context.saveGState() +// context.translateBy(x: imageRect.midX, y: imageRect.midY) +// context.scaleBy(x: 1.0, y: -1.0) +// context.translateBy(x: -imageRect.midX, y: -imageRect.midY) +// context.draw(iconImage, in: imageRect) +// context.restoreGState() +// } let lineWidth: CGFloat if parameters.sparks { diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoExport.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoExport.swift index 5b39d0811b..63c713f057 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoExport.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoExport.swift @@ -389,7 +389,7 @@ public final class MediaEditorVideoExport { try? videoTrack.insertTimeRange(timeRange, of: videoAssetTrack, at: .zero) - if let audioAssetTrack = asset.tracks(withMediaType: .audio).first, let audioTrack = mixComposition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid) { + if let audioAssetTrack = asset.tracks(withMediaType: .audio).first, let audioTrack = mixComposition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid), !self.configuration.values.videoIsMuted { try? audioTrack.insertTimeRange(timeRange, of: audioAssetTrack, at: .zero) } @@ -488,7 +488,7 @@ public final class MediaEditorVideoExport { } let audioTracks = inputAsset.tracks(withMediaType: .audio) - if audioTracks.count > 0, !self.configuration.values.videoIsMuted { + if audioTracks.count > 0, !self.configuration.values.videoIsMuted || self.configuration.values.audioTrack != nil { let audioOutput = AVAssetReaderAudioMixOutput(audioTracks: audioTracks, audioSettings: nil) audioOutput.alwaysCopiesSampleData = false if reader.canAdd(audioOutput) { diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift index f73d32a596..24e8a96a00 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift @@ -189,7 +189,7 @@ private class ExtendedMediaOverlayNode: ASDisplayNode { var isRevealed = false var tapped: () -> Void = {} - init(enableAnimations: Bool) { + init(hasImageOverlay: Bool, enableAnimations: Bool) { self.blurredImageNode = TransformImageNode() self.blurredImageNode.contentAnimations = [] @@ -212,7 +212,9 @@ private class ExtendedMediaOverlayNode: ASDisplayNode { super.init() - self.addSubnode(self.blurredImageNode) + if hasImageOverlay { + self.addSubnode(self.blurredImageNode) + } self.addSubnode(self.dustNode) self.addSubnode(self.buttonNode) @@ -1986,7 +1988,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio } else { secretProgressIcon = PresentationResourcesChat.chatBubbleSecretMediaCompactIcon(theme) } - if isSecretMedia, let (maybeBeginTime, timeout) = secretBeginTimeAndTimeout, let beginTime = maybeBeginTime { + if isSecretMedia, let (maybeBeginTime, timeout) = secretBeginTimeAndTimeout, let beginTime = maybeBeginTime, timeout != viewOnceTimeout { state = .secretTimeout(color: messageTheme.mediaOverlayControlColors.foregroundColor, icon: secretProgressIcon, beginTime: beginTime, timeout: timeout, sparks: true) backgroundColor = messageTheme.mediaDateAndStatusFillColor } else if isSecretMedia, let _ = secretProgressIcon { @@ -2133,7 +2135,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio if displaySpoiler { if self.extendedMediaOverlayNode == nil { - let extendedMediaOverlayNode = ExtendedMediaOverlayNode(enableAnimations: self.context?.sharedContext.energyUsageSettings.fullTranslucency ?? true) + let extendedMediaOverlayNode = ExtendedMediaOverlayNode(hasImageOverlay: !isSecretMedia, enableAnimations: self.context?.sharedContext.energyUsageSettings.fullTranslucency ?? true) extendedMediaOverlayNode.tapped = { [weak self] in self?.internallyVisible = true self?.updateVisibility() From 70db4d97184e4c2a1bd0afb2f9c8e0ce25bd8913 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 31 Aug 2023 20:46:59 +0400 Subject: [PATCH 03/13] Fix build --- .../TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift index 24e8a96a00..8ce5239466 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift @@ -1988,7 +1988,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio } else { secretProgressIcon = PresentationResourcesChat.chatBubbleSecretMediaCompactIcon(theme) } - if isSecretMedia, let (maybeBeginTime, timeout) = secretBeginTimeAndTimeout, let beginTime = maybeBeginTime, timeout != viewOnceTimeout { + if isSecretMedia, let (maybeBeginTime, timeout) = secretBeginTimeAndTimeout, let beginTime = maybeBeginTime, Int32(timeout) != viewOnceTimeout { state = .secretTimeout(color: messageTheme.mediaOverlayControlColors.foregroundColor, icon: secretProgressIcon, beginTime: beginTime, timeout: timeout, sparks: true) backgroundColor = messageTheme.mediaDateAndStatusFillColor } else if isSecretMedia, let _ = secretProgressIcon { From 24b671a53ccbaf71edacefa5b0ce5426d646057b Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 31 Aug 2023 21:41:02 +0400 Subject: [PATCH 04/13] Various improvements --- .../Telegram-iOS/en.lproj/Localizable.strings | 3 +++ .../GalleryUI/Sources/GalleryController.swift | 2 ++ .../Sources/Items/ChatImageGalleryItem.swift | 14 +++++++++----- .../Sources/SecretMediaPreviewController.swift | 14 +++++++++++--- .../CameraScreen/Sources/CameraScreen.swift | 17 +++++++++++++++-- 5 files changed, 40 insertions(+), 10 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 150de95c72..185f84c5be 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -9875,3 +9875,6 @@ Sorry for the inconvenience."; "Story.Editor.TooltipMutedWithAudio" = "Original audio will be removed"; "Story.Editor.TooltipUnmutedWithAudio" = "Original audio will be preserved"; + +"SecretImage.ViewOnce.Title" = "Disappearing Photo"; +"SecretVideo.ViewOnce.Title" = "Disappearing Video"; diff --git a/submodules/GalleryUI/Sources/GalleryController.swift b/submodules/GalleryUI/Sources/GalleryController.swift index baee287134..9c6e0295f5 100644 --- a/submodules/GalleryUI/Sources/GalleryController.swift +++ b/submodules/GalleryUI/Sources/GalleryController.swift @@ -184,6 +184,7 @@ public func galleryItemForEntry( location: location, translateToLanguage: translateToLanguage, peerIsCopyProtected: peerIsCopyProtected, + isSecret: isSecret, displayInfoOnTop: displayInfoOnTop, performAction: performAction, openActionOptions: openActionOptions, @@ -265,6 +266,7 @@ public func galleryItemForEntry( location: location, translateToLanguage: translateToLanguage, peerIsCopyProtected: peerIsCopyProtected, + isSecret: isSecret, displayInfoOnTop: displayInfoOnTop, performAction: performAction, openActionOptions: openActionOptions, diff --git a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift index f6ca4b29c5..3cfe8427e6 100644 --- a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift @@ -115,18 +115,20 @@ class ChatImageGalleryItem: GalleryItem { let location: MessageHistoryEntryLocation? let translateToLanguage: String? let peerIsCopyProtected: Bool + let isSecret: Bool let displayInfoOnTop: Bool let performAction: (GalleryControllerInteractionTapAction) -> Void let openActionOptions: (GalleryControllerInteractionTapAction, Message) -> Void let present: (ViewController, Any?) -> Void - init(context: AccountContext, presentationData: PresentationData, message: Message, location: MessageHistoryEntryLocation?, translateToLanguage: String? = nil, peerIsCopyProtected: Bool = false, displayInfoOnTop: Bool, performAction: @escaping (GalleryControllerInteractionTapAction) -> Void, openActionOptions: @escaping (GalleryControllerInteractionTapAction, Message) -> Void, present: @escaping (ViewController, Any?) -> Void) { + init(context: AccountContext, presentationData: PresentationData, message: Message, location: MessageHistoryEntryLocation?, translateToLanguage: String? = nil, peerIsCopyProtected: Bool = false, isSecret: Bool = false, displayInfoOnTop: Bool, performAction: @escaping (GalleryControllerInteractionTapAction) -> Void, openActionOptions: @escaping (GalleryControllerInteractionTapAction, Message) -> Void, present: @escaping (ViewController, Any?) -> Void) { self.context = context self.presentationData = presentationData self.message = message self.location = location self.translateToLanguage = translateToLanguage self.peerIsCopyProtected = peerIsCopyProtected + self.isSecret = isSecret self.displayInfoOnTop = displayInfoOnTop self.performAction = performAction self.openActionOptions = openActionOptions @@ -136,7 +138,7 @@ class ChatImageGalleryItem: GalleryItem { func node(synchronous: Bool) -> GalleryItemNode { let node = ChatImageGalleryItemNode(context: self.context, presentationData: self.presentationData, performAction: self.performAction, openActionOptions: self.openActionOptions, present: self.present) - node.setMessage(self.message, displayInfo: !self.displayInfoOnTop, translateToLanguage: self.translateToLanguage, peerIsCopyProtected: self.peerIsCopyProtected) + node.setMessage(self.message, displayInfo: !self.displayInfoOnTop, translateToLanguage: self.translateToLanguage, peerIsCopyProtected: self.peerIsCopyProtected, isSecret: self.isSecret) for media in self.message.media { if let invoice = media as? TelegramMediaInvoice, let extendedMedia = invoice.extendedMedia, case let .full(fullMedia) = extendedMedia, let image = fullMedia as? TelegramMediaImage { node.setImage(userLocation: .peer(self.message.id.peerId), imageReference: .message(message: MessageReference(self.message), media: image)) @@ -175,7 +177,7 @@ class ChatImageGalleryItem: GalleryItem { if self.displayInfoOnTop { node.titleContentView?.setMessage(self.message, presentationData: self.presentationData, accountPeerId: self.context.account.peerId) } - node.setMessage(self.message, displayInfo: !self.displayInfoOnTop, translateToLanguage: self.translateToLanguage, peerIsCopyProtected: self.peerIsCopyProtected) + node.setMessage(self.message, displayInfo: !self.displayInfoOnTop, translateToLanguage: self.translateToLanguage, peerIsCopyProtected: self.peerIsCopyProtected, isSecret: self.isSecret) } } @@ -204,6 +206,7 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode { private var message: Message? private var translateToLanguage: String? private var peerIsCopyProtected: Bool = false + private var isSecret: Bool = false private let presentationData: PresentationData private let imageNode: TransformImageNode @@ -328,11 +331,12 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode { transition.updateFrame(node: self.statusNode, frame: CGRect(origin: CGPoint(), size: statusSize)) } - fileprivate func setMessage(_ message: Message, displayInfo: Bool, translateToLanguage: String?, peerIsCopyProtected: Bool) { + fileprivate func setMessage(_ message: Message, displayInfo: Bool, translateToLanguage: String?, peerIsCopyProtected: Bool, isSecret: Bool) { self.message = message self.translateToLanguage = translateToLanguage self.peerIsCopyProtected = peerIsCopyProtected - self.imageNode.captureProtected = message.isCopyProtected() + self.isSecret = isSecret + self.imageNode.captureProtected = message.isCopyProtected() || peerIsCopyProtected || isSecret self.footerContentNode.setMessage(message, displayInfo: displayInfo, translateToLanguage: translateToLanguage, peerIsCopyProtected: peerIsCopyProtected) } diff --git a/submodules/GalleryUI/Sources/SecretMediaPreviewController.swift b/submodules/GalleryUI/Sources/SecretMediaPreviewController.swift index 8a0c90bf06..2a72ee37c4 100644 --- a/submodules/GalleryUI/Sources/SecretMediaPreviewController.swift +++ b/submodules/GalleryUI/Sources/SecretMediaPreviewController.swift @@ -60,7 +60,7 @@ private final class SecretMediaPreviewControllerNode: GalleryControllerNode { var beginTimeAndTimeout: (Double, Double)? { didSet { - if let (beginTime, timeout) = self.beginTimeAndTimeout { + if let (beginTime, timeout) = self.beginTimeAndTimeout, Int32(timeout) != viewOnceTimeout { if self.timeoutNode == nil { let timeoutNode = RadialStatusNode(backgroundNodeColor: UIColor(white: 0.0, alpha: 0.5)) self.timeoutNode = timeoutNode @@ -289,10 +289,18 @@ public final class SecretMediaPreviewController: ViewController { if file.isAnimated { strongSelf.title = strongSelf.presentationData.strings.SecretGif_Title } else { - strongSelf.title = strongSelf.presentationData.strings.SecretVideo_Title + if strongSelf.currentNodeMessageIsViewOnce { + strongSelf.title = strongSelf.presentationData.strings.SecretVideo_ViewOnce_Title + } else { + strongSelf.title = strongSelf.presentationData.strings.SecretVideo_Title + } } } else { - strongSelf.title = strongSelf.presentationData.strings.SecretImage_Title + if strongSelf.currentNodeMessageIsViewOnce { + strongSelf.title = strongSelf.presentationData.strings.SecretImage_ViewOnce_Title + } else { + strongSelf.title = strongSelf.presentationData.strings.SecretImage_Title + } } if let beginTimeAndTimeout = beginTimeAndTimeout { diff --git a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift index 6ba26378ce..12ecfd9e54 100644 --- a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift +++ b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift @@ -1466,6 +1466,7 @@ public class CameraScreen: ViewController { }) } + fileprivate var captureStartTimestamp: Double? private func setupCamera() { guard self.camera == nil else { return @@ -1575,6 +1576,7 @@ public class CameraScreen: ViewController { camera.focus(at: CGPoint(x: 0.5, y: 0.5), autoFocus: true) camera.startCapture() + self.captureStartTimestamp = CACurrentMediaTime() self.camera = camera @@ -2515,8 +2517,19 @@ public class CameraScreen: ViewController { guard let self, !self.didStopCameraCapture else { return } - self.didStopCameraCapture = true - self.node.pauseCameraCapture() + let currentTimestamp = CACurrentMediaTime() + if let startTimestamp = self.node.captureStartTimestamp { + let difference = currentTimestamp - startTimestamp + if difference < 2.0 { + Queue.mainQueue().after(2.0 - difference) { + self.didStopCameraCapture = true + self.node.pauseCameraCapture() + } + } else { + self.didStopCameraCapture = true + self.node.pauseCameraCapture() + } + } } let resumeCameraCapture = { [weak self] in From 4b5ccefbc8db7a8fe3a656b14cdb933d1735fd84 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Fri, 1 Sep 2023 00:02:49 +0400 Subject: [PATCH 05/13] Various improvements --- .../Telegram-iOS/en.lproj/Localizable.strings | 14 ++ .../Sources/DrawingStickerEntity.swift | 8 +- .../ChatItemGalleryFooterContentNode.swift | 4 + .../Sources/LegacyMessageInputPanel.swift | 62 ++++---- .../Sources/MessageInputPanelComponent.swift | 41 +----- .../Sources/TimeoutContentComponent.swift | 137 ++++++++++++++++++ 6 files changed, 198 insertions(+), 68 deletions(-) create mode 100644 submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/TimeoutContentComponent.swift diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 185f84c5be..2a92f3ebe8 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -9878,3 +9878,17 @@ Sorry for the inconvenience."; "SecretImage.ViewOnce.Title" = "Disappearing Photo"; "SecretVideo.ViewOnce.Title" = "Disappearing Video"; + +"MediaPicker.Timer.Description" = "Choose how long the media will be kept after opening."; +"MediaPicker.Timer.ViewOnce" = "View Once"; +"MediaPicker.Timer.Seconds_1" = "%d Second"; +"MediaPicker.Timer.Seconds_any" = "%d Seconds"; +"MediaPicker.Timer.DoNotDelete" = "Do Not Delete"; + +"MediaPicker.Timer.Photo.ViewOnceTooltip" = "Photo set to view once."; +"MediaPicker.Timer.Photo.TimerTooltip" = "Photo will be deleted in\n%@ seconds after opening."; +"MediaPicker.Timer.Photo.KeepTooltip" = "Photo will be kept in chat."; + +"MediaPicker.Timer.Video.ViewOnceTooltip" = "Video set to view once."; +"MediaPicker.Timer.Video.TimerTooltip" = "Video will be deleted in\n%@ seconds after opening."; +"MediaPicker.Timer.Video.KeepTooltip" = "Video will be kept in chat."; diff --git a/submodules/DrawingUI/Sources/DrawingStickerEntity.swift b/submodules/DrawingUI/Sources/DrawingStickerEntity.swift index c31b1e5969..f0ccd4f1d1 100644 --- a/submodules/DrawingUI/Sources/DrawingStickerEntity.swift +++ b/submodules/DrawingUI/Sources/DrawingStickerEntity.swift @@ -392,11 +392,15 @@ public final class DrawingStickerEntityView: DrawingEntityView { } override func onSelection() { - self.presentReactionSelection() + if self.isReaction { + self.presentReactionSelection() + } } func onDeselection() { - let _ = self.dismissReactionSelection() + if self.isReaction { + let _ = self.dismissReactionSelection() + } } private weak var reactionContextNode: ReactionContextNode? diff --git a/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift b/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift index ab0adbfdff..675f3aecf5 100644 --- a/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift +++ b/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift @@ -880,6 +880,10 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll displayCaption = !self.textNode.isHidden } + if metrics.isTablet { + self.fullscreenButton.isHidden = true + } + var textFrame = CGRect() var visibleTextHeight: CGFloat = 0.0 if !self.textNode.isHidden { diff --git a/submodules/TelegramUI/Components/LegacyMessageInputPanel/Sources/LegacyMessageInputPanel.swift b/submodules/TelegramUI/Components/LegacyMessageInputPanel/Sources/LegacyMessageInputPanel.swift index ebc43e38ec..5143f56eb7 100644 --- a/submodules/TelegramUI/Components/LegacyMessageInputPanel/Sources/LegacyMessageInputPanel.swift +++ b/submodules/TelegramUI/Components/LegacyMessageInputPanel/Sources/LegacyMessageInputPanel.swift @@ -81,6 +81,7 @@ public class LegacyMessageInputPanelNode: ASDisplayNode, TGCaptionPanelView { } public func setTimeout(_ timeout: Int32) { + self.dismissTimeoutTooltip() var timeout: Int32? = timeout if timeout == 0 { timeout = nil @@ -250,12 +251,12 @@ public class LegacyMessageInputPanelNode: ASDisplayNode, TGCaptionPanelView { let currentValue = self.currentTimeout let presentationData = self.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: defaultDarkPresentationTheme) - let title = "Choose how long the media will be kept after opening." + let title = presentationData.strings.MediaPicker_Timer_Description let emptyAction: ((ContextMenuActionItem.Action) -> Void)? = nil items.append(.action(ContextMenuActionItem(text: title, textLayout: .multiline, textFont: .small, icon: { _ in return nil }, action: emptyAction))) - items.append(.action(ContextMenuActionItem(text: "View Once", icon: { theme in + items.append(.action(ContextMenuActionItem(text: presentationData.strings.MediaPicker_Timer_ViewOnce, icon: { theme in return currentValue == viewOnceTimeout ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil }, action: { _, a in a(.default) @@ -263,31 +264,19 @@ public class LegacyMessageInputPanelNode: ASDisplayNode, TGCaptionPanelView { updateTimeout(viewOnceTimeout) }))) - items.append(.action(ContextMenuActionItem(text: "3 Seconds", icon: { theme in - return currentValue == 3 ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil - }, action: { _, a in - a(.default) - - updateTimeout(3) - }))) + let values: [Int32] = [3, 10, 30] - items.append(.action(ContextMenuActionItem(text: "10 Seconds", icon: { theme in - return currentValue == 10 ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil - }, action: { _, a in - a(.default) + for value in values { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.MediaPicker_Timer_Seconds(value), icon: { theme in + return currentValue == value ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + }, action: { _, a in + a(.default) + + updateTimeout(value) + }))) + } - updateTimeout(10) - }))) - - items.append(.action(ContextMenuActionItem(text: "30 Seconds", icon: { theme in - return currentValue == 30 ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil - }, action: { _, a in - a(.default) - - updateTimeout(30) - }))) - - items.append(.action(ContextMenuActionItem(text: "Do Not Delete", icon: { theme in + items.append(.action(ContextMenuActionItem(text: presentationData.strings.MediaPicker_Timer_DoNotDelete, icon: { theme in return currentValue == nil ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil }, action: { _, a in a(.default) @@ -300,29 +289,36 @@ public class LegacyMessageInputPanelNode: ASDisplayNode, TGCaptionPanelView { } private weak var tooltipController: TooltipScreen? - private func presentTimeoutTooltip(sourceView: UIView, timeout: Int32?) { - guard let superview = self.view.superview?.superview else { - return - } + + private func dismissTimeoutTooltip() { if let tooltipController = self.tooltipController { self.tooltipController = nil tooltipController.dismiss() } + } + + private func presentTimeoutTooltip(sourceView: UIView, timeout: Int32?) { + guard let superview = self.view.superview?.superview else { + return + } + self.dismissTimeoutTooltip() let parentFrame = superview.convert(superview.bounds, to: nil) 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 - 2.0), size: CGSize()) + let isVideo = !"".isEmpty + let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } let text: String let iconName: String if timeout == viewOnceTimeout { - text = "Photo set to view once." + text = isVideo ? presentationData.strings.MediaPicker_Timer_Video_ViewOnceTooltip : presentationData.strings.MediaPicker_Timer_Photo_ViewOnceTooltip iconName = "anim_autoremove_on" } else if let timeout { - text = "Photo will be deleted in \(timeout) seconds after opening." + text = isVideo ? presentationData.strings.MediaPicker_Timer_Video_TimerTooltip("\(timeout)").string : presentationData.strings.MediaPicker_Timer_Photo_TimerTooltip("\(timeout)").string iconName = "anim_autoremove_on" } else { - text = "Photo will be kept in chat." + text = isVideo ? presentationData.strings.MediaPicker_Timer_Video_KeepTooltip : presentationData.strings.MediaPicker_Timer_Photo_KeepTooltip iconName = "anim_autoremove_off" } @@ -330,7 +326,7 @@ public class LegacyMessageInputPanelNode: ASDisplayNode, TGCaptionPanelView { account: self.context.account, sharedContext: self.context.sharedContext, text: .plain(text: text), - balancedTextLayout: true, + balancedTextLayout: false, style: .customBlur(UIColor(rgb: 0x18181a), 0.0), arrowStyle: .small, icon: .animation(name: iconName, delay: 0.1, tintColor: nil), diff --git a/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift b/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift index 911e501c3a..911970a76b 100644 --- a/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift +++ b/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift @@ -1456,42 +1456,17 @@ public final class MessageInputPanelComponent: Component { let accentColor = component.theme.chat.inputPanel.panelControlAccentColor if let timeoutAction = component.timeoutAction, let timeoutValue = component.timeoutValue { - func generateIcon(value: String, selected: Bool) -> UIImage? { - let image = UIImage(bundleImageName: "Media Editor/Timeout")! - let valueString = NSAttributedString(string: value, font: Font.with(size: value.count == 1 ? 12.0 : 10.0, design: .round, weight: .semibold), textColor: .white, paragraphAlignment: .center) - - return generateImage(image.size, contextGenerator: { size, context in - let bounds = CGRect(origin: CGPoint(), size: size) - context.clear(bounds) - - if selected { - context.setFillColor(accentColor.cgColor) - context.fillEllipse(in: CGRect(origin: .zero, size: size)) - } else { - if let cgImage = image.cgImage { - context.draw(cgImage, in: CGRect(origin: .zero, size: size)) - } - } - - var offset: CGPoint = CGPoint(x: 0.0, y: -3.0 - UIScreenPixel) - if value == "∞" { - offset.x += UIScreenPixel - offset.y += 1.0 - UIScreenPixel - } - - let valuePath = CGMutablePath() - valuePath.addRect(bounds.offsetBy(dx: offset.x, dy: offset.y)) - let valueFramesetter = CTFramesetterCreateWithAttributedString(valueString as CFAttributedString) - let valyeFrame = CTFramesetterCreateFrame(valueFramesetter, CFRangeMake(0, valueString.length), valuePath, nil) - CTFrameDraw(valyeFrame, context) - }) - } - - let icon = generateIcon(value: timeoutValue, selected: component.timeoutSelected) let timeoutButtonSize = self.timeoutButton.update( transition: transition, component: AnyComponent(Button( - content: AnyComponent(Image(image: icon, size: CGSize(width: 20.0, height: 20.0))), + content: AnyComponent( + TimeoutContentComponent( + color: .white, + accentColor: accentColor, + isSelected: component.timeoutSelected, + value: timeoutValue + ) + ), action: { [weak self] in guard let self, let timeoutButtonView = self.timeoutButton.view else { return diff --git a/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/TimeoutContentComponent.swift b/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/TimeoutContentComponent.swift new file mode 100644 index 0000000000..64a511b0bd --- /dev/null +++ b/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/TimeoutContentComponent.swift @@ -0,0 +1,137 @@ +import Foundation +import UIKit +import Display +import ComponentFlow + +public final class TimeoutContentComponent: Component { + public let color: UIColor + public let accentColor: UIColor + public let isSelected: Bool + public let value: String + + public init( + color: UIColor, + accentColor: UIColor, + isSelected: Bool, + value: String + ) { + self.color = color + self.accentColor = accentColor + self.isSelected = isSelected + self.value = value + } + + public static func ==(lhs: TimeoutContentComponent, rhs: TimeoutContentComponent) -> Bool { + if lhs.color != rhs.color { + return false + } + if lhs.accentColor != rhs.accentColor { + return false + } + if lhs.isSelected != rhs.isSelected { + return false + } + if lhs.value != rhs.value { + return false + } + return true + } + + public final class View: UIView { + private var component: TimeoutContentComponent? + private weak var state: EmptyComponentState? + + private let background: UIImageView + private let foreground: UIImageView + private let text = ComponentView() + + override init(frame: CGRect) { + self.background = UIImageView(image: UIImage(bundleImageName: "Media Editor/Timeout")) + self.foreground = UIImageView() + + super.init(frame: frame) + + self.addSubview(self.background) + self.addSubview(self.foreground) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(component: TimeoutContentComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + let previousComponent = self.component + self.component = component + self.state = state + + let size = CGSize(width: 20.0, height: 20.0) + if previousComponent?.accentColor != component.accentColor { + self.foreground.image = generateFilledCircleImage(diameter: size.width, color: component.accentColor) + } + + var updated = false + if let previousComponent { + if previousComponent.isSelected != component.isSelected { + updated = true + } + if previousComponent.value != component.value { + if let textView = self.text.view, let snapshotView = textView.snapshotView(afterScreenUpdates: false) { + snapshotView.frame = textView.frame + self.addSubview(snapshotView) + snapshotView.layer.animatePosition(from: .zero, to: CGPoint(x: 0.0, y: 3.0), duration: 0.2, removeOnCompletion: false, additive: true) + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in + snapshotView.removeFromSuperview() + }) + + textView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + textView.layer.animatePosition(from: CGPoint(x: 0.0, y: -3.0), to: .zero, duration: 0.2, additive: true) + } + } + } + + let fontSize: CGFloat + let textOffset: CGFloat + if component.value.count == 1 { + fontSize = 12.0 + textOffset = UIScreenPixel + } else { + fontSize = 10.0 + textOffset = -UIScreenPixel + } + + let font = Font.with(size: fontSize, design: .round, weight: .semibold) + let textSize = self.text.update( + transition: .immediate, + component: AnyComponent(Text(text: component.value, font: font, color: .white)), + environment: {}, + containerSize: CGSize(width: 100.0, height: 100.0) + ) + if let textView = self.text.view { + if textView.superview == nil { + self.addSubview(textView) + } + let textFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0) + UIScreenPixel, y: floorToScreenPixels((size.height - textSize.height) / 2.0) + textOffset), size: textSize) + transition.setPosition(view: textView, position: textFrame.center) + textView.bounds = CGRect(origin: CGPoint(), size: textFrame.size) + } + + self.background.frame = CGRect(origin: .zero, size: size) + + self.foreground.bounds = CGRect(origin: .zero, size: size) + self.foreground.center = CGPoint(x: size.width / 2.0, y: size.height / 2.0) + + let foregroundTransition: Transition = updated ? .easeInOut(duration: 0.2) : transition + foregroundTransition.setScale(view: self.foreground, scale: component.isSelected ? 1.0 : 0.001) + + return size + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} From 46f9dd3af28905eca9ea6c0096019b82cc2d1367 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Fri, 1 Sep 2023 03:33:28 +0400 Subject: [PATCH 06/13] Various fixes --- .../Sources/LegacyICloudFilePicker.swift | 3 +- .../Sources/LocationPickerController.swift | 4 + .../ContextReferenceButtonComponent/BUILD | 20 +++ .../ContextReferenceButtonComponent.swift | 123 ++++++++++++++++++ .../Sources/LegacyMessageInputPanel.swift | 8 +- .../Sources/MediaEditorScreen.swift | 41 ++++-- .../MessageInputPanelComponent/BUILD | 2 + .../Sources/MessageInputPanelComponent.swift | 20 +-- .../Sources/TimeoutContentComponent.swift | 4 +- .../Sources/ShareWithPeersScreen.swift | 61 ++++++--- .../ChatMessageInteractiveMediaNode.swift | 2 +- 11 files changed, 239 insertions(+), 49 deletions(-) create mode 100644 submodules/TelegramUI/Components/ContextReferenceButtonComponent/BUILD create mode 100644 submodules/TelegramUI/Components/ContextReferenceButtonComponent/Sources/ContextReferenceButtonComponent.swift diff --git a/submodules/LegacyMediaPickerUI/Sources/LegacyICloudFilePicker.swift b/submodules/LegacyMediaPickerUI/Sources/LegacyICloudFilePicker.swift index 592b6590bc..4a3de19f0c 100644 --- a/submodules/LegacyMediaPickerUI/Sources/LegacyICloudFilePicker.swift +++ b/submodules/LegacyMediaPickerUI/Sources/LegacyICloudFilePicker.swift @@ -63,7 +63,7 @@ public enum LegacyICloudFilePickerMode { } } -public func legacyICloudFilePicker(theme: PresentationTheme, mode: LegacyICloudFilePickerMode = .default, documentTypes: [String] = ["public.item"], forceDarkTheme: Bool = false, completion: @escaping ([URL]) -> Void) -> ViewController { +public func legacyICloudFilePicker(theme: PresentationTheme, mode: LegacyICloudFilePickerMode = .default, documentTypes: [String] = ["public.item"], forceDarkTheme: Bool = false, dismissed: @escaping () -> Void = {}, completion: @escaping ([URL]) -> Void) -> ViewController { var dismissImpl: (() -> Void)? let legacyController = LegacyICloudFileController(presentation: .modal(animateIn: true), theme: theme, completion: { urls in dismissImpl?() @@ -96,6 +96,7 @@ public func legacyICloudFilePicker(theme: PresentationTheme, mode: LegacyICloudF if let legacyController = legacyController { legacyController.dismiss() } + dismissed() } legacyController.bind(controller: UIViewController()) return legacyController diff --git a/submodules/LocationUI/Sources/LocationPickerController.swift b/submodules/LocationUI/Sources/LocationPickerController.swift index 5317fa1122..c557339e3a 100644 --- a/submodules/LocationUI/Sources/LocationPickerController.swift +++ b/submodules/LocationUI/Sources/LocationPickerController.swift @@ -412,6 +412,7 @@ private final class LocationPickerContext: AttachmentMediaPickerContext { public func storyLocationPickerController( context: AccountContext, location: CLLocationCoordinate2D?, + dismissed: @escaping () -> Void, completion: @escaping (TelegramMediaMap, Int64?, String?, String?, String?) -> Void ) -> ViewController { let presentationData = context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: defaultDarkColorPresentationTheme) @@ -427,5 +428,8 @@ public func storyLocationPickerController( } controller.navigationPresentation = .flatModal controller.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) + controller.didDismiss = { + dismissed() + } return controller } diff --git a/submodules/TelegramUI/Components/ContextReferenceButtonComponent/BUILD b/submodules/TelegramUI/Components/ContextReferenceButtonComponent/BUILD new file mode 100644 index 0000000000..b41112ece6 --- /dev/null +++ b/submodules/TelegramUI/Components/ContextReferenceButtonComponent/BUILD @@ -0,0 +1,20 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "ContextReferenceButtonComponent", + module_name = "ContextReferenceButtonComponent", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/Display", + "//submodules/ComponentFlow", + "//submodules/ContextUI", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/ContextReferenceButtonComponent/Sources/ContextReferenceButtonComponent.swift b/submodules/TelegramUI/Components/ContextReferenceButtonComponent/Sources/ContextReferenceButtonComponent.swift new file mode 100644 index 0000000000..6181c32abb --- /dev/null +++ b/submodules/TelegramUI/Components/ContextReferenceButtonComponent/Sources/ContextReferenceButtonComponent.swift @@ -0,0 +1,123 @@ +import Foundation +import UIKit +import Display +import ComponentFlow + +public final class ContextReferenceButtonComponent: Component { + let content: AnyComponent + let tag: AnyObject? + let minSize: CGSize? + let action: (UIView, ContextGesture?) -> Void + + public init( + content: AnyComponent, + tag: AnyObject? = nil, + minSize: CGSize?, + action: @escaping (UIView, ContextGesture?) -> Void + ) { + self.content = content + self.tag = tag + self.minSize = minSize + self.action = action + } + + public static func ==(lhs: ContextReferenceButtonComponent, rhs: ContextReferenceButtonComponent) -> Bool { + if lhs.content != rhs.content { + return false + } + if lhs.tag !== rhs.tag { + return false + } + if lhs.minSize != rhs.minSize { + return false + } + return true + } + + public final class View: UIView, ComponentTaggedView { + let buttonView: HighlightableButtonNode + let sourceView: ContextControllerSourceNode + let contextContentView: ContextReferenceContentNode + + private let componentView: ComponentView + + private var component: ContextReferenceButtonComponent? + + public func matches(tag: Any) -> Bool { + if let component = self.component, let componentTag = component.tag { + let tag = tag as AnyObject + if componentTag === tag { + return true + } + } + return false + } + + public init() { + self.componentView = ComponentView() + self.buttonView = HighlightableButtonNode() + self.sourceView = ContextControllerSourceNode() + self.contextContentView = ContextReferenceContentNode() + + super.init(frame: CGRect()) + + self.addSubview(self.buttonView.view) + self.buttonView.addSubnode(self.sourceView) + self.sourceView.addSubnode(self.contextContentView) + + self.sourceView.activated = { [weak self] gesture, _ in + if let self, let component = self.component { + component.action(self, gesture) + } + } + self.buttonView.addTarget(self, action: #selector(self.pressed), forControlEvents: .touchUpInside) + } + + required init?(coder aDecoder: NSCoder) { + preconditionFailure() + } + + @objc private func pressed() { + self.component?.action(self, nil) + } + + public func update(component: ContextReferenceButtonComponent, availableSize: CGSize, transition: Transition) -> CGSize { + self.component = component + + let componentSize = self.componentView.update( + transition: transition, + component: component.content, + environment: {}, + containerSize: availableSize + ) + + var size = componentSize + if let minSize = component.minSize { + size.width = max(size.width, minSize.width) + size.height = max(size.height, minSize.height) + } + + if let componentView = self.componentView.view { + componentView.isUserInteractionEnabled = false + if componentView.superview == nil { + self.contextContentView.view.addSubview(componentView) + } + transition.setFrame(view: componentView, frame: CGRect(origin: CGPoint(x: floor((size.width - componentSize.width) / 2.0), y: floor((size.height - componentSize.height) / 2.0)), size: componentSize)) + } + + transition.setFrame(view: self.buttonView.view, frame: CGRect(origin: .zero, size: size)) + transition.setFrame(view: self.sourceView.view, frame: CGRect(origin: .zero, size: size)) + transition.setFrame(view: self.contextContentView.view, frame: CGRect(origin: .zero, size: size)) + + return size + } + } + + public func makeView() -> View { + return View() + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/LegacyMessageInputPanel/Sources/LegacyMessageInputPanel.swift b/submodules/TelegramUI/Components/LegacyMessageInputPanel/Sources/LegacyMessageInputPanel.swift index 5143f56eb7..3e4435f83c 100644 --- a/submodules/TelegramUI/Components/LegacyMessageInputPanel/Sources/LegacyMessageInputPanel.swift +++ b/submodules/TelegramUI/Components/LegacyMessageInputPanel/Sources/LegacyMessageInputPanel.swift @@ -181,9 +181,9 @@ public class LegacyMessageInputPanelNode: ASDisplayNode, TGCaptionPanelView { likeAction: nil, likeOptionsAction: nil, inputModeAction: nil, - timeoutAction: self.chatLocation.peerId?.namespace == Namespaces.Peer.CloudUser ? { [weak self] sourceView in + timeoutAction: self.chatLocation.peerId?.namespace == Namespaces.Peer.CloudUser ? { [weak self] sourceView, gesture in if let self { - self.presentTimeoutSetup(sourceView: sourceView) + self.presentTimeoutSetup(sourceView: sourceView, gesture: gesture) } } : nil, forwardAction: nil, @@ -235,7 +235,7 @@ public class LegacyMessageInputPanelNode: ASDisplayNode, TGCaptionPanelView { return inputPanelSize.height - 8.0 } - private func presentTimeoutSetup(sourceView: UIView) { + private func presentTimeoutSetup(sourceView: UIView, gesture: ContextGesture?) { self.hapticFeedback.impact(.light) var items: [ContextMenuItem] = [] @@ -284,7 +284,7 @@ public class LegacyMessageInputPanelNode: ASDisplayNode, TGCaptionPanelView { updateTimeout(nil) }))) - let contextController = ContextController(presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), gesture: nil) + let contextController = ContextController(presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) self.present(contextController) } diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index baabfbea55..9dbc5dc3bf 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -1117,7 +1117,7 @@ final class MediaEditorScreenComponent: Component { } } }, - timeoutAction: isEditingStory ? nil : { [weak self] view in + timeoutAction: isEditingStory ? nil : { [weak self] view, gesture in guard let self, let controller = self.environment?.controller() as? MediaEditorScreen else { return } @@ -1130,7 +1130,7 @@ final class MediaEditorScreenComponent: Component { } else { hasPremium = false } - controller?.presentTimeoutSetup(sourceView: view, hasPremium: hasPremium) + controller?.presentTimeoutSetup(sourceView: view, gesture: gesture, hasPremium: hasPremium) }) }, forwardAction: nil, @@ -2972,7 +2972,15 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate location = draft.location } } - let locationController = storyLocationPickerController(context: self.context, location: location, completion: { [weak self] location, queryId, resultId, address, countryCode in + let locationController = storyLocationPickerController( + context: self.context, + location: location, + dismissed: { [weak self] in + if let self { + self.mediaEditor?.play() + } + }, + completion: { [weak self] location, queryId, resultId, address, countryCode in if let self { let emojiFile: Signal if let countryCode { @@ -3050,7 +3058,13 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } func presentAudioPicker() { - self.controller?.present(legacyICloudFilePicker(theme: self.presentationData.theme, mode: .import, documentTypes: ["public.mp3"], forceDarkTheme: true, completion: { [weak self] urls in + self.controller?.present(legacyICloudFilePicker(theme: self.presentationData.theme, mode: .import, documentTypes: ["public.mp3"], forceDarkTheme: true, dismissed: { [weak self] in + if let self { + Queue.mainQueue().after(0.1) { + self.mediaEditor?.play() + } + } + }, completion: { [weak self] urls in guard let self, let mediaEditor = self.mediaEditor, !urls.isEmpty, let url = urls.first else { return } @@ -3075,10 +3089,6 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } self.requestUpdate(transition: .easeInOut(duration: 0.2)) - - Queue.mainQueue().after(0.1) { - self.mediaEditor?.play() - } }), in: .window(.root)) } @@ -3096,7 +3106,13 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate action: { [weak self] f in f.dismissWithResult(.default) if let self { - self.mediaEditor?.setAudioTrack(nil) + if let mediaEditor = self.mediaEditor { + mediaEditor.setAudioTrack(nil) + + if !mediaEditor.sourceIsVideo && !mediaEditor.isPlaying { + mediaEditor.play() + } + } self.requestUpdate(transition: .easeInOut(duration: 0.25)) } } @@ -3887,7 +3903,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate }) } - func presentTimeoutSetup(sourceView: UIView, hasPremium: Bool) { + func presentTimeoutSetup(sourceView: UIView, gesture: ContextGesture?, hasPremium: Bool) { self.hapticFeedback.impact(.light) var items: [ContextMenuItem] = [] @@ -3908,7 +3924,6 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate let presentationData = self.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: defaultDarkPresentationTheme) let title = presentationData.strings.Story_Editor_ExpirationText let currentValue = self.state.privacy.timeout - let currentArchived = self.state.privacy.pin let emptyAction: ((ContextMenuActionItem.Action) -> Void)? = nil items.append(.action(ContextMenuActionItem(text: title, textLayout: .multiline, textFont: .small, icon: { _ in return nil }, action: emptyAction))) @@ -3944,7 +3959,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } }))) items.append(.action(ContextMenuActionItem(text: presentationData.strings.Story_Editor_ExpirationValue(24), icon: { theme in - return currentValue == 86400 && !currentArchived ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + return currentValue == 86400 ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil }, action: { _, a in a(.default) @@ -3966,7 +3981,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } }))) - let contextController = ContextController(presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: self, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), gesture: nil) + let contextController = ContextController(presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: self, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) self.present(contextController, in: .window(.root)) } diff --git a/submodules/TelegramUI/Components/MessageInputPanelComponent/BUILD b/submodules/TelegramUI/Components/MessageInputPanelComponent/BUILD index 76e70fbb35..225d0f982e 100644 --- a/submodules/TelegramUI/Components/MessageInputPanelComponent/BUILD +++ b/submodules/TelegramUI/Components/MessageInputPanelComponent/BUILD @@ -37,6 +37,8 @@ swift_library( "//submodules/AnimatedCountLabelNode", "//submodules/TelegramUI/Components/MessageInputActionButtonComponent", "//submodules/SearchPeerMembers", + "//submodules/ContextUI", + "//submodules/TelegramUI/Components/ContextReferenceButtonComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift b/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift index 911970a76b..898f0aee37 100644 --- a/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift +++ b/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift @@ -18,6 +18,9 @@ import AudioToolbox import AnimatedTextComponent import AnimatedCountLabelNode import MessageInputActionButtonComponent +import ContextReferenceButtonComponent + +private let timeoutButtonTag = GenericComponentViewTag() public final class MessageInputPanelComponent: Component { public struct ContextQueryTypes: OptionSet { @@ -120,7 +123,7 @@ public final class MessageInputPanelComponent: Component { public let likeAction: (() -> Void)? public let likeOptionsAction: ((UIView, ContextGesture?) -> Void)? public let inputModeAction: (() -> Void)? - public let timeoutAction: ((UIView) -> Void)? + public let timeoutAction: ((UIView, ContextGesture?) -> Void)? public let forwardAction: (() -> Void)? public let moreAction: ((UIView, ContextGesture?) -> Void)? public let presentVoiceMessagesUnavailableTooltip: ((UIView) -> Void)? @@ -172,7 +175,7 @@ public final class MessageInputPanelComponent: Component { likeAction: (() -> Void)?, likeOptionsAction: ((UIView, ContextGesture?) -> Void)?, inputModeAction: (() -> Void)?, - timeoutAction: ((UIView) -> Void)?, + timeoutAction: ((UIView, ContextGesture?) -> Void)?, forwardAction: (() -> Void)?, moreAction: ((UIView, ContextGesture?) -> Void)?, presentVoiceMessagesUnavailableTooltip: ((UIView) -> Void)?, @@ -1458,7 +1461,7 @@ public final class MessageInputPanelComponent: Component { if let timeoutAction = component.timeoutAction, let timeoutValue = component.timeoutValue { let timeoutButtonSize = self.timeoutButton.update( transition: transition, - component: AnyComponent(Button( + component: AnyComponent(ContextReferenceButtonComponent( content: AnyComponent( TimeoutContentComponent( color: .white, @@ -1467,13 +1470,12 @@ public final class MessageInputPanelComponent: Component { value: timeoutValue ) ), - action: { [weak self] in - guard let self, let timeoutButtonView = self.timeoutButton.view else { - return - } - timeoutAction(timeoutButtonView) + tag: timeoutButtonTag, + minSize: CGSize(width: 32.0, height: 32.0), + action: { view, gesture in + timeoutAction(view, gesture) } - ).minSize(CGSize(width: 32.0, height: 32.0))), + )), environment: {}, containerSize: CGSize(width: 32.0, height: 32.0) ) diff --git a/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/TimeoutContentComponent.swift b/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/TimeoutContentComponent.swift index 64a511b0bd..c8c854e20e 100644 --- a/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/TimeoutContentComponent.swift +++ b/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/TimeoutContentComponent.swift @@ -78,13 +78,13 @@ public final class TimeoutContentComponent: Component { if let textView = self.text.view, let snapshotView = textView.snapshotView(afterScreenUpdates: false) { snapshotView.frame = textView.frame self.addSubview(snapshotView) - snapshotView.layer.animatePosition(from: .zero, to: CGPoint(x: 0.0, y: 3.0), duration: 0.2, removeOnCompletion: false, additive: true) + snapshotView.layer.animatePosition(from: .zero, to: CGPoint(x: 0.0, y: -3.0), duration: 0.2, removeOnCompletion: false, additive: true) snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in snapshotView.removeFromSuperview() }) textView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) - textView.layer.animatePosition(from: CGPoint(x: 0.0, y: -3.0), to: .zero, duration: 0.2, additive: true) + textView.layer.animatePosition(from: CGPoint(x: 0.0, y: 3.0), to: .zero, duration: 0.2, additive: true) } } } diff --git a/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift b/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift index b4c2c05db2..dc2b29f7a4 100644 --- a/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift +++ b/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift @@ -503,6 +503,8 @@ final class ShareWithPeersScreenComponent: Component { let translation = recognizer.translation(in: self) self.dismissPanState = DismissPanState(translation: translation.y) self.state?.updated(transition: .immediate) + + self.updateModalOverlayTransition(transition: .immediate) case .cancelled, .ended: if self.dismissPanState != nil { let translation = recognizer.translation(in: self) @@ -512,8 +514,13 @@ final class ShareWithPeersScreenComponent: Component { if translation.y > 100.0 || velocity.y > 10.0 { controller.requestDismiss() + Queue.mainQueue().justDispatch { + controller.updateModalStyleOverlayTransitionFactor(0.0, transition: .animated(duration: 0.3, curve: .spring)) + } } else { - self.state?.updated(transition: Transition(animation: .curve(duration: 0.3, curve: .spring))) + let transition = Transition(animation: .curve(duration: 0.3, curve: .spring)) + self.state?.updated(transition: transition) + self.updateModalOverlayTransition(transition: transition) } } default: @@ -782,7 +789,7 @@ final class ShareWithPeersScreenComponent: Component { guard let self else { return } - let controller = ShareWithPeersScreen( + let peersController = ShareWithPeersScreen( context: component.context, initialPrivacy: EngineStoryPrivacy(base: .nobody, additionallyIncludePeers: []), stateContext: stateContext, @@ -797,10 +804,40 @@ final class ShareWithPeersScreenComponent: Component { self.state?.updated(transition: .spring(duration: 0.4)) } ) - self.environment?.controller()?.push(controller) + if let controller = self.environment?.controller() as? ShareWithPeersScreen { + controller.dismissAllTooltips() + controller.push(peersController) + } }) } + private func updateModalOverlayTransition(transition: Transition) { + guard let _ = self.component, let environment = self.environment, let itemLayout = self.itemLayout else { + return + } + + var topOffset = -self.scrollView.bounds.minY + itemLayout.topInset + topOffset = max(0.0, topOffset) + if let dismissPanState = self.dismissPanState { + topOffset += dismissPanState.translation + } + + let topOffsetDistance: CGFloat = min(200.0, floor(itemLayout.containerSize.height * 0.25)) + var topOffsetFraction = topOffset / topOffsetDistance + topOffsetFraction = max(0.0, min(1.0, topOffsetFraction)) + + let transitionFactor: CGFloat = 1.0 - topOffsetFraction + if let controller = environment.controller() { + Queue.mainQueue().justDispatch { + var transition = transition + if controller.modalStyleOverlayTransitionFactor.isZero && transitionFactor > 0.0, transition.animation.isImmediate { + transition = .spring(duration: 0.4) + } + controller.updateModalStyleOverlayTransitionFactor(transitionFactor, transition: transition.containedViewLayoutTransition) + } + } + } + private func updateScrolling(transition: Transition) { guard let component = self.component, let environment = self.environment, let itemLayout = self.itemLayout else { return @@ -819,21 +856,7 @@ final class ShareWithPeersScreenComponent: Component { var bottomAlpha: CGFloat = bottomDistance / bottomAlphaDistance bottomAlpha = max(0.0, min(1.0, bottomAlpha)) - let topOffsetDistance: CGFloat = min(200.0, floor(itemLayout.containerSize.height * 0.25)) - self.topOffsetDistance = topOffsetDistance - var topOffsetFraction = topOffset / topOffsetDistance - topOffsetFraction = max(0.0, min(1.0, topOffsetFraction)) - - let transitionFactor: CGFloat = 1.0 - topOffsetFraction - if let controller = environment.controller() { - Queue.mainQueue().justDispatch { - var transition = transition - if controller.modalStyleOverlayTransitionFactor.isZero && transitionFactor > 0.0, transition.animation.isImmediate { - transition = .spring(duration: 0.4) - } - controller.updateModalStyleOverlayTransitionFactor(transitionFactor, transition: transition.containedViewLayoutTransition) - } - } + self.updateModalOverlayTransition(transition: transition) var visibleBounds = self.scrollView.bounds visibleBounds.origin.y -= itemLayout.topInset @@ -1043,7 +1066,7 @@ final class ShareWithPeersScreenComponent: Component { if isStories { let _ = self.presentSendAsPeer() } else { - self.hapticFeedback.tap() + self.hapticFeedback.impact(.light) self.environment?.controller()?.dismiss() self.component?.peerCompletion(peer.id) } diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift index 8ce5239466..c85ae9df19 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift @@ -2045,7 +2045,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio if isSecretMedia { let remainingTime: Int32? - if let (maybeBeginTime, timeout) = secretBeginTimeAndTimeout { + if let (maybeBeginTime, timeout) = secretBeginTimeAndTimeout, Int32(timeout) != viewOnceTimeout { if let beginTime = maybeBeginTime { let elapsedTime = CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970 - beginTime remainingTime = Int32(max(0.0, timeout - elapsedTime)) From 40cdc47aabc5438f3051452654cb516e5a033705 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Fri, 1 Sep 2023 12:06:02 +0400 Subject: [PATCH 07/13] Various fixes --- .../Sources/RadialStatusSecretTimeoutContentNode.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/submodules/RadialStatusNode/Sources/RadialStatusSecretTimeoutContentNode.swift b/submodules/RadialStatusNode/Sources/RadialStatusSecretTimeoutContentNode.swift index 0305e5b912..8c424477d7 100644 --- a/submodules/RadialStatusNode/Sources/RadialStatusSecretTimeoutContentNode.swift +++ b/submodules/RadialStatusNode/Sources/RadialStatusSecretTimeoutContentNode.swift @@ -68,7 +68,6 @@ final class RadialStatusSecretTimeoutContentNode: RadialStatusContentNode { super.init() self.isOpaque = false -// self.isLayerBacked = true class DisplayLinkProxy: NSObject { weak var target: RadialStatusSecretTimeoutContentNode? @@ -85,7 +84,9 @@ final class RadialStatusSecretTimeoutContentNode: RadialStatusContentNode { self.displayLink?.isPaused = true self.displayLink?.add(to: RunLoop.main, forMode: .common) - self.addSubnode(self.animationNode) + if icon != nil { + self.addSubnode(self.animationNode) + } } deinit { From c0868f1581bd5730a0e25df33318c471c22799a4 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Fri, 1 Sep 2023 13:36:36 +0400 Subject: [PATCH 08/13] Web app improvements --- .../WebUI/Sources/WebAppController.swift | 80 ++++++++++++++----- 1 file changed, 60 insertions(+), 20 deletions(-) diff --git a/submodules/WebUI/Sources/WebAppController.swift b/submodules/WebUI/Sources/WebAppController.swift index 2e3f0d07d6..13eb3fef98 100644 --- a/submodules/WebUI/Sources/WebAppController.swift +++ b/submodules/WebUI/Sources/WebAppController.swift @@ -1121,7 +1121,7 @@ public final class WebAppController: ViewController, AttachmentContainable { } fileprivate func shareAccountContact() { - guard let controller = self.controller else { + guard let controller = self.controller, let botId = self.controller?.botId else { return } @@ -1135,28 +1135,68 @@ public final class WebAppController: ViewController, AttachmentContainable { self.webView?.sendEvent(name: "phone_requested", data: paramsString) } - let alertController = textAlertController(context: self.context, updatedPresentationData: controller.updatedPresentationData, title: self.presentationData.strings.Conversation_ShareBotContactConfirmationTitle, text: self.presentationData.strings.Conversation_ShareBotContactConfirmation, actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_Cancel, action: { - sendEvent(false) - }), TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: { [weak self] in - guard let self else { + let context = self.context + let _ = (self.context.engine.data.get( + TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId), + TelegramEngine.EngineData.Item.Peer.IsBlocked(id: botId) + ) + |> deliverOnMainQueue).start(next: { [weak self, weak controller] accountPeer, isBlocked in + guard let self, let controller, let accountPeer else { return } - let _ = (self.context.account.postbox.loadedPeerWithId(self.context.account.peerId) - |> deliverOnMainQueue).start(next: { [weak self] peer in - if let self, let botId = self.controller?.botId, let peer = peer as? TelegramUser, let phone = peer.phone, !phone.isEmpty { - let _ = enqueueMessages(account: self.context.account, peerId: botId, messages: [ - .message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: TelegramMediaContact(firstName: peer.firstName ?? "", lastName: peer.lastName ?? "", phoneNumber: phone, peerId: peer.id, vCardData: nil)), replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: []) - ]).start() - sendEvent(true) - } - }) - })]) - alertController.dismissed = { byOutsideTap in - if byOutsideTap { - sendEvent(false) + var requiresUnblock = false + if case let .known(value) = isBlocked, value { + requiresUnblock = true } - } - controller.present(alertController, in: .window(.root)) + + let alertController = textAlertController(context: self.context, updatedPresentationData: controller.updatedPresentationData, title: self.presentationData.strings.Conversation_ShareBotContactConfirmationTitle, text: self.presentationData.strings.Conversation_ShareBotContactConfirmation, actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_Cancel, action: { + sendEvent(false) + }), TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: { [weak self] in + guard let self, case let .user(user) = accountPeer, let phone = user.phone, !phone.isEmpty else { + return + } + + let sendMessageSignal = enqueueMessages(account: self.context.account, peerId: botId, messages: [ + .message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: TelegramMediaContact(firstName: user.firstName ?? "", lastName: user.lastName ?? "", phoneNumber: phone, peerId: user.id, vCardData: nil)), replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: []) + ]) + |> mapToSignal { messageIds in + if let maybeMessageId = messageIds.first, let messageId = maybeMessageId { + return context.account.pendingMessageManager.pendingMessageStatus(messageId) + |> mapToSignal { status, _ -> Signal in + if status != nil { + return .never() + } else { + return .single(true) + } + } + |> take(1) + } else { + return .complete() + } + } + + let sendMessage = { + let _ = sendMessageSignal.start(completed: { + sendEvent(true) + }) + } + + if requiresUnblock { + let _ = (context.engine.privacy.requestUpdatePeerIsBlocked(peerId: botId, isBlocked: false) + |> deliverOnMainQueue).start(completed: { + sendMessage() + }) + } else { + sendMessage() + } + })]) + alertController.dismissed = { byOutsideTap in + if byOutsideTap { + sendEvent(false) + } + } + controller.present(alertController, in: .window(.root)) + }) } fileprivate func invokeCustomMethod(requestId: String, method: String, params: String) { From eb3da67032f20bb8e8667b79227efa079d22f8da Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Fri, 1 Sep 2023 13:38:18 +0400 Subject: [PATCH 09/13] Web app improvements --- submodules/WebUI/Sources/WebAppController.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/submodules/WebUI/Sources/WebAppController.swift b/submodules/WebUI/Sources/WebAppController.swift index 13eb3fef98..43b342fd06 100644 --- a/submodules/WebUI/Sources/WebAppController.swift +++ b/submodules/WebUI/Sources/WebAppController.swift @@ -1176,7 +1176,8 @@ public final class WebAppController: ViewController, AttachmentContainable { } let sendMessage = { - let _ = sendMessageSignal.start(completed: { + let _ = (sendMessageSignal + |> deliverOnMainQueue).start(completed: { sendEvent(true) }) } From f432842f75fbd72256d7b9c8f7398ef2398703a4 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Fri, 1 Sep 2023 14:31:51 +0400 Subject: [PATCH 10/13] Web app improvements --- .../Telegram-iOS/en.lproj/Localizable.strings | 8 ++++++++ .../Sources/ApiUtils/TelegramMediaAction.swift | 15 +++++++++------ .../SyncCore/SyncCore_TelegramMediaAction.swift | 10 +++++++--- .../Sources/ServiceMessageStrings.swift | 12 ++++++++++-- .../TelegramUI/Sources/ChatController.swift | 2 +- submodules/WebUI/Sources/WebAppController.swift | 11 ++++++----- 6 files changed, 41 insertions(+), 17 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 2a92f3ebe8..bcb8eb3d62 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -9863,6 +9863,8 @@ Sorry for the inconvenience."; "Story.ViewList.ViewerCount_any" = "%d Viewers"; "AuthSessions.MessageApp" = "You allowed this bot to message you when you opened %@."; +"Notification.BotWriteAllowedMenu" = "You allowed this bot to message you when you added it to your attachment menu."; +"Notification.BotWriteAllowedRequest" = "You allowed this bot to message you in the app."; "Story.Privacy.PostStoryAs" = "Post Story As"; "Story.Privacy.PostStoryAsHeader" = "POST STORY AS"; @@ -9892,3 +9894,9 @@ Sorry for the inconvenience."; "MediaPicker.Timer.Video.ViewOnceTooltip" = "Video set to view once."; "MediaPicker.Timer.Video.TimerTooltip" = "Video will be deleted in\n%@ seconds after opening."; "MediaPicker.Timer.Video.KeepTooltip" = "Video will be kept in chat."; + +"WebApp.AllowWriteTitle" = "Allow Sending Messages?"; +"WebApp.AllowWriteConfirmation" = "This will allow the bot **%@** to message you on Telegram."; + +"WebApp.SharePhoneTitle" = "Share Phone Number?"; +"WebApp.SharePhoneConfirmation" = "**%@** will know your phone number. This can be useful for integration with other services."; diff --git a/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift b/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift index eeaff7396f..997719f0cd 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift @@ -53,16 +53,19 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe case let .messageActionBotAllowed(flags, domain, app): if let domain = domain { return TelegramMediaAction(action: .botDomainAccessGranted(domain: domain)) - } else if case let .botApp(_, _, _, _, appName, _, _, _, _) = app { + } else { + var appName: String? + if case let .botApp(_, _, _, _, appNameValue, _, _, _, _) = app { + appName = appNameValue + } var type: BotSendMessageAccessGrantedType? - if (flags & (1 << 3)) != 0 { - type = .request - } else if (flags & (1 << 1)) != 0 { + if (flags & (1 << 1)) != 0 { type = .attachMenu } + if (flags & (1 << 3)) != 0 { + type = .request + } return TelegramMediaAction(action: .botAppAccessGranted(appName: appName, type: type)) - } else { - return nil } case .messageActionSecureValuesSentMe: return nil diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift index 829908f62b..9e1cf940a1 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift @@ -91,7 +91,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { case paymentSent(currency: String, totalAmount: Int64, invoiceSlug: String?, isRecurringInit: Bool, isRecurringUsed: Bool) case customText(text: String, entities: [MessageTextEntity]) case botDomainAccessGranted(domain: String) - case botAppAccessGranted(appName: String, type: BotSendMessageAccessGrantedType?) + case botAppAccessGranted(appName: String?, type: BotSendMessageAccessGrantedType?) case botSentSecureValues(types: [SentSecureValueType]) case peerJoined case phoneNumberRequest @@ -202,7 +202,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { self = .unknown } case 35: - self = .botAppAccessGranted(appName: decoder.decodeStringForKey("app", orElse: ""), type: decoder.decodeOptionalInt32ForKey("atp").flatMap { BotSendMessageAccessGrantedType(rawValue: $0) }) + self = .botAppAccessGranted(appName: decoder.decodeOptionalStringForKey("app"), type: decoder.decodeOptionalInt32ForKey("atp").flatMap { BotSendMessageAccessGrantedType(rawValue: $0) }) default: self = .unknown } @@ -372,7 +372,11 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { encoder.encode(TelegramWallpaperNativeCodable(wallpaper), forKey: "wallpaper") case let .botAppAccessGranted(appName, type): encoder.encodeInt32(35, forKey: "_rawValue") - encoder.encodeString(appName, forKey: "app") + if let appName = appName { + encoder.encodeString(appName, forKey: "app") + } else { + encoder.encodeNil(forKey: "app") + } if let type = type { encoder.encodeInt32(type.rawValue, forKey: "atp") } else { diff --git a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift index 1159187c22..0998610d15 100644 --- a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift +++ b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift @@ -629,8 +629,16 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, attributedString = stringWithAppliedEntities(text, entities: entities, baseColor: primaryTextColor, linkColor: primaryTextColor, baseFont: titleFont, linkFont: titleBoldFont, boldFont: titleBoldFont, italicFont: titleFont, boldItalicFont: titleBoldFont, fixedFont: titleFont, blockQuoteFont: titleFont, underlineLinks: false, message: message._asMessage()) case let .botDomainAccessGranted(domain): attributedString = NSAttributedString(string: strings.AuthSessions_Message(domain).string, font: titleFont, textColor: primaryTextColor) - case let .botAppAccessGranted(appName, _): - attributedString = NSAttributedString(string: strings.AuthSessions_MessageApp(appName).string, font: titleFont, textColor: primaryTextColor) + case let .botAppAccessGranted(appName, type): + let text: String + if type == .attachMenu { + text = strings.Notification_BotWriteAllowedMenu + } else if type == .request { + text = strings.Notification_BotWriteAllowedRequest + } else { + text = strings.AuthSessions_MessageApp(appName ?? "").string + } + attributedString = NSAttributedString(string: text, font: titleFont, textColor: primaryTextColor) case let .botSentSecureValues(types): var typesString = "" var hasIdentity = false diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index e38ad40d13..638d95be18 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -13213,7 +13213,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } let buttons: Signal<([AttachmentButtonType], [AttachmentButtonType], AttachmentButtonType?), NoError> - if !isScheduledMessages { + if !isScheduledMessages && !peer.isDeleted { buttons = self.context.engine.messages.attachMenuBots() |> map { attachMenuBots in var buttons = availableButtons diff --git a/submodules/WebUI/Sources/WebAppController.swift b/submodules/WebUI/Sources/WebAppController.swift index 43b342fd06..6541cc3de7 100644 --- a/submodules/WebUI/Sources/WebAppController.swift +++ b/submodules/WebUI/Sources/WebAppController.swift @@ -1104,7 +1104,7 @@ public final class WebAppController: ViewController, AttachmentContainable { if result { sendEvent(true) } else { - controller.present(textAlertController(context: self.context, updatedPresentationData: controller.updatedPresentationData, title: "Allow Sending Messages?", text: "Allow \(controller.botName) to send messages?", actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_Cancel, action: { + controller.present(textAlertController(context: self.context, updatedPresentationData: controller.updatedPresentationData, title: self.presentationData.strings.WebApp_AllowWriteTitle, text: self.presentationData.strings.WebApp_AllowWriteConfirmation(controller.botName).string, actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_Cancel, action: { sendEvent(false) }), TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: { [weak self] in guard let self else { @@ -1115,16 +1115,17 @@ public final class WebAppController: ViewController, AttachmentContainable { |> deliverOnMainQueue).start(completed: { sendEvent(true) }) - })]), in: .window(.root)) + })], parseMarkdown: true), in: .window(.root)) } }) } fileprivate func shareAccountContact() { - guard let controller = self.controller, let botId = self.controller?.botId else { + guard let controller = self.controller, let botId = self.controller?.botId, let botName = self.controller?.botName else { return } + let sendEvent: (Bool) -> Void = { success in var paramsString: String if success { @@ -1149,7 +1150,7 @@ public final class WebAppController: ViewController, AttachmentContainable { requiresUnblock = true } - let alertController = textAlertController(context: self.context, updatedPresentationData: controller.updatedPresentationData, title: self.presentationData.strings.Conversation_ShareBotContactConfirmationTitle, text: self.presentationData.strings.Conversation_ShareBotContactConfirmation, actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_Cancel, action: { + let alertController = textAlertController(context: self.context, updatedPresentationData: controller.updatedPresentationData, title: self.presentationData.strings.WebApp_SharePhoneTitle, text: self.presentationData.strings.WebApp_SharePhoneConfirmation(botName).string, actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_Cancel, action: { sendEvent(false) }), TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: { [weak self] in guard let self, case let .user(user) = accountPeer, let phone = user.phone, !phone.isEmpty else { @@ -1190,7 +1191,7 @@ public final class WebAppController: ViewController, AttachmentContainable { } else { sendMessage() } - })]) + })], parseMarkdown: true) alertController.dismissed = { byOutsideTap in if byOutsideTap { sendEvent(false) From ea52d6e3699fb46b94384f73be03cfa22006b392 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Fri, 1 Sep 2023 15:01:36 +0400 Subject: [PATCH 11/13] Web app improvements --- Telegram/Telegram-iOS/en.lproj/Localizable.strings | 1 + submodules/WebUI/Sources/WebAppController.swift | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index bcb8eb3d62..6138ce1437 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -9900,3 +9900,4 @@ Sorry for the inconvenience."; "WebApp.SharePhoneTitle" = "Share Phone Number?"; "WebApp.SharePhoneConfirmation" = "**%@** will know your phone number. This can be useful for integration with other services."; +"WebApp.SharePhoneConfirmationUnblock" = "**%@** will know your phone number. This can be useful for integration with other services.\n\nThe bot will also be unblocked."; diff --git a/submodules/WebUI/Sources/WebAppController.swift b/submodules/WebUI/Sources/WebAppController.swift index 6541cc3de7..90074e202e 100644 --- a/submodules/WebUI/Sources/WebAppController.swift +++ b/submodules/WebUI/Sources/WebAppController.swift @@ -1150,7 +1150,14 @@ public final class WebAppController: ViewController, AttachmentContainable { requiresUnblock = true } - let alertController = textAlertController(context: self.context, updatedPresentationData: controller.updatedPresentationData, title: self.presentationData.strings.WebApp_SharePhoneTitle, text: self.presentationData.strings.WebApp_SharePhoneConfirmation(botName).string, actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_Cancel, action: { + let text: String + if requiresUnblock { + text = self.presentationData.strings.WebApp_SharePhoneConfirmationUnblock(botName).string + } else { + text = self.presentationData.strings.WebApp_SharePhoneConfirmation(botName).string + } + + let alertController = textAlertController(context: self.context, updatedPresentationData: controller.updatedPresentationData, title: self.presentationData.strings.WebApp_SharePhoneTitle, text: text, actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_Cancel, action: { sendEvent(false) }), TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: { [weak self] in guard let self, case let .user(user) = accountPeer, let phone = user.phone, !phone.isEmpty else { From f850481b2f35879c494dfca09704f0ee2ef85dc6 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Fri, 1 Sep 2023 15:24:59 +0400 Subject: [PATCH 12/13] Web app improvements --- Telegram/Telegram-iOS/en.lproj/Localizable.strings | 2 +- submodules/WebUI/Sources/WebAppController.swift | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 6138ce1437..b8b3f1f7f8 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -9900,4 +9900,4 @@ Sorry for the inconvenience."; "WebApp.SharePhoneTitle" = "Share Phone Number?"; "WebApp.SharePhoneConfirmation" = "**%@** will know your phone number. This can be useful for integration with other services."; -"WebApp.SharePhoneConfirmationUnblock" = "**%@** will know your phone number. This can be useful for integration with other services.\n\nThe bot will also be unblocked."; +"WebApp.SharePhoneConfirmationUnblock" = "**%@** will know your phone number. This can be useful for integration with other services.\n\nThis will also unblock the bot."; diff --git a/submodules/WebUI/Sources/WebAppController.swift b/submodules/WebUI/Sources/WebAppController.swift index 90074e202e..78a0d927f6 100644 --- a/submodules/WebUI/Sources/WebAppController.swift +++ b/submodules/WebUI/Sources/WebAppController.swift @@ -1104,7 +1104,7 @@ public final class WebAppController: ViewController, AttachmentContainable { if result { sendEvent(true) } else { - controller.present(textAlertController(context: self.context, updatedPresentationData: controller.updatedPresentationData, title: self.presentationData.strings.WebApp_AllowWriteTitle, text: self.presentationData.strings.WebApp_AllowWriteConfirmation(controller.botName).string, actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_Cancel, action: { + let alertController = textAlertController(context: self.context, updatedPresentationData: controller.updatedPresentationData, title: self.presentationData.strings.WebApp_AllowWriteTitle, text: self.presentationData.strings.WebApp_AllowWriteConfirmation(controller.botName).string, actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_Cancel, action: { sendEvent(false) }), TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: { [weak self] in guard let self else { @@ -1115,7 +1115,13 @@ public final class WebAppController: ViewController, AttachmentContainable { |> deliverOnMainQueue).start(completed: { sendEvent(true) }) - })], parseMarkdown: true), in: .window(.root)) + })], parseMarkdown: true) + alertController.dismissed = { byOutsideTap in + if byOutsideTap { + sendEvent(false) + } + } + controller.present(alertController, in: .window(.root)) } }) } From 650ec2296eb46a54ab01fa0241e75a7a5f3a5ddc Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Fri, 1 Sep 2023 15:45:46 +0400 Subject: [PATCH 13/13] Fix initial caption setup --- .../Sources/LegacyMessageInputPanel.swift | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/submodules/TelegramUI/Components/LegacyMessageInputPanel/Sources/LegacyMessageInputPanel.swift b/submodules/TelegramUI/Components/LegacyMessageInputPanel/Sources/LegacyMessageInputPanel.swift index 3e4435f83c..bacea16101 100644 --- a/submodules/TelegramUI/Components/LegacyMessageInputPanel/Sources/LegacyMessageInputPanel.swift +++ b/submodules/TelegramUI/Components/LegacyMessageInputPanel/Sources/LegacyMessageInputPanel.swift @@ -69,9 +69,13 @@ public class LegacyMessageInputPanelNode: ASDisplayNode, TGCaptionPanelView { } } + private var scheduledMessageInput: MessageInputPanelComponent.SendMessageInput? public func setCaption(_ caption: NSAttributedString?) { + let sendMessageInput = MessageInputPanelComponent.SendMessageInput.text(caption ?? NSAttributedString()) if let view = self.inputPanel.view as? MessageInputPanelComponent.View { - view.setSendMessageInput(value: .text(caption ?? NSAttributedString()), updateState: true) + view.setSendMessageInput(value: sendMessageInput, updateState: true) + } else { + self.scheduledMessageInput = sendMessageInput } } @@ -143,6 +147,12 @@ public class LegacyMessageInputPanelNode: ASDisplayNode, TGCaptionPanelView { maxInputPanelHeight = 60.0 } + var resetInputContents: MessageInputPanelComponent.SendMessageInput? + if let scheduledMessageInput = self.scheduledMessageInput { + resetInputContents = scheduledMessageInput + self.scheduledMessageInput = nil + } + self.inputPanel.parentState = self.state let inputPanelSize = self.inputPanel.update( transition: Transition(transition), @@ -157,7 +167,7 @@ public class LegacyMessageInputPanelNode: ASDisplayNode, TGCaptionPanelView { maxLength: 1024, queryTypes: [.mention], alwaysDarkWhenHasText: false, - resetInputContents: nil, + resetInputContents: resetInputContents, nextInputMode: { _ in return .emoji },