mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
[WIP] Stories
This commit is contained in:
parent
e42e8c0ba7
commit
1ffc683a82
@ -1244,7 +1244,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
return
|
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
|
let _ = (storyContent.state
|
||||||
|> filter { $0.slice != nil }
|
|> filter { $0.slice != nil }
|
||||||
|> take(1)
|
|> take(1)
|
||||||
@ -2325,7 +2325,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
return
|
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
|
let _ = (storyContent.state
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] storyContentState in
|
|> deliverOnMainQueue).start(next: { [weak self] storyContentState in
|
||||||
|
@ -515,7 +515,7 @@ public class ContactsController: ViewController {
|
|||||||
return
|
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
|
let _ = (storyContent.state
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] storyContentState in
|
|> deliverOnMainQueue).start(next: { [weak self] storyContentState in
|
||||||
|
@ -328,10 +328,13 @@ public final class StoryContentContextImpl: StoryContentContext {
|
|||||||
private var preloadStoryResourceDisposables: [MediaResourceId: Disposable] = [:]
|
private var preloadStoryResourceDisposables: [MediaResourceId: Disposable] = [:]
|
||||||
private var pollStoryMetadataDisposables = DisposableSet()
|
private var pollStoryMetadataDisposables = DisposableSet()
|
||||||
|
|
||||||
|
private var singlePeerListContext: PeerExpiringStoryListContext?
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
includeHidden: Bool,
|
includeHidden: Bool,
|
||||||
focusedPeerId: EnginePeer.Id?
|
focusedPeerId: EnginePeer.Id?,
|
||||||
|
singlePeer: Bool
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.includeHidden = includeHidden
|
self.includeHidden = includeHidden
|
||||||
@ -339,73 +342,163 @@ public final class StoryContentContextImpl: StoryContentContext {
|
|||||||
self.focusedItem = (focusedPeerId, nil)
|
self.focusedItem = (focusedPeerId, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.storySubscriptionsDisposable = (context.engine.messages.storySubscriptions(includeHidden: includeHidden)
|
if singlePeer {
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] storySubscriptions in
|
guard let focusedPeerId else {
|
||||||
guard let self else {
|
assertionFailure()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
let singlePeerListContext = PeerExpiringStoryListContext(account: context.account, peerId: focusedPeerId)
|
||||||
let startedWithUnseen: Bool
|
self.singlePeerListContext = singlePeerListContext
|
||||||
if let current = self.startedWithUnseen {
|
self.storySubscriptionsDisposable = (combineLatest(
|
||||||
startedWithUnseen = current
|
context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: focusedPeerId))
|
||||||
} else {
|
singlePeerListContext.state
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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()
|
|> deliverOnMainQueue).start(next: { [weak self] peer, state in
|
||||||
})
|
guard let self, let peer else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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 {
|
deinit {
|
||||||
@ -415,6 +508,7 @@ public final class StoryContentContextImpl: StoryContentContext {
|
|||||||
disposable.dispose()
|
disposable.dispose()
|
||||||
}
|
}
|
||||||
self.pollStoryMetadataDisposables.dispose()
|
self.pollStoryMetadataDisposables.dispose()
|
||||||
|
self.storySubscriptionsDisposable?.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updatePeerContexts() {
|
private func updatePeerContexts() {
|
||||||
|
@ -88,6 +88,8 @@ import AvatarEditorScreen
|
|||||||
import SendInviteLinkScreen
|
import SendInviteLinkScreen
|
||||||
import PeerInfoVisualMediaPaneNode
|
import PeerInfoVisualMediaPaneNode
|
||||||
import PeerInfoStoryGridScreen
|
import PeerInfoStoryGridScreen
|
||||||
|
import StoryContainerScreen
|
||||||
|
import StoryContentComponent
|
||||||
|
|
||||||
enum PeerInfoAvatarEditingMode {
|
enum PeerInfoAvatarEditingMode {
|
||||||
case generic
|
case generic
|
||||||
@ -2146,6 +2148,10 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||||||
private var translationState: ChatTranslationState?
|
private var translationState: ChatTranslationState?
|
||||||
private var translationStateDisposable: Disposable?
|
private var translationStateDisposable: Disposable?
|
||||||
|
|
||||||
|
private var expiringStoryList: PeerExpiringStoryListContext?
|
||||||
|
private var expiringStoryListState: PeerExpiringStoryListContext.State?
|
||||||
|
private var expiringStoryListDisposable: Disposable?
|
||||||
|
|
||||||
private let _ready = Promise<Bool>()
|
private let _ready = Promise<Bool>()
|
||||||
var ready: Promise<Bool> {
|
var ready: Promise<Bool> {
|
||||||
return self._ready
|
return self._ready
|
||||||
@ -3038,6 +3044,58 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||||||
return
|
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 {
|
guard peer.smallProfileImage != nil else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -3852,6 +3910,16 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||||||
|> deliverOnMainQueue).start(next: { [weak self] translationState in
|
|> deliverOnMainQueue).start(next: { [weak self] translationState in
|
||||||
self?.translationState = translationState
|
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.refreshMessageTagStatsDisposable?.dispose()
|
||||||
self.forumTopicNotificationExceptionsDisposable?.dispose()
|
self.forumTopicNotificationExceptionsDisposable?.dispose()
|
||||||
self.translationStateDisposable?.dispose()
|
self.translationStateDisposable?.dispose()
|
||||||
|
|
||||||
self.copyProtectionTooltipController?.dismiss()
|
self.copyProtectionTooltipController?.dismiss()
|
||||||
|
self.expiringStoryListDisposable?.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func didLoad() {
|
override func didLoad() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user