Update Context UI

This commit is contained in:
Ali 2021-09-10 22:04:30 +04:00
parent bc0aefc32e
commit 2e9c265b14
28 changed files with 202 additions and 151 deletions

View File

@ -6777,7 +6777,7 @@ Telegram offers free and unlimited service to hundreds of millions of users, whi
[url]
Ads should no longer be synonymous with abuse of user privacy. Let us redefine how a tech compony should operate — together.";
"SponsoredMessageInfo.Action" = "Learn More";
"SponsoredMessageInfo.ActionUrl" = "https://telegram.org/ads";
"SponsoredMessageInfo.Url" = "https://telegram.org/ads";
"Chat.NavigationNoChannels" = "You have no unread channels";

View File

@ -106,14 +106,14 @@ public final class AdInfoScreen: ViewController {
if !didAddUrl {
didAddUrl = true
items.append(.link(LinkNode(text: self.presentationData.strings.SponsoredMessageInfo_ActionUrl, color: self.presentationData.theme.list.itemAccentColor, action: {
items.append(.link(LinkNode(text: self.presentationData.strings.SponsoredMessageInfo_Url, color: self.presentationData.theme.list.itemAccentColor, action: {
openUrl?()
})))
}
}
if !didAddUrl {
didAddUrl = true
items.append(.link(LinkNode(text: self.presentationData.strings.SponsoredMessageInfo_ActionUrl, color: self.presentationData.theme.list.itemAccentColor, action: {
items.append(.link(LinkNode(text: self.presentationData.strings.SponsoredMessageInfo_Url, color: self.presentationData.theme.list.itemAccentColor, action: {
openUrl?()
})))
}
@ -138,7 +138,7 @@ public final class AdInfoScreen: ViewController {
guard let strongSelf = self else {
return
}
strongSelf.context.sharedContext.applicationBindings.openUrl(strongSelf.presentationData.strings.SponsoredMessageInfo_ActionUrl)
strongSelf.context.sharedContext.applicationBindings.openUrl(strongSelf.presentationData.strings.SponsoredMessageInfo_Url)
}
}

View File

@ -358,7 +358,7 @@ public final class CallListController: TelegramBaseController {
}
}
let contextController = ContextController(account: self.context.account, presentationData: self.presentationData, source: .extracted(ExtractedContentSourceImpl(controller: self, sourceNode: buttonNode.contentNode, keepInPlace: false, blurBackground: false)), items: .single(items), reactionItems: [], gesture: nil)
let contextController = ContextController(account: self.context.account, presentationData: self.presentationData, source: .extracted(ExtractedContentSourceImpl(controller: self, sourceNode: buttonNode.contentNode, keepInPlace: false, blurBackground: false)), items: .single(ContextController.Items(items: items)), reactionItems: [], gesture: nil)
self.presentInGlobalOverlay(contextController)
}

View File

@ -250,10 +250,10 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
updatedItems.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_Back, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.contextMenu.primaryColor)
}, action: { c, _ in
c.setItems(chatContextMenuItems(context: context, peerId: peerId, promoInfo: promoInfo, source: source, chatListController: chatListController, joined: joined), minHeight: nil)
c.setItems(chatContextMenuItems(context: context, peerId: peerId, promoInfo: promoInfo, source: source, chatListController: chatListController, joined: joined) |> map { ContextController.Items(items: $0) }, minHeight: nil)
})))
c.setItems(.single(updatedItems), minHeight: nil)
c.setItems(.single(ContextController.Items(items: updatedItems)), minHeight: nil)
})))
}
}

View File

@ -839,12 +839,12 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
case let .groupReference(groupId, _, _, _, _):
let chatListController = ChatListControllerImpl(context: strongSelf.context, groupId: groupId, controlsHistoryPreload: false, hideNetworkActivityStatus: true, previewing: true, enableDebugActions: false)
chatListController.navigationPresentation = .master
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatListController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: archiveContextMenuItems(context: strongSelf.context, groupId: groupId, chatListController: strongSelf), reactionItems: [], gesture: gesture)
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatListController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: archiveContextMenuItems(context: strongSelf.context, groupId: groupId, chatListController: strongSelf) |> map { ContextController.Items(items: $0) }, reactionItems: [], gesture: gesture)
strongSelf.presentInGlobalOverlay(contextController)
case let .peer(_, peer, _, _, _, _, _, _, promoInfo, _, _, _):
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peer.peerId), subject: nil, botStart: nil, mode: .standard(previewing: true))
chatController.canReadHistory.set(false)
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: chatContextMenuItems(context: strongSelf.context, peerId: peer.peerId, promoInfo: promoInfo, source: .chatList(filter: strongSelf.chatListDisplayNode.containerNode.currentItemNode.chatListFilter), chatListController: strongSelf, joined: joined), reactionItems: [], gesture: gesture)
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: chatContextMenuItems(context: strongSelf.context, peerId: peer.peerId, promoInfo: promoInfo, source: .chatList(filter: strongSelf.chatListDisplayNode.containerNode.currentItemNode.chatListFilter), chatListController: strongSelf, joined: joined) |> map { ContextController.Items(items: $0) }, reactionItems: [], gesture: gesture)
strongSelf.presentInGlobalOverlay(contextController)
}
}
@ -868,7 +868,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
contextContentSource = .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController))
}
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: contextContentSource, items: chatContextMenuItems(context: strongSelf.context, peerId: peer.id, promoInfo: nil, source: .search(source), chatListController: strongSelf, joined: false), reactionItems: [], gesture: gesture)
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: contextContentSource, items: chatContextMenuItems(context: strongSelf.context, peerId: peer.id, promoInfo: nil, source: .search(source), chatListController: strongSelf, joined: false) |> map { ContextController.Items(items: $0) }, reactionItems: [], gesture: gesture)
strongSelf.presentInGlobalOverlay(contextController)
}
@ -1095,7 +1095,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
})))
}
let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatListHeaderBarContextExtractedContentSource(controller: strongSelf, sourceNode: sourceNode, keepInPlace: keepInPlace)), items: .single(items), reactionItems: [], recognizer: nil, gesture: gesture)
let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatListHeaderBarContextExtractedContentSource(controller: strongSelf, sourceNode: sourceNode, keepInPlace: keepInPlace)), items: .single(ContextController.Items(items: items)), reactionItems: [], recognizer: nil, gesture: gesture)
strongSelf.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller)
})
}
@ -2842,7 +2842,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
}
}
let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatListTabBarContextExtractedContentSource(controller: strongSelf, sourceNode: sourceNode)), items: .single(items), reactionItems: [], recognizer: nil, gesture: gesture)
let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatListTabBarContextExtractedContentSource(controller: strongSelf, sourceNode: sourceNode)), items: .single(ContextController.Items(items: items)), reactionItems: [], recognizer: nil, gesture: gesture)
strongSelf.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller)
})
}

View File

@ -737,7 +737,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
return items
}
let controller = ContextController(account: self.context.account, presentationData: self.presentationData, source: .extracted(MessageContextExtractedContentSource(sourceNode: node)), items: items, reactionItems: [], recognizer: nil, gesture: gesture)
let controller = ContextController(account: self.context.account, presentationData: self.presentationData, source: .extracted(MessageContextExtractedContentSource(sourceNode: node)), items: items |> map { ContextController.Items(items: $0) }, reactionItems: [], recognizer: nil, gesture: gesture)
self.presentInGlobalOverlay?(controller, nil)
}
@ -797,7 +797,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
switch previewData {
case let .gallery(gallery):
gallery.setHintWillBePresentedInPreviewingContext(true)
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: gallery, sourceNode: node)), items: items, reactionItems: [], gesture: gesture)
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: gallery, sourceNode: node)), items: items |> map { ContextController.Items(items: $0) }, reactionItems: [], gesture: gesture)
strongSelf.presentInGlobalOverlay?(contextController, nil)
case .instantPage:
break

