mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-06 17:00:13 +00:00
Various improvements
This commit is contained in:
parent
1da6943b7d
commit
f273c81d33
@ -1183,7 +1183,7 @@ public protocol SharedAccountContext: AnyObject {
|
||||
func makeHashtagSearchController(context: AccountContext, peer: EnginePeer?, query: String, stories: Bool, forceDark: Bool) -> ViewController
|
||||
func makeStorySearchController(context: AccountContext, scope: StorySearchControllerScope, listContext: SearchStoryListContext?) -> ViewController
|
||||
func makeMyStoriesController(context: AccountContext, isArchive: Bool) -> ViewController
|
||||
func makeStorySelectionController(context: AccountContext, peerId: EnginePeer.Id, completion: @escaping ([EngineStoryItem]) -> Void) -> ViewController
|
||||
func makeStorySelectionController(context: AccountContext, peerId: EnginePeer.Id, excludeIds: [Int32], completion: @escaping ([EngineStoryItem]) -> Void) -> ViewController
|
||||
func makeArchiveSettingsController(context: AccountContext) -> ViewController
|
||||
func makeFilterSettingsController(context: AccountContext, modal: Bool, scrollToTags: Bool, dismissed: (() -> Void)?) -> ViewController
|
||||
func makeBusinessSetupScreen(context: AccountContext) -> ViewController
|
||||
|
||||
@ -862,7 +862,7 @@ public final class PeerStoryListContext: StoryListContext {
|
||||
|
||||
#if DEBUG
|
||||
if folderId != nil {
|
||||
signal = signal |> delay(2.0, queue: queue)
|
||||
//signal = signal |> delay(2.0, queue: queue)
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -2008,6 +2008,21 @@ public final class PeerStoryListContext: StoryListContext {
|
||||
return (peerReference, result)
|
||||
}
|
||||
}
|
||||
|
||||
public static func cachedFolderState(peerId: EnginePeer.Id, account: Account, folderId: Int64) -> Signal<(count: Int, ids: [Int32])?, NoError> {
|
||||
return account.postbox.transaction { transaction -> (count: Int, ids: [Int32])? in
|
||||
let key = ValueBoxKey(length: 8 + 1 + 8)
|
||||
key.setInt64(0, value: peerId.toInt64())
|
||||
key.setInt8(8, value: 0)
|
||||
key.setInt64(8 + 1, value: folderId)
|
||||
|
||||
if let cached = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerStoryListHeads, key: key))?.get(CachedPeerStoryListHead.self) {
|
||||
return (Int(cached.totalCount), cached.items.map(\.id))
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final class SearchStoryListContext: StoryListContext {
|
||||
|
||||
@ -24,17 +24,20 @@ final class PeerInfoStoryGridScreenComponent: Component {
|
||||
let context: AccountContext
|
||||
let peerId: EnginePeer.Id
|
||||
let scope: PeerInfoStoryGridScreen.Scope
|
||||
let excludeIds: [Int32]
|
||||
let selectionModeCompletion: (([EngineStoryItem]) -> Void)?
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
peerId: EnginePeer.Id,
|
||||
scope: PeerInfoStoryGridScreen.Scope,
|
||||
excludeIds: [Int32],
|
||||
selectionModeCompletion: (([EngineStoryItem]) -> Void)?
|
||||
) {
|
||||
self.context = context
|
||||
self.peerId = peerId
|
||||
self.scope = scope
|
||||
self.excludeIds = excludeIds
|
||||
self.selectionModeCompletion = selectionModeCompletion
|
||||
}
|
||||
|
||||
@ -48,6 +51,9 @@ final class PeerInfoStoryGridScreenComponent: Component {
|
||||
if lhs.scope != rhs.scope {
|
||||
return false
|
||||
}
|
||||
if lhs.excludeIds != rhs.excludeIds {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
@ -482,6 +488,7 @@ final class PeerInfoStoryGridScreenComponent: Component {
|
||||
captureProtected: false,
|
||||
isProfileEmbedded: false,
|
||||
canManageStories: true,
|
||||
excludeIds: component.excludeIds,
|
||||
navigationController: { [weak self] in
|
||||
guard let self else {
|
||||
return nil
|
||||
@ -604,6 +611,7 @@ public class PeerInfoStoryGridScreen: ViewControllerComponentContainer {
|
||||
context: AccountContext,
|
||||
peerId: EnginePeer.Id,
|
||||
scope: Scope,
|
||||
excludeIds: [Int32] = [],
|
||||
selectionModeCompletion: (([EngineStoryItem]) -> Void)? = nil
|
||||
) {
|
||||
self.context = context
|
||||
@ -614,6 +622,7 @@ public class PeerInfoStoryGridScreen: ViewControllerComponentContainer {
|
||||
context: context,
|
||||
peerId: peerId,
|
||||
scope: scope,
|
||||
excludeIds: excludeIds,
|
||||
selectionModeCompletion: selectionModeCompletion
|
||||
), navigationBarAppearance: .default, theme: .default)
|
||||
|
||||
|
||||
@ -107,6 +107,7 @@ private final class VisualMediaItem: SparseItemGrid.Item {
|
||||
let authorPeer: EnginePeer?
|
||||
let isPinned: Bool
|
||||
let isReorderableValue: Bool
|
||||
let isEnabled: Bool
|
||||
|
||||
override var id: AnyHashable {
|
||||
return AnyHashable(self.storyId)
|
||||
@ -120,7 +121,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, isReorderable: Bool) {
|
||||
init(index: Int, peer: PeerReference, storyId: StoryId, story: EngineStoryItem, authorPeer: EnginePeer?, isPinned: Bool, localMonthTimestamp: Int32, isReorderable: Bool, isEnabled: Bool) {
|
||||
self.indexValue = index
|
||||
self.peer = peer
|
||||
self.storyId = storyId
|
||||
@ -129,6 +130,7 @@ private final class VisualMediaItem: SparseItemGrid.Item {
|
||||
self.isPinned = isPinned
|
||||
self.localMonthTimestamp = localMonthTimestamp
|
||||
self.isReorderableValue = isReorderable
|
||||
self.isEnabled = isEnabled
|
||||
}
|
||||
}
|
||||
|
||||
@ -1309,7 +1311,7 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding {
|
||||
}
|
||||
|
||||
var isSelected: Bool?
|
||||
if let selectedIds = self.itemInteraction?.selectedIds {
|
||||
if item.isEnabled, let selectedIds = self.itemInteraction?.selectedIds {
|
||||
isSelected = selectedIds.contains(story.id)
|
||||
}
|
||||
layer.updateSelection(theme: self.checkNodeTheme, isSelected: isSelected, animated: false)
|
||||
@ -1350,6 +1352,8 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding {
|
||||
}
|
||||
|
||||
layer.updateDuration(size: layer.bounds.size, viewCount: viewCount, duration: duration, topRightIcon: topRightIcon, author: item.authorPeer, isMin: isMin, minFactor: min(1.0, layer.bounds.height / 74.0), directMediaImageCache: self.directMediaImageCache, synchronous: synchronous)
|
||||
|
||||
layer.opacity = item.isEnabled ? 1.0 : 0.6
|
||||
}
|
||||
|
||||
func unbindLayer(layer: SparseItemGridLayer) {
|
||||
@ -1548,6 +1552,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
|
||||
public let scope: Scope
|
||||
private let isProfileEmbedded: Bool
|
||||
private let canManageStories: Bool
|
||||
private let excludeIds: Set<Int32>
|
||||
|
||||
private let navigationController: () -> NavigationController?
|
||||
|
||||
@ -1670,6 +1675,9 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
|
||||
private var currentStoryFolders: [StoryListContext.State.Folder] = []
|
||||
private var removedStoryFolders = Set<Int64>()
|
||||
|
||||
private let maxStoryFolders: Int
|
||||
private let maxStoriesPerFolder: Int
|
||||
|
||||
private var numberOfItemsToRequest: Int = 50
|
||||
private var isRequestingView: Bool = false
|
||||
private var isFirstHistoryView: Bool = true
|
||||
@ -1744,12 +1752,13 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
|
||||
return 0.0
|
||||
}
|
||||
|
||||
public init(context: AccountContext, scope: Scope, captureProtected: Bool, isProfileEmbedded: Bool, canManageStories: Bool, navigationController: @escaping () -> NavigationController?, listContext: StoryListContext?, initialStoryFolderId: Int64?) {
|
||||
public init(context: AccountContext, scope: Scope, captureProtected: Bool, isProfileEmbedded: Bool, canManageStories: Bool, excludeIds: [Int32] = [], navigationController: @escaping () -> NavigationController?, listContext: StoryListContext?, initialStoryFolderId: Int64?) {
|
||||
self.context = context
|
||||
self.scope = scope
|
||||
self.navigationController = navigationController
|
||||
self.isProfileEmbedded = isProfileEmbedded
|
||||
self.canManageStories = canManageStories
|
||||
self.excludeIds = Set(excludeIds)
|
||||
self.initialStoryFolderId = initialStoryFolderId
|
||||
|
||||
switch scope {
|
||||
@ -1790,10 +1799,28 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
|
||||
self.calendarSource = nil
|
||||
|
||||
var maxBotPreviewCount = 10
|
||||
if let data = self.context.currentAppConfiguration.with({ $0 }).data, let value = data["bot_preview_medias_max"] as? Double {
|
||||
maxBotPreviewCount = Int(value)
|
||||
var maxStoryFolders = 100
|
||||
var maxStoriesPerFolder = 1000
|
||||
if let data = self.context.currentAppConfiguration.with({ $0 }).data {
|
||||
if let value = data["bot_preview_medias_max"] as? Double {
|
||||
maxBotPreviewCount = Int(value)
|
||||
}
|
||||
if let value = data["stories_albums_limit"] as? Double {
|
||||
maxStoryFolders = Int(value)
|
||||
}
|
||||
if let value = data["stories_album_stories_limit"] as? Double {
|
||||
maxStoriesPerFolder = Int(value)
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
//maxStoryFolders = 10
|
||||
//maxStoriesPerFolder = 5
|
||||
#endif
|
||||
|
||||
self.maxBotPreviewCount = maxBotPreviewCount
|
||||
self.maxStoryFolders = maxStoryFolders
|
||||
self.maxStoriesPerFolder = maxStoriesPerFolder
|
||||
|
||||
super.init()
|
||||
|
||||
@ -2129,6 +2156,9 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
|
||||
guard let self, let itemInteraction = self._itemInteraction else {
|
||||
return
|
||||
}
|
||||
if self.excludeIds.contains(id) {
|
||||
return
|
||||
}
|
||||
|
||||
if let parentController = self.parentController as? PeerInfoScreen {
|
||||
parentController.toggleStorySelection(ids: [id], isSelected: value)
|
||||
@ -2540,7 +2570,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
|
||||
}
|
||||
|
||||
if let selectedMedia {
|
||||
if let result = self.directMediaImageCache.getImage(peer: peerReference, story: story, media: selectedMedia, width: 24, aspectRatio: 1.0, possibleWidths: [24], includeBlurred: false, synchronous: true) {
|
||||
if let result = self.directMediaImageCache.getImage(peer: peerReference, story: story, media: selectedMedia, width: 48, aspectRatio: 1.0, possibleWidths: [48], includeBlurred: false, synchronous: true) {
|
||||
if let loadSignal = result.loadSignal {
|
||||
imageSignal = .single(result.image) |> then(loadSignal)
|
||||
} else {
|
||||
@ -3060,7 +3090,8 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
|
||||
authorPeer: authorPeer,
|
||||
isPinned: state.pinnedIds.contains(item.storyItem.id),
|
||||
localMonthTimestamp: Month(localTimestamp: item.storyItem.timestamp + timezoneOffset).packedValue,
|
||||
isReorderable: isReorderable
|
||||
isReorderable: isReorderable,
|
||||
isEnabled: !self.excludeIds.contains(item.storyItem.id)
|
||||
))
|
||||
}
|
||||
if mappedItems.count < state.totalCount, let lastItem = state.items.last, let _ = state.loadMoreToken {
|
||||
@ -3437,7 +3468,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
|
||||
tempContextContentItemNode.frame = rect
|
||||
|
||||
var isSelected: Bool?
|
||||
if let selectedIds = self.itemInteraction.selectedIds {
|
||||
if !self.excludeIds.contains(tempContextContentItemNode.item.id), let selectedIds = self.itemInteraction.selectedIds {
|
||||
isSelected = selectedIds.contains(tempContextContentItemNode.item.id)
|
||||
}
|
||||
tempContextContentItemNode.itemView.updateSelection(theme: self.itemGridBinding.checkNodeTheme, isSelected: isSelected, animated: true)
|
||||
@ -3460,7 +3491,11 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
|
||||
guard let itemLayer = item.layer as? ItemLayer, let item = itemLayer.item else {
|
||||
return
|
||||
}
|
||||
itemLayer.updateSelection(theme: self.itemGridBinding.checkNodeTheme, isSelected: self.itemInteraction.selectedIds?.contains(item.story.id), animated: animated)
|
||||
var isSelected: Bool?
|
||||
if item.isEnabled {
|
||||
isSelected = self.itemInteraction.selectedIds?.contains(item.story.id)
|
||||
}
|
||||
itemLayer.updateSelection(theme: self.itemGridBinding.checkNodeTheme, isSelected: isSelected, animated: animated)
|
||||
}
|
||||
|
||||
var isSelecting = false
|
||||
@ -3805,14 +3840,18 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
|
||||
|
||||
var folderItems: [TabSelectorComponent.Item] = []
|
||||
let mainTitle: String
|
||||
let addTitle: String
|
||||
let addTitle: String?
|
||||
if case .botPreview = self.scope {
|
||||
mainTitle = self.presentationData.strings.BotPreviews_LanguageTab_Main
|
||||
addTitle = self.presentationData.strings.BotPreviews_LanguageTab_Add
|
||||
} else {
|
||||
//TODO:localize
|
||||
mainTitle = "All Stories"
|
||||
addTitle = "+ Add Album"
|
||||
if self.currentStoryFolders.count < self.maxStoryFolders {
|
||||
addTitle = "+ Add Album"
|
||||
} else {
|
||||
addTitle = nil
|
||||
}
|
||||
}
|
||||
folderItems.append(TabSelectorComponent.Item(
|
||||
id: AnyHashable("_main"),
|
||||
@ -3836,112 +3875,123 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
guard let sourceNode = sourceNode as? ContextExtractedContentContainingNode else {
|
||||
return
|
||||
}
|
||||
guard let controller = self.parentController else {
|
||||
return
|
||||
}
|
||||
|
||||
var items: [ContextMenuItem] = []
|
||||
|
||||
if self.canManageStories {
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Add Stories", icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat List/AddStoryIcon"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, a in
|
||||
guard let self else {
|
||||
a(.default)
|
||||
return
|
||||
}
|
||||
|
||||
a(.default)
|
||||
|
||||
self.presentAddStoriesToFolder()
|
||||
})))
|
||||
}
|
||||
|
||||
if self.canManageStories {
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Rename Album", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
||||
guard let self else {
|
||||
c?.dismiss(completion: nil)
|
||||
return
|
||||
}
|
||||
|
||||
c?.dismiss(completion: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.presentRenameStoryFolder(id: folder.id, title: folder.title)
|
||||
})
|
||||
})))
|
||||
Task { @MainActor [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Share", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
||||
guard let self else {
|
||||
c?.dismiss(completion: nil)
|
||||
return
|
||||
}
|
||||
|
||||
c?.dismiss(completion: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.shareFolder(id: folder.id)
|
||||
})
|
||||
})))
|
||||
}
|
||||
|
||||
if self.canManageStories {
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Reorder", icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReorderItems"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, a in
|
||||
guard let self else {
|
||||
a(.default)
|
||||
return
|
||||
}
|
||||
|
||||
a(.default)
|
||||
|
||||
self.beginReordering()
|
||||
})))
|
||||
guard let sourceNode = sourceNode as? ContextExtractedContentContainingNode else {
|
||||
return
|
||||
}
|
||||
guard let controller = self.parentController else {
|
||||
return
|
||||
}
|
||||
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Delete Album", textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { [weak self] c, _ in
|
||||
guard let self else {
|
||||
c?.dismiss(completion: nil)
|
||||
return
|
||||
}
|
||||
|
||||
c?.dismiss(completion: { [weak self] in
|
||||
var items: [ContextMenuItem] = []
|
||||
|
||||
var canAddStories = false
|
||||
if self.canManageStories {
|
||||
canAddStories = await self.canAddStoriesToFolder(folder: folder)
|
||||
}
|
||||
if canAddStories {
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Add Stories", icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat List/AddStoryIcon"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, a in
|
||||
guard let self else {
|
||||
a(.default)
|
||||
return
|
||||
}
|
||||
self.presentDeleteStoryFolder(id: folder.id)
|
||||
})
|
||||
})))
|
||||
|
||||
a(.default)
|
||||
|
||||
self.presentAddStoriesToFolder(folderId: folder.id)
|
||||
})))
|
||||
}
|
||||
|
||||
if self.canManageStories {
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Rename Album", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
||||
guard let self else {
|
||||
c?.dismiss(completion: nil)
|
||||
return
|
||||
}
|
||||
|
||||
c?.dismiss(completion: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.presentRenameStoryFolder(id: folder.id, title: folder.title)
|
||||
})
|
||||
})))
|
||||
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Share", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
||||
guard let self else {
|
||||
c?.dismiss(completion: nil)
|
||||
return
|
||||
}
|
||||
|
||||
c?.dismiss(completion: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.shareFolder(id: folder.id)
|
||||
})
|
||||
})))
|
||||
}
|
||||
|
||||
if self.canManageStories {
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Reorder", icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReorderItems"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, a in
|
||||
guard let self else {
|
||||
a(.default)
|
||||
return
|
||||
}
|
||||
|
||||
a(.default)
|
||||
|
||||
self.beginReordering()
|
||||
})))
|
||||
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Delete Album", textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { [weak self] c, _ in
|
||||
guard let self else {
|
||||
c?.dismiss(completion: nil)
|
||||
return
|
||||
}
|
||||
|
||||
c?.dismiss(completion: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.presentDeleteStoryFolder(id: folder.id)
|
||||
})
|
||||
})))
|
||||
}
|
||||
|
||||
let presentationData = self.presentationData
|
||||
let contextController = ContextController(
|
||||
presentationData: presentationData,
|
||||
source: .extracted(ItemExtractedContentSource(
|
||||
sourceNode: sourceNode,
|
||||
containerView: controller.view,
|
||||
keepInPlace: false
|
||||
)),
|
||||
items: .single(ContextController.Items(content: .list(items))),
|
||||
recognizer: nil,
|
||||
gesture: gesture
|
||||
)
|
||||
controller.presentInGlobalOverlay(contextController)
|
||||
}
|
||||
|
||||
let presentationData = self.presentationData
|
||||
let contextController = ContextController(
|
||||
presentationData: presentationData,
|
||||
source: .extracted(ItemExtractedContentSource(
|
||||
sourceNode: sourceNode,
|
||||
containerView: controller.view,
|
||||
keepInPlace: false
|
||||
)),
|
||||
items: .single(ContextController.Items(content: .list(items))),
|
||||
recognizer: nil,
|
||||
gesture: gesture
|
||||
)
|
||||
controller.presentInGlobalOverlay(contextController)
|
||||
}
|
||||
))
|
||||
}
|
||||
}
|
||||
if self.canManageStories {
|
||||
if self.canManageStories, let addTitle {
|
||||
folderItems.append(TabSelectorComponent.Item(
|
||||
id: AnyHashable("_add"),
|
||||
title: addTitle
|
||||
@ -4412,7 +4462,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
|
||||
} else {
|
||||
actionPanelGeneralTransition = ComponentTransition(transition)
|
||||
}
|
||||
if self.selectionPanel == nil, self.isProfileEmbedded, self.canManageStories, case let .peer(_, _, isArchived) = self.scope, self.canManageStories, !isArchived, self.isProfileEmbedded, self.currentStoryFolder != nil, let items = self.items, !items.items.isEmpty {
|
||||
if self.selectionPanel == nil, self.isProfileEmbedded, self.canManageStories, case let .peer(_, _, isArchived) = self.scope, self.canManageStories, !isArchived, self.isProfileEmbedded, self.currentStoryFolder != nil, let items = self.items, !items.items.isEmpty, items.count < self.maxStoriesPerFolder {
|
||||
let actionPanel: ComponentView<Empty>
|
||||
var actionPanelTransition = ComponentTransition(transition)
|
||||
if let current = self.actionPanel {
|
||||
@ -4433,10 +4483,10 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
|
||||
isEnabled: true,
|
||||
insets: UIEdgeInsets(top: 0.0, left: sideInset + 12.0, bottom: bottomInset, right: sideInset + 12.0),
|
||||
action: { [weak self] in
|
||||
guard let self else {
|
||||
guard let self, let currentStoryFolder = self.currentStoryFolder else {
|
||||
return
|
||||
}
|
||||
self.presentAddStoriesToFolder()
|
||||
self.presentAddStoriesToFolder(folderId: currentStoryFolder.id)
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
@ -4487,10 +4537,10 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
|
||||
text: "Add some stories to this album.",
|
||||
actionTitle: "Add to Album",
|
||||
action: { [weak self] in
|
||||
guard let self else {
|
||||
guard let self, let currentStoryFolder = self.currentStoryFolder else {
|
||||
return
|
||||
}
|
||||
self.presentAddStoriesToFolder()
|
||||
self.presentAddStoriesToFolder(folderId: currentStoryFolder.id)
|
||||
},
|
||||
additionalActionTitle: nil,
|
||||
additionalAction: {},
|
||||
@ -4927,7 +4977,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
|
||||
return items.count > 1
|
||||
} else {
|
||||
if self.currentStoryFolder == nil {
|
||||
return false
|
||||
return self.canManageStories
|
||||
}
|
||||
|
||||
guard let items = self.items else {
|
||||
@ -5010,25 +5060,54 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
|
||||
self.parentController?.present(promptController, in: .window(.root))
|
||||
}
|
||||
|
||||
private func presentAddStoriesToFolder() {
|
||||
@MainActor
|
||||
private func canAddStoriesToFolder(folder: PeerStoryListContext.State.Folder) async -> Bool {
|
||||
guard case let .peer(peerId, _, _) = self.scope else {
|
||||
return
|
||||
}
|
||||
guard let folder = self.currentStoryFolder else {
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
let controller = self.context.sharedContext.makeStorySelectionController(context: self.context, peerId: peerId, completion: { [weak self] items in
|
||||
if self.currentStoryFolder?.id == folder.id {
|
||||
let state = await (self.listSource.state |> take(1)).get()
|
||||
return state.totalCount < self.maxStoriesPerFolder
|
||||
} else {
|
||||
if let cachedState = await PeerStoryListContext.cachedFolderState(peerId: peerId, account: self.context.account, folderId: folder.id).get() {
|
||||
return cachedState.count < self.maxStoriesPerFolder
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func presentAddStoriesToFolder(folderId: Int64) {
|
||||
Task { @MainActor [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if let listSource = self.listSource as? PeerStoryListContext {
|
||||
let _ = listSource.addToFolder(id: folder.id, items: items)
|
||||
guard case let .peer(peerId, _, _) = self.scope else {
|
||||
return
|
||||
}
|
||||
})
|
||||
controller.navigationPresentation = .modal
|
||||
|
||||
self.parentController?.push(controller)
|
||||
|
||||
var existingIds: [Int32] = []
|
||||
if self.currentStoryFolder?.id == folderId {
|
||||
existingIds = (await self.listSource.state.get()).items.map(\.id.id)
|
||||
} else {
|
||||
if let cachedState = await PeerStoryListContext.cachedFolderState(peerId: peerId, account: self.context.account, folderId: folderId).get() {
|
||||
existingIds = cachedState.ids
|
||||
}
|
||||
}
|
||||
|
||||
let controller = self.context.sharedContext.makeStorySelectionController(context: self.context, peerId: peerId, excludeIds: existingIds, completion: { [weak self] items in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if let listSource = self.listSource as? PeerStoryListContext {
|
||||
let _ = listSource.addToFolder(id: folderId, items: items)
|
||||
}
|
||||
})
|
||||
controller.navigationPresentation = .modal
|
||||
|
||||
self.parentController?.push(controller)
|
||||
}
|
||||
}
|
||||
|
||||
public func presentDeleteCurrentStoryFolder() {
|
||||
|
||||
@ -925,7 +925,7 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
|
||||
if let selectedMedia {
|
||||
let directMediaImageCache = DirectMediaImageCache(account: component.context.account)
|
||||
if let result = directMediaImageCache.getImage(peer: peerReference, story: story, media: selectedMedia, width: 24, aspectRatio: 1.0, possibleWidths: [24], includeBlurred: false, synchronous: true) {
|
||||
if let result = directMediaImageCache.getImage(peer: peerReference, story: story, media: selectedMedia, width: 48, aspectRatio: 1.0, possibleWidths: [48], includeBlurred: false, synchronous: true) {
|
||||
if let loadSignal = result.loadSignal {
|
||||
imageSignal = .single(result.image) |> then(loadSignal)
|
||||
} else {
|
||||
|
||||
@ -6159,7 +6159,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
|
||||
if let selectedMedia {
|
||||
let directMediaImageCache = DirectMediaImageCache(account: component.context.account)
|
||||
if let result = directMediaImageCache.getImage(peer: peerReference, story: story, media: selectedMedia, width: 24, aspectRatio: 1.0, possibleWidths: [24], includeBlurred: false, synchronous: true) {
|
||||
if let result = directMediaImageCache.getImage(peer: peerReference, story: story, media: selectedMedia, width: 48, aspectRatio: 1.0, possibleWidths: [48], includeBlurred: false, synchronous: true) {
|
||||
if let loadSignal = result.loadSignal {
|
||||
imageSignal = .single(result.image) |> then(loadSignal)
|
||||
} else {
|
||||
|
||||
@ -2546,8 +2546,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
return PeerInfoStoryGridScreen(context: context, peerId: context.account.peerId, scope: isArchive ? .archive : .saved)
|
||||
}
|
||||
|
||||
public func makeStorySelectionController(context: AccountContext, peerId: EnginePeer.Id, completion: @escaping ([EngineStoryItem]) -> Void) -> ViewController {
|
||||
return PeerInfoStoryGridScreen(context: context, peerId: peerId, scope: .saved, selectionModeCompletion: completion)
|
||||
public func makeStorySelectionController(context: AccountContext, peerId: EnginePeer.Id, excludeIds: [Int32], completion: @escaping ([EngineStoryItem]) -> Void) -> ViewController {
|
||||
return PeerInfoStoryGridScreen(context: context, peerId: peerId, scope: .saved, excludeIds: excludeIds, selectionModeCompletion: completion)
|
||||
}
|
||||
|
||||
public func makeArchiveSettingsController(context: AccountContext) -> ViewController {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user