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 {
|
switch self {
|
||||||
case let .topPeers(peers, theme, strings):
|
case let .topPeers(peers, theme, strings):
|
||||||
return ChatListRecentPeersListItem(theme: theme, strings: strings, context: context, peers: peers, peerSelected: { peer in
|
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 .channels = key {
|
||||||
if case .recommendedChannels = section {
|
if case .recommendedChannels = section {
|
||||||
//TODO:localize
|
//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 {
|
} 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 {
|
} else {
|
||||||
header = ChatListSearchItemHeader(type: .recentPeers, theme: theme, strings: strings, actionTitle: strings.WebSearch_RecentSectionClear, action: {
|
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 {
|
private func chatListSearchContainerPreparedRecentTransition(
|
||||||
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
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 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 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), 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)
|
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>
|
let hasRecentPeers: Signal<Bool, NoError>
|
||||||
if case .channels = key {
|
if case .channels = key {
|
||||||
hasRecentPeers = .single(false)
|
hasRecentPeers = .single(false)
|
||||||
@ -2761,7 +2797,20 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
|||||||
|> distinctUntilChanged
|
|> 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,
|
hasRecentPeers,
|
||||||
fixedRecentlySearchedPeers |> mapToSignal { peers -> Signal<([RecentlySearchedPeer], [EnginePeer.Id: PeerStoryStats], [EnginePeer.Id: Bool], Set<EnginePeer.Id>), NoError> in
|
fixedRecentlySearchedPeers |> mapToSignal { peers -> Signal<([RecentlySearchedPeer], [EnginePeer.Id: PeerStoryStats], [EnginePeer.Id: Bool], Set<EnginePeer.Id>), NoError> in
|
||||||
return context.engine.data.subscribe(
|
return context.engine.data.subscribe(
|
||||||
@ -2804,7 +2853,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
|||||||
presentationDataPromise.get(),
|
presentationDataPromise.get(),
|
||||||
context.engine.data.subscribe(TelegramEngine.EngineData.Item.NotificationSettings.Global())
|
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
|
let (peers, peerStoryStats, requiresPremiumForMessaging, refreshIsPremiumRequiredForMessaging) = peersAndStories
|
||||||
|
|
||||||
if !refreshIsPremiumRequiredForMessaging.isEmpty {
|
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) {
|
if peersFilter.contains(.excludeRecent) {
|
||||||
recentItems = .single([])
|
recentItems = .single(RecentItems(entries: [], isChannelsTabExpanded: false, recommendedChannelOrder: []))
|
||||||
}
|
}
|
||||||
if case .savedMessagesChats = location {
|
if case .savedMessagesChats = location {
|
||||||
recentItems = .single([])
|
recentItems = .single(RecentItems(entries: [], isChannelsTabExpanded: false, recommendedChannelOrder: []))
|
||||||
}
|
}
|
||||||
if case .channels = key {
|
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 remoteChannels: Signal<RecommendedChannels?, NoError> = context.engine.peers.recommendedChannels(peerId: nil)
|
||||||
|
|
||||||
let _ = self.context.engine.peers.requestGlobalRecommendedChannelsIfNeeded().startStandalone()
|
let _ = self.context.engine.peers.requestGlobalRecommendedChannelsIfNeeded().startStandalone()
|
||||||
@ -2853,16 +2913,19 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
|||||||
localChannels,
|
localChannels,
|
||||||
remoteChannels
|
remoteChannels
|
||||||
)
|
)
|
||||||
|> mapToSignal { localChannelIds, remoteChannels -> Signal<[ChatListRecentEntry], NoError> in
|
|> mapToSignal { localChannels, remoteChannels -> Signal<RecentItems, NoError> in
|
||||||
var allChannelIds = localChannelIds
|
var allChannelIds = localChannels.peerIds
|
||||||
|
let isChannelsTabExpanded = localChannels.isExpanded
|
||||||
|
|
||||||
var cachedSubscribers: [EnginePeer.Id: Int32] = [:]
|
var cachedSubscribers: [EnginePeer.Id: Int32] = [:]
|
||||||
|
var recommendedChannelOrder: [EnginePeer.Id] = []
|
||||||
if let remoteChannels {
|
if let remoteChannels {
|
||||||
for channel in remoteChannels.channels {
|
for channel in remoteChannels.channels {
|
||||||
if !allChannelIds.contains(channel.peer.id) {
|
if !allChannelIds.contains(channel.peer.id) {
|
||||||
allChannelIds.append(channel.peer.id)
|
allChannelIds.append(channel.peer.id)
|
||||||
}
|
}
|
||||||
cachedSubscribers[channel.peer.id] = channel.subscribers
|
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)
|
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(
|
EngineDataMap(
|
||||||
allChannelIds.map { peerId -> TelegramEngine.EngineData.Item.Peer.ParticipantCount in
|
allChannelIds.map { peerId -> TelegramEngine.EngineData.Item.Peer.ParticipantCount in
|
||||||
return TelegramEngine.EngineData.Item.Peer.ParticipantCount(id: peerId)
|
return TelegramEngine.EngineData.Item.Peer.ParticipantCount(id: peerId)
|
||||||
@ -2889,11 +2957,11 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
|||||||
),
|
),
|
||||||
TelegramEngine.EngineData.Item.NotificationSettings.Global()
|
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 result: [ChatListRecentEntry] = []
|
||||||
var existingIds = Set<PeerId>()
|
var existingIds = Set<PeerId>()
|
||||||
|
|
||||||
for id in localChannelIds {
|
for id in localChannels.peerIds {
|
||||||
if existingIds.contains(id) {
|
if existingIds.contains(id) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -2908,6 +2976,10 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
|||||||
} else if let count = cachedSubscribers[id] {
|
} else if let count = cachedSubscribers[id] {
|
||||||
subpeerSummary = RecentlySearchedPeerSubpeerSummary(count: Int(count))
|
subpeerSummary = RecentlySearchedPeerSubpeerSummary(count: Int(count))
|
||||||
}
|
}
|
||||||
|
var peerStoryStats: PeerStoryStats?
|
||||||
|
if let value = storyStats[peer.id] {
|
||||||
|
peerStoryStats = value
|
||||||
|
}
|
||||||
result.append(.peer(
|
result.append(.peer(
|
||||||
index: result.count,
|
index: result.count,
|
||||||
peer: RecentlySearchedPeer(
|
peer: RecentlySearchedPeer(
|
||||||
@ -2924,7 +2996,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
|||||||
presentationData.nameSortOrder,
|
presentationData.nameSortOrder,
|
||||||
presentationData.nameDisplayOrder,
|
presentationData.nameDisplayOrder,
|
||||||
globalNotificationSettings,
|
globalNotificationSettings,
|
||||||
nil,
|
peerStoryStats,
|
||||||
false
|
false
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -2944,6 +3016,10 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
|||||||
} else if let count = cachedSubscribers[channel.peer.id] {
|
} else if let count = cachedSubscribers[channel.peer.id] {
|
||||||
subpeerSummary = RecentlySearchedPeerSubpeerSummary(count: Int(count))
|
subpeerSummary = RecentlySearchedPeerSubpeerSummary(count: Int(count))
|
||||||
}
|
}
|
||||||
|
var peerStoryStats: PeerStoryStats?
|
||||||
|
if let value = storyStats[peer.id] {
|
||||||
|
peerStoryStats = value
|
||||||
|
}
|
||||||
result.append(.peer(
|
result.append(.peer(
|
||||||
index: result.count,
|
index: result.count,
|
||||||
peer: RecentlySearchedPeer(
|
peer: RecentlySearchedPeer(
|
||||||
@ -2960,13 +3036,13 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
|||||||
presentationData.nameSortOrder,
|
presentationData.nameSortOrder,
|
||||||
presentationData.nameDisplayOrder,
|
presentationData.nameDisplayOrder,
|
||||||
globalNotificationSettings,
|
globalNotificationSettings,
|
||||||
nil,
|
peerStoryStats,
|
||||||
false
|
false
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return RecentItems(entries: result, isChannelsTabExpanded: isChannelsTabExpanded, recommendedChannelOrder: recommendedChannelOrder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2979,18 +3055,26 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
|||||||
presentationDataPromise.get(),
|
presentationDataPromise.get(),
|
||||||
recentItems
|
recentItems
|
||||||
)
|
)
|
||||||
|> deliverOnMainQueue).startStrict(next: { [weak self] presentationData, entries in
|
|> deliverOnMainQueue).startStrict(next: { [weak self] presentationData, recentItems in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
let previousEntries = previousRecentItems.swap(entries)
|
let previousRecentItems = previousRecentItemsValue.swap(recentItems)
|
||||||
|
|
||||||
var firstTime = previousEntries == nil
|
var firstTime = previousRecentItems == nil
|
||||||
if let previousEntries {
|
var forceUpdateAll = false
|
||||||
if previousEntries.count < entries.count {
|
if let previousRecentItems {
|
||||||
|
if previousRecentItems.entries.count < recentItems.entries.count {
|
||||||
firstTime = true
|
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 {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -2998,7 +3082,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
|||||||
if case .channels = key {
|
if case .channels = key {
|
||||||
if let navigationController = self.navigationController {
|
if let navigationController = self.navigationController {
|
||||||
var customChatNavigationStack: [EnginePeer.Id] = []
|
var customChatNavigationStack: [EnginePeer.Id] = []
|
||||||
if let entries = previousRecentItems.with({ $0 }) {
|
if let entries = previousRecentItemsValue.with({ $0 })?.entries {
|
||||||
for entry in entries {
|
for entry in entries {
|
||||||
if case let .peer(_, peer, _, _, _, _, _, _, _, _, _) = entry {
|
if case let .peer(_, peer, _, _, _, _, _, _, _, _, _) = entry {
|
||||||
customChatNavigationStack.append(peer.peer.peerId)
|
customChatNavigationStack.append(peer.peer.peerId)
|
||||||
@ -3040,6 +3124,10 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
|||||||
let _ = context.engine.peers.removeRecentlySearchedPeer(peerId: peerId).startStandalone()
|
let _ = context.engine.peers.removeRecentlySearchedPeer(peerId: peerId).startStandalone()
|
||||||
}, animationCache: strongSelf.animationCache, animationRenderer: strongSelf.animationRenderer, openStories: { peerId, avatarNode in
|
}, animationCache: strongSelf.animationCache, animationRenderer: strongSelf.animationRenderer, openStories: { peerId, avatarNode in
|
||||||
interaction.openStories?(peerId, avatarNode)
|
interaction.openStories?(peerId, avatarNode)
|
||||||
|
},
|
||||||
|
isChannelsTabExpanded: recentItems.isChannelsTabExpanded,
|
||||||
|
toggleChannelsTabExpanded: {
|
||||||
|
toggleChannelsTabExpanded()
|
||||||
})
|
})
|
||||||
strongSelf.enqueueRecentTransition(transition, firstTime: firstTime)
|
strongSelf.enqueueRecentTransition(transition, firstTime: firstTime)
|
||||||
}
|
}
|
||||||
|
@ -1438,7 +1438,9 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present
|
|||||||
entries.append(.experimentalCompatibility(experimentalSettings.experimentalCompatibility))
|
entries.append(.experimentalCompatibility(experimentalSettings.experimentalCompatibility))
|
||||||
entries.append(.enableDebugDataDisplay(experimentalSettings.enableDebugDataDisplay))
|
entries.append(.enableDebugDataDisplay(experimentalSettings.enableDebugDataDisplay))
|
||||||
entries.append(.acceleratedStickers(experimentalSettings.acceleratedStickers))
|
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))
|
entries.append(.localTranscription(experimentalSettings.localTranscription))
|
||||||
if case .internal = sharedContext.applicationBindings.appBuildType {
|
if case .internal = sharedContext.applicationBindings.appBuildType {
|
||||||
entries.append(.enableReactionOverrides(experimentalSettings.enableReactionOverrides))
|
entries.append(.enableReactionOverrides(experimentalSettings.enableReactionOverrides))
|
||||||
|
@ -1411,7 +1411,7 @@ public extension TelegramEngine {
|
|||||||
return _internal_reportAdMessage(account: self.account, peerId: peerId, opaqueId: opaqueId, option: option)
|
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
|
return self.account.postbox.transaction { transaction -> [EnginePeer.Id] in
|
||||||
var result: [EnginePeer.Id] = []
|
var result: [EnginePeer.Id] = []
|
||||||
|
|
||||||
@ -1444,7 +1444,7 @@ public extension TelegramEngine {
|
|||||||
}
|
}
|
||||||
filteredResult.append(id)
|
filteredResult.append(id)
|
||||||
|
|
||||||
if filteredResult.count >= 5 {
|
if filteredResult.count >= count {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -419,7 +419,18 @@ private final class PeerInfoPendingPane {
|
|||||||
let paneNode: PeerInfoPaneNode
|
let paneNode: PeerInfoPaneNode
|
||||||
switch key {
|
switch key {
|
||||||
case .stories, .storyArchive:
|
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
|
paneNode = visualPaneNode
|
||||||
visualPaneNode.openCurrentDate = {
|
visualPaneNode.openCurrentDate = {
|
||||||
openMediaCalendar()
|
openMediaCalendar()
|
||||||
|
@ -462,6 +462,7 @@ final class PeerInfoStoryGridScreenComponent: Component {
|
|||||||
isSaved: true,
|
isSaved: true,
|
||||||
isArchive: component.scope == .archive,
|
isArchive: component.scope == .archive,
|
||||||
isProfileEmbedded: false,
|
isProfileEmbedded: false,
|
||||||
|
canManageStories: true,
|
||||||
navigationController: { [weak self] in
|
navigationController: { [weak self] in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return nil
|
return nil
|
||||||
|
@ -1185,6 +1185,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
|
|||||||
private let isSaved: Bool
|
private let isSaved: Bool
|
||||||
private let isArchive: Bool
|
private let isArchive: Bool
|
||||||
private let isProfileEmbedded: Bool
|
private let isProfileEmbedded: Bool
|
||||||
|
private let canManageStories: Bool
|
||||||
public private(set) var contentType: ContentType
|
public private(set) var contentType: ContentType
|
||||||
private var contentTypePromise: ValuePromise<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 contextControllerToDismissOnSelection: ContextControllerProtocol?
|
||||||
private weak var tempContextContentItemNode: TempExtractedItemNode?
|
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.context = context
|
||||||
self.peerId = peerId
|
self.peerId = peerId
|
||||||
self.chatLocation = chatLocation
|
self.chatLocation = chatLocation
|
||||||
@ -1308,6 +1309,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
|
|||||||
self.isSaved = isSaved
|
self.isSaved = isSaved
|
||||||
self.isArchive = isArchive
|
self.isArchive = isArchive
|
||||||
self.isProfileEmbedded = isProfileEmbedded
|
self.isProfileEmbedded = isProfileEmbedded
|
||||||
|
self.canManageStories = canManageStories
|
||||||
|
|
||||||
self.isSelectionModeActive = !isProfileEmbedded && isArchive
|
self.isSelectionModeActive = !isProfileEmbedded && isArchive
|
||||||
|
|
||||||
@ -1657,12 +1659,25 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
|
|||||||
guard let item = strongSelf.itemGrid.item(at: point) else {
|
guard let item = strongSelf.itemGrid.item(at: point) else {
|
||||||
return false
|
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 let result = strongSelf.view.hitTest(point, with: nil) {
|
||||||
if result.asyncdisplaykit_node is SparseItemGridScrollingArea {
|
if result.asyncdisplaykit_node is SparseItemGridScrollingArea {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !strongSelf.canManageStories {
|
||||||
|
if !storyItem.story.isForwardingDisabled, case .everyone = storyItem.story.privacy?.base {
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
strongSelf.currentGestureItem = item
|
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?) {
|
private func openContextMenu(item: EngineStoryItem, itemLayer: ItemLayer, rect: CGRect, gesture: ContextGesture?) {
|
||||||
guard let parentController = self.parentController else {
|
let _ = (self.context.engine.data.get(
|
||||||
return
|
TelegramEngine.EngineData.Item.Peer.Peer(id: self.peerId)
|
||||||
}
|
)
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] _ in
|
||||||
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
|
|
||||||
guard let self else {
|
guard let self else {
|
||||||
f(.default)
|
return
|
||||||
|
}
|
||||||
|
guard let parentController = self.parentController else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.isArchive {
|
let canManage = self.canManageStories
|
||||||
f(.default)
|
|
||||||
} else {
|
|
||||||
f(.dismissWithoutContent)
|
|
||||||
}
|
|
||||||
|
|
||||||
let _ = self.context.engine.messages.updateStoriesArePinned(peerId: self.peerId, ids: [item.id: item], isPinned: self.isArchive ? true : false).startStandalone()
|
var items: [ContextMenuItem] = []
|
||||||
})))
|
|
||||||
|
//TODO:localize
|
||||||
if !self.isArchive {
|
|
||||||
let isPinned = self.pinnedIds.contains(item.id)
|
if canManage {
|
||||||
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
|
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
|
||||||
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: {
|
|
||||||
guard let self else {
|
guard let self else {
|
||||||
|
f(.default)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = (self.context.engine.data.get(
|
if self.isArchive {
|
||||||
TelegramEngine.EngineData.Item.Peer.Peer(id: self.peerId)
|
f(.default)
|
||||||
)
|
} else {
|
||||||
|> deliverOnMainQueue).startStandalone(next: { [weak self] peer in
|
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 {
|
guard let self else {
|
||||||
return
|
f(.default)
|
||||||
}
|
|
||||||
guard let peer, let peerReference = PeerReference(peer._asPeer()) else {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let shareController = ShareController(
|
if !isPinned && self.pinnedIds.count >= 3 {
|
||||||
context: self.context,
|
f(.default)
|
||||||
subject: .media(.story(peer: peerReference, id: item.id, media: TelegramMediaStory(storyId: StoryId(peerId: self.peerId, id: item.id), isMention: false))),
|
|
||||||
presetText: nil,
|
let presentationData = self.presentationData
|
||||||
preferredAction: .default,
|
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)
|
||||||
showInChat: nil,
|
|
||||||
fromForeignApp: false,
|
return
|
||||||
segmentedValues: nil,
|
}
|
||||||
externalShare: false,
|
|
||||||
immediateExternalShare: false,
|
f(.dismissWithoutContent)
|
||||||
switchableAccounts: [],
|
|
||||||
immediatePeerId: nil,
|
var updatedPinnedIds = self.pinnedIds
|
||||||
updatedPresentationData: nil,
|
if isPinned {
|
||||||
forceTheme: nil,
|
updatedPinnedIds.remove(item.id)
|
||||||
forcedActionTitle: nil,
|
} else {
|
||||||
shareAsLink: false,
|
updatedPinnedIds.insert(item.id)
|
||||||
collectibleItemInfo: nil
|
}
|
||||||
)
|
let _ = self.context.engine.messages.updatePinnedToTopStories(peerId: self.peerId, ids: Array(updatedPinnedIds)).startStandalone()
|
||||||
self.parentController?.present(shareController, in: .window(.root))
|
|
||||||
})
|
//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: 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]))
|
/*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
|
||||||
items.append(.separator)
|
}
|
||||||
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Conversation_ContextMenuSelect, icon: { theme in
|
let _ = self
|
||||||
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)
|
})))*/
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
self.contextControllerToDismissOnSelection = c
|
let tempSourceNode = TempExtractedItemNode(
|
||||||
parentController.toggleStorySelection(ids: [item.id], isSelected: true)
|
item: item,
|
||||||
|
itemLayer: itemLayer
|
||||||
|
)
|
||||||
|
tempSourceNode.frame = rect
|
||||||
|
tempSourceNode.update(size: rect.size)
|
||||||
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.5, execute: { [weak self] in
|
let scaleSide = itemLayer.bounds.width
|
||||||
guard let self, let contextControllerToDismissOnSelection = self.contextControllerToDismissOnSelection else {
|
let minScale: CGFloat = max(0.7, (scaleSide - 15.0) / scaleSide)
|
||||||
return
|
let currentScale = minScale
|
||||||
}
|
|
||||||
if let contextControllerToDismissOnSelection = contextControllerToDismissOnSelection as? ContextController {
|
ContainedViewLayoutTransition.immediate.updateSublayerTransformScale(node: tempSourceNode.contextSourceNode.contentNode, scale: currentScale)
|
||||||
contextControllerToDismissOnSelection.dismissWithCustomTransition(transition: .animated(duration: 0.4, curve: .spring), completion: nil)
|
ContainedViewLayoutTransition.immediate.updateTransformScale(layer: itemLayer, scale: 1.0)
|
||||||
}
|
|
||||||
})
|
self.tempContextContentItemNode = tempSourceNode
|
||||||
})))
|
self.addSubnode(tempSourceNode)
|
||||||
|
|
||||||
let tempSourceNode = TempExtractedItemNode(
|
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)
|
||||||
item: item,
|
parentController.presentInGlobalOverlay(contextController)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func updateContentType(contentType: ContentType) {
|
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)
|
self.currentParams = (size, topInset, sideInset, bottomInset, deviceMetrics, visibleHeight, isScrollingLockedAtTop, expandProgress, navigationHeight, presentationData)
|
||||||
|
|
||||||
var bottomInset = bottomInset
|
var bottomInset = bottomInset
|
||||||
if let selectedIds = self.itemInteraction.selectedIds {
|
if self.isProfileEmbedded, let selectedIds = self.itemInteraction.selectedIds, self.canManageStories {
|
||||||
let selectionPanel: ComponentView<Empty>
|
let selectionPanel: ComponentView<Empty>
|
||||||
var selectionPanelTransition = Transition(transition)
|
var selectionPanelTransition = Transition(transition)
|
||||||
if let current = self.selectionPanel {
|
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 _ = 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(
|
selectionItems.append(BottomActionsPanelComponent.Item(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user