diff --git a/TelegramUI.xcodeproj/project.pbxproj b/TelegramUI.xcodeproj/project.pbxproj index e1b4d79603..94c08ea208 100644 --- a/TelegramUI.xcodeproj/project.pbxproj +++ b/TelegramUI.xcodeproj/project.pbxproj @@ -35,6 +35,7 @@ 0958FBB9218AD6AF00E0CBD8 /* InstantPageFeedbackItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0958FBB8218AD6AF00E0CBD8 /* InstantPageFeedbackItem.swift */; }; 0958FBBB218AD6BC00E0CBD8 /* InstantPageFeedbackNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0958FBBA218AD6BC00E0CBD8 /* InstantPageFeedbackNode.swift */; }; 0958FBBD218B03CA00E0CBD8 /* InstantPageDetailsNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0958FBBC218B03CA00E0CBD8 /* InstantPageDetailsNode.swift */; }; + 09619B8E21A34C0100493558 /* InstantPageScrollableNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09619B8D21A34C0100493558 /* InstantPageScrollableNode.swift */; }; 096C98BA21787A5C00C211FF /* LegacyBridgeAudio.swift in Sources */ = {isa = PBXBuildFile; fileRef = 096C98B921787A5C00C211FF /* LegacyBridgeAudio.swift */; }; 096C98BF21787C6700C211FF /* TGBridgeAudioEncoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 096C98BB21787C6600C211FF /* TGBridgeAudioEncoder.m */; }; 096C98C021787C6700C211FF /* TGBridgeAudioEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 096C98BC21787C6600C211FF /* TGBridgeAudioEncoder.h */; }; @@ -1091,6 +1092,7 @@ 0958FBB8218AD6AF00E0CBD8 /* InstantPageFeedbackItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstantPageFeedbackItem.swift; sourceTree = ""; }; 0958FBBA218AD6BC00E0CBD8 /* InstantPageFeedbackNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstantPageFeedbackNode.swift; sourceTree = ""; }; 0958FBBC218B03CA00E0CBD8 /* InstantPageDetailsNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstantPageDetailsNode.swift; sourceTree = ""; }; + 09619B8D21A34C0100493558 /* InstantPageScrollableNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstantPageScrollableNode.swift; sourceTree = ""; }; 096C98B921787A5C00C211FF /* LegacyBridgeAudio.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyBridgeAudio.swift; sourceTree = ""; }; 096C98BB21787C6600C211FF /* TGBridgeAudioEncoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGBridgeAudioEncoder.m; sourceTree = ""; }; 096C98BC21787C6600C211FF /* TGBridgeAudioEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGBridgeAudioEncoder.h; sourceTree = ""; }; @@ -3097,6 +3099,7 @@ 0958FBBC218B03CA00E0CBD8 /* InstantPageDetailsNode.swift */, 0958FBB8218AD6AF00E0CBD8 /* InstantPageFeedbackItem.swift */, 0958FBBA218AD6BC00E0CBD8 /* InstantPageFeedbackNode.swift */, + 09619B8D21A34C0100493558 /* InstantPageScrollableNode.swift */, ); name = "Instant Page"; sourceTree = ""; @@ -5090,6 +5093,7 @@ D0EC6D231EB9F58800EBF1C3 /* StickerResources.swift in Sources */, 09C9EA3821A044B500E90146 /* StringForDuration.swift in Sources */, D0EC6D241EB9F58800EBF1C3 /* CachedResourceRepresentations.swift in Sources */, + 09619B8E21A34C0100493558 /* InstantPageScrollableNode.swift in Sources */, D01BAA201ECC9A2500295217 /* CallListNodeLocation.swift in Sources */, D0EC6D251EB9F58800EBF1C3 /* FetchCachedRepresentations.swift in Sources */, D0EC6D261EB9F58800EBF1C3 /* TransformOutgoingMessageMedia.swift in Sources */, diff --git a/TelegramUI/ChatController.swift b/TelegramUI/ChatController.swift index a3ac21116e..cd2ee54334 100644 --- a/TelegramUI/ChatController.swift +++ b/TelegramUI/ChatController.swift @@ -4863,7 +4863,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, UID if let applicationContext = self.account.applicationContext as? TelegramApplicationContext { let actionSheet = OpenInActionSheetController(postbox: self.account.postbox, applicationContext: applicationContext, theme: self.presentationData.theme, strings: self.presentationData.strings, item: .url(url: url), openUrl: { [weak self] url in if let strongSelf = self, let applicationContext = strongSelf.account.applicationContext as? TelegramApplicationContext, let navigationController = strongSelf.navigationController as? NavigationController { - openExternalUrl(account: strongSelf.account, url: url, presentationData: strongSelf.presentationData, applicationContext: applicationContext, navigationController: navigationController, dismissInput: { + openExternalUrl(account: strongSelf.account, url: url, forceExternal: true, presentationData: strongSelf.presentationData, applicationContext: applicationContext, navigationController: navigationController, dismissInput: { self?.chatDisplayNode.dismissInput() }) } diff --git a/TelegramUI/ChatControllerNode.swift b/TelegramUI/ChatControllerNode.swift index 6932df0283..c98caf12d9 100644 --- a/TelegramUI/ChatControllerNode.swift +++ b/TelegramUI/ChatControllerNode.swift @@ -705,7 +705,6 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { transition.updateBounds(node: self.historyNode, bounds: CGRect(origin: CGPoint(), size: contentBounds.size)) transition.updatePosition(node: self.historyNode, position: CGPoint(x: contentBounds.size.width / 2.0, y: contentBounds.size.height / 2.0)) - self.loadingNode.updateLayout(size: contentBounds.size, insets: insets, transition: transition) transition.updateFrame(node: self.loadingNode, frame: contentBounds) if let restrictedNode = self.restrictedNode { @@ -855,6 +854,8 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { } } + self.loadingNode.updateLayout(size: contentBounds.size, insets: UIEdgeInsetsMake(containerInsets.top, 0.0, containerInsets.bottom + contentBottomInset, 0.0), transition: transition) + if let containerNode = self.containerNode { contentBottomInset += 8.0 let containerNodeFrame = CGRect(origin: CGPoint(x: wrappingInsets.left, y: wrappingInsets.top), size: CGSize(width: contentBounds.size.width, height: contentBounds.size.height - containerInsets.bottom - inputPanelsHeight - 8.0)) diff --git a/TelegramUI/ChatItemGalleryFooterContentNode.swift b/TelegramUI/ChatItemGalleryFooterContentNode.swift index b16277f5c1..7b608bca5a 100644 --- a/TelegramUI/ChatItemGalleryFooterContentNode.swift +++ b/TelegramUI/ChatItemGalleryFooterContentNode.swift @@ -344,11 +344,11 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode { } else { self.dateNode.attributedText = nil } - - //self.deleteButton.isHidden = !canDelete - + self.requestLayout?(.immediate) } + + self.deleteButton.isHidden = origin == nil } func setMessage(_ message: Message) { @@ -695,7 +695,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode { if let strongSelf = self { let openInController = OpenInActionSheetController(postbox: strongSelf.account.postbox, applicationContext: strongSelf.account.telegramApplicationContext, theme: presentationData.theme, strings: presentationData.strings, item: item, additionalAction: nil, openUrl: { [weak self] url in if let strongSelf = self, let applicationContext = strongSelf.account.applicationContext as? TelegramApplicationContext { - openExternalUrl(account: strongSelf.account, url: url, presentationData: presentationData, applicationContext: applicationContext, navigationController: nil, dismissInput: {}) + openExternalUrl(account: strongSelf.account, url: url, forceExternal: true, presentationData: presentationData, applicationContext: applicationContext, navigationController: nil, dismissInput: {}) } }) strongSelf.controllerInteraction?.presentController(openInController, nil) diff --git a/TelegramUI/ImageTransparency.swift b/TelegramUI/ImageTransparency.swift index 8fd2cab2f3..216f920630 100644 --- a/TelegramUI/ImageTransparency.swift +++ b/TelegramUI/ImageTransparency.swift @@ -1,5 +1,7 @@ import UIKit import Accelerate +import Display +import TelegramCore private func generateHistogram(cgImage: CGImage) -> ([[vImagePixelCount]], Int)? { var sourceBuffer = vImage_Buffer() @@ -58,22 +60,57 @@ func imageHasTransparency(_ cgImage: CGImage) -> Bool { return false } -func imageIsMonochrome(_ cgImage: CGImage) -> Bool { +private func scaledContext(_ cgImage: CGImage, maxSize: CGSize) -> DrawingContext { + var size = CGSize(width: cgImage.width, height: cgImage.height) + if (size.width > maxSize.width && size.height > maxSize.height) { + size = size.aspectFilled(maxSize) + } + let context = DrawingContext(size: size, scale: 1.0, clear: true) + context.withFlippedContext { context in + context.draw(cgImage, in: CGRect(origin: CGPoint(), size: size)) + } + return context +} + +func imageRequiresInversion(_ cgImage: CGImage) -> Bool { guard cgImage.bitsPerComponent == 8, cgImage.bitsPerPixel == 32 else { return false } - if let (histogramBins, alphaBinIndex) = generateHistogram(cgImage: cgImage) { - + guard [.first, .last, .premultipliedFirst, .premultipliedLast].contains(cgImage.alphaInfo) else { + return false } -// SSE, bias = 0, [0,0,0] -// if adjust_color_bias: -// bias = ImageStat.Stat(thumb).mean[:3] -// bias = [b - sum(bias)/3 for b in bias ] -// for pixel in thumb.getdata(): -// mu = sum(pixel)/3 -// SSE += sum((pixel[i] - mu - bias[i])*(pixel[i] - mu - bias[i]) for i in [0,1,2]) - - + let context = scaledContext(cgImage, maxSize: CGSize(width: 128.0, height: 128.0)) + if let cgImage = context.generateImage()?.cgImage, let (histogramBins, alphaBinIndex) = generateHistogram(cgImage: cgImage) { + var hasAlpha = false + for i in 0 ..< 255 { + if histogramBins[alphaBinIndex][i] > 0 { + hasAlpha = true + } + } + guard hasAlpha else { + return false + } + + var matching: Int = 0 + var total: Int = 0 + for y in 0 ..< Int(context.size.height) { + for x in 0 ..< Int(context.size.width) { + var hue: CGFloat = 0.0 + var saturation: CGFloat = 0.0 + var brightness: CGFloat = 0.0 + var alpha: CGFloat = 0.0 + context.colorAt(CGPoint(x: x, y: y)).getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha) + + if alpha > 0.0 { + total += 1 + if saturation < 0.1 && brightness < 0.25 { + matching += 1 + } + } + } + } + return CGFloat(matching) / CGFloat(total) > 0.85 + } return false } diff --git a/TelegramUI/InstantPageControllerNode.swift b/TelegramUI/InstantPageControllerNode.swift index 4ef0fca5a5..8f22140be9 100644 --- a/TelegramUI/InstantPageControllerNode.swift +++ b/TelegramUI/InstantPageControllerNode.swift @@ -54,6 +54,9 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { private let resolveUrlDisposable = MetaDisposable() private let loadWebpageDisposable = MetaDisposable() + private let loadProgress = ValuePromise(1.0, ignoreRepeated: true) + private let loadProgressDisposable = MetaDisposable() + private let updateLayoutDisposable = MetaDisposable() private var themeReferenceDate: Date? @@ -118,12 +121,18 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { strongSelf.scrollNode.view.setContentOffset(CGPoint(x: 0.0, y: -strongSelf.scrollNode.view.contentInset.top), animated: true) } } + + self.loadProgressDisposable.set((self.loadProgress.get() + |> deliverOnMainQueue).start(next: { [weak self] value in + self?.navigationBar.setLoadProgress(value) + })) } deinit { self.hiddenMediaDisposable.dispose() self.resolveUrlDisposable.dispose() self.loadWebpageDisposable.dispose() + self.loadProgressDisposable.dispose() } func update(settings: InstantPagePresentationSettings, strings: PresentationStrings) { @@ -404,6 +413,8 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { var embedIndex = -1 var detailsIndex = -1 + var previousDetailsNode: InstantPageDetailsNode? + for item in self.currentLayoutItemsWithNodes { itemIndex += 1 if item is InstantPageWebEmbedItem { @@ -462,9 +473,6 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { }, currentExpandedDetails: self.currentExpandedDetails) { newNode.frame = itemFrame newNode.updateLayout(size: itemFrame.size, transition: transition) -// if case let .animated(duration, _) = transition { -// newNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration) -// } if let topNode = topNode { self.scrollNode.insertSubnode(newNode, aboveSubnode: topNode) } else { @@ -480,6 +488,13 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { strongSelf.updateVisibleItems(visibleBounds: strongSelf.scrollNode.view.bounds, animated: true) } } + + if let previousDetailsNode = previousDetailsNode { + if itemNode.frame.minY - previousDetailsNode.frame.maxY < 1.0 { + itemNode.previousNode = previousDetailsNode + } + } + previousDetailsNode = itemNode } } } else { @@ -676,8 +691,8 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { let itemFrame = effectiveFrameForItem(item) if itemFrame.contains(location) { var contentOffset = CGPoint() - if let item = item as? InstantPageTableItem { - contentOffset = tableContentOffset(item: item) + if let item = item as? InstantPageScrollableItem { + contentOffset = scrollableContentOffset(item: item) } var itemRects = item.linkSelectionRects(at: location.offsetBy(dx: -itemFrame.minX + contentOffset.x, dy: -itemFrame.minY)) @@ -713,10 +728,10 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { } } - private func tableContentOffset(item: InstantPageTableItem) -> CGPoint { + private func scrollableContentOffset(item: InstantPageScrollableItem) -> CGPoint { var contentOffset = CGPoint() for (_, itemNode) in self.visibleItemsWithNodes { - if let itemNode = itemNode as? InstantPageTableNode, itemNode.item === item { + if let itemNode = itemNode as? InstantPageScrollableNode, itemNode.item === item { contentOffset = itemNode.contentOffset break } @@ -778,12 +793,12 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { private func textItemAtLocation(_ location: CGPoint) -> (InstantPageTextItem, CGPoint)? { if let currentLayout = self.currentLayout { for item in currentLayout.items { - let itemFrame = self.effectiveFrameForItem(item) + let itemFrame = self.effectiveFrameForItem(item).insetBy(dx: -2.0, dy: -2.0) if itemFrame.contains(location) { if let item = item as? InstantPageTextItem, item.selectable { return (item, CGPoint(x: itemFrame.minX - item.frame.minX, y: itemFrame.minY - item.frame.minY)) - } else if let item = item as? InstantPageTableItem { - let contentOffset = tableContentOffset(item: item) + } else if let item = item as? InstantPageScrollableItem { + let contentOffset = scrollableContentOffset(item: item) if let (textItem, parentOffset) = item.textItemAtLocation(location.offsetBy(dx: -itemFrame.minX + contentOffset.x, dy: -itemFrame.minY)) { return (textItem, itemFrame.origin.offsetBy(dx: parentOffset.x - contentOffset.x, dy: parentOffset.y)) } @@ -951,35 +966,12 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { return } - var cancelImpl: (() -> Void)? - let progressSignal = Signal { [weak self] subscriber in - guard let strongSelf = self else { - return EmptyDisposable - } - - let controller = OverlayStatusController(theme: strongSelf.presentationTheme, strings: strongSelf.strings, type: .loading(cancelled: { - cancelImpl?() - })) - strongSelf.present(controller, nil) - return ActionDisposable { [weak controller] in - Queue.mainQueue().async() { - controller?.dismiss() - } - } - } - |> runOn(Queue.mainQueue()) - |> delay(0.15, queue: Queue.mainQueue()) - let progressDisposable = progressSignal.start() - + self.loadProgress.set(0.02) let resolveSignal = resolveUrl(account: self.account, url: url.url) - |> afterDisposed { - Queue.mainQueue().async { - progressDisposable.dispose() - } - } - cancelImpl = { [weak self] in - self?.resolveUrlDisposable.set(nil) + |> afterCompleted { [weak self] in + self?.loadProgress.set(0.07) } + self.resolveUrlDisposable.set((resolveSignal |> deliverOnMainQueue).start(next: { [weak self] result in if let strongSelf = self { switch result { @@ -989,9 +981,19 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { if let anchorRange = externalUrl.range(of: "#") { anchor = String(externalUrl[anchorRange.upperBound...]) } - strongSelf.loadWebpageDisposable.set((webpagePreview(account: strongSelf.account, url: externalUrl, webpageId: webpageId) |> deliverOnMainQueue).start(next: { webpage in - if let strongSelf = self, let webpage = webpage { - strongSelf.pushController(InstantPageController(account: strongSelf.account, webPage: webpage, anchor: anchor)) + strongSelf.loadWebpageDisposable.set((webpagePreviewWithProgress(account: strongSelf.account, url: externalUrl, webpageId: webpageId) + |> deliverOnMainQueue).start(next: { result in + if let strongSelf = self { + switch result { + case let .result(webpage): + if let webpage = webpage { + strongSelf.loadProgress.set(1.0) + strongSelf.pushController(InstantPageController(account: strongSelf.account, webPage: webpage, anchor: anchor)) + } + break + case let .progress(progress): + strongSelf.loadProgress.set(CGFloat(0.07 + progress * (1.0 - 0.07))) + } } })) } else { @@ -1072,14 +1074,18 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { return } - var medias: [InstantPageMedia] = mediasFromItems(items) - medias = medias.filter { - $0.media is TelegramMediaImage - } - var entries: [InstantPageGalleryEntry] = [] - for media in medias { - entries.append(InstantPageGalleryEntry(index: Int32(media.index), pageId: webPage.webpageId, media: media, caption: media.caption, credit: media.credit, location: InstantPageGalleryEntryLocation(position: Int32(entries.count), totalCount: Int32(medias.count)))) + if media.media is TelegramMediaWebpage { + entries.append(InstantPageGalleryEntry(index: 0, pageId: webPage.webpageId, media: media, caption: nil, credit: nil, location: InstantPageGalleryEntryLocation(position: 0, totalCount: 1))) + } else { + var medias: [InstantPageMedia] = mediasFromItems(items) + medias = medias.filter { + $0.media is TelegramMediaImage + } + + for media in medias { + entries.append(InstantPageGalleryEntry(index: Int32(media.index), pageId: webPage.webpageId, media: media, caption: media.caption, credit: media.credit, location: InstantPageGalleryEntryLocation(position: Int32(entries.count), totalCount: Int32(medias.count)))) + } } var centralIndex: Int? diff --git a/TelegramUI/InstantPageDetailsNode.swift b/TelegramUI/InstantPageDetailsNode.swift index 04b21c3ec4..013ec181a6 100644 --- a/TelegramUI/InstantPageDetailsNode.swift +++ b/TelegramUI/InstantPageDetailsNode.swift @@ -320,10 +320,10 @@ final class InstantPageDetailsContentNode : ASDisplayNode { } } - private func tableContentOffset(item: InstantPageTableItem) -> CGPoint { + private func scrollableContentOffset(item: InstantPageScrollableItem) -> CGPoint { var contentOffset = CGPoint() for (_, itemNode) in self.visibleItemsWithNodes { - if let itemNode = itemNode as? InstantPageTableNode, itemNode.item === item { + if let itemNode = itemNode as? InstantPageScrollableNode, itemNode.item === item { contentOffset = itemNode.contentOffset break } @@ -388,8 +388,8 @@ final class InstantPageDetailsContentNode : ASDisplayNode { if itemFrame.contains(location) { if let item = item as? InstantPageTextItem, item.selectable { return (item, CGPoint(x: itemFrame.minX - item.frame.minX, y: itemFrame.minY - item.frame.minY)) - } else if let item = item as? InstantPageTableItem { - let contentOffset = tableContentOffset(item: item) + } else if let item = item as? InstantPageScrollableItem { + let contentOffset = scrollableContentOffset(item: item) if let (textItem, parentOffset) = item.textItemAtLocation(location.offsetBy(dx: -itemFrame.minX + contentOffset.x, dy: -itemFrame.minY)) { return (textItem, itemFrame.origin.offsetBy(dx: parentOffset.x - contentOffset.x, dy: parentOffset.y)) } @@ -446,12 +446,14 @@ final class InstantPageDetailsNode: ASDisplayNode, InstantPageNode { private let highlightedBackgroundNode: ASDisplayNode private let buttonNode: HighlightableButtonNode private let arrowNode: InstantPageDetailsArrowNode - private let separatorNode: ASDisplayNode + let separatorNode: ASDisplayNode let contentNode: InstantPageDetailsContentNode private let updateExpanded: (Bool) -> Void var expanded: Bool + var previousNode: InstantPageDetailsNode? + var requestLayoutUpdate: (() -> Void)? init(account: Account, strings: PresentationStrings, theme: InstantPageTheme, item: InstantPageDetailsItem, openMedia: @escaping (InstantPageMedia) -> Void, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, currentlyExpanded: Bool?, updateDetailsExpanded: @escaping (Bool) -> Void) { @@ -503,15 +505,18 @@ final class InstantPageDetailsNode: ASDisplayNode, InstantPageNode { if highlighted { strongSelf.highlightedBackgroundNode.layer.removeAnimation(forKey: "opacity") strongSelf.highlightedBackgroundNode.alpha = 1.0 - if strongSelf.separatorNode.frame.minY < strongSelf.highlightedBackgroundNode.frame.maxY { - strongSelf.separatorNode.alpha = 0.0 + strongSelf.separatorNode.alpha = 0.0 + if let previousSeparator = strongSelf.previousNode?.separatorNode { + previousSeparator.alpha = 0.0 } } else { strongSelf.highlightedBackgroundNode.alpha = 0.0 strongSelf.highlightedBackgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2) - if strongSelf.separatorNode.alpha < 1.0 { - strongSelf.separatorNode.alpha = 1.0 - strongSelf.separatorNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + strongSelf.separatorNode.alpha = 1.0 + strongSelf.separatorNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + if let previousSeparator = strongSelf.previousNode?.separatorNode { + previousSeparator.alpha = 1.0 + previousSeparator.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) } } } diff --git a/TelegramUI/InstantPageGalleryController.swift b/TelegramUI/InstantPageGalleryController.swift index 41b0a9713d..2a59f0b9d9 100644 --- a/TelegramUI/InstantPageGalleryController.swift +++ b/TelegramUI/InstantPageGalleryController.swift @@ -64,7 +64,6 @@ struct InstantPageGalleryEntry: Equatable { styleStack.push(.linkColor(UIColor(rgb: 0x5ac8fa))) styleStack.push(.linkMarkerColor(UIColor(rgb: 0x5ac8fa, alpha: 0.2))) styleStack.push(.fontSerif(false)) - //styleStack.push(.lineSpacingFactor(1.0)) credit = attributedStringForRichText(mediaCredit, styleStack: styleStack) } else { credit = NSAttributedString(string: "") @@ -75,6 +74,12 @@ struct InstantPageGalleryEntry: Equatable { return InstantImageGalleryItem(account: account, presentationData: presentationData, imageReference: .webPage(webPage: WebpageReference(webPage), media: image), caption: caption, credit: credit, location: self.location, openUrl: openUrl, openUrlOptions: openUrlOptions) } else if let file = self.media.media as? TelegramMediaFile, file.isVideo { return UniversalVideoGalleryItem(account: account, presentationData: presentationData, content: NativeVideoContent(id: .instantPage(self.pageId, file.fileId), fileReference: .webPage(webPage: WebpageReference(webPage), media: file)), originData: nil, indexData: GalleryItemIndexData(position: self.location.position, totalCount: self.location.totalCount), contentInfo: .webPage(webPage, file), caption: caption, credit: credit, openUrl: { _ in }, openUrlOptions: { _ in }) + } else if let embedWebpage = self.media.media as? TelegramMediaWebpage, case let .Loaded(webpageContent) = embedWebpage.content { + if let content = WebEmbedVideoContent(webPage: embedWebpage, webpageContent: webpageContent) { + return UniversalVideoGalleryItem(account: account, presentationData: presentationData, content: content, originData: nil, indexData: nil, contentInfo: nil, caption: NSAttributedString(string: ""), openUrl: { _ in }, openUrlOptions: { _ in }) + } else { + preconditionFailure() + } } else { preconditionFailure() } diff --git a/TelegramUI/InstantPageImageNode.swift b/TelegramUI/InstantPageImageNode.swift index 538ce47114..c5258734cc 100644 --- a/TelegramUI/InstantPageImageNode.swift +++ b/TelegramUI/InstantPageImageNode.swift @@ -17,6 +17,7 @@ final class InstantPageImageNode: ASDisplayNode, InstantPageNode { private let openMedia: (InstantPageMedia) -> Void private let imageNode: TransformImageNode + private let statusNode: RadialStatusNode private let linkIconNode: ASImageNode private let pinNode: ChatMessageLiveLocationPositionNode @@ -38,6 +39,7 @@ final class InstantPageImageNode: ASDisplayNode, InstantPageNode { self.openMedia = openMedia self.imageNode = TransformImageNode() + self.statusNode = RadialStatusNode(backgroundNodeColor: UIColor(white: 0.0, alpha: 0.6)) self.linkIconNode = ASImageNode() self.pinNode = ChatMessageLiveLocationPositionNode() @@ -80,6 +82,8 @@ final class InstantPageImageNode: ASDisplayNode, InstantPageNode { let imageReference = ImageMediaReference.webPage(webPage: WebpageReference(webPage), media: image) self.imageNode.setSignal(chatMessagePhoto(postbox: account.postbox, photoReference: imageReference)) self.fetchedDisposable.set(chatMessagePhotoInteractiveFetched(account: account, photoReference: imageReference, storeToDownloadsPeerType: nil).start()) + self.statusNode.transitionToState(.play(.white), animated: false, completion: {}) + self.addSubnode(statusNode) } } @@ -92,6 +96,8 @@ final class InstantPageImageNode: ASDisplayNode, InstantPageNode { if self.interactive { self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))) + } else { + self.view.isUserInteractionEnabled = false } } @@ -102,7 +108,7 @@ final class InstantPageImageNode: ASDisplayNode, InstantPageNode { } func update(strings: PresentationStrings, theme: InstantPageTheme) { - if self.theme.imageEmptyColor != theme.imageEmptyColor { + if self.theme.imageTintColor != theme.imageTintColor { self.theme = theme self.themeUpdated = true self.setNeedsLayout() @@ -130,7 +136,7 @@ final class InstantPageImageNode: ASDisplayNode, InstantPageNode { self.linkIconNode.frame = CGRect(x: size.width - 38.0, y: 14.0, width: 24.0, height: 24.0) } else if let file = self.media.media as? TelegramMediaFile, let dimensions = file.dimensions { - let emptyColor = file.mimeType.hasPrefix("image/") ? self.theme.imageEmptyColor : nil + let emptyColor = file.mimeType.hasPrefix("image/") ? self.theme.imageTintColor : nil let imageSize = dimensions.aspectFilled(size) let boundingSize = size @@ -155,6 +161,16 @@ final class InstantPageImageNode: ASDisplayNode, InstantPageNode { let (pinSize, pinApply) = makePinLayout(self.account, theme, nil, false) self.pinNode.frame = CGRect(origin: CGPoint(x: floor((size.width - pinSize.width) / 2.0), y: floor(size.height * 0.5 - 10.0 - pinSize.height / 2.0)), size: pinSize) pinApply() + } else if let webPage = media.media as? TelegramMediaWebpage, case let .Loaded(content) = webPage.content, let image = content.image, let largest = largestImageRepresentation(image.representations) { + let imageSize = largest.dimensions.aspectFilled(size) + let boundingSize = size + let radius: CGFloat = self.roundCorners ? floor(min(imageSize.width, imageSize.height) / 2.0) : 0.0 + let makeLayout = self.imageNode.asyncLayout() + let apply = makeLayout(TransformImageArguments(corners: ImageCorners(radius: radius), imageSize: imageSize, boundingSize: boundingSize, intrinsicInsets: UIEdgeInsets(), emptyColor: self.theme.pageBackgroundColor)) + apply() + + let radialStatusSize: CGFloat = 50.0 + self.statusNode.frame = CGRect(x: floorToScreenPixels((size.width - radialStatusSize) / 2.0), y: floorToScreenPixels((size.height - radialStatusSize) / 2.0), width: radialStatusSize, height: radialStatusSize) } } } @@ -172,6 +188,7 @@ final class InstantPageImageNode: ASDisplayNode, InstantPageNode { func updateHiddenMedia(media: InstantPageMedia?) { self.imageNode.isHidden = self.media == media + self.statusNode.isHidden = self.imageNode.isHidden } @objc func tapGesture(_ recognizer: UITapGestureRecognizer) { diff --git a/TelegramUI/InstantPageLayout.swift b/TelegramUI/InstantPageLayout.swift index 9ed0d19ed5..9d236c3a1b 100644 --- a/TelegramUI/InstantPageLayout.swift +++ b/TelegramUI/InstantPageLayout.swift @@ -164,7 +164,7 @@ func layoutInstantPageBlock(webpage: TelegramMediaWebpage, rtl: Bool, block: Ins case let .paragraph(text): let styleStack = InstantPageTextStyleStack() setupStyleStack(styleStack, theme: theme, category: .paragraph, link: false) - let (_, items, contentSize) = layoutTextItemWithString(attributedStringForRichText(text, styleStack: styleStack), boundingWidth: boundingWidth - horizontalInset * 2.0, offset: CGPoint(x: horizontalInset, y: 0.0), media: media, webpage: webpage) + let (_, items, contentSize) = layoutTextItemWithString(attributedStringForRichText(text, styleStack: styleStack), boundingWidth: boundingWidth - horizontalInset * 2.0, horizontalInset: horizontalInset, offset: CGPoint(x: horizontalInset, y: 0.0), media: media, webpage: webpage) return InstantPageLayout(origin: CGPoint(), contentSize: contentSize, items: items) case let .preformatted(text): let styleStack = InstantPageTextStyleStack() @@ -215,12 +215,12 @@ func layoutInstantPageBlock(webpage: TelegramMediaWebpage, rtl: Bool, block: Ins } else { value = "\(i + 1)." } - let (textItem, textItems, _) = layoutTextItemWithString(attributedStringForRichText(.plain(value), styleStack: styleStack), boundingWidth: boundingWidth - horizontalInset * 2.0, offset: CGPoint()) + let (textItem, _, _) = layoutTextItemWithString(attributedStringForRichText(.plain(value), styleStack: styleStack), boundingWidth: boundingWidth - horizontalInset * 2.0, offset: CGPoint()) if let textItem = textItem, let line = textItem.lines.first { textItem.selectable = false maxIndexWidth = max(maxIndexWidth, line.frame.width) + indexItems.append(textItem) } - indexItems.append(textItems.first!) } else { let shapeItem = InstantPageShapeItem(frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: 6.0, height: 12.0)), shapeFrame: CGRect(origin: CGPoint(x: 0.0, y: 3.0), size: CGSize(width: 6.0, height: 6.0)), shape: .ellipse, color: theme.textCategories.paragraph.color) indexItems.append(shapeItem) @@ -614,11 +614,11 @@ func layoutInstantPageBlock(webpage: TelegramMediaWebpage, rtl: Bool, block: Ins var contentSize: CGSize let frame = CGRect(origin: CGPoint(x: floor((boundingWidth - size.width) / 2.0), y: 0.0), size: size) let item: InstantPageItem - if let url = url, let coverId = coverId, let image = media[coverId] as? TelegramMediaImage, let largest = largestImageRepresentation(image.representations) { + if let url = url, let coverId = coverId, let image = media[coverId] as? TelegramMediaImage { let loadedContent = TelegramMediaWebpageLoadedContent(url: url, displayUrl: url, hash: 0, type: "video", websiteName: nil, title: nil, text: nil, embedUrl: url, embedType: "video", embedSize: size, duration: nil, author: nil, image: image, file: nil, instantPage: nil) let content = TelegramMediaWebpageContent.Loaded(loadedContent) - item = InstantPageImageItem(frame: frame, webPage: webpage, media: InstantPageMedia(index: embedIndex, media: TelegramMediaWebpage(webpageId: MediaId(namespace: 0, id: 0), content: content), url: nil, caption: nil, credit: nil), attributes: [], interactive: true, roundCorners: false, fit: false) + item = InstantPageImageItem(frame: frame, webPage: webpage, media: InstantPageMedia(index: embedIndex, media: TelegramMediaWebpage(webpageId: MediaId(namespace: Namespaces.Media.LocalWebpage, id: -1), content: content), url: nil, caption: nil, credit: nil), attributes: [], interactive: true, roundCorners: false, fit: false) } else { item = InstantPageWebEmbedItem(frame: frame, url: url, html: html, enableScrolling: allowScrolling) diff --git a/TelegramUI/InstantPageNavigationBar.swift b/TelegramUI/InstantPageNavigationBar.swift index 218f8a85b4..f90b3ec714 100644 --- a/TelegramUI/InstantPageNavigationBar.swift +++ b/TelegramUI/InstantPageNavigationBar.swift @@ -6,6 +6,49 @@ private let backArrowImage = NavigationBarTheme.generateBackArrowImage(color: .w private let moreImage = generateTintedImage(image: UIImage(bundleImageName: "Instant View/MoreIcon"), color: .white) private let actionImage = generateTintedImage(image: UIImage(bundleImageName: "Instant View/ActionIcon"), color: .white) +final private class InstantPageProgressNode: ASDisplayNode { + private let foregroundNode: ASDisplayNode + private var progress: CGFloat = 0.0 + + override init() { + self.foregroundNode = ASDisplayNode() + self.foregroundNode.backgroundColor = .white + + super.init() + + self.addSubnode(self.foregroundNode) + } + + func setProgress(_ progress: CGFloat, animated: Bool = false) { + if self.progress == progress && animated { + return + } + + let size = self.bounds.size + + self.progress = progress + + let transition: ContainedViewLayoutTransition + if animated { + transition = .animated(duration: 0.5, curve: .spring) + } else { + transition = .immediate + } + + let alpaTransition: ContainedViewLayoutTransition + if animated { + alpaTransition = .animated(duration: 0.3, curve: .easeInOut) + } else { + alpaTransition = .immediate + } + + transition.updateFrame(node: self.foregroundNode, frame: CGRect(x: 0.0, y: 0.0, width: size.width * progress, height: size.height)) + + let alpha: CGFloat = progress < 0.001 || progress > 0.999 ? 0.0 : 1.0 + alpaTransition.updateAlpha(node: self.foregroundNode, alpha: alpha) + } +} + final class InstantPageNavigationBar: ASDisplayNode { private var strings: PresentationStrings @@ -17,7 +60,7 @@ final class InstantPageNavigationBar: ASDisplayNode { private let arrowNode: ASImageNode private let titleNode: ASTextNode - private let progressNode: ASDisplayNode + private let progressNode: InstantPageProgressNode private let intrinsicMoreSize: CGSize private let intrinsicSmallMoreSize: CGSize @@ -65,8 +108,7 @@ final class InstantPageNavigationBar: ASDisplayNode { self.titleNode = ASTextNode() self.titleNode.maximumNumberOfLines = 1 - self.progressNode = ASDisplayNode() - self.progressNode.backgroundColor = .white + self.progressNode = InstantPageProgressNode() super.init() @@ -116,6 +158,10 @@ final class InstantPageNavigationBar: ASDisplayNode { } } + func setLoadProgress(_ progress: CGFloat) { + self.progressNode.setProgress(progress, animated: true) + } + func updateLayout(size: CGSize, minHeight: CGFloat, maxHeight: CGFloat, topInset: CGFloat, leftInset: CGFloat, rightInset: CGFloat, title: String?, pageProgress: CGFloat, transition: ContainedViewLayoutTransition) { let progressHeight: CGFloat if !topInset.isZero { @@ -183,6 +229,9 @@ final class InstantPageNavigationBar: ASDisplayNode { transition.updateAlpha(node: self.actionButton, alpha: alphaFactor) transition.updateFrame(node: self.scrollToTopButton, frame: CGRect(origin: CGPoint(x: leftInset + 64.0, y: 0.0), size: CGSize(width: size.width - leftInset - rightInset - 64.0, height: size.height))) + + let loadProgressHeight: CGFloat = 2.0 + transition.updateFrame(node: self.progressNode, frame: CGRect(origin: CGPoint(x: 0.0, y: size.height - loadProgressHeight - UIScreenPixel), size: CGSize(width: size.width, height: loadProgressHeight))) } override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { diff --git a/TelegramUI/InstantPageScrollableNode.swift b/TelegramUI/InstantPageScrollableNode.swift new file mode 100644 index 0000000000..b0fe1613e2 --- /dev/null +++ b/TelegramUI/InstantPageScrollableNode.swift @@ -0,0 +1,106 @@ +import Foundation +import TelegramCore +import Postbox +import Display + +protocol InstantPageScrollableItem: class, InstantPageItem { + var contentSize: CGSize { get } + var horizontalInset: CGFloat { get } + var isRTL: Bool { get } + + func textItemAtLocation(_ location: CGPoint) -> (InstantPageTextItem, CGPoint)? +} + +private final class InstantPageScrollableContentNodeParameters: NSObject { + let item: InstantPageScrollableItem + + init(item: InstantPageScrollableItem) { + self.item = item + super.init() + } +} + +final class InstantPageScrollableContentNode: ASDisplayNode { + let item: InstantPageScrollableItem + + init(item: InstantPageScrollableItem, additionalNodes: [InstantPageNode]) { + self.item = item + super.init() + + self.isOpaque = false + self.isUserInteractionEnabled = false + + for case let node as ASDisplayNode in additionalNodes { + self.addSubnode(node) + } + } + + override func drawParameters(forAsyncLayer layer: _ASDisplayLayer) -> NSObjectProtocol? { + return InstantPageScrollableContentNodeParameters(item: self.item) + } + + @objc override public class func draw(_ bounds: CGRect, withParameters parameters: Any?, isCancelled: () -> Bool, isRasterizing: Bool) { + let context = UIGraphicsGetCurrentContext()! + + if let parameters = parameters as? InstantPageScrollableContentNodeParameters { + parameters.item.drawInTile(context: context) + } + } +} + +final class InstantPageScrollableNode: ASScrollNode, InstantPageNode { + let item: InstantPageScrollableItem + let contentNode: InstantPageScrollableContentNode + + var contentOffset: CGPoint { + return self.view.contentOffset + } + + init(item: InstantPageScrollableItem, additionalNodes: [InstantPageNode]) { + self.item = item + self.contentNode = InstantPageScrollableContentNode(item: item, additionalNodes: additionalNodes) + super.init() + + self.isOpaque = false + self.contentNode.frame = CGRect(origin: CGPoint(x: item.horizontalInset, y: 0.0), size: item.contentSize) + self.view.contentSize = CGSize(width: item.contentSize.width + item.horizontalInset * 2.0, height: item.contentSize.height) + if item.isRTL { + self.view.contentOffset = CGPoint(x: self.view.contentSize.width - item.frame.width, y: 0.0) + } + self.view.alwaysBounceVertical = false + self.view.showsHorizontalScrollIndicator = false + self.view.showsVerticalScrollIndicator = false + if #available(iOSApplicationExtension 11.0, *) { + self.view.contentInsetAdjustmentBehavior = .never + } + self.addSubnode(self.contentNode) + + self.view.interactiveTransitionGestureRecognizerTest = { [weak self] point -> Bool in + if let strongSelf = self { + if strongSelf.view.contentOffset.x < 1.0 { + return false + } else { + return point.x - strongSelf.view.contentOffset.x > 30.0 + } + } else { + return false + } + } + } + + func updateIsVisible(_ isVisible: Bool) { + } + + func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) { + } + + func transitionNode(media: InstantPageMedia) -> (ASDisplayNode, () -> UIView?)? { + return nil + } + + func updateHiddenMedia(media: InstantPageMedia?) { + } + + func update(strings: PresentationStrings, theme: InstantPageTheme) { + } +} diff --git a/TelegramUI/InstantPageTableItem.swift b/TelegramUI/InstantPageTableItem.swift index f59c5061bb..4f146a45a1 100644 --- a/TelegramUI/InstantPageTableItem.swift +++ b/TelegramUI/InstantPageTableItem.swift @@ -94,7 +94,7 @@ private let tableCellInsets = UIEdgeInsetsMake(14.0, 12.0, 14.0, 12.0) private let tableBorderWidth: CGFloat = 1.0 private let tableCornerRadius: CGFloat = 5.0 -final class InstantPageTableItem: InstantPageItem { +final class InstantPageTableItem: InstantPageScrollableItem { var frame: CGRect let totalWidth: CGFloat let horizontalInset: CGFloat @@ -104,7 +104,7 @@ final class InstantPageTableItem: InstantPageItem { let theme: InstantPageTheme - let rtl: Bool + let isRTL: Bool fileprivate let cells: [InstantPageTableCellItem] private let borderWidth: CGFloat @@ -115,7 +115,11 @@ final class InstantPageTableItem: InstantPageItem { self.borderWidth = borderWidth self.theme = theme self.cells = cells - self.rtl = rtl + self.isRTL = rtl + } + + var contentSize: CGSize { + return CGSize(width: self.totalWidth, height: self.frame.height) } func drawInTile(context: CGContext) { @@ -175,11 +179,22 @@ final class InstantPageTableItem: InstantPageItem { } func node(account: Account, strings: PresentationStrings, theme: InstantPageTheme, openMedia: @escaping (InstantPageMedia) -> Void, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void, currentExpandedDetails: [Int : Bool]?) -> (InstantPageNode & ASDisplayNode)? { - return InstantPageTableNode(item: self, account: account, strings: strings, theme: theme) + var additionalNodes: [InstantPageNode] = [] + for cell in self.cells { + for item in cell.additionalItems { + if item.wantsNode { + if let node = item.node(account: account, strings: strings, theme: theme, openMedia: { _ in }, openPeer: { _ in }, openUrl: { _ in}, updateWebEmbedHeight: { _ in }, updateDetailsExpanded: { _ in }, currentExpandedDetails: nil) { + node.frame = item.frame.offsetBy(dx: cell.frame.minX, dy: cell.frame.minY) + additionalNodes.append(node) + } + } + } + } + return InstantPageScrollableNode(item: self, additionalNodes: additionalNodes) } func matchesNode(_ node: InstantPageNode) -> Bool { - if let node = node as? InstantPageTableNode { + if let node = node as? InstantPageScrollableNode { return node.item === self } return false @@ -213,107 +228,6 @@ final class InstantPageTableItem: InstantPageItem { } } -private final class InstantPageTableNodeParameters: NSObject { - let item: InstantPageTableItem - - init(item: InstantPageTableItem) { - self.item = item - super.init() - } -} - -final class InstantPageTableContentNode: ASDisplayNode { - private let item: InstantPageTableItem - - init(item: InstantPageTableItem, account: Account, strings: PresentationStrings, theme: InstantPageTheme) { - self.item = item - super.init() - - self.isOpaque = false - self.isUserInteractionEnabled = false - - for cell in self.item.cells { - for item in cell.additionalItems { - if item.wantsNode { - if let node = item.node(account: account, strings: strings, theme: theme, openMedia: { _ in }, openPeer: { _ in }, openUrl: { _ in}, updateWebEmbedHeight: { _ in }, updateDetailsExpanded: { _ in }, currentExpandedDetails: nil) { - node.frame = item.frame.offsetBy(dx: cell.frame.minX, dy: cell.frame.minY) - self.addSubnode(node) - } - } - } - } - } - - override func drawParameters(forAsyncLayer layer: _ASDisplayLayer) -> NSObjectProtocol? { - return InstantPageTableNodeParameters(item: self.item) - } - - @objc override public class func draw(_ bounds: CGRect, withParameters parameters: Any?, isCancelled: () -> Bool, isRasterizing: Bool) { - let context = UIGraphicsGetCurrentContext()! - - if let parameters = parameters as? InstantPageTableNodeParameters { - parameters.item.drawInTile(context: context) - } - } -} - -final class InstantPageTableNode: ASScrollNode, InstantPageNode { - let item: InstantPageTableItem - let contentNode: InstantPageTableContentNode - - var contentOffset: CGPoint { - return self.view.contentOffset - } - - init(item: InstantPageTableItem, account: Account, strings: PresentationStrings, theme: InstantPageTheme) { - self.item = item - self.contentNode = InstantPageTableContentNode(item: item, account: account, strings: strings, theme: theme) - super.init() - - self.isOpaque = false - self.contentNode.frame = CGRect(x: item.horizontalInset, y: 0.0, width: item.totalWidth, height: item.frame.height) - self.view.contentSize = CGSize(width: item.totalWidth + item.horizontalInset * 2.0, height: item.frame.height) - if item.rtl { - self.view.contentOffset = CGPoint(x: self.view.contentSize.width - item.frame.width, y: 0.0) - } - self.view.alwaysBounceVertical = false - self.view.showsHorizontalScrollIndicator = false - self.view.showsVerticalScrollIndicator = false - if #available(iOSApplicationExtension 11.0, *) { - self.view.contentInsetAdjustmentBehavior = .never - } - self.addSubnode(self.contentNode) - - self.view.interactiveTransitionGestureRecognizerTest = { [weak self] point -> Bool in - if let strongSelf = self { - if strongSelf.view.contentOffset.x < 1.0 { - return false - } else { - return point.x - strongSelf.view.contentOffset.x > 30.0 - } - } else { - return false - } - } - } - - func updateIsVisible(_ isVisible: Bool) { - } - - func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) { - } - - func transitionNode(media: InstantPageMedia) -> (ASDisplayNode, () -> UIView?)? { - return nil - } - - func updateHiddenMedia(media: InstantPageMedia?) { - } - - func update(strings: PresentationStrings, theme: InstantPageTheme) { - } -} - private struct TableRow { var minColumnWidths: [Int : CGFloat] var maxColumnWidths: [Int : CGFloat] diff --git a/TelegramUI/InstantPageTextItem.swift b/TelegramUI/InstantPageTextItem.swift index 3a015ea3a9..3b0f88854e 100644 --- a/TelegramUI/InstantPageTextItem.swift +++ b/TelegramUI/InstantPageTextItem.swift @@ -324,6 +324,83 @@ final class InstantPageTextItem: InstantPageItem { } } +final class InstantPageScrollableTextItem: InstantPageScrollableItem { + var frame: CGRect + let totalWidth: CGFloat + let horizontalInset: CGFloat + let medias: [InstantPageMedia] = [] + let wantsNode: Bool = true + let separatesTiles: Bool = false + + let item: InstantPageTextItem + let additionalItems: [InstantPageItem] + let isRTL: Bool + + fileprivate init(frame: CGRect, item: InstantPageTextItem, additionalItems: [InstantPageItem], totalWidth: CGFloat, horizontalInset: CGFloat, rtl: Bool) { + self.frame = frame + self.item = item + self.additionalItems = additionalItems + self.totalWidth = totalWidth + self.horizontalInset = horizontalInset + self.isRTL = rtl + } + + var contentSize: CGSize { + return CGSize(width: self.totalWidth, height: self.frame.height) + } + + func drawInTile(context: CGContext) { + context.saveGState() + context.translateBy(x: self.item.frame.minX, y: self.item.frame.minY) + self.item.drawInTile(context: context) + context.restoreGState() + } + + func node(account: Account, strings: PresentationStrings, theme: InstantPageTheme, openMedia: @escaping (InstantPageMedia) -> Void, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void, currentExpandedDetails: [Int : Bool]?) -> (ASDisplayNode & InstantPageNode)? { + var additionalNodes: [InstantPageNode] = [] + for item in additionalItems { + if item.wantsNode { + if let node = item.node(account: account, strings: strings, theme: theme, openMedia: { _ in }, openPeer: { _ in }, openUrl: { _ in}, updateWebEmbedHeight: { _ in }, updateDetailsExpanded: { _ in }, currentExpandedDetails: nil) { + node.frame = item.frame + additionalNodes.append(node) + } + } + } + return InstantPageScrollableNode(item: self, additionalNodes: additionalNodes) + } + + func matchesAnchor(_ anchor: String) -> Bool { + return self.item.matchesAnchor(anchor) + } + + func matchesNode(_ node: InstantPageNode) -> Bool { + if let node = node as? InstantPageScrollableNode { + return node.item === self + } + return false + } + + func distanceThresholdGroup() -> Int? { + return nil + } + + func distanceThresholdWithGroupCount(_ count: Int) -> CGFloat { + return 0.0 + } + + func linkSelectionRects(at point: CGPoint) -> [CGRect] { + let rects = self.item.linkSelectionRects(at: point.offsetBy(dx: -self.item.frame.minX - self.horizontalInset, dy: -self.item.frame.minY)) + return rects.map { $0.offsetBy(dx: self.item.frame.minX + self.horizontalInset, dy: -self.item.frame.minY) } + } + + func textItemAtLocation(_ location: CGPoint) -> (InstantPageTextItem, CGPoint)? { + if self.item.selectable, self.item.frame.contains(location.offsetBy(dx: -self.item.frame.minX - self.horizontalInset, dy: -self.item.frame.minY)) { + return (item, self.item.frame.origin.offsetBy(dx: self.horizontalInset, dy: -self.item.frame.minY)) + } + return nil + } +} + func attributedStringForRichText(_ text: RichText, styleStack: InstantPageTextStyleStack, url: InstantPageUrlItem? = nil) -> NSAttributedString { switch text { case .empty: @@ -407,7 +484,7 @@ func attributedStringForRichText(_ text: RichText, styleStack: InstantPageTextSt let width: CGFloat } let extentBuffer = UnsafeMutablePointer.allocate(capacity: 1) - extentBuffer.initialize(to: RunStruct(ascent: dimensions.height, descent: 0.0, width: dimensions.width)) + extentBuffer.initialize(to: RunStruct(ascent: 0.0, descent: 0.0, width: dimensions.width)) var callbacks = CTRunDelegateCallbacks(version: kCTRunDelegateVersion1, dealloc: { (pointer) in }, getAscent: { (pointer) -> CGFloat in let d = pointer.assumingMemoryBound(to: RunStruct.self) @@ -420,7 +497,7 @@ func attributedStringForRichText(_ text: RichText, styleStack: InstantPageTextSt return d.pointee.width }) let delegate = CTRunDelegateCreate(&callbacks, extentBuffer) - let attrDictionaryDelegate = [(kCTRunDelegateAttributeName as NSAttributedStringKey): (delegate as Any), NSAttributedStringKey(rawValue: InstantPageMediaIdAttribute): id.id] + let attrDictionaryDelegate = [(kCTRunDelegateAttributeName as NSAttributedStringKey): (delegate as Any), NSAttributedStringKey(rawValue: InstantPageMediaIdAttribute): id.id, NSAttributedStringKey(rawValue: InstantPageMediaDimensionsAttribute): dimensions] return NSAttributedString(string: " ", attributes: attrDictionaryDelegate) case let .anchor(text, name): styleStack.push(.anchor(name)) @@ -434,7 +511,7 @@ func attributedStringForRichText(_ text: RichText, styleStack: InstantPageTextSt } } -func layoutTextItemWithString(_ string: NSAttributedString, boundingWidth: CGFloat, alignment: NSTextAlignment = .natural, offset: CGPoint, media: [MediaId: Media] = [:], webpage: TelegramMediaWebpage? = nil, minimizeWidth: Bool = false, maxNumberOfLines: Int = 0) -> (InstantPageTextItem?, [InstantPageItem], CGSize) { +func layoutTextItemWithString(_ string: NSAttributedString, boundingWidth: CGFloat, horizontalInset: CGFloat = 0.0, alignment: NSTextAlignment = .natural, offset: CGPoint, media: [MediaId: Media] = [:], webpage: TelegramMediaWebpage? = nil, minimizeWidth: Bool = false, maxNumberOfLines: Int = 0) -> (InstantPageTextItem?, [InstantPageItem], CGSize) { if string.length == 0 { return (nil, [], CGSize()) } @@ -470,12 +547,15 @@ func layoutTextItemWithString(_ string: NSAttributedString, boundingWidth: CGFlo var lastIndex: CFIndex = 0 var currentLineOrigin = CGPoint() + var maxLineWidth: CGFloat = 0.0 var maxImageHeight: CGFloat = 0.0 var extraDescent: CGFloat = 0.0 let text = string.string var indexOffset: CFIndex? while true { - let currentMaxWidth = boundingWidth - currentLineOrigin.x + var workingLineOrigin = currentLineOrigin + + let currentMaxWidth = boundingWidth - workingLineOrigin.x let lineCharacterCount: CFIndex var hadIndexOffset = false if minimizeWidth { @@ -516,33 +596,7 @@ func layoutTextItemWithString(_ string: NSAttributedString, boundingWidth: CGFlo stop = true } - var strikethroughItems: [InstantPageTextStrikethroughItem] = [] - var markedItems: [InstantPageTextMarkedItem] = [] - var anchorItems: [InstantPageTextAnchorItem] = [] - - string.enumerateAttributes(in: lineRange, options: []) { attributes, range, _ in - if let _ = attributes[NSAttributedStringKey.strikethroughStyle] { - let lowerX = floor(CTLineGetOffsetForStringIndex(line, range.location, nil)) - let upperX = ceil(CTLineGetOffsetForStringIndex(line, range.location + range.length, nil)) - strikethroughItems.append(InstantPageTextStrikethroughItem(frame: CGRect(x: currentLineOrigin.x + lowerX, y: currentLineOrigin.y, width: upperX - lowerX, height: fontLineHeight))) - } - if let color = attributes[NSAttributedStringKey.init(rawValue: InstantPageMarkerColorAttribute)] as? UIColor { - var lineHeight = fontLineHeight - var delta: CGFloat = 0.0 - - if let offset = attributes[NSAttributedStringKey.baselineOffset] as? CGFloat { - lineHeight = floorToScreenPixels(lineHeight * 0.85) - delta = offset * 0.6 - } - let lowerX = floor(CTLineGetOffsetForStringIndex(line, range.location, nil)) - let upperX = ceil(CTLineGetOffsetForStringIndex(line, range.location + range.length, nil)) - markedItems.append(InstantPageTextMarkedItem(frame: CGRect(x: currentLineOrigin.x + lowerX, y: currentLineOrigin.y + delta, width: upperX - lowerX, height: lineHeight), color: color)) - } - if let item = attributes[NSAttributedStringKey.init(rawValue: InstantPageAnchorAttribute)] as? String { - anchorItems.append(InstantPageTextAnchorItem(name: item)) - } - } - + let hadExtraDescent = extraDescent > 0.0 extraDescent = 0.0 var lineImageItems: [InstantPageTextImageItem] = [] var isRTL = false @@ -556,25 +610,22 @@ func layoutTextItemWithString(_ string: NSAttributedString, boundingWidth: CGFlo let cfRunRange = CTRunGetStringRange(run) let runRange = NSMakeRange(cfRunRange.location == kCFNotFound ? NSNotFound : cfRunRange.location, cfRunRange.length) string.enumerateAttributes(in: runRange, options: []) { attributes, range, _ in - if let id = attributes[NSAttributedStringKey.init(rawValue: InstantPageMediaIdAttribute)] as? Int64 { - var imageFrame = CGRect() - var ascent: CGFloat = 0 - imageFrame.size.width = CGFloat(CTRunGetTypographicBounds(run, CFRangeMake(0, 0), &ascent, nil, nil)) - imageFrame.size.height = ascent + if let id = attributes[NSAttributedStringKey.init(rawValue: InstantPageMediaIdAttribute)] as? Int64, let dimensions = attributes[NSAttributedStringKey.init(rawValue: InstantPageMediaDimensionsAttribute)] as? CGSize { + var imageFrame = CGRect(origin: CGPoint(), size: dimensions) let xOffset = CTLineGetOffsetForStringIndex(line, CTRunGetStringRange(run).location, nil) let yOffset = fontLineHeight.isZero ? 0.0 : floorToScreenPixels((fontLineHeight - imageFrame.size.height) / 2.0) - imageFrame.origin = imageFrame.origin.offsetBy(dx: currentLineOrigin.x + xOffset, dy: currentLineOrigin.y + yOffset) + imageFrame.origin = imageFrame.origin.offsetBy(dx: workingLineOrigin.x + xOffset, dy: workingLineOrigin.y + yOffset) - let minSpacing = fontLineSpacing - 3.0 - let delta = currentLineOrigin.y - minSpacing - imageFrame.minY - appliedLineOffset + let minSpacing = fontLineSpacing - 4.0 + let delta = workingLineOrigin.y - minSpacing - imageFrame.minY - appliedLineOffset if !fontAscent.isZero && delta > 0.0 { - currentLineOrigin.y += delta + workingLineOrigin.y += delta appliedLineOffset += delta imageFrame.origin = imageFrame.origin.offsetBy(dx: 0.0, dy: delta) } if !fontLineHeight.isZero { - extraDescent = max(extraDescent, imageFrame.maxY - (currentLineOrigin.y + fontLineHeight + minSpacing)) + extraDescent = max(extraDescent, imageFrame.maxY - (workingLineOrigin.y + fontLineHeight + minSpacing)) } maxImageHeight = max(maxImageHeight, imageFrame.height) lineImageItems.append(InstantPageTextImageItem(frame: imageFrame, range: range, id: MediaId(namespace: Namespaces.Media.CloudFile, id: id))) @@ -583,19 +634,55 @@ func layoutTextItemWithString(_ string: NSAttributedString, boundingWidth: CGFlo } } - if !minimizeWidth && !hadIndexOffset && lineCharacterCount > 1 && lineWidth > currentMaxWidth, let imageItem = lineImageItems.last { + if !minimizeWidth && !hadIndexOffset && lineCharacterCount > 1 && lineWidth > currentMaxWidth + 1.0, let imageItem = lineImageItems.last { indexOffset = -(lastIndex + lineCharacterCount - imageItem.range.lowerBound) continue } + var strikethroughItems: [InstantPageTextStrikethroughItem] = [] + var markedItems: [InstantPageTextMarkedItem] = [] + var anchorItems: [InstantPageTextAnchorItem] = [] + + string.enumerateAttributes(in: lineRange, options: []) { attributes, range, _ in + if let _ = attributes[NSAttributedStringKey.strikethroughStyle] { + let lowerX = floor(CTLineGetOffsetForStringIndex(line, range.location, nil)) + let upperX = ceil(CTLineGetOffsetForStringIndex(line, range.location + range.length, nil)) + strikethroughItems.append(InstantPageTextStrikethroughItem(frame: CGRect(x: workingLineOrigin.x + lowerX, y: workingLineOrigin.y, width: upperX - lowerX, height: fontLineHeight))) + } + if let color = attributes[NSAttributedStringKey.init(rawValue: InstantPageMarkerColorAttribute)] as? UIColor { + var lineHeight = fontLineHeight + var delta: CGFloat = 0.0 + + if let offset = attributes[NSAttributedStringKey.baselineOffset] as? CGFloat { + lineHeight = floorToScreenPixels(lineHeight * 0.85) + delta = offset * 0.6 + } + let lowerX = floor(CTLineGetOffsetForStringIndex(line, range.location, nil)) + let upperX = ceil(CTLineGetOffsetForStringIndex(line, range.location + range.length, nil)) + markedItems.append(InstantPageTextMarkedItem(frame: CGRect(x: workingLineOrigin.x + lowerX, y: workingLineOrigin.y + delta, width: upperX - lowerX, height: lineHeight), color: color)) + } + if let item = attributes[NSAttributedStringKey.init(rawValue: InstantPageAnchorAttribute)] as? String { + anchorItems.append(InstantPageTextAnchorItem(name: item)) + } + } + + if hadExtraDescent && extraDescent > 0 { + workingLineOrigin.y += fontLineSpacing + } + let height = !fontLineHeight.isZero ? fontLineHeight : maxImageHeight - let textLine = InstantPageTextLine(line: line, range: lineRange, frame: CGRect(x: currentLineOrigin.x, y: currentLineOrigin.y, width: lineWidth, height: height), strikethroughItems: strikethroughItems, markedItems: markedItems, imageItems: lineImageItems, anchorItems: anchorItems, isRTL: isRTL) + let textLine = InstantPageTextLine(line: line, range: lineRange, frame: CGRect(x: workingLineOrigin.x, y: workingLineOrigin.y, width: lineWidth, height: height), strikethroughItems: strikethroughItems, markedItems: markedItems, imageItems: lineImageItems, anchorItems: anchorItems, isRTL: isRTL) lines.append(textLine) imageItems.append(contentsOf: lineImageItems) - currentLineOrigin.x = 0.0; - currentLineOrigin.y += fontLineHeight + fontLineSpacing + extraDescent + if lineWidth > maxLineWidth { + maxLineWidth = lineWidth + } + + workingLineOrigin.x = 0.0 + workingLineOrigin.y += fontLineHeight + fontLineSpacing + extraDescent + currentLineOrigin = workingLineOrigin lastIndex += lineCharacterCount @@ -612,23 +699,56 @@ func layoutTextItemWithString(_ string: NSAttributedString, boundingWidth: CGFlo height = lines.last!.frame.maxY + extraDescent } - let textItem = InstantPageTextItem(frame: CGRect(x: 0.0, y: 0.0, width: boundingWidth, height: height), attributedString: string, alignment: alignment, lines: lines) - textItem.frame = textItem.frame.offsetBy(dx: offset.x, dy: offset.y) + var textWidth = boundingWidth + var requiresScroll = false + if maxLineWidth > boundingWidth + 10.0 { + textWidth = maxLineWidth + requiresScroll = true + } + + let textItem = InstantPageTextItem(frame: CGRect(x: 0.0, y: 0.0, width: textWidth, height: height), attributedString: string, alignment: alignment, lines: lines) + if !requiresScroll { + textItem.frame = textItem.frame.offsetBy(dx: offset.x, dy: offset.y) + } var items: [InstantPageItem] = [] - if imageItems.isEmpty || string.length > 1 { + if !requiresScroll && (imageItems.isEmpty || string.length > 1) { items.append(textItem) } + var topInset: CGFloat = 0.0 + var bottomInset: CGFloat = 0.0 + var additionalItems: [InstantPageItem] = [] if let webpage = webpage { + let offset = requiresScroll ? CGPoint() : offset for line in textItem.lines { let lineFrame = frameForLine(line, boundingWidth: boundingWidth, alignment: alignment) for imageItem in line.imageItems { if let image = media[imageItem.id] as? TelegramMediaFile { - items.append(InstantPageImageItem(frame: imageItem.frame.offsetBy(dx: lineFrame.minX + offset.x, dy: offset.y), webPage: webpage, media: InstantPageMedia(index: -1, media: image, url: nil, caption: nil, credit: nil), interactive: false, roundCorners: false, fit: false)) + let item = InstantPageImageItem(frame: imageItem.frame.offsetBy(dx: lineFrame.minX + offset.x, dy: offset.y), webPage: webpage, media: InstantPageMedia(index: -1, media: image, url: nil, caption: nil, credit: nil), interactive: false, roundCorners: false, fit: false) + additionalItems.append(item) + + if item.frame.minY < topInset { + topInset = item.frame.minY + } + if item.frame.maxY > height { + bottomInset = max(bottomInset, item.frame.maxY - height) + } } } } } - return (textItem, items, textItem.frame.size) + if requiresScroll { + textItem.frame = textItem.frame.offsetBy(dx: 0.0, dy: fabs(topInset)) + for var item in additionalItems { + item.frame = item.frame.offsetBy(dx: 0.0, dy: fabs(topInset)) + } + + let scrollableItem = InstantPageScrollableTextItem(frame: CGRect(x: 0.0, y: 0.0, width: boundingWidth + horizontalInset * 2.0, height: height + fabs(topInset) + bottomInset), item: textItem, additionalItems: additionalItems, totalWidth: textWidth, horizontalInset: horizontalInset, rtl: textItem.containsRTL) + items.append(scrollableItem) + } else { + items.append(contentsOf: additionalItems) + } + + return (requiresScroll ? nil : textItem, items, textItem.frame.size) } diff --git a/TelegramUI/InstantPageTextStyleStack.swift b/TelegramUI/InstantPageTextStyleStack.swift index dae914fab5..f86790e2dc 100644 --- a/TelegramUI/InstantPageTextStyleStack.swift +++ b/TelegramUI/InstantPageTextStyleStack.swift @@ -25,6 +25,7 @@ enum InstantPageTextStyle { let InstantPageLineSpacingFactorAttribute = "LineSpacingFactorAttribute" let InstantPageMarkerColorAttribute = "MarkerColorAttribute" let InstantPageMediaIdAttribute = "MediaIdAttribute" +let InstantPageMediaDimensionsAttribute = "MediaDimensionsAttribute" let InstantPageAnchorAttribute = "AnchorAttribute" final class InstantPageTextStyleStack { diff --git a/TelegramUI/InstantPageTheme.swift b/TelegramUI/InstantPageTheme.swift index 01839c70ce..943d7886c7 100644 --- a/TelegramUI/InstantPageTheme.swift +++ b/TelegramUI/InstantPageTheme.swift @@ -103,9 +103,9 @@ final class InstantPageTheme { let controlColor: UIColor - let imageEmptyColor: UIColor? + let imageTintColor: UIColor? - init(pageBackgroundColor: UIColor, textCategories: InstantPageTextCategories, serif: Bool, codeBlockBackgroundColor: UIColor, linkColor: UIColor, textHighlightColor: UIColor, linkHighlightColor: UIColor, markerColor: UIColor, panelBackgroundColor: UIColor, panelHighlightedBackgroundColor: UIColor, panelPrimaryColor: UIColor, panelSecondaryColor: UIColor, panelAccentColor: UIColor, tableBorderColor: UIColor, tableHeaderColor: UIColor, controlColor: UIColor, imageEmptyColor: UIColor?) { + init(pageBackgroundColor: UIColor, textCategories: InstantPageTextCategories, serif: Bool, codeBlockBackgroundColor: UIColor, linkColor: UIColor, textHighlightColor: UIColor, linkHighlightColor: UIColor, markerColor: UIColor, panelBackgroundColor: UIColor, panelHighlightedBackgroundColor: UIColor, panelPrimaryColor: UIColor, panelSecondaryColor: UIColor, panelAccentColor: UIColor, tableBorderColor: UIColor, tableHeaderColor: UIColor, controlColor: UIColor, imageTintColor: UIColor?) { self.pageBackgroundColor = pageBackgroundColor self.textCategories = textCategories self.serif = serif @@ -122,11 +122,11 @@ final class InstantPageTheme { self.tableBorderColor = tableBorderColor self.tableHeaderColor = tableHeaderColor self.controlColor = controlColor - self.imageEmptyColor = imageEmptyColor + self.imageTintColor = imageTintColor } func withUpdatedFontStyles(sizeMultiplier: CGFloat, forceSerif: Bool) -> InstantPageTheme { - return InstantPageTheme(pageBackgroundColor: pageBackgroundColor, textCategories: self.textCategories.withUpdatedFontStyles(sizeMultiplier: sizeMultiplier, forceSerif: forceSerif), serif: forceSerif, codeBlockBackgroundColor: codeBlockBackgroundColor, linkColor: linkColor, textHighlightColor: textHighlightColor, linkHighlightColor: linkHighlightColor, markerColor: markerColor, panelBackgroundColor: panelBackgroundColor, panelHighlightedBackgroundColor: panelHighlightedBackgroundColor, panelPrimaryColor: panelPrimaryColor, panelSecondaryColor: panelSecondaryColor, panelAccentColor: panelAccentColor, tableBorderColor: tableBorderColor, tableHeaderColor: tableHeaderColor, controlColor: controlColor, imageEmptyColor: imageEmptyColor) + return InstantPageTheme(pageBackgroundColor: pageBackgroundColor, textCategories: self.textCategories.withUpdatedFontStyles(sizeMultiplier: sizeMultiplier, forceSerif: forceSerif), serif: forceSerif, codeBlockBackgroundColor: codeBlockBackgroundColor, linkColor: linkColor, textHighlightColor: textHighlightColor, linkHighlightColor: linkHighlightColor, markerColor: markerColor, panelBackgroundColor: panelBackgroundColor, panelHighlightedBackgroundColor: panelHighlightedBackgroundColor, panelPrimaryColor: panelPrimaryColor, panelSecondaryColor: panelSecondaryColor, panelAccentColor: panelAccentColor, tableBorderColor: tableBorderColor, tableHeaderColor: tableHeaderColor, controlColor: controlColor, imageTintColor: imageTintColor) } } @@ -156,7 +156,7 @@ private let lightTheme = InstantPageTheme( tableBorderColor: UIColor(rgb: 0xe2e2e2), tableHeaderColor: UIColor(rgb: 0xf4f4f4), controlColor: UIColor(rgb: 0xc7c7cd), - imageEmptyColor: nil + imageTintColor: nil ) private let sepiaTheme = InstantPageTheme( @@ -185,7 +185,7 @@ private let sepiaTheme = InstantPageTheme( tableBorderColor: UIColor(rgb: 0xddd1b8), tableHeaderColor: UIColor(rgb: 0xf0e7d4), controlColor: UIColor(rgb: 0xddd1b8), - imageEmptyColor: nil + imageTintColor: nil ) private let grayTheme = InstantPageTheme( @@ -214,7 +214,7 @@ private let grayTheme = InstantPageTheme( tableBorderColor: UIColor(rgb: 0x484848), tableHeaderColor: UIColor(rgb: 0x555556), controlColor: UIColor(rgb: 0x484848), - imageEmptyColor: nil + imageTintColor: UIColor(rgb: 0xcecece) ) private let darkTheme = InstantPageTheme( @@ -243,7 +243,7 @@ private let darkTheme = InstantPageTheme( tableBorderColor: UIColor(rgb: 0x303030), tableHeaderColor: UIColor(rgb: 0x131313), controlColor: UIColor(rgb: 0x303030), - imageEmptyColor: UIColor(rgb: 0xb0b0b0) + imageTintColor: UIColor(rgb: 0xb0b0b0) ) private func fontSizeMultiplierForVariant(_ variant: InstantPagePresentationFontSize) -> CGFloat { diff --git a/TelegramUI/PhotoResources.swift b/TelegramUI/PhotoResources.swift index a6b4e8b38d..159ff2a46b 100644 --- a/TelegramUI/PhotoResources.swift +++ b/TelegramUI/PhotoResources.swift @@ -2029,9 +2029,9 @@ func instantPageImageFile(account: Account, fileReference: FileMediaReference, f context.withFlippedContext { c in if var fullSizeImage = fullSizeImage { -// if true || imageIsMonochrome(fullSizeImage), let tintedImage = generateTintedImage(image: UIImage(cgImage: fullSizeImage), color: .white)?.cgImage { -// fullSizeImage = tintedImage -// } + if let color = arguments.emptyColor, imageRequiresInversion(fullSizeImage), let tintedImage = generateTintedImage(image: UIImage(cgImage: fullSizeImage), color: color)?.cgImage { + fullSizeImage = tintedImage + } c.setBlendMode(.normal) c.interpolationQuality = .medium diff --git a/TelegramUI/PresentationCallManager.swift b/TelegramUI/PresentationCallManager.swift index 9607166241..557f0a5303 100644 --- a/TelegramUI/PresentationCallManager.swift +++ b/TelegramUI/PresentationCallManager.swift @@ -228,7 +228,7 @@ public final class PresentationCallManager { private func ringingStatesUpdated(_ ringingStates: [(Peer, CallSessionRingingState, Bool)], currentNetworkType: NetworkType, enableCallKit: Bool) { if let firstState = ringingStates.first { if self.currentCall == nil { - let call = PresentationCall(account: self.account, audioSession: self.audioSession, callSessionManager: self.callSessionManager, callKitIntegration: enableCallKit ? self.callKitIntegration : nil, serializedData: self.callSettings?.1.serializedData, dataSaving: self.callSettings?.0.dataSaving ?? .never, getDeviceAccessData: self.getDeviceAccessData, internalId: firstState.1.id, peerId: firstState.1.peerId, isOutgoing: false, peer: firstState.0, proxyServer: self.proxyServer, currentNetworkType: currentNetworkType, updatedNetworkType: self.networkType) + let call = PresentationCall(account: self.account, audioSession: self.audioSession, callSessionManager: self.callSessionManager, callKitIntegration: enableCallKit ? callKitIntegrationIfEnabled(self.callKitIntegration, settings: self.callSettings?.0) : nil, serializedData: self.callSettings?.1.serializedData, dataSaving: self.callSettings?.0.dataSaving ?? .never, getDeviceAccessData: self.getDeviceAccessData, internalId: firstState.1.id, peerId: firstState.1.peerId, isOutgoing: false, peer: firstState.0, proxyServer: self.proxyServer, currentNetworkType: currentNetworkType, updatedNetworkType: self.networkType) self.currentCall = call self.currentCallPromise.set(.single(call)) self.hasActiveCallsPromise.set(true) diff --git a/TelegramUI/ShareController.swift b/TelegramUI/ShareController.swift index a0bf9ac496..43dbcb552f 100644 --- a/TelegramUI/ShareController.swift +++ b/TelegramUI/ShareController.swift @@ -257,7 +257,7 @@ public final class ShareController: ViewController { showInChat(message) }) } - if let chatPeer = message.peers[message.id.peerId] as? TelegramChannel, messages.count == 1 || sameGroupingKey { + else if let chatPeer = message.peers[message.id.peerId] as? TelegramChannel, messages.count == 1 || sameGroupingKey { if message.id.namespace == Namespaces.Message.Cloud, let addressName = chatPeer.addressName, !addressName.isEmpty { self.defaultAction = ShareControllerAction(title: self.presentationData.strings.ShareMenu_CopyShareLink, action: { [weak self] in UIPasteboard.general.string = "https://t.me/\(addressName)/\(message.id.id)" diff --git a/TelegramUI/StickerPaneSearchContainerNode.swift b/TelegramUI/StickerPaneSearchContainerNode.swift index 9e6e944a45..a8e7bbde00 100644 --- a/TelegramUI/StickerPaneSearchContainerNode.swift +++ b/TelegramUI/StickerPaneSearchContainerNode.swift @@ -172,7 +172,6 @@ final class StickerPaneSearchContainerNode: ASDisplayNode { self.gridNode = GridNode() self.notFoundNode = ASImageNode() - self.notFoundNode.isLayerBacked = true self.notFoundNode.displayWithoutProcessing = true self.notFoundNode.displaysAsynchronously = false self.notFoundNode.clipsToBounds = false