diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index 7564a61896..2d008e1681 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -973,7 +973,7 @@ public protocol MediaEditorScreenResult { public protocol TelegramRootControllerInterface: NavigationController { @discardableResult - func openStoryCamera(customTarget: Stories.PendingTarget?, transitionIn: StoryCameraTransitionIn?, transitionedIn: @escaping () -> Void, transitionOut: @escaping (Stories.PendingTarget?, Bool) -> StoryCameraTransitionOut?) -> StoryCameraTransitionInCoordinator? + func openStoryCamera(customTarget: Stories.PendingTarget?, resumeLiveStream: Bool, transitionIn: StoryCameraTransitionIn?, transitionedIn: @escaping () -> Void, transitionOut: @escaping (Stories.PendingTarget?, Bool) -> StoryCameraTransitionOut?) -> StoryCameraTransitionInCoordinator? func proceedWithStoryUpload(target: Stories.PendingTarget, results: [MediaEditorScreenResult], existingMedia: EngineMedia?, forwardInfo: Stories.PendingForwardInfo?, externalState: MediaEditorTransitionOutExternalState, commit: @escaping (@escaping () -> Void) -> Void) func getContactsController() -> ViewController? diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 4ad49c626a..a44254db78 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -2909,6 +2909,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController var premiumNeeded = false var hasActiveCall = false var hasActiveGroupCall = false + var hasLiveStream = false + + if let componentView = self.chatListHeaderView(), let storyPeerListView = componentView.storyPeerListView(), storyPeerListView.isLiveStreaming { + hasLiveStream = true + } let storiesCountLimit = self.context.userLimits.maxExpiringStoriesCount var storiesCount = 0 @@ -2938,7 +2943,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } } - if reachedCountLimit { + if !hasLiveStream && reachedCountLimit { let context = self.context var replaceImpl: ((ViewController) -> Void)? let controller = PremiumLimitScreen(context: context, subject: .expiringStories, count: Int32(storiesCount), action: { @@ -2955,7 +2960,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController return } - if premiumNeeded || hasActiveCall || hasActiveGroupCall { + if !hasLiveStream && (premiumNeeded || hasActiveCall || hasActiveGroupCall) { if let storyCameraTooltip = self.storyCameraTooltip { self.storyCameraTooltip = nil storyCameraTooltip.dismiss() @@ -3042,7 +3047,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } if let rootController = self.context.sharedContext.mainWindow?.viewController as? TelegramRootControllerInterface { - let coordinator = rootController.openStoryCamera(customTarget: nil, transitionIn: cameraTransitionIn, transitionedIn: {}, transitionOut: self.storyCameraTransitionOut()) + let coordinator = rootController.openStoryCamera(customTarget: nil, resumeLiveStream: hasLiveStream, transitionIn: cameraTransitionIn, transitionedIn: {}, transitionOut: self.storyCameraTransitionOut()) coordinator?.animateIn() } } @@ -6420,7 +6425,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController if let current = self.storyCameraTransitionInCoordinator { coordinator = current } else { - coordinator = rootController.openStoryCamera(customTarget: nil, transitionIn: nil, transitionedIn: {}, transitionOut: { [weak self] target, _ in + coordinator = rootController.openStoryCamera(customTarget: nil, resumeLiveStream: false, transitionIn: nil, transitionedIn: {}, transitionOut: { [weak self] target, _ in guard let self, let target else { return nil } diff --git a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraLiveStreamComponent.swift b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraLiveStreamComponent.swift index dfe3034c58..80a6e982fa 100644 --- a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraLiveStreamComponent.swift +++ b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraLiveStreamComponent.swift @@ -155,6 +155,19 @@ final class CameraLiveStreamComponent: Component { self.storyContentState = state self.state?.updated() }) + + self.inputMediaNodeDataPromise.set( + ChatEntityKeyboardInputNode.inputData( + context: component.context, + chatPeerId: nil, + areCustomEmojiEnabled: true, + hasTrending: true, + hasSearch: true, + hideBackground: true, + maskEdge: .clip, + sendGif: nil + ) + ) } if let storyContentState = self.storyContentState, let slice = storyContentState.slice { diff --git a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift index 80ab2ac57c..e4cfac25c0 100644 --- a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift +++ b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift @@ -374,6 +374,11 @@ private final class CameraScreenComponent: CombinedComponent { } if let controller = getController() { + if controller.resumeLiveStream { + controller.updateCameraState({ $0.updatedMode(.live).updatedIsWaitingForStream(true) }, update: false, transition: .immediate) + self.startLiveStream(rtmp: false) + } + if let customTarget = controller.customTarget { self.sendAsPeerId = customTarget self.isCustomTarget = true @@ -1080,6 +1085,7 @@ private final class CameraScreenComponent: CombinedComponent { }) } + private var storyListContext: PeerExpiringStoryListContext? func startLiveStream(rtmp: Bool) { guard let controller = self.getController() else { return @@ -1117,41 +1123,36 @@ private final class CameraScreenComponent: CombinedComponent { }) } -// let _ = (self.context.engine.messages.storySubscriptions(isHidden: false) -// |> take(1) -// |> deliverOnMainQueue).start(next: { [weak self, weak controller] subscriptions in -// guard let self else { -// return -// } -// if subscriptions.accountItem?.hasLiveItems == true { -// let storyList = PeerExpiringStoryListContext(account: self.context.account, peerId: peerId) -// let _ = (storyList.state -// |> filter { !$0.isLoading } -// |> take(1) -// |> deliverOnMainQueue).start(next: { [weak self, weak controller] state in -// guard let self else { -// return -// } -// for item in state.items.reversed() { -// let _ = (self.context.engine.messages.getStory(peerId: peerId, id: item.id) -// |> deliverOnMainQueue).start(next: { [weak self, weak controller] item in -// guard let self, let item else { -// return -// } -// if case .liveStream = item.media { -// self.liveStreamStory = item -// controller?.updateCameraState({ $0.updatedIsStreaming(true) }, transition: .spring(duration: 0.4)) -// self.updated(transition: .immediate) -// return -// } -// }) -// } -// startNewStream() -// }) -// } else { + let _ = (self.context.engine.messages.storySubscriptions(isHidden: false) + |> take(1) + |> deliverOnMainQueue).start(next: { [weak self, weak controller] subscriptions in + guard let self else { + return + } + if subscriptions.accountItem?.hasLiveItems == true { + let storyList = PeerExpiringStoryListContext(account: self.context.account, peerId: peerId) + self.storyListContext = storyList + let _ = (storyList.state + |> filter { !$0.isLoading } + |> take(1) + |> deliverOnMainQueue).start(next: { [weak self, weak controller] state in + guard let self else { + return + } + for item in state.items.reversed() { + if case let .item(item) = item, case .liveStream = item.media { + self.liveStreamStory = item + controller?.updateCameraState({ $0.updatedIsStreaming(true) }, transition: .spring(duration: 0.4)) + self.updated(transition: .immediate) + return + } + } + startNewLiveStream() + }) + } else { startNewLiveStream() -// } -// }) + } + }) } func endLiveStream() { @@ -2815,7 +2816,7 @@ public class CameraScreenImpl: ViewController, CameraScreen { return false } } - if let code = filteredCodes.first, !self.cameraState.isCollageEnabled && self.cameraState.recording == CameraState.Recording.none { + if let code = filteredCodes.first, !self.cameraState.isCollageEnabled && self.cameraState.recording == CameraState.Recording.none && self.cameraState.mode != .live { self.controller?.updateFocusedCode(code) } else { self.controller?.updateFocusedCode(nil) @@ -3915,6 +3916,7 @@ public class CameraScreenImpl: ViewController, CameraScreen { private let context: AccountContext fileprivate let mode: Mode fileprivate let customTarget: EnginePeer.Id? + fileprivate let resumeLiveStream: Bool fileprivate let holder: CameraHolder? fileprivate let transitionIn: TransitionIn? fileprivate let transitionOut: (Bool) -> TransitionOut? @@ -3972,15 +3974,18 @@ public class CameraScreenImpl: ViewController, CameraScreen { public var isEmbedded = false - fileprivate func updateCameraState(_ f: (CameraState) -> CameraState, transition: ComponentTransition) { + fileprivate func updateCameraState(_ f: (CameraState) -> CameraState, update: Bool = true, transition: ComponentTransition) { self.node.cameraState = f(self.node.cameraState) - self.node.requestUpdateLayout(transition: transition) + if update { + self.node.requestUpdateLayout(transition: transition) + } } public init( context: AccountContext, mode: Mode, customTarget: EnginePeer.Id? = nil, + resumeLiveStream: Bool = false, holder: CameraHolder? = nil, transitionIn: TransitionIn?, transitionOut: @escaping (Bool) -> TransitionOut?, @@ -3989,6 +3994,7 @@ public class CameraScreenImpl: ViewController, CameraScreen { self.context = context self.mode = mode self.customTarget = customTarget + self.resumeLiveStream = resumeLiveStream self.holder = holder self.transitionIn = transitionIn self.transitionOut = transitionOut @@ -4030,7 +4036,7 @@ public class CameraScreenImpl: ViewController, CameraScreen { super.displayNodeDidLoad() self.node.didAppear = { [weak self] in - guard let self else { + guard let self, !self.resumeLiveStream else { return } self.postingAvailabilityDisposable = (self.postingAvailabilityPromise.get() diff --git a/submodules/TelegramUI/Components/Gifts/GiftItemComponent/Sources/GiftItemComponent.swift b/submodules/TelegramUI/Components/Gifts/GiftItemComponent/Sources/GiftItemComponent.swift index b77bc4a5bc..ac7233667d 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftItemComponent/Sources/GiftItemComponent.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftItemComponent/Sources/GiftItemComponent.swift @@ -634,7 +634,7 @@ public final class GiftItemComponent: Component { if case let .starGift(gift, _) = component.subject, gift.flags.contains(.isAuction) { buttonColor = component.theme.overallDarkAppearance ? UIColor(rgb: 0xffc337) : UIColor(rgb: 0xd3720a) //todo:localize - price = "Place a Bid" + price = "Join" } else { if priceValue.contains("#") { buttonColor = component.theme.overallDarkAppearance ? UIColor(rgb: 0xffc337) : UIColor(rgb: 0xd3720a) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index c47c21c951..ad318df6ff 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -10646,7 +10646,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } if let rootController = self.context.sharedContext.mainWindow?.viewController as? TelegramRootControllerInterface { - let coordinator = rootController.openStoryCamera(customTarget: self.peerId == self.context.account.peerId ? nil : .peer(self.peerId), transitionIn: cameraTransitionIn, transitionedIn: {}, transitionOut: self.storyCameraTransitionOut()) + let coordinator = rootController.openStoryCamera(customTarget: self.peerId == self.context.account.peerId ? nil : .peer(self.peerId), resumeLiveStream: false, transitionIn: cameraTransitionIn, transitionedIn: {}, transitionOut: self.storyCameraTransitionOut()) coordinator?.animateIn() } case .channelBoostRequired: diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoStoryGridScreen/Sources/PeerInfoStoryGridScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoStoryGridScreen/Sources/PeerInfoStoryGridScreen.swift index ac6dc07ef7..22e0ecf465 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoStoryGridScreen/Sources/PeerInfoStoryGridScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoStoryGridScreen/Sources/PeerInfoStoryGridScreen.swift @@ -310,7 +310,7 @@ final class PeerInfoStoryGridScreenComponent: Component { return } if let rootController = component.context.sharedContext.mainWindow?.viewController as? TelegramRootControllerInterface { - let coordinator = rootController.openStoryCamera(customTarget: nil, transitionIn: nil, transitionedIn: {}, transitionOut: { _, _ in return nil }) + let coordinator = rootController.openStoryCamera(customTarget: nil, resumeLiveStream: false, transitionIn: nil, transitionedIn: {}, transitionOut: { _, _ in return nil }) coordinator?.animateIn() } } diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift index 19dda418c3..a4959ce403 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift @@ -1147,13 +1147,21 @@ final class StoryItemContentComponent: Component { if self.liveCallStatsDisposable == nil { self.liveCallStatsDisposable = (mediaStreamCall.members |> deliverOnMainQueue).startStandalone(next: { [weak self] members in - guard let self, let environment = self.environment else { + guard let self, let component = self.component, let environment = self.environment else { return } //TODO:localize let subtitle: String if let members { - subtitle = "\(max(1, members.totalCount)) watching" + var totalCount = members.totalCount + if component.isEmbeddedInCamera { + totalCount -= 1 + } + if totalCount == 0 && component.isEmbeddedInCamera { + subtitle = "no viewers" + } else { + subtitle = "\(max(1, totalCount)) watching" + } } else { subtitle = "loading..." } diff --git a/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListComponent.swift b/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListComponent.swift index c9c1f9a1bc..544deae7ab 100644 --- a/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListComponent.swift @@ -533,6 +533,18 @@ public final class StoryPeerListComponent: Component { } } + public var isLiveStreaming: Bool { + guard let component = self.component else { + return false + } + for itemSet in self.sortedItems { + if itemSet.peer.id == component.context.account.peerId, itemSet.hasLiveItems { + return true + } + } + return false + } + public func transitionViewForItem(peerId: EnginePeer.Id) -> (UIView, StoryContainerScreen.TransitionView)? { if self.collapsedButton.isUserInteractionEnabled { return nil diff --git a/submodules/TelegramUI/Sources/TelegramRootController.swift b/submodules/TelegramUI/Sources/TelegramRootController.swift index 797fce2cbd..c0f1427eee 100644 --- a/submodules/TelegramUI/Sources/TelegramRootController.swift +++ b/submodules/TelegramUI/Sources/TelegramRootController.swift @@ -301,7 +301,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon } @discardableResult - public func openStoryCamera(customTarget: Stories.PendingTarget?, transitionIn: StoryCameraTransitionIn?, transitionedIn: @escaping () -> Void, transitionOut: @escaping (Stories.PendingTarget?, Bool) -> StoryCameraTransitionOut?) -> StoryCameraTransitionInCoordinator? { + public func openStoryCamera(customTarget: Stories.PendingTarget?, resumeLiveStream: Bool, transitionIn: StoryCameraTransitionIn?, transitionedIn: @escaping () -> Void, transitionOut: @escaping (Stories.PendingTarget?, Bool) -> StoryCameraTransitionOut?) -> StoryCameraTransitionInCoordinator? { guard let controller = self.viewControllers.last as? ViewController else { return nil } @@ -335,6 +335,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon context: context, mode: .story, customTarget: mediaEditorCustomTarget, + resumeLiveStream: resumeLiveStream, transitionIn: transitionIn.flatMap { if let sourceView = $0.sourceView { return CameraScreenImpl.TransitionIn(