View File

@ -175,7 +175,7 @@ final class ContactsControllerNode: ASDisplayNode {
}
let chatController = self.context.sharedContext.makeChatController(context: self.context, chatLocation: .peer(peer.id), subject: nil, botStart: nil, mode: .standard(previewing: true))
chatController.canReadHistory.set(false)
let contextController = ContextController(account: self.context.account, presentationData: self.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: contactContextMenuItems(context: self.context, peerId: peer.id, contactsController: contactsController), reactionItems: [], gesture: gesture)
let contextController = ContextController(account: self.context.account, presentationData: self.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: contactContextMenuItems(context: self.context, peerId: peer.id, contactsController: contactsController) |> map { ContextController.Items(items: $0) }, reactionItems: [], gesture: gesture)
contactsController.presentInGlobalOverlay(contextController)
}

View File

@ -469,7 +469,7 @@ final class ContextActionsContainerNode: ASDisplayNode {
return self.additionalActionsNode != nil
}
init(presentationData: PresentationData, items: [ContextMenuItem], getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void, feedbackTap: @escaping () -> Void, tip: ContextController.Tip?, blurBackground: Bool) {
init(presentationData: PresentationData, items: ContextController.Items, getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void, feedbackTap: @escaping () -> Void, blurBackground: Bool) {
self.blurBackground = blurBackground
self.shadowNode = ASImageNode()
self.shadowNode.displaysAsynchronously = false
@ -479,7 +479,7 @@ final class ContextActionsContainerNode: ASDisplayNode {
self.shadowNode.isHidden = true
var items = items
if let firstItem = items.first, case let .custom(_, additional) = firstItem, additional {
if let firstItem = items.items.first, case let .custom(_, additional) = firstItem, additional {
let additionalShadowNode = ASImageNode()
additionalShadowNode.displaysAsynchronously = false
additionalShadowNode.displayWithoutProcessing = true
@ -489,14 +489,14 @@ final class ContextActionsContainerNode: ASDisplayNode {
self.additionalShadowNode = additionalShadowNode
self.additionalActionsNode = InnerActionsContainerNode(presentationData: presentationData, items: [firstItem], getController: getController, actionSelected: actionSelected, feedbackTap: feedbackTap, blurBackground: blurBackground)
items.removeFirst()
items.items.removeFirst()
} else {
self.additionalShadowNode = nil
self.additionalActionsNode = nil
}
self.actionsNode = InnerActionsContainerNode(presentationData: presentationData, items: items, getController: getController, actionSelected: actionSelected, feedbackTap: feedbackTap, blurBackground: blurBackground)
if let tip = tip {
self.actionsNode = InnerActionsContainerNode(presentationData: presentationData, items: items.items, getController: getController, actionSelected: actionSelected, feedbackTap: feedbackTap, blurBackground: blurBackground)
if let tip = items.tip {
let textSelectionTipNode = InnerTextSelectionTipContainerNode(presentationData: presentationData, tip: tip)
textSelectionTipNode.isUserInteractionEnabled = false
self.textSelectionTipNode = textSelectionTipNode

View File

@ -15,8 +15,8 @@ public protocol ContextControllerProtocol {
var immediateItemsTransitionAnimation: Bool { get set }
func getActionsMinHeight() -> CGFloat?
func setItems(_ items: Signal<[ContextMenuItem], NoError>, minHeight: CGFloat?)
func setItems(_ items: Signal<[ContextMenuItem], NoError>, minHeight: CGFloat?, previousActionsTransition: ContextController.PreviousActionsTransition)
func setItems(_ items: Signal<ContextController.Items, NoError>, minHeight: CGFloat?)
func setItems(_ items: Signal<ContextController.Items, NoError>, minHeight: CGFloat?, previousActionsTransition: ContextController.PreviousActionsTransition)
func dismiss(completion: (() -> Void)?)
}
@ -117,7 +117,7 @@ private func convertFrame(_ frame: CGRect, from fromView: UIView, to toView: UIV
private final class ContextControllerNode: ViewControllerTracingNode, UIScrollViewDelegate {
private var presentationData: PresentationData
private let source: ContextContentSource
private var items: Signal<[ContextMenuItem], NoError>
private var items: Signal<ContextController.Items, NoError>
private let beginDismiss: (ContextMenuActionResult) -> Void
private let reactionSelected: (ReactionContextItem.Reaction) -> Void
private let beganAnimatingOut: () -> Void
@ -125,13 +125,12 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
fileprivate var dismissedForCancel: (() -> Void)?
private let getController: () -> ContextControllerProtocol?
private weak var gesture: ContextGesture?
private var tip: ContextController.Tip?
private var didSetItemsReady = false
let itemsReady = Promise<Bool>()
let contentReady = Promise<Bool>()
private var currentItems: [ContextMenuItem]?
private var currentItems: ContextController.Items?
private var currentActionsMinHeight: CGFloat?
private var validLayout: ContainerViewLayout?
@ -169,7 +168,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
private let blurBackground: Bool
init(account: Account, controller: ContextController, presentationData: PresentationData, source: ContextContentSource, items: Signal<[ContextMenuItem], NoError>, reactionItems: [ReactionContextItem], beginDismiss: @escaping (ContextMenuActionResult) -> Void, recognizer: TapLongTapOrDoubleTapGestureRecognizer?, gesture: ContextGesture?, reactionSelected: @escaping (ReactionContextItem.Reaction) -> Void, beganAnimatingOut: @escaping () -> Void, attemptTransitionControllerIntoNavigation: @escaping () -> Void, tip: ContextController.Tip?) {
init(account: Account, controller: ContextController, presentationData: PresentationData, source: ContextContentSource, items: Signal<ContextController.Items, NoError>, reactionItems: [ReactionContextItem], beginDismiss: @escaping (ContextMenuActionResult) -> Void, recognizer: TapLongTapOrDoubleTapGestureRecognizer?, gesture: ContextGesture?, reactionSelected: @escaping (ReactionContextItem.Reaction) -> Void, beganAnimatingOut: @escaping () -> Void, attemptTransitionControllerIntoNavigation: @escaping () -> Void) {
self.presentationData = presentationData
self.source = source
self.items = items
@ -178,7 +177,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
self.beganAnimatingOut = beganAnimatingOut
self.attemptTransitionControllerIntoNavigation = attemptTransitionControllerIntoNavigation
self.gesture = gesture
self.tip = tip
self.getController = { [weak controller] in
return controller
@ -231,13 +229,13 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
}
self.blurBackground = blurBackground
self.actionsContainerNode = ContextActionsContainerNode(presentationData: presentationData, items: [], getController: { [weak controller] in
self.actionsContainerNode = ContextActionsContainerNode(presentationData: presentationData, items: ContextController.Items(items: []), getController: { [weak controller] in
return controller
}, actionSelected: { result in
beginDismiss(result)
}, feedbackTap: {
feedbackTap?()
}, tip: self.tip, blurBackground: blurBackground)
}, blurBackground: blurBackground)
if !reactionItems.isEmpty {
let reactionContextNode = ReactionContextNode(account: account, theme: presentationData.theme, items: reactionItems)
@ -1179,7 +1177,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
}
}
func setItemsSignal(items: Signal<[ContextMenuItem], NoError>, minHeight: CGFloat?, previousActionsTransition: ContextController.PreviousActionsTransition) {
func setItemsSignal(items: Signal<ContextController.Items, NoError>, minHeight: CGFloat?, previousActionsTransition: ContextController.PreviousActionsTransition) {
self.items = items
self.itemsDisposable.set((items
|> deliverOnMainQueue).start(next: { [weak self] items in
@ -1190,7 +1188,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
}))
}
private func setItems(items: [ContextMenuItem], minHeight: CGFloat?, previousActionsTransition: ContextController.PreviousActionsTransition) {
private func setItems(items: ContextController.Items, minHeight: CGFloat?, previousActionsTransition: ContextController.PreviousActionsTransition) {
if let _ = self.currentItems, !self.didCompleteAnimationIn && self.getController()?.immediateItemsTransitionAnimation == true {
return
}
@ -1206,7 +1204,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
self?.beginDismiss(result)
}, feedbackTap: { [weak self] in
self?.hapticFeedback.tap()
}, tip: self.tip, blurBackground: self.blurBackground)
}, blurBackground: self.blurBackground)
self.scrollNode.insertSubnode(self.actionsContainerNode, aboveSubnode: previousActionsContainerNode)
if let layout = self.validLayout {
@ -1217,7 +1215,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
if !self.didSetItemsReady {
self.didSetItemsReady = true
self.tip = nil
self.itemsReady.set(.single(true))
}
}
@ -1867,6 +1864,21 @@ public enum ContextContentSource {
}
public final class ContextController: ViewController, StandalonePresentableController, ContextControllerProtocol {
public struct Items {
public var items: [ContextMenuItem]
public var tip: Tip?
public init(items: [ContextMenuItem], tip: Tip? = nil) {
self.items = items
self.tip = tip
}
public init() {
self.items = []
self.tip = nil
}
}
public enum PreviousActionsTransition {
case scale
case slide(forward: Bool)
@ -1880,7 +1892,7 @@ public final class ContextController: ViewController, StandalonePresentableContr
private let account: Account
private var presentationData: PresentationData
private let source: ContextContentSource
private var items: Signal<[ContextMenuItem], NoError>
private var items: Signal<ContextController.Items, NoError>
private var reactionItems: [ReactionContextItem]
private let _ready = Promise<Bool>()
@ -1890,7 +1902,6 @@ public final class ContextController: ViewController, StandalonePresentableContr
private weak var recognizer: TapLongTapOrDoubleTapGestureRecognizer?
private weak var gesture: ContextGesture?
private let tip: Tip?
private var animatedDidAppear = false
private var wasDismissed = false
@ -1912,7 +1923,7 @@ public final class ContextController: ViewController, StandalonePresentableContr
private var shouldBeDismissedDisposable: Disposable?
public init(account: Account, presentationData: PresentationData, source: ContextContentSource, items: Signal<[ContextMenuItem], NoError>, reactionItems: [ReactionContextItem], recognizer: TapLongTapOrDoubleTapGestureRecognizer? = nil, gesture: ContextGesture? = nil, tip: Tip? = nil) {
public init(account: Account, presentationData: PresentationData, source: ContextContentSource, items: Signal<ContextController.Items, NoError>, reactionItems: [ReactionContextItem], recognizer: TapLongTapOrDoubleTapGestureRecognizer? = nil, gesture: ContextGesture? = nil) {
self.account = account
self.presentationData = presentationData
self.source = source
@ -1920,7 +1931,6 @@ public final class ContextController: ViewController, StandalonePresentableContr
self.reactionItems = reactionItems
self.recognizer = recognizer
self.gesture = gesture
self.tip = tip
super.init(navigationBarPresentationData: nil)
@ -1991,7 +2001,7 @@ public final class ContextController: ViewController, StandalonePresentableContr
default:
break
}
}, tip: self.tip)
})
self.controllerNode.dismissedForCancel = self.dismissedForCancel
self.displayNodeDidLoad()
@ -2026,14 +2036,14 @@ public final class ContextController: ViewController, StandalonePresentableContr
return nil
}
public func setItems(_ items: Signal<[ContextMenuItem], NoError>, minHeight: CGFloat?) {
public func setItems(_ items: Signal<ContextController.Items, NoError>, minHeight: CGFloat?) {
self.items = items
if self.isNodeLoaded {
self.controllerNode.setItemsSignal(items: items, minHeight: minHeight, previousActionsTransition: .scale)
}
}
public func setItems(_ items: Signal<[ContextMenuItem], NoError>, minHeight: CGFloat?, previousActionsTransition: ContextController.PreviousActionsTransition) {
public func setItems(_ items: Signal<ContextController.Items, NoError>, minHeight: CGFloat?, previousActionsTransition: ContextController.PreviousActionsTransition) {
self.items = items
if self.isNodeLoaded {
self.controllerNode.setItemsSignal(items: items, minHeight: minHeight, previousActionsTransition: previousActionsTransition)

View File

@ -38,10 +38,10 @@ public final class PeekController: ViewController, ContextControllerProtocol {
return nil
}
public func setItems(_ items: Signal<[ContextMenuItem], NoError>, minHeight: CGFloat?) {
public func setItems(_ items: Signal<ContextController.Items, NoError>, minHeight: CGFloat?) {
}
public func setItems(_ items: Signal<[ContextMenuItem], NoError>, minHeight: CGFloat?, previousActionsTransition: ContextController.PreviousActionsTransition) {
public func setItems(_ items: Signal<ContextController.Items, NoError>, minHeight: CGFloat?, previousActionsTransition: ContextController.PreviousActionsTransition) {
}
private var controllerNode: PeekControllerNode {

View File

@ -72,13 +72,13 @@ final class PeekControllerNode: ViewControllerTracingNode {
var feedbackTapImpl: (() -> Void)?
var activatedActionImpl: (() -> Void)?
self.actionsContainerNode = ContextActionsContainerNode(presentationData: presentationData, items: content.menuItems(), getController: { [weak controller] in
self.actionsContainerNode = ContextActionsContainerNode(presentationData: presentationData, items: ContextController.Items(items: content.menuItems()), getController: { [weak controller] in
return controller
}, actionSelected: { result in
activatedActionImpl?()
}, feedbackTap: {
feedbackTapImpl?()
}, tip: nil, blurBackground: true)
}, blurBackground: true)
self.actionsContainerNode.alpha = 0.0
super.init()
@ -328,13 +328,13 @@ final class PeekControllerNode: ViewControllerTracingNode {
self.contentNodeHasValidLayout = false
let previousActionsContainerNode = self.actionsContainerNode
self.actionsContainerNode = ContextActionsContainerNode(presentationData: self.presentationData, items: content.menuItems(), getController: { [weak self] in
self.actionsContainerNode = ContextActionsContainerNode(presentationData: self.presentationData, items: ContextController.Items(items: content.menuItems()), getController: { [weak self] in
return self?.controller
}, actionSelected: { [weak self] result in
self?.requestDismiss()
}, feedbackTap: { [weak self] in
self?.hapticFeedback.tap()
}, tip: nil, blurBackground: true)
}, blurBackground: true)
self.actionsContainerNode.alpha = 0.0
self.insertSubnode(self.actionsContainerNode, aboveSubnode: previousActionsContainerNode)
previousActionsContainerNode.removeFromSupernode()

View File

@ -2032,7 +2032,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
return
}
let contextController = ContextController(account: self.context.account, presentationData: self.presentationData.withUpdated(theme: defaultDarkColorPresentationTheme), source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceNode: self.moreBarButton.referenceNode)), items: items, reactionItems: [], gesture: gesture)
let contextController = ContextController(account: self.context.account, presentationData: self.presentationData.withUpdated(theme: defaultDarkColorPresentationTheme), source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceNode: self.moreBarButton.referenceNode)), items: items |> map { ContextController.Items(items: $0) }, reactionItems: [], gesture: gesture)
self.isShowingContextMenuPromise.set(true)
controller.presentInGlobalOverlay(contextController)
@ -2087,7 +2087,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
return
}
c.setItems(strongSelf.contextMenuSpeedItems(), minHeight: nil)
c.setItems(strongSelf.contextMenuSpeedItems() |> map { ContextController.Items(items: $0) }, minHeight: nil)
})))
if let (message, _, _) = strongSelf.contentInfo() {
@ -2206,7 +2206,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
c.dismiss(completion: nil)
return
}
c.setItems(strongSelf.contextMenuMainItems(), minHeight: nil)
c.setItems(strongSelf.contextMenuMainItems() |> map { ContextController.Items(items: $0) }, minHeight: nil)
})))
return items

View File

@ -418,7 +418,7 @@ public final class InviteLinkInviteController: ViewController {
})
})))
let contextController = ContextController(account: context.account, presentationData: presentationData, source: .reference(InviteLinkContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(items), reactionItems: [], gesture: gesture)
let contextController = ContextController(account: context.account, presentationData: presentationData, source: .reference(InviteLinkContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(items: items)), reactionItems: [], gesture: gesture)
self?.controller?.presentInGlobalOverlay(contextController)
}, copyLink: { [weak self] invite in
UIPasteboard.general.string = invite.link

View File

@ -550,7 +550,7 @@ public func inviteLinkListController(context: AccountContext, updatedPresentatio
})))
}
let contextController = ContextController(account: context.account, presentationData: presentationData, source: .reference(InviteLinkContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(items), reactionItems: [], gesture: gesture)
let contextController = ContextController(account: context.account, presentationData: presentationData, source: .reference(InviteLinkContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(items: items)), reactionItems: [], gesture: gesture)
presentInGlobalOverlayImpl?(contextController)
}, createLink: {
let controller = inviteLinkEditController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, invite: nil, completion: { invite in
@ -714,7 +714,7 @@ public func inviteLinkListController(context: AccountContext, updatedPresentatio
})))
}
let contextController = ContextController(account: context.account, presentationData: presentationData, source: .extracted(InviteLinkContextExtractedContentSource(controller: controller, sourceNode: node, blurBackground: true)), items: .single(items), reactionItems: [], gesture: gesture)
let contextController = ContextController(account: context.account, presentationData: presentationData, source: .extracted(InviteLinkContextExtractedContentSource(controller: controller, sourceNode: node, blurBackground: true)), items: .single(ContextController.Items(items: items)), reactionItems: [], gesture: gesture)
presentInGlobalOverlayImpl?(contextController)
}, openAdmin: { admin in
let controller = inviteLinkListController(context: context, peerId: peerId, admin: admin)

View File

@ -565,7 +565,7 @@ public final class InviteLinkViewController: ViewController {
})))
}
let contextController = ContextController(account: context.account, presentationData: presentationData, source: .reference(InviteLinkContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(items), reactionItems: [], gesture: gesture)
let contextController = ContextController(account: context.account, presentationData: presentationData, source: .reference(InviteLinkContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(items: items)), reactionItems: [], gesture: gesture)
self?.controller?.presentInGlobalOverlay(contextController)
})

