[WIP] Topics

This commit is contained in:
Ali 2022-10-18 01:08:49 +04:00
parent 14772a6c67
commit 02b28ee6fc
37 changed files with 727 additions and 306 deletions

View File

@ -41,21 +41,23 @@ public final class PeerSelectionControllerParams {
public let context: AccountContext public let context: AccountContext
public let updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? public let updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?
public let filter: ChatListNodePeersFilter public let filter: ChatListNodePeersFilter
public let forumPeerId: EnginePeer.Id?
public let hasChatListSelector: Bool public let hasChatListSelector: Bool
public let hasContactSelector: Bool public let hasContactSelector: Bool
public let hasGlobalSearch: Bool public let hasGlobalSearch: Bool
public let title: String? public let title: String?
public let attemptSelection: ((Peer) -> Void)? public let attemptSelection: ((Peer, Int64?) -> Void)?
public let createNewGroup: (() -> Void)? public let createNewGroup: (() -> Void)?
public let pretendPresentedInModal: Bool public let pretendPresentedInModal: Bool
public let multipleSelection: Bool public let multipleSelection: Bool
public let forwardedMessageIds: [EngineMessage.Id] public let forwardedMessageIds: [EngineMessage.Id]
public let hasTypeHeaders: Bool public let hasTypeHeaders: Bool
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, filter: ChatListNodePeersFilter = [.onlyWriteable], hasChatListSelector: Bool = true, hasContactSelector: Bool = true, hasGlobalSearch: Bool = true, title: String? = nil, attemptSelection: ((Peer) -> Void)? = nil, createNewGroup: (() -> Void)? = nil, pretendPresentedInModal: Bool = false, multipleSelection: Bool = false, forwardedMessageIds: [EngineMessage.Id] = [], hasTypeHeaders: Bool = false) { public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, filter: ChatListNodePeersFilter = [.onlyWriteable], forumPeerId: EnginePeer.Id? = nil, hasChatListSelector: Bool = true, hasContactSelector: Bool = true, hasGlobalSearch: Bool = true, title: String? = nil, attemptSelection: ((Peer, Int64?) -> Void)? = nil, createNewGroup: (() -> Void)? = nil, pretendPresentedInModal: Bool = false, multipleSelection: Bool = false, forwardedMessageIds: [EngineMessage.Id] = [], hasTypeHeaders: Bool = false) {
self.context = context self.context = context
self.updatedPresentationData = updatedPresentationData self.updatedPresentationData = updatedPresentationData
self.filter = filter self.filter = filter
self.forumPeerId = forumPeerId
self.hasChatListSelector = hasChatListSelector self.hasChatListSelector = hasChatListSelector
self.hasContactSelector = hasContactSelector self.hasContactSelector = hasContactSelector
self.hasGlobalSearch = hasGlobalSearch self.hasGlobalSearch = hasGlobalSearch
@ -76,7 +78,7 @@ public enum AttachmentTextInputPanelSendMode {
} }
public protocol PeerSelectionController: ViewController { public protocol PeerSelectionController: ViewController {
var peerSelected: ((Peer) -> Void)? { get set } var peerSelected: ((Peer, Int64?) -> Void)? { get set }
var multiplePeersSelected: (([Peer], [PeerId: Peer], NSAttributedString, AttachmentTextInputPanelSendMode, ChatInterfaceForwardOptionsState?) -> Void)? { get set } var multiplePeersSelected: (([Peer], [PeerId: Peer], NSAttributedString, AttachmentTextInputPanelSendMode, ChatInterfaceForwardOptionsState?) -> Void)? { get set }
var inProgress: Bool { get set } var inProgress: Bool { get set }
var customDismiss: (() -> Void)? { get set } var customDismiss: (() -> Void)? { get set }

View File

@ -182,7 +182,7 @@ private final class ChatListShimmerNode: ASDisplayNode {
let peer1: EnginePeer = .user(TelegramUser(id: EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: EnginePeer.Id.Id._internalFromInt64Value(0)), accessHash: nil, firstName: "FirstName", lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [])) let peer1: EnginePeer = .user(TelegramUser(id: EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: EnginePeer.Id.Id._internalFromInt64Value(0)), accessHash: nil, firstName: "FirstName", lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: []))
let timestamp1: Int32 = 100000 let timestamp1: Int32 = 100000
let peers: [EnginePeer.Id: EnginePeer] = [:] let peers: [EnginePeer.Id: EnginePeer] = [:]
let interaction = ChatListNodeInteraction(context: context, animationCache: animationCache, animationRenderer: animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in let interaction = ChatListNodeInteraction(context: context, animationCache: animationCache, animationRenderer: animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _, _ in }, togglePeerSelected: { _, _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
}, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in }, activateChatPreview: { _, _, gesture, _ in }, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in }, activateChatPreview: { _, _, gesture, _ in
gesture?.cancel() gesture?.cancel()
}, present: { _ in }) }, present: { _ in })
@ -1313,7 +1313,7 @@ final class ChatListControllerNode: ASDisplayNode {
let contentNode = ChatListSearchContainerNode(context: self.context, animationCache: self.animationCache, animationRenderer: self.animationRenderer, filter: filter, location: location, displaySearchFilters: displaySearchFilters, hasDownloads: hasDownloads, initialFilter: initialFilter, openPeer: { [weak self] peer, _, threadId, dismissSearch in let contentNode = ChatListSearchContainerNode(context: self.context, animationCache: self.animationCache, animationRenderer: self.animationRenderer, filter: filter, location: location, displaySearchFilters: displaySearchFilters, hasDownloads: hasDownloads, initialFilter: initialFilter, openPeer: { [weak self] peer, _, threadId, dismissSearch in
self?.requestOpenPeerFromSearch?(peer, threadId, dismissSearch) self?.requestOpenPeerFromSearch?(peer, threadId, dismissSearch)
}, openDisabledPeer: { _ in }, openDisabledPeer: { _, _ in
}, openRecentPeerOptions: { [weak self] peer in }, openRecentPeerOptions: { [weak self] peer in
self?.requestOpenRecentPeerOptions?(peer) self?.requestOpenRecentPeerOptions?(peer)
}, openMessage: { [weak self] peer, threadId, messageId, deactivateOnAction in }, openMessage: { [weak self] peer, threadId, messageId, deactivateOnAction in

View File

@ -44,7 +44,7 @@ private enum ChatListTokenId: Int32 {
final class ChatListSearchInteraction { final class ChatListSearchInteraction {
let openPeer: (EnginePeer, EnginePeer?, Int64?, Bool) -> Void let openPeer: (EnginePeer, EnginePeer?, Int64?, Bool) -> Void
let openDisabledPeer: (EnginePeer) -> Void let openDisabledPeer: (EnginePeer, Int64?) -> Void
let openMessage: (EnginePeer, Int64?, EngineMessage.Id, Bool) -> Void let openMessage: (EnginePeer, Int64?, EngineMessage.Id, Bool) -> Void
let openUrl: (String) -> Void let openUrl: (String) -> Void
let clearRecentSearch: () -> Void let clearRecentSearch: () -> Void
@ -57,7 +57,7 @@ final class ChatListSearchInteraction {
let dismissInput: () -> Void let dismissInput: () -> Void
let getSelectedMessageIds: () -> Set<EngineMessage.Id>? let getSelectedMessageIds: () -> Set<EngineMessage.Id>?
init(openPeer: @escaping (EnginePeer, EnginePeer?, Int64?, Bool) -> Void, openDisabledPeer: @escaping (EnginePeer) -> Void, openMessage: @escaping (EnginePeer, Int64?, EngineMessage.Id, Bool) -> Void, openUrl: @escaping (String) -> Void, clearRecentSearch: @escaping () -> Void, addContact: @escaping (String) -> Void, toggleMessageSelection: @escaping (EngineMessage.Id, Bool) -> Void, messageContextAction: @escaping ((EngineMessage, ASDisplayNode?, CGRect?, UIGestureRecognizer?, ChatListSearchPaneKey, (id: String, size: Int64, isFirstInList: Bool)?) -> Void), mediaMessageContextAction: @escaping ((EngineMessage, ASDisplayNode?, CGRect?, UIGestureRecognizer?) -> Void), peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, getSelectedMessageIds: @escaping () -> Set<EngineMessage.Id>?) { init(openPeer: @escaping (EnginePeer, EnginePeer?, Int64?, Bool) -> Void, openDisabledPeer: @escaping (EnginePeer, Int64?) -> Void, openMessage: @escaping (EnginePeer, Int64?, EngineMessage.Id, Bool) -> Void, openUrl: @escaping (String) -> Void, clearRecentSearch: @escaping () -> Void, addContact: @escaping (String) -> Void, toggleMessageSelection: @escaping (EngineMessage.Id, Bool) -> Void, messageContextAction: @escaping ((EngineMessage, ASDisplayNode?, CGRect?, UIGestureRecognizer?, ChatListSearchPaneKey, (id: String, size: Int64, isFirstInList: Bool)?) -> Void), mediaMessageContextAction: @escaping ((EngineMessage, ASDisplayNode?, CGRect?, UIGestureRecognizer?) -> Void), peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, getSelectedMessageIds: @escaping () -> Set<EngineMessage.Id>?) {
self.openPeer = openPeer self.openPeer = openPeer
self.openDisabledPeer = openDisabledPeer self.openDisabledPeer = openDisabledPeer
self.openMessage = openMessage self.openMessage = openMessage
@ -133,7 +133,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
private var validLayout: (ContainerViewLayout, CGFloat)? private var validLayout: (ContainerViewLayout, CGFloat)?
public init(context: AccountContext, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, filter: ChatListNodePeersFilter, location: ChatListControllerLocation, displaySearchFilters: Bool, hasDownloads: Bool, initialFilter: ChatListSearchFilter = .chats, openPeer originalOpenPeer: @escaping (EnginePeer, EnginePeer?, Int64?, Bool) -> Void, openDisabledPeer: @escaping (EnginePeer) -> Void, openRecentPeerOptions: @escaping (EnginePeer) -> Void, openMessage originalOpenMessage: @escaping (EnginePeer, Int64?, EngineMessage.Id, Bool) -> Void, addContact: ((String) -> Void)?, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, navigationController: NavigationController?) { public init(context: AccountContext, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, filter: ChatListNodePeersFilter, location: ChatListControllerLocation, displaySearchFilters: Bool, hasDownloads: Bool, initialFilter: ChatListSearchFilter = .chats, openPeer originalOpenPeer: @escaping (EnginePeer, EnginePeer?, Int64?, Bool) -> Void, openDisabledPeer: @escaping (EnginePeer, Int64?) -> Void, openRecentPeerOptions: @escaping (EnginePeer) -> Void, openMessage originalOpenMessage: @escaping (EnginePeer, Int64?, EngineMessage.Id, Bool) -> Void, addContact: ((String) -> Void)?, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, navigationController: NavigationController?) {
self.context = context self.context = context
self.peersFilter = filter self.peersFilter = filter
self.location = location self.location = location
@ -164,8 +164,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
if peer.id.namespace != Namespaces.Peer.SecretChat { if peer.id.namespace != Namespaces.Peer.SecretChat {
addAppLogEvent(postbox: context.account.postbox, type: "search_global_open_peer", peerId: peer.id) addAppLogEvent(postbox: context.account.postbox, type: "search_global_open_peer", peerId: peer.id)
} }
}, openDisabledPeer: { peer in }, openDisabledPeer: { peer, threadId in
openDisabledPeer(peer) openDisabledPeer(peer, threadId)
}, openMessage: { peer, threadId, messageId, deactivateOnAction in }, openMessage: { peer, threadId, messageId, deactivateOnAction in
originalOpenMessage(peer, threadId, messageId, deactivateOnAction) originalOpenMessage(peer, threadId, messageId, deactivateOnAction)
if peer.id.namespace != Namespaces.Peer.SecretChat { if peer.id.namespace != Namespaces.Peer.SecretChat {
@ -1320,7 +1320,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
(strongSelf.navigationController?.topViewController as? ViewController)?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) (strongSelf.navigationController?.topViewController as? ViewController)?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
} }
peerSelectionController.peerSelected = { [weak self, weak peerSelectionController] peer in peerSelectionController.peerSelected = { [weak self, weak peerSelectionController] peer, _ in
let peerId = peer.id let peerId = peer.id
if let strongSelf = self, let _ = peerSelectionController { if let strongSelf = self, let _ = peerSelectionController {
if peerId == strongSelf.context.account.peerId { if peerId == strongSelf.context.account.peerId {

View File

@ -89,11 +89,11 @@ private enum ChatListRecentEntry: Comparable, Identifiable {
} }
} }
func item(context: AccountContext, presentationData: ChatListPresentationData, filter: ChatListNodePeersFilter, peerSelected: @escaping (EnginePeer) -> Void, disabledPeerSelected: @escaping (EnginePeer) -> Void, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, clearRecentlySearchedPeers: @escaping () -> Void, deletePeer: @escaping (EnginePeer.Id) -> Void, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer) -> ListViewItem { func item(context: AccountContext, presentationData: ChatListPresentationData, filter: ChatListNodePeersFilter, peerSelected: @escaping (EnginePeer, Int64?) -> Void, disabledPeerSelected: @escaping (EnginePeer, Int64?) -> Void, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, clearRecentlySearchedPeers: @escaping () -> Void, deletePeer: @escaping (EnginePeer.Id) -> Void, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer) -> 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
peerSelected(peer) peerSelected(peer, nil)
}, peerContextAction: { peer, node, gesture, location in }, peerContextAction: { peer, node, gesture, location in
if let peerContextAction = peerContextAction { if let peerContextAction = peerContextAction {
peerContextAction(peer, .recentPeers, node, gesture, location) peerContextAction(peer, .recentPeers, node, gesture, location)
@ -213,12 +213,12 @@ private enum ChatListRecentEntry: Comparable, Identifiable {
}), }),
action: { _ in action: { _ in
if let chatPeer = peer.peer.peers[peer.peer.peerId] { if let chatPeer = peer.peer.peers[peer.peer.peerId] {
peerSelected(EnginePeer(chatPeer)) peerSelected(EnginePeer(chatPeer), nil)
} }
}, },
disabledAction: { _ in disabledAction: { _ in
if let chatPeer = peer.peer.peers[peer.peer.peerId] { if let chatPeer = peer.peer.peers[peer.peer.peerId] {
disabledPeerSelected(EnginePeer(chatPeer)) disabledPeerSelected(EnginePeer(chatPeer), nil)
} }
}, },
deletePeer: deletePeer, deletePeer: deletePeer,
@ -477,7 +477,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
toggleExpandGlobalResults() toggleExpandGlobalResults()
}) })
return ContactsPeerItem(presentationData: ItemListPresentationData(presentationData), sortOrder: .firstLast, displayOrder: .firstLast, context: context, peerMode: .generalSearch, peer: .thread(peer: peer, title: threadInfo.info.title, icon: threadInfo.info.icon, color: 0), status: .none, badge: nil, enabled: true, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: header, action: { _ in return ContactsPeerItem(presentationData: ItemListPresentationData(presentationData), sortOrder: .firstLast, displayOrder: .firstLast, context: context, peerMode: .generalSearch, peer: .thread(peer: peer, title: threadInfo.info.title, icon: threadInfo.info.icon, color: threadInfo.info.iconColor), status: .none, badge: nil, enabled: true, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: header, action: { _ in
interaction.peerSelected(peer, nil, threadInfo.id, nil) interaction.peerSelected(peer, nil, threadInfo.id, nil)
}, contextAction: nil, animationCache: interaction.animationCache, animationRenderer: interaction.animationRenderer) }, contextAction: nil, animationCache: interaction.animationCache, animationRenderer: interaction.animationRenderer)
case let .recentlySearchedPeer(peer, associatedPeer, unreadBadge, _, theme, strings, nameSortOrder, nameDisplayOrder): case let .recentlySearchedPeer(peer, associatedPeer, unreadBadge, _, theme, strings, nameSortOrder, nameDisplayOrder):
@ -783,7 +783,7 @@ public struct ChatListSearchContainerTransition {
} }
} }
private func chatListSearchContainerPreparedRecentTransition(from fromEntries: [ChatListRecentEntry], to toEntries: [ChatListRecentEntry], context: AccountContext, presentationData: ChatListPresentationData, filter: ChatListNodePeersFilter, peerSelected: @escaping (EnginePeer) -> Void, disabledPeerSelected: @escaping (EnginePeer) -> Void, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, clearRecentlySearchedPeers: @escaping () -> Void, deletePeer: @escaping (EnginePeer.Id) -> Void, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer) -> ChatListSearchContainerRecentTransition { private func chatListSearchContainerPreparedRecentTransition(from fromEntries: [ChatListRecentEntry], to toEntries: [ChatListRecentEntry], context: AccountContext, presentationData: ChatListPresentationData, filter: ChatListNodePeersFilter, peerSelected: @escaping (EnginePeer, Int64?) -> Void, disabledPeerSelected: @escaping (EnginePeer, Int64?) -> Void, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, clearRecentlySearchedPeers: @escaping () -> Void, deletePeer: @escaping (EnginePeer.Id) -> Void, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer) -> ChatListSearchContainerRecentTransition {
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) } let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
@ -1819,8 +1819,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
interaction.openPeer(peer, chatPeer, threadId, false) interaction.openPeer(peer, chatPeer, threadId, false)
let _ = context.engine.peers.addRecentlySearchedPeer(peerId: peer.id).start() let _ = context.engine.peers.addRecentlySearchedPeer(peerId: peer.id).start()
self?.listNode.clearHighlightAnimated(true) self?.listNode.clearHighlightAnimated(true)
}, disabledPeerSelected: { _ in }, disabledPeerSelected: { _, _ in
}, togglePeerSelected: { _ in }, togglePeerSelected: { _, _ in
}, togglePeersSelection: { _, _ in }, togglePeersSelection: { _, _ in
}, additionalCategorySelected: { _ in }, additionalCategorySelected: { _ in
}, messageSelected: { [weak self] peer, threadId, message, _ in }, messageSelected: { [weak self] peer, threadId, message, _ in
@ -2162,12 +2162,14 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
let previousEntries = previousRecentItems.swap(entries) let previousEntries = previousRecentItems.swap(entries)
let firstTime = previousEntries == nil let firstTime = previousEntries == nil
let transition = chatListSearchContainerPreparedRecentTransition(from: previousEntries ?? [], to: entries, context: context, presentationData: presentationData, filter: peersFilter, peerSelected: { peer in let transition = chatListSearchContainerPreparedRecentTransition(from: previousEntries ?? [], to: entries, context: context, presentationData: presentationData, filter: peersFilter, peerSelected: { peer, threadId in
interaction.openPeer(peer, nil, nil, true) interaction.openPeer(peer, nil, threadId, true)
let _ = context.engine.peers.addRecentlySearchedPeer(peerId: peer.id).start() if threadId == nil {
let _ = context.engine.peers.addRecentlySearchedPeer(peerId: peer.id).start()
}
self?.recentListNode.clearHighlightAnimated(true) self?.recentListNode.clearHighlightAnimated(true)
}, disabledPeerSelected: { peer in }, disabledPeerSelected: { peer, threadId in
interaction.openDisabledPeer(peer) interaction.openDisabledPeer(peer, threadId)
}, peerContextAction: { peer, source, node, gesture, location in }, peerContextAction: { peer, source, node, gesture, location in
if let peerContextAction = interaction.peerContextAction { if let peerContextAction = interaction.peerContextAction {
peerContextAction(peer, source, node, gesture, location) peerContextAction(peer, source, node, gesture, location)
@ -3050,7 +3052,7 @@ private final class ChatListSearchShimmerNode: ASDisplayNode {
let timestamp1: Int32 = 100000 let timestamp1: Int32 = 100000
var peers: [EnginePeer.Id: EnginePeer] = [:] var peers: [EnginePeer.Id: EnginePeer] = [:]
peers[peer1.id] = peer1 peers[peer1.id] = peer1
let interaction = ChatListNodeInteraction(context: context, animationCache: animationCache, animationRenderer: animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in let interaction = ChatListNodeInteraction(context: context, animationCache: animationCache, animationRenderer: animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _, _ in }, togglePeerSelected: { _, _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
}, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in }, activateChatPreview: { _, _, gesture, _ in }, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in }, activateChatPreview: { _, _, gesture, _ in
gesture?.cancel() gesture?.cancel()
}, present: { _ in }) }, present: { _ in })

View File

@ -125,7 +125,7 @@ private final class ChatListSearchPendingPane {
key: ChatListSearchPaneKey, key: ChatListSearchPaneKey,
hasBecomeReady: @escaping (ChatListSearchPaneKey) -> Void hasBecomeReady: @escaping (ChatListSearchPaneKey) -> Void
) { ) {
let paneNode = ChatListSearchListPaneNode(context: context, animationCache: animationCache, animationRenderer: animationRenderer, updatedPresentationData: updatedPresentationData, interaction: interaction, key: key, peersFilter: key == .chats ? peersFilter : [], location: location, searchQuery: searchQuery, searchOptions: searchOptions, navigationController: navigationController) let paneNode = ChatListSearchListPaneNode(context: context, animationCache: animationCache, animationRenderer: animationRenderer, updatedPresentationData: updatedPresentationData, interaction: interaction, key: key, peersFilter: (key == .chats || key == .topics) ? peersFilter : [], location: location, searchQuery: searchQuery, searchOptions: searchOptions, navigationController: navigationController)
self.pane = ChatListSearchPaneWrapper(key: key, node: paneNode) self.pane = ChatListSearchPaneWrapper(key: key, node: paneNode)
self.disposable = (paneNode.isReady self.disposable = (paneNode.isReady

View File

@ -1029,7 +1029,14 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
} }
if case let .peer(_, peer, _, _, _, _, _, _, _, _, promoInfo, _, _, _, _) = item.content { if case let .peer(_, peer, _, _, _, _, _, _, _, _, promoInfo, _, _, _, _) = item.content {
if promoInfo == nil, let mainPeer = peer.peer { if promoInfo == nil, let mainPeer = peer.peer {
item.interaction.togglePeerSelected(mainPeer) var threadId: Int64?
switch item.index {
case let .forum(_, _, threadIdValue, _, _):
threadId = threadIdValue
case .chatList:
break
}
item.interaction.togglePeerSelected(mainPeer, threadId)
} }
} }
} }
@ -2016,7 +2023,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
let avatarIconContent: EmojiStatusComponent.Content let avatarIconContent: EmojiStatusComponent.Content
if let fileId = threadInfo.info.icon, fileId != 0 { if let fileId = threadInfo.info.icon, fileId != 0 {
avatarIconContent = .animation(content: .customEmoji(fileId: fileId), size: CGSize(width: 48.0, height: 48.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: nil, loopMode: .forever) avatarIconContent = .animation(content: .customEmoji(fileId: fileId), size: CGSize(width: 48.0, height: 48.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .forever)
} else { } else {
avatarIconContent = .topic(title: String(threadInfo.info.title.prefix(1)), colorIndex: Int(clamping: abs(threadInfo.id)), size: CGSize(width: 32.0, height: 32.0)) avatarIconContent = .topic(title: String(threadInfo.info.title.prefix(1)), colorIndex: Int(clamping: abs(threadInfo.id)), size: CGSize(width: 32.0, height: 32.0))
} }

View File

@ -57,8 +57,8 @@ public final class ChatListNodeInteraction {
let activateSearch: () -> Void let activateSearch: () -> Void
let peerSelected: (EnginePeer, EnginePeer?, Int64?, ChatListNodeEntryPromoInfo?) -> Void let peerSelected: (EnginePeer, EnginePeer?, Int64?, ChatListNodeEntryPromoInfo?) -> Void
let disabledPeerSelected: (EnginePeer) -> Void let disabledPeerSelected: (EnginePeer, Int64?) -> Void
let togglePeerSelected: (EnginePeer) -> Void let togglePeerSelected: (EnginePeer, Int64?) -> Void
let togglePeersSelection: ([PeerEntry], Bool) -> Void let togglePeersSelection: ([PeerEntry], Bool) -> Void
let additionalCategorySelected: (Int) -> Void let additionalCategorySelected: (Int) -> Void
let messageSelected: (EnginePeer, Int64?, EngineMessage, ChatListNodeEntryPromoInfo?) -> Void let messageSelected: (EnginePeer, Int64?, EngineMessage, ChatListNodeEntryPromoInfo?) -> Void
@ -91,8 +91,8 @@ public final class ChatListNodeInteraction {
animationRenderer: MultiAnimationRenderer, animationRenderer: MultiAnimationRenderer,
activateSearch: @escaping () -> Void, activateSearch: @escaping () -> Void,
peerSelected: @escaping (EnginePeer, EnginePeer?, Int64?, ChatListNodeEntryPromoInfo?) -> Void, peerSelected: @escaping (EnginePeer, EnginePeer?, Int64?, ChatListNodeEntryPromoInfo?) -> Void,
disabledPeerSelected: @escaping (EnginePeer) -> Void, disabledPeerSelected: @escaping (EnginePeer, Int64?) -> Void,
togglePeerSelected: @escaping (EnginePeer) -> Void, togglePeerSelected: @escaping (EnginePeer, Int64?) -> Void,
togglePeersSelection: @escaping ([PeerEntry], Bool) -> Void, togglePeersSelection: @escaping ([PeerEntry], Bool) -> Void,
additionalCategorySelected: @escaping (Int) -> Void, additionalCategorySelected: @escaping (Int) -> Void,
messageSelected: @escaping (EnginePeer, Int64?, EngineMessage, ChatListNodeEntryPromoInfo?) -> Void, messageSelected: @escaping (EnginePeer, Int64?, EngineMessage, ChatListNodeEntryPromoInfo?) -> Void,
@ -381,6 +381,21 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
status = .none status = .none
} }
} }
let peerContent: ContactsPeerItemPeer
if let threadInfo = threadInfo, let itemPeer = itemPeer {
peerContent = .thread(peer: itemPeer, title: threadInfo.info.title, icon: threadInfo.info.icon, color: threadInfo.info.iconColor)
} else {
peerContent = .peer(peer: itemPeer, chatPeer: chatPeer)
}
var threadId: Int64?
switch index {
case let .forum(_, _, threadIdValue, _, _):
threadId = threadIdValue
case .chatList:
break
}
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ContactsPeerItem( return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ContactsPeerItem(
presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings), presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings),
@ -388,7 +403,7 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
displayOrder: presentationData.nameDisplayOrder, displayOrder: presentationData.nameDisplayOrder,
context: context, context: context,
peerMode: .generalSearch, peerMode: .generalSearch,
peer: .peer(peer: itemPeer, chatPeer: chatPeer), peer: peerContent,
status: status, status: status,
enabled: enabled, enabled: enabled,
selection: editing ? .selectable(selected: selected) : .none, selection: editing ? .selectable(selected: selected) : .none,
@ -398,14 +413,14 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
action: { _ in action: { _ in
if let chatPeer = chatPeer { if let chatPeer = chatPeer {
if editing { if editing {
nodeInteraction.togglePeerSelected(chatPeer) nodeInteraction.togglePeerSelected(chatPeer, threadId)
} else { } else {
nodeInteraction.peerSelected(chatPeer, nil, nil, nil) nodeInteraction.peerSelected(chatPeer, nil, threadId, nil)
} }
} }
}, disabledAction: { _ in }, disabledAction: { _ in
if let chatPeer = chatPeer { if let chatPeer = chatPeer {
nodeInteraction.disabledPeerSelected(chatPeer) nodeInteraction.disabledPeerSelected(chatPeer, threadId)
} }
}, },
animationCache: nodeInteraction.animationCache, animationCache: nodeInteraction.animationCache,
@ -525,6 +540,21 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
status = .none status = .none
} }
} }
let peerContent: ContactsPeerItemPeer
if let threadInfo = threadInfo, let itemPeer = itemPeer {
peerContent = .thread(peer: itemPeer, title: threadInfo.info.title, icon: threadInfo.info.icon, color: threadInfo.info.iconColor)
} else {
peerContent = .peer(peer: itemPeer, chatPeer: chatPeer)
}
var threadId: Int64?
switch index {
case let .forum(_, _, threadIdValue, _, _):
threadId = threadIdValue
case .chatList:
break
}
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ContactsPeerItem( return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ContactsPeerItem(
presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings), presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings),
@ -532,7 +562,7 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
displayOrder: presentationData.nameDisplayOrder, displayOrder: presentationData.nameDisplayOrder,
context: context, context: context,
peerMode: .generalSearch, peerMode: .generalSearch,
peer: .peer(peer: itemPeer, chatPeer: chatPeer), peer: peerContent,
status: status, status: status,
enabled: enabled, enabled: enabled,
selection: editing ? .selectable(selected: selected) : .none, selection: editing ? .selectable(selected: selected) : .none,
@ -542,14 +572,14 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
action: { _ in action: { _ in
if let chatPeer = chatPeer { if let chatPeer = chatPeer {
if editing { if editing {
nodeInteraction.togglePeerSelected(chatPeer) nodeInteraction.togglePeerSelected(chatPeer, threadId)
} else { } else {
nodeInteraction.peerSelected(chatPeer, nil, nil, nil) nodeInteraction.peerSelected(chatPeer, nil, threadId, nil)
} }
} }
}, disabledAction: { _ in }, disabledAction: { _ in
if let chatPeer = chatPeer { if let chatPeer = chatPeer {
nodeInteraction.disabledPeerSelected(chatPeer) nodeInteraction.disabledPeerSelected(chatPeer, threadId)
} }
}, },
animationCache: nodeInteraction.animationCache, animationCache: nodeInteraction.animationCache,
@ -661,7 +691,7 @@ public final class ChatListNode: ListView {
} }
public var peerSelected: ((EnginePeer, Int64?, Bool, Bool, ChatListNodeEntryPromoInfo?) -> Void)? public var peerSelected: ((EnginePeer, Int64?, Bool, Bool, ChatListNodeEntryPromoInfo?) -> Void)?
public var disabledPeerSelected: ((EnginePeer) -> Void)? public var disabledPeerSelected: ((EnginePeer, Int64?) -> Void)?
public var additionalCategorySelected: ((Int) -> Void)? public var additionalCategorySelected: ((Int) -> Void)?
public var groupSelected: ((EngineChatList.Group) -> Void)? public var groupSelected: ((EngineChatList.Group) -> Void)?
public var addContact: ((String) -> Void)? public var addContact: ((String) -> Void)?
@ -799,11 +829,11 @@ public final class ChatListNode: ListView {
if let strongSelf = self, let peerSelected = strongSelf.peerSelected { if let strongSelf = self, let peerSelected = strongSelf.peerSelected {
peerSelected(peer, threadId, true, true, promoInfo) peerSelected(peer, threadId, true, true, promoInfo)
} }
}, disabledPeerSelected: { [weak self] peer in }, disabledPeerSelected: { [weak self] peer, threadId in
if let strongSelf = self, let disabledPeerSelected = strongSelf.disabledPeerSelected { if let strongSelf = self, let disabledPeerSelected = strongSelf.disabledPeerSelected {
disabledPeerSelected(peer) disabledPeerSelected(peer, threadId)
} }
}, togglePeerSelected: { [weak self] peer in }, togglePeerSelected: { [weak self] peer, _ in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }

View File

@ -1051,7 +1051,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
let avatarIconContent: EmojiStatusComponent.Content let avatarIconContent: EmojiStatusComponent.Content
if let fileId = icon, fileId != 0 { if let fileId = icon, fileId != 0 {
avatarIconContent = .animation(content: .customEmoji(fileId: fileId), size: CGSize(width: 48.0, height: 48.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: nil, loopMode: .forever) avatarIconContent = .animation(content: .customEmoji(fileId: fileId), size: CGSize(width: 48.0, height: 48.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .forever)
} else { } else {
avatarIconContent = .topic(title: String(title.prefix(1)), colorIndex: Int(clamping: abs(color)), size: CGSize(width: 32.0, height: 32.0)) avatarIconContent = .topic(title: String(title.prefix(1)), colorIndex: Int(clamping: abs(color)), size: CGSize(width: 32.0, height: 32.0))
} }

View File

@ -344,7 +344,8 @@ private final class InnerActionsContainerNode: ASDisplayNode {
final class InnerTextSelectionTipContainerNode: ASDisplayNode { final class InnerTextSelectionTipContainerNode: ASDisplayNode {
private let presentationData: PresentationData private let presentationData: PresentationData
private let shadowNode: ASImageNode let shadowNode: ASImageNode
private let backgroundNode: ASDisplayNode
private var effectView: UIVisualEffectView? private var effectView: UIVisualEffectView?
private let highlightBackgroundNode: ASDisplayNode private let highlightBackgroundNode: ASDisplayNode
private let buttonNode: HighlightTrackingButtonNode private let buttonNode: HighlightTrackingButtonNode
@ -376,6 +377,8 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
self.shadowNode.contentMode = .scaleToFill self.shadowNode.contentMode = .scaleToFill
self.shadowNode.isHidden = true self.shadowNode.isHidden = true
self.backgroundNode = ASDisplayNode()
self.highlightBackgroundNode = ASDisplayNode() self.highlightBackgroundNode = ASDisplayNode()
self.highlightBackgroundNode.isAccessibilityElement = false self.highlightBackgroundNode.isAccessibilityElement = false
self.highlightBackgroundNode.alpha = 0.0 self.highlightBackgroundNode.alpha = 0.0
@ -436,10 +439,12 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
super.init() super.init()
self.clipsToBounds = true self.backgroundNode.backgroundColor = presentationData.theme.contextMenu.backgroundColor
self.cornerRadius = 14.0 self.backgroundNode.clipsToBounds = true
self.backgroundNode.cornerRadius = 14.0
self.backgroundColor = presentationData.theme.contextMenu.backgroundColor self.highlightBackgroundNode.clipsToBounds = true
self.highlightBackgroundNode.cornerRadius = 14.0
let textSelectionNode = TextSelectionNode(theme: TextSelectionTheme(selection: presentationData.theme.contextMenu.primaryColor.withAlphaComponent(0.15), knob: presentationData.theme.contextMenu.primaryColor, knobDiameter: 8.0), strings: presentationData.strings, textNode: self.textNode.textNode, updateIsActive: { _ in let textSelectionNode = TextSelectionNode(theme: TextSelectionTheme(selection: presentationData.theme.contextMenu.primaryColor.withAlphaComponent(0.15), knob: presentationData.theme.contextMenu.primaryColor, knobDiameter: 8.0), strings: presentationData.strings, textNode: self.textNode.textNode, updateIsActive: { _ in
}, present: { _, _ in }, present: { _, _ in
@ -447,6 +452,7 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
}) })
self.textSelectionNode = textSelectionNode self.textSelectionNode = textSelectionNode
self.addSubnode(self.backgroundNode)
self.addSubnode(self.highlightBackgroundNode) self.addSubnode(self.highlightBackgroundNode)
self.addSubnode(self.textNode.textNode) self.addSubnode(self.textNode.textNode)
self.addSubnode(self.iconNode) self.addSubnode(self.iconNode)
@ -514,20 +520,19 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
} }
func updateLayout(widthClass: ContainerViewLayoutSizeClass, presentation: ContextControllerActionsStackNode.Presentation, width: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize { func updateLayout(widthClass: ContainerViewLayoutSizeClass, presentation: ContextControllerActionsStackNode.Presentation, width: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
switch presentation { var needsBlur = false
case .inline: if case .regular = widthClass {
self.shadowNode.isHidden = true needsBlur = true
case .modal: } else if case .inline = presentation {
self.shadowNode.isHidden = false needsBlur = true
} }
switch widthClass { if !needsBlur {
case .compact:
if let effectView = self.effectView { if let effectView = self.effectView {
self.effectView = nil self.effectView = nil
effectView.removeFromSuperview() effectView.removeFromSuperview()
} }
case .regular: } else {
if self.effectView == nil { if self.effectView == nil {
let effectView: UIVisualEffectView let effectView: UIVisualEffectView
if #available(iOS 13.0, *) { if #available(iOS 13.0, *) {
@ -541,6 +546,8 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
} else { } else {
effectView = UIVisualEffectView(effect: UIBlurEffect(style: .light)) effectView = UIVisualEffectView(effect: UIBlurEffect(style: .light))
} }
effectView.clipsToBounds = true
effectView.layer.cornerRadius = self.backgroundNode.cornerRadius
self.effectView = effectView self.effectView = effectView
self.view.insertSubview(effectView, at: 0) self.view.insertSubview(effectView, at: 0)
} }
@ -618,6 +625,13 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
textSelectionNode.highlightAreaNode.frame = textFrame textSelectionNode.highlightAreaNode.frame = textFrame
} }
switch presentation {
case .modal:
self.shadowNode.isHidden = true
case .inline:
self.shadowNode.isHidden = false
}
if let effectView = self.effectView { if let effectView = self.effectView {
transition.updateFrame(view: effectView, frame: CGRect(origin: CGPoint(), size: size)) transition.updateFrame(view: effectView, frame: CGRect(origin: CGPoint(), size: size))
} }
@ -628,11 +642,9 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
} }
func setActualSize(size: CGSize, transition: ContainedViewLayoutTransition) { func setActualSize(size: CGSize, transition: ContainedViewLayoutTransition) {
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: size))
self.highlightBackgroundNode.frame = CGRect(origin: CGPoint(), size: size) self.highlightBackgroundNode.frame = CGRect(origin: CGPoint(), size: size)
self.buttonNode.frame = CGRect(origin: CGPoint(), size: size) self.buttonNode.frame = CGRect(origin: CGPoint(), size: size)
let bounds = CGRect(origin: CGPoint(), size: size)
transition.updateFrame(node: self.shadowNode, frame: bounds.insetBy(dx: -30.0, dy: -30.0))
} }
func updateTheme(presentationData: PresentationData) { func updateTheme(presentationData: PresentationData) {

View File

@ -1321,22 +1321,32 @@ final class ContextControllerActionsStackNode: ASDisplayNode {
let tipTransition = transition let tipTransition = transition
var animateTipIn = false var animateTipIn = false
if tip.tipNode.supernode == nil { if tip.tipNode.supernode == nil {
self.insertSubnode(tip.tipNode.shadowNode, at: 0)
self.addSubnode(tip.tipNode) self.addSubnode(tip.tipNode)
animateTipIn = transition.isAnimated animateTipIn = transition.isAnimated
tip.tipNode.frame = CGRect(origin: CGPoint(x: previousNavigationContainerFrame.minX, y: previousNavigationContainerFrame.maxY + tipSpacing), size: CGSize(width: itemLayouts[i].size.width, height: tip.tipHeight)) let tipFrame = CGRect(origin: CGPoint(x: previousNavigationContainerFrame.minX, y: previousNavigationContainerFrame.maxY + tipSpacing), size: CGSize(width: itemLayouts[i].size.width, height: tip.tipHeight))
tip.tipNode.setActualSize(size: tip.tipNode.bounds.size, transition: .immediate) tip.tipNode.frame = tipFrame
tip.tipNode.setActualSize(size: tipFrame.size, transition: .immediate)
transition.updateFrame(node: tip.tipNode.shadowNode, frame: tipFrame.insetBy(dx: -30.0, dy: -30.0))
} }
let tipAlpha: CGFloat = itemLayouts[i].alphaTransitionFraction let tipAlpha: CGFloat = itemLayouts[i].alphaTransitionFraction
tipTransition.updateFrame(node: tip.tipNode, frame: CGRect(origin: CGPoint(x: navigationContainerFrame.minX, y: navigationContainerFrame.maxY + tipSpacing), size: CGSize(width: itemLayouts[i].size.width, height: tip.tipHeight)), beginWithCurrentState: true) let tipFrame = CGRect(origin: CGPoint(x: navigationContainerFrame.minX, y: navigationContainerFrame.maxY + tipSpacing), size: CGSize(width: itemLayouts[i].size.width, height: tip.tipHeight))
tipTransition.updateFrame(node: tip.tipNode, frame: tipFrame, beginWithCurrentState: true)
transition.updateFrame(node: tip.tipNode.shadowNode, frame: tipFrame.insetBy(dx: -30.0, dy: -30.0))
tip.tipNode.setActualSize(size: tip.tipNode.bounds.size, transition: tipTransition) tip.tipNode.setActualSize(size: tip.tipNode.bounds.size, transition: tipTransition)
if animateTipIn { if animateTipIn {
tip.tipNode.alpha = tipAlpha tip.tipNode.alpha = tipAlpha
tip.tipNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) tip.tipNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
tip.tipNode.shadowNode.alpha = tipAlpha
tip.tipNode.shadowNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
} else { } else {
tipTransition.updateAlpha(node: tip.tipNode, alpha: tipAlpha, beginWithCurrentState: true) tipTransition.updateAlpha(node: tip.tipNode, alpha: tipAlpha, beginWithCurrentState: true)
tipTransition.updateAlpha(node: tip.tipNode.shadowNode, alpha: tipAlpha, beginWithCurrentState: true)
} }
if i == self.itemContainers.count - 1 { if i == self.itemContainers.count - 1 {
@ -1356,10 +1366,17 @@ final class ContextControllerActionsStackNode: ASDisplayNode {
itemContainer?.removeFromSupernode() itemContainer?.removeFromSupernode()
}) })
if let tipNode = itemContainer.tipNode { if let tipNode = itemContainer.tipNode {
transition.updateFrame(node: tipNode, frame: CGRect(origin: CGPoint(x: navigationContainerFrame.minX, y: navigationContainerFrame.maxY + tipSpacing), size: tipNode.frame.size), beginWithCurrentState: true) let tipFrame = CGRect(origin: CGPoint(x: navigationContainerFrame.minX, y: navigationContainerFrame.maxY + tipSpacing), size: tipNode.frame.size)
transition.updateFrame(node: tipNode, frame: tipFrame, beginWithCurrentState: true)
transition.updateFrame(node: tipNode.shadowNode, frame: tipFrame.insetBy(dx: -30.0, dy: -30.0))
transition.updateAlpha(node: tipNode, alpha: 0.0, completion: { [weak tipNode] _ in transition.updateAlpha(node: tipNode, alpha: 0.0, completion: { [weak tipNode] _ in
tipNode?.removeFromSupernode() tipNode?.removeFromSupernode()
}) })
let shadowNode = tipNode.shadowNode
transition.updateAlpha(node: shadowNode, alpha: 0.0, completion: { [weak shadowNode] _ in
shadowNode?.removeFromSupernode()
})
} }
} }
self.dismissingItemContainers.removeAll() self.dismissingItemContainers.removeAll()

View File

@ -251,7 +251,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
actionSheet?.dismissAnimated() actionSheet?.dismissAnimated()
let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyWriteable, .excludeDisabled])) let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyWriteable, .excludeDisabled]))
controller.peerSelected = { [weak controller] peer in controller.peerSelected = { [weak controller] peer, _ in
let peerId = peer.id let peerId = peer.id
if let strongController = controller { if let strongController = controller {
@ -335,7 +335,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
actionSheet?.dismissAnimated() actionSheet?.dismissAnimated()
let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyWriteable, .excludeDisabled])) let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyWriteable, .excludeDisabled]))
controller.peerSelected = { [weak controller] peer in controller.peerSelected = { [weak controller] peer, _ in
let peerId = peer.id let peerId = peer.id
if let strongController = controller { if let strongController = controller {
@ -417,7 +417,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
actionSheet?.dismissAnimated() actionSheet?.dismissAnimated()
let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyWriteable, .excludeDisabled])) let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyWriteable, .excludeDisabled]))
controller.peerSelected = { [weak controller] peer in controller.peerSelected = { [weak controller] peer, _ in
let peerId = peer.id let peerId = peer.id
if let strongController = controller { if let strongController = controller {
@ -501,7 +501,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
actionSheet?.dismissAnimated() actionSheet?.dismissAnimated()
let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyWriteable, .excludeDisabled])) let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyWriteable, .excludeDisabled]))
controller.peerSelected = { [weak controller] peer in controller.peerSelected = { [weak controller] peer, _ in
let peerId = peer.id let peerId = peer.id
if let strongController = controller { if let strongController = controller {
@ -586,7 +586,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
actionSheet?.dismissAnimated() actionSheet?.dismissAnimated()
let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyWriteable, .excludeDisabled])) let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyWriteable, .excludeDisabled]))
controller.peerSelected = { [weak controller] peer in controller.peerSelected = { [weak controller] peer, _ in
let peerId = peer.id let peerId = peer.id
if let strongController = controller { if let strongController = controller {
@ -670,7 +670,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
actionSheet?.dismissAnimated() actionSheet?.dismissAnimated()
let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyWriteable, .excludeDisabled])) let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyWriteable, .excludeDisabled]))
controller.peerSelected = { [weak controller] peer in controller.peerSelected = { [weak controller] peer, _ in
let peerId = peer.id let peerId = peer.id
if let strongController = controller { if let strongController = controller {
@ -743,7 +743,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
actionSheet?.dismissAnimated() actionSheet?.dismissAnimated()
let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyWriteable, .excludeDisabled])) let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyWriteable, .excludeDisabled]))
controller.peerSelected = { [weak controller] peer in controller.peerSelected = { [weak controller] peer, _ in
let peerId = peer.id let peerId = peer.id
if let strongController = controller { if let strongController = controller {
@ -1345,7 +1345,7 @@ public func triggerDebugSendLogsUI(context: AccountContext, additionalInfo: Stri
let _ = (Logger.shared.collectLogs() let _ = (Logger.shared.collectLogs()
|> deliverOnMainQueue).start(next: { logs in |> deliverOnMainQueue).start(next: { logs in
let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyWriteable, .excludeDisabled])) let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyWriteable, .excludeDisabled]))
controller.peerSelected = { [weak controller] peer in controller.peerSelected = { [weak controller] peer, _ in
let peerId = peer.id let peerId = peer.id
if let strongController = controller { if let strongController = controller {

View File

@ -58,8 +58,8 @@ public final class HashtagSearchController: TelegramBaseController {
} }
let interaction = ChatListNodeInteraction(context: context, animationCache: self.animationCache, animationRenderer: self.animationRenderer, activateSearch: { let interaction = ChatListNodeInteraction(context: context, animationCache: self.animationCache, animationRenderer: self.animationRenderer, activateSearch: {
}, peerSelected: { _, _, _, _ in }, peerSelected: { _, _, _, _ in
}, disabledPeerSelected: { _ in }, disabledPeerSelected: { _, _ in
}, togglePeerSelected: { _ in }, togglePeerSelected: { _, _ in
}, togglePeersSelection: { _, _ in }, togglePeersSelection: { _, _ in
}, additionalCategorySelected: { _ in }, additionalCategorySelected: { _ in
}, messageSelected: { [weak self] peer, _, message, _ in }, messageSelected: { [weak self] peer, _, message, _ in

View File

@ -14,6 +14,7 @@ swift_library(
"//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit",
"//submodules/Display:Display", "//submodules/Display:Display",
"//submodules/TelegramCore:TelegramCore", "//submodules/TelegramCore:TelegramCore",
"//submodules/Postbox:Postbox",
"//submodules/AvatarNode:AvatarNode", "//submodules/AvatarNode:AvatarNode",
"//submodules/TelegramPresentationData:TelegramPresentationData", "//submodules/TelegramPresentationData:TelegramPresentationData",
"//submodules/TelegramStringFormatting:TelegramStringFormatting", "//submodules/TelegramStringFormatting:TelegramStringFormatting",

View File

@ -4,6 +4,7 @@ import Display
import AsyncDisplayKit import AsyncDisplayKit
import SwiftSignalKit import SwiftSignalKit
import TelegramCore import TelegramCore
import Postbox
import TelegramPresentationData import TelegramPresentationData
import TelegramUIPreferences import TelegramUIPreferences
import ItemListUI import ItemListUI
@ -319,6 +320,7 @@ public final class ItemListPeerItem: ListViewItem, ItemListItem {
let nameDisplayOrder: PresentationPersonNameOrder let nameDisplayOrder: PresentationPersonNameOrder
let context: AccountContext let context: AccountContext
let peer: EnginePeer let peer: EnginePeer
let threadInfo: EngineMessageHistoryThread.Info?
let height: ItemListPeerItemHeight let height: ItemListPeerItemHeight
let aliasHandling: ItemListPeerItemAliasHandling let aliasHandling: ItemListPeerItemAliasHandling
let nameColor: ItemListPeerItemNameColor let nameColor: ItemListPeerItemNameColor
@ -348,12 +350,13 @@ public final class ItemListPeerItem: ListViewItem, ItemListItem {
let displayDecorations: Bool let displayDecorations: Bool
let disableInteractiveTransitionIfNecessary: Bool let disableInteractiveTransitionIfNecessary: Bool
public init(presentationData: ItemListPresentationData, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, context: AccountContext, peer: EnginePeer, height: ItemListPeerItemHeight = .peerList, aliasHandling: ItemListPeerItemAliasHandling = .standard, nameColor: ItemListPeerItemNameColor = .primary, nameStyle: ItemListPeerItemNameStyle = .distinctBold, presence: EnginePeer.Presence?, text: ItemListPeerItemText, label: ItemListPeerItemLabel, editing: ItemListPeerItemEditing, revealOptions: ItemListPeerItemRevealOptions? = nil, switchValue: ItemListPeerItemSwitch?, enabled: Bool, highlighted: Bool = false, selectable: Bool, sectionId: ItemListSectionId, action: (() -> Void)?, setPeerIdWithRevealedOptions: @escaping (EnginePeer.Id?, EnginePeer.Id?) -> Void, removePeer: @escaping (EnginePeer.Id) -> Void, toggleUpdated: ((Bool) -> Void)? = nil, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? = nil, hasTopStripe: Bool = true, hasTopGroupInset: Bool = true, noInsets: Bool = false, noCorners: Bool = false, tag: ItemListItemTag? = nil, header: ListViewItemHeader? = nil, shimmering: ItemListPeerItemShimmering? = nil, displayDecorations: Bool = true, disableInteractiveTransitionIfNecessary: Bool = false) { public init(presentationData: ItemListPresentationData, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, context: AccountContext, peer: EnginePeer, threadInfo: EngineMessageHistoryThread.Info? = nil, height: ItemListPeerItemHeight = .peerList, aliasHandling: ItemListPeerItemAliasHandling = .standard, nameColor: ItemListPeerItemNameColor = .primary, nameStyle: ItemListPeerItemNameStyle = .distinctBold, presence: EnginePeer.Presence?, text: ItemListPeerItemText, label: ItemListPeerItemLabel, editing: ItemListPeerItemEditing, revealOptions: ItemListPeerItemRevealOptions? = nil, switchValue: ItemListPeerItemSwitch?, enabled: Bool, highlighted: Bool = false, selectable: Bool, sectionId: ItemListSectionId, action: (() -> Void)?, setPeerIdWithRevealedOptions: @escaping (EnginePeer.Id?, EnginePeer.Id?) -> Void, removePeer: @escaping (EnginePeer.Id) -> Void, toggleUpdated: ((Bool) -> Void)? = nil, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? = nil, hasTopStripe: Bool = true, hasTopGroupInset: Bool = true, noInsets: Bool = false, noCorners: Bool = false, tag: ItemListItemTag? = nil, header: ListViewItemHeader? = nil, shimmering: ItemListPeerItemShimmering? = nil, displayDecorations: Bool = true, disableInteractiveTransitionIfNecessary: Bool = false) {
self.presentationData = presentationData self.presentationData = presentationData
self.dateTimeFormat = dateTimeFormat self.dateTimeFormat = dateTimeFormat
self.nameDisplayOrder = nameDisplayOrder self.nameDisplayOrder = nameDisplayOrder
self.context = context self.context = context
self.peer = peer self.peer = peer
self.threadInfo = threadInfo
self.height = height self.height = height
self.aliasHandling = aliasHandling self.aliasHandling = aliasHandling
self.nameColor = nameColor self.nameColor = nameColor
@ -457,6 +460,9 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
} }
fileprivate let avatarNode: AvatarNode fileprivate let avatarNode: AvatarNode
private var avatarIconComponent: EmojiStatusComponent?
private var avatarIconView: ComponentView<Empty>?
private let titleNode: TextNode private let titleNode: TextNode
private let labelNode: TextNode private let labelNode: TextNode
private let labelBadgeNode: ASImageNode private let labelBadgeNode: ASImageNode
@ -503,6 +509,14 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
containerSize: credibilityIconView.bounds.size containerSize: credibilityIconView.bounds.size
) )
} }
if let avatarIconView = self.avatarIconView, let avatarIconComponentView = avatarIconView.view, let avatarIconComponent = self.avatarIconComponent {
let _ = avatarIconView.update(
transition: .immediate,
component: AnyComponent(avatarIconComponent.withVisibleForAnimations(self.visibilityStatus)),
environment: {},
containerSize: avatarIconComponentView.bounds.size
)
}
} }
} }
} }
@ -761,7 +775,9 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
currentBoldFont = titleFont currentBoldFont = titleFont
} }
if item.peer.id == item.context.account.peerId, case .threatSelfAsSaved = item.aliasHandling { if let threadInfo = item.threadInfo {
titleAttributedString = NSAttributedString(string: threadInfo.title, font: currentBoldFont, textColor: titleColor)
} else if item.peer.id == item.context.account.peerId, case .threatSelfAsSaved = item.aliasHandling {
titleAttributedString = NSAttributedString(string: item.presentationData.strings.DialogList_SavedMessages, font: currentBoldFont, textColor: titleColor) titleAttributedString = NSAttributedString(string: item.presentationData.strings.DialogList_SavedMessages, font: currentBoldFont, textColor: titleColor)
} else if item.peer.id.isReplies { } else if item.peer.id.isReplies {
titleAttributedString = NSAttributedString(string: item.presentationData.strings.DialogList_Replies, font: currentBoldFont, textColor: titleColor) titleAttributedString = NSAttributedString(string: item.presentationData.strings.DialogList_Replies, font: currentBoldFont, textColor: titleColor)
@ -1219,16 +1235,62 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
let avatarFrame = CGRect(origin: CGPoint(x: params.leftInset + revealOffset + editingOffset + 15.0, y: floorToScreenPixels((layout.contentSize.height - avatarSize) / 2.0)), size: CGSize(width: avatarSize, height: avatarSize)) let avatarFrame = CGRect(origin: CGPoint(x: params.leftInset + revealOffset + editingOffset + 15.0, y: floorToScreenPixels((layout.contentSize.height - avatarSize) / 2.0)), size: CGSize(width: avatarSize, height: avatarSize))
transition.updateFrame(node: strongSelf.avatarNode, frame: avatarFrame) transition.updateFrame(node: strongSelf.avatarNode, frame: avatarFrame)
if item.peer.id == item.context.account.peerId, case .threatSelfAsSaved = item.aliasHandling { if let threadInfo = item.threadInfo {
strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: item.peer, overrideImage: .savedMessagesIcon, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoad) let threadIconSize = floor(avatarSize * 0.9)
} else if item.peer.id.isReplies { let threadIconFrame = CGRect(origin: CGPoint(x: avatarFrame.minX + floor((avatarFrame.width - threadIconSize) / 2.0), y: avatarFrame.minY + floor((avatarFrame.height - threadIconSize) / 2.0)), size: CGSize(width: threadIconSize, height: threadIconSize))
strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: item.peer, overrideImage: .repliesIcon, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoad)
} else { strongSelf.avatarNode.isHidden = true
var overrideImage: AvatarNodeImageOverride?
if item.peer.isDeleted { let avatarIconView: ComponentView<Empty>
overrideImage = .deletedIcon if let current = strongSelf.avatarIconView {
avatarIconView = current
} else {
avatarIconView = ComponentView<Empty>()
strongSelf.avatarIconView = avatarIconView
}
let avatarIconContent: EmojiStatusComponent.Content
if let fileId = threadInfo.icon, fileId != 0 {
avatarIconContent = .animation(content: .customEmoji(fileId: fileId), size: CGSize(width: 48.0, height: 48.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .forever)
} else {
avatarIconContent = .topic(title: String(threadInfo.title.prefix(1)), colorIndex: Int(clamping: abs(threadInfo.iconColor)), size: threadIconFrame.size)
}
let avatarIconComponent = EmojiStatusComponent(
context: item.context,
animationCache: item.context.animationCache,
animationRenderer: item.context.animationRenderer,
content: avatarIconContent,
isVisibleForAnimations: strongSelf.visibilityStatus,
action: nil,
emojiFileUpdated: nil
)
strongSelf.avatarIconComponent = avatarIconComponent
let _ = avatarIconView.update(
transition: .immediate,
component: AnyComponent(avatarIconComponent),
environment: {},
containerSize: threadIconFrame.size
)
if let avatarIconComponentView = avatarIconView.view {
if avatarIconComponentView.superview == nil {
strongSelf.containerNode.view.addSubview(avatarIconComponentView)
}
transition.updateFrame(view: avatarIconComponentView, frame: threadIconFrame)
}
} else {
if item.peer.id == item.context.account.peerId, case .threatSelfAsSaved = item.aliasHandling {
strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: item.peer, overrideImage: .savedMessagesIcon, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoad)
} else if item.peer.id.isReplies {
strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: item.peer, overrideImage: .repliesIcon, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoad)
} else {
var overrideImage: AvatarNodeImageOverride?
if item.peer.isDeleted {
overrideImage = .deletedIcon
}
strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: item.peer, overrideImage: overrideImage, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoad)
} }
strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: item.peer, overrideImage: overrideImage, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoad)
} }
strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: params.width, height: layout.contentSize.height + UIScreenPixel + UIScreenPixel)) strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: params.width, height: layout.contentSize.height + UIScreenPixel + UIScreenPixel))
@ -1410,6 +1472,14 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
transition.updateFrame(node: self.labelBadgeNode, frame: CGRect(origin: CGPoint(x: offset + params.width - rightLabelInset - badgeWidth, y: self.labelBadgeNode.frame.minY), size: CGSize(width: badgeWidth, height: badgeDiameter))) transition.updateFrame(node: self.labelBadgeNode, frame: CGRect(origin: CGPoint(x: offset + params.width - rightLabelInset - badgeWidth, y: self.labelBadgeNode.frame.minY), size: CGSize(width: badgeWidth, height: badgeDiameter)))
transition.updateFrame(node: self.avatarNode, frame: CGRect(origin: CGPoint(x: revealOffset + editingOffset + params.leftInset + 15.0, y: self.avatarNode.frame.minY), size: self.avatarNode.bounds.size)) transition.updateFrame(node: self.avatarNode, frame: CGRect(origin: CGPoint(x: revealOffset + editingOffset + params.leftInset + 15.0, y: self.avatarNode.frame.minY), size: self.avatarNode.bounds.size))
if let avatarIconComponentView = self.avatarIconView?.view {
let avatarFrame = self.avatarNode.frame
let threadIconSize = floor(avatarFrame.width * 0.9)
let threadIconFrame = CGRect(origin: CGPoint(x: avatarFrame.minX + floor((avatarFrame.width - threadIconSize) / 2.0), y: avatarFrame.minY + floor((avatarFrame.height - threadIconSize) / 2.0)), size: CGSize(width: threadIconSize, height: threadIconSize))
transition.updateFrame(view: avatarIconComponentView, frame: threadIconFrame)
}
} }
override public func revealOptionsInteractivelyOpened() { override public func revealOptionsInteractivelyOpened() {

View File

@ -803,8 +803,11 @@ final class NotificationExceptionsControllerNode: ViewControllerTracingNode {
let presentPeerSettings: (PeerId, @escaping () -> Void) -> Void = { [weak self] peerId, completion in let presentPeerSettings: (PeerId, @escaping () -> Void) -> Void = { [weak self] peerId, completion in
(self?.searchDisplayController?.contentNode as? NotificationExceptionsSearchContainerNode)?.listNode.clearHighlightAnimated(true) (self?.searchDisplayController?.contentNode as? NotificationExceptionsSearchContainerNode)?.listNode.clearHighlightAnimated(true)
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) let _ = (context.engine.data.get(
|> deliverOnMainQueue).start(next: { peer in TelegramEngine.EngineData.Item.Peer.Peer(id: peerId),
TelegramEngine.EngineData.Item.NotificationSettings.Global()
)
|> deliverOnMainQueue).start(next: { peer, globalSettings in
completion() completion()
guard let peer = peer else { guard let peer = peer else {
@ -814,7 +817,20 @@ final class NotificationExceptionsControllerNode: ViewControllerTracingNode {
let mode = stateValue.with { $0.mode } let mode = stateValue.with { $0.mode }
dismissInputImpl?() dismissInputImpl?()
presentControllerImpl?(notificationPeerExceptionController(context: context, peer: peer._asPeer(), threadId: nil, mode: mode, updatePeerSound: { peerId, sound in
let canRemove = mode.peerIds.contains(peerId)
let defaultSound: PeerMessageSound
switch mode {
case .channels:
defaultSound = globalSettings.channels.sound._asMessageSound()
case .groups:
defaultSound = globalSettings.groupChats.sound._asMessageSound()
case .users:
defaultSound = globalSettings.privateChats.sound._asMessageSound()
}
presentControllerImpl?(notificationPeerExceptionController(context: context, peer: peer._asPeer(), threadId: nil, canRemove: canRemove, defaultSound: defaultSound, updatePeerSound: { peerId, sound in
_ = updatePeerSound(peer.id, sound).start(next: { _ in _ = updatePeerSound(peer.id, sound).start(next: { _ in
updateNotificationsDisposable.set(nil) updateNotificationsDisposable.set(nil)
_ = combineLatest(updatePeerSound(peer.id, sound), context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) |> deliverOnMainQueue).start(next: { _, peer in _ = combineLatest(updatePeerSound(peer.id, sound), context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) |> deliverOnMainQueue).start(next: { _, peer in
@ -883,7 +899,7 @@ final class NotificationExceptionsControllerNode: ViewControllerTracingNode {
filter.insert(.onlyChannels) filter.insert(.onlyChannels)
} }
let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: filter, hasContactSelector: false, title: presentationData.strings.Notifications_AddExceptionTitle)) let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: filter, hasContactSelector: false, title: presentationData.strings.Notifications_AddExceptionTitle))
controller.peerSelected = { [weak controller] peer in controller.peerSelected = { [weak controller] peer, _ in
let peerId = peer.id let peerId = peer.id
presentPeerSettings(peerId, { presentPeerSettings(peerId, {

View File

@ -365,7 +365,7 @@ private struct NotificationExceptionPeerState : Equatable {
} }
} }
public func notificationPeerExceptionController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peer: Peer, threadId: Int64?, mode: NotificationExceptionMode, edit: Bool = false, updatePeerSound: @escaping(PeerId, PeerMessageSound) -> Void, updatePeerNotificationInterval: @escaping(PeerId, Int32?) -> Void, updatePeerDisplayPreviews: @escaping(PeerId, PeerNotificationDisplayPreviews) -> Void, removePeerFromExceptions: @escaping () -> Void, modifiedPeer: @escaping () -> Void) -> ViewController { public func notificationPeerExceptionController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peer: Peer, customTitle: String? = nil, threadId: Int64?, canRemove: Bool, defaultSound: PeerMessageSound, edit: Bool = false, updatePeerSound: @escaping(PeerId, PeerMessageSound) -> Void, updatePeerNotificationInterval: @escaping(PeerId, Int32?) -> Void, updatePeerDisplayPreviews: @escaping(PeerId, PeerNotificationDisplayPreviews) -> Void, removePeerFromExceptions: @escaping () -> Void, modifiedPeer: @escaping () -> Void) -> ViewController {
let initialState = NotificationExceptionPeerState(canRemove: false) let initialState = NotificationExceptionPeerState(canRemove: false)
let statePromise = Promise(initialState) let statePromise = Promise(initialState)
let stateValue = Atomic(value: initialState) let stateValue = Atomic(value: initialState)
@ -426,16 +426,8 @@ public func notificationPeerExceptionController(context: AccountContext, updated
|> map { peerNotificationSettings, threadNotificationSettings, globalNotificationSettings -> NotificationExceptionPeerState in |> map { peerNotificationSettings, threadNotificationSettings, globalNotificationSettings -> NotificationExceptionPeerState in
let effectiveSettings = threadNotificationSettings ?? peerNotificationSettings let effectiveSettings = threadNotificationSettings ?? peerNotificationSettings
var state = NotificationExceptionPeerState(canRemove: mode.peerIds.contains(peer.id), notifications: effectiveSettings._asNotificationSettings()) var state = NotificationExceptionPeerState(canRemove: canRemove, notifications: effectiveSettings._asNotificationSettings())
let globalSettings = globalNotificationSettings state.defaultSound = defaultSound
switch mode {
case .channels:
state.defaultSound = globalSettings.channels.sound._asMessageSound()
case .groups:
state.defaultSound = globalSettings.groupChats.sound._asMessageSound()
case .users:
state.defaultSound = globalSettings.privateChats.sound._asMessageSound()
}
let _ = stateValue.swap(state) let _ = stateValue.swap(state)
return state return state
}) })
@ -467,7 +459,14 @@ public func notificationPeerExceptionController(context: AccountContext, updated
animated = true animated = true
} }
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back)) let titleString: String
if let customTitle = customTitle {
titleString = customTitle
} else {
titleString = EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
}
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(titleString), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: notificationPeerExceptionEntries(presentationData: presentationData, notificationSoundList: notificationSoundList, state: state), style: .blocks, animateChanges: animated) let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: notificationPeerExceptionEntries(presentationData: presentationData, notificationSoundList: notificationSoundList, state: state), style: .blocks, animateChanges: animated)
return (controllerState, (listState, arguments)) return (controllerState, (listState, arguments))

View File

@ -493,8 +493,11 @@ public func notificationsPeerCategoryController(context: AccountContext, categor
updateNotificationsView({}) updateNotificationsView({})
let presentPeerSettings: (PeerId, @escaping () -> Void) -> Void = { peerId, completion in let presentPeerSettings: (PeerId, @escaping () -> Void) -> Void = { peerId, completion in
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) let _ = (context.engine.data.get(
|> deliverOnMainQueue).start(next: { peer in TelegramEngine.EngineData.Item.Peer.Peer(id: peerId),
TelegramEngine.EngineData.Item.NotificationSettings.Global()
)
|> deliverOnMainQueue).start(next: { peer, globalSettings in
completion() completion()
guard let peer = peer else { guard let peer = peer else {
@ -502,7 +505,20 @@ public func notificationsPeerCategoryController(context: AccountContext, categor
} }
let mode = stateValue.with { $0.mode } let mode = stateValue.with { $0.mode }
pushControllerImpl?(notificationPeerExceptionController(context: context, peer: peer._asPeer(), threadId: nil, mode: mode, updatePeerSound: { peerId, sound in
let canRemove = mode.peerIds.contains(peerId)
let defaultSound: PeerMessageSound
switch mode {
case .channels:
defaultSound = globalSettings.channels.sound._asMessageSound()
case .groups:
defaultSound = globalSettings.groupChats.sound._asMessageSound()
case .users:
defaultSound = globalSettings.privateChats.sound._asMessageSound()
}
pushControllerImpl?(notificationPeerExceptionController(context: context, peer: peer._asPeer(), threadId: nil, canRemove: canRemove, defaultSound: defaultSound, updatePeerSound: { peerId, sound in
_ = updatePeerSound(peer.id, sound).start(next: { _ in _ = updatePeerSound(peer.id, sound).start(next: { _ in
updateNotificationsDisposable.set(nil) updateNotificationsDisposable.set(nil)
_ = combineLatest(updatePeerSound(peer.id, sound), context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) |> deliverOnMainQueue).start(next: { _, peer in _ = combineLatest(updatePeerSound(peer.id, sound), context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) |> deliverOnMainQueue).start(next: { _, peer in
@ -610,7 +626,7 @@ public func notificationsPeerCategoryController(context: AccountContext, categor
filter.insert(.onlyChannels) filter.insert(.onlyChannels)
} }
let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: filter, hasContactSelector: false, title: presentationData.strings.Notifications_AddExceptionTitle)) let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: filter, hasContactSelector: false, title: presentationData.strings.Notifications_AddExceptionTitle))
controller.peerSelected = { [weak controller] peer in controller.peerSelected = { [weak controller] peer, _ in
let peerId = peer.id let peerId = peer.id
presentPeerSettings(peerId, { presentPeerSettings(peerId, {

View File

@ -228,7 +228,7 @@ public func blockedPeersController(context: AccountContext, blockedPeersContext:
}, addPeer: { }, addPeer: {
let presentationData = context.sharedContext.currentPresentationData.with { $0 } let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyPrivateChats, .excludeSavedMessages, .removeSearchHeader, .excludeRecent, .doNotSearchMessages], title: presentationData.strings.BlockedUsers_SelectUserTitle)) let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyPrivateChats, .excludeSavedMessages, .removeSearchHeader, .excludeRecent, .doNotSearchMessages], title: presentationData.strings.BlockedUsers_SelectUserTitle))
controller.peerSelected = { [weak controller] peer in controller.peerSelected = { [weak controller] peer, _ in
let peerId = peer.id let peerId = peer.id
guard let strongController = controller else { guard let strongController = controller else {

View File

@ -218,7 +218,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
private func updateChatsLayout(layout: ContainerViewLayout, topInset: CGFloat, transition: ContainedViewLayoutTransition) { private func updateChatsLayout(layout: ContainerViewLayout, topInset: CGFloat, transition: ContainedViewLayoutTransition) {
var items: [ChatListItem] = [] var items: [ChatListItem] = []
let interaction = ChatListNodeInteraction(context: self.context, animationCache: self.animationCache, animationRenderer: self.animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in let interaction = ChatListNodeInteraction(context: self.context, animationCache: self.animationCache, animationRenderer: self.animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _, _ in }, togglePeerSelected: { _, _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
}, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in }, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in
}, activateChatPreview: { _, _, gesture, _ in }, activateChatPreview: { _, _, gesture, _ in
gesture?.cancel() gesture?.cancel()

View File

@ -838,7 +838,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
private func updateChatsLayout(layout: ContainerViewLayout, topInset: CGFloat, transition: ContainedViewLayoutTransition) { private func updateChatsLayout(layout: ContainerViewLayout, topInset: CGFloat, transition: ContainedViewLayoutTransition) {
var items: [ChatListItem] = [] var items: [ChatListItem] = []
let interaction = ChatListNodeInteraction(context: self.context, animationCache: self.animationCache, animationRenderer: self.animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in let interaction = ChatListNodeInteraction(context: self.context, animationCache: self.animationCache, animationRenderer: self.animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _, _ in }, togglePeerSelected: { _, _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
}, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in }, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in
}, activateChatPreview: { _, _, gesture, _ in }, activateChatPreview: { _, _, gesture, _ in
gesture?.cancel() gesture?.cancel()

View File

@ -362,7 +362,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
private func updateChatsLayout(layout: ContainerViewLayout, topInset: CGFloat, transition: ContainedViewLayoutTransition) { private func updateChatsLayout(layout: ContainerViewLayout, topInset: CGFloat, transition: ContainedViewLayoutTransition) {
var items: [ChatListItem] = [] var items: [ChatListItem] = []
let interaction = ChatListNodeInteraction(context: self.context, animationCache: self.animationCache, animationRenderer: self.animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in let interaction = ChatListNodeInteraction(context: self.context, animationCache: self.animationCache, animationRenderer: self.animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _, _ in }, togglePeerSelected: { _, _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
}, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in }, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in
}, activateChatPreview: { _, _, gesture, _ in }, activateChatPreview: { _, _, gesture, _ in
gesture?.cancel() gesture?.cancel()

View File

@ -209,6 +209,17 @@ public extension TelegramEngine {
} }
|> ignoreValues |> ignoreValues
} }
public func removeCustomThreadNotificationSettings(peerId: EnginePeer.Id, threadIds: [Int64]) -> Signal<Never, NoError> {
return self.account.postbox.transaction { transaction -> Void in
for threadId in threadIds {
_internal_updatePeerNotificationSoundInteractive(account: self.account, transaction: transaction, peerId: peerId, threadId: threadId, sound: .default)
_internal_updatePeerMuteSetting(account: self.account, transaction: transaction, peerId: peerId, threadId: threadId, muteInterval: nil)
_internal_updatePeerDisplayPreviewsSetting(account: self.account, transaction: transaction, peerId: peerId, threadId: threadId, displayPreviews: .default)
}
}
|> ignoreValues
}
public func channelAdminEventLog(peerId: PeerId) -> ChannelAdminEventLogContext { public func channelAdminEventLog(peerId: PeerId) -> ChannelAdminEventLogContext {
return ChannelAdminEventLogContext(postbox: self.account.postbox, network: self.account.network, peerId: peerId) return ChannelAdminEventLogContext(postbox: self.account.postbox, network: self.account.network, peerId: peerId)

View File

@ -30,6 +30,7 @@ swift_library(
"//submodules/TelegramUI/Components/EntityKeyboard:EntityKeyboard", "//submodules/TelegramUI/Components/EntityKeyboard:EntityKeyboard",
"//submodules/TelegramUI/Components/EmojiTextAttachmentView:EmojiTextAttachmentView", "//submodules/TelegramUI/Components/EmojiTextAttachmentView:EmojiTextAttachmentView",
"//submodules/Components/PagerComponent:PagerComponent", "//submodules/Components/PagerComponent:PagerComponent",
"//submodules/PremiumUI",
], ],
visibility = [ visibility = [
"//visibility:public", "//visibility:public",

View File

@ -14,6 +14,7 @@ import PagerComponent
import MultilineTextComponent import MultilineTextComponent
import EmojiStatusComponent import EmojiStatusComponent
import Postbox import Postbox
import PremiumUI
private final class TitleFieldComponent: Component { private final class TitleFieldComponent: Component {
typealias EnvironmentType = Empty typealias EnvironmentType = Empty
@ -315,13 +316,15 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent {
let mode: ForumCreateTopicScreen.Mode let mode: ForumCreateTopicScreen.Mode
let titleUpdated: (String) -> Void let titleUpdated: (String) -> Void
let iconUpdated: (Int64?) -> Void let iconUpdated: (Int64?) -> Void
let openPremium: () -> Void
init(context: AccountContext, peerId: EnginePeer.Id, mode: ForumCreateTopicScreen.Mode, titleUpdated: @escaping (String) -> Void, iconUpdated: @escaping (Int64?) -> Void) { init(context: AccountContext, peerId: EnginePeer.Id, mode: ForumCreateTopicScreen.Mode, titleUpdated: @escaping (String) -> Void, iconUpdated: @escaping (Int64?) -> Void, openPremium: @escaping () -> Void) {
self.context = context self.context = context
self.peerId = peerId self.peerId = peerId
self.mode = mode self.mode = mode
self.titleUpdated = titleUpdated self.titleUpdated = titleUpdated
self.iconUpdated = iconUpdated self.iconUpdated = iconUpdated
self.openPremium = openPremium
} }
static func ==(lhs: ForumCreateTopicScreenComponent, rhs: ForumCreateTopicScreenComponent) -> Bool { static func ==(lhs: ForumCreateTopicScreenComponent, rhs: ForumCreateTopicScreenComponent) -> Bool {
@ -341,17 +344,26 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent {
private let context: AccountContext private let context: AccountContext
private let titleUpdated: (String) -> Void private let titleUpdated: (String) -> Void
private let iconUpdated: (Int64?) -> Void private let iconUpdated: (Int64?) -> Void
private let openPremium: () -> Void
var emojiContent: EmojiPagerContentComponent? var emojiContent: EmojiPagerContentComponent?
private let emojiContentDisposable = MetaDisposable() private let emojiContentDisposable = MetaDisposable()
private var isPremiumDisposable: Disposable?
private var defaultIconFilesDisposable: Disposable?
private var defaultIconFiles = Set<Int64>()
var title: String var title: String
var fileId: Int64 var fileId: Int64
init(context: AccountContext, mode: ForumCreateTopicScreen.Mode, titleUpdated: @escaping (String) -> Void, iconUpdated: @escaping (Int64?) -> Void) { private var hasPremium: Bool = false
init(context: AccountContext, mode: ForumCreateTopicScreen.Mode, titleUpdated: @escaping (String) -> Void, iconUpdated: @escaping (Int64?) -> Void, openPremium: @escaping () -> Void) {
self.context = context self.context = context
self.titleUpdated = titleUpdated self.titleUpdated = titleUpdated
self.iconUpdated = iconUpdated self.iconUpdated = iconUpdated
self.openPremium = openPremium
switch mode { switch mode {
case .create: case .create:
@ -384,10 +396,36 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent {
self?.emojiContent = content self?.emojiContent = content
self?.updated(transition: .immediate) self?.updated(transition: .immediate)
})) }))
self.isPremiumDisposable = (context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId))
|> map { peer -> Bool in
guard case let .user(user) = peer else {
return false
}
return user.isPremium
}
|> distinctUntilChanged).start(next: { [weak self] hasPremium in
self?.hasPremium = hasPremium
})
self.defaultIconFilesDisposable = (context.engine.stickers.loadedStickerPack(reference: .iconTopicEmoji, forceActualized: false)
|> deliverOnMainQueue).start(next: { [weak self] result in
guard let strongSelf = self else {
return
}
switch result {
case let .result(_, items, _):
strongSelf.defaultIconFiles = Set(items.map(\.file.fileId.id))
default:
break
}
})
} }
deinit { deinit {
self.emojiContentDisposable.dispose() self.emojiContentDisposable.dispose()
self.defaultIconFilesDisposable?.dispose()
self.isPremiumDisposable?.dispose()
} }
func updateTitle(_ text: String) { func updateTitle(_ text: String) {
@ -423,7 +461,17 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent {
return return
} }
self.fileId = item.itemFile?.fileId.id ?? 0 if let fileId = item.itemFile?.fileId.id {
if !self.hasPremium && !self.defaultIconFiles.contains(fileId) {
self.openPremium()
return
}
self.fileId = fileId
} else {
self.fileId = 0
}
self.updated(transition: .immediate) self.updated(transition: .immediate)
self.iconUpdated(self.fileId != 0 ? self.fileId : nil) self.iconUpdated(self.fileId != 0 ? self.fileId : nil)
@ -456,7 +504,8 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent {
context: self.context, context: self.context,
mode: self.mode, mode: self.mode,
titleUpdated: self.titleUpdated, titleUpdated: self.titleUpdated,
iconUpdated: self.iconUpdated iconUpdated: self.iconUpdated,
openPremium: self.openPremium
) )
} }
@ -679,10 +728,14 @@ public class ForumCreateTopicScreen: ViewControllerComponentContainer {
public init(context: AccountContext, peerId: EnginePeer.Id, mode: ForumCreateTopicScreen.Mode) { public init(context: AccountContext, peerId: EnginePeer.Id, mode: ForumCreateTopicScreen.Mode) {
var titleUpdatedImpl: ((String) -> Void)? var titleUpdatedImpl: ((String) -> Void)?
var iconUpdatedImpl: ((Int64?) -> Void)? var iconUpdatedImpl: ((Int64?) -> Void)?
var openPremiumImpl: (() -> Void)?
super.init(context: context, component: ForumCreateTopicScreenComponent(context: context, peerId: peerId, mode: mode, titleUpdated: { title in super.init(context: context, component: ForumCreateTopicScreenComponent(context: context, peerId: peerId, mode: mode, titleUpdated: { title in
titleUpdatedImpl?(title) titleUpdatedImpl?(title)
}, iconUpdated: { fileId in }, iconUpdated: { fileId in
iconUpdatedImpl?(fileId) iconUpdatedImpl?(fileId)
}, openPremium: {
openPremiumImpl?()
}), navigationBarAppearance: .transparent) }), navigationBarAppearance: .transparent)
//TODO:localize //TODO:localize
@ -725,12 +778,30 @@ public class ForumCreateTopicScreen: ViewControllerComponentContainer {
strongSelf.state = (strongSelf.state.0, fileId) strongSelf.state = (strongSelf.state.0, fileId)
} }
openPremiumImpl = { [weak self] in
guard let strongSelf = self else {
return
}
var replaceImpl: ((ViewController) -> Void)?
let controller = PremiumDemoScreen(context: context, subject: .uniqueReactions, action: {
let controller = PremiumIntroScreen(context: context, source: .reactions)
replaceImpl?(controller)
})
replaceImpl = { [weak controller] c in
controller?.replace(with: c)
}
strongSelf.push(controller)
}
} }
required public init(coder aDecoder: NSCoder) { required public init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
deinit {
}
@objc private func cancelPressed() { @objc private func cancelPressed() {
self.dismiss() self.dismiss()
} }

View File

@ -30,6 +30,7 @@ swift_library(
"//submodules/ItemListPeerItem", "//submodules/ItemListPeerItem",
"//submodules/ItemListPeerActionItem", "//submodules/ItemListPeerActionItem",
"//submodules/NotificationSoundSelectionUI", "//submodules/NotificationSoundSelectionUI",
"//submodules/SettingsUI",
], ],
visibility = [ visibility = [
"//visibility:public", "//visibility:public",

View File

@ -16,6 +16,7 @@ import NotificationSoundSelectionUI
import TelegramStringFormatting import TelegramStringFormatting
import ItemListPeerItem import ItemListPeerItem
import ItemListPeerActionItem import ItemListPeerActionItem
import SettingsUI
private extension EnginePeer.NotificationSettings.MuteState { private extension EnginePeer.NotificationSettings.MuteState {
var timeInterval: Int32? { var timeInterval: Int32? {
@ -98,7 +99,7 @@ private enum NotificationsPeerCategoryEntry: ItemListNodeEntry {
case exceptionsHeader(String) case exceptionsHeader(String)
case addException(String) case addException(String)
case exception(Int32, PresentationDateTimeFormat, PresentationPersonNameOrder, EngineMessageHistoryThread.Info, String, TelegramPeerNotificationSettings, Bool, Bool) case exception(Int32, PresentationDateTimeFormat, PresentationPersonNameOrder, EnginePeer, Int64, EngineMessageHistoryThread.Info, String, TelegramPeerNotificationSettings, Bool, Bool)
case removeAllExceptions(String) case removeAllExceptions(String)
var section: ItemListSectionId { var section: ItemListSectionId {
@ -126,7 +127,7 @@ private enum NotificationsPeerCategoryEntry: ItemListNodeEntry {
return 4 return 4
case .addException: case .addException:
return 5 return 5
case let .exception(index, _, _, _, _, _, _, _): case let .exception(index, _, _, _, _, _, _, _, _, _):
return 6 + index return 6 + index
case .removeAllExceptions: case .removeAllExceptions:
return 100000 return 100000
@ -184,8 +185,8 @@ private enum NotificationsPeerCategoryEntry: ItemListNodeEntry {
} else { } else {
return false return false
} }
case let .exception(lhsIndex, lhsDateTimeFormat, lhsDisplayNameOrder, lhsInfo, lhsDescription, lhsSettings, lhsEditing, lhsRevealed): case let .exception(lhsIndex, lhsDateTimeFormat, lhsDisplayNameOrder, lhsPeer, lhsThreadId, lhsInfo, lhsDescription, lhsSettings, lhsEditing, lhsRevealed):
if case let .exception(rhsIndex, rhsDateTimeFormat, rhsDisplayNameOrder, rhsInfo, rhsDescription, rhsSettings, rhsEditing, rhsRevealed) = rhs, lhsIndex == rhsIndex, lhsDateTimeFormat == rhsDateTimeFormat, lhsDisplayNameOrder == rhsDisplayNameOrder, lhsInfo == rhsInfo, lhsDescription == rhsDescription, lhsSettings == rhsSettings, lhsEditing == rhsEditing, lhsRevealed == rhsRevealed { if case let .exception(rhsIndex, rhsDateTimeFormat, rhsDisplayNameOrder, rhsPeer, rhsThreadId, rhsInfo, rhsDescription, rhsSettings, rhsEditing, rhsRevealed) = rhs, lhsIndex == rhsIndex, lhsDateTimeFormat == rhsDateTimeFormat, lhsDisplayNameOrder == rhsDisplayNameOrder, lhsPeer == rhsPeer, lhsThreadId == rhsThreadId, lhsInfo == rhsInfo, lhsDescription == rhsDescription, lhsSettings == rhsSettings, lhsEditing == rhsEditing, lhsRevealed == rhsRevealed {
return true return true
} else { } else {
return false return false
@ -226,16 +227,18 @@ private enum NotificationsPeerCategoryEntry: ItemListNodeEntry {
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.plusIconImage(presentationData.theme), title: text, sectionId: self.section, height: .peerList, color: .accent, editing: false, action: { return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.plusIconImage(presentationData.theme), title: text, sectionId: self.section, height: .peerList, color: .accent, editing: false, action: {
arguments.addException() arguments.addException()
}) })
//case let .exception(_, dateTimeFormat, nameDisplayOrder, info, description, _, editing, revealed): case let .exception(_, dateTimeFormat, nameDisplayOrder, peer, threadId, info, description, _, editing, revealed):
case .exception: return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, context: arguments.context, peer: peer, threadInfo: info, presence: nil, text: .text(description, .secondary), label: .none, editing: ItemListPeerItemEditing(editable: true, editing: editing, revealed: revealed), switchValue: nil, enabled: true, selectable: true, sectionId: self.section, action: {
preconditionFailure() arguments.openException(threadId)
/*return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, context: arguments.context, peer: EnginePeer(peer), presence: nil, text: .text(description, .secondary), label: .none, editing: ItemListPeerItemEditing(editable: true, editing: editing, revealed: revealed), switchValue: nil, enabled: true, selectable: true, sectionId: self.section, action: {
arguments.openException(peer)
}, setPeerIdWithRevealedOptions: { peerId, fromPeerId in }, setPeerIdWithRevealedOptions: { peerId, fromPeerId in
arguments.updateRevealedPeerId(peerId) if let _ = peerId {
}, removePeer: { peerId in arguments.updateRevealedThreadId(threadId)
arguments.removePeer(peer) } else {
}, hasTopStripe: false, hasTopGroupInset: false, noInsets: false)*/ arguments.updateRevealedThreadId(nil)
}
}, removePeer: { _ in
arguments.removeThread(threadId)
}, hasTopStripe: false, hasTopGroupInset: false, noInsets: false)
case let .removeAllExceptions(text): case let .removeAllExceptions(text):
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.deleteIconImage(presentationData.theme), title: text, sectionId: self.section, height: .generic, color: .destructive, editing: false, action: { return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.deleteIconImage(presentationData.theme), title: text, sectionId: self.section, height: .generic, color: .destructive, editing: false, action: {
arguments.removeAllExceptions() arguments.removeAllExceptions()
@ -244,7 +247,7 @@ private enum NotificationsPeerCategoryEntry: ItemListNodeEntry {
} }
} }
private func notificationsPeerCategoryEntries(notificationSettings: EnginePeer.NotificationSettings, state: NotificationExceptionState, presentationData: PresentationData, notificationSoundList: NotificationSoundList?) -> [NotificationsPeerCategoryEntry] { private func notificationsPeerCategoryEntries(peerId: EnginePeer.Id, notificationSettings: EnginePeer.NotificationSettings, state: NotificationExceptionState, presentationData: PresentationData, notificationSoundList: NotificationSoundList?) -> [NotificationsPeerCategoryEntry] {
var entries: [NotificationsPeerCategoryEntry] = [] var entries: [NotificationsPeerCategoryEntry] = []
var notificationsEnabled = true var notificationsEnabled = true
@ -328,7 +331,7 @@ private func notificationsPeerCategoryEntries(notificationSettings: EnginePeer.N
} }
} }
existingThreadIds.insert(value.threadId) existingThreadIds.insert(value.threadId)
entries.append(.exception(Int32(index), presentationData.dateTimeFormat, presentationData.nameDisplayOrder, value.info, title, value.notificationSettings._asNotificationSettings(), state.editing, state.revealedThreadId == value.threadId)) entries.append(.exception(Int32(index), presentationData.dateTimeFormat, presentationData.nameDisplayOrder, .channel(TelegramChannel(id: peerId, accessHash: nil, title: "", username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(TelegramChannelGroupInfo(flags: [])), flags: [.isForum], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [])), value.threadId, value.info, title, value.notificationSettings._asNotificationSettings(), state.editing, state.revealedThreadId == value.threadId))
index += 1 index += 1
} }
@ -339,10 +342,82 @@ private func notificationsPeerCategoryEntries(notificationSettings: EnginePeer.N
return entries return entries
} }
private extension EnginePeer.NotificationSettings {
var isDefault: Bool {
switch self.muteState {
case .default:
break
case .muted, .unmuted:
return false
}
switch self.messageSound {
case .default:
break
case .none, .bundledClassic, .bundledModern, .cloud:
return false
}
switch self.displayPreviews {
case .default:
break
case .hide, .show:
return false
}
return true
}
}
private struct NotificationExceptionState: Equatable { private struct NotificationExceptionState: Equatable {
var revealedThreadId: Int64? = nil var revealedThreadId: Int64? = nil
var editing: Bool = false var editing: Bool = false
var notificationExceptions: [EngineMessageHistoryThread.NotificationException] = [] var notificationExceptions: [EngineMessageHistoryThread.NotificationException] = []
mutating func updateSound(threadId: Int64, info: EngineMessageHistoryThread.Info, sound: PeerMessageSound) {
if let index = self.notificationExceptions.firstIndex(where: { $0.threadId == threadId }) {
self.notificationExceptions[index].notificationSettings.messageSound = EnginePeer.NotificationSettings.MessageSound(sound)
if self.notificationExceptions[index].notificationSettings.isDefault {
self.notificationExceptions.remove(at: index)
}
} else {
var settings = EnginePeer.NotificationSettings(.defaultSettings)
settings.messageSound = EnginePeer.NotificationSettings.MessageSound(sound)
if !settings.isDefault {
notificationExceptions.insert(EngineMessageHistoryThread.NotificationException(threadId: threadId, info: info, notificationSettings: settings), at: 0)
}
}
}
mutating func updateMuteInterval(threadId: Int64, info: EngineMessageHistoryThread.Info, muteInterval: Int32?) {
if let index = self.notificationExceptions.firstIndex(where: { $0.threadId == threadId }) {
self.notificationExceptions[index].notificationSettings.muteState = muteInterval.flatMap { .muted(until: $0) } ?? .unmuted
if self.notificationExceptions[index].notificationSettings.isDefault {
self.notificationExceptions.remove(at: index)
}
} else {
var settings = EnginePeer.NotificationSettings(.defaultSettings)
settings.muteState = muteInterval.flatMap { .muted(until: $0) } ?? .unmuted
if !settings.isDefault {
notificationExceptions.insert(EngineMessageHistoryThread.NotificationException(threadId: threadId, info: info, notificationSettings: settings), at: 0)
}
}
}
mutating func updateDisplayPreviews(threadId: Int64, info: EngineMessageHistoryThread.Info, displayPreviews: PeerNotificationDisplayPreviews) {
if let index = self.notificationExceptions.firstIndex(where: { $0.threadId == threadId }) {
self.notificationExceptions[index].notificationSettings.displayPreviews = EnginePeer.NotificationSettings.DisplayPreviews(displayPreviews)
if self.notificationExceptions[index].notificationSettings.isDefault {
self.notificationExceptions.remove(at: index)
}
} else {
var settings = EnginePeer.NotificationSettings(.defaultSettings)
settings.displayPreviews = EnginePeer.NotificationSettings.DisplayPreviews(displayPreviews)
if !settings.isDefault {
notificationExceptions.insert(EngineMessageHistoryThread.NotificationException(threadId: threadId, info: info, notificationSettings: settings), at: 0)
}
}
}
} }
public func threadNotificationExceptionsScreen(context: AccountContext, peerId: EnginePeer.Id, notificationExceptions: [EngineMessageHistoryThread.NotificationException], updated: @escaping ([EngineMessageHistoryThread.NotificationException]) -> Void) -> ViewController { public func threadNotificationExceptionsScreen(context: AccountContext, peerId: EnginePeer.Id, notificationExceptions: [EngineMessageHistoryThread.NotificationException], updated: @escaping ([EngineMessageHistoryThread.NotificationException]) -> Void) -> ViewController {
@ -358,23 +433,80 @@ public func threadNotificationExceptionsScreen(context: AccountContext, peerId:
let updateState: ((NotificationExceptionState) -> NotificationExceptionState) -> Void = { f in let updateState: ((NotificationExceptionState) -> NotificationExceptionState) -> Void = { f in
let result = stateValue.modify { f($0) } let result = stateValue.modify { f($0) }
statePromise.set(result) statePromise.set(result)
//updatedMode(result.mode)
} }
/*let updatePeerSound: (PeerId, PeerMessageSound) -> Signal<Void, NoError> = { peerId, sound in let updateThreadSound: (Int64, PeerMessageSound) -> Signal<Void, NoError> = { threadId, sound in
return context.engine.peers.updatePeerNotificationSoundInteractive(peerId: peerId, threadId: nil, sound: sound) return context.engine.peers.updatePeerNotificationSoundInteractive(peerId: peerId, threadId: threadId, sound: sound)
|> deliverOnMainQueue |> deliverOnMainQueue
} }
let updatePeerNotificationInterval: (PeerId, Int32?) -> Signal<Void, NoError> = { peerId, muteInterval in let updateThreadNotificationInterval: (Int64, Int32?) -> Signal<Void, NoError> = { threadId, muteInterval in
return context.engine.peers.updatePeerMuteSetting(peerId: peerId, threadId: nil, muteInterval: muteInterval) return context.engine.peers.updatePeerMuteSetting(peerId: peerId, threadId: threadId, muteInterval: muteInterval)
|> deliverOnMainQueue |> deliverOnMainQueue
} }
let updatePeerDisplayPreviews: (PeerId, PeerNotificationDisplayPreviews) -> Signal<Void, NoError> = { let updateThreadDisplayPreviews: (Int64, PeerNotificationDisplayPreviews) -> Signal<Void, NoError> = {
peerId, displayPreviews in threadId, displayPreviews in
return context.engine.peers.updatePeerDisplayPreviewsSetting(peerId: peerId, threadId: nil, displayPreviews: displayPreviews) |> deliverOnMainQueue return context.engine.peers.updatePeerDisplayPreviewsSetting(peerId: peerId, threadId: threadId, displayPreviews: displayPreviews) |> deliverOnMainQueue
}*/ }
let presentThreadSettings: (EngineMessageHistoryThread.NotificationException, @escaping () -> Void) -> Void = { item, completion in
let _ = (context.engine.data.get(
TelegramEngine.EngineData.Item.Peer.Peer(id: peerId),
TelegramEngine.EngineData.Item.NotificationSettings.Global()
)
|> deliverOnMainQueue).start(next: { peer, globalSettings in
completion()
guard let peer = peer else {
return
}
let canRemove = true
let defaultSound: PeerMessageSound = globalSettings.groupChats.sound._asMessageSound()
pushControllerImpl?(notificationPeerExceptionController(context: context, peer: peer._asPeer(), customTitle: item.info.title, threadId: item.threadId, canRemove: canRemove, defaultSound: defaultSound, updatePeerSound: { _, sound in
let _ = (updateThreadSound(item.threadId, sound)
|> deliverOnMainQueue).start(next: { _ in
updateState { value in
var value = value
value.updateSound(threadId: item.threadId, info: item.info, sound: sound)
return value
}
updated(stateValue.with({ $0 }).notificationExceptions)
})
}, updatePeerNotificationInterval: { _, muteInterval in
let _ = (updateThreadNotificationInterval(item.threadId, muteInterval)
|> deliverOnMainQueue).start(next: { _ in
updateState { value in
var value = value
value.updateMuteInterval(threadId: item.threadId, info: item.info, muteInterval: muteInterval)
return value
}
updated(stateValue.with({ $0 }).notificationExceptions)
})
}, updatePeerDisplayPreviews: { _, displayPreviews in
let _ = (updateThreadDisplayPreviews(item.threadId, displayPreviews)
|> deliverOnMainQueue).start(next: { _ in
updateState { value in
var value = value
value.updateDisplayPreviews(threadId: item.threadId, info: item.info, displayPreviews: displayPreviews)
return value
}
updated(stateValue.with({ $0 }).notificationExceptions)
})
}, removePeerFromExceptions: {
let _ = context.engine.peers.removeCustomThreadNotificationSettings(peerId: peerId, threadIds: [item.threadId]).start()
updateState { current in
var current = current
current.notificationExceptions.removeAll(where: { $0.threadId == item.threadId })
return current
}
updated(stateValue.with({ $0 }).notificationExceptions)
}, modifiedPeer: {
}))
})
}
let _ = presentControllerImpl let _ = presentControllerImpl
@ -388,61 +520,53 @@ public func threadNotificationExceptionsScreen(context: AccountContext, peerId:
}) })
pushControllerImpl?(controller) pushControllerImpl?(controller)
}, addException: { }, addException: {
/*let presentationData = context.sharedContext.currentPresentationData.with { $0 } let presentationData = context.sharedContext.currentPresentationData.with { $0 }
var filter: ChatListNodePeersFilter = [.excludeRecent, .doNotSearchMessages, .removeSearchHeader] let filter: ChatListNodePeersFilter = [.excludeRecent, .doNotSearchMessages, .removeSearchHeader]
switch category { let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: filter, forumPeerId: peerId, hasContactSelector: false, title: presentationData.strings.Notifications_AddExceptionTitle))
case .privateChat: controller.peerSelected = { [weak controller] _, threadId in
filter.insert(.onlyPrivateChats) guard let threadId = threadId else {
filter.insert(.excludeSavedMessages) return
filter.insert(.excludeSecretChats) }
case .group: let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.ThreadData(id: peerId, threadId: threadId))
filter.insert(.onlyGroups) |> deliverOnMainQueue).start(next: { threadData in
case .channel: guard let threadData = threadData else {
filter.insert(.onlyChannels) return
} }
let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: filter, hasContactSelector: false, title: presentationData.strings.Notifications_AddExceptionTitle))
controller.peerSelected = { [weak controller] peer in presentThreadSettings(EngineMessageHistoryThread.NotificationException(threadId: threadId, info: threadData.info, notificationSettings: EnginePeer.NotificationSettings(.defaultSettings)), {
let peerId = peer.id controller?.dismiss()
})
presentPeerSettings(peerId, {
controller?.dismiss()
}) })
} }
pushControllerImpl?(controller)*/ pushControllerImpl?(controller)
}, openException: { peer in }, openException: { threadId in
//presentPeerSettings(peer.id, {}) if let item = stateValue.with({ $0 }).notificationExceptions.first(where: { $0.threadId == threadId }) {
presentThreadSettings(item, {})
}
}, removeAllExceptions: { }, removeAllExceptions: {
/*let presentationData = context.sharedContext.currentPresentationData.with { $0 } let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let actionSheet = ActionSheetController(presentationData: presentationData) let actionSheet = ActionSheetController(presentationData: presentationData)
actionSheet.setItemGroups([ActionSheetItemGroup(items: [ actionSheet.setItemGroups([ActionSheetItemGroup(items: [
ActionSheetTextItem(title: presentationData.strings.Notification_Exceptions_DeleteAllConfirmation), ActionSheetTextItem(title: presentationData.strings.Notification_Exceptions_DeleteAllConfirmation),
ActionSheetButtonItem(title: presentationData.strings.Notification_Exceptions_DeleteAll, color: .destructive, action: { [weak actionSheet] in ActionSheetButtonItem(title: presentationData.strings.Notification_Exceptions_DeleteAll, color: .destructive, action: { [weak actionSheet] in
actionSheet?.dismissAnimated() actionSheet?.dismissAnimated()
let values = stateValue.with { $0.mode.settings.values } var threadIds: [Int64] = []
updateState { current in
let _ = (context.engine.peers.ensurePeersAreLocallyAvailable(peers: values.map { EnginePeer($0.peer) }) var current = current
|> deliverOnMainQueue).start(completed: { threadIds = current.notificationExceptions.map(\.threadId)
updateNotificationsDisposable.set(nil) current.notificationExceptions.removeAll()
updateState { state in return current
var state = state }
for value in values { updated(stateValue.with({ $0 }).notificationExceptions)
state = state.withUpdatedPeerMuteInterval(value.peer, nil).withUpdatedPeerSound(value.peer, .default).withUpdatedPeerDisplayPreviews(value.peer, .default) let _ = context.engine.peers.removeCustomThreadNotificationSettings(peerId: peerId, threadIds: threadIds).start()
}
return state
}
let _ = (context.engine.peers.removeCustomNotificationSettings(peerIds: values.map(\.peer.id))
|> deliverOnMainQueue).start(completed: {
updateNotificationsView({})
})
})
}) })
]), ActionSheetItemGroup(items: [ ]), ActionSheetItemGroup(items: [
ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
actionSheet?.dismissAnimated() actionSheet?.dismissAnimated()
}) })
])]) ])])
presentControllerImpl?(actionSheet, nil)*/ presentControllerImpl?(actionSheet, nil)
}, updateRevealedThreadId: { threadId in }, updateRevealedThreadId: { threadId in
updateState { current in updateState { current in
var current = current var current = current
@ -450,17 +574,13 @@ public func threadNotificationExceptionsScreen(context: AccountContext, peerId:
return current return current
} }
}, removeThread: { threadId in }, removeThread: { threadId in
/*let _ = (context.engine.peers.ensurePeersAreLocallyAvailable(peers: [EnginePeer(peer)]) let _ = context.engine.peers.removeCustomThreadNotificationSettings(peerId: peerId, threadIds: [threadId]).start()
|> deliverOnMainQueue).start(completed: { updateState { current in
updateNotificationsDisposable.set(nil) var current = current
updateState { value in current.notificationExceptions.removeAll(where: { $0.threadId == threadId })
return value.withUpdatedPeerMuteInterval(peer, nil).withUpdatedPeerSound(peer, .default).withUpdatedPeerDisplayPreviews(peer, .default) return current
} }
let _ = (context.engine.peers.removeCustomNotificationSettings(peerIds: [peer.id]) updated(stateValue.with({ $0 }).notificationExceptions)
|> deliverOnMainQueue).start(completed: {
updateNotificationsView({})
})
})*/
}) })
let signal = combineLatest(queue: .mainQueue(), let signal = combineLatest(queue: .mainQueue(),
@ -471,7 +591,7 @@ public func threadNotificationExceptionsScreen(context: AccountContext, peerId:
statePromise.get() statePromise.get()
) )
|> map { presentationData, notificationSoundList, peer, notificationSettings, state -> (ItemListControllerState, (ItemListNodeState, Any)) in |> map { presentationData, notificationSoundList, peer, notificationSettings, state -> (ItemListControllerState, (ItemListNodeState, Any)) in
let entries = notificationsPeerCategoryEntries(notificationSettings: notificationSettings, state: state, presentationData: presentationData, notificationSoundList: notificationSoundList) let entries = notificationsPeerCategoryEntries(peerId: peerId, notificationSettings: notificationSettings, state: state, presentationData: presentationData, notificationSoundList: notificationSoundList)
var scrollToItem: ListViewScrollToItem? var scrollToItem: ListViewScrollToItem?
scrollToItem = nil scrollToItem = nil

View File

@ -313,6 +313,15 @@ final class AuthorizedApplicationContext {
if let strongSelf = self, let (messages, _, notify, threadData) = messageList.last, let firstMessage = messages.first { if let strongSelf = self, let (messages, _, notify, threadData) = messageList.last, let firstMessage = messages.first {
if UIApplication.shared.applicationState == .active { if UIApplication.shared.applicationState == .active {
let chatLocation: ChatLocation
if let _ = threadData, let threadId = firstMessage.threadId {
chatLocation = .replyThread(message: ChatReplyThreadMessage(
messageId: MessageId(peerId: firstMessage.id.peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false
))
} else {
chatLocation = .peer(id: firstMessage.id.peerId)
}
var chatIsVisible = false var chatIsVisible = false
if let topController = strongSelf.rootController.topViewController as? ChatControllerImpl, topController.traceVisibility() { if let topController = strongSelf.rootController.topViewController as? ChatControllerImpl, topController.traceVisibility() {
if topController.chatLocation.peerId == firstMessage.id.peerId { if topController.chatLocation.peerId == firstMessage.id.peerId {
@ -326,7 +335,7 @@ final class AuthorizedApplicationContext {
if !chatIsVisible { if !chatIsVisible {
strongSelf.mainWindow.forEachViewController({ controller in strongSelf.mainWindow.forEachViewController({ controller in
if let controller = controller as? ChatControllerImpl, case .peer(firstMessage.id.peerId) = controller.chatLocation { if let controller = controller as? ChatControllerImpl, controller.chatLocation.peerId == chatLocation.peerId, controller.chatLocation.threadId == chatLocation.threadId {
chatIsVisible = true chatIsVisible = true
return false return false
} }
@ -406,14 +415,14 @@ final class AuthorizedApplicationContext {
return true return true
} }
if let topController = strongSelf.rootController.topViewController as? ChatControllerImpl, case .peer(firstMessage.id.peerId) = topController.chatLocation { if let topController = strongSelf.rootController.topViewController as? ChatControllerImpl, topController.chatLocation.peerId == chatLocation.peerId, topController.chatLocation.threadId == chatLocation.threadId {
strongSelf.notificationController.removeItemsWithGroupingKey(firstMessage.id.peerId) strongSelf.notificationController.removeItemsWithGroupingKey(firstMessage.id.peerId)
return false return false
} }
for controller in strongSelf.rootController.viewControllers { for controller in strongSelf.rootController.viewControllers {
if let controller = controller as? ChatControllerImpl, case .peer(firstMessage.id.peerId) = controller.chatLocation { if let controller = controller as? ChatControllerImpl, controller.chatLocation.peerId == chatLocation.peerId, controller.chatLocation.threadId == chatLocation.threadId {
return true return true
} }
} }
@ -430,13 +439,13 @@ final class AuthorizedApplicationContext {
} }
if !processed { if !processed {
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: strongSelf.rootController, context: strongSelf.context, chatLocation: .peer(id: firstMessage.id.peerId))) strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: strongSelf.rootController, context: strongSelf.context, chatLocation: chatLocation))
} }
} }
return false return false
}, expandAction: { expandData in }, expandAction: { expandData in
if let strongSelf = self { if let strongSelf = self {
let chatController = ChatControllerImpl(context: strongSelf.context, chatLocation: .peer(id: firstMessage.id.peerId), mode: .overlay(strongSelf.rootController)) let chatController = ChatControllerImpl(context: strongSelf.context, chatLocation: chatLocation, mode: .overlay(strongSelf.rootController))
chatController.presentationArguments = ChatControllerOverlayPresentationData(expandData: expandData()) chatController.presentationArguments = ChatControllerOverlayPresentationData(expandData: expandData())
(strongSelf.rootController.viewControllers.last as? ViewController)?.present(chatController, in: .window(.root), with: ChatControllerOverlayPresentationData(expandData: expandData())) (strongSelf.rootController.viewControllers.last as? ViewController)?.present(chatController, in: .window(.root), with: ChatControllerOverlayPresentationData(expandData: expandData()))
} }

View File

@ -4039,8 +4039,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.navigationBar?.userInfo = PeerInfoNavigationSourceTag(peerId: peerId) self.navigationBar?.userInfo = PeerInfoNavigationSourceTag(peerId: peerId)
} }
} }
self.navigationBar?.allowsCustomTransition = { self.navigationBar?.allowsCustomTransition = { [weak self] in
return true guard let strongSelf = self else {
return false
}
return strongSelf.navigationItem.rightBarButtonItem === strongSelf.chatInfoNavigationButton?.buttonItem
} }
self.chatTitleView = ChatTitleView(context: self.context, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, animationCache: controllerInteraction.presentationContext.animationCache, animationRenderer: controllerInteraction.presentationContext.animationRenderer) self.chatTitleView = ChatTitleView(context: self.context, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, animationCache: controllerInteraction.presentationContext.animationCache, animationRenderer: controllerInteraction.presentationContext.animationRenderer)
@ -4826,7 +4829,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let avatarContent: EmojiStatusComponent.Content let avatarContent: EmojiStatusComponent.Content
if let fileId = threadInfo.icon { if let fileId = threadInfo.icon {
avatarContent = .animation(content: .customEmoji(fileId: fileId), size: CGSize(width: 48.0, height: 48.0), placeholderColor: strongSelf.presentationData.theme.list.mediaPlaceholderColor, themeColor: nil, loopMode: .count(2)) avatarContent = .animation(content: .customEmoji(fileId: fileId), size: CGSize(width: 48.0, height: 48.0), placeholderColor: strongSelf.presentationData.theme.list.mediaPlaceholderColor, themeColor: strongSelf.presentationData.theme.list.itemAccentColor, loopMode: .count(2))
} else { } else {
avatarContent = .topic(title: String(threadInfo.title.prefix(1)), colorIndex: Int(threadInfo.iconColor), size: CGSize(width: 32.0, height: 32.0)) avatarContent = .topic(title: String(threadInfo.title.prefix(1)), colorIndex: Int(threadInfo.iconColor), size: CGSize(width: 32.0, height: 32.0))
} }
@ -14948,7 +14951,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
} }
var attemptSelectionImpl: ((Peer) -> Void)? var attemptSelectionImpl: ((Peer) -> Void)?
let controller = self.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: self.context, updatedPresentationData: self.updatedPresentationData, filter: filter, attemptSelection: { peer in let controller = self.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: self.context, updatedPresentationData: self.updatedPresentationData, filter: filter, attemptSelection: { peer, _ in
attemptSelectionImpl?(peer) attemptSelectionImpl?(peer)
}, multipleSelection: true, forwardedMessageIds: messages.map { $0.id })) }, multipleSelection: true, forwardedMessageIds: messages.map { $0.id }))
let context = self.context let context = self.context
@ -15072,7 +15075,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}) })
} }
} }
controller.peerSelected = { [weak self, weak controller] peer in controller.peerSelected = { [weak self, weak controller] peer, _ in
guard let strongSelf = self, let strongController = controller else { guard let strongSelf = self, let strongController = controller else {
return return
} }
@ -15289,7 +15292,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
case let .chat(textInputState, _, _): case let .chat(textInputState, _, _):
if let textInputState = textInputState { if let textInputState = textInputState {
let controller = self.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: self.context, updatedPresentationData: self.updatedPresentationData)) let controller = self.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: self.context, updatedPresentationData: self.updatedPresentationData))
controller.peerSelected = { [weak self, weak controller] peer in controller.peerSelected = { [weak self, weak controller] peer, _ in
let peerId = peer.id let peerId = peer.id
if let strongSelf = self, let strongController = controller { if let strongSelf = self, let strongController = controller {
@ -16946,7 +16949,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
private func presentTopicDiscardAlert(action: @escaping () -> Void = {}, delay: Bool = false, performAction: Bool = true) -> Bool { private func presentTopicDiscardAlert(action: @escaping () -> Void = {}, delay: Bool = false, performAction: Bool = true) -> Bool {
if self.chatDisplayNode.emptyType == .topic { if self.chatDisplayNode.emptyType == .topic, !"".isEmpty {
Queue.mainQueue().after(delay ? 0.2 : 0.0) { Queue.mainQueue().after(delay ? 0.2 : 0.0) {
self.present(textAlertController(context: self.context, updatedPresentationData: self.updatedPresentationData, title: "Delete Topic", text: "Topic isn't created, because you haven't posted a message.\n\nDo you want to discard this topic?", actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_Yes, action: { [weak self] in self.present(textAlertController(context: self.context, updatedPresentationData: self.updatedPresentationData, title: "Delete Topic", text: "Topic isn't created, because you haven't posted a message.\n\nDo you want to discard this topic?", actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_Yes, action: { [weak self] in
guard let strongSelf = self else { guard let strongSelf = self else {

View File

@ -2617,6 +2617,8 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
} }
} else if case .empty(.joined) = loadState, let entry = transition.historyView.originalView.entries.first { } else if case .empty(.joined) = loadState, let entry = transition.historyView.originalView.entries.first {
strongSelf.updateMaxVisibleReadIncomingMessageIndex(entry.message.index) strongSelf.updateMaxVisibleReadIncomingMessageIndex(entry.message.index)
} else if case .empty(.topic) = loadState, let entry = transition.historyView.originalView.entries.first {
strongSelf.updateMaxVisibleReadIncomingMessageIndex(entry.message.index)
} }
if !strongSelf.didSetInitialData { if !strongSelf.didSetInitialData {

View File

@ -703,7 +703,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
f(.dismissWithoutContent) f(.dismissWithoutContent)
let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyWriteable, .excludeDisabled])) let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyWriteable, .excludeDisabled]))
controller.peerSelected = { [weak controller] peer in controller.peerSelected = { [weak controller] peer, _ in
let peerId = peer.id let peerId = peer.id
if let strongController = controller { if let strongController = controller {

View File

@ -209,8 +209,8 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe
let interaction = ChatListNodeInteraction(context: context, animationCache: self.animationCache, animationRenderer: self.animationRenderer, activateSearch: { let interaction = ChatListNodeInteraction(context: context, animationCache: self.animationCache, animationRenderer: self.animationRenderer, activateSearch: {
}, peerSelected: { _, _, _, _ in }, peerSelected: { _, _, _, _ in
}, disabledPeerSelected: { _ in }, disabledPeerSelected: { _, _ in
}, togglePeerSelected: { _ in }, togglePeerSelected: { _, _ in
}, togglePeersSelection: { _, _ in }, togglePeersSelection: { _, _ in
}, additionalCategorySelected: { _ in }, additionalCategorySelected: { _ in
}, messageSelected: { [weak self] peer, _, message, _ in }, messageSelected: { [weak self] peer, _, message, _ in

View File

@ -72,7 +72,7 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
openPeer(EnginePeer(peer), .withBotStartPayload(ChatControllerInitialBotStart(payload: payload, behavior: .interactive))) openPeer(EnginePeer(peer), .withBotStartPayload(ChatControllerInitialBotStart(payload: payload, behavior: .interactive)))
case let .groupBotStart(botPeerId, payload, adminRights): case let .groupBotStart(botPeerId, payload, adminRights):
let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyGroupsAndChannels, .onlyManageable, .excludeDisabled, .excludeRecent, .doNotSearchMessages], hasContactSelector: false, title: presentationData.strings.Bot_AddToChat_Title)) let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyGroupsAndChannels, .onlyManageable, .excludeDisabled, .excludeRecent, .doNotSearchMessages], hasContactSelector: false, title: presentationData.strings.Bot_AddToChat_Title))
controller.peerSelected = { [weak controller] peer in controller.peerSelected = { [weak controller] peer, _ in
let peerId = peer.id let peerId = peer.id
let addMemberImpl = { let addMemberImpl = {
@ -310,7 +310,7 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
context.sharedContext.applicationBindings.dismissNativeController() context.sharedContext.applicationBindings.dismissNativeController()
} else { } else {
let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyWriteable, .excludeDisabled])) let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyWriteable, .excludeDisabled]))
controller.peerSelected = { [weak controller] peer in controller.peerSelected = { [weak controller] peer, _ in
let peerId = peer.id let peerId = peer.id
if let strongController = controller { if let strongController = controller {
@ -578,7 +578,7 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
if let navigationController = navigationController { if let navigationController = navigationController {
let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, updatedPresentationData: updatedPresentationData, filter: filters, hasChatListSelector: true, hasContactSelector: false, title: presentationData.strings.WebApp_SelectChat)) let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, updatedPresentationData: updatedPresentationData, filter: filters, hasChatListSelector: true, hasContactSelector: false, title: presentationData.strings.WebApp_SelectChat))
controller.peerSelected = { peer in controller.peerSelected = { peer, _ in
let _ = context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peer.id), attachBotStart: ChatControllerInitialAttachBotStart(botId: bot.peer.id, payload: payload, justInstalled: false), keepStack: .never, useExisting: true)) let _ = context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peer.id), attachBotStart: ChatControllerInitialAttachBotStart(botId: bot.peer.id, payload: payload, justInstalled: false), keepStack: .never, useExisting: true))
} }
navigationController.pushViewController(controller) navigationController.pushViewController(controller)
@ -621,7 +621,7 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
if let navigationController = navigationController { if let navigationController = navigationController {
let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, updatedPresentationData: updatedPresentationData, filter: filters, hasChatListSelector: true, hasContactSelector: false, title: presentationData.strings.WebApp_SelectChat)) let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, updatedPresentationData: updatedPresentationData, filter: filters, hasChatListSelector: true, hasContactSelector: false, title: presentationData.strings.WebApp_SelectChat))
controller.peerSelected = { peer in controller.peerSelected = { peer, _ in
let _ = context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peer.id), attachBotStart: ChatControllerInitialAttachBotStart(botId: botPeer.id, payload: payload, justInstalled: true), useExisting: true)) let _ = context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peer.id), attachBotStart: ChatControllerInitialAttachBotStart(botId: botPeer.id, payload: payload, justInstalled: true), useExisting: true))
} }
navigationController.pushViewController(controller) navigationController.pushViewController(controller)

View File

@ -423,7 +423,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
} }
let content: EmojiStatusComponent.Content let content: EmojiStatusComponent.Content
if let iconFileId = threadInfo.icon { if let iconFileId = threadInfo.icon {
content = .animation(content: .customEmoji(fileId: iconFileId), size: CGSize(width: avatarSize, height: avatarSize), placeholderColor: theme.list.mediaPlaceholderColor, themeColor: nil, loopMode: .forever) content = .animation(content: .customEmoji(fileId: iconFileId), size: CGSize(width: avatarSize, height: avatarSize), placeholderColor: theme.list.mediaPlaceholderColor, themeColor: theme.list.itemAccentColor, loopMode: .forever)
} else { } else {
content = .topic(title: String(threadInfo.title.prefix(1)), colorIndex: Int(threadInfo.iconColor), size: CGSize(width: avatarSize, height: avatarSize)) content = .topic(title: String(threadInfo.title.prefix(1)), colorIndex: Int(threadInfo.iconColor), size: CGSize(width: avatarSize, height: avatarSize))
} }

View File

@ -1609,7 +1609,7 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
})) }))
} }
if isCreator || (channel.adminRights?.rights.contains(.canChangeInfo) == true) { if isCreator {
//TODO:localize //TODO:localize
items[.peerDataSettings]!.append(PeerInfoScreenSwitchItem(id: ItemTopics, text: "Topics", value: channel.flags.contains(.isForum), icon: UIImage(bundleImageName: "Settings/Menu/ChatListFilters"), toggled: { value in items[.peerDataSettings]!.append(PeerInfoScreenSwitchItem(id: ItemTopics, text: "Topics", value: channel.flags.contains(.isForum), icon: UIImage(bundleImageName: "Settings/Menu/ChatListFilters"), toggled: { value in
interaction.toggleForumTopics(value) interaction.toggleForumTopics(value)
@ -4047,78 +4047,91 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
}))) })))
} }
let context = self.context
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.PeerInfo_NotificationsCustomize, icon: { theme in items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.PeerInfo_NotificationsCustomize, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Customize"), color: theme.contextMenu.primaryColor) return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Customize"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] _, f in }, action: { [weak self] _, f in
f(.dismissWithoutContent) f(.dismissWithoutContent)
guard let strongSelf = self, let peer = strongSelf.data?.peer else { let _ = (context.engine.data.get(
return TelegramEngine.EngineData.Item.NotificationSettings.Global()
} )
let threadId = strongSelf.chatLocation.threadId |> deliverOnMainQueue).start(next: { globalSettings in
guard let strongSelf = self, let peer = strongSelf.data?.peer else {
let context = strongSelf.context return
let updatePeerSound: (PeerId, PeerMessageSound) -> Signal<Void, NoError> = { peerId, sound in }
return context.engine.peers.updatePeerNotificationSoundInteractive(peerId: peerId, threadId: threadId, sound: sound) |> deliverOnMainQueue let threadId = strongSelf.chatLocation.threadId
}
let context = strongSelf.context
let updatePeerNotificationInterval: (PeerId, Int32?) -> Signal<Void, NoError> = { peerId, muteInterval in let updatePeerSound: (PeerId, PeerMessageSound) -> Signal<Void, NoError> = { peerId, sound in
return context.engine.peers.updatePeerMuteSetting(peerId: peerId, threadId: threadId, muteInterval: muteInterval) |> deliverOnMainQueue return context.engine.peers.updatePeerNotificationSoundInteractive(peerId: peerId, threadId: threadId, sound: sound) |> deliverOnMainQueue
} }
let updatePeerDisplayPreviews: (PeerId, PeerNotificationDisplayPreviews) -> Signal<Void, NoError> = { let updatePeerNotificationInterval: (PeerId, Int32?) -> Signal<Void, NoError> = { peerId, muteInterval in
peerId, displayPreviews in return context.engine.peers.updatePeerMuteSetting(peerId: peerId, threadId: threadId, muteInterval: muteInterval) |> deliverOnMainQueue
return context.engine.peers.updatePeerDisplayPreviewsSetting(peerId: peerId, threadId: threadId, displayPreviews: displayPreviews) |> deliverOnMainQueue }
}
let updatePeerDisplayPreviews: (PeerId, PeerNotificationDisplayPreviews) -> Signal<Void, NoError> = {
let mode: NotificationExceptionMode peerId, displayPreviews in
if let _ = peer as? TelegramUser { return context.engine.peers.updatePeerDisplayPreviewsSetting(peerId: peerId, threadId: threadId, displayPreviews: displayPreviews) |> deliverOnMainQueue
mode = .users([:]) }
} else if let _ = peer as? TelegramSecretChat {
mode = .users([:]) let mode: NotificationExceptionMode
} else if let channel = peer as? TelegramChannel { let defaultSound: PeerMessageSound
if case .broadcast = channel.info { if let _ = peer as? TelegramUser {
mode = .channels([:]) mode = .users([:])
defaultSound = globalSettings.privateChats.sound._asMessageSound()
} else if let _ = peer as? TelegramSecretChat {
mode = .users([:])
defaultSound = globalSettings.privateChats.sound._asMessageSound()
} else if let channel = peer as? TelegramChannel {
if case .broadcast = channel.info {
mode = .channels([:])
defaultSound = globalSettings.channels.sound._asMessageSound()
} else {
mode = .groups([:])
defaultSound = globalSettings.groupChats.sound._asMessageSound()
}
} else { } else {
mode = .groups([:]) mode = .groups([:])
defaultSound = globalSettings.groupChats.sound._asMessageSound()
} }
} else { let _ = mode
mode = .groups([:])
} let canRemove = false
let exceptionController = notificationPeerExceptionController(context: context, updatedPresentationData: strongSelf.controller?.updatedPresentationData, peer: peer, threadId: threadId, mode: mode, edit: true, updatePeerSound: { peerId, sound in let exceptionController = notificationPeerExceptionController(context: context, updatedPresentationData: strongSelf.controller?.updatedPresentationData, peer: peer, threadId: threadId, canRemove: canRemove, defaultSound: defaultSound, edit: true, updatePeerSound: { peerId, sound in
let _ = (updatePeerSound(peer.id, sound) let _ = (updatePeerSound(peer.id, sound)
|> deliverOnMainQueue).start(next: { _ in |> deliverOnMainQueue).start(next: { _ in
})
}, updatePeerNotificationInterval: { peerId, muteInterval in
let _ = (updatePeerNotificationInterval(peerId, muteInterval)
|> deliverOnMainQueue).start(next: { _ in
guard let strongSelf = self else {
return
}
if let muteInterval = muteInterval, muteInterval == Int32.max {
let iconColor: UIColor = .white
strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_profilemute", scale: 0.075, colors: [
"Middle.Group 1.Fill 1": iconColor,
"Top.Group 1.Fill 1": iconColor,
"Bottom.Group 1.Fill 1": iconColor,
"EXAMPLE.Group 1.Fill 1": iconColor,
"Line.Group 1.Stroke 1": iconColor
], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipMutedForever, customUndoText: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
}
})
}, updatePeerDisplayPreviews: { peerId, displayPreviews in
let _ = (updatePeerDisplayPreviews(peerId, displayPreviews)
|> deliverOnMainQueue).start(next: { _ in
})
}, removePeerFromExceptions: {
}, modifiedPeer: {
}) })
}, updatePeerNotificationInterval: { peerId, muteInterval in exceptionController.navigationPresentation = .modal
let _ = (updatePeerNotificationInterval(peerId, muteInterval) controller.push(exceptionController)
|> deliverOnMainQueue).start(next: { _ in
guard let strongSelf = self else {
return
}
if let muteInterval = muteInterval, muteInterval == Int32.max {
let iconColor: UIColor = .white
strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_profilemute", scale: 0.075, colors: [
"Middle.Group 1.Fill 1": iconColor,
"Top.Group 1.Fill 1": iconColor,
"Bottom.Group 1.Fill 1": iconColor,
"EXAMPLE.Group 1.Fill 1": iconColor,
"Line.Group 1.Stroke 1": iconColor
], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipMutedForever, customUndoText: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
}
})
}, updatePeerDisplayPreviews: { peerId, displayPreviews in
let _ = (updatePeerDisplayPreviews(peerId, displayPreviews)
|> deliverOnMainQueue).start(next: { _ in
})
}, removePeerFromExceptions: {
}, modifiedPeer: {
}) })
exceptionController.navigationPresentation = .modal
controller.push(exceptionController)
}))) })))
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.PeerInfo_MuteForever, textColor: .destructive, icon: { theme in items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.PeerInfo_MuteForever, textColor: .destructive, icon: { theme in
@ -4155,12 +4168,12 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
guard let self else { guard let self else {
return return
} }
self.controller?.present(threadNotificationExceptionsScreen(context: self.context, peerId: self.peerId, notificationExceptions: self.forumTopicNotificationExceptions, updated: { [weak self] value in self.controller?.push(threadNotificationExceptionsScreen(context: self.context, peerId: self.peerId, notificationExceptions: self.forumTopicNotificationExceptions, updated: { [weak self] value in
guard let self else { guard let self else {
return return
} }
self.forumTopicNotificationExceptions = value self.forumTopicNotificationExceptions = value
}), in: .window(.root)) }))
}) })
} }
@ -7237,7 +7250,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
} }
peerSelectionController.peerSelected = { [weak self, weak peerSelectionController] peer in peerSelectionController.peerSelected = { [weak self, weak peerSelectionController] peer, _ in
let peerId = peer.id let peerId = peer.id
if let strongSelf = self, let _ = peerSelectionController { if let strongSelf = self, let _ = peerSelectionController {

View File

@ -18,11 +18,12 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
private var customTitle: String? private var customTitle: String?
public var peerSelected: ((Peer) -> Void)? public var peerSelected: ((Peer, Int64?) -> Void)?
public var multiplePeersSelected: (([Peer], [PeerId: Peer], NSAttributedString, AttachmentTextInputPanelSendMode, ChatInterfaceForwardOptionsState?) -> Void)? public var multiplePeersSelected: (([Peer], [PeerId: Peer], NSAttributedString, AttachmentTextInputPanelSendMode, ChatInterfaceForwardOptionsState?) -> Void)?
private let filter: ChatListNodePeersFilter private let filter: ChatListNodePeersFilter
private let forumPeerId: EnginePeer.Id?
private let attemptSelection: ((Peer) -> Void)? private let attemptSelection: ((Peer, Int64?) -> Void)?
private let createNewGroup: (() -> Void)? private let createNewGroup: (() -> Void)?
public var inProgress: Bool = false { public var inProgress: Bool = false {
@ -80,6 +81,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
public init(_ params: PeerSelectionControllerParams) { public init(_ params: PeerSelectionControllerParams) {
self.context = params.context self.context = params.context
self.filter = params.filter self.filter = params.filter
self.forumPeerId = params.forumPeerId
self.hasChatListSelector = params.hasChatListSelector self.hasChatListSelector = params.hasChatListSelector
self.hasContactSelector = params.hasContactSelector self.hasContactSelector = params.hasContactSelector
self.hasGlobalSearch = params.hasGlobalSearch self.hasGlobalSearch = params.hasGlobalSearch
@ -153,7 +155,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
} }
override public func loadDisplayNode() { override public func loadDisplayNode() {
self.displayNode = PeerSelectionControllerNode(context: self.context, presentationData: self.presentationData, filter: self.filter, hasChatListSelector: self.hasChatListSelector, hasContactSelector: self.hasContactSelector, hasGlobalSearch: self.hasGlobalSearch, forwardedMessageIds: self.forwardedMessageIds, hasTypeHeaders: self.hasTypeHeaders, createNewGroup: self.createNewGroup, present: { [weak self] c, a in self.displayNode = PeerSelectionControllerNode(context: self.context, presentationData: self.presentationData, filter: self.filter, forumPeerId: self.forumPeerId, hasChatListSelector: self.hasChatListSelector, hasContactSelector: self.hasContactSelector, hasGlobalSearch: self.hasGlobalSearch, forwardedMessageIds: self.forwardedMessageIds, hasTypeHeaders: self.hasTypeHeaders, createNewGroup: self.createNewGroup, present: { [weak self] c, a in
self?.present(c, in: .window(.root), with: a) self?.present(c, in: .window(.root), with: a)
}, presentInGlobalOverlay: { [weak self] c, a in }, presentInGlobalOverlay: { [weak self] c, a in
self?.presentInGlobalOverlay(c, with: a) self?.presentInGlobalOverlay(c, with: a)
@ -175,24 +177,24 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
self?.activateSearch() self?.activateSearch()
} }
self.peerSelectionNode.requestOpenPeer = { [weak self] peer in self.peerSelectionNode.requestOpenPeer = { [weak self] peer, threadId in
if let strongSelf = self, let peerSelected = strongSelf.peerSelected { if let strongSelf = self, let peerSelected = strongSelf.peerSelected {
peerSelected(peer) peerSelected(peer, threadId)
} }
} }
self.peerSelectionNode.requestOpenDisabledPeer = { [weak self] peer in self.peerSelectionNode.requestOpenDisabledPeer = { [weak self] peer, threadId in
if let strongSelf = self { if let strongSelf = self {
strongSelf.attemptSelection?(peer) strongSelf.attemptSelection?(peer, threadId)
} }
} }
self.peerSelectionNode.requestOpenPeerFromSearch = { [weak self] peer in self.peerSelectionNode.requestOpenPeerFromSearch = { [weak self] peer, threadId in
if let strongSelf = self { if let strongSelf = self {
strongSelf.openMessageFromSearchDisposable.set((strongSelf.context.engine.peers.ensurePeerIsLocallyAvailable(peer: EnginePeer(peer)) strongSelf.openMessageFromSearchDisposable.set((strongSelf.context.engine.peers.ensurePeerIsLocallyAvailable(peer: EnginePeer(peer))
|> deliverOnMainQueue).start(completed: { [weak strongSelf] in |> deliverOnMainQueue).start(completed: { [weak strongSelf] in
if let strongSelf = strongSelf, let peerSelected = strongSelf.peerSelected { if let strongSelf = strongSelf, let peerSelected = strongSelf.peerSelected {
peerSelected(peer) peerSelected(peer, threadId)
} }
})) }))
} }

View File

@ -25,6 +25,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
private let presentInGlobalOverlay: (ViewController, Any?) -> Void private let presentInGlobalOverlay: (ViewController, Any?) -> Void
private let dismiss: () -> Void private let dismiss: () -> Void
private let filter: ChatListNodePeersFilter private let filter: ChatListNodePeersFilter
private let forumPeerId: EnginePeer.Id?
private let hasGlobalSearch: Bool private let hasGlobalSearch: Bool
private let forwardedMessageIds: [EngineMessage.Id] private let forwardedMessageIds: [EngineMessage.Id]
private let hasTypeHeaders: Bool private let hasTypeHeaders: Bool
@ -61,9 +62,9 @@ final class PeerSelectionControllerNode: ASDisplayNode {
var requestActivateSearch: (() -> Void)? var requestActivateSearch: (() -> Void)?
var requestDeactivateSearch: (() -> Void)? var requestDeactivateSearch: (() -> Void)?
var requestOpenPeer: ((Peer) -> Void)? var requestOpenPeer: ((Peer, Int64?) -> Void)?
var requestOpenDisabledPeer: ((Peer) -> Void)? var requestOpenDisabledPeer: ((Peer, Int64?) -> Void)?
var requestOpenPeerFromSearch: ((Peer) -> Void)? var requestOpenPeerFromSearch: ((Peer, Int64?) -> Void)?
var requestOpenMessageFromSearch: ((Peer, Int64?, MessageId) -> Void)? var requestOpenMessageFromSearch: ((Peer, Int64?, MessageId) -> Void)?
var requestSend: (([Peer], [PeerId: Peer], NSAttributedString, AttachmentTextInputPanelSendMode, ChatInterfaceForwardOptionsState?) -> Void)? var requestSend: (([Peer], [PeerId: Peer], NSAttributedString, AttachmentTextInputPanelSendMode, ChatInterfaceForwardOptionsState?) -> Void)?
@ -86,12 +87,13 @@ final class PeerSelectionControllerNode: ASDisplayNode {
return (self.presentationData, self.presentationDataPromise.get()) return (self.presentationData, self.presentationDataPromise.get())
} }
init(context: AccountContext, presentationData: PresentationData, filter: ChatListNodePeersFilter, hasChatListSelector: Bool, hasContactSelector: Bool, hasGlobalSearch: Bool, forwardedMessageIds: [EngineMessage.Id], hasTypeHeaders: Bool, createNewGroup: (() -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, dismiss: @escaping () -> Void) { init(context: AccountContext, presentationData: PresentationData, filter: ChatListNodePeersFilter, forumPeerId: EnginePeer.Id?, hasChatListSelector: Bool, hasContactSelector: Bool, hasGlobalSearch: Bool, forwardedMessageIds: [EngineMessage.Id], hasTypeHeaders: Bool, createNewGroup: (() -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, dismiss: @escaping () -> Void) {
self.context = context self.context = context
self.present = present self.present = present
self.presentInGlobalOverlay = presentInGlobalOverlay self.presentInGlobalOverlay = presentInGlobalOverlay
self.dismiss = dismiss self.dismiss = dismiss
self.filter = filter self.filter = filter
self.forumPeerId = forumPeerId
self.hasGlobalSearch = hasGlobalSearch self.hasGlobalSearch = hasGlobalSearch
self.forwardedMessageIds = forwardedMessageIds self.forwardedMessageIds = forwardedMessageIds
self.hasTypeHeaders = hasTypeHeaders self.hasTypeHeaders = hasTypeHeaders
@ -127,8 +129,15 @@ final class PeerSelectionControllerNode: ASDisplayNode {
if let _ = createNewGroup { if let _ = createNewGroup {
chatListCategories.append(ChatListNodeAdditionalCategory(id: 0, icon: PresentationResourcesItemList.createGroupIcon(self.presentationData.theme), title: self.presentationData.strings.PeerSelection_ImportIntoNewGroup, appearance: .action)) chatListCategories.append(ChatListNodeAdditionalCategory(id: 0, icon: PresentationResourcesItemList.createGroupIcon(self.presentationData.theme), title: self.presentationData.strings.PeerSelection_ImportIntoNewGroup, appearance: .action))
} }
let chatListLocation: ChatListControllerLocation
if let forumPeerId = self.forumPeerId {
chatListLocation = .forum(peerId: forumPeerId)
} else {
chatListLocation = .chatList(groupId: .root)
}
self.chatListNode = ChatListNode(context: context, location: .chatList(groupId: .root), previewing: false, fillPreloadItems: false, mode: .peers(filter: filter, isSelecting: false, additionalCategories: chatListCategories, chatListFilters: nil), theme: self.presentationData.theme, fontSize: presentationData.listsFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameSortOrder: presentationData.nameSortOrder, nameDisplayOrder: presentationData.nameDisplayOrder, animationCache: self.animationCache, animationRenderer: self.animationRenderer, disableAnimations: true) self.chatListNode = ChatListNode(context: context, location: chatListLocation, previewing: false, fillPreloadItems: false, mode: .peers(filter: filter, isSelecting: false, additionalCategories: chatListCategories, chatListFilters: nil), theme: self.presentationData.theme, fontSize: presentationData.listsFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameSortOrder: presentationData.nameSortOrder, nameDisplayOrder: presentationData.nameDisplayOrder, animationCache: self.animationCache, animationRenderer: self.animationRenderer, disableAnimations: true)
super.init() super.init()
@ -153,13 +162,13 @@ final class PeerSelectionControllerNode: ASDisplayNode {
self?.requestActivateSearch?() self?.requestActivateSearch?()
} }
self.chatListNode.peerSelected = { [weak self] peer, _, _, _, _ in self.chatListNode.peerSelected = { [weak self] peer, threadId, _, _, _ in
self?.chatListNode.clearHighlightAnimated(true) self?.chatListNode.clearHighlightAnimated(true)
self?.requestOpenPeer?(peer._asPeer()) self?.requestOpenPeer?(peer._asPeer(), threadId)
} }
self.chatListNode.disabledPeerSelected = { [weak self] peer in self.chatListNode.disabledPeerSelected = { [weak self] peer, threadId in
self?.requestOpenDisabledPeer?(peer._asPeer()) self?.requestOpenDisabledPeer?(peer._asPeer(), threadId)
} }
self.chatListNode.contentOffsetChanged = { [weak self] offset in self.chatListNode.contentOffsetChanged = { [weak self] offset in
@ -589,6 +598,13 @@ final class PeerSelectionControllerNode: ASDisplayNode {
} }
if self.chatListNode.supernode != nil { if self.chatListNode.supernode != nil {
let chatListLocation: ChatListControllerLocation
if let forumPeerId = self.forumPeerId {
chatListLocation = .forum(peerId: forumPeerId)
} else {
chatListLocation = .chatList(groupId: EngineChatList.Group(.root))
}
self.searchDisplayController = SearchDisplayController( self.searchDisplayController = SearchDisplayController(
presentationData: self.presentationData, presentationData: self.presentationData,
contentNode: ChatListSearchContainerNode( contentNode: ChatListSearchContainerNode(
@ -597,10 +613,10 @@ final class PeerSelectionControllerNode: ASDisplayNode {
animationRenderer: self.animationRenderer, animationRenderer: self.animationRenderer,
updatedPresentationData: self.updatedPresentationData, updatedPresentationData: self.updatedPresentationData,
filter: self.filter, filter: self.filter,
location: .chatList(groupId: EngineChatList.Group(.root)), location: chatListLocation,
displaySearchFilters: false, displaySearchFilters: false,
hasDownloads: false, hasDownloads: false,
openPeer: { [weak self] peer, chatPeer, _, _ in openPeer: { [weak self] peer, chatPeer, threadId, _ in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
@ -643,11 +659,11 @@ final class PeerSelectionControllerNode: ASDisplayNode {
strongSelf.textInputPanelNode?.updateSendButtonEnabled(count > 0, animated: true) strongSelf.textInputPanelNode?.updateSendButtonEnabled(count > 0, animated: true)
strongSelf.requestDeactivateSearch?() strongSelf.requestDeactivateSearch?()
} else if let requestOpenPeerFromSearch = strongSelf.requestOpenPeerFromSearch { } else if let requestOpenPeerFromSearch = strongSelf.requestOpenPeerFromSearch {
requestOpenPeerFromSearch(peer._asPeer()) requestOpenPeerFromSearch(peer._asPeer(), threadId)
} }
}, },
openDisabledPeer: { [weak self] peer in openDisabledPeer: { [weak self] peer, threadId in
self?.requestOpenDisabledPeer?(peer._asPeer()) self?.requestOpenDisabledPeer?(peer._asPeer(), threadId)
}, },
openRecentPeerOptions: { _ in openRecentPeerOptions: { _ in
}, },
@ -724,7 +740,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peer.id)) let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peer.id))
|> deliverOnMainQueue).start(next: { peer in |> deliverOnMainQueue).start(next: { peer in
if let strongSelf = self, let peer = peer { if let strongSelf = self, let peer = peer {
strongSelf.requestOpenPeerFromSearch?(peer._asPeer()) strongSelf.requestOpenPeerFromSearch?(peer._asPeer(), nil)
} }
}) })
case .deviceContact: case .deviceContact:
@ -796,7 +812,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
contactListNode.openPeer = { [weak self] peer, _ in contactListNode.openPeer = { [weak self] peer, _ in
if case let .peer(peer, _, _) = peer { if case let .peer(peer, _, _) = peer {
self?.contactListNode?.listNode.clearHighlightAnimated(true) self?.contactListNode?.listNode.clearHighlightAnimated(true)
self?.requestOpenPeer?(peer) self?.requestOpenPeer?(peer, nil)
} }
} }
contactListNode.suppressPermissionWarning = { [weak self] in contactListNode.suppressPermissionWarning = { [weak self] in

View File

@ -659,7 +659,7 @@ public class ShareRootControllerImpl {
case let .group(groupTitle): case let .group(groupTitle):
var attemptSelectionImpl: ((Peer) -> Void)? var attemptSelectionImpl: ((Peer) -> Void)?
var createNewGroupImpl: (() -> Void)? var createNewGroupImpl: (() -> Void)?
let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyGroups, .onlyManageable, .excludeDisabled, .doNotSearchMessages], hasContactSelector: false, hasGlobalSearch: false, title: presentationData.strings.ChatImport_Title, attemptSelection: { peer in let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyGroups, .onlyManageable, .excludeDisabled, .doNotSearchMessages], hasContactSelector: false, hasGlobalSearch: false, title: presentationData.strings.ChatImport_Title, attemptSelection: { peer, _ in
attemptSelectionImpl?(peer) attemptSelectionImpl?(peer)
}, createNewGroup: { }, createNewGroup: {
createNewGroupImpl?() createNewGroupImpl?()
@ -669,7 +669,7 @@ public class ShareRootControllerImpl {
self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil) self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil)
} }
controller.peerSelected = { peer in controller.peerSelected = { peer, _ in
attemptSelectionImpl?(peer) attemptSelectionImpl?(peer)
} }
@ -835,7 +835,7 @@ public class ShareRootControllerImpl {
let presentationData = internalContext.sharedContext.currentPresentationData.with { $0 } let presentationData = internalContext.sharedContext.currentPresentationData.with { $0 }
var attemptSelectionImpl: ((Peer) -> Void)? var attemptSelectionImpl: ((Peer) -> Void)?
let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyPrivateChats, .excludeDisabled, .doNotSearchMessages, .excludeSecretChats], hasChatListSelector: false, hasContactSelector: true, hasGlobalSearch: false, title: presentationData.strings.ChatImport_Title, attemptSelection: { peer in let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyPrivateChats, .excludeDisabled, .doNotSearchMessages, .excludeSecretChats], hasChatListSelector: false, hasContactSelector: true, hasGlobalSearch: false, title: presentationData.strings.ChatImport_Title, attemptSelection: { peer, _ in
attemptSelectionImpl?(peer) attemptSelectionImpl?(peer)
}, pretendPresentedInModal: true)) }, pretendPresentedInModal: true))
@ -843,7 +843,7 @@ public class ShareRootControllerImpl {
self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil) self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil)
} }
controller.peerSelected = { peer in controller.peerSelected = { peer, _ in
attemptSelectionImpl?(peer) attemptSelectionImpl?(peer)
} }
@ -908,7 +908,7 @@ public class ShareRootControllerImpl {
case let .unknown(peerTitle): case let .unknown(peerTitle):
var attemptSelectionImpl: ((Peer) -> Void)? var attemptSelectionImpl: ((Peer) -> Void)?
var createNewGroupImpl: (() -> Void)? var createNewGroupImpl: (() -> Void)?
let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.excludeDisabled, .doNotSearchMessages], hasContactSelector: true, hasGlobalSearch: false, title: presentationData.strings.ChatImport_Title, attemptSelection: { peer in let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.excludeDisabled, .doNotSearchMessages], hasContactSelector: true, hasGlobalSearch: false, title: presentationData.strings.ChatImport_Title, attemptSelection: { peer, _ in
attemptSelectionImpl?(peer) attemptSelectionImpl?(peer)
}, createNewGroup: { }, createNewGroup: {
createNewGroupImpl?() createNewGroupImpl?()
@ -918,7 +918,7 @@ public class ShareRootControllerImpl {
self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil) self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil)
} }
controller.peerSelected = { peer in controller.peerSelected = { peer, _ in
attemptSelectionImpl?(peer) attemptSelectionImpl?(peer)
} }