diff --git a/Telegram/Telegram-iOS/Resources/QrDataRain.tgs b/Telegram/Telegram-iOS/Resources/QrDataRain.tgs new file mode 100644 index 0000000000..8a97b29eae Binary files /dev/null and b/Telegram/Telegram-iOS/Resources/QrDataRain.tgs differ diff --git a/Telegram/Telegram-iOS/Resources/QrLoading.tgs b/Telegram/Telegram-iOS/Resources/QrLoading.tgs deleted file mode 100644 index ab42f2f02e..0000000000 Binary files a/Telegram/Telegram-iOS/Resources/QrLoading.tgs and /dev/null differ diff --git a/submodules/AnimatedCountLabelNode/Sources/AnimatedCountLabelNode.swift b/submodules/AnimatedCountLabelNode/Sources/AnimatedCountLabelNode.swift index 162445af7f..f34b9a1fa3 100644 --- a/submodules/AnimatedCountLabelNode/Sources/AnimatedCountLabelNode.swift +++ b/submodules/AnimatedCountLabelNode/Sources/AnimatedCountLabelNode.swift @@ -79,6 +79,7 @@ public class AnimatedCountLabelNode: ASDisplayNode { fileprivate var resolvedSegments: [ResolvedSegment.Key: (ResolvedSegment, TextNode)] = [:] public var reverseAnimationDirection: Bool = false + public var alwaysOneDirection: Bool = false override public init() { super.init() @@ -91,6 +92,7 @@ public class AnimatedCountLabelNode: ASDisplayNode { segmentLayouts[segmentKey] = TextNode.asyncLayout(segmentAndTextNode.1) } let reverseAnimationDirection = self.reverseAnimationDirection + let alwaysOneDirection = self.alwaysOneDirection return { [weak self] size, initialSegments in var segments: [ResolvedSegment] = [] @@ -173,7 +175,7 @@ public class AnimatedCountLabelNode: ASDisplayNode { fromAlpha = CGFloat(presentation.opacity) } var offsetY: CGFloat - if currentValue > updatedValue { + if currentValue > updatedValue || alwaysOneDirection { offsetY = -floor(currentTextNode.bounds.height * 0.6) } else { offsetY = floor(currentTextNode.bounds.height * 0.6) diff --git a/submodules/QrCode/Sources/QrCode.swift b/submodules/QrCode/Sources/QrCode.swift index ba0074e0e7..9f34ce850a 100644 --- a/submodules/QrCode/Sources/QrCode.swift +++ b/submodules/QrCode/Sources/QrCode.swift @@ -38,7 +38,7 @@ public func qrCodeCutout(size: Int, dimensions: CGSize, scale: CGFloat?) -> (Int return (cutoutSize, cutoutRect, quadSize) } -public func qrCode(string: String, color: UIColor, backgroundColor: UIColor? = nil, icon: QrCodeIcon, ecl: String = "M") -> Signal<(Int, (TransformImageArguments) -> DrawingContext?), NoError> { +public func qrCode(string: String, color: UIColor, backgroundColor: UIColor? = nil, icon: QrCodeIcon, ecl: String = "M", onlyMarkers: Bool = false) -> Signal<(Int, (TransformImageArguments) -> DrawingContext?), NoError> { return Signal<(Data, Int, Int), NoError> { subscriber in if let data = string.data(using: .isoLatin1, allowLossyConversion: false), let filter = CIFilter(name: "CIQRCodeGenerator") { filter.setValue(data, forKey: "inputMessage") @@ -182,46 +182,48 @@ public func qrCode(string: String, color: UIColor, backgroundColor: UIColor? = n } } - for y in 0 ..< size { - for x in 0 ..< size { - if (y < markerSize + 1 && (x < markerSize + 1 || x > size - markerSize - 2)) || (y > size - markerSize - 2 && x < markerSize + 1) { - continue - } - - var corners: UIRectCorner = [] - if valueAt(x: x, y: y) { - corners = .allCorners - if valueAt(x: x, y: y - 1) { - corners.remove(.topLeft) - corners.remove(.topRight) + if !onlyMarkers { + for y in 0 ..< size { + for x in 0 ..< size { + if (y < markerSize + 1 && (x < markerSize + 1 || x > size - markerSize - 2)) || (y > size - markerSize - 2 && x < markerSize + 1) { + continue } - if valueAt(x: x, y: y + 1) { - corners.remove(.bottomLeft) - corners.remove(.bottomRight) + + var corners: UIRectCorner = [] + if valueAt(x: x, y: y) { + corners = .allCorners + if valueAt(x: x, y: y - 1) { + corners.remove(.topLeft) + corners.remove(.topRight) + } + if valueAt(x: x, y: y + 1) { + corners.remove(.bottomLeft) + corners.remove(.bottomRight) + } + if valueAt(x: x - 1, y: y) { + corners.remove(.topLeft) + corners.remove(.bottomLeft) + } + if valueAt(x: x + 1, y: y) { + corners.remove(.topRight) + corners.remove(.bottomRight) + } + drawAt(x: x, y: y, fill: true, corners: corners) + } else { + if valueAt(x: x - 1, y: y - 1) && valueAt(x: x - 1, y: y) && valueAt(x: x, y: y - 1) { + corners.insert(.topLeft) + } + if valueAt(x: x + 1, y: y - 1) && valueAt(x: x + 1, y: y) && valueAt(x: x, y: y - 1) { + corners.insert(.topRight) + } + if valueAt(x: x - 1, y: y + 1) && valueAt(x: x - 1, y: y) && valueAt(x: x, y: y + 1) { + corners.insert(.bottomLeft) + } + if valueAt(x: x + 1, y: y + 1) && valueAt(x: x + 1, y: y) && valueAt(x: x, y: y + 1) { + corners.insert(.bottomRight) + } + drawAt(x: x, y: y, fill: false, corners: corners) } - if valueAt(x: x - 1, y: y) { - corners.remove(.topLeft) - corners.remove(.bottomLeft) - } - if valueAt(x: x + 1, y: y) { - corners.remove(.topRight) - corners.remove(.bottomRight) - } - drawAt(x: x, y: y, fill: true, corners: corners) - } else { - if valueAt(x: x - 1, y: y - 1) && valueAt(x: x - 1, y: y) && valueAt(x: x, y: y - 1) { - corners.insert(.topLeft) - } - if valueAt(x: x + 1, y: y - 1) && valueAt(x: x + 1, y: y) && valueAt(x: x, y: y - 1) { - corners.insert(.topRight) - } - if valueAt(x: x - 1, y: y + 1) && valueAt(x: x - 1, y: y) && valueAt(x: x, y: y + 1) { - corners.insert(.bottomLeft) - } - if valueAt(x: x + 1, y: y + 1) && valueAt(x: x + 1, y: y) && valueAt(x: x, y: y + 1) { - corners.insert(.bottomRight) - } - drawAt(x: x, y: y, fill: false, corners: corners) } } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ContactToken.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ContactToken.swift index f5902e1415..8a8910199d 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ContactToken.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ContactToken.swift @@ -2,3 +2,34 @@ import Foundation import Postbox import TelegramApi import SwiftSignalKit + +func _internal_importContactToken(account: Account, token: String) -> Signal { + return account.network.request(Api.functions.contacts.importContactToken(token: token)) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> map { result -> EnginePeer? in + return result.flatMap { EnginePeer(TelegramUser(user: $0)) } + } +} + +public struct ExportedContactToken { + public let url: String + public let expires: Int32 +} + +func _internal_exportContactToken(account: Account) -> Signal { + return account.network.request(Api.functions.contacts.exportContactToken()) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> map { result -> ExportedContactToken? in + if let result = result, case let .exportedContactToken(url, expires) = result { + return ExportedContactToken(url: url, expires: expires) + } else { + return nil + } + } +} diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift index f82bef8fe0..7952dd019f 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift @@ -973,6 +973,14 @@ public extension TelegramEngine { public func forumChannelTopicNotificationExceptions(id: EnginePeer.Id) -> Signal<[EngineMessageHistoryThread.NotificationException], NoError> { return _internal_forumChannelTopicNotificationExceptions(account: self.account, id: id) } + + public func importContactToken(token: String) -> Signal { + return _internal_importContactToken(account: self.account, token: token) + } + + public func exportContactToken() -> Signal { + return _internal_exportContactToken(account: self.account) + } } } diff --git a/submodules/TelegramUI/Sources/ChatQrCodeScreen.swift b/submodules/TelegramUI/Sources/ChatQrCodeScreen.swift index 0a35d20bea..821cead160 100644 --- a/submodules/TelegramUI/Sources/ChatQrCodeScreen.swift +++ b/submodules/TelegramUI/Sources/ChatQrCodeScreen.swift @@ -33,6 +33,7 @@ import TelegramUniversalVideoContent import GalleryUI import SaveToCameraRoll import SegmentedControlNode +import AnimatedCountLabelNode private func closeButtonImage(theme: PresentationTheme) -> UIImage? { return generateImage(CGSize(width: 30.0, height: 30.0), contextGenerator: { size, context in @@ -787,6 +788,8 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, UIScrollViewDeleg private var containerLayout: (ContainerViewLayout, CGFloat)? private let disposable = MetaDisposable() + private let contactDisposable = MetaDisposable() + private var currentContactToken: ExportedContactToken? var present: ((ViewController) -> Void)? var previewTheme: ((String?, Bool?, PresentationTheme) -> Void)? @@ -1154,6 +1157,43 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, UIScrollViewDeleg } self.ready.set(self.contentNode.isReady) + + if case let .peer(_, _, temporary) = controller.subject, temporary { + self.contactDisposable.set( + (context.engine.peers.exportContactToken() + |> deliverOnMainQueue).start(next: { [weak self] token in + if let strongSelf = self { + strongSelf.currentContactToken = token + if let contentNode = strongSelf.contentNode as? QrContentNode, let token = token { + contentNode.setContactToken(token, animated: true) + } + } + }) + ) + + if let contentNode = self.contentNode as? QrContentNode { + contentNode.requestNextToken = { [weak self] in + if let strongSelf = self { + strongSelf.contactDisposable.set( + (context.engine.peers.exportContactToken() + |> deliverOnMainQueue).start(next: { [weak self] token in + if let strongSelf = self { + strongSelf.currentContactToken = token + if let contentNode = strongSelf.contentNode as? QrContentNode, let token = token { + contentNode.setContactToken(token, animated: true) + } + } + }) + ) + } + } + } + } + } + + deinit { + self.disposable.dispose() + self.contactDisposable.dispose() } private func enqueueTransition(_ transition: ThemeSettingsThemeItemNodeTransition) { @@ -1451,7 +1491,11 @@ private class QrContentNode: ASDisplayNode, ContentNode { private var codeForegroundDimNode: ASDisplayNode private let codeMaskNode: ASDisplayNode private let codeTextNode: ImmediateTextNode + private let codeCountdownNode: ImmediateAnimatedCountLabelNode private let codeImageNode: TransformImageNode + private let codeMarkersNode: TransformImageNode + private var codeSnapshotView: UIView? + private var codePlaceholderNode: AnimatedStickerNode private let codeIconBackgroundNode: ASImageNode private let codeStaticIconNode: ASImageNode? private let codeAnimatedIconNode: AnimatedStickerNode? @@ -1471,7 +1515,10 @@ private class QrContentNode: ASDisplayNode, ContentNode { } private var timer: SwiftSignalKit.Timer? - private var setupTimestamp: Double + private var token: ExportedContactToken? + private var gettingNextToken = false + private var tokenUpdated = false + var requestNextToken: () -> Void = {} init(context: AccountContext, peer: Peer, threadId: Int64?, isStatic: Bool = false, temporary: Bool) { self.context = context @@ -1479,9 +1526,7 @@ private class QrContentNode: ASDisplayNode, ContentNode { self.threadId = threadId self.isStatic = isStatic self.temporary = temporary - - self.setupTimestamp = CACurrentMediaTime() - + self.containerNode = ASDisplayNode() self.wallpaperBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: true, useSharedAnimationPhase: false, useExperimentalImplementation: context.sharedContext.immediateExperimentalUISettings.experimentalBackground) @@ -1503,8 +1548,11 @@ private class QrContentNode: ASDisplayNode, ContentNode { self.codeMaskNode = ASDisplayNode() self.codeImageNode = TransformImageNode() + self.codeMarkersNode = TransformImageNode() self.codeIconBackgroundNode = ASImageNode() + self.codePlaceholderNode = DefaultAnimatedStickerNodeImpl() + if isStatic { let codeStaticIconNode = ASImageNode() codeStaticIconNode.displaysAsynchronously = false @@ -1521,27 +1569,41 @@ private class QrContentNode: ASDisplayNode, ContentNode { } var codeText: String - if temporary { - codeText = "5:00" + if let addressName = peer.addressName, !addressName.isEmpty { + codeText = "@\(peer.addressName ?? "")".uppercased() } else { - if let addressName = peer.addressName, !addressName.isEmpty { - codeText = "@\(peer.addressName ?? "")".uppercased() - } else { - codeText = peer.debugDisplayTitle.uppercased() - } - if let threadId = self.threadId, threadId != 0 { - codeText += "/\(threadId)" - } + codeText = peer.debugDisplayTitle.uppercased() } + if let threadId = self.threadId, threadId != 0 { + codeText += "/\(threadId)" + } + + let codeFont = Font.with(size: 23.0, design: .round, weight: .bold, traits: [.monospacedNumbers]) self.codeTextNode = ImmediateTextNode() self.codeTextNode.displaysAsynchronously = false - self.codeTextNode.attributedText = NSAttributedString(string: codeText, font: Font.with(size: 23.0, design: .round, weight: .bold, traits: [.monospacedNumbers]), textColor: .black) + self.codeTextNode.attributedText = NSAttributedString(string: codeText, font: codeFont, textColor: .black) self.codeTextNode.truncationMode = .byCharWrapping self.codeTextNode.maximumNumberOfLines = 2 self.codeTextNode.textAlignment = .center + + self.codeCountdownNode = ImmediateAnimatedCountLabelNode() + self.codeCountdownNode.alwaysOneDirection = true + if isStatic { + if temporary { + self.codeCountdownNode.isHidden = true + } self.codeTextNode.setNeedsDisplayAtScale(3.0) + } else if temporary { + self.codeTextNode.isHidden = true + + self.codeCountdownNode.segments = [ + .number(Int(5), NSAttributedString(string: "5", font: codeFont, textColor: .black)), + .text(0, NSAttributedString(string: ":", font: codeFont, textColor: .black)), + .number(Int(0), NSAttributedString(string: "0", font: codeFont, textColor: .black)), + .number(Int(0), NSAttributedString(string: "0", font: codeFont, textColor: .black)) + ] } self.avatarNode = ImageNode() @@ -1560,8 +1622,13 @@ private class QrContentNode: ASDisplayNode, ContentNode { self.codeForegroundNode.addSubnode(self.codeForegroundDimNode) self.codeMaskNode.addSubnode(self.codeImageNode) + self.codeMaskNode.addSubnode(self.codeMarkersNode) + if temporary { + self.codeMaskNode.addSubnode(self.codePlaceholderNode) + } self.codeMaskNode.addSubnode(self.codeIconBackgroundNode) self.codeMaskNode.addSubnode(self.codeTextNode) + self.codeMaskNode.addSubnode(self.codeCountdownNode) self.containerNode.addSubnode(self.avatarNode) @@ -1607,6 +1674,13 @@ private class QrContentNode: ASDisplayNode, ContentNode { self?.tick() }, queue: Queue.mainQueue()) self.timer?.start() + + self.codePlaceholderNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "QrDataRain"), width: 512, height: 512, playbackMode: .loop, mode: .direct(cachePathPrefix: nil)) + self.codePlaceholderNode.visibility = true + + self.codeImageNode.alpha = 0.0 + + self.codeMarkersNode.setSignal(qrCode(string: "https://t.me/contact/000000:abcdef", color: .black, backgroundColor: nil, icon: .cutout, ecl: "Q", onlyMarkers: true) |> map { $0.1 }, attemptSynchronously: true) } } @@ -1621,14 +1695,85 @@ private class QrContentNode: ASDisplayNode, ContentNode { } private func tick() { - let timeout: Double = 5.0 * 60.0 - let currentTime = CACurrentMediaTime() - let elapsed = max(0.0, timeout - (currentTime - self.setupTimestamp)) + let currentTime = Int32(Date().timeIntervalSince1970) + let elapsed: Int32 + if let token = self.token { + elapsed = token.expires - currentTime + } else { + elapsed = 300 + } + + let string = stringForDuration(max(0, elapsed)) + + let codeFont = Font.with(size: 23.0, design: .round, weight: .bold, traits: [.monospacedNumbers]) + + var segments: [AnimatedCountLabelNode.Segment] = [] + for char in string { + if let intValue = Int(String(char)) { + segments.append(.number(intValue, NSAttributedString(string: String(char), font: codeFont, textColor: .black))) + } else { + segments.append(.text(0, NSAttributedString(string: String(char), font: codeFont, textColor: .black))) + } + } + self.codeCountdownNode.segments = segments - self.codeTextNode.attributedText = NSAttributedString(string: stringForDuration(Int32(elapsed)), font: Font.with(size: 23.0, design: .round, weight: .bold, traits: [.monospacedNumbers]), textColor: .black) if let (size, topInset, bottomInset) = self.validLayout { self.updateLayout(size: size, topInset: topInset, bottomInset: bottomInset, transition: .immediate) } + + if elapsed <= 1 { + if !self.gettingNextToken { + self.gettingNextToken = true + self.requestNextToken() + + let codeSnapshotView = UIImageView(image: self.codeImageNode.image) + codeSnapshotView.frame = self.codeImageNode.frame + self.codeImageNode.view.superview?.addSubview(codeSnapshotView) + self.codeSnapshotView = codeSnapshotView + + self.codeImageNode.isHidden = true + } + } + if self.tokenUpdated { + self.tokenUpdated = false + + self.codeImageNode.isHidden = false + self.codeImageNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) + if let codeSnapshotView = self.codeSnapshotView { + self.codeSnapshotView = nil + codeSnapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak codeSnapshotView] _ in + codeSnapshotView?.removeFromSuperview() + }) + } + } + } + + func setContactToken(_ token: ExportedContactToken, animated: Bool) { + self.token = token + if self.gettingNextToken { + self.gettingNextToken = false + self.tokenUpdated = true + } + + self.codeImageNode.setSignal(qrCode(string: token.url, color: .black, backgroundColor: nil, icon: .cutout, ecl: "Q") |> beforeNext { [weak self] size, _ in + guard let strongSelf = self else { + return + } + strongSelf.qrCodeSize = size + if let (size, topInset, bottomInset) = strongSelf.validLayout { + strongSelf.updateLayout(size: size, topInset: topInset, bottomInset: bottomInset, transition: .immediate) + } + + if strongSelf.codePlaceholderNode.visibility { + strongSelf.codeImageNode.alpha = 1.0 + strongSelf.codeImageNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + strongSelf.codePlaceholderNode.alpha = 0.0 + strongSelf.codePlaceholderNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak self] _ in + self?.codePlaceholderNode.visibility = false + }) + } + + } |> map { $0.1 }, attemptSynchronously: true) } func generateImage(completion: @escaping (UIImage?) -> Void) { @@ -1640,6 +1785,9 @@ private class QrContentNode: ASDisplayNode, ContentNode { let scale: CGFloat = 3.0 let copyNode = QrContentNode(context: self.context, peer: self.peer, threadId: self.threadId, isStatic: true, temporary: false) + if let token = self.token { + copyNode.setContactToken(token, animated: false) + } func prepare(view: UIView, scale: CGFloat) { view.contentScaleFactor = scale @@ -1763,10 +1911,23 @@ private class QrContentNode: ASDisplayNode, ContentNode { let imageFrame = CGRect(origin: CGPoint(x: floor((codeBackgroundFrame.width - imageSize.width) / 2.0), y: floor((codeBackgroundFrame.width - imageSize.height) / 2.0)), size: imageSize) transition.updateFrame(node: self.codeImageNode, frame: imageFrame) - + + let makeMarkersLayout = self.codeMarkersNode.asyncLayout() + let markersApply = makeMarkersLayout(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets(), emptyColor: nil, scale: self.isStatic ? 3.0 : nil )) + let _ = markersApply() + + transition.updateFrame(node: self.codeMarkersNode, frame: imageFrame) + + let codePlaceholderFrame = imageFrame.insetBy(dx: 10.0, dy: 10.0) + self.codePlaceholderNode.updateLayout(size: codePlaceholderFrame.size) + self.codePlaceholderNode.frame = codePlaceholderFrame + let codeTextSize = self.codeTextNode.updateLayout(CGSize(width: codeBackgroundFrame.width - floor(imageFrame.minX * 1.2), height: codeBackgroundFrame.height)) transition.updateFrame(node: self.codeTextNode, frame: CGRect(origin: CGPoint(x: floor((codeBackgroundFrame.width - codeTextSize.width) / 2.0), y: imageFrame.maxY + floor((codeBackgroundHeight - imageFrame.maxY - codeTextSize.height) / 2.0) - 5.0), size: codeTextSize)) + let codeCountdownSize = self.codeCountdownNode.updateLayout(size: CGSize(width: codeBackgroundFrame.width - floor(imageFrame.minX * 1.2), height: codeBackgroundFrame.height), animated: true) + transition.updateFrame(node: self.codeCountdownNode, frame: CGRect(origin: CGPoint(x: floor((codeBackgroundFrame.width - codeCountdownSize.width) / 2.0), y: imageFrame.maxY + floor((codeBackgroundHeight - imageFrame.maxY - codeCountdownSize.height) / 2.0) - 5.0), size: codeCountdownSize)) + transition.updateFrame(node: self.avatarNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - avatarSize.width) / 2.0), y: codeBackgroundFrame.minY - floor(avatarSize.height * 0.7)), size: avatarSize)) if let qrCodeSize = self.qrCodeSize { diff --git a/submodules/TelegramUI/Sources/OpenUrl.swift b/submodules/TelegramUI/Sources/OpenUrl.swift index 026ce7f838..1cc5499d27 100644 --- a/submodules/TelegramUI/Sources/OpenUrl.swift +++ b/submodules/TelegramUI/Sources/OpenUrl.swift @@ -556,6 +556,22 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur convertedUrl = "https://t.me/login/\(code)" } } + } else if parsedUrl.host == "contact" { + if let components = URLComponents(string: "/?" + query) { + var token: String? + if let queryItems = components.queryItems { + for queryItem in queryItems { + if let value = queryItem.value { + if queryItem.name == "token" { + token = value + } + } + } + } + if let token = token { + convertedUrl = "https://t.me/contact/\(token)" + } + } } else if parsedUrl.host == "confirmphone" { if let components = URLComponents(string: "/?" + query) { var phone: String? diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 56c4e6eda6..9cfc58c416 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -7024,11 +7024,11 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate threadId = Int64(message.messageId.id) } -// var temporary = false -// if self.isSettings && self.data?.globalSettings?.privacySettings?.phoneDiscoveryEnabled == false { -// temporary = true -// } - controller.present(ChatQrCodeScreen(context: self.context, subject: .peer(peer: peer, threadId: threadId, temporary: false)), in: .window(.root)) + var temporary = false + if self.isSettings && self.data?.globalSettings?.privacySettings?.phoneDiscoveryEnabled == false { + temporary = true + } + controller.present(ChatQrCodeScreen(context: self.context, subject: .peer(peer: peer, threadId: threadId, temporary: temporary)), in: .window(.root)) } fileprivate func openSettings(section: PeerInfoSettingsSection) { diff --git a/submodules/UrlHandling/Sources/UrlHandling.swift b/submodules/UrlHandling/Sources/UrlHandling.swift index 89d7ee6c72..086dd11966 100644 --- a/submodules/UrlHandling/Sources/UrlHandling.swift +++ b/submodules/UrlHandling/Sources/UrlHandling.swift @@ -94,6 +94,7 @@ public enum ParsedInternalUrl { case theme(String) case phone(String, String?, String?) case startAttach(String, String?, String?) + case contactToken(String) } private enum ParsedUrl { @@ -160,7 +161,7 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? { if let _ = url { return .internalInstantView(url: "https://t.me/\(query)") } - } else if peerName == "login" { + } else if peerName == "contact" { var code: String? for queryItem in queryItems { if let value = queryItem.value { @@ -290,6 +291,8 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? { if let code = Int(pathComponents[1]) { return .confirmationCode(code) } + } else if peerName == "contact" { + return .contactToken(pathComponents[1]) } else if pathComponents[0] == "share" && pathComponents[1] == "url" { if let queryItems = components.queryItems { var url: String? @@ -655,6 +658,15 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl) return .single(.inaccessiblePeer) } } + case let .contactToken(token): + return context.engine.peers.importContactToken(token: token) + |> mapToSignal { peer -> Signal in + if let peer = peer { + return .single(.peer(peer._asPeer(), .info)) + } else { + return .single(.peer(nil, .info)) + } + } case let .privateMessage(messageId, threadId, timecode): return context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: messageId.peerId)) |> mapToSignal { peer -> Signal in