Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios

This commit is contained in:
Ilya Laktyushin 2020-03-31 15:25:31 +04:00
commit 2d02870d59
35 changed files with 3401 additions and 3156 deletions

View File

@ -5481,3 +5481,6 @@ Any member of this group will be able to see messages in the channel.";
"ForwardedDices_any" = "%@ forwarded dices";
"ForwardedDices_many" = "%@ forwarded dices";
"ForwardedDices_0" = "%@ forwarded dices";
"Conversation.ContextMenuDiscuss" = "Discuss";

View File

@ -157,7 +157,7 @@ private final class ItemNode: ASDisplayNode {
self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
self.containerNode.activated = { [weak self] gesture in
self.containerNode.activated = { [weak self] gesture, _ in
guard let strongSelf = self else {
return
}

View File

@ -135,7 +135,7 @@ private final class ItemNode: ASDisplayNode {
self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
self.containerNode.activated = { [weak self] gesture in
self.containerNode.activated = { [weak self] gesture, _ in
guard let strongSelf = self else {
return
}

View File

@ -529,7 +529,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
}
})
self.contextContainer.activated = { [weak self] gesture in
self.contextContainer.activated = { [weak self] gesture, _ in
guard let strongSelf = self, let item = strongSelf.item else {
return
}

View File

@ -366,7 +366,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
}
})
self.containerNode.activated = { [weak self] gesture in
self.containerNode.activated = { [weak self] gesture, _ in
guard let strongSelf = self, let item = strongSelf.item, let contextAction = item.contextAction else {
gesture.cancel()
return

View File

@ -9,10 +9,11 @@ public final class ContextControllerSourceNode: ASDisplayNode {
self.contextGesture?.isEnabled = self.isGestureEnabled
}
}
public var activated: ((ContextGesture) -> Void)?
public var activated: ((ContextGesture, CGPoint) -> Void)?
public var shouldBegin: ((CGPoint) -> Bool)?
public var customActivationProgress: ((CGFloat, ContextGestureTransition) -> Void)?
public var targetNodeForActivationProgress: ASDisplayNode?
public var targetNodeForActivationProgressContentRect: CGRect?
public func cancelGesture() {
self.contextGesture?.cancel()
@ -41,25 +42,54 @@ public final class ContextControllerSourceNode: ASDisplayNode {
if let customActivationProgress = strongSelf.customActivationProgress {
customActivationProgress(progress, update)
} else {
let targetNode = strongSelf.targetNodeForActivationProgress ?? strongSelf
let targetNode: ASDisplayNode
let targetContentRect: CGRect
if let targetNodeForActivationProgress = strongSelf.targetNodeForActivationProgress {
targetNode = targetNodeForActivationProgress
if let targetNodeForActivationProgressContentRect = strongSelf.targetNodeForActivationProgressContentRect {
targetContentRect = targetNodeForActivationProgressContentRect
} else {
targetContentRect = CGRect(origin: CGPoint(), size: targetNode.bounds.size)
}
} else {
targetNode = strongSelf
targetContentRect = CGRect(origin: CGPoint(), size: targetNode.bounds.size)
}
let minScale: CGFloat = (strongSelf.bounds.width - 10.0) / strongSelf.bounds.width
let scaleSide = max(targetContentRect.width, targetContentRect.height)
let minScale: CGFloat = (scaleSide - 10.0) / scaleSide
let currentScale = 1.0 * (1.0 - progress) + minScale * progress
let originalCenterOffsetX: CGFloat = targetNode.bounds.width / 2.0 - targetContentRect.midX
let scaledCaneterOffsetX: CGFloat = originalCenterOffsetX * currentScale
let originalCenterOffsetY: CGFloat = targetNode.bounds.height / 2.0 - targetContentRect.midY
let scaledCaneterOffsetY: CGFloat = originalCenterOffsetY * currentScale
let scaleMidX: CGFloat = scaledCaneterOffsetX - originalCenterOffsetX
let scaleMidY: CGFloat = scaledCaneterOffsetY - originalCenterOffsetY
switch update {
case .update:
targetNode.layer.sublayerTransform = CATransform3DMakeScale(currentScale, currentScale, 1.0)
let sublayerTransform = CATransform3DTranslate(CATransform3DScale(CATransform3DIdentity, currentScale, currentScale, 1.0), scaleMidX, scaleMidY, 0.0)
targetNode.layer.sublayerTransform = sublayerTransform
case .begin:
targetNode.layer.sublayerTransform = CATransform3DMakeScale(currentScale, currentScale, 1.0)
case let .ended(previousProgress):
let previousScale = 1.0 * (1.0 - previousProgress) + minScale * previousProgress
targetNode.layer.sublayerTransform = CATransform3DMakeScale(currentScale, currentScale, 1.0)
targetNode.layer.animateSpring(from: previousScale as NSNumber, to: currentScale as NSNumber, keyPath: "sublayerTransform.scale", duration: 0.5, delay: 0.0, initialVelocity: 0.0, damping: 90.0)
let sublayerTransform = CATransform3DTranslate(CATransform3DScale(CATransform3DIdentity, currentScale, currentScale, 1.0), scaleMidX, scaleMidY, 0.0)
targetNode.layer.sublayerTransform = sublayerTransform
case .ended:
let sublayerTransform = CATransform3DTranslate(CATransform3DScale(CATransform3DIdentity, currentScale, currentScale, 1.0), scaleMidX, scaleMidY, 0.0)
let previousTransform = targetNode.layer.sublayerTransform
targetNode.layer.sublayerTransform = sublayerTransform
targetNode.layer.animate(from: NSValue(caTransform3D: previousTransform), to: NSValue(caTransform3D: sublayerTransform), keyPath: "sublayerTransform", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.2)
//targetNode.layer.animateSpring(from: previousScale as NSNumber, to: currentScale as NSNumber, keyPath: "sublayerTransform.scale", duration: 0.5, delay: 0.0, initialVelocity: 0.0, damping: 90.0)
}
}
}
contextGesture.activated = { [weak self] gesture in
contextGesture.activated = { [weak self] gesture, location in
if let activated = self?.activated {
activated(gesture)
activated(gesture, location)
} else {
gesture.cancel()
}

View File

@ -57,7 +57,7 @@ public final class ContextGesture: UIGestureRecognizer, UIGestureRecognizerDeleg
public var shouldBegin: ((CGPoint) -> Bool)?
public var activationProgress: ((CGFloat, ContextGestureTransition) -> Void)?
public var activated: ((ContextGesture) -> Void)?
public var activated: ((ContextGesture, CGPoint) -> Void)?
public var externalUpdated: ((UIView?, CGPoint) -> Void)?
public var externalEnded: (((UIView?, CGPoint)?) -> Void)?
@ -95,9 +95,10 @@ public final class ContextGesture: UIGestureRecognizer, UIGestureRecognizerDeleg
guard let touch = touches.first else {
return
}
let location = touch.location(in: self.view)
if let shouldBegin = self.shouldBegin {
if !shouldBegin(touch.location(in: self.view)) {
if !shouldBegin(location) {
self.state = .failed
return
}
@ -132,7 +133,7 @@ public final class ContextGesture: UIGestureRecognizer, UIGestureRecognizerDeleg
case .possible:
strongSelf.delayTimer?.invalidate()
strongSelf.animator?.invalidate()
strongSelf.activated?(strongSelf)
strongSelf.activated?(strongSelf, location)
if let view = strongSelf.view?.superview {
if let window = view.window {
cancelOtherGestures(gesture: strongSelf, view: window)
@ -167,7 +168,7 @@ public final class ContextGesture: UIGestureRecognizer, UIGestureRecognizerDeleg
case .possible:
self.delayTimer?.invalidate()
self.animator?.invalidate()
self.activated?(self)
self.activated?(self, touch.location(in: self.view))
if let view = self.view?.superview {
if let window = view.window {
cancelOtherGestures(gesture: self, view: window)

View File

@ -248,7 +248,7 @@ private final class TabBarNodeContainer {
updateSelectedImage(value)
}
imageNode.containerNode.activated = { [weak self] gesture in
imageNode.containerNode.activated = { [weak self] gesture, _ in
guard let strongSelf = self else {
return
}

View File

@ -135,7 +135,7 @@ public class BaseLinesChartController: BaseChartController {
}
public override func didTapZoomIn(date: Date, pointIndex: Int) {
guard isZoomed == false else { return }
guard !isZoomed, isZoomable else { return }
setDetailsViewModel?(chartDetailsViewModel(closestDate: date, pointIndex: pointIndex, loading: true), false, false)

View File

@ -245,7 +245,7 @@ public class PercentPieChartController: BaseChartController {
}
func didTapZoomIn(date: Date, animated: Bool) {
guard isZoomed == false else { return }
guard !isZoomed, isZoomable else { return }
cancelChartInteraction()
let currentCollection = percentController.chartsCollection
let range: Int = Constants.zoomedRange

View File

@ -206,7 +206,7 @@ public class DailyBarsChartController: BaseChartController {
}
public override func didTapZoomIn(date: Date, pointIndex: Int) {
guard isZoomed == false else { return }
guard !isZoomed, isZoomable else { return }
if isZoomed {
return linesController.hideDetailsView(animated: true)
}

View File

@ -221,7 +221,7 @@ public class StackedBarsChartController: BaseChartController {
}
public override func didTapZoomIn(date: Date, pointIndex: Int) {
guard isZoomed == false else { return }
guard !isZoomed, isZoomable else { return }
if isZoomed {
return zoomedBarsController.hideDetailsView(animated: true)
}

View File

@ -231,7 +231,7 @@ public class StepBarsChartController: BaseChartController {
}
public override func didTapZoomIn(date: Date, pointIndex: Int) {
guard isZoomed == false else { return }
guard !isZoomed, isZoomable else { return }
if isZoomed {
return zoomedBarsController.hideDetailsView(animated: true)
}

View File

@ -529,7 +529,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
}
})
self.containerNode.activated = { [weak self] gesture in
self.containerNode.activated = { [weak self] gesture, _ in
guard let strongSelf = self, let item = strongSelf.layoutParams?.0, let contextAction = item.contextAction else {
gesture.cancel()
return

View File

@ -215,7 +215,7 @@ private final class ChatListViewSpaceState {
if let lastMessage = lowerOrAtAnchorMessages.min(by: { $0.entryIndex < $1.entryIndex }) {
nextLowerIndex = lastMessage.entryIndex
} else {
nextLowerIndex = resolvedAnchorIndex.predecessor
nextLowerIndex = resolvedAnchorIndex
}
let loadedLowerMessages = postbox.chatListTable.entries(groupId: groupId, from: (nextLowerIndex.index, nextLowerIndex.isMessage), to: (lowerBound.index, lowerBound.isMessage), peerChatInterfaceStateTable: postbox.peerChatInterfaceStateTable, count: self.halfLimit - lowerOrAtAnchorMessages.count, predicate: filterPredicate.flatMap { mappedChatListFilterPredicate(postbox: postbox, groupId: groupId, predicate: $0) }).map(mapEntry)
lowerOrAtAnchorMessages.append(contentsOf: loadedLowerMessages)

View File

@ -127,7 +127,7 @@ public final class SelectablePeerNode: ASDisplayNode {
self.contextContainer.addSubnode(self.textNode)
self.contextContainer.addSubnode(self.onlineNode)
self.contextContainer.activated = { [weak self] gesture in
self.contextContainer.activated = { [weak self] gesture, _ in
guard let strongSelf = self, let contextAction = strongSelf.contextAction else {
gesture.cancel()
return

View File

@ -313,7 +313,7 @@ private final class ThemeSettingsAccentColorIconItemNode : ListViewItemNode {
self.containerNode.addSubnode(self.dotsNode)
self.containerNode.addSubnode(self.centerNode)
self.containerNode.activated = { [weak self] gesture in
self.containerNode.activated = { [weak self] gesture, _ in
guard let strongSelf = self, let item = strongSelf.item else {
gesture.cancel()
return

View File

@ -224,7 +224,7 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode {
self.containerNode.addSubnode(self.overlayNode)
self.containerNode.addSubnode(self.titleNode)
self.containerNode.activated = { [weak self] gesture in
self.containerNode.activated = { [weak self] gesture, _ in
guard let strongSelf = self, let item = strongSelf.item else {
gesture.cancel()
return

View File

@ -63,7 +63,7 @@ final class ChatAvatarNavigationNode: ASDisplayNode {
self.containerNode.addSubnode(self.avatarNode)
self.addSubnode(self.containerNode)
self.containerNode.activated = { [weak self] gesture in
self.containerNode.activated = { [weak self] gesture, _ in
guard let strongSelf = self else {
return
}

View File

@ -545,13 +545,15 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self?.openPeer(peerId: id, navigation: navigation, fromMessage: fromMessage)
}, openPeerMention: { [weak self] name in
self?.openPeerMention(name)
}, openMessageContextMenu: { [weak self] message, selectAll, node, frame, recognizer in
}, openMessageContextMenu: { [weak self] message, selectAll, node, frame, anyRecognizer in
guard let strongSelf = self, strongSelf.isNodeLoaded else {
return
}
if strongSelf.presentationInterfaceState.interfaceState.selectionState != nil {
return
}
let recognizer: TapLongTapOrDoubleTapGestureRecognizer? = anyRecognizer as? TapLongTapOrDoubleTapGestureRecognizer
let gesture: ContextGesture? = anyRecognizer as? ContextGesture
if let messages = strongSelf.chatDisplayNode.historyNode.messageGroupInCurrentHistoryView(message.id) {
(strongSelf.view.window as? WindowHost)?.cancelInteractiveKeyboardGestures()
strongSelf.chatDisplayNode.cancelInteractiveKeyboardGestures()
@ -600,7 +602,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let _ = ApplicationSpecificNotice.incrementChatTextSelectionTips(accountManager: strongSelf.context.sharedContext.accountManager).start()
}
let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, message: message)), items: .single(actions), reactionItems: reactionItems, recognizer: recognizer, displayTextSelectionTip: displayTextSelectionTip)
let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, message: message)), items: .single(actions), reactionItems: reactionItems, recognizer: recognizer, gesture: gesture, displayTextSelectionTip: displayTextSelectionTip)
strongSelf.currentContextController = controller
controller.reactionSelected = { [weak controller] value in
guard let strongSelf = self, let message = updatedMessages.first else {

View File

@ -53,7 +53,7 @@ public final class ChatControllerInteraction {
let openMessage: (Message, ChatControllerInteractionOpenMessageMode) -> Bool
let openPeer: (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void
let openPeerMention: (String) -> Void
let openMessageContextMenu: (Message, Bool, ASDisplayNode, CGRect, TapLongTapOrDoubleTapGestureRecognizer?) -> Void
let openMessageContextMenu: (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?) -> Void
let openMessageContextActions: (Message, ASDisplayNode, CGRect, ContextGesture?) -> Void
let navigateToMessage: (MessageId, MessageId) -> Void
let tapMessage: ((Message) -> Void)?
@ -121,7 +121,7 @@ public final class ChatControllerInteraction {
var searchTextHighightState: (String, [MessageIndex])?
var seenOneTimeAnimatedMedia = Set<MessageId>()
init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect, TapLongTapOrDoubleTapGestureRecognizer?) -> Void, openMessageContextActions: @escaping (Message, ASDisplayNode, CGRect, ContextGesture?) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, tapMessage: ((Message) -> Void)?, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendCurrentMessage: @escaping (Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool, ASDisplayNode, CGRect) -> Bool, sendGif: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool) -> Void, requestMessageActionUrlAuth: @escaping (String, MessageId, Int32) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?, Message?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message, ChatMessageItemAssociatedData?) -> Void, openWallpaper: @escaping (Message) -> Void, openTheme: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, chatControllerNode: @escaping () -> ASDisplayNode?, reactionContainerNode: @escaping () -> ReactionSelectionParentNode?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction, Message?) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> Bool, navigateToFirstDateMessage: @escaping(Int32) ->Void, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> Void, rateCall: @escaping (Message, CallId) -> Void, requestSelectMessagePollOptions: @escaping (MessageId, [Data]) -> Void, requestOpenMessagePollResults: @escaping (MessageId, MediaId) -> Void, openAppStorePage: @escaping () -> Void, displayMessageTooltip: @escaping (MessageId, String, ASDisplayNode?, CGRect?) -> Void, seekToTimecode: @escaping (Message, Double, Bool) -> Void, scheduleCurrentMessage: @escaping () -> Void, sendScheduledMessagesNow: @escaping ([MessageId]) -> Void, editScheduledMessagesTime: @escaping ([MessageId]) -> Void, performTextSelectionAction: @escaping (UInt32, String, TextSelectionAction) -> Void, updateMessageReaction: @escaping (MessageId, String?) -> Void, openMessageReactions: @escaping (MessageId) -> Void, displaySwipeToReplyHint: @escaping () -> Void, dismissReplyMarkupMessage: @escaping (Message) -> Void, openMessagePollResults: @escaping (MessageId, Data) -> Void, openPollCreation: @escaping (Bool?) -> Void, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, pollActionState: ChatInterfacePollActionState, stickerSettings: ChatInterfaceStickerSettings) {
init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?) -> Void, openMessageContextActions: @escaping (Message, ASDisplayNode, CGRect, ContextGesture?) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, tapMessage: ((Message) -> Void)?, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendCurrentMessage: @escaping (Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool, ASDisplayNode, CGRect) -> Bool, sendGif: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool) -> Void, requestMessageActionUrlAuth: @escaping (String, MessageId, Int32) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?, Message?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message, ChatMessageItemAssociatedData?) -> Void, openWallpaper: @escaping (Message) -> Void, openTheme: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, chatControllerNode: @escaping () -> ASDisplayNode?, reactionContainerNode: @escaping () -> ReactionSelectionParentNode?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction, Message?) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> Bool, navigateToFirstDateMessage: @escaping(Int32) ->Void, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> Void, rateCall: @escaping (Message, CallId) -> Void, requestSelectMessagePollOptions: @escaping (MessageId, [Data]) -> Void, requestOpenMessagePollResults: @escaping (MessageId, MediaId) -> Void, openAppStorePage: @escaping () -> Void, displayMessageTooltip: @escaping (MessageId, String, ASDisplayNode?, CGRect?) -> Void, seekToTimecode: @escaping (Message, Double, Bool) -> Void, scheduleCurrentMessage: @escaping () -> Void, sendScheduledMessagesNow: @escaping ([MessageId]) -> Void, editScheduledMessagesTime: @escaping ([MessageId]) -> Void, performTextSelectionAction: @escaping (UInt32, String, TextSelectionAction) -> Void, updateMessageReaction: @escaping (MessageId, String?) -> Void, openMessageReactions: @escaping (MessageId) -> Void, displaySwipeToReplyHint: @escaping () -> Void, dismissReplyMarkupMessage: @escaping (Message) -> Void, openMessagePollResults: @escaping (MessageId, Data) -> Void, openPollCreation: @escaping (Bool?) -> Void, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, pollActionState: ChatInterfacePollActionState, stickerSettings: ChatInterfaceStickerSettings) {
self.openMessage = openMessage
self.openPeer = openPeer
self.openPeerMention = openPeerMention

View File

@ -257,6 +257,7 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
var loadCopyMediaResource: MediaResource?
var isAction = false
var isDice = false
var canDiscuss = false
if messages.count == 1 {
for media in messages[0].media {
if let file = media as? TelegramMediaFile {
@ -292,6 +293,17 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
if let channel = messages[0].peers[messages[0].id.peerId] as? TelegramChannel {
if !isAction {
canPin = channel.hasPermission(.pinMessages)
if messages[0].id.namespace == Namespaces.Message.Cloud {
switch channel.info {
case let .broadcast(info):
if info.flags.contains(.hasDiscussionGroup) {
canDiscuss = true
}
case .group:
break
}
}
}
} else if let group = messages[0].peers[messages[0].id.peerId] as? TelegramGroup {
if !isAction {
@ -693,6 +705,94 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
})))
}
if canDiscuss {
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuDiscuss, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Message"), color: theme.actionSheet.primaryTextColor)
}, action: { c, _ in
let timestamp = messages[0].timestamp
let channelMessageId = messages[0].id
enum DiscussMessageResult {
case message(MessageId)
case peer(PeerId)
}
let signal = context.account.postbox.transaction { transaction -> PeerId? in
if let cachedData = transaction.getPeerCachedData(peerId: messages[0].id.peerId) as? CachedChannelData {
return cachedData.linkedDiscussionPeerId
} else {
return nil
}
}
|> mapToSignal { peerId -> Signal<DiscussMessageResult?, NoError> in
guard let peerId = peerId else {
return .single(nil)
}
let historyView = preloadedChatHistoryViewForLocation(ChatHistoryLocationInput(content: .InitialSearch(location: .index(MessageIndex(id: MessageId(peerId: peerId, namespace: 0, id: 0), timestamp: timestamp)), count: 30), id: 0), account: context.account, chatLocation: .peer(peerId), fixedCombinedReadStates: nil, tagMask: nil, additionalData: [])
return historyView
|> mapToSignal { historyView -> Signal<(MessageIndex?, Bool), NoError> in
switch historyView {
case .Loading:
return .single((nil, true))
case let .HistoryView(view, _, _, _, _, _, _):
for entry in view.entries {
for attribute in entry.message.attributes {
if let attribute = attribute as? SourceReferenceMessageAttribute {
if attribute.messageId == channelMessageId {
return .single((entry.message.index, false))
}
}
}
}
return .single((nil, false))
}
}
|> take(until: { index in
return SignalTakeAction(passthrough: true, complete: !index.1)
})
|> last
|> map { result -> DiscussMessageResult? in
if let index = result?.0 {
return .message(index.id)
} else {
return .peer(peerId)
}
}
}
let foundIndex = Promise<DiscussMessageResult?>()
foundIndex.set(signal)
c.dismiss(completion: { [weak interfaceInteraction] in
var cancelImpl: (() -> Void)?
let statusController = OverlayStatusController(theme: chatPresentationInterfaceState.theme, type: .loading(cancelled: {
cancelImpl?()
}))
controllerInteraction.presentController(statusController, nil)
let disposable = (foundIndex.get()
|> take(1)
|> deliverOnMainQueue).start(next: { [weak statusController] result in
statusController?.dismiss()
if let result = result {
switch result {
case let .message(id):
interfaceInteraction?.navigateToMessage(id)
case let .peer(peerId):
interfaceInteraction?.navigateToChat(peerId)
}
}
})
cancelImpl = { [weak statusController] in
disposable.dispose()
statusController?.dismiss()
}
})
})))
}
if data.messageActions.options.contains(.report) {
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuReport, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Report"), color: theme.actionSheet.primaryTextColor)

View File

@ -23,6 +23,12 @@ import GridMessageSelectionNode
import AppBundle
import Markdown
private enum InternalBubbleTapAction {
case action(() -> Void)
case optionalAction(() -> Void)
case openContextMenu(tapMessage: Message, selectAll: Bool, subFrame: CGRect)
}
private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> [(Message, AnyClass, ChatMessageEntryAttributes)] {
var result: [(Message, AnyClass, ChatMessageEntryAttributes)] = []
var skipText = false
@ -145,6 +151,7 @@ private enum ContentNodeOperation {
class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode {
private let contextSourceNode: ContextExtractedContentContainingNode
private let containerNode: ContextControllerSourceNode
private let backgroundWallpaperNode: ChatMessageBubbleBackdrop
private let backgroundNode: ChatMessageBackground
private let shadowNode: ChatMessageShadowNode
@ -201,6 +208,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
required init() {
self.contextSourceNode = ContextExtractedContentContainingNode()
self.containerNode = ContextControllerSourceNode()
self.backgroundWallpaperNode = ChatMessageBubbleBackdrop()
self.backgroundNode = ChatMessageBackground()
@ -209,7 +217,48 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
super.init(layerBacked: false)
self.addSubnode(self.contextSourceNode)
self.containerNode.shouldBegin = { [weak self] location in
guard let strongSelf = self else {
return false
}
if !strongSelf.backgroundNode.frame.contains(location) {
return false
}
if let action = strongSelf.gestureRecognized(gesture: .tap, location: location, recognizer: nil) {
if case .action = action {
return false
}
}
if let action = strongSelf.gestureRecognized(gesture: .longTap, location: location, recognizer: nil) {
switch action {
case .action, .optionalAction:
return false
case .openContextMenu:
return true
}
}
return true
}
self.containerNode.activated = { [weak self] gesture, location in
guard let strongSelf = self, let item = strongSelf.item else {
return
}
if let action = strongSelf.gestureRecognized(gesture: .longTap, location: location, recognizer: nil) {
switch action {
case .action, .optionalAction:
break
case let .openContextMenu(tapMessage, selectAll, subFrame):
item.controllerInteraction.openMessageContextMenu(tapMessage, selectAll, strongSelf, subFrame, gesture)
}
}
}
self.containerNode.addSubnode(self.contextSourceNode)
self.containerNode.targetNodeForActivationProgress = self.contextSourceNode.contentNode
self.addSubnode(self.containerNode)
self.contextSourceNode.contentNode.addSubnode(self.backgroundWallpaperNode)
self.contextSourceNode.contentNode.addSubnode(self.backgroundNode)
self.addSubnode(self.messageAccessibilityArea)
@ -385,8 +434,18 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
return
}
strongSelf.reactionRecognizer?.cancel()
if strongSelf.gestureRecognized(gesture: .longTap, location: point, recognizer: recognizer) {
recognizer.cancel()
if let action = strongSelf.gestureRecognized(gesture: .longTap, location: point, recognizer: recognizer) {
switch action {
case let .action(f):
f()
recognizer.cancel()
case let .optionalAction(f):
f()
recognizer.cancel()
case .openContextMenu:
//recognizer.cancel()
break
}
}
}
recognizer.highlight = { [weak self] point in
@ -1688,7 +1747,8 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
guard let strongSelf = selfReference.value else {
return
}
let previousContextFrame = strongSelf.contextSourceNode.frame
let previousContextFrame = strongSelf.containerNode.frame
strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: layout.contentSize)
strongSelf.contextSourceNode.frame = CGRect(origin: CGPoint(), size: layout.contentSize)
strongSelf.contextSourceNode.contentNode.frame = CGRect(origin: CGPoint(), size: layout.contentSize)
@ -2052,6 +2112,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
let previousContextContentFrame = strongSelf.contextSourceNode.contentRect
strongSelf.contextSourceNode.contentRect = backgroundFrame.offsetBy(dx: incomingOffset, dy: 0.0)
strongSelf.containerNode.targetNodeForActivationProgressContentRect = strongSelf.contextSourceNode.contentRect
if previousContextFrame.size != strongSelf.contextSourceNode.bounds.size || previousContextContentFrame != strongSelf.contextSourceNode.contentRect {
strongSelf.contextSourceNode.layoutUpdated?(strongSelf.contextSourceNode.bounds.size)
@ -2212,6 +2273,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
break
}
self.contextSourceNode.contentRect = backgroundFrame.offsetBy(dx: incomingOffset, dy: 0.0)
self.containerNode.targetNodeForActivationProgressContentRect = self.contextSourceNode.contentRect
if !self.contextSourceNode.isExtractedToContextPreview {
if let (rect, size) = self.absoluteRect {
self.updateAbsoluteRect(rect, within: size)
@ -2246,14 +2308,25 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
switch recognizer.state {
case .ended:
if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation {
let _ = self.gestureRecognized(gesture: gesture, location: location, recognizer: nil)
if let action = self.gestureRecognized(gesture: gesture, location: location, recognizer: nil) {
switch action {
case let .action(f):
f()
case let .optionalAction(f):
f()
case let .openContextMenu(tapMessage, selectAll, subFrame):
self.item?.controllerInteraction.openMessageContextMenu(tapMessage, selectAll, self, subFrame, nil)
}
} else if case .tap = gesture {
self.item?.controllerInteraction.clickThroughMessage()
}
}
default:
break
}
}
private func gestureRecognized(gesture: TapLongTapOrDoubleTapGesture, location: CGPoint, recognizer: TapLongTapOrDoubleTapGestureRecognizer?) -> Bool {
private func gestureRecognized(gesture: TapLongTapOrDoubleTapGesture, location: CGPoint, recognizer: TapLongTapOrDoubleTapGestureRecognizer?) -> InternalBubbleTapAction? {
var mediaMessage: Message?
var forceOpen = false
if let item = self.item {
@ -2290,38 +2363,39 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
switch gesture {
case .tap:
if let avatarNode = self.accessoryItemNode as? ChatMessageAvatarAccessoryItemNode, avatarNode.frame.contains(location) {
if let item = self.item, let author = item.content.firstMessage.author {
var openPeerId = item.effectiveAuthorId ?? author.id
var navigate: ChatControllerInteractionNavigateToPeer
if item.content.firstMessage.id.peerId == item.context.account.peerId {
navigate = .chat(textInputState: nil, subject: nil)
} else {
navigate = .info
}
for attribute in item.content.firstMessage.attributes {
if let attribute = attribute as? SourceReferenceMessageAttribute {
openPeerId = attribute.messageId.peerId
navigate = .chat(textInputState: nil, subject: .message(attribute.messageId))
return .action({
if let item = self.item, let author = item.content.firstMessage.author {
var openPeerId = item.effectiveAuthorId ?? author.id
var navigate: ChatControllerInteractionNavigateToPeer
if item.content.firstMessage.id.peerId == item.context.account.peerId {
navigate = .chat(textInputState: nil, subject: nil)
} else {
navigate = .info
}
}
if item.effectiveAuthorId?.namespace == Namespaces.Peer.Empty {
item.controllerInteraction.displayMessageTooltip(item.content.firstMessage.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, self, avatarNode.frame)
} else {
if item.message.id.peerId == item.context.account.peerId, let channel = item.content.firstMessage.forwardInfo?.author as? TelegramChannel, channel.username == nil {
if case let .broadcast(info) = channel.info, info.flags.contains(.hasDiscussionGroup) {
} else if case .member = channel.participationStatus {
} else {
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_PrivateChannelTooltip, self, avatarNode.frame)
return true
for attribute in item.content.firstMessage.attributes {
if let attribute = attribute as? SourceReferenceMessageAttribute {
openPeerId = attribute.messageId.peerId
navigate = .chat(textInputState: nil, subject: .message(attribute.messageId))
}
}
item.controllerInteraction.openPeer(openPeerId, navigate, item.message)
if item.effectiveAuthorId?.namespace == Namespaces.Peer.Empty {
item.controllerInteraction.displayMessageTooltip(item.content.firstMessage.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, self, avatarNode.frame)
} else {
if item.message.id.peerId == item.context.account.peerId, let channel = item.content.firstMessage.forwardInfo?.author as? TelegramChannel, channel.username == nil {
if case let .broadcast(info) = channel.info, info.flags.contains(.hasDiscussionGroup) {
} else if case .member = channel.participationStatus {
} else {
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_PrivateChannelTooltip, self, avatarNode.frame)
return
}
}
item.controllerInteraction.openPeer(openPeerId, navigate, item.message)
}
}
}
return true
})
}
if let nameNode = self.nameNode, nameNode.frame.contains(location) {
@ -2335,15 +2409,16 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
botAddressName = attribute.title
}
if let botAddressName = botAddressName {
item.controllerInteraction.updateInputState { textInputState in
return ChatTextInputState(inputText: NSAttributedString(string: "@" + botAddressName + " "))
return .optionalAction({
if let botAddressName = botAddressName {
item.controllerInteraction.updateInputState { textInputState in
return ChatTextInputState(inputText: NSAttributedString(string: "@" + botAddressName + " "))
}
item.controllerInteraction.updateInputMode { _ in
return .text
}
}
item.controllerInteraction.updateInputMode { _ in
return .text
}
}
return true
})
}
}
}
@ -2351,119 +2426,133 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
if let item = self.item {
for attribute in item.message.attributes {
if let attribute = attribute as? ReplyMessageAttribute {
item.controllerInteraction.navigateToMessage(item.message.id, attribute.messageId)
return true
return .optionalAction({
item.controllerInteraction.navigateToMessage(item.message.id, attribute.messageId)
})
}
}
}
}
if let forwardInfoNode = self.forwardInfoNode, forwardInfoNode.frame.contains(location) {
if let item = self.item, let forwardInfo = item.message.forwardInfo {
if let sourceMessageId = forwardInfo.sourceMessageId {
if let channel = forwardInfo.author as? TelegramChannel, channel.username == nil {
if case let .broadcast(info) = channel.info, info.flags.contains(.hasDiscussionGroup) {
} else if case .member = channel.participationStatus {
} else {
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_PrivateChannelTooltip, forwardInfoNode, nil)
return true
return .optionalAction({
if let sourceMessageId = forwardInfo.sourceMessageId {
if let channel = forwardInfo.author as? TelegramChannel, channel.username == nil {
if case let .broadcast(info) = channel.info, info.flags.contains(.hasDiscussionGroup) {
} else if case .member = channel.participationStatus {
} else {
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_PrivateChannelTooltip, forwardInfoNode, nil)
return
}
}
item.controllerInteraction.navigateToMessage(item.message.id, sourceMessageId)
} else if let id = forwardInfo.source?.id ?? forwardInfo.author?.id {
item.controllerInteraction.openPeer(id, .info, nil)
} else if let _ = forwardInfo.authorSignature {
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, forwardInfoNode, nil)
}
item.controllerInteraction.navigateToMessage(item.message.id, sourceMessageId)
} else if let id = forwardInfo.source?.id ?? forwardInfo.author?.id {
item.controllerInteraction.openPeer(id, .info, nil)
} else if let _ = forwardInfo.authorSignature {
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, forwardInfoNode, nil)
}
return true
})
}
}
var foundTapAction = false
loop: for contentNode in self.contentNodes {
let tapAction = contentNode.tapActionAtPoint(CGPoint(x: location.x - contentNode.frame.minX, y: location.y - contentNode.frame.minY), gesture: gesture, isEstimating: false)
switch tapAction {
case .none, .ignore:
if let item = self.item, self.backgroundNode.frame.contains(CGPoint(x: self.frame.width - location.x, y: location.y)), let tapMessage = self.item?.controllerInteraction.tapMessage {
foundTapAction = true
case .none:
if let item = self.item, self.backgroundNode.frame.contains(CGPoint(x: self.frame.width - location.x, y: location.y)), let tapMessage = self.item?.controllerInteraction.tapMessage {
return .action({
tapMessage(item.message)
}
break
case let .url(url, concealed):
foundTapAction = true
})
}
case .ignore:
if let item = self.item, self.backgroundNode.frame.contains(CGPoint(x: self.frame.width - location.x, y: location.y)), let tapMessage = self.item?.controllerInteraction.tapMessage {
return .action({
tapMessage(item.message)
})
} else {
return .action({
})
}
case let .url(url, concealed):
return .action({
self.item?.controllerInteraction.openUrl(url, concealed, nil, self.item?.content.firstMessage)
break loop
case let .peerMention(peerId, _):
foundTapAction = true
})
case let .peerMention(peerId, _):
return .action({
self.item?.controllerInteraction.openPeer(peerId, .chat(textInputState: nil, subject: nil), nil)
break loop
case let .textMention(name):
foundTapAction = true
})
case let .textMention(name):
return .action({
self.item?.controllerInteraction.openPeerMention(name)
break loop
case let .botCommand(command):
foundTapAction = true
if let item = self.item {
})
case let .botCommand(command):
if let item = self.item {
return .action({
item.controllerInteraction.sendBotCommand(item.message.id, command)
}
break loop
case let .hashtag(peerName, hashtag):
foundTapAction = true
})
}
case let .hashtag(peerName, hashtag):
return .action({
self.item?.controllerInteraction.openHashtag(peerName, hashtag)
break loop
case .instantPage:
foundTapAction = true
if let item = self.item {
})
case .instantPage:
if let item = self.item {
return .optionalAction({
item.controllerInteraction.openInstantPage(item.message, item.associatedData)
}
break loop
case .wallpaper:
foundTapAction = true
if let item = self.item {
})
}
case .wallpaper:
if let item = self.item {
return .action({
item.controllerInteraction.openWallpaper(item.message)
}
break loop
case .theme:
foundTapAction = true
if let item = self.item {
})
}
case .theme:
if let item = self.item {
return .action({
item.controllerInteraction.openTheme(item.message)
}
break loop
case let .call(peerId):
foundTapAction = true
})
}
case let .call(peerId):
return .optionalAction({
self.item?.controllerInteraction.callPeer(peerId)
break loop
case .openMessage:
foundTapAction = true
if let item = self.item {
let _ = item.controllerInteraction.openMessage(item.message, .default)
})
case .openMessage:
if let item = self.item {
if let type = self.backgroundNode.type, case .none = type {
return .optionalAction({
let _ = item.controllerInteraction.openMessage(item.message, .default)
})
} else {
return .action({
let _ = item.controllerInteraction.openMessage(item.message, .default)
})
}
break loop
case let .timecode(timecode, _):
foundTapAction = true
if let item = self.item, let mediaMessage = mediaMessage {
}
case let .timecode(timecode, _):
if let item = self.item, let mediaMessage = mediaMessage {
return .action({
item.controllerInteraction.seekToTimecode(mediaMessage, timecode, forceOpen)
}
break loop
case let .bankCard(number):
foundTapAction = true
if let item = self.item {
})
}
case let .bankCard(number):
if let item = self.item {
return .action({
item.controllerInteraction.longTap(.bankCard(number), item.message)
}
case let .tooltip(text, node, rect):
foundTapAction = true
if let item = self.item {
})
}
case let .tooltip(text, node, rect):
if let item = self.item {
return .action({
let _ = item.controllerInteraction.displayMessageTooltip(item.message.id, text, node, rect)
}
break loop
})
}
}
}
if !foundTapAction {
self.item?.controllerInteraction.clickThroughMessage()
}
return nil
case .longTap, .doubleTap:
if let item = self.item, self.backgroundNode.frame.contains(location) {
let message = item.message
var foundTapAction = false
var tapMessage: Message? = item.content.firstMessage
var selectAll = true
loop: for contentNode in self.contentNodes {
@ -2475,53 +2564,53 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
tapMessage = contentNode.item?.message
let tapAction = contentNode.tapActionAtPoint(CGPoint(x: location.x - contentNode.frame.minX, y: location.y - contentNode.frame.minY), gesture: gesture, isEstimating: false)
switch tapAction {
case .none, .ignore:
break
case let .url(url, _):
foundTapAction = true
case .none, .ignore:
break
case let .url(url, _):
return .action({
item.controllerInteraction.longTap(.url(url), message)
break loop
case let .peerMention(peerId, mention):
foundTapAction = true
})
case let .peerMention(peerId, mention):
return .action({
item.controllerInteraction.longTap(.peerMention(peerId, mention), message)
break loop
case let .textMention(name):
foundTapAction = true
})
case let .textMention(name):
return .action({
item.controllerInteraction.longTap(.mention(name), message)
break loop
case let .botCommand(command):
foundTapAction = true
})
case let .botCommand(command):
return .action({
item.controllerInteraction.longTap(.command(command), message)
break loop
case let .hashtag(_, hashtag):
foundTapAction = true
})
case let .hashtag(_, hashtag):
return .action({
item.controllerInteraction.longTap(.hashtag(hashtag), message)
break loop
case .instantPage:
break
case .wallpaper:
break
case .theme:
break
case .call:
break
case .openMessage:
foundTapAction = false
break
case let .timecode(timecode, text):
foundTapAction = true
if let mediaMessage = mediaMessage {
})
case .instantPage:
break
case .wallpaper:
break
case .theme:
break
case .call:
break
case .openMessage:
break
case let .timecode(timecode, text):
if let mediaMessage = mediaMessage {
return .action({
item.controllerInteraction.longTap(.timecode(timecode, text), mediaMessage)
}
break loop
case let .bankCard(number):
foundTapAction = true
})
}
case let .bankCard(number):
return .action({
item.controllerInteraction.longTap(.bankCard(number), message)
case .tooltip:
break
})
case .tooltip:
break
}
}
if !foundTapAction, let tapMessage = tapMessage {
if let tapMessage = tapMessage {
var subFrame = self.backgroundNode.frame
if case .group = item.content {
for contentNode in self.contentNodes {
@ -2531,14 +2620,13 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
}
}
}
item.controllerInteraction.openMessageContextMenu(tapMessage, selectAll, self, subFrame, recognizer)
return false
return .openContextMenu(tapMessage: tapMessage, selectAll: selectAll, subFrame: subFrame)
}
}
default:
break
}
return true
return nil
}
private func traceSelectionNodes(parent: ASDisplayNode, point: CGPoint) -> ASDisplayNode? {

View File

@ -118,7 +118,7 @@ final class ChatRecordingPreviewInputPanelNode: ChatInputPanelNode {
let gestureRecognizer = ContextGesture(target: nil, action: nil)
self.sendButton.view.addGestureRecognizer(gestureRecognizer)
self.gestureRecognizer = gestureRecognizer
gestureRecognizer.activated = { [weak self] gesture in
gestureRecognizer.activated = { [weak self] gesture, _ in
guard let strongSelf = self else {
return
}

View File

@ -70,7 +70,7 @@ final class ChatTextInputActionButtonsNode: ASDisplayNode {
let gestureRecognizer = ContextGesture(target: nil, action: nil)
self.gestureRecognizer = gestureRecognizer
self.sendButton.view.addGestureRecognizer(gestureRecognizer)
gestureRecognizer.activated = { [weak self] recognizer in
gestureRecognizer.activated = { [weak self] recognizer, _ in
guard let strongSelf = self else {
return
}

View File

@ -183,7 +183,7 @@ final class GridMessageItemNode: GridItemNode {
self.containerNode.addSubnode(self.imageNode)
self.containerNode.addSubnode(self.mediaBadgeNode)
self.containerNode.activated = { [weak self] gesture in
self.containerNode.activated = { [weak self] gesture, _ in
guard let strongSelf = self, let item = strongSelf.item, let controllerInteraction = strongSelf.controllerInteraction else {
gesture.cancel()
return
@ -458,8 +458,7 @@ final class GridMessageItemNode: GridItemNode {
let _ = controllerInteraction.openMessage(message, .default)
}
case .longTap:
break
//controllerInteraction.openMessageContextMenu(message, false, self, self.bounds, nil)
break
default:
break
}

View File

@ -162,7 +162,7 @@ final class MultiplexedVideoNode: ASDisplayNode, UIScrollViewDelegate {
return strongSelf.fileAt(point: point) != nil
}
self.contextContainerNode.activated = { [weak self] gesture in
self.contextContainerNode.activated = { [weak self] gesture, _ in
guard let strongSelf = self, let gestureLocation = gestureLocation else {
return
}

View File

@ -71,7 +71,7 @@ private final class VisualMediaItemNode: ASDisplayNode {
self.containerNode.addSubnode(self.imageNode)
self.containerNode.addSubnode(self.mediaBadgeNode)
self.containerNode.activated = { [weak self] gesture in
self.containerNode.activated = { [weak self] gesture, _ in
guard let strongSelf = self, let item = strongSelf.item else {
return
}

View File

@ -142,7 +142,7 @@ public class PeerMediaCollectionController: TelegramBaseController {
guard let strongSelf = self else {
return
}
let items = (chatAvailableMessageActionsImpl(postbox: strongSelf.context.account.postbox, accountPeerId: strongSelf.context.account.peerId, messageIds: [message.id])
(chatAvailableMessageActionsImpl(postbox: strongSelf.context.account.postbox, accountPeerId: strongSelf.context.account.peerId, messageIds: [message.id])
|> deliverOnMainQueue).start(next: { actions in
var messageIds = Set<MessageId>()
messageIds.insert(message.id)

View File

@ -130,7 +130,12 @@ public final class WalletInfoScreen: ViewController {
guard let strongSelf = self else {
return
}
strongSelf.push(WalletTransactionInfoScreen(context: strongSelf.context, walletInfo: strongSelf.walletInfo, walletTransaction: transaction, walletState: (strongSelf.displayNode as! WalletInfoScreenNode).statePromise.get(), enableDebugActions: strongSelf.enableDebugActions))
strongSelf.push(WalletTransactionInfoScreen(context: strongSelf.context, walletInfo: strongSelf.walletInfo, walletTransaction: transaction, walletState: (strongSelf.displayNode as! WalletInfoScreenNode).statePromise.get(), enableDebugActions: strongSelf.enableDebugActions, decryptionKeyUpdated: { key in
guard let strongSelf = self else {
return
}
(strongSelf.displayNode as! WalletInfoScreenNode).updateTransactionDecryptionKey(key)
}))
}, present: { [weak self] c, a in
guard let strongSelf = self else {
return
@ -653,10 +658,11 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
guard let strongSelf = self, let (_, _) = strongSelf.validLayout else {
return
}
let topInset = strongSelf.listNode.insets.top - strongSelf.listNode.headerInsets.top
switch strongSelf.listNode.visibleContentOffset() {
case let .known(offset):
if offset < strongSelf.listNode.insets.top {
if offset > strongSelf.listNode.insets.top / 2.0 {
if offset < topInset {
if offset > topInset / 2.0 {
strongSelf.scrollToHideHeader()
} else {
strongSelf.scrollToTop()
@ -844,6 +850,10 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
self.transactionDecryptionKeyDisposable?.dispose()
}
func updateTransactionDecryptionKey(_ key: WalletTransactionDecryptionKey) {
self.transactionDecryptionKey.set(.single(key))
}
func scrollToHideHeader() {
guard let (_, navigationHeight) = self.validLayout else {
return

View File

@ -449,12 +449,12 @@ public final class WalletStrings: Equatable {
public var Wallet_SecureStorageReset_Title: String { return self._s[219]! }
public var Wallet_Receive_CommentHeader: String { return self._s[220]! }
public var Wallet_Info_ReceiveGrams: String { return self._s[221]! }
public func Wallet_Updated_HoursAgo(_ value: Int32) -> String {
public func Wallet_Updated_MinutesAgo(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = walletStringsFormattedNumber(value, self.groupingSeparator)
return String(format: self._ps[0 * 6 + Int(form.rawValue)]!, stringValue)
}
public func Wallet_Updated_MinutesAgo(_ value: Int32) -> String {
public func Wallet_Updated_HoursAgo(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = walletStringsFormattedNumber(value, self.groupingSeparator)
return String(format: self._ps[1 * 6 + Int(form.rawValue)]!, stringValue)

View File

@ -191,17 +191,19 @@ final class WalletTransactionInfoScreen: ViewController {
private let walletInfo: WalletInfo?
private var walletTransaction: WalletInfoTransaction
private let walletState: Signal<(CombinedWalletState, Bool), NoError>
private let decryptionKeyUpdated: (WalletTransactionDecryptionKey) -> Void
private var presentationData: WalletPresentationData
private var walletStateDisposable: Disposable?
private var combinedState: CombinedWalletState?
private var reloadingState = false
public init(context: WalletContext, walletInfo: WalletInfo?, walletTransaction: WalletInfoTransaction, walletState: Signal<(CombinedWalletState, Bool), NoError>, enableDebugActions: Bool) {
public init(context: WalletContext, walletInfo: WalletInfo?, walletTransaction: WalletInfoTransaction, walletState: Signal<(CombinedWalletState, Bool), NoError>, enableDebugActions: Bool, decryptionKeyUpdated: @escaping (WalletTransactionDecryptionKey) -> Void) {
self.context = context
self.walletInfo = walletInfo
self.walletTransaction = walletTransaction
self.walletState = walletState
self.decryptionKeyUpdated = decryptionKeyUpdated
self.presentationData = context.presentationData
@ -238,7 +240,7 @@ final class WalletTransactionInfoScreen: ViewController {
}
override public func loadDisplayNode() {
self.displayNode = WalletTransactionInfoScreenNode(context: self.context, presentationData: self.presentationData, walletTransaction: self.walletTransaction)
self.displayNode = WalletTransactionInfoScreenNode(context: self.context, presentationData: self.presentationData, walletTransaction: self.walletTransaction, decryptionKeyUpdated: self.decryptionKeyUpdated)
(self.displayNode as! WalletTransactionInfoScreenNode).send = { [weak self] address in
guard let strongSelf = self else {
return
@ -284,6 +286,7 @@ final class WalletTransactionInfoScreen: ViewController {
return
}
if let decryptionKey = decryptionKey {
strongSelf.decryptionKeyUpdated(decryptionKey)
let _ = (decryptWalletTransactions(decryptionKey: decryptionKey, transactions: [walletTransaction], tonInstance: strongSelf.context.tonInstance)
|> deliverOnMainQueue).start(next: { result in
guard let strongSelf = self, let updatedTransaction = result.first else {
@ -400,6 +403,7 @@ private final class WalletTransactionInfoScreenNode: ViewControllerTracingNode,
private var presentationData: WalletPresentationData
private var walletTransaction: WalletInfoTransaction
private let incoming: Bool
private let decryptionKeyUpdated: (WalletTransactionDecryptionKey) -> Void
private let titleNode: ImmediateTextNode
private let timeNode: ImmediateTextNode
@ -426,18 +430,21 @@ private final class WalletTransactionInfoScreenNode: ViewControllerTracingNode,
var displayFeesTooltip: ((ASDisplayNode, CGRect) -> Void)?
var displayCopyContextMenu: ((ASDisplayNode, CGRect, String) -> Void)?
init(context: WalletContext, presentationData: WalletPresentationData, walletTransaction: WalletInfoTransaction) {
init(context: WalletContext, presentationData: WalletPresentationData, walletTransaction: WalletInfoTransaction, decryptionKeyUpdated: @escaping (WalletTransactionDecryptionKey) -> Void) {
self.context = context
self.presentationData = presentationData
self.walletTransaction = walletTransaction
self.decryptionKeyUpdated = decryptionKeyUpdated
self.titleNode = ImmediateTextNode()
self.titleNode.textAlignment = .center
self.titleNode.maximumNumberOfLines = 1
self.titleNode.displaysAsynchronously = false
self.timeNode = ImmediateTextNode()
self.timeNode.textAlignment = .center
self.timeNode.maximumNumberOfLines = 1
self.timeNode.displaysAsynchronously = false
self.navigationBackgroundNode = ASDisplayNode()
self.navigationBackgroundNode.backgroundColor = self.presentationData.theme.navigationBar.backgroundColor
@ -453,6 +460,7 @@ private final class WalletTransactionInfoScreenNode: ViewControllerTracingNode,
self.feesNode.textAlignment = .center
self.feesNode.maximumNumberOfLines = 2
self.feesNode.lineSpacing = 0.35
self.feesNode.displaysAsynchronously = false
self.feesInfoIconNode = ASImageNode()
self.feesInfoIconNode.displaysAsynchronously = false
@ -469,6 +477,7 @@ private final class WalletTransactionInfoScreenNode: ViewControllerTracingNode,
self.commentTextNode.textAlignment = .natural
self.commentTextNode.maximumNumberOfLines = 0
self.commentTextNode.isUserInteractionEnabled = false
self.commentTextNode.displaysAsynchronously = false
self.commentSeparatorNode = ASDisplayNode()
self.commentSeparatorNode.backgroundColor = self.presentationData.theme.list.itemPlainSeparatorColor
@ -478,6 +487,7 @@ private final class WalletTransactionInfoScreenNode: ViewControllerTracingNode,
self.commentDecryptButtonTitle.textAlignment = .natural
self.commentDecryptButtonTitle.maximumNumberOfLines = 0
self.commentDecryptButtonTitle.isUserInteractionEnabled = false
self.commentDecryptButtonTitle.displaysAsynchronously = false
self.commentDecryptButton = HighlightableButtonNode()
self.commentDecryptButton.hitTestSlop = UIEdgeInsets(top: -10.0, left: -10.0, bottom: -10.0, right: -10.0)
@ -486,6 +496,7 @@ private final class WalletTransactionInfoScreenNode: ViewControllerTracingNode,
self.addressTextNode.maximumNumberOfLines = 4
self.addressTextNode.textAlignment = .justified
self.addressTextNode.lineSpacing = 0.35
self.addressTextNode.displaysAsynchronously = false
self.buttonNode = SolidRoundedButtonNode(title: "", icon: nil, theme: SolidRoundedButtonTheme(backgroundColor: self.presentationData.theme.setup.buttonFillColor, foregroundColor: self.presentationData.theme.setup.buttonForegroundColor), height: 50.0, cornerRadius: 10.0, gloss: false)