mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
157594469f
@ -6778,7 +6778,7 @@ Telegram offers free and unlimited service to hundreds of millions of users, whi
|
|||||||
[url]
|
[url]
|
||||||
Ads should no longer be synonymous with abuse of user privacy. Let us redefine how a tech compony should operate — together.";
|
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.Action" = "Learn More";
|
||||||
"SponsoredMessageInfo.ActionUrl" = "https://telegram.org/ads";
|
"SponsoredMessageInfo.Url" = "https://telegram.org/ads";
|
||||||
|
|
||||||
"Chat.NavigationNoChannels" = "You have no unread channels";
|
"Chat.NavigationNoChannels" = "You have no unread channels";
|
||||||
|
|
||||||
@ -6817,3 +6817,5 @@ Ads should no longer be synonymous with abuse of user privacy. Let us redefine h
|
|||||||
|
|
||||||
"VideoChat.RecordingSaved" = "Video chat recording saved to **Saved Messages**.";
|
"VideoChat.RecordingSaved" = "Video chat recording saved to **Saved Messages**.";
|
||||||
"LiveStream.RecordingSaved" = "Live stream recording saved to **Saved Messages**.";
|
"LiveStream.RecordingSaved" = "Live stream recording saved to **Saved Messages**.";
|
||||||
|
|
||||||
|
"ChatContextMenu.MessageViewsPrivacyTip" = "To protect privacy, views are only stored for 7 days.";
|
||||||
|
@ -106,14 +106,14 @@ public final class AdInfoScreen: ViewController {
|
|||||||
|
|
||||||
if !didAddUrl {
|
if !didAddUrl {
|
||||||
didAddUrl = true
|
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?()
|
openUrl?()
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !didAddUrl {
|
if !didAddUrl {
|
||||||
didAddUrl = true
|
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?()
|
openUrl?()
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
@ -138,7 +138,7 @@ public final class AdInfoScreen: ViewController {
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.context.sharedContext.applicationBindings.openUrl(strongSelf.presentationData.strings.SponsoredMessageInfo_ActionUrl)
|
strongSelf.context.sharedContext.applicationBindings.openUrl(strongSelf.presentationData.strings.SponsoredMessageInfo_Url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
self.presentInGlobalOverlay(contextController)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,10 +250,10 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
|
|||||||
updatedItems.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_Back, icon: { theme in
|
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)
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.contextMenu.primaryColor)
|
||||||
}, action: { c, _ in
|
}, 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)
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -839,12 +839,12 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
case let .groupReference(groupId, _, _, _, _):
|
case let .groupReference(groupId, _, _, _, _):
|
||||||
let chatListController = ChatListControllerImpl(context: strongSelf.context, groupId: groupId, controlsHistoryPreload: false, hideNetworkActivityStatus: true, previewing: true, enableDebugActions: false)
|
let chatListController = ChatListControllerImpl(context: strongSelf.context, groupId: groupId, controlsHistoryPreload: false, hideNetworkActivityStatus: true, previewing: true, enableDebugActions: false)
|
||||||
chatListController.navigationPresentation = .master
|
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)
|
strongSelf.presentInGlobalOverlay(contextController)
|
||||||
case let .peer(_, peer, _, _, _, _, _, _, promoInfo, _, _, _):
|
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))
|
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)
|
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)
|
strongSelf.presentInGlobalOverlay(contextController)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -868,7 +868,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
contextContentSource = .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController))
|
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)
|
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)
|
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)
|
strongSelf.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -737,7 +737,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
|||||||
return items
|
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)
|
self.presentInGlobalOverlay?(controller, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -797,7 +797,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
|||||||
switch previewData {
|
switch previewData {
|
||||||
case let .gallery(gallery):
|
case let .gallery(gallery):
|
||||||
gallery.setHintWillBePresentedInPreviewingContext(true)
|
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)
|
strongSelf.presentInGlobalOverlay?(contextController, nil)
|
||||||
case .instantPage:
|
case .instantPage:
|
||||||
break
|
break
|
||||||
|
@ -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))
|
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)
|
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)
|
contactsController.presentInGlobalOverlay(contextController)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,20 +324,26 @@ private final class InnerTextSelectionTipContainerNode: ASDisplayNode {
|
|||||||
private let iconNode: ASImageNode
|
private let iconNode: ASImageNode
|
||||||
|
|
||||||
private let text: String
|
private let text: String
|
||||||
private let targetSelectionIndex: Int
|
private let targetSelectionIndex: Int?
|
||||||
|
|
||||||
init(presentationData: PresentationData) {
|
init(presentationData: PresentationData, tip: ContextController.Tip) {
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
self.textNode = TextNode()
|
self.textNode = TextNode()
|
||||||
|
|
||||||
var rawText = self.presentationData.strings.ChatContextMenu_TextSelectionTip
|
switch tip {
|
||||||
if let range = rawText.range(of: "|") {
|
case .textSelection:
|
||||||
rawText.removeSubrange(range)
|
var rawText = self.presentationData.strings.ChatContextMenu_TextSelectionTip
|
||||||
self.text = rawText
|
if let range = rawText.range(of: "|") {
|
||||||
self.targetSelectionIndex = NSRange(range, in: rawText).lowerBound
|
rawText.removeSubrange(range)
|
||||||
} else {
|
self.text = rawText
|
||||||
self.text = rawText
|
self.targetSelectionIndex = NSRange(range, in: rawText).lowerBound
|
||||||
self.targetSelectionIndex = 1
|
} else {
|
||||||
|
self.text = rawText
|
||||||
|
self.targetSelectionIndex = 1
|
||||||
|
}
|
||||||
|
case .messageViewsPrivacy:
|
||||||
|
self.text = self.presentationData.strings.ChatContextMenu_MessageViewsPrivacyTip
|
||||||
|
self.targetSelectionIndex = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
self.iconNode = ASImageNode()
|
self.iconNode = ASImageNode()
|
||||||
@ -430,13 +436,13 @@ private final class InnerTextSelectionTipContainerNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func animateIn() {
|
func animateIn() {
|
||||||
if let textSelectionNode = self.textSelectionNode {
|
if let textSelectionNode = self.textSelectionNode, let targetSelectionIndex = self.targetSelectionIndex {
|
||||||
textSelectionNode.pretendInitiateSelection()
|
textSelectionNode.pretendInitiateSelection()
|
||||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.5, execute: { [weak self] in
|
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.5, execute: { [weak self] in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.textSelectionNode?.pretendExtendSelection(to: strongSelf.targetSelectionIndex)
|
strongSelf.textSelectionNode?.pretendExtendSelection(to: targetSelectionIndex)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -463,7 +469,7 @@ final class ContextActionsContainerNode: ASDisplayNode {
|
|||||||
return self.additionalActionsNode != nil
|
return self.additionalActionsNode != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
init(presentationData: PresentationData, items: [ContextMenuItem], getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void, feedbackTap: @escaping () -> Void, displayTextSelectionTip: Bool, blurBackground: Bool) {
|
init(presentationData: PresentationData, items: ContextController.Items, getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void, feedbackTap: @escaping () -> Void, blurBackground: Bool) {
|
||||||
self.blurBackground = blurBackground
|
self.blurBackground = blurBackground
|
||||||
self.shadowNode = ASImageNode()
|
self.shadowNode = ASImageNode()
|
||||||
self.shadowNode.displaysAsynchronously = false
|
self.shadowNode.displaysAsynchronously = false
|
||||||
@ -473,7 +479,7 @@ final class ContextActionsContainerNode: ASDisplayNode {
|
|||||||
self.shadowNode.isHidden = true
|
self.shadowNode.isHidden = true
|
||||||
|
|
||||||
var items = items
|
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()
|
let additionalShadowNode = ASImageNode()
|
||||||
additionalShadowNode.displaysAsynchronously = false
|
additionalShadowNode.displaysAsynchronously = false
|
||||||
additionalShadowNode.displayWithoutProcessing = true
|
additionalShadowNode.displayWithoutProcessing = true
|
||||||
@ -483,15 +489,15 @@ final class ContextActionsContainerNode: ASDisplayNode {
|
|||||||
self.additionalShadowNode = additionalShadowNode
|
self.additionalShadowNode = additionalShadowNode
|
||||||
|
|
||||||
self.additionalActionsNode = InnerActionsContainerNode(presentationData: presentationData, items: [firstItem], getController: getController, actionSelected: actionSelected, feedbackTap: feedbackTap, blurBackground: blurBackground)
|
self.additionalActionsNode = InnerActionsContainerNode(presentationData: presentationData, items: [firstItem], getController: getController, actionSelected: actionSelected, feedbackTap: feedbackTap, blurBackground: blurBackground)
|
||||||
items.removeFirst()
|
items.items.removeFirst()
|
||||||
} else {
|
} else {
|
||||||
self.additionalShadowNode = nil
|
self.additionalShadowNode = nil
|
||||||
self.additionalActionsNode = nil
|
self.additionalActionsNode = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
self.actionsNode = InnerActionsContainerNode(presentationData: presentationData, items: items, getController: getController, actionSelected: actionSelected, feedbackTap: feedbackTap, blurBackground: blurBackground)
|
self.actionsNode = InnerActionsContainerNode(presentationData: presentationData, items: items.items, getController: getController, actionSelected: actionSelected, feedbackTap: feedbackTap, blurBackground: blurBackground)
|
||||||
if displayTextSelectionTip {
|
if let tip = items.tip {
|
||||||
let textSelectionTipNode = InnerTextSelectionTipContainerNode(presentationData: presentationData)
|
let textSelectionTipNode = InnerTextSelectionTipContainerNode(presentationData: presentationData, tip: tip)
|
||||||
textSelectionTipNode.isUserInteractionEnabled = false
|
textSelectionTipNode.isUserInteractionEnabled = false
|
||||||
self.textSelectionTipNode = textSelectionTipNode
|
self.textSelectionTipNode = textSelectionTipNode
|
||||||
} else {
|
} else {
|
||||||
|
@ -15,8 +15,8 @@ public protocol ContextControllerProtocol {
|
|||||||
var immediateItemsTransitionAnimation: Bool { get set }
|
var immediateItemsTransitionAnimation: Bool { get set }
|
||||||
|
|
||||||
func getActionsMinHeight() -> CGFloat?
|
func getActionsMinHeight() -> CGFloat?
|
||||||
func setItems(_ items: Signal<[ContextMenuItem], NoError>, minHeight: CGFloat?)
|
func setItems(_ items: Signal<ContextController.Items, NoError>, minHeight: CGFloat?)
|
||||||
func setItems(_ items: Signal<[ContextMenuItem], NoError>, minHeight: CGFloat?, previousActionsTransition: ContextController.PreviousActionsTransition)
|
func setItems(_ items: Signal<ContextController.Items, NoError>, minHeight: CGFloat?, previousActionsTransition: ContextController.PreviousActionsTransition)
|
||||||
func dismiss(completion: (() -> Void)?)
|
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 final class ContextControllerNode: ViewControllerTracingNode, UIScrollViewDelegate {
|
||||||
private var presentationData: PresentationData
|
private var presentationData: PresentationData
|
||||||
private let source: ContextContentSource
|
private let source: ContextContentSource
|
||||||
private var items: Signal<[ContextMenuItem], NoError>
|
private var items: Signal<ContextController.Items, NoError>
|
||||||
private let beginDismiss: (ContextMenuActionResult) -> Void
|
private let beginDismiss: (ContextMenuActionResult) -> Void
|
||||||
private let reactionSelected: (ReactionContextItem.Reaction) -> Void
|
private let reactionSelected: (ReactionContextItem.Reaction) -> Void
|
||||||
private let beganAnimatingOut: () -> Void
|
private let beganAnimatingOut: () -> Void
|
||||||
@ -125,13 +125,12 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||||||
fileprivate var dismissedForCancel: (() -> Void)?
|
fileprivate var dismissedForCancel: (() -> Void)?
|
||||||
private let getController: () -> ContextControllerProtocol?
|
private let getController: () -> ContextControllerProtocol?
|
||||||
private weak var gesture: ContextGesture?
|
private weak var gesture: ContextGesture?
|
||||||
private var displayTextSelectionTip: Bool
|
|
||||||
|
|
||||||
private var didSetItemsReady = false
|
private var didSetItemsReady = false
|
||||||
let itemsReady = Promise<Bool>()
|
let itemsReady = Promise<Bool>()
|
||||||
let contentReady = Promise<Bool>()
|
let contentReady = Promise<Bool>()
|
||||||
|
|
||||||
private var currentItems: [ContextMenuItem]?
|
private var currentItems: ContextController.Items?
|
||||||
private var currentActionsMinHeight: CGFloat?
|
private var currentActionsMinHeight: CGFloat?
|
||||||
|
|
||||||
private var validLayout: ContainerViewLayout?
|
private var validLayout: ContainerViewLayout?
|
||||||
@ -169,7 +168,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||||||
|
|
||||||
private let blurBackground: Bool
|
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, displayTextSelectionTip: Bool) {
|
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.presentationData = presentationData
|
||||||
self.source = source
|
self.source = source
|
||||||
self.items = items
|
self.items = items
|
||||||
@ -178,7 +177,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||||||
self.beganAnimatingOut = beganAnimatingOut
|
self.beganAnimatingOut = beganAnimatingOut
|
||||||
self.attemptTransitionControllerIntoNavigation = attemptTransitionControllerIntoNavigation
|
self.attemptTransitionControllerIntoNavigation = attemptTransitionControllerIntoNavigation
|
||||||
self.gesture = gesture
|
self.gesture = gesture
|
||||||
self.displayTextSelectionTip = displayTextSelectionTip
|
|
||||||
|
|
||||||
self.getController = { [weak controller] in
|
self.getController = { [weak controller] in
|
||||||
return controller
|
return controller
|
||||||
@ -231,13 +229,13 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||||||
}
|
}
|
||||||
self.blurBackground = blurBackground
|
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
|
return controller
|
||||||
}, actionSelected: { result in
|
}, actionSelected: { result in
|
||||||
beginDismiss(result)
|
beginDismiss(result)
|
||||||
}, feedbackTap: {
|
}, feedbackTap: {
|
||||||
feedbackTap?()
|
feedbackTap?()
|
||||||
}, displayTextSelectionTip: self.displayTextSelectionTip, blurBackground: blurBackground)
|
}, blurBackground: blurBackground)
|
||||||
|
|
||||||
if !reactionItems.isEmpty {
|
if !reactionItems.isEmpty {
|
||||||
let reactionContextNode = ReactionContextNode(account: account, theme: presentationData.theme, items: reactionItems)
|
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.items = items
|
||||||
self.itemsDisposable.set((items
|
self.itemsDisposable.set((items
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] items in
|
|> 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 {
|
if let _ = self.currentItems, !self.didCompleteAnimationIn && self.getController()?.immediateItemsTransitionAnimation == true {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1199,24 +1197,24 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||||||
self.currentActionsMinHeight = minHeight
|
self.currentActionsMinHeight = minHeight
|
||||||
|
|
||||||
let previousActionsContainerNode = self.actionsContainerNode
|
let previousActionsContainerNode = self.actionsContainerNode
|
||||||
|
let previousActionsContainerFrame = previousActionsContainerNode.view.convert(previousActionsContainerNode.bounds, to: self.view)
|
||||||
self.actionsContainerNode = ContextActionsContainerNode(presentationData: self.presentationData, items: items, getController: { [weak self] in
|
self.actionsContainerNode = ContextActionsContainerNode(presentationData: self.presentationData, items: items, getController: { [weak self] in
|
||||||
return self?.getController()
|
return self?.getController()
|
||||||
}, actionSelected: { [weak self] result in
|
}, actionSelected: { [weak self] result in
|
||||||
self?.beginDismiss(result)
|
self?.beginDismiss(result)
|
||||||
}, feedbackTap: { [weak self] in
|
}, feedbackTap: { [weak self] in
|
||||||
self?.hapticFeedback.tap()
|
self?.hapticFeedback.tap()
|
||||||
}, displayTextSelectionTip: self.displayTextSelectionTip, blurBackground: self.blurBackground)
|
}, blurBackground: self.blurBackground)
|
||||||
self.scrollNode.insertSubnode(self.actionsContainerNode, aboveSubnode: previousActionsContainerNode)
|
self.scrollNode.insertSubnode(self.actionsContainerNode, aboveSubnode: previousActionsContainerNode)
|
||||||
|
|
||||||
if let layout = self.validLayout {
|
if let layout = self.validLayout {
|
||||||
self.updateLayout(layout: layout, transition: .animated(duration: 0.3, curve: .spring), previousActionsContainerNode: previousActionsContainerNode, previousActionsTransition: previousActionsTransition)
|
self.updateLayout(layout: layout, transition: .animated(duration: 0.3, curve: .spring), previousActionsContainerNode: previousActionsContainerNode, previousActionsContainerFrame: previousActionsContainerFrame, previousActionsTransition: previousActionsTransition)
|
||||||
} else {
|
} else {
|
||||||
previousActionsContainerNode.removeFromSupernode()
|
previousActionsContainerNode.removeFromSupernode()
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.didSetItemsReady {
|
if !self.didSetItemsReady {
|
||||||
self.didSetItemsReady = true
|
self.didSetItemsReady = true
|
||||||
self.displayTextSelectionTip = false
|
|
||||||
self.itemsReady.set(.single(true))
|
self.itemsReady.set(.single(true))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1228,11 +1226,11 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||||||
self.actionsContainerNode.updateTheme(presentationData: presentationData)
|
self.actionsContainerNode.updateTheme(presentationData: presentationData)
|
||||||
|
|
||||||
if let validLayout = self.validLayout {
|
if let validLayout = self.validLayout {
|
||||||
self.updateLayout(layout: validLayout, transition: .immediate, previousActionsContainerNode: nil)
|
self.updateLayout(layout: validLayout, transition: .immediate, previousActionsContainerNode: nil, previousActionsContainerFrame: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateLayout(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition, previousActionsContainerNode: ContextActionsContainerNode?, previousActionsTransition: ContextController.PreviousActionsTransition = .scale) {
|
func updateLayout(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition, previousActionsContainerNode: ContextActionsContainerNode?, previousActionsContainerFrame: CGRect? = nil, previousActionsTransition: ContextController.PreviousActionsTransition = .scale) {
|
||||||
if self.isAnimatingOut {
|
if self.isAnimatingOut {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1475,12 +1473,18 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||||||
actionsContainerTransition.updateFrame(node: self.actionsContainerNode, frame: originalActionsFrame.offsetBy(dx: 0.0, dy: -overflowOffset))
|
actionsContainerTransition.updateFrame(node: self.actionsContainerNode, frame: originalActionsFrame.offsetBy(dx: 0.0, dy: -overflowOffset))
|
||||||
|
|
||||||
if isInitialLayout {
|
if isInitialLayout {
|
||||||
|
let previousContentOffset = self.scrollNode.view.contentOffset.y
|
||||||
if !keepInPlace {
|
if !keepInPlace {
|
||||||
self.scrollNode.view.contentOffset = CGPoint(x: 0.0, y: -overflowOffset)
|
self.scrollNode.view.contentOffset = CGPoint(x: 0.0, y: -overflowOffset)
|
||||||
}
|
}
|
||||||
let currentContainerFrame = self.view.convert(self.contentContainerNode.frame, from: self.scrollNode.view)
|
let currentContainerFrame = self.view.convert(self.contentContainerNode.frame, from: self.scrollNode.view)
|
||||||
|
var offset: CGFloat = 0.0
|
||||||
|
offset -= previousContentOffset - self.scrollNode.view.contentOffset.y
|
||||||
|
//offset += previousContainerFrame.minY - currentContainerFrame.minY
|
||||||
|
transition.animatePositionAdditive(node: self.contentContainerNode, offset: CGPoint(x: 0.0, y: offset))
|
||||||
if overflowOffset < 0.0 {
|
if overflowOffset < 0.0 {
|
||||||
transition.animateOffsetAdditive(node: self.scrollNode, offset: currentContainerFrame.minY - previousContainerFrame.minY)
|
let _ = currentContainerFrame
|
||||||
|
let _ = previousContainerFrame
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1650,6 +1654,10 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||||||
})
|
})
|
||||||
self.actionsContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
self.actionsContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||||
} else {
|
} else {
|
||||||
|
if let previousActionsContainerFrame = previousActionsContainerFrame {
|
||||||
|
previousActionsContainerNode.frame = self.view.convert(previousActionsContainerFrame, to: self.actionsContainerNode.view.superview!)
|
||||||
|
}
|
||||||
|
|
||||||
switch previousActionsTransition {
|
switch previousActionsTransition {
|
||||||
case .scale:
|
case .scale:
|
||||||
transition.updateTransformScale(node: previousActionsContainerNode, scale: 0.1)
|
transition.updateTransformScale(node: previousActionsContainerNode, scale: 0.1)
|
||||||
@ -1660,33 +1668,26 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||||||
transition.animateTransformScale(node: self.actionsContainerNode, from: 0.1)
|
transition.animateTransformScale(node: self.actionsContainerNode, from: 0.1)
|
||||||
self.actionsContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
self.actionsContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||||
case let .slide(forward):
|
case let .slide(forward):
|
||||||
if case .compact = layout.metrics.widthClass {
|
let deltaY = self.actionsContainerNode.frame.minY - previousActionsContainerNode.frame.minY
|
||||||
if forward {
|
var previousNodePosition = previousActionsContainerNode.position.offsetBy(dx: 0.0, dy: deltaY)
|
||||||
transition.updatePosition(node: previousActionsContainerNode, position: CGPoint(x: -previousActionsContainerNode.bounds.width / 2.0, y: previousActionsContainerNode.position.y), completion: { [weak previousActionsContainerNode] _ in
|
let additionalHorizontalOffset: CGFloat = 20.0
|
||||||
previousActionsContainerNode?.removeFromSupernode()
|
let currentNodeOffset: CGFloat
|
||||||
})
|
if forward {
|
||||||
transition.animatePositionAdditive(node: self.actionsContainerNode, offset: CGPoint(x: layout.size.width + self.actionsContainerNode.bounds.width / 2.0 - self.actionsContainerNode.position.x, y: 0.0))
|
previousNodePosition = previousNodePosition.offsetBy(dx: -previousActionsContainerNode.frame.width / 2.0 - additionalHorizontalOffset, dy: -previousActionsContainerNode.frame.height / 2.0)
|
||||||
} else {
|
currentNodeOffset = self.actionsContainerNode.bounds.width / 2.0 + additionalHorizontalOffset
|
||||||
transition.updatePosition(node: previousActionsContainerNode, position: CGPoint(x: layout.size.width + previousActionsContainerNode.bounds.width / 2.0, y: previousActionsContainerNode.position.y), completion: { [weak previousActionsContainerNode] _ in
|
|
||||||
previousActionsContainerNode?.removeFromSupernode()
|
|
||||||
})
|
|
||||||
transition.animatePositionAdditive(node: self.actionsContainerNode, offset: CGPoint(x: -self.actionsContainerNode.bounds.width / 2.0 - self.actionsContainerNode.position.x, y: 0.0))
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
let offset: CGFloat
|
previousNodePosition = previousNodePosition.offsetBy(dx: previousActionsContainerNode.frame.width / 2.0 + additionalHorizontalOffset, dy: -previousActionsContainerNode.frame.height / 2.0)
|
||||||
if forward {
|
currentNodeOffset = -self.actionsContainerNode.bounds.width / 2.0 - additionalHorizontalOffset
|
||||||
offset = previousActionsContainerNode.bounds.width
|
|
||||||
} else {
|
|
||||||
offset = -previousActionsContainerNode.bounds.width
|
|
||||||
}
|
|
||||||
transition.updatePosition(node: previousActionsContainerNode, position: previousActionsContainerNode.position.offsetBy(dx: -offset, dy: 0.0))
|
|
||||||
previousActionsContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak previousActionsContainerNode] _ in
|
|
||||||
previousActionsContainerNode?.removeFromSupernode()
|
|
||||||
})
|
|
||||||
|
|
||||||
transition.animatePositionAdditive(node: self.actionsContainerNode, offset: CGPoint(x: offset, y: 0.0))
|
|
||||||
self.actionsContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
|
||||||
}
|
}
|
||||||
|
transition.updatePosition(node: previousActionsContainerNode, position: previousNodePosition)
|
||||||
|
transition.updateTransformScale(node: previousActionsContainerNode, scale: 0.01)
|
||||||
|
previousActionsContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak previousActionsContainerNode] _ in
|
||||||
|
previousActionsContainerNode?.removeFromSupernode()
|
||||||
|
})
|
||||||
|
|
||||||
|
transition.animatePositionAdditive(node: self.actionsContainerNode, offset: CGPoint(x: currentNodeOffset, y: -deltaY - self.actionsContainerNode.bounds.height / 2.0))
|
||||||
|
transition.animateTransformScale(node: self.actionsContainerNode, from: 0.01)
|
||||||
|
self.actionsContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -1863,15 +1864,35 @@ public enum ContextContentSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final class ContextController: ViewController, StandalonePresentableController, ContextControllerProtocol {
|
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 {
|
public enum PreviousActionsTransition {
|
||||||
case scale
|
case scale
|
||||||
case slide(forward: Bool)
|
case slide(forward: Bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum Tip {
|
||||||
|
case textSelection
|
||||||
|
case messageViewsPrivacy
|
||||||
|
}
|
||||||
|
|
||||||
private let account: Account
|
private let account: Account
|
||||||
private var presentationData: PresentationData
|
private var presentationData: PresentationData
|
||||||
private let source: ContextContentSource
|
private let source: ContextContentSource
|
||||||
private var items: Signal<[ContextMenuItem], NoError>
|
private var items: Signal<ContextController.Items, NoError>
|
||||||
private var reactionItems: [ReactionContextItem]
|
private var reactionItems: [ReactionContextItem]
|
||||||
|
|
||||||
private let _ready = Promise<Bool>()
|
private let _ready = Promise<Bool>()
|
||||||
@ -1881,7 +1902,6 @@ public final class ContextController: ViewController, StandalonePresentableContr
|
|||||||
|
|
||||||
private weak var recognizer: TapLongTapOrDoubleTapGestureRecognizer?
|
private weak var recognizer: TapLongTapOrDoubleTapGestureRecognizer?
|
||||||
private weak var gesture: ContextGesture?
|
private weak var gesture: ContextGesture?
|
||||||
private let displayTextSelectionTip: Bool
|
|
||||||
|
|
||||||
private var animatedDidAppear = false
|
private var animatedDidAppear = false
|
||||||
private var wasDismissed = false
|
private var wasDismissed = false
|
||||||
@ -1903,7 +1923,7 @@ public final class ContextController: ViewController, StandalonePresentableContr
|
|||||||
|
|
||||||
private var shouldBeDismissedDisposable: Disposable?
|
private var shouldBeDismissedDisposable: Disposable?
|
||||||
|
|
||||||
public init(account: Account, presentationData: PresentationData, source: ContextContentSource, items: Signal<[ContextMenuItem], NoError>, reactionItems: [ReactionContextItem], recognizer: TapLongTapOrDoubleTapGestureRecognizer? = nil, gesture: ContextGesture? = nil, displayTextSelectionTip: Bool = false) {
|
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.account = account
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
self.source = source
|
self.source = source
|
||||||
@ -1911,7 +1931,6 @@ public final class ContextController: ViewController, StandalonePresentableContr
|
|||||||
self.reactionItems = reactionItems
|
self.reactionItems = reactionItems
|
||||||
self.recognizer = recognizer
|
self.recognizer = recognizer
|
||||||
self.gesture = gesture
|
self.gesture = gesture
|
||||||
self.displayTextSelectionTip = displayTextSelectionTip
|
|
||||||
|
|
||||||
super.init(navigationBarPresentationData: nil)
|
super.init(navigationBarPresentationData: nil)
|
||||||
|
|
||||||
@ -1982,7 +2001,7 @@ public final class ContextController: ViewController, StandalonePresentableContr
|
|||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}, displayTextSelectionTip: self.displayTextSelectionTip)
|
})
|
||||||
self.controllerNode.dismissedForCancel = self.dismissedForCancel
|
self.controllerNode.dismissedForCancel = self.dismissedForCancel
|
||||||
self.displayNodeDidLoad()
|
self.displayNodeDidLoad()
|
||||||
|
|
||||||
@ -2017,14 +2036,14 @@ public final class ContextController: ViewController, StandalonePresentableContr
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
public func setItems(_ items: Signal<[ContextMenuItem], NoError>, minHeight: CGFloat?) {
|
public func setItems(_ items: Signal<ContextController.Items, NoError>, minHeight: CGFloat?) {
|
||||||
self.items = items
|
self.items = items
|
||||||
if self.isNodeLoaded {
|
if self.isNodeLoaded {
|
||||||
self.controllerNode.setItemsSignal(items: items, minHeight: minHeight, previousActionsTransition: .scale)
|
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
|
self.items = items
|
||||||
if self.isNodeLoaded {
|
if self.isNodeLoaded {
|
||||||
self.controllerNode.setItemsSignal(items: items, minHeight: minHeight, previousActionsTransition: previousActionsTransition)
|
self.controllerNode.setItemsSignal(items: items, minHeight: minHeight, previousActionsTransition: previousActionsTransition)
|
||||||
|
@ -38,10 +38,10 @@ public final class PeekController: ViewController, ContextControllerProtocol {
|
|||||||
return nil
|
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 {
|
private var controllerNode: PeekControllerNode {
|
||||||
|
@ -72,13 +72,13 @@ final class PeekControllerNode: ViewControllerTracingNode {
|
|||||||
|
|
||||||
var feedbackTapImpl: (() -> Void)?
|
var feedbackTapImpl: (() -> Void)?
|
||||||
var activatedActionImpl: (() -> 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
|
return controller
|
||||||
}, actionSelected: { result in
|
}, actionSelected: { result in
|
||||||
activatedActionImpl?()
|
activatedActionImpl?()
|
||||||
}, feedbackTap: {
|
}, feedbackTap: {
|
||||||
feedbackTapImpl?()
|
feedbackTapImpl?()
|
||||||
}, displayTextSelectionTip: false, blurBackground: true)
|
}, blurBackground: true)
|
||||||
self.actionsContainerNode.alpha = 0.0
|
self.actionsContainerNode.alpha = 0.0
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
@ -328,13 +328,13 @@ final class PeekControllerNode: ViewControllerTracingNode {
|
|||||||
self.contentNodeHasValidLayout = false
|
self.contentNodeHasValidLayout = false
|
||||||
|
|
||||||
let previousActionsContainerNode = self.actionsContainerNode
|
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
|
return self?.controller
|
||||||
}, actionSelected: { [weak self] result in
|
}, actionSelected: { [weak self] result in
|
||||||
self?.requestDismiss()
|
self?.requestDismiss()
|
||||||
}, feedbackTap: { [weak self] in
|
}, feedbackTap: { [weak self] in
|
||||||
self?.hapticFeedback.tap()
|
self?.hapticFeedback.tap()
|
||||||
}, displayTextSelectionTip: false, blurBackground: true)
|
}, blurBackground: true)
|
||||||
self.actionsContainerNode.alpha = 0.0
|
self.actionsContainerNode.alpha = 0.0
|
||||||
self.insertSubnode(self.actionsContainerNode, aboveSubnode: previousActionsContainerNode)
|
self.insertSubnode(self.actionsContainerNode, aboveSubnode: previousActionsContainerNode)
|
||||||
previousActionsContainerNode.removeFromSupernode()
|
previousActionsContainerNode.removeFromSupernode()
|
||||||
|
@ -2032,7 +2032,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
return
|
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)
|
self.isShowingContextMenuPromise.set(true)
|
||||||
controller.presentInGlobalOverlay(contextController)
|
controller.presentInGlobalOverlay(contextController)
|
||||||
|
|
||||||
@ -2087,7 +2087,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.setItems(strongSelf.contextMenuSpeedItems(), minHeight: nil)
|
c.setItems(strongSelf.contextMenuSpeedItems() |> map { ContextController.Items(items: $0) }, minHeight: nil)
|
||||||
})))
|
})))
|
||||||
|
|
||||||
if let (message, _, _) = strongSelf.contentInfo() {
|
if let (message, _, _) = strongSelf.contentInfo() {
|
||||||
@ -2206,7 +2206,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
c.dismiss(completion: nil)
|
c.dismiss(completion: nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.setItems(strongSelf.contextMenuMainItems(), minHeight: nil)
|
c.setItems(strongSelf.contextMenuMainItems() |> map { ContextController.Items(items: $0) }, minHeight: nil)
|
||||||
})))
|
})))
|
||||||
|
|
||||||
return items
|
return items
|
||||||
|
@ -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)
|
self?.controller?.presentInGlobalOverlay(contextController)
|
||||||
}, copyLink: { [weak self] invite in
|
}, copyLink: { [weak self] invite in
|
||||||
UIPasteboard.general.string = invite.link
|
UIPasteboard.general.string = invite.link
|
||||||
|
@ -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)
|
presentInGlobalOverlayImpl?(contextController)
|
||||||
}, createLink: {
|
}, createLink: {
|
||||||
let controller = inviteLinkEditController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, invite: nil, completion: { invite in
|
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)
|
presentInGlobalOverlayImpl?(contextController)
|
||||||
}, openAdmin: { admin in
|
}, openAdmin: { admin in
|
||||||
let controller = inviteLinkListController(context: context, peerId: peerId, admin: admin)
|
let controller = inviteLinkListController(context: context, peerId: peerId, admin: admin)
|
||||||
|
@ -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)
|
self?.controller?.presentInGlobalOverlay(contextController)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -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)
|
presentInGlobalOverlayImpl?(contextController)
|
||||||
}, manageInviteLinks: {
|
}, manageInviteLinks: {
|
||||||
let controller = inviteLinkListController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, admin: nil)
|
let controller = inviteLinkListController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, admin: nil)
|
||||||
|
@ -158,7 +158,7 @@ public func presentPeerReportOptions(context: AccountContext, parent: ViewContro
|
|||||||
backAction(c)
|
backAction(c)
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
contextController.setItems(.single(items), minHeight: nil)
|
contextController.setItems(.single(ContextController.Items(items: items)), minHeight: nil)
|
||||||
} else {
|
} else {
|
||||||
contextController?.dismiss(completion: nil)
|
contextController?.dismiss(completion: nil)
|
||||||
parent.view.endEditing(true)
|
parent.view.endEditing(true)
|
||||||
|
@ -494,7 +494,7 @@ public func peersNearbyController(context: AccountContext) -> ViewController {
|
|||||||
chatController.canReadHistory.set(false)
|
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
|
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)
|
presentControllerImpl?(c, nil)
|
||||||
}), reactionItems: [], gesture: gesture)
|
}) |> map { ContextController.Items(items: $0) }, reactionItems: [], gesture: gesture)
|
||||||
presentInGlobalOverlayImpl?(contextController)
|
presentInGlobalOverlayImpl?(contextController)
|
||||||
}, expandUsers: {
|
}, expandUsers: {
|
||||||
expandedPromise.set(true)
|
expandedPromise.set(true)
|
||||||
|
@ -88,11 +88,15 @@ public final class Transaction {
|
|||||||
return self.postbox!.messageHistoryThreadHoleIndexTable.closest(peerId: peerId, threadId: threadId, namespace: namespace, space: .everywhere, range: 1 ... (Int32.max - 1))
|
return self.postbox!.messageHistoryThreadHoleIndexTable.closest(peerId: peerId, threadId: threadId, namespace: namespace, space: .everywhere, range: 1 ... (Int32.max - 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
public func getThreadMessageCount(peerId: PeerId, threadId: Int64, namespace: MessageId.Namespace, fromId: Int32?, toIndex: MessageIndex) -> Int? {
|
public func getThreadMessageCount(peerId: PeerId, threadId: Int64, namespace: MessageId.Namespace, fromIdExclusive: Int32?, toIndex: MessageIndex) -> Int? {
|
||||||
assert(!self.disposed)
|
assert(!self.disposed)
|
||||||
let fromIndex: MessageIndex?
|
let fromIndex: MessageIndex?
|
||||||
if let fromId = fromId {
|
if let fromIdExclusive = fromIdExclusive {
|
||||||
fromIndex = self.postbox!.messageHistoryIndexTable.closestIndex(id: MessageId(peerId: peerId, namespace: namespace, id: fromId))
|
if let message = self.postbox?.getMessage(MessageId(peerId: peerId, namespace: namespace, id: fromIdExclusive)) {
|
||||||
|
fromIndex = message.index.peerLocalSuccessor()
|
||||||
|
} else {
|
||||||
|
fromIndex = self.postbox!.messageHistoryIndexTable.closestIndex(id: MessageId(peerId: peerId, namespace: namespace, id: fromIdExclusive))?.peerLocalSuccessor()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
fromIndex = nil
|
fromIndex = nil
|
||||||
}
|
}
|
||||||
|
@ -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)
|
presentInGlobalOverlayImpl?(contextController, nil)
|
||||||
})
|
})
|
||||||
}, colorContextAction: { isCurrent, reference, accentColor, node, gesture in
|
}, 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)
|
presentInGlobalOverlayImpl?(contextController, nil)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -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)
|
controller.presentInGlobalOverlay(contextController)
|
||||||
}
|
}
|
||||||
return controller
|
return controller
|
||||||
|
@ -555,7 +555,7 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
let items: Signal<[ContextMenuItem], NoError> = self.contextMenuSpeedItems()
|
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)
|
self.presentInGlobalOverlay?(contextController)
|
||||||
}
|
}
|
||||||
|
@ -1787,7 +1787,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
dismissPromise.set(true)
|
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
|
contextController.useComplexItemsTransitionAnimation = true
|
||||||
strongSelf.controller?.presentInGlobalOverlay(contextController)
|
strongSelf.controller?.presentInGlobalOverlay(contextController)
|
||||||
}, getPeerVideo: { [weak self] endpointId, position in
|
}, getPeerVideo: { [weak self] endpointId, position in
|
||||||
@ -2444,7 +2444,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
private func openSettingsMenu(sourceNode: ASDisplayNode, gesture: ContextGesture?) {
|
private func openSettingsMenu(sourceNode: ASDisplayNode, gesture: ContextGesture?) {
|
||||||
let items: Signal<[ContextMenuItem], NoError> = self.contextMenuMainItems()
|
let items: Signal<[ContextMenuItem], NoError> = self.contextMenuMainItems()
|
||||||
if let controller = self.controller {
|
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)
|
controller.presentInGlobalOverlay(contextController)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2473,7 +2473,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.setItems(strongSelf.contextMenuDisplayAsItems(), minHeight: nil)
|
c.setItems(strongSelf.contextMenuDisplayAsItems() |> map { ContextController.Items(items: $0) }, minHeight: nil)
|
||||||
})))
|
})))
|
||||||
items.append(.separator)
|
items.append(.separator)
|
||||||
break
|
break
|
||||||
@ -2506,7 +2506,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
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 {
|
guard let strongSelf = self else {
|
||||||
return
|
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 {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.setItems(strongSelf.contextMenuMainItems(), minHeight: nil)
|
c.setItems(strongSelf.contextMenuMainItems() |> map { ContextController.Items(items: $0) }, minHeight: nil)
|
||||||
})))
|
})))
|
||||||
return .single(items)
|
return .single(items)
|
||||||
}
|
}
|
||||||
@ -2898,7 +2898,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.setItems(strongSelf.contextMenuMainItems(), minHeight: nil)
|
c.setItems(strongSelf.contextMenuMainItems() |> map { ContextController.Items(items: $0) }, minHeight: nil)
|
||||||
})))
|
})))
|
||||||
return items
|
return items
|
||||||
}
|
}
|
||||||
@ -2944,7 +2944,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.setItems(strongSelf.contextMenuMainItems(), minHeight: nil)
|
c.setItems(strongSelf.contextMenuMainItems() |> map { ContextController.Items(items: $0) }, minHeight: nil)
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
return .single(items)
|
return .single(items)
|
||||||
|
@ -35,11 +35,16 @@ func _internal_messageReadStats(account: Account, id: MessageId) -> Signal<Messa
|
|||||||
var peerIds: [PeerId] = []
|
var peerIds: [PeerId] = []
|
||||||
var missingPeerIds: [PeerId] = []
|
var missingPeerIds: [PeerId] = []
|
||||||
|
|
||||||
|
let authorId = transaction.getMessage(id)?.author?.id
|
||||||
|
|
||||||
for id in result {
|
for id in result {
|
||||||
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(id))
|
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(id))
|
||||||
if peerId == account.peerId {
|
if peerId == account.peerId {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if peerId == authorId {
|
||||||
|
continue
|
||||||
|
}
|
||||||
peerIds.append(peerId)
|
peerIds.append(peerId)
|
||||||
if transaction.getPeer(peerId) == nil {
|
if transaction.getPeer(peerId) == nil {
|
||||||
missingPeerIds.append(peerId)
|
missingPeerIds.append(peerId)
|
||||||
|
@ -322,13 +322,14 @@ private class ReplyThreadHistoryContextImpl {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let fromId: Int32?
|
let fromIdExclusive: Int32?
|
||||||
let toIndex = messageIndex
|
let toIndex = messageIndex
|
||||||
if let maxReadIncomingMessageId = self.maxReadIncomingMessageIdValue {
|
if let maxReadIncomingMessageId = self.maxReadIncomingMessageIdValue {
|
||||||
fromId = maxReadIncomingMessageId.id + 1
|
fromIdExclusive = maxReadIncomingMessageId.id
|
||||||
} else {
|
} else {
|
||||||
fromId = nil
|
fromIdExclusive = nil
|
||||||
}
|
}
|
||||||
|
self.maxReadIncomingMessageIdValue = messageIndex.id
|
||||||
|
|
||||||
let account = self.account
|
let account = self.account
|
||||||
|
|
||||||
@ -370,7 +371,7 @@ private class ReplyThreadHistoryContextImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let inputPeer = transaction.getPeer(messageIndex.id.peerId).flatMap(apiInputPeer)
|
let inputPeer = transaction.getPeer(messageIndex.id.peerId).flatMap(apiInputPeer)
|
||||||
let readCount = transaction.getThreadMessageCount(peerId: messageId.peerId, threadId: makeMessageThreadId(messageId), namespace: messageId.namespace, fromId: fromId, toIndex: toIndex)
|
let readCount = transaction.getThreadMessageCount(peerId: messageId.peerId, threadId: makeMessageThreadId(messageId), namespace: messageId.namespace, fromIdExclusive: fromIdExclusive, toIndex: toIndex)
|
||||||
let topMessageId = transaction.getMessagesWithThreadId(peerId: messageId.peerId, namespace: messageId.namespace, threadId: makeMessageThreadId(messageId), from: MessageIndex.upperBound(peerId: messageId.peerId, namespace: messageId.namespace), includeFrom: false, to: MessageIndex.lowerBound(peerId: messageId.peerId, namespace: messageId.namespace), limit: 1).first?.id
|
let topMessageId = transaction.getMessagesWithThreadId(peerId: messageId.peerId, namespace: messageId.namespace, threadId: makeMessageThreadId(messageId), from: MessageIndex.upperBound(peerId: messageId.peerId, namespace: messageId.namespace), includeFrom: false, to: MessageIndex.lowerBound(peerId: messageId.peerId, namespace: messageId.namespace), limit: 1).first?.id
|
||||||
|
|
||||||
return (inputPeer, topMessageId, readCount)
|
return (inputPeer, topMessageId, readCount)
|
||||||
@ -386,7 +387,6 @@ private class ReplyThreadHistoryContextImpl {
|
|||||||
|
|
||||||
var revalidate = false
|
var revalidate = false
|
||||||
|
|
||||||
strongSelf.maxReadIncomingMessageIdValue = messageIndex.id
|
|
||||||
var unreadCountValue = strongSelf.unreadCountValue
|
var unreadCountValue = strongSelf.unreadCountValue
|
||||||
if let readCount = readCount {
|
if let readCount = readCount {
|
||||||
unreadCountValue = max(0, unreadCountValue - Int(readCount))
|
unreadCountValue = max(0, unreadCountValue - Int(readCount))
|
||||||
@ -404,7 +404,12 @@ private class ReplyThreadHistoryContextImpl {
|
|||||||
|
|
||||||
if let state = strongSelf.stateValue {
|
if let state = strongSelf.stateValue {
|
||||||
if let indices = state.holeIndices[messageIndex.id.namespace] {
|
if let indices = state.holeIndices[messageIndex.id.namespace] {
|
||||||
let fromIdInt = Int(fromId ?? 1)
|
let fromIdInt: Int
|
||||||
|
if let fromIdExclusive = fromIdExclusive {
|
||||||
|
fromIdInt = Int(fromIdExclusive + 1)
|
||||||
|
} else {
|
||||||
|
fromIdInt = 1
|
||||||
|
}
|
||||||
let toIdInt = Int(toIndex.id.id)
|
let toIdInt = Int(toIndex.id.id)
|
||||||
if fromIdInt <= toIdInt, indices.intersects(integersIn: fromIdInt ..< toIdInt) {
|
if fromIdInt <= toIdInt, indices.intersects(integersIn: fromIdInt ..< toIdInt) {
|
||||||
revalidate = true
|
revalidate = true
|
||||||
|
@ -169,6 +169,7 @@ private enum ApplicationSpecificGlobalNotice: Int32 {
|
|||||||
case dismissedTrendingStickerPacks = 22
|
case dismissedTrendingStickerPacks = 22
|
||||||
case chatSpecificThemesDarkPreviewTip = 23
|
case chatSpecificThemesDarkPreviewTip = 23
|
||||||
case chatForwardOptionsTip = 24
|
case chatForwardOptionsTip = 24
|
||||||
|
case messageViewsPrivacyTips = 25
|
||||||
|
|
||||||
var key: ValueBoxKey {
|
var key: ValueBoxKey {
|
||||||
let v = ValueBoxKey(length: 4)
|
let v = ValueBoxKey(length: 4)
|
||||||
@ -293,6 +294,10 @@ private struct ApplicationSpecificNoticeKeys {
|
|||||||
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.chatTextSelectionTip.key)
|
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.chatTextSelectionTip.key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func messageViewsPrivacyTips() -> NoticeEntryKey {
|
||||||
|
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.messageViewsPrivacyTips.key)
|
||||||
|
}
|
||||||
|
|
||||||
static func themeChangeTip() -> NoticeEntryKey {
|
static func themeChangeTip() -> NoticeEntryKey {
|
||||||
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.themeChangeTip.key)
|
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.themeChangeTip.key)
|
||||||
}
|
}
|
||||||
@ -746,6 +751,28 @@ public struct ApplicationSpecificNotice {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static func getMessageViewsPrivacyTips(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<Int32, NoError> {
|
||||||
|
return accountManager.transaction { transaction -> Int32 in
|
||||||
|
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.messageViewsPrivacyTips()) as? ApplicationSpecificCounterNotice {
|
||||||
|
return value.value
|
||||||
|
} else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func incrementMessageViewsPrivacyTips(accountManager: AccountManager<TelegramAccountManagerTypes>, count: Int32 = 1) -> Signal<Void, NoError> {
|
||||||
|
return accountManager.transaction { transaction -> Void in
|
||||||
|
var currentValue: Int32 = 0
|
||||||
|
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.messageViewsPrivacyTips()) as? ApplicationSpecificCounterNotice {
|
||||||
|
currentValue = value.value
|
||||||
|
}
|
||||||
|
currentValue += count
|
||||||
|
|
||||||
|
transaction.setNotice(ApplicationSpecificNoticeKeys.messageViewsPrivacyTips(), ApplicationSpecificCounterNotice(value: currentValue))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static func getThemeChangeTip(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<Bool, NoError> {
|
public static func getThemeChangeTip(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<Bool, NoError> {
|
||||||
return accountManager.transaction { transaction -> Bool in
|
return accountManager.transaction { transaction -> Bool in
|
||||||
if let _ = transaction.getNotice(ApplicationSpecificNoticeKeys.themeChangeTip()) as? ApplicationSpecificBoolNotice {
|
if let _ = transaction.getNotice(ApplicationSpecificNoticeKeys.themeChangeTip()) as? ApplicationSpecificBoolNotice {
|
||||||
|
@ -926,7 +926,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
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)
|
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
|
).start(next: { actions, animatedEmojiStickers, chatTextSelectionTips in
|
||||||
guard let strongSelf = self, !actions.isEmpty else {
|
var actions = actions
|
||||||
|
|
||||||
|
guard let strongSelf = self, !actions.items.isEmpty else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var reactionItems: [ReactionContextItem] = []
|
var reactionItems: [ReactionContextItem] = []
|
||||||
@ -959,13 +961,22 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
reactionItems = []
|
reactionItems = []
|
||||||
}
|
}
|
||||||
|
|
||||||
let numberOfComponents = message.text.components(separatedBy: CharacterSet.whitespacesAndNewlines).count
|
var tip: ContextController.Tip?
|
||||||
let displayTextSelectionTip = numberOfComponents >= 3 && !message.text.isEmpty && chatTextSelectionTips < 3
|
|
||||||
if displayTextSelectionTip {
|
if tip == nil {
|
||||||
let _ = ApplicationSpecificNotice.incrementChatTextSelectionTips(accountManager: strongSelf.context.sharedContext.accountManager).start()
|
let numberOfComponents = message.text.components(separatedBy: CharacterSet.whitespacesAndNewlines).count
|
||||||
|
let displayTextSelectionTip = numberOfComponents >= 3 && !message.text.isEmpty && chatTextSelectionTips < 3
|
||||||
|
if displayTextSelectionTip {
|
||||||
|
let _ = ApplicationSpecificNotice.incrementChatTextSelectionTips(accountManager: strongSelf.context.sharedContext.accountManager).start()
|
||||||
|
tip = .textSelection
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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, displayTextSelectionTip: displayTextSelectionTip)
|
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)
|
||||||
strongSelf.currentContextController = controller
|
strongSelf.currentContextController = controller
|
||||||
controller.reactionSelected = { [weak controller] value in
|
controller.reactionSelected = { [weak controller] value in
|
||||||
guard let strongSelf = self, let message = updatedMessages.first else {
|
guard let strongSelf = self, let message = updatedMessages.first else {
|
||||||
@ -2221,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.currentContextController = controller
|
||||||
strongSelf.forEachController({ controller in
|
strongSelf.forEachController({ controller in
|
||||||
if let controller = controller as? TooltipScreen {
|
if let controller = controller as? TooltipScreen {
|
||||||
@ -2298,7 +2309,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
f(.dismissWithoutContent)
|
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.currentContextController = controller
|
||||||
strongSelf.forEachController({ controller in
|
strongSelf.forEachController({ controller in
|
||||||
if let controller = controller as? TooltipScreen {
|
if let controller = controller as? TooltipScreen {
|
||||||
@ -2741,7 +2752,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
return items
|
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)
|
strongSelf.presentInGlobalOverlay(contextController)
|
||||||
})
|
})
|
||||||
}, openMessageReplies: { [weak self] messageId, isChannelPost, displayModalProgress in
|
}, openMessageReplies: { [weak self] messageId, isChannelPost, displayModalProgress in
|
||||||
@ -2978,7 +2989,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
return items
|
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)
|
strongSelf.presentInGlobalOverlay(contextController)
|
||||||
}
|
}
|
||||||
chatInfoButtonItem = UIBarButtonItem(customDisplayNode: avatarNode)!
|
chatInfoButtonItem = UIBarButtonItem(customDisplayNode: avatarNode)!
|
||||||
@ -5006,40 +5017,48 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
let curve: ContainedViewLayoutTransitionCurve = strongSelf.chatDisplayNode.messageTransitionNode.hasScheduledTransitions ? ChatMessageTransitionNode.verticalAnimationCurve : .easeInOut
|
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 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
|
strongSelf.chatDisplayNode.containerLayoutUpdated(validLayout, navigationBarHeight: strongSelf.navigationLayout(layout: validLayout).navigationFrame.maxY, transition: .animated(duration: duration, curve: curve), listViewTransaction: { updateSizeAndInsets, _, _, _ in
|
||||||
|
|
||||||
var options = transition.options
|
var options = transition.options
|
||||||
let _ = options.insert(.Synchronous)
|
let _ = options.insert(.Synchronous)
|
||||||
let _ = options.insert(.LowLatency)
|
let _ = options.insert(.LowLatency)
|
||||||
let _ = options.insert(.PreferSynchronousResourceLoading)
|
let _ = options.insert(.PreferSynchronousResourceLoading)
|
||||||
options.remove(.AnimateInsertion)
|
|
||||||
options.insert(.RequestItemInsertionAnimations)
|
|
||||||
|
|
||||||
let deleteItems = transition.deleteItems.map({ item in
|
var deleteItems = transition.deleteItems
|
||||||
return ListViewDeleteItem(index: item.index, directionHint: nil)
|
|
||||||
})
|
|
||||||
|
|
||||||
var maxInsertedItem: Int?
|
|
||||||
var insertedIndex: Int?
|
|
||||||
var insertItems: [ListViewInsertItem] = []
|
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)?
|
var stationaryItemRange: (Int, Int)?
|
||||||
if let maxInsertedItem = maxInsertedItem {
|
var scrollToItem: ListViewScrollToItem?
|
||||||
stationaryItemRange = (maxInsertedItem + 1, Int.max)
|
|
||||||
|
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)
|
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)
|
||||||
@ -5755,7 +5774,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
return items
|
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
|
contextController.dismissedForCancel = { [weak chatController] in
|
||||||
if let selectedMessageIds = (chatController as? ChatControllerImpl)?.selectedMessageIds {
|
if let selectedMessageIds = (chatController as? ChatControllerImpl)?.selectedMessageIds {
|
||||||
var forwardMessageIds = strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds ?? []
|
var forwardMessageIds = strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds ?? []
|
||||||
@ -6509,7 +6528,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
})
|
})
|
||||||
})))
|
})))
|
||||||
|
|
||||||
contextController.setItems(.single(contextItems), minHeight: nil)
|
contextController.setItems(.single(ContextController.Items(items: contextItems)), minHeight: nil)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
@ -6528,7 +6547,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
})
|
})
|
||||||
})))
|
})))
|
||||||
|
|
||||||
contextController.setItems(.single(contextItems), minHeight: nil)
|
contextController.setItems(.single(ContextController.Items(items: contextItems)), minHeight: nil)
|
||||||
|
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
@ -7263,7 +7282,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))
|
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)
|
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)
|
strongSelf.presentInGlobalOverlay(contextController)
|
||||||
}, joinGroupCall: { [weak self] activeCall in
|
}, joinGroupCall: { [weak self] activeCall in
|
||||||
guard let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else {
|
guard let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else {
|
||||||
@ -12672,7 +12691,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
|
|
||||||
if canDisplayContextMenu, let contextController = contextController {
|
if canDisplayContextMenu, let contextController = contextController {
|
||||||
contextController.setItems(.single(contextItems), minHeight: nil)
|
contextController.setItems(.single(ContextController.Items(items: contextItems)), minHeight: nil)
|
||||||
} else {
|
} else {
|
||||||
actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [
|
actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [
|
||||||
ActionSheetButtonItem(title: self.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
|
ActionSheetButtonItem(title: self.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
|
||||||
|
@ -2578,6 +2578,19 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
return false
|
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() {
|
switch self.historyNode.visibleContentOffset() {
|
||||||
case let .known(value) where value < 20.0:
|
case let .known(value) where value < 20.0:
|
||||||
return true
|
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 {
|
var shouldAllowOverscrollActions: Bool {
|
||||||
if let inputHeight = self.validLayout?.0.inputHeight, inputHeight > 0.0 {
|
if let inputHeight = self.validLayout?.0.inputHeight, inputHeight > 0.0 {
|
||||||
return false
|
return false
|
||||||
|
@ -22,6 +22,7 @@ import ShimmerEffect
|
|||||||
import AnimatedAvatarSetNode
|
import AnimatedAvatarSetNode
|
||||||
import AvatarNode
|
import AvatarNode
|
||||||
import AdUI
|
import AdUI
|
||||||
|
import TelegramNotices
|
||||||
|
|
||||||
private struct MessageContextMenuData {
|
private struct MessageContextMenuData {
|
||||||
let starStatus: Bool?
|
let starStatus: Bool?
|
||||||
@ -141,15 +142,32 @@ private func canEditMessage(accountPeerId: PeerId, limitsConfiguration: LimitsCo
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func canViewReadStats(message: Message, isMessageRead: Bool, appConfig: AppConfiguration) -> Bool {
|
private func canViewReadStats(message: Message, isMessageRead: Bool, appConfig: AppConfiguration) -> Bool {
|
||||||
if !isMessageRead {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if message.flags.contains(.Incoming) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
guard let peer = message.peers[message.id.peerId] else {
|
guard let peer = message.peers[message.id.peerId] else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if message.flags.contains(.Incoming) {
|
||||||
|
switch peer {
|
||||||
|
case let channel as TelegramChannel:
|
||||||
|
if channel.adminRights == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case let group as TelegramGroup:
|
||||||
|
switch group.role {
|
||||||
|
case .creator, .admin:
|
||||||
|
break
|
||||||
|
case .member:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !isMessageRead {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for media in message.media {
|
for media in message.media {
|
||||||
if let _ = media as? TelegramMediaAction {
|
if let _ = media as? TelegramMediaAction {
|
||||||
return false
|
return false
|
||||||
@ -351,9 +369,9 @@ func updatedChatEditInterfaceMessageState(state: ChatPresentationInterfaceState,
|
|||||||
return updated
|
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 {
|
guard let interfaceInteraction = interfaceInteraction, let controllerInteraction = controllerInteraction else {
|
||||||
return .single([])
|
return .single(ContextController.Items(items: []))
|
||||||
}
|
}
|
||||||
|
|
||||||
if messages.count == 1, let _ = messages[0].adAttribute {
|
if messages.count == 1, let _ = messages[0].adAttribute {
|
||||||
@ -420,7 +438,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
|||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
return .single(actions)
|
return .single(ContextController.Items(items: actions))
|
||||||
}
|
}
|
||||||
|
|
||||||
var loadStickerSaveStatus: MediaId?
|
var loadStickerSaveStatus: MediaId?
|
||||||
@ -534,7 +552,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
|||||||
return transaction.getCombinedPeerReadState(messages[0].id.peerId)
|
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,
|
loadLimits,
|
||||||
loadStickerSaveStatusSignal,
|
loadStickerSaveStatusSignal,
|
||||||
loadResourceStatusSignal,
|
loadResourceStatusSignal,
|
||||||
@ -542,9 +560,10 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
|||||||
context.account.pendingUpdateMessageManager.updatingMessageMedia
|
context.account.pendingUpdateMessageManager.updatingMessageMedia
|
||||||
|> take(1),
|
|> take(1),
|
||||||
cachedData,
|
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
|
let (limitsConfiguration, appConfig) = limitsAndAppConfig
|
||||||
var canEdit = false
|
var canEdit = false
|
||||||
if !isAction {
|
if !isAction {
|
||||||
@ -557,12 +576,12 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
|||||||
isMessageRead = readState.isOutgoingMessageIndexRead(message.index)
|
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
|
return dataSignal
|
||||||
|> deliverOnMainQueue
|
|> deliverOnMainQueue
|
||||||
|> map { data, updatingMessageMedia, cachedData, appConfig, isMessageRead -> [ContextMenuItem] in
|
|> map { data, updatingMessageMedia, cachedData, appConfig, isMessageRead, messageViewsPrivacyTips -> ContextController.Items in
|
||||||
var actions: [ContextMenuItem] = []
|
var actions: [ContextMenuItem] = []
|
||||||
|
|
||||||
var isPinnedMessages = false
|
var isPinnedMessages = false
|
||||||
@ -1191,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))
|
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 {
|
for peer in stats.peers {
|
||||||
let avatarSignal = peerAvatarCompleteImage(account: context.account, peer: peer._asPeer(), size: CGSize(width: 30.0, height: 30.0))
|
let avatarSignal = peerAvatarCompleteImage(account: context.account, peer: peer._asPeer(), size: CGSize(width: 30.0, height: 30.0))
|
||||||
|
|
||||||
@ -1201,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()
|
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 {
|
} else {
|
||||||
f(.default)
|
f(.default)
|
||||||
}
|
}
|
||||||
@ -1210,7 +1237,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return actions
|
return ContextController.Items(items: actions, tip: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1458,7 +1458,7 @@ final class ChatMediaInputNode: ChatInputNode {
|
|||||||
|
|
||||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
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)
|
strongSelf.controllerInteraction.presentGlobalOverlayController(contextController, nil)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -207,7 +207,7 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe
|
|||||||
if let message = peer.messages.first {
|
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))
|
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)
|
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)
|
presentInGlobalOverlay(contextController)
|
||||||
} else {
|
} else {
|
||||||
gesture?.cancel()
|
gesture?.cancel()
|
||||||
|
@ -1797,7 +1797,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
let presentationData = strongSelf.presentationData
|
let presentationData = strongSelf.presentationData
|
||||||
let peerId = strongSelf.peerId
|
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
|
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] = []
|
var items: [ContextMenuItem] = []
|
||||||
let messageIds = [message.id]
|
let messageIds = [message.id]
|
||||||
|
|
||||||
@ -1849,7 +1849,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return items
|
return ContextController.Items(items: items)
|
||||||
}, minHeight: nil)
|
}, 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)
|
strongSelf.controller?.window?.presentInGlobalOverlay(controller)
|
||||||
})
|
})
|
||||||
}, activateMessagePinch: { _ in
|
}, activateMessagePinch: { _ in
|
||||||
@ -1931,7 +1931,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
|
|
||||||
if actions.options.contains(.deleteLocally) || actions.options.contains(.deleteGlobally) {
|
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
|
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] = []
|
var items: [ContextMenuItem] = []
|
||||||
let messageIds = [message.id]
|
let messageIds = [message.id]
|
||||||
|
|
||||||
@ -1983,7 +1983,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return items
|
return ContextController.Items(items: items)
|
||||||
}, minHeight: nil)
|
}, minHeight: nil)
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
@ -2006,7 +2006,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
switch previewData {
|
switch previewData {
|
||||||
case let .gallery(gallery):
|
case let .gallery(gallery):
|
||||||
gallery.setHintWillBePresentedInPreviewingContext(true)
|
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)
|
strongSelf.controller?.presentInGlobalOverlay(contextController)
|
||||||
case .instantPage:
|
case .instantPage:
|
||||||
break
|
break
|
||||||
@ -2228,7 +2228,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
self?.chatInterfaceInteraction.openPeer(peer.id, .default, nil)
|
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)
|
controller.presentInGlobalOverlay(contextController)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2834,7 +2834,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
}, synchronousLoad: true)
|
}, synchronousLoad: true)
|
||||||
galleryController.setHintWillBePresentedInPreviewingContext(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)
|
strongSelf.controller?.presentInGlobalOverlay(contextController)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3460,7 +3460,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
self.view.endEditing(true)
|
self.view.endEditing(true)
|
||||||
|
|
||||||
if let sourceNode = self.headerNode.buttonNodes[.mute]?.referenceNode {
|
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
|
contextController.dismissed = { [weak self] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.state = strongSelf.state.withHighlightedButton(nil)
|
strongSelf.state = strongSelf.state.withHighlightedButton(nil)
|
||||||
@ -3691,7 +3691,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
}, action: { [weak self] c, f in
|
}, action: { [weak self] c, f in
|
||||||
self?.openReport(user: false, contextController: c, backAction: { c in
|
self?.openReport(user: false, contextController: c, backAction: { c in
|
||||||
if let mainItemsImpl = mainItemsImpl {
|
if let mainItemsImpl = mainItemsImpl {
|
||||||
c.setItems(mainItemsImpl(), minHeight: nil)
|
c.setItems(mainItemsImpl() |> map { ContextController.Items(items: $0) }, minHeight: nil)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})))
|
})))
|
||||||
@ -3772,7 +3772,8 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
self.view.endEditing(true)
|
self.view.endEditing(true)
|
||||||
|
|
||||||
if let sourceNode = self.headerNode.buttonNodes[.more]?.referenceNode {
|
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
|
contextController.dismissed = { [weak self] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.state = strongSelf.state.withHighlightedButton(nil)
|
strongSelf.state = strongSelf.state.withHighlightedButton(nil)
|
||||||
@ -4433,7 +4434,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
})))
|
})))
|
||||||
|
|
||||||
if let contextController = contextController {
|
if let contextController = contextController {
|
||||||
contextController.setItems(.single(items), minHeight: nil)
|
contextController.setItems(.single(ContextController.Items(items: items)), minHeight: nil)
|
||||||
} else {
|
} else {
|
||||||
strongSelf.state = strongSelf.state.withHighlightedButton(.voiceChat)
|
strongSelf.state = strongSelf.state.withHighlightedButton(.voiceChat)
|
||||||
if let (layout, navigationHeight) = strongSelf.validLayout {
|
if let (layout, navigationHeight) = strongSelf.validLayout {
|
||||||
@ -4441,7 +4442,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let sourceNode = strongSelf.headerNode.buttonNodes[.voiceChat]?.referenceNode, let controller = strongSelf.controller {
|
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
|
contextController.dismissed = { [weak self] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.state = strongSelf.state.withHighlightedButton(nil)
|
strongSelf.state = strongSelf.state.withHighlightedButton(nil)
|
||||||
@ -4529,7 +4530,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let contextController = contextController {
|
if let contextController = contextController {
|
||||||
contextController.setItems(.single(items), minHeight: nil)
|
contextController.setItems(.single(ContextController.Items(items: items)), minHeight: nil)
|
||||||
} else {
|
} else {
|
||||||
strongSelf.state = strongSelf.state.withHighlightedButton(.voiceChat)
|
strongSelf.state = strongSelf.state.withHighlightedButton(.voiceChat)
|
||||||
if let (layout, navigationHeight) = strongSelf.validLayout {
|
if let (layout, navigationHeight) = strongSelf.validLayout {
|
||||||
@ -4537,7 +4538,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let sourceNode = strongSelf.headerNode.buttonNodes[.voiceChat]?.referenceNode, let controller = strongSelf.controller {
|
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
|
contextController.dismissed = { [weak self] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.state = strongSelf.state.withHighlightedButton(nil)
|
strongSelf.state = strongSelf.state.withHighlightedButton(nil)
|
||||||
@ -5610,7 +5611,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
|
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)
|
self?.logoutAccount(id: id)
|
||||||
}), reactionItems: [], gesture: gesture)
|
}) |> map { ContextController.Items(items: $0) }, reactionItems: [], gesture: gesture)
|
||||||
self.controller?.presentInGlobalOverlay(contextController)
|
self.controller?.presentInGlobalOverlay(contextController)
|
||||||
} else {
|
} else {
|
||||||
gesture?.cancel()
|
gesture?.cancel()
|
||||||
@ -6909,7 +6910,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)
|
self.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user