[WIP] Stories

This commit is contained in:
Ali 2023-06-09 16:42:24 +04:00
parent e42e8c0ba7
commit 1ffc683a82
4 changed files with 219 additions and 57 deletions

View File

@ -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

View File

@ -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

View File

@ -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() {

View File

@ -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<Bool>()
var ready: Promise<Bool> {
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() {