[WIP] Topics

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

View File

@ -41,21 +41,23 @@ public final class PeerSelectionControllerParams {
public let context: AccountContext
public let 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 }

View File

@ -182,7 +182,7 @@ private final class ChatListShimmerNode: ASDisplayNode {
let peer1: EnginePeer = .user(TelegramUser(id: EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: EnginePeer.Id.Id._internalFromInt64Value(0)), accessHash: nil, firstName: "FirstName", lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: []))
let 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

View File

@ -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 {

View File

@ -89,11 +89,11 @@ private enum ChatListRecentEntry: Comparable, Identifiable {
}
}
func item(context: AccountContext, presentationData: ChatListPresentationData, filter: ChatListNodePeersFilter, peerSelected: @escaping (EnginePeer) -> Void, disabledPeerSelected: @escaping (EnginePeer) -> Void, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, clearRecentlySearchedPeers: @escaping () -> Void, deletePeer: @escaping (EnginePeer.Id) -> Void, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer) -> ListViewItem {
func item(context: AccountContext, presentationData: ChatListPresentationData, filter: ChatListNodePeersFilter, peerSelected: @escaping (EnginePeer, Int64?) -> Void, disabledPeerSelected: @escaping (EnginePeer, Int64?) -> Void, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, clearRecentlySearchedPeers: @escaping () -> Void, deletePeer: @escaping (EnginePeer.Id) -> Void, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer) -> ListViewItem {
switch self {
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 })

View File

@ -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

View File

@ -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))
}

View File

@ -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
}

View File

@ -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))
}

View File

@ -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) {

View File

@ -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()

View File

@ -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 {

View File

@ -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

View File

@ -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",

View File

@ -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() {

View File

@ -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, {

View File

@ -365,7 +365,7 @@ private struct NotificationExceptionPeerState : Equatable {
}
}
public func notificationPeerExceptionController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peer: Peer, threadId: Int64?, mode: NotificationExceptionMode, edit: Bool = false, updatePeerSound: @escaping(PeerId, PeerMessageSound) -> Void, updatePeerNotificationInterval: @escaping(PeerId, Int32?) -> Void, updatePeerDisplayPreviews: @escaping(PeerId, PeerNotificationDisplayPreviews) -> Void, removePeerFromExceptions: @escaping () -> Void, modifiedPeer: @escaping () -> Void) -> ViewController {
public func notificationPeerExceptionController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peer: Peer, customTitle: String? = nil, threadId: Int64?, canRemove: Bool, defaultSound: PeerMessageSound, edit: Bool = false, updatePeerSound: @escaping(PeerId, PeerMessageSound) -> Void, updatePeerNotificationInterval: @escaping(PeerId, Int32?) -> Void, updatePeerDisplayPreviews: @escaping(PeerId, PeerNotificationDisplayPreviews) -> Void, removePeerFromExceptions: @escaping () -> Void, modifiedPeer: @escaping () -> Void) -> ViewController {
let initialState = NotificationExceptionPeerState(canRemove: false)
let 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))

View File

@ -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, {

View File

@ -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 {

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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)

View File

@ -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",

View File

@ -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()
}

View File

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

View File

@ -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

View File

@ -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()))
}

View File

@ -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 {

View File

@ -2617,6 +2617,8 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
}
} else if case .empty(.joined) = loadState, let entry = transition.historyView.originalView.entries.first {
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 {

View File

@ -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 {

View File

@ -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

View File

@ -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)

View File

@ -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))
}

View File

@ -1609,7 +1609,7 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
}))
}
if isCreator || (channel.adminRights?.rights.contains(.canChangeInfo) == true) {
if isCreator {
//TODO:localize
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 {

View File

@ -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)
}
}))
}

View File

@ -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

View File

@ -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)
}