View File

@ -1050,7 +1050,7 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta
})
})))
let contextController = ContextController(account: context.account, presentationData: presentationData, source: .extracted(InviteLinkContextExtractedContentSource(controller: controller, sourceNode: node)), items: .single(items), reactionItems: [], gesture: gesture)
let contextController = ContextController(account: context.account, presentationData: presentationData, source: .extracted(InviteLinkContextExtractedContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(items: items)), reactionItems: [], gesture: gesture)
presentInGlobalOverlayImpl?(contextController)
}, manageInviteLinks: {
let controller = inviteLinkListController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, admin: nil)

View File

@ -158,7 +158,7 @@ public func presentPeerReportOptions(context: AccountContext, parent: ViewContro
backAction(c)
})))
}
contextController.setItems(.single(items), minHeight: nil)
contextController.setItems(.single(ContextController.Items(items: items)), minHeight: nil)
} else {
contextController?.dismiss(completion: nil)
parent.view.endEditing(true)

View File

@ -494,7 +494,7 @@ public func peersNearbyController(context: AccountContext) -> ViewController {
chatController.canReadHistory.set(false)
let contextController = ContextController(account: context.account, presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: peerNearbyContextMenuItems(context: context, peerId: peer.id, present: { c in
presentControllerImpl?(c, nil)
}), reactionItems: [], gesture: gesture)
}) |> map { ContextController.Items(items: $0) }, reactionItems: [], gesture: gesture)
presentInGlobalOverlayImpl?(contextController)
}, expandUsers: {
expandedPromise.set(true)

View File

@ -804,7 +804,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
})))
}
let contextController = ContextController(account: context.account, presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: themeController, sourceNode: node)), items: .single(items), reactionItems: [], gesture: gesture)
let contextController = ContextController(account: context.account, presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: themeController, sourceNode: node)), items: .single(ContextController.Items(items: items)), reactionItems: [], gesture: gesture)
presentInGlobalOverlayImpl?(contextController, nil)
})
}, colorContextAction: { isCurrent, reference, accentColor, node, gesture in
@ -1041,7 +1041,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
}
}
}
let contextController = ContextController(account: context.account, presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: themeController, sourceNode: node)), items: .single(items), reactionItems: [], gesture: gesture)
let contextController = ContextController(account: context.account, presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: themeController, sourceNode: node)), items: .single(ContextController.Items(items: items)), reactionItems: [], gesture: gesture)
presentInGlobalOverlayImpl?(contextController, nil)
})
})

