diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index b25c0e1b89..4d3431911f 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -796,7 +796,7 @@ public struct StoryCameraTransitionInCoordinator { public protocol TelegramRootControllerInterface: NavigationController { @discardableResult - func openStoryCamera(transitionIn: StoryCameraTransitionIn?, transitionedIn: @escaping () -> Void, transitionOut: @escaping (Stories.PendingTarget?, Bool) -> StoryCameraTransitionOut?) -> StoryCameraTransitionInCoordinator? + func openStoryCamera(customTarget: EnginePeer.Id?, transitionIn: StoryCameraTransitionIn?, transitionedIn: @escaping () -> Void, transitionOut: @escaping (Stories.PendingTarget?, Bool) -> StoryCameraTransitionOut?) -> StoryCameraTransitionInCoordinator? func getContactsController() -> ViewController? func getChatsController() -> ViewController? diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 47e16057fe..56b8177ad4 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -2710,7 +2710,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } if let rootController = self.context.sharedContext.mainWindow?.viewController as? TelegramRootControllerInterface { - let coordinator = rootController.openStoryCamera(transitionIn: cameraTransitionIn, transitionedIn: {}, transitionOut: self.storyCameraTransitionOut()) + let coordinator = rootController.openStoryCamera(customTarget: nil, transitionIn: cameraTransitionIn, transitionedIn: {}, transitionOut: self.storyCameraTransitionOut()) coordinator?.animateIn() } } @@ -5684,7 +5684,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController if let current = self.storyCameraTransitionInCoordinator { coordinator = current } else { - coordinator = rootController.openStoryCamera(transitionIn: nil, transitionedIn: {}, transitionOut: { [weak self] target, _ in + coordinator = rootController.openStoryCamera(customTarget: nil, transitionIn: nil, transitionedIn: {}, transitionOut: { [weak self] target, _ in guard let self, let target else { return nil } diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift index 7b0736df4a..8b783db238 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift @@ -294,6 +294,7 @@ public enum PresentationResourceKey: Int32 { case uploadToneIcon case storyViewListLikeIcon + case navigationPostStoryIcon } public enum ChatExpiredStoryIndicatorType: Hashable { diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesRootController.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesRootController.swift index de037642ad..51c5ae79c9 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesRootController.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesRootController.swift @@ -169,4 +169,10 @@ public struct PresentationResourcesRootController { })?.stretchableImage(withLeftCapWidth: Int(inset) + 15, topCapHeight: 8 * 2 + 15) }) } + + public static func navigationPostStoryIcon(_ theme: PresentationTheme) -> UIImage? { + return theme.image(PresentationResourceKey.navigationPostStoryIcon.rawValue, { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat List/AddStoryIcon"), color: theme.rootController.navigationBar.accentTextColor) + }) + } } diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift index 4e546d94a4..cf8074299d 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift @@ -203,6 +203,7 @@ final class PeerInfoScreenData { let threadData: MessageHistoryThreadData? let appConfiguration: AppConfiguration? let isPowerSavingEnabled: Bool? + let accountIsPremium: Bool let _isContact: Bool var forceIsContact: Bool = false @@ -236,7 +237,8 @@ final class PeerInfoScreenData { requestsContext: PeerInvitationImportersContext?, threadData: MessageHistoryThreadData?, appConfiguration: AppConfiguration?, - isPowerSavingEnabled: Bool? + isPowerSavingEnabled: Bool?, + accountIsPremium: Bool ) { self.peer = peer self.chatPeer = chatPeer @@ -259,6 +261,7 @@ final class PeerInfoScreenData { self.threadData = threadData self.appConfiguration = appConfiguration self.isPowerSavingEnabled = isPowerSavingEnabled + self.accountIsPremium = accountIsPremium } } @@ -599,7 +602,8 @@ func peerInfoScreenSettingsData(context: AccountContext, peerId: EnginePeer.Id, requestsContext: nil, threadData: nil, appConfiguration: appConfiguration, - isPowerSavingEnabled: isPowerSavingEnabled + isPowerSavingEnabled: isPowerSavingEnabled, + accountIsPremium: peer?.isPremium ?? false ) } } @@ -632,7 +636,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen requestsContext: nil, threadData: nil, appConfiguration: nil, - isPowerSavingEnabled: nil + isPowerSavingEnabled: nil, + accountIsPremium: false )) case let .user(userPeerId, secretChatId, kind): let groupsInCommon: GroupsInCommonContext? @@ -741,15 +746,22 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen } |> distinctUntilChanged + let accountIsPremium = context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId)) + |> map { peer -> Bool in + return peer?.isPremium ?? false + } + |> distinctUntilChanged + return combineLatest( context.account.viewTracker.peerView(peerId, updateData: true), peerInfoAvailableMediaPanes(context: context, peerId: peerId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder), context.engine.data.subscribe(TelegramEngine.EngineData.Item.NotificationSettings.Global()), secretChatKeyFingerprint, status, - hasStories + hasStories, + accountIsPremium ) - |> map { peerView, availablePanes, globalNotificationSettings, encryptionKeyFingerprint, status, hasStories -> PeerInfoScreenData in + |> map { peerView, availablePanes, globalNotificationSettings, encryptionKeyFingerprint, status, hasStories, accountIsPremium -> PeerInfoScreenData in var availablePanes = availablePanes if let hasStories { @@ -787,7 +799,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen requestsContext: nil, threadData: nil, appConfiguration: nil, - isPowerSavingEnabled: nil + isPowerSavingEnabled: nil, + accountIsPremium: accountIsPremium ) } case .channel: @@ -820,6 +833,12 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen } |> distinctUntilChanged + let accountIsPremium = context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId)) + |> map { peer -> Bool in + return peer?.isPremium ?? false + } + |> distinctUntilChanged + return combineLatest( context.account.viewTracker.peerView(peerId, updateData: true), peerInfoAvailableMediaPanes(context: context, peerId: peerId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder), @@ -829,9 +848,10 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen invitationsStatePromise.get(), requestsContextPromise.get(), requestsStatePromise.get(), - hasStories + hasStories, + accountIsPremium ) - |> map { peerView, availablePanes, globalNotificationSettings, status, currentInvitationsContext, invitations, currentRequestsContext, requests, hasStories -> PeerInfoScreenData in + |> map { peerView, availablePanes, globalNotificationSettings, status, currentInvitationsContext, invitations, currentRequestsContext, requests, hasStories, accountIsPremium -> PeerInfoScreenData in var availablePanes = availablePanes if let hasStories { if hasStories { @@ -887,7 +907,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen requestsContext: currentRequestsContext, threadData: nil, appConfiguration: nil, - isPowerSavingEnabled: nil + isPowerSavingEnabled: nil, + accountIsPremium: accountIsPremium ) } case let .group(groupId): @@ -1006,6 +1027,12 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen threadData = .single(nil) } + let accountIsPremium = context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId)) + |> map { peer -> Bool in + return peer?.isPremium ?? false + } + |> distinctUntilChanged + return combineLatest(queue: .mainQueue(), context.account.viewTracker.peerView(groupId, updateData: true), peerInfoAvailableMediaPanes(context: context, peerId: groupId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder), @@ -1017,9 +1044,10 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen requestsContextPromise.get(), requestsStatePromise.get(), threadData, - context.account.postbox.preferencesView(keys: [PreferencesKeys.appConfiguration]) + context.account.postbox.preferencesView(keys: [PreferencesKeys.appConfiguration]), + accountIsPremium ) - |> mapToSignal { peerView, availablePanes, globalNotificationSettings, status, membersData, currentInvitationsContext, invitations, currentRequestsContext, requests, threadData, preferencesView -> Signal in + |> mapToSignal { peerView, availablePanes, globalNotificationSettings, status, membersData, currentInvitationsContext, invitations, currentRequestsContext, requests, threadData, preferencesView, accountIsPremium -> Signal in var discussionPeer: Peer? if case let .known(maybeLinkedDiscussionPeerId) = (peerView.cachedData as? CachedChannelData)?.linkedDiscussionPeerId, let linkedDiscussionPeerId = maybeLinkedDiscussionPeerId, let peer = peerView.peers[linkedDiscussionPeerId] { discussionPeer = peer @@ -1091,7 +1119,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen requestsContext: currentRequestsContext, threadData: threadData, appConfiguration: appConfiguration, - isPowerSavingEnabled: nil + isPowerSavingEnabled: nil, + accountIsPremium: accountIsPremium )) } } diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift index bb869ed528..7627cbf1c2 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift @@ -1401,6 +1401,8 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { if self.isWhite != oldValue { if case .qrCode = self.key, let theme = self.theme { self.iconNode.image = self.isWhite ? generateTintedImage(image: PresentationResourcesRootController.navigationQrCodeIcon(theme), color: .white) : PresentationResourcesRootController.navigationQrCodeIcon(theme) + } else if case .postStory = self.key, let theme = self.theme { + self.iconNode.image = self.isWhite ? generateTintedImage(image: PresentationResourcesRootController.navigationPostStoryIcon(theme), color: .white) : PresentationResourcesRootController.navigationPostStoryIcon(theme) } self.regularTextNode.isHidden = self.isWhite @@ -1502,6 +1504,10 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { case .moreToSearch: text = "" accessibilityText = "" + case .postStory: + text = "" + accessibilityText = presentationData.strings.Story_Privacy_PostStory + icon = PresentationResourcesRootController.navigationPostStoryIcon(presentationData.theme) } self.accessibilityLabel = accessibilityText self.containerNode.isGestureEnabled = isGestureEnabled @@ -1581,6 +1587,7 @@ enum PeerInfoHeaderNavigationButtonKey { case more case qrCode case moreToSearch + case postStory } struct PeerInfoHeaderNavigationButtonSpec: Equatable { @@ -1725,7 +1732,10 @@ final class PeerInfoHeaderNavigationButtonContainerNode: SparseNode { } let buttonSize = buttonNode.update(key: spec.key, presentationData: presentationData, height: size.height) var nextButtonOrigin = spec.isForExpandedView ? nextExpandedButtonOrigin : nextRegularButtonOrigin - let buttonFrame = CGRect(origin: CGPoint(x: nextButtonOrigin - buttonSize.width, y: expandOffset + (spec.isForExpandedView ? maximumExpandOffset : 0.0)), size: buttonSize) + var buttonFrame = CGRect(origin: CGPoint(x: nextButtonOrigin - buttonSize.width, y: expandOffset + (spec.isForExpandedView ? maximumExpandOffset : 0.0)), size: buttonSize) + if case .postStory = spec.key { + buttonFrame.origin.x -= 12.0 + } nextButtonOrigin -= buttonSize.width + 4.0 if spec.isForExpandedView { nextExpandedButtonOrigin = nextButtonOrigin @@ -1781,7 +1791,10 @@ final class PeerInfoHeaderNavigationButtonContainerNode: SparseNode { if let buttonNode = self.rightButtonNodes[key] { let buttonSize = buttonNode.bounds.size var nextButtonOrigin = spec.isForExpandedView ? nextExpandedButtonOrigin : nextRegularButtonOrigin - let buttonFrame = CGRect(origin: CGPoint(x: nextButtonOrigin - buttonSize.width, y: expandOffset + (spec.isForExpandedView ? maximumExpandOffset : 0.0)), size: buttonSize) + var buttonFrame = CGRect(origin: CGPoint(x: nextButtonOrigin - buttonSize.width, y: expandOffset + (spec.isForExpandedView ? maximumExpandOffset : 0.0)), size: buttonSize) + if case .postStory = spec.key { + buttonFrame.origin.x -= 12.0 + } nextButtonOrigin -= buttonSize.width + 4.0 if spec.isForExpandedView { nextExpandedButtonOrigin = nextButtonOrigin diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index ee74fff9d8..4fc7d41384 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -92,6 +92,7 @@ import StoryContainerScreen import ChatAvatarNavigationNode import PeerReportScreen import WebUI +import ShareWithPeersScreen enum PeerInfoAvatarEditingMode { case generic @@ -2168,6 +2169,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro private var expiringStoryList: PeerExpiringStoryListContext? private var expiringStoryListState: PeerExpiringStoryListContext.State? private var expiringStoryListDisposable: Disposable? + private var postingAvailabilityDisposable: Disposable? private let storiesReady = ValuePromise(true, ignoreRepeated: true) @@ -3608,6 +3610,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } case .qrCode: strongSelf.openQrCode() + case .postStory: + strongSelf.openPostStory() case .editPhoto, .editVideo, .moreToSearch: break } @@ -4004,6 +4008,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro self.translationStateDisposable?.dispose() self.copyProtectionTooltipController?.dismiss() self.expiringStoryListDisposable?.dispose() + self.postingAvailabilityDisposable?.dispose() } override func didLoad() { @@ -8261,6 +8266,84 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro controller.present(ChatQrCodeScreen(context: self.context, subject: .peer(peer: peer, threadId: threadId, temporary: temporary)), in: .window(.root)) } + private func openPostStory() { + self.postingAvailabilityDisposable?.dispose() + + self.postingAvailabilityDisposable = (self.context.engine.messages.checkStoriesUploadAvailability(target: .peer(self.peerId)) + |> deliverOnMainQueue).start(next: { [weak self] status in + guard let self else { + return + } + switch status { + case .available: + var cameraTransitionIn: StoryCameraTransitionIn? + if let rightButton = self.headerNode.navigationButtonContainer.rightButtonNodes[.postStory] { + cameraTransitionIn = StoryCameraTransitionIn( + sourceView: rightButton.view, + sourceRect: rightButton.view.bounds, + sourceCornerRadius: rightButton.view.bounds.height * 0.5 + ) + } + + if let rootController = self.context.sharedContext.mainWindow?.viewController as? TelegramRootControllerInterface { + let coordinator = rootController.openStoryCamera(customTarget: self.peerId, transitionIn: cameraTransitionIn, transitionedIn: {}, transitionOut: self.storyCameraTransitionOut()) + coordinator?.animateIn() + } + case .channelBoostRequired: + let _ = combineLatest( + queue: Queue.mainQueue(), + self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.peerId)), + self.context.engine.peers.getChannelBoostStatus(peerId: self.peerId) + ).start(next: { [weak self] peer, status in + guard let self, let peer, let status else { + return + } + + let link: String + if let addressName = peer.addressName, !addressName.isEmpty { + link = "t.me/\(peer.addressName ?? "")?boost" + } else { + link = "t.me/c/\(peer.id.id._internalGetInt64Value())?boost" + } + + if let navigationController = self.controller?.navigationController as? NavigationController { + if let previousController = navigationController.viewControllers.last as? ShareWithPeersScreen { + previousController.dismiss() + } + let controller = self.context.sharedContext.makePremiumLimitController(context: self.context, subject: .storiesChannelBoost(peer: peer, isCurrent: true, level: Int32(status.level), currentLevelBoosts: Int32(status.currentLevelBoosts), nextLevelBoosts: status.nextLevelBoosts.flatMap(Int32.init), link: link, boosted: false), count: Int32(status.boosts), forceDark: false, cancel: {}, action: { + UIPasteboard.general.string = "https://\(link)" + return true + }) + navigationController.pushViewController(controller) + } + + self.hapticFeedback.impact(.light) + }) + default: + break + } + }).strict() + } + + private func storyCameraTransitionOut() -> (Stories.PendingTarget?, Bool) -> StoryCameraTransitionOut? { + return { [weak self] target, _ in + guard let self else { + return nil + } + let _ = self + + /*if let transitionView = self.headerNode.navigationButtonContainer.rightButtonNodes[.postStory]?.view { + return StoryCameraTransitionOut( + destinationView: transitionView, + destinationRect: transitionView.bounds, + destinationCornerRadius: transitionView.bounds.height * 0.5 + ) + }*/ + + return nil + } + } + fileprivate func openSettings(section: PeerInfoSettingsSection) { let push: (ViewController) -> Void = { [weak self] c in guard let strongSelf = self, let navigationController = strongSelf.controller?.navigationController as? NavigationController else { @@ -9756,6 +9839,10 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } else if peerInfoCanEdit(peer: self.data?.peer, chatLocation: self.chatLocation, threadData: self.data?.threadData, cachedData: self.data?.cachedData, isContact: self.data?.isContact) { rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .edit, isForExpandedView: false)) } + if let data = self.data, data.accountIsPremium, let channel = data.peer as? TelegramChannel, channel.hasPermission(.postStories) { + rightNavigationButtons.insert(PeerInfoHeaderNavigationButtonSpec(key: .postStory, isForExpandedView: false), at: 0) + } + if self.state.selectedMessageIds == nil { if let currentPaneKey = self.paneContainerNode.currentPaneKey { switch currentPaneKey { diff --git a/submodules/TelegramUI/Sources/TelegramRootController.swift b/submodules/TelegramUI/Sources/TelegramRootController.swift index cb76952e84..98064c6b55 100644 --- a/submodules/TelegramUI/Sources/TelegramRootController.swift +++ b/submodules/TelegramUI/Sources/TelegramRootController.swift @@ -266,7 +266,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon } @discardableResult - public func openStoryCamera(transitionIn: StoryCameraTransitionIn?, transitionedIn: @escaping () -> Void, transitionOut: @escaping (Stories.PendingTarget?, Bool) -> StoryCameraTransitionOut?) -> StoryCameraTransitionInCoordinator? { + public func openStoryCamera(customTarget: EnginePeer.Id?, transitionIn: StoryCameraTransitionIn?, transitionedIn: @escaping () -> Void, transitionOut: @escaping (Stories.PendingTarget?, Bool) -> StoryCameraTransitionOut?) -> StoryCameraTransitionInCoordinator? { guard let controller = self.viewControllers.last as? ViewController else { return nil } @@ -379,12 +379,17 @@ public final class TelegramRootController: NavigationController, TelegramRootCon let target: Stories.PendingTarget let targetPeerId: EnginePeer.Id - if let sendAsPeerId = options.sendAsPeerId { - target = .peer(sendAsPeerId) - targetPeerId = sendAsPeerId + if let customTarget { + target = .peer(customTarget) + targetPeerId = customTarget } else { - target = .myStories - targetPeerId = context.account.peerId + if let sendAsPeerId = options.sendAsPeerId { + target = .peer(sendAsPeerId) + targetPeerId = sendAsPeerId + } else { + target = .myStories + targetPeerId = context.account.peerId + } } storyTarget = target