mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
[WIP] Quotes
This commit is contained in:
parent
679a10cbdf
commit
ecb4e13969
@ -330,10 +330,10 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
|
||||
updatedItems.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_Back, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.contextMenu.primaryColor)
|
||||
}, iconPosition: .left, action: { c, _ in
|
||||
c.setItems(chatContextMenuItems(context: context, peerId: peerId, promoInfo: promoInfo, source: source, chatListController: chatListController, joined: joined) |> map { ContextController.Items(content: .list($0)) }, minHeight: nil)
|
||||
c.setItems(chatContextMenuItems(context: context, peerId: peerId, promoInfo: promoInfo, source: source, chatListController: chatListController, joined: joined) |> map { ContextController.Items(content: .list($0)) }, minHeight: nil, animated: true)
|
||||
})))
|
||||
|
||||
c.setItems(.single(ContextController.Items(content: .list(updatedItems))), minHeight: nil)
|
||||
c.setItems(.single(ContextController.Items(content: .list(updatedItems))), minHeight: nil, animated: true)
|
||||
})))
|
||||
}
|
||||
}
|
||||
@ -658,7 +658,7 @@ func chatForumTopicMenuItems(context: AccountContext, peerId: PeerId, threadId:
|
||||
})))
|
||||
|
||||
//c.pushItems(items: .single(ContextController.Items(content: .list(subItems))))
|
||||
c.setItems(.single(ContextController.Items(content: .list(subItems))), minHeight: nil)
|
||||
c.setItems(.single(ContextController.Items(content: .list(subItems))), minHeight: nil, animated: true)
|
||||
})))
|
||||
|
||||
items.append(.separator)
|
||||
@ -813,7 +813,7 @@ func chatForumTopicMenuItems(context: AccountContext, peerId: PeerId, threadId:
|
||||
], title: nil, text: presentationData.strings.PeerInfo_TooltipMutedForever, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
|
||||
})))
|
||||
|
||||
c.setItems(.single(ContextController.Items(content: .list(items))), minHeight: nil)
|
||||
c.setItems(.single(ContextController.Items(content: .list(items))), minHeight: nil, animated: true)
|
||||
}
|
||||
})))
|
||||
|
||||
|
@ -24,7 +24,7 @@ public protocol ContextControllerProtocol: ViewController {
|
||||
func dismiss(completion: (() -> Void)?)
|
||||
|
||||
func getActionsMinHeight() -> ContextController.ActionsHeight?
|
||||
func setItems(_ items: Signal<ContextController.Items, NoError>, minHeight: ContextController.ActionsHeight?)
|
||||
func setItems(_ items: Signal<ContextController.Items, NoError>, minHeight: ContextController.ActionsHeight?, animated: Bool)
|
||||
func setItems(_ items: Signal<ContextController.Items, NoError>, minHeight: ContextController.ActionsHeight?, previousActionsTransition: ContextController.PreviousActionsTransition)
|
||||
func pushItems(items: Signal<ContextController.Items, NoError>)
|
||||
func popItems()
|
||||
@ -1326,9 +1326,9 @@ final class ContextControllerNode: ViewControllerTracingNode, UIScrollViewDelega
|
||||
}
|
||||
}
|
||||
|
||||
func setItemsSignal(items: Signal<ContextController.Items, NoError>, minHeight: ContextController.ActionsHeight?, previousActionsTransition: ContextController.PreviousActionsTransition) {
|
||||
func setItemsSignal(items: Signal<ContextController.Items, NoError>, minHeight: ContextController.ActionsHeight?, previousActionsTransition: ContextController.PreviousActionsTransition, animated: Bool) {
|
||||
if let sourceContainer = self.sourceContainer {
|
||||
sourceContainer.setItems(items: items, animated: false)
|
||||
sourceContainer.setItems(items: items, animated: animated)
|
||||
} else {
|
||||
self.legacyItems = items
|
||||
self.itemsDisposable.set((items
|
||||
@ -2555,12 +2555,12 @@ public final class ContextController: ViewController, StandalonePresentableContr
|
||||
return nil
|
||||
}
|
||||
|
||||
public func setItems(_ items: Signal<ContextController.Items, NoError>, minHeight: ContextController.ActionsHeight?) {
|
||||
public func setItems(_ items: Signal<ContextController.Items, NoError>, minHeight: ContextController.ActionsHeight?, animated: Bool) {
|
||||
//self.items = items
|
||||
|
||||
if self.isNodeLoaded {
|
||||
self.immediateItemsTransitionAnimation = false
|
||||
self.controllerNode.setItemsSignal(items: items, minHeight: minHeight, previousActionsTransition: .scale)
|
||||
self.controllerNode.setItemsSignal(items: items, minHeight: minHeight, previousActionsTransition: .scale, animated: animated)
|
||||
} else {
|
||||
assertionFailure()
|
||||
}
|
||||
@ -2570,7 +2570,7 @@ public final class ContextController: ViewController, StandalonePresentableContr
|
||||
//self.items = items
|
||||
|
||||
if self.isNodeLoaded {
|
||||
self.controllerNode.setItemsSignal(items: items, minHeight: minHeight, previousActionsTransition: previousActionsTransition)
|
||||
self.controllerNode.setItemsSignal(items: items, minHeight: minHeight, previousActionsTransition: previousActionsTransition, animated: true)
|
||||
} else {
|
||||
assertionFailure()
|
||||
}
|
||||
|
@ -234,7 +234,6 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
private let requestAnimateOut: (ContextMenuActionResult, @escaping () -> Void) -> Void
|
||||
private let source: ContentSource
|
||||
|
||||
private let backgroundNode: NavigationBackgroundNode
|
||||
private let dismissTapNode: ASDisplayNode
|
||||
private let dismissAccessibilityArea: AccessibilityAreaNode
|
||||
private let clippingNode: ASDisplayNode
|
||||
@ -282,8 +281,6 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
self.requestAnimateOut = requestAnimateOut
|
||||
self.source = source
|
||||
|
||||
self.backgroundNode = NavigationBackgroundNode(color: .clear, enableBlur: false)
|
||||
|
||||
self.dismissTapNode = ASDisplayNode()
|
||||
|
||||
self.dismissAccessibilityArea = AccessibilityAreaNode()
|
||||
@ -330,7 +327,6 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
self.view.addSubview(self.scroller)
|
||||
self.scroller.isHidden = true
|
||||
|
||||
self.addSubnode(self.backgroundNode)
|
||||
self.addSubnode(self.clippingNode)
|
||||
self.clippingNode.addSubnode(self.scrollNode)
|
||||
self.scrollNode.addSubnode(self.dismissTapNode)
|
||||
@ -553,7 +549,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
) {
|
||||
self.validLayout = layout
|
||||
|
||||
let contentActionsSpacing: CGFloat = 7.0
|
||||
var contentActionsSpacing: CGFloat = 7.0
|
||||
let actionsEdgeInset: CGFloat
|
||||
let actionsSideInset: CGFloat
|
||||
let topInset: CGFloat = layout.insets(options: .statusBar).top + 8.0
|
||||
@ -571,37 +567,17 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
|
||||
switch self.source {
|
||||
case .location, .reference:
|
||||
self.backgroundNode.updateColor(
|
||||
color: .clear,
|
||||
enableBlur: false,
|
||||
forceKeepBlur: false,
|
||||
transition: .immediate
|
||||
)
|
||||
actionsEdgeInset = 16.0
|
||||
actionsSideInset = 6.0
|
||||
case .extracted:
|
||||
self.backgroundNode.updateColor(
|
||||
color: presentationData.theme.contextMenu.dimColor,
|
||||
enableBlur: true,
|
||||
forceKeepBlur: true,
|
||||
transition: .immediate
|
||||
)
|
||||
actionsEdgeInset = 12.0
|
||||
actionsSideInset = 6.0
|
||||
case .controller:
|
||||
self.backgroundNode.updateColor(
|
||||
color: presentationData.theme.contextMenu.dimColor,
|
||||
enableBlur: true,
|
||||
forceKeepBlur: true,
|
||||
transition: .immediate
|
||||
)
|
||||
actionsEdgeInset = 12.0
|
||||
actionsSideInset = -2.0
|
||||
contentActionsSpacing += 3.0
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: layout.size), beginWithCurrentState: true)
|
||||
self.backgroundNode.update(size: layout.size, transition: transition)
|
||||
|
||||
transition.updateFrame(node: self.clippingNode, frame: CGRect(origin: CGPoint(), size: layout.size), beginWithCurrentState: true)
|
||||
if self.scrollNode.frame != CGRect(origin: CGPoint(), size: layout.size) {
|
||||
transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: layout.size), beginWithCurrentState: true)
|
||||
@ -1085,8 +1061,6 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
|
||||
self.scroller.contentOffset = CGPoint(x: 0.0, y: defaultScrollY)
|
||||
|
||||
self.backgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
|
||||
let animationInContentYDistance: CGFloat
|
||||
let currentContentScreenFrame: CGRect
|
||||
if let contentNode = itemContentNode {
|
||||
@ -1473,8 +1447,6 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
additive: true
|
||||
)
|
||||
|
||||
self.backgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration, removeOnCompletion: false)
|
||||
|
||||
if let reactionContextNode = self.reactionContextNode {
|
||||
reactionContextNode.animateOut(to: currentContentScreenFrame, animatingOutToReaction: self.reactionContextNodeIsAnimatingOut)
|
||||
}
|
||||
|
@ -335,6 +335,8 @@ final class ContextSourceContainer: ASDisplayNode {
|
||||
|
||||
private weak var controller: ContextController?
|
||||
|
||||
private let backgroundNode: NavigationBackgroundNode
|
||||
|
||||
var sources: [Source] = []
|
||||
var activeIndex: Int = 0
|
||||
|
||||
@ -360,8 +362,12 @@ final class ContextSourceContainer: ASDisplayNode {
|
||||
init(controller: ContextController, configuration: ContextController.Configuration) {
|
||||
self.controller = controller
|
||||
|
||||
self.backgroundNode = NavigationBackgroundNode(color: .clear, enableBlur: false)
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.backgroundNode)
|
||||
|
||||
for i in 0 ..< configuration.sources.count {
|
||||
let source = configuration.sources[i]
|
||||
|
||||
@ -443,6 +449,8 @@ final class ContextSourceContainer: ASDisplayNode {
|
||||
}
|
||||
|
||||
func animateIn() {
|
||||
self.backgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
|
||||
if let activeSource = self.activeSource {
|
||||
activeSource.animateIn()
|
||||
}
|
||||
@ -452,6 +460,8 @@ final class ContextSourceContainer: ASDisplayNode {
|
||||
}
|
||||
|
||||
func animateOut(result: ContextMenuActionResult, completion: @escaping () -> Void) {
|
||||
self.backgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||
|
||||
if let tabSelectorView = self.tabSelector?.view {
|
||||
tabSelectorView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||
}
|
||||
@ -543,6 +553,35 @@ final class ContextSourceContainer: ASDisplayNode {
|
||||
|
||||
var childLayout = layout
|
||||
|
||||
if let activeSource = self.activeSource {
|
||||
switch activeSource.source {
|
||||
case .location, .reference:
|
||||
self.backgroundNode.updateColor(
|
||||
color: .clear,
|
||||
enableBlur: false,
|
||||
forceKeepBlur: false,
|
||||
transition: .immediate
|
||||
)
|
||||
case .extracted:
|
||||
self.backgroundNode.updateColor(
|
||||
color: presentationData.theme.contextMenu.dimColor,
|
||||
enableBlur: true,
|
||||
forceKeepBlur: true,
|
||||
transition: .immediate
|
||||
)
|
||||
case .controller:
|
||||
self.backgroundNode.updateColor(
|
||||
color: presentationData.theme.contextMenu.dimColor,
|
||||
enableBlur: true,
|
||||
forceKeepBlur: true,
|
||||
transition: .immediate
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: layout.size), beginWithCurrentState: true)
|
||||
self.backgroundNode.update(size: layout.size, transition: transition)
|
||||
|
||||
if self.sources.count > 1 {
|
||||
let tabSelector: ComponentView<Empty>
|
||||
if let current = self.tabSelector {
|
||||
@ -561,6 +600,9 @@ final class ContextSourceContainer: ASDisplayNode {
|
||||
foreground: presentationData.theme.contextMenu.primaryColor.withMultipliedAlpha(0.8),
|
||||
selection: presentationData.theme.contextMenu.primaryColor.withMultipliedAlpha(0.1)
|
||||
),
|
||||
customLayout: TabSelectorComponent.CustomLayout(
|
||||
spacing: 9.0
|
||||
),
|
||||
items: mappedItems,
|
||||
selectedId: self.activeSource?.id,
|
||||
setSelectedId: { [weak self] id in
|
||||
@ -576,7 +618,7 @@ final class ContextSourceContainer: ASDisplayNode {
|
||||
environment: {},
|
||||
containerSize: CGSize(width: layout.size.width, height: 44.0)
|
||||
)
|
||||
childLayout.intrinsicInsets.bottom += 44.0
|
||||
childLayout.intrinsicInsets.bottom += 30.0
|
||||
|
||||
if let tabSelectorView = tabSelector.view {
|
||||
if tabSelectorView.superview == nil {
|
||||
|
@ -38,7 +38,7 @@ public final class PeekController: ViewController, ContextControllerProtocol {
|
||||
return nil
|
||||
}
|
||||
|
||||
public func setItems(_ items: Signal<ContextController.Items, NoError>, minHeight: ContextController.ActionsHeight?) {
|
||||
public func setItems(_ items: Signal<ContextController.Items, NoError>, minHeight: ContextController.ActionsHeight?, animated: Bool) {
|
||||
}
|
||||
|
||||
public func setItems(_ items: Signal<ContextController.Items, NoError>, minHeight: ContextController.ActionsHeight?, previousActionsTransition: ContextController.PreviousActionsTransition) {
|
||||
|
@ -2505,7 +2505,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
return
|
||||
}
|
||||
|
||||
c.setItems(strongSelf.contextMenuSpeedItems(dismiss: dismiss) |> map { ContextController.Items(content: .list($0)) }, minHeight: nil)
|
||||
c.setItems(strongSelf.contextMenuSpeedItems(dismiss: dismiss) |> map { ContextController.Items(content: .list($0)) }, minHeight: nil, animated: true)
|
||||
})))
|
||||
|
||||
items.append(.separator)
|
||||
@ -2634,7 +2634,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
c.dismiss(completion: nil)
|
||||
return
|
||||
}
|
||||
c.setItems(strongSelf.contextMenuMainItems(dismiss: dismiss) |> map { ContextController.Items(content: .list($0)) }, minHeight: nil)
|
||||
c.setItems(strongSelf.contextMenuMainItems(dismiss: dismiss) |> map { ContextController.Items(content: .list($0)) }, minHeight: nil, animated: true)
|
||||
})))
|
||||
|
||||
let sliderValuePromise = ValuePromise<Double?>(nil)
|
||||
|
@ -2517,7 +2517,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
c.setItems(strongSelf.contextMenuDisplayAsItems() |> map { ContextController.Items(content: .list($0)) }, minHeight: nil)
|
||||
c.setItems(strongSelf.contextMenuDisplayAsItems() |> map { ContextController.Items(content: .list($0)) }, minHeight: nil, animated: true)
|
||||
})))
|
||||
items.append(.separator)
|
||||
break
|
||||
@ -2550,7 +2550,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
c.setItems(strongSelf.contextMenuAudioItems() |> map { ContextController.Items(content: .list($0)) }, minHeight: nil)
|
||||
c.setItems(strongSelf.contextMenuAudioItems() |> map { ContextController.Items(content: .list($0)) }, minHeight: nil, animated: true)
|
||||
})))
|
||||
}
|
||||
|
||||
@ -2587,7 +2587,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
c.setItems(strongSelf.contextMenuPermissionItems() |> map { ContextController.Items(content: .list($0)) }, minHeight: nil)
|
||||
c.setItems(strongSelf.contextMenuPermissionItems() |> map { ContextController.Items(content: .list($0)) }, minHeight: nil, animated: true)
|
||||
})))
|
||||
}
|
||||
}
|
||||
@ -2865,7 +2865,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
c.setItems(strongSelf.contextMenuMainItems() |> map { ContextController.Items(content: .list($0)) }, minHeight: nil)
|
||||
c.setItems(strongSelf.contextMenuMainItems() |> map { ContextController.Items(content: .list($0)) }, minHeight: nil, animated: true)
|
||||
})))
|
||||
return .single(items)
|
||||
}
|
||||
@ -2960,7 +2960,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
c.setItems(strongSelf.contextMenuMainItems() |> map { ContextController.Items(content: .list($0)) }, minHeight: nil)
|
||||
c.setItems(strongSelf.contextMenuMainItems() |> map { ContextController.Items(content: .list($0)) }, minHeight: nil, animated: true)
|
||||
})))
|
||||
return items
|
||||
}
|
||||
@ -3006,7 +3006,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
c.setItems(strongSelf.contextMenuMainItems() |> map { ContextController.Items(content: .list($0)) }, minHeight: nil)
|
||||
c.setItems(strongSelf.contextMenuMainItems() |> map { ContextController.Items(content: .list($0)) }, minHeight: nil, animated: true)
|
||||
})))
|
||||
}
|
||||
return .single(items)
|
||||
|
@ -6,7 +6,7 @@ import TelegramPresentationData
|
||||
import ChatPresentationInterfaceState
|
||||
import ShimmerEffect
|
||||
|
||||
private let buttonFont = Font.semibold(13.0)
|
||||
private let buttonFont = Font.semibold(14.0)
|
||||
|
||||
public final class ChatMessageAttachedContentButtonNode: HighlightTrackingButtonNode {
|
||||
private let textNode: TextNode
|
||||
@ -107,7 +107,7 @@ public final class ChatMessageAttachedContentButtonNode: HighlightTrackingButton
|
||||
})
|
||||
}
|
||||
|
||||
public static func asyncLayout(_ current: ChatMessageAttachedContentButtonNode?) -> (_ width: CGFloat, _ regularImage: UIImage, _ highlightedImage: UIImage, _ iconImage: UIImage?, _ highlightedIconImage: UIImage?, _ cornerIcon: Bool, _ title: String, _ titleColor: UIColor, _ highlightedTitleColor: UIColor, _ inProgress: Bool) -> (CGFloat, (CGFloat) -> (CGSize, () -> ChatMessageAttachedContentButtonNode)) {
|
||||
public static func asyncLayout(_ current: ChatMessageAttachedContentButtonNode?) -> (_ width: CGFloat, _ regularImage: UIImage?, _ highlightedImage: UIImage?, _ iconImage: UIImage?, _ highlightedIconImage: UIImage?, _ cornerIcon: Bool, _ title: String, _ titleColor: UIColor, _ highlightedTitleColor: UIColor, _ inProgress: Bool) -> (CGFloat, (CGFloat, CGFloat) -> (CGSize, () -> ChatMessageAttachedContentButtonNode)) {
|
||||
let previousRegularImage = current?.regularImage
|
||||
let previousHighlightedImage = current?.highlightedImage
|
||||
let previousRegularIconImage = current?.regularIconImage
|
||||
@ -169,10 +169,14 @@ public final class ChatMessageAttachedContentButtonNode: HighlightTrackingButton
|
||||
|
||||
let (_, highlightedTextApply) = makeHighlightedTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: title, font: buttonFont, textColor: highlightedTitleColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: max(1.0, width - labelInset * 2.0), height: CGFloat.greatestFiniteMagnitude), alignment: .left, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
return (textSize.size.width + labelInset * 2.0, { refinedWidth in
|
||||
return (CGSize(width: refinedWidth, height: 33.0), {
|
||||
return (textSize.size.width + labelInset * 2.0, { refinedWidth, refinedHeight in
|
||||
let size = CGSize(width: refinedWidth, height: refinedHeight)
|
||||
return (size, {
|
||||
targetNode.accessibilityLabel = title
|
||||
|
||||
//targetNode.borderColor = UIColor.red.cgColor
|
||||
//targetNode.borderWidth = 1.0
|
||||
|
||||
targetNode.titleColor = titleColor
|
||||
|
||||
if let updatedRegularImage = updatedRegularImage {
|
||||
@ -203,15 +207,15 @@ public final class ChatMessageAttachedContentButtonNode: HighlightTrackingButton
|
||||
let _ = textApply()
|
||||
let _ = highlightedTextApply()
|
||||
|
||||
let backgroundFrame = CGRect(origin: CGPoint(), size: CGSize(width: refinedWidth, height: 33.0))
|
||||
var textFrame = CGRect(origin: CGPoint(x: floor((refinedWidth - textSize.size.width) / 2.0), y: floor((34.0 - textSize.size.height) / 2.0)), size: textSize.size)
|
||||
let backgroundFrame = CGRect(origin: CGPoint(), size: CGSize(width: refinedWidth, height: size.height))
|
||||
var textFrame = CGRect(origin: CGPoint(x: floor((refinedWidth - textSize.size.width) / 2.0), y: floor((backgroundFrame.height - textSize.size.height) / 2.0)), size: textSize.size)
|
||||
targetNode.backgroundNode.frame = backgroundFrame
|
||||
if let image = targetNode.iconNode.image {
|
||||
if cornerIcon {
|
||||
targetNode.iconNode.frame = CGRect(origin: CGPoint(x: backgroundFrame.maxX - image.size.width - 5.0, y: 5.0), size: image.size)
|
||||
} else {
|
||||
textFrame.origin.x += floor(image.size.width / 2.0)
|
||||
targetNode.iconNode.frame = CGRect(origin: CGPoint(x: textFrame.minX - image.size.width - 5.0, y: textFrame.minY + 2.0), size: image.size)
|
||||
targetNode.iconNode.frame = CGRect(origin: CGPoint(x: textFrame.minX - image.size.width - 5.0, y: textFrame.minY + floorToScreenPixels((textFrame.height - image.size.height) * 0.5)), size: image.size)
|
||||
}
|
||||
if targetNode.iconNode.supernode == nil {
|
||||
targetNode.addSubnode(targetNode.iconNode)
|
||||
|
@ -51,7 +51,7 @@ public struct ChatMessageAttachedContentNodeMediaFlags: OptionSet {
|
||||
public static let titleBeforeMedia = ChatMessageAttachedContentNodeMediaFlags(rawValue: 1 << 3)
|
||||
}
|
||||
|
||||
public final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
public final class ChatMessageAttachedContentNode: HighlightTrackingButtonNode {
|
||||
private var backgroundView: UIImageView?
|
||||
private let topTitleNode: TextNode
|
||||
private let textNode: TextNodeWithEntities
|
||||
@ -60,6 +60,7 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
private var contentInstantVideoNode: ChatMessageInteractiveInstantVideoNode?
|
||||
private var contentFileNode: ChatMessageInteractiveFileNode?
|
||||
private var buttonNode: ChatMessageAttachedContentButtonNode?
|
||||
private var buttonSeparatorLayer: SimpleLayer?
|
||||
|
||||
public let statusNode: ChatMessageDateAndStatusNode
|
||||
private var additionalImageBadgeNode: ChatMessageInteractiveMediaBadge?
|
||||
@ -93,7 +94,7 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
override public init() {
|
||||
public init() {
|
||||
self.topTitleNode = TextNode()
|
||||
self.topTitleNode.isUserInteractionEnabled = false
|
||||
self.topTitleNode.displaysAsynchronously = false
|
||||
@ -108,17 +109,35 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
|
||||
self.inlineImageNode = TransformImageNode()
|
||||
self.inlineImageNode.contentAnimations = [.subsequentUpdates]
|
||||
self.inlineImageNode.isLayerBacked = !smartInvertColorsEnabled()
|
||||
self.inlineImageNode.isLayerBacked = false
|
||||
self.inlineImageNode.displaysAsynchronously = false
|
||||
|
||||
self.statusNode = ChatMessageDateAndStatusNode()
|
||||
|
||||
super.init()
|
||||
super.init(pointerStyle: .default)
|
||||
|
||||
self.addSubnode(self.topTitleNode)
|
||||
self.addSubnode(self.textNode.textNode)
|
||||
|
||||
self.addSubnode(self.statusNode)
|
||||
|
||||
self.highligthedChanged = { [weak self] highlighted in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if self.bounds.width < 1.0 {
|
||||
return
|
||||
}
|
||||
let transition: ContainedViewLayoutTransition = .animated(duration: highlighted ? 0.1 : 0.2, curve: .easeInOut)
|
||||
let scale: CGFloat = highlighted ? ((self.bounds.width - 10.0) / self.bounds.width) : 1.0
|
||||
transition.updateSublayerTransformScale(node: self, scale: scale)
|
||||
}
|
||||
|
||||
self.addTarget(self, action: #selector(self.pressed), forControlEvents: .touchUpInside)
|
||||
}
|
||||
|
||||
@objc private func pressed() {
|
||||
self.activateAction?()
|
||||
}
|
||||
|
||||
public func asyncLayout() -> (_ presentationData: ChatPresentationData, _ automaticDownloadSettings: MediaAutoDownloadSettings, _ associatedData: ChatMessageItemAssociatedData, _ attributes: ChatMessageEntryAttributes, _ context: AccountContext, _ controllerInteraction: ChatControllerInteraction, _ message: Message, _ messageRead: Bool, _ chatLocation: ChatLocation, _ title: String?, _ subtitle: NSAttributedString?, _ text: String?, _ entities: [MessageTextEntity]?, _ media: (Media, ChatMessageAttachedContentNodeMediaFlags)?, _ mediaBadge: String?, _ actionIcon: ChatMessageAttachedContentActionIcon?, _ actionTitle: String?, _ displayLine: Bool, _ layoutConstants: ChatMessageItemLayoutConstants, _ preparePosition: ChatMessageBubblePreparePosition, _ constrainedSize: CGSize, _ animationCache: AnimationCache, _ animationRenderer: MultiAnimationRenderer) -> (CGFloat, (CGSize, ChatMessageBubbleContentPosition) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool, ListViewItemApply?) -> Void))) {
|
||||
@ -142,7 +161,7 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
if message.adAttribute != nil {
|
||||
fontSize = floor(presentationData.fontSize.baseDisplaySize)
|
||||
} else {
|
||||
fontSize = floor(presentationData.fontSize.baseDisplaySize * 15.0 / 17.0)
|
||||
fontSize = floor(presentationData.fontSize.baseDisplaySize * 14.0 / 17.0)
|
||||
}
|
||||
|
||||
let titleFont = Font.semibold(fontSize)
|
||||
@ -160,8 +179,8 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
|
||||
var horizontalInsets = UIEdgeInsets(top: 0.0, left: 10.0, bottom: 0.0, right: 10.0)
|
||||
if displayLine {
|
||||
horizontalInsets.left += 12.0
|
||||
horizontalInsets.right += 12.0
|
||||
horizontalInsets.left += 10.0
|
||||
horizontalInsets.right += 9.0
|
||||
}
|
||||
|
||||
var titleBeforeMedia = false
|
||||
@ -509,7 +528,7 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
if let _ = inlineImageDimensions {
|
||||
inlineImageSize = CGSize(width: 54.0, height: 54.0)
|
||||
inlineImageSize = CGSize(width: 53.0, height: 53.0)
|
||||
|
||||
if let inlineImageSize = inlineImageSize {
|
||||
textCutout.topRight = CGSize(width: inlineImageSize.width + 10.0, height: inlineImageSize.height + 10.0)
|
||||
@ -517,19 +536,24 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
return (initialWidth, { constrainedSize, position in
|
||||
var insets = UIEdgeInsets(top: 0.0, left: horizontalInsets.left, bottom: 5.0, right: horizontalInsets.right)
|
||||
var lineInsets = insets
|
||||
|
||||
//insets.top += 4.0
|
||||
//insets.bottom += 4.0
|
||||
var insets = UIEdgeInsets(top: 0.0, left: horizontalInsets.left, bottom: 0.0, right: horizontalInsets.right)
|
||||
|
||||
switch position {
|
||||
case .linear(.None, _):
|
||||
case let .linear(topNeighbor, bottomNeighbor):
|
||||
switch topNeighbor {
|
||||
case .None:
|
||||
insets.top += 10.0
|
||||
insets.bottom += 8.0
|
||||
lineInsets.top += 10.0 + 8.0
|
||||
default:
|
||||
break
|
||||
}
|
||||
switch bottomNeighbor {
|
||||
case .None:
|
||||
insets.bottom += 12.0
|
||||
default:
|
||||
insets.bottom += 0.0
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
let textConstrainedSize = CGSize(width: constrainedSize.width - insets.left - insets.right, height: constrainedSize.height - insets.top - insets.bottom)
|
||||
@ -614,9 +638,7 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
var boundingSize = textFrame.size
|
||||
var lineHeight = textLayout.rawTextSize.height
|
||||
if titleBeforeMedia {
|
||||
lineHeight += topTitleLayout.size.height + 4.0
|
||||
boundingSize.height += topTitleLayout.size.height + 4.0
|
||||
boundingSize.width = max(boundingSize.width, topTitleLayout.size.width)
|
||||
}
|
||||
@ -624,9 +646,6 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
if boundingSize.height < inlineImageSize.height {
|
||||
boundingSize.height = inlineImageSize.height
|
||||
}
|
||||
if lineHeight < inlineImageSize.height {
|
||||
lineHeight = inlineImageSize.height
|
||||
}
|
||||
}
|
||||
|
||||
if let statusSuggestedWidthAndContinue = statusSuggestedWidthAndContinue {
|
||||
@ -652,8 +671,6 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
boundingSize.width = max(boundingSize.width, videoLayout.contentSize.width + videoLayout.overflowLeft + videoLayout.overflowRight)
|
||||
}
|
||||
|
||||
lineHeight += lineInsets.top + lineInsets.bottom
|
||||
|
||||
var imageApply: (() -> Void)?
|
||||
if let inlineImageSize = inlineImageSize, let inlineImageDimensions = inlineImageDimensions {
|
||||
let imageCorners = ImageCorners(topLeft: .Corner(4.0), topRight: .Corner(4.0), bottomLeft: .Corner(4.0), bottomRight: .Corner(4.0))
|
||||
@ -661,18 +678,14 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
imageApply = imageLayout(arguments)
|
||||
}
|
||||
|
||||
var continueActionButtonLayout: ((CGFloat) -> (CGSize, () -> ChatMessageAttachedContentButtonNode))?
|
||||
var continueActionButtonLayout: ((CGFloat, CGFloat) -> (CGSize, () -> ChatMessageAttachedContentButtonNode))?
|
||||
if let actionTitle = actionTitle, !isPreview {
|
||||
let buttonImage: UIImage
|
||||
let buttonHighlightedImage: UIImage
|
||||
var buttonIconImage: UIImage?
|
||||
var buttonHighlightedIconImage: UIImage?
|
||||
var cornerIcon = false
|
||||
let titleColor: UIColor
|
||||
let titleHighlightedColor: UIColor
|
||||
if incoming {
|
||||
buttonImage = PresentationResourcesChat.chatMessageAttachedContentButtonIncoming(presentationData.theme.theme)!
|
||||
buttonHighlightedImage = PresentationResourcesChat.chatMessageAttachedContentHighlightedButtonIncoming(presentationData.theme.theme)!
|
||||
if let actionIcon {
|
||||
switch actionIcon {
|
||||
case .instant:
|
||||
@ -688,8 +701,6 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
let bubbleColor = bubbleColorComponents(theme: presentationData.theme.theme, incoming: true, wallpaper: !presentationData.theme.wallpaper.isEmpty)
|
||||
titleHighlightedColor = bubbleColor.fill[0]
|
||||
} else {
|
||||
buttonImage = PresentationResourcesChat.chatMessageAttachedContentButtonOutgoing(presentationData.theme.theme)!
|
||||
buttonHighlightedImage = PresentationResourcesChat.chatMessageAttachedContentHighlightedButtonOutgoing(presentationData.theme.theme)!
|
||||
if let actionIcon {
|
||||
switch actionIcon {
|
||||
case .instant:
|
||||
@ -705,7 +716,7 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
let bubbleColor = bubbleColorComponents(theme: presentationData.theme.theme, incoming: false, wallpaper: !presentationData.theme.wallpaper.isEmpty)
|
||||
titleHighlightedColor = bubbleColor.fill[0]
|
||||
}
|
||||
let (buttonWidth, continueLayout) = makeButtonLayout(constrainedSize.width, buttonImage, buttonHighlightedImage, buttonIconImage, buttonHighlightedIconImage, cornerIcon, actionTitle, titleColor, titleHighlightedColor, false)
|
||||
let (buttonWidth, continueLayout) = makeButtonLayout(constrainedSize.width, nil, nil, buttonIconImage, buttonHighlightedIconImage, cornerIcon, actionTitle, titleColor, titleHighlightedColor, false)
|
||||
boundingSize.width = max(buttonWidth, boundingSize.width)
|
||||
continueActionButtonLayout = continueLayout
|
||||
}
|
||||
@ -715,11 +726,10 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
|
||||
return (boundingSize.width, { boundingWidth in
|
||||
var adjustedBoundingSize = boundingSize
|
||||
var adjustedLineHeight = lineHeight
|
||||
|
||||
var imageFrame: CGRect?
|
||||
if let inlineImageSize = inlineImageSize {
|
||||
imageFrame = CGRect(origin: CGPoint(x: boundingWidth - inlineImageSize.width - insets.right, y: 0.0), size: inlineImageSize)
|
||||
imageFrame = CGRect(origin: CGPoint(x: boundingWidth - inlineImageSize.width - insets.right + 4.0, y: 0.0), size: inlineImageSize)
|
||||
}
|
||||
|
||||
var contentImageSizeAndApply: (CGSize, (ListViewItemUpdateAnimation, Bool) -> ChatMessageInteractiveMediaNode)?
|
||||
@ -732,8 +742,7 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
imageHeightAddition += 2.0
|
||||
}
|
||||
|
||||
adjustedBoundingSize.height += imageHeightAddition + 5.0
|
||||
adjustedLineHeight += imageHeightAddition + 4.0
|
||||
adjustedBoundingSize.height += imageHeightAddition + 7.0
|
||||
}
|
||||
|
||||
var contentFileSizeAndApply: (CGSize, (Bool, ListViewItemUpdateAnimation, ListViewItemApply?) -> ChatMessageInteractiveFileNode)?
|
||||
@ -749,25 +758,26 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
adjustedBoundingSize.height += imageHeightAddition + 5.0
|
||||
adjustedLineHeight += imageHeightAddition + 4.0
|
||||
}
|
||||
|
||||
if let (videoLayout, _) = contentInstantVideoSizeAndApply {
|
||||
let imageHeightAddition = videoLayout.contentSize.height + 6.0
|
||||
if textFrame.size.height > CGFloat.ulpOfOne {
|
||||
//imageHeightAddition += 2.0
|
||||
}
|
||||
|
||||
adjustedBoundingSize.height += imageHeightAddition// + 5.0
|
||||
adjustedLineHeight += imageHeightAddition// + 4.0
|
||||
}
|
||||
|
||||
var actionButtonSizeAndApply: ((CGSize, () -> ChatMessageAttachedContentButtonNode))?
|
||||
if let continueActionButtonLayout = continueActionButtonLayout {
|
||||
let (size, apply) = continueActionButtonLayout(boundingWidth - 12.0 - insets.right)
|
||||
let (size, apply) = continueActionButtonLayout(boundingWidth - 5.0 - insets.right, 38.0)
|
||||
actionButtonSizeAndApply = (size, apply)
|
||||
adjustedBoundingSize.width = max(adjustedBoundingSize.width, insets.left + size.width + insets.right)
|
||||
adjustedBoundingSize.height += 7.0 + size.height
|
||||
adjustedBoundingSize.height += 4.0 + size.height
|
||||
if let text, !text.isEmpty {
|
||||
if contentImageSizeAndApply == nil {
|
||||
adjustedBoundingSize.height += 5.0
|
||||
} else if let (_, flags) = mediaAndFlags, flags.contains(.preferMediaBeforeText) {
|
||||
adjustedBoundingSize.height += 5.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var statusSizeAndApply: ((CGSize), (ListViewItemUpdateAnimation) -> Void)?
|
||||
@ -776,7 +786,6 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
}
|
||||
if let statusSizeAndApply = statusSizeAndApply {
|
||||
adjustedBoundingSize.height += statusSizeAndApply.0.height
|
||||
adjustedLineHeight += statusSizeAndApply.0.height
|
||||
|
||||
if let imageFrame = imageFrame, statusSizeAndApply.0.height == 0.0 {
|
||||
if statusInText {
|
||||
@ -819,7 +828,6 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
finalStatusFrame.origin.y += 14.0
|
||||
|
||||
adjustedBoundingSize.height += 14.0
|
||||
adjustedLineHeight += 14.0
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -847,9 +855,12 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
}
|
||||
backgroundView.tintColor = mainColor
|
||||
|
||||
animation.animator.updateFrame(layer: backgroundView.layer, frame: CGRect(origin: CGPoint(x: 11.0, y: insets.top), size: CGSize(width: adjustedBoundingSize.width - 1.0 - insets.right, height: adjustedLineHeight - insets.top - insets.bottom - 2.0)), completion: nil)
|
||||
animation.animator.updateFrame(layer: backgroundView.layer, frame: CGRect(origin: CGPoint(x: 11.0, y: insets.top - 3.0), size: CGSize(width: adjustedBoundingSize.width - 4.0 - insets.right, height: adjustedBoundingSize.height - insets.top - insets.bottom + 4.0)), completion: nil)
|
||||
backgroundView.isHidden = !displayLine
|
||||
|
||||
//strongSelf.borderColor = UIColor.red.cgColor
|
||||
//strongSelf.borderWidth = 2.0
|
||||
|
||||
strongSelf.textNode.textNode.displaysAsynchronously = !isPreview
|
||||
|
||||
let _ = topTitleApply()
|
||||
@ -1003,20 +1014,47 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
|
||||
if let (size, apply) = actionButtonSizeAndApply {
|
||||
let buttonNode = apply()
|
||||
|
||||
let buttonFrame = CGRect(origin: CGPoint(x: 12.0, y: adjustedBoundingSize.height - insets.bottom - size.height), size: size)
|
||||
if buttonNode !== strongSelf.buttonNode {
|
||||
strongSelf.buttonNode?.removeFromSupernode()
|
||||
strongSelf.buttonNode = buttonNode
|
||||
buttonNode.isUserInteractionEnabled = false
|
||||
strongSelf.addSubnode(buttonNode)
|
||||
buttonNode.pressed = {
|
||||
if let strongSelf = self {
|
||||
strongSelf.activateAction?()
|
||||
}
|
||||
}
|
||||
buttonNode.frame = buttonFrame
|
||||
} else {
|
||||
animation.animator.updateFrame(layer: buttonNode.layer, frame: buttonFrame, completion: nil)
|
||||
}
|
||||
|
||||
let buttonSeparatorFrame = CGRect(origin: CGPoint(x: buttonFrame.minX + 8.0, y: buttonFrame.minY - 2.0), size: CGSize(width: buttonFrame.width - 8.0 - 8.0, height: UIScreenPixel))
|
||||
|
||||
let buttonSeparatorLayer: SimpleLayer
|
||||
if let current = strongSelf.buttonSeparatorLayer {
|
||||
buttonSeparatorLayer = current
|
||||
animation.animator.updateFrame(layer: buttonSeparatorLayer, frame: buttonSeparatorFrame, completion: nil)
|
||||
} else {
|
||||
buttonSeparatorLayer = SimpleLayer()
|
||||
strongSelf.buttonSeparatorLayer = buttonSeparatorLayer
|
||||
strongSelf.layer.addSublayer(buttonSeparatorLayer)
|
||||
buttonSeparatorLayer.frame = buttonSeparatorFrame
|
||||
}
|
||||
|
||||
buttonSeparatorLayer.backgroundColor = mainColor.withMultipliedAlpha(0.5).cgColor
|
||||
} else {
|
||||
if let buttonNode = strongSelf.buttonNode {
|
||||
strongSelf.buttonNode = nil
|
||||
buttonNode.removeFromSupernode()
|
||||
}
|
||||
|
||||
if let buttonSeparatorLayer = strongSelf.buttonSeparatorLayer {
|
||||
strongSelf.buttonSeparatorLayer = nil
|
||||
buttonSeparatorLayer.removeFromSuperlayer()
|
||||
}
|
||||
buttonNode.frame = CGRect(origin: CGPoint(x: 12.0, y: adjustedLineHeight - insets.top - insets.bottom - 2.0 + 6.0), size: size)
|
||||
} else if let buttonNode = strongSelf.buttonNode {
|
||||
buttonNode.removeFromSupernode()
|
||||
strongSelf.buttonNode = nil
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -1090,10 +1128,10 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
} else if let hashtag = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Hashtag)] as? TelegramHashtag {
|
||||
return .hashtag(hashtag.peerName, hashtag.hashtag)
|
||||
} else {
|
||||
return .none
|
||||
return .ignore
|
||||
}
|
||||
} else {
|
||||
return .none
|
||||
return .ignore
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -69,6 +69,9 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
|
||||
private var textSelectionState: Promise<ChatControllerSubject.MessageOptionsInfo.SelectionState>?
|
||||
|
||||
private var linkPreviewOptionsDisposable: Disposable?
|
||||
private var linkPreviewHighlightingNodes: [LinkHighlightingNode] = []
|
||||
|
||||
override public var visibility: ListViewItemNodeVisibility {
|
||||
didSet {
|
||||
if oldValue != self.visibility {
|
||||
@ -128,6 +131,10 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.linkPreviewOptionsDisposable?.dispose()
|
||||
}
|
||||
|
||||
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 textLayout = TextNodeWithEntities.asyncLayout(self.textNode)
|
||||
let spoilerTextLayout = TextNodeWithEntities.asyncLayout(self.spoilerTextNode)
|
||||
@ -566,27 +573,40 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
strongSelf.statusNode.pressed = nil
|
||||
}
|
||||
|
||||
if let subject = item.associatedData.subject, case let .messageOptions(_, _, info) = subject, case let .reply(info) = info {
|
||||
if strongSelf.textSelectionNode == nil {
|
||||
strongSelf.updateIsExtractedToContextPreview(true)
|
||||
if let initialQuote = info.quote, item.message.id == initialQuote.messageId, let string = strongSelf.textNode.textNode.cachedLayout?.attributedString {
|
||||
let nsString = string.string as NSString
|
||||
let subRange = nsString.range(of: initialQuote.text)
|
||||
if subRange.location != NSNotFound {
|
||||
strongSelf.beginTextSelection(range: subRange, displayMenu: false)
|
||||
if let subject = item.associatedData.subject, case let .messageOptions(_, _, info) = subject {
|
||||
if case let .reply(info) = info {
|
||||
if strongSelf.textSelectionNode == nil {
|
||||
strongSelf.updateIsExtractedToContextPreview(true)
|
||||
if let initialQuote = info.quote, item.message.id == initialQuote.messageId, let string = strongSelf.textNode.textNode.cachedLayout?.attributedString {
|
||||
let nsString = string.string as NSString
|
||||
let subRange = nsString.range(of: initialQuote.text)
|
||||
if subRange.location != NSNotFound {
|
||||
strongSelf.beginTextSelection(range: subRange, displayMenu: false)
|
||||
}
|
||||
}
|
||||
|
||||
if strongSelf.textSelectionState == nil {
|
||||
if let textSelectionNode = strongSelf.textSelectionNode {
|
||||
let range = textSelectionNode.getSelection()
|
||||
strongSelf.textSelectionState = Promise(strongSelf.getSelectionState(range: range))
|
||||
} else {
|
||||
strongSelf.textSelectionState = Promise(strongSelf.getSelectionState(range: nil))
|
||||
}
|
||||
}
|
||||
if let textSelectionState = strongSelf.textSelectionState {
|
||||
info.selectionState.set(textSelectionState.get())
|
||||
}
|
||||
}
|
||||
|
||||
if strongSelf.textSelectionState == nil {
|
||||
if let textSelectionNode = strongSelf.textSelectionNode {
|
||||
let range = textSelectionNode.getSelection()
|
||||
strongSelf.textSelectionState = Promise(strongSelf.getSelectionState(range: range))
|
||||
} else {
|
||||
strongSelf.textSelectionState = Promise(strongSelf.getSelectionState(range: nil))
|
||||
}
|
||||
}
|
||||
if let textSelectionState = strongSelf.textSelectionState {
|
||||
info.selectionState.set(textSelectionState.get())
|
||||
} else if case let .link(link) = info {
|
||||
if strongSelf.linkPreviewOptionsDisposable == nil {
|
||||
strongSelf.linkPreviewOptionsDisposable = (link.options
|
||||
|> deliverOnMainQueue).startStrict(next: { [weak strongSelf] options in
|
||||
guard let strongSelf else {
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.updateLinkPreviewTextHighlightState(text: options.url)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -613,6 +633,13 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
|
||||
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 .none
|
||||
}
|
||||
}
|
||||
|
||||
let textNodeFrame = self.textNode.textNode.frame
|
||||
if let (index, attributes) = self.textNode.textNode.attributesAtPoint(CGPoint(x: point.x - textNodeFrame.minX, y: point.y - textNodeFrame.minY)) {
|
||||
if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Spoiler)], !(self.dustNode?.isRevealed ?? true) {
|
||||
@ -764,7 +791,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
for i in 0 ..< rectsSet.count {
|
||||
let rects = rectsSet[i]
|
||||
let textHighlightNode: LinkHighlightingNode
|
||||
if self.textHighlightingNodes.count < i {
|
||||
if i < self.textHighlightingNodes.count {
|
||||
textHighlightNode = self.textHighlightingNodes[i]
|
||||
} else {
|
||||
textHighlightNode = LinkHighlightingNode(color: item.message.effectivelyIncoming(item.context.account.peerId) ? item.presentationData.theme.theme.chat.message.incoming.textHighlightColor : item.presentationData.theme.theme.chat.message.outgoing.textHighlightColor)
|
||||
@ -780,6 +807,39 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
}
|
||||
|
||||
private func updateLinkPreviewTextHighlightState(text: String?) {
|
||||
guard let item = self.item else {
|
||||
return
|
||||
}
|
||||
var rectsSet: [[CGRect]] = []
|
||||
if let text = text, !text.isEmpty, let cachedLayout = self.textNode.textNode.cachedLayout, let string = cachedLayout.attributedString?.string {
|
||||
let nsString = string as NSString
|
||||
let range = nsString.range(of: text)
|
||||
if range.location != NSNotFound {
|
||||
if let rects = cachedLayout.rangeRects(in: range)?.rects, !rects.isEmpty {
|
||||
rectsSet = [rects]
|
||||
}
|
||||
}
|
||||
}
|
||||
for i in 0 ..< rectsSet.count {
|
||||
let rects = rectsSet[i]
|
||||
let textHighlightNode: LinkHighlightingNode
|
||||
if i < self.linkPreviewHighlightingNodes.count {
|
||||
textHighlightNode = self.linkPreviewHighlightingNodes[i]
|
||||
} else {
|
||||
textHighlightNode = LinkHighlightingNode(color: item.message.effectivelyIncoming(item.context.account.peerId) ? item.presentationData.theme.theme.chat.message.incoming.linkHighlightColor : item.presentationData.theme.theme.chat.message.outgoing.linkHighlightColor)
|
||||
self.linkPreviewHighlightingNodes.append(textHighlightNode)
|
||||
self.insertSubnode(textHighlightNode, belowSubnode: self.textNode.textNode)
|
||||
}
|
||||
textHighlightNode.frame = self.textNode.textNode.frame
|
||||
textHighlightNode.updateRects(rects)
|
||||
}
|
||||
for i in (rectsSet.count ..< self.linkPreviewHighlightingNodes.count).reversed() {
|
||||
self.linkPreviewHighlightingNodes[i].removeFromSupernode()
|
||||
self.linkPreviewHighlightingNodes.remove(at: i)
|
||||
}
|
||||
}
|
||||
|
||||
override public func willUpdateIsExtractedToContextPreview(_ value: Bool) {
|
||||
if !value {
|
||||
if let textSelectionNode = self.textSelectionNode {
|
||||
|
@ -24,7 +24,7 @@ private let titleFont: UIFont = Font.semibold(15.0)
|
||||
public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
private var webPage: TelegramMediaWebpage?
|
||||
|
||||
private let contentNode: ChatMessageAttachedContentNode
|
||||
private var contentNode: ChatMessageAttachedContentNode
|
||||
|
||||
override public var visibility: ListViewItemNodeVisibility {
|
||||
didSet {
|
||||
@ -83,6 +83,8 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent
|
||||
if let webpage = webPageContent {
|
||||
if webpage.story != nil {
|
||||
let _ = item.controllerInteraction.openMessage(item.message, .default)
|
||||
} else if webpage.instantPage != nil {
|
||||
strongSelf.contentNode.openMedia?(.default)
|
||||
} else {
|
||||
item.controllerInteraction.openUrl(webpage.url, false, nil, nil)
|
||||
}
|
||||
@ -351,10 +353,12 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent
|
||||
}
|
||||
}
|
||||
|
||||
if webpage.displayOptions.largeMedia == false {
|
||||
mediaAndFlags?.1.insert(.preferMediaInline)
|
||||
} else {
|
||||
mediaAndFlags?.1.remove(.preferMediaInline)
|
||||
if let largeMedia = webpage.displayOptions.largeMedia {
|
||||
if largeMedia {
|
||||
mediaAndFlags?.1.remove(.preferMediaInline)
|
||||
} else {
|
||||
mediaAndFlags?.1.insert(.preferMediaInline)
|
||||
}
|
||||
}
|
||||
} else if let adAttribute = item.message.adAttribute {
|
||||
title = nil
|
||||
|
@ -18,6 +18,14 @@ public final class TabSelectorComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
public struct CustomLayout: Equatable {
|
||||
public var spacing: CGFloat
|
||||
|
||||
public init(spacing: CGFloat) {
|
||||
self.spacing = spacing
|
||||
}
|
||||
}
|
||||
|
||||
public struct Item: Equatable {
|
||||
public var id: AnyHashable
|
||||
public var title: String
|
||||
@ -32,17 +40,20 @@ public final class TabSelectorComponent: Component {
|
||||
}
|
||||
|
||||
public let colors: Colors
|
||||
public let customLayout: CustomLayout?
|
||||
public let items: [Item]
|
||||
public let selectedId: AnyHashable?
|
||||
public let setSelectedId: (AnyHashable) -> Void
|
||||
|
||||
public init(
|
||||
colors: Colors,
|
||||
customLayout: CustomLayout? = nil,
|
||||
items: [Item],
|
||||
selectedId: AnyHashable?,
|
||||
setSelectedId: @escaping (AnyHashable) -> Void
|
||||
) {
|
||||
self.colors = colors
|
||||
self.customLayout = customLayout
|
||||
self.items = items
|
||||
self.selectedId = selectedId
|
||||
self.setSelectedId = setSelectedId
|
||||
@ -52,6 +63,9 @@ public final class TabSelectorComponent: Component {
|
||||
if lhs.colors != rhs.colors {
|
||||
return false
|
||||
}
|
||||
if lhs.customLayout != rhs.customLayout {
|
||||
return false
|
||||
}
|
||||
if lhs.items != rhs.items {
|
||||
return false
|
||||
}
|
||||
@ -96,7 +110,14 @@ public final class TabSelectorComponent: Component {
|
||||
|
||||
let baseHeight: CGFloat = 28.0
|
||||
let innerInset: CGFloat = 12.0
|
||||
let spacing: CGFloat = 2.0
|
||||
let spacing: CGFloat = component.customLayout?.spacing ?? 2.0
|
||||
|
||||
let itemFont: UIFont
|
||||
if component.customLayout != nil {
|
||||
itemFont = Font.medium(14.0)
|
||||
} else {
|
||||
itemFont = Font.semibold(14.0)
|
||||
}
|
||||
|
||||
if self.selectionView.image == nil {
|
||||
self.selectionView.image = generateStretchableFilledCircleImage(diameter: baseHeight, color: component.colors.selection)
|
||||
@ -123,7 +144,7 @@ public final class TabSelectorComponent: Component {
|
||||
let itemSize = itemView.title.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(PlainButtonComponent(
|
||||
content: AnyComponent(Text(text: item.title, font: Font.semibold(14.0), color: component.colors.foreground)),
|
||||
content: AnyComponent(Text(text: item.title, font: itemFont, color: component.colors.foreground)),
|
||||
effectAlignment: .center,
|
||||
minSize: nil,
|
||||
action: { [weak self] in
|
||||
|
@ -1,22 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "ConversationInstantPageButtonIconIncoming@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "ConversationInstantPageButtonIconIncoming@3x.png",
|
||||
"scale" : "3x"
|
||||
"filename" : "InstantIcon.svg",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 367 B |
Binary file not shown.
Before Width: | Height: | Size: 447 B |
@ -0,0 +1,3 @@
|
||||
<svg width="11" height="16" viewBox="0 0 11 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.07593 6.66558C6.74821 6.66558 6.49801 6.37278 6.54912 6.04906L7.26175 1.53576C7.35044 0.974067 6.61589 0.682105 6.29478 1.15142L1.26837 8.4977C1.02619 8.85167 1.27965 9.3322 1.70854 9.3322H3.91886C4.24658 9.3322 4.49678 9.625 4.44567 9.94871L3.73304 14.462C3.64435 15.0237 4.3789 15.3157 4.70001 14.8464L9.72642 7.50008C9.9686 7.14611 9.71514 6.66558 9.28625 6.66558H7.07593Z" fill="#3CA5EC"/>
|
||||
</svg>
|
After Width: | Height: | Size: 549 B |
@ -27,6 +27,12 @@ private func presentChatInputOptions(selfController: ChatControllerImpl, sourceN
|
||||
|
||||
let replySelectionState = Promise<ChatControllerSubject.MessageOptionsInfo.SelectionState>(ChatControllerSubject.MessageOptionsInfo.SelectionState(quote: nil))
|
||||
|
||||
if let source = chatReplyOptions(selfController: selfController, sourceNode: sourceNode, getContextController: {
|
||||
return getContextController?()
|
||||
}, selectionState: replySelectionState) {
|
||||
sources.append(source)
|
||||
}
|
||||
|
||||
var forwardDismissedForCancel: (() -> Void)?
|
||||
if let (source, dismissedForCancel) = chatForwardOptions(selfController: selfController, sourceNode: sourceNode, getContextController: {
|
||||
return getContextController?()
|
||||
@ -34,11 +40,6 @@ private func presentChatInputOptions(selfController: ChatControllerImpl, sourceN
|
||||
forwardDismissedForCancel = dismissedForCancel
|
||||
sources.append(source)
|
||||
}
|
||||
if let source = chatReplyOptions(selfController: selfController, sourceNode: sourceNode, getContextController: {
|
||||
return getContextController?()
|
||||
}, selectionState: replySelectionState) {
|
||||
sources.append(source)
|
||||
}
|
||||
|
||||
if let source = chatLinkOptions(selfController: selfController, sourceNode: sourceNode, getContextController: {
|
||||
return getContextController?()
|
||||
@ -744,10 +745,9 @@ private func chatLinkOptions(selfController: ChatControllerImpl, sourceNode: ASD
|
||||
guard let selfController else {
|
||||
return
|
||||
}
|
||||
//selfController.updateChatPresentationInterfaceState(interactive: false, { $0.updatedInterfaceState({ $0.withUpdatedForwardMessageIds(forwardMessageIds) }) })
|
||||
//selfController.controllerInteraction?.sendCurrentMessage(false)
|
||||
|
||||
let _ = selfController
|
||||
selfController.chatDisplayNode.dismissUrlPreview()
|
||||
|
||||
let _ = chatController
|
||||
|
||||
f(.default)
|
||||
|
@ -6752,6 +6752,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
info.1.dispose()
|
||||
}
|
||||
self.urlPreviewQueryState?.1.dispose()
|
||||
self.editingUrlPreviewQueryState?.1.dispose()
|
||||
self.audioRecorderDisposable?.dispose()
|
||||
self.audioRecorderStatusDisposable?.dispose()
|
||||
self.videoRecorderDisposable?.dispose()
|
||||
@ -9806,7 +9807,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
})
|
||||
})))
|
||||
|
||||
contextController.setItems(.single(ContextController.Items(content: .list(contextItems))), minHeight: nil)
|
||||
contextController.setItems(.single(ContextController.Items(content: .list(contextItems))), minHeight: nil, animated: true)
|
||||
}
|
||||
return
|
||||
} else {
|
||||
@ -9825,7 +9826,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
})
|
||||
})))
|
||||
|
||||
contextController.setItems(.single(ContextController.Items(content: .list(contextItems))), minHeight: nil)
|
||||
contextController.setItems(.single(ContextController.Items(content: .list(contextItems))), minHeight: nil, animated: true)
|
||||
|
||||
return
|
||||
} else {
|
||||
@ -18058,7 +18059,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
|
||||
if canDisplayContextMenu, let contextController = contextController {
|
||||
contextController.setItems(.single(ContextController.Items(content: .list(contextItems))), minHeight: nil)
|
||||
contextController.setItems(.single(ContextController.Items(content: .list(contextItems))), minHeight: nil, animated: true)
|
||||
} else {
|
||||
actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [
|
||||
ActionSheetButtonItem(title: self.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
|
||||
|
@ -1153,8 +1153,11 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
self.replyRecognizer = replyRecognizer
|
||||
self.view.addGestureRecognizer(replyRecognizer)
|
||||
|
||||
if let item = self.item, let subject = item.associatedData.subject, case .messageOptions = subject {
|
||||
//self.tapRecognizer?.isEnabled = false
|
||||
if let item = self.item, let subject = item.associatedData.subject, case let .messageOptions(_, _, info) = subject {
|
||||
if case .link = info {
|
||||
} else {
|
||||
self.tapRecognizer?.isEnabled = false
|
||||
}
|
||||
self.replyRecognizer?.isEnabled = false
|
||||
}
|
||||
}
|
||||
@ -3578,8 +3581,11 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
strongSelf.mainContextSourceNode.layoutUpdated?(strongSelf.mainContextSourceNode.bounds.size, animation)
|
||||
}
|
||||
|
||||
if let subject = item.associatedData.subject, case .messageOptions = subject {
|
||||
//strongSelf.tapRecognizer?.isEnabled = false
|
||||
if let subject = item.associatedData.subject, case let .messageOptions(_, _, info) = subject {
|
||||
if case .link = info {
|
||||
} else {
|
||||
strongSelf.tapRecognizer?.isEnabled = false
|
||||
}
|
||||
strongSelf.replyRecognizer?.isEnabled = false
|
||||
strongSelf.mainContainerNode.isGestureEnabled = false
|
||||
for contentContainer in strongSelf.contentContainers {
|
||||
|
@ -284,7 +284,7 @@ class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
return (contentWidth, { boundingWidth in
|
||||
let baseAvatarFrame = CGRect(origin: CGPoint(x: layoutConstants.text.bubbleInsets.right, y: layoutConstants.text.bubbleInsets.top), size: avatarSize)
|
||||
|
||||
let (buttonSize, buttonApply) = continueLayout(boundingWidth - layoutConstants.text.bubbleInsets.right * 2.0)
|
||||
let (buttonSize, buttonApply) = continueLayout(boundingWidth - layoutConstants.text.bubbleInsets.right * 2.0, 33.0)
|
||||
let buttonSpacing: CGFloat = 4.0
|
||||
|
||||
let statusSizeAndApply = statusSuggestedWidthAndContinue?.1(boundingWidth - sideInsets)
|
||||
|
@ -387,7 +387,7 @@ class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
let contentWidth = maxContentWidth + layoutConstants.text.bubbleInsets.right * 2.0
|
||||
|
||||
return (contentWidth, { boundingWidth in
|
||||
let (buttonSize, buttonApply) = continueLayout(boundingWidth - layoutConstants.text.bubbleInsets.right * 2.0)
|
||||
let (buttonSize, buttonApply) = continueLayout(boundingWidth - layoutConstants.text.bubbleInsets.right * 2.0, 33.0)
|
||||
let buttonSpacing: CGFloat = 4.0
|
||||
|
||||
let (channelButtonSize, channelButtonApply) = continueChannelLayout(boundingWidth - layoutConstants.text.bubbleInsets.right * 2.0)
|
||||
|
@ -511,8 +511,13 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible {
|
||||
let (top, bottom, dateAtBottom) = self.mergedWithItems(top: previousItem, bottom: nextItem)
|
||||
|
||||
var disableDate = self.disableDate
|
||||
if let subject = self.associatedData.subject, case let .messageOptions(_, _, info) = subject, case .reply = info {
|
||||
disableDate = true
|
||||
if let subject = self.associatedData.subject, case let .messageOptions(_, _, info) = subject {
|
||||
switch info {
|
||||
case .reply, .link:
|
||||
disableDate = true
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
let (layout, apply) = nodeLayout(self, params, top, bottom, dateAtBottom && !disableDate)
|
||||
|
@ -67,7 +67,7 @@ final class ChatMessageUnsupportedBubbleContentNode: ChatMessageBubbleContentNod
|
||||
var actionButtonSizeAndApply: ((CGSize, () -> ChatMessageAttachedContentButtonNode))?
|
||||
let refinedButtonWidth = max(boundingWidth - insets.left - insets.right, buttonWidth)
|
||||
|
||||
let (size, apply) = continueActionButtonLayout(refinedButtonWidth)
|
||||
let (size, apply) = continueActionButtonLayout(refinedButtonWidth, 33.0)
|
||||
actionButtonSizeAndApply = (size, apply)
|
||||
let adjustedBoundingSize = CGSize(width: refinedButtonWidth + insets.left + insets.right, height: insets.bottom + size.height)
|
||||
|
||||
|
@ -2550,7 +2550,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
}
|
||||
|
||||
return ContextController.Items(content: .list(items))
|
||||
}, minHeight: nil)
|
||||
}, minHeight: nil, animated: true)
|
||||
})))
|
||||
}
|
||||
if strongSelf.searchDisplayController == nil {
|
||||
@ -2700,7 +2700,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
}
|
||||
|
||||
return ContextController.Items(content: .list(items))
|
||||
}, minHeight: nil)
|
||||
}, minHeight: nil, animated: true)
|
||||
})))
|
||||
}
|
||||
|
||||
@ -5538,7 +5538,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
}, action: { [weak self] c, f in
|
||||
self?.openReport(type: .default, contextController: c, backAction: { c in
|
||||
if let mainItemsImpl = mainItemsImpl {
|
||||
c.setItems(mainItemsImpl() |> map { ContextController.Items(content: .list($0)) }, minHeight: nil)
|
||||
c.setItems(mainItemsImpl() |> map { ContextController.Items(content: .list($0)) }, minHeight: nil, animated: true)
|
||||
}
|
||||
})
|
||||
})))
|
||||
@ -6792,7 +6792,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
}
|
||||
|
||||
if let contextController = contextController {
|
||||
contextController.setItems(.single(ContextController.Items(content: .list(items))), minHeight: nil)
|
||||
contextController.setItems(.single(ContextController.Items(content: .list(items))), minHeight: nil, animated: true)
|
||||
} else {
|
||||
strongSelf.state = strongSelf.state.withHighlightedButton(.voiceChat)
|
||||
if let (layout, navigationHeight) = strongSelf.validLayout {
|
||||
@ -6888,7 +6888,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
}
|
||||
|
||||
if let contextController = contextController {
|
||||
contextController.setItems(.single(ContextController.Items(content: .list(items))), minHeight: nil)
|
||||
contextController.setItems(.single(ContextController.Items(content: .list(items))), minHeight: nil, animated: true)
|
||||
} else {
|
||||
strongSelf.state = strongSelf.state.withHighlightedButton(.voiceChat)
|
||||
if let (layout, navigationHeight) = strongSelf.validLayout {
|
||||
|
Loading…
x
Reference in New Issue
Block a user