View File

@ -523,7 +523,7 @@ public func channelStatsController(context: AccountContext, updatedPresentationD
})
})))
let contextController = ContextController(account: context.account, presentationData: presentationData, source: .extracted(ChannelStatsContextExtractedContentSource(controller: controller, sourceNode: sourceNode, keepInPlace: false)), items: .single(items), reactionItems: [], gesture: gesture)
let contextController = ContextController(account: context.account, presentationData: presentationData, source: .extracted(ChannelStatsContextExtractedContentSource(controller: controller, sourceNode: sourceNode, keepInPlace: false)), items: .single(ContextController.Items(items: items)), reactionItems: [], gesture: gesture)
controller.presentInGlobalOverlay(contextController)
}
return controller

View File

@ -555,7 +555,7 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi
return
}
let items: Signal<[ContextMenuItem], NoError> = self.contextMenuSpeedItems()
let contextController = ContextController(account: self.context.account, presentationData: self.context.sharedContext.currentPresentationData.with { $0 }, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceNode: self.rateButton.referenceNode, shouldBeDismissed: self.dismissedPromise.get())), items: items, reactionItems: [], gesture: gesture)
let contextController = ContextController(account: self.context.account, presentationData: self.context.sharedContext.currentPresentationData.with { $0 }, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceNode: self.rateButton.referenceNode, shouldBeDismissed: self.dismissedPromise.get())), items: items |> map { ContextController.Items(items: $0) }, reactionItems: [], gesture: gesture)
self.presentInGlobalOverlay?(contextController)
}

