mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Bot preview
This commit is contained in:
parent
9b49738353
commit
7ac40d5b78
@ -190,6 +190,10 @@ public final class SparseItemGrid: ASDisplayNode {
|
||||
open var holeAnchor: HoleAnchor {
|
||||
preconditionFailure()
|
||||
}
|
||||
|
||||
open var isReorderable: Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
public init() {
|
||||
}
|
||||
@ -1148,7 +1152,7 @@ public final class SparseItemGrid: ASDisplayNode {
|
||||
|
||||
var itemScale: CGFloat
|
||||
let itemCornerRadius: CGFloat
|
||||
if self.isReordering {
|
||||
if self.isReordering && item.isReorderable {
|
||||
itemScale = (itemFrame.height - 6.0 * 2.0) / itemFrame.height
|
||||
itemCornerRadius = 10.0
|
||||
} else {
|
||||
@ -1240,7 +1244,7 @@ public final class SparseItemGrid: ASDisplayNode {
|
||||
if let layer = item.layer {
|
||||
layer.update(size: layer.bounds.size, insets: layout.containerLayout.insets, displayItem: item, binding: items.itemBinding, item: contentItem)
|
||||
|
||||
if self.isReordering {
|
||||
if self.isReordering, let contentItem, contentItem.isReorderable {
|
||||
if layer.animation(forKey: "shaking_position") == nil {
|
||||
startShaking(layer: layer)
|
||||
}
|
||||
|
@ -565,7 +565,7 @@ public struct StoryListContextState: Equatable {
|
||||
|
||||
public var peerReference: PeerReference?
|
||||
public var items: [Item]
|
||||
public var pinnedIds: Set<Int32>
|
||||
public var pinnedIds: [Int32]
|
||||
public var totalCount: Int
|
||||
public var loadMoreToken: AnyHashable?
|
||||
public var isCached: Bool
|
||||
@ -575,7 +575,7 @@ public struct StoryListContextState: Equatable {
|
||||
public init(
|
||||
peerReference: PeerReference?,
|
||||
items: [Item],
|
||||
pinnedIds: Set<Int32>,
|
||||
pinnedIds: [Int32],
|
||||
totalCount: Int,
|
||||
loadMoreToken: AnyHashable?,
|
||||
isCached: Bool,
|
||||
@ -633,7 +633,7 @@ public final class PeerStoryListContext: StoryListContext {
|
||||
self.peerId = peerId
|
||||
self.isArchived = isArchived
|
||||
|
||||
self.stateValue = State(peerReference: nil, items: [], pinnedIds: Set(), totalCount: 0, loadMoreToken: AnyHashable(0 as Int), isCached: true, hasCache: false, allEntityFiles: [:], isLoading: false)
|
||||
self.stateValue = State(peerReference: nil, items: [], pinnedIds: [], totalCount: 0, loadMoreToken: AnyHashable(0 as Int), isCached: true, hasCache: false, allEntityFiles: [:], isLoading: false)
|
||||
|
||||
let _ = (account.postbox.transaction { transaction -> (PeerReference?, [State.Item], [Int32], Int, [MediaId: TelegramMediaFile], Bool) in
|
||||
let key = ValueBoxKey(length: 8 + 1)
|
||||
@ -723,17 +723,19 @@ public final class PeerStoryListContext: StoryListContext {
|
||||
return
|
||||
}
|
||||
|
||||
var updatedState = State(peerReference: peerReference, items: items, pinnedIds: Set(pinnedIds), totalCount: totalCount, loadMoreToken: AnyHashable(0 as Int), isCached: true, hasCache: hasCache, allEntityFiles: allEntityFiles, isLoading: false)
|
||||
var updatedState = State(peerReference: peerReference, items: items, pinnedIds: pinnedIds, totalCount: totalCount, loadMoreToken: AnyHashable(0 as Int), isCached: true, hasCache: hasCache, allEntityFiles: allEntityFiles, isLoading: false)
|
||||
updatedState.items.sort(by: { lhs, rhs in
|
||||
let lhsPinned = updatedState.pinnedIds.contains(lhs.storyItem.id)
|
||||
let rhsPinned = updatedState.pinnedIds.contains(rhs.storyItem.id)
|
||||
if lhsPinned != rhsPinned {
|
||||
if lhsPinned {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
let lhsPinned = updatedState.pinnedIds.firstIndex(of: lhs.storyItem.id)
|
||||
let rhsPinned = updatedState.pinnedIds.firstIndex(of: rhs.storyItem.id)
|
||||
|
||||
if let lhsPinned, let rhsPinned {
|
||||
if lhsPinned != rhsPinned {
|
||||
return lhsPinned < rhsPinned
|
||||
}
|
||||
} else if (lhsPinned == nil) != (rhsPinned == nil) {
|
||||
return lhsPinned != nil
|
||||
}
|
||||
|
||||
return lhs.storyItem.timestamp > rhs.storyItem.timestamp
|
||||
})
|
||||
self.stateValue = updatedState
|
||||
@ -862,7 +864,7 @@ public final class PeerStoryListContext: StoryListContext {
|
||||
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.storyItem.asStoryItem()) }, pinnedIds: Array(pinnedIds), totalCount: count)) {
|
||||
if let entry = CodableEntry(CachedPeerStoryListHead(items: storyItems.prefix(100).map { .item($0.storyItem.asStoryItem()) }, pinnedIds: pinnedIds, totalCount: count)) {
|
||||
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerStoryListHeads, key: key), entry: entry)
|
||||
}
|
||||
}
|
||||
@ -1124,14 +1126,15 @@ public final class PeerStoryListContext: StoryListContext {
|
||||
author: item.authorId.flatMap { peers[$0].flatMap(EnginePeer.init) }
|
||||
), peer: nil))
|
||||
updatedState.items.sort(by: { lhs, rhs in
|
||||
let lhsPinned = updatedState.pinnedIds.contains(lhs.storyItem.id)
|
||||
let rhsPinned = updatedState.pinnedIds.contains(rhs.storyItem.id)
|
||||
if lhsPinned != rhsPinned {
|
||||
if lhsPinned {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
let lhsPinned = updatedState.pinnedIds.firstIndex(of: lhs.storyItem.id)
|
||||
let rhsPinned = updatedState.pinnedIds.firstIndex(of: rhs.storyItem.id)
|
||||
|
||||
if let lhsPinned, let rhsPinned {
|
||||
if lhsPinned != rhsPinned {
|
||||
return lhsPinned < rhsPinned
|
||||
}
|
||||
} else if (lhsPinned == nil) != (rhsPinned == nil) {
|
||||
return lhsPinned != nil
|
||||
}
|
||||
return lhs.storyItem.timestamp > rhs.storyItem.timestamp
|
||||
})
|
||||
@ -1180,14 +1183,15 @@ public final class PeerStoryListContext: StoryListContext {
|
||||
author: item.authorId.flatMap { peers[$0].flatMap(EnginePeer.init) }
|
||||
), peer: nil))
|
||||
updatedState.items.sort(by: { lhs, rhs in
|
||||
let lhsPinned = updatedState.pinnedIds.contains(lhs.storyItem.id)
|
||||
let rhsPinned = updatedState.pinnedIds.contains(rhs.storyItem.id)
|
||||
if lhsPinned != rhsPinned {
|
||||
if lhsPinned {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
let lhsPinned = updatedState.pinnedIds.firstIndex(of: lhs.storyItem.id)
|
||||
let rhsPinned = updatedState.pinnedIds.firstIndex(of: rhs.storyItem.id)
|
||||
|
||||
if let lhsPinned, let rhsPinned {
|
||||
if lhsPinned != rhsPinned {
|
||||
return lhsPinned < rhsPinned
|
||||
}
|
||||
} else if (lhsPinned == nil) != (rhsPinned == nil) {
|
||||
return lhsPinned != nil
|
||||
}
|
||||
return lhs.storyItem.timestamp > rhs.storyItem.timestamp
|
||||
})
|
||||
@ -1204,18 +1208,19 @@ public final class PeerStoryListContext: StoryListContext {
|
||||
case let .updatePinnedToTopList(peerId, ids):
|
||||
if self.peerId == peerId && !self.isArchived {
|
||||
let previousIds = (finalUpdatedState ?? self.stateValue).pinnedIds
|
||||
if previousIds != Set(ids) {
|
||||
if previousIds != ids {
|
||||
var updatedState = finalUpdatedState ?? self.stateValue
|
||||
updatedState.pinnedIds = Set(ids)
|
||||
updatedState.pinnedIds = ids
|
||||
updatedState.items.sort(by: { lhs, rhs in
|
||||
let lhsPinned = updatedState.pinnedIds.contains(lhs.storyItem.id)
|
||||
let rhsPinned = updatedState.pinnedIds.contains(rhs.storyItem.id)
|
||||
if lhsPinned != rhsPinned {
|
||||
if lhsPinned {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
let lhsPinned = updatedState.pinnedIds.firstIndex(of: lhs.storyItem.id)
|
||||
let rhsPinned = updatedState.pinnedIds.firstIndex(of: rhs.storyItem.id)
|
||||
|
||||
if let lhsPinned, let rhsPinned {
|
||||
if lhsPinned != rhsPinned {
|
||||
return lhsPinned < rhsPinned
|
||||
}
|
||||
} else if (lhsPinned == nil) != (rhsPinned == nil) {
|
||||
return lhsPinned != nil
|
||||
}
|
||||
return lhs.storyItem.timestamp > rhs.storyItem.timestamp
|
||||
})
|
||||
@ -1235,7 +1240,7 @@ public final class PeerStoryListContext: StoryListContext {
|
||||
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.storyItem.asStoryItem()) }, pinnedIds: Array(pinnedIds), totalCount: Int32(totalCount))) {
|
||||
if let entry = CodableEntry(CachedPeerStoryListHead(items: items.prefix(100).map { .item($0.storyItem.asStoryItem()) }, pinnedIds: pinnedIds, totalCount: Int32(totalCount))) {
|
||||
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerStoryListHeads, key: key), entry: entry)
|
||||
}
|
||||
}).start()
|
||||
@ -1308,7 +1313,7 @@ public final class SearchStoryListContext: StoryListContext {
|
||||
self.account = account
|
||||
self.source = source
|
||||
|
||||
self.stateValue = State(peerReference: nil, items: [], pinnedIds: Set(), totalCount: 0, loadMoreToken: AnyHashable(""), isCached: false, hasCache: false, allEntityFiles: [:], isLoading: false)
|
||||
self.stateValue = State(peerReference: nil, items: [], pinnedIds: [], totalCount: 0, loadMoreToken: AnyHashable(""), isCached: false, hasCache: false, allEntityFiles: [:], isLoading: false)
|
||||
self.statePromise.set(.single(self.stateValue))
|
||||
|
||||
self.loadMore(completion: nil)
|
||||
@ -2107,7 +2112,7 @@ public final class BotPreviewStoryListContext: StoryListContext {
|
||||
|
||||
self.isArchived = isArchived
|
||||
|
||||
self.stateValue = State(peerReference: nil, items: [], pinnedIds: Set(), totalCount: 0, loadMoreToken: AnyHashable(0 as Int), isCached: true, hasCache: false, allEntityFiles: [:], isLoading: false)
|
||||
self.stateValue = State(peerReference: nil, items: [], pinnedIds: [], totalCount: 0, loadMoreToken: AnyHashable(0 as Int), isCached: true, hasCache: false, allEntityFiles: [:], isLoading: false)
|
||||
|
||||
let localStateKey: PostboxViewKey = .storiesState(key: .local)
|
||||
|
||||
@ -2222,7 +2227,7 @@ public final class BotPreviewStoryListContext: StoryListContext {
|
||||
self.stateValue = State(
|
||||
peerReference: (peer?._asPeer()).flatMap(PeerReference.init),
|
||||
items: items,
|
||||
pinnedIds: Set(),
|
||||
pinnedIds: [],
|
||||
totalCount: items.count,
|
||||
loadMoreToken: nil,
|
||||
isCached: botPreview != nil,
|
||||
|
@ -1305,7 +1305,7 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
||||
}
|
||||
}
|
||||
|
||||
if let user = peerView.peers[peerView.peerId] as? TelegramUser, let botInfo = user.botInfo, botInfo.flags.contains(.canEdit) {
|
||||
if let user = peerView.peers[peerView.peerId] as? TelegramUser, let botInfo = user.botInfo, botInfo.flags.contains(.hasWebApp), botInfo.flags.contains(.canEdit) {
|
||||
availablePanes?.insert(.botPreview, at: 0)
|
||||
} else if let cachedData = peerView.cachedData as? CachedUserData, let botPreview = cachedData.botPreview, !botPreview.media.isEmpty {
|
||||
availablePanes?.insert(.botPreview, at: 0)
|
||||
|
@ -92,12 +92,16 @@ private final class VisualMediaItem: SparseItemGrid.Item {
|
||||
override var index: Int {
|
||||
return self.indexValue
|
||||
}
|
||||
override var isReorderable: Bool {
|
||||
return self.isReorderableValue
|
||||
}
|
||||
let localMonthTimestamp: Int32
|
||||
let peer: PeerReference
|
||||
let storyId: StoryId
|
||||
let story: EngineStoryItem
|
||||
let authorPeer: EnginePeer?
|
||||
let isPinned: Bool
|
||||
let isReorderableValue: Bool
|
||||
|
||||
override var id: AnyHashable {
|
||||
return AnyHashable(self.storyId)
|
||||
@ -111,7 +115,7 @@ private final class VisualMediaItem: SparseItemGrid.Item {
|
||||
return VisualMediaHoleAnchor(index: self.index, storyId: self.storyId, localMonthTimestamp: self.localMonthTimestamp)
|
||||
}
|
||||
|
||||
init(index: Int, peer: PeerReference, storyId: StoryId, story: EngineStoryItem, authorPeer: EnginePeer?, isPinned: Bool, localMonthTimestamp: Int32) {
|
||||
init(index: Int, peer: PeerReference, storyId: StoryId, story: EngineStoryItem, authorPeer: EnginePeer?, isPinned: Bool, localMonthTimestamp: Int32, isReorderable: Bool) {
|
||||
self.indexValue = index
|
||||
self.peer = peer
|
||||
self.storyId = storyId
|
||||
@ -119,6 +123,7 @@ private final class VisualMediaItem: SparseItemGrid.Item {
|
||||
self.authorPeer = authorPeer
|
||||
self.isPinned = isPinned
|
||||
self.localMonthTimestamp = localMonthTimestamp
|
||||
self.isReorderableValue = isReorderable
|
||||
}
|
||||
}
|
||||
|
||||
@ -2453,6 +2458,18 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
|
||||
}
|
||||
self.parentController?.present(UndoOverlayController(presentationData: presentationData, content: .universal(animation: isPinned ? "anim_toastunpin" : "anim_toastpin", scale: 0.06, colors: [:], title: toastTitle, text: toastText, customUndoText: nil, timeout: 5), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current)
|
||||
})))
|
||||
if isPinned {
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Reorder", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReorderItems"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
||||
c?.dismiss(completion: {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
self.beginReordering()
|
||||
})
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.StoryList_ItemAction_Edit, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
||||
@ -2769,6 +2786,18 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
|
||||
continue
|
||||
}
|
||||
|
||||
var isReorderable = false
|
||||
switch self.scope {
|
||||
case .botPreview:
|
||||
isReorderable = !item.storyItem.isPending
|
||||
case let .peer(id, _, _):
|
||||
if id == self.context.account.peerId {
|
||||
isReorderable = state.pinnedIds.contains(item.storyItem.id)
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
mappedItems.append(VisualMediaItem(
|
||||
index: mappedItems.count,
|
||||
peer: peerReference,
|
||||
@ -2776,7 +2805,8 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
|
||||
story: item.storyItem,
|
||||
authorPeer: item.peer,
|
||||
isPinned: state.pinnedIds.contains(item.storyItem.id),
|
||||
localMonthTimestamp: Month(localTimestamp: item.storyItem.timestamp + timezoneOffset).packedValue
|
||||
localMonthTimestamp: Month(localTimestamp: item.storyItem.timestamp + timezoneOffset).packedValue,
|
||||
isReorderable: isReorderable
|
||||
))
|
||||
}
|
||||
if mappedItems.count < state.totalCount, let lastItem = state.items.last, let _ = state.loadMoreToken {
|
||||
@ -2809,7 +2839,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
|
||||
|
||||
let currentSynchronous = synchronous && firstTime
|
||||
let currentReloadAtTop = reloadAtTop && firstTime
|
||||
self.updateHistory(items: items, pinnedIds: state.pinnedIds, synchronous: currentSynchronous, reloadAtTop: currentReloadAtTop, animated: animated)
|
||||
self.updateHistory(items: items, pinnedIds: Set(state.pinnedIds), synchronous: currentSynchronous, reloadAtTop: currentReloadAtTop, animated: animated)
|
||||
}
|
||||
|
||||
private func updateHistory(items: SparseItemGrid.Items, pinnedIds: Set<Int32>, synchronous: Bool, reloadAtTop: Bool, animated: Bool) {
|
||||
@ -2845,13 +2875,31 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
|
||||
}
|
||||
|
||||
private func reorderIfPossible(item: SparseItemGrid.Item, toIndex: Int) {
|
||||
if case .botPreview = self.scope, let items = self.items, let item = item as? VisualMediaItem {
|
||||
if let items = self.items, let item = item as? VisualMediaItem {
|
||||
var toIndex = toIndex
|
||||
if case .botPreview = self.scope {
|
||||
} else if case let .peer(id, _, _) = self.scope {
|
||||
if id == self.context.account.peerId {
|
||||
let maxPinnedIndex = items.items.lastIndex(where: { ($0 as? VisualMediaItem)?.isPinned == true })
|
||||
if let maxPinnedIndex {
|
||||
toIndex = min(toIndex, maxPinnedIndex)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
guard let toItem = items.items.first(where: { $0.index == toIndex }) as? VisualMediaItem else {
|
||||
return
|
||||
}
|
||||
if item.story.isPending || toItem.story.isPending {
|
||||
return
|
||||
}
|
||||
if !item.isReorderable {
|
||||
return
|
||||
}
|
||||
|
||||
var ids = items.items.compactMap { item -> StoryId? in
|
||||
return (item as? VisualMediaItem)?.storyId
|
||||
@ -3980,6 +4028,21 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
|
||||
self.reorderedIds = nil
|
||||
if case .botPreview = self.scope, let listSource = self.listSource as? BotPreviewStoryListContext {
|
||||
listSource.reorderItems(ids: reorderedIds)
|
||||
} else if case let .peer(id, _, _) = self.scope, id == self.context.account.peerId, let items = self.items {
|
||||
var updatedPinnedIds: [Int32] = []
|
||||
for id in reorderedIds {
|
||||
inner: for item in items.items {
|
||||
if let item = item as? VisualMediaItem {
|
||||
if item.storyId == id {
|
||||
if item.isPinned {
|
||||
updatedPinnedIds.append(id.id)
|
||||
break inner
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let _ = self.context.engine.messages.updatePinnedToTopStories(peerId: id, ids: updatedPinnedIds).startStandalone()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user