mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various improvements
This commit is contained in:
parent
3423fb8319
commit
a742df315c
@ -95,7 +95,22 @@ private enum ChatListRecentEntry: Comparable, Identifiable {
|
||||
}
|
||||
}
|
||||
|
||||
func item(context: AccountContext, presentationData: ChatListPresentationData, filter: ChatListNodePeersFilter, key: ChatListSearchPaneKey, peerSelected: @escaping (EnginePeer, Int64?) -> Void, disabledPeerSelected: @escaping (EnginePeer, Int64?, ChatListDisabledPeerReason) -> Void, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, clearRecentlySearchedPeers: @escaping () -> Void, deletePeer: @escaping (EnginePeer.Id) -> Void, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, openStories: @escaping (EnginePeer.Id, AvatarNode) -> Void) -> ListViewItem {
|
||||
func item(
|
||||
context: AccountContext,
|
||||
presentationData: ChatListPresentationData,
|
||||
filter: ChatListNodePeersFilter,
|
||||
key: ChatListSearchPaneKey,
|
||||
peerSelected: @escaping (EnginePeer, Int64?) -> Void,
|
||||
disabledPeerSelected: @escaping (EnginePeer, Int64?, ChatListDisabledPeerReason) -> Void,
|
||||
peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?,
|
||||
clearRecentlySearchedPeers: @escaping () -> Void,
|
||||
deletePeer: @escaping (EnginePeer.Id) -> Void,
|
||||
animationCache: AnimationCache,
|
||||
animationRenderer: MultiAnimationRenderer,
|
||||
openStories: @escaping (EnginePeer.Id, AvatarNode) -> Void,
|
||||
isChannelsTabExpanded: Bool,
|
||||
toggleChannelsTabExpanded: @escaping () -> Void
|
||||
) -> ListViewItem {
|
||||
switch self {
|
||||
case let .topPeers(peers, theme, strings):
|
||||
return ChatListRecentPeersListItem(theme: theme, strings: strings, context: context, peers: peers, peerSelected: { peer in
|
||||
@ -224,9 +239,12 @@ private enum ChatListRecentEntry: Comparable, Identifiable {
|
||||
if case .channels = key {
|
||||
if case .recommendedChannels = section {
|
||||
//TODO:localize
|
||||
header = ChatListSearchItemHeader(type: .text("RECOMMENDED CHANNELS", 0), theme: theme, strings: strings)
|
||||
header = ChatListSearchItemHeader(type: .text("RECOMMENDED CHANNELS", 1), theme: theme, strings: strings)
|
||||
} else {
|
||||
header = nil
|
||||
//TODO:localize
|
||||
header = ChatListSearchItemHeader(type: .text("CHANNELS YOU JOINED", 0), theme: theme, strings: strings, actionTitle: isChannelsTabExpanded ? "Show less" : "Show more", action: {
|
||||
toggleChannelsTabExpanded()
|
||||
})
|
||||
}
|
||||
} else {
|
||||
header = ChatListSearchItemHeader(type: .recentPeers, theme: theme, strings: strings, actionTitle: strings.WebSearch_RecentSectionClear, action: {
|
||||
@ -944,12 +962,30 @@ public struct ChatListSearchContainerTransition {
|
||||
}
|
||||
}
|
||||
|
||||
private func chatListSearchContainerPreparedRecentTransition(from fromEntries: [ChatListRecentEntry], to toEntries: [ChatListRecentEntry], context: AccountContext, presentationData: ChatListPresentationData, filter: ChatListNodePeersFilter, key: ChatListSearchPaneKey, peerSelected: @escaping (EnginePeer, Int64?) -> Void, disabledPeerSelected: @escaping (EnginePeer, Int64?, ChatListDisabledPeerReason) -> Void, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, clearRecentlySearchedPeers: @escaping () -> Void, deletePeer: @escaping (EnginePeer.Id) -> Void, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, openStories: @escaping (EnginePeer.Id, AvatarNode) -> Void) -> ChatListSearchContainerRecentTransition {
|
||||
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
||||
private func chatListSearchContainerPreparedRecentTransition(
|
||||
from fromEntries: [ChatListRecentEntry],
|
||||
to toEntries: [ChatListRecentEntry],
|
||||
forceUpdateAll: Bool,
|
||||
context: AccountContext,
|
||||
presentationData: ChatListPresentationData,
|
||||
filter: ChatListNodePeersFilter,
|
||||
key: ChatListSearchPaneKey,
|
||||
peerSelected: @escaping (EnginePeer, Int64?) -> Void,
|
||||
disabledPeerSelected: @escaping (EnginePeer, Int64?, ChatListDisabledPeerReason) -> Void,
|
||||
peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?,
|
||||
clearRecentlySearchedPeers: @escaping () -> Void,
|
||||
deletePeer: @escaping (EnginePeer.Id) -> Void,
|
||||
animationCache: AnimationCache,
|
||||
animationRenderer: MultiAnimationRenderer,
|
||||
openStories: @escaping (EnginePeer.Id, AvatarNode) -> Void,
|
||||
isChannelsTabExpanded: Bool,
|
||||
toggleChannelsTabExpanded: @escaping () -> Void
|
||||
) -> ChatListSearchContainerRecentTransition {
|
||||
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries, allUpdated: forceUpdateAll)
|
||||
|
||||
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
||||
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, filter: filter, key: key, peerSelected: peerSelected, disabledPeerSelected: disabledPeerSelected, peerContextAction: peerContextAction, clearRecentlySearchedPeers: clearRecentlySearchedPeers, deletePeer: deletePeer, animationCache: animationCache, animationRenderer: animationRenderer, openStories: openStories), directionHint: nil) }
|
||||
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, filter: filter, key: key, peerSelected: peerSelected, disabledPeerSelected: disabledPeerSelected, peerContextAction: peerContextAction, clearRecentlySearchedPeers: clearRecentlySearchedPeers, deletePeer: deletePeer, animationCache: animationCache, animationRenderer: animationRenderer, openStories: openStories), directionHint: nil) }
|
||||
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, filter: filter, key: key, peerSelected: peerSelected, disabledPeerSelected: disabledPeerSelected, peerContextAction: peerContextAction, clearRecentlySearchedPeers: clearRecentlySearchedPeers, deletePeer: deletePeer, animationCache: animationCache, animationRenderer: animationRenderer, openStories: openStories, isChannelsTabExpanded: isChannelsTabExpanded, toggleChannelsTabExpanded: toggleChannelsTabExpanded), directionHint: nil) }
|
||||
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, filter: filter, key: key, peerSelected: peerSelected, disabledPeerSelected: disabledPeerSelected, peerContextAction: peerContextAction, clearRecentlySearchedPeers: clearRecentlySearchedPeers, deletePeer: deletePeer, animationCache: animationCache, animationRenderer: animationRenderer, openStories: openStories, isChannelsTabExpanded: isChannelsTabExpanded, toggleChannelsTabExpanded: toggleChannelsTabExpanded), directionHint: nil) }
|
||||
|
||||
return ChatListSearchContainerRecentTransition(deletions: deletions, insertions: insertions, updates: updates)
|
||||
}
|
||||
@ -2744,7 +2780,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
}
|
||||
}))
|
||||
|
||||
let previousRecentItems = Atomic<[ChatListRecentEntry]?>(value: nil)
|
||||
let previousRecentItemsValue = Atomic<RecentItems?>(value: nil)
|
||||
let hasRecentPeers: Signal<Bool, NoError>
|
||||
if case .channels = key {
|
||||
hasRecentPeers = .single(false)
|
||||
@ -2761,7 +2797,20 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
|> distinctUntilChanged
|
||||
}
|
||||
|
||||
var recentItems = combineLatest(
|
||||
struct RecentItems {
|
||||
var entries: [ChatListRecentEntry]
|
||||
var isChannelsTabExpanded: Bool
|
||||
var recommendedChannelOrder: [EnginePeer.Id]
|
||||
}
|
||||
|
||||
let isChannelsTabExpandedValue = ValuePromise<Bool>(false, ignoreRepeated: true)
|
||||
let toggleChannelsTabExpanded: () -> Void = {
|
||||
let _ = (isChannelsTabExpandedValue.get() |> take(1)).startStandalone(next: { value in
|
||||
isChannelsTabExpandedValue.set(!value)
|
||||
})
|
||||
}
|
||||
|
||||
var recentItems: Signal<RecentItems, NoError> = combineLatest(
|
||||
hasRecentPeers,
|
||||
fixedRecentlySearchedPeers |> mapToSignal { peers -> Signal<([RecentlySearchedPeer], [EnginePeer.Id: PeerStoryStats], [EnginePeer.Id: Bool], Set<EnginePeer.Id>), NoError> in
|
||||
return context.engine.data.subscribe(
|
||||
@ -2804,7 +2853,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
presentationDataPromise.get(),
|
||||
context.engine.data.subscribe(TelegramEngine.EngineData.Item.NotificationSettings.Global())
|
||||
)
|
||||
|> mapToSignal { hasRecentPeers, peersAndStories, presentationData, globalNotificationSettings -> Signal<[ChatListRecentEntry], NoError> in
|
||||
|> mapToSignal { hasRecentPeers, peersAndStories, presentationData, globalNotificationSettings -> Signal<RecentItems, NoError> in
|
||||
let (peers, peerStoryStats, requiresPremiumForMessaging, refreshIsPremiumRequiredForMessaging) = peersAndStories
|
||||
|
||||
if !refreshIsPremiumRequiredForMessaging.isEmpty {
|
||||
@ -2834,17 +2883,28 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
}
|
||||
}
|
||||
|
||||
return .single(entries)
|
||||
return .single(RecentItems(entries: entries, isChannelsTabExpanded: false, recommendedChannelOrder: []))
|
||||
}
|
||||
|
||||
if peersFilter.contains(.excludeRecent) {
|
||||
recentItems = .single([])
|
||||
recentItems = .single(RecentItems(entries: [], isChannelsTabExpanded: false, recommendedChannelOrder: []))
|
||||
}
|
||||
if case .savedMessagesChats = location {
|
||||
recentItems = .single([])
|
||||
recentItems = .single(RecentItems(entries: [], isChannelsTabExpanded: false, recommendedChannelOrder: []))
|
||||
}
|
||||
if case .channels = key {
|
||||
let localChannels = context.engine.messages.getAllLocalChannels()
|
||||
struct LocalChannels {
|
||||
var peerIds: [EnginePeer.Id]
|
||||
var isExpanded: Bool
|
||||
}
|
||||
let localChannels = isChannelsTabExpandedValue.get()
|
||||
|> mapToSignal { isChannelsTabExpanded -> Signal<LocalChannels, NoError> in
|
||||
return context.engine.messages.getAllLocalChannels(count: isChannelsTabExpanded ? 500 : 5)
|
||||
|> map { peerIds -> LocalChannels in
|
||||
return LocalChannels(peerIds: peerIds, isExpanded: isChannelsTabExpanded)
|
||||
}
|
||||
}
|
||||
|
||||
let remoteChannels: Signal<RecommendedChannels?, NoError> = context.engine.peers.recommendedChannels(peerId: nil)
|
||||
|
||||
let _ = self.context.engine.peers.requestGlobalRecommendedChannelsIfNeeded().startStandalone()
|
||||
@ -2853,16 +2913,19 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
localChannels,
|
||||
remoteChannels
|
||||
)
|
||||
|> mapToSignal { localChannelIds, remoteChannels -> Signal<[ChatListRecentEntry], NoError> in
|
||||
var allChannelIds = localChannelIds
|
||||
|> mapToSignal { localChannels, remoteChannels -> Signal<RecentItems, NoError> in
|
||||
var allChannelIds = localChannels.peerIds
|
||||
let isChannelsTabExpanded = localChannels.isExpanded
|
||||
|
||||
var cachedSubscribers: [EnginePeer.Id: Int32] = [:]
|
||||
var recommendedChannelOrder: [EnginePeer.Id] = []
|
||||
if let remoteChannels {
|
||||
for channel in remoteChannels.channels {
|
||||
if !allChannelIds.contains(channel.peer.id) {
|
||||
allChannelIds.append(channel.peer.id)
|
||||
}
|
||||
cachedSubscribers[channel.peer.id] = channel.subscribers
|
||||
recommendedChannelOrder.append(channel.peer.id)
|
||||
}
|
||||
}
|
||||
|
||||
@ -2882,6 +2945,11 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
return TelegramEngine.EngineData.Item.Messages.PeerUnreadCount(id: peerId)
|
||||
}
|
||||
),
|
||||
EngineDataMap(
|
||||
allChannelIds.map { peerId -> TelegramEngine.EngineData.Item.Peer.StoryStats in
|
||||
return TelegramEngine.EngineData.Item.Peer.StoryStats(id: peerId)
|
||||
}
|
||||
),
|
||||
EngineDataMap(
|
||||
allChannelIds.map { peerId -> TelegramEngine.EngineData.Item.Peer.ParticipantCount in
|
||||
return TelegramEngine.EngineData.Item.Peer.ParticipantCount(id: peerId)
|
||||
@ -2889,11 +2957,11 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
),
|
||||
TelegramEngine.EngineData.Item.NotificationSettings.Global()
|
||||
)
|
||||
|> map { peers, notificationSettings, unreadCounts, participantCounts, globalNotificationSettings -> [ChatListRecentEntry] in
|
||||
|> map { peers, notificationSettings, unreadCounts, storyStats, participantCounts, globalNotificationSettings -> RecentItems in
|
||||
var result: [ChatListRecentEntry] = []
|
||||
var existingIds = Set<PeerId>()
|
||||
|
||||
for id in localChannelIds {
|
||||
for id in localChannels.peerIds {
|
||||
if existingIds.contains(id) {
|
||||
continue
|
||||
}
|
||||
@ -2908,6 +2976,10 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
} else if let count = cachedSubscribers[id] {
|
||||
subpeerSummary = RecentlySearchedPeerSubpeerSummary(count: Int(count))
|
||||
}
|
||||
var peerStoryStats: PeerStoryStats?
|
||||
if let value = storyStats[peer.id] {
|
||||
peerStoryStats = value
|
||||
}
|
||||
result.append(.peer(
|
||||
index: result.count,
|
||||
peer: RecentlySearchedPeer(
|
||||
@ -2924,7 +2996,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
presentationData.nameSortOrder,
|
||||
presentationData.nameDisplayOrder,
|
||||
globalNotificationSettings,
|
||||
nil,
|
||||
peerStoryStats,
|
||||
false
|
||||
))
|
||||
}
|
||||
@ -2944,6 +3016,10 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
} else if let count = cachedSubscribers[channel.peer.id] {
|
||||
subpeerSummary = RecentlySearchedPeerSubpeerSummary(count: Int(count))
|
||||
}
|
||||
var peerStoryStats: PeerStoryStats?
|
||||
if let value = storyStats[peer.id] {
|
||||
peerStoryStats = value
|
||||
}
|
||||
result.append(.peer(
|
||||
index: result.count,
|
||||
peer: RecentlySearchedPeer(
|
||||
@ -2960,13 +3036,13 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
presentationData.nameSortOrder,
|
||||
presentationData.nameDisplayOrder,
|
||||
globalNotificationSettings,
|
||||
nil,
|
||||
peerStoryStats,
|
||||
false
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
return RecentItems(entries: result, isChannelsTabExpanded: isChannelsTabExpanded, recommendedChannelOrder: recommendedChannelOrder)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2979,18 +3055,26 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
presentationDataPromise.get(),
|
||||
recentItems
|
||||
)
|
||||
|> deliverOnMainQueue).startStrict(next: { [weak self] presentationData, entries in
|
||||
|> deliverOnMainQueue).startStrict(next: { [weak self] presentationData, recentItems in
|
||||
if let strongSelf = self {
|
||||
let previousEntries = previousRecentItems.swap(entries)
|
||||
let previousRecentItems = previousRecentItemsValue.swap(recentItems)
|
||||
|
||||
var firstTime = previousEntries == nil
|
||||
if let previousEntries {
|
||||
if previousEntries.count < entries.count {
|
||||
var firstTime = previousRecentItems == nil
|
||||
var forceUpdateAll = false
|
||||
if let previousRecentItems {
|
||||
if previousRecentItems.entries.count < recentItems.entries.count {
|
||||
firstTime = true
|
||||
}
|
||||
if previousRecentItems.recommendedChannelOrder != recentItems.recommendedChannelOrder {
|
||||
firstTime = true
|
||||
}
|
||||
if previousRecentItems.isChannelsTabExpanded != recentItems.isChannelsTabExpanded {
|
||||
firstTime = true
|
||||
forceUpdateAll = true
|
||||
}
|
||||
}
|
||||
|
||||
let transition = chatListSearchContainerPreparedRecentTransition(from: previousEntries ?? [], to: entries, context: context, presentationData: presentationData, filter: peersFilter, key: key, peerSelected: { peer, threadId in
|
||||
let transition = chatListSearchContainerPreparedRecentTransition(from: previousRecentItems?.entries ?? [], to: recentItems.entries, forceUpdateAll: forceUpdateAll, context: context, presentationData: presentationData, filter: peersFilter, key: key, peerSelected: { peer, threadId in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
@ -2998,7 +3082,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
if case .channels = key {
|
||||
if let navigationController = self.navigationController {
|
||||
var customChatNavigationStack: [EnginePeer.Id] = []
|
||||
if let entries = previousRecentItems.with({ $0 }) {
|
||||
if let entries = previousRecentItemsValue.with({ $0 })?.entries {
|
||||
for entry in entries {
|
||||
if case let .peer(_, peer, _, _, _, _, _, _, _, _, _) = entry {
|
||||
customChatNavigationStack.append(peer.peer.peerId)
|
||||
@ -3040,6 +3124,10 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
let _ = context.engine.peers.removeRecentlySearchedPeer(peerId: peerId).startStandalone()
|
||||
}, animationCache: strongSelf.animationCache, animationRenderer: strongSelf.animationRenderer, openStories: { peerId, avatarNode in
|
||||
interaction.openStories?(peerId, avatarNode)
|
||||
},
|
||||
isChannelsTabExpanded: recentItems.isChannelsTabExpanded,
|
||||
toggleChannelsTabExpanded: {
|
||||
toggleChannelsTabExpanded()
|
||||
})
|
||||
strongSelf.enqueueRecentTransition(transition, firstTime: firstTime)
|
||||
}
|
||||
|
@ -1438,7 +1438,9 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present
|
||||
entries.append(.experimentalCompatibility(experimentalSettings.experimentalCompatibility))
|
||||
entries.append(.enableDebugDataDisplay(experimentalSettings.enableDebugDataDisplay))
|
||||
entries.append(.acceleratedStickers(experimentalSettings.acceleratedStickers))
|
||||
entries.append(.browserExperiment(experimentalSettings.browserExperiment))
|
||||
if sharedContext.applicationBindings.appBuildType == .internal {
|
||||
entries.append(.browserExperiment(experimentalSettings.browserExperiment))
|
||||
}
|
||||
entries.append(.localTranscription(experimentalSettings.localTranscription))
|
||||
if case .internal = sharedContext.applicationBindings.appBuildType {
|
||||
entries.append(.enableReactionOverrides(experimentalSettings.enableReactionOverrides))
|
||||
|
@ -1411,7 +1411,7 @@ public extension TelegramEngine {
|
||||
return _internal_reportAdMessage(account: self.account, peerId: peerId, opaqueId: opaqueId, option: option)
|
||||
}
|
||||
|
||||
public func getAllLocalChannels() -> Signal<[EnginePeer.Id], NoError> {
|
||||
public func getAllLocalChannels(count: Int) -> Signal<[EnginePeer.Id], NoError> {
|
||||
return self.account.postbox.transaction { transaction -> [EnginePeer.Id] in
|
||||
var result: [EnginePeer.Id] = []
|
||||
|
||||
@ -1444,7 +1444,7 @@ public extension TelegramEngine {
|
||||
}
|
||||
filteredResult.append(id)
|
||||
|
||||
if filteredResult.count >= 5 {
|
||||
if filteredResult.count >= count {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -419,7 +419,18 @@ private final class PeerInfoPendingPane {
|
||||
let paneNode: PeerInfoPaneNode
|
||||
switch key {
|
||||
case .stories, .storyArchive:
|
||||
let visualPaneNode = PeerInfoStoryPaneNode(context: context, peerId: peerId, chatLocation: chatLocation, contentType: .photoOrVideo, captureProtected: captureProtected, isSaved: false, isArchive: key == .storyArchive, isProfileEmbedded: true, navigationController: chatControllerInteraction.navigationController, listContext: key == .storyArchive ? data.storyArchiveListContext : data.storyListContext)
|
||||
var canManage = false
|
||||
if let peer = data.peer {
|
||||
if peer.id == context.account.peerId {
|
||||
canManage = true
|
||||
} else if let channel = peer as? TelegramChannel {
|
||||
if channel.hasPermission(.editStories) {
|
||||
canManage = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let visualPaneNode = PeerInfoStoryPaneNode(context: context, peerId: peerId, chatLocation: chatLocation, contentType: .photoOrVideo, captureProtected: captureProtected, isSaved: false, isArchive: key == .storyArchive, isProfileEmbedded: true, canManageStories: canManage, navigationController: chatControllerInteraction.navigationController, listContext: key == .storyArchive ? data.storyArchiveListContext : data.storyListContext)
|
||||
paneNode = visualPaneNode
|
||||
visualPaneNode.openCurrentDate = {
|
||||
openMediaCalendar()
|
||||
|
@ -462,6 +462,7 @@ final class PeerInfoStoryGridScreenComponent: Component {
|
||||
isSaved: true,
|
||||
isArchive: component.scope == .archive,
|
||||
isProfileEmbedded: false,
|
||||
canManageStories: true,
|
||||
navigationController: { [weak self] in
|
||||
guard let self else {
|
||||
return nil
|
||||
|
@ -1185,6 +1185,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
|
||||
private let isSaved: Bool
|
||||
private let isArchive: Bool
|
||||
private let isProfileEmbedded: Bool
|
||||
private let canManageStories: Bool
|
||||
public private(set) var contentType: ContentType
|
||||
private var contentTypePromise: ValuePromise<ContentType>
|
||||
|
||||
@ -1298,7 +1299,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
|
||||
private weak var contextControllerToDismissOnSelection: ContextControllerProtocol?
|
||||
private weak var tempContextContentItemNode: TempExtractedItemNode?
|
||||
|
||||
public init(context: AccountContext, peerId: PeerId, chatLocation: ChatLocation, contentType: ContentType, captureProtected: Bool, isSaved: Bool, isArchive: Bool, isProfileEmbedded: Bool, navigationController: @escaping () -> NavigationController?, listContext: PeerStoryListContext?) {
|
||||
public init(context: AccountContext, peerId: PeerId, chatLocation: ChatLocation, contentType: ContentType, captureProtected: Bool, isSaved: Bool, isArchive: Bool, isProfileEmbedded: Bool, canManageStories: Bool, navigationController: @escaping () -> NavigationController?, listContext: PeerStoryListContext?) {
|
||||
self.context = context
|
||||
self.peerId = peerId
|
||||
self.chatLocation = chatLocation
|
||||
@ -1308,6 +1309,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
|
||||
self.isSaved = isSaved
|
||||
self.isArchive = isArchive
|
||||
self.isProfileEmbedded = isProfileEmbedded
|
||||
self.canManageStories = canManageStories
|
||||
|
||||
self.isSelectionModeActive = !isProfileEmbedded && isArchive
|
||||
|
||||
@ -1657,12 +1659,25 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
|
||||
guard let item = strongSelf.itemGrid.item(at: point) else {
|
||||
return false
|
||||
}
|
||||
guard let layer = item.layer as? ItemLayer else {
|
||||
return false
|
||||
}
|
||||
guard let storyItem = layer.item else {
|
||||
return false
|
||||
}
|
||||
|
||||
if let result = strongSelf.view.hitTest(point, with: nil) {
|
||||
if result.asyncdisplaykit_node is SparseItemGridScrollingArea {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if !strongSelf.canManageStories {
|
||||
if !storyItem.story.isForwardingDisabled, case .everyone = storyItem.story.privacy?.base {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
strongSelf.currentGestureItem = item
|
||||
|
||||
@ -1768,167 +1783,190 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
|
||||
}
|
||||
|
||||
private func openContextMenu(item: EngineStoryItem, itemLayer: ItemLayer, rect: CGRect, gesture: ContextGesture?) {
|
||||
guard let parentController = self.parentController else {
|
||||
return
|
||||
}
|
||||
|
||||
var items: [ContextMenuItem] = []
|
||||
|
||||
//TODO:localize
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: !self.isArchive ? "Archive" : "Unarchive", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: self.isArchive ? "Chat/Context Menu/Archive" : "Chat/Context Menu/Unarchive"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in
|
||||
let _ = (self.context.engine.data.get(
|
||||
TelegramEngine.EngineData.Item.Peer.Peer(id: self.peerId)
|
||||
)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] _ in
|
||||
guard let self else {
|
||||
f(.default)
|
||||
return
|
||||
}
|
||||
guard let parentController = self.parentController else {
|
||||
return
|
||||
}
|
||||
|
||||
if self.isArchive {
|
||||
f(.default)
|
||||
} else {
|
||||
f(.dismissWithoutContent)
|
||||
}
|
||||
let canManage = self.canManageStories
|
||||
|
||||
let _ = self.context.engine.messages.updateStoriesArePinned(peerId: self.peerId, ids: [item.id: item], isPinned: self.isArchive ? true : false).startStandalone()
|
||||
})))
|
||||
|
||||
if !self.isArchive {
|
||||
let isPinned = self.pinnedIds.contains(item.id)
|
||||
items.append(.action(ContextMenuActionItem(text: isPinned ? "Unpin" : "Pin", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: isPinned ? "Chat/Context Menu/Unpin" : "Chat/Context Menu/Pin"), color: theme.contextMenu.primaryColor) }, action: { [weak self, weak itemLayer] _, f in
|
||||
itemLayer?.isHidden = false
|
||||
guard let self else {
|
||||
f(.default)
|
||||
return
|
||||
}
|
||||
|
||||
if !isPinned && self.pinnedIds.count >= 3 {
|
||||
f(.default)
|
||||
|
||||
let presentationData = self.presentationData
|
||||
self.parentController?.present(UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: "You can't pin more than 3 posts.", timeout: nil, customUndoText: nil), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
f(.dismissWithoutContent)
|
||||
|
||||
var updatedPinnedIds = self.pinnedIds
|
||||
if isPinned {
|
||||
updatedPinnedIds.remove(item.id)
|
||||
} else {
|
||||
updatedPinnedIds.insert(item.id)
|
||||
}
|
||||
let _ = self.context.engine.messages.updatePinnedToTopStories(peerId: self.peerId, ids: Array(updatedPinnedIds)).startStandalone()
|
||||
|
||||
//TODO:localize
|
||||
let presentationData = self.presentationData
|
||||
self.parentController?.present(UndoOverlayController(presentationData: presentationData, content: .actionSucceeded(title: isPinned ? nil : "Story Pinned", text: isPinned ? "Story Unpinned." : "Now it will always be shown on the top.", cancel: nil, destructive: false), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current)
|
||||
})))
|
||||
}
|
||||
|
||||
/*items.append(.action(ContextMenuActionItem(text: "Edit", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
||||
c.dismiss(completion: {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let _ = self
|
||||
|
||||
|
||||
})
|
||||
})))*/
|
||||
|
||||
if !item.isForwardingDisabled {
|
||||
items.append(.action(ContextMenuActionItem(text: "Forward", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
||||
c.dismiss(completion: {
|
||||
var items: [ContextMenuItem] = []
|
||||
|
||||
//TODO:localize
|
||||
|
||||
if canManage {
|
||||
items.append(.action(ContextMenuActionItem(text: !self.isArchive ? "Archive" : "Unarchive", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: self.isArchive ? "Chat/Context Menu/Archive" : "Chat/Context Menu/Unarchive"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in
|
||||
guard let self else {
|
||||
f(.default)
|
||||
return
|
||||
}
|
||||
|
||||
let _ = (self.context.engine.data.get(
|
||||
TelegramEngine.EngineData.Item.Peer.Peer(id: self.peerId)
|
||||
)
|
||||
|> deliverOnMainQueue).startStandalone(next: { [weak self] peer in
|
||||
if self.isArchive {
|
||||
f(.default)
|
||||
} else {
|
||||
f(.dismissWithoutContent)
|
||||
}
|
||||
|
||||
let _ = self.context.engine.messages.updateStoriesArePinned(peerId: self.peerId, ids: [item.id: item], isPinned: self.isArchive ? true : false).startStandalone()
|
||||
self.parentController?.present(UndoOverlayController(presentationData: presentationData, content: .actionSucceeded(title: nil, text: self.isArchive ? "Story unarchived." : "Story archived.", cancel: nil, destructive: false), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current)
|
||||
})))
|
||||
|
||||
if !self.isArchive {
|
||||
let isPinned = self.pinnedIds.contains(item.id)
|
||||
items.append(.action(ContextMenuActionItem(text: isPinned ? "Unpin" : "Pin", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: isPinned ? "Chat/Context Menu/Unpin" : "Chat/Context Menu/Pin"), color: theme.contextMenu.primaryColor) }, action: { [weak self, weak itemLayer] _, f in
|
||||
itemLayer?.isHidden = false
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
guard let peer, let peerReference = PeerReference(peer._asPeer()) else {
|
||||
f(.default)
|
||||
return
|
||||
}
|
||||
|
||||
let shareController = ShareController(
|
||||
context: self.context,
|
||||
subject: .media(.story(peer: peerReference, id: item.id, media: TelegramMediaStory(storyId: StoryId(peerId: self.peerId, id: item.id), isMention: false))),
|
||||
presetText: nil,
|
||||
preferredAction: .default,
|
||||
showInChat: nil,
|
||||
fromForeignApp: false,
|
||||
segmentedValues: nil,
|
||||
externalShare: false,
|
||||
immediateExternalShare: false,
|
||||
switchableAccounts: [],
|
||||
immediatePeerId: nil,
|
||||
updatedPresentationData: nil,
|
||||
forceTheme: nil,
|
||||
forcedActionTitle: nil,
|
||||
shareAsLink: false,
|
||||
collectibleItemInfo: nil
|
||||
)
|
||||
self.parentController?.present(shareController, in: .window(.root))
|
||||
})
|
||||
})
|
||||
})))
|
||||
}
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Conversation_ContextMenuDelete, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { [weak self] c, _ in
|
||||
c.dismiss(completion: {
|
||||
guard let self else {
|
||||
return
|
||||
if !isPinned && self.pinnedIds.count >= 3 {
|
||||
f(.default)
|
||||
|
||||
let presentationData = self.presentationData
|
||||
self.parentController?.present(UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: "You can't pin more than 3 posts.", timeout: nil, customUndoText: nil), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
f(.dismissWithoutContent)
|
||||
|
||||
var updatedPinnedIds = self.pinnedIds
|
||||
if isPinned {
|
||||
updatedPinnedIds.remove(item.id)
|
||||
} else {
|
||||
updatedPinnedIds.insert(item.id)
|
||||
}
|
||||
let _ = self.context.engine.messages.updatePinnedToTopStories(peerId: self.peerId, ids: Array(updatedPinnedIds)).startStandalone()
|
||||
|
||||
//TODO:localize
|
||||
let presentationData = self.presentationData
|
||||
self.parentController?.present(UndoOverlayController(presentationData: presentationData, content: .actionSucceeded(title: isPinned ? nil : "Story Pinned", text: isPinned ? "Story Unpinned." : "Now it will always be shown on the top.", cancel: nil, destructive: false), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current)
|
||||
})))
|
||||
}
|
||||
|
||||
self.presentDeleteConfirmation(ids: Set([item.id]))
|
||||
})
|
||||
})))
|
||||
|
||||
items.append(.separator)
|
||||
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Conversation_ContextMenuSelect, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Select"), color: theme.actionSheet.primaryTextColor)
|
||||
}, action: { [weak self] c, f in
|
||||
guard let self, let parentController = self.parentController as? PeerInfoScreen else {
|
||||
f(.default)
|
||||
/*items.append(.action(ContextMenuActionItem(text: "Edit", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
||||
c.dismiss(completion: {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let _ = self
|
||||
|
||||
|
||||
})
|
||||
})))*/
|
||||
}
|
||||
|
||||
if !item.isForwardingDisabled, case .everyone = item.privacy?.base {
|
||||
items.append(.action(ContextMenuActionItem(text: "Forward", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
||||
c.dismiss(completion: {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
let _ = (self.context.engine.data.get(
|
||||
TelegramEngine.EngineData.Item.Peer.Peer(id: self.peerId)
|
||||
)
|
||||
|> deliverOnMainQueue).startStandalone(next: { [weak self] peer in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
guard let peer, let peerReference = PeerReference(peer._asPeer()) else {
|
||||
return
|
||||
}
|
||||
|
||||
let shareController = ShareController(
|
||||
context: self.context,
|
||||
subject: .media(.story(peer: peerReference, id: item.id, media: TelegramMediaStory(storyId: StoryId(peerId: self.peerId, id: item.id), isMention: false))),
|
||||
presetText: nil,
|
||||
preferredAction: .default,
|
||||
showInChat: nil,
|
||||
fromForeignApp: false,
|
||||
segmentedValues: nil,
|
||||
externalShare: false,
|
||||
immediateExternalShare: false,
|
||||
switchableAccounts: [],
|
||||
immediatePeerId: nil,
|
||||
updatedPresentationData: nil,
|
||||
forceTheme: nil,
|
||||
forcedActionTitle: nil,
|
||||
shareAsLink: false,
|
||||
collectibleItemInfo: nil
|
||||
)
|
||||
self.parentController?.present(shareController, in: .window(.root))
|
||||
})
|
||||
})
|
||||
})))
|
||||
}
|
||||
|
||||
if canManage {
|
||||
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Conversation_ContextMenuDelete, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { [weak self] c, _ in
|
||||
c.dismiss(completion: {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
self.presentDeleteConfirmation(ids: Set([item.id]))
|
||||
})
|
||||
})))
|
||||
}
|
||||
|
||||
if self.canManageStories {
|
||||
if !items.isEmpty {
|
||||
items.append(.separator)
|
||||
}
|
||||
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Conversation_ContextMenuSelect, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Select"), color: theme.actionSheet.primaryTextColor)
|
||||
}, action: { [weak self] c, f in
|
||||
guard let self, let parentController = self.parentController as? PeerInfoScreen else {
|
||||
f(.default)
|
||||
return
|
||||
}
|
||||
|
||||
self.contextControllerToDismissOnSelection = c
|
||||
parentController.toggleStorySelection(ids: [item.id], isSelected: true)
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.5, execute: { [weak self] in
|
||||
guard let self, let contextControllerToDismissOnSelection = self.contextControllerToDismissOnSelection else {
|
||||
return
|
||||
}
|
||||
if let contextControllerToDismissOnSelection = contextControllerToDismissOnSelection as? ContextController {
|
||||
contextControllerToDismissOnSelection.dismissWithCustomTransition(transition: .animated(duration: 0.4, curve: .spring), completion: nil)
|
||||
}
|
||||
})
|
||||
})))
|
||||
}
|
||||
|
||||
if items.isEmpty {
|
||||
return
|
||||
}
|
||||
|
||||
self.contextControllerToDismissOnSelection = c
|
||||
parentController.toggleStorySelection(ids: [item.id], isSelected: true)
|
||||
let tempSourceNode = TempExtractedItemNode(
|
||||
item: item,
|
||||
itemLayer: itemLayer
|
||||
)
|
||||
tempSourceNode.frame = rect
|
||||
tempSourceNode.update(size: rect.size)
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.5, execute: { [weak self] in
|
||||
guard let self, let contextControllerToDismissOnSelection = self.contextControllerToDismissOnSelection else {
|
||||
return
|
||||
}
|
||||
if let contextControllerToDismissOnSelection = contextControllerToDismissOnSelection as? ContextController {
|
||||
contextControllerToDismissOnSelection.dismissWithCustomTransition(transition: .animated(duration: 0.4, curve: .spring), completion: nil)
|
||||
}
|
||||
})
|
||||
})))
|
||||
|
||||
let tempSourceNode = TempExtractedItemNode(
|
||||
item: item,
|
||||
itemLayer: itemLayer
|
||||
)
|
||||
tempSourceNode.frame = rect
|
||||
tempSourceNode.update(size: rect.size)
|
||||
|
||||
let scaleSide = itemLayer.bounds.width
|
||||
let minScale: CGFloat = max(0.7, (scaleSide - 15.0) / scaleSide)
|
||||
let currentScale = minScale
|
||||
|
||||
ContainedViewLayoutTransition.immediate.updateSublayerTransformScale(node: tempSourceNode.contextSourceNode.contentNode, scale: currentScale)
|
||||
ContainedViewLayoutTransition.immediate.updateTransformScale(layer: itemLayer, scale: 1.0)
|
||||
|
||||
self.tempContextContentItemNode = tempSourceNode
|
||||
self.addSubnode(tempSourceNode)
|
||||
|
||||
let contextController = ContextController(presentationData: self.presentationData, source: .extracted(ExtractedContentSourceImpl(controller: parentController, sourceNode: tempSourceNode.contextSourceNode, keepInPlace: false, blurBackground: true)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
parentController.presentInGlobalOverlay(contextController)
|
||||
let scaleSide = itemLayer.bounds.width
|
||||
let minScale: CGFloat = max(0.7, (scaleSide - 15.0) / scaleSide)
|
||||
let currentScale = minScale
|
||||
|
||||
ContainedViewLayoutTransition.immediate.updateSublayerTransformScale(node: tempSourceNode.contextSourceNode.contentNode, scale: currentScale)
|
||||
ContainedViewLayoutTransition.immediate.updateTransformScale(layer: itemLayer, scale: 1.0)
|
||||
|
||||
self.tempContextContentItemNode = tempSourceNode
|
||||
self.addSubnode(tempSourceNode)
|
||||
|
||||
let contextController = ContextController(presentationData: self.presentationData, source: .extracted(ExtractedContentSourceImpl(controller: parentController, sourceNode: tempSourceNode.contextSourceNode, keepInPlace: false, blurBackground: true)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
parentController.presentInGlobalOverlay(contextController)
|
||||
})
|
||||
}
|
||||
|
||||
public func updateContentType(contentType: ContentType) {
|
||||
@ -2400,7 +2438,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
|
||||
self.currentParams = (size, topInset, sideInset, bottomInset, deviceMetrics, visibleHeight, isScrollingLockedAtTop, expandProgress, navigationHeight, presentationData)
|
||||
|
||||
var bottomInset = bottomInset
|
||||
if let selectedIds = self.itemInteraction.selectedIds {
|
||||
if self.isProfileEmbedded, let selectedIds = self.itemInteraction.selectedIds, self.canManageStories {
|
||||
let selectionPanel: ComponentView<Empty>
|
||||
var selectionPanelTransition = Transition(transition)
|
||||
if let current = self.selectionPanel {
|
||||
@ -2484,6 +2522,22 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
|
||||
}
|
||||
|
||||
let _ = self.context.engine.messages.updateStoriesArePinned(peerId: self.peerId, ids: items, isPinned: self.isArchive ? true : false).startStandalone()
|
||||
|
||||
let text: String
|
||||
if self.isArchive {
|
||||
if items.count == 1 {
|
||||
text = "Story unarchived."
|
||||
} else {
|
||||
text = "Stories unarchived."
|
||||
}
|
||||
} else {
|
||||
if items.count == 1 {
|
||||
text = "Story archived."
|
||||
} else {
|
||||
text = "Stories archived."
|
||||
}
|
||||
}
|
||||
self.parentController?.present(UndoOverlayController(presentationData: presentationData, content: .actionSucceeded(title: nil, text: text, cancel: nil, destructive: false), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current)
|
||||
}
|
||||
))
|
||||
selectionItems.append(BottomActionsPanelComponent.Item(
|
||||
|
Loading…
x
Reference in New Issue
Block a user