mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various improvements
This commit is contained in:
parent
c929cce94f
commit
b9f141aae4
@ -7,64 +7,145 @@ import SwiftSignalKit
|
|||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
import AccountContext
|
import AccountContext
|
||||||
|
|
||||||
private struct BotCheckoutPasswordAlertAction {
|
private final class BotCheckoutPassworInputFieldNode: ASDisplayNode, UITextFieldDelegate {
|
||||||
public let title: String
|
private var theme: PresentationTheme
|
||||||
public let action: () -> Void
|
private let backgroundNode: ASImageNode
|
||||||
|
private let textInputNode: TextFieldNode
|
||||||
|
private let placeholderNode: ASTextNode
|
||||||
|
|
||||||
public init(title: String, action: @escaping () -> Void) {
|
var updateHeight: (() -> Void)?
|
||||||
self.title = title
|
var complete: (() -> Void)?
|
||||||
self.action = action
|
var textChanged: ((String) -> Void)?
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final class BotCheckoutPasswordAlertActionNode: HighlightableButtonNode {
|
|
||||||
private let backgroundNode: ASDisplayNode
|
|
||||||
|
|
||||||
let action: BotCheckoutPasswordAlertAction
|
private let backgroundInsets = UIEdgeInsets(top: 8.0, left: 16.0, bottom: 15.0, right: 16.0)
|
||||||
|
private let inputInsets = UIEdgeInsets(top: 5.0, left: 12.0, bottom: 5.0, right: 12.0)
|
||||||
|
|
||||||
init(theme: PresentationTheme, action: BotCheckoutPasswordAlertAction) {
|
var text: String {
|
||||||
self.backgroundNode = ASDisplayNode()
|
get {
|
||||||
self.backgroundNode.isLayerBacked = true
|
return self.textInputNode.textField.text ?? ""
|
||||||
self.backgroundNode.backgroundColor = theme.actionSheet.opaqueItemHighlightedBackgroundColor
|
}
|
||||||
self.backgroundNode.alpha = 0.0
|
set {
|
||||||
|
self.textInputNode.textField.text = newValue
|
||||||
self.action = action
|
self.placeholderNode.isHidden = !newValue.isEmpty
|
||||||
|
|
||||||
super.init()
|
|
||||||
|
|
||||||
self.setTitle(action.title, with: Font.regular(17.0), with: theme.actionSheet.controlAccentColor, for: [])
|
|
||||||
self.setTitle(action.title, with: Font.regular(17.0), with: theme.actionSheet.disabledActionTextColor, for: [.disabled])
|
|
||||||
|
|
||||||
self.highligthedChanged = { [weak self] value in
|
|
||||||
if let strongSelf = self {
|
|
||||||
if value {
|
|
||||||
if strongSelf.backgroundNode.supernode == nil {
|
|
||||||
strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0)
|
|
||||||
}
|
|
||||||
strongSelf.backgroundNode.layer.removeAnimation(forKey: "opacity")
|
|
||||||
strongSelf.backgroundNode.alpha = 1.0
|
|
||||||
} else if !strongSelf.backgroundNode.alpha.isZero {
|
|
||||||
strongSelf.backgroundNode.alpha = 0.0
|
|
||||||
strongSelf.backgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func didLoad() {
|
var placeholder: String = "" {
|
||||||
super.didLoad()
|
didSet {
|
||||||
|
self.placeholderNode.attributedText = NSAttributedString(string: self.placeholder, font: Font.regular(17.0), textColor: self.theme.actionSheet.inputPlaceholderColor)
|
||||||
self.addTarget(self, action: #selector(self.pressed), forControlEvents: .touchUpInside)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func pressed() {
|
init(theme: PresentationTheme, placeholder: String) {
|
||||||
self.action.action()
|
self.theme = theme
|
||||||
|
|
||||||
|
self.backgroundNode = ASImageNode()
|
||||||
|
self.backgroundNode.isLayerBacked = true
|
||||||
|
self.backgroundNode.displaysAsynchronously = false
|
||||||
|
self.backgroundNode.displayWithoutProcessing = true
|
||||||
|
self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 12.0, color: theme.actionSheet.inputHollowBackgroundColor, strokeColor: theme.actionSheet.inputBorderColor, strokeWidth: 1.0)
|
||||||
|
|
||||||
|
self.textInputNode = TextFieldNode()
|
||||||
|
self.textInputNode.textField.typingAttributes = [NSAttributedString.Key.font: Font.regular(17.0), NSAttributedString.Key.foregroundColor: theme.actionSheet.inputTextColor]
|
||||||
|
self.textInputNode.textField.clipsToBounds = true
|
||||||
|
self.textInputNode.hitTestSlop = UIEdgeInsets(top: -5.0, left: -5.0, bottom: -5.0, right: -5.0)
|
||||||
|
self.textInputNode.textField.keyboardAppearance = theme.rootController.keyboardColor.keyboardAppearance
|
||||||
|
self.textInputNode.textField.returnKeyType = .done
|
||||||
|
self.textInputNode.textField.isSecureTextEntry = true
|
||||||
|
self.textInputNode.textField.tintColor = theme.actionSheet.controlAccentColor
|
||||||
|
|
||||||
|
self.placeholderNode = ASTextNode()
|
||||||
|
self.placeholderNode.isUserInteractionEnabled = false
|
||||||
|
self.placeholderNode.displaysAsynchronously = false
|
||||||
|
self.placeholderNode.attributedText = NSAttributedString(string: placeholder, font: Font.regular(17.0), textColor: self.theme.actionSheet.inputPlaceholderColor)
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
self.textInputNode.textField.delegate = self
|
||||||
|
self.textInputNode.textField.addTarget(self, action: #selector(self.textDidChange), for: .editingChanged)
|
||||||
|
|
||||||
|
self.addSubnode(self.backgroundNode)
|
||||||
|
self.addSubnode(self.textInputNode)
|
||||||
|
self.addSubnode(self.placeholderNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func layout() {
|
func updateTheme(_ theme: PresentationTheme) {
|
||||||
super.layout()
|
self.theme = theme
|
||||||
|
|
||||||
self.backgroundNode.frame = self.bounds
|
self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 12.0, color: self.theme.actionSheet.inputHollowBackgroundColor, strokeColor: self.theme.actionSheet.inputBorderColor, strokeWidth: 1.0)
|
||||||
|
self.textInputNode.textField.keyboardAppearance = self.theme.rootController.keyboardColor.keyboardAppearance
|
||||||
|
self.placeholderNode.attributedText = NSAttributedString(string: self.placeholderNode.attributedText?.string ?? "", font: Font.regular(17.0), textColor: self.theme.actionSheet.inputPlaceholderColor)
|
||||||
|
self.textInputNode.textField.tintColor = self.theme.actionSheet.controlAccentColor
|
||||||
|
self.textInputNode.textField.typingAttributes = [NSAttributedString.Key.font: Font.regular(17.0), NSAttributedString.Key.foregroundColor: theme.actionSheet.inputTextColor]
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateLayout(width: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||||
|
let backgroundInsets = self.backgroundInsets
|
||||||
|
let inputInsets = self.inputInsets
|
||||||
|
|
||||||
|
let textFieldHeight = self.calculateTextFieldMetrics(width: width)
|
||||||
|
let panelHeight = textFieldHeight + backgroundInsets.top + backgroundInsets.bottom
|
||||||
|
|
||||||
|
let backgroundFrame = CGRect(origin: CGPoint(x: backgroundInsets.left, y: backgroundInsets.top), size: CGSize(width: width - backgroundInsets.left - backgroundInsets.right, height: panelHeight - backgroundInsets.top - backgroundInsets.bottom))
|
||||||
|
transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame)
|
||||||
|
|
||||||
|
let placeholderSize = self.placeholderNode.measure(backgroundFrame.size)
|
||||||
|
transition.updateFrame(node: self.placeholderNode, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX + inputInsets.left, y: backgroundFrame.minY + floor((backgroundFrame.size.height - placeholderSize.height) / 2.0)), size: placeholderSize))
|
||||||
|
|
||||||
|
transition.updateFrame(node: self.textInputNode, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX + inputInsets.left, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.size.width - inputInsets.left - inputInsets.right, height: backgroundFrame.size.height)))
|
||||||
|
|
||||||
|
return panelHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
func activateInput() {
|
||||||
|
self.textInputNode.becomeFirstResponder()
|
||||||
|
}
|
||||||
|
|
||||||
|
func deactivateInput() {
|
||||||
|
self.textInputNode.resignFirstResponder()
|
||||||
|
}
|
||||||
|
|
||||||
|
func shake() {
|
||||||
|
self.layer.addShakeAnimation()
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func textDidChange() {
|
||||||
|
self.updateTextNodeText(animated: true)
|
||||||
|
self.textChanged?(self.textInputNode.textField.text ?? "")
|
||||||
|
self.placeholderNode.isHidden = !(self.textInputNode.textField.text ?? "").isEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
|
||||||
|
if text == "\n" {
|
||||||
|
self.complete?()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private func calculateTextFieldMetrics(width: CGFloat) -> CGFloat {
|
||||||
|
let backgroundInsets = self.backgroundInsets
|
||||||
|
let inputInsets = self.inputInsets
|
||||||
|
|
||||||
|
let unboundTextFieldHeight = max(33.0, ceil(self.textInputNode.measure(CGSize(width: width - backgroundInsets.left - backgroundInsets.right - inputInsets.left - inputInsets.right, height: CGFloat.greatestFiniteMagnitude)).height))
|
||||||
|
|
||||||
|
return min(61.0, max(33.0, unboundTextFieldHeight))
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateTextNodeText(animated: Bool) {
|
||||||
|
let backgroundInsets = self.backgroundInsets
|
||||||
|
|
||||||
|
let textFieldHeight = self.calculateTextFieldMetrics(width: self.bounds.size.width)
|
||||||
|
|
||||||
|
let panelHeight = textFieldHeight + backgroundInsets.top + backgroundInsets.bottom
|
||||||
|
if !self.bounds.size.height.isEqual(to: panelHeight) {
|
||||||
|
self.updateHeight?()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func clearPressed() {
|
||||||
|
self.textInputNode.textField.text = nil
|
||||||
|
self.deactivateInput()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,14 +159,13 @@ private final class BotCheckoutPasswordAlertContentNode: AlertContentNode {
|
|||||||
private let textNode: ASTextNode
|
private let textNode: ASTextNode
|
||||||
|
|
||||||
private let actionNodesSeparator: ASDisplayNode
|
private let actionNodesSeparator: ASDisplayNode
|
||||||
private let actionNodes: [BotCheckoutPasswordAlertActionNode]
|
private let actionNodes: [TextAlertContentActionNode]
|
||||||
private let actionVerticalSeparators: [ASDisplayNode]
|
private let actionVerticalSeparators: [ASDisplayNode]
|
||||||
|
|
||||||
private let cancelActionNode: BotCheckoutPasswordAlertActionNode
|
private let cancelActionNode: TextAlertContentActionNode
|
||||||
private let doneActionNode: BotCheckoutPasswordAlertActionNode
|
private let doneActionNode: TextAlertContentActionNode
|
||||||
|
|
||||||
private let textFieldNodeBackground: ASImageNode
|
let inputFieldNode: BotCheckoutPassworInputFieldNode
|
||||||
private let textFieldNode: TextFieldNode
|
|
||||||
|
|
||||||
private var validLayout: CGSize?
|
private var validLayout: CGSize?
|
||||||
private var isVerifying = false
|
private var isVerifying = false
|
||||||
@ -99,6 +179,8 @@ private final class BotCheckoutPasswordAlertContentNode: AlertContentNode {
|
|||||||
self.requiresBiometrics = requiresBiometrics
|
self.requiresBiometrics = requiresBiometrics
|
||||||
self.completion = completion
|
self.completion = completion
|
||||||
|
|
||||||
|
let alertTheme = AlertControllerTheme(presentationTheme: theme, fontSize: .regular)
|
||||||
|
|
||||||
let titleNode = ASTextNode()
|
let titleNode = ASTextNode()
|
||||||
titleNode.attributedText = NSAttributedString(string: strings.Checkout_PasswordEntry_Title, font: Font.semibold(17.0), textColor: theme.actionSheet.primaryTextColor, paragraphAlignment: .center)
|
titleNode.attributedText = NSAttributedString(string: strings.Checkout_PasswordEntry_Title, font: Font.semibold(17.0), textColor: theme.actionSheet.primaryTextColor, paragraphAlignment: .center)
|
||||||
titleNode.displaysAsynchronously = false
|
titleNode.displaysAsynchronously = false
|
||||||
@ -112,16 +194,18 @@ private final class BotCheckoutPasswordAlertContentNode: AlertContentNode {
|
|||||||
self.textNode.displaysAsynchronously = false
|
self.textNode.displaysAsynchronously = false
|
||||||
self.textNode.isUserInteractionEnabled = false
|
self.textNode.isUserInteractionEnabled = false
|
||||||
|
|
||||||
|
self.inputFieldNode = BotCheckoutPassworInputFieldNode(theme: theme, placeholder: passwordTip ?? "")
|
||||||
|
|
||||||
self.actionNodesSeparator = ASDisplayNode()
|
self.actionNodesSeparator = ASDisplayNode()
|
||||||
self.actionNodesSeparator.isLayerBacked = true
|
self.actionNodesSeparator.isLayerBacked = true
|
||||||
self.actionNodesSeparator.backgroundColor = theme.actionSheet.opaqueItemSeparatorColor
|
self.actionNodesSeparator.backgroundColor = theme.actionSheet.opaqueItemSeparatorColor
|
||||||
|
|
||||||
self.cancelActionNode = BotCheckoutPasswordAlertActionNode(theme: theme, action: BotCheckoutPasswordAlertAction(title: strings.Common_Cancel, action: {
|
self.cancelActionNode = TextAlertContentActionNode(theme: alertTheme, action: TextAlertAction(type: .genericAction, title: strings.Common_Cancel, action: {
|
||||||
cancel()
|
cancel()
|
||||||
}))
|
}))
|
||||||
|
|
||||||
var doneImpl: (() -> Void)?
|
var doneImpl: (() -> Void)?
|
||||||
self.doneActionNode = BotCheckoutPasswordAlertActionNode(theme: theme, action: BotCheckoutPasswordAlertAction(title: strings.Checkout_PasswordEntry_Pay, action: {
|
self.doneActionNode = TextAlertContentActionNode(theme: alertTheme, action: TextAlertAction(type: .defaultAction, title: strings.Checkout_PasswordEntry_Pay, action: {
|
||||||
doneImpl?()
|
doneImpl?()
|
||||||
}))
|
}))
|
||||||
|
|
||||||
@ -138,26 +222,6 @@ private final class BotCheckoutPasswordAlertContentNode: AlertContentNode {
|
|||||||
}
|
}
|
||||||
self.actionVerticalSeparators = actionVerticalSeparators
|
self.actionVerticalSeparators = actionVerticalSeparators
|
||||||
|
|
||||||
self.textFieldNodeBackground = ASImageNode()
|
|
||||||
self.textFieldNodeBackground.displaysAsynchronously = false
|
|
||||||
self.textFieldNodeBackground.displayWithoutProcessing = true
|
|
||||||
self.textFieldNodeBackground.image = generateImage(CGSize(width: 4.0, height: 4.0), rotatedContext: { size, context in
|
|
||||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
|
||||||
context.setStrokeColor(theme.actionSheet.primaryTextColor.cgColor)
|
|
||||||
context.setLineWidth(UIScreenPixel)
|
|
||||||
context.stroke(CGRect(origin: CGPoint(), size: size))
|
|
||||||
})?.stretchableImage(withLeftCapWidth: 2, topCapHeight: 2)
|
|
||||||
|
|
||||||
self.textFieldNode = TextFieldNode()
|
|
||||||
self.textFieldNode.textField.textColor = theme.actionSheet.primaryTextColor
|
|
||||||
self.textFieldNode.textField.font = Font.regular(12.0)
|
|
||||||
self.textFieldNode.textField.typingAttributes = [NSAttributedString.Key.font: Font.regular(12.0)]
|
|
||||||
self.textFieldNode.textField.keyboardAppearance = theme.rootController.keyboardColor.keyboardAppearance
|
|
||||||
self.textFieldNode.textField.isSecureTextEntry = true
|
|
||||||
self.textFieldNode.textField.tintColor = theme.list.itemAccentColor
|
|
||||||
self.textFieldNode.textField.placeholder = passwordTip
|
|
||||||
|
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
self.addSubnode(self.titleNode)
|
self.addSubnode(self.titleNode)
|
||||||
@ -173,11 +237,14 @@ private final class BotCheckoutPasswordAlertContentNode: AlertContentNode {
|
|||||||
self.addSubnode(separatorNode)
|
self.addSubnode(separatorNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.addSubnode(self.textFieldNodeBackground)
|
self.addSubnode(self.inputFieldNode)
|
||||||
self.addSubnode(self.textFieldNode)
|
|
||||||
|
|
||||||
self.textFieldNode.textField.addTarget(self, action: #selector(self.textFieldChanged(_:)), for: .editingChanged)
|
|
||||||
|
|
||||||
|
self.inputFieldNode.textChanged = { [weak self] _ in
|
||||||
|
if let strongSelf = self {
|
||||||
|
strongSelf.updateState()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.updateState()
|
self.updateState()
|
||||||
|
|
||||||
doneImpl = { [weak self] in
|
doneImpl = { [weak self] in
|
||||||
@ -213,13 +280,11 @@ private final class BotCheckoutPasswordAlertContentNode: AlertContentNode {
|
|||||||
let textFrame = CGRect(origin: CGPoint(x: insets.left + floor((contentWidth - textSize.width) / 2.0), y: titleFrame.maxY + spacing), size: textSize)
|
let textFrame = CGRect(origin: CGPoint(x: insets.left + floor((contentWidth - textSize.width) / 2.0), y: titleFrame.maxY + spacing), size: textSize)
|
||||||
transition.updateFrame(node: self.textNode, frame: textFrame)
|
transition.updateFrame(node: self.textNode, frame: textFrame)
|
||||||
|
|
||||||
let inputHeight: CGFloat = 38.0
|
let resultSize = CGSize(width: contentWidth + insets.left + insets.right, height: titleSize.height + spacing + textSize.height + actionsHeight + insets.top + insets.bottom + 46.0)
|
||||||
|
|
||||||
let resultSize = CGSize(width: contentWidth + insets.left + insets.right, height: titleSize.height + spacing + textSize.height + actionsHeight + insets.top + insets.bottom + inputHeight)
|
let inputFieldWidth = resultSize.width
|
||||||
|
let inputFieldHeight = self.inputFieldNode.updateLayout(width: inputFieldWidth, transition: transition)
|
||||||
let textFieldBackgroundFrame = CGRect(origin: CGPoint(x: insets.left, y: resultSize.height - inputHeight + 12.0 - actionsHeight - insets.bottom), size: CGSize(width: resultSize.width - insets.left - insets.right, height: 25.0))
|
transition.updateFrame(node: self.inputFieldNode, frame: CGRect(x: 0.0, y: resultSize.height - 36.0 - actionsHeight - insets.bottom, width: resultSize.width, height: inputFieldHeight))
|
||||||
self.textFieldNodeBackground.frame = textFieldBackgroundFrame
|
|
||||||
self.textFieldNode.frame = textFieldBackgroundFrame.offsetBy(dx: 0.0, dy: 0.0).insetBy(dx: 4.0, dy: 0.0)
|
|
||||||
|
|
||||||
self.actionNodesSeparator.frame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))
|
self.actionNodesSeparator.frame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))
|
||||||
|
|
||||||
@ -250,7 +315,7 @@ private final class BotCheckoutPasswordAlertContentNode: AlertContentNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if previousLayout == nil {
|
if previousLayout == nil {
|
||||||
self.textFieldNode.textField.becomeFirstResponder()
|
self.inputFieldNode.activateInput()
|
||||||
}
|
}
|
||||||
|
|
||||||
return resultSize
|
return resultSize
|
||||||
@ -262,24 +327,15 @@ private final class BotCheckoutPasswordAlertContentNode: AlertContentNode {
|
|||||||
|
|
||||||
private func updateState() {
|
private func updateState() {
|
||||||
var enabled = true
|
var enabled = true
|
||||||
|
if self.isVerifying || self.inputFieldNode.text.isEmpty {
|
||||||
if self.isVerifying {
|
|
||||||
enabled = false
|
enabled = false
|
||||||
}
|
}
|
||||||
|
self.doneActionNode.actionEnabled = enabled
|
||||||
if let text = self.textFieldNode.textField.text {
|
|
||||||
if text.isEmpty {
|
|
||||||
enabled = false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
enabled = false
|
|
||||||
}
|
|
||||||
|
|
||||||
self.doneActionNode.isEnabled = enabled
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func verify() {
|
private func verify() {
|
||||||
guard let text = self.textFieldNode.textField.text, !text.isEmpty else {
|
let text = self.inputFieldNode.text
|
||||||
|
guard !text.isEmpty else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,8 +346,7 @@ private final class BotCheckoutPasswordAlertContentNode: AlertContentNode {
|
|||||||
}
|
}
|
||||||
}, error: { [weak self] _ in
|
}, error: { [weak self] _ in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.textFieldNodeBackground.layer.addShakeAnimation()
|
strongSelf.inputFieldNode.shake()
|
||||||
strongSelf.textFieldNode.layer.addShakeAnimation()
|
|
||||||
strongSelf.hapticFeedback.error()
|
strongSelf.hapticFeedback.error()
|
||||||
strongSelf.isVerifying = false
|
strongSelf.isVerifying = false
|
||||||
strongSelf.updateState()
|
strongSelf.updateState()
|
||||||
|
@ -45,6 +45,7 @@
|
|||||||
_explicit = explicit;
|
_explicit = explicit;
|
||||||
|
|
||||||
_sliderView = [[TGPhotoEditorSliderView alloc] initWithFrame:CGRectZero];
|
_sliderView = [[TGPhotoEditorSliderView alloc] initWithFrame:CGRectZero];
|
||||||
|
_sliderView.enablePanHandling = true;
|
||||||
if (editorItem.segmented)
|
if (editorItem.segmented)
|
||||||
_sliderView.positionsCount = (NSInteger)editorItem.maximumValue + 1;
|
_sliderView.positionsCount = (NSInteger)editorItem.maximumValue + 1;
|
||||||
_sliderView.minimumValue = editorItem.minimumValue;
|
_sliderView.minimumValue = editorItem.minimumValue;
|
||||||
|
@ -68,6 +68,7 @@ const CGFloat TGPhotoEditorSliderViewInternalMargin = 7.0f;
|
|||||||
|
|
||||||
_panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
|
_panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
|
||||||
_panGestureRecognizer.enabled = false;
|
_panGestureRecognizer.enabled = false;
|
||||||
|
_panGestureRecognizer.delegate = self;
|
||||||
[self addGestureRecognizer:_panGestureRecognizer];
|
[self addGestureRecognizer:_panGestureRecognizer];
|
||||||
|
|
||||||
_tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
|
_tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
|
||||||
@ -531,6 +532,19 @@ const CGFloat TGPhotoEditorSliderViewInternalMargin = 7.0f;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)gestureRecognizer {
|
||||||
|
if (gestureRecognizer == _panGestureRecognizer) {
|
||||||
|
CGPoint velocity = [gestureRecognizer velocityInView:gestureRecognizer.view];
|
||||||
|
if (ABS(velocity.x) > ABS(velocity.y)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)__unused event
|
- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)__unused event
|
||||||
{
|
{
|
||||||
if (!_enablePanHandling) {
|
if (!_enablePanHandling) {
|
||||||
|
@ -266,6 +266,7 @@ const CGFloat TGPhotoEditorToolsLandscapePanelSize = TGPhotoEditorToolsPanelSize
|
|||||||
_portraitCollectionView.toolsDataSource = self;
|
_portraitCollectionView.toolsDataSource = self;
|
||||||
_portraitCollectionView.interactionBegan = _interactionBegan;
|
_portraitCollectionView.interactionBegan = _interactionBegan;
|
||||||
_portraitCollectionView.interactionEnded = _interactionEnded;
|
_portraitCollectionView.interactionEnded = _interactionEnded;
|
||||||
|
_portraitCollectionView.canCancelContentTouches = true;
|
||||||
[_portraitToolsWrapperView addSubview:_portraitCollectionView];
|
[_portraitToolsWrapperView addSubview:_portraitCollectionView];
|
||||||
|
|
||||||
if (!TGIsPad())
|
if (!TGIsPad())
|
||||||
@ -278,6 +279,7 @@ const CGFloat TGPhotoEditorToolsLandscapePanelSize = TGPhotoEditorToolsPanelSize
|
|||||||
_landscapeCollectionView.toolsDataSource = self;
|
_landscapeCollectionView.toolsDataSource = self;
|
||||||
_landscapeCollectionView.interactionBegan = _interactionBegan;
|
_landscapeCollectionView.interactionBegan = _interactionBegan;
|
||||||
_landscapeCollectionView.interactionEnded = _interactionEnded;
|
_landscapeCollectionView.interactionEnded = _interactionEnded;
|
||||||
|
_landscapeCollectionView.canCancelContentTouches = true;
|
||||||
[_landscapeToolsWrapperView addSubview:_landscapeCollectionView];
|
[_landscapeToolsWrapperView addSubview:_landscapeCollectionView];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,6 +93,17 @@ private final class TabBarItemNode: ASDisplayNode {
|
|||||||
var contentWidth: CGFloat?
|
var contentWidth: CGFloat?
|
||||||
var isSelected: Bool = false
|
var isSelected: Bool = false
|
||||||
|
|
||||||
|
let ringImageNode: ASImageNode
|
||||||
|
var ringColor: UIColor? {
|
||||||
|
didSet {
|
||||||
|
if let ringColor = self.ringColor {
|
||||||
|
self.ringImageNode.image = generateCircleImage(diameter: 29.0, lineWidth: 1.0, color: ringColor, backgroundColor: nil)
|
||||||
|
} else {
|
||||||
|
self.ringImageNode.image = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var swiped: ((TabBarItemSwipeDirection) -> Void)?
|
var swiped: ((TabBarItemSwipeDirection) -> Void)?
|
||||||
|
|
||||||
var pointerInteraction: PointerInteraction?
|
var pointerInteraction: PointerInteraction?
|
||||||
@ -101,6 +112,11 @@ private final class TabBarItemNode: ASDisplayNode {
|
|||||||
self.extractedContainerNode = ContextExtractedContentContainingNode()
|
self.extractedContainerNode = ContextExtractedContentContainingNode()
|
||||||
self.containerNode = ContextControllerSourceNode()
|
self.containerNode = ContextControllerSourceNode()
|
||||||
|
|
||||||
|
self.ringImageNode = ASImageNode()
|
||||||
|
self.ringImageNode.isUserInteractionEnabled = false
|
||||||
|
self.ringImageNode.displayWithoutProcessing = true
|
||||||
|
self.ringImageNode.displaysAsynchronously = false
|
||||||
|
|
||||||
self.imageNode = ASImageNode()
|
self.imageNode = ASImageNode()
|
||||||
self.imageNode.isUserInteractionEnabled = false
|
self.imageNode.isUserInteractionEnabled = false
|
||||||
self.imageNode.displayWithoutProcessing = true
|
self.imageNode.displayWithoutProcessing = true
|
||||||
@ -136,6 +152,7 @@ private final class TabBarItemNode: ASDisplayNode {
|
|||||||
|
|
||||||
self.isAccessibilityElement = true
|
self.isAccessibilityElement = true
|
||||||
|
|
||||||
|
self.extractedContainerNode.contentNode.addSubnode(self.ringImageNode)
|
||||||
self.extractedContainerNode.contentNode.addSubnode(self.textImageNode)
|
self.extractedContainerNode.contentNode.addSubnode(self.textImageNode)
|
||||||
self.extractedContainerNode.contentNode.addSubnode(self.imageNode)
|
self.extractedContainerNode.contentNode.addSubnode(self.imageNode)
|
||||||
self.extractedContainerNode.contentNode.addSubnode(self.animationContainerNode)
|
self.extractedContainerNode.contentNode.addSubnode(self.animationContainerNode)
|
||||||
@ -150,6 +167,7 @@ private final class TabBarItemNode: ASDisplayNode {
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
transition.updateAlpha(node: strongSelf.ringImageNode, alpha: isExtracted ? 0.0 : 1.0)
|
||||||
transition.updateAlpha(node: strongSelf.imageNode, alpha: isExtracted ? 0.0 : 1.0)
|
transition.updateAlpha(node: strongSelf.imageNode, alpha: isExtracted ? 0.0 : 1.0)
|
||||||
transition.updateAlpha(node: strongSelf.animationNode, alpha: isExtracted ? 0.0 : 1.0)
|
transition.updateAlpha(node: strongSelf.animationNode, alpha: isExtracted ? 0.0 : 1.0)
|
||||||
transition.updateAlpha(node: strongSelf.textImageNode, alpha: isExtracted ? 0.0 : 1.0)
|
transition.updateAlpha(node: strongSelf.textImageNode, alpha: isExtracted ? 0.0 : 1.0)
|
||||||
@ -441,6 +459,12 @@ class TabBarNode: ASDisplayNode {
|
|||||||
}, swipeAction: { [weak self] direction in
|
}, swipeAction: { [weak self] direction in
|
||||||
self?.swipeAction(i, direction)
|
self?.swipeAction(i, direction)
|
||||||
})
|
})
|
||||||
|
if item.item.ringSelection {
|
||||||
|
node.ringColor = self.theme.tabBarSelectedIconColor
|
||||||
|
} else {
|
||||||
|
node.ringColor = nil
|
||||||
|
}
|
||||||
|
|
||||||
if let selectedIndex = self.selectedIndex, selectedIndex == i {
|
if let selectedIndex = self.selectedIndex, selectedIndex == i {
|
||||||
let (textImage, contentWidth) = tabBarItemImage(item.item.selectedImage, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarSelectedTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered)
|
let (textImage, contentWidth) = tabBarItemImage(item.item.selectedImage, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarSelectedTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered)
|
||||||
let (image, imageContentWidth): (UIImage, CGFloat)
|
let (image, imageContentWidth): (UIImage, CGFloat)
|
||||||
@ -507,6 +531,12 @@ class TabBarNode: ASDisplayNode {
|
|||||||
|
|
||||||
self.centered = self.theme.tabBarTextColor == .clear
|
self.centered = self.theme.tabBarTextColor == .clear
|
||||||
|
|
||||||
|
if item.item.ringSelection {
|
||||||
|
node.ringColor = self.theme.tabBarSelectedIconColor
|
||||||
|
} else {
|
||||||
|
node.ringColor = nil
|
||||||
|
}
|
||||||
|
|
||||||
let previousImageSize = node.imageNode.image?.size ?? CGSize()
|
let previousImageSize = node.imageNode.image?.size ?? CGSize()
|
||||||
let previousTextImageSize = node.textImageNode.image?.size ?? CGSize()
|
let previousTextImageSize = node.textImageNode.image?.size ?? CGSize()
|
||||||
if let selectedIndex = self.selectedIndex, selectedIndex == index {
|
if let selectedIndex = self.selectedIndex, selectedIndex == index {
|
||||||
@ -524,7 +554,11 @@ class TabBarNode: ASDisplayNode {
|
|||||||
node.animationNode.setOverlayColor(self.theme.tabBarSelectedIconColor, replace: true, animated: false)
|
node.animationNode.setOverlayColor(self.theme.tabBarSelectedIconColor, replace: true, animated: false)
|
||||||
node.animationNode.updateLayout(size: CGSize(width: 51.0, height: 51.0))
|
node.animationNode.updateLayout(size: CGSize(width: 51.0, height: 51.0))
|
||||||
} else {
|
} else {
|
||||||
(image, imageContentWidth) = tabBarItemImage(item.item.selectedImage, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarSelectedIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered)
|
if item.item.ringSelection {
|
||||||
|
(image, imageContentWidth) = (item.item.selectedImage ?? UIImage(), item.item.selectedImage?.size.width ?? 0.0)
|
||||||
|
} else {
|
||||||
|
(image, imageContentWidth) = tabBarItemImage(item.item.selectedImage, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarSelectedIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered)
|
||||||
|
}
|
||||||
|
|
||||||
node.animationNode.isHidden = true
|
node.animationNode.isHidden = true
|
||||||
node.animationNode.visibility = false
|
node.animationNode.visibility = false
|
||||||
@ -539,9 +573,22 @@ class TabBarNode: ASDisplayNode {
|
|||||||
node.contextImageNode.image = contextImage
|
node.contextImageNode.image = contextImage
|
||||||
node.contentWidth = max(contentWidth, imageContentWidth)
|
node.contentWidth = max(contentWidth, imageContentWidth)
|
||||||
node.isSelected = true
|
node.isSelected = true
|
||||||
|
|
||||||
|
ContainedViewLayoutTransition.animated(duration: 0.35, curve: .easeInOut).updateTransformScale(node: node.ringImageNode, scale: 1.0, delay: 0.1)
|
||||||
|
node.imageNode.layer.animateScale(from: 1.0, to: 0.87, duration: 0.1, removeOnCompletion: false, completion: { [weak node] _ in
|
||||||
|
node?.imageNode.layer.animateScale(from: 0.87, to: 1.0, duration: 0.35, removeOnCompletion: false, completion: { [weak node] _ in
|
||||||
|
node?.imageNode.layer.removeAllAnimations()
|
||||||
|
})
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
let (textImage, contentWidth) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered)
|
let (textImage, contentWidth) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered)
|
||||||
let (image, imageContentWidth) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered)
|
|
||||||
|
let (image, imageContentWidth): (UIImage, CGFloat)
|
||||||
|
if item.item.ringSelection {
|
||||||
|
(image, imageContentWidth) = (item.item.image ?? UIImage(), item.item.image?.size.width ?? 0.0)
|
||||||
|
} else {
|
||||||
|
(image, imageContentWidth) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered)
|
||||||
|
}
|
||||||
let (contextTextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered)
|
let (contextTextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered)
|
||||||
let (contextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered)
|
let (contextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered)
|
||||||
|
|
||||||
@ -556,6 +603,8 @@ class TabBarNode: ASDisplayNode {
|
|||||||
node.contextImageNode.image = contextImage
|
node.contextImageNode.image = contextImage
|
||||||
node.contentWidth = max(contentWidth, imageContentWidth)
|
node.contentWidth = max(contentWidth, imageContentWidth)
|
||||||
node.isSelected = false
|
node.isSelected = false
|
||||||
|
|
||||||
|
ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut).updateTransformScale(node: node.ringImageNode, scale: 0.5)
|
||||||
}
|
}
|
||||||
|
|
||||||
let updatedImageSize = node.imageNode.image?.size ?? CGSize()
|
let updatedImageSize = node.imageNode.image?.size ?? CGSize()
|
||||||
@ -647,18 +696,33 @@ class TabBarNode: ASDisplayNode {
|
|||||||
node.containerNode.frame = CGRect(origin: CGPoint(), size: nodeFrame.size)
|
node.containerNode.frame = CGRect(origin: CGPoint(), size: nodeFrame.size)
|
||||||
node.hitTestSlop = UIEdgeInsets(top: -3.0, left: -horizontalHitTestInset, bottom: -3.0, right: -horizontalHitTestInset)
|
node.hitTestSlop = UIEdgeInsets(top: -3.0, left: -horizontalHitTestInset, bottom: -3.0, right: -horizontalHitTestInset)
|
||||||
node.containerNode.hitTestSlop = UIEdgeInsets(top: -3.0, left: -horizontalHitTestInset, bottom: -3.0, right: -horizontalHitTestInset)
|
node.containerNode.hitTestSlop = UIEdgeInsets(top: -3.0, left: -horizontalHitTestInset, bottom: -3.0, right: -horizontalHitTestInset)
|
||||||
node.imageNode.frame = CGRect(origin: CGPoint(), size: nodeFrame.size)
|
if node.ringColor == nil {
|
||||||
|
node.imageNode.frame = CGRect(origin: CGPoint(), size: nodeFrame.size)
|
||||||
|
}
|
||||||
node.textImageNode.frame = CGRect(origin: CGPoint(), size: nodeFrame.size)
|
node.textImageNode.frame = CGRect(origin: CGPoint(), size: nodeFrame.size)
|
||||||
node.contextImageNode.frame = CGRect(origin: CGPoint(), size: nodeFrame.size)
|
node.contextImageNode.frame = CGRect(origin: CGPoint(), size: nodeFrame.size)
|
||||||
node.contextTextImageNode.frame = CGRect(origin: CGPoint(), size: nodeFrame.size)
|
node.contextTextImageNode.frame = CGRect(origin: CGPoint(), size: nodeFrame.size)
|
||||||
|
|
||||||
let scaleFactor: CGFloat = horizontal ? 0.8 : 1.0
|
let scaleFactor: CGFloat = horizontal ? 0.8 : 1.0
|
||||||
node.animationContainerNode.subnodeTransform = CATransform3DMakeScale(scaleFactor, scaleFactor, 1.0)
|
node.animationContainerNode.subnodeTransform = CATransform3DMakeScale(scaleFactor, scaleFactor, 1.0)
|
||||||
let animationOffset: CGPoint = self.tabBarItems[i].item.animationOffset
|
let animationOffset: CGPoint = self.tabBarItems[i].item.animationOffset
|
||||||
|
let ringImageFrame: CGRect
|
||||||
|
let imageFrame: CGRect
|
||||||
if horizontal {
|
if horizontal {
|
||||||
node.animationNode.frame = CGRect(origin: CGPoint(x: -10.0 - UIScreenPixel, y: -4.0 - UIScreenPixel), size: CGSize(width: 51.0, height: 51.0))
|
node.animationNode.frame = CGRect(origin: CGPoint(x: -10.0 - UIScreenPixel, y: -4.0 - UIScreenPixel), size: CGSize(width: 51.0, height: 51.0))
|
||||||
|
ringImageFrame = CGRect(origin: CGPoint(x: UIScreenPixel, y: 5.0 + UIScreenPixel), size: CGSize(width: 23.0, height: 23.0))
|
||||||
|
imageFrame = ringImageFrame.insetBy(dx: -1.0 + UIScreenPixel, dy: -1.0 + UIScreenPixel)
|
||||||
} else {
|
} else {
|
||||||
node.animationNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((nodeSize.width - 51.0) / 2.0), y: -10.0 - UIScreenPixel).offsetBy(dx: animationOffset.x, dy: animationOffset.y), size: CGSize(width: 51.0, height: 51.0))
|
node.animationNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((nodeSize.width - 51.0) / 2.0), y: -10.0 - UIScreenPixel).offsetBy(dx: animationOffset.x, dy: animationOffset.y), size: CGSize(width: 51.0, height: 51.0))
|
||||||
|
ringImageFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((nodeSize.width - 29.0) / 2.0), y: 1.0), size: CGSize(width: 29.0, height: 29.0))
|
||||||
|
imageFrame = ringImageFrame.insetBy(dx: -1.0, dy: -1.0)
|
||||||
|
}
|
||||||
|
node.ringImageNode.bounds = CGRect(origin: CGPoint(), size: ringImageFrame.size)
|
||||||
|
node.ringImageNode.position = ringImageFrame.center
|
||||||
|
|
||||||
|
if node.ringColor != nil {
|
||||||
|
node.imageNode.bounds = CGRect(origin: CGPoint(), size: imageFrame.size)
|
||||||
|
node.imageNode.position = imageFrame.center
|
||||||
}
|
}
|
||||||
|
|
||||||
if container.badgeValue != container.appliedBadgeValue {
|
if container.badgeValue != container.appliedBadgeValue {
|
||||||
|
@ -367,7 +367,12 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode {
|
|||||||
self.expandBackgroundNode.image = generateExpandBackground(size: expandBackgroundFrame.size, color: presentationData.theme.list.itemBlocksBackgroundColor)
|
self.expandBackgroundNode.image = generateExpandBackground(size: expandBackgroundFrame.size, color: presentationData.theme.list.itemBlocksBackgroundColor)
|
||||||
|
|
||||||
transition.updateFrame(node: self.labelNode, frame: labelFrame)
|
transition.updateFrame(node: self.labelNode, frame: labelFrame)
|
||||||
transition.updateFrame(node: self.textNode, frame: textFrame)
|
|
||||||
|
var textTransition = transition
|
||||||
|
if self.textNode.frame.size != textFrame.size {
|
||||||
|
textTransition = .immediate
|
||||||
|
}
|
||||||
|
textTransition.updateFrame(node: self.textNode, frame: textFrame)
|
||||||
|
|
||||||
let height = labelSize.height + 3.0 + textSize.height + 22.0
|
let height = labelSize.height + 3.0 + textSize.height + 22.0
|
||||||
|
|
||||||
|
@ -2010,7 +2010,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
var requestAvatarExpansion: ((Bool, [AvatarGalleryEntry], AvatarGalleryEntry?, (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?) -> Void)?
|
var requestAvatarExpansion: ((Bool, [AvatarGalleryEntry], AvatarGalleryEntry?, (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?) -> Void)?
|
||||||
var requestOpenAvatarForEditing: ((Bool) -> Void)?
|
var requestOpenAvatarForEditing: ((Bool) -> Void)?
|
||||||
var cancelUpload: (() -> Void)?
|
var cancelUpload: (() -> Void)?
|
||||||
var requestUpdateLayout: (() -> Void)?
|
var requestUpdateLayout: ((Bool) -> Void)?
|
||||||
var animateOverlaysFadeIn: (() -> Void)?
|
var animateOverlaysFadeIn: (() -> Void)?
|
||||||
|
|
||||||
var displayAvatarContextMenu: ((ASDisplayNode, ContextGesture?) -> Void)?
|
var displayAvatarContextMenu: ((ASDisplayNode, ContextGesture?) -> Void)?
|
||||||
@ -2100,7 +2100,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
requestUpdateLayoutImpl = { [weak self] in
|
requestUpdateLayoutImpl = { [weak self] in
|
||||||
self?.requestUpdateLayout?()
|
self?.requestUpdateLayout?(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -460,7 +460,7 @@ private final class PeerInfoInteraction {
|
|||||||
let performMemberAction: (PeerInfoMember, PeerInfoMemberAction) -> Void
|
let performMemberAction: (PeerInfoMember, PeerInfoMemberAction) -> Void
|
||||||
let openPeerInfoContextMenu: (PeerInfoContextSubject, ASDisplayNode) -> Void
|
let openPeerInfoContextMenu: (PeerInfoContextSubject, ASDisplayNode) -> Void
|
||||||
let performBioLinkAction: (TextLinkItemActionType, TextLinkItem) -> Void
|
let performBioLinkAction: (TextLinkItemActionType, TextLinkItem) -> Void
|
||||||
let requestLayout: () -> Void
|
let requestLayout: (Bool) -> Void
|
||||||
let openEncryptionKey: () -> Void
|
let openEncryptionKey: () -> Void
|
||||||
let openSettings: (PeerInfoSettingsSection) -> Void
|
let openSettings: (PeerInfoSettingsSection) -> Void
|
||||||
let switchToAccount: (AccountRecordId) -> Void
|
let switchToAccount: (AccountRecordId) -> Void
|
||||||
@ -502,7 +502,7 @@ private final class PeerInfoInteraction {
|
|||||||
performMemberAction: @escaping (PeerInfoMember, PeerInfoMemberAction) -> Void,
|
performMemberAction: @escaping (PeerInfoMember, PeerInfoMemberAction) -> Void,
|
||||||
openPeerInfoContextMenu: @escaping (PeerInfoContextSubject, ASDisplayNode) -> Void,
|
openPeerInfoContextMenu: @escaping (PeerInfoContextSubject, ASDisplayNode) -> Void,
|
||||||
performBioLinkAction: @escaping (TextLinkItemActionType, TextLinkItem) -> Void,
|
performBioLinkAction: @escaping (TextLinkItemActionType, TextLinkItem) -> Void,
|
||||||
requestLayout: @escaping () -> Void,
|
requestLayout: @escaping (Bool) -> Void,
|
||||||
openEncryptionKey: @escaping () -> Void,
|
openEncryptionKey: @escaping () -> Void,
|
||||||
openSettings: @escaping (PeerInfoSettingsSection) -> Void,
|
openSettings: @escaping (PeerInfoSettingsSection) -> Void,
|
||||||
switchToAccount: @escaping (AccountRecordId) -> Void,
|
switchToAccount: @escaping (AccountRecordId) -> Void,
|
||||||
@ -864,7 +864,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
|||||||
}, longTapAction: { sourceNode in
|
}, longTapAction: { sourceNode in
|
||||||
interaction.openPeerInfoContextMenu(.phone(formattedPhone), sourceNode)
|
interaction.openPeerInfoContextMenu(.phone(formattedPhone), sourceNode)
|
||||||
}, requestLayout: {
|
}, requestLayout: {
|
||||||
interaction.requestLayout()
|
interaction.requestLayout(false)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
if let username = user.username {
|
if let username = user.username {
|
||||||
@ -875,21 +875,21 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
|||||||
}, iconAction: {
|
}, iconAction: {
|
||||||
interaction.openQrCode()
|
interaction.openQrCode()
|
||||||
}, requestLayout: {
|
}, requestLayout: {
|
||||||
interaction.requestLayout()
|
interaction.requestLayout(false)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
if let cachedData = data.cachedData as? CachedUserData {
|
if let cachedData = data.cachedData as? CachedUserData {
|
||||||
if user.isFake {
|
if user.isFake {
|
||||||
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: user.botInfo == nil ? presentationData.strings.Profile_About : presentationData.strings.Profile_BotInfo, text: user.botInfo != nil ? presentationData.strings.UserInfo_FakeBotWarning : presentationData.strings.UserInfo_FakeUserWarning, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: user.botInfo != nil ? enabledPrivateBioEntities : []), action: nil, requestLayout: {
|
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: user.botInfo == nil ? presentationData.strings.Profile_About : presentationData.strings.Profile_BotInfo, text: user.botInfo != nil ? presentationData.strings.UserInfo_FakeBotWarning : presentationData.strings.UserInfo_FakeUserWarning, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: user.botInfo != nil ? enabledPrivateBioEntities : []), action: nil, requestLayout: {
|
||||||
interaction.requestLayout()
|
interaction.requestLayout(false)
|
||||||
}))
|
}))
|
||||||
} else if user.isScam {
|
} else if user.isScam {
|
||||||
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: user.botInfo == nil ? presentationData.strings.Profile_About : presentationData.strings.Profile_BotInfo, text: user.botInfo != nil ? presentationData.strings.UserInfo_ScamBotWarning : presentationData.strings.UserInfo_ScamUserWarning, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: user.botInfo != nil ? enabledPrivateBioEntities : []), action: nil, requestLayout: {
|
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: user.botInfo == nil ? presentationData.strings.Profile_About : presentationData.strings.Profile_BotInfo, text: user.botInfo != nil ? presentationData.strings.UserInfo_ScamBotWarning : presentationData.strings.UserInfo_ScamUserWarning, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: user.botInfo != nil ? enabledPrivateBioEntities : []), action: nil, requestLayout: {
|
||||||
interaction.requestLayout()
|
interaction.requestLayout(false)
|
||||||
}))
|
}))
|
||||||
} else if let about = cachedData.about, !about.isEmpty {
|
} else if let about = cachedData.about, !about.isEmpty {
|
||||||
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: user.botInfo == nil ? presentationData.strings.Profile_About : presentationData.strings.Profile_BotInfo, text: about, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledPrivateBioEntities), action: nil, longTapAction: bioContextAction, linkItemAction: bioLinkAction, requestLayout: {
|
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: user.botInfo == nil ? presentationData.strings.Profile_About : presentationData.strings.Profile_BotInfo, text: about, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledPrivateBioEntities), action: nil, longTapAction: bioContextAction, linkItemAction: bioLinkAction, requestLayout: {
|
||||||
interaction.requestLayout()
|
interaction.requestLayout(false)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -982,7 +982,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
|||||||
}, iconAction: {
|
}, iconAction: {
|
||||||
interaction.openQrCode()
|
interaction.openQrCode()
|
||||||
}, requestLayout: {
|
}, requestLayout: {
|
||||||
interaction.requestLayout()
|
interaction.requestLayout(false)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
if let cachedData = data.cachedData as? CachedChannelData {
|
if let cachedData = data.cachedData as? CachedChannelData {
|
||||||
@ -1011,7 +1011,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
|||||||
enabledEntities = enabledPrivateBioEntities
|
enabledEntities = enabledPrivateBioEntities
|
||||||
}
|
}
|
||||||
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: ItemAbout, label: presentationData.strings.Channel_Info_Description, text: aboutText, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledEntities), action: nil, longTapAction: bioContextAction, linkItemAction: bioLinkAction, requestLayout: {
|
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: ItemAbout, label: presentationData.strings.Channel_Info_Description, text: aboutText, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledEntities), action: nil, longTapAction: bioContextAction, linkItemAction: bioLinkAction, requestLayout: {
|
||||||
interaction.requestLayout()
|
interaction.requestLayout(true)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1056,7 +1056,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
|||||||
|
|
||||||
if let aboutText = aboutText {
|
if let aboutText = aboutText {
|
||||||
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: presentationData.strings.Channel_Info_Description, text: aboutText, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledPrivateBioEntities), action: nil, longTapAction: bioContextAction, linkItemAction: bioLinkAction, requestLayout: {
|
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: presentationData.strings.Channel_Info_Description, text: aboutText, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledPrivateBioEntities), action: nil, longTapAction: bioContextAction, linkItemAction: bioLinkAction, requestLayout: {
|
||||||
interaction.requestLayout()
|
interaction.requestLayout(true)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1776,8 +1776,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
performBioLinkAction: { [weak self] action, item in
|
performBioLinkAction: { [weak self] action, item in
|
||||||
self?.performBioLinkAction(action: action, item: item)
|
self?.performBioLinkAction(action: action, item: item)
|
||||||
},
|
},
|
||||||
requestLayout: { [weak self] in
|
requestLayout: { [weak self] animated in
|
||||||
self?.requestLayout()
|
self?.requestLayout(animated: animated)
|
||||||
},
|
},
|
||||||
openEncryptionKey: { [weak self] in
|
openEncryptionKey: { [weak self] in
|
||||||
self?.openEncryptionKey()
|
self?.openEncryptionKey()
|
||||||
@ -2571,12 +2571,12 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
navigationBar.layer.animateAlpha(from: 0.0, to: navigationBar.alpha, duration: 0.25)
|
navigationBar.layer.animateAlpha(from: 0.0, to: navigationBar.alpha, duration: 0.25)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.headerNode.requestUpdateLayout = { [weak self] in
|
self.headerNode.requestUpdateLayout = { [weak self] animated in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if let (layout, navigationHeight) = strongSelf.validLayout {
|
if let (layout, navigationHeight) = strongSelf.validLayout {
|
||||||
strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false)
|
strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: animated ? .animated(duration: 0.35, curve: .slide) : .immediate, additive: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5630,8 +5630,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
self.context.sharedContext.handleTextLinkAction(context: self.context, peerId: peer.id, navigateDisposable: self.resolveUrlDisposable, controller: controller, action: action, itemLink: item)
|
self.context.sharedContext.handleTextLinkAction(context: self.context, peerId: peer.id, navigateDisposable: self.resolveUrlDisposable, controller: controller, action: action, itemLink: item)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func requestLayout() {
|
private func requestLayout(animated: Bool = false) {
|
||||||
self.headerNode.requestUpdateLayout?()
|
self.headerNode.requestUpdateLayout?(animated)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func openDeletePeer() {
|
private func openDeletePeer() {
|
||||||
@ -7763,18 +7763,8 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen {
|
|||||||
if let signal = peerAvatarImage(account: primary.0, peerReference: PeerReference(primary.1._asPeer()), authorOfMessage: nil, representation: primary.1.profileImageRepresentations.first, displayDimensions: size, inset: 3.0, emptyColor: nil, synchronousLoad: false) {
|
if let signal = peerAvatarImage(account: primary.0, peerReference: PeerReference(primary.1._asPeer()), authorOfMessage: nil, representation: primary.1.profileImageRepresentations.first, displayDimensions: size, inset: 3.0, emptyColor: nil, synchronousLoad: false) {
|
||||||
return signal
|
return signal
|
||||||
|> map { imageVersions -> (UIImage, UIImage)? in
|
|> map { imageVersions -> (UIImage, UIImage)? in
|
||||||
let image = imageVersions?.0
|
if let image = imageVersions?.0 {
|
||||||
if let image = image, let selectedImage = generateImage(size, rotatedContext: { size, context in
|
return (image.withRenderingMode(.alwaysOriginal), image.withRenderingMode(.alwaysOriginal))
|
||||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
|
||||||
context.translateBy(x: size.width / 2.0, y: size.height / 2.0)
|
|
||||||
context.scaleBy(x: 1.0, y: -1.0)
|
|
||||||
context.translateBy(x: -size.width / 2.0, y: -size.height / 2.0)
|
|
||||||
context.draw(image.cgImage!, in: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height))
|
|
||||||
context.setLineWidth(1.0)
|
|
||||||
context.setStrokeColor(primary.2.rootController.tabBar.selectedIconColor.cgColor)
|
|
||||||
context.strokeEllipse(in: CGRect(x: 1.5, y: 1.5, width: 28.0, height: 28.0))
|
|
||||||
}) {
|
|
||||||
return (image.withRenderingMode(.alwaysOriginal), selectedImage.withRenderingMode(.alwaysOriginal))
|
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -7792,22 +7782,8 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen {
|
|||||||
|
|
||||||
drawPeerAvatarLetters(context: context, size: CGSize(width: size.width - inset * 2.0, height: size.height - inset * 2.0), font: avatarFont, letters: displayLetters, peerId: primary.1.id)
|
drawPeerAvatarLetters(context: context, size: CGSize(width: size.width - inset * 2.0, height: size.height - inset * 2.0), font: avatarFont, letters: displayLetters, peerId: primary.1.id)
|
||||||
})?.withRenderingMode(.alwaysOriginal)
|
})?.withRenderingMode(.alwaysOriginal)
|
||||||
|
if let image = image {
|
||||||
let selectedImage = generateImage(size, rotatedContext: { size, context in
|
subscriber.putNext((image, image))
|
||||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
|
||||||
context.translateBy(x: size.width / 2.0, y: size.height / 2.0)
|
|
||||||
context.scaleBy(x: 1.0, y: -1.0)
|
|
||||||
context.translateBy(x: -size.width / 2.0, y: -size.height / 2.0)
|
|
||||||
if let cgImage = image?.cgImage {
|
|
||||||
context.draw(cgImage, in: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height))
|
|
||||||
}
|
|
||||||
context.setLineWidth(1.0)
|
|
||||||
context.setStrokeColor(primary.2.rootController.tabBar.selectedIconColor.cgColor)
|
|
||||||
context.strokeEllipse(in: CGRect(x: 1.5, y: 1.5, width: 28.0, height: 28.0))
|
|
||||||
})?.withRenderingMode(.alwaysOriginal)
|
|
||||||
|
|
||||||
if let image = image, let selectedImage = selectedImage {
|
|
||||||
subscriber.putNext((image, selectedImage))
|
|
||||||
} else {
|
} else {
|
||||||
subscriber.putNext(nil)
|
subscriber.putNext(nil)
|
||||||
}
|
}
|
||||||
@ -7884,6 +7860,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen {
|
|||||||
strongSelf.tabBarItem.image = image
|
strongSelf.tabBarItem.image = image
|
||||||
strongSelf.tabBarItem.selectedImage = selectedImage
|
strongSelf.tabBarItem.selectedImage = selectedImage
|
||||||
strongSelf.tabBarItem.animationName = isAvatar ? nil : "TabSettings"
|
strongSelf.tabBarItem.animationName = isAvatar ? nil : "TabSettings"
|
||||||
|
strongSelf.tabBarItem.ringSelection = isAvatar
|
||||||
strongSelf.tabBarItem.badgeValue = badgeValue
|
strongSelf.tabBarItem.badgeValue = badgeValue
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -50,5 +50,6 @@ NSInteger UITabBarItem_addSetBadgeListener(UITabBarItem * _Nonnull item, UITabBa
|
|||||||
|
|
||||||
@property (nonatomic, strong) NSString * _Nullable animationName;
|
@property (nonatomic, strong) NSString * _Nullable animationName;
|
||||||
@property (nonatomic, assign) CGPoint animationOffset;
|
@property (nonatomic, assign) CGPoint animationOffset;
|
||||||
|
@property (nonatomic, assign) bool ringSelection;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -18,6 +18,7 @@ static const void *setBadgeListenerBagKey = &setBadgeListenerBagKey;
|
|||||||
static const void *badgeKey = &badgeKey;
|
static const void *badgeKey = &badgeKey;
|
||||||
static const void *animationNameKey = &animationNameKey;
|
static const void *animationNameKey = &animationNameKey;
|
||||||
static const void *animationOffsetKey = &animationOffsetKey;
|
static const void *animationOffsetKey = &animationOffsetKey;
|
||||||
|
static const void *ringSelectionKey = &ringSelectionKey;
|
||||||
|
|
||||||
@implementation UINavigationItem (Proxy)
|
@implementation UINavigationItem (Proxy)
|
||||||
|
|
||||||
@ -419,4 +420,12 @@ NSInteger UITabBarItem_addSetBadgeListener(UITabBarItem *item, UITabBarItemSetBa
|
|||||||
return ((NSValue *)[self associatedObjectForKey:animationOffsetKey]).CGPointValue;
|
return ((NSValue *)[self associatedObjectForKey:animationOffsetKey]).CGPointValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)setRingSelection:(bool)ringSelection {
|
||||||
|
[self setAssociatedObject:@(ringSelection) forKey:ringSelectionKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (bool)ringSelection {
|
||||||
|
return ((NSNumber *)[self associatedObjectForKey:ringSelectionKey]).boolValue;
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user