From 9ab61f350c0e85fa70c4ddc4e17906985cc7cf24 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Tue, 16 May 2023 23:04:40 +0400 Subject: [PATCH] [WIP] Stories --- .../Sources/ChatListController.swift | 4 +- .../Sources/PeersNearbyController.swift | 5 +- .../TelegramEngine/Messages/Stories.swift | 3 + .../Components/MediaEditorScreen/BUILD | 1 + .../Sources/MediaEditorScreen.swift | 30 +++++-- .../Sources/ShareWithPeersScreen.swift | 38 +++++++- .../StoryItemSetContainerComponent.swift | 81 ----------------- .../Sources/TelegramRootController.swift | 90 ++++++++----------- 8 files changed, 105 insertions(+), 147 deletions(-) diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 21bb25f1d1..9640d1ab72 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -2490,10 +2490,10 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController if let transitionView = componentView.storyPeerListView()?.transitionViewForItem(peerId: peerId) { let localRect = transitionView.convert(transitionView.bounds, to: self.view) - Queue.mainQueue().after(0.2 * UIView.animationDurationFactor, { [weak self] in + /*Queue.mainQueue().after(0.2 * UIView.animationDurationFactor, { [weak self] in HapticFeedback().impact() self?.animateRipple(centerLocation: localRect.center) - }) + })*/ return StoryContainerScreen.TransitionOut( destinationView: transitionView, diff --git a/submodules/PeersNearbyUI/Sources/PeersNearbyController.swift b/submodules/PeersNearbyUI/Sources/PeersNearbyController.swift index f4eb9bd3d7..4e11b6eadb 100644 --- a/submodules/PeersNearbyUI/Sources/PeersNearbyController.swift +++ b/submodules/PeersNearbyUI/Sources/PeersNearbyController.swift @@ -502,10 +502,7 @@ public func peersNearbyController(context: AccountContext) -> ViewController { }) |> mapToSignal { coordinate -> Signal in guard let coordinate = coordinate else { - #if !DEBUG - #error("fix") - #endif - preconditionFailure() + return .single(nil) /*let peersNearbyContext = PeersNearbyContext(network: context.account.network, stateManager: context.account.stateManager, coordinate: nil) return peersNearbyContext.get() |> map { peersNearby -> PeersNearbyData in diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift index 49f39a437a..39c4af3eae 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift @@ -13,6 +13,7 @@ public struct EngineStoryPrivacy: Equatable { case everyone case contacts case closeFriends + case nobody } public var base: Base @@ -116,6 +117,8 @@ func _internal_uploadStory(account: Account, media: EngineStoryInputMedia, text: privacyRules = [.inputPrivacyValueAllowContacts] case .closeFriends: privacyRules = [.inputPrivacyValueAllowCloseFriends] + case .nobody: + privacyRules = [.inputPrivacyValueDisallowAll] } var privacyUsers: [Api.InputUser] = [] var privacyChats: [Int64] = [] diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/BUILD b/submodules/TelegramUI/Components/MediaEditorScreen/BUILD index 6b969908c7..b0124f5b14 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/BUILD +++ b/submodules/TelegramUI/Components/MediaEditorScreen/BUILD @@ -34,6 +34,7 @@ swift_library( "//submodules/TooltipUI", "//submodules/Components/BlurredBackgroundComponent", "//submodules/AvatarNode", + "//submodules/TelegramUI/Components/ShareWithPeersScreen", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index 6f0bd29b30..9d3bd84021 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -19,6 +19,7 @@ import EntityKeyboard import TooltipUI import BlurredBackgroundComponent import AvatarNode +import ShareWithPeersScreen enum DrawingScreenType { case drawing @@ -631,6 +632,8 @@ final class MediaEditorScreenComponent: Component { privacyText = "Close Friends" case .contacts: privacyText = "Contacts" + case .nobody: + privacyText = "Selected Contacts" } @@ -1359,14 +1362,14 @@ public final class MediaEditorScreen: ViewController { public var sourceHint: SourceHint? public var cancelled: () -> Void = {} - public var completion: (MediaEditorScreen.Result, @escaping () -> Void) -> Void = { _, _ in } + public var completion: (MediaEditorScreen.Result, @escaping () -> Void, EngineStoryPrivacy) -> Void = { _, _, _ in } public init( context: AccountContext, subject: Signal, transitionIn: TransitionIn?, transitionOut: @escaping (Bool) -> TransitionOut?, - completion: @escaping (MediaEditorScreen.Result, @escaping () -> Void) -> Void + completion: @escaping (MediaEditorScreen.Result, @escaping () -> Void, EngineStoryPrivacy) -> Void ) { self.context = context self.subject = subject @@ -1393,7 +1396,22 @@ public final class MediaEditorScreen: ViewController { } func presentPrivacySettings() { - enum AdditionalCategoryId: Int { + let stateContext = ShareWithPeersScreen.StateContext(context: self.context) + 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: self.node.storyPrivacy, stateContext: stateContext, completion: { [weak self] privacy in + guard let self else { + return + } + self.node.storyPrivacy = privacy + self.node.requestUpdate() + })) + }) + + /*enum AdditionalCategoryId: Int { case everyone case contacts case closeFriends @@ -1464,7 +1482,7 @@ public final class MediaEditorScreen: ViewController { } self?.node.storyPrivacy = privacy self?.node.requestUpdate() - }) + })*/ } func requestDismiss(animated: Bool) { @@ -1514,7 +1532,7 @@ public final class MediaEditorScreen: ViewController { self?.node.animateOut(finished: true, completion: { [weak self] in self?.dismiss() }) - }) + }, self.node.storyPrivacy) } else { if let image = mediaEditor.resultImage { makeEditorImageComposition(account: self.context.account, inputImage: image, dimensions: storyDimensions, values: mediaEditor.values, time: .zero, completion: { resultImage in @@ -1523,7 +1541,7 @@ public final class MediaEditorScreen: ViewController { self?.node.animateOut(finished: true, completion: { [weak self] in self?.dismiss() }) - }) + }, self.node.storyPrivacy) } }) } diff --git a/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift b/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift index 970642b91a..bf886c3f3c 100644 --- a/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift +++ b/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift @@ -23,17 +23,20 @@ final class ShareWithPeersScreenComponent: Component { let context: AccountContext let stateContext: ShareWithPeersScreen.StateContext + let initialPrivacy: EngineStoryPrivacy let categoryItems: [CategoryItem] let completion: (EngineStoryPrivacy) -> Void init( context: AccountContext, stateContext: ShareWithPeersScreen.StateContext, + initialPrivacy: EngineStoryPrivacy, categoryItems: [CategoryItem], completion: @escaping (EngineStoryPrivacy) -> Void ) { self.context = context self.stateContext = stateContext + self.initialPrivacy = initialPrivacy self.categoryItems = categoryItems self.completion = completion } @@ -45,6 +48,9 @@ final class ShareWithPeersScreenComponent: Component { if lhs.stateContext !== rhs.stateContext { return false } + if lhs.initialPrivacy != rhs.initialPrivacy { + return false + } if lhs.categoryItems != rhs.categoryItems { return false } @@ -681,7 +687,16 @@ final class ShareWithPeersScreenComponent: Component { let sideInset: CGFloat = 0.0 if self.component == nil { - self.selectedCategories.insert(.everyone) + switch component.initialPrivacy.base { + case .everyone: + self.selectedCategories.insert(.everyone) + case .closeFriends: + self.selectedCategories.insert(.closeFriends) + case .contacts: + self.selectedCategories.insert(.contacts) + case .nobody: + self.selectedCategories.insert(.selectedContacts) + } var applyState = false self.defaultStateValue = component.stateContext.stateValue @@ -779,6 +794,9 @@ final class ShareWithPeersScreenComponent: Component { } else if let peerId = tokenId.base as? EnginePeer.Id { self.selectedPeers.removeAll(where: { $0 == peerId }) } + if self.selectedCategories.isEmpty { + self.selectedCategories.insert(.everyone) + } self.state?.updated(transition: Transition(animation: .curve(duration: 0.35, curve: .spring))) } )), @@ -969,8 +987,21 @@ final class ShareWithPeersScreenComponent: Component { return } + let base: EngineStoryPrivacy.Base + if self.selectedCategories.contains(.everyone) { + base = .everyone + } else if self.selectedCategories.contains(.closeFriends) { + base = .closeFriends + } else if self.selectedCategories.contains(.contacts) { + base = .contacts + } else if self.selectedCategories.contains(.selectedContacts) { + base = .nobody + } else { + base = .nobody + } + component.completion(EngineStoryPrivacy( - base: .everyone, + base: base, additionallyIncludePeers: self.selectedPeers )) controller.dismiss() @@ -1152,7 +1183,7 @@ public class ShareWithPeersScreen: ViewControllerComponentContainer { private var isDismissed: Bool = false - public init(context: AccountContext, stateContext: StateContext, completion: @escaping (EngineStoryPrivacy) -> Void) { + public init(context: AccountContext, initialPrivacy: EngineStoryPrivacy, stateContext: StateContext, completion: @escaping (EngineStoryPrivacy) -> Void) { self.context = context var categoryItems: [ShareWithPeersScreenComponent.CategoryItem] = [] @@ -1188,6 +1219,7 @@ public class ShareWithPeersScreen: ViewControllerComponentContainer { super.init(context: context, component: ShareWithPeersScreenComponent( context: context, stateContext: stateContext, + initialPrivacy: initialPrivacy, categoryItems: categoryItems, completion: completion ), navigationBarAppearance: .none, theme: .dark) diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift index af10d4ede0..bc5aa58465 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift @@ -1439,88 +1439,7 @@ public final class StoryItemSetContainerComponent: Component { } private func openItemPrivacySettings() { - guard let component = self.component else { - return - } - guard let focusedItemId = self.focusedItemId, let focusedItem = self.currentSlice?.items.first(where: { $0.id == focusedItemId }) else { - return - } - guard let storyItem = focusedItem.storyItem else { - return - } - - enum AdditionalCategoryId: Int { - case everyone - case contacts - case closeFriends - } - - let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme) - - let additionalCategories: [ChatListNodeAdditionalCategory] = [ - ChatListNodeAdditionalCategory( - id: AdditionalCategoryId.everyone.rawValue, - icon: generateAvatarImage(size: CGSize(width: 40.0, height: 40.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/Filters/Channel"), color: .white), cornerRadius: nil, color: .blue), - smallIcon: generateAvatarImage(size: CGSize(width: 22.0, height: 22.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/Filters/Channel"), color: .white), iconScale: 0.6, cornerRadius: 6.0, circleCorners: true, color: .blue), - title: "Everyone", - appearance: .option(sectionTitle: "WHO CAN VIEW FOR 24 HOURS") - ), - ChatListNodeAdditionalCategory( - id: AdditionalCategoryId.contacts.rawValue, - icon: generateAvatarImage(size: CGSize(width: 40.0, height: 40.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/Tabs/IconContacts"), color: .white), iconScale: 1.0 * 0.8, cornerRadius: nil, color: .yellow), - smallIcon: generateAvatarImage(size: CGSize(width: 22.0, height: 22.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/Tabs/IconContacts"), color: .white), iconScale: 0.6 * 0.8, cornerRadius: 6.0, circleCorners: true, color: .yellow), - title: presentationData.strings.ChatListFolder_CategoryContacts, - appearance: .option(sectionTitle: "WHO CAN VIEW FOR 24 HOURS") - ), - ChatListNodeAdditionalCategory( - id: AdditionalCategoryId.closeFriends.rawValue, - icon: generateAvatarImage(size: CGSize(width: 40.0, height: 40.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Call/StarHighlighted"), color: .white), iconScale: 1.0 * 0.6, cornerRadius: nil, color: .green), - smallIcon: generateAvatarImage(size: CGSize(width: 22.0, height: 22.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Call/StarHighlighted"), color: .white), iconScale: 0.6 * 0.6, cornerRadius: 6.0, circleCorners: true, color: .green), - title: "Close Friends", - appearance: .option(sectionTitle: "WHO CAN VIEW FOR 24 HOURS") - ) - ] - - var selectedChats = Set() - var selectedCategories = Set() - if let privacy = storyItem.privacy { - selectedChats.formUnion(privacy.additionallyIncludePeers) - switch privacy.base { - case .everyone: - selectedCategories.insert(AdditionalCategoryId.everyone.rawValue) - case .contacts: - selectedCategories.insert(AdditionalCategoryId.contacts.rawValue) - case .closeFriends: - selectedCategories.insert(AdditionalCategoryId.closeFriends.rawValue) - } - } - - let selectionController = component.context.sharedContext.makeContactMultiselectionController(ContactMultiselectionControllerParams(context: component.context, mode: .chatSelection(ContactMultiselectionControllerMode.ChatSelection( - title: "Share Story", - searchPlaceholder: "Search contacts", - selectedChats: selectedChats, - additionalCategories: ContactMultiselectionControllerAdditionalCategories(categories: additionalCategories, selectedCategories: selectedCategories), - chatListFilters: nil, - displayPresence: true - )), options: [], filters: [.excludeSelf], alwaysEnabled: true, limit: 1000, reachedLimit: { _ in - })) - component.controller()?.present(selectionController, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) - - let _ = (selectionController.result - |> take(1) - |> deliverOnMainQueue).start(next: { [weak selectionController] result in - guard case let .result(peerIds, additionalCategoryIds) = result else { - selectionController?.dismiss() - return - } - - let _ = peerIds - let _ = additionalCategoryIds - - selectionController?.dismiss() - }) } - } public func makeView() -> View { diff --git a/submodules/TelegramUI/Sources/TelegramRootController.swift b/submodules/TelegramUI/Sources/TelegramRootController.swift index 863fbea304..a72e999375 100644 --- a/submodules/TelegramUI/Sources/TelegramRootController.swift +++ b/submodules/TelegramUI/Sources/TelegramRootController.swift @@ -349,60 +349,48 @@ public final class TelegramRootController: NavigationController, TelegramRootCon } else { return nil } - }, completion: { mediaResult, commit in - let stateContext = ShareWithPeersScreen.StateContext(context: self.context) - let _ = (stateContext.ready |> filter { $0 } |> take(1) |> deliverOnMainQueue).start(next: { [weak self] _ in - guard let self else { - return - } - guard let controller = self.viewControllers.last as? ViewController else { - return - } - - controller.push(ShareWithPeersScreen(context: self.context, stateContext: stateContext, completion: { [weak self] privacy in - guard let self else { - dismissCameraImpl?() - commit() - return + }, completion: { [weak self] mediaResult, commit, privacy in + guard let self else { + dismissCameraImpl?() + commit() + return + } + + if let chatListController = self.chatListController as? ChatListControllerImpl, let storyListContext = chatListController.storyListContext { + switch mediaResult { + case let .image(image, dimensions, caption): + if let data = image.jpegData(compressionQuality: 0.8) { + storyListContext.upload(media: .image(dimensions: dimensions, data: data), text: caption?.string ?? "", entities: [], privacy: privacy) + Queue.mainQueue().after(0.2, { [weak chatListController] in + chatListController?.animateStoryUploadRipple() + }) } - - if let chatListController = self.chatListController as? ChatListControllerImpl, let storyListContext = chatListController.storyListContext { - switch mediaResult { - case let .image(image, dimensions, caption): - if let data = image.jpegData(compressionQuality: 0.8) { - storyListContext.upload(media: .image(dimensions: dimensions, data: data), text: caption?.string ?? "", entities: [], privacy: privacy) - Queue.mainQueue().after(0.3, { [weak chatListController] in - chatListController?.animateStoryUploadRipple() - }) - } - case let .video(content, _, values, duration, dimensions, caption): - let adjustments: VideoMediaResourceAdjustments - if let valuesData = try? JSONEncoder().encode(values) { - let data = MemoryBuffer(data: valuesData) - let digest = MemoryBuffer(data: data.md5Digest()) - adjustments = VideoMediaResourceAdjustments(data: data, digest: digest, isStory: true) - - let resource: TelegramMediaResource - switch content { - case let .imageFile(path): - resource = LocalFileVideoMediaResource(randomId: Int64.random(in: .min ... .max), path: path, adjustments: adjustments) - case let .videoFile(path): - resource = LocalFileVideoMediaResource(randomId: Int64.random(in: .min ... .max), path: path, adjustments: adjustments) - case let .asset(localIdentifier): - resource = VideoLibraryMediaResource(localIdentifier: localIdentifier, conversion: .compress(adjustments)) - } - storyListContext.upload(media: .video(dimensions: dimensions, duration: Int(duration), resource: resource), text: caption?.string ?? "", entities: [], privacy: privacy) - Queue.mainQueue().after(0.3, { [weak chatListController] in - chatListController?.animateStoryUploadRipple() - }) - } + case let .video(content, _, values, duration, dimensions, caption): + let adjustments: VideoMediaResourceAdjustments + if let valuesData = try? JSONEncoder().encode(values) { + let data = MemoryBuffer(data: valuesData) + let digest = MemoryBuffer(data: data.md5Digest()) + adjustments = VideoMediaResourceAdjustments(data: data, digest: digest, isStory: true) + + let resource: TelegramMediaResource + switch content { + case let .imageFile(path): + resource = LocalFileVideoMediaResource(randomId: Int64.random(in: .min ... .max), path: path, adjustments: adjustments) + case let .videoFile(path): + resource = LocalFileVideoMediaResource(randomId: Int64.random(in: .min ... .max), path: path, adjustments: adjustments) + case let .asset(localIdentifier): + resource = VideoLibraryMediaResource(localIdentifier: localIdentifier, conversion: .compress(adjustments)) } + storyListContext.upload(media: .video(dimensions: dimensions, duration: Int(duration), resource: resource), text: caption?.string ?? "", entities: [], privacy: privacy) + Queue.mainQueue().after(0.2, { [weak chatListController] in + chatListController?.animateStoryUploadRipple() + }) } - - dismissCameraImpl?() - commit() - })) - }) + } + } + + dismissCameraImpl?() + commit() }) controller.sourceHint = .camera controller.cancelled = {