View File

@ -1787,7 +1787,7 @@ public final class VoiceChatController: ViewController {
dismissPromise.set(true)
}
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData.withUpdated(theme: strongSelf.darkTheme), source: .extracted(source), items: items, reactionItems: [], gesture: gesture)
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData.withUpdated(theme: strongSelf.darkTheme), source: .extracted(source), items: items |> map { ContextController.Items(items: $0) }, reactionItems: [], gesture: gesture)
contextController.useComplexItemsTransitionAnimation = true
strongSelf.controller?.presentInGlobalOverlay(contextController)
}, getPeerVideo: { [weak self] endpointId, position in
@ -2444,7 +2444,7 @@ public final class VoiceChatController: ViewController {
private func openSettingsMenu(sourceNode: ASDisplayNode, gesture: ContextGesture?) {
let items: Signal<[ContextMenuItem], NoError> = self.contextMenuMainItems()
if let controller = self.controller {
let contextController = ContextController(account: self.context.account, presentationData: self.presentationData.withUpdated(theme: self.darkTheme), source: .reference(VoiceChatContextReferenceContentSource(controller: controller, sourceNode: self.optionsButton.referenceNode)), items: items, reactionItems: [], gesture: gesture)
let contextController = ContextController(account: self.context.account, presentationData: self.presentationData.withUpdated(theme: self.darkTheme), source: .reference(VoiceChatContextReferenceContentSource(controller: controller, sourceNode: self.optionsButton.referenceNode)), items: items |> map { ContextController.Items(items: $0) }, reactionItems: [], gesture: gesture)
controller.presentInGlobalOverlay(contextController)
}
}
@ -2473,7 +2473,7 @@ public final class VoiceChatController: ViewController {
guard let strongSelf = self else {
return
}
c.setItems(strongSelf.contextMenuDisplayAsItems(), minHeight: nil)
c.setItems(strongSelf.contextMenuDisplayAsItems() |> map { ContextController.Items(items: $0) }, minHeight: nil)
})))
items.append(.separator)
break
@ -2506,7 +2506,7 @@ public final class VoiceChatController: ViewController {
guard let strongSelf = self else {
return
}
c.setItems(strongSelf.contextMenuAudioItems(), minHeight: nil)
c.setItems(strongSelf.contextMenuAudioItems() |> map { ContextController.Items(items: $0) }, minHeight: nil)
})))
}
@ -2543,7 +2543,7 @@ public final class VoiceChatController: ViewController {
guard let strongSelf = self else {
return
}
c.setItems(strongSelf.contextMenuPermissionItems(), minHeight: nil)
c.setItems(strongSelf.contextMenuPermissionItems() |> map { ContextController.Items(items: $0) }, minHeight: nil)
})))
}
}
@ -2803,7 +2803,7 @@ public final class VoiceChatController: ViewController {
guard let strongSelf = self else {
return
}
c.setItems(strongSelf.contextMenuMainItems(), minHeight: nil)
c.setItems(strongSelf.contextMenuMainItems() |> map { ContextController.Items(items: $0) }, minHeight: nil)
})))
return .single(items)
}
@ -2898,7 +2898,7 @@ public final class VoiceChatController: ViewController {
guard let strongSelf = self else {
return
}
c.setItems(strongSelf.contextMenuMainItems(), minHeight: nil)
c.setItems(strongSelf.contextMenuMainItems() |> map { ContextController.Items(items: $0) }, minHeight: nil)
})))
return items
}
@ -2944,7 +2944,7 @@ public final class VoiceChatController: ViewController {
guard let strongSelf = self else {
return
}
c.setItems(strongSelf.contextMenuMainItems(), minHeight: nil)
c.setItems(strongSelf.contextMenuMainItems() |> map { ContextController.Items(items: $0) }, minHeight: nil)
})))
}
return .single(items)

View File

