mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-05-21 18:20:41 +00:00
Various improvements
This commit is contained in:
@@ -98,6 +98,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
case fakeGlass(Bool)
|
||||
case forceClearGlass(Bool)
|
||||
case debugRipple(Bool)
|
||||
case debugRichText(Bool)
|
||||
case browserExperiment(Bool)
|
||||
case allForumsHaveTabs(Bool)
|
||||
case enableReactionOverrides(Bool)
|
||||
@@ -137,7 +138,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
return DebugControllerSection.web.rawValue
|
||||
case .keepChatNavigationStack, .skipReadHistory, .alwaysDisplayTyping, .debugRatingLayout, .crashOnSlowQueries, .crashOnMemoryPressure:
|
||||
return DebugControllerSection.experiments.rawValue
|
||||
case .clearTips, .resetNotifications, .crash, .fillLocalSavedMessageCache, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .resetTagHoles, .reindexUnread, .resetCacheIndex, .reindexCache, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .compressedEmojiCache, .storiesJpegExperiment, .checkSerializedData, .enableQuickReactionSwitch, .experimentalCompatibility, .enableDebugDataDisplay, .fakeGlass, .forceClearGlass, .debugRipple, .browserExperiment, .allForumsHaveTabs, .enableReactionOverrides, .restorePurchases, .disableReloginTokens, .liveStreamV2, .experimentalCallMute, .playerV2, .devRequests, .enableUpdates, .pwa, .enableLocalTranslation:
|
||||
case .clearTips, .resetNotifications, .crash, .fillLocalSavedMessageCache, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .resetTagHoles, .reindexUnread, .resetCacheIndex, .reindexCache, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .compressedEmojiCache, .storiesJpegExperiment, .checkSerializedData, .enableQuickReactionSwitch, .experimentalCompatibility, .enableDebugDataDisplay, .fakeGlass, .forceClearGlass, .debugRipple, .debugRichText, .browserExperiment, .allForumsHaveTabs, .enableReactionOverrides, .restorePurchases, .disableReloginTokens, .liveStreamV2, .experimentalCallMute, .playerV2, .devRequests, .enableUpdates, .pwa, .enableLocalTranslation:
|
||||
return DebugControllerSection.experiments.rawValue
|
||||
case .logTranslationRecognition, .resetTranslationStates:
|
||||
return DebugControllerSection.translation.rawValue
|
||||
@@ -234,44 +235,46 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
return 40
|
||||
case .debugRipple:
|
||||
return 41
|
||||
case .browserExperiment:
|
||||
case .debugRichText:
|
||||
return 42
|
||||
case .allForumsHaveTabs:
|
||||
case .browserExperiment:
|
||||
return 43
|
||||
case .enableReactionOverrides:
|
||||
case .allForumsHaveTabs:
|
||||
return 44
|
||||
case .restorePurchases:
|
||||
case .enableReactionOverrides:
|
||||
return 45
|
||||
case .logTranslationRecognition:
|
||||
case .restorePurchases:
|
||||
return 46
|
||||
case .resetTranslationStates:
|
||||
case .logTranslationRecognition:
|
||||
return 47
|
||||
case .compressedEmojiCache:
|
||||
case .resetTranslationStates:
|
||||
return 48
|
||||
case .storiesJpegExperiment:
|
||||
case .compressedEmojiCache:
|
||||
return 49
|
||||
case .disableReloginTokens:
|
||||
case .storiesJpegExperiment:
|
||||
return 50
|
||||
case .checkSerializedData:
|
||||
case .disableReloginTokens:
|
||||
return 51
|
||||
case .enableQuickReactionSwitch:
|
||||
case .checkSerializedData:
|
||||
return 52
|
||||
case .liveStreamV2:
|
||||
case .enableQuickReactionSwitch:
|
||||
return 53
|
||||
case .experimentalCallMute:
|
||||
case .liveStreamV2:
|
||||
return 54
|
||||
case .playerV2:
|
||||
case .experimentalCallMute:
|
||||
return 55
|
||||
case .devRequests:
|
||||
case .playerV2:
|
||||
return 56
|
||||
case .pwa:
|
||||
case .devRequests:
|
||||
return 57
|
||||
case .enableLocalTranslation:
|
||||
case .pwa:
|
||||
return 58
|
||||
case .enableUpdates:
|
||||
case .enableLocalTranslation:
|
||||
return 59
|
||||
case .enableUpdates:
|
||||
return 60
|
||||
case let .preferredVideoCodec(index, _, _, _):
|
||||
return 60 + index
|
||||
return 61 + index
|
||||
case .disableVideoAspectScaling:
|
||||
return 100
|
||||
case .enableNetworkFramework:
|
||||
@@ -1305,6 +1308,16 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
})
|
||||
}).start()
|
||||
})
|
||||
case let .debugRichText(value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, systemStyle: .glass, title: "Debug Text", value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
let _ = arguments.sharedContext.accountManager.transaction ({ transaction in
|
||||
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in
|
||||
var settings = settings?.get(ExperimentalUISettings.self) ?? ExperimentalUISettings.defaultSettings
|
||||
settings.debugRichText = value
|
||||
return PreferencesEntry(settings)
|
||||
})
|
||||
}).start()
|
||||
})
|
||||
case let .browserExperiment(value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, systemStyle: .glass, title: "Inline UI", value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
let _ = arguments.sharedContext.accountManager.transaction ({ transaction in
|
||||
@@ -1583,6 +1596,7 @@ private func debugControllerEntries(context: AccountContext?, sharedContext: Sha
|
||||
entries.append(.fakeGlass(experimentalSettings.fakeGlass))
|
||||
entries.append(.forceClearGlass(experimentalSettings.forceClearGlass))
|
||||
entries.append(.debugRipple(experimentalSettings.debugRipple))
|
||||
entries.append(.debugRichText(experimentalSettings.debugRichText))
|
||||
#if DEBUG
|
||||
entries.append(.browserExperiment(experimentalSettings.browserExperiment))
|
||||
#else
|
||||
|
||||
@@ -1046,7 +1046,7 @@ public func layoutInstantPageBlock(webpage: TelegramMediaWebpage, userLocation:
|
||||
}
|
||||
}
|
||||
|
||||
public func instantPageLayoutForWebPage(_ webPage: TelegramMediaWebpage, instantPage: InstantPage?, userLocation: MediaResourceUserLocation, boundingWidth: CGFloat, safeInset: CGFloat, strings: PresentationStrings, theme: InstantPageTheme, dateTimeFormat: PresentationDateTimeFormat, webEmbedHeights: [Int : CGFloat] = [:], cachedMessageSyntaxHighlight: CachedMessageSyntaxHighlight? = nil) -> InstantPageLayout {
|
||||
public func instantPageLayoutForWebPage(_ webPage: TelegramMediaWebpage, instantPage: InstantPage?, userLocation: MediaResourceUserLocation, boundingWidth: CGFloat, safeInset: CGFloat, strings: PresentationStrings, theme: InstantPageTheme, dateTimeFormat: PresentationDateTimeFormat, webEmbedHeights: [Int : CGFloat] = [:], cachedMessageSyntaxHighlight: CachedMessageSyntaxHighlight? = nil, addFeedback: Bool = true) -> InstantPageLayout {
|
||||
var maybeLoadedContent: TelegramMediaWebpageLoadedContent?
|
||||
if case let .Loaded(content) = webPage.content {
|
||||
maybeLoadedContent = content
|
||||
@@ -1088,7 +1088,7 @@ public func instantPageLayoutForWebPage(_ webPage: TelegramMediaWebpage, instant
|
||||
let closingSpacing = spacingBetweenBlocks(upper: previousBlock, lower: nil)
|
||||
contentSize.height += closingSpacing
|
||||
|
||||
if webPage.webpageId.id != 0 {
|
||||
if webPage.webpageId.id != 0 && addFeedback {
|
||||
let feedbackItem = InstantPageFeedbackItem(frame: CGRect(x: 0.0, y: contentSize.height, width: boundingWidth, height: 40.0), webPage: webPage)
|
||||
contentSize.height += feedbackItem.frame.height
|
||||
items.append(feedbackItem)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
|
||||
private final class InstantPageTileNodeParameters: NSObject {
|
||||
let tile: InstantPageTile
|
||||
@@ -45,9 +46,11 @@ public final class InstantPageTileNode: ASDisplayNode {
|
||||
|
||||
if let parameters = parameters as? InstantPageTileNodeParameters {
|
||||
if !isRasterizing {
|
||||
context.setBlendMode(.copy)
|
||||
context.setFillColor(parameters.backgroundColor.cgColor)
|
||||
context.fill(bounds)
|
||||
if !parameters.backgroundColor.alpha.isZero {
|
||||
context.setBlendMode(.copy)
|
||||
context.setFillColor(parameters.backgroundColor.cgColor)
|
||||
context.fill(bounds)
|
||||
}
|
||||
}
|
||||
|
||||
parameters.tile.draw(context: context)
|
||||
|
||||
@@ -96,6 +96,7 @@ swift_library(
|
||||
"//submodules/AvatarNode",
|
||||
"//submodules/TelegramUI/Components/Chat/ChatMessageSuggestedPostInfoNode",
|
||||
"//submodules/TelegramUI/Components/PremiumAlertController",
|
||||
"//submodules/TelegramUI/Components/Chat/ChatMessageRichDataBubbleContentNode",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
||||
+23
-4
@@ -35,6 +35,7 @@ import ChatMessageDateAndStatusNode
|
||||
import ChatMessageBubbleContentNode
|
||||
import ChatHistoryEntry
|
||||
import ChatMessageTextBubbleContentNode
|
||||
import ChatMessageRichDataBubbleContentNode
|
||||
import ChatMessageItemCommon
|
||||
import ChatMessageReplyInfoNode
|
||||
import ChatMessageCallBubbleContentNode
|
||||
@@ -382,7 +383,11 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([
|
||||
if let attribute = message.attributes.first(where: { $0 is WebpagePreviewMessageAttribute }) as? WebpagePreviewMessageAttribute, attribute.leadingPreview {
|
||||
result.insert((message, ChatMessageWebpageBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default)), at: addedPriceInfo ? 1 : 0)
|
||||
} else {
|
||||
result.append((message, ChatMessageWebpageBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default)))
|
||||
if content.instantPage != nil && item.context.sharedContext.immediateExperimentalUISettings.debugRichText {
|
||||
result.append((message, ChatMessageRichDataBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default)))
|
||||
} else {
|
||||
result.append((message, ChatMessageWebpageBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default)))
|
||||
}
|
||||
}
|
||||
needReactions = false
|
||||
}
|
||||
@@ -1620,6 +1625,12 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
||||
|
||||
var allowFullWidth = false
|
||||
let chatLocationPeerId: PeerId = item.chatLocation.peerId ?? item.content.firstMessage.id.peerId
|
||||
|
||||
var isInlinePage = false
|
||||
if item.context.sharedContext.immediateExperimentalUISettings.debugRichText, let webpage = item.message.media.first(where: { $0 is TelegramMediaWebpage }) as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content, content.instantPage != nil {
|
||||
allowFullWidth = true
|
||||
isInlinePage = true
|
||||
}
|
||||
|
||||
do {
|
||||
let peerId = chatLocationPeerId
|
||||
@@ -1888,6 +1899,10 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
||||
if let subject = item.associatedData.subject, case .messageOptions = subject {
|
||||
needsShareButton = false
|
||||
}
|
||||
|
||||
if isInlinePage {
|
||||
needsShareButton = false
|
||||
}
|
||||
|
||||
var tmpWidth: CGFloat
|
||||
if allowFullWidth {
|
||||
@@ -1895,7 +1910,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
||||
if (needsShareButton && !isSidePanelOpen) || isAd {
|
||||
tmpWidth -= 45.0
|
||||
} else {
|
||||
tmpWidth -= 4.0
|
||||
tmpWidth -= 3.0
|
||||
}
|
||||
} else {
|
||||
tmpWidth = layoutConstants.bubble.maximumWidthFill.widthFor(baseWidth)
|
||||
@@ -2262,7 +2277,11 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
||||
bubbleReactions = ReactionsMessageAttribute(canViewList: false, isTags: false, reactions: [], recentPeers: [], topPeers: [])
|
||||
}
|
||||
if !bubbleReactions.reactions.isEmpty && !item.presentationData.isPreview {
|
||||
bottomNodeMergeStatus = .Right
|
||||
if incoming {
|
||||
bottomNodeMergeStatus = .Both
|
||||
} else {
|
||||
bottomNodeMergeStatus = .Right
|
||||
}
|
||||
}
|
||||
|
||||
var currentCredibilityIcon: (EmojiStatusComponent.Content, UIColor?)?
|
||||
@@ -7348,7 +7367,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
||||
}
|
||||
|
||||
for contentNode in self.contentNodes {
|
||||
if contentNode is ChatMessageMediaBubbleContentNode || contentNode is ChatMessageGiftBubbleContentNode || contentNode is ChatMessageWebpageBubbleContentNode || contentNode is ChatMessageInvoiceBubbleContentNode || contentNode is ChatMessageGameBubbleContentNode || contentNode is ChatMessageInstantVideoBubbleContentNode {
|
||||
if contentNode is ChatMessageMediaBubbleContentNode || contentNode is ChatMessageGiftBubbleContentNode || contentNode is ChatMessageWebpageBubbleContentNode || contentNode is ChatMessageInvoiceBubbleContentNode || contentNode is ChatMessageGameBubbleContentNode || contentNode is ChatMessageInstantVideoBubbleContentNode || contentNode is ChatMessageRichDataBubbleContentNode {
|
||||
contentNode.visibility = mapVisibility(effectiveMediaVisibility, boundsSize: self.bounds.size, insets: self.insets, to: contentNode)
|
||||
} else {
|
||||
contentNode.visibility = mapVisibility(effectiveVisibility, boundsSize: self.bounds.size, insets: self.insets, to: contentNode)
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "ChatMessageRichDataBubbleContentNode",
|
||||
module_name = "ChatMessageRichDataBubbleContentNode",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
copts = [
|
||||
"-warnings-as-errors",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/AsyncDisplayKit",
|
||||
"//submodules/Display",
|
||||
"//submodules/TelegramCore",
|
||||
"//submodules/Postbox",
|
||||
"//submodules/SSignalKit/SwiftSignalKit",
|
||||
"//submodules/AccountContext",
|
||||
"//submodules/InstantPageUI",
|
||||
"//submodules/TelegramUI/Components/Chat/ChatMessageBubbleContentNode",
|
||||
"//submodules/TelegramUI/Components/Chat/ChatMessageItemCommon",
|
||||
"//submodules/TelegramUIPreferences",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
||||
+478
@@ -0,0 +1,478 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import TelegramCore
|
||||
import Postbox
|
||||
import SwiftSignalKit
|
||||
import AccountContext
|
||||
import ChatMessageBubbleContentNode
|
||||
import ChatMessageItemCommon
|
||||
import InstantPageUI
|
||||
import TelegramUIPreferences
|
||||
|
||||
public class ChatMessageRichDataBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
public final class ContainerNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
private let containerNode: ContainerNode
|
||||
private var currentLayoutTiles: [InstantPageTile] = []
|
||||
private var visibleTiles: [Int: InstantPageTileNode] = [:]
|
||||
private var visibleItemsWithNodes: [Int: InstantPageNode] = [:]
|
||||
private var currentPageLayout: (boundingWidth: CGFloat, layout: InstantPageLayout)?
|
||||
private var distanceThresholdGroupCount: [Int: Int] = [:]
|
||||
private var currentLayoutItemsWithNodes: [InstantPageItem] = []
|
||||
private var currentExpandedDetails: [Int : Bool]?
|
||||
|
||||
override public var visibility: ListViewItemNodeVisibility {
|
||||
didSet {
|
||||
if oldValue != self.visibility {
|
||||
self.updateVisibility()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
required public init() {
|
||||
self.containerNode = ContainerNode()
|
||||
self.containerNode.clipsToBounds = true
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.containerNode)
|
||||
}
|
||||
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
}
|
||||
|
||||
override public func asyncLayoutContent() -> (_ item: ChatMessageBubbleContentItem, _ layoutConstants: ChatMessageItemLayoutConstants, _ preparePosition: ChatMessageBubblePreparePosition, _ messageSelection: Bool?, _ constrainedSize: CGSize, _ avatarInset: CGFloat) -> (ChatMessageBubbleContentProperties, CGSize?, CGFloat, (CGSize, ChatMessageBubbleContentPosition) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool, ListViewItemApply?) -> Void))) {
|
||||
let currentPageLayout = self.currentPageLayout
|
||||
let previousCurrentLayoutTiles = self.currentLayoutTiles
|
||||
|
||||
return { [weak self] item, layoutConstants, _, _, _, _ in
|
||||
let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: false, headerSpacing: 0.0, hidesBackground: .never, forceFullCorners: false, forceAlignment: .none)
|
||||
|
||||
return (contentProperties, nil, CGFloat.greatestFiniteMagnitude, { constrainedSize, position in
|
||||
let suggestedBoundingWidth: CGFloat = constrainedSize.width
|
||||
|
||||
return (suggestedBoundingWidth, { boundingWidth in
|
||||
var boundingSize = CGSize(width: boundingWidth, height: 0.0)
|
||||
|
||||
var pageLayout: InstantPageLayout?
|
||||
var currentLayoutTiles: [InstantPageTile] = []
|
||||
|
||||
if let webpage = item.message.media.first(where: { $0 is TelegramMediaWebpage }) as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content, let instantPage = content.instantPage {
|
||||
if let current = currentPageLayout, current.boundingWidth == boundingSize.width {
|
||||
pageLayout = current.layout
|
||||
currentLayoutTiles = previousCurrentLayoutTiles
|
||||
} else {
|
||||
let pageTheme = instantPageThemeForType(item.presentationData.theme.theme.overallDarkAppearance ? .dark : .light, settings: InstantPagePresentationSettings(
|
||||
themeType: item.presentationData.theme.theme.overallDarkAppearance ? .dark : .light,
|
||||
fontSize: .standard,
|
||||
forceSerif: false,
|
||||
autoNightMode: false,
|
||||
ignoreAutoNightModeUntil: 0
|
||||
))
|
||||
pageLayout = instantPageLayoutForWebPage(webpage, instantPage: instantPage._parse(), userLocation: .other, boundingWidth: boundingWidth - 2.0, safeInset: 0.0, strings: item.presentationData.strings, theme: pageTheme, dateTimeFormat: item.presentationData.dateTimeFormat, webEmbedHeights: [:], addFeedback: false)
|
||||
if let pageLayout {
|
||||
currentLayoutTiles = instantPageTilesFromLayout(pageLayout, boundingWidth: boundingWidth)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let pageLayout {
|
||||
boundingSize.height = pageLayout.contentSize.height + 2.0
|
||||
}
|
||||
|
||||
return (boundingSize, { animation, synchronousLoads, itemApply in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.item = item
|
||||
|
||||
self.containerNode.frame = CGRect(origin: CGPoint(x: 1.0, y: 1.0), size: CGSize(width: boundingSize.width - 2.0, height: boundingSize.height - 2.0))
|
||||
|
||||
if let pageLayout {
|
||||
self.currentPageLayout = (boundingSize.width, pageLayout)
|
||||
self.currentLayoutTiles = currentLayoutTiles
|
||||
|
||||
var distanceThresholdGroupCount: [Int : Int] = [:]
|
||||
|
||||
for item in pageLayout.items {
|
||||
if item.wantsNode {
|
||||
self.currentLayoutItemsWithNodes.append(item)
|
||||
|
||||
if let group = item.distanceThresholdGroup() {
|
||||
let count: Int
|
||||
if let currentCount = distanceThresholdGroupCount[Int(group)] {
|
||||
count = currentCount
|
||||
} else {
|
||||
count = 0
|
||||
}
|
||||
distanceThresholdGroupCount[Int(group)] = count + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.distanceThresholdGroupCount = distanceThresholdGroupCount
|
||||
} else {
|
||||
self.currentPageLayout = nil
|
||||
self.currentLayoutTiles = []
|
||||
self.distanceThresholdGroupCount = [:]
|
||||
}
|
||||
|
||||
self.updateVisibility()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private func effectiveFrameForTile(_ tile: InstantPageTile) -> CGRect {
|
||||
let layoutOrigin = tile.frame.origin
|
||||
let origin = layoutOrigin
|
||||
return CGRect(origin: origin, size: tile.frame.size)
|
||||
}
|
||||
|
||||
private func updateVisibility() {
|
||||
switch self.visibility {
|
||||
case .none:
|
||||
self.updateVisibleItems(visibleBounds: CGRect(), animated: false)
|
||||
case let .visible(_, subRect):
|
||||
self.updateVisibleItems(visibleBounds: subRect, animated: false)
|
||||
}
|
||||
}
|
||||
|
||||
private func updateVisibleItems(visibleBounds: CGRect, animated: Bool = false) {
|
||||
guard let messageItem = self.item else {
|
||||
return
|
||||
}
|
||||
let pageTheme = instantPageThemeForType(messageItem.presentationData.theme.theme.overallDarkAppearance ? .dark : .light, settings: InstantPagePresentationSettings(
|
||||
themeType: messageItem.presentationData.theme.theme.overallDarkAppearance ? .dark : .light,
|
||||
fontSize: .standard,
|
||||
forceSerif: false,
|
||||
autoNightMode: false,
|
||||
ignoreAutoNightModeUntil: 0
|
||||
))
|
||||
let sourceLocation = InstantPageSourceLocation(userLocation: .other, peerType: .otherPrivate)
|
||||
|
||||
var visibleTileIndices = Set<Int>()
|
||||
var visibleItemIndices = Set<Int>()
|
||||
|
||||
var topNode: ASDisplayNode?
|
||||
let topTileNode = topNode
|
||||
if let containerSubnodes = self.containerNode.subnodes {
|
||||
for node in containerSubnodes.reversed() {
|
||||
if let node = node as? InstantPageTileNode {
|
||||
topNode = node
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var collapseOffset: CGFloat = 0.0
|
||||
collapseOffset = 0.0
|
||||
let transition: ContainedViewLayoutTransition
|
||||
if animated {
|
||||
transition = .animated(duration: 0.3, curve: .spring)
|
||||
} else {
|
||||
transition = .immediate
|
||||
}
|
||||
|
||||
var itemIndex = -1
|
||||
var embedIndex = -1
|
||||
var detailsIndex = -1
|
||||
|
||||
var previousDetailsNode: InstantPageDetailsNode?
|
||||
|
||||
for item in self.currentLayoutItemsWithNodes {
|
||||
itemIndex += 1
|
||||
if item is InstantPageWebEmbedItem {
|
||||
embedIndex += 1
|
||||
}
|
||||
if let imageItem = item as? InstantPageImageItem, case .webpage = imageItem.media.media {
|
||||
embedIndex += 1
|
||||
}
|
||||
if item is InstantPageDetailsItem {
|
||||
detailsIndex += 1
|
||||
}
|
||||
|
||||
var itemThreshold: CGFloat = 0.0
|
||||
if let group = item.distanceThresholdGroup() {
|
||||
var count: Int = 0
|
||||
if let currentCount = self.distanceThresholdGroupCount[group] {
|
||||
count = currentCount
|
||||
}
|
||||
itemThreshold = item.distanceThresholdWithGroupCount(count)
|
||||
}
|
||||
|
||||
let itemFrame = item.frame.offsetBy(dx: 0.0, dy: -collapseOffset)
|
||||
var thresholdedItemFrame = itemFrame
|
||||
thresholdedItemFrame.origin.y -= itemThreshold
|
||||
thresholdedItemFrame.size.height += itemThreshold * 2.0
|
||||
|
||||
if visibleBounds.intersects(thresholdedItemFrame) {
|
||||
visibleItemIndices.insert(itemIndex)
|
||||
|
||||
var itemNode = self.visibleItemsWithNodes[itemIndex]
|
||||
if let currentItemNode = itemNode {
|
||||
if !item.matchesNode(currentItemNode) {
|
||||
currentItemNode.removeFromSupernode()
|
||||
self.visibleItemsWithNodes.removeValue(forKey: itemIndex)
|
||||
itemNode = nil
|
||||
}
|
||||
}
|
||||
|
||||
if itemNode == nil {
|
||||
let itemIndex = itemIndex
|
||||
//let embedIndex = embedIndex
|
||||
//let detailsIndex = detailsIndex
|
||||
if let newNode = item.node(context: messageItem.context, strings: messageItem.presentationData.strings, nameDisplayOrder: messageItem.presentationData.nameDisplayOrder, theme: pageTheme, sourceLocation: sourceLocation, openMedia: { [weak self] media in
|
||||
let _ = self
|
||||
//self?.openMedia(media)
|
||||
}, longPressMedia: { [weak self] media in
|
||||
//self?.longPressMedia(media)
|
||||
let _ = self
|
||||
}, activatePinchPreview: { [weak self] sourceNode in
|
||||
/*guard let strongSelf = self, let controller = strongSelf.controller else {
|
||||
return
|
||||
}
|
||||
let pinchController = makePinchController(sourceNode: sourceNode, getContentAreaInScreenSpace: {
|
||||
guard let strongSelf = self else {
|
||||
return CGRect()
|
||||
}
|
||||
|
||||
let localRect = CGRect(origin: CGPoint(x: 0.0, y: strongSelf.navigationBar.frame.maxY), size: CGSize(width: strongSelf.bounds.width, height: strongSelf.bounds.height - strongSelf.navigationBar.frame.maxY))
|
||||
return strongSelf.view.convert(localRect, to: nil)
|
||||
})
|
||||
controller.window?.presentInGlobalOverlay(pinchController)*/
|
||||
let _ = self
|
||||
}, pinchPreviewFinished: { [weak self] itemNode in
|
||||
/*guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
for (_, listItemNode) in strongSelf.visibleItemsWithNodes {
|
||||
if let listItemNode = listItemNode as? InstantPagePeerReferenceNode {
|
||||
if listItemNode.frame.intersects(itemNode.frame) && listItemNode.frame.maxY <= itemNode.frame.maxY + 2.0 {
|
||||
listItemNode.layer.animateAlpha(from: 0.0, to: listItemNode.alpha, duration: 0.25)
|
||||
break
|
||||
}
|
||||
}
|
||||
}*/
|
||||
let _ = self
|
||||
}, openPeer: { [weak self] peerId in
|
||||
let _ = self
|
||||
//self?.openPeer(peerId)
|
||||
}, openUrl: { [weak self] url in
|
||||
let _ = self
|
||||
//self?.openUrl(url)
|
||||
}, updateWebEmbedHeight: { [weak self] height in
|
||||
let _ = self
|
||||
//self?.updateWebEmbedHeight(embedIndex, height)
|
||||
}, updateDetailsExpanded: { [weak self] expanded in
|
||||
let _ = self
|
||||
//self?.updateDetailsExpanded(detailsIndex, expanded)
|
||||
}, currentExpandedDetails: self.currentExpandedDetails, getPreloadedResource: { _ in return nil }) {
|
||||
newNode.frame = itemFrame
|
||||
newNode.updateLayout(size: itemFrame.size, transition: transition)
|
||||
if let topNode = topNode {
|
||||
self.containerNode.insertSubnode(newNode, aboveSubnode: topNode)
|
||||
} else {
|
||||
self.containerNode.insertSubnode(newNode, at: 0)
|
||||
}
|
||||
topNode = newNode
|
||||
self.visibleItemsWithNodes[itemIndex] = newNode
|
||||
itemNode = newNode
|
||||
|
||||
if let itemNode = itemNode as? InstantPageDetailsNode {
|
||||
itemNode.requestLayoutUpdate = { [weak self] animated in
|
||||
let _ = self
|
||||
/*if let strongSelf = self {
|
||||
strongSelf.updateVisibleItems(visibleBounds: strongSelf.scrollNode.view.bounds, animated: animated)
|
||||
}*/
|
||||
}
|
||||
|
||||
if let previousDetailsNode = previousDetailsNode {
|
||||
if itemNode.frame.minY - previousDetailsNode.frame.maxY < 1.0 {
|
||||
itemNode.previousNode = previousDetailsNode
|
||||
}
|
||||
}
|
||||
previousDetailsNode = itemNode
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let itemNode = itemNode, itemNode.frame != itemFrame {
|
||||
transition.updateFrame(node: itemNode, frame: itemFrame)
|
||||
itemNode.updateLayout(size: itemFrame.size, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
if let itemNode = itemNode as? InstantPageDetailsNode {
|
||||
itemNode.updateVisibleItems(visibleBounds: visibleBounds.offsetBy(dx: -itemNode.frame.minX, dy: -itemNode.frame.minY), animated: animated)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
topNode = topTileNode
|
||||
|
||||
var tileIndex = -1
|
||||
for tile in self.currentLayoutTiles {
|
||||
tileIndex += 1
|
||||
|
||||
let tileFrame = effectiveFrameForTile(tile)
|
||||
var tileVisibleFrame = tileFrame
|
||||
tileVisibleFrame.origin.y -= 400.0
|
||||
tileVisibleFrame.size.height += 400.0 * 2.0
|
||||
if tileVisibleFrame.intersects(visibleBounds) {
|
||||
visibleTileIndices.insert(tileIndex)
|
||||
|
||||
if self.visibleTiles[tileIndex] == nil {
|
||||
let tileNode = InstantPageTileNode(tile: tile, backgroundColor: .clear)
|
||||
tileNode.frame = tileFrame
|
||||
if let topNode = topNode {
|
||||
self.containerNode.insertSubnode(tileNode, aboveSubnode: topNode)
|
||||
} else {
|
||||
self.containerNode.insertSubnode(tileNode, at: 0)
|
||||
}
|
||||
topNode = tileNode
|
||||
self.visibleTiles[tileIndex] = tileNode
|
||||
} else {
|
||||
if let tileNode = self.visibleTiles[tileIndex] {
|
||||
tileNode.update(tile: tile, backgroundColor: .clear)
|
||||
if tileNode.frame != tileFrame {
|
||||
transition.updateFrame(node: tileNode, frame: tileFrame)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var removeTileIndices: [Int] = []
|
||||
for (index, tileNode) in self.visibleTiles {
|
||||
if !visibleTileIndices.contains(index) {
|
||||
removeTileIndices.append(index)
|
||||
tileNode.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
for index in removeTileIndices {
|
||||
self.visibleTiles.removeValue(forKey: index)
|
||||
}
|
||||
|
||||
var removeItemIndices: [Int] = []
|
||||
for (index, itemNode) in self.visibleItemsWithNodes {
|
||||
if !visibleItemIndices.contains(index) {
|
||||
removeItemIndices.append(index)
|
||||
itemNode.removeFromSupernode()
|
||||
} else {
|
||||
var itemFrame = itemNode.frame
|
||||
let itemThreshold: CGFloat = 200.0
|
||||
itemFrame.origin.y -= itemThreshold
|
||||
itemFrame.size.height += itemThreshold * 2.0
|
||||
itemNode.updateIsVisible(visibleBounds.intersects(itemFrame))
|
||||
}
|
||||
}
|
||||
for index in removeItemIndices {
|
||||
self.visibleItemsWithNodes.removeValue(forKey: index)
|
||||
}
|
||||
}
|
||||
|
||||
override public func animateInsertion(_ currentTimestamp: Double, duration: Double) {
|
||||
/*self.textNode.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
if let statusNode = self.statusNode, statusNode.alpha != 0.0 {
|
||||
statusNode.layer.animateAlpha(from: 0.0, to: statusNode.alpha, duration: 0.2)
|
||||
}*/
|
||||
}
|
||||
|
||||
override public func animateAdded(_ currentTimestamp: Double, duration: Double) {
|
||||
/*self.textNode.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
if let statusNode = self.statusNode, statusNode.alpha != 0.0 {
|
||||
statusNode.layer.animateAlpha(from: 0.0, to: statusNode.alpha, duration: 0.2)
|
||||
}*/
|
||||
}
|
||||
|
||||
override public func animateRemoved(_ currentTimestamp: Double, duration: Double) {
|
||||
/*self.textNode.textNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||
if let statusNode = self.statusNode, statusNode.alpha != 0.0 {
|
||||
statusNode.layer.animateAlpha(from: statusNode.alpha, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||
}*/
|
||||
}
|
||||
|
||||
override public func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction {
|
||||
if case .tap = gesture {
|
||||
} else {
|
||||
if let item = self.item, let subject = item.associatedData.subject, case .messageOptions = subject {
|
||||
return ChatMessageBubbleContentTapAction(content: .none)
|
||||
}
|
||||
}
|
||||
|
||||
/*func makeActivate(_ urlRange: NSRange?) -> (() -> Promise<Bool>?)? {
|
||||
return { [weak self] in
|
||||
guard let self else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let promise = Promise<Bool>()
|
||||
|
||||
self.linkProgressDisposable?.dispose()
|
||||
|
||||
if self.linkProgressRange != nil {
|
||||
self.linkProgressRange = nil
|
||||
self.updateLinkProgressState()
|
||||
}
|
||||
|
||||
self.linkProgressDisposable = (promise.get() |> deliverOnMainQueue).startStrict(next: { [weak self] value in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let updatedRange: NSRange? = value ? urlRange : nil
|
||||
if self.linkProgressRange != updatedRange {
|
||||
self.linkProgressRange = updatedRange
|
||||
self.updateLinkProgressState()
|
||||
}
|
||||
})
|
||||
|
||||
return promise
|
||||
}
|
||||
}*/
|
||||
|
||||
return ChatMessageBubbleContentTapAction(content: .none)
|
||||
}
|
||||
|
||||
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
return super.hitTest(point, with: event)
|
||||
}
|
||||
|
||||
override public func updateTouchesAtPoint(_ point: CGPoint?) {
|
||||
}
|
||||
|
||||
override public func updateSearchTextHighlightState(text: String?, messages: [MessageIndex]?) {
|
||||
}
|
||||
|
||||
override public func willUpdateIsExtractedToContextPreview(_ value: Bool) {
|
||||
}
|
||||
|
||||
override public func updateIsExtractedToContextPreview(_ value: Bool) {
|
||||
}
|
||||
|
||||
override public func reactionTargetView(value: MessageReaction.Reaction) -> UIView? {
|
||||
/*if let statusNode = self.statusNode, !statusNode.isHidden {
|
||||
return statusNode.reactionView(value: value)
|
||||
}*/
|
||||
return nil
|
||||
}
|
||||
|
||||
override public func messageEffectTargetView() -> UIView? {
|
||||
/*if let statusNode = self.statusNode, !statusNode.isHidden {
|
||||
return statusNode.messageEffectTargetView()
|
||||
}*/
|
||||
return nil
|
||||
}
|
||||
|
||||
override public func getStatusNode() -> ASDisplayNode? {
|
||||
return nil
|
||||
//return self.statusNode
|
||||
}
|
||||
}
|
||||
+8
-1
@@ -822,6 +822,7 @@ private final class TextStyleEditSheetComponent: Component {
|
||||
theme: theme,
|
||||
strings: environmentValue.strings,
|
||||
actionTitle: actionButtonTitle,
|
||||
displayProgress: self.isActionInProgress,
|
||||
action: isMainActionEnabled ? performMainAction : nil
|
||||
)
|
||||
),
|
||||
@@ -998,17 +999,20 @@ private final class ActionButtonsComponent: Component {
|
||||
let theme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let actionTitle: String
|
||||
let displayProgress: Bool
|
||||
let action: (() -> Void)?
|
||||
|
||||
init(
|
||||
theme: PresentationTheme,
|
||||
strings: PresentationStrings,
|
||||
actionTitle: String,
|
||||
displayProgress: Bool,
|
||||
action: (() -> Void)?
|
||||
) {
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.actionTitle = actionTitle
|
||||
self.displayProgress = displayProgress
|
||||
self.action = action
|
||||
}
|
||||
|
||||
@@ -1022,6 +1026,9 @@ private final class ActionButtonsComponent: Component {
|
||||
if lhs.actionTitle != rhs.actionTitle {
|
||||
return false
|
||||
}
|
||||
if lhs.displayProgress != rhs.displayProgress {
|
||||
return false
|
||||
}
|
||||
if (lhs.action == nil) != (rhs.action == nil) {
|
||||
return false
|
||||
}
|
||||
@@ -1070,7 +1077,7 @@ private final class ActionButtonsComponent: Component {
|
||||
))
|
||||
),
|
||||
isEnabled: component.action != nil,
|
||||
displaysProgress: false,
|
||||
displaysProgress: component.displayProgress,
|
||||
action: { [weak self] in
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
|
||||
@@ -72,6 +72,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
public var enablePWA: Bool
|
||||
public var forceClearGlass: Bool
|
||||
public var debugRipple: Bool
|
||||
public var debugRichText: Bool
|
||||
|
||||
public static var defaultSettings: ExperimentalUISettings {
|
||||
return ExperimentalUISettings(
|
||||
@@ -121,7 +122,8 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
enableUpdates: false,
|
||||
enablePWA: false,
|
||||
forceClearGlass: false,
|
||||
debugRipple: false
|
||||
debugRipple: false,
|
||||
debugRichText: false
|
||||
)
|
||||
}
|
||||
|
||||
@@ -172,7 +174,8 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
enableUpdates: Bool,
|
||||
enablePWA: Bool,
|
||||
forceClearGlass: Bool,
|
||||
debugRipple: Bool
|
||||
debugRipple: Bool,
|
||||
debugRichText: Bool
|
||||
) {
|
||||
self.keepChatNavigationStack = keepChatNavigationStack
|
||||
self.skipReadHistory = skipReadHistory
|
||||
@@ -221,6 +224,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
self.enablePWA = enablePWA
|
||||
self.forceClearGlass = forceClearGlass
|
||||
self.debugRipple = debugRipple
|
||||
self.debugRichText = debugRichText
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
@@ -273,6 +277,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
self.enablePWA = try container.decodeIfPresent(Bool.self, forKey: "enablePWA") ?? false
|
||||
self.forceClearGlass = try container.decodeIfPresent(Bool.self, forKey: "forceClearGlass") ?? false
|
||||
self.debugRipple = try container.decodeIfPresent(Bool.self, forKey: "debugRipple") ?? false
|
||||
self.debugRichText = try container.decodeIfPresent(Bool.self, forKey: "debugRichText") ?? false
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
@@ -325,6 +330,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
try container.encodeIfPresent(self.enablePWA, forKey: "enablePWA")
|
||||
try container.encodeIfPresent(self.forceClearGlass, forKey: "forceClearGlass")
|
||||
try container.encodeIfPresent(self.debugRipple, forKey: "debugRipple")
|
||||
try container.encodeIfPresent(self.debugRichText, forKey: "debugRichText")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user