mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
[WIP] Topics
This commit is contained in:
parent
14772a6c67
commit
02b28ee6fc
@ -41,21 +41,23 @@ public final class PeerSelectionControllerParams {
|
||||
public let context: AccountContext
|
||||
public let updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?
|
||||
public let filter: ChatListNodePeersFilter
|
||||
public let forumPeerId: EnginePeer.Id?
|
||||
public let hasChatListSelector: Bool
|
||||
public let hasContactSelector: Bool
|
||||
public let hasGlobalSearch: Bool
|
||||
public let title: String?
|
||||
public let attemptSelection: ((Peer) -> Void)?
|
||||
public let attemptSelection: ((Peer, Int64?) -> Void)?
|
||||
public let createNewGroup: (() -> Void)?
|
||||
public let pretendPresentedInModal: Bool
|
||||
public let multipleSelection: Bool
|
||||
public let forwardedMessageIds: [EngineMessage.Id]
|
||||
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.updatedPresentationData = updatedPresentationData
|
||||
self.filter = filter
|
||||
self.forumPeerId = forumPeerId
|
||||
self.hasChatListSelector = hasChatListSelector
|
||||
self.hasContactSelector = hasContactSelector
|
||||
self.hasGlobalSearch = hasGlobalSearch
|
||||
@ -76,7 +78,7 @@ public enum AttachmentTextInputPanelSendMode {
|
||||
}
|
||||
|
||||
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 inProgress: Bool { get set }
|
||||
var customDismiss: (() -> Void)? { get set }
|
||||
|
@ -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 timestamp1: Int32 = 100000
|
||||
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
|
||||
gesture?.cancel()
|
||||
}, 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
|
||||
self?.requestOpenPeerFromSearch?(peer, threadId, dismissSearch)
|
||||
}, openDisabledPeer: { _ in
|
||||
}, openDisabledPeer: { _, _ in
|
||||
}, openRecentPeerOptions: { [weak self] peer in
|
||||
self?.requestOpenRecentPeerOptions?(peer)
|
||||
}, openMessage: { [weak self] peer, threadId, messageId, deactivateOnAction in
|
||||
|
@ -44,7 +44,7 @@ private enum ChatListTokenId: Int32 {
|
||||
|
||||
final class ChatListSearchInteraction {
|
||||
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 openUrl: (String) -> Void
|
||||
let clearRecentSearch: () -> Void
|
||||
@ -57,7 +57,7 @@ final class ChatListSearchInteraction {
|
||||
let dismissInput: () -> Void
|
||||
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.openDisabledPeer = openDisabledPeer
|
||||
self.openMessage = openMessage
|
||||
@ -133,7 +133,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
|
||||
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.peersFilter = filter
|
||||
self.location = location
|
||||
@ -164,8 +164,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
if peer.id.namespace != Namespaces.Peer.SecretChat {
|
||||
addAppLogEvent(postbox: context.account.postbox, type: "search_global_open_peer", peerId: peer.id)
|
||||
}
|
||||
}, openDisabledPeer: { peer in
|
||||
openDisabledPeer(peer)
|
||||
}, openDisabledPeer: { peer, threadId in
|
||||
openDisabledPeer(peer, threadId)
|
||||
}, openMessage: { peer, threadId, messageId, deactivateOnAction in
|
||||
originalOpenMessage(peer, threadId, messageId, deactivateOnAction)
|
||||
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)
|
||||
}
|
||||
peerSelectionController.peerSelected = { [weak self, weak peerSelectionController] peer in
|
||||
peerSelectionController.peerSelected = { [weak self, weak peerSelectionController] peer, _ in
|
||||
let peerId = peer.id
|
||||
if let strongSelf = self, let _ = peerSelectionController {
|
||||
if peerId == strongSelf.context.account.peerId {
|
||||
|
@ -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 {
|
||||
case let .topPeers(peers, theme, strings):
|
||||
return ChatListRecentPeersListItem(theme: theme, strings: strings, context: context, peers: peers, peerSelected: { peer in
|
||||
peerSelected(peer)
|
||||
peerSelected(peer, nil)
|
||||
}, peerContextAction: { peer, node, gesture, location in
|
||||
if let peerContextAction = peerContextAction {
|
||||
peerContextAction(peer, .recentPeers, node, gesture, location)
|
||||
@ -213,12 +213,12 @@ private enum ChatListRecentEntry: Comparable, Identifiable {
|
||||
}),
|
||||
action: { _ in
|
||||
if let chatPeer = peer.peer.peers[peer.peer.peerId] {
|
||||
peerSelected(EnginePeer(chatPeer))
|
||||
peerSelected(EnginePeer(chatPeer), nil)
|
||||
}
|
||||
},
|
||||
disabledAction: { _ in
|
||||
if let chatPeer = peer.peer.peers[peer.peer.peerId] {
|
||||
disabledPeerSelected(EnginePeer(chatPeer))
|
||||
disabledPeerSelected(EnginePeer(chatPeer), nil)
|
||||
}
|
||||
},
|
||||
deletePeer: deletePeer,
|
||||
@ -477,7 +477,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
|
||||
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)
|
||||
}, contextAction: nil, animationCache: interaction.animationCache, animationRenderer: interaction.animationRenderer)
|
||||
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 deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
||||
@ -1819,8 +1819,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
interaction.openPeer(peer, chatPeer, threadId, false)
|
||||
let _ = context.engine.peers.addRecentlySearchedPeer(peerId: peer.id).start()
|
||||
self?.listNode.clearHighlightAnimated(true)
|
||||
}, disabledPeerSelected: { _ in
|
||||
}, togglePeerSelected: { _ in
|
||||
}, disabledPeerSelected: { _, _ in
|
||||
}, togglePeerSelected: { _, _ in
|
||||
}, togglePeersSelection: { _, _ in
|
||||
}, additionalCategorySelected: { _ in
|
||||
}, messageSelected: { [weak self] peer, threadId, message, _ in
|
||||
@ -2162,12 +2162,14 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
let previousEntries = previousRecentItems.swap(entries)
|
||||
|
||||
let firstTime = previousEntries == nil
|
||||
let transition = chatListSearchContainerPreparedRecentTransition(from: previousEntries ?? [], to: entries, context: context, presentationData: presentationData, filter: peersFilter, peerSelected: { peer in
|
||||
interaction.openPeer(peer, nil, nil, true)
|
||||
let _ = context.engine.peers.addRecentlySearchedPeer(peerId: peer.id).start()
|
||||
let transition = chatListSearchContainerPreparedRecentTransition(from: previousEntries ?? [], to: entries, context: context, presentationData: presentationData, filter: peersFilter, peerSelected: { peer, threadId in
|
||||
interaction.openPeer(peer, nil, threadId, true)
|
||||
if threadId == nil {
|
||||
let _ = context.engine.peers.addRecentlySearchedPeer(peerId: peer.id).start()
|
||||
}
|
||||
self?.recentListNode.clearHighlightAnimated(true)
|
||||
}, disabledPeerSelected: { peer in
|
||||
interaction.openDisabledPeer(peer)
|
||||
}, disabledPeerSelected: { peer, threadId in
|
||||
interaction.openDisabledPeer(peer, threadId)
|
||||
}, peerContextAction: { peer, source, node, gesture, location in
|
||||
if let peerContextAction = interaction.peerContextAction {
|
||||
peerContextAction(peer, source, node, gesture, location)
|
||||
@ -3050,7 +3052,7 @@ private final class ChatListSearchShimmerNode: ASDisplayNode {
|
||||
let timestamp1: Int32 = 100000
|
||||
var peers: [EnginePeer.Id: EnginePeer] = [:]
|
||||
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
|
||||
gesture?.cancel()
|
||||
}, present: { _ in })
|
||||
|
@ -125,7 +125,7 @@ private final class ChatListSearchPendingPane {
|
||||
key: ChatListSearchPaneKey,
|
||||
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.disposable = (paneNode.isReady
|
||||
|
@ -1029,7 +1029,14 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
}
|
||||
if case let .peer(_, peer, _, _, _, _, _, _, _, _, promoInfo, _, _, _, _) = item.content {
|
||||
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
|
||||
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 {
|
||||
avatarIconContent = .topic(title: String(threadInfo.info.title.prefix(1)), colorIndex: Int(clamping: abs(threadInfo.id)), size: CGSize(width: 32.0, height: 32.0))
|
||||
}
|
||||
|
@ -57,8 +57,8 @@ public final class ChatListNodeInteraction {
|
||||
|
||||
let activateSearch: () -> Void
|
||||
let peerSelected: (EnginePeer, EnginePeer?, Int64?, ChatListNodeEntryPromoInfo?) -> Void
|
||||
let disabledPeerSelected: (EnginePeer) -> Void
|
||||
let togglePeerSelected: (EnginePeer) -> Void
|
||||
let disabledPeerSelected: (EnginePeer, Int64?) -> Void
|
||||
let togglePeerSelected: (EnginePeer, Int64?) -> Void
|
||||
let togglePeersSelection: ([PeerEntry], Bool) -> Void
|
||||
let additionalCategorySelected: (Int) -> Void
|
||||
let messageSelected: (EnginePeer, Int64?, EngineMessage, ChatListNodeEntryPromoInfo?) -> Void
|
||||
@ -91,8 +91,8 @@ public final class ChatListNodeInteraction {
|
||||
animationRenderer: MultiAnimationRenderer,
|
||||
activateSearch: @escaping () -> Void,
|
||||
peerSelected: @escaping (EnginePeer, EnginePeer?, Int64?, ChatListNodeEntryPromoInfo?) -> Void,
|
||||
disabledPeerSelected: @escaping (EnginePeer) -> Void,
|
||||
togglePeerSelected: @escaping (EnginePeer) -> Void,
|
||||
disabledPeerSelected: @escaping (EnginePeer, Int64?) -> Void,
|
||||
togglePeerSelected: @escaping (EnginePeer, Int64?) -> Void,
|
||||
togglePeersSelection: @escaping ([PeerEntry], Bool) -> Void,
|
||||
additionalCategorySelected: @escaping (Int) -> Void,
|
||||
messageSelected: @escaping (EnginePeer, Int64?, EngineMessage, ChatListNodeEntryPromoInfo?) -> Void,
|
||||
@ -381,6 +381,21 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
|
||||
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(
|
||||
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,
|
||||
context: context,
|
||||
peerMode: .generalSearch,
|
||||
peer: .peer(peer: itemPeer, chatPeer: chatPeer),
|
||||
peer: peerContent,
|
||||
status: status,
|
||||
enabled: enabled,
|
||||
selection: editing ? .selectable(selected: selected) : .none,
|
||||
@ -398,14 +413,14 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
|
||||
action: { _ in
|
||||
if let chatPeer = chatPeer {
|
||||
if editing {
|
||||
nodeInteraction.togglePeerSelected(chatPeer)
|
||||
nodeInteraction.togglePeerSelected(chatPeer, threadId)
|
||||
} else {
|
||||
nodeInteraction.peerSelected(chatPeer, nil, nil, nil)
|
||||
nodeInteraction.peerSelected(chatPeer, nil, threadId, nil)
|
||||
}
|
||||
}
|
||||
}, disabledAction: { _ in
|
||||
if let chatPeer = chatPeer {
|
||||
nodeInteraction.disabledPeerSelected(chatPeer)
|
||||
nodeInteraction.disabledPeerSelected(chatPeer, threadId)
|
||||
}
|
||||
},
|
||||
animationCache: nodeInteraction.animationCache,
|
||||
@ -525,6 +540,21 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
|
||||
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(
|
||||
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,
|
||||
context: context,
|
||||
peerMode: .generalSearch,
|
||||
peer: .peer(peer: itemPeer, chatPeer: chatPeer),
|
||||
peer: peerContent,
|
||||
status: status,
|
||||
enabled: enabled,
|
||||
selection: editing ? .selectable(selected: selected) : .none,
|
||||
@ -542,14 +572,14 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
|
||||
action: { _ in
|
||||
if let chatPeer = chatPeer {
|
||||
if editing {
|
||||
nodeInteraction.togglePeerSelected(chatPeer)
|
||||
nodeInteraction.togglePeerSelected(chatPeer, threadId)
|
||||
} else {
|
||||
nodeInteraction.peerSelected(chatPeer, nil, nil, nil)
|
||||
nodeInteraction.peerSelected(chatPeer, nil, threadId, nil)
|
||||
}
|
||||
}
|
||||
}, disabledAction: { _ in
|
||||
if let chatPeer = chatPeer {
|
||||
nodeInteraction.disabledPeerSelected(chatPeer)
|
||||
nodeInteraction.disabledPeerSelected(chatPeer, threadId)
|
||||
}
|
||||
},
|
||||
animationCache: nodeInteraction.animationCache,
|
||||
@ -661,7 +691,7 @@ public final class ChatListNode: ListView {
|
||||
}
|
||||
|
||||
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 groupSelected: ((EngineChatList.Group) -> Void)?
|
||||
public var addContact: ((String) -> Void)?
|
||||
@ -799,11 +829,11 @@ public final class ChatListNode: ListView {
|
||||
if let strongSelf = self, let peerSelected = strongSelf.peerSelected {
|
||||
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 {
|
||||
disabledPeerSelected(peer)
|
||||
disabledPeerSelected(peer, threadId)
|
||||
}
|
||||
}, togglePeerSelected: { [weak self] peer in
|
||||
}, togglePeerSelected: { [weak self] peer, _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
@ -1051,7 +1051,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
|
||||
|
||||
let avatarIconContent: EmojiStatusComponent.Content
|
||||
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 {
|
||||
avatarIconContent = .topic(title: String(title.prefix(1)), colorIndex: Int(clamping: abs(color)), size: CGSize(width: 32.0, height: 32.0))
|
||||
}
|
||||
|
@ -344,7 +344,8 @@ private final class InnerActionsContainerNode: ASDisplayNode {
|
||||
|
||||
final class InnerTextSelectionTipContainerNode: ASDisplayNode {
|
||||
private let presentationData: PresentationData
|
||||
private let shadowNode: ASImageNode
|
||||
let shadowNode: ASImageNode
|
||||
private let backgroundNode: ASDisplayNode
|
||||
private var effectView: UIVisualEffectView?
|
||||
private let highlightBackgroundNode: ASDisplayNode
|
||||
private let buttonNode: HighlightTrackingButtonNode
|
||||
@ -376,6 +377,8 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
|
||||
self.shadowNode.contentMode = .scaleToFill
|
||||
self.shadowNode.isHidden = true
|
||||
|
||||
self.backgroundNode = ASDisplayNode()
|
||||
|
||||
self.highlightBackgroundNode = ASDisplayNode()
|
||||
self.highlightBackgroundNode.isAccessibilityElement = false
|
||||
self.highlightBackgroundNode.alpha = 0.0
|
||||
@ -436,10 +439,12 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
|
||||
|
||||
super.init()
|
||||
|
||||
self.clipsToBounds = true
|
||||
self.cornerRadius = 14.0
|
||||
self.backgroundNode.backgroundColor = presentationData.theme.contextMenu.backgroundColor
|
||||
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
|
||||
}, present: { _, _ in
|
||||
@ -447,6 +452,7 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
|
||||
})
|
||||
self.textSelectionNode = textSelectionNode
|
||||
|
||||
self.addSubnode(self.backgroundNode)
|
||||
self.addSubnode(self.highlightBackgroundNode)
|
||||
self.addSubnode(self.textNode.textNode)
|
||||
self.addSubnode(self.iconNode)
|
||||
@ -514,20 +520,19 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
func updateLayout(widthClass: ContainerViewLayoutSizeClass, presentation: ContextControllerActionsStackNode.Presentation, width: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||
switch presentation {
|
||||
case .inline:
|
||||
self.shadowNode.isHidden = true
|
||||
case .modal:
|
||||
self.shadowNode.isHidden = false
|
||||
var needsBlur = false
|
||||
if case .regular = widthClass {
|
||||
needsBlur = true
|
||||
} else if case .inline = presentation {
|
||||
needsBlur = true
|
||||
}
|
||||
|
||||
switch widthClass {
|
||||
case .compact:
|
||||
if !needsBlur {
|
||||
if let effectView = self.effectView {
|
||||
self.effectView = nil
|
||||
effectView.removeFromSuperview()
|
||||
}
|
||||
case .regular:
|
||||
} else {
|
||||
if self.effectView == nil {
|
||||
let effectView: UIVisualEffectView
|
||||
if #available(iOS 13.0, *) {
|
||||
@ -541,6 +546,8 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
|
||||
} else {
|
||||
effectView = UIVisualEffectView(effect: UIBlurEffect(style: .light))
|
||||
}
|
||||
effectView.clipsToBounds = true
|
||||
effectView.layer.cornerRadius = self.backgroundNode.cornerRadius
|
||||
self.effectView = effectView
|
||||
self.view.insertSubview(effectView, at: 0)
|
||||
}
|
||||
@ -618,6 +625,13 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
|
||||
textSelectionNode.highlightAreaNode.frame = textFrame
|
||||
}
|
||||
|
||||
switch presentation {
|
||||
case .modal:
|
||||
self.shadowNode.isHidden = true
|
||||
case .inline:
|
||||
self.shadowNode.isHidden = false
|
||||
}
|
||||
|
||||
if let effectView = self.effectView {
|
||||
transition.updateFrame(view: effectView, frame: CGRect(origin: CGPoint(), size: size))
|
||||
}
|
||||
@ -628,11 +642,9 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
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.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) {
|
||||
|
@ -1321,22 +1321,32 @@ final class ContextControllerActionsStackNode: ASDisplayNode {
|
||||
let tipTransition = transition
|
||||
var animateTipIn = false
|
||||
if tip.tipNode.supernode == nil {
|
||||
self.insertSubnode(tip.tipNode.shadowNode, at: 0)
|
||||
self.addSubnode(tip.tipNode)
|
||||
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))
|
||||
tip.tipNode.setActualSize(size: tip.tipNode.bounds.size, transition: .immediate)
|
||||
let tipFrame = CGRect(origin: CGPoint(x: previousNavigationContainerFrame.minX, y: previousNavigationContainerFrame.maxY + tipSpacing), size: CGSize(width: itemLayouts[i].size.width, height: tip.tipHeight))
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
if animateTipIn {
|
||||
tip.tipNode.alpha = tipAlpha
|
||||
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 {
|
||||
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 {
|
||||
@ -1356,10 +1366,17 @@ final class ContextControllerActionsStackNode: ASDisplayNode {
|
||||
itemContainer?.removeFromSupernode()
|
||||
})
|
||||
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
|
||||
tipNode?.removeFromSupernode()
|
||||
})
|
||||
let shadowNode = tipNode.shadowNode
|
||||
transition.updateAlpha(node: shadowNode, alpha: 0.0, completion: { [weak shadowNode] _ in
|
||||
shadowNode?.removeFromSupernode()
|
||||
})
|
||||
}
|
||||
}
|
||||
self.dismissingItemContainers.removeAll()
|
||||
|
@ -251,7 +251,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
actionSheet?.dismissAnimated()
|
||||
|
||||
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
|
||||
|
||||
if let strongController = controller {
|
||||
@ -335,7 +335,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
actionSheet?.dismissAnimated()
|
||||
|
||||
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
|
||||
|
||||
if let strongController = controller {
|
||||
@ -417,7 +417,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
actionSheet?.dismissAnimated()
|
||||
|
||||
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
|
||||
|
||||
if let strongController = controller {
|
||||
@ -501,7 +501,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
actionSheet?.dismissAnimated()
|
||||
|
||||
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
|
||||
|
||||
if let strongController = controller {
|
||||
@ -586,7 +586,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
actionSheet?.dismissAnimated()
|
||||
|
||||
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
|
||||
|
||||
if let strongController = controller {
|
||||
@ -670,7 +670,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
actionSheet?.dismissAnimated()
|
||||
|
||||
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
|
||||
|
||||
if let strongController = controller {
|
||||
@ -743,7 +743,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
actionSheet?.dismissAnimated()
|
||||
|
||||
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
|
||||
|
||||
if let strongController = controller {
|
||||
@ -1345,7 +1345,7 @@ public func triggerDebugSendLogsUI(context: AccountContext, additionalInfo: Stri
|
||||
let _ = (Logger.shared.collectLogs()
|
||||
|> deliverOnMainQueue).start(next: { logs in
|
||||
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
|
||||
|
||||
if let strongController = controller {
|
||||
|
@ -58,8 +58,8 @@ public final class HashtagSearchController: TelegramBaseController {
|
||||
}
|
||||
let interaction = ChatListNodeInteraction(context: context, animationCache: self.animationCache, animationRenderer: self.animationRenderer, activateSearch: {
|
||||
}, peerSelected: { _, _, _, _ in
|
||||
}, disabledPeerSelected: { _ in
|
||||
}, togglePeerSelected: { _ in
|
||||
}, disabledPeerSelected: { _, _ in
|
||||
}, togglePeerSelected: { _, _ in
|
||||
}, togglePeersSelection: { _, _ in
|
||||
}, additionalCategorySelected: { _ in
|
||||
}, messageSelected: { [weak self] peer, _, message, _ in
|
||||
|
@ -14,6 +14,7 @@ swift_library(
|
||||
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
|
||||
"//submodules/Display:Display",
|
||||
"//submodules/TelegramCore:TelegramCore",
|
||||
"//submodules/Postbox:Postbox",
|
||||
"//submodules/AvatarNode:AvatarNode",
|
||||
"//submodules/TelegramPresentationData:TelegramPresentationData",
|
||||
"//submodules/TelegramStringFormatting:TelegramStringFormatting",
|
||||
|
@ -4,6 +4,7 @@ import Display
|
||||
import AsyncDisplayKit
|
||||
import SwiftSignalKit
|
||||
import TelegramCore
|
||||
import Postbox
|
||||
import TelegramPresentationData
|
||||
import TelegramUIPreferences
|
||||
import ItemListUI
|
||||
@ -319,6 +320,7 @@ public final class ItemListPeerItem: ListViewItem, ItemListItem {
|
||||
let nameDisplayOrder: PresentationPersonNameOrder
|
||||
let context: AccountContext
|
||||
let peer: EnginePeer
|
||||
let threadInfo: EngineMessageHistoryThread.Info?
|
||||
let height: ItemListPeerItemHeight
|
||||
let aliasHandling: ItemListPeerItemAliasHandling
|
||||
let nameColor: ItemListPeerItemNameColor
|
||||
@ -348,12 +350,13 @@ public final class ItemListPeerItem: ListViewItem, ItemListItem {
|
||||
let displayDecorations: 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.dateTimeFormat = dateTimeFormat
|
||||
self.nameDisplayOrder = nameDisplayOrder
|
||||
self.context = context
|
||||
self.peer = peer
|
||||
self.threadInfo = threadInfo
|
||||
self.height = height
|
||||
self.aliasHandling = aliasHandling
|
||||
self.nameColor = nameColor
|
||||
@ -457,6 +460,9 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
||||
}
|
||||
|
||||
fileprivate let avatarNode: AvatarNode
|
||||
private var avatarIconComponent: EmojiStatusComponent?
|
||||
private var avatarIconView: ComponentView<Empty>?
|
||||
|
||||
private let titleNode: TextNode
|
||||
private let labelNode: TextNode
|
||||
private let labelBadgeNode: ASImageNode
|
||||
@ -503,6 +509,14 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
} else if item.peer.id.isReplies {
|
||||
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))
|
||||
transition.updateFrame(node: strongSelf.avatarNode, frame: avatarFrame)
|
||||
|
||||
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
|
||||
if let threadInfo = item.threadInfo {
|
||||
let threadIconSize = floor(avatarSize * 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))
|
||||
|
||||
strongSelf.avatarNode.isHidden = true
|
||||
|
||||
let avatarIconView: ComponentView<Empty>
|
||||
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))
|
||||
@ -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.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() {
|
||||
|
@ -803,8 +803,11 @@ final class NotificationExceptionsControllerNode: ViewControllerTracingNode {
|
||||
let presentPeerSettings: (PeerId, @escaping () -> Void) -> Void = { [weak self] peerId, completion in
|
||||
(self?.searchDisplayController?.contentNode as? NotificationExceptionsSearchContainerNode)?.listNode.clearHighlightAnimated(true)
|
||||
|
||||
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> deliverOnMainQueue).start(next: { peer 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 {
|
||||
@ -814,7 +817,20 @@ final class NotificationExceptionsControllerNode: ViewControllerTracingNode {
|
||||
let mode = stateValue.with { $0.mode }
|
||||
|
||||
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
|
||||
updateNotificationsDisposable.set(nil)
|
||||
_ = 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)
|
||||
}
|
||||
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
|
||||
|
||||
presentPeerSettings(peerId, {
|
||||
|
@ -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 statePromise = Promise(initialState)
|
||||
let stateValue = Atomic(value: initialState)
|
||||
@ -426,16 +426,8 @@ public func notificationPeerExceptionController(context: AccountContext, updated
|
||||
|> map { peerNotificationSettings, threadNotificationSettings, globalNotificationSettings -> NotificationExceptionPeerState in
|
||||
let effectiveSettings = threadNotificationSettings ?? peerNotificationSettings
|
||||
|
||||
var state = NotificationExceptionPeerState(canRemove: mode.peerIds.contains(peer.id), notifications: effectiveSettings._asNotificationSettings())
|
||||
let globalSettings = globalNotificationSettings
|
||||
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()
|
||||
}
|
||||
var state = NotificationExceptionPeerState(canRemove: canRemove, notifications: effectiveSettings._asNotificationSettings())
|
||||
state.defaultSound = defaultSound
|
||||
let _ = stateValue.swap(state)
|
||||
return state
|
||||
})
|
||||
@ -467,7 +459,14 @@ public func notificationPeerExceptionController(context: AccountContext, updated
|
||||
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)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
|
@ -493,8 +493,11 @@ public func notificationsPeerCategoryController(context: AccountContext, categor
|
||||
updateNotificationsView({})
|
||||
|
||||
let presentPeerSettings: (PeerId, @escaping () -> Void) -> Void = { peerId, completion in
|
||||
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> deliverOnMainQueue).start(next: { peer 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 {
|
||||
@ -502,7 +505,20 @@ public func notificationsPeerCategoryController(context: AccountContext, categor
|
||||
}
|
||||
|
||||
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
|
||||
updateNotificationsDisposable.set(nil)
|
||||
_ = 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)
|
||||
}
|
||||
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
|
||||
|
||||
presentPeerSettings(peerId, {
|
||||
|
@ -228,7 +228,7 @@ public func blockedPeersController(context: AccountContext, blockedPeersContext:
|
||||
}, addPeer: {
|
||||
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))
|
||||
controller.peerSelected = { [weak controller] peer in
|
||||
controller.peerSelected = { [weak controller] peer, _ in
|
||||
let peerId = peer.id
|
||||
|
||||
guard let strongController = controller else {
|
||||
|
@ -218,7 +218,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
|
||||
private func updateChatsLayout(layout: ContainerViewLayout, topInset: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
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
|
||||
}, activateChatPreview: { _, _, gesture, _ in
|
||||
gesture?.cancel()
|
||||
|
@ -838,7 +838,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
||||
private func updateChatsLayout(layout: ContainerViewLayout, topInset: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
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
|
||||
}, activateChatPreview: { _, _, gesture, _ in
|
||||
gesture?.cancel()
|
||||
|
@ -362,7 +362,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
private func updateChatsLayout(layout: ContainerViewLayout, topInset: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
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
|
||||
}, activateChatPreview: { _, _, gesture, _ in
|
||||
gesture?.cancel()
|
||||
|
@ -209,6 +209,17 @@ public extension TelegramEngine {
|
||||
}
|
||||
|> 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 {
|
||||
return ChannelAdminEventLogContext(postbox: self.account.postbox, network: self.account.network, peerId: peerId)
|
||||
|
@ -30,6 +30,7 @@ swift_library(
|
||||
"//submodules/TelegramUI/Components/EntityKeyboard:EntityKeyboard",
|
||||
"//submodules/TelegramUI/Components/EmojiTextAttachmentView:EmojiTextAttachmentView",
|
||||
"//submodules/Components/PagerComponent:PagerComponent",
|
||||
"//submodules/PremiumUI",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -14,6 +14,7 @@ import PagerComponent
|
||||
import MultilineTextComponent
|
||||
import EmojiStatusComponent
|
||||
import Postbox
|
||||
import PremiumUI
|
||||
|
||||
private final class TitleFieldComponent: Component {
|
||||
typealias EnvironmentType = Empty
|
||||
@ -315,13 +316,15 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent {
|
||||
let mode: ForumCreateTopicScreen.Mode
|
||||
let titleUpdated: (String) -> 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.peerId = peerId
|
||||
self.mode = mode
|
||||
self.titleUpdated = titleUpdated
|
||||
self.iconUpdated = iconUpdated
|
||||
self.openPremium = openPremium
|
||||
}
|
||||
|
||||
static func ==(lhs: ForumCreateTopicScreenComponent, rhs: ForumCreateTopicScreenComponent) -> Bool {
|
||||
@ -341,17 +344,26 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent {
|
||||
private let context: AccountContext
|
||||
private let titleUpdated: (String) -> Void
|
||||
private let iconUpdated: (Int64?) -> Void
|
||||
private let openPremium: () -> Void
|
||||
|
||||
var emojiContent: EmojiPagerContentComponent?
|
||||
private let emojiContentDisposable = MetaDisposable()
|
||||
|
||||
private var isPremiumDisposable: Disposable?
|
||||
|
||||
private var defaultIconFilesDisposable: Disposable?
|
||||
private var defaultIconFiles = Set<Int64>()
|
||||
|
||||
var title: String
|
||||
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.titleUpdated = titleUpdated
|
||||
self.iconUpdated = iconUpdated
|
||||
self.openPremium = openPremium
|
||||
|
||||
switch mode {
|
||||
case .create:
|
||||
@ -384,10 +396,36 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent {
|
||||
self?.emojiContent = content
|
||||
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 {
|
||||
self.emojiContentDisposable.dispose()
|
||||
self.defaultIconFilesDisposable?.dispose()
|
||||
self.isPremiumDisposable?.dispose()
|
||||
}
|
||||
|
||||
func updateTitle(_ text: String) {
|
||||
@ -423,7 +461,17 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent {
|
||||
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.iconUpdated(self.fileId != 0 ? self.fileId : nil)
|
||||
@ -456,7 +504,8 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent {
|
||||
context: self.context,
|
||||
mode: self.mode,
|
||||
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) {
|
||||
var titleUpdatedImpl: ((String) -> Void)?
|
||||
var iconUpdatedImpl: ((Int64?) -> Void)?
|
||||
var openPremiumImpl: (() -> Void)?
|
||||
|
||||
super.init(context: context, component: ForumCreateTopicScreenComponent(context: context, peerId: peerId, mode: mode, titleUpdated: { title in
|
||||
titleUpdatedImpl?(title)
|
||||
}, iconUpdated: { fileId in
|
||||
iconUpdatedImpl?(fileId)
|
||||
}, openPremium: {
|
||||
openPremiumImpl?()
|
||||
}), navigationBarAppearance: .transparent)
|
||||
|
||||
//TODO:localize
|
||||
@ -725,12 +778,30 @@ public class ForumCreateTopicScreen: ViewControllerComponentContainer {
|
||||
|
||||
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) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
}
|
||||
|
||||
@objc private func cancelPressed() {
|
||||
self.dismiss()
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ swift_library(
|
||||
"//submodules/ItemListPeerItem",
|
||||
"//submodules/ItemListPeerActionItem",
|
||||
"//submodules/NotificationSoundSelectionUI",
|
||||
"//submodules/SettingsUI",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -16,6 +16,7 @@ import NotificationSoundSelectionUI
|
||||
import TelegramStringFormatting
|
||||
import ItemListPeerItem
|
||||
import ItemListPeerActionItem
|
||||
import SettingsUI
|
||||
|
||||
private extension EnginePeer.NotificationSettings.MuteState {
|
||||
var timeInterval: Int32? {
|
||||
@ -98,7 +99,7 @@ private enum NotificationsPeerCategoryEntry: ItemListNodeEntry {
|
||||
|
||||
case exceptionsHeader(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)
|
||||
|
||||
var section: ItemListSectionId {
|
||||
@ -126,7 +127,7 @@ private enum NotificationsPeerCategoryEntry: ItemListNodeEntry {
|
||||
return 4
|
||||
case .addException:
|
||||
return 5
|
||||
case let .exception(index, _, _, _, _, _, _, _):
|
||||
case let .exception(index, _, _, _, _, _, _, _, _, _):
|
||||
return 6 + index
|
||||
case .removeAllExceptions:
|
||||
return 100000
|
||||
@ -184,8 +185,8 @@ private enum NotificationsPeerCategoryEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .exception(lhsIndex, lhsDateTimeFormat, lhsDisplayNameOrder, 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 {
|
||||
case let .exception(lhsIndex, lhsDateTimeFormat, lhsDisplayNameOrder, lhsPeer, lhsThreadId, lhsInfo, lhsDescription, lhsSettings, lhsEditing, lhsRevealed):
|
||||
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
|
||||
} else {
|
||||
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: {
|
||||
arguments.addException()
|
||||
})
|
||||
//case let .exception(_, dateTimeFormat, nameDisplayOrder, info, description, _, editing, revealed):
|
||||
case .exception:
|
||||
preconditionFailure()
|
||||
/*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)
|
||||
case let .exception(_, dateTimeFormat, nameDisplayOrder, peer, threadId, info, description, _, editing, revealed):
|
||||
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: {
|
||||
arguments.openException(threadId)
|
||||
}, setPeerIdWithRevealedOptions: { peerId, fromPeerId in
|
||||
arguments.updateRevealedPeerId(peerId)
|
||||
}, removePeer: { peerId in
|
||||
arguments.removePeer(peer)
|
||||
}, hasTopStripe: false, hasTopGroupInset: false, noInsets: false)*/
|
||||
if let _ = peerId {
|
||||
arguments.updateRevealedThreadId(threadId)
|
||||
} else {
|
||||
arguments.updateRevealedThreadId(nil)
|
||||
}
|
||||
}, removePeer: { _ in
|
||||
arguments.removeThread(threadId)
|
||||
}, hasTopStripe: false, hasTopGroupInset: false, noInsets: false)
|
||||
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: {
|
||||
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 notificationsEnabled = true
|
||||
@ -328,7 +331,7 @@ private func notificationsPeerCategoryEntries(notificationSettings: EnginePeer.N
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
@ -339,10 +342,82 @@ private func notificationsPeerCategoryEntries(notificationSettings: EnginePeer.N
|
||||
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 {
|
||||
var revealedThreadId: Int64? = nil
|
||||
var editing: Bool = false
|
||||
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 {
|
||||
@ -358,23 +433,80 @@ public func threadNotificationExceptionsScreen(context: AccountContext, peerId:
|
||||
let updateState: ((NotificationExceptionState) -> NotificationExceptionState) -> Void = { f in
|
||||
let result = stateValue.modify { f($0) }
|
||||
statePromise.set(result)
|
||||
//updatedMode(result.mode)
|
||||
}
|
||||
|
||||
/*let updatePeerSound: (PeerId, PeerMessageSound) -> Signal<Void, NoError> = { peerId, sound in
|
||||
return context.engine.peers.updatePeerNotificationSoundInteractive(peerId: peerId, threadId: nil, sound: sound)
|
||||
let updateThreadSound: (Int64, PeerMessageSound) -> Signal<Void, NoError> = { threadId, sound in
|
||||
return context.engine.peers.updatePeerNotificationSoundInteractive(peerId: peerId, threadId: threadId, sound: sound)
|
||||
|> deliverOnMainQueue
|
||||
}
|
||||
|
||||
let updatePeerNotificationInterval: (PeerId, Int32?) -> Signal<Void, NoError> = { peerId, muteInterval in
|
||||
return context.engine.peers.updatePeerMuteSetting(peerId: peerId, threadId: nil, muteInterval: muteInterval)
|
||||
let updateThreadNotificationInterval: (Int64, Int32?) -> Signal<Void, NoError> = { threadId, muteInterval in
|
||||
return context.engine.peers.updatePeerMuteSetting(peerId: peerId, threadId: threadId, muteInterval: muteInterval)
|
||||
|> deliverOnMainQueue
|
||||
}
|
||||
|
||||
let updatePeerDisplayPreviews: (PeerId, PeerNotificationDisplayPreviews) -> Signal<Void, NoError> = {
|
||||
peerId, displayPreviews in
|
||||
return context.engine.peers.updatePeerDisplayPreviewsSetting(peerId: peerId, threadId: nil, displayPreviews: displayPreviews) |> deliverOnMainQueue
|
||||
}*/
|
||||
let updateThreadDisplayPreviews: (Int64, PeerNotificationDisplayPreviews) -> Signal<Void, NoError> = {
|
||||
threadId, displayPreviews in
|
||||
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
|
||||
|
||||
@ -388,61 +520,53 @@ public func threadNotificationExceptionsScreen(context: AccountContext, peerId:
|
||||
})
|
||||
pushControllerImpl?(controller)
|
||||
}, addException: {
|
||||
/*let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
var filter: ChatListNodePeersFilter = [.excludeRecent, .doNotSearchMessages, .removeSearchHeader]
|
||||
switch category {
|
||||
case .privateChat:
|
||||
filter.insert(.onlyPrivateChats)
|
||||
filter.insert(.excludeSavedMessages)
|
||||
filter.insert(.excludeSecretChats)
|
||||
case .group:
|
||||
filter.insert(.onlyGroups)
|
||||
case .channel:
|
||||
filter.insert(.onlyChannels)
|
||||
}
|
||||
let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: filter, hasContactSelector: false, title: presentationData.strings.Notifications_AddExceptionTitle))
|
||||
controller.peerSelected = { [weak controller] peer in
|
||||
let peerId = peer.id
|
||||
|
||||
presentPeerSettings(peerId, {
|
||||
controller?.dismiss()
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let filter: ChatListNodePeersFilter = [.excludeRecent, .doNotSearchMessages, .removeSearchHeader]
|
||||
let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: filter, forumPeerId: peerId, hasContactSelector: false, title: presentationData.strings.Notifications_AddExceptionTitle))
|
||||
controller.peerSelected = { [weak controller] _, threadId in
|
||||
guard let threadId = threadId else {
|
||||
return
|
||||
}
|
||||
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.ThreadData(id: peerId, threadId: threadId))
|
||||
|> deliverOnMainQueue).start(next: { threadData in
|
||||
guard let threadData = threadData else {
|
||||
return
|
||||
}
|
||||
|
||||
presentThreadSettings(EngineMessageHistoryThread.NotificationException(threadId: threadId, info: threadData.info, notificationSettings: EnginePeer.NotificationSettings(.defaultSettings)), {
|
||||
controller?.dismiss()
|
||||
})
|
||||
})
|
||||
}
|
||||
pushControllerImpl?(controller)*/
|
||||
}, openException: { peer in
|
||||
//presentPeerSettings(peer.id, {})
|
||||
pushControllerImpl?(controller)
|
||||
}, openException: { threadId in
|
||||
if let item = stateValue.with({ $0 }).notificationExceptions.first(where: { $0.threadId == threadId }) {
|
||||
presentThreadSettings(item, {})
|
||||
}
|
||||
}, removeAllExceptions: {
|
||||
/*let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let actionSheet = ActionSheetController(presentationData: presentationData)
|
||||
actionSheet.setItemGroups([ActionSheetItemGroup(items: [
|
||||
ActionSheetTextItem(title: presentationData.strings.Notification_Exceptions_DeleteAllConfirmation),
|
||||
ActionSheetButtonItem(title: presentationData.strings.Notification_Exceptions_DeleteAll, color: .destructive, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
|
||||
let values = stateValue.with { $0.mode.settings.values }
|
||||
|
||||
let _ = (context.engine.peers.ensurePeersAreLocallyAvailable(peers: values.map { EnginePeer($0.peer) })
|
||||
|> deliverOnMainQueue).start(completed: {
|
||||
updateNotificationsDisposable.set(nil)
|
||||
updateState { state in
|
||||
var state = state
|
||||
for value in values {
|
||||
state = state.withUpdatedPeerMuteInterval(value.peer, nil).withUpdatedPeerSound(value.peer, .default).withUpdatedPeerDisplayPreviews(value.peer, .default)
|
||||
}
|
||||
return state
|
||||
}
|
||||
let _ = (context.engine.peers.removeCustomNotificationSettings(peerIds: values.map(\.peer.id))
|
||||
|> deliverOnMainQueue).start(completed: {
|
||||
updateNotificationsView({})
|
||||
})
|
||||
})
|
||||
var threadIds: [Int64] = []
|
||||
updateState { current in
|
||||
var current = current
|
||||
threadIds = current.notificationExceptions.map(\.threadId)
|
||||
current.notificationExceptions.removeAll()
|
||||
return current
|
||||
}
|
||||
updated(stateValue.with({ $0 }).notificationExceptions)
|
||||
let _ = context.engine.peers.removeCustomThreadNotificationSettings(peerId: peerId, threadIds: threadIds).start()
|
||||
})
|
||||
]), ActionSheetItemGroup(items: [
|
||||
ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
})
|
||||
])])
|
||||
presentControllerImpl?(actionSheet, nil)*/
|
||||
presentControllerImpl?(actionSheet, nil)
|
||||
}, updateRevealedThreadId: { threadId in
|
||||
updateState { current in
|
||||
var current = current
|
||||
@ -450,17 +574,13 @@ public func threadNotificationExceptionsScreen(context: AccountContext, peerId:
|
||||
return current
|
||||
}
|
||||
}, removeThread: { threadId in
|
||||
/*let _ = (context.engine.peers.ensurePeersAreLocallyAvailable(peers: [EnginePeer(peer)])
|
||||
|> deliverOnMainQueue).start(completed: {
|
||||
updateNotificationsDisposable.set(nil)
|
||||
updateState { value in
|
||||
return value.withUpdatedPeerMuteInterval(peer, nil).withUpdatedPeerSound(peer, .default).withUpdatedPeerDisplayPreviews(peer, .default)
|
||||
}
|
||||
let _ = (context.engine.peers.removeCustomNotificationSettings(peerIds: [peer.id])
|
||||
|> deliverOnMainQueue).start(completed: {
|
||||
updateNotificationsView({})
|
||||
})
|
||||
})*/
|
||||
let _ = context.engine.peers.removeCustomThreadNotificationSettings(peerId: peerId, threadIds: [threadId]).start()
|
||||
updateState { current in
|
||||
var current = current
|
||||
current.notificationExceptions.removeAll(where: { $0.threadId == threadId })
|
||||
return current
|
||||
}
|
||||
updated(stateValue.with({ $0 }).notificationExceptions)
|
||||
})
|
||||
|
||||
let signal = combineLatest(queue: .mainQueue(),
|
||||
@ -471,7 +591,7 @@ public func threadNotificationExceptionsScreen(context: AccountContext, peerId:
|
||||
statePromise.get()
|
||||
)
|
||||
|> 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?
|
||||
scrollToItem = nil
|
||||
|
@ -313,6 +313,15 @@ final class AuthorizedApplicationContext {
|
||||
|
||||
if let strongSelf = self, let (messages, _, notify, threadData) = messageList.last, let firstMessage = messages.first {
|
||||
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
|
||||
if let topController = strongSelf.rootController.topViewController as? ChatControllerImpl, topController.traceVisibility() {
|
||||
if topController.chatLocation.peerId == firstMessage.id.peerId {
|
||||
@ -326,7 +335,7 @@ final class AuthorizedApplicationContext {
|
||||
|
||||
if !chatIsVisible {
|
||||
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
|
||||
return false
|
||||
}
|
||||
@ -406,14 +415,14 @@ final class AuthorizedApplicationContext {
|
||||
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)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -430,13 +439,13 @@ final class AuthorizedApplicationContext {
|
||||
}
|
||||
|
||||
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
|
||||
}, expandAction: { expandData in
|
||||
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())
|
||||
(strongSelf.rootController.viewControllers.last as? ViewController)?.present(chatController, in: .window(.root), with: ChatControllerOverlayPresentationData(expandData: expandData()))
|
||||
}
|
||||
|
@ -4039,8 +4039,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
self.navigationBar?.userInfo = PeerInfoNavigationSourceTag(peerId: peerId)
|
||||
}
|
||||
}
|
||||
self.navigationBar?.allowsCustomTransition = {
|
||||
return true
|
||||
self.navigationBar?.allowsCustomTransition = { [weak self] in
|
||||
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)
|
||||
@ -4826,7 +4829,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
let avatarContent: EmojiStatusComponent.Content
|
||||
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 {
|
||||
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)?
|
||||
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)
|
||||
}, multipleSelection: true, forwardedMessageIds: messages.map { $0.id }))
|
||||
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 {
|
||||
return
|
||||
}
|
||||
@ -15289,7 +15292,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
case let .chat(textInputState, _, _):
|
||||
if let textInputState = textInputState {
|
||||
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
|
||||
|
||||
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 {
|
||||
if self.chatDisplayNode.emptyType == .topic {
|
||||
if self.chatDisplayNode.emptyType == .topic, !"".isEmpty {
|
||||
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
|
||||
guard let strongSelf = self else {
|
||||
|
@ -2617,6 +2617,8 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
}
|
||||
} else if case .empty(.joined) = loadState, let entry = transition.historyView.originalView.entries.first {
|
||||
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 {
|
||||
|
@ -703,7 +703,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
||||
f(.dismissWithoutContent)
|
||||
|
||||
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
|
||||
|
||||
if let strongController = controller {
|
||||
|
@ -209,8 +209,8 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe
|
||||
|
||||
let interaction = ChatListNodeInteraction(context: context, animationCache: self.animationCache, animationRenderer: self.animationRenderer, activateSearch: {
|
||||
}, peerSelected: { _, _, _, _ in
|
||||
}, disabledPeerSelected: { _ in
|
||||
}, togglePeerSelected: { _ in
|
||||
}, disabledPeerSelected: { _, _ in
|
||||
}, togglePeerSelected: { _, _ in
|
||||
}, togglePeersSelection: { _, _ in
|
||||
}, additionalCategorySelected: { _ in
|
||||
}, messageSelected: { [weak self] peer, _, message, _ in
|
||||
|
@ -72,7 +72,7 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
|
||||
openPeer(EnginePeer(peer), .withBotStartPayload(ChatControllerInitialBotStart(payload: payload, behavior: .interactive)))
|
||||
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))
|
||||
controller.peerSelected = { [weak controller] peer in
|
||||
controller.peerSelected = { [weak controller] peer, _ in
|
||||
let peerId = peer.id
|
||||
|
||||
let addMemberImpl = {
|
||||
@ -310,7 +310,7 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
|
||||
context.sharedContext.applicationBindings.dismissNativeController()
|
||||
} else {
|
||||
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
|
||||
|
||||
if let strongController = controller {
|
||||
@ -578,7 +578,7 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
|
||||
|
||||
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))
|
||||
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))
|
||||
}
|
||||
navigationController.pushViewController(controller)
|
||||
@ -621,7 +621,7 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
|
||||
|
||||
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))
|
||||
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))
|
||||
}
|
||||
navigationController.pushViewController(controller)
|
||||
|
@ -423,7 +423,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
||||
}
|
||||
let content: EmojiStatusComponent.Content
|
||||
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 {
|
||||
content = .topic(title: String(threadInfo.title.prefix(1)), colorIndex: Int(threadInfo.iconColor), size: CGSize(width: avatarSize, height: avatarSize))
|
||||
}
|
||||
|
@ -1609,7 +1609,7 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
|
||||
}))
|
||||
}
|
||||
|
||||
if isCreator || (channel.adminRights?.rights.contains(.canChangeInfo) == true) {
|
||||
if isCreator {
|
||||
//TODO:localize
|
||||
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)
|
||||
@ -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
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Customize"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, f in
|
||||
f(.dismissWithoutContent)
|
||||
|
||||
guard let strongSelf = self, let peer = strongSelf.data?.peer else {
|
||||
return
|
||||
}
|
||||
let threadId = strongSelf.chatLocation.threadId
|
||||
|
||||
let context = strongSelf.context
|
||||
let updatePeerSound: (PeerId, PeerMessageSound) -> Signal<Void, NoError> = { peerId, sound in
|
||||
return context.engine.peers.updatePeerNotificationSoundInteractive(peerId: peerId, threadId: threadId, sound: sound) |> deliverOnMainQueue
|
||||
}
|
||||
|
||||
let updatePeerNotificationInterval: (PeerId, Int32?) -> Signal<Void, NoError> = { peerId, muteInterval in
|
||||
return context.engine.peers.updatePeerMuteSetting(peerId: peerId, threadId: threadId, muteInterval: muteInterval) |> deliverOnMainQueue
|
||||
}
|
||||
|
||||
let updatePeerDisplayPreviews: (PeerId, PeerNotificationDisplayPreviews) -> Signal<Void, NoError> = {
|
||||
peerId, displayPreviews in
|
||||
return context.engine.peers.updatePeerDisplayPreviewsSetting(peerId: peerId, threadId: threadId, displayPreviews: displayPreviews) |> deliverOnMainQueue
|
||||
}
|
||||
|
||||
let mode: NotificationExceptionMode
|
||||
if let _ = peer as? TelegramUser {
|
||||
mode = .users([:])
|
||||
} else if let _ = peer as? TelegramSecretChat {
|
||||
mode = .users([:])
|
||||
} else if let channel = peer as? TelegramChannel {
|
||||
if case .broadcast = channel.info {
|
||||
mode = .channels([:])
|
||||
let _ = (context.engine.data.get(
|
||||
TelegramEngine.EngineData.Item.NotificationSettings.Global()
|
||||
)
|
||||
|> deliverOnMainQueue).start(next: { globalSettings in
|
||||
guard let strongSelf = self, let peer = strongSelf.data?.peer else {
|
||||
return
|
||||
}
|
||||
let threadId = strongSelf.chatLocation.threadId
|
||||
|
||||
let context = strongSelf.context
|
||||
let updatePeerSound: (PeerId, PeerMessageSound) -> Signal<Void, NoError> = { peerId, sound in
|
||||
return context.engine.peers.updatePeerNotificationSoundInteractive(peerId: peerId, threadId: threadId, sound: sound) |> deliverOnMainQueue
|
||||
}
|
||||
|
||||
let updatePeerNotificationInterval: (PeerId, Int32?) -> Signal<Void, NoError> = { peerId, muteInterval in
|
||||
return context.engine.peers.updatePeerMuteSetting(peerId: peerId, threadId: threadId, muteInterval: muteInterval) |> deliverOnMainQueue
|
||||
}
|
||||
|
||||
let updatePeerDisplayPreviews: (PeerId, PeerNotificationDisplayPreviews) -> Signal<Void, NoError> = {
|
||||
peerId, displayPreviews in
|
||||
return context.engine.peers.updatePeerDisplayPreviewsSetting(peerId: peerId, threadId: threadId, displayPreviews: displayPreviews) |> deliverOnMainQueue
|
||||
}
|
||||
|
||||
let mode: NotificationExceptionMode
|
||||
let defaultSound: PeerMessageSound
|
||||
if let _ = peer as? TelegramUser {
|
||||
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 {
|
||||
mode = .groups([:])
|
||||
defaultSound = globalSettings.groupChats.sound._asMessageSound()
|
||||
}
|
||||
} else {
|
||||
mode = .groups([:])
|
||||
}
|
||||
|
||||
let exceptionController = notificationPeerExceptionController(context: context, updatedPresentationData: strongSelf.controller?.updatedPresentationData, peer: peer, threadId: threadId, mode: mode, edit: true, updatePeerSound: { peerId, sound in
|
||||
let _ = (updatePeerSound(peer.id, sound)
|
||||
|> deliverOnMainQueue).start(next: { _ in
|
||||
|
||||
let _ = mode
|
||||
|
||||
let canRemove = false
|
||||
|
||||
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)
|
||||
|> 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
|
||||
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: {
|
||||
exceptionController.navigationPresentation = .modal
|
||||
controller.push(exceptionController)
|
||||
})
|
||||
exceptionController.navigationPresentation = .modal
|
||||
controller.push(exceptionController)
|
||||
})))
|
||||
|
||||
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 {
|
||||
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 {
|
||||
return
|
||||
}
|
||||
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)
|
||||
}
|
||||
peerSelectionController.peerSelected = { [weak self, weak peerSelectionController] peer in
|
||||
peerSelectionController.peerSelected = { [weak self, weak peerSelectionController] peer, _ in
|
||||
let peerId = peer.id
|
||||
|
||||
if let strongSelf = self, let _ = peerSelectionController {
|
||||
|
@ -18,11 +18,12 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
|
||||
|
||||
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)?
|
||||
private let filter: ChatListNodePeersFilter
|
||||
private let forumPeerId: EnginePeer.Id?
|
||||
|
||||
private let attemptSelection: ((Peer) -> Void)?
|
||||
private let attemptSelection: ((Peer, Int64?) -> Void)?
|
||||
private let createNewGroup: (() -> Void)?
|
||||
|
||||
public var inProgress: Bool = false {
|
||||
@ -80,6 +81,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
|
||||
public init(_ params: PeerSelectionControllerParams) {
|
||||
self.context = params.context
|
||||
self.filter = params.filter
|
||||
self.forumPeerId = params.forumPeerId
|
||||
self.hasChatListSelector = params.hasChatListSelector
|
||||
self.hasContactSelector = params.hasContactSelector
|
||||
self.hasGlobalSearch = params.hasGlobalSearch
|
||||
@ -153,7 +155,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
|
||||
}
|
||||
|
||||
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)
|
||||
}, presentInGlobalOverlay: { [weak self] c, a in
|
||||
self?.presentInGlobalOverlay(c, with: a)
|
||||
@ -175,24 +177,24 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
|
||||
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 {
|
||||
peerSelected(peer)
|
||||
peerSelected(peer, threadId)
|
||||
}
|
||||
}
|
||||
|
||||
self.peerSelectionNode.requestOpenDisabledPeer = { [weak self] peer in
|
||||
self.peerSelectionNode.requestOpenDisabledPeer = { [weak self] peer, threadId in
|
||||
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 {
|
||||
strongSelf.openMessageFromSearchDisposable.set((strongSelf.context.engine.peers.ensurePeerIsLocallyAvailable(peer: EnginePeer(peer))
|
||||
|> deliverOnMainQueue).start(completed: { [weak strongSelf] in
|
||||
if let strongSelf = strongSelf, let peerSelected = strongSelf.peerSelected {
|
||||
peerSelected(peer)
|
||||
peerSelected(peer, threadId)
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
private let presentInGlobalOverlay: (ViewController, Any?) -> Void
|
||||
private let dismiss: () -> Void
|
||||
private let filter: ChatListNodePeersFilter
|
||||
private let forumPeerId: EnginePeer.Id?
|
||||
private let hasGlobalSearch: Bool
|
||||
private let forwardedMessageIds: [EngineMessage.Id]
|
||||
private let hasTypeHeaders: Bool
|
||||
@ -61,9 +62,9 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
|
||||
var requestActivateSearch: (() -> Void)?
|
||||
var requestDeactivateSearch: (() -> Void)?
|
||||
var requestOpenPeer: ((Peer) -> Void)?
|
||||
var requestOpenDisabledPeer: ((Peer) -> Void)?
|
||||
var requestOpenPeerFromSearch: ((Peer) -> Void)?
|
||||
var requestOpenPeer: ((Peer, Int64?) -> Void)?
|
||||
var requestOpenDisabledPeer: ((Peer, Int64?) -> Void)?
|
||||
var requestOpenPeerFromSearch: ((Peer, Int64?) -> Void)?
|
||||
var requestOpenMessageFromSearch: ((Peer, Int64?, MessageId) -> Void)?
|
||||
var requestSend: (([Peer], [PeerId: Peer], NSAttributedString, AttachmentTextInputPanelSendMode, ChatInterfaceForwardOptionsState?) -> Void)?
|
||||
|
||||
@ -86,12 +87,13 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
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.present = present
|
||||
self.presentInGlobalOverlay = presentInGlobalOverlay
|
||||
self.dismiss = dismiss
|
||||
self.filter = filter
|
||||
self.forumPeerId = forumPeerId
|
||||
self.hasGlobalSearch = hasGlobalSearch
|
||||
self.forwardedMessageIds = forwardedMessageIds
|
||||
self.hasTypeHeaders = hasTypeHeaders
|
||||
@ -127,8 +129,15 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
if let _ = createNewGroup {
|
||||
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()
|
||||
|
||||
@ -153,13 +162,13 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
self?.requestActivateSearch?()
|
||||
}
|
||||
|
||||
self.chatListNode.peerSelected = { [weak self] peer, _, _, _, _ in
|
||||
self.chatListNode.peerSelected = { [weak self] peer, threadId, _, _, _ in
|
||||
self?.chatListNode.clearHighlightAnimated(true)
|
||||
self?.requestOpenPeer?(peer._asPeer())
|
||||
self?.requestOpenPeer?(peer._asPeer(), threadId)
|
||||
}
|
||||
|
||||
self.chatListNode.disabledPeerSelected = { [weak self] peer in
|
||||
self?.requestOpenDisabledPeer?(peer._asPeer())
|
||||
self.chatListNode.disabledPeerSelected = { [weak self] peer, threadId in
|
||||
self?.requestOpenDisabledPeer?(peer._asPeer(), threadId)
|
||||
}
|
||||
|
||||
self.chatListNode.contentOffsetChanged = { [weak self] offset in
|
||||
@ -589,6 +598,13 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
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(
|
||||
presentationData: self.presentationData,
|
||||
contentNode: ChatListSearchContainerNode(
|
||||
@ -597,10 +613,10 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
animationRenderer: self.animationRenderer,
|
||||
updatedPresentationData: self.updatedPresentationData,
|
||||
filter: self.filter,
|
||||
location: .chatList(groupId: EngineChatList.Group(.root)),
|
||||
location: chatListLocation,
|
||||
displaySearchFilters: false,
|
||||
hasDownloads: false,
|
||||
openPeer: { [weak self] peer, chatPeer, _, _ in
|
||||
openPeer: { [weak self] peer, chatPeer, threadId, _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
@ -643,11 +659,11 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
strongSelf.textInputPanelNode?.updateSendButtonEnabled(count > 0, animated: true)
|
||||
strongSelf.requestDeactivateSearch?()
|
||||
} else if let requestOpenPeerFromSearch = strongSelf.requestOpenPeerFromSearch {
|
||||
requestOpenPeerFromSearch(peer._asPeer())
|
||||
requestOpenPeerFromSearch(peer._asPeer(), threadId)
|
||||
}
|
||||
},
|
||||
openDisabledPeer: { [weak self] peer in
|
||||
self?.requestOpenDisabledPeer?(peer._asPeer())
|
||||
openDisabledPeer: { [weak self] peer, threadId in
|
||||
self?.requestOpenDisabledPeer?(peer._asPeer(), threadId)
|
||||
},
|
||||
openRecentPeerOptions: { _ in
|
||||
},
|
||||
@ -724,7 +740,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peer.id))
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
if let strongSelf = self, let peer = peer {
|
||||
strongSelf.requestOpenPeerFromSearch?(peer._asPeer())
|
||||
strongSelf.requestOpenPeerFromSearch?(peer._asPeer(), nil)
|
||||
}
|
||||
})
|
||||
case .deviceContact:
|
||||
@ -796,7 +812,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
contactListNode.openPeer = { [weak self] peer, _ in
|
||||
if case let .peer(peer, _, _) = peer {
|
||||
self?.contactListNode?.listNode.clearHighlightAnimated(true)
|
||||
self?.requestOpenPeer?(peer)
|
||||
self?.requestOpenPeer?(peer, nil)
|
||||
}
|
||||
}
|
||||
contactListNode.suppressPermissionWarning = { [weak self] in
|
||||
|
@ -659,7 +659,7 @@ public class ShareRootControllerImpl {
|
||||
case let .group(groupTitle):
|
||||
var attemptSelectionImpl: ((Peer) -> 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)
|
||||
}, createNewGroup: {
|
||||
createNewGroupImpl?()
|
||||
@ -669,7 +669,7 @@ public class ShareRootControllerImpl {
|
||||
self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil)
|
||||
}
|
||||
|
||||
controller.peerSelected = { peer in
|
||||
controller.peerSelected = { peer, _ in
|
||||
attemptSelectionImpl?(peer)
|
||||
}
|
||||
|
||||
@ -835,7 +835,7 @@ public class ShareRootControllerImpl {
|
||||
let presentationData = internalContext.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
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)
|
||||
}, pretendPresentedInModal: true))
|
||||
|
||||
@ -843,7 +843,7 @@ public class ShareRootControllerImpl {
|
||||
self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil)
|
||||
}
|
||||
|
||||
controller.peerSelected = { peer in
|
||||
controller.peerSelected = { peer, _ in
|
||||
attemptSelectionImpl?(peer)
|
||||
}
|
||||
|
||||
@ -908,7 +908,7 @@ public class ShareRootControllerImpl {
|
||||
case let .unknown(peerTitle):
|
||||
var attemptSelectionImpl: ((Peer) -> 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)
|
||||
}, createNewGroup: {
|
||||
createNewGroupImpl?()
|
||||
@ -918,7 +918,7 @@ public class ShareRootControllerImpl {
|
||||
self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil)
|
||||
}
|
||||
|
||||
controller.peerSelected = { peer in
|
||||
controller.peerSelected = { peer, _ in
|
||||
attemptSelectionImpl?(peer)
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user