@ -924,10 +924,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
break
}
}
let _ = combineLatest(queue: .mainQueue(), contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState: strongSelf.presentationInterfaceState, context: strongSelf.context, messages: updatedMessages, controllerInteraction: strongSelf.controllerInteraction, selectAll: selectAll, interfaceInteraction: strongSelf.interfaceInteraction), strongSelf.context.engine.stickers.loadedStickerPack(reference: .animatedEmoji, forceActualized: false), ApplicationSpecificNotice.getChatTextSelectionTips(accountManager: strongSelf.context.sharedContext.accountManager),
ApplicationSpecificNotice.getMessageViewsPrivacyTips(accountManager: strongSelf.context.sharedContext.accountManager)
).start(next: { actions, animatedEmojiStickers, chatTextSelectionTips, messageViewsPrivacyTips in
guard let strongSelf = self, !actions.isEmpty else {
let _ = combineLatest(queue: .mainQueue(), contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState: strongSelf.presentationInterfaceState, context: strongSelf.context, messages: updatedMessages, controllerInteraction: strongSelf.controllerInteraction, selectAll: selectAll, interfaceInteraction: strongSelf.interfaceInteraction), strongSelf.context.engine.stickers.loadedStickerPack(reference: .animatedEmoji, forceActualized: false), ApplicationSpecificNotice.getChatTextSelectionTips(accountManager: strongSelf.context.sharedContext.accountManager)
).start(next: { actions, animatedEmojiStickers, chatTextSelectionTips in
var actions = actions
guard let strongSelf = self, !actions.items.isEmpty else {
return
}
var reactionItems: [ReactionContextItem] = []
@ -961,19 +962,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
var tip: ContextController.Tip?
for action in actions {
switch action {
case let .custom(item, _):
if item is ChatReadReportContextItem {
if messageViewsPrivacyTips < 3 {
tip = .messageViewsPrivacy
let _ = ApplicationSpecificNotice.incrementMessageViewsPrivacyTips(accountManager: strongSelf.context.sharedContext.accountManager).start()
}
}
default:
break
}
}
if tip == nil {
let numberOfComponents = message.text.components(separatedBy: CharacterSet.whitespacesAndNewlines).count
@ -983,8 +971,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
tip = .textSelection
}
}
if actions.tip == nil {
actions.tip = tip
}
let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, postbox: strongSelf.context.account.postbox, message: message, selectAll: selectAll)), items: .single(actions), reactionItems: reactionItems, recognizer: recognizer, gesture: gesture, tip: tip)
let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, postbox: strongSelf.context.account.postbox, message: message, selectAll: selectAll)), items: .single(actions), reactionItems: reactionItems, recognizer: recognizer, gesture: gesture)
strongSelf.currentContextController = controller
controller.reactionSelected = { [weak controller] value in
guard let strongSelf = self, let message = updatedMessages.first else {
@ -2240,7 +2232,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
})))
let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, postbox: strongSelf.context.account.postbox, message: message, selectAll: true)), items: .single(actions), reactionItems: [], recognizer: nil)
let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, postbox: strongSelf.context.account.postbox, message: message, selectAll: true)), items: .single(ContextController.Items(items: actions)), reactionItems: [], recognizer: nil)
strongSelf.currentContextController = controller
strongSelf.forEachController({ controller in
if let controller = controller as? TooltipScreen {
@ -2317,7 +2309,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
f(.dismissWithoutContent)
})))
let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, postbox: strongSelf.context.account.postbox, message: topMessage, selectAll: true)), items: .single(actions), reactionItems: [], recognizer: nil)
let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, postbox: strongSelf.context.account.postbox, message: topMessage, selectAll: true)), items: .single(ContextController.Items(items: actions)), reactionItems: [], recognizer: nil)
strongSelf.currentContextController = controller
strongSelf.forEachController({ controller in
if let controller = controller as? TooltipScreen {
@ -2760,7 +2752,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return items
}
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: galleryController, sourceNode: node, passthroughTouches: false)), items: items, reactionItems: [], gesture: gesture)
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: galleryController, sourceNode: node, passthroughTouches: false)), items: items |> map { ContextController.Items(items: $0) }, reactionItems: [], gesture: gesture)
strongSelf.presentInGlobalOverlay(contextController)
})
}, openMessageReplies: { [weak self] messageId, isChannelPost, displayModalProgress in
@ -2992,7 +2984,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return items
}
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: galleryController, sourceNode: node, passthroughTouches: false)), items: items, reactionItems: [], gesture: gesture)
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: galleryController, sourceNode: node, passthroughTouches: false)), items: items |> map { ContextController.Items(items: $0) }, reactionItems: [], gesture: gesture)
strongSelf.presentInGlobalOverlay(contextController)
}
chatInfoButtonItem = UIBarButtonItem(customDisplayNode: avatarNode)!
@ -5019,41 +5011,49 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let duration: Double = strongSelf.chatDisplayNode.messageTransitionNode.hasScheduledTransitions ? ChatMessageTransitionNode.animationDuration : 0.18
let curve: ContainedViewLayoutTransitionCurve = strongSelf.chatDisplayNode.messageTransitionNode.hasScheduledTransitions ? ChatMessageTransitionNode.verticalAnimationCurve : .easeInOut
let controlPoints: (Float, Float, Float, Float) = strongSelf.chatDisplayNode.messageTransitionNode.hasScheduledTransitions ? ChatMessageTransitionNode.verticalAnimationControlPoints : (0.5, 0.33, 0.0, 0.0)
let shouldUseFastMessageSendAnimation = strongSelf.chatDisplayNode.shouldUseFastMessageSendAnimation
strongSelf.chatDisplayNode.containerLayoutUpdated(validLayout, navigationBarHeight: strongSelf.navigationLayout(layout: validLayout).navigationFrame.maxY, transition: .animated(duration: duration, curve: curve), listViewTransaction: { updateSizeAndInsets, _, _, _ in
var options = transition.options
let _ = options.insert(.Synchronous)
let _ = options.insert(.LowLatency)
let _ = options.insert(.PreferSynchronousResourceLoading)
options.remove(.AnimateInsertion)
options.insert(.RequestItemInsertionAnimations)
let deleteItems = transition.deleteItems.map({ item in
return ListViewDeleteItem(index: item.index, directionHint: nil)
})
var maxInsertedItem: Int?
var insertedIndex: Int?
var deleteItems = transition.deleteItems
var insertItems: [ListViewInsertItem] = []
for i in 0 ..< transition.insertItems.count {
let item = transition.insertItems[i]
if item.directionHint == .Down && (maxInsertedItem == nil || maxInsertedItem! < item.index) {
maxInsertedItem = item.index
}
insertedIndex = item.index
insertItems.append(ListViewInsertItem(index: item.index, previousIndex: item.previousIndex, item: item.item, directionHint: item.directionHint == .Down ? .Up : nil))
}
var scrollToItem: ListViewScrollToItem?
if isScheduledMessages, let insertedIndex = insertedIndex {
scrollToItem = ListViewScrollToItem(index: insertedIndex, position: .visible, animated: true, curve: .Custom(duration: duration, controlPoints.0, controlPoints.1, controlPoints.2, controlPoints.3), directionHint: .Down)
} else if transition.historyView.originalView.laterId == nil {
scrollToItem = ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Custom(duration: duration, controlPoints.0, controlPoints.1, controlPoints.2, controlPoints.3), directionHint: .Up)
}
var stationaryItemRange: (Int, Int)?
if let maxInsertedItem = maxInsertedItem {
stationaryItemRange = (maxInsertedItem + 1, Int.max)
var scrollToItem: ListViewScrollToItem?
if shouldUseFastMessageSendAnimation {
options.remove(.AnimateInsertion)
options.insert(.RequestItemInsertionAnimations)
deleteItems = transition.deleteItems.map({ item in
return ListViewDeleteItem(index: item.index, directionHint: nil)
})
var maxInsertedItem: Int?
var insertedIndex: Int?
for i in 0 ..< transition.insertItems.count {
let item = transition.insertItems[i]
if item.directionHint == .Down && (maxInsertedItem == nil || maxInsertedItem! < item.index) {
maxInsertedItem = item.index
}
insertedIndex = item.index
insertItems.append(ListViewInsertItem(index: item.index, previousIndex: item.previousIndex, item: item.item, directionHint: item.directionHint == .Down ? .Up : nil))
}
if isScheduledMessages, let insertedIndex = insertedIndex {
scrollToItem = ListViewScrollToItem(index: insertedIndex, position: .visible, animated: true, curve: .Custom(duration: duration, controlPoints.0, controlPoints.1, controlPoints.2, controlPoints.3), directionHint: .Down)
} else if transition.historyView.originalView.laterId == nil {
scrollToItem = ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Custom(duration: duration, controlPoints.0, controlPoints.1, controlPoints.2, controlPoints.3), directionHint: .Up)
}
if let maxInsertedItem = maxInsertedItem {
stationaryItemRange = (maxInsertedItem + 1, Int.max)
}
}
mappedTransition = (ChatHistoryListViewTransition(historyView: transition.historyView, deleteItems: deleteItems, insertItems: insertItems, updateItems: transition.updateItems, options: options, scrollToItem: scrollToItem, stationaryItemRange: stationaryItemRange, initialData: transition.initialData, keyboardButtonsMessage: transition.keyboardButtonsMessage, cachedData: transition.cachedData, cachedDataMessages: transition.cachedDataMessages, readStateData: transition.readStateData, scrolledToIndex: transition.scrolledToIndex, scrolledToSomeIndex: transition.scrolledToSomeIndex, peerType: transition.peerType, networkType: transition.networkType, animateIn: false, reason: transition.reason, flashIndicators: transition.flashIndicators), updateSizeAndInsets)
@ -5769,7 +5769,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return items
}
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: sourceNode, passthroughTouches: true)), items: items, reactionItems: [])
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: sourceNode, passthroughTouches: true)), items: items |> map { ContextController.Items(items: $0) }, reactionItems: [])
contextController.dismissedForCancel = { [weak chatController] in
if let selectedMessageIds = (chatController as? ChatControllerImpl)?.selectedMessageIds {
var forwardMessageIds = strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds ?? []
@ -6523,7 +6523,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
})
})))
contextController.setItems(.single(contextItems), minHeight: nil)
contextController.setItems(.single(ContextController.Items(items: contextItems)), minHeight: nil)
}
return
} else {
@ -6542,7 +6542,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
})
})))
contextController.setItems(.single(contextItems), minHeight: nil)
contextController.setItems(.single(ContextController.Items(items: contextItems)), minHeight: nil)
return
} else {
@ -7277,7 +7277,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peerId), subject: .pinnedMessages(id: pinnedMessage.message.id), botStart: nil, mode: .standard(previewing: true))
chatController.canReadHistory.set(false)
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, passthroughTouches: true)), items: .single(items), reactionItems: [], gesture: gesture)
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, passthroughTouches: true)), items: .single(ContextController.Items(items: items)), reactionItems: [], gesture: gesture)
strongSelf.presentInGlobalOverlay(contextController)
}, joinGroupCall: { [weak self] activeCall in
guard let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else {
@ -12655,7 +12655,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
if canDisplayContextMenu, let contextController = contextController {
contextController.setItems(.single(contextItems), minHeight: nil)
contextController.setItems(.single(ContextController.Items(items: contextItems)), minHeight: nil)
} else {
actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [
ActionSheetButtonItem(title: self.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in

View File

@ -2578,6 +2578,19 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
return false
}
var hasAd = false
self.historyNode.forEachVisibleItemNode { itemNode in
if let itemNode = itemNode as? ChatMessageItemView {
if let _ = itemNode.item?.message.adAttribute {
hasAd = true
}
}
}
if hasAd {
return false
}
switch self.historyNode.visibleContentOffset() {
case let .known(value) where value < 20.0:
return true
@ -2588,6 +2601,23 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
}
}
var shouldUseFastMessageSendAnimation: Bool {
var hasAd = false
self.historyNode.forEachVisibleItemNode { itemNode in
if let itemNode = itemNode as? ChatMessageItemView {
if let _ = itemNode.item?.message.adAttribute {
hasAd = true
}
}
}
if hasAd {
return false
}
return true
}
var shouldAllowOverscrollActions: Bool {
if let inputHeight = self.validLayout?.0.inputHeight, inputHeight > 0.0 {
return false

View File

@ -22,6 +22,7 @@ import ShimmerEffect
import AnimatedAvatarSetNode
import AvatarNode
import AdUI
import TelegramNotices
private struct MessageContextMenuData {
let starStatus: Bool?
@ -368,9 +369,9 @@ func updatedChatEditInterfaceMessageState(state: ChatPresentationInterfaceState,
return updated
}
func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState: ChatPresentationInterfaceState, context: AccountContext, messages: [Message], controllerInteraction: ChatControllerInteraction?, selectAll: Bool, interfaceInteraction: ChatPanelInterfaceInteraction?, readStats: MessageReadStats? = nil) -> Signal<[ContextMenuItem], NoError> {
func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState: ChatPresentationInterfaceState, context: AccountContext, messages: [Message], controllerInteraction: ChatControllerInteraction?, selectAll: Bool, interfaceInteraction: ChatPanelInterfaceInteraction?, readStats: MessageReadStats? = nil) -> Signal<ContextController.Items, NoError> {
guard let interfaceInteraction = interfaceInteraction, let controllerInteraction = controllerInteraction else {
return .single([])
return .single(ContextController.Items(items: []))
}
if messages.count == 1, let _ = messages[0].adAttribute {
@ -437,7 +438,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
})))
}
return .single(actions)
return .single(ContextController.Items(items: actions))
}
var loadStickerSaveStatus: MediaId?
@ -551,7 +552,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
return transaction.getCombinedPeerReadState(messages[0].id.peerId)
}
let dataSignal: Signal<(MessageContextMenuData, [MessageId: ChatUpdatingMessageMedia], CachedPeerData?, AppConfiguration, Bool), NoError> = combineLatest(
let dataSignal: Signal<(MessageContextMenuData, [MessageId: ChatUpdatingMessageMedia], CachedPeerData?, AppConfiguration, Bool, Int32), NoError> = combineLatest(
loadLimits,
loadStickerSaveStatusSignal,
loadResourceStatusSignal,
@ -559,9 +560,10 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
context.account.pendingUpdateMessageManager.updatingMessageMedia
|> take(1),
cachedData,
readState
readState,
ApplicationSpecificNotice.getMessageViewsPrivacyTips(accountManager: context.sharedContext.accountManager)
)
|> map { limitsAndAppConfig, stickerSaveStatus, resourceStatus, messageActions, updatingMessageMedia, cachedData, readState -> (MessageContextMenuData, [MessageId: ChatUpdatingMessageMedia], CachedPeerData?, AppConfiguration, Bool) in
|> map { limitsAndAppConfig, stickerSaveStatus, resourceStatus, messageActions, updatingMessageMedia, cachedData, readState, messageViewsPrivacyTips -> (MessageContextMenuData, [MessageId: ChatUpdatingMessageMedia], CachedPeerData?, AppConfiguration, Bool, Int32) in
let (limitsConfiguration, appConfig) = limitsAndAppConfig
var canEdit = false
if !isAction {
@ -574,12 +576,12 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
isMessageRead = readState.isOutgoingMessageIndexRead(message.index)
}
return (MessageContextMenuData(starStatus: stickerSaveStatus, canReply: canReply, canPin: canPin, canEdit: canEdit, canSelect: canSelect, resourceStatus: resourceStatus, messageActions: messageActions), updatingMessageMedia, cachedData, appConfig, isMessageRead)
return (MessageContextMenuData(starStatus: stickerSaveStatus, canReply: canReply, canPin: canPin, canEdit: canEdit, canSelect: canSelect, resourceStatus: resourceStatus, messageActions: messageActions), updatingMessageMedia, cachedData, appConfig, isMessageRead, messageViewsPrivacyTips)
}
return dataSignal
|> deliverOnMainQueue
|> map { data, updatingMessageMedia, cachedData, appConfig, isMessageRead -> [ContextMenuItem] in
|> map { data, updatingMessageMedia, cachedData, appConfig, isMessageRead, messageViewsPrivacyTips -> ContextController.Items in
var actions: [ContextMenuItem] = []
var isPinnedMessages = false
@ -1208,6 +1210,8 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
controller.setItems(contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState: chatPresentationInterfaceState, context: context, messages: messages, controllerInteraction: controllerInteraction, selectAll: selectAll, interfaceInteraction: interfaceInteraction, readStats: stats), minHeight: nil, previousActionsTransition: .slide(forward: false))
})))
subActions.append(.separator)
for peer in stats.peers {
let avatarSignal = peerAvatarCompleteImage(account: context.account, peer: peer._asPeer(), size: CGSize(width: 30.0, height: 30.0))
@ -1218,8 +1222,14 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
})))
}
var tip: ContextController.Tip?
if messageViewsPrivacyTips < 3 {
tip = .messageViewsPrivacy
let _ = ApplicationSpecificNotice.incrementMessageViewsPrivacyTips(accountManager: context.sharedContext.accountManager).start()
}
let minHeight = c.getActionsMinHeight()
c.setItems(.single(subActions), minHeight: minHeight, previousActionsTransition: .slide(forward: true))
c.setItems(.single(ContextController.Items(items: subActions, tip: tip)), minHeight: minHeight, previousActionsTransition: .slide(forward: true))
} else {
f(.default)
}
@ -1227,7 +1237,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
}
}
return actions
return ContextController.Items(items: actions, tip: nil)
}
}

