From 89cdac66e1028601814b355a8898f5c57243941e Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 21 Jun 2023 20:43:39 +0400 Subject: [PATCH] Stories privacy improvements --- .../SyncCore/SyncCore_TelegramUser.swift | 2 +- .../TelegramEngine/Messages/Stories.swift | 6 +- .../Sources/MediaEditorDraft.swift | 50 +- .../Sources/MediaEditorScreen.swift | 430 ++++++------------ .../Sources/ShareWithPeersScreen.swift | 42 +- .../StoryItemSetContainerComponent.swift | 4 +- .../Sources/TelegramRootController.swift | 134 +----- 7 files changed, 209 insertions(+), 459 deletions(-) diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramUser.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramUser.swift index 6a45326dec..bb7cff414e 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramUser.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramUser.swift @@ -366,7 +366,7 @@ public final class TelegramUser: Peer, Equatable { } public func withUpdatedFlags(_ flags: UserInfoFlags) -> TelegramUser { - return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: self.username, phone: self.phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: emojiStatus, usernames: self.usernames, storiesHidden: self.storiesHidden) + return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: self.username, phone: self.phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: flags, emojiStatus: self.emojiStatus, usernames: self.usernames, storiesHidden: self.storiesHidden) } public func withUpdatedStoriesHidden(_ storiesHidden: Bool?) -> TelegramUser { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift index bd0a6ac903..155aa5352f 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift @@ -625,7 +625,11 @@ private func apiInputPrivacyRules(privacy: EngineStoryPrivacy, transaction: Tran } } if !privacyUsers.isEmpty { - privacyRules.append(.inputPrivacyValueAllowUsers(users: privacyUsers)) + if case .contacts = privacy.base { + privacyRules.append(.inputPrivacyValueDisallowUsers(users: privacyUsers)) + } else { + privacyRules.append(.inputPrivacyValueAllowUsers(users: privacyUsers)) + } } if !privacyChats.isEmpty { privacyRules.append(.inputPrivacyValueAllowChatParticipants(chats: privacyChats)) diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorDraft.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorDraft.swift index 51a60b055d..02353f9bc9 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorDraft.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorDraft.swift @@ -7,10 +7,7 @@ import PersistentStringHash import Postbox import AccountContext -public enum MediaEditorResultPrivacy: Codable, Equatable { - case story(privacy: EngineStoryPrivacy, timeout: Int, archive: Bool) - case message(peers: [EnginePeer.Id], timeout: Int?) - +public struct MediaEditorResultPrivacy: Codable, Equatable { private enum CodingKeys: String, CodingKey { case type case privacy @@ -19,37 +16,34 @@ public enum MediaEditorResultPrivacy: Codable, Equatable { case archive } + public let privacy: EngineStoryPrivacy + public let timeout: Int + public let archive: Bool + + public init( + privacy: EngineStoryPrivacy, + timeout: Int, + archive: Bool + ) { + self.privacy = privacy + self.timeout = timeout + self.archive = archive + } + public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - if let privacy = try container.decodeIfPresent(EngineStoryPrivacy.self, forKey: .privacy) { - let timeout = try container.decode(Int32.self, forKey: .timeout) - let archive = try container.decode(Bool.self, forKey: .archive) - self = .story(privacy: privacy, timeout: Int(timeout), archive: archive) - } else if let peers = try container.decodeIfPresent([EnginePeer.Id].self, forKey: .peers) { - let timeout = try container.decodeIfPresent(Int32.self, forKey: .timeout) - self = .message(peers: peers, timeout: timeout.flatMap { Int($0) }) - } else { - fatalError() - } + self.privacy = try container.decode(EngineStoryPrivacy.self, forKey: .privacy) + self.timeout = Int(try container.decode(Int32.self, forKey: .timeout)) + self.archive = try container.decode(Bool.self, forKey: .archive) } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) - - switch self { - case let .story(privacy, timeout, archive): - try container.encode(privacy, forKey: .privacy) - try container.encode(Int32(timeout), forKey: .timeout) - try container.encode(archive, forKey: .archive) - case let .message(peers, timeout): - try container.encode(peers, forKey: .peers) - if let timeout { - try container.encode(Int32(timeout), forKey: .timeout) - } else { - try container.encodeNil(forKey: .timeout) - } - } + + try container.encode(privacy, forKey: .privacy) + try container.encode(Int32(timeout), forKey: .timeout) + try container.encode(archive, forKey: .archive) } } diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index 6cb711ed18..963c6cf81a 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -675,20 +675,11 @@ final class MediaEditorScreenComponent: Component { image: state.image(.done), size: CGSize(width: 33.0, height: 33.0) )), - action: { [weak self] in - guard let self, let controller = environment.controller() as? MediaEditorScreen else { + action: { + guard let controller = environment.controller() as? MediaEditorScreen else { return } - guard let inputPanelView = self.inputPanel.view as? MessageInputPanelComponent.View else { - return - } - var inputText = NSAttributedString(string: "") - switch inputPanelView.getSendMessageInput() { - case let .text(text): - inputText = text - } - - controller.requestCompletion(caption: inputText, animated: true) + controller.requestCompletion(animated: true) } )), environment: {}, @@ -879,29 +870,23 @@ final class MediaEditorScreenComponent: Component { var timeoutValue: String let timeoutSelected: Bool - switch component.privacy { - case let .story(_, timeout, archive): - switch timeout { - case 21600: - timeoutValue = "6" - case 43200: - timeoutValue = "12" - case 86400: - timeoutValue = "24" - case 172800: - timeoutValue = "48" - default: - timeoutValue = "24" - } - if archive { - timeoutValue = "∞" - } - timeoutSelected = false - case let .message(_, timeout): - timeoutValue = "\(timeout ?? 1)" - timeoutSelected = timeout != nil + switch component.privacy.timeout { + case 21600: + timeoutValue = "6" + case 43200: + timeoutValue = "12" + case 86400: + timeoutValue = "24" + case 172800: + timeoutValue = "48" + default: + timeoutValue = "24" } - + if component.privacy.archive { + timeoutValue = "∞" + } + timeoutSelected = false + var inputPanelAvailableWidth = previewSize.width var inputPanelAvailableHeight = 115.0 if case .regular = environment.metrics.widthClass { @@ -1067,24 +1052,15 @@ final class MediaEditorScreenComponent: Component { } let privacyText: String - switch component.privacy { - case let .story(privacy, _, _): - switch privacy.base { - case .everyone: - privacyText = "Everyone" - case .closeFriends: - privacyText = "Close Friends" - case .contacts: - privacyText = "Contacts" - case .nobody: - privacyText = "Selected Contacts" - } - case let .message(peerIds, _): - if peerIds.count == 1 { - privacyText = "1 Recipient" - } else { - privacyText = "\(peerIds.count) Recipients" - } + switch component.privacy.privacy.base { + case .everyone: + privacyText = "Everyone" + case .closeFriends: + privacyText = "Close Friends" + case .contacts: + privacyText = "Contacts" + case .nobody: + privacyText = "Selected Contacts" } let displayTopButtons = !(self.inputPanelExternalState.isEditing || isEditingTextEntity || component.isDisplayingTool) @@ -1519,7 +1495,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } struct State { - var privacy: MediaEditorResultPrivacy = .story(privacy: EngineStoryPrivacy(base: .everyone, additionallyIncludePeers: []), timeout: 86400, archive: false) + var privacy: MediaEditorResultPrivacy = MediaEditorResultPrivacy(privacy: EngineStoryPrivacy(base: .everyone, additionallyIncludePeers: []), timeout: 86400, archive: false) } var state = State() { @@ -2158,18 +2134,11 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } } - if finished, case .message = controller.state.privacy { - if let view = self.componentHost.view as? MediaEditorScreenComponent.View { - view.animateOut(to: .camera) - } - let transition = Transition(animation: .curve(duration: 0.25, curve: .easeInOut)) - transition.setAlpha(view: self.previewContainerView, alpha: 0.0, completion: { _ in - completion() - if let view = self.entitiesView.getView(where: { $0 is DrawingMediaEntityView }) as? DrawingMediaEntityView { - view.previewView = nil - } - }) - } else if let transitionOut = controller.transitionOut(finished, isNew), let destinationView = transitionOut.destinationView { + if isNew == true { + self.entitiesView.seek(to: 0.0) + } + + if let transitionOut = controller.transitionOut(finished, isNew), let destinationView = transitionOut.destinationView { var destinationTransitionView: UIView? if !finished { if let transitionIn = controller.transitionIn, case let .gallery(galleryTransitionIn) = transitionIn, let sourceImage = galleryTransitionIn.sourceImage, isNew != true { @@ -2436,7 +2405,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate private weak var storyArchiveTooltip: ViewController? func presentStoryArchiveTooltip(sourceView: UIView) { - guard let controller = self.controller, case let .story(_, _, archive) = controller.state.privacy else { + guard let controller = self.controller else { return } @@ -2450,7 +2419,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.minY - 5.0), size: CGSize()) let text: String - if archive { + if controller.state.privacy.archive { text = "Story will be kept on your page." } else { text = "Story will disappear in 24 hours." @@ -2900,66 +2869,48 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate self.displayNode.view.addInteraction(dropInteraction) } - func openPrivacySettings() { + func openPrivacySettings(_ privacy: MediaEditorResultPrivacy? = nil) { self.hapticFeedback.impact(.light) - if case .message(_, _) = self.state.privacy { - self.openSendAsMessage() - } else { - let stateContext = ShareWithPeersScreen.StateContext(context: self.context, subject: .stories) - let _ = (stateContext.ready |> filter { $0 } |> take(1) |> deliverOnMainQueue).start(next: { [weak self] _ in - guard let self else { - return - } - - var archive = true - var timeout: Int = 86400 - let initialPrivacy: EngineStoryPrivacy - if case let .story(privacy, timeoutValue, archiveValue) = self.state.privacy { - initialPrivacy = privacy - timeout = timeoutValue - archive = archiveValue - } else { - initialPrivacy = EngineStoryPrivacy(base: .everyone, additionallyIncludePeers: []) - } - - self.push( - ShareWithPeersScreen( - context: self.context, - initialPrivacy: initialPrivacy, - stateContext: stateContext, - completion: { [weak self] privacy in - guard let self else { - return - } - self.state.privacy = .story(privacy: privacy, timeout: timeout, archive: archive) - }, - editCategory: { [weak self] privacy in - guard let self else { - return - } - self.openEditCategory(privacy: privacy, completion: { [weak self] privacy in - guard let self else { - return - } - self.state.privacy = .story(privacy: privacy, timeout: timeout, archive: archive) - self.openPrivacySettings() - }) - }, - secondaryAction: { [weak self] in - guard let self else { - return - } - self.openSendAsMessage() + let privacy = privacy ?? self.state.privacy + + let stateContext = ShareWithPeersScreen.StateContext(context: self.context, subject: .stories, initialPeerIds: Set(privacy.privacy.additionallyIncludePeers)) + let _ = (stateContext.ready |> filter { $0 } |> take(1) |> deliverOnMainQueue).start(next: { [weak self] _ in + guard let self else { + return + } + let initialPrivacy = privacy.privacy + let timeout = privacy.timeout + let archive = privacy.archive + self.push( + ShareWithPeersScreen( + context: self.context, + initialPrivacy: initialPrivacy, + stateContext: stateContext, + completion: { [weak self] privacy in + guard let self else { + return } - ) + self.state.privacy = MediaEditorResultPrivacy(privacy: privacy, timeout: timeout, archive: archive) + }, + editCategory: { [weak self] privacy in + guard let self else { + return + } + self.openEditCategory(privacy: privacy, completion: { [weak self] privacy in + guard let self else { + return + } + self.openPrivacySettings(MediaEditorResultPrivacy(privacy: privacy, timeout: timeout, archive: archive)) + }) + } ) - }) - } + ) + }) } private func openEditCategory(privacy: EngineStoryPrivacy, completion: @escaping (EngineStoryPrivacy) -> Void) { - let stateContext = ShareWithPeersScreen.StateContext(context: self.context, subject: .contacts(privacy.base)) + let stateContext = ShareWithPeersScreen.StateContext(context: self.context, subject: .contacts(privacy.base), initialPeerIds: Set(privacy.additionallyIncludePeers)) let _ = (stateContext.ready |> filter { $0 } |> take(1) |> deliverOnMainQueue).start(next: { [weak self] _ in guard let self else { return @@ -2979,42 +2930,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } completion(result) }, - editCategory: { _ in }, - secondaryAction: { [weak self] in - guard let self else { - return - } - self.openSendAsMessage() - } - ) - ) - }) - } - - private func openSendAsMessage() { - var initialPeerIds = Set() - if case let .message(peers, _) = self.state.privacy { - initialPeerIds = Set(peers) - } - let stateContext = ShareWithPeersScreen.StateContext(context: self.context, subject: .chats, initialPeerIds: initialPeerIds) - let _ = (stateContext.ready |> filter { $0 } |> take(1) |> deliverOnMainQueue).start(next: { [weak self] _ in - guard let self else { - return - } - - self.push( - ShareWithPeersScreen( - context: self.context, - initialPrivacy: EngineStoryPrivacy(base: .everyone, additionallyIncludePeers: []), - stateContext: stateContext, - completion: { [weak self] privacy in - guard let self else { - return - } - self.state.privacy = .message(peers: privacy.additionallyIncludePeers, timeout: nil) - }, - editCategory: { _ in }, - secondaryAction: {} + editCategory: { _ in } ) ) }) @@ -3029,133 +2945,80 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate guard let self else { return } - switch self.state.privacy { - case let .story(privacy, _, _): - self.state.privacy = .story(privacy: privacy, timeout: timeout ?? 86400, archive: archive) - case let .message(peers, _): - self.state.privacy = .message(peers: peers, timeout: timeout) - } + self.state.privacy = MediaEditorResultPrivacy(privacy: self.state.privacy.privacy, timeout: timeout ?? 86400, archive: archive) } - - var currentValue: Int? - var currentArchived = false + + let title = "Choose how long the story will be visible." + let currentValue = self.state.privacy.timeout + let currentArchived = self.state.privacy.archive let emptyAction: ((ContextMenuActionItem.Action) -> Void)? = nil - let title: String - switch self.state.privacy { - case let .story(_, timeoutValue, archivedValue): - title = "Choose how long the story will be visible." - currentValue = timeoutValue - currentArchived = archivedValue - case let .message(_, timeoutValue): - title = "Choose how long the media will be kept after opening." - currentValue = timeoutValue - } items.append(.action(ContextMenuActionItem(text: title, textLayout: .multiline, textFont: .small, icon: { _ in return nil }, action: emptyAction))) - switch self.state.privacy { - case .story: - items.append(.action(ContextMenuActionItem(text: "6 Hours", icon: { theme in - if !hasPremium { - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/TextLockIcon"), color: theme.contextMenu.secondaryColor) - } else { - return currentValue == 3600 * 6 ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil - } - }, action: { [weak self] _, a in - a(.default) - - if hasPremium { - updateTimeout(3600 * 6, false) - } else { - self?.presentTimeoutPremiumSuggestion(3600 * 6) - } - }))) - items.append(.action(ContextMenuActionItem(text: "12 Hours", icon: { theme in - if !hasPremium { - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/TextLockIcon"), color: theme.contextMenu.secondaryColor) - } else { - return currentValue == 3600 * 12 ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil - } - }, action: { [weak self] _, a in - a(.default) - - if hasPremium { - updateTimeout(3600 * 12, false) - } else { - self?.presentTimeoutPremiumSuggestion(3600 * 12) - } - }))) - items.append(.action(ContextMenuActionItem(text: "24 Hours", icon: { theme in - return currentValue == 86400 && !currentArchived ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil - }, action: { _, a in - a(.default) - - updateTimeout(86400, false) - }))) - items.append(.action(ContextMenuActionItem(text: "48 Hours", icon: { theme in - if !hasPremium { - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/TextLockIcon"), color: theme.contextMenu.secondaryColor) - } else { - return currentValue == 86400 * 2 ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil - } - }, action: { [weak self] _, a in - a(.default) - - if hasPremium { - updateTimeout(86400 * 2, false) - } else { - self?.presentTimeoutPremiumSuggestion(86400 * 2) - } - }))) - items.append(.action(ContextMenuActionItem(text: "Keep Always", icon: { theme in - return currentArchived ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil - }, action: { _, a in - a(.default) - - updateTimeout(86400, true) - }))) - items.append(.separator) - items.append(.action(ContextMenuActionItem(text: "Select 'Keep Always' to show the story on your page.", textLayout: .multiline, textFont: .small, icon: { theme in - return nil - }, action: { _, _ in - }))) - case .message: - items.append(.action(ContextMenuActionItem(text: "Until First View", icon: { _ in - return nil - }, action: { _, a in - a(.default) - - updateTimeout(1, false) - }))) - items.append(.action(ContextMenuActionItem(text: "3 Seconds", icon: { _ in - return nil - }, action: { _, a in - a(.default) - - updateTimeout(3, false) - }))) - items.append(.action(ContextMenuActionItem(text: "10 Seconds", icon: { _ in - return nil - }, action: { _, a in - a(.default) - - updateTimeout(10, false) - }))) - items.append(.action(ContextMenuActionItem(text: "1 Minute", icon: { _ in - return nil - }, action: { _, a in - a(.default) - - updateTimeout(60, false) - }))) - items.append(.action(ContextMenuActionItem(text: "Keep Always", icon: { _ in - return nil - }, action: { _, a in - a(.default) - - updateTimeout(nil, false) - }))) - } + items.append(.action(ContextMenuActionItem(text: "6 Hours", icon: { theme in + if !hasPremium { + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/TextLockIcon"), color: theme.contextMenu.secondaryColor) + } else { + return currentValue == 3600 * 6 ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + } + }, action: { [weak self] _, a in + a(.default) + + if hasPremium { + updateTimeout(3600 * 6, false) + } else { + self?.presentTimeoutPremiumSuggestion(3600 * 6) + } + }))) + items.append(.action(ContextMenuActionItem(text: "12 Hours", icon: { theme in + if !hasPremium { + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/TextLockIcon"), color: theme.contextMenu.secondaryColor) + } else { + return currentValue == 3600 * 12 ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + } + }, action: { [weak self] _, a in + a(.default) + + if hasPremium { + updateTimeout(3600 * 12, false) + } else { + self?.presentTimeoutPremiumSuggestion(3600 * 12) + } + }))) + items.append(.action(ContextMenuActionItem(text: "24 Hours", icon: { theme in + return currentValue == 86400 && !currentArchived ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + }, action: { _, a in + a(.default) + + updateTimeout(86400, false) + }))) + items.append(.action(ContextMenuActionItem(text: "48 Hours", icon: { theme in + if !hasPremium { + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/TextLockIcon"), color: theme.contextMenu.secondaryColor) + } else { + return currentValue == 86400 * 2 ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + } + }, action: { [weak self] _, a in + a(.default) + + if hasPremium { + updateTimeout(86400 * 2, false) + } else { + self?.presentTimeoutPremiumSuggestion(86400 * 2) + } + }))) + items.append(.action(ContextMenuActionItem(text: "Keep Always", icon: { theme in + return currentArchived ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + }, action: { _, a in + a(.default) + + updateTimeout(86400, true) + }))) + items.append(.separator) + items.append(.action(ContextMenuActionItem(text: "Select 'Keep Always' to show the story on your page.", textLayout: .multiline, textFont: .small, icon: { theme in + return nil + }, action: { _, _ in + }))) let presentationData = self.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: defaultDarkPresentationTheme) let contextController = ContextController(account: self.context.account, presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: self, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), gesture: nil) @@ -3251,6 +3114,10 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate }) } + private func getCaption() -> NSAttributedString { + return (self.node.componentHost.view as? MediaEditorScreenComponent.View)?.getInputText() ?? NSAttributedString() + } + private func saveDraft(id: Int64?) { guard let subject = self.node.subject, let values = self.node.mediaEditor?.values else { return @@ -3258,7 +3125,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate try? FileManager.default.createDirectory(atPath: draftPath(), withIntermediateDirectories: true) let privacy = self.state.privacy - let caption = (self.node.componentHost.view as? MediaEditorScreenComponent.View)?.getInputText() ?? NSAttributedString() + let caption = self.getCaption() if let resultImage = self.node.mediaEditor?.resultImage { self.node.mediaEditor?.seek(0.0, andPlay: false) @@ -3330,7 +3197,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } private var didComplete = false - func requestCompletion(caption: NSAttributedString, animated: Bool) { + func requestCompletion(animated: Bool) { guard let mediaEditor = self.node.mediaEditor, let subject = self.node.subject, !self.didComplete else { return } @@ -3351,13 +3218,14 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate let codableEntities = DrawingEntitiesView.encodeEntities(entities, entitiesView: self.node.entitiesView) mediaEditor.setDrawingAndEntities(data: nil, image: mediaEditor.values.drawing, entities: codableEntities) + let caption = self.getCaption() + let randomId: Int64 if case let .draft(_, id) = subject, let id { randomId = id } else { randomId = Int64.random(in: .min ... .max) } - if mediaEditor.resultIsVideo { var firstFrame: Signal let firstFrameTime = CMTime(seconds: mediaEditor.values.videoTrimRange?.lowerBound ?? 0.0, preferredTimescale: CMTimeScale(60)) diff --git a/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift b/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift index 10ef7958cb..59e5ad059b 100644 --- a/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift +++ b/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift @@ -519,19 +519,11 @@ final class ShareWithPeersScreenComponent: Component { guard let self else { return } - switch categoryId { - case .everyone: - if self.selectedCategories.contains(categoryId) { - } else { - self.selectedCategories.removeAll() - self.selectedCategories.insert(categoryId) - } - case .contacts, .closeFriends, .selectedContacts: - if self.selectedCategories.contains(categoryId) { - } else { - self.selectedCategories.removeAll() - self.selectedCategories.insert(categoryId) - } + if self.selectedCategories.contains(categoryId) { + } else { + self.selectedPeers = [] + self.selectedCategories.removeAll() + self.selectedCategories.insert(categoryId) } self.state?.updated(transition: Transition(animation: .curve(duration: 0.35, curve: .spring))) }, @@ -1189,6 +1181,7 @@ public class ShareWithPeersScreen: ViewControllerComponentContainer { self.stateValue = state self.stateSubject.set(.single(state)) self.readySubject.set(true) + self.initialPeerIds = initialPeerIds case .chats: self.stateDisposable = (context.engine.messages.chatList(group: .root, count: 200) |> deliverOnMainQueue).start(next: { [weak self] chatList in @@ -1237,16 +1230,23 @@ public class ShareWithPeersScreen: ViewControllerComponentContainer { selectedPeers.append(peer) } } - selectedPeers = selectedPeers.sorted(by: { lhs, rhs in - let result = lhs.indexName.isLessThan(other: rhs.indexName, ordering: .firstLast) - if result == .orderedSame { - return lhs.id < rhs.id - } else { - return result == .orderedAscending - } - }) self.initialPeerIds = Set(selectedPeers.map { $0.id }) + } else { + for peer in contactList.peers { + if case let .user(user) = peer, initialPeerIds.contains(user.id) { + selectedPeers.append(peer) + } + } + self.initialPeerIds = initialPeerIds } + selectedPeers = selectedPeers.sorted(by: { lhs, rhs in + let result = lhs.indexName.isLessThan(other: rhs.indexName, ordering: .firstLast) + if result == .orderedSame { + return lhs.id < rhs.id + } else { + return result == .orderedAscending + } + }) var peers: [EnginePeer] = [] peers = contactList.peers.filter { !self.initialPeerIds.contains($0.id) && $0.id != context.account.peerId }.sorted(by: { lhs, rhs in diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift index 025b9d7c4b..a78cd69935 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift @@ -2165,8 +2165,8 @@ public final class StoryItemSetContainerComponent: Component { completion: { [weak self] _, mediaResult, privacy, commit in switch mediaResult { case let .image(image, dimensions, caption): - if let imageData = compressImageToJPEG(image, quality: 0.6), case let .story(storyPrivacy, _, _) = privacy { - let _ = (context.engine.messages.editStory(media: .image(dimensions: dimensions, data: imageData), id: id, text: caption?.string ?? "", entities: [], privacy: storyPrivacy) + if let imageData = compressImageToJPEG(image, quality: 0.6) { + let _ = (context.engine.messages.editStory(media: .image(dimensions: dimensions, data: imageData), id: id, text: caption?.string ?? "", entities: [], privacy: privacy.privacy) |> deliverOnMainQueue).start(next: { [weak self] result in switch result { case let .progress(progress): diff --git a/submodules/TelegramUI/Sources/TelegramRootController.swift b/submodules/TelegramUI/Sources/TelegramRootController.swift index 5f106303c3..c6f8e03e0e 100644 --- a/submodules/TelegramUI/Sources/TelegramRootController.swift +++ b/submodules/TelegramUI/Sources/TelegramRootController.swift @@ -358,92 +358,11 @@ public final class TelegramRootController: NavigationController, TelegramRootCon chatListController.scrollToStories() switch mediaResult { case let .image(image, dimensions, caption): - if let imageData = compressImageToJPEG(image, quality: 0.6) { - switch privacy { - case let .story(storyPrivacy, period, pin): - let text = caption ?? NSAttributedString() - let entities = generateChatInputTextEntities(text) - self.context.engine.messages.uploadStory(media: .image(dimensions: dimensions, data: imageData), text: text.string, entities: entities, pin: pin, privacy: storyPrivacy, period: period, randomId: randomId) - - /*let _ = (self.context.engine.messages.uploadStory(media: .image(dimensions: dimensions, data: imageData), text: caption?.string ?? "", entities: [], pin: pin, privacy: storyPrivacy, period: period, randomId: randomId) - |> deliverOnMainQueue).start(next: { [weak chatListController] result in - if let chatListController { - switch result { - case let .progress(progress): - chatListController.updateStoryUploadProgress(progress) - case let .completed(id): - if let id { - moveStorySource(engine: context.engine, from: randomId, to: Int64(id)) - } - Queue.mainQueue().after(0.2) { - chatListController.updateStoryUploadProgress(nil) - } - - let undoOverlayController = UndoOverlayController(presentationData: presentationData, content: .image(image: image, title: nil, text: "Story successfully uploaded", round: false, undoText: "View"), elevatedLayout: false, action: { action in - switch action { - case .undo: - break - default: - break - } - return true - }) - chatListController.present(undoOverlayController, in: .current) - } - } - })*/ - Queue.mainQueue().justDispatch { - commit({}) - } - case let .message(peerIds, timeout): - var randomId: Int64 = 0 - arc4random_buf(&randomId, 8) - let tempFilePath = NSTemporaryDirectory() + "\(randomId).jpg" - let _ = try? imageData.write(to: URL(fileURLWithPath: tempFilePath)) - - var representations: [TelegramMediaImageRepresentation] = [] - let resource = LocalFileReferenceMediaResource(localFilePath: tempFilePath, randomId: randomId) - representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(image.size), resource: resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false)) - - var attributes: [MessageAttribute] = [] - let imageFlags: TelegramMediaImageFlags = [] - - let media = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: randomId), representations: representations, immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: imageFlags) - if let timeout, timeout > 0 && timeout <= 60 { - attributes.append(AutoremoveTimeoutMessageAttribute(timeout: Int32(timeout), countdownBeginTime: nil)) - } - - let text = trimChatInputText(convertMarkdownToAttributes(caption ?? NSAttributedString())) - let entities = generateTextEntities(text.string, enabledTypes: .all, currentEntities: generateChatInputTextEntities(text)) - if !entities.isEmpty { - attributes.append(TextEntitiesMessageAttribute(entities: entities)) - } - var bubbleUpEmojiOrStickersetsById: [Int64: ItemCollectionId] = [:] - text.enumerateAttribute(ChatTextInputAttributes.customEmoji, in: NSRange(location: 0, length: text.length), using: { value, _, _ in - if let value = value as? ChatTextInputTextCustomEmojiAttribute { - if let file = value.file { - if let packId = value.interactivelySelectedFromPackId { - bubbleUpEmojiOrStickersetsById[file.fileId.id] = packId - } - } - } - }) - var bubbleUpEmojiOrStickersets: [ItemCollectionId] = [] - for entity in entities { - if case let .CustomEmoji(_, fileId) = entity.type { - if let packId = bubbleUpEmojiOrStickersetsById[fileId] { - if !bubbleUpEmojiOrStickersets.contains(packId) { - bubbleUpEmojiOrStickersets.append(packId) - } - } - } - } - - let _ = enqueueMessagesToMultiplePeers( - account: self.context.account, - peerIds: peerIds, threadIds: [:], - messages: [.message(text: text.string, attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: media), replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: bubbleUpEmojiOrStickersets)]).start() - + if let imageData = compressImageToJPEG(image, quality: 0.7) { + let text = caption ?? NSAttributedString() + let entities = generateChatInputTextEntities(text) + self.context.engine.messages.uploadStory(media: .image(dimensions: dimensions, data: imageData), text: text.string, entities: entities, pin: privacy.archive, privacy: privacy.privacy, period: privacy.timeout, randomId: randomId) + Queue.mainQueue().justDispatch { commit({}) } } @@ -463,46 +382,11 @@ public final class TelegramRootController: NavigationController, TelegramRootCon case let .asset(localIdentifier): resource = VideoLibraryMediaResource(localIdentifier: localIdentifier, conversion: .compress(adjustments)) } - let imageData = firstFrameImage.flatMap { compressImageToJPEG($0, quality: 0.6) } - - if case let .story(storyPrivacy, period, pin) = privacy { - let text = caption ?? NSAttributedString() - let entities = generateChatInputTextEntities(text) - self.context.engine.messages.uploadStory(media: .video(dimensions: dimensions, duration: duration, resource: resource, firstFrameImageData: imageData), text: text.string, entities: entities, pin: pin, privacy: storyPrivacy, period: period, randomId: randomId) - /*let _ = (self.context.engine.messages.uploadStory(media: .video(dimensions: dimensions, duration: duration, resource: resource), text: caption?.string ?? "", entities: [], pin: pin, privacy: storyPrivacy, period: period, randomId: randomId) - |> deliverOnMainQueue).start(next: { [weak chatListController] result in - if let chatListController { - switch result { - case let .progress(progress): - chatListController.updateStoryUploadProgress(progress) - case let .completed(id): - if let id { - moveStorySource(engine: context.engine, from: randomId, to: Int64(id)) - } - Queue.mainQueue().after(0.2) { - chatListController.updateStoryUploadProgress(nil) - } - - if let image { - let undoOverlayController = UndoOverlayController(presentationData: presentationData, content: .image(image: image, title: nil, text: "Story successfully uploaded", round: false, undoText: "View"), elevatedLayout: false, action: { action in - switch action { - case .undo: - break - default: - break - } - return true - }) - chatListController.present(undoOverlayController, in: .current) - } - } - } - })*/ - Queue.mainQueue().justDispatch { - commit({}) - } - } else { + let text = caption ?? NSAttributedString() + let entities = generateChatInputTextEntities(text) + self.context.engine.messages.uploadStory(media: .video(dimensions: dimensions, duration: duration, resource: resource, firstFrameImageData: imageData), text: text.string, entities: entities, pin: privacy.archive, privacy: privacy.privacy, period: privacy.timeout, randomId: randomId) + Queue.mainQueue().justDispatch { commit({}) } }