mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-16 03:09:56 +00:00
Merge commit '7015c76e74c0fdac2597079b860f66fd1c6b14ab'
This commit is contained in:
commit
399b679532
@ -2590,6 +2590,7 @@ Unused sets are archived when you add more.";
|
||||
"Conversation.HoldForVideo" = "Hold to record video. Tap to switch to audio.";
|
||||
|
||||
"UserInfo.TelegramCall" = "Telegram Call";
|
||||
"UserInfo.TelegramVideoCall" = "Telegram Video Call";
|
||||
"UserInfo.PhoneCall" = "Phone Call";
|
||||
|
||||
"SharedMedia.CategoryMedia" = "Media";
|
||||
|
||||
@ -649,7 +649,6 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode {
|
||||
iconNode.isLayerBacked = true
|
||||
iconNode.displaysAsynchronously = false
|
||||
iconNode.displayWithoutProcessing = true
|
||||
strongSelf.containerNode.addSubnode(iconNode)
|
||||
strongSelf.credibilityIconNode = iconNode
|
||||
}
|
||||
iconNode.image = currentCredibilityIconImage
|
||||
@ -780,7 +779,7 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode {
|
||||
transition.updateFrameAdditive(node: self.dateNode, frame: CGRect(origin: CGPoint(x: editingOffset + revealOffset + self.bounds.size.width - dateRightInset - self.dateNode.bounds.size.width, y: self.dateNode.frame.minY), size: self.dateNode.bounds.size))
|
||||
|
||||
transition.updateFrameAdditive(node: self.typeIconNode, frame: CGRect(origin: CGPoint(x: revealOffset + leftInset - 81.0, y: self.typeIconNode.frame.minY), size: self.typeIconNode.bounds.size))
|
||||
|
||||
|
||||
transition.updateFrameAdditive(node: self.infoButtonNode, frame: CGRect(origin: CGPoint(x: revealOffset + self.bounds.size.width - infoIconRightInset - self.infoButtonNode.bounds.width, y: self.infoButtonNode.frame.minY), size: self.infoButtonNode.bounds.size))
|
||||
}
|
||||
}
|
||||
|
||||
@ -499,7 +499,7 @@ private final class CallListTabBarContextExtractedContentSource: ContextExtracte
|
||||
let keepInPlace: Bool = true
|
||||
let ignoreContentTouches: Bool = true
|
||||
let blurBackground: Bool = true
|
||||
let centerActionsHorizontally: Bool = true
|
||||
let actionsHorizontalAlignment: ContextActionsHorizontalAlignment = .center
|
||||
|
||||
private let controller: ViewController
|
||||
private let sourceNode: ContextExtractedContentContainingNode
|
||||
|
||||
@ -3813,7 +3813,7 @@ private final class ChatListTabBarContextExtractedContentSource: ContextExtracte
|
||||
let keepInPlace: Bool = true
|
||||
let ignoreContentTouches: Bool = true
|
||||
let blurBackground: Bool = true
|
||||
let centerActionsHorizontally: Bool = true
|
||||
let actionsHorizontalAlignment: ContextActionsHorizontalAlignment = .center
|
||||
|
||||
private let controller: ChatListController
|
||||
private let sourceNode: ContextExtractedContentContainingNode
|
||||
|
||||
@ -686,7 +686,7 @@ private final class ContactsTabBarContextExtractedContentSource: ContextExtracte
|
||||
let keepInPlace: Bool = true
|
||||
let ignoreContentTouches: Bool = true
|
||||
let blurBackground: Bool = true
|
||||
let centerActionsHorizontally: Bool = true
|
||||
let actionsHorizontalAlignment: ContextActionsHorizontalAlignment = .center
|
||||
|
||||
private let controller: ViewController
|
||||
private let sourceNode: ContextExtractedContentContainingNode
|
||||
|
||||
@ -965,6 +965,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
}
|
||||
}
|
||||
|
||||
private var delayLayoutUpdate = false
|
||||
func animateOut(result initialResult: ContextMenuActionResult, completion: @escaping () -> Void) {
|
||||
self.isUserInteractionEnabled = false
|
||||
|
||||
@ -973,11 +974,15 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
if let _ = self.presentationNode {
|
||||
self.currentPresentationStateTransition = .animateOut(result: initialResult, completion: completion)
|
||||
if let validLayout = self.validLayout {
|
||||
self.updateLayout(
|
||||
layout: validLayout,
|
||||
transition: .animated(duration: 0.35, curve: .easeInOut),
|
||||
previousActionsContainerNode: nil
|
||||
)
|
||||
self.delayLayoutUpdate = true
|
||||
Queue.mainQueue().after(0.05) {
|
||||
self.delayLayoutUpdate = false
|
||||
self.updateLayout(
|
||||
layout: validLayout,
|
||||
transition: .animated(duration: 0.35, curve: .easeInOut),
|
||||
previousActionsContainerNode: nil
|
||||
)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -1520,7 +1525,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
}
|
||||
|
||||
func updateLayout(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition, previousActionsContainerNode: ContextActionsContainerNode?, previousActionsContainerFrame: CGRect? = nil, previousActionsTransition: ContextController.PreviousActionsTransition = .scale) {
|
||||
if self.isAnimatingOut {
|
||||
if self.isAnimatingOut || self.delayLayoutUpdate {
|
||||
return
|
||||
}
|
||||
|
||||
@ -2220,14 +2225,22 @@ public final class ContextControllerPutBackViewInfo {
|
||||
}
|
||||
}
|
||||
|
||||
public enum ContextActionsHorizontalAlignment {
|
||||
case `default`
|
||||
case left
|
||||
case center
|
||||
case right
|
||||
}
|
||||
|
||||
public protocol ContextExtractedContentSource: AnyObject {
|
||||
var centerVertically: Bool { get }
|
||||
var keepInPlace: Bool { get }
|
||||
var ignoreContentTouches: Bool { get }
|
||||
var blurBackground: Bool { get }
|
||||
var centerActionsHorizontally: Bool { get }
|
||||
var shouldBeDismissed: Signal<Bool, NoError> { get }
|
||||
|
||||
var actionsHorizontalAlignment: ContextActionsHorizontalAlignment { get }
|
||||
|
||||
func takeView() -> ContextControllerTakeViewInfo?
|
||||
func putBack() -> ContextControllerPutBackViewInfo?
|
||||
}
|
||||
@ -2237,8 +2250,8 @@ public extension ContextExtractedContentSource {
|
||||
return false
|
||||
}
|
||||
|
||||
var centerActionsHorizontally: Bool {
|
||||
return false
|
||||
var actionsHorizontalAlignment: ContextActionsHorizontalAlignment {
|
||||
return .default
|
||||
}
|
||||
|
||||
var shouldBeDismissed: Signal<Bool, NoError> {
|
||||
|
||||
@ -647,14 +647,14 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
}
|
||||
|
||||
let keepInPlace: Bool
|
||||
let centerActionsHorizontally: Bool
|
||||
let actionsHorizontalAlignment: ContextActionsHorizontalAlignment
|
||||
switch self.source {
|
||||
case .location, .reference:
|
||||
keepInPlace = true
|
||||
centerActionsHorizontally = false
|
||||
actionsHorizontalAlignment = .default
|
||||
case let .extracted(source):
|
||||
keepInPlace = source.keepInPlace
|
||||
centerActionsHorizontally = source.centerActionsHorizontally
|
||||
actionsHorizontalAlignment = source.actionsHorizontalAlignment
|
||||
}
|
||||
|
||||
var defaultScrollY: CGFloat = 0.0
|
||||
@ -769,7 +769,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
if let reactionContextNode = self.reactionContextNode {
|
||||
additionalVisibleOffsetY += reactionContextNode.visibleExtensionDistance
|
||||
}
|
||||
if centerActionsHorizontally {
|
||||
if case .center = actionsHorizontalAlignment {
|
||||
actionsFrame.origin.x = floor(contentParentGlobalFrame.minX + contentRect.midX - actionsFrame.width / 2.0)
|
||||
if actionsFrame.maxX > layout.size.width - actionsEdgeInset {
|
||||
actionsFrame.origin.x = layout.size.width - actionsEdgeInset - actionsFrame.width
|
||||
@ -780,20 +780,24 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
} else {
|
||||
if case .location = self.source {
|
||||
actionsFrame.origin.x = contentParentGlobalFrame.minX + contentRect.minX + actionsSideInset - 4.0
|
||||
} else if contentRect.midX < layout.size.width / 2.0 {
|
||||
actionsFrame.origin.x = contentParentGlobalFrame.minX + contentRect.minX + actionsSideInset - 4.0
|
||||
} else if case .right = actionsHorizontalAlignment {
|
||||
actionsFrame.origin.x = contentParentGlobalFrame.minX + contentRect.maxX - actionsSideInset - actionsSize.width - 1.0
|
||||
} else {
|
||||
switch self.source {
|
||||
case .location, .reference:
|
||||
actionsFrame.origin.x = floor(contentParentGlobalFrame.minX + contentRect.midX - actionsFrame.width / 2.0)
|
||||
if actionsFrame.maxX > layout.size.width - actionsEdgeInset {
|
||||
actionsFrame.origin.x = layout.size.width - actionsEdgeInset - actionsFrame.width
|
||||
if contentRect.midX < layout.size.width / 2.0 {
|
||||
actionsFrame.origin.x = contentParentGlobalFrame.minX + contentRect.minX + actionsSideInset - 4.0
|
||||
} else {
|
||||
switch self.source {
|
||||
case .location, .reference:
|
||||
actionsFrame.origin.x = floor(contentParentGlobalFrame.minX + contentRect.midX - actionsFrame.width / 2.0)
|
||||
if actionsFrame.maxX > layout.size.width - actionsEdgeInset {
|
||||
actionsFrame.origin.x = layout.size.width - actionsEdgeInset - actionsFrame.width
|
||||
}
|
||||
if actionsFrame.minX < actionsEdgeInset {
|
||||
actionsFrame.origin.x = actionsEdgeInset
|
||||
}
|
||||
case .extracted:
|
||||
actionsFrame.origin.x = contentParentGlobalFrame.minX + contentRect.maxX - actionsSideInset - actionsSize.width - 1.0
|
||||
}
|
||||
if actionsFrame.minX < actionsEdgeInset {
|
||||
actionsFrame.origin.x = actionsEdgeInset
|
||||
}
|
||||
case .extracted:
|
||||
actionsFrame.origin.x = contentParentGlobalFrame.minX + contentRect.maxX - actionsSideInset - actionsSize.width - 1.0
|
||||
}
|
||||
}
|
||||
if actionsFrame.maxX > layout.size.width - actionsEdgeInset {
|
||||
@ -900,7 +904,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
let actionsSize = self.actionsStackNode.bounds.size
|
||||
|
||||
var actionsPositionDeltaXDistance: CGFloat = 0.0
|
||||
if centerActionsHorizontally {
|
||||
if case .center = actionsHorizontalAlignment {
|
||||
actionsPositionDeltaXDistance = currentContentScreenFrame.midX - self.actionsStackNode.frame.midX
|
||||
}
|
||||
|
||||
@ -1116,7 +1120,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
let actionsSize = self.actionsStackNode.bounds.size
|
||||
|
||||
var actionsPositionDeltaXDistance: CGFloat = 0.0
|
||||
if centerActionsHorizontally {
|
||||
if case .center = actionsHorizontalAlignment {
|
||||
actionsPositionDeltaXDistance = currentContentScreenFrame.midX - self.actionsStackNode.frame.midX
|
||||
}
|
||||
let actionsPositionDeltaYDistance = -animationInContentDistance + actionsVerticalTransitionDirection * actionsSize.height / 2.0 - contentActionsSpacing
|
||||
|
||||
@ -1206,7 +1206,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
|
||||
|
||||
for message in messages {
|
||||
let currentKind = messageContentKind(contentSettings: strongSelf.context.currentContentSettings.with { $0 }, message: message, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder, dateTimeFormat: presentationData.dateTimeFormat, accountPeerId: strongSelf.context.account.peerId)
|
||||
if beganContentKindScanning && currentKind != generalMessageContentKind {
|
||||
if beganContentKindScanning, let messageContentKind = generalMessageContentKind, !messageContentKind.isSemanticallyEqual(to: currentKind) {
|
||||
generalMessageContentKind = nil
|
||||
} else if !beganContentKindScanning || currentKind == generalMessageContentKind {
|
||||
beganContentKindScanning = true
|
||||
@ -1225,6 +1225,8 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
|
||||
case .video:
|
||||
preferredAction = .saveToCameraRoll
|
||||
actionCompletionText = strongSelf.presentationData.strings.Gallery_VideoSaved
|
||||
case .file:
|
||||
preferredAction = .saveToCameraRoll
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
@ -2255,10 +2255,12 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
||||
return nil
|
||||
})
|
||||
|
||||
var loadedEmojiPack: LoadedStickerPack?
|
||||
var highlightableLinks = false
|
||||
let secondaryTitleText: String
|
||||
if let otherPeerName = state.otherPeerName {
|
||||
if case let .emojiStatus(_, _, file, maybeEmojiPack) = context.component.source, let emojiPack = maybeEmojiPack, case let .result(info, _, _) = emojiPack {
|
||||
loadedEmojiPack = maybeEmojiPack
|
||||
highlightableLinks = true
|
||||
|
||||
var packReference: StickerPackReference?
|
||||
@ -2337,7 +2339,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
||||
if let emojiFile = state?.emojiFile, let controller = environment?.controller() as? PremiumIntroScreen, let navigationController = controller.navigationController as? NavigationController {
|
||||
for attribute in emojiFile.attributes {
|
||||
if case let .CustomEmoji(_, _, packReference) = attribute, let packReference = packReference {
|
||||
let controller = accountContext.sharedContext.makeStickerPackScreen(context: accountContext, updatedPresentationData: nil, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: [], parentNavigationController: navigationController, sendSticker: { _, _, _ in
|
||||
let controller = accountContext.sharedContext.makeStickerPackScreen(context: accountContext, updatedPresentationData: nil, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: loadedEmojiPack.flatMap { [$0] } ?? [], parentNavigationController: navigationController, sendSticker: { _, _, _ in
|
||||
return false
|
||||
})
|
||||
presentController(controller)
|
||||
|
||||
@ -47,6 +47,119 @@ public enum MessageContentKind: Equatable {
|
||||
case dice(String)
|
||||
case invoice(String)
|
||||
|
||||
public func isSemanticallyEqual(to other: MessageContentKind) -> Bool {
|
||||
switch self {
|
||||
case .text:
|
||||
if case .text = other {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case .image:
|
||||
if case .image = other {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case .video:
|
||||
if case .video = other {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case .videoMessage:
|
||||
if case .videoMessage = other {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case .audioMessage:
|
||||
if case .audioMessage = other {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case .sticker:
|
||||
if case .sticker = other {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case .animation:
|
||||
if case .animation = other {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case .file:
|
||||
if case .file = other {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case .contact:
|
||||
if case .contact = other {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case .game:
|
||||
if case .game = other {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case .location:
|
||||
if case .location = other {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case .liveLocation:
|
||||
if case .liveLocation = other {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case .expiredImage:
|
||||
if case .expiredImage = other {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case .expiredVideo:
|
||||
if case .expiredVideo = other {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case .poll:
|
||||
if case .poll = other {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case .restricted:
|
||||
if case .restricted = other {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case .dice:
|
||||
if case .dice = other {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case .invoice:
|
||||
if case .invoice = other {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var key: MessageContentKindKey {
|
||||
switch self {
|
||||
case .text:
|
||||
|
||||
@ -99,7 +99,7 @@ final class ChatMessageReactionContextExtractedContentSource: ContextExtractedCo
|
||||
let keepInPlace: Bool = false
|
||||
let ignoreContentTouches: Bool = true
|
||||
let blurBackground: Bool = true
|
||||
let centerActionsHorizontally: Bool = true
|
||||
let actionsHorizontalAlignment: ContextActionsHorizontalAlignment = .center
|
||||
|
||||
private weak var chatNode: ChatControllerNode?
|
||||
private let engine: TelegramEngine
|
||||
@ -174,7 +174,7 @@ final class ChatMessageNavigationButtonContextExtractedContentSource: ContextExt
|
||||
let keepInPlace: Bool = false
|
||||
let ignoreContentTouches: Bool = true
|
||||
let blurBackground: Bool = true
|
||||
let centerActionsHorizontally: Bool = true
|
||||
let actionsHorizontalAlignment: ContextActionsHorizontalAlignment = .center
|
||||
|
||||
private weak var chatNode: ChatControllerNode?
|
||||
private let contentNode: ContextExtractedContentContainingNode
|
||||
|
||||
@ -80,6 +80,7 @@ final class ChatTextInputActionButtonsNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
self.micButton.layer.allowsGroupOpacity = true
|
||||
self.view.addSubview(self.micButton)
|
||||
|
||||
self.addSubnode(self.sendContainerNode)
|
||||
|
||||
@ -6,6 +6,7 @@ import TextFormat
|
||||
import UIKit
|
||||
import AppBundle
|
||||
import TelegramStringFormatting
|
||||
import ContextUI
|
||||
|
||||
enum PeerInfoScreenLabeledValueTextColor {
|
||||
case primary
|
||||
@ -29,10 +30,11 @@ final class PeerInfoScreenLabeledValueItem: PeerInfoScreenItem {
|
||||
let textColor: PeerInfoScreenLabeledValueTextColor
|
||||
let textBehavior: PeerInfoScreenLabeledValueTextBehavior
|
||||
let icon: PeerInfoScreenLabeledValueIcon?
|
||||
let action: (() -> Void)?
|
||||
let action: ((ASDisplayNode) -> Void)?
|
||||
let longTapAction: ((ASDisplayNode) -> Void)?
|
||||
let linkItemAction: ((TextLinkItemActionType, TextLinkItem) -> Void)?
|
||||
let iconAction: (() -> Void)?
|
||||
let contextAction: ((ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?
|
||||
let requestLayout: () -> Void
|
||||
|
||||
init(
|
||||
@ -43,10 +45,11 @@ final class PeerInfoScreenLabeledValueItem: PeerInfoScreenItem {
|
||||
textColor: PeerInfoScreenLabeledValueTextColor = .primary,
|
||||
textBehavior: PeerInfoScreenLabeledValueTextBehavior = .singleLine,
|
||||
icon: PeerInfoScreenLabeledValueIcon? = nil,
|
||||
action: (() -> Void)?,
|
||||
action: ((ASDisplayNode) -> Void)?,
|
||||
longTapAction: ((ASDisplayNode) -> Void)? = nil,
|
||||
linkItemAction: ((TextLinkItemActionType, TextLinkItem) -> Void)? = nil,
|
||||
iconAction: (() -> Void)? = nil,
|
||||
contextAction: ((ASDisplayNode, ContextGesture?, CGPoint?) -> Void)? = nil,
|
||||
requestLayout: @escaping () -> Void
|
||||
) {
|
||||
self.id = id
|
||||
@ -60,6 +63,7 @@ final class PeerInfoScreenLabeledValueItem: PeerInfoScreenItem {
|
||||
self.longTapAction = longTapAction
|
||||
self.linkItemAction = linkItemAction
|
||||
self.iconAction = iconAction
|
||||
self.contextAction = contextAction
|
||||
self.requestLayout = requestLayout
|
||||
}
|
||||
|
||||
@ -85,6 +89,14 @@ private func generateExpandBackground(size: CGSize, color: UIColor) -> UIImage?
|
||||
}
|
||||
|
||||
private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode {
|
||||
private let containerNode: ContextControllerSourceNode
|
||||
private let contextSourceNode: ContextExtractedContentContainingNode
|
||||
|
||||
private let extractedBackgroundImageNode: ASImageNode
|
||||
|
||||
private var extractedRect: CGRect?
|
||||
private var nonExtractedRect: CGRect?
|
||||
|
||||
private let selectionNode: PeerInfoScreenSelectableBackgroundNode
|
||||
private let maskNode: ASImageNode
|
||||
private let labelNode: ImmediateTextNode
|
||||
@ -111,6 +123,14 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode {
|
||||
|
||||
override init() {
|
||||
var bringToFrontForHighlightImpl: (() -> Void)?
|
||||
|
||||
self.contextSourceNode = ContextExtractedContentContainingNode()
|
||||
self.containerNode = ContextControllerSourceNode()
|
||||
|
||||
self.extractedBackgroundImageNode = ASImageNode()
|
||||
self.extractedBackgroundImageNode.displaysAsynchronously = false
|
||||
self.extractedBackgroundImageNode.alpha = 0.0
|
||||
|
||||
self.selectionNode = PeerInfoScreenSelectableBackgroundNode(bringToFrontForHighlight: { bringToFrontForHighlightImpl?() })
|
||||
self.selectionNode.isUserInteractionEnabled = false
|
||||
|
||||
@ -161,17 +181,25 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode {
|
||||
|
||||
self.addSubnode(self.bottomSeparatorNode)
|
||||
self.addSubnode(self.selectionNode)
|
||||
|
||||
self.containerNode.addSubnode(self.contextSourceNode)
|
||||
self.containerNode.targetNodeForActivationProgress = self.contextSourceNode.contentNode
|
||||
self.addSubnode(self.containerNode)
|
||||
|
||||
self.addSubnode(self.maskNode)
|
||||
self.addSubnode(self.labelNode)
|
||||
self.addSubnode(self.textNode)
|
||||
self.addSubnode(self.additionalTextNode)
|
||||
|
||||
self.addSubnode(self.expandBackgroundNode)
|
||||
self.addSubnode(self.expandNode)
|
||||
self.addSubnode(self.expandButonNode)
|
||||
self.contextSourceNode.contentNode.addSubnode(self.extractedBackgroundImageNode)
|
||||
|
||||
self.addSubnode(self.iconNode)
|
||||
self.addSubnode(self.iconButtonNode)
|
||||
self.contextSourceNode.contentNode.addSubnode(self.labelNode)
|
||||
self.contextSourceNode.contentNode.addSubnode(self.textNode)
|
||||
self.contextSourceNode.contentNode.addSubnode(self.additionalTextNode)
|
||||
|
||||
self.contextSourceNode.contentNode.addSubnode(self.expandBackgroundNode)
|
||||
self.contextSourceNode.contentNode.addSubnode(self.expandNode)
|
||||
self.contextSourceNode.contentNode.addSubnode(self.expandButonNode)
|
||||
|
||||
self.contextSourceNode.contentNode.addSubnode(self.iconNode)
|
||||
self.contextSourceNode.contentNode.addSubnode(self.iconButtonNode)
|
||||
|
||||
self.addSubnode(self.activateArea)
|
||||
|
||||
@ -200,6 +228,35 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.containerNode.activated = { [weak self] gesture, _ in
|
||||
guard let strongSelf = self, let item = strongSelf.item, let contextAction = item.contextAction else {
|
||||
gesture.cancel()
|
||||
return
|
||||
}
|
||||
contextAction(strongSelf.contextSourceNode, gesture, nil)
|
||||
}
|
||||
|
||||
self.contextSourceNode.willUpdateIsExtractedToContextPreview = { [weak self] isExtracted, transition in
|
||||
guard let strongSelf = self, let theme = strongSelf.theme else {
|
||||
return
|
||||
}
|
||||
|
||||
if isExtracted {
|
||||
strongSelf.extractedBackgroundImageNode.image = generateStretchableFilledCircleImage(diameter: 28.0, color: theme.list.plainBackgroundColor)
|
||||
}
|
||||
|
||||
if let extractedRect = strongSelf.extractedRect, let nonExtractedRect = strongSelf.nonExtractedRect {
|
||||
let rect = isExtracted ? extractedRect : nonExtractedRect
|
||||
transition.updateFrame(node: strongSelf.extractedBackgroundImageNode, frame: rect)
|
||||
}
|
||||
|
||||
transition.updateAlpha(node: strongSelf.extractedBackgroundImageNode, alpha: isExtracted ? 1.0 : 0.0, completion: { _ in
|
||||
if !isExtracted {
|
||||
self?.extractedBackgroundImageNode.image = nil
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func expandPressed() {
|
||||
@ -260,7 +317,7 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode {
|
||||
} else if case .longTap = gesture {
|
||||
item.longTapAction?(self)
|
||||
} else if case .tap = gesture {
|
||||
item.action?()
|
||||
item.action?(self.contextSourceNode)
|
||||
}
|
||||
}
|
||||
default:
|
||||
@ -280,8 +337,16 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode {
|
||||
self.item = item
|
||||
self.theme = presentationData.theme
|
||||
|
||||
self.selectionNode.pressed = item.action
|
||||
|
||||
if let action = item.action {
|
||||
self.selectionNode.pressed = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
action(strongSelf.contextSourceNode)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.selectionNode.pressed = nil
|
||||
}
|
||||
|
||||
let sideInset: CGFloat = 16.0 + safeInsets.left
|
||||
|
||||
self.bottomSeparatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor
|
||||
@ -460,6 +525,25 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode {
|
||||
self.activateArea.accessibilityLabel = item.label
|
||||
self.activateArea.accessibilityValue = item.text
|
||||
|
||||
|
||||
let contentSize = CGSize(width: width, height: height)
|
||||
self.containerNode.frame = CGRect(origin: CGPoint(), size: contentSize)
|
||||
self.contextSourceNode.frame = CGRect(origin: CGPoint(), size: contentSize)
|
||||
self.contextSourceNode.contentNode.frame = CGRect(origin: CGPoint(), size: contentSize)
|
||||
self.containerNode.isGestureEnabled = false
|
||||
|
||||
let nonExtractedRect = CGRect(origin: CGPoint(), size: CGSize(width: contentSize.width, height: contentSize.height))
|
||||
let extractedRect = nonExtractedRect
|
||||
self.extractedRect = extractedRect
|
||||
self.nonExtractedRect = nonExtractedRect
|
||||
|
||||
if self.contextSourceNode.isExtractedToContextPreview {
|
||||
self.extractedBackgroundImageNode.frame = extractedRect
|
||||
} else {
|
||||
self.extractedBackgroundImageNode.frame = nonExtractedRect
|
||||
}
|
||||
self.contextSourceNode.contentRect = extractedRect
|
||||
|
||||
return height
|
||||
}
|
||||
|
||||
|
||||
@ -462,7 +462,7 @@ private enum PeerInfoReportType {
|
||||
private final class PeerInfoInteraction {
|
||||
let openChat: () -> Void
|
||||
let openUsername: (String) -> Void
|
||||
let openPhone: (String) -> Void
|
||||
let openPhone: (String, ASDisplayNode, ContextGesture?) -> Void
|
||||
let editingOpenNotificationSettings: () -> Void
|
||||
let editingOpenSoundSettings: () -> Void
|
||||
let editingToggleShowMessageText: (Bool) -> Void
|
||||
@ -505,7 +505,7 @@ private final class PeerInfoInteraction {
|
||||
|
||||
init(
|
||||
openUsername: @escaping (String) -> Void,
|
||||
openPhone: @escaping (String) -> Void,
|
||||
openPhone: @escaping (String, ASDisplayNode, ContextGesture?) -> Void,
|
||||
editingOpenNotificationSettings: @escaping () -> Void,
|
||||
editingOpenSoundSettings: @escaping () -> Void,
|
||||
editingToggleShowMessageText: @escaping (Bool) -> Void,
|
||||
@ -942,10 +942,10 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
|
||||
if let phone = user.phone {
|
||||
let formattedPhone = formatPhoneNumber(phone)
|
||||
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 2, label: presentationData.strings.ContactInfo_PhoneLabelMobile, text: formattedPhone, textColor: .accent, action: {
|
||||
interaction.openPhone(phone)
|
||||
}, longTapAction: { sourceNode in
|
||||
interaction.openPeerInfoContextMenu(.phone(formattedPhone), sourceNode)
|
||||
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 2, label: presentationData.strings.ContactInfo_PhoneLabelMobile, text: formattedPhone, textColor: .accent, action: { node in
|
||||
interaction.openPhone(phone, node, nil)
|
||||
}, longTapAction: nil, contextAction: { node, gesture, _ in
|
||||
interaction.openPhone(phone, node, gesture)
|
||||
}, requestLayout: {
|
||||
interaction.requestLayout(false)
|
||||
}))
|
||||
@ -959,7 +959,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
additionalText: nil, //presentationData.strings.Profile_AdditionalUsernames("@username1, @username2, @username3, @username4"),
|
||||
textColor: .accent,
|
||||
icon: .qrCode,
|
||||
action: {
|
||||
action: { _ in
|
||||
interaction.openUsername(username)
|
||||
}, longTapAction: { sourceNode in
|
||||
interaction.openPeerInfoContextMenu(.link, sourceNode)
|
||||
@ -1076,7 +1076,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
}
|
||||
|
||||
if let username = channel.username {
|
||||
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: ItemUsername, label: presentationData.strings.Channel_LinkItem, text: "https://t.me/\(username)", textColor: .accent, icon: .qrCode, action: {
|
||||
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: ItemUsername, label: presentationData.strings.Channel_LinkItem, text: "https://t.me/\(username)", textColor: .accent, icon: .qrCode, action: { _ in
|
||||
interaction.openUsername(username)
|
||||
}, longTapAction: { sourceNode in
|
||||
interaction.openPeerInfoContextMenu(.link, sourceNode)
|
||||
@ -1812,8 +1812,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
openUsername: { [weak self] value in
|
||||
self?.openUsername(value: value)
|
||||
},
|
||||
openPhone: { [weak self] value in
|
||||
self?.openPhone(value: value)
|
||||
openPhone: { [weak self] value, node, gesture in
|
||||
self?.openPhone(value: value, node: node, gesture: gesture)
|
||||
},
|
||||
editingOpenNotificationSettings: { [weak self] in
|
||||
self?.editingOpenNotificationSettings()
|
||||
@ -4892,39 +4892,110 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
}
|
||||
}
|
||||
|
||||
private func openPhone(value: String) {
|
||||
private func openPhone(value: String, node: ASDisplayNode, gesture: ContextGesture?) {
|
||||
guard let sourceNode = node as? ContextExtractedContentContainingNode else {
|
||||
return
|
||||
}
|
||||
|
||||
let _ = (getUserPeer(engine: self.context.engine, peerId: self.peerId)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if case let .user(peer) = peer, let peerPhoneNumber = peer.phone, formatPhoneNumber(value) == formatPhoneNumber(peerPhoneNumber) {
|
||||
let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData)
|
||||
let dismissAction: () -> Void = { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
// if case let .user(peer) = peer, let peerPhoneNumber = peer.phone, formatPhoneNumber(value) == formatPhoneNumber(peerPhoneNumber) {
|
||||
// let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData)
|
||||
// let dismissAction: () -> Void = { [weak actionSheet] in
|
||||
// actionSheet?.dismissAnimated()
|
||||
// }
|
||||
// actionSheet.setItemGroups([
|
||||
// ActionSheetItemGroup(items: [
|
||||
// ActionSheetButtonItem(title: strongSelf.presentationData.strings.UserInfo_TelegramCall, action: {
|
||||
// dismissAction()
|
||||
// self?.requestCall(isVideo: false)
|
||||
// }),
|
||||
// ActionSheetButtonItem(title: strongSelf.presentationData.strings.UserInfo_PhoneCall, action: {
|
||||
// dismissAction()
|
||||
//
|
||||
// guard let strongSelf = self else {
|
||||
// return
|
||||
// }
|
||||
// strongSelf.context.sharedContext.applicationBindings.openUrl("tel:\(formatPhoneNumber(value).replacingOccurrences(of: " ", with: ""))")
|
||||
// }),
|
||||
// ]),
|
||||
// ActionSheetItemGroup(items: [ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, action: { dismissAction() })])
|
||||
// ])
|
||||
// strongSelf.view.endEditing(true)
|
||||
// strongSelf.controller?.present(actionSheet, in: .window(.root))
|
||||
// } else {
|
||||
// strongSelf.context.sharedContext.applicationBindings.openUrl("tel:\(formatPhoneNumber(value).replacingOccurrences(of: " ", with: ""))")
|
||||
// }
|
||||
//
|
||||
let presentationData = strongSelf.presentationData
|
||||
|
||||
let telegramCallAction: (Bool) -> Void = { [weak self] isVideo in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.requestCall(isVideo: isVideo)
|
||||
}
|
||||
|
||||
let phoneCallAction = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
actionSheet.setItemGroups([
|
||||
ActionSheetItemGroup(items: [
|
||||
ActionSheetButtonItem(title: strongSelf.presentationData.strings.UserInfo_TelegramCall, action: {
|
||||
dismissAction()
|
||||
self?.requestCall(isVideo: false)
|
||||
}),
|
||||
ActionSheetButtonItem(title: strongSelf.presentationData.strings.UserInfo_PhoneCall, action: {
|
||||
dismissAction()
|
||||
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.context.sharedContext.applicationBindings.openUrl("tel:\(formatPhoneNumber(value).replacingOccurrences(of: " ", with: ""))")
|
||||
}),
|
||||
]),
|
||||
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, action: { dismissAction() })])
|
||||
])
|
||||
strongSelf.view.endEditing(true)
|
||||
strongSelf.controller?.present(actionSheet, in: .window(.root))
|
||||
} else {
|
||||
strongSelf.context.sharedContext.applicationBindings.openUrl("tel:\(formatPhoneNumber(value).replacingOccurrences(of: " ", with: ""))")
|
||||
}
|
||||
|
||||
let copyAction = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
UIPasteboard.general.string = formatPhoneNumber(value)
|
||||
|
||||
strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .copy(text: presentationData.strings.Conversation_PhoneCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current)
|
||||
}
|
||||
|
||||
var items: [ContextMenuItem] = []
|
||||
if case let .user(peer) = peer, let peerPhoneNumber = peer.phone, formatPhoneNumber(value) == formatPhoneNumber(peerPhoneNumber) {
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_TelegramCall, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Call"), color: theme.contextMenu.primaryColor) }, action: { c, _ in
|
||||
c.dismiss {
|
||||
telegramCallAction(false)
|
||||
}
|
||||
})))
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_TelegramVideoCall, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/VideoCall"), color: theme.contextMenu.primaryColor) }, action: { c, _ in
|
||||
c.dismiss {
|
||||
telegramCallAction(true)
|
||||
}
|
||||
})))
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_PhoneCall, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: ""), color: theme.contextMenu.primaryColor) }, action: { c, _ in
|
||||
c.dismiss {
|
||||
phoneCallAction()
|
||||
}
|
||||
})))
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_ContextMenuCopy, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { c, _ in
|
||||
c.dismiss {
|
||||
copyAction()
|
||||
}
|
||||
})))
|
||||
} else {
|
||||
items = [
|
||||
.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_PhoneCall, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: ""), color: theme.contextMenu.primaryColor) }, action: { c, _ in
|
||||
c.dismiss {
|
||||
phoneCallAction()
|
||||
}
|
||||
})),
|
||||
.action(ContextMenuActionItem(text: presentationData.strings.Conversation_ContextMenuCopy, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { c, _ in
|
||||
c.dismiss {
|
||||
copyAction()
|
||||
}
|
||||
})),
|
||||
]
|
||||
}
|
||||
|
||||
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(PeerInfoContextExtractedContentSource(sourceNode: sourceNode)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
// contextController.useComplexItemsTransitionAnimation = true
|
||||
strongSelf.controller?.present(contextController, in: .window(.root))
|
||||
})
|
||||
}
|
||||
|
||||
@ -8516,7 +8587,7 @@ private final class SettingsTabBarContextExtractedContentSource: ContextExtracte
|
||||
let keepInPlace: Bool = true
|
||||
let ignoreContentTouches: Bool = true
|
||||
let blurBackground: Bool = true
|
||||
let centerActionsHorizontally: Bool = true
|
||||
let actionsHorizontalAlignment: ContextActionsHorizontalAlignment = .center
|
||||
|
||||
private let controller: ViewController
|
||||
private let sourceNode: ContextExtractedContentContainingNode
|
||||
@ -8814,6 +8885,28 @@ private final class MessageContextExtractedContentSource: ContextExtractedConten
|
||||
}
|
||||
}
|
||||
|
||||
private final class PeerInfoContextExtractedContentSource: ContextExtractedContentSource {
|
||||
var keepInPlace: Bool = false
|
||||
let ignoreContentTouches: Bool = true
|
||||
let blurBackground: Bool = true
|
||||
|
||||
let actionsHorizontalAlignment: ContextActionsHorizontalAlignment = .right
|
||||
|
||||
private let sourceNode: ContextExtractedContentContainingNode
|
||||
|
||||
init(sourceNode: ContextExtractedContentContainingNode) {
|
||||
self.sourceNode = sourceNode
|
||||
}
|
||||
|
||||
func takeView() -> ContextControllerTakeViewInfo? {
|
||||
return ContextControllerTakeViewInfo(containingItem: .node(self.sourceNode), contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
}
|
||||
|
||||
func putBack() -> ContextControllerPutBackViewInfo? {
|
||||
return ContextControllerPutBackViewInfo(contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
}
|
||||
}
|
||||
|
||||
private final class PeerInfoContextReferenceContentSource: ContextReferenceContentSource {
|
||||
private let controller: ViewController
|
||||
private let sourceNode: ContextReferenceContentNode
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user