View File

@ -1458,7 +1458,7 @@ final class ChatMediaInputNode: ChatInputNode {
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
let contextController = ContextController(account: strongSelf.context.account, presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: gallery, sourceNode: sourceNode, sourceRect: sourceRect)), items: .single(items), reactionItems: [], gesture: gesture)
let contextController = ContextController(account: strongSelf.context.account, presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: gallery, sourceNode: sourceNode, sourceRect: sourceRect)), items: .single(ContextController.Items(items: items)), reactionItems: [], gesture: gesture)
strongSelf.controllerInteraction.presentGlobalOverlayController(contextController, nil)
})
}

View File

@ -207,7 +207,7 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe
if let message = peer.messages.first {
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peer.peer.peerId), subject: .message(id: message.id, highlight: true, timecode: nil), botStart: nil, mode: .standard(previewing: true))
chatController.canReadHistory.set(false)
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: .single([]), reactionItems: [], gesture: gesture)
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: .single(ContextController.Items(items: [])), reactionItems: [], gesture: gesture)
presentInGlobalOverlay(contextController)
} else {
gesture?.cancel()

View File

@ -1797,7 +1797,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
let presentationData = strongSelf.presentationData
let peerId = strongSelf.peerId
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_ContextMenuDelete, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { c, _ in
c.setItems(context.account.postbox.transaction { transaction -> [ContextMenuItem] in
c.setItems(context.account.postbox.transaction { transaction -> ContextController.Items in
var items: [ContextMenuItem] = []
let messageIds = [message.id]
@ -1849,7 +1849,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}
}
return items
return ContextController.Items(items: items)
}, minHeight: nil)
})))
}
@ -1866,7 +1866,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
})))
}
let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(MessageContextExtractedContentSource(sourceNode: node)), items: .single(items), reactionItems: [], recognizer: nil, gesture: gesture)
let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(MessageContextExtractedContentSource(sourceNode: node)), items: .single(ContextController.Items(items: items)), reactionItems: [], recognizer: nil, gesture: gesture)
strongSelf.controller?.window?.presentInGlobalOverlay(controller)
})
}, activateMessagePinch: { _ in
@ -1931,7 +1931,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
if actions.options.contains(.deleteLocally) || actions.options.contains(.deleteGlobally) {
items.append(.action(ContextMenuActionItem(text: strings.Conversation_ContextMenuDelete, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { c, f in
c.setItems(context.account.postbox.transaction { transaction -> [ContextMenuItem] in
c.setItems(context.account.postbox.transaction { transaction -> ContextController.Items in
var items: [ContextMenuItem] = []
let messageIds = [message.id]
@ -1983,7 +1983,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}
}
return items
return ContextController.Items(items: items)
}, minHeight: nil)
})))
}
@ -2006,7 +2006,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
switch previewData {
case let .gallery(gallery):
gallery.setHintWillBePresentedInPreviewingContext(true)
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: gallery, sourceNode: node)), items: items, reactionItems: [], gesture: gesture)
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: gallery, sourceNode: node)), items: items |> map { ContextController.Items(items: $0) }, reactionItems: [], gesture: gesture)
strongSelf.controller?.presentInGlobalOverlay(contextController)
case .instantPage:
break
@ -2226,7 +2226,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
self?.chatInterfaceInteraction.openPeer(peer.id, .default, nil)
}))
]
let contextController = ContextController(account: strongSelf.context.account, presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: .single(items), reactionItems: [], gesture: gesture)
let contextController = ContextController(account: strongSelf.context.account, presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: .single(ContextController.Items(items: items)), reactionItems: [], gesture: gesture)
controller.presentInGlobalOverlay(contextController)
}
@ -2832,7 +2832,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}, synchronousLoad: true)
galleryController.setHintWillBePresentedInPreviewingContext(true)
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: galleryController, sourceNode: node)), items: .single(items), reactionItems: [], gesture: gesture)
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: galleryController, sourceNode: node)), items: .single(ContextController.Items(items: items)), reactionItems: [], gesture: gesture)
strongSelf.controller?.presentInGlobalOverlay(contextController)
}
@ -3458,7 +3458,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
self.view.endEditing(true)
if let sourceNode = self.headerNode.buttonNodes[.mute]?.referenceNode {
let contextController = ContextController(account: self.context.account, presentationData: self.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: sourceNode)), items: .single(items), reactionItems: [], gesture: gesture)
let contextController = ContextController(account: self.context.account, presentationData: self.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: sourceNode)), items: .single(ContextController.Items(items: items)), reactionItems: [], gesture: gesture)
contextController.dismissed = { [weak self] in
if let strongSelf = self {
strongSelf.state = strongSelf.state.withHighlightedButton(nil)
@ -3689,7 +3689,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}, action: { [weak self] c, f in
self?.openReport(user: false, contextController: c, backAction: { c in
if let mainItemsImpl = mainItemsImpl {
c.setItems(mainItemsImpl(), minHeight: nil)
c.setItems(mainItemsImpl() |> map { ContextController.Items(items: $0) }, minHeight: nil)
}
})
})))
@ -3770,7 +3770,8 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
self.view.endEditing(true)
if let sourceNode = self.headerNode.buttonNodes[.more]?.referenceNode {
let contextController = ContextController(account: self.context.account, presentationData: self.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: sourceNode)), items: mainItemsImpl?() ?? .single([]), reactionItems: [], gesture: gesture)
let items = mainItemsImpl?() ?? .single([])
let contextController = ContextController(account: self.context.account, presentationData: self.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: sourceNode)), items: items |> map { ContextController.Items(items: $0) }, reactionItems: [], gesture: gesture)
contextController.dismissed = { [weak self] in
if let strongSelf = self {
strongSelf.state = strongSelf.state.withHighlightedButton(nil)
@ -4431,7 +4432,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
})))
if let contextController = contextController {
contextController.setItems(.single(items), minHeight: nil)
contextController.setItems(.single(ContextController.Items(items: items)), minHeight: nil)
} else {
strongSelf.state = strongSelf.state.withHighlightedButton(.voiceChat)
if let (layout, navigationHeight) = strongSelf.validLayout {
@ -4439,7 +4440,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}
if let sourceNode = strongSelf.headerNode.buttonNodes[.voiceChat]?.referenceNode, let controller = strongSelf.controller {
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: sourceNode)), items: .single(items), reactionItems: [], gesture: gesture)
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: sourceNode)), items: .single(ContextController.Items(items: items)), reactionItems: [], gesture: gesture)
contextController.dismissed = { [weak self] in
if let strongSelf = self {
strongSelf.state = strongSelf.state.withHighlightedButton(nil)
@ -4527,7 +4528,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}
if let contextController = contextController {
contextController.setItems(.single(items), minHeight: nil)
contextController.setItems(.single(ContextController.Items(items: items)), minHeight: nil)
} else {
strongSelf.state = strongSelf.state.withHighlightedButton(.voiceChat)
if let (layout, navigationHeight) = strongSelf.validLayout {
@ -4535,7 +4536,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}
if let sourceNode = strongSelf.headerNode.buttonNodes[.voiceChat]?.referenceNode, let controller = strongSelf.controller {
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: sourceNode)), items: .single(items), reactionItems: [], gesture: gesture)
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: sourceNode)), items: .single(ContextController.Items(items: items)), reactionItems: [], gesture: gesture)
contextController.dismissed = { [weak self] in
if let strongSelf = self {
strongSelf.state = strongSelf.state.withHighlightedButton(nil)
@ -5608,7 +5609,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
let contextController = ContextController(account: accountContext.account, presentationData: self.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatListController, sourceNode: node)), items: accountContextMenuItems(context: accountContext, logout: { [weak self] in
self?.logoutAccount(id: id)
}), reactionItems: [], gesture: gesture)
}) |> map { ContextController.Items(items: $0) }, reactionItems: [], gesture: gesture)
self.controller?.presentInGlobalOverlay(contextController)
} else {
gesture?.cancel()
@ -6907,7 +6908,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen {
})))
}
let controller = ContextController(account: primary.0.account, presentationData: self.presentationData, source: .extracted(SettingsTabBarContextExtractedContentSource(controller: self, sourceNode: sourceNode)), items: .single(items), reactionItems: [], recognizer: nil, gesture: gesture)
let controller = ContextController(account: primary.0.account, presentationData: self.presentationData, source: .extracted(SettingsTabBarContextExtractedContentSource(controller: self, sourceNode: sourceNode)), items: .single(ContextController.Items(items: items)), reactionItems: [], recognizer: nil, gesture: gesture)
self.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller)
}
}