diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index d8faf72ab7..6fd78b7dc6 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -1244,7 +1244,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController return } - let storyContent = StoryContentContextImpl(context: self.context, includeHidden: false, focusedPeerId: peerId) + let storyContent = StoryContentContextImpl(context: self.context, includeHidden: false, focusedPeerId: peerId, singlePeer: false) let _ = (storyContent.state |> filter { $0.slice != nil } |> take(1) @@ -2325,7 +2325,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController return } - let storyContent = StoryContentContextImpl(context: self.context, includeHidden: false, focusedPeerId: peer?.id) + let storyContent = StoryContentContextImpl(context: self.context, includeHidden: false, focusedPeerId: peer?.id, singlePeer: false) let _ = (storyContent.state |> take(1) |> deliverOnMainQueue).start(next: { [weak self] storyContentState in diff --git a/submodules/ContactListUI/Sources/ContactsController.swift b/submodules/ContactListUI/Sources/ContactsController.swift index cc14d02e07..16ad97877a 100644 --- a/submodules/ContactListUI/Sources/ContactsController.swift +++ b/submodules/ContactListUI/Sources/ContactsController.swift @@ -515,7 +515,7 @@ public class ContactsController: ViewController { return } - let storyContent = StoryContentContextImpl(context: self.context, includeHidden: true, focusedPeerId: peer?.id) + let storyContent = StoryContentContextImpl(context: self.context, includeHidden: true, focusedPeerId: peer?.id, singlePeer: false) let _ = (storyContent.state |> take(1) |> deliverOnMainQueue).start(next: { [weak self] storyContentState in diff --git a/submodules/TelegramUI/Components/Stories/StoryContentComponent/Sources/StoryChatContent.swift b/submodules/TelegramUI/Components/Stories/StoryContentComponent/Sources/StoryChatContent.swift index 15d64a0607..a7f5954fdd 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContentComponent/Sources/StoryChatContent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContentComponent/Sources/StoryChatContent.swift @@ -328,10 +328,13 @@ public final class StoryContentContextImpl: StoryContentContext { private var preloadStoryResourceDisposables: [MediaResourceId: Disposable] = [:] private var pollStoryMetadataDisposables = DisposableSet() + private var singlePeerListContext: PeerExpiringStoryListContext? + public init( context: AccountContext, includeHidden: Bool, - focusedPeerId: EnginePeer.Id? + focusedPeerId: EnginePeer.Id?, + singlePeer: Bool ) { self.context = context self.includeHidden = includeHidden @@ -339,73 +342,163 @@ public final class StoryContentContextImpl: StoryContentContext { self.focusedItem = (focusedPeerId, nil) } - self.storySubscriptionsDisposable = (context.engine.messages.storySubscriptions(includeHidden: includeHidden) - |> deliverOnMainQueue).start(next: { [weak self] storySubscriptions in - guard let self else { + if singlePeer { + guard let focusedPeerId else { + assertionFailure() return } - - let startedWithUnseen: Bool - if let current = self.startedWithUnseen { - startedWithUnseen = current - } else { - var startedWithUnseenValue = false + let singlePeerListContext = PeerExpiringStoryListContext(account: context.account, peerId: focusedPeerId) + self.singlePeerListContext = singlePeerListContext + self.storySubscriptionsDisposable = (combineLatest( + context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: focusedPeerId)) + singlePeerListContext.state + ) + |> deliverOnMainQueue).start(next: { [weak self] peer, state in + guard let self, let peer else { + return + } - if let (focusedPeerId, _) = self.focusedItem, focusedPeerId == self.context.account.peerId { + let storySubscriptions = EngineStorySubscriptions( + accountItem: nil, + items: [EngineStorySubscriptions.Item( + peer: peer, + hasUnseen: state.hasUnseen, + storyCount: state.items.count, + lastTimestamp: state.items.last?.timestamp ?? 0 + )], + hasMoreToken: nil + ) + + let startedWithUnseen: Bool + if let current = self.startedWithUnseen { + startedWithUnseen = current } else { - var centralIndex: Int? - if let (focusedPeerId, _) = self.focusedItem { - if let index = storySubscriptions.items.firstIndex(where: { $0.peer.id == focusedPeerId }) { - centralIndex = index + var startedWithUnseenValue = false + + if let (focusedPeerId, _) = self.focusedItem, focusedPeerId == self.context.account.peerId { + } else { + var centralIndex: Int? + if let (focusedPeerId, _) = self.focusedItem { + if let index = storySubscriptions.items.firstIndex(where: { $0.peer.id == focusedPeerId }) { + centralIndex = index + } } - } - if centralIndex == nil { - if let index = storySubscriptions.items.firstIndex(where: { $0.hasUnseen }) { - centralIndex = index + if centralIndex == nil { + if let index = storySubscriptions.items.firstIndex(where: { $0.hasUnseen }) { + centralIndex = index + } } - } - if centralIndex == nil { - if !storySubscriptions.items.isEmpty { - centralIndex = 0 + if centralIndex == nil { + if !storySubscriptions.items.isEmpty { + centralIndex = 0 + } + } + + if let centralIndex { + if storySubscriptions.items[centralIndex].hasUnseen { + startedWithUnseenValue = true + } } } - if let centralIndex { - if storySubscriptions.items[centralIndex].hasUnseen { - startedWithUnseenValue = true - } - } + self.startedWithUnseen = startedWithUnseenValue + startedWithUnseen = startedWithUnseenValue } - self.startedWithUnseen = startedWithUnseenValue - startedWithUnseen = startedWithUnseenValue - } - - var sortedItems: [EngineStorySubscriptions.Item] = [] - for peerId in self.fixedSubscriptionOrder { - if let index = storySubscriptions.items.firstIndex(where: { $0.peer.id == peerId }) { - sortedItems.append(storySubscriptions.items[index]) + var sortedItems: [EngineStorySubscriptions.Item] = [] + for peerId in self.fixedSubscriptionOrder { + if let index = storySubscriptions.items.firstIndex(where: { $0.peer.id == peerId }) { + sortedItems.append(storySubscriptions.items[index]) + } } - } - for item in storySubscriptions.items { - if !sortedItems.contains(where: { $0.peer.id == item.peer.id }) { - if startedWithUnseen { - if !item.hasUnseen { - continue + for item in storySubscriptions.items { + if !sortedItems.contains(where: { $0.peer.id == item.peer.id }) { + if startedWithUnseen { + if !item.hasUnseen { + continue + } + } + sortedItems.append(item) + } + } + self.fixedSubscriptionOrder = sortedItems.map(\.peer.id) + + self.storySubscriptions = EngineStorySubscriptions( + accountItem: storySubscriptions.accountItem, + items: sortedItems, + hasMoreToken: storySubscriptions.hasMoreToken + ) + self.updatePeerContexts() + }) + } else { + self.storySubscriptionsDisposable = (context.engine.messages.storySubscriptions(includeHidden: includeHidden) + |> deliverOnMainQueue).start(next: { [weak self] storySubscriptions in + guard let self else { + return + } + + let startedWithUnseen: Bool + if let current = self.startedWithUnseen { + startedWithUnseen = current + } else { + var startedWithUnseenValue = false + + if let (focusedPeerId, _) = self.focusedItem, focusedPeerId == self.context.account.peerId { + } else { + var centralIndex: Int? + if let (focusedPeerId, _) = self.focusedItem { + if let index = storySubscriptions.items.firstIndex(where: { $0.peer.id == focusedPeerId }) { + centralIndex = index + } + } + if centralIndex == nil { + if let index = storySubscriptions.items.firstIndex(where: { $0.hasUnseen }) { + centralIndex = index + } + } + if centralIndex == nil { + if !storySubscriptions.items.isEmpty { + centralIndex = 0 + } + } + + if let centralIndex { + if storySubscriptions.items[centralIndex].hasUnseen { + startedWithUnseenValue = true + } } } - sortedItems.append(item) + + self.startedWithUnseen = startedWithUnseenValue + startedWithUnseen = startedWithUnseenValue } - } - self.fixedSubscriptionOrder = sortedItems.map(\.peer.id) - - self.storySubscriptions = EngineStorySubscriptions( - accountItem: storySubscriptions.accountItem, - items: sortedItems, - hasMoreToken: storySubscriptions.hasMoreToken - ) - self.updatePeerContexts() - }) + + var sortedItems: [EngineStorySubscriptions.Item] = [] + for peerId in self.fixedSubscriptionOrder { + if let index = storySubscriptions.items.firstIndex(where: { $0.peer.id == peerId }) { + sortedItems.append(storySubscriptions.items[index]) + } + } + for item in storySubscriptions.items { + if !sortedItems.contains(where: { $0.peer.id == item.peer.id }) { + if startedWithUnseen { + if !item.hasUnseen { + continue + } + } + sortedItems.append(item) + } + } + self.fixedSubscriptionOrder = sortedItems.map(\.peer.id) + + self.storySubscriptions = EngineStorySubscriptions( + accountItem: storySubscriptions.accountItem, + items: sortedItems, + hasMoreToken: storySubscriptions.hasMoreToken + ) + self.updatePeerContexts() + }) + } } deinit { @@ -415,6 +508,7 @@ public final class StoryContentContextImpl: StoryContentContext { disposable.dispose() } self.pollStoryMetadataDisposables.dispose() + self.storySubscriptionsDisposable?.dispose() } private func updatePeerContexts() { diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 9618dd1fe0..f8cb4c7bdf 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -88,6 +88,8 @@ import AvatarEditorScreen import SendInviteLinkScreen import PeerInfoVisualMediaPaneNode import PeerInfoStoryGridScreen +import StoryContainerScreen +import StoryContentComponent enum PeerInfoAvatarEditingMode { case generic @@ -2146,6 +2148,10 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro private var translationState: ChatTranslationState? private var translationStateDisposable: Disposable? + private var expiringStoryList: PeerExpiringStoryListContext? + private var expiringStoryListState: PeerExpiringStoryListContext.State? + private var expiringStoryListDisposable: Disposable? + private let _ready = Promise() var ready: Promise { return self._ready @@ -3038,6 +3044,58 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro return } + if !gallery, let expiringStoryList = strongSelf.expiringStoryList, let expiringStoryListState = strongSelf.expiringStoryListState, !expiringStoryListState.items.isEmpty { + let _ = expiringStoryList + let storyContent = StoryContentContextImpl(context: strongSelf.context, includeHidden: false, focusedPeerId: strongSelf.peerId, singlePeer: true) + let _ = (storyContent.state + |> take(1) + |> deliverOnMainQueue).start(next: { storyContentState in + guard let self else { + return + } + var transitionIn: StoryContainerScreen.TransitionIn? + transitionIn = nil + + let transitionView = self.headerNode.avatarListNode.avatarContainerNode.avatarNode.view + transitionIn = StoryContainerScreen.TransitionIn( + sourceView: transitionView, + sourceRect: transitionView.bounds, + sourceCornerRadius: transitionView.bounds.height * 0.5 + ) + + self.headerNode.avatarListNode.avatarContainerNode.avatarNode.isHidden = true + + let storyContainerScreen = StoryContainerScreen( + context: self.context, + content: storyContent, + transitionIn: transitionIn, + transitionOut: { [weak self] peerId, _ in + guard let self else { + return nil + } + + let transitionView = self.headerNode.avatarListNode.avatarContainerNode.avatarNode.view + return StoryContainerScreen.TransitionOut( + destinationView: transitionView, + transitionView: nil, + destinationRect: transitionView.bounds, + destinationCornerRadius: transitionView.bounds.height * 0.5, + destinationIsAvatar: true, + completed: { [weak self] in + guard let self else { + return + } + self.headerNode.avatarListNode.avatarContainerNode.avatarNode.isHidden = false + } + ) + } + ) + self.controller?.push(storyContainerScreen) + }) + + return + } + guard peer.smallProfileImage != nil else { return } @@ -3852,6 +3910,16 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro |> deliverOnMainQueue).start(next: { [weak self] translationState in self?.translationState = translationState }) + } else if peerId.namespace == Namespaces.Peer.CloudUser { + let expiringStoryList = PeerExpiringStoryListContext(account: context.account, peerId: peerId) + self.expiringStoryList = expiringStoryList + self.expiringStoryListDisposable = (expiringStoryList.state + |> deliverOnMainQueue).start(next: { [weak self] state in + guard let self else { + return + } + self.expiringStoryListState = state + }) } } @@ -3877,8 +3945,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro self.refreshMessageTagStatsDisposable?.dispose() self.forumTopicNotificationExceptionsDisposable?.dispose() self.translationStateDisposable?.dispose() - self.copyProtectionTooltipController?.dismiss() + self.expiringStoryListDisposable?.dispose() } override func didLoad() {