mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Refactor url handling
This commit is contained in:
parent
dd46ccd6ed
commit
c45607ffd6
@ -304,6 +304,11 @@ public enum ResolvedUrl {
|
||||
case premiumGiftCode(slug: String)
|
||||
}
|
||||
|
||||
public enum ResolveUrlResult {
|
||||
case progress
|
||||
case result(ResolvedUrl)
|
||||
}
|
||||
|
||||
public enum NavigateToChatKeepStack {
|
||||
case `default`
|
||||
case always
|
||||
@ -887,6 +892,7 @@ public protocol SharedAccountContext: AnyObject {
|
||||
func chatAvailableMessageActions(engine: TelegramEngine, accountPeerId: EnginePeer.Id, messageIds: Set<EngineMessage.Id>) -> Signal<ChatAvailableMessageActions, NoError>
|
||||
func chatAvailableMessageActions(engine: TelegramEngine, accountPeerId: EnginePeer.Id, messageIds: Set<EngineMessage.Id>, messages: [EngineMessage.Id: EngineMessage], peers: [EnginePeer.Id: EnginePeer]) -> Signal<ChatAvailableMessageActions, NoError>
|
||||
func resolveUrl(context: AccountContext, peerId: PeerId?, url: String, skipUrlAuth: Bool) -> Signal<ResolvedUrl, NoError>
|
||||
func resolveUrlWithProgress(context: AccountContext, peerId: PeerId?, url: String, skipUrlAuth: Bool) -> Signal<ResolveUrlResult, NoError>
|
||||
func openResolvedUrl(_ resolvedUrl: ResolvedUrl, context: AccountContext, urlContext: OpenURLContext, navigationController: NavigationController?, forceExternal: Bool, openPeer: @escaping (EnginePeer, ChatControllerInteractionNavigateToPeer) -> Void, sendFile: ((FileMediaReference) -> Void)?, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?, requestMessageActionUrlAuth: ((MessageActionUrlSubject) -> Void)?, joinVoiceChat: ((PeerId, String?, CachedChannelData.ActiveCall) -> Void)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, contentContext: Any?)
|
||||
func openAddContact(context: AccountContext, firstName: String, lastName: String, phoneNumber: String, label: String, present: @escaping (ViewController, Any?) -> Void, pushController: @escaping (ViewController) -> Void, completed: @escaping () -> Void)
|
||||
func openAddPersonContact(context: AccountContext, peerId: PeerId, pushController: @escaping (ViewController) -> Void, present: @escaping (ViewController, Any?) -> Void)
|
||||
|
@ -66,21 +66,26 @@ public class ChatMessageBackground: ASDisplayNode {
|
||||
private var hasWallpaper: Bool?
|
||||
private var graphics: PrincipalThemeEssentialGraphics?
|
||||
private var maskMode: Bool?
|
||||
private let imageNode: ASImageNode
|
||||
private let outlineImageNode: ASImageNode
|
||||
private weak var backgroundNode: WallpaperBackgroundNode?
|
||||
|
||||
private var imageFrame: CGRect?
|
||||
private var imageView: UIImageView?
|
||||
private var imageViewImage: UIImage?
|
||||
|
||||
public var customHighlightColor: UIColor? {
|
||||
didSet {
|
||||
self.imageView?.tintColor = self.customHighlightColor
|
||||
}
|
||||
}
|
||||
|
||||
public var backgroundFrame: CGRect = .zero
|
||||
|
||||
public var hasImage: Bool {
|
||||
self.imageNode.image != nil
|
||||
self.imageViewImage != nil
|
||||
}
|
||||
|
||||
public override init() {
|
||||
self.imageNode = ASImageNode()
|
||||
self.imageNode.displaysAsynchronously = false
|
||||
self.imageNode.displayWithoutProcessing = true
|
||||
|
||||
self.outlineImageNode = ASImageNode()
|
||||
self.outlineImageNode.displaysAsynchronously = false
|
||||
self.outlineImageNode.displayWithoutProcessing = true
|
||||
@ -89,16 +94,39 @@ public class ChatMessageBackground: ASDisplayNode {
|
||||
|
||||
self.isUserInteractionEnabled = false
|
||||
self.addSubnode(self.outlineImageNode)
|
||||
self.addSubnode(self.imageNode)
|
||||
}
|
||||
|
||||
override public func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
let imageView = UIImageView()
|
||||
self.imageView = imageView
|
||||
self.view.addSubview(imageView)
|
||||
|
||||
imageView.image = self.imageViewImage
|
||||
imageView.tintColor = self.customHighlightColor
|
||||
|
||||
if let imageFrame = self.imageFrame {
|
||||
imageView.frame = imageFrame
|
||||
}
|
||||
}
|
||||
|
||||
public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
|
||||
transition.updateFrame(node: self.imageNode, frame: CGRect(origin: CGPoint(), size: size).insetBy(dx: -1.0, dy: -1.0))
|
||||
let imageFrame = CGRect(origin: CGPoint(), size: size).insetBy(dx: -1.0, dy: -1.0)
|
||||
self.imageFrame = imageFrame
|
||||
if let imageView = self.imageView {
|
||||
transition.updateFrame(view: imageView, frame: imageFrame)
|
||||
}
|
||||
transition.updateFrame(node: self.outlineImageNode, frame: CGRect(origin: CGPoint(), size: size).insetBy(dx: -1.0, dy: -1.0))
|
||||
}
|
||||
|
||||
public func updateLayout(size: CGSize, transition: ListViewItemUpdateAnimation) {
|
||||
transition.animator.updateFrame(layer: self.imageNode.layer, frame: CGRect(origin: CGPoint(), size: size).insetBy(dx: -1.0, dy: -1.0), completion: nil)
|
||||
let imageFrame = CGRect(origin: CGPoint(), size: size).insetBy(dx: -1.0, dy: -1.0)
|
||||
self.imageFrame = imageFrame
|
||||
if let imageView = self.imageView {
|
||||
transition.animator.updateFrame(layer: imageView.layer, frame: imageFrame, completion: nil)
|
||||
}
|
||||
|
||||
transition.animator.updateFrame(layer: self.outlineImageNode.layer, frame: CGRect(origin: CGPoint(), size: size).insetBy(dx: -1.0, dy: -1.0), completion: nil)
|
||||
}
|
||||
|
||||
@ -217,13 +245,11 @@ public class ChatMessageBackground: ASDisplayNode {
|
||||
}
|
||||
|
||||
let outlineImage: UIImage?
|
||||
var isIncoming = false
|
||||
if hasWallpaper {
|
||||
switch type {
|
||||
case .none:
|
||||
outlineImage = nil
|
||||
case let .incoming(mergeType):
|
||||
isIncoming = true
|
||||
switch mergeType {
|
||||
case .None:
|
||||
outlineImage = graphics.chatMessageBackgroundIncomingOutlineImage
|
||||
@ -267,38 +293,38 @@ public class ChatMessageBackground: ASDisplayNode {
|
||||
}
|
||||
|
||||
if let previousType = previousType, previousType != .none, type == .none {
|
||||
if transition.isAnimated {
|
||||
if transition.isAnimated, let imageView = self.imageView {
|
||||
let tempLayer = CALayer()
|
||||
tempLayer.contents = self.imageNode.layer.contents
|
||||
tempLayer.contentsScale = self.imageNode.layer.contentsScale
|
||||
tempLayer.rasterizationScale = self.imageNode.layer.rasterizationScale
|
||||
tempLayer.contentsGravity = self.imageNode.layer.contentsGravity
|
||||
tempLayer.contentsCenter = self.imageNode.layer.contentsCenter
|
||||
tempLayer.contents = imageView.layer.contents
|
||||
tempLayer.contentsScale = imageView.layer.contentsScale
|
||||
tempLayer.rasterizationScale = imageView.layer.rasterizationScale
|
||||
tempLayer.contentsGravity = imageView.layer.contentsGravity
|
||||
tempLayer.contentsCenter = imageView.layer.contentsCenter
|
||||
|
||||
tempLayer.frame = self.imageNode.frame
|
||||
self.layer.insertSublayer(tempLayer, above: self.imageNode.layer)
|
||||
tempLayer.frame = imageView.frame
|
||||
self.layer.insertSublayer(tempLayer, above: imageView.layer)
|
||||
transition.updateAlpha(layer: tempLayer, alpha: 0.0, completion: { [weak tempLayer] _ in
|
||||
tempLayer?.removeFromSuperlayer()
|
||||
})
|
||||
}
|
||||
} else if transition.isAnimated {
|
||||
if let previousContents = self.imageNode.layer.contents {
|
||||
} else if transition.isAnimated, let imageView = self.imageView {
|
||||
if let previousContents = imageView.layer.contents {
|
||||
if let image = image {
|
||||
if (previousContents as AnyObject) !== image.cgImage {
|
||||
self.imageNode.layer.animate(from: previousContents as AnyObject, to: image.cgImage! as AnyObject, keyPath: "contents", timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, duration: 0.42)
|
||||
imageView.layer.animate(from: previousContents as AnyObject, to: image.cgImage! as AnyObject, keyPath: "contents", timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, duration: 0.42)
|
||||
}
|
||||
} else {
|
||||
let tempLayer = CALayer()
|
||||
tempLayer.contents = self.imageNode.layer.contents
|
||||
tempLayer.contentsScale = self.imageNode.layer.contentsScale
|
||||
tempLayer.rasterizationScale = self.imageNode.layer.rasterizationScale
|
||||
tempLayer.contentsGravity = self.imageNode.layer.contentsGravity
|
||||
tempLayer.contentsCenter = self.imageNode.layer.contentsCenter
|
||||
tempLayer.compositingFilter = self.imageNode.layer.compositingFilter
|
||||
tempLayer.contents = imageView.layer.contents
|
||||
tempLayer.contentsScale = imageView.layer.contentsScale
|
||||
tempLayer.rasterizationScale = imageView.layer.rasterizationScale
|
||||
tempLayer.contentsGravity = imageView.layer.contentsGravity
|
||||
tempLayer.contentsCenter = imageView.layer.contentsCenter
|
||||
tempLayer.compositingFilter = imageView.layer.compositingFilter
|
||||
|
||||
tempLayer.frame = self.imageNode.frame
|
||||
tempLayer.frame = imageView.frame
|
||||
|
||||
self.imageNode.supernode?.layer.insertSublayer(tempLayer, above: self.imageNode.layer)
|
||||
imageView.superview?.layer.insertSublayer(tempLayer, above: imageView.layer)
|
||||
transition.updateAlpha(layer: tempLayer, alpha: 0.0, completion: { [weak tempLayer] _ in
|
||||
tempLayer?.removeFromSuperlayer()
|
||||
})
|
||||
@ -306,26 +332,17 @@ public class ChatMessageBackground: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
self.imageNode.image = image
|
||||
if highlighted && maskMode, let backdropNode = self.backdropNode, backdropNode.hasImage && isIncoming {
|
||||
self.imageNode.layer.compositingFilter = "overlayBlendMode"
|
||||
self.imageNode.alpha = 1.0
|
||||
|
||||
backdropNode.addSubnode(self.imageNode)
|
||||
} else {
|
||||
self.imageNode.layer.compositingFilter = nil
|
||||
self.imageNode.alpha = 1.0
|
||||
|
||||
if self.imageNode.supernode != self {
|
||||
self.addSubnode(self.imageNode)
|
||||
}
|
||||
self.imageViewImage = image
|
||||
if let imageView = self.imageView {
|
||||
imageView.image = image
|
||||
}
|
||||
|
||||
self.outlineImageNode.image = outlineImage
|
||||
}
|
||||
|
||||
public func animateFrom(sourceView: UIView, transition: CombinedTransition) {
|
||||
if transition.isAnimated {
|
||||
self.imageNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1)
|
||||
self.imageView?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1)
|
||||
self.outlineImageNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1)
|
||||
|
||||
self.view.addSubview(sourceView)
|
||||
@ -334,9 +351,11 @@ public class ChatMessageBackground: ASDisplayNode {
|
||||
sourceView?.removeFromSuperview()
|
||||
})
|
||||
|
||||
transition.animateFrame(layer: self.imageNode.layer, from: sourceView.frame)
|
||||
if let imageView = imageView {
|
||||
transition.animateFrame(layer: imageView.layer, from: sourceView.frame)
|
||||
transition.updateFrame(layer: sourceView.layer, frame: CGRect(origin: imageView.frame.origin, size: CGSize(width: imageView.frame.width - 7.0, height: imageView.frame.height)))
|
||||
}
|
||||
transition.animateFrame(layer: self.outlineImageNode.layer, from: sourceView.frame)
|
||||
transition.updateFrame(layer: sourceView.layer, frame: CGRect(origin: self.imageNode.frame.origin, size: CGSize(width: self.imageNode.frame.width - 7.0, height: self.imageNode.frame.height)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -53,6 +53,12 @@ private func cachedInternalInstantPage(context: AccountContext, url: String) ->
|
||||
return cachedInstantPage(engine: context.engine, url: cachedUrl)
|
||||
|> mapToSignal { cachedInstantPage -> Signal<ResolvedUrl, NoError> in
|
||||
let updated = resolveInstantViewUrl(account: context.account, url: url)
|
||||
|> mapToSignal { result -> Signal<ResolvedUrl, NoError> in
|
||||
guard case let .result(result) = result else {
|
||||
return .complete()
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
|> afterNext { result in
|
||||
if case let .instantView(webPage, _) = result, case let .Loaded(content) = webPage.content, let instantPage = content.instantPage {
|
||||
if instantPage.isComplete {
|
||||
|
@ -71,7 +71,14 @@ final class InstantPageFeedbackNode: ASDisplayNode, InstantPageNode {
|
||||
}
|
||||
|
||||
@objc func buttonPressed() {
|
||||
self.resolveDisposable.set((self.context.engine.peers.resolvePeerByName(name: "previews") |> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
self.resolveDisposable.set((self.context.engine.peers.resolvePeerByName(name: "previews")
|
||||
|> mapToSignal { result -> Signal<EnginePeer?, NoError> in
|
||||
guard case let .result(result) = result else {
|
||||
return .complete()
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
if let strongSelf = self, let _ = peer, let webPageId = strongSelf.webPage.id?.id {
|
||||
strongSelf.openUrl(InstantPageUrlItem(url: "https://t.me/previews?start=webpage\(webPageId)", webpageId: nil))
|
||||
}
|
||||
|
@ -150,7 +150,10 @@ final class InstantPagePeerReferenceNode: ASDisplayNode, InstantPageNode {
|
||||
|> mapToSignal({ peer -> Signal<EnginePeer, NoError> in
|
||||
if let peer = peer as? TelegramChannel, let username = peer.addressName, peer.accessHash == nil {
|
||||
return .single(.channel(peer)) |> then(engine.peers.resolvePeerByName(name: username)
|
||||
|> mapToSignal({ updatedPeer -> Signal<EnginePeer, NoError> in
|
||||
|> mapToSignal({ result -> Signal<EnginePeer, NoError> in
|
||||
guard case let .result(updatedPeer) = result else {
|
||||
return .complete()
|
||||
}
|
||||
if let updatedPeer = updatedPeer {
|
||||
return .single(updatedPeer)
|
||||
} else {
|
||||
|
@ -49,7 +49,12 @@ public func nearbyVenues(context: AccountContext, story: Bool = false, latitude:
|
||||
return botUsername
|
||||
|> mapToSignal { botUsername in
|
||||
return context.engine.peers.resolvePeerByName(name: botUsername)
|
||||
|> take(1)
|
||||
|> mapToSignal { result -> Signal<EnginePeer?, NoError> in
|
||||
guard case let .result(result) = result else {
|
||||
return .complete()
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
|> mapToSignal { peer -> Signal<ChatContextResultCollection?, NoError> in
|
||||
guard let peer = peer else {
|
||||
return .single(nil)
|
||||
|
@ -990,7 +990,14 @@ public func channelPermissionsController(context: AccountContext, updatedPresent
|
||||
}
|
||||
pushControllerImpl?(controller)
|
||||
}, openChannelExample: {
|
||||
resolveDisposable.set((context.engine.peers.resolvePeerByName(name: "durov") |> deliverOnMainQueue).start(next: { peer in
|
||||
resolveDisposable.set((context.engine.peers.resolvePeerByName(name: "durov")
|
||||
|> mapToSignal { result -> Signal<EnginePeer?, NoError> in
|
||||
guard case let .result(result) = result else {
|
||||
return .complete()
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
if let peer = peer {
|
||||
navigateToChatControllerImpl?(peer.id)
|
||||
}
|
||||
|
@ -392,7 +392,14 @@ public func groupStickerPackSetupController(context: AccountContext, updatedPres
|
||||
}, updateSearchText: { text in
|
||||
searchText.set(text)
|
||||
}, openStickersBot: {
|
||||
resolveDisposable.set((context.engine.peers.resolvePeerByName(name: "stickers") |> deliverOnMainQueue).start(next: { peer in
|
||||
resolveDisposable.set((context.engine.peers.resolvePeerByName(name: "stickers")
|
||||
|> mapToSignal { result -> Signal<EnginePeer?, NoError> in
|
||||
guard case let .result(result) = result else {
|
||||
return .complete()
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
if let peer = peer {
|
||||
dismissImpl?()
|
||||
navigateToChatControllerImpl?(peer.id)
|
||||
|
@ -24,7 +24,6 @@ public func openUserGeneratedUrl(context: AccountContext, peerId: PeerId?, url:
|
||||
}
|
||||
}
|
||||
|> runOn(Queue.mainQueue())
|
||||
|> delay(0.05, queue: Queue.mainQueue())
|
||||
} else {
|
||||
progressSignal = Signal<Never, NoError> { subscriber in
|
||||
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: {
|
||||
@ -38,18 +37,18 @@ public func openUserGeneratedUrl(context: AccountContext, peerId: PeerId?, url:
|
||||
}
|
||||
}
|
||||
|> runOn(Queue.mainQueue())
|
||||
|> delay(0.1, queue: Queue.mainQueue())
|
||||
}
|
||||
let progressDisposable = progressSignal.start()
|
||||
let progressDisposable = MetaDisposable()
|
||||
var didStartProgress = false
|
||||
|
||||
cancelImpl = {
|
||||
disposable.dispose()
|
||||
}
|
||||
|
||||
var resolveSignal: Signal<ResolvedUrl, NoError>
|
||||
resolveSignal = context.sharedContext.resolveUrl(context: context, peerId: peerId, url: url, skipUrlAuth: skipUrlAuth)
|
||||
var resolveSignal: Signal<ResolveUrlResult, NoError>
|
||||
resolveSignal = context.sharedContext.resolveUrlWithProgress(context: context, peerId: peerId, url: url, skipUrlAuth: skipUrlAuth)
|
||||
#if DEBUG
|
||||
resolveSignal = resolveSignal |> delay(2.0, queue: .mainQueue())
|
||||
//resolveSignal = .single(.progress) |> then(resolveSignal |> delay(2.0, queue: .mainQueue()))
|
||||
#endif
|
||||
|
||||
disposable.set((resolveSignal
|
||||
@ -59,8 +58,16 @@ public func openUserGeneratedUrl(context: AccountContext, peerId: PeerId?, url:
|
||||
}
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { result in
|
||||
progressDisposable.dispose()
|
||||
openResolved(result)
|
||||
switch result {
|
||||
case .progress:
|
||||
if !didStartProgress {
|
||||
didStartProgress = true
|
||||
progressDisposable.set(progressSignal.start())
|
||||
}
|
||||
case let .result(result):
|
||||
progressDisposable.dispose()
|
||||
openResolved(result)
|
||||
}
|
||||
}))
|
||||
|
||||
return ActionDisposable {
|
||||
|
@ -295,6 +295,12 @@ public func deleteAccountOptionsController(context: AccountContext, navigationCo
|
||||
faqUrl = "https://telegram.org/faq#q-can-i-delete-my-messages"
|
||||
}
|
||||
let resolvedUrl = resolveInstantViewUrl(account: context.account, url: faqUrl)
|
||||
|> mapToSignal { result -> Signal<ResolvedUrl, NoError> in
|
||||
guard case let .result(result) = result else {
|
||||
return .complete()
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
|
||||
let resolvedUrlPromise = Promise<ResolvedUrl>()
|
||||
resolvedUrlPromise.set(resolvedUrl)
|
||||
@ -330,6 +336,12 @@ public func deleteAccountOptionsController(context: AccountContext, navigationCo
|
||||
faqUrl = "https://telegram.org/faq#general"
|
||||
}
|
||||
let resolvedUrl = resolveInstantViewUrl(account: context.account, url: faqUrl)
|
||||
|> mapToSignal { result -> Signal<ResolvedUrl, NoError> in
|
||||
guard case let .result(result) = result else {
|
||||
return .complete()
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
|
||||
let resolvedUrlPromise = Promise<ResolvedUrl>()
|
||||
resolvedUrlPromise.set(resolvedUrl)
|
||||
|
@ -202,6 +202,12 @@ public func logoutOptionsController(context: AccountContext, navigationControlle
|
||||
faqUrl = "https://telegram.org/faq#general"
|
||||
}
|
||||
let resolvedUrl = resolveInstantViewUrl(account: context.account, url: faqUrl)
|
||||
|> mapToSignal { result -> Signal<ResolvedUrl, NoError> in
|
||||
guard case let .result(result) = result else {
|
||||
return .complete()
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
|
||||
let resolvedUrlPromise = Promise<ResolvedUrl>()
|
||||
resolvedUrlPromise.set(resolvedUrl)
|
||||
|
@ -732,7 +732,14 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
|
||||
])
|
||||
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}, openStickersBot: {
|
||||
resolveDisposable.set((context.engine.peers.resolvePeerByName(name: "stickers") |> deliverOnMainQueue).start(next: { peer in
|
||||
resolveDisposable.set((context.engine.peers.resolvePeerByName(name: "stickers")
|
||||
|> mapToSignal { result -> Signal<EnginePeer?, NoError> in
|
||||
guard case let .result(result) = result else {
|
||||
return .complete()
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
if let peer = peer {
|
||||
navigateToChatControllerImpl?(peer.id)
|
||||
}
|
||||
|
@ -469,6 +469,12 @@ final class ThemeGridSearchContentNode: SearchDisplayControllerContentNode {
|
||||
return .single(nil)
|
||||
}
|
||||
return context.engine.peers.resolvePeerByName(name: name)
|
||||
|> mapToSignal { result -> Signal<EnginePeer?, NoError> in
|
||||
guard case let .result(result) = result else {
|
||||
return .complete()
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
|> mapToSignal { peer -> Signal<Peer?, NoError> in
|
||||
if let peer = peer {
|
||||
return .single(peer._asPeer())
|
||||
|
@ -136,6 +136,12 @@ public final class StickerPackPreviewController: ViewController, StandalonePrese
|
||||
}
|
||||
|
||||
strongSelf.openMentionDisposable.set((strongSelf.context.engine.peers.resolvePeerByName(name: mention)
|
||||
|> mapToSignal { result -> Signal<EnginePeer?, NoError> in
|
||||
guard case let .result(result) = result else {
|
||||
return .complete()
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
|> mapToSignal { peer -> Signal<Peer?, NoError> in
|
||||
if let peer = peer {
|
||||
return .single(peer._asPeer())
|
||||
|
@ -2029,6 +2029,12 @@ public final class StickerPackScreenImpl: ViewController {
|
||||
}
|
||||
|
||||
strongSelf.openMentionDisposable.set((strongSelf.context.engine.peers.resolvePeerByName(name: mention)
|
||||
|> mapToSignal { result -> Signal<EnginePeer?, NoError> in
|
||||
guard case let .result(result) = result else {
|
||||
return .complete()
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
|> mapToSignal { peer -> Signal<Peer?, NoError> in
|
||||
if let peer = peer {
|
||||
return .single(peer._asPeer())
|
||||
|
@ -307,6 +307,7 @@ func apiMessageAssociatedMessageIds(_ message: Api.Message) -> (replyIds: Refere
|
||||
struct ParsedMessageWebpageAttributes {
|
||||
var forceLargeMedia: Bool?
|
||||
var isManuallyAdded: Bool
|
||||
var isSafe: Bool
|
||||
}
|
||||
|
||||
func textMediaAndExpirationTimerFromApiMedia(_ media: Api.MessageMedia?, _ peerId: PeerId) -> (media: Media?, expirationTimer: Int32?, nonPremium: Bool?, hasSpoiler: Bool?, webpageAttributes: ParsedMessageWebpageAttributes?) {
|
||||
@ -352,7 +353,8 @@ func textMediaAndExpirationTimerFromApiMedia(_ media: Api.MessageMedia?, _ peerI
|
||||
|
||||
return (mediaWebpage, nil, nil, nil, ParsedMessageWebpageAttributes(
|
||||
forceLargeMedia: webpageForceLargeMedia,
|
||||
isManuallyAdded: (flags & (1 << 3)) != 0
|
||||
isManuallyAdded: (flags & (1 << 3)) != 0,
|
||||
isSafe: (flags & (1 << 4)) != 0
|
||||
))
|
||||
}
|
||||
case .messageMediaUnsupported:
|
||||
@ -708,7 +710,7 @@ extension StoreMessage {
|
||||
let leadingPreview = (flags & (1 << 27)) != 0
|
||||
|
||||
if let webpageAttributes {
|
||||
attributes.append(WebpagePreviewMessageAttribute(leadingPreview: leadingPreview, forceLargeMedia: webpageAttributes.forceLargeMedia, isManuallyAdded: webpageAttributes.isManuallyAdded))
|
||||
attributes.append(WebpagePreviewMessageAttribute(leadingPreview: leadingPreview, forceLargeMedia: webpageAttributes.forceLargeMedia, isManuallyAdded: webpageAttributes.isManuallyAdded, isSafe: webpageAttributes.isSafe))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -72,17 +72,20 @@ public class WebpagePreviewMessageAttribute: MessageAttribute, Equatable {
|
||||
public let leadingPreview: Bool
|
||||
public let forceLargeMedia: Bool?
|
||||
public let isManuallyAdded: Bool
|
||||
public let isSafe: Bool
|
||||
|
||||
public init(leadingPreview: Bool, forceLargeMedia: Bool?, isManuallyAdded: Bool) {
|
||||
public init(leadingPreview: Bool, forceLargeMedia: Bool?, isManuallyAdded: Bool, isSafe: Bool) {
|
||||
self.leadingPreview = leadingPreview
|
||||
self.forceLargeMedia = forceLargeMedia
|
||||
self.isManuallyAdded = isManuallyAdded
|
||||
self.isSafe = isSafe
|
||||
}
|
||||
|
||||
required public init(decoder: PostboxDecoder) {
|
||||
self.leadingPreview = decoder.decodeBoolForKey("lp", orElse: false)
|
||||
self.forceLargeMedia = decoder.decodeOptionalBoolForKey("lm")
|
||||
self.isManuallyAdded = decoder.decodeBoolForKey("ma", orElse: false)
|
||||
self.isSafe = decoder.decodeBoolForKey("sf", orElse: false)
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
@ -93,6 +96,7 @@ public class WebpagePreviewMessageAttribute: MessageAttribute, Equatable {
|
||||
encoder.encodeNil(forKey: "lm")
|
||||
}
|
||||
encoder.encodeBool(self.isManuallyAdded, forKey: "ma")
|
||||
encoder.encodeBool(self.isSafe, forKey: "sf")
|
||||
}
|
||||
|
||||
public static func ==(lhs: WebpagePreviewMessageAttribute, rhs: WebpagePreviewMessageAttribute) -> Bool {
|
||||
@ -105,6 +109,9 @@ public class WebpagePreviewMessageAttribute: MessageAttribute, Equatable {
|
||||
if lhs.isManuallyAdded != rhs.isManuallyAdded {
|
||||
return false
|
||||
}
|
||||
if lhs.isSafe != rhs.isSafe {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -253,7 +253,12 @@ func _internal_createForumChannelTopic(account: Account, peerId: PeerId, title:
|
||||
}
|
||||
}
|
||||
|
||||
func _internal_fetchForumChannelTopic(account: Account, peerId: PeerId, threadId: Int64) -> Signal<EngineMessageHistoryThread.Info?, NoError> {
|
||||
public enum FetchForumChannelTopicResult {
|
||||
case progress
|
||||
case result(EngineMessageHistoryThread.Info?)
|
||||
}
|
||||
|
||||
func _internal_fetchForumChannelTopic(account: Account, peerId: PeerId, threadId: Int64) -> Signal<FetchForumChannelTopicResult, NoError> {
|
||||
return account.postbox.transaction { transaction -> (info: EngineMessageHistoryThread.Info?, inputChannel: Api.InputChannel?) in
|
||||
if let data = transaction.getMessageHistoryThreadInfo(peerId: peerId, threadId: threadId)?.data.get(MessageHistoryThreadData.self) {
|
||||
return (data.info, nil)
|
||||
@ -261,20 +266,20 @@ func _internal_fetchForumChannelTopic(account: Account, peerId: PeerId, threadId
|
||||
return (nil, transaction.getPeer(peerId).flatMap(apiInputChannel))
|
||||
}
|
||||
}
|
||||
|> mapToSignal { info, _ -> Signal<EngineMessageHistoryThread.Info?, NoError> in
|
||||
|> mapToSignal { info, _ -> Signal<FetchForumChannelTopicResult, NoError> in
|
||||
if let info = info {
|
||||
return .single(info)
|
||||
return .single(.result(info))
|
||||
} else {
|
||||
return resolveForumThreads(accountPeerId: account.peerId, postbox: account.postbox, network: account.network, ids: [MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId))])
|
||||
|> mapToSignal { _ -> Signal<EngineMessageHistoryThread.Info?, NoError> in
|
||||
return account.postbox.transaction { transaction -> EngineMessageHistoryThread.Info? in
|
||||
return .single(.progress) |> then(resolveForumThreads(accountPeerId: account.peerId, postbox: account.postbox, network: account.network, ids: [MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId))])
|
||||
|> mapToSignal { _ -> Signal<FetchForumChannelTopicResult, NoError> in
|
||||
return account.postbox.transaction { transaction -> FetchForumChannelTopicResult in
|
||||
if let data = transaction.getMessageHistoryThreadInfo(peerId: peerId, threadId: threadId)?.data.get(MessageHistoryThreadData.self) {
|
||||
return data.info
|
||||
return .result(data.info)
|
||||
} else {
|
||||
return nil
|
||||
return .result(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -190,38 +190,38 @@ private func convertForwardedMediaForSecretChat(_ media: Media) -> Media {
|
||||
private func filterMessageAttributesForOutgoingMessage(_ attributes: [MessageAttribute]) -> [MessageAttribute] {
|
||||
return attributes.filter { attribute in
|
||||
switch attribute {
|
||||
case _ as TextEntitiesMessageAttribute:
|
||||
return true
|
||||
case _ as InlineBotMessageAttribute:
|
||||
return true
|
||||
case _ as OutgoingMessageInfoAttribute:
|
||||
return false
|
||||
case _ as OutgoingContentInfoMessageAttribute:
|
||||
return true
|
||||
case _ as ReplyMarkupMessageAttribute:
|
||||
return true
|
||||
case _ as OutgoingChatContextResultMessageAttribute:
|
||||
return true
|
||||
case _ as AutoremoveTimeoutMessageAttribute:
|
||||
return true
|
||||
case _ as NotificationInfoMessageAttribute:
|
||||
return true
|
||||
case _ as OutgoingScheduleInfoMessageAttribute:
|
||||
return true
|
||||
case _ as EmbeddedMediaStickersMessageAttribute:
|
||||
return true
|
||||
case _ as EmojiSearchQueryMessageAttribute:
|
||||
return true
|
||||
case _ as ForwardOptionsMessageAttribute:
|
||||
return true
|
||||
case _ as SendAsMessageAttribute:
|
||||
return true
|
||||
case _ as MediaSpoilerMessageAttribute:
|
||||
return true
|
||||
case _ as WebpagePreviewMessageAttribute:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
case _ as TextEntitiesMessageAttribute:
|
||||
return true
|
||||
case _ as InlineBotMessageAttribute:
|
||||
return true
|
||||
case _ as OutgoingMessageInfoAttribute:
|
||||
return false
|
||||
case _ as OutgoingContentInfoMessageAttribute:
|
||||
return true
|
||||
case _ as ReplyMarkupMessageAttribute:
|
||||
return true
|
||||
case _ as OutgoingChatContextResultMessageAttribute:
|
||||
return true
|
||||
case _ as AutoremoveTimeoutMessageAttribute:
|
||||
return true
|
||||
case _ as NotificationInfoMessageAttribute:
|
||||
return true
|
||||
case _ as OutgoingScheduleInfoMessageAttribute:
|
||||
return true
|
||||
case _ as EmbeddedMediaStickersMessageAttribute:
|
||||
return true
|
||||
case _ as EmojiSearchQueryMessageAttribute:
|
||||
return true
|
||||
case _ as ForwardOptionsMessageAttribute:
|
||||
return true
|
||||
case _ as SendAsMessageAttribute:
|
||||
return true
|
||||
case _ as MediaSpoilerMessageAttribute:
|
||||
return true
|
||||
case _ as WebpagePreviewMessageAttribute:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -244,6 +244,9 @@ private func filterMessageAttributesForForwardedMessage(_ attributes: [MessageAt
|
||||
case _ as MediaSpoilerMessageAttribute:
|
||||
return true
|
||||
case let attribute as ReplyMessageAttribute:
|
||||
if attribute.quote != nil {
|
||||
return true
|
||||
}
|
||||
if let forwardedMessageIds = forwardedMessageIds {
|
||||
return forwardedMessageIds.contains(attribute.messageId)
|
||||
} else {
|
||||
|
@ -1154,7 +1154,7 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox:
|
||||
|
||||
if mediaValue is TelegramMediaWebpage {
|
||||
if let webpageAttributes {
|
||||
attributes.append(WebpagePreviewMessageAttribute(leadingPreview: false, forceLargeMedia: webpageAttributes.forceLargeMedia, isManuallyAdded: webpageAttributes.isManuallyAdded))
|
||||
attributes.append(WebpagePreviewMessageAttribute(leadingPreview: false, forceLargeMedia: webpageAttributes.forceLargeMedia, isManuallyAdded: webpageAttributes.isManuallyAdded, isSafe: webpageAttributes.isSafe))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -408,7 +408,10 @@ public final class AccountViewTracker {
|
||||
for messageId in addedMessageIds {
|
||||
if self.webpageDisposables[messageId] == nil {
|
||||
if let (_, url) = localWebpages[messageId] {
|
||||
self.webpageDisposables[messageId] = (webpagePreview(account: account, url: url) |> mapToSignal { webpage -> Signal<Void, NoError> in
|
||||
self.webpageDisposables[messageId] = (webpagePreview(account: account, url: url) |> mapToSignal { result -> Signal<Void, NoError> in
|
||||
guard case let .result(webpage) = result else {
|
||||
return .complete()
|
||||
}
|
||||
return account.postbox.transaction { transaction -> Void in
|
||||
if let webpage = webpage {
|
||||
transaction.updateMessage(messageId, update: { currentMessage in
|
||||
|
@ -337,3 +337,40 @@ public func messageTextEntitiesInRange(entities: [MessageTextEntity], range: NSR
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
public func quoteMaxLength(appConfig: AppConfiguration) -> Int {
|
||||
if let data = appConfig.data, let quoteLengthMax = data["quote_length_max"] as? Double {
|
||||
return Int(quoteLengthMax)
|
||||
}
|
||||
return 1024
|
||||
}
|
||||
|
||||
public func trimStringWithEntities(string: String, entities: [MessageTextEntity], maxLength: Int) -> (string: String, entities: [MessageTextEntity]) {
|
||||
let nsString = string as NSString
|
||||
var range = 0 ..< nsString.length
|
||||
|
||||
while range.lowerBound < nsString.length {
|
||||
let c = nsString.character(at: range.lowerBound)
|
||||
if c == 0x0a || c == 0x20 {
|
||||
range = (range.lowerBound + 1) ..< range.upperBound
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
while range.upperBound > range.lowerBound {
|
||||
let c = nsString.character(at: range.lowerBound)
|
||||
if c == 0x0a || c == 0x20 {
|
||||
range = range.lowerBound ..< (range.upperBound - 1)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
while range.upperBound - range.lowerBound > maxLength {
|
||||
range = range.lowerBound ..< (range.upperBound - 1)
|
||||
}
|
||||
|
||||
let nsRange = NSRange(location: range.lowerBound, length: range.upperBound - range.lowerBound)
|
||||
return (nsString.substring(with: nsRange), messageTextEntitiesInRange(entities: entities, range: nsRange, onlyQuoteable: false))
|
||||
}
|
||||
|
@ -4,13 +4,17 @@ import SwiftSignalKit
|
||||
import TelegramApi
|
||||
import MtProtoKit
|
||||
|
||||
public enum GetMessagesResult {
|
||||
case progress
|
||||
case result([Message])
|
||||
}
|
||||
|
||||
public enum GetMessagesStrategy {
|
||||
case local
|
||||
case cloud(skipLocal: Bool)
|
||||
}
|
||||
|
||||
func _internal_getMessagesLoadIfNecessary(_ messageIds: [MessageId], postbox: Postbox, network: Network, accountPeerId: PeerId, strategy: GetMessagesStrategy = .cloud(skipLocal: false)) -> Signal <[Message], NoError> {
|
||||
func _internal_getMessagesLoadIfNecessary(_ messageIds: [MessageId], postbox: Postbox, network: Network, accountPeerId: PeerId, strategy: GetMessagesStrategy = .cloud(skipLocal: false)) -> Signal<GetMessagesResult, NoError> {
|
||||
let postboxSignal = postbox.transaction { transaction -> ([Message], Set<MessageId>, SimpleDictionary<PeerId, Peer>) in
|
||||
var ids = messageIds
|
||||
|
||||
@ -78,8 +82,8 @@ func _internal_getMessagesLoadIfNecessary(_ messageIds: [MessageId], postbox: Po
|
||||
}
|
||||
}
|
||||
|
||||
return combineLatest(signals) |> mapToSignal { results -> Signal<[Message], NoError> in
|
||||
return postbox.transaction { transaction -> [Message] in
|
||||
return .single(.progress) |> then(combineLatest(signals) |> mapToSignal { results -> Signal<GetMessagesResult, NoError> in
|
||||
return postbox.transaction { transaction -> GetMessagesResult in
|
||||
for (peer, messages, chats, users) in results {
|
||||
if !messages.isEmpty {
|
||||
var storeMessages: [StoreMessage] = []
|
||||
@ -102,13 +106,14 @@ func _internal_getMessagesLoadIfNecessary(_ messageIds: [MessageId], postbox: Po
|
||||
}
|
||||
}
|
||||
|
||||
return existMessages + loadedMessages
|
||||
return .result(existMessages + loadedMessages)
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
} else {
|
||||
return postboxSignal |> map {$0.0}
|
||||
return postboxSignal
|
||||
|> map {
|
||||
return .result($0.0)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -159,7 +159,7 @@ public extension TelegramEngine {
|
||||
return _internal_markAllChatsAsRead(postbox: self.account.postbox, network: self.account.network, stateManager: self.account.stateManager)
|
||||
}
|
||||
|
||||
public func getMessagesLoadIfNecessary(_ messageIds: [MessageId], strategy: GetMessagesStrategy = .cloud(skipLocal: false)) -> Signal <[Message], NoError> {
|
||||
public func getMessagesLoadIfNecessary(_ messageIds: [MessageId], strategy: GetMessagesStrategy = .cloud(skipLocal: false)) -> Signal<GetMessagesResult, NoError> {
|
||||
return _internal_getMessagesLoadIfNecessary(messageIds, postbox: self.account.postbox, network: self.account.network, accountPeerId: self.account.peerId, strategy: strategy)
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,17 @@ public enum ResolvePeerByNameOptionRemote {
|
||||
case update
|
||||
}
|
||||
|
||||
func _internal_resolvePeerByName(account: Account, name: String, ageLimit: Int32 = 2 * 60 * 60 * 24) -> Signal<PeerId?, NoError> {
|
||||
public enum ResolvePeerIdByNameResult {
|
||||
case progress
|
||||
case result(PeerId?)
|
||||
}
|
||||
|
||||
public enum ResolvePeerResult {
|
||||
case progress
|
||||
case result(EnginePeer?)
|
||||
}
|
||||
|
||||
func _internal_resolvePeerByName(account: Account, name: String, ageLimit: Int32 = 2 * 60 * 60 * 24) -> Signal<ResolvePeerIdByNameResult, NoError> {
|
||||
var normalizedName = name
|
||||
if normalizedName.hasPrefix("@") {
|
||||
normalizedName = String(normalizedName[name.index(after: name.startIndex)...])
|
||||
@ -24,17 +34,19 @@ func _internal_resolvePeerByName(account: Account, name: String, ageLimit: Int32
|
||||
|
||||
return account.postbox.transaction { transaction -> CachedResolvedByNamePeer? in
|
||||
return transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.resolvedByNamePeers, key: CachedResolvedByNamePeer.key(name: normalizedName)))?.get(CachedResolvedByNamePeer.self)
|
||||
} |> mapToSignal { cachedEntry -> Signal<PeerId?, NoError> in
|
||||
}
|
||||
|> mapToSignal { cachedEntry -> Signal<ResolvePeerIdByNameResult, NoError> in
|
||||
let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
|
||||
if let cachedEntry = cachedEntry, cachedEntry.timestamp <= timestamp && cachedEntry.timestamp >= timestamp - ageLimit {
|
||||
return .single(cachedEntry.peerId)
|
||||
return .single(.result(cachedEntry.peerId))
|
||||
} else {
|
||||
return account.network.request(Api.functions.contacts.resolveUsername(username: normalizedName))
|
||||
return .single(.progress)
|
||||
|> then(account.network.request(Api.functions.contacts.resolveUsername(username: normalizedName))
|
||||
|> mapError { _ -> Void in
|
||||
return Void()
|
||||
}
|
||||
|> mapToSignal { result -> Signal<PeerId?, Void> in
|
||||
return account.postbox.transaction { transaction -> PeerId? in
|
||||
|> mapToSignal { result -> Signal<ResolvePeerIdByNameResult, Void> in
|
||||
return account.postbox.transaction { transaction -> ResolvePeerIdByNameResult in
|
||||
var peerId: PeerId? = nil
|
||||
|
||||
switch result {
|
||||
@ -52,13 +64,13 @@ func _internal_resolvePeerByName(account: Account, name: String, ageLimit: Int32
|
||||
if let entry = CodableEntry(CachedResolvedByNamePeer(peerId: peerId, timestamp: timestamp)) {
|
||||
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.resolvedByNamePeers, key: CachedResolvedByNamePeer.key(name: normalizedName)), entry: entry)
|
||||
}
|
||||
return peerId
|
||||
return .result(peerId)
|
||||
}
|
||||
|> castError(Void.self)
|
||||
}
|
||||
|> `catch` { _ -> Signal<PeerId?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> `catch` { _ -> Signal<ResolvePeerIdByNameResult, NoError> in
|
||||
return .single(.result(nil))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -114,14 +114,19 @@ public extension TelegramEngine {
|
||||
return _internal_inactiveChannelList(network: self.account.network)
|
||||
}
|
||||
|
||||
public func resolvePeerByName(name: String, ageLimit: Int32 = 2 * 60 * 60 * 24) -> Signal<EnginePeer?, NoError> {
|
||||
public func resolvePeerByName(name: String, ageLimit: Int32 = 2 * 60 * 60 * 24) -> Signal<ResolvePeerResult, NoError> {
|
||||
return _internal_resolvePeerByName(account: self.account, name: name, ageLimit: ageLimit)
|
||||
|> mapToSignal { peerId -> Signal<EnginePeer?, NoError> in
|
||||
guard let peerId = peerId else {
|
||||
return .single(nil)
|
||||
}
|
||||
return self.account.postbox.transaction { transaction -> EnginePeer? in
|
||||
return transaction.getPeer(peerId).flatMap(EnginePeer.init)
|
||||
|> mapToSignal { result -> Signal<ResolvePeerResult, NoError> in
|
||||
switch result {
|
||||
case .progress:
|
||||
return .single(.progress)
|
||||
case let .result(peerId):
|
||||
guard let peerId = peerId else {
|
||||
return .single(.result(nil))
|
||||
}
|
||||
return self.account.postbox.transaction { transaction -> ResolvePeerResult in
|
||||
return .result(transaction.getPeer(peerId).flatMap(EnginePeer.init))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1005,7 +1010,7 @@ public extension TelegramEngine {
|
||||
return _internal_createForumChannelTopic(account: self.account, peerId: id, title: title, iconColor: iconColor, iconFileId: iconFileId)
|
||||
}
|
||||
|
||||
public func fetchForumChannelTopic(id: EnginePeer.Id, threadId: Int64) -> Signal<EngineMessageHistoryThread.Info?, NoError> {
|
||||
public func fetchForumChannelTopic(id: EnginePeer.Id, threadId: Int64) -> Signal<FetchForumChannelTopicResult, NoError> {
|
||||
return _internal_fetchForumChannelTopic(account: self.account, peerId: id, threadId: threadId)
|
||||
}
|
||||
|
||||
|
@ -596,7 +596,12 @@ func _internal_searchGifs(account: Account, query: String, nextOffset: String =
|
||||
let configuration = currentSearchBotsConfiguration(transaction: transaction)
|
||||
return configuration.gifBotUsername ?? "gif"
|
||||
} |> mapToSignal {
|
||||
return _internal_resolvePeerByName(account: account, name: $0)
|
||||
return _internal_resolvePeerByName(account: account, name: $0) |> mapToSignal { result in
|
||||
guard case let .result(result) = result else {
|
||||
return .complete()
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
} |> filter { $0 != nil }
|
||||
|> map { $0! }
|
||||
|> mapToSignal { peerId -> Signal<Peer, NoError> in
|
||||
|
@ -5,13 +5,18 @@ import TelegramApi
|
||||
import MtProtoKit
|
||||
|
||||
|
||||
public func webpagePreview(account: Account, url: String, webpageId: MediaId? = nil) -> Signal<TelegramMediaWebpage?, NoError> {
|
||||
public enum WebpagePreviewResult {
|
||||
case progress
|
||||
case result(TelegramMediaWebpage?)
|
||||
}
|
||||
|
||||
public func webpagePreview(account: Account, url: String, webpageId: MediaId? = nil) -> Signal<WebpagePreviewResult, NoError> {
|
||||
return webpagePreviewWithProgress(account: account, url: url)
|
||||
|> mapToSignal { next -> Signal<TelegramMediaWebpage?, NoError> in
|
||||
|> mapToSignal { next -> Signal<WebpagePreviewResult, NoError> in
|
||||
if case let .result(result) = next {
|
||||
return .single(result)
|
||||
return .single(.result(result))
|
||||
} else {
|
||||
return .complete()
|
||||
return .single(.progress)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -382,27 +382,27 @@ public final class PrincipalThemeEssentialGraphics {
|
||||
self.chatMessageBackgroundIncomingOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true)
|
||||
self.chatMessageBackgroundIncomingExtractedOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .extracted, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true)
|
||||
self.chatMessageBackgroundIncomingShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyShadow: true)
|
||||
self.chatMessageBackgroundIncomingHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true)
|
||||
self.chatMessageBackgroundIncomingHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .white, strokeColor: .clear, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true).withRenderingMode(.alwaysTemplate)
|
||||
self.chatMessageBackgroundIncomingMergedTopMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .top(side: false), theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
|
||||
self.chatMessageBackgroundIncomingMergedTopImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true)
|
||||
self.chatMessageBackgroundIncomingMergedTopOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true)
|
||||
self.chatMessageBackgroundIncomingMergedTopShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyShadow: true)
|
||||
self.chatMessageBackgroundIncomingMergedTopHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true)
|
||||
self.chatMessageBackgroundIncomingMergedTopHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .white, strokeColor: .clear, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true).withRenderingMode(.alwaysTemplate)
|
||||
self.chatMessageBackgroundIncomingMergedTopSideMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .top(side: true), theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
|
||||
self.chatMessageBackgroundIncomingMergedTopSideImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true)
|
||||
self.chatMessageBackgroundIncomingMergedTopSideOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true)
|
||||
self.chatMessageBackgroundIncomingMergedTopSideShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyShadow: true)
|
||||
self.chatMessageBackgroundIncomingMergedTopSideHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true)
|
||||
self.chatMessageBackgroundIncomingMergedTopSideHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .white, strokeColor: .clear, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true).withRenderingMode(.alwaysTemplate)
|
||||
self.chatMessageBackgroundIncomingMergedBottomMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .bottom, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
|
||||
self.chatMessageBackgroundIncomingMergedBottomImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true)
|
||||
self.chatMessageBackgroundIncomingMergedBottomOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true)
|
||||
self.chatMessageBackgroundIncomingMergedBottomShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyShadow: true)
|
||||
self.chatMessageBackgroundIncomingMergedBottomHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true)
|
||||
self.chatMessageBackgroundIncomingMergedBottomHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .white, strokeColor: .clear, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true).withRenderingMode(.alwaysTemplate)
|
||||
self.chatMessageBackgroundIncomingMergedBothMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .both, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
|
||||
self.chatMessageBackgroundIncomingMergedBothImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true)
|
||||
self.chatMessageBackgroundIncomingMergedBothOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true)
|
||||
self.chatMessageBackgroundIncomingMergedBothShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyShadow: true)
|
||||
self.chatMessageBackgroundIncomingMergedBothHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true)
|
||||
self.chatMessageBackgroundIncomingMergedBothHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .white, strokeColor: .clear, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true).withRenderingMode(.alwaysTemplate)
|
||||
|
||||
self.chatMessageBackgroundOutgoingMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .none, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
|
||||
self.chatMessageBackgroundOutgoingExtractedMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .extracted, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
|
||||
@ -411,27 +411,27 @@ public final class PrincipalThemeEssentialGraphics {
|
||||
self.chatMessageBackgroundOutgoingOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true)
|
||||
self.chatMessageBackgroundOutgoingExtractedOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .extracted, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true)
|
||||
self.chatMessageBackgroundOutgoingShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyShadow: true)
|
||||
self.chatMessageBackgroundOutgoingHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true)
|
||||
self.chatMessageBackgroundOutgoingHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .white, strokeColor: .clear, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true).withRenderingMode(.alwaysTemplate)
|
||||
self.chatMessageBackgroundOutgoingMergedTopMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .top(side: false), theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
|
||||
self.chatMessageBackgroundOutgoingMergedTopImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true)
|
||||
self.chatMessageBackgroundOutgoingMergedTopOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true)
|
||||
self.chatMessageBackgroundOutgoingMergedTopShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyShadow: true)
|
||||
self.chatMessageBackgroundOutgoingMergedTopHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true)
|
||||
self.chatMessageBackgroundOutgoingMergedTopHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .white, strokeColor: .clear, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true).withRenderingMode(.alwaysTemplate)
|
||||
self.chatMessageBackgroundOutgoingMergedTopSideMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .top(side: true), theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
|
||||
self.chatMessageBackgroundOutgoingMergedTopSideImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true)
|
||||
self.chatMessageBackgroundOutgoingMergedTopSideOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true)
|
||||
self.chatMessageBackgroundOutgoingMergedTopSideShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyShadow: true)
|
||||
self.chatMessageBackgroundOutgoingMergedTopSideHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true)
|
||||
self.chatMessageBackgroundOutgoingMergedTopSideHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .white, strokeColor: .clear, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true).withRenderingMode(.alwaysTemplate)
|
||||
self.chatMessageBackgroundOutgoingMergedBottomMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .bottom, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
|
||||
self.chatMessageBackgroundOutgoingMergedBottomImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true)
|
||||
self.chatMessageBackgroundOutgoingMergedBottomOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true)
|
||||
self.chatMessageBackgroundOutgoingMergedBottomShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyShadow: true)
|
||||
self.chatMessageBackgroundOutgoingMergedBottomHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true)
|
||||
self.chatMessageBackgroundOutgoingMergedBottomHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .white, strokeColor: .clear, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true).withRenderingMode(.alwaysTemplate)
|
||||
self.chatMessageBackgroundOutgoingMergedBothMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .both, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
|
||||
self.chatMessageBackgroundOutgoingMergedBothImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true)
|
||||
self.chatMessageBackgroundOutgoingMergedBothOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true)
|
||||
self.chatMessageBackgroundOutgoingMergedBothShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyShadow: true)
|
||||
self.chatMessageBackgroundOutgoingMergedBothHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true)
|
||||
self.chatMessageBackgroundOutgoingMergedBothHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .white, strokeColor: .clear, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true).withRenderingMode(.alwaysTemplate)
|
||||
|
||||
self.chatMessageBackgroundIncomingMergedSideMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .side, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
|
||||
self.chatMessageBackgroundIncomingMergedSideImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill[0], strokeColor: incoming.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true)
|
||||
@ -441,8 +441,8 @@ public final class PrincipalThemeEssentialGraphics {
|
||||
self.chatMessageBackgroundOutgoingMergedSideImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true)
|
||||
self.chatMessageBackgroundOutgoingMergedSideOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true)
|
||||
self.chatMessageBackgroundOutgoingMergedSideShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill[0], strokeColor: outgoing.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyShadow: true)
|
||||
self.chatMessageBackgroundIncomingMergedSideHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true)
|
||||
self.chatMessageBackgroundOutgoingMergedSideHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true)
|
||||
self.chatMessageBackgroundIncomingMergedSideHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .white, strokeColor: .clear, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true).withRenderingMode(.alwaysTemplate)
|
||||
self.chatMessageBackgroundOutgoingMergedSideHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .white, strokeColor: .clear, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: highlightKnockout, extendedEdges: true, alwaysFillColor: true).withRenderingMode(.alwaysTemplate)
|
||||
|
||||
self.checkBubbleFullImage = generateCheckImage(partial: false, color: theme.message.outgoingCheckColor, width: 11.0)!
|
||||
self.checkBubblePartialImage = generateCheckImage(partial: true, color: theme.message.outgoingCheckColor, width: 11.0)!
|
||||
|
@ -572,6 +572,8 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
||||
private var appliedForwardInfo: (Peer?, String?)?
|
||||
private var disablesComments = true
|
||||
|
||||
private var authorNameColor: UIColor?
|
||||
|
||||
private var tapRecognizer: TapLongTapOrDoubleTapGestureRecognizer?
|
||||
|
||||
private var replyRecognizer: ChatSwipeToReplyRecognizer?
|
||||
@ -1854,6 +1856,18 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let peer = firstMessage.peers[firstMessage.id.peerId] as? TelegramChannel, case .broadcast = peer.info, item.content.firstMessage.adAttribute == nil {
|
||||
authorNameColor = (peer as Peer).nameColor?.color
|
||||
} else if let effectiveAuthor = effectiveAuthor {
|
||||
let nameColor: UIColor
|
||||
if incoming {
|
||||
nameColor = (effectiveAuthor.nameColor ?? .blue).color
|
||||
} else {
|
||||
nameColor = item.presentationData.theme.theme.chat.message.outgoing.accentTextColor
|
||||
}
|
||||
authorNameColor = nameColor
|
||||
}
|
||||
|
||||
if initialDisplayHeader && displayAuthorInfo {
|
||||
if let peer = firstMessage.peers[firstMessage.id.peerId] as? TelegramChannel, case .broadcast = peer.info, item.content.firstMessage.adAttribute == nil {
|
||||
@ -2696,6 +2710,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
||||
nameNodeSizeApply: nameNodeSizeApply,
|
||||
contentOrigin: contentOrigin,
|
||||
nameNodeOriginY: nameNodeOriginY,
|
||||
authorNameColor: authorNameColor,
|
||||
layoutConstants: layoutConstants,
|
||||
currentCredibilityIcon: currentCredibilityIcon,
|
||||
adminNodeSizeApply: adminNodeSizeApply,
|
||||
@ -2747,6 +2762,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
||||
nameNodeSizeApply: (CGSize, () -> TextNode?),
|
||||
contentOrigin: CGPoint,
|
||||
nameNodeOriginY: CGFloat,
|
||||
authorNameColor: UIColor?,
|
||||
layoutConstants: ChatMessageItemLayoutConstants,
|
||||
currentCredibilityIcon: EmojiStatusComponent.Content?,
|
||||
adminNodeSizeApply: (CGSize, () -> TextNode?),
|
||||
@ -2786,6 +2802,8 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
||||
strongSelf.updateAccessibilityData(accessibilityData)
|
||||
strongSelf.disablesComments = disablesComments
|
||||
|
||||
strongSelf.authorNameColor = authorNameColor
|
||||
|
||||
strongSelf.replyRecognizer?.allowBothDirections = !item.context.sharedContext.immediateExperimentalUISettings.unidirectionalSwipeToReply
|
||||
strongSelf.view.disablesInteractiveTransitionGestureRecognizer = !item.context.sharedContext.immediateExperimentalUISettings.unidirectionalSwipeToReply
|
||||
|
||||
@ -4499,6 +4517,26 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
||||
self.mainContextSourceNode.contentNode.insertSubnode(backgroundHighlightNode, aboveSubnode: self.backgroundNode)
|
||||
self.backgroundHighlightNode = backgroundHighlightNode
|
||||
|
||||
let hasWallpaper = item.presentationData.theme.wallpaper.hasWallpaper
|
||||
let incoming: PresentationThemeBubbleColorComponents = !hasWallpaper ? item.presentationData.theme.theme.chat.message.incoming.bubble.withoutWallpaper : item.presentationData.theme.theme.chat.message.incoming.bubble.withWallpaper
|
||||
let outgoing: PresentationThemeBubbleColorComponents = !hasWallpaper ? item.presentationData.theme.theme.chat.message.outgoing.bubble.withoutWallpaper : item.presentationData.theme.theme.chat.message.outgoing.bubble.withWallpaper
|
||||
|
||||
let highlightColor: UIColor
|
||||
if item.message.effectivelyIncoming(item.context.account.peerId) {
|
||||
if let authorNameColor = self.authorNameColor {
|
||||
highlightColor = authorNameColor.withMultipliedAlpha(0.2)
|
||||
} else {
|
||||
highlightColor = incoming.highlightedFill
|
||||
}
|
||||
} else {
|
||||
if let authorNameColor = self.authorNameColor {
|
||||
highlightColor = authorNameColor.withMultipliedAlpha(0.2)
|
||||
} else {
|
||||
highlightColor = outgoing.highlightedFill
|
||||
}
|
||||
}
|
||||
|
||||
backgroundHighlightNode.customHighlightColor = highlightColor
|
||||
backgroundHighlightNode.setType(type: backgroundType, highlighted: true, graphics: graphics, maskMode: true, hasWallpaper: false, transition: .immediate, backgroundNode: nil)
|
||||
|
||||
backgroundHighlightNode.frame = self.backgroundNode.frame
|
||||
@ -4511,17 +4549,6 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
||||
}
|
||||
|
||||
if let highlightedState = self.highlightedState, let quote = highlightedState.quote {
|
||||
let hasWallpaper = item.presentationData.theme.wallpaper.hasWallpaper
|
||||
let incoming: PresentationThemeBubbleColorComponents = !hasWallpaper ? item.presentationData.theme.theme.chat.message.incoming.bubble.withoutWallpaper : item.presentationData.theme.theme.chat.message.incoming.bubble.withWallpaper
|
||||
let outgoing: PresentationThemeBubbleColorComponents = !hasWallpaper ? item.presentationData.theme.theme.chat.message.outgoing.bubble.withoutWallpaper : item.presentationData.theme.theme.chat.message.outgoing.bubble.withWallpaper
|
||||
|
||||
let highlightColor: UIColor
|
||||
if item.message.effectivelyIncoming(item.context.account.peerId) {
|
||||
highlightColor = incoming.highlightedFill
|
||||
} else {
|
||||
highlightColor = outgoing.highlightedFill
|
||||
}
|
||||
|
||||
let transition: ContainedViewLayoutTransition = .animated(duration: 0.4, curve: .spring)
|
||||
|
||||
var quoteFrame: CGRect?
|
||||
@ -4552,7 +4579,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
||||
|
||||
backgroundHighlightNode.updateLayout(size: quoteFrame.size, transition: transition)
|
||||
transition.updateFrame(node: backgroundHighlightNode, frame: quoteFrame)
|
||||
backgroundHighlightNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.1, delay: 0.1, removeOnCompletion: false, completion: { [weak backgroundHighlightNode] _ in
|
||||
backgroundHighlightNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.1, delay: 0.05, removeOnCompletion: false, completion: { [weak backgroundHighlightNode] _ in
|
||||
backgroundHighlightNode?.removeFromSupernode()
|
||||
})
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ private let channelIcon: UIImage = {
|
||||
return generateImage(CGSize(width: sourceImage.size.width + 4.0, height: sourceImage.size.height + 4.0), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
UIGraphicsPushContext(context)
|
||||
sourceImage.draw(at: CGPoint(x: 2.0, y: 2.0))
|
||||
sourceImage.draw(at: CGPoint(x: 2.0, y: 1.0 + UIScreenPixel))
|
||||
UIGraphicsPopContext()
|
||||
})!.precomposed().withRenderingMode(.alwaysTemplate)
|
||||
}()
|
||||
@ -42,7 +42,7 @@ private let groupIcon: UIImage = {
|
||||
return generateImage(CGSize(width: sourceImage.size.width + 3.0, height: sourceImage.size.height + 4.0), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
UIGraphicsPushContext(context)
|
||||
sourceImage.draw(at: CGPoint(x: 3.0, y: 1.0))
|
||||
sourceImage.draw(at: CGPoint(x: 3.0, y: 1.0 - UIScreenPixel))
|
||||
UIGraphicsPopContext()
|
||||
})!.precomposed().withRenderingMode(.alwaysTemplate)
|
||||
}()
|
||||
@ -388,7 +388,7 @@ public class ChatMessageReplyInfoNode: ASDisplayNode {
|
||||
var text: String
|
||||
var messageEntities: [MessageTextEntity]
|
||||
|
||||
if let quote = arguments.quote {
|
||||
if let quote = arguments.quote, !quote.text.isEmpty {
|
||||
text = quote.text
|
||||
messageEntities = quote.entities
|
||||
} else {
|
||||
|
@ -956,7 +956,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
currentRect = currentRect.insetBy(dx: -quoteHighlightingNode.inset, dy: -quoteHighlightingNode.inset)
|
||||
let innerRect = currentRect.offsetBy(dx: quoteHighlightingNode.frame.minX, dy: quoteHighlightingNode.frame.minY)
|
||||
|
||||
quoteHighlightingNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.08, delay: 0.1)
|
||||
quoteHighlightingNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1, delay: 0.04)
|
||||
|
||||
let fromScale = CGPoint(x: sourceFrame.width / innerRect.width, y: sourceFrame.height / innerRect.height)
|
||||
|
||||
|
@ -54,12 +54,17 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent
|
||||
item.controllerInteraction.openTheme(item.message)
|
||||
return
|
||||
} else {
|
||||
if content.title != nil || content.text != nil {
|
||||
if content.embedUrl == nil && (content.title != nil || content.text != nil) {
|
||||
var isConcealed = true
|
||||
if item.message.text.contains(content.url) {
|
||||
isConcealed = false
|
||||
}
|
||||
item.controllerInteraction.openUrl(ChatControllerInteraction.OpenUrl(url: content.url, concealed: isConcealed))
|
||||
if let attribute = item.message.webpagePreviewAttribute {
|
||||
if attribute.isSafe {
|
||||
isConcealed = false
|
||||
}
|
||||
}
|
||||
item.controllerInteraction.openUrl(ChatControllerInteraction.OpenUrl(url: content.url, concealed: isConcealed, progress: strongSelf.contentNode.makeProgress()))
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -100,6 +105,11 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent
|
||||
if item.message.text.contains(webpage.url) {
|
||||
isConcealed = false
|
||||
}
|
||||
if let attribute = item.message.webpagePreviewAttribute {
|
||||
if attribute.isSafe {
|
||||
isConcealed = false
|
||||
}
|
||||
}
|
||||
item.controllerInteraction.openUrl(ChatControllerInteraction.OpenUrl(url: webpage.url, concealed: isConcealed, progress: strongSelf.contentNode.makeProgress()))
|
||||
}
|
||||
}
|
||||
@ -119,6 +129,11 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent
|
||||
if item.message.text.contains(content.url) {
|
||||
isConcealed = false
|
||||
}
|
||||
if let attribute = item.message.webpagePreviewAttribute {
|
||||
if attribute.isSafe {
|
||||
isConcealed = false
|
||||
}
|
||||
}
|
||||
return ChatMessageBubbleContentTapAction(content: .url(ChatMessageBubbleContentTapAction.Url(url: content.url, concealed: isConcealed, allowInlineWebpageResolution: true)), activate: { [weak self] in
|
||||
guard let self else {
|
||||
return nil
|
||||
|
@ -296,6 +296,12 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
let resolveSignal: Signal<Peer?, NoError>
|
||||
if let peerName = peerName {
|
||||
resolveSignal = strongSelf.context.engine.peers.resolvePeerByName(name: peerName)
|
||||
|> mapToSignal { result -> Signal<EnginePeer?, NoError> in
|
||||
guard case let .result(result) = result else {
|
||||
return .complete()
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
|> mapToSignal { peer -> Signal<Peer?, NoError> in
|
||||
return .single(peer?._asPeer())
|
||||
}
|
||||
@ -824,7 +830,12 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
|
||||
private func openPeerMention(_ name: String) {
|
||||
self.navigationActionDisposable.set((self.context.engine.peers.resolvePeerByName(name: name, ageLimit: 10)
|
||||
|> take(1)
|
||||
|> mapToSignal { result -> Signal<EnginePeer?, NoError> in
|
||||
guard case let .result(result) = result else {
|
||||
return .complete()
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
|> deliverOnMainQueue).startStrict(next: { [weak self] peer in
|
||||
if let strongSelf = self {
|
||||
if let peer = peer {
|
||||
|
@ -232,7 +232,7 @@ private final class LineView: UIView {
|
||||
dashBackgroundView.layer.add(animation, forKey: "progress")
|
||||
}
|
||||
} else {
|
||||
let phaseDuration: Double = 0.8
|
||||
let phaseDuration: Double = 1.0
|
||||
if self.backgroundView.layer.animation(forKey: "progress") == nil {
|
||||
let animation = self.backgroundView.layer.makeAnimation(from: 0.0 as NSNumber, to: -params.size.height as NSNumber, keyPath: "position.y", timingFunction: kCAMediaTimingFunctionSpring, duration: phaseDuration * 0.5, delay: 0.0, mediaTimingFunction: nil, removeOnCompletion: false, additive: true)
|
||||
animation.repeatCount = 1.0
|
||||
|
@ -55,6 +55,12 @@ public func paneGifSearchForQuery(context: AccountContext, query: String, offset
|
||||
|> mapToSignal { searchBots -> Signal<EnginePeer?, NoError> in
|
||||
let botName = searchBots.gifBotUsername ?? "gif"
|
||||
return context.engine.peers.resolvePeerByName(name: botName)
|
||||
|> mapToSignal { result in
|
||||
guard case let .result(result) = result else {
|
||||
return .complete()
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
}
|
||||
|> mapToSignal { peer -> Signal<(ChatPresentationInputQueryResult?, Bool, Bool), NoError> in
|
||||
if case let .user(user) = peer, let botInfo = user.botInfo, let _ = botInfo.inlinePlaceholder {
|
||||
|
@ -2772,7 +2772,12 @@ final class StoryItemSetContainerSendMessage {
|
||||
self.resolvePeerByNameDisposable.set(nil)
|
||||
}
|
||||
disposable.set((resolveSignal
|
||||
|> take(1)
|
||||
|> mapToSignal { result -> Signal<EnginePeer?, NoError> in
|
||||
guard case let .result(result) = result else {
|
||||
return .complete()
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
|> mapToSignal { peer -> Signal<Peer?, NoError> in
|
||||
return .single(peer?._asPeer())
|
||||
}
|
||||
@ -2805,6 +2810,12 @@ final class StoryItemSetContainerSendMessage {
|
||||
var resolveSignal: Signal<Peer?, NoError>
|
||||
if let peerName = peerName {
|
||||
resolveSignal = component.context.engine.peers.resolvePeerByName(name: peerName)
|
||||
|> mapToSignal { result -> Signal<EnginePeer?, NoError> in
|
||||
guard case let .result(result) = result else {
|
||||
return .complete()
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
|> mapToSignal { peer -> Signal<Peer?, NoError> in
|
||||
if let peer = peer {
|
||||
return .single(peer._asPeer())
|
||||
|
@ -487,7 +487,14 @@ final class AuthorizedApplicationContext {
|
||||
|> deliverOnMainQueue).start(completed: {
|
||||
controller?.dismiss()
|
||||
if let strongSelf = self, let botName = botName {
|
||||
strongSelf.termsOfServiceProceedToBotDisposable.set((strongSelf.context.engine.peers.resolvePeerByName(name: botName, ageLimit: 10) |> take(1) |> deliverOnMainQueue).start(next: { peer in
|
||||
strongSelf.termsOfServiceProceedToBotDisposable.set((strongSelf.context.engine.peers.resolvePeerByName(name: botName, ageLimit: 10)
|
||||
|> mapToSignal { result -> Signal<EnginePeer?, NoError> in
|
||||
guard case let .result(result) = result else {
|
||||
return .complete()
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
if let strongSelf = self, let peer = peer {
|
||||
self?.rootController.pushViewController(ChatControllerImpl(context: strongSelf.context, chatLocation: .peer(id: peer.id)))
|
||||
}
|
||||
|
@ -246,6 +246,12 @@ func makeAttachmentFileControllerImpl(context: AccountContext, updatedPresentati
|
||||
},
|
||||
send: { message in
|
||||
let _ = (context.engine.messages.getMessagesLoadIfNecessary([message.id], strategy: .cloud(skipLocal: true))
|
||||
|> mapToSignal { result -> Signal<[Message], NoError> in
|
||||
guard case let .result(result) = result else {
|
||||
return .complete()
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
|> deliverOnMainQueue).startStandalone(next: { messages in
|
||||
if let message = messages.first, let file = message.media.first(where: { $0 is TelegramMediaFile }) as? TelegramMediaFile {
|
||||
send(.message(message: MessageReference(message), media: file))
|
||||
|
@ -267,6 +267,16 @@ private func chatForwardOptions(selfController: ChatControllerImpl, sourceNode:
|
||||
f(.default)
|
||||
})))
|
||||
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Remove Forward", textColor: .destructive, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { [weak selfController] c, f in
|
||||
f(.default)
|
||||
|
||||
guard let selfController else {
|
||||
return
|
||||
}
|
||||
selfController.updateChatPresentationInterfaceState(interactive: false, { $0.updatedInterfaceState({ $0.withUpdatedForwardMessageIds(nil).withoutSelectionState() }) })
|
||||
})))
|
||||
|
||||
return items
|
||||
}
|
||||
|
||||
@ -338,8 +348,13 @@ private func generateChatReplyOptionItems(selfController: ChatControllerImpl, ch
|
||||
guard let textSelection = contentNode.getCurrentTextSelection() else {
|
||||
return
|
||||
}
|
||||
var quote: EngineMessageReplyQuote?
|
||||
let trimmedText = trimStringWithEntities(string: textSelection.text, entities: textSelection.entities, maxLength: quoteMaxLength(appConfig: selfController.context.currentAppConfiguration.with({ $0 })))
|
||||
if !trimmedText.string.isEmpty {
|
||||
quote = EngineMessageReplyQuote(text: trimmedText.string, entities: trimmedText.entities, media: nil)
|
||||
}
|
||||
|
||||
selfController.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedReplyMessageSubject(ChatInterfaceState.ReplyMessageSubject(messageId: replySubject.messageId, quote: EngineMessageReplyQuote(text: textSelection.text, entities: textSelection.entities, media: nil))).withoutSelectionState() }) })
|
||||
selfController.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedReplyMessageSubject(ChatInterfaceState.ReplyMessageSubject(messageId: replySubject.messageId, quote: quote)).withoutSelectionState() }) })
|
||||
|
||||
f(.default)
|
||||
})))
|
||||
@ -381,7 +396,13 @@ private func generateChatReplyOptionItems(selfController: ChatControllerImpl, ch
|
||||
return
|
||||
}
|
||||
|
||||
selfController.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedReplyMessageSubject(ChatInterfaceState.ReplyMessageSubject(messageId: replySubject.messageId, quote: EngineMessageReplyQuote(text: textSelection.text, entities: textSelection.entities, media: nil))).withoutSelectionState() }) })
|
||||
var quote: EngineMessageReplyQuote?
|
||||
let trimmedText = trimStringWithEntities(string: textSelection.text, entities: textSelection.entities, maxLength: quoteMaxLength(appConfig: selfController.context.currentAppConfiguration.with({ $0 })))
|
||||
if !trimmedText.string.isEmpty {
|
||||
quote = EngineMessageReplyQuote(text: trimmedText.string, entities: trimmedText.entities, media: nil)
|
||||
}
|
||||
|
||||
selfController.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedReplyMessageSubject(ChatInterfaceState.ReplyMessageSubject(messageId: replySubject.messageId, quote: quote)).withoutSelectionState() }) })
|
||||
|
||||
f(.default)
|
||||
})))
|
||||
@ -424,6 +445,17 @@ private func generateChatReplyOptionItems(selfController: ChatControllerImpl, ch
|
||||
replySubject.quote = nil
|
||||
selfController.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedReplyMessageSubject(replySubject).withoutSelectionState() }).updatedSearch(nil) })
|
||||
})))
|
||||
} else {
|
||||
items.append(.action(ContextMenuActionItem(text: "Remove Reply", textColor: .destructive, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { [weak selfController] c, f in
|
||||
f(.default)
|
||||
|
||||
guard let selfController else {
|
||||
return
|
||||
}
|
||||
var replySubject = replySubject
|
||||
replySubject.quote = nil
|
||||
selfController.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedReplyMessageSubject(nil).withoutSelectionState() }).updatedSearch(nil) })
|
||||
})))
|
||||
}
|
||||
|
||||
return items
|
||||
|
@ -3974,7 +3974,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
let nsRange = NSRange(location: range.lowerBound, length: range.upperBound - range.lowerBound)
|
||||
let quoteText = (message.text as NSString).substring(with: nsRange)
|
||||
quoteData = EngineMessageReplyQuote(text: quoteText, entities: messageTextEntitiesInRange(entities: message.textEntitiesAttribute?.entities ?? [], range: nsRange, onlyQuoteable: true), media: nil)
|
||||
|
||||
let trimmedText = trimStringWithEntities(string: quoteText, entities: messageTextEntitiesInRange(entities: message.textEntitiesAttribute?.entities ?? [], range: nsRange, onlyQuoteable: true), maxLength: quoteMaxLength(appConfig: strongSelf.context.currentAppConfiguration.with({ $0 })))
|
||||
if !trimmedText.string.isEmpty {
|
||||
quoteData = EngineMessageReplyQuote(text: trimmedText.string, entities: trimmedText.entities, media: nil)
|
||||
}
|
||||
|
||||
let replySubject = ChatInterfaceState.ReplyMessageSubject(
|
||||
messageId: message.id,
|
||||
@ -9038,7 +9042,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
disableUrlPreview = true
|
||||
} else {
|
||||
webpage = urlPreview.webPage
|
||||
webpagePreviewAttribute = WebpagePreviewMessageAttribute(leadingPreview: !urlPreview.positionBelowText, forceLargeMedia: urlPreview.largeMedia, isManuallyAdded: true)
|
||||
webpagePreviewAttribute = WebpagePreviewMessageAttribute(leadingPreview: !urlPreview.positionBelowText, forceLargeMedia: urlPreview.largeMedia, isManuallyAdded: true, isSafe: false)
|
||||
}
|
||||
}
|
||||
|
||||
@ -9103,7 +9107,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
if let strongSelf = self {
|
||||
if let currentMessage = currentMessage {
|
||||
let currentEntities = currentMessage.textEntitiesAttribute?.entities ?? []
|
||||
let currentWebpagePreviewAttribute = currentMessage.webpagePreviewAttribute ?? WebpagePreviewMessageAttribute(leadingPreview: false, forceLargeMedia: nil, isManuallyAdded: true)
|
||||
let currentWebpagePreviewAttribute = currentMessage.webpagePreviewAttribute ?? WebpagePreviewMessageAttribute(leadingPreview: false, forceLargeMedia: nil, isManuallyAdded: true, isSafe: false)
|
||||
|
||||
if currentMessage.text != text.string || currentEntities != entities || updatingMedia || webpagePreviewAttribute != currentWebpagePreviewAttribute || disableUrlPreview {
|
||||
strongSelf.context.account.pendingUpdateMessageManager.add(messageId: editMessage.messageId, text: text.string, media: media, entities: entitiesAttribute, inlineStickers: inlineStickers, webpagePreviewAttribute: webpagePreviewAttribute, disableUrlPreview: disableUrlPreview)
|
||||
@ -16803,6 +16807,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
let _ = (combineLatest(
|
||||
self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: messageId.peerId)),
|
||||
self.context.engine.messages.getMessagesLoadIfNecessary([messageId], strategy: .local)
|
||||
|> mapToSignal { result -> Signal<[Message], NoError> in
|
||||
guard case let .result(result) = result else {
|
||||
return .complete()
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
)
|
||||
|> deliverOnMainQueue).startStandalone(next: { [weak self] peer, messages in
|
||||
guard let self, let peer = peer else {
|
||||
@ -17566,6 +17576,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
self.resolvePeerByNameDisposable = disposable
|
||||
}
|
||||
var resolveSignal = self.context.engine.peers.resolvePeerByName(name: name, ageLimit: 10)
|
||||
|> mapToSignal { result -> Signal<EnginePeer?, NoError> in
|
||||
guard case let .result(result) = result else {
|
||||
return .complete()
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
|
||||
var cancelImpl: (() -> Void)?
|
||||
let presentationData = self.presentationData
|
||||
@ -17627,6 +17643,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
var resolveSignal: Signal<Peer?, NoError>
|
||||
if let peerName = peerName {
|
||||
resolveSignal = self.context.engine.peers.resolvePeerByName(name: peerName)
|
||||
|> mapToSignal { result -> Signal<EnginePeer?, NoError> in
|
||||
guard case let .result(result) = result else {
|
||||
return .complete()
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
|> mapToSignal { peer -> Signal<Peer?, NoError> in
|
||||
if let peer = peer {
|
||||
return .single(peer._asPeer())
|
||||
|
@ -426,8 +426,11 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
return false
|
||||
}
|
||||
if let attribute = attribute as? ReplyMessageAttribute {
|
||||
if !forwardedMessageIds.contains(attribute.messageId) || hideNames {
|
||||
return false
|
||||
if attribute.quote != nil {
|
||||
} else {
|
||||
if !forwardedMessageIds.contains(attribute.messageId) || hideNames {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
if attribute is ReplyMarkupMessageAttribute {
|
||||
@ -535,7 +538,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
|
||||
attributes.append(TextEntitiesMessageAttribute(entities: options.messageEntities))
|
||||
|
||||
attributes.append(WebpagePreviewMessageAttribute(leadingPreview: !options.linkBelowText, forceLargeMedia: options.largeMedia, isManuallyAdded: true))
|
||||
attributes.append(WebpagePreviewMessageAttribute(leadingPreview: !options.linkBelowText, forceLargeMedia: options.largeMedia, isManuallyAdded: true, isSafe: false))
|
||||
|
||||
if let replyMessage {
|
||||
associatedMessages[replyMessage.id] = replyMessage
|
||||
@ -3436,7 +3439,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
attributes.append(OutgoingContentInfoMessageAttribute(flags: [.disableLinkPreviews]))
|
||||
} else {
|
||||
webpage = urlPreview.webPage
|
||||
attributes.append(WebpagePreviewMessageAttribute(leadingPreview: !urlPreview.positionBelowText, forceLargeMedia: urlPreview.largeMedia, isManuallyAdded: true))
|
||||
attributes.append(WebpagePreviewMessageAttribute(leadingPreview: !urlPreview.positionBelowText, forceLargeMedia: urlPreview.largeMedia, isManuallyAdded: true, isSafe: false))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -240,6 +240,12 @@ private func updatedContextQueryResultStateForQuery(context: AccountContext, pee
|
||||
|
||||
let chatPeer = peer
|
||||
let contextBot = context.engine.peers.resolvePeerByName(name: addressName)
|
||||
|> mapToSignal { result -> Signal<EnginePeer?, NoError> in
|
||||
guard case let .result(result) = result else {
|
||||
return .complete()
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
|> castError(ChatContextQueryError.self)
|
||||
|> mapToSignal { peer -> Signal<(ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?, ChatContextQueryError> in
|
||||
if case let .user(user) = peer, let botInfo = user.botInfo, let _ = botInfo.inlinePlaceholder {
|
||||
@ -512,7 +518,14 @@ func urlPreviewStateForInputText(_ inputText: NSAttributedString?, context: Acco
|
||||
let detectedUrl = detectUrls(inputText).first
|
||||
if detectedUrl != currentQuery {
|
||||
if let detectedUrl = detectedUrl {
|
||||
return (detectedUrl, webpagePreview(account: context.account, url: detectedUrl) |> map { value in
|
||||
return (detectedUrl, webpagePreview(account: context.account, url: detectedUrl)
|
||||
|> mapToSignal { result -> Signal<TelegramMediaWebpage?, NoError> in
|
||||
guard case let .result(result) = result else {
|
||||
return .complete()
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
|> map { value in
|
||||
return { _ in return value }
|
||||
})
|
||||
} else {
|
||||
|
@ -318,6 +318,12 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
|
||||
if let to = to {
|
||||
if to.hasPrefix("@") {
|
||||
let _ = (context.engine.peers.resolvePeerByName(name: String(to[to.index(to.startIndex, offsetBy: 1)...]))
|
||||
|> mapToSignal { result -> Signal<EnginePeer?, NoError> in
|
||||
guard case let .result(result) = result else {
|
||||
return .complete()
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
|> deliverOnMainQueue).startStandalone(next: { peer in
|
||||
if let peer = peer {
|
||||
context.sharedContext.applicationBindings.dismissNativeController()
|
||||
|
@ -4599,6 +4599,12 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
self.resolvePeerByNameDisposable = disposable
|
||||
}
|
||||
var resolveSignal = self.context.engine.peers.resolvePeerByName(name: name, ageLimit: 10)
|
||||
|> mapToSignal { result -> Signal<EnginePeer?, NoError> in
|
||||
guard case let .result(result) = result else {
|
||||
return .complete()
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
|
||||
var cancelImpl: (() -> Void)?
|
||||
let presentationData = self.presentationData
|
||||
@ -4655,6 +4661,12 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
var resolveSignal: Signal<Peer?, NoError>
|
||||
if let peerName = peerName {
|
||||
resolveSignal = self.context.engine.peers.resolvePeerByName(name: peerName)
|
||||
|> mapToSignal { result -> Signal<EnginePeer?, NoError> in
|
||||
guard case let .result(result) = result else {
|
||||
return .complete()
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
|> mapToSignal { peer -> Signal<Peer?, NoError> in
|
||||
return .single(peer?._asPeer())
|
||||
}
|
||||
@ -8674,7 +8686,14 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
|
||||
let context = self.context
|
||||
let navigationController = self.controller?.navigationController as? NavigationController
|
||||
self.tipsPeerDisposable.set((self.context.engine.peers.resolvePeerByName(name: self.presentationData.strings.Settings_TipsUsername) |> deliverOnMainQueue).startStrict(next: { [weak controller] peer in
|
||||
self.tipsPeerDisposable.set((self.context.engine.peers.resolvePeerByName(name: self.presentationData.strings.Settings_TipsUsername)
|
||||
|> mapToSignal { result -> Signal<EnginePeer?, NoError> in
|
||||
guard case let .result(result) = result else {
|
||||
return .complete()
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
|> deliverOnMainQueue).startStrict(next: { [weak controller] peer in
|
||||
controller?.dismiss()
|
||||
if let peer = peer, let navigationController = navigationController {
|
||||
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peer)))
|
||||
|
@ -1402,6 +1402,18 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
|
||||
public func resolveUrl(context: AccountContext, peerId: PeerId?, url: String, skipUrlAuth: Bool) -> Signal<ResolvedUrl, NoError> {
|
||||
return resolveUrlImpl(context: context, peerId: peerId, url: url, skipUrlAuth: skipUrlAuth)
|
||||
|> mapToSignal { result -> Signal<ResolvedUrl, NoError> in
|
||||
switch result {
|
||||
case .progress:
|
||||
return .complete()
|
||||
case let .result(value):
|
||||
return .single(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func resolveUrlWithProgress(context: AccountContext, peerId: PeerId?, url: String, skipUrlAuth: Bool) -> Signal<ResolveUrlResult, NoError> {
|
||||
return resolveUrlImpl(context: context, peerId: peerId, url: url, skipUrlAuth: skipUrlAuth)
|
||||
}
|
||||
|
||||
public func openResolvedUrl(_ resolvedUrl: ResolvedUrl, context: AccountContext, urlContext: OpenURLContext, navigationController: NavigationController?, forceExternal: Bool, openPeer: @escaping (EnginePeer, ChatControllerInteractionNavigateToPeer) -> Void, sendFile: ((FileMediaReference) -> Void)?, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?, requestMessageActionUrlAuth: ((MessageActionUrlSubject) -> Void)?, joinVoiceChat: ((PeerId, String?, CachedChannelData.ActiveCall) -> Void)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, contentContext: Any?) {
|
||||
|
@ -104,7 +104,14 @@ func handleTextLinkActionImpl(context: AccountContext, peerId: EnginePeer.Id?, n
|
||||
}
|
||||
|
||||
let openPeerMentionImpl: (String) -> Void = { mention in
|
||||
navigateDisposable.set((context.engine.peers.resolvePeerByName(name: mention, ageLimit: 10) |> take(1) |> deliverOnMainQueue).start(next: { peer in
|
||||
navigateDisposable.set((context.engine.peers.resolvePeerByName(name: mention, ageLimit: 10)
|
||||
|> mapToSignal { result -> Signal<EnginePeer?, NoError> in
|
||||
guard case let .result(result) = result else {
|
||||
return .complete()
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
openResolvedPeerImpl(peer, .default)
|
||||
}))
|
||||
}
|
||||
|
@ -591,227 +591,277 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? {
|
||||
return nil
|
||||
}
|
||||
|
||||
private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl) -> Signal<ResolvedUrl?, NoError> {
|
||||
private enum ResolveInternalUrlResult {
|
||||
case progress
|
||||
case result(ResolvedUrl?)
|
||||
}
|
||||
|
||||
private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl) -> Signal<ResolveInternalUrlResult, NoError> {
|
||||
switch url {
|
||||
case let .phone(phone, attach, startAttach):
|
||||
return context.engine.peers.resolvePeerByPhone(phone: phone)
|
||||
|> take(1)
|
||||
|> mapToSignal { peer -> Signal<ResolvedUrl?, NoError> in
|
||||
|> mapToSignal { peer -> Signal<ResolveInternalUrlResult, NoError> in
|
||||
if let peer = peer?._asPeer() {
|
||||
if let attach = attach {
|
||||
return context.engine.peers.resolvePeerByName(name: attach)
|
||||
|> take(1)
|
||||
|> map { botPeer -> ResolvedUrl? in
|
||||
if let botPeer = botPeer?._asPeer() {
|
||||
return .peer(peer, .withAttachBot(ChatControllerInitialAttachBotStart(botId: botPeer.id, payload: startAttach, justInstalled: false)))
|
||||
} else {
|
||||
return .peer(peer, .chat(textInputState: nil, subject: nil, peekData: nil))
|
||||
|> map { result -> ResolveInternalUrlResult in
|
||||
switch result {
|
||||
case .progress:
|
||||
return .progress
|
||||
case let .result(botPeer):
|
||||
if let botPeer = botPeer?._asPeer() {
|
||||
return .result(.peer(peer, .withAttachBot(ChatControllerInitialAttachBotStart(botId: botPeer.id, payload: startAttach, justInstalled: false))))
|
||||
} else {
|
||||
return .result(.peer(peer, .chat(textInputState: nil, subject: nil, peekData: nil)))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return .single(.peer(peer, .chat(textInputState: nil, subject: nil, peekData: nil)))
|
||||
return .single(.result(.peer(peer, .chat(textInputState: nil, subject: nil, peekData: nil))))
|
||||
}
|
||||
} else {
|
||||
return .single(.peer(nil, .info))
|
||||
return .single(.result(.peer(nil, .info)))
|
||||
}
|
||||
}
|
||||
case let .peer(reference, parameter):
|
||||
let resolvedPeer: Signal<Peer?, NoError>
|
||||
let resolvedPeer: Signal<ResolvePeerResult, NoError>
|
||||
switch reference {
|
||||
case let .name(name):
|
||||
resolvedPeer = context.engine.peers.resolvePeerByName(name: name)
|
||||
|> take(1)
|
||||
|> mapToSignal { peer -> Signal<Peer?, NoError> in
|
||||
return .single(peer?._asPeer())
|
||||
|> mapToSignal { result -> Signal<ResolvePeerResult, NoError> in
|
||||
switch result {
|
||||
case .progress:
|
||||
return .single(.progress)
|
||||
case let .result(peer):
|
||||
return .single(.result(peer))
|
||||
}
|
||||
}
|
||||
case let .id(id):
|
||||
if id.namespace == Namespaces.Peer.CloudChannel {
|
||||
resolvedPeer = context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: id))
|
||||
|> mapToSignal { peer -> Signal<Peer?, NoError> in
|
||||
let foundPeer: Signal<Peer?, NoError>
|
||||
|> mapToSignal { peer -> Signal<ResolvePeerResult, NoError> in
|
||||
let foundPeer: Signal<ResolvePeerResult, NoError>
|
||||
if let peer = peer {
|
||||
foundPeer = .single(peer._asPeer())
|
||||
foundPeer = .single(.result(peer))
|
||||
} else {
|
||||
foundPeer = TelegramEngine(account: context.account).peers.findChannelById(channelId: id.id._internalGetInt64Value())
|
||||
|> map { peer -> Peer? in
|
||||
return peer?._asPeer()
|
||||
}
|
||||
foundPeer = .single(.progress) |> then(context.engine.peers.findChannelById(channelId: id.id._internalGetInt64Value())
|
||||
|> map { peer -> ResolvePeerResult in
|
||||
return .result(peer)
|
||||
})
|
||||
}
|
||||
return foundPeer
|
||||
}
|
||||
} else {
|
||||
resolvedPeer = .single(nil)
|
||||
resolvedPeer = .single(.result(nil))
|
||||
}
|
||||
}
|
||||
|
||||
return resolvedPeer
|
||||
|> mapToSignal { peer -> Signal<ResolvedUrl?, NoError> in
|
||||
|> mapToSignal { result -> Signal<ResolveInternalUrlResult, NoError> in
|
||||
guard case let .result(peer) = result else {
|
||||
return .single(.progress)
|
||||
}
|
||||
|
||||
if let peer = peer {
|
||||
if let parameter = parameter {
|
||||
switch parameter {
|
||||
case let .botStart(payload):
|
||||
return .single(.botStart(peer: peer, payload: payload))
|
||||
return .single(.result(.botStart(peer: peer._asPeer(), payload: payload)))
|
||||
case let .groupBotStart(payload, adminRights):
|
||||
return .single(.groupBotStart(peerId: peer.id, payload: payload, adminRights: adminRights))
|
||||
return .single(.result(.groupBotStart(peerId: peer.id, payload: payload, adminRights: adminRights)))
|
||||
case let .gameStart(game):
|
||||
return .single(.gameStart(peerId: peer.id, game: game))
|
||||
return .single(.result(.gameStart(peerId: peer.id, game: game)))
|
||||
case let .attachBotStart(name, payload):
|
||||
return context.engine.peers.resolvePeerByName(name: name)
|
||||
|> take(1)
|
||||
|> mapToSignal { botPeer -> Signal<Peer?, NoError> in
|
||||
return .single(botPeer?._asPeer())
|
||||
}
|
||||
|> mapToSignal { botPeer -> Signal<ResolvedUrl?, NoError> in
|
||||
if let botPeer = botPeer {
|
||||
return .single(.peer(peer, .withAttachBot(ChatControllerInitialAttachBotStart(botId: botPeer.id, payload: payload, justInstalled: false))))
|
||||
} else {
|
||||
return .single(.peer(peer, .chat(textInputState: nil, subject: nil, peekData: nil)))
|
||||
|> mapToSignal { botPeerResult -> Signal<ResolveInternalUrlResult, NoError> in
|
||||
switch botPeerResult {
|
||||
case .progress:
|
||||
return .single(.progress)
|
||||
case let .result(botPeer):
|
||||
if let botPeer = botPeer {
|
||||
return .single(.result(.peer(peer._asPeer(), .withAttachBot(ChatControllerInitialAttachBotStart(botId: botPeer.id, payload: payload, justInstalled: false)))))
|
||||
} else {
|
||||
return .single(.result(.peer(peer._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil))))
|
||||
}
|
||||
}
|
||||
}
|
||||
case let .appStart(name, payload):
|
||||
return context.engine.messages.getBotApp(botId: peer.id, shortName: name, cached: false)
|
||||
return .single(.progress) |> then(context.engine.messages.getBotApp(botId: peer.id, shortName: name, cached: false)
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<BotApp?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> take(1)
|
||||
|> mapToSignal { botApp -> Signal<ResolvedUrl?, NoError> in
|
||||
|> mapToSignal { botApp -> Signal<ResolveInternalUrlResult, NoError> in
|
||||
if let botApp {
|
||||
return .single(.peer(peer, .withBotApp(ChatControllerInitialBotAppStart(botApp: botApp, payload: payload, justInstalled: false))))
|
||||
return .single(.result(.peer(peer._asPeer(), .withBotApp(ChatControllerInitialBotAppStart(botApp: botApp, payload: payload, justInstalled: false)))))
|
||||
} else {
|
||||
return .single(.peer(peer, .chat(textInputState: nil, subject: nil, peekData: nil)))
|
||||
return .single(.result(.peer(peer._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil))))
|
||||
}
|
||||
}
|
||||
})
|
||||
case let .channelMessage(id, timecode):
|
||||
if let channel = peer as? TelegramChannel, channel.flags.contains(.isForum) {
|
||||
if case let .channel(channel) = peer, channel.flags.contains(.isForum) {
|
||||
let messageId = MessageId(peerId: channel.id, namespace: Namespaces.Message.Cloud, id: id)
|
||||
return context.engine.messages.getMessagesLoadIfNecessary([messageId], strategy: .cloud(skipLocal: false))
|
||||
|> take(1)
|
||||
|> mapToSignal { messages -> Signal<ResolvedUrl?, NoError> in
|
||||
if let threadId = messages.first?.threadId {
|
||||
return context.engine.peers.fetchForumChannelTopic(id: channel.id, threadId: threadId)
|
||||
|> map { info -> ResolvedUrl? in
|
||||
if let _ = info {
|
||||
return .replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage(messageId: MessageId(peerId: channel.id, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false), messageId: messageId)
|
||||
} else {
|
||||
return .peer(peer, .chat(textInputState: nil, subject: nil, peekData: nil))
|
||||
|> mapToSignal { result -> Signal<ResolveInternalUrlResult, NoError> in
|
||||
switch result {
|
||||
case .progress:
|
||||
return .single(.progress)
|
||||
case let .result(messages):
|
||||
if let threadId = messages.first?.threadId {
|
||||
return context.engine.peers.fetchForumChannelTopic(id: channel.id, threadId: threadId)
|
||||
|> map { result -> ResolveInternalUrlResult in
|
||||
switch result {
|
||||
case .progress:
|
||||
return .progress
|
||||
case let .result(info):
|
||||
if let _ = info {
|
||||
return .result(.replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage(messageId: MessageId(peerId: channel.id, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false), messageId: messageId))
|
||||
} else {
|
||||
return .result(.peer(peer._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil)))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return .single(.result(.peer(peer._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil))))
|
||||
}
|
||||
} else {
|
||||
return .single(.peer(peer, .chat(textInputState: nil, subject: nil, peekData: nil)))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return .single(.channelMessage(peer: peer, messageId: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: id), timecode: timecode))
|
||||
return .single(.result(.channelMessage(peer: peer._asPeer(), messageId: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: id), timecode: timecode)))
|
||||
}
|
||||
case let .replyThread(id, replyId):
|
||||
let replyThreadMessageId = MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: id)
|
||||
|
||||
if let channel = peer as? TelegramChannel, channel.flags.contains(.isForum) {
|
||||
if case let .channel(channel) = peer, channel.flags.contains(.isForum) {
|
||||
return context.engine.peers.fetchForumChannelTopic(id: channel.id, threadId: Int64(replyThreadMessageId.id))
|
||||
|> map { info -> ResolvedUrl? in
|
||||
if let _ = info {
|
||||
return .replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage(messageId: MessageId(peerId: channel.id, namespace: Namespaces.Message.Cloud, id: Int32(clamping: replyThreadMessageId.id)), channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false), messageId: MessageId(peerId: channel.id, namespace: Namespaces.Message.Cloud, id: replyId))
|
||||
} else {
|
||||
return .peer(peer, .chat(textInputState: nil, subject: nil, peekData: nil))
|
||||
|> map { result -> ResolveInternalUrlResult in
|
||||
switch result {
|
||||
case .progress:
|
||||
return .progress
|
||||
case let .result(info):
|
||||
if let _ = info {
|
||||
return .result(.replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage(messageId: MessageId(peerId: channel.id, namespace: Namespaces.Message.Cloud, id: Int32(clamping: replyThreadMessageId.id)), channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false), messageId: MessageId(peerId: channel.id, namespace: Namespaces.Message.Cloud, id: replyId)))
|
||||
} else {
|
||||
return .result(.peer(peer._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil)))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return context.engine.messages.fetchChannelReplyThreadMessage(messageId: replyThreadMessageId, atMessageId: nil)
|
||||
return .single(.progress) |> then(context.engine.messages.fetchChannelReplyThreadMessage(messageId: replyThreadMessageId, atMessageId: nil)
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<ChatReplyThreadMessage?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> map { result -> ResolvedUrl? in
|
||||
|> map { result -> ResolveInternalUrlResult in
|
||||
guard let result = result else {
|
||||
return .channelMessage(peer: peer, messageId: replyThreadMessageId, timecode: nil)
|
||||
return .result(.channelMessage(peer: peer._asPeer(), messageId: replyThreadMessageId, timecode: nil))
|
||||
}
|
||||
return .replyThreadMessage(replyThreadMessage: result, messageId: MessageId(peerId: result.messageId.peerId, namespace: Namespaces.Message.Cloud, id: replyId))
|
||||
}
|
||||
return .result(.replyThreadMessage(replyThreadMessage: result, messageId: MessageId(peerId: result.messageId.peerId, namespace: Namespaces.Message.Cloud, id: replyId)))
|
||||
})
|
||||
}
|
||||
case let .voiceChat(invite):
|
||||
return .single(.joinVoiceChat(peer.id, invite))
|
||||
return .single(.result(.joinVoiceChat(peer.id, invite)))
|
||||
case let .story(id):
|
||||
return context.engine.messages.refreshStories(peerId: peer.id, ids: [id])
|
||||
|> map { _ -> ResolvedUrl? in
|
||||
return .single(.progress) |> then(context.engine.messages.refreshStories(peerId: peer.id, ids: [id])
|
||||
|> map { _ -> ResolveInternalUrlResult in
|
||||
}
|
||||
|> then(.single(.story(peerId: peer.id, id: id)))
|
||||
|> then(.single(.result(.story(peerId: peer.id, id: id)))))
|
||||
case .boost:
|
||||
return combineLatest(
|
||||
return .single(.progress) |> then(combineLatest(
|
||||
context.engine.peers.getChannelBoostStatus(peerId: peer.id),
|
||||
context.engine.peers.getMyBoostStatus()
|
||||
)
|
||||
|> map { boostStatus, myBoostStatus -> ResolvedUrl? in
|
||||
return .boost(peerId: peer.id, status: boostStatus, myBoostStatus: myBoostStatus)
|
||||
}
|
||||
|> map { boostStatus, myBoostStatus -> ResolveInternalUrlResult in
|
||||
return .result(.boost(peerId: peer.id, status: boostStatus, myBoostStatus: myBoostStatus))
|
||||
})
|
||||
}
|
||||
} else {
|
||||
return .single(.peer(peer, .chat(textInputState: nil, subject: nil, peekData: nil)))
|
||||
return .single(.result(.peer(peer._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil))))
|
||||
}
|
||||
} else {
|
||||
return .single(.peer(nil, .info))
|
||||
return .single(.result(.peer(nil, .info)))
|
||||
}
|
||||
}
|
||||
case let .peerId(peerId):
|
||||
return context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> mapToSignal { peer -> Signal<ResolvedUrl?, NoError> in
|
||||
|> mapToSignal { peer -> Signal<ResolveInternalUrlResult, NoError> in
|
||||
if let peer = peer {
|
||||
return .single(.peer(peer._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil)))
|
||||
return .single(.result(.peer(peer._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil))))
|
||||
} else {
|
||||
return .single(.inaccessiblePeer)
|
||||
return .single(.result(.inaccessiblePeer))
|
||||
}
|
||||
}
|
||||
case let .contactToken(token):
|
||||
return context.engine.peers.importContactToken(token: token)
|
||||
|> mapToSignal { peer -> Signal<ResolvedUrl?, NoError> in
|
||||
return .single(.progress) |> then(context.engine.peers.importContactToken(token: token)
|
||||
|> mapToSignal { peer -> Signal<ResolveInternalUrlResult, NoError> in
|
||||
if let peer = peer {
|
||||
return .single(.peer(peer._asPeer(), .info))
|
||||
return .single(.result(.peer(peer._asPeer(), .info)))
|
||||
} else {
|
||||
return .single(.peer(nil, .info))
|
||||
return .single(.result(.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<ResolvedUrl?, NoError> in
|
||||
|> mapToSignal { peer -> Signal<ResolveInternalUrlResult, NoError> in
|
||||
let foundPeer: Signal<EnginePeer?, NoError>
|
||||
if let peer = peer {
|
||||
foundPeer = .single(peer)
|
||||
} else {
|
||||
foundPeer = TelegramEngine(account: context.account).peers.findChannelById(channelId: messageId.peerId.id._internalGetInt64Value())
|
||||
foundPeer = context.engine.peers.findChannelById(channelId: messageId.peerId.id._internalGetInt64Value())
|
||||
}
|
||||
return foundPeer
|
||||
|> mapToSignal { foundPeer -> Signal<ResolvedUrl?, NoError> in
|
||||
return .single(.progress) |> then(foundPeer
|
||||
|> mapToSignal { foundPeer -> Signal<ResolveInternalUrlResult, NoError> in
|
||||
if let foundPeer = foundPeer {
|
||||
if case let .channel(channel) = foundPeer, channel.flags.contains(.isForum) {
|
||||
if let threadId = threadId {
|
||||
return context.engine.peers.fetchForumChannelTopic(id: channel.id, threadId: Int64(threadId))
|
||||
|> map { info -> ResolvedUrl? in
|
||||
if let _ = info {
|
||||
return .replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage(messageId: MessageId(peerId: channel.id, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false), messageId: messageId)
|
||||
} else {
|
||||
return .peer(peer?._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil))
|
||||
|> map { result -> ResolveInternalUrlResult in
|
||||
switch result {
|
||||
case .progress:
|
||||
return .progress
|
||||
case let .result(info):
|
||||
if let _ = info {
|
||||
return .result(.replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage(messageId: MessageId(peerId: channel.id, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false), messageId: messageId))
|
||||
} else {
|
||||
return .result(.peer(peer?._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil)))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return context.engine.messages.getMessagesLoadIfNecessary([messageId], strategy: .cloud(skipLocal: false))
|
||||
|> take(1)
|
||||
|> mapToSignal { messages -> Signal<ResolvedUrl?, NoError> in
|
||||
if let threadId = messages.first?.threadId {
|
||||
return context.engine.peers.fetchForumChannelTopic(id: channel.id, threadId: threadId)
|
||||
|> map { info -> ResolvedUrl? in
|
||||
if let _ = info {
|
||||
return .replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage(messageId: MessageId(peerId: channel.id, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false), messageId: messageId)
|
||||
} else {
|
||||
return .peer(peer?._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil))
|
||||
|> mapToSignal { result -> Signal<ResolveInternalUrlResult, NoError> in
|
||||
switch result {
|
||||
case .progress:
|
||||
return .single(.progress)
|
||||
case let .result(messages):
|
||||
if let threadId = messages.first?.threadId {
|
||||
return context.engine.peers.fetchForumChannelTopic(id: channel.id, threadId: threadId)
|
||||
|> map { result -> ResolveInternalUrlResult in
|
||||
switch result {
|
||||
case .progress:
|
||||
return .progress
|
||||
case let .result(info):
|
||||
if let _ = info {
|
||||
return .result(.replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage(messageId: MessageId(peerId: channel.id, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false), messageId: messageId))
|
||||
} else {
|
||||
return .result(.peer(peer?._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return context.engine.peers.fetchForumChannelTopic(id: channel.id, threadId: Int64(messageId.id))
|
||||
|> map { info -> ResolvedUrl? in
|
||||
if let _ = info {
|
||||
return .replyThread(messageId: messageId)
|
||||
} else {
|
||||
return .peer(foundPeer._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil))
|
||||
} else {
|
||||
return context.engine.peers.fetchForumChannelTopic(id: channel.id, threadId: Int64(messageId.id))
|
||||
|> map { result -> ResolveInternalUrlResult in
|
||||
switch result {
|
||||
case .progress:
|
||||
return .progress
|
||||
case let .result(info):
|
||||
if let _ = info {
|
||||
return .result(.replyThread(messageId: messageId))
|
||||
} else {
|
||||
return .result(.peer(foundPeer._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -819,60 +869,67 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl)
|
||||
}
|
||||
} else if let threadId = threadId {
|
||||
let replyThreadMessageId = MessageId(peerId: foundPeer.id, namespace: Namespaces.Message.Cloud, id: threadId)
|
||||
return context.engine.messages.fetchChannelReplyThreadMessage(messageId: replyThreadMessageId, atMessageId: nil)
|
||||
return .single(.progress) |> then(context.engine.messages.fetchChannelReplyThreadMessage(messageId: replyThreadMessageId, atMessageId: nil)
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<ChatReplyThreadMessage?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> map { result -> ResolvedUrl? in
|
||||
|> map { result -> ResolveInternalUrlResult in
|
||||
guard let result = result else {
|
||||
return .channelMessage(peer: foundPeer._asPeer(), messageId: replyThreadMessageId, timecode: timecode)
|
||||
return .result(.channelMessage(peer: foundPeer._asPeer(), messageId: replyThreadMessageId, timecode: timecode))
|
||||
}
|
||||
return .replyThreadMessage(replyThreadMessage: result, messageId: messageId)
|
||||
}
|
||||
return .result(.replyThreadMessage(replyThreadMessage: result, messageId: messageId))
|
||||
})
|
||||
} else {
|
||||
return .single(.peer(foundPeer._asPeer(), .chat(textInputState: nil, subject: .message(id: .id(messageId), highlight: ChatControllerSubject.MessageHighlight(quote: nil), timecode: timecode), peekData: nil)))
|
||||
return .single(.result(.peer(foundPeer._asPeer(), .chat(textInputState: nil, subject: .message(id: .id(messageId), highlight: ChatControllerSubject.MessageHighlight(quote: nil), timecode: timecode), peekData: nil))))
|
||||
}
|
||||
} else {
|
||||
return .single(.inaccessiblePeer)
|
||||
return .single(.result(.inaccessiblePeer))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
case let .stickerPack(name, type):
|
||||
return .single(.stickerPack(name: name, type: type))
|
||||
return .single(.result(.stickerPack(name: name, type: type)))
|
||||
case let .chatFolder(slug):
|
||||
return .single(.chatFolder(slug: slug))
|
||||
return .single(.result(.chatFolder(slug: slug)))
|
||||
case let .invoice(slug):
|
||||
return context.engine.payments.fetchBotPaymentInvoice(source: .slug(slug))
|
||||
return .single(.progress) |> then(context.engine.payments.fetchBotPaymentInvoice(source: .slug(slug))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<TelegramMediaInvoice?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> map { invoice -> ResolvedUrl? in
|
||||
|> map { invoice -> ResolveInternalUrlResult in
|
||||
guard let invoice = invoice else {
|
||||
return .invoice(slug: slug, invoice: nil)
|
||||
return .result(.invoice(slug: slug, invoice: nil))
|
||||
}
|
||||
return .invoice(slug: slug, invoice: invoice)
|
||||
}
|
||||
return .result(.invoice(slug: slug, invoice: invoice))
|
||||
})
|
||||
case let .join(link):
|
||||
return .single(.join(link))
|
||||
return .single(.result(.join(link)))
|
||||
case let .localization(identifier):
|
||||
return .single(.localization(identifier))
|
||||
return .single(.result(.localization(identifier)))
|
||||
case let .proxy(host, port, username, password, secret):
|
||||
return .single(.proxy(host: host, port: port, username: username, password: password, secret: secret))
|
||||
return .single(.result(.proxy(host: host, port: port, username: username, password: password, secret: secret)))
|
||||
case let .internalInstantView(url):
|
||||
return resolveInstantViewUrl(account: context.account, url: url)
|
||||
|> map(Optional.init)
|
||||
|> map { result in
|
||||
switch result {
|
||||
case .progress:
|
||||
return .progress
|
||||
case let .result(result):
|
||||
return .result(result)
|
||||
}
|
||||
}
|
||||
case let .confirmationCode(code):
|
||||
return .single(.confirmationCode(code))
|
||||
return .single(.result(.confirmationCode(code)))
|
||||
case let .cancelAccountReset(phone, hash):
|
||||
return .single(.cancelAccountReset(phone: phone, hash: hash))
|
||||
return .single(.result(.cancelAccountReset(phone: phone, hash: hash)))
|
||||
case let .share(url, text, to):
|
||||
return .single(.share(url: url, text: text, to: to))
|
||||
return .single(.result(.share(url: url, text: text, to: to)))
|
||||
case let .wallpaper(parameter):
|
||||
return .single(.wallpaper(parameter))
|
||||
return .single(.result(.wallpaper(parameter)))
|
||||
case let .theme(slug):
|
||||
return .single(.theme(slug))
|
||||
return .single(.result(.theme(slug)))
|
||||
case let .startAttach(name, payload, chooseValue):
|
||||
var choose: ResolvedBotChoosePeerTypes = []
|
||||
if let chooseValue = chooseValue?.lowercased() {
|
||||
@ -891,19 +948,20 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl)
|
||||
}
|
||||
}
|
||||
return context.engine.peers.resolvePeerByName(name: name)
|
||||
|> take(1)
|
||||
|> mapToSignal { peer -> Signal<Peer?, NoError> in
|
||||
return .single(peer?._asPeer())
|
||||
}
|
||||
|> mapToSignal { peer -> Signal<ResolvedUrl?, NoError> in
|
||||
if let peer = peer {
|
||||
return .single(.startAttach(peerId: peer.id, payload: payload, choose: !choose.isEmpty ? choose : nil))
|
||||
} else {
|
||||
return .single(.inaccessiblePeer)
|
||||
|> mapToSignal { result -> Signal<ResolveInternalUrlResult, NoError> in
|
||||
switch result {
|
||||
case .progress:
|
||||
return .single(.progress)
|
||||
case let .result(peer):
|
||||
if let peer = peer {
|
||||
return .single(.result(.startAttach(peerId: peer.id, payload: payload, choose: !choose.isEmpty ? choose : nil)))
|
||||
} else {
|
||||
return .single(.result(.inaccessiblePeer))
|
||||
}
|
||||
}
|
||||
}
|
||||
case let .premiumGiftCode(slug):
|
||||
return .single(.premiumGiftCode(slug: slug))
|
||||
return .single(.result(.premiumGiftCode(slug: slug)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1020,13 +1078,13 @@ private struct UrlHandlingConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
public func resolveUrlImpl(context: AccountContext, peerId: PeerId?, url: String, skipUrlAuth: Bool) -> Signal<ResolvedUrl, NoError> {
|
||||
public func resolveUrlImpl(context: AccountContext, peerId: PeerId?, url: String, skipUrlAuth: Bool) -> Signal<ResolveUrlResult, NoError> {
|
||||
let schemes = ["http://", "https://", ""]
|
||||
|
||||
return ApplicationSpecificNotice.getSecretChatLinkPreviews(accountManager: context.sharedContext.accountManager)
|
||||
|> mapToSignal { linkPreviews -> Signal<ResolvedUrl, NoError> in
|
||||
|> mapToSignal { linkPreviews -> Signal<ResolveUrlResult, NoError> in
|
||||
return context.engine.data.get(TelegramEngine.EngineData.Item.Configuration.App(), TelegramEngine.EngineData.Item.Configuration.Links())
|
||||
|> mapToSignal { appConfiguration, linksConfiguration -> Signal<ResolvedUrl, NoError> in
|
||||
|> mapToSignal { appConfiguration, linksConfiguration -> Signal<ResolveUrlResult, NoError> in
|
||||
let urlHandlingConfiguration = UrlHandlingConfiguration.with(appConfiguration: appConfiguration)
|
||||
|
||||
var skipUrlAuth = skipUrlAuth
|
||||
@ -1052,7 +1110,7 @@ public func resolveUrlImpl(context: AccountContext, peerId: PeerId?, url: String
|
||||
components.queryItems = queryItems
|
||||
url = components.url?.absoluteString ?? url
|
||||
} else if !skipUrlAuth && urlHandlingConfiguration.urlAuthDomains.contains(host) {
|
||||
return .single(.urlAuth(url))
|
||||
return .single(.result(.urlAuth(url)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1067,15 +1125,20 @@ public func resolveUrlImpl(context: AccountContext, peerId: PeerId?, url: String
|
||||
if url.lowercased().hasPrefix(basePrefix) {
|
||||
if let internalUrl = parseInternalUrl(query: String(url[basePrefix.endIndex...])) {
|
||||
return resolveInternalUrl(context: context, url: internalUrl)
|
||||
|> map { resolved -> ResolvedUrl in
|
||||
if let resolved = resolved {
|
||||
return resolved
|
||||
} else {
|
||||
return .externalUrl(url)
|
||||
|> map { result -> ResolveUrlResult in
|
||||
switch result {
|
||||
case .progress:
|
||||
return .progress
|
||||
case let .result(resolved):
|
||||
if let resolved = resolved {
|
||||
return .result(resolved)
|
||||
} else {
|
||||
return .result(.externalUrl(url))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return .single(.externalUrl(url))
|
||||
return .single(.result(.externalUrl(url)))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1088,33 +1151,38 @@ public func resolveUrlImpl(context: AccountContext, peerId: PeerId?, url: String
|
||||
}
|
||||
}
|
||||
}
|
||||
return .single(.externalUrl(url))
|
||||
return .single(.result(.externalUrl(url)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func resolveInstantViewUrl(account: Account, url: String) -> Signal<ResolvedUrl, NoError> {
|
||||
public func resolveInstantViewUrl(account: Account, url: String) -> Signal<ResolveUrlResult, NoError> {
|
||||
return webpagePreview(account: account, url: url)
|
||||
|> mapToSignal { webpage -> Signal<ResolvedUrl, NoError> in
|
||||
if let webpage = webpage {
|
||||
if case let .Loaded(content) = webpage.content {
|
||||
if content.instantPage != nil {
|
||||
var anchorValue: String?
|
||||
if let anchorRange = url.range(of: "#") {
|
||||
let anchor = url[anchorRange.upperBound...]
|
||||
if !anchor.isEmpty {
|
||||
anchorValue = String(anchor)
|
||||
|> mapToSignal { result -> Signal<ResolveUrlResult, NoError> in
|
||||
switch result {
|
||||
case .progress:
|
||||
return .single(.progress)
|
||||
case let .result(webpage):
|
||||
if let webpage = webpage {
|
||||
if case let .Loaded(content) = webpage.content {
|
||||
if content.instantPage != nil {
|
||||
var anchorValue: String?
|
||||
if let anchorRange = url.range(of: "#") {
|
||||
let anchor = url[anchorRange.upperBound...]
|
||||
if !anchor.isEmpty {
|
||||
anchorValue = String(anchor)
|
||||
}
|
||||
}
|
||||
return .single(.result(.instantView(webpage, anchorValue)))
|
||||
} else {
|
||||
return .single(.result(.externalUrl(url)))
|
||||
}
|
||||
return .single(.instantView(webpage, anchorValue))
|
||||
} else {
|
||||
return .single(.externalUrl(url))
|
||||
return .complete()
|
||||
}
|
||||
} else {
|
||||
return .complete()
|
||||
return .single(.result(.externalUrl(url)))
|
||||
}
|
||||
} else {
|
||||
return .single(.externalUrl(url))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -742,9 +742,15 @@ final class WatchLocationHandler: WatchRequestHandler {
|
||||
|> mapToSignal({ context -> Signal<[ChatContextResultMessage], NoError> in
|
||||
if let context = context {
|
||||
return context.engine.peers.resolvePeerByName(name: "foursquare")
|
||||
|> mapToSignal { result -> Signal<EnginePeer?, NoError> in
|
||||
guard case let .result(result) = result else {
|
||||
return .complete()
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
|> take(1)
|
||||
|> mapToSignal { peer -> Signal<ChatContextResultCollection?, NoError> in
|
||||
guard let peer = peer else {
|
||||
guard let peer = peer?._asPeer() else {
|
||||
return .single(nil)
|
||||
}
|
||||
return context.engine.messages.requestChatContextResults(botId: peer.id, peerId: context.account.peerId, query: "", location: .single((args.coordinate.latitude, args.coordinate.longitude)), offset: "")
|
||||
|
@ -490,8 +490,11 @@ public final class WebSearchController: ViewController {
|
||||
|
||||
let context = self.context
|
||||
let contextBot = self.context.engine.peers.resolvePeerByName(name: name)
|
||||
|> mapToSignal { peer -> Signal<EnginePeer?, NoError> in
|
||||
return .single(peer)
|
||||
|> mapToSignal { result -> Signal<EnginePeer?, NoError> in
|
||||
guard case let .result(result) = result else {
|
||||
return .complete()
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
|> mapToSignal { peer -> Signal<(ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?, NoError> in
|
||||
if case let .user(user) = peer, let botInfo = user.botInfo, let _ = botInfo.inlinePlaceholder {
|
||||
|
@ -812,6 +812,12 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
self.webView?.lastTouchTimestamp = nil
|
||||
if tryInstantView {
|
||||
let _ = (resolveInstantViewUrl(account: self.context.account, url: url)
|
||||
|> mapToSignal { result -> Signal<ResolvedUrl, NoError> in
|
||||
guard case let .result(result) = result else {
|
||||
return .complete()
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { [weak self] result in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
|
Loading…
x
Reference in New Issue
Block a user