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
3a2f75ab82
commit
9b7e421107
@ -3493,7 +3493,9 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
||||
headerNodesTransition.0.animatePositionAdditive(node: topItemOverscrollBackground, offset: CGPoint(x: 0.0, y: -headerNodesTransition.2))
|
||||
}
|
||||
|
||||
self.updateVisibleContentOffset()
|
||||
if !self.useMainQueueTransactions {
|
||||
self.updateVisibleContentOffset()
|
||||
}
|
||||
|
||||
if self.debugInfo {
|
||||
//let delta = CACurrentMediaTime() - timestamp
|
||||
@ -3501,6 +3503,10 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
||||
}
|
||||
|
||||
completion()
|
||||
|
||||
if self.useMainQueueTransactions {
|
||||
self.updateVisibleContentOffset()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4351,10 +4351,6 @@ func replayFinalState(
|
||||
}
|
||||
}
|
||||
|
||||
var filteredSubscriptionsOpaqueState: String?
|
||||
if let state = transaction.getSubscriptionsStoriesState(key: .filtered)?.get(Stories.SubscriptionsState.self) {
|
||||
filteredSubscriptionsOpaqueState = state.opaqueState
|
||||
}
|
||||
var appliedMaxReadId: Int32?
|
||||
if let currentState = transaction.getPeerStoryState(peerId: peerId)?.get(Stories.PeerState.self) {
|
||||
if let appliedMaxReadIdValue = appliedMaxReadId {
|
||||
@ -4366,7 +4362,6 @@ func replayFinalState(
|
||||
|
||||
transaction.setStoryItems(peerId: peerId, items: updatedPeerEntries)
|
||||
transaction.setPeerStoryState(peerId: peerId, state: CodableEntry(Stories.PeerState(
|
||||
subscriptionsOpaqueState: filteredSubscriptionsOpaqueState,
|
||||
maxReadId: appliedMaxReadId ?? 0
|
||||
)))
|
||||
|
||||
@ -4381,12 +4376,7 @@ func replayFinalState(
|
||||
appliedMaxReadId = max(appliedMaxReadId, currentState.maxReadId)
|
||||
}
|
||||
|
||||
var filteredSubscriptionsOpaqueState: String?
|
||||
if let state = transaction.getSubscriptionsStoriesState(key: .filtered)?.get(Stories.SubscriptionsState.self) {
|
||||
filteredSubscriptionsOpaqueState = state.opaqueState
|
||||
}
|
||||
transaction.setPeerStoryState(peerId: peerId, state: CodableEntry(Stories.PeerState(
|
||||
subscriptionsOpaqueState: filteredSubscriptionsOpaqueState,
|
||||
maxReadId: appliedMaxReadId
|
||||
)))
|
||||
|
||||
|
@ -106,6 +106,7 @@ public struct Namespaces {
|
||||
public static let featuredStickersConfiguration: Int8 = 24
|
||||
public static let emojiSearchCategories: Int8 = 25
|
||||
public static let cachedEmojiQueryResults: Int8 = 26
|
||||
public static let cachedPeerStoryListHeads: Int8 = 27
|
||||
}
|
||||
|
||||
public struct UnorderedItemList {
|
||||
|
@ -352,39 +352,30 @@ public enum Stories {
|
||||
|
||||
public final class PeerState: Equatable, Codable {
|
||||
private enum CodingKeys: CodingKey {
|
||||
case subscriptionsOpaqueState
|
||||
case maxReadId
|
||||
}
|
||||
|
||||
public let subscriptionsOpaqueState: String?
|
||||
public let maxReadId: Int32
|
||||
|
||||
public init(
|
||||
subscriptionsOpaqueState: String?,
|
||||
maxReadId: Int32
|
||||
){
|
||||
self.subscriptionsOpaqueState = subscriptionsOpaqueState
|
||||
self.maxReadId = maxReadId
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
self.subscriptionsOpaqueState = try container.decodeIfPresent(String.self, forKey: .subscriptionsOpaqueState)
|
||||
self.maxReadId = try container.decode(Int32.self, forKey: .maxReadId)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
try container.encodeIfPresent(self.subscriptionsOpaqueState, forKey: .subscriptionsOpaqueState)
|
||||
try container.encode(self.maxReadId, forKey: .maxReadId)
|
||||
}
|
||||
|
||||
public static func ==(lhs: PeerState, rhs: PeerState) -> Bool {
|
||||
if lhs.subscriptionsOpaqueState != rhs.subscriptionsOpaqueState {
|
||||
return false
|
||||
}
|
||||
if lhs.maxReadId != rhs.maxReadId {
|
||||
return false
|
||||
}
|
||||
@ -864,7 +855,6 @@ func _internal_markStoryAsSeen(account: Account, peerId: PeerId, id: Int32, asPi
|
||||
return account.postbox.transaction { transaction -> Api.InputUser? in
|
||||
if let peerStoryState = transaction.getPeerStoryState(peerId: peerId)?.get(Stories.PeerState.self) {
|
||||
transaction.setPeerStoryState(peerId: peerId, state: CodableEntry(Stories.PeerState(
|
||||
subscriptionsOpaqueState: peerStoryState.subscriptionsOpaqueState,
|
||||
maxReadId: max(peerStoryState.maxReadId, id)
|
||||
)))
|
||||
}
|
||||
|
@ -318,15 +318,6 @@ public final class StorySubscriptionsContext {
|
||||
var updatedPeerEntries: [StoryItemsTableEntry] = []
|
||||
for story in stories {
|
||||
if let storedItem = Stories.StoredItem(apiStoryItem: story, peerId: peerId, transaction: transaction) {
|
||||
/*#if DEBUG
|
||||
if "".isEmpty {
|
||||
if let codedEntry = CodableEntry(Stories.StoredItem.placeholder(Stories.Placeholder(id: storedItem.id, timestamp: storedItem.timestamp))) {
|
||||
updatedPeerEntries.append(StoryItemsTableEntry(value: codedEntry, id: storedItem.id))
|
||||
}
|
||||
continue
|
||||
}
|
||||
#endif*/
|
||||
|
||||
if case .placeholder = storedItem, let previousEntry = previousPeerEntries.first(where: { $0.id == storedItem.id }) {
|
||||
updatedPeerEntries.append(previousEntry)
|
||||
} else {
|
||||
@ -341,7 +332,6 @@ public final class StorySubscriptionsContext {
|
||||
|
||||
transaction.setStoryItems(peerId: peerId, items: updatedPeerEntries)
|
||||
transaction.setPeerStoryState(peerId: peerId, state: CodableEntry(Stories.PeerState(
|
||||
subscriptionsOpaqueState: state,
|
||||
maxReadId: maxReadId ?? 0
|
||||
)))
|
||||
}
|
||||
@ -415,122 +405,470 @@ public final class StorySubscriptionsContext {
|
||||
}
|
||||
}
|
||||
|
||||
private final class CachedPeerStoryListHead: Codable {
|
||||
let items: [Stories.StoredItem]
|
||||
let totalCount: Int32
|
||||
|
||||
init(items: [Stories.StoredItem], totalCount: Int32) {
|
||||
self.items = items
|
||||
self.totalCount = totalCount
|
||||
}
|
||||
}
|
||||
|
||||
public final class PeerStoryListContext {
|
||||
private final class Impl {
|
||||
private let queue: Queue
|
||||
private let account: Account
|
||||
private let peerId: EnginePeer.Id
|
||||
private let isArchived: Bool
|
||||
|
||||
private let statePromise = Promise<State>()
|
||||
private var stateValue: State {
|
||||
didSet {
|
||||
self.statePromise.set(.single(self.stateValue))
|
||||
}
|
||||
}
|
||||
var state: Signal<State, NoError> {
|
||||
return self.statePromise.get()
|
||||
}
|
||||
|
||||
private var isLoadingMore: Bool = false
|
||||
private var requestDisposable: Disposable?
|
||||
|
||||
private var updatesDisposable: Disposable?
|
||||
|
||||
init(queue: Queue, account: Account, peerId: EnginePeer.Id, isArchived: Bool) {
|
||||
self.queue = queue
|
||||
self.account = account
|
||||
self.peerId = peerId
|
||||
self.isArchived = isArchived
|
||||
|
||||
self.stateValue = State(peerReference: nil, items: [], totalCount: 0, loadMoreToken: 0, isCached: true)
|
||||
|
||||
let _ = (account.postbox.transaction { transaction -> (PeerReference?, [EngineStoryItem], Int) in
|
||||
let key = ValueBoxKey(length: 8 + 1)
|
||||
key.setInt64(0, value: peerId.toInt64())
|
||||
key.setInt8(8, value: isArchived ? 1 : 0)
|
||||
let cached = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerStoryListHeads, key: key))?.get(CachedPeerStoryListHead.self)
|
||||
guard let cached = cached else {
|
||||
return (nil, [], 0)
|
||||
}
|
||||
var items: [EngineStoryItem] = []
|
||||
for storedItem in cached.items {
|
||||
if case let .item(item) = storedItem, let media = item.media {
|
||||
let mappedItem = EngineStoryItem(
|
||||
id: item.id,
|
||||
timestamp: item.timestamp,
|
||||
expirationTimestamp: item.expirationTimestamp,
|
||||
media: EngineMedia(media),
|
||||
text: item.text,
|
||||
entities: item.entities,
|
||||
views: item.views.flatMap { views in
|
||||
return EngineStoryItem.Views(
|
||||
seenCount: views.seenCount,
|
||||
seenPeers: views.seenPeerIds.compactMap { id -> EnginePeer? in
|
||||
return transaction.getPeer(id).flatMap(EnginePeer.init)
|
||||
}
|
||||
)
|
||||
},
|
||||
privacy: item.privacy.flatMap(EngineStoryPrivacy.init),
|
||||
isPinned: item.isPinned,
|
||||
isExpired: item.isExpired,
|
||||
isPublic: item.isPublic
|
||||
)
|
||||
items.append(mappedItem)
|
||||
}
|
||||
}
|
||||
|
||||
let peerReference = transaction.getPeer(peerId).flatMap(PeerReference.init)
|
||||
|
||||
return (peerReference, items, Int(cached.totalCount))
|
||||
}
|
||||
|> deliverOn(self.queue)).start(next: { [weak self] peerReference, items, totalCount in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
self.stateValue = State(peerReference: peerReference, items: items, totalCount: totalCount, loadMoreToken: 0, isCached: true)
|
||||
self.loadMore()
|
||||
})
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.requestDisposable?.dispose()
|
||||
}
|
||||
|
||||
func loadMore() {
|
||||
if self.isLoadingMore {
|
||||
return
|
||||
}
|
||||
guard let loadMoreToken = self.stateValue.loadMoreToken else {
|
||||
return
|
||||
}
|
||||
|
||||
self.isLoadingMore = true
|
||||
|
||||
let peerId = self.peerId
|
||||
let account = self.account
|
||||
let isArchived = self.isArchived
|
||||
self.requestDisposable = (self.account.postbox.transaction { transaction -> Api.InputUser? in
|
||||
return transaction.getPeer(peerId).flatMap(apiInputUser)
|
||||
}
|
||||
|> mapToSignal { inputUser -> Signal<([EngineStoryItem], Int, PeerReference?), NoError> in
|
||||
guard let inputUser = inputUser else {
|
||||
return .single(([], 0, nil))
|
||||
}
|
||||
|
||||
let signal: Signal<Api.stories.Stories, MTRpcError>
|
||||
if isArchived {
|
||||
signal = account.network.request(Api.functions.stories.getStoriesArchive(offsetId: Int32(loadMoreToken), limit: 100))
|
||||
} else {
|
||||
signal = account.network.request(Api.functions.stories.getPinnedStories(userId: inputUser, offsetId: Int32(loadMoreToken), limit: 100))
|
||||
}
|
||||
return signal
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.stories.Stories?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> mapToSignal { result -> Signal<([EngineStoryItem], Int, PeerReference?), NoError> in
|
||||
guard let result = result else {
|
||||
return .single(([], 0, nil))
|
||||
}
|
||||
|
||||
return account.postbox.transaction { transaction -> ([EngineStoryItem], Int, PeerReference?) in
|
||||
var storyItems: [EngineStoryItem] = []
|
||||
var totalCount: Int = 0
|
||||
|
||||
switch result {
|
||||
case let .stories(count, stories, users):
|
||||
totalCount = Int(count)
|
||||
|
||||
var peers: [Peer] = []
|
||||
var peerPresences: [PeerId: Api.User] = [:]
|
||||
|
||||
for user in users {
|
||||
let telegramUser = TelegramUser(user: user)
|
||||
peers.append(telegramUser)
|
||||
peerPresences[telegramUser.id] = user
|
||||
}
|
||||
|
||||
updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in
|
||||
return updated
|
||||
})
|
||||
updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences)
|
||||
|
||||
for story in stories {
|
||||
if let storedItem = Stories.StoredItem(apiStoryItem: story, peerId: peerId, transaction: transaction) {
|
||||
if case let .item(item) = storedItem, let media = item.media {
|
||||
let mappedItem = EngineStoryItem(
|
||||
id: item.id,
|
||||
timestamp: item.timestamp,
|
||||
expirationTimestamp: item.expirationTimestamp,
|
||||
media: EngineMedia(media),
|
||||
text: item.text,
|
||||
entities: item.entities,
|
||||
views: item.views.flatMap { views in
|
||||
return EngineStoryItem.Views(
|
||||
seenCount: views.seenCount,
|
||||
seenPeers: views.seenPeerIds.compactMap { id -> EnginePeer? in
|
||||
return transaction.getPeer(id).flatMap(EnginePeer.init)
|
||||
}
|
||||
)
|
||||
},
|
||||
privacy: item.privacy.flatMap(EngineStoryPrivacy.init),
|
||||
isPinned: item.isPinned,
|
||||
isExpired: item.isExpired,
|
||||
isPublic: item.isPublic
|
||||
)
|
||||
storyItems.append(mappedItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if loadMoreToken == 0 {
|
||||
let key = ValueBoxKey(length: 8 + 1)
|
||||
key.setInt64(0, value: peerId.toInt64())
|
||||
key.setInt8(8, value: isArchived ? 1 : 0)
|
||||
if let entry = CodableEntry(CachedPeerStoryListHead(items: storyItems.prefix(100).map { .item($0.asStoryItem()) }, totalCount: count)) {
|
||||
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerStoryListHeads, key: key), entry: entry)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (storyItems, totalCount, transaction.getPeer(peerId).flatMap(PeerReference.init))
|
||||
}
|
||||
}
|
||||
}
|
||||
|> deliverOn(self.queue)).start(next: { [weak self] storyItems, totalCount, peerReference in
|
||||
guard let `self` = self else {
|
||||
return
|
||||
}
|
||||
|
||||
self.isLoadingMore = false
|
||||
|
||||
var updatedState = self.stateValue
|
||||
if updatedState.isCached {
|
||||
updatedState.items.removeAll()
|
||||
updatedState.isCached = false
|
||||
}
|
||||
|
||||
var existingIds = Set(updatedState.items.map { $0.id })
|
||||
for item in storyItems {
|
||||
if existingIds.contains(item.id) {
|
||||
continue
|
||||
}
|
||||
existingIds.insert(item.id)
|
||||
|
||||
updatedState.items.append(item)
|
||||
}
|
||||
|
||||
if updatedState.peerReference == nil {
|
||||
updatedState.peerReference = peerReference
|
||||
}
|
||||
|
||||
updatedState.loadMoreToken = (storyItems.last?.id).flatMap(Int.init)
|
||||
if updatedState.loadMoreToken != nil {
|
||||
updatedState.totalCount = max(totalCount, updatedState.items.count)
|
||||
} else {
|
||||
updatedState.totalCount = updatedState.items.count
|
||||
}
|
||||
self.stateValue = updatedState
|
||||
|
||||
if self.updatesDisposable == nil {
|
||||
self.updatesDisposable = (self.account.stateManager.storyUpdates
|
||||
|> deliverOn(self.queue)).start(next: { [weak self] updates in
|
||||
guard let `self` = self else {
|
||||
return
|
||||
}
|
||||
let selfPeerId = self.peerId
|
||||
let _ = (self.account.postbox.transaction { transaction -> [PeerId: Peer] in
|
||||
var peers: [PeerId: Peer] = [:]
|
||||
|
||||
for update in updates {
|
||||
switch update {
|
||||
case let .added(peerId, item):
|
||||
if selfPeerId == peerId {
|
||||
if case let .item(item) = item {
|
||||
if let views = item.views {
|
||||
for id in views.seenPeerIds {
|
||||
if let peer = transaction.getPeer(id) {
|
||||
peers[peer.id] = peer
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return peers
|
||||
}
|
||||
|> deliverOn(self.queue)).start(next: { [weak self] peers in
|
||||
guard let `self` = self else {
|
||||
return
|
||||
}
|
||||
|
||||
var finalUpdatedState: State?
|
||||
|
||||
for update in updates {
|
||||
switch update {
|
||||
case let .deleted(peerId, id):
|
||||
if self.peerId == peerId {
|
||||
if let index = self.stateValue.items.firstIndex(where: { $0.id == id }) {
|
||||
var updatedState = finalUpdatedState ?? self.stateValue
|
||||
updatedState.items.remove(at: index)
|
||||
updatedState.totalCount = max(0, updatedState.totalCount - 1)
|
||||
finalUpdatedState = updatedState
|
||||
}
|
||||
}
|
||||
case let .added(peerId, item):
|
||||
if self.peerId == peerId {
|
||||
if let index = self.stateValue.items.firstIndex(where: { $0.id == item.id }) {
|
||||
if !self.isArchived {
|
||||
if case let .item(item) = item {
|
||||
if item.isPinned {
|
||||
if let media = item.media {
|
||||
var updatedState = finalUpdatedState ?? self.stateValue
|
||||
updatedState.items[index] = EngineStoryItem(
|
||||
id: item.id,
|
||||
timestamp: item.timestamp,
|
||||
expirationTimestamp: item.expirationTimestamp,
|
||||
media: EngineMedia(media),
|
||||
text: item.text,
|
||||
entities: item.entities,
|
||||
views: item.views.flatMap { views in
|
||||
return EngineStoryItem.Views(
|
||||
seenCount: views.seenCount,
|
||||
seenPeers: views.seenPeerIds.compactMap { id -> EnginePeer? in
|
||||
return peers[id].flatMap(EnginePeer.init)
|
||||
}
|
||||
)
|
||||
},
|
||||
privacy: item.privacy.flatMap(EngineStoryPrivacy.init),
|
||||
isPinned: item.isPinned,
|
||||
isExpired: item.isExpired,
|
||||
isPublic: item.isPublic
|
||||
)
|
||||
finalUpdatedState = updatedState
|
||||
}
|
||||
} else {
|
||||
var updatedState = finalUpdatedState ?? self.stateValue
|
||||
updatedState.items.remove(at: index)
|
||||
updatedState.totalCount = max(0, updatedState.totalCount - 1)
|
||||
finalUpdatedState = updatedState
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if !self.isArchived {
|
||||
if case let .item(item) = item {
|
||||
if item.isPinned {
|
||||
if let media = item.media {
|
||||
var updatedState = finalUpdatedState ?? self.stateValue
|
||||
updatedState.items.append(EngineStoryItem(
|
||||
id: item.id,
|
||||
timestamp: item.timestamp,
|
||||
expirationTimestamp: item.expirationTimestamp,
|
||||
media: EngineMedia(media),
|
||||
text: item.text,
|
||||
entities: item.entities,
|
||||
views: item.views.flatMap { views in
|
||||
return EngineStoryItem.Views(
|
||||
seenCount: views.seenCount,
|
||||
seenPeers: views.seenPeerIds.compactMap { id -> EnginePeer? in
|
||||
return peers[id].flatMap(EnginePeer.init)
|
||||
}
|
||||
)
|
||||
},
|
||||
privacy: item.privacy.flatMap(EngineStoryPrivacy.init),
|
||||
isPinned: item.isPinned,
|
||||
isExpired: item.isExpired,
|
||||
isPublic: item.isPublic
|
||||
))
|
||||
updatedState.items.sort(by: { lhs, rhs in
|
||||
return lhs.timestamp > rhs.timestamp
|
||||
})
|
||||
finalUpdatedState = updatedState
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case .read:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if let finalUpdatedState = finalUpdatedState {
|
||||
self.stateValue = finalUpdatedState
|
||||
|
||||
let items = finalUpdatedState.items
|
||||
let totalCount = finalUpdatedState.totalCount
|
||||
let _ = (self.account.postbox.transaction { transaction -> Void in
|
||||
let key = ValueBoxKey(length: 8 + 1)
|
||||
key.setInt64(0, value: peerId.toInt64())
|
||||
key.setInt8(8, value: isArchived ? 1 : 0)
|
||||
if let entry = CodableEntry(CachedPeerStoryListHead(items: items.prefix(100).map { .item($0.asStoryItem()) }, totalCount: Int32(totalCount))) {
|
||||
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerStoryListHeads, key: key), entry: entry)
|
||||
}
|
||||
}).start()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
public struct State: Equatable {
|
||||
public var peerReference: PeerReference?
|
||||
public var items: [EngineStoryItem]
|
||||
public var totalCount: Int
|
||||
public var loadMoreToken: Int?
|
||||
public var isCached: Bool
|
||||
|
||||
init(
|
||||
peerReference: PeerReference?,
|
||||
items: [EngineStoryItem],
|
||||
totalCount: Int,
|
||||
loadMoreToken: Int?
|
||||
loadMoreToken: Int?,
|
||||
isCached: Bool
|
||||
) {
|
||||
self.peerReference = peerReference
|
||||
self.items = items
|
||||
self.totalCount = totalCount
|
||||
self.loadMoreToken = loadMoreToken
|
||||
self.isCached = isCached
|
||||
}
|
||||
}
|
||||
|
||||
private let account: Account
|
||||
private let peerId: EnginePeer.Id
|
||||
private let isArchived: Bool
|
||||
|
||||
private let statePromise = Promise<State>()
|
||||
private var stateValue: State {
|
||||
didSet {
|
||||
self.statePromise.set(.single(self.stateValue))
|
||||
}
|
||||
}
|
||||
public var state: Signal<State, NoError> {
|
||||
return self.statePromise.get()
|
||||
return impl.signalWith { impl, subscriber in
|
||||
return impl.state.start(next: subscriber.putNext)
|
||||
}
|
||||
}
|
||||
|
||||
private var isLoadingMore: Bool = false
|
||||
private var requestDisposable: Disposable?
|
||||
|
||||
private var updatesDisposable: Disposable?
|
||||
private let queue: Queue
|
||||
private let impl: QueueLocalObject<Impl>
|
||||
|
||||
public init(account: Account, peerId: EnginePeer.Id, isArchived: Bool) {
|
||||
self.account = account
|
||||
self.peerId = peerId
|
||||
self.isArchived = isArchived
|
||||
|
||||
self.stateValue = State(peerReference: nil, items: [], totalCount: 0, loadMoreToken: 0)
|
||||
self.statePromise.set(.single(self.stateValue))
|
||||
|
||||
self.loadMore()
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.requestDisposable?.dispose()
|
||||
let queue = Queue.mainQueue()
|
||||
self.queue = queue
|
||||
self.impl = QueueLocalObject(queue: queue, generate: {
|
||||
return Impl(queue: queue, account: account, peerId: peerId, isArchived: isArchived)
|
||||
})
|
||||
}
|
||||
|
||||
public func loadMore() {
|
||||
if self.isLoadingMore {
|
||||
return
|
||||
}
|
||||
guard let loadMoreToken = self.stateValue.loadMoreToken else {
|
||||
return
|
||||
self.impl.with { impl in
|
||||
impl.loadMore()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final class PeerExpiringStoryListContext {
|
||||
private final class Impl {
|
||||
private let queue: Queue
|
||||
private let account: Account
|
||||
private let peerId: EnginePeer.Id
|
||||
|
||||
self.isLoadingMore = true
|
||||
private var listDisposable: Disposable?
|
||||
private var pollDisposable: Disposable?
|
||||
|
||||
let peerId = self.peerId
|
||||
let account = self.account
|
||||
let isArchived = self.isArchived
|
||||
self.requestDisposable = (self.account.postbox.transaction { transaction -> Api.InputUser? in
|
||||
return transaction.getPeer(peerId).flatMap(apiInputUser)
|
||||
}
|
||||
|> mapToSignal { inputUser -> Signal<([EngineStoryItem], Int, PeerReference?), NoError> in
|
||||
guard let inputUser = inputUser else {
|
||||
return .single(([], 0, nil))
|
||||
}
|
||||
private let statePromise = Promise<State>()
|
||||
|
||||
init(queue: Queue, account: Account, peerId: EnginePeer.Id) {
|
||||
self.queue = queue
|
||||
self.account = account
|
||||
self.peerId = peerId
|
||||
|
||||
let signal: Signal<Api.stories.Stories, MTRpcError>
|
||||
if isArchived {
|
||||
signal = account.network.request(Api.functions.stories.getStoriesArchive(offsetId: Int32(loadMoreToken), limit: 100))
|
||||
} else {
|
||||
signal = account.network.request(Api.functions.stories.getPinnedStories(userId: inputUser, offsetId: Int32(loadMoreToken), limit: 100))
|
||||
}
|
||||
return signal
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.stories.Stories?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> mapToSignal { result -> Signal<([EngineStoryItem], Int, PeerReference?), NoError> in
|
||||
guard let result = result else {
|
||||
return .single(([], 0, nil))
|
||||
self.listDisposable = (account.postbox.combinedView(keys: [
|
||||
PostboxViewKey.storiesState(key: .peer(peerId)),
|
||||
PostboxViewKey.storyItems(peerId: peerId)
|
||||
])
|
||||
|> deliverOn(self.queue)).start(next: { [weak self] views in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
guard let stateView = views.views[PostboxViewKey.storiesState(key: .peer(peerId))] as? StoryStatesView else {
|
||||
return
|
||||
}
|
||||
guard let itemsView = views.views[PostboxViewKey.storyItems(peerId: peerId)] as? StoryItemsView else {
|
||||
return
|
||||
}
|
||||
|
||||
return account.postbox.transaction { transaction -> ([EngineStoryItem], Int, PeerReference?) in
|
||||
var storyItems: [EngineStoryItem] = []
|
||||
var totalCount: Int = 0
|
||||
let _ = (self.account.postbox.transaction { transaction -> State? in
|
||||
let state = stateView.value?.get(Stories.PeerState.self)
|
||||
|
||||
switch result {
|
||||
case let .stories(count, stories, users):
|
||||
totalCount = Int(count)
|
||||
|
||||
var peers: [Peer] = []
|
||||
var peerPresences: [PeerId: Api.User] = [:]
|
||||
|
||||
for user in users {
|
||||
let telegramUser = TelegramUser(user: user)
|
||||
peers.append(telegramUser)
|
||||
peerPresences[telegramUser.id] = user
|
||||
}
|
||||
|
||||
updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in
|
||||
return updated
|
||||
})
|
||||
updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences)
|
||||
|
||||
for story in stories {
|
||||
if let storedItem = Stories.StoredItem(apiStoryItem: story, peerId: peerId, transaction: transaction) {
|
||||
if case let .item(item) = storedItem, let media = item.media {
|
||||
var items: [Item] = []
|
||||
for item in itemsView.items {
|
||||
if let item = item.value.get(Stories.StoredItem.self) {
|
||||
switch item {
|
||||
case let .item(item):
|
||||
if let media = item.media {
|
||||
let mappedItem = EngineStoryItem(
|
||||
id: item.id,
|
||||
timestamp: item.timestamp,
|
||||
@ -551,180 +889,156 @@ public final class PeerStoryListContext {
|
||||
isExpired: item.isExpired,
|
||||
isPublic: item.isPublic
|
||||
)
|
||||
storyItems.append(mappedItem)
|
||||
items.append(.item(mappedItem))
|
||||
}
|
||||
case let .placeholder(placeholder):
|
||||
items.append(.placeholder(id: placeholder.id, timestamp: placeholder.timestamp, expirationTimestamp: placeholder.expirationTimestamp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (storyItems, totalCount, transaction.getPeer(peerId).flatMap(PeerReference.init))
|
||||
return State(
|
||||
items: items,
|
||||
isCached: false,
|
||||
maxReadId: state?.maxReadId ?? 0
|
||||
)
|
||||
}
|
||||
|> deliverOn(self.queue)).start(next: { [weak self] state in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
guard let state else {
|
||||
return
|
||||
}
|
||||
self.statePromise.set(.single(state))
|
||||
})
|
||||
})
|
||||
|
||||
self.poll()
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.listDisposable?.dispose()
|
||||
self.pollDisposable?.dispose()
|
||||
}
|
||||
|
||||
private func poll() {
|
||||
self.pollDisposable?.dispose()
|
||||
|
||||
let account = self.account
|
||||
let peerId = self.peerId
|
||||
self.pollDisposable = (self.account.postbox.transaction { transaction -> Api.InputUser? in
|
||||
return transaction.getPeer(peerId).flatMap(apiInputUser)
|
||||
}
|
||||
}).start(next: { [weak self] storyItems, totalCount, peerReference in
|
||||
guard let `self` = self else {
|
||||
return
|
||||
}
|
||||
|
||||
self.isLoadingMore = false
|
||||
|
||||
var updatedState = self.stateValue
|
||||
|
||||
var existingIds = Set(updatedState.items.map { $0.id })
|
||||
for item in storyItems {
|
||||
if existingIds.contains(item.id) {
|
||||
continue
|
||||
|> mapToSignal { inputUser -> Signal<Never, NoError> in
|
||||
guard let inputUser else {
|
||||
return .complete()
|
||||
}
|
||||
existingIds.insert(item.id)
|
||||
|
||||
updatedState.items.append(item)
|
||||
}
|
||||
|
||||
if updatedState.peerReference == nil {
|
||||
updatedState.peerReference = peerReference
|
||||
}
|
||||
|
||||
updatedState.loadMoreToken = (storyItems.last?.id).flatMap(Int.init)
|
||||
if updatedState.loadMoreToken != nil {
|
||||
updatedState.totalCount = max(totalCount, updatedState.items.count)
|
||||
} else {
|
||||
updatedState.totalCount = updatedState.items.count
|
||||
}
|
||||
self.stateValue = updatedState
|
||||
|
||||
if self.updatesDisposable == nil {
|
||||
self.updatesDisposable = (self.account.stateManager.storyUpdates
|
||||
|> deliverOnMainQueue).start(next: { [weak self] updates in
|
||||
return account.network.request(Api.functions.stories.getUserStories(userId: inputUser))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.stories.UserStories?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> mapToSignal { result -> Signal<Never, NoError> in
|
||||
return account.postbox.transaction { transaction -> Void in
|
||||
var updatedPeerEntries: [StoryItemsTableEntry] = []
|
||||
updatedPeerEntries.removeAll()
|
||||
|
||||
if let result = result, case let .userStories(stories, users) = result {
|
||||
var peers: [Peer] = []
|
||||
var peerPresences: [PeerId: Api.User] = [:]
|
||||
|
||||
for user in users {
|
||||
let telegramUser = TelegramUser(user: user)
|
||||
peers.append(telegramUser)
|
||||
peerPresences[telegramUser.id] = user
|
||||
}
|
||||
|
||||
switch stories {
|
||||
case let .userStories(_, userId, maxReadId, stories):
|
||||
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId))
|
||||
|
||||
let previousPeerEntries: [StoryItemsTableEntry] = transaction.getStoryItems(peerId: peerId)
|
||||
|
||||
for story in stories {
|
||||
if let storedItem = Stories.StoredItem(apiStoryItem: story, peerId: peerId, transaction: transaction) {
|
||||
if case .placeholder = storedItem, let previousEntry = previousPeerEntries.first(where: { $0.id == storedItem.id }) {
|
||||
updatedPeerEntries.append(previousEntry)
|
||||
} else {
|
||||
if let codedEntry = CodableEntry(storedItem) {
|
||||
updatedPeerEntries.append(StoryItemsTableEntry(value: codedEntry, id: storedItem.id))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
transaction.setPeerStoryState(peerId: peerId, state: CodableEntry(Stories.PeerState(
|
||||
maxReadId: maxReadId ?? 0
|
||||
)))
|
||||
}
|
||||
|
||||
updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in
|
||||
return updated
|
||||
})
|
||||
updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences)
|
||||
}
|
||||
|
||||
transaction.setStoryItems(peerId: peerId, items: updatedPeerEntries)
|
||||
}
|
||||
|> ignoreValues
|
||||
}
|
||||
}).start(completed: { [weak self] in
|
||||
guard let `self` = self else {
|
||||
return
|
||||
}
|
||||
self.pollDisposable = (Signal<Never, NoError>.complete() |> suspendAwareDelay(60.0, queue: self.queue) |> deliverOn(self.queue)).start(completed: { [weak self] in
|
||||
guard let `self` = self else {
|
||||
return
|
||||
}
|
||||
let selfPeerId = self.peerId
|
||||
let _ = (self.account.postbox.transaction { transaction -> [PeerId: Peer] in
|
||||
var peers: [PeerId: Peer] = [:]
|
||||
|
||||
for update in updates {
|
||||
switch update {
|
||||
case let .added(peerId, item):
|
||||
if selfPeerId == peerId {
|
||||
if case let .item(item) = item {
|
||||
if let views = item.views {
|
||||
for id in views.seenPeerIds {
|
||||
if let peer = transaction.getPeer(id) {
|
||||
peers[peer.id] = peer
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return peers
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peers in
|
||||
guard let `self` = self else {
|
||||
return
|
||||
}
|
||||
|
||||
var finalUpdatedState: State?
|
||||
|
||||
for update in updates {
|
||||
switch update {
|
||||
case let .deleted(peerId, id):
|
||||
if self.peerId == peerId {
|
||||
if let index = self.stateValue.items.firstIndex(where: { $0.id == id }) {
|
||||
var updatedState = finalUpdatedState ?? self.stateValue
|
||||
updatedState.items.remove(at: index)
|
||||
updatedState.totalCount = max(0, updatedState.totalCount - 1)
|
||||
finalUpdatedState = updatedState
|
||||
}
|
||||
}
|
||||
case let .added(peerId, item):
|
||||
if self.peerId == peerId {
|
||||
if let index = self.stateValue.items.firstIndex(where: { $0.id == item.id }) {
|
||||
if !self.isArchived {
|
||||
if case let .item(item) = item {
|
||||
if item.isPinned {
|
||||
if let media = item.media {
|
||||
var updatedState = finalUpdatedState ?? self.stateValue
|
||||
updatedState.items[index] = EngineStoryItem(
|
||||
id: item.id,
|
||||
timestamp: item.timestamp,
|
||||
expirationTimestamp: item.expirationTimestamp,
|
||||
media: EngineMedia(media),
|
||||
text: item.text,
|
||||
entities: item.entities,
|
||||
views: item.views.flatMap { views in
|
||||
return EngineStoryItem.Views(
|
||||
seenCount: views.seenCount,
|
||||
seenPeers: views.seenPeerIds.compactMap { id -> EnginePeer? in
|
||||
return peers[id].flatMap(EnginePeer.init)
|
||||
}
|
||||
)
|
||||
},
|
||||
privacy: item.privacy.flatMap(EngineStoryPrivacy.init),
|
||||
isPinned: item.isPinned,
|
||||
isExpired: item.isExpired,
|
||||
isPublic: item.isPublic
|
||||
)
|
||||
finalUpdatedState = updatedState
|
||||
}
|
||||
} else {
|
||||
var updatedState = finalUpdatedState ?? self.stateValue
|
||||
updatedState.items.remove(at: index)
|
||||
updatedState.totalCount = max(0, updatedState.totalCount - 1)
|
||||
finalUpdatedState = updatedState
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if !self.isArchived {
|
||||
if case let .item(item) = item {
|
||||
if item.isPinned {
|
||||
if let media = item.media {
|
||||
var updatedState = finalUpdatedState ?? self.stateValue
|
||||
updatedState.items.append(EngineStoryItem(
|
||||
id: item.id,
|
||||
timestamp: item.timestamp,
|
||||
expirationTimestamp: item.expirationTimestamp,
|
||||
media: EngineMedia(media),
|
||||
text: item.text,
|
||||
entities: item.entities,
|
||||
views: item.views.flatMap { views in
|
||||
return EngineStoryItem.Views(
|
||||
seenCount: views.seenCount,
|
||||
seenPeers: views.seenPeerIds.compactMap { id -> EnginePeer? in
|
||||
return peers[id].flatMap(EnginePeer.init)
|
||||
}
|
||||
)
|
||||
},
|
||||
privacy: item.privacy.flatMap(EngineStoryPrivacy.init),
|
||||
isPinned: item.isPinned,
|
||||
isExpired: item.isExpired,
|
||||
isPublic: item.isPublic
|
||||
))
|
||||
updatedState.items.sort(by: { lhs, rhs in
|
||||
return lhs.timestamp > rhs.timestamp
|
||||
})
|
||||
finalUpdatedState = updatedState
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case .read:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if let finalUpdatedState = finalUpdatedState {
|
||||
self.stateValue = finalUpdatedState
|
||||
}
|
||||
})
|
||||
self.poll()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
public enum Item: Equatable {
|
||||
case item(EngineStoryItem)
|
||||
case placeholder(id: Int32, timestamp: Int32, expirationTimestamp: Int32)
|
||||
}
|
||||
|
||||
public final class State: Equatable {
|
||||
public let items: [Item]
|
||||
public let isCached: Bool
|
||||
public let maxReadId: Int32
|
||||
|
||||
public init(items: [Item], isCached: Bool, maxReadId: Int32) {
|
||||
self.items = items
|
||||
self.isCached = isCached
|
||||
self.maxReadId = maxReadId
|
||||
}
|
||||
|
||||
public static func ==(lhs: State, rhs: State) -> Bool {
|
||||
if lhs === rhs {
|
||||
return true
|
||||
}
|
||||
if lhs.items != rhs.items {
|
||||
return false
|
||||
}
|
||||
if lhs.maxReadId != rhs.maxReadId {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private let queue: Queue
|
||||
private let impl: QueueLocalObject<Impl>
|
||||
|
||||
public init(account: Account, peerId: EnginePeer.Id) {
|
||||
let queue = Queue.mainQueue()
|
||||
self.queue = queue
|
||||
self.impl = QueueLocalObject(queue: queue, generate: {
|
||||
return Impl(queue: queue, account: account, peerId: peerId)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -386,7 +386,8 @@ final class PeerInfoStoryGridScreenComponent: Component {
|
||||
return nil
|
||||
}
|
||||
return self.environment?.controller()?.navigationController as? NavigationController
|
||||
}
|
||||
},
|
||||
listContext: nil
|
||||
)
|
||||
self.paneNode = paneNode
|
||||
self.addSubview(paneNode.view)
|
||||
|
@ -890,7 +890,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
|
||||
private var presentationData: PresentationData
|
||||
private var presentationDataDisposable: Disposable?
|
||||
|
||||
public init(context: AccountContext, peerId: PeerId, chatLocation: ChatLocation, contentType: ContentType, captureProtected: Bool, isSaved: Bool, isArchive: Bool, navigationController: @escaping () -> NavigationController?) {
|
||||
public init(context: AccountContext, peerId: PeerId, chatLocation: ChatLocation, contentType: ContentType, captureProtected: Bool, isSaved: Bool, isArchive: Bool, navigationController: @escaping () -> NavigationController?, listContext: PeerStoryListContext?) {
|
||||
self.context = context
|
||||
self.peerId = peerId
|
||||
self.chatLocation = chatLocation
|
||||
@ -913,7 +913,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
|
||||
captureProtected: captureProtected
|
||||
)
|
||||
|
||||
self.listSource = PeerStoryListContext(account: context.account, peerId: peerId, isArchived: self.isArchive)
|
||||
self.listSource = listContext ?? PeerStoryListContext(account: context.account, peerId: peerId, isArchived: self.isArchive)
|
||||
self.calendarSource = nil
|
||||
|
||||
super.init()
|
||||
|
@ -192,6 +192,7 @@ final class PeerInfoScreenData {
|
||||
let groupsInCommon: GroupsInCommonContext?
|
||||
let linkedDiscussionPeer: Peer?
|
||||
let members: PeerInfoMembersData?
|
||||
let storyListContext: PeerStoryListContext?
|
||||
let encryptionKeyFingerprint: SecretChatKeyFingerprint?
|
||||
let globalSettings: TelegramGlobalSettings?
|
||||
let invitations: PeerExportedInvitationsState?
|
||||
@ -214,6 +215,7 @@ final class PeerInfoScreenData {
|
||||
groupsInCommon: GroupsInCommonContext?,
|
||||
linkedDiscussionPeer: Peer?,
|
||||
members: PeerInfoMembersData?,
|
||||
storyListContext: PeerStoryListContext?,
|
||||
encryptionKeyFingerprint: SecretChatKeyFingerprint?,
|
||||
globalSettings: TelegramGlobalSettings?,
|
||||
invitations: PeerExportedInvitationsState?,
|
||||
@ -235,6 +237,7 @@ final class PeerInfoScreenData {
|
||||
self.groupsInCommon = groupsInCommon
|
||||
self.linkedDiscussionPeer = linkedDiscussionPeer
|
||||
self.members = members
|
||||
self.storyListContext = storyListContext
|
||||
self.encryptionKeyFingerprint = encryptionKeyFingerprint
|
||||
self.globalSettings = globalSettings
|
||||
self.invitations = invitations
|
||||
@ -390,10 +393,22 @@ func keepPeerInfoScreenDataHot(context: AccountContext, peerId: PeerId, chatLoca
|
||||
case .none, .settings:
|
||||
return .complete()
|
||||
case .user, .channel, .group:
|
||||
return combineLatest(
|
||||
context.peerChannelMemberCategoriesContextsManager.profileData(postbox: context.account.postbox, network: context.account.network, peerId: peerId, customData: peerInfoAvailableMediaPanes(context: context, peerId: peerId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder) |> ignoreValues),
|
||||
context.peerChannelMemberCategoriesContextsManager.profilePhotos(postbox: context.account.postbox, network: context.account.network, peerId: peerId, fetch: peerInfoProfilePhotos(context: context, peerId: peerId)) |> ignoreValues
|
||||
)
|
||||
var signals: [Signal<Never, NoError>] = []
|
||||
|
||||
signals.append(context.peerChannelMemberCategoriesContextsManager.profileData(postbox: context.account.postbox, network: context.account.network, peerId: peerId, customData: peerInfoAvailableMediaPanes(context: context, peerId: peerId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder) |> ignoreValues) |> ignoreValues)
|
||||
signals.append(context.peerChannelMemberCategoriesContextsManager.profilePhotos(postbox: context.account.postbox, network: context.account.network, peerId: peerId, fetch: peerInfoProfilePhotos(context: context, peerId: peerId)) |> ignoreValues)
|
||||
|
||||
if case .user = inputData {
|
||||
signals.append(Signal { _ in
|
||||
let listContext = PeerStoryListContext(account: context.account, peerId: peerId, isArchived: false)
|
||||
|
||||
return ActionDisposable {
|
||||
let _ = listContext
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return combineLatest(signals)
|
||||
|> ignoreValues
|
||||
}
|
||||
}
|
||||
@ -529,6 +544,7 @@ func peerInfoScreenSettingsData(context: AccountContext, peerId: EnginePeer.Id,
|
||||
groupsInCommon: nil,
|
||||
linkedDiscussionPeer: nil,
|
||||
members: nil,
|
||||
storyListContext: nil,
|
||||
encryptionKeyFingerprint: nil,
|
||||
globalSettings: globalSettings,
|
||||
invitations: nil,
|
||||
@ -561,6 +577,7 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
||||
groupsInCommon: nil,
|
||||
linkedDiscussionPeer: nil,
|
||||
members: nil,
|
||||
storyListContext: nil,
|
||||
encryptionKeyFingerprint: nil,
|
||||
globalSettings: nil,
|
||||
invitations: nil,
|
||||
@ -667,17 +684,25 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
||||
secretChatKeyFingerprint = context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.SecretChatKeyFingerprint(id: secretChatId))
|
||||
}
|
||||
|
||||
let storyListContext = PeerStoryListContext(account: context.account, peerId: peerId, isArchived: false)
|
||||
let hasStories: Signal<Bool, NoError> = storyListContext.state
|
||||
|> map { state -> Bool in
|
||||
return !state.items.isEmpty
|
||||
}
|
||||
|> 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
|
||||
status,
|
||||
hasStories
|
||||
)
|
||||
|> map { peerView, availablePanes, globalNotificationSettings, encryptionKeyFingerprint, status -> PeerInfoScreenData in
|
||||
|> map { peerView, availablePanes, globalNotificationSettings, encryptionKeyFingerprint, status, hasStories -> PeerInfoScreenData in
|
||||
var availablePanes = availablePanes
|
||||
|
||||
if peerView.peers[peerView.peerId] is TelegramUser {
|
||||
if hasStories, peerView.peers[peerView.peerId] is TelegramUser {
|
||||
availablePanes?.insert(.stories, at: 0)
|
||||
}
|
||||
|
||||
@ -700,6 +725,7 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
||||
groupsInCommon: groupsInCommon,
|
||||
linkedDiscussionPeer: nil,
|
||||
members: nil,
|
||||
storyListContext: storyListContext,
|
||||
encryptionKeyFingerprint: encryptionKeyFingerprint,
|
||||
globalSettings: nil,
|
||||
invitations: nil,
|
||||
@ -779,6 +805,7 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
||||
groupsInCommon: nil,
|
||||
linkedDiscussionPeer: discussionPeer,
|
||||
members: nil,
|
||||
storyListContext: nil,
|
||||
encryptionKeyFingerprint: nil,
|
||||
globalSettings: nil,
|
||||
invitations: invitations,
|
||||
@ -982,6 +1009,7 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
||||
groupsInCommon: nil,
|
||||
linkedDiscussionPeer: discussionPeer,
|
||||
members: membersData,
|
||||
storyListContext: nil,
|
||||
encryptionKeyFingerprint: nil,
|
||||
globalSettings: nil,
|
||||
invitations: invitations,
|
||||
|
@ -368,7 +368,7 @@ private final class PeerInfoPendingPane {
|
||||
let paneNode: PeerInfoPaneNode
|
||||
switch key {
|
||||
case .stories:
|
||||
let visualPaneNode = PeerInfoStoryPaneNode(context: context, peerId: peerId, chatLocation: chatLocation, contentType: .photoOrVideo, captureProtected: captureProtected, isSaved: false, isArchive: false, navigationController: chatControllerInteraction.navigationController)
|
||||
let visualPaneNode = PeerInfoStoryPaneNode(context: context, peerId: peerId, chatLocation: chatLocation, contentType: .photoOrVideo, captureProtected: captureProtected, isSaved: false, isArchive: false, navigationController: chatControllerInteraction.navigationController, listContext: data.storyListContext)
|
||||
paneNode = visualPaneNode
|
||||
visualPaneNode.openCurrentDate = {
|
||||
openMediaCalendar()
|
||||
|
Loading…
x
Reference in New Issue
Block a user