From f918ce716e7e60be0eb50444bb62f9c7596e57e4 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Fri, 30 Jun 2023 12:39:09 +0200 Subject: [PATCH] Various improvements --- .../PendingMessageUploadedContent.swift | 2 +- .../TelegramEngine/Messages/Stories.swift | 13 +- .../Sources/VideoTextureSource.swift | 9 +- .../Sources/MediaEditorScreen.swift | 43 +++--- .../Sources/ShareWithPeersScreen.swift | 140 ++++++++++++++++-- 5 files changed, 171 insertions(+), 36 deletions(-) diff --git a/submodules/TelegramCore/Sources/PendingMessages/PendingMessageUploadedContent.swift b/submodules/TelegramCore/Sources/PendingMessages/PendingMessageUploadedContent.swift index 855e7ec65e..37b9aa36ad 100644 --- a/submodules/TelegramCore/Sources/PendingMessages/PendingMessageUploadedContent.swift +++ b/submodules/TelegramCore/Sources/PendingMessages/PendingMessageUploadedContent.swift @@ -96,7 +96,7 @@ func messageContentToUpload(accountPeerId: PeerId, network: Network, postbox: Po return .content(PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaStory(userId: inputUser, id: media.storyId.id), ""), reuploadInfo: nil, cacheReferenceKey: nil)) } |> castError(PendingMessageUploadError.self), .text) - } else if let media = media.first, let mediaResult = mediaContentToUpload(accountPeerId: accountPeerId, network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, revalidationContext: revalidationContext, forceReupload: forceReupload, isGrouped: isGrouped, passFetchProgress: false, peerId: peerId, media: media, text: text, autoremoveMessageAttribute: autoremoveMessageAttribute, autoclearMessageAttribute: autoclearMessageAttribute, messageId: messageId, attributes: attributes) { + } else if let media = media.first, let mediaResult = mediaContentToUpload(accountPeerId: accountPeerId, network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, revalidationContext: revalidationContext, forceReupload: forceReupload, isGrouped: isGrouped, passFetchProgress: passFetchProgress, peerId: peerId, media: media, text: text, autoremoveMessageAttribute: autoremoveMessageAttribute, autoclearMessageAttribute: autoclearMessageAttribute, messageId: messageId, attributes: attributes) { return .signal(mediaResult, .media) } else { return .signal(.single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .text(text), reuploadInfo: nil, cacheReferenceKey: nil))), .text) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift index fd85989db6..2fcb4eff13 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift @@ -607,7 +607,7 @@ private func prepareUploadStoryContent(account: Account, media: EngineStoryInput } } -private func uploadedStoryContent(postbox: Postbox, network: Network, media: Media, accountPeerId: PeerId, messageMediaPreuploadManager: MessageMediaPreuploadManager, revalidationContext: MediaReferenceRevalidationContext, auxiliaryMethods: AccountAuxiliaryMethods) -> (signal: Signal, media: Media) { +private func uploadedStoryContent(postbox: Postbox, network: Network, media: Media, accountPeerId: PeerId, messageMediaPreuploadManager: MessageMediaPreuploadManager, revalidationContext: MediaReferenceRevalidationContext, auxiliaryMethods: AccountAuxiliaryMethods, passFetchProgress: Bool) -> (signal: Signal, media: Media) { let originalMedia: Media = media let contentToUpload: MessageContentToUpload @@ -621,7 +621,7 @@ private func uploadedStoryContent(postbox: Postbox, network: Network, media: Med revalidationContext: revalidationContext, forceReupload: true, isGrouped: false, - passFetchProgress: false, + passFetchProgress: passFetchProgress, peerId: accountPeerId, messageId: nil, attributes: [], @@ -758,7 +758,8 @@ private func _internal_putPendingStoryIdMapping(accountPeerId: PeerId, stableId: } func _internal_uploadStoryImpl(postbox: Postbox, network: Network, accountPeerId: PeerId, stateManager: AccountStateManager, messageMediaPreuploadManager: MessageMediaPreuploadManager, revalidationContext: MediaReferenceRevalidationContext, auxiliaryMethods: AccountAuxiliaryMethods, stableId: Int32, media: Media, text: String, entities: [MessageTextEntity], pin: Bool, privacy: EngineStoryPrivacy, isForwardingDisabled: Bool, period: Int, randomId: Int64) -> Signal { - let (contentSignal, originalMedia) = uploadedStoryContent(postbox: postbox, network: network, media: media, accountPeerId: accountPeerId, messageMediaPreuploadManager: messageMediaPreuploadManager, revalidationContext: revalidationContext, auxiliaryMethods: auxiliaryMethods) + let passFetchProgress = media is TelegramMediaFile + let (contentSignal, originalMedia) = uploadedStoryContent(postbox: postbox, network: network, media: media, accountPeerId: accountPeerId, messageMediaPreuploadManager: messageMediaPreuploadManager, revalidationContext: revalidationContext, auxiliaryMethods: auxiliaryMethods, passFetchProgress: passFetchProgress) return contentSignal |> mapToSignal { result -> Signal in switch result { @@ -896,7 +897,11 @@ func _internal_editStory(account: Account, media: EngineStoryInputMedia?, id: In let contentSignal: Signal let originalMedia: Media? if let media = media { - (contentSignal, originalMedia) = uploadedStoryContent(postbox: account.postbox, network: account.network, media: prepareUploadStoryContent(account: account, media: media), accountPeerId: account.peerId, messageMediaPreuploadManager: account.messageMediaPreuploadManager, revalidationContext: account.mediaReferenceRevalidationContext, auxiliaryMethods: account.auxiliaryMethods) + var passFetchProgress = false + if case .video = media { + passFetchProgress = true + } + (contentSignal, originalMedia) = uploadedStoryContent(postbox: account.postbox, network: account.network, media: prepareUploadStoryContent(account: account, media: media), accountPeerId: account.peerId, messageMediaPreuploadManager: account.messageMediaPreuploadManager, revalidationContext: account.mediaReferenceRevalidationContext, auxiliaryMethods: account.auxiliaryMethods, passFetchProgress: passFetchProgress) } else { contentSignal = .single(nil) originalMedia = nil diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/VideoTextureSource.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/VideoTextureSource.swift index fb017e0585..480a78a79b 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/VideoTextureSource.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/VideoTextureSource.swift @@ -641,9 +641,13 @@ final class VideoInputScalePass: RenderPass { } func process(input: MTLTexture, secondInput: MTLTexture?, timestamp: CMTime, device: MTLDevice, commandBuffer: MTLCommandBuffer) -> MTLTexture? { +#if targetEnvironment(simulator) + +#else guard max(input.width, input.height) > 1920 || secondInput != nil else { return input } +#endif let scaledSize = CGSize(width: input.width, height: input.height).fitted(CGSize(width: 1920.0, height: 1920.0)) let width: Int @@ -691,8 +695,11 @@ final class VideoInputScalePass: RenderPass { renderCommandEncoder.setRenderPipelineState(self.mainPipelineState!) +#if targetEnvironment(simulator) + let secondInput = input +#endif + let (mainVideoState, additionalVideoState, transitionVideoState) = self.transitionState(for: timestamp, mainInput: input, additionalInput: secondInput) - if let transitionVideoState { self.encodeVideo( diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index 923a4da0b6..2607702552 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -28,6 +28,7 @@ import CameraButtonComponent import UndoUI import ChatEntityKeyboardInputNode import ChatPresentationInterfaceState +import TextFormat enum DrawingScreenType { case drawing @@ -1862,25 +1863,25 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } } } -//#if DEBUG -// if case let .asset(asset) = subject, asset.mediaType == .video { -// let videoEntity = DrawingStickerEntity(content: .dualVideoReference) -// videoEntity.referenceDrawingSize = storyDimensions -// videoEntity.scale = 1.49 -// videoEntity.position = PIPPosition.bottomRight.getPosition(storyDimensions) -// self.entitiesView.add(videoEntity, announce: false) -// -// mediaEditor.setAdditionalVideo("", positionChanges: [VideoPositionChange(additional: false, timestamp: 0.0), VideoPositionChange(additional: true, timestamp: 3.0)]) -// mediaEditor.setAdditionalVideoPosition(videoEntity.position, scale: videoEntity.scale, rotation: videoEntity.rotation) -// if let entityView = self.entitiesView.getView(for: videoEntity.uuid) as? DrawingStickerEntityView { -// entityView.updated = { [weak videoEntity, weak self] in -// if let self, let videoEntity { -// self.mediaEditor?.setAdditionalVideoPosition(videoEntity.position, scale: videoEntity.scale, rotation: videoEntity.rotation) -// } -// } -// } -// } -//#endif +#if targetEnvironment(simulator) + if case let .asset(asset) = subject, asset.mediaType == .video { + let videoEntity = DrawingStickerEntity(content: .dualVideoReference) + videoEntity.referenceDrawingSize = storyDimensions + videoEntity.scale = 1.49 + videoEntity.position = PIPPosition.bottomRight.getPosition(storyDimensions) + self.entitiesView.add(videoEntity, announce: false) + + mediaEditor.setAdditionalVideo("", positionChanges: [VideoPositionChange(additional: false, timestamp: 0.0), VideoPositionChange(additional: true, timestamp: 3.0)]) + mediaEditor.setAdditionalVideoPosition(videoEntity.position, scale: videoEntity.scale, rotation: videoEntity.rotation) + if let entityView = self.entitiesView.getView(for: videoEntity.uuid) as? DrawingStickerEntityView { + entityView.updated = { [weak videoEntity, weak self] in + if let self, let videoEntity { + self.mediaEditor?.setAdditionalVideoPosition(videoEntity.position, scale: videoEntity.scale, rotation: videoEntity.rotation) + } + } + } + } +#endif self.gradientColorsDisposable = mediaEditor.gradientColors.start(next: { [weak self] colors in if let self, let colors { @@ -3103,6 +3104,9 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate let privacy = privacy ?? self.state.privacy + let text = self.getCaption().string + let mentions = generateTextEntities(text, enabledTypes: [.mention], currentEntities: []).map { (text as NSString).substring(with: NSRange(location: $0.range.lowerBound + 1, length: $0.range.upperBound - $0.range.lowerBound - 1)) } + let stateContext = ShareWithPeersScreen.StateContext(context: self.context, subject: .stories(editing: false), initialPeerIds: Set(privacy.privacy.additionallyIncludePeers)) let _ = (stateContext.ready |> filter { $0 } |> take(1) |> deliverOnMainQueue).start(next: { [weak self] _ in guard let self else { @@ -3117,6 +3121,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate allowScreenshots: !privacy.isForwardingDisabled, pin: privacy.pin, timeout: privacy.timeout, + mentions: mentions, stateContext: stateContext, completion: { [weak self] privacy, allowScreenshots, pin in guard let self else { diff --git a/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift b/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift index 33ee596e91..2735fc2978 100644 --- a/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift +++ b/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift @@ -9,6 +9,7 @@ import ComponentDisplayAdapters import TelegramPresentationData import AccountContext import TelegramCore +import Postbox import MultilineTextComponent import SolidRoundedButtonComponent import PresentationDataUtils @@ -31,6 +32,7 @@ final class ShareWithPeersScreenComponent: Component { let screenshot: Bool let pin: Bool let timeout: Int + let mentions: [String] let categoryItems: [CategoryItem] let optionItems: [OptionItem] let completion: (EngineStoryPrivacy, Bool, Bool) -> Void @@ -43,6 +45,7 @@ final class ShareWithPeersScreenComponent: Component { screenshot: Bool, pin: Bool, timeout: Int, + mentions: [String], categoryItems: [CategoryItem], optionItems: [OptionItem], completion: @escaping (EngineStoryPrivacy, Bool, Bool) -> Void, @@ -54,6 +57,7 @@ final class ShareWithPeersScreenComponent: Component { self.screenshot = screenshot self.pin = pin self.timeout = timeout + self.mentions = mentions self.categoryItems = categoryItems self.optionItems = optionItems self.completion = completion @@ -79,6 +83,9 @@ final class ShareWithPeersScreenComponent: Component { if lhs.timeout != rhs.timeout { return false } + if lhs.mentions != rhs.mentions { + return false + } if lhs.categoryItems != rhs.categoryItems { return false } @@ -1508,7 +1515,7 @@ final class ShareWithPeersScreenComponent: Component { guard let self, let component = self.component, let controller = self.environment?.controller() as? ShareWithPeersScreen else { return } - + let base: EngineStoryPrivacy.Base if self.selectedCategories.contains(.everyone) { base = .everyone @@ -1522,17 +1529,126 @@ final class ShareWithPeersScreenComponent: Component { base = .nobody } - component.completion( - EngineStoryPrivacy( - base: base, - additionallyIncludePeers: self.selectedPeers - ), - self.selectedOptions.contains(.screenshot), - self.selectedOptions.contains(.pin) - ) + let proceed = { + component.completion( + EngineStoryPrivacy( + base: base, + additionallyIncludePeers: self.selectedPeers + ), + self.selectedOptions.contains(.screenshot), + self.selectedOptions.contains(.pin) + ) - controller.dismissAllTooltips() - controller.dismiss() + controller.dismissAllTooltips() + controller.dismiss() + } + + let presentAlert: ([String]) -> Void = { usernames in + let usernamesString = String(usernames.map { "@\($0)" }.joined(separator: ", ")) + let alertController = textAlertController( + context: component.context, + forceTheme: defaultDarkColorPresentationTheme, + title: "Privacy Restrictions", + text: "The privacy settings of your story will prevent some users you tagged (\( usernamesString )) from viewing it.", + actions: [ + TextAlertAction(type: .defaultAction, title: "Proceed Anyway", action: { + proceed() + }), + TextAlertAction(type: .genericAction, title: "Cancel", action: {}) + ], + actionLayout: .vertical + ) + controller.present(alertController, in: .window(.root)) + } + + func matchingUsername(user: TelegramUser, usernames: Set) -> String? { + for username in user.usernames { + if usernames.contains(username.username) { + return username.username + } + } + if let username = user.username { + if usernames.contains(username) { + return username + } + } + return nil + } + + let context = component.context + let selectedPeerIds = self.selectedPeers + + if case .stories = component.stateContext.subject { + if component.mentions.isEmpty { + proceed() + } else if case .nobody = base { + if selectedPeerIds.isEmpty { + presentAlert(component.mentions) + } else { + let _ = (context.account.postbox.transaction { transaction in + var filteredMentions = Set(component.mentions) + for peerId in selectedPeerIds { + if let user = transaction.getPeer(peerId) as? TelegramUser, let username = matchingUsername(user: user, usernames: filteredMentions) { + filteredMentions.remove(username) + } + } + return Array(filteredMentions) + } + |> deliverOnMainQueue).start(next: { mentions in + if mentions.isEmpty { + proceed() + } else { + presentAlert(mentions) + } + }) + } + } else if case .contacts = base { + let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Contacts.List(includePresences: false)) + |> map { contacts -> [String] in + var filteredMentions = Set(component.mentions) + let peers = contacts.peers + for peer in peers { + if selectedPeerIds.contains(peer.id) { + continue + } + if case let .user(user) = peer, let username = matchingUsername(user: user, usernames: filteredMentions) { + filteredMentions.remove(username) + } + } + return Array(filteredMentions) + } + |> deliverOnMainQueue).start(next: { mentions in + if mentions.isEmpty { + proceed() + } else { + presentAlert(mentions) + } + }) + } else if case .closeFriends = base { + let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Contacts.List(includePresences: false)) + |> map { contacts -> [String] in + var filteredMentions = Set(component.mentions) + let peers = contacts.peers + for peer in peers { + if case let .user(user) = peer, user.flags.contains(.isCloseFriend), let username = matchingUsername(user: user, usernames: filteredMentions) { + filteredMentions.remove(username) + } + } + return Array(filteredMentions) + } + |> deliverOnMainQueue).start(next: { mentions in + if mentions.isEmpty { + proceed() + } else { + presentAlert(mentions) + } + }) + } else { + proceed() + } + } else { + proceed() + } } )), environment: {}, @@ -1821,6 +1937,7 @@ public class ShareWithPeersScreen: ViewControllerComponentContainer { allowScreenshots: Bool = true, pin: Bool = false, timeout: Int = 0, + mentions: [String] = [], stateContext: StateContext, completion: @escaping (EngineStoryPrivacy, Bool, Bool) -> Void, editCategory: @escaping (EngineStoryPrivacy, Bool, Bool) -> Void @@ -1921,6 +2038,7 @@ public class ShareWithPeersScreen: ViewControllerComponentContainer { screenshot: allowScreenshots, pin: pin, timeout: timeout, + mentions: mentions, categoryItems: categoryItems, optionItems: optionItems, completion: completion,