mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-04 05:26:48 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
2751cf54d8
@ -1060,14 +1060,9 @@ public class AttachmentController: ViewController, MinimizableController {
|
||||
let sourceButtonScale = sourceButtonFrame.width / targetFrame.width
|
||||
|
||||
if let sourceGlassView = findParentGlassBackgroundView(attachmentButton), let glassParams = sourceGlassView.params {
|
||||
let containerView = UIView()
|
||||
containerView.clipsToBounds = true
|
||||
let containerView = ClipContainerView()
|
||||
containerView.update(bounds: CGRect(origin: CGPoint(x: 0.0, y: (targetFrame.height - targetFrame.width) * 0.5), size: CGSize(width: targetFrame.width, height: targetFrame.width)), topCornerRadius: targetFrame.width * 0.5, bottomCornerRadius: targetFrame.width * 0.5, boundsTransition: .immediate, cornersTransition: .immediate)
|
||||
containerView.frame = targetFrame
|
||||
if #available(iOS 26.0, *) {
|
||||
containerView.cornerConfiguration = .uniformCorners(radius: .fixed(containerView.bounds.width * 0.5))
|
||||
} else {
|
||||
containerView.layer.cornerRadius = containerView.bounds.width * 0.5
|
||||
}
|
||||
self.view.addSubview(containerView)
|
||||
|
||||
let localGlassView = GlassBackgroundView()
|
||||
@ -1079,7 +1074,7 @@ public class AttachmentController: ViewController, MinimizableController {
|
||||
transition: .immediate
|
||||
)
|
||||
localGlassView.frame = CGRect(origin: .zero, size: targetFrame.size)
|
||||
containerView.addSubview(localGlassView)
|
||||
containerView.contentView.addSubview(localGlassView)
|
||||
|
||||
let initialContainerBounds = self.container.bounds
|
||||
let initialContainerFrame = self.container.frame
|
||||
@ -1090,7 +1085,7 @@ public class AttachmentController: ViewController, MinimizableController {
|
||||
self.container.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((targetFrame.width - self.container.frame.width) / 2.0), y: -clipInnerFrame.minY), size: self.container.frame.size)
|
||||
self.container.view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
self.container.bottomClipNode.cornerRadius = 0.0
|
||||
containerView.addSubnode(self.container)
|
||||
containerView.contentView.addSubnode(self.container)
|
||||
|
||||
let buttonIcon = GlassBackgroundView.ContentImageView()
|
||||
let presentationData = controller.context.sharedContext.currentPresentationData.with { $0 }
|
||||
@ -1104,14 +1099,8 @@ public class AttachmentController: ViewController, MinimizableController {
|
||||
localGlassView.contentView.addSubview(buttonIcon)
|
||||
ComponentTransition(buttonTransition).animateBlur(layer: buttonIcon.layer, fromRadius: 0.0, toRadius: 10.0)
|
||||
|
||||
scaleTransition.animateBounds(layer: containerView.layer, from: CGRect(origin: CGPoint(x: 0.0, y: (targetFrame.height - targetFrame.width) * 0.5), size: CGSize(width: targetFrame.width, height: targetFrame.width)))
|
||||
cornersTransition.animateView {
|
||||
if #available(iOS 26.0, *) {
|
||||
containerView.cornerConfiguration = .corners(topLeftRadius: 38.0, topRightRadius: 38.0, bottomLeftRadius: .fixed(layout.deviceMetrics.screenCornerRadius - 2.0), bottomRightRadius: .fixed(layout.deviceMetrics.screenCornerRadius - 2.0))
|
||||
} else {
|
||||
containerView.layer.cornerRadius = layout.deviceMetrics.screenCornerRadius - 2.0
|
||||
}
|
||||
}
|
||||
containerView.update(bounds: CGRect(origin: .zero, size: targetFrame.size), topCornerRadius: 38.0, bottomCornerRadius: layout.deviceMetrics.screenCornerRadius - 2.0, boundsTransition: scaleTransition, cornersTransition: cornersTransition)
|
||||
scaleTransition.animateBounds(layer: containerView.layer, from: CGRect(origin: .zero, size: CGSize(width: targetFrame.width, height: targetFrame.width)))
|
||||
scaleTransition.animateTransformScale(view: containerView, from: sourceButtonScale)
|
||||
positionTransition.animatePosition(layer: containerView.layer, from: sourceButtonFrame.center, to: containerView.center, completion: { _ in
|
||||
self.container.bottomClipNode.cornerRadius = initialBottomClipRadius
|
||||
@ -1176,14 +1165,9 @@ public class AttachmentController: ViewController, MinimizableController {
|
||||
let targetButtonScale = targetButtonFrame.width / initialFrame.width
|
||||
|
||||
if let sourceGlassView = findParentGlassBackgroundView(attachmentButton), let glassParams = sourceGlassView.params {
|
||||
let containerView = UIView()
|
||||
containerView.clipsToBounds = true
|
||||
let containerView = ClipContainerView()
|
||||
containerView.frame = initialFrame
|
||||
if #available(iOS 26.0, *) {
|
||||
containerView.cornerConfiguration = .corners(topLeftRadius: 38.0, topRightRadius: 38.0, bottomLeftRadius: .fixed(layout.deviceMetrics.screenCornerRadius - 2.0), bottomRightRadius: .fixed(layout.deviceMetrics.screenCornerRadius - 2.0))
|
||||
} else {
|
||||
containerView.layer.cornerRadius = layout.deviceMetrics.screenCornerRadius - 2.0
|
||||
}
|
||||
containerView.update(bounds: CGRect(origin: .zero, size: initialFrame.size), topCornerRadius: 38.0, bottomCornerRadius: layout.deviceMetrics.screenCornerRadius - 2.0, boundsTransition: .immediate, cornersTransition: .immediate)
|
||||
self.view.addSubview(containerView)
|
||||
|
||||
let localGlassView = GlassBackgroundView()
|
||||
@ -1195,14 +1179,14 @@ public class AttachmentController: ViewController, MinimizableController {
|
||||
transition: .immediate
|
||||
)
|
||||
localGlassView.frame = CGRect(origin: .zero, size: initialFrame.size)
|
||||
containerView.addSubview(localGlassView)
|
||||
containerView.contentView.addSubview(localGlassView)
|
||||
|
||||
let clipInnerFrame = self.container.container.view.convert(self.container.container.view.bounds, to: self.container.view)
|
||||
self.container.bounds = CGRect(origin: .zero, size: self.container.bounds.size)
|
||||
self.container.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((initialFrame.width - self.container.frame.width) / 2.0), y: -clipInnerFrame.minY), size: self.container.frame.size)
|
||||
self.container.isUserInteractionEnabled = false
|
||||
self.container.view.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.12, removeOnCompletion: false)
|
||||
containerView.addSubnode(self.container)
|
||||
containerView.contentView.addSubnode(self.container)
|
||||
|
||||
let buttonIcon = GlassBackgroundView.ContentImageView()
|
||||
let presentationData = controller.context.sharedContext.currentPresentationData.with { $0 }
|
||||
@ -1217,14 +1201,9 @@ public class AttachmentController: ViewController, MinimizableController {
|
||||
ComponentTransition(buttonTransition).animateBlur(layer: buttonIcon.layer, fromRadius: 10.0, toRadius: 0.0)
|
||||
ComponentTransition(buttonTransition).animateBlur(layer: sourceGlassView.contentView.layer, fromRadius: 10.0, toRadius: 0.0)
|
||||
|
||||
scaleTransition.updateBounds(layer: containerView.layer, bounds: CGRect(origin: CGPoint(x: 0.0, y: (initialFrame.height - initialFrame.width) * 0.5), size: CGSize(width: initialFrame.width, height: initialFrame.width)))
|
||||
cornersTransition.animateView {
|
||||
if #available(iOS 26.0, *) {
|
||||
containerView.cornerConfiguration = .uniformCorners(radius: .fixed(containerView.bounds.width * 0.5))
|
||||
} else {
|
||||
containerView.layer.cornerRadius = containerView.bounds.width * 0.5
|
||||
}
|
||||
}
|
||||
containerView.update(bounds: CGRect(origin: CGPoint(x: 0.0, y: (initialFrame.height - initialFrame.width) * 0.5), size: CGSize(width: initialFrame.width, height: initialFrame.width)), topCornerRadius: initialFrame.width * 0.5, bottomCornerRadius: initialFrame.width * 0.5, boundsTransition: scaleTransition, cornersTransition: cornersTransition)
|
||||
scaleTransition.updateBounds(layer: containerView.layer, bounds: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: initialFrame.width, height: initialFrame.width)))
|
||||
|
||||
scaleTransition.updateTransformScale(layer: containerView.layer, scale: targetButtonScale)
|
||||
positionTransition.updatePosition(layer: containerView.layer, position: targetButtonFrame.center, completion: { [weak self] _ in
|
||||
let _ = self?.container.dismiss(transition: .immediate, completion: completion)
|
||||
@ -1232,7 +1211,6 @@ public class AttachmentController: ViewController, MinimizableController {
|
||||
})
|
||||
|
||||
localGlassView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, delay: 0.2, timingFunction: CAMediaTimingFunctionName.linear.rawValue, removeOnCompletion: false)
|
||||
//sourceGlassView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, delay: 0.27, timingFunction: CAMediaTimingFunctionName.linear.rawValue)
|
||||
scaleTransition.animateTransformScale(view: sourceGlassView, from: 1.0 / targetButtonScale)
|
||||
|
||||
positionTransition.animatePosition(layer: sourceGlassView.layer, from: self.view.convert(initialFrame.center, to: sourceGlassView.superview), to: sourceGlassView.center)
|
||||
@ -1712,3 +1690,36 @@ private func findParentGlassBackgroundView(_ view: UIView) -> GlassBackgroundVie
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
private final class ClipContainerView: UIView {
|
||||
private let clipView = UIView()
|
||||
let contentView = UIView()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
self.clipView.clipsToBounds = true
|
||||
self.clipView.layer.cornerCurve = .continuous
|
||||
self.clipView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
|
||||
|
||||
self.contentView.clipsToBounds = true
|
||||
self.contentView.layer.cornerCurve = .continuous
|
||||
self.contentView.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
|
||||
|
||||
self.addSubview(self.clipView)
|
||||
self.clipView.addSubview(self.contentView)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
preconditionFailure()
|
||||
}
|
||||
|
||||
func update(bounds: CGRect, topCornerRadius: CGFloat, bottomCornerRadius: CGFloat, boundsTransition: ContainedViewLayoutTransition, cornersTransition: ContainedViewLayoutTransition) {
|
||||
cornersTransition.updateCornerRadius(layer: self.clipView.layer, cornerRadius: topCornerRadius)
|
||||
cornersTransition.updateCornerRadius(layer: self.contentView.layer, cornerRadius: bottomCornerRadius)
|
||||
|
||||
boundsTransition.updateFrame(view: self.clipView, frame: CGRect(origin: .zero, size: bounds.size))
|
||||
boundsTransition.updatePosition(layer: self.contentView.layer, position: CGPoint(x: bounds.size.width * 0.5, y: bounds.size.height * 0.5))
|
||||
boundsTransition.updateBounds(layer: self.contentView.layer, bounds: bounds)
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,6 +157,40 @@ public final class SheetComponent<ChildEnvironmentType: Sendable & Equatable>: C
|
||||
}
|
||||
}
|
||||
|
||||
final class BackgroundView: UIView {
|
||||
let topCornersView = UIView()
|
||||
let bottomCornersView = UIView()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
self.topCornersView.clipsToBounds = true
|
||||
self.topCornersView.layer.cornerCurve = .continuous
|
||||
self.topCornersView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
|
||||
|
||||
self.bottomCornersView.clipsToBounds = true
|
||||
self.bottomCornersView.layer.cornerCurve = .continuous
|
||||
self.bottomCornersView.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
|
||||
|
||||
self.addSubview(self.topCornersView)
|
||||
self.topCornersView.addSubview(self.bottomCornersView)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
preconditionFailure()
|
||||
}
|
||||
|
||||
func update(size: CGSize, color: UIColor, topCornerRadius: CGFloat, bottomCornerRadius: CGFloat, transition: ComponentTransition) {
|
||||
transition.setCornerRadius(layer: self.topCornersView.layer, cornerRadius: topCornerRadius)
|
||||
transition.setCornerRadius(layer: self.bottomCornersView.layer, cornerRadius: bottomCornerRadius)
|
||||
|
||||
transition.setFrame(view: self.topCornersView, frame: CGRect(origin: .zero, size: size))
|
||||
transition.setFrame(view: self.bottomCornersView, frame: CGRect(origin: .zero, size: size))
|
||||
|
||||
transition.setBackgroundColor(view: self.bottomCornersView, color: color)
|
||||
}
|
||||
}
|
||||
|
||||
public final class View: UIView, UIScrollViewDelegate, ComponentTaggedView {
|
||||
public final class Tag {
|
||||
public init() {
|
||||
@ -174,7 +208,7 @@ public final class SheetComponent<ChildEnvironmentType: Sendable & Equatable>: C
|
||||
|
||||
private let dimView: UIView
|
||||
private let scrollView: ScrollView
|
||||
private let backgroundView: DynamicCornerRadiusView
|
||||
private let backgroundView: BackgroundView
|
||||
private var effectView: UIVisualEffectView?
|
||||
private let contentView: ComponentView<ChildEnvironmentType>
|
||||
private var headerView: ComponentView<Empty>?
|
||||
@ -198,7 +232,7 @@ public final class SheetComponent<ChildEnvironmentType: Sendable & Equatable>: C
|
||||
self.scrollView.showsHorizontalScrollIndicator = false
|
||||
self.scrollView.alwaysBounceVertical = true
|
||||
|
||||
self.backgroundView = DynamicCornerRadiusView()
|
||||
self.backgroundView = BackgroundView()
|
||||
|
||||
self.contentView = ComponentView<ChildEnvironmentType>()
|
||||
|
||||
@ -377,6 +411,7 @@ public final class SheetComponent<ChildEnvironmentType: Sendable & Equatable>: C
|
||||
bottomCornerRadius = 12.0
|
||||
}
|
||||
|
||||
var backgroundColor: UIColor = .clear
|
||||
switch component.backgroundColor {
|
||||
case let .blur(style):
|
||||
self.backgroundView.isHidden = true
|
||||
@ -388,7 +423,7 @@ public final class SheetComponent<ChildEnvironmentType: Sendable & Equatable>: C
|
||||
self.effectView = effectView
|
||||
}
|
||||
case let .color(color):
|
||||
self.backgroundView.updateColor(color: color, transition: .immediate)
|
||||
backgroundColor = color
|
||||
self.backgroundView.isHidden = false
|
||||
self.effectView?.removeFromSuperview()
|
||||
self.effectView = nil
|
||||
@ -432,6 +467,7 @@ public final class SheetComponent<ChildEnvironmentType: Sendable & Equatable>: C
|
||||
}
|
||||
contentView.clipsToBounds = component.clipsContent
|
||||
contentView.layer.cornerRadius = topCornerRadius
|
||||
|
||||
if sheetEnvironment.isCentered {
|
||||
let y: CGFloat = floorToScreenPixels((availableSize.height - contentSize.height) / 2.0)
|
||||
transition.setFrame(view: contentView, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - contentSize.width) / 2.0), y: -y), size: contentSize), completion: nil)
|
||||
@ -439,7 +475,7 @@ public final class SheetComponent<ChildEnvironmentType: Sendable & Equatable>: C
|
||||
if let effectView = self.effectView {
|
||||
transition.setFrame(view: effectView, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - contentSize.width) / 2.0), y: -y), size: contentSize), completion: nil)
|
||||
}
|
||||
self.backgroundView.update(size: contentSize, corners: .init(minXMinY: topCornerRadius, maxXMinY: topCornerRadius, minXMaxY: bottomCornerRadius, maxXMaxY: bottomCornerRadius), transition: transition)
|
||||
self.backgroundView.update(size: contentSize, color: backgroundColor, topCornerRadius: topCornerRadius, bottomCornerRadius: topCornerRadius, transition: transition)
|
||||
} else {
|
||||
switch component.style {
|
||||
case .glass:
|
||||
@ -452,7 +488,7 @@ public final class SheetComponent<ChildEnvironmentType: Sendable & Equatable>: C
|
||||
transition.setFrame(view: effectView, frame: CGRect(origin: .zero, size: CGSize(width: contentSize.width, height: contentSize.height + 1000.0)), completion: nil)
|
||||
}
|
||||
}
|
||||
self.backgroundView.update(size: contentSize, corners: .init(minXMinY: topCornerRadius, maxXMinY: topCornerRadius, minXMaxY: bottomCornerRadius, maxXMaxY: bottomCornerRadius), transition: transition)
|
||||
self.backgroundView.update(size: contentSize, color: backgroundColor, topCornerRadius: topCornerRadius, bottomCornerRadius: bottomCornerRadius, transition: transition)
|
||||
}
|
||||
}
|
||||
transition.setFrame(view: self.scrollView, frame: CGRect(origin: CGPoint(), size: availableSize), completion: nil)
|
||||
|
||||
@ -25,7 +25,7 @@ import EmojiStatusComponent
|
||||
|
||||
private let extensionImageCache = Atomic<[UInt32: UIImage]>(value: [:])
|
||||
|
||||
private let redColors: (UInt32, UInt32) = (0xed6b7b, 0xe63f45)
|
||||
private let redColors: (UInt32, UInt32) = (0xff875f, 0xff5069)
|
||||
private let greenColors: (UInt32, UInt32) = (0x99de6f, 0x5fb84f)
|
||||
private let blueColors: (UInt32, UInt32) = (0x72d5fd, 0x2a9ef1)
|
||||
private let yellowColors: (UInt32, UInt32) = (0xffa24b, 0xed705c)
|
||||
@ -63,11 +63,22 @@ private func generateExtensionImage(colors: (UInt32, UInt32)) -> UIImage? {
|
||||
|
||||
context.restoreGState()
|
||||
|
||||
context.saveGState()
|
||||
let rounded = UIBezierPath(roundedRect: CGRect(origin: .zero, size: size), cornerRadius: 13).cgPath
|
||||
let full = UIBezierPath(rect: CGRect(origin: .zero, size: size)).cgPath
|
||||
context.addPath(full)
|
||||
context.addPath(rounded)
|
||||
context.setBlendMode(.destinationOut)
|
||||
context.drawPath(using: .eoFill)
|
||||
context.setBlendMode(.normal)
|
||||
context.restoreGState()
|
||||
|
||||
context.beginPath()
|
||||
let _ = try? drawSvgPath(context, path: "M6,0 L26.7573593,0 C27.5530088,-8.52837125e-16 28.3160705,0.316070521 28.8786797,0.878679656 L39.1213203,11.1213203 C39.6839295,11.6839295 40,12.4469912 40,13.2426407 L40,34 C40,37.3137085 37.3137085,40 34,40 L6,40 C2.6862915,40 4.05812251e-16,37.3137085 0,34 L0,6 C-4.05812251e-16,2.6862915 2.6862915,6.08718376e-16 6,0 ")
|
||||
context.clip()
|
||||
|
||||
context.setFillColor(UIColor(rgb: 0xffffff, alpha: 0.2).cgColor)
|
||||
context.setBlendMode(.overlay)
|
||||
context.setFillColor(UIColor(rgb: 0xffffff, alpha: 0.5).cgColor)
|
||||
context.translateBy(x: 40.0 - 14.0, y: 0.0)
|
||||
let _ = try? drawSvgPath(context, path: "M-1,0 L14,0 L14,15 L14,14 C14,12.8954305 13.1045695,12 12,12 L4,12 C2.8954305,12 2,11.1045695 2,10 L2,2 C2,0.8954305 1.1045695,-2.02906125e-16 0,0 L-1,0 L-1,0 Z ")
|
||||
})
|
||||
@ -1250,7 +1261,7 @@ public final class ListMessageFileItemNode: ListMessageNode {
|
||||
let iconFrame = CGRect(origin: CGPoint(x: params.leftInset + leftOffset + 12.0, y: 8.0 + verticalInset), size: iconSize)
|
||||
transition.updateFrame(node: strongSelf.extensionIconNode, frame: iconFrame)
|
||||
strongSelf.extensionIconNode.image = extensionIconImage
|
||||
transition.updateFrame(node: strongSelf.extensionIconText, frame: CGRect(origin: CGPoint(x: iconFrame.minX + floorToScreenPixels((iconFrame.width - extensionTextLayout.size.width) / 2.0), y: iconFrame.minY + 7.0 + floorToScreenPixels((iconFrame.height - extensionTextLayout.size.height) / 2.0)), size: extensionTextLayout.size))
|
||||
transition.updateFrame(node: strongSelf.extensionIconText, frame: CGRect(origin: CGPoint(x: iconFrame.minX + floorToScreenPixels((iconFrame.width - extensionTextLayout.size.width) / 2.0), y: iconFrame.minY + 5.0 + floorToScreenPixels((iconFrame.height - extensionTextLayout.size.height) / 2.0)), size: extensionTextLayout.size))
|
||||
|
||||
transition.updateFrame(node: strongSelf.iconStatusNode, frame: iconFrame)
|
||||
|
||||
|
||||
@ -2564,7 +2564,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
||||
} else {
|
||||
topEdgeColor = .clear
|
||||
}
|
||||
let topEdgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: 100.0))
|
||||
let topEdgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: 80.0))
|
||||
transition.updateFrame(view: self.controllerNode.topEdgeEffectView, frame: topEdgeEffectFrame)
|
||||
transition.updateAlpha(layer: self.controllerNode.topEdgeEffectView.layer, alpha: self.controllerNode.scrolledExactlyToTop && self.controllerNode.currentDisplayMode == .all ? 0.0 : 1.0)
|
||||
self.controllerNode.topEdgeEffectView.update(content: topEdgeColor, blur: true, alpha: 0.8, rect: topEdgeEffectFrame, edge: .top, edgeSize: topEdgeEffectFrame.height, transition: ComponentTransition(transition))
|
||||
|
||||
@ -399,7 +399,7 @@ public final class SegmentedControlNode: ASDisplayNode, ASGestureRecognizerDeleg
|
||||
}
|
||||
|
||||
if !self.dividerNodes.isEmpty {
|
||||
let dividerSize = CGSize(width: 1.0, height: 16.0)
|
||||
let dividerSize = CGSize(width: 1.0, height: size.height - 8.0)
|
||||
let delta: CGFloat = size.width / CGFloat(self.dividerNodes.count + 1)
|
||||
for i in 0 ..< self.dividerNodes.count {
|
||||
let dividerNode = self.dividerNodes[i]
|
||||
|
||||
@ -797,6 +797,7 @@ public final class SolidRoundedButtonNode: ASDisplayNode {
|
||||
self.subtitleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||
}
|
||||
|
||||
if self.glass {
|
||||
let chromeView: UIImageView
|
||||
var chromeTransition = transition
|
||||
if let current = self.chromeView {
|
||||
@ -816,6 +817,7 @@ public final class SolidRoundedButtonNode: ASDisplayNode {
|
||||
chromeView.image = GlassBackgroundView.generateForegroundImage(size: CGSize(width: 26.0 * 2.0, height: 26.0 * 2.0), isDark: self.theme.backgroundColor.lightness < 0.4, fillColor: .clear)
|
||||
}
|
||||
chromeTransition.updateFrame(view: chromeView, frame: CGRect(origin: .zero, size: buttonSize))
|
||||
}
|
||||
|
||||
return buttonSize.height
|
||||
}
|
||||
|
||||
@ -218,7 +218,7 @@ public class CallStatusBarNodeImpl: CallStatusBarNode {
|
||||
private var reactionItems: [ReactionItem]?
|
||||
private var messagesState: GroupCallMessagesContext.State?
|
||||
private let messagesStateDisposable = MetaDisposable()
|
||||
private var currentMessageId: Int64?
|
||||
private var currentMessageId: GroupCallMessagesContext.Message.Id?
|
||||
|
||||
private let hierarchyTrackingNode: HierarchyTrackingNode
|
||||
private var isCurrentlyInHierarchy = true
|
||||
|
||||
@ -930,9 +930,25 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
if let data = accountContext.currentAppConfiguration.with({ $0 }).data, let value = data["group_call_message_ttl"] as? Double {
|
||||
messageLifetime = Int32(value)
|
||||
}
|
||||
|
||||
var createMessageContext = true
|
||||
|
||||
if isStream {
|
||||
messageLifetime = Int32.max
|
||||
|
||||
if self.isStream {
|
||||
createMessageContext = false
|
||||
if let data = self.accountContext.currentAppConfiguration.with({ $0 }).data {
|
||||
if let dev = data["dev"] as? Double, dev != 0.0 {
|
||||
createMessageContext = true
|
||||
}
|
||||
if data["ios_can_join_streams"] != nil {
|
||||
createMessageContext = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if createMessageContext {
|
||||
self.messagesContext = accountContext.engine.messages.groupCallMessages(
|
||||
callId: initialCall.description.id,
|
||||
reference: .id(id: initialCall.description.id, accessHash: initialCall.description.accessHash),
|
||||
@ -942,6 +958,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
)
|
||||
self.messagesStatePromise.set(self.messagesContext!.state)
|
||||
}
|
||||
}
|
||||
|
||||
var sharedAudioContext = sharedAudioContext
|
||||
if sharedAudioContext == nil {
|
||||
@ -2028,6 +2045,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
}
|
||||
|
||||
self.currentLocalSsrc = ssrc
|
||||
|
||||
self.requestDisposable.set((self.accountContext.engine.calls.joinGroupCall(
|
||||
peerId: self.peerId,
|
||||
joinAs: self.joinAsPeerId,
|
||||
@ -4042,7 +4060,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
}
|
||||
}
|
||||
|
||||
public func deleteMessage(id: Int64) {
|
||||
public func deleteMessage(id: GroupCallMessagesContext.Message.Id) {
|
||||
if let messagesContext = self.messagesContext {
|
||||
messagesContext.deleteMessage(id: id)
|
||||
}
|
||||
|
||||
@ -3674,7 +3674,22 @@ private func deserializeGroupCallMessage(data: Data) -> (randomId: Int64, text:
|
||||
|
||||
public final class GroupCallMessagesContext {
|
||||
public final class Message: Equatable {
|
||||
public let id: Int64
|
||||
public struct Id: Hashable {
|
||||
public enum Space {
|
||||
case local
|
||||
case remote
|
||||
}
|
||||
|
||||
public var space: Space
|
||||
public var id: Int64
|
||||
|
||||
public init(space: Space, id: Int64) {
|
||||
self.space = space
|
||||
self.id = id
|
||||
}
|
||||
}
|
||||
|
||||
public let id: Id
|
||||
public let author: EnginePeer?
|
||||
public let text: String
|
||||
public let entities: [MessageTextEntity]
|
||||
@ -3682,7 +3697,7 @@ public final class GroupCallMessagesContext {
|
||||
public let lifetime: Int32
|
||||
public let paidStars: Int64?
|
||||
|
||||
public init(id: Int64, author: EnginePeer?, text: String, entities: [MessageTextEntity], date: Int32, lifetime: Int32, paidStars: Int64?) {
|
||||
public init(id: Id, author: EnginePeer?, text: String, entities: [MessageTextEntity], date: Int32, lifetime: Int32, paidStars: Int64?) {
|
||||
self.id = id
|
||||
self.author = author
|
||||
self.text = text
|
||||
@ -3692,7 +3707,7 @@ public final class GroupCallMessagesContext {
|
||||
self.paidStars = paidStars
|
||||
}
|
||||
|
||||
public func withId(_ id: Int64) -> Message {
|
||||
public func withId(_ id: Id) -> Message {
|
||||
return Message(
|
||||
id: id,
|
||||
author: self.author,
|
||||
@ -3793,9 +3808,7 @@ public final class GroupCallMessagesContext {
|
||||
}
|
||||
switch update.update {
|
||||
case let .newPlaintextMessage(authorId, messageId, text, entities, timestamp, paidMessageStars):
|
||||
if authorId != self.account.peerId {
|
||||
addedMessages.append((authorId, messageId, text, entities, timestamp, paidMessageStars))
|
||||
}
|
||||
case let .newOpaqueMessage(authorId, data):
|
||||
if authorId != self.account.peerId {
|
||||
addedOpaqueMessages.append((authorId, data))
|
||||
@ -3833,7 +3846,7 @@ public final class GroupCallMessagesContext {
|
||||
continue
|
||||
}
|
||||
messages.append(Message(
|
||||
id: randomId,
|
||||
id: Message.Id(space: .remote, id: randomId),
|
||||
author: transaction.getPeer(addedOpaqueMessage.authorId).flatMap(EnginePeer.init),
|
||||
text: text,
|
||||
entities: entities,
|
||||
@ -3856,7 +3869,7 @@ public final class GroupCallMessagesContext {
|
||||
}
|
||||
|
||||
let message = Message(
|
||||
id: Int64(addedMessage.messageId),
|
||||
id: Message.Id(space: .remote, id: Int64(addedMessage.messageId)),
|
||||
author: transaction.getPeer(addedMessage.authorId).flatMap(EnginePeer.init),
|
||||
text: addedMessage.text,
|
||||
entities: addedMessage.entities,
|
||||
@ -3874,10 +3887,13 @@ public final class GroupCallMessagesContext {
|
||||
return
|
||||
}
|
||||
for message in messages {
|
||||
self.processedIds.insert(message.id)
|
||||
self.processedIds.insert(message.id.id)
|
||||
}
|
||||
var state = self.state
|
||||
var existingIds = Set(state.messages.map(\.id))
|
||||
|
||||
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||
|
||||
for message in messages {
|
||||
if existingIds.contains(message.id) {
|
||||
continue
|
||||
@ -3885,9 +3901,11 @@ public final class GroupCallMessagesContext {
|
||||
existingIds.insert(message.id)
|
||||
state.messages.append(message)
|
||||
if self.isLiveStream && message.paidStars != nil {
|
||||
if message.date + message.lifetime >= currentTime {
|
||||
state.pinnedMessages.append(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
self.state = state
|
||||
})
|
||||
}
|
||||
@ -3912,7 +3930,7 @@ public final class GroupCallMessagesContext {
|
||||
if !self.isLiveStream {
|
||||
for i in (0 ..< self.state.messages.count).reversed() {
|
||||
let message = self.state.messages[i]
|
||||
if (now - message.date) < message.lifetime {
|
||||
if message.date + message.lifetime < now {
|
||||
if updatedState == nil {
|
||||
updatedState = self.state
|
||||
}
|
||||
@ -3923,7 +3941,7 @@ public final class GroupCallMessagesContext {
|
||||
|
||||
for i in (0 ..< self.state.pinnedMessages.count).reversed() {
|
||||
let message = self.state.pinnedMessages[i]
|
||||
if (now - message.date) < message.lifetime {
|
||||
if message.date + message.lifetime < now {
|
||||
if updatedState == nil {
|
||||
updatedState = self.state
|
||||
}
|
||||
@ -3963,7 +3981,7 @@ public final class GroupCallMessagesContext {
|
||||
|
||||
var state = self.state
|
||||
let message = Message(
|
||||
id: randomId,
|
||||
id: Message.Id(space: .local, id: randomId),
|
||||
author: fromPeer.flatMap(EnginePeer.init),
|
||||
text: text,
|
||||
entities: entities,
|
||||
@ -4029,12 +4047,13 @@ public final class GroupCallMessagesContext {
|
||||
for update in updates.allUpdates {
|
||||
if case let .updateMessageID(id, randomIdValue) = update {
|
||||
if randomIdValue == randomId {
|
||||
self.processedIds.insert(Int64(id))
|
||||
var state = self.state
|
||||
if let index = state.messages.firstIndex(where: { $0.id == randomId }) {
|
||||
state.messages[index] = state.messages[index].withId(Int64(id))
|
||||
if let index = state.messages.firstIndex(where: { $0.id == Message.Id(space: .local, id: randomId) }) {
|
||||
state.messages[index] = state.messages[index].withId(Message.Id(space: .remote, id: Int64(id)))
|
||||
}
|
||||
if let index = state.pinnedMessages.firstIndex(where: { $0.id == randomId }) {
|
||||
state.pinnedMessages[index] = state.pinnedMessages[index].withId(Int64(id))
|
||||
if let index = state.pinnedMessages.firstIndex(where: { $0.id == Message.Id(space: .local, id: randomId) }) {
|
||||
state.pinnedMessages[index] = state.pinnedMessages[index].withId(Message.Id(space: .remote, id: Int64(id)))
|
||||
}
|
||||
self.state = state
|
||||
break
|
||||
@ -4048,7 +4067,7 @@ public final class GroupCallMessagesContext {
|
||||
})
|
||||
}
|
||||
|
||||
func deleteMessage(id: Int64) {
|
||||
func deleteMessage(id: Message.Id) {
|
||||
var updatedState: State?
|
||||
if let index = self.state.messages.firstIndex(where: { $0.id == id }) {
|
||||
if updatedState == nil {
|
||||
@ -4091,7 +4110,7 @@ public final class GroupCallMessagesContext {
|
||||
}
|
||||
}
|
||||
|
||||
public func deleteMessage(id: Int64) {
|
||||
public func deleteMessage(id: Message.Id) {
|
||||
self.impl.with { impl in
|
||||
impl.deleteMessage(id: id)
|
||||
}
|
||||
|
||||
@ -10,7 +10,6 @@ import PresentationDataUtils
|
||||
import TelegramPresentationData
|
||||
import TelegramUIPreferences
|
||||
import AccountContext
|
||||
import SearchBarNode
|
||||
import MergeLists
|
||||
import ChatListSearchItemHeader
|
||||
import ItemListUI
|
||||
@ -20,92 +19,6 @@ import ListMessageItem
|
||||
import ComponentFlow
|
||||
import SearchInputPanelComponent
|
||||
|
||||
private let searchBarFont = Font.regular(17.0)
|
||||
|
||||
private final class AttachmentFileSearchNavigationContentNode: NavigationBarContentNode, ItemListControllerSearchNavigationContentNode {
|
||||
private var theme: PresentationTheme
|
||||
private let strings: PresentationStrings
|
||||
|
||||
private let focus: () -> Void
|
||||
private let cancel: () -> Void
|
||||
|
||||
private let searchBar: SearchBarNode
|
||||
|
||||
private var queryUpdated: ((String) -> Void)?
|
||||
var activity: Bool = false {
|
||||
didSet {
|
||||
self.searchBar.activity = activity
|
||||
}
|
||||
}
|
||||
init(theme: PresentationTheme, strings: PresentationStrings, focus: @escaping () -> Void, cancel: @escaping () -> Void, updateActivity: @escaping(@escaping(Bool)->Void) -> Void) {
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
|
||||
self.focus = focus
|
||||
self.cancel = cancel
|
||||
|
||||
self.searchBar = SearchBarNode(theme: SearchBarNodeTheme(theme: theme, hasSeparator: false), strings: strings, fieldStyle: .modern, displayBackground: false)
|
||||
|
||||
super.init()
|
||||
|
||||
//self.addSubnode(self.searchBar)
|
||||
|
||||
self.searchBar.cancel = { [weak self] in
|
||||
self?.searchBar.deactivate(clear: false)
|
||||
self?.cancel()
|
||||
}
|
||||
|
||||
self.searchBar.textUpdated = { [weak self] query, _ in
|
||||
self?.queryUpdated?(query)
|
||||
}
|
||||
|
||||
self.searchBar.focusUpdated = { [weak self] focus in
|
||||
if focus {
|
||||
self?.focus()
|
||||
}
|
||||
}
|
||||
|
||||
updateActivity({ [weak self] value in
|
||||
self?.activity = value
|
||||
})
|
||||
|
||||
self.updatePlaceholder()
|
||||
}
|
||||
|
||||
func setQueryUpdated(_ f: @escaping (String) -> Void) {
|
||||
self.queryUpdated = f
|
||||
}
|
||||
|
||||
func updateTheme(_ theme: PresentationTheme) {
|
||||
self.theme = theme
|
||||
self.searchBar.updateThemeAndStrings(theme: SearchBarNodeTheme(theme: self.theme), strings: self.strings)
|
||||
self.updatePlaceholder()
|
||||
}
|
||||
|
||||
func updatePlaceholder() {
|
||||
self.searchBar.placeholderString = NSAttributedString(string: self.strings.Attachment_FilesSearchPlaceholder, font: searchBarFont, textColor: self.theme.rootController.navigationSearchBar.inputPlaceholderTextColor)
|
||||
}
|
||||
|
||||
override var nominalHeight: CGFloat {
|
||||
return 56.0
|
||||
}
|
||||
|
||||
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
let searchBarFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height - self.nominalHeight), size: CGSize(width: size.width, height: 56.0))
|
||||
self.searchBar.frame = searchBarFrame
|
||||
self.searchBar.updateLayout(boundingSize: searchBarFrame.size, leftInset: leftInset, rightInset: rightInset, transition: transition)
|
||||
}
|
||||
|
||||
func activate() {
|
||||
//self.searchBar.activate()
|
||||
}
|
||||
|
||||
func deactivate() {
|
||||
//self.searchBar.deactivate(clear: false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final class AttachmentFileSearchItem: ItemListControllerSearch {
|
||||
let context: AccountContext
|
||||
let presentationData: PresentationData
|
||||
@ -153,15 +66,6 @@ final class AttachmentFileSearchItem: ItemListControllerSearch {
|
||||
|
||||
func titleContentNode(current: (NavigationBarContentNode & ItemListControllerSearchNavigationContentNode)?) -> (NavigationBarContentNode & ItemListControllerSearchNavigationContentNode)? {
|
||||
return nil
|
||||
// let presentationData = self.presentationData
|
||||
// if let current = current as? AttachmentFileSearchNavigationContentNode {
|
||||
// current.updateTheme(presentationData.theme)
|
||||
// return current
|
||||
// } else {
|
||||
// return AttachmentFileSearchNavigationContentNode(theme: presentationData.theme, strings: presentationData.strings, focus: self.focus, cancel: self.cancel, updateActivity: { [weak self] value in
|
||||
// self?.updateActivity = value
|
||||
// })
|
||||
// }
|
||||
}
|
||||
|
||||
func node(current: ItemListControllerSearchNode?, titleContentNode: (NavigationBarContentNode & ItemListControllerSearchNavigationContentNode)?) -> ItemListControllerSearchNode {
|
||||
@ -195,7 +99,6 @@ private final class AttachmentFileSearchItemNode: ItemListControllerSearchNode {
|
||||
send(message)
|
||||
}, updateActivity: updateActivity)
|
||||
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.containerNode)
|
||||
@ -346,7 +249,7 @@ private final class AttachmentFileSearchEntry: Comparable, Identifiable {
|
||||
interaction.send(message)
|
||||
return false
|
||||
}, openMessageContextMenu: { _, _, _, _, _ in }, toggleMessagesSelection: { _, _ in }, openUrl: { _, _, _, _ in }, openInstantPage: { _, _ in }, longTap: { _, _ in }, getHiddenMedia: { return [:] })
|
||||
return ListMessageItem(presentationData: ChatPresentationData(presentationData: interaction.context.sharedContext.currentPresentationData.with({$0})), context: interaction.context, chatLocation: .peer(id: PeerId(0)), interaction: itemInteraction, message: message, selection: .none, displayHeader: true, displayFileInfo: false, displayBackground: true, style: .plain)
|
||||
return ListMessageItem(presentationData: ChatPresentationData(presentationData: interaction.context.sharedContext.currentPresentationData.with({$0})), systemStyle: .glass, context: interaction.context, chatLocation: .peer(id: PeerId(0)), interaction: itemInteraction, message: message, selection: .none, displayHeader: true, displayFileInfo: false, displayBackground: true, style: .plain)
|
||||
}
|
||||
}
|
||||
|
||||
@ -412,7 +315,7 @@ public final class AttachmentFileSearchContainerNode: SearchDisplayControllerCon
|
||||
self.presentationDataPromise = Promise(self.presentationData)
|
||||
|
||||
self.dimNode = ASDisplayNode()
|
||||
self.dimNode.backgroundColor = .clear // UIColor.black.withAlphaComponent(0.5)
|
||||
self.dimNode.backgroundColor = .clear
|
||||
|
||||
self.listNode = ListView()
|
||||
self.listNode.accessibilityPageScrolledString = { row, count in
|
||||
@ -435,7 +338,6 @@ public final class AttachmentFileSearchContainerNode: SearchDisplayControllerCon
|
||||
|
||||
self.listNode.backgroundColor = self.presentationData.theme.chatList.backgroundColor
|
||||
self.listNode.alpha = 0.0
|
||||
//self.listNode.isHidden = true
|
||||
|
||||
self._hasDim = true
|
||||
|
||||
@ -488,7 +390,7 @@ public final class AttachmentFileSearchContainerNode: SearchDisplayControllerCon
|
||||
index += 1
|
||||
}
|
||||
} else {
|
||||
for _ in 0 ..< 2 {
|
||||
for _ in 0 ..< 16 {
|
||||
entries.append(AttachmentFileSearchEntry(index: index, message: nil))
|
||||
index += 1
|
||||
}
|
||||
|
||||
@ -174,43 +174,43 @@ public final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode {
|
||||
self.button = HighlightableButton()
|
||||
self.buttonBackgroundView = GlassBackgroundView()
|
||||
self.buttonBackgroundView.isUserInteractionEnabled = false
|
||||
self.button.addSubview(self.buttonBackgroundView)
|
||||
self.buttonTitle = ImmediateTextNode()
|
||||
self.buttonTitle.isUserInteractionEnabled = false
|
||||
self.buttonTintTitle = ImmediateTextNode()
|
||||
self.buttonBackgroundView.contentView.addSubview(self.buttonTitle.view)
|
||||
self.buttonBackgroundView.maskContentView.addSubview(self.buttonTintTitle.view)
|
||||
self.buttonBackgroundView.contentView.addSubview(self.button)
|
||||
|
||||
self.helpButton = HighlightableButton()
|
||||
self.helpButton.isHidden = true
|
||||
self.helpButtonBackgroundView = GlassBackgroundView()
|
||||
self.helpButtonBackgroundView.isUserInteractionEnabled = false
|
||||
self.helpButton.addSubview(self.helpButtonBackgroundView)
|
||||
self.helpButtonIconView = GlassBackgroundView.ContentImageView()
|
||||
self.helpButtonBackgroundView.contentView.addSubview(self.helpButtonIconView)
|
||||
self.helpButtonBackgroundView.contentView.addSubview(self.helpButton)
|
||||
self.helpButtonBackgroundView.isHidden = true
|
||||
|
||||
self.giftButton = HighlightableButton()
|
||||
self.giftButton.isHidden = true
|
||||
self.giftButtonBackgroundView = GlassBackgroundView()
|
||||
self.giftButtonBackgroundView.isUserInteractionEnabled = false
|
||||
self.giftButton.addSubview(self.giftButtonBackgroundView)
|
||||
self.giftButtonIconView = GlassBackgroundView.ContentImageView()
|
||||
self.giftButtonBackgroundView.contentView.addSubview(self.giftButtonIconView)
|
||||
self.giftButtonBackgroundView.contentView.addSubview(self.giftButton)
|
||||
self.giftButtonBackgroundView.isHidden = true
|
||||
|
||||
self.suggestedPostButton = HighlightableButton()
|
||||
self.suggestedPostButton.isHidden = true
|
||||
self.suggestedPostButtonBackgroundView = GlassBackgroundView()
|
||||
self.suggestedPostButtonBackgroundView.isUserInteractionEnabled = false
|
||||
self.suggestedPostButton.addSubview(self.suggestedPostButtonBackgroundView)
|
||||
self.suggestedPostButtonIconView = GlassBackgroundView.ContentImageView()
|
||||
self.suggestedPostButtonBackgroundView.contentView.addSubview(self.suggestedPostButtonIconView)
|
||||
self.suggestedPostButtonBackgroundView.contentView.addSubview(self.suggestedPostButton)
|
||||
self.suggestedPostButtonBackgroundView.isHidden = true
|
||||
|
||||
super.init()
|
||||
|
||||
self.view.addSubview(self.button)
|
||||
self.view.addSubview(self.helpButton)
|
||||
self.view.addSubview(self.giftButton)
|
||||
self.view.addSubview(self.suggestedPostButton)
|
||||
self.view.addSubview(self.buttonBackgroundView)
|
||||
self.view.addSubview(self.helpButtonBackgroundView)
|
||||
self.view.addSubview(self.giftButtonBackgroundView)
|
||||
self.view.addSubview(self.suggestedPostButtonBackgroundView)
|
||||
self.button.addTarget(self, action: #selector(self.buttonPressed), for: .touchUpInside)
|
||||
self.helpButton.addTarget(self, action: #selector(self.helpPressed), for: .touchUpInside)
|
||||
self.giftButton.addTarget(self, action: #selector(self.giftPressed), for: .touchUpInside)
|
||||
@ -442,69 +442,69 @@ public final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode {
|
||||
self.giftButton.layer.animateScale(from: 0.01, to: 1.0, duration: 0.2)
|
||||
}
|
||||
|
||||
self.giftButton.isHidden = false
|
||||
self.helpButton.isHidden = true
|
||||
self.suggestedPostButton.isHidden = !broadcastInfo.flags.contains(.hasMonoforum)
|
||||
self.giftButtonBackgroundView.isHidden = false
|
||||
self.helpButtonBackgroundView.isHidden = true
|
||||
self.suggestedPostButtonBackgroundView.isHidden = !broadcastInfo.flags.contains(.hasMonoforum)
|
||||
self.presentGiftOrSuggestTooltip()
|
||||
} else if case let .broadcast(broadcastInfo) = peer.info, broadcastInfo.flags.contains(.hasMonoforum) {
|
||||
self.giftButton.isHidden = true
|
||||
self.helpButton.isHidden = true
|
||||
self.suggestedPostButton.isHidden = false
|
||||
self.giftButtonBackgroundView.isHidden = true
|
||||
self.helpButtonBackgroundView.isHidden = true
|
||||
self.suggestedPostButtonBackgroundView.isHidden = false
|
||||
self.presentGiftOrSuggestTooltip()
|
||||
} else if peer.flags.contains(.isGigagroup), self.action == .muteNotifications || self.action == .unmuteNotifications {
|
||||
self.giftButton.isHidden = true
|
||||
self.helpButton.isHidden = false
|
||||
self.suggestedPostButton.isHidden = true
|
||||
self.giftButtonBackgroundView.isHidden = true
|
||||
self.helpButtonBackgroundView.isHidden = false
|
||||
self.suggestedPostButtonBackgroundView.isHidden = true
|
||||
} else {
|
||||
self.giftButton.isHidden = true
|
||||
self.helpButton.isHidden = true
|
||||
self.suggestedPostButton.isHidden = true
|
||||
self.giftButtonBackgroundView.isHidden = true
|
||||
self.helpButtonBackgroundView.isHidden = true
|
||||
self.suggestedPostButtonBackgroundView.isHidden = true
|
||||
}
|
||||
} else {
|
||||
self.giftButton.isHidden = true
|
||||
self.helpButton.isHidden = true
|
||||
self.suggestedPostButton.isHidden = true
|
||||
self.giftButtonBackgroundView.isHidden = true
|
||||
self.helpButtonBackgroundView.isHidden = true
|
||||
self.suggestedPostButtonBackgroundView.isHidden = true
|
||||
}
|
||||
|
||||
let buttonTitleSize = self.buttonTitle.updateLayout(CGSize(width: width, height: panelHeight))
|
||||
let _ = self.buttonTintTitle.updateLayout(CGSize(width: width, height: panelHeight))
|
||||
let buttonSize = CGSize(width: buttonTitleSize.width + 16.0 * 2.0, height: 40.0)
|
||||
let buttonFrame = CGRect(origin: CGPoint(x: floor((width - buttonSize.width) / 2.0), y: floor((panelHeight - buttonSize.height) * 0.5)), size: buttonSize)
|
||||
transition.updateFrame(view: self.button, frame: buttonFrame)
|
||||
transition.updateFrame(view: self.buttonBackgroundView, frame: CGRect(origin: CGPoint(), size: buttonFrame.size))
|
||||
transition.updateFrame(view: self.buttonBackgroundView, frame: buttonFrame)
|
||||
transition.updateFrame(view: self.button, frame: CGRect(origin: CGPoint(), size: buttonFrame.size))
|
||||
let buttonTintColor: GlassBackgroundView.TintColor
|
||||
if case .join = self.action {
|
||||
buttonTintColor = .init(kind: .custom, color: interfaceState.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7), innerColor: interfaceState.theme.chat.inputPanel.actionControlFillColor)
|
||||
} else {
|
||||
buttonTintColor = .init(kind: .panel, color: interfaceState.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7))
|
||||
}
|
||||
self.buttonBackgroundView.update(size: buttonFrame.size, cornerRadius: buttonFrame.height * 0.5, isDark: interfaceState.theme.overallDarkAppearance, tintColor: buttonTintColor, transition: ComponentTransition(transition))
|
||||
self.buttonBackgroundView.update(size: buttonFrame.size, cornerRadius: buttonFrame.height * 0.5, isDark: interfaceState.theme.overallDarkAppearance, tintColor: buttonTintColor, isInteractive: true, transition: ComponentTransition(transition))
|
||||
self.buttonTitle.frame = CGRect(origin: CGPoint(x: floor((buttonFrame.width - buttonTitleSize.width) * 0.5), y: floor((buttonFrame.height - buttonTitleSize.height) * 0.5)), size: buttonTitleSize)
|
||||
self.buttonTintTitle.frame = self.buttonTitle.frame
|
||||
|
||||
let giftButtonFrame = CGRect(x: width - rightInset - 40.0 - 8.0, y: floor((panelHeight - 40.0) * 0.5), width: 40.0, height: 40.0)
|
||||
transition.updateFrame(view: self.giftButton, frame: giftButtonFrame)
|
||||
transition.updateFrame(view: self.giftButtonBackgroundView, frame: giftButtonFrame)
|
||||
if let image = self.giftButtonIconView.image {
|
||||
transition.updateFrame(view: self.giftButtonIconView, frame: image.size.centered(in: CGRect(origin: CGPoint(), size: giftButtonFrame.size)))
|
||||
}
|
||||
transition.updateFrame(view: self.giftButtonBackgroundView, frame: CGRect(origin: CGPoint(), size: giftButtonFrame.size))
|
||||
self.giftButtonBackgroundView.update(size: giftButtonFrame.size, cornerRadius: giftButtonFrame.height * 0.5, isDark: interfaceState.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: interfaceState.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)), transition: ComponentTransition(transition))
|
||||
transition.updateFrame(view: self.giftButton, frame: CGRect(origin: CGPoint(), size: giftButtonFrame.size))
|
||||
self.giftButtonBackgroundView.update(size: giftButtonFrame.size, cornerRadius: giftButtonFrame.height * 0.5, isDark: interfaceState.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: interfaceState.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)), isInteractive: true, transition: ComponentTransition(transition))
|
||||
|
||||
let helpButtonFrame = CGRect(x: width - rightInset - 8.0 - 40.0, y: floor((panelHeight - 40.0) * 0.5), width: 40.0, height: 40.0)
|
||||
transition.updateFrame(view: self.helpButton, frame: helpButtonFrame)
|
||||
transition.updateFrame(view: self.helpButtonBackgroundView, frame: helpButtonFrame)
|
||||
if let image = self.helpButtonIconView.image {
|
||||
transition.updateFrame(view: self.helpButtonIconView, frame: image.size.centered(in: CGRect(origin: CGPoint(), size: helpButtonFrame.size)))
|
||||
}
|
||||
transition.updateFrame(view: self.helpButtonBackgroundView, frame: CGRect(origin: CGPoint(), size: helpButtonFrame.size))
|
||||
self.helpButtonBackgroundView.update(size: helpButtonFrame.size, cornerRadius: helpButtonFrame.height * 0.5, isDark: interfaceState.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: interfaceState.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)), transition: ComponentTransition(transition))
|
||||
transition.updateFrame(view: self.helpButton, frame: CGRect(origin: CGPoint(), size: helpButtonFrame.size))
|
||||
self.helpButtonBackgroundView.update(size: helpButtonFrame.size, cornerRadius: helpButtonFrame.height * 0.5, isDark: interfaceState.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: interfaceState.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)), isInteractive: true, transition: ComponentTransition(transition))
|
||||
|
||||
let suggestedPostButtonFrame = CGRect(x: leftInset + 8.0, y: floor((panelHeight - 40.0) * 0.5), width: 40.0, height: 40.0)
|
||||
transition.updateFrame(view: self.suggestedPostButton, frame: suggestedPostButtonFrame)
|
||||
transition.updateFrame(view: self.suggestedPostButtonBackgroundView, frame: suggestedPostButtonFrame)
|
||||
if let image = self.suggestedPostButtonIconView.image {
|
||||
transition.updateFrame(view: self.suggestedPostButtonIconView, frame: image.size.centered(in: CGRect(origin: CGPoint(), size: suggestedPostButtonFrame.size)))
|
||||
}
|
||||
transition.updateFrame(view: self.suggestedPostButtonBackgroundView, frame: CGRect(origin: CGPoint(), size: suggestedPostButtonFrame.size))
|
||||
self.suggestedPostButtonBackgroundView.update(size: suggestedPostButtonFrame.size, cornerRadius: suggestedPostButtonFrame.height * 0.5, isDark: interfaceState.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: interfaceState.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)), transition: ComponentTransition(transition))
|
||||
transition.updateFrame(view: self.suggestedPostButton, frame: CGRect(origin: CGPoint(), size: suggestedPostButtonFrame.size))
|
||||
self.suggestedPostButtonBackgroundView.update(size: suggestedPostButtonFrame.size, cornerRadius: suggestedPostButtonFrame.height * 0.5, isDark: interfaceState.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: interfaceState.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)), isInteractive: true, transition: ComponentTransition(transition))
|
||||
|
||||
return panelHeight
|
||||
}
|
||||
|
||||
@ -112,6 +112,7 @@ public final class ChatTextInputPanelComponent: Component {
|
||||
let hideKeyboard: Bool
|
||||
let insets: UIEdgeInsets
|
||||
let maxHeight: CGFloat
|
||||
let maxLength: Int?
|
||||
let sendAction: (() -> Void)?
|
||||
let sendContextAction: ((UIView, ContextGesture) -> Void)?
|
||||
|
||||
@ -130,6 +131,7 @@ public final class ChatTextInputPanelComponent: Component {
|
||||
hideKeyboard: Bool,
|
||||
insets: UIEdgeInsets,
|
||||
maxHeight: CGFloat,
|
||||
maxLength: Int?,
|
||||
sendAction: (() -> Void)?,
|
||||
sendContextAction: ((UIView, ContextGesture) -> Void)?
|
||||
) {
|
||||
@ -147,6 +149,7 @@ public final class ChatTextInputPanelComponent: Component {
|
||||
self.hideKeyboard = hideKeyboard
|
||||
self.insets = insets
|
||||
self.maxHeight = maxHeight
|
||||
self.maxLength = maxLength
|
||||
self.sendAction = sendAction
|
||||
self.sendContextAction = sendContextAction
|
||||
}
|
||||
@ -194,6 +197,9 @@ public final class ChatTextInputPanelComponent: Component {
|
||||
if lhs.maxHeight != rhs.maxHeight {
|
||||
return false
|
||||
}
|
||||
if lhs.maxLength != rhs.maxLength {
|
||||
return false
|
||||
}
|
||||
if (lhs.sendAction == nil) != (rhs.sendAction == nil) {
|
||||
return false
|
||||
}
|
||||
@ -234,6 +240,13 @@ public final class ChatTextInputPanelComponent: Component {
|
||||
textView.deleteBackward()
|
||||
}
|
||||
|
||||
public func activateInput() {
|
||||
guard let panelNode = self.panelNode else {
|
||||
return
|
||||
}
|
||||
panelNode.ensureFocused()
|
||||
}
|
||||
|
||||
public func updateState(transition: ComponentTransition) {
|
||||
self.state?.updated(transition: transition)
|
||||
}
|
||||
@ -776,6 +789,7 @@ public final class ChatTextInputPanelComponent: Component {
|
||||
}
|
||||
|
||||
panelNode.customSendColor = component.sendColor
|
||||
panelNode.customInputTextMaxLength = component.maxLength
|
||||
|
||||
if let resetInputState = component.externalState.resetInputState {
|
||||
component.externalState.resetInputState = nil
|
||||
|
||||
@ -387,6 +387,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
|
||||
public var customLeftAction: LeftAction?
|
||||
public var customRightAction: RightAction?
|
||||
public var customSendColor: UIColor?
|
||||
public var customInputTextMaxLength: Int?
|
||||
|
||||
private var starReactionButton: ComponentView<Empty>?
|
||||
|
||||
@ -1783,20 +1784,6 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
|
||||
peerUpdated = true
|
||||
}
|
||||
|
||||
if let customLeftAction = self.customLeftAction {
|
||||
switch customLeftAction {
|
||||
case let .toggleExpanded(_, isExpanded):
|
||||
var iconTransform = CATransform3DIdentity
|
||||
iconTransform = CATransform3DTranslate(iconTransform, 0.0, 1.0, 0.0)
|
||||
if isExpanded || "".isEmpty {
|
||||
iconTransform = CATransform3DRotate(iconTransform, CGFloat.pi, 0.0, 0.0, 1.0)
|
||||
}
|
||||
transition.updateTransform(layer: self.attachmentButtonIcon.layer, transform: iconTransform)
|
||||
}
|
||||
} else {
|
||||
self.attachmentButtonIcon.layer.transform = CATransform3DIdentity
|
||||
}
|
||||
|
||||
if peerUpdated || previousState?.chatLocation != interfaceState.chatLocation || previousState?.interfaceState.silentPosting != interfaceState.interfaceState.silentPosting || themeUpdated || !self.initializedPlaceholder || previousState?.keyboardButtonsMessage?.id != interfaceState.keyboardButtonsMessage?.id || previousState?.keyboardButtonsMessage?.visibleReplyMarkupPlaceholder != interfaceState.keyboardButtonsMessage?.visibleReplyMarkupPlaceholder || dismissedButtonMessageUpdated || replyMessageUpdated || (previousState?.interfaceState.editMessage == nil) != (interfaceState.interfaceState.editMessage == nil) || previousState?.forumTopicData != interfaceState.forumTopicData || previousState?.replyMessage?.id != interfaceState.replyMessage?.id || previousState?.sendPaidMessageStars != interfaceState.sendPaidMessageStars {
|
||||
self.initializedPlaceholder = true
|
||||
|
||||
@ -1923,6 +1910,20 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
|
||||
}
|
||||
}
|
||||
|
||||
if let customLeftAction = self.customLeftAction {
|
||||
switch customLeftAction {
|
||||
case let .toggleExpanded(_, isExpanded):
|
||||
var iconTransform = CATransform3DIdentity
|
||||
iconTransform = CATransform3DTranslate(iconTransform, 0.0, 1.0, 0.0)
|
||||
if isExpanded {
|
||||
iconTransform = CATransform3DRotate(iconTransform, CGFloat.pi, 0.0, 0.0, 1.0)
|
||||
}
|
||||
transition.updateTransform(layer: self.attachmentButtonIcon.layer, transform: iconTransform)
|
||||
}
|
||||
} else {
|
||||
self.attachmentButtonIcon.layer.transform = CATransform3DIdentity
|
||||
}
|
||||
|
||||
var textFieldMinHeight: CGFloat = 33.0
|
||||
if let presentationInterfaceState = self.presentationInterfaceState {
|
||||
textFieldMinHeight = calclulateTextFieldMinHeight(presentationInterfaceState, metrics: metrics)
|
||||
@ -2868,7 +2869,10 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
|
||||
transition.updateFrame(node: self.attachmentButtonDisabledNode, frame: self.attachmentButtonBackground.frame)
|
||||
|
||||
if let image = self.attachmentButtonIcon.image {
|
||||
transition.updateFrame(view: self.attachmentButtonIcon, frame: CGRect(origin: CGPoint(x: floor((attachmentButtonFrame.width - image.size.width) * 0.5), y: floor((attachmentButtonFrame.height - image.size.height) * 0.5)), size: image.size))
|
||||
let attachmentButtonIconFrame = CGRect(origin: CGPoint(x: floor((attachmentButtonFrame.width - image.size.width) * 0.5), y: floor((attachmentButtonFrame.height - image.size.height) * 0.5)), size: image.size)
|
||||
let transition = ComponentTransition(transition)
|
||||
transition.setPosition(view: self.attachmentButtonIcon, position: attachmentButtonIconFrame.center)
|
||||
transition.setBounds(view: self.attachmentButtonIcon, bounds: CGRect(origin: CGPoint(), size: attachmentButtonIconFrame.size))
|
||||
}
|
||||
|
||||
if let context = self.context, let interfaceState = self.presentationInterfaceState, let editMessageState = interfaceState.editMessageState, let updatedMediaReference = editMessageState.mediaReference {
|
||||
@ -3577,7 +3581,9 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
|
||||
|
||||
private func updateCounterTextNode(transition: ContainedViewLayoutTransition) {
|
||||
var inputTextMaxLength: Int32?
|
||||
if let presentationInterfaceState = self.presentationInterfaceState {
|
||||
if let customInputTextMaxLength = self.customInputTextMaxLength {
|
||||
inputTextMaxLength = Int32(customInputTextMaxLength)
|
||||
} else if let presentationInterfaceState = self.presentationInterfaceState {
|
||||
if let editMessage = presentationInterfaceState.interfaceState.editMessage, let inputTextMaxLengthValue = editMessage.inputTextMaxLength {
|
||||
inputTextMaxLength = inputTextMaxLengthValue
|
||||
} else if case let .customChatContents(customChatContents) = presentationInterfaceState.subject, case .businessLinkSetup = customChatContents.kind {
|
||||
|
||||
@ -141,7 +141,7 @@ private final class ChatScheduleTimeSheetContentComponent: Component {
|
||||
size: barButtonSize,
|
||||
backgroundColor: environment.theme.rootController.navigationBar.glassBarButtonBackgroundColor,
|
||||
isDark: environment.theme.overallDarkAppearance,
|
||||
state: .glass,
|
||||
state: .generic,
|
||||
component: AnyComponentWithIdentity(id: "close", component: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Navigation/Close",
|
||||
|
||||
@ -112,7 +112,7 @@ private final class SheetContent: CombinedComponent {
|
||||
size: CGSize(width: 40.0, height: 40.0),
|
||||
backgroundColor: theme.rootController.navigationBar.glassBarButtonBackgroundColor,
|
||||
isDark: theme.overallDarkAppearance,
|
||||
state: .tintedGlass,
|
||||
state: .generic,
|
||||
component: AnyComponentWithIdentity(id: "close", component: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Navigation/Close",
|
||||
|
||||
@ -492,6 +492,7 @@ public final class GlassBackgroundContainerView: UIView {
|
||||
}
|
||||
|
||||
private let legacyView: ContentView?
|
||||
private let nativeParamsView: EffectSettingsContainerView?
|
||||
private let nativeView: UIVisualEffectView?
|
||||
|
||||
public var contentView: UIView {
|
||||
@ -506,17 +507,24 @@ public final class GlassBackgroundContainerView: UIView {
|
||||
if #available(iOS 26.0, *) {
|
||||
let effect = UIGlassContainerEffect()
|
||||
effect.spacing = 7.0
|
||||
self.nativeView = UIVisualEffectView(effect: effect)
|
||||
let nativeView = UIVisualEffectView(effect: effect)
|
||||
self.nativeView = nativeView
|
||||
|
||||
let nativeParamsView = EffectSettingsContainerView(frame: CGRect())
|
||||
self.nativeParamsView = nativeParamsView
|
||||
nativeParamsView.addSubview(nativeView)
|
||||
|
||||
self.legacyView = nil
|
||||
} else {
|
||||
self.nativeView = nil
|
||||
self.nativeParamsView = nil
|
||||
self.legacyView = ContentView()
|
||||
}
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
if let nativeView = self.nativeView {
|
||||
self.addSubview(nativeView)
|
||||
if let nativeParamsView = self.nativeParamsView {
|
||||
self.addSubview(nativeParamsView)
|
||||
} else if let legacyView = self.legacyView {
|
||||
self.addSubview(legacyView)
|
||||
}
|
||||
@ -529,7 +537,7 @@ public final class GlassBackgroundContainerView: UIView {
|
||||
override public func didAddSubview(_ subview: UIView) {
|
||||
super.didAddSubview(subview)
|
||||
|
||||
if subview !== self.nativeView && subview !== self.legacyView {
|
||||
if subview !== self.nativeParamsView && subview !== self.legacyView {
|
||||
assertionFailure()
|
||||
}
|
||||
}
|
||||
@ -538,16 +546,21 @@ public final class GlassBackgroundContainerView: UIView {
|
||||
guard let result = self.contentView.hitTest(point, with: event) else {
|
||||
return nil
|
||||
}
|
||||
if result === self.contentView {
|
||||
//return nil
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
public func update(size: CGSize, isDark: Bool, transition: ComponentTransition) {
|
||||
if let nativeView = self.nativeView {
|
||||
if let nativeParamsView = self.nativeParamsView, let nativeView = self.nativeView {
|
||||
nativeView.overrideUserInterfaceStyle = isDark ? .dark : .light
|
||||
|
||||
if isDark {
|
||||
nativeParamsView.lumaMin = 0.0
|
||||
nativeParamsView.lumaMax = 0.15
|
||||
} else {
|
||||
nativeParamsView.lumaMin = 0.25
|
||||
nativeParamsView.lumaMax = 1.0
|
||||
}
|
||||
|
||||
transition.setFrame(view: nativeView, frame: CGRect(origin: CGPoint(), size: size))
|
||||
} else if let legacyView = self.legacyView {
|
||||
transition.setFrame(view: legacyView, frame: CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
@ -694,6 +694,11 @@ public final class MessageInputPanelComponent: Component {
|
||||
}
|
||||
|
||||
public func activateInput() {
|
||||
if let inputPanelView = self.inputPanel?.view as? ChatTextInputPanelComponent.View {
|
||||
inputPanelView.activateInput()
|
||||
return
|
||||
}
|
||||
|
||||
if let textFieldView = self.textField.view as? TextFieldComponent.View {
|
||||
textFieldView.activateInput()
|
||||
}
|
||||
@ -947,6 +952,7 @@ public final class MessageInputPanelComponent: Component {
|
||||
hideKeyboard: component.hideKeyboard,
|
||||
insets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: component.bottomInset, right: 0.0),
|
||||
maxHeight: availableSize.height,
|
||||
maxLength: component.maxLength,
|
||||
sendAction: { [weak self] in
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
|
||||
@ -283,7 +283,7 @@ final class ThemeGridControllerNode: ASDisplayNode {
|
||||
self.descriptionItem = ItemListTextItem(presentationData: ItemListPresentationData(presentationData), text: .plain(descriptionText), sectionId: 0)
|
||||
|
||||
self.resetItemNode = ItemListActionItemNode()
|
||||
self.resetItem = ItemListActionItem(presentationData: ItemListPresentationData(presentationData), title: presentationData.strings.Wallpaper_ResetWallpapers, kind: .generic, alignment: .natural, sectionId: 0, style: .blocks, action: {
|
||||
self.resetItem = ItemListActionItem(presentationData: ItemListPresentationData(presentationData), systemStyle: .glass, title: presentationData.strings.Wallpaper_ResetWallpapers, kind: .generic, alignment: .natural, sectionId: 0, style: .blocks, action: {
|
||||
resetWallpapers()
|
||||
})
|
||||
self.resetDescriptionItemNode = ItemListTextItemNode()
|
||||
|
||||
@ -30,7 +30,7 @@ swift_library(
|
||||
"//submodules/UndoUI",
|
||||
"//submodules/TextFormat",
|
||||
"//submodules/Components/BundleIconComponent",
|
||||
"//submodules/Components/SolidRoundedButtonComponent",
|
||||
"//submodules/TelegramUI/Components/ButtonComponent",
|
||||
"//submodules/AvatarNode",
|
||||
"//submodules/TelegramUI/Components/Stars/StarsImageComponent",
|
||||
"//submodules/TelegramUI/Components/Stars/StarsAvatarComponent",
|
||||
@ -38,6 +38,7 @@ swift_library(
|
||||
"//submodules/TelegramUI/Components/MiniAppListScreen",
|
||||
"//submodules/TelegramUI/Components/Premium/PremiumStarComponent",
|
||||
"//submodules/TelegramUI/Components/Gifts/GiftAnimationComponent",
|
||||
"//submodules/TelegramUI/Components/GlassBarButtonComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
||||
@ -14,7 +14,7 @@ import SheetComponent
|
||||
import MultilineTextComponent
|
||||
import MultilineTextWithEntitiesComponent
|
||||
import BundleIconComponent
|
||||
import SolidRoundedButtonComponent
|
||||
import ButtonComponent
|
||||
import Markdown
|
||||
import BalancedTextComponent
|
||||
import AvatarNode
|
||||
@ -27,6 +27,7 @@ import StarsAvatarComponent
|
||||
import MiniAppListScreen
|
||||
import PremiumStarComponent
|
||||
import GiftAnimationComponent
|
||||
import GlassBarButtonComponent
|
||||
|
||||
private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
@ -86,8 +87,6 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
|
||||
var peerMap: [EnginePeer.Id: EnginePeer] = [:]
|
||||
|
||||
var cachedCloseImage: (UIImage, PresentationTheme)?
|
||||
var cachedOverlayCloseImage: UIImage?
|
||||
var cachedChevronImage: (UIImage, PresentationTheme)?
|
||||
|
||||
var inProgress = false
|
||||
@ -153,7 +152,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
}
|
||||
|
||||
static var body: Body {
|
||||
let closeButton = Child(Button.self)
|
||||
let closeButton = Child(GlassBarButtonComponent.self)
|
||||
let title = Child(MultilineTextComponent.self)
|
||||
let star = Child(StarsImageComponent.self)
|
||||
let activeStar = Child(PremiumStarComponent.self)
|
||||
@ -165,8 +164,8 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
let table = Child(TableComponent.self)
|
||||
let additional = Child(BalancedTextComponent.self)
|
||||
let status = Child(BalancedTextComponent.self)
|
||||
let cancelButton = Child(SolidRoundedButtonComponent.self)
|
||||
let button = Child(SolidRoundedButtonComponent.self)
|
||||
let cancelButton = Child(ButtonComponent.self)
|
||||
let button = Child(ButtonComponent.self)
|
||||
|
||||
let transactionStatusBackgound = Child(RoundedRectangle.self)
|
||||
let transactionStatusText = Child(MultilineTextComponent.self)
|
||||
@ -190,22 +189,6 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
let sideInset: CGFloat = 16.0 + environment.safeInsets.left
|
||||
let textSideInset: CGFloat = 32.0 + environment.safeInsets.left
|
||||
|
||||
let closeImage: UIImage
|
||||
if let (image, theme) = state.cachedCloseImage, theme === environment.theme {
|
||||
closeImage = image
|
||||
} else {
|
||||
closeImage = generateCloseButtonImage(backgroundColor: UIColor(rgb: 0x808084, alpha: 0.1), foregroundColor: theme.actionSheet.inputClearButtonColor)!
|
||||
state.cachedCloseImage = (closeImage, theme)
|
||||
}
|
||||
|
||||
let closeOverlayImage: UIImage
|
||||
if let image = state.cachedOverlayCloseImage {
|
||||
closeOverlayImage = image
|
||||
} else {
|
||||
closeOverlayImage = generateCloseButtonImage(backgroundColor: UIColor(rgb: 0xffffff, alpha: 0.1), foregroundColor: .white)!
|
||||
state.cachedOverlayCloseImage = closeOverlayImage
|
||||
}
|
||||
|
||||
let titleText: String
|
||||
let amountText: String
|
||||
var descriptionText: String
|
||||
@ -655,15 +638,20 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
descriptionText = modifiedString
|
||||
}
|
||||
|
||||
var closeButtonImage = closeImage
|
||||
if case .unique = giftAnimationSubject {
|
||||
closeButtonImage = closeOverlayImage
|
||||
}
|
||||
let closeButton = closeButton.update(
|
||||
component: Button(
|
||||
content: AnyComponent(Image(image: closeButtonImage)),
|
||||
action: { [weak component] in
|
||||
component?.cancel(true)
|
||||
component: GlassBarButtonComponent(
|
||||
size: CGSize(width: 40.0, height: 40.0),
|
||||
backgroundColor: theme.rootController.navigationBar.glassBarButtonBackgroundColor,
|
||||
isDark: theme.overallDarkAppearance,
|
||||
state: .generic,
|
||||
component: AnyComponentWithIdentity(id: "close", component: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Navigation/Close",
|
||||
tintColor: theme.rootController.navigationBar.glassBarButtonForegroundColor
|
||||
)
|
||||
)),
|
||||
action: { _ in
|
||||
component.cancel(true)
|
||||
}
|
||||
),
|
||||
availableSize: CGSize(width: 30.0, height: 30.0),
|
||||
@ -1615,18 +1603,39 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
|
||||
if let cancelButtonText {
|
||||
let cancelButton = cancelButton.update(
|
||||
component: SolidRoundedButtonComponent(
|
||||
title: cancelButtonText,
|
||||
theme: SolidRoundedButtonComponent.Theme(backgroundColor: .clear, foregroundColor: linkColor),
|
||||
font: .regular,
|
||||
fontSize: 17.0,
|
||||
height: 50.0,
|
||||
// component: SolidRoundedButtonComponent(
|
||||
// title: cancelButtonText,
|
||||
// theme: SolidRoundedButtonComponent.Theme(backgroundColor: .clear, foregroundColor: linkColor),
|
||||
// font: .regular,
|
||||
// fontSize: 17.0,
|
||||
// height: 50.0,
|
||||
// cornerRadius: 10.0,
|
||||
// gloss: false,
|
||||
// iconName: nil,
|
||||
// animationName: nil,
|
||||
// iconPosition: .left,
|
||||
// isLoading: state.inProgress,
|
||||
// action: {
|
||||
// component.cancel(true)
|
||||
// if isSubscription {
|
||||
// component.updateSubscription()
|
||||
// }
|
||||
// }
|
||||
// ),
|
||||
component: ButtonComponent(
|
||||
background: ButtonComponent.Background(
|
||||
style: .glass,
|
||||
color: theme.list.itemCheckColors.fillColor,
|
||||
foreground: theme.list.itemCheckColors.foregroundColor,
|
||||
pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9),
|
||||
cornerRadius: 10.0,
|
||||
gloss: false,
|
||||
iconName: nil,
|
||||
animationName: nil,
|
||||
iconPosition: .left,
|
||||
isLoading: state.inProgress,
|
||||
),
|
||||
content: AnyComponentWithIdentity(
|
||||
id: AnyHashable(0),
|
||||
component: AnyComponent(MultilineTextComponent(text: .plain(NSMutableAttributedString(string: cancelButtonText, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center))))
|
||||
),
|
||||
isEnabled: true,
|
||||
displaysProgress: state.inProgress,
|
||||
action: {
|
||||
component.cancel(true)
|
||||
if isSubscription {
|
||||
@ -1634,13 +1643,12 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
}
|
||||
}
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0),
|
||||
availableSize: CGSize(width: context.availableSize.width - 30.0 * 2.0, height: 52.0),
|
||||
transition: context.transition
|
||||
)
|
||||
|
||||
let cancelButtonFrame = CGRect(origin: CGPoint(x: sideInset, y: originY), size: cancelButton.size)
|
||||
context.add(cancelButton
|
||||
.position(CGPoint(x: cancelButtonFrame.midX, y: cancelButtonFrame.midY))
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: originY + cancelButton.size.height / 2.0))
|
||||
)
|
||||
originY += cancelButton.size.height
|
||||
originY += 8.0
|
||||
@ -1648,18 +1656,20 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
|
||||
if let buttonText {
|
||||
let button = button.update(
|
||||
component: SolidRoundedButtonComponent(
|
||||
title: buttonText,
|
||||
theme: SolidRoundedButtonComponent.Theme(theme: theme),
|
||||
font: .bold,
|
||||
fontSize: 17.0,
|
||||
height: 50.0,
|
||||
component: ButtonComponent(
|
||||
background: ButtonComponent.Background(
|
||||
style: .glass,
|
||||
color: theme.list.itemCheckColors.fillColor,
|
||||
foreground: theme.list.itemCheckColors.foregroundColor,
|
||||
pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9),
|
||||
cornerRadius: 10.0,
|
||||
gloss: false,
|
||||
iconName: nil,
|
||||
animationName: nil,
|
||||
iconPosition: .left,
|
||||
isLoading: state.inProgress,
|
||||
),
|
||||
content: AnyComponentWithIdentity(
|
||||
id: AnyHashable(0),
|
||||
component: AnyComponent(MultilineTextComponent(text: .plain(NSMutableAttributedString(string: buttonText, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center))))
|
||||
),
|
||||
isEnabled: true,
|
||||
displaysProgress: state.inProgress,
|
||||
action: {
|
||||
component.cancel(true)
|
||||
if isSubscription && cancelButtonText == nil {
|
||||
@ -1667,20 +1677,19 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
}
|
||||
}
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0),
|
||||
availableSize: CGSize(width: context.availableSize.width - 30.0 * 2.0, height: 52.0),
|
||||
transition: context.transition
|
||||
)
|
||||
|
||||
let buttonFrame = CGRect(origin: CGPoint(x: sideInset, y: originY), size: button.size)
|
||||
context.add(button
|
||||
.position(CGPoint(x: buttonFrame.midX, y: buttonFrame.midY))
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: originY + button.size.height / 2.0))
|
||||
)
|
||||
originY += button.size.height
|
||||
originY += 7.0
|
||||
}
|
||||
|
||||
context.add(closeButton
|
||||
.position(CGPoint(x: context.availableSize.width - environment.safeInsets.left - closeButton.size.width, y: 28.0))
|
||||
.position(CGPoint(x: 16.0 + closeButton.size.width / 2.0, y: 16.0 + closeButton.size.height / 2.0))
|
||||
)
|
||||
|
||||
let effectiveBottomInset: CGFloat = environment.metrics.isTablet ? 0.0 : environment.safeInsets.bottom
|
||||
@ -1773,6 +1782,7 @@ private final class StarsTransactionSheetComponent: CombinedComponent {
|
||||
updateSubscription: context.component.updateSubscription,
|
||||
sendGift: context.component.sendGift
|
||||
)),
|
||||
style: .glass,
|
||||
backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor),
|
||||
followContentSizeChanges: true,
|
||||
clipsContent: true,
|
||||
|
||||
@ -19,7 +19,7 @@ swift_library(
|
||||
"//submodules/Components/ViewControllerComponent:ViewControllerComponent",
|
||||
"//submodules/Components/ComponentDisplayAdapters:ComponentDisplayAdapters",
|
||||
"//submodules/Components/MultilineTextComponent:MultilineTextComponent",
|
||||
"//submodules/Components/SolidRoundedButtonComponent",
|
||||
"//submodules/TelegramUI/Components/ButtonComponent",
|
||||
"//submodules/TelegramPresentationData:TelegramPresentationData",
|
||||
"//submodules/AccountContext:AccountContext",
|
||||
"//submodules/AppBundle:AppBundle",
|
||||
|
||||
@ -51,12 +51,50 @@ final class SegmentControlComponent: Component {
|
||||
return true
|
||||
}
|
||||
|
||||
class SegmentedControlView: UISegmentedControl {
|
||||
var foregroundColor: UIColor? {
|
||||
didSet {
|
||||
self.resetChrome()
|
||||
}
|
||||
}
|
||||
|
||||
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
super.touchesBegan(touches, with: event)
|
||||
self.resetChrome()
|
||||
}
|
||||
|
||||
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
super.touchesEnded(touches, with: event)
|
||||
self.resetChrome()
|
||||
}
|
||||
|
||||
func resetChrome() {
|
||||
for case let view as UIImageView in self.subviews {
|
||||
view.isHidden = true
|
||||
}
|
||||
if let selectorView = self.subviews.last, let loupe = selectorView.subviews.first, let innerLoupe = loupe.subviews.first {
|
||||
for view in innerLoupe.subviews {
|
||||
if type(of: view) == UIView.self {
|
||||
view.backgroundColor = self.foregroundColor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
self.resetChrome()
|
||||
}
|
||||
}
|
||||
|
||||
class View: UIView {
|
||||
private let title = ComponentView<Empty>()
|
||||
|
||||
private var component: SegmentControlComponent?
|
||||
|
||||
private var segmentedNode: SegmentedControlNode?
|
||||
private var nativeSegmentedView: SegmentedControlView?
|
||||
private var legacySegmentedNode: SegmentedControlNode?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
@ -71,32 +109,71 @@ final class SegmentControlComponent: Component {
|
||||
|
||||
self.component = component
|
||||
|
||||
var controlSize = CGSize()
|
||||
if #available(iOS 26.0, *) {
|
||||
let segmentedView: SegmentedControlView
|
||||
if let current = self.nativeSegmentedView {
|
||||
segmentedView = current
|
||||
} else {
|
||||
let mappedActions: [UIAction] = component.items.map { item -> UIAction in
|
||||
return UIAction(title: item.title, handler: { [weak self] _ in
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
component.action(item.id)
|
||||
})
|
||||
}
|
||||
segmentedView = SegmentedControlView(frame: .zero, actions: mappedActions)
|
||||
segmentedView.selectedSegmentIndex = component.items.firstIndex(where: { $0.id == component.selectedId }) ?? 0
|
||||
self.nativeSegmentedView = segmentedView
|
||||
self.addSubview(segmentedView)
|
||||
}
|
||||
|
||||
if themeUpdated {
|
||||
let backgroundColor = component.theme.overallDarkAppearance ? component.theme.list.itemBlocksBackgroundColor : component.theme.rootController.navigationBar.segmentedBackgroundColor
|
||||
segmentedView.setTitleTextAttributes([
|
||||
.font: UIFont.boldSystemFont(ofSize: 15.0),
|
||||
.foregroundColor: component.theme.rootController.navigationBar.segmentedTextColor
|
||||
], for: .normal)
|
||||
segmentedView.foregroundColor = component.theme.rootController.navigationBar.segmentedForegroundColor
|
||||
segmentedView.backgroundColor = backgroundColor
|
||||
}
|
||||
|
||||
controlSize = segmentedView.sizeThatFits(availableSize)
|
||||
controlSize.width = min(availableSize.width - 32.0, max(300.0, controlSize.width))
|
||||
controlSize.height = 36.0
|
||||
segmentedView.frame = CGRect(origin: .zero, size: controlSize)
|
||||
} else {
|
||||
let segmentedNode: SegmentedControlNode
|
||||
if let current = self.segmentedNode {
|
||||
if let current = self.legacySegmentedNode {
|
||||
segmentedNode = current
|
||||
|
||||
if themeUpdated {
|
||||
segmentedNode.updateTheme(SegmentedControlTheme(theme: component.theme))
|
||||
let backgroundColor = component.theme.overallDarkAppearance ? component.theme.list.itemBlocksBackgroundColor : component.theme.rootController.navigationBar.segmentedBackgroundColor
|
||||
let controlTheme = SegmentedControlTheme(backgroundColor: backgroundColor, foregroundColor: component.theme.rootController.navigationBar.segmentedForegroundColor, shadowColor: .clear, textColor: component.theme.rootController.navigationBar.segmentedTextColor, dividerColor: component.theme.rootController.navigationBar.segmentedDividerColor)
|
||||
segmentedNode.updateTheme(controlTheme)
|
||||
}
|
||||
} else {
|
||||
let mappedItems: [SegmentedControlItem] = component.items.map { item -> SegmentedControlItem in
|
||||
return SegmentedControlItem(title: item.title)
|
||||
}
|
||||
segmentedNode = SegmentedControlNode(theme: SegmentedControlTheme(theme: component.theme), items: mappedItems, selectedIndex: component.items.firstIndex(where: { $0.id == component.selectedId }) ?? 0)
|
||||
self.segmentedNode = segmentedNode
|
||||
let backgroundColor = component.theme.overallDarkAppearance ? component.theme.list.itemBlocksBackgroundColor : component.theme.rootController.navigationBar.segmentedBackgroundColor
|
||||
let controlTheme = SegmentedControlTheme(backgroundColor: backgroundColor, foregroundColor: component.theme.rootController.navigationBar.segmentedForegroundColor, shadowColor: .clear, textColor: component.theme.rootController.navigationBar.segmentedTextColor, dividerColor: component.theme.rootController.navigationBar.segmentedDividerColor)
|
||||
segmentedNode = SegmentedControlNode(theme: controlTheme, items: mappedItems, selectedIndex: component.items.firstIndex(where: { $0.id == component.selectedId }) ?? 0, cornerRadius: 18.0)
|
||||
self.legacySegmentedNode = segmentedNode
|
||||
self.addSubnode(segmentedNode)
|
||||
|
||||
segmentedNode.selectedIndexChanged = { [weak self] index in
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
self.component?.action(component.items[index].id)
|
||||
component.action(component.items[index].id)
|
||||
}
|
||||
}
|
||||
|
||||
let controlSize = segmentedNode.updateLayout(SegmentedControlLayout.sizeToFit(maximumWidth: availableSize.width, minimumWidth: min(availableSize.width, 296.0), height: 31.0), transition: transition.containedViewLayoutTransition)
|
||||
|
||||
controlSize = segmentedNode.updateLayout(SegmentedControlLayout.sizeToFit(maximumWidth: availableSize.width, minimumWidth: min(availableSize.width, 300.0), height: 36.0), transition: transition.containedViewLayoutTransition)
|
||||
transition.containedViewLayoutTransition.updateFrame(node: segmentedNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: controlSize))
|
||||
}
|
||||
|
||||
return controlSize
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@ import TelegramCore
|
||||
import MultilineTextComponent
|
||||
import EmojiStatusComponent
|
||||
import CheckNode
|
||||
import SolidRoundedButtonComponent
|
||||
import ButtonComponent
|
||||
|
||||
final class StorageCategoriesComponent: Component {
|
||||
struct CategoryData: Equatable {
|
||||
@ -214,35 +214,48 @@ final class StorageCategoriesComponent: Component {
|
||||
label = dataSizeString(totalSelectedSize, formatting: DataSizeStringFormatting(strings: component.strings, decimalSeparator: "."))
|
||||
}
|
||||
|
||||
var buttonContents: [AnyComponentWithIdentity<Empty>] = [
|
||||
AnyComponentWithIdentity(id: "title", component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSMutableAttributedString(string: clearTitle, font: Font.semibold(17.0), textColor: component.theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center)))
|
||||
))
|
||||
]
|
||||
if let label {
|
||||
buttonContents.append(
|
||||
AnyComponentWithIdentity(id: "label", component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSMutableAttributedString(string: label, font: Font.semibold(17.0), textColor: component.theme.list.itemCheckColors.foregroundColor.withMultipliedAlpha(0.6), paragraphAlignment: .center)))
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
contentHeight += 8.0
|
||||
let buttonSize = self.button.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(SolidRoundedButtonComponent(
|
||||
title: clearTitle,
|
||||
label: label,
|
||||
theme: SolidRoundedButtonComponent.Theme(
|
||||
backgroundColor: component.theme.list.itemCheckColors.fillColor,
|
||||
backgroundColors: [],
|
||||
foregroundColor: component.theme.list.itemCheckColors.foregroundColor
|
||||
component: AnyComponent(
|
||||
ButtonComponent(
|
||||
background: ButtonComponent.Background(
|
||||
style: .glass,
|
||||
color: component.theme.list.itemCheckColors.fillColor,
|
||||
foreground: component.theme.list.itemCheckColors.foregroundColor,
|
||||
pressedColor: component.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9)
|
||||
),
|
||||
content: AnyComponentWithIdentity(
|
||||
id: AnyHashable(0),
|
||||
component: AnyComponent(
|
||||
HStack(buttonContents, spacing: 4.0)
|
||||
)
|
||||
),
|
||||
font: .bold,
|
||||
fontSize: 17.0,
|
||||
height: 52.0,
|
||||
cornerRadius: 26.0,
|
||||
gloss: false,
|
||||
isEnabled: totalSelectedSize != 0,
|
||||
animationName: nil,
|
||||
iconPosition: .right,
|
||||
iconSpacing: 4.0,
|
||||
displaysProgress: false,
|
||||
action: { [weak self] in
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
component.clearAction()
|
||||
}
|
||||
)),
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - 16.0 * 2.0, height: 50.0)
|
||||
containerSize: CGSize(width: availableSize.width - 16.0 * 2.0, height: 52.0)
|
||||
)
|
||||
let buttonFrame = CGRect(origin: CGPoint(x: 16.0, y: contentHeight), size: buttonSize)
|
||||
if let buttonView = self.button.view {
|
||||
|
||||
@ -117,7 +117,7 @@ private final class MessageItemComponent: Component {
|
||||
|
||||
self.containerNode.isGestureEnabled = component.contextGesture != nil
|
||||
|
||||
let insets = UIEdgeInsets(top: 8.0, left: 8.0, bottom: 8.0, right: 8.0)
|
||||
let insets = UIEdgeInsets(top: 9.0, left: 20.0, bottom: 9.0, right: 20.0)
|
||||
let avatarSize: CGFloat = 24.0
|
||||
let avatarSpacing: CGFloat = 6.0
|
||||
|
||||
@ -175,7 +175,8 @@ private final class MessageItemComponent: Component {
|
||||
textView.bounds = CGRect(origin: CGPoint(), size: textFrame.size)
|
||||
}
|
||||
|
||||
let backgroundFrame = CGRect(origin: CGPoint(x: 6.0, y: 2.0), size: CGSize(width: textFrame.maxX + 8.0 - 6.0, height: textFrame.maxY + 3.0))
|
||||
let backgroundOrigin = CGPoint(x: avatarFrame.minX - 2.0, y: avatarFrame.minY - 2.0)
|
||||
let backgroundFrame = CGRect(origin: backgroundOrigin, size: CGSize(width: textFrame.maxX + 8.0 - backgroundOrigin.x, height: max(avatarFrame.maxY + 2.0, textFrame.maxY + 5.0) - backgroundOrigin.y))
|
||||
|
||||
if let paidStars = component.message.paidStars {
|
||||
let backgroundView: UIImageView
|
||||
@ -185,7 +186,7 @@ private final class MessageItemComponent: Component {
|
||||
backgroundView = UIImageView()
|
||||
self.backgroundView = backgroundView
|
||||
self.extractedContainerNode.contentNode.view.insertSubview(backgroundView, at: 0)
|
||||
backgroundView.image = generateStretchableFilledCircleImage(diameter: 28.0, color: .white)?.withRenderingMode(.alwaysTemplate)
|
||||
backgroundView.image = generateStretchableFilledCircleImage(diameter: avatarSize + 2.0 * 2.0, color: .white)?.withRenderingMode(.alwaysTemplate)
|
||||
}
|
||||
transition.setFrame(view: backgroundView, frame: backgroundFrame)
|
||||
backgroundView.tintColor = getStarAmountColorMapping(value: paidStars)
|
||||
@ -219,7 +220,7 @@ private final class MessageItemComponent: Component {
|
||||
|
||||
self.extractedContainerNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
self.extractedContainerNode.contentNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
self.extractedContainerNode.contentRect = backgroundFrame
|
||||
self.extractedContainerNode.contentRect = backgroundFrame.insetBy(dx: -4.0, dy: 0.0)
|
||||
self.containerNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
|
||||
return size
|
||||
@ -548,7 +549,7 @@ private final class PinnedBarComponent: Component {
|
||||
|
||||
let itemHeight: CGFloat = 32.0
|
||||
|
||||
let insets = UIEdgeInsets(top: 16.0, left: 16.0, bottom: 16.0, right: 16.0)
|
||||
let insets = UIEdgeInsets(top: 13.0, left: 20.0, bottom: 13.0, right: 20.0)
|
||||
|
||||
let size = CGSize(width: availableSize.width, height: insets.top + itemHeight + insets.bottom)
|
||||
|
||||
@ -564,7 +565,7 @@ private final class PinnedBarComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
let listInsets = UIEdgeInsets(top: 0.0, left: 8.0, bottom: 0.0, right: 8.0)
|
||||
let listInsets = UIEdgeInsets(top: 0.0, left: insets.left, bottom: 0.0, right: insets.right)
|
||||
let listFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height))
|
||||
let _ = self.list.update(
|
||||
transition: transition,
|
||||
@ -665,6 +666,8 @@ final class StoryContentLiveChatComponent: Component {
|
||||
private var messagesState: GroupCallMessagesContext.State?
|
||||
private var stateDisposable: Disposable?
|
||||
|
||||
private var currentListIsEmpty: Bool = true
|
||||
|
||||
public var isChatEmpty: Bool {
|
||||
guard let messagesState = self.messagesState else {
|
||||
return true
|
||||
@ -761,7 +764,7 @@ final class StoryContentLiveChatComponent: Component {
|
||||
self.state?.updated(transition: .spring(duration: 0.4))
|
||||
}
|
||||
|
||||
private func openMessageContextMenu(id: Int64, gesture: ContextGesture, sourceNode: ContextExtractedContentContainingNode) {
|
||||
private func openMessageContextMenu(id: GroupCallMessagesContext.Message.Id, gesture: ContextGesture, sourceNode: ContextExtractedContentContainingNode) {
|
||||
Task { @MainActor [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
@ -845,6 +848,8 @@ final class StoryContentLiveChatComponent: Component {
|
||||
self.isUpdating = false
|
||||
}
|
||||
|
||||
let alphaTransition: ComponentTransition = transition.animation.isImmediate ? .immediate : .easeInOut(duration: 0.2)
|
||||
|
||||
if self.component?.call !== component.call {
|
||||
self.stateDisposable?.dispose()
|
||||
if let call = component.call as? PresentationGroupCallImpl {
|
||||
@ -868,6 +873,8 @@ final class StoryContentLiveChatComponent: Component {
|
||||
self.component = component
|
||||
self.state = state
|
||||
|
||||
let previousListIsEmpty = self.currentListIsEmpty
|
||||
|
||||
var listItems: [AnyComponentWithIdentity<Empty>] = []
|
||||
var topMessageByPeerId: [EnginePeer.Id: GroupCallMessagesContext.Message] = [:]
|
||||
if let messagesState = self.messagesState {
|
||||
@ -908,6 +915,8 @@ final class StoryContentLiveChatComponent: Component {
|
||||
return lhs.date > rhs.date
|
||||
})
|
||||
|
||||
self.currentListIsEmpty = listItems.isEmpty
|
||||
|
||||
let pinnedBarSize = self.pinnedBar.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(PinnedBarComponent(
|
||||
@ -938,13 +947,19 @@ final class StoryContentLiveChatComponent: Component {
|
||||
transition.setAlpha(view: pinnedBarView, alpha: topMessages.isEmpty ? 0.0 : 1.0)
|
||||
}
|
||||
|
||||
var listInsets = UIEdgeInsets(top: component.insets.bottom + 16.0, left: component.insets.right, bottom: component.insets.top + 8.0, right: component.insets.left)
|
||||
var listInsets = UIEdgeInsets(top: component.insets.bottom + 8.0, left: component.insets.right, bottom: component.insets.top + 8.0, right: component.insets.left)
|
||||
if !topMessages.isEmpty {
|
||||
listInsets.top = availableSize.height - pinnedBarFrame.minY
|
||||
}
|
||||
listInsets.top += 4.0
|
||||
listInsets.top += 1.0
|
||||
|
||||
var listTransition = transition
|
||||
if previousListIsEmpty != self.currentListIsEmpty {
|
||||
listTransition = listTransition.withAnimation(.none)
|
||||
}
|
||||
|
||||
let _ = self.list.update(
|
||||
transition: transition,
|
||||
transition: listTransition,
|
||||
component: AnyComponent(AsyncListComponent(
|
||||
externalState: self.listState,
|
||||
items: listItems,
|
||||
@ -963,6 +978,7 @@ final class StoryContentLiveChatComponent: Component {
|
||||
}
|
||||
transition.setPosition(view: listView, position: listFrame.offsetBy(dx: 0.0, dy: self.isChatExpanded ? 0.0 : listFrame.height).center)
|
||||
transition.setBounds(view: listView, bounds: CGRect(origin: CGPoint(), size: listFrame.size))
|
||||
alphaTransition.setAlpha(view: listView, alpha: listItems.isEmpty ? 0.0 : 1.0)
|
||||
}
|
||||
|
||||
transition.setFrame(view: self.listContainer, frame: CGRect(origin: CGPoint(), size: availableSize))
|
||||
|
||||
@ -110,6 +110,7 @@ final class StoryItemContentComponent: Component {
|
||||
private var component: StoryItemContentComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
private var environment: StoryContentItem.Environment?
|
||||
private var isUpdating: Bool = false
|
||||
|
||||
private var unsupportedText: ComponentView<Empty>?
|
||||
private var unsupportedButton: ComponentView<Empty>?
|
||||
@ -180,7 +181,7 @@ final class StoryItemContentComponent: Component {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.state?.updated(transition: .immediate)
|
||||
self.state?.updated(transition: .immediate, isLocal: true)
|
||||
}
|
||||
}
|
||||
|
||||
@ -298,8 +299,8 @@ final class StoryItemContentComponent: Component {
|
||||
}
|
||||
}
|
||||
videoNode.canAttachContent = true
|
||||
if update {
|
||||
self.state?.updated(transition: .immediate)
|
||||
if update && !self.isUpdating {
|
||||
self.state?.updated(transition: .immediate, isLocal: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -546,7 +547,7 @@ final class StoryItemContentComponent: Component {
|
||||
|
||||
if !self.contentLoaded {
|
||||
self.contentLoaded = true
|
||||
self.state?.updated(transition: .immediate)
|
||||
self.state?.updated(transition: .immediate, isLocal: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -612,6 +613,11 @@ final class StoryItemContentComponent: Component {
|
||||
}
|
||||
|
||||
func update(component: StoryItemContentComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<StoryContentItem.Environment>, transition: ComponentTransition) -> CGSize {
|
||||
self.isUpdating = true
|
||||
defer {
|
||||
self.isUpdating = false
|
||||
}
|
||||
|
||||
let previousItem = self.component?.item
|
||||
|
||||
self.component = component
|
||||
@ -735,7 +741,7 @@ final class StoryItemContentComponent: Component {
|
||||
}
|
||||
if !self.contentLoaded {
|
||||
self.contentLoaded = true
|
||||
self.state?.updated(transition: .immediate)
|
||||
self.state?.updated(transition: .immediate, isLocal: true)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -900,7 +906,7 @@ final class StoryItemContentComponent: Component {
|
||||
}
|
||||
self.contentLoaded = true
|
||||
if applyState {
|
||||
self.state?.updated(transition: .immediate)
|
||||
self.state?.updated(transition: .immediate, isLocal: true)
|
||||
}
|
||||
}
|
||||
self.imageView.update(
|
||||
|
||||
@ -3791,7 +3791,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
let contentFrame = CGRect(origin: CGPoint(x: 0.0, y: component.containerInsets.top - (contentSize.height - contentVisualHeight) * 0.5 - contentBottomInsetOverflow), size: contentSize)
|
||||
var contentInsets = UIEdgeInsets(top: 54.0, left: 0.0, bottom: 0.0, right: 0.0)
|
||||
if let inputPanelFrameValue {
|
||||
contentInsets.bottom = max(0.0, contentFrame.maxY - (inputPanelFrameValue.minY + 8.0))
|
||||
contentInsets.bottom = max(0.0, contentFrame.maxY - inputPanelFrameValue.minY - 2.0)
|
||||
}
|
||||
|
||||
transition.setFrame(view: self.viewListsContainer, frame: CGRect(origin: CGPoint(x: contentFrame.minX, y: 0.0), size: CGSize(width: contentSize.width, height: availableSize.height)))
|
||||
|
||||
@ -223,7 +223,7 @@ private final class StoryStealthModeSheetContentComponent: Component {
|
||||
size: CGSize(width: 40.0, height: 40.0),
|
||||
backgroundColor: environment.theme.rootController.navigationBar.glassBarButtonBackgroundColor,
|
||||
isDark: environment.theme.overallDarkAppearance,
|
||||
state: .tintedGlass,
|
||||
state: .generic,
|
||||
component: AnyComponentWithIdentity(id: "close", component: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Navigation/Close",
|
||||
|
||||
@ -65,9 +65,10 @@ public final class AnimatableProperty<T: Interpolatable> {
|
||||
guard let animation = self.animation, case let .curve(duration, curve) = animation.animation else {
|
||||
return false
|
||||
}
|
||||
let effectiveDuration = duration * UIView.animationDurationFactor()
|
||||
|
||||
let timeFromStart = timestamp - animation.startTimestamp
|
||||
var t = max(0.0, timeFromStart / duration)
|
||||
var t = max(0.0, timeFromStart / effectiveDuration)
|
||||
switch curve {
|
||||
case .linear:
|
||||
break
|
||||
@ -80,7 +81,7 @@ public final class AnimatableProperty<T: Interpolatable> {
|
||||
}
|
||||
self.presentationValue = animation.valueAt(t) as! T
|
||||
|
||||
if timeFromStart <= duration {
|
||||
if timeFromStart <= effectiveDuration {
|
||||
return true
|
||||
}
|
||||
self.animation = nil
|
||||
|
||||
@ -174,7 +174,7 @@ static bool notyfyingShiftState = false;
|
||||
@end
|
||||
|
||||
static EffectSettingsContainerView *findTopmostEffectSuperview(UIView *view, int depth) {
|
||||
if (depth > 5) {
|
||||
if (depth > 10) {
|
||||
return nil;
|
||||
}
|
||||
if ([view isKindOfClass:[EffectSettingsContainerView class]]) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"app": "12.1.1",
|
||||
"app": "12.2",
|
||||
"xcode": "26.0",
|
||||
"bazel": "8.3.1:0cac3a67dc5429c68272dc6944104952e9e4cf84b29d126a5ff3fbaa59045217",
|
||||
"macos": "26"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user