mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Cherry-pick some fixes
This commit is contained in:
parent
930d1fcc46
commit
fdae5ffdf3
@ -384,7 +384,7 @@
|
|||||||
"Tour.Text2" = "**Telegram** delivers messages\nfaster than any other application.";
|
"Tour.Text2" = "**Telegram** delivers messages\nfaster than any other application.";
|
||||||
|
|
||||||
"Tour.Title3" = "Powerful";
|
"Tour.Title3" = "Powerful";
|
||||||
"Tour.Text3" = "**Telegram** has no limits on\nthe size of your chats and media.";
|
"Tour.Text3" = "**Telegram** has no limits on\nthe size of your media and chats.";
|
||||||
|
|
||||||
"Tour.Title4" = "Secure";
|
"Tour.Title4" = "Secure";
|
||||||
"Tour.Text4" = "**Telegram** keeps your messages\nsafe from hacker attacks.";
|
"Tour.Text4" = "**Telegram** keeps your messages\nsafe from hacker attacks.";
|
||||||
@ -393,30 +393,30 @@
|
|||||||
"Tour.Text5" = "**Telegram** lets you access your\nmessages from multiple devices.";
|
"Tour.Text5" = "**Telegram** lets you access your\nmessages from multiple devices.";
|
||||||
|
|
||||||
"Tour.Title6" = "Free";
|
"Tour.Title6" = "Free";
|
||||||
"Tour.Text6" = "**Telegram** provides free unlimited cloud storage\nfor chats and media.";
|
"Tour.Text6" = "**Telegram** provides free unlimited\ncloud storage for chats and media.";
|
||||||
|
|
||||||
"Tour.StartButton" = "Start Messaging";
|
"Tour.StartButton" = "Start Messaging";
|
||||||
|
|
||||||
// Login
|
// Login
|
||||||
"Login.PhoneAndCountryHelp" = "Please confirm your country code and enter your phone number.";
|
"Login.PhoneAndCountryHelp" = "Please confirm your country code\nand enter your phone number.";
|
||||||
"Login.CodeSentInternal" = "We've sent the code to the **Telegram** app on your other device";
|
"Login.CodeSentInternal" = "We've sent the code to the **Telegram** app on your other device";
|
||||||
"Login.HaveNotReceivedCodeInternal" = "Haven't received the code?";
|
"Login.HaveNotReceivedCodeInternal" = "Didn't get the code?";
|
||||||
"Login.CodeSentSms" = "We have sent you an SMS with the code";
|
"Login.CodeSentSms" = "We've sent you an SMS with the code";
|
||||||
"Login.Code" = "Code";
|
"Login.Code" = "Code";
|
||||||
"Login.WillCallYou" = "You can request a voice call in %@";
|
"Login.WillCallYou" = "Telegram will call you in %@";
|
||||||
"Login.CallRequestState2" = "Requesting a call from Telegram...";
|
"Login.CallRequestState2" = "Requesting a call from Telegram…";
|
||||||
"Login.CallRequestState3" = "Telegram dialed your number\n[Didn't get the code?]";
|
"Login.CallRequestState3" = "Telegram dialed your number\n[Didn't get the code?]";
|
||||||
"Login.EmailNotConfiguredError" = "Please set up an email account.";
|
"Login.EmailNotConfiguredError" = "An email account is required so that you can send us details about the error.\n\nPlease go to your device‘s settings > Passwords & Accounts > Add account and set up an email account.";
|
||||||
"Login.EmailCodeSubject" = "%@, no code";
|
"Login.EmailCodeSubject" = "%@, no code";
|
||||||
"Login.EmailCodeBody" = "My phone number is:\n%@\nI can't get an activation code for Telegram.";
|
"Login.EmailCodeBody" = "My phone number is:\n%@\nI can't get an activation code for Telegram.";
|
||||||
"Login.UnknownError" = "An error occurred. Please try again later";
|
"Login.UnknownError" = "An error occurred, please try again later.";
|
||||||
"Login.InvalidCodeError" = "You have entered an invalid code. Please try again.";
|
"Login.InvalidCodeError" = "Invalid code, please try again.";
|
||||||
"Login.NetworkError" = "Please check your internet connection and try again.";
|
"Login.NetworkError" = "Please check your internet connection and try again.";
|
||||||
"Login.CodeExpiredError" = "Code expired. Please try again.";
|
"Login.CodeExpiredError" = "Code expired, please start over.";
|
||||||
"Login.CodeFloodError" = "Limit exceeded. Please try again later.";
|
"Login.CodeFloodError" = "Too many attempts, please try again later.";
|
||||||
"Login.InvalidPhoneError" = "Invalid phone number. Please try again.";
|
"Login.InvalidPhoneError" = "Invalid phone number, please try again.";
|
||||||
"Login.InvalidFirstNameError" = "Invalid first name. Please try again.";
|
"Login.InvalidFirstNameError" = "This first name is not allowed, please try another.";
|
||||||
"Login.InvalidLastNameError" = "Invalid last name. Please try again.";
|
"Login.InvalidLastNameError" = "Sorry, this last name can't be used.";
|
||||||
|
|
||||||
"Login.InvalidPhoneEmailSubject" = "Invalid phone number: %@";
|
"Login.InvalidPhoneEmailSubject" = "Invalid phone number: %@";
|
||||||
"Login.InvalidPhoneEmailBody" = "I'm trying to use my mobile phone number: %1$@\nBut Telegram says it's invalid. Please help.\n\nApp version: %2$@\nOS version: %3$@\nLocale: %4$@\nMNC: %5$@";
|
"Login.InvalidPhoneEmailBody" = "I'm trying to use my mobile phone number: %1$@\nBut Telegram says it's invalid. Please help.\n\nApp version: %2$@\nOS version: %3$@\nLocale: %4$@\nMNC: %5$@";
|
||||||
@ -438,8 +438,8 @@
|
|||||||
"Login.InfoAvatarPhoto" = "photo";
|
"Login.InfoAvatarPhoto" = "photo";
|
||||||
"Login.InfoFirstNamePlaceholder" = "First Name";
|
"Login.InfoFirstNamePlaceholder" = "First Name";
|
||||||
"Login.InfoLastNamePlaceholder" = "Last Name";
|
"Login.InfoLastNamePlaceholder" = "Last Name";
|
||||||
"Login.InfoDeletePhoto" = "Delete Photo";
|
"Login.InfoDeletePhoto" = "Remove Photo";
|
||||||
"Login.InfoHelp" = "Enter your name and add a profile picture.";
|
"Login.InfoHelp" = "Enter your name and add a profile photo.";
|
||||||
|
|
||||||
// Login.SelectCountry
|
// Login.SelectCountry
|
||||||
"Login.SelectCountry.Title" = "Country";
|
"Login.SelectCountry.Title" = "Country";
|
||||||
@ -8919,3 +8919,14 @@ Sorry for the inconvenience.";
|
|||||||
|
|
||||||
"ImportStickerPack.ImportingEmojis" = "Importing Emojis";
|
"ImportStickerPack.ImportingEmojis" = "Importing Emojis";
|
||||||
"ImportStickerPack.CreateNewEmojiPack" = "Create a New Emoji Pack";
|
"ImportStickerPack.CreateNewEmojiPack" = "Create a New Emoji Pack";
|
||||||
|
|
||||||
|
"VoiceOver.Chat.Sending" = "Sending";
|
||||||
|
"VoiceOver.Chat.Failed" = "Failed to send";
|
||||||
|
|
||||||
|
"VoiceOver.Chat.PlayedByRecipient" = "Played by recipient";
|
||||||
|
"VoiceOver.Chat.PlayedByRecipients" = "Played by recipients";
|
||||||
|
|
||||||
|
"VoiceOver.Chat.NotPlayedByRecipient" = "Not played by recipient";
|
||||||
|
"VoiceOver.Chat.NotPlayedByRecipients" = "Not played by recipients";
|
||||||
|
|
||||||
|
"VoiceOver.Chat.ReplyingToMessage" = "In reply to message: %@";
|
||||||
|
@ -782,8 +782,8 @@ final class PhoneConfirmationController: ViewController {
|
|||||||
self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.4)
|
self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.4)
|
||||||
|
|
||||||
self.backgroundNode = ASDisplayNode()
|
self.backgroundNode = ASDisplayNode()
|
||||||
self.backgroundNode.backgroundColor = theme.list.plainBackgroundColor
|
self.backgroundNode.backgroundColor = theme.list.itemBlocksBackgroundColor
|
||||||
self.backgroundNode.cornerRadius = 11.0
|
self.backgroundNode.cornerRadius = 24.0
|
||||||
|
|
||||||
self.textNode = ImmediateTextNode()
|
self.textNode = ImmediateTextNode()
|
||||||
self.textNode.displaysAsynchronously = false
|
self.textNode.displaysAsynchronously = false
|
||||||
|
@ -120,6 +120,10 @@ public struct Transition {
|
|||||||
return Transition(animation: .curve(duration: duration, curve: .easeInOut))
|
return Transition(animation: .curve(duration: duration, curve: .easeInOut))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static func spring(duration: Double) -> Transition {
|
||||||
|
return Transition(animation: .curve(duration: duration, curve: .spring))
|
||||||
|
}
|
||||||
|
|
||||||
public init(animation: Animation) {
|
public init(animation: Animation) {
|
||||||
self.animation = animation
|
self.animation = animation
|
||||||
}
|
}
|
||||||
|
@ -126,7 +126,7 @@ public final class Button: Component {
|
|||||||
alpha = 1.0
|
alpha = 1.0
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
alpha = 0.4
|
alpha = 0.3
|
||||||
}
|
}
|
||||||
transition.setAlpha(view: self.contentView, alpha: alpha)
|
transition.setAlpha(view: self.contentView, alpha: alpha)
|
||||||
}
|
}
|
||||||
|
@ -7,13 +7,16 @@ import ComponentDisplayAdapters
|
|||||||
public final class BlurredBackgroundComponent: Component {
|
public final class BlurredBackgroundComponent: Component {
|
||||||
public let color: UIColor
|
public let color: UIColor
|
||||||
public let tintContainerView: UIView?
|
public let tintContainerView: UIView?
|
||||||
|
public let cornerRadius: CGFloat
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
color: UIColor,
|
color: UIColor,
|
||||||
tintContainerView: UIView? = nil
|
tintContainerView: UIView? = nil,
|
||||||
|
cornerRadius: CGFloat = 0.0
|
||||||
) {
|
) {
|
||||||
self.color = color
|
self.color = color
|
||||||
self.tintContainerView = tintContainerView
|
self.tintContainerView = tintContainerView
|
||||||
|
self.cornerRadius = cornerRadius
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func ==(lhs: BlurredBackgroundComponent, rhs: BlurredBackgroundComponent) -> Bool {
|
public static func ==(lhs: BlurredBackgroundComponent, rhs: BlurredBackgroundComponent) -> Bool {
|
||||||
@ -23,6 +26,9 @@ public final class BlurredBackgroundComponent: Component {
|
|||||||
if lhs.tintContainerView !== rhs.tintContainerView {
|
if lhs.tintContainerView !== rhs.tintContainerView {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.cornerRadius != rhs.cornerRadius {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,7 +112,7 @@ public final class BlurredBackgroundComponent: Component {
|
|||||||
view.frame = CGRect(origin: CGPoint(), size: availableSize)
|
view.frame = CGRect(origin: CGPoint(), size: availableSize)
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
self.update(size: availableSize, transition: transition.containedViewLayoutTransition)
|
self.update(size: availableSize, cornerRadius: component.cornerRadius, transition: transition.containedViewLayoutTransition)
|
||||||
|
|
||||||
if let tintContainerView = self.tintContainerView {
|
if let tintContainerView = self.tintContainerView {
|
||||||
transition.setFrame(view: tintContainerView, frame: CGRect(origin: CGPoint(), size: availableSize))
|
transition.setFrame(view: tintContainerView, frame: CGRect(origin: CGPoint(), size: availableSize))
|
||||||
|
@ -1193,22 +1193,25 @@ open class NavigationController: UINavigationController, ContainableController,
|
|||||||
split.isInFocus = true
|
split.isInFocus = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var masterTopHasOpaque = topHasOpaque
|
||||||
|
var detailTopHasOpaque = topHasOpaque
|
||||||
|
|
||||||
if let controller = split.masterControllers.last {
|
if let controller = split.masterControllers.last {
|
||||||
if topHasOpaque {
|
if masterTopHasOpaque {
|
||||||
controller.displayNode.accessibilityElementsHidden = true
|
controller.displayNode.accessibilityElementsHidden = true
|
||||||
} else {
|
} else {
|
||||||
if controller.isOpaqueWhenInOverlay || controller.blocksBackgroundWhenInOverlay {
|
if controller.isOpaqueWhenInOverlay || controller.blocksBackgroundWhenInOverlay {
|
||||||
topHasOpaque = true
|
masterTopHasOpaque = true
|
||||||
}
|
}
|
||||||
controller.displayNode.accessibilityElementsHidden = false
|
controller.displayNode.accessibilityElementsHidden = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let controller = split.detailControllers.last {
|
if let controller = split.detailControllers.last {
|
||||||
if topHasOpaque {
|
if detailTopHasOpaque {
|
||||||
controller.displayNode.accessibilityElementsHidden = true
|
controller.displayNode.accessibilityElementsHidden = true
|
||||||
} else {
|
} else {
|
||||||
if controller.isOpaqueWhenInOverlay || controller.blocksBackgroundWhenInOverlay {
|
if controller.isOpaqueWhenInOverlay || controller.blocksBackgroundWhenInOverlay {
|
||||||
topHasOpaque = true
|
detailTopHasOpaque = true
|
||||||
}
|
}
|
||||||
controller.displayNode.accessibilityElementsHidden = false
|
controller.displayNode.accessibilityElementsHidden = false
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ open class SparseNode: ASDisplayNode {
|
|||||||
if self.alpha.isZero {
|
if self.alpha.isZero {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if !self.bounds.contains(point) {
|
if !self.bounds.inset(by: self.hitTestSlop).contains(point) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
for view in self.view.subviews.reversed() {
|
for view in self.view.subviews.reversed() {
|
||||||
|
@ -87,6 +87,8 @@ final class MediaPickerGridItemNode: GridItemNode {
|
|||||||
private let typeIconNode: ASImageNode
|
private let typeIconNode: ASImageNode
|
||||||
private let durationNode: ImmediateTextNode
|
private let durationNode: ImmediateTextNode
|
||||||
|
|
||||||
|
private let activateAreaNode: AccessibilityAreaNode
|
||||||
|
|
||||||
private var interaction: MediaPickerInteraction?
|
private var interaction: MediaPickerInteraction?
|
||||||
private var theme: PresentationTheme?
|
private var theme: PresentationTheme?
|
||||||
|
|
||||||
@ -115,9 +117,13 @@ final class MediaPickerGridItemNode: GridItemNode {
|
|||||||
|
|
||||||
self.durationNode = ImmediateTextNode()
|
self.durationNode = ImmediateTextNode()
|
||||||
|
|
||||||
|
self.activateAreaNode = AccessibilityAreaNode()
|
||||||
|
self.activateAreaNode.accessibilityTraits = [.image]
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
self.addSubnode(self.imageNode)
|
self.addSubnode(self.imageNode)
|
||||||
|
self.addSubnode(self.activateAreaNode)
|
||||||
|
|
||||||
self.imageNode.contentUpdated = { [weak self] image in
|
self.imageNode.contentUpdated = { [weak self] image in
|
||||||
self?.spoilerNode?.setImage(image)
|
self?.spoilerNode?.setImage(image)
|
||||||
@ -285,6 +291,10 @@ final class MediaPickerGridItemNode: GridItemNode {
|
|||||||
let editingContext = interaction.editingState
|
let editingContext = interaction.editingState
|
||||||
let asset = fetchResult.object(at: index)
|
let asset = fetchResult.object(at: index)
|
||||||
|
|
||||||
|
if #available(iOS 15.0, *) {
|
||||||
|
self.activateAreaNode.accessibilityLabel = "Photo \(asset.creationDate?.formatted(date: .abbreviated, time: .standard) ?? "")"
|
||||||
|
}
|
||||||
|
|
||||||
let editedSignal = Signal<UIImage?, NoError> { subscriber in
|
let editedSignal = Signal<UIImage?, NoError> { subscriber in
|
||||||
if let signal = editingContext.thumbnailImageSignal(forIdentifier: asset.localIdentifier) {
|
if let signal = editingContext.thumbnailImageSignal(forIdentifier: asset.localIdentifier) {
|
||||||
let disposable = signal.start(next: { next in
|
let disposable = signal.start(next: { next in
|
||||||
@ -411,6 +421,7 @@ final class MediaPickerGridItemNode: GridItemNode {
|
|||||||
self.imageNode.frame = self.bounds
|
self.imageNode.frame = self.bounds
|
||||||
self.gradientNode.frame = CGRect(x: 0.0, y: self.bounds.height - 24.0, width: self.bounds.width, height: 24.0)
|
self.gradientNode.frame = CGRect(x: 0.0, y: self.bounds.height - 24.0, width: self.bounds.width, height: 24.0)
|
||||||
self.typeIconNode.frame = CGRect(x: 0.0, y: self.bounds.height - 20.0, width: 19.0, height: 19.0)
|
self.typeIconNode.frame = CGRect(x: 0.0, y: self.bounds.height - 20.0, width: 19.0, height: 19.0)
|
||||||
|
self.activateAreaNode.frame = self.bounds
|
||||||
|
|
||||||
if self.durationNode.supernode != nil {
|
if self.durationNode.supernode != nil {
|
||||||
let durationSize = self.durationNode.updateLayout(self.bounds.size)
|
let durationSize = self.durationNode.updateLayout(self.bounds.size)
|
||||||
|
@ -192,6 +192,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
|||||||
private let backgroundNode: NavigationBackgroundNode
|
private let backgroundNode: NavigationBackgroundNode
|
||||||
private let gridNode: GridNode
|
private let gridNode: GridNode
|
||||||
fileprivate var cameraView: TGAttachmentCameraView?
|
fileprivate var cameraView: TGAttachmentCameraView?
|
||||||
|
private var cameraActivateAreaNode: AccessibilityAreaNode
|
||||||
private var placeholderNode: MediaPickerPlaceholderNode?
|
private var placeholderNode: MediaPickerPlaceholderNode?
|
||||||
private var manageNode: MediaPickerManageNode?
|
private var manageNode: MediaPickerManageNode?
|
||||||
private var scrollingArea: SparseItemGridScrollingArea
|
private var scrollingArea: SparseItemGridScrollingArea
|
||||||
@ -233,6 +234,10 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
|||||||
self.gridNode = GridNode()
|
self.gridNode = GridNode()
|
||||||
self.scrollingArea = SparseItemGridScrollingArea()
|
self.scrollingArea = SparseItemGridScrollingArea()
|
||||||
|
|
||||||
|
self.cameraActivateAreaNode = AccessibilityAreaNode()
|
||||||
|
self.cameraActivateAreaNode.accessibilityLabel = "Camera"
|
||||||
|
self.cameraActivateAreaNode.accessibilityTraits = [.button]
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
if case .assets(nil) = controller.subject {
|
if case .assets(nil) = controller.subject {
|
||||||
@ -416,6 +421,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
|||||||
cameraView.startPreview()
|
cameraView.startPreview()
|
||||||
|
|
||||||
self.gridNode.scrollView.addSubview(cameraView)
|
self.gridNode.scrollView.addSubview(cameraView)
|
||||||
|
self.gridNode.addSubnode(self.cameraActivateAreaNode)
|
||||||
} else {
|
} else {
|
||||||
self.containerNode.clipsToBounds = true
|
self.containerNode.clipsToBounds = true
|
||||||
}
|
}
|
||||||
@ -1067,6 +1073,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
|||||||
if let cameraView = self.cameraView {
|
if let cameraView = self.cameraView {
|
||||||
if let cameraRect = cameraRect {
|
if let cameraRect = cameraRect {
|
||||||
transition.updateFrame(view: cameraView, frame: cameraRect)
|
transition.updateFrame(view: cameraView, frame: cameraRect)
|
||||||
|
self.cameraActivateAreaNode.frame = cameraRect
|
||||||
cameraView.isHidden = false
|
cameraView.isHidden = false
|
||||||
} else {
|
} else {
|
||||||
cameraView.isHidden = true
|
cameraView.isHidden = true
|
||||||
|
@ -110,7 +110,9 @@ class EmojiHeaderComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.statusView.isHidden = false
|
self.statusView.isHidden = false
|
||||||
|
if containerView.subviews.count > 1 && containerView.subviews[1].subviews.count > 1 {
|
||||||
containerView = containerView.subviews[1].subviews[1]
|
containerView = containerView.subviews[1].subviews[1]
|
||||||
|
}
|
||||||
|
|
||||||
let initialPosition = self.statusView.center
|
let initialPosition = self.statusView.center
|
||||||
let targetPosition = self.statusView.superview!.convert(self.statusView.center, to: containerView)
|
let targetPosition = self.statusView.superview!.convert(self.statusView.center, to: containerView)
|
||||||
|
@ -187,8 +187,10 @@ private class RecentSessionScreenNode: ViewControllerTracingNode, UIScrollViewDe
|
|||||||
private let acceptHeaderNode: ImmediateTextNode
|
private let acceptHeaderNode: ImmediateTextNode
|
||||||
private let secretChatsTitleNode: ImmediateTextNode
|
private let secretChatsTitleNode: ImmediateTextNode
|
||||||
private let secretChatsSwitchNode: SwitchNode
|
private let secretChatsSwitchNode: SwitchNode
|
||||||
|
private let secretChatsActivateAreaNode: AccessibilityAreaNode
|
||||||
private let incomingCallsTitleNode: ImmediateTextNode
|
private let incomingCallsTitleNode: ImmediateTextNode
|
||||||
private let incomingCallsSwitchNode: SwitchNode
|
private let incomingCallsSwitchNode: SwitchNode
|
||||||
|
private let incomingCallsActivateAreaNode: AccessibilityAreaNode
|
||||||
private let acceptSeparatorNode: ASDisplayNode
|
private let acceptSeparatorNode: ASDisplayNode
|
||||||
|
|
||||||
private let cancelButton: HighlightableButtonNode
|
private let cancelButton: HighlightableButtonNode
|
||||||
@ -264,8 +266,13 @@ private class RecentSessionScreenNode: ViewControllerTracingNode, UIScrollViewDe
|
|||||||
self.incomingCallsTitleNode = ImmediateTextNode()
|
self.incomingCallsTitleNode = ImmediateTextNode()
|
||||||
self.incomingCallsSwitchNode = SwitchNode()
|
self.incomingCallsSwitchNode = SwitchNode()
|
||||||
|
|
||||||
|
self.secretChatsActivateAreaNode = AccessibilityAreaNode()
|
||||||
|
self.incomingCallsActivateAreaNode = AccessibilityAreaNode()
|
||||||
|
|
||||||
self.cancelButton = HighlightableButtonNode()
|
self.cancelButton = HighlightableButtonNode()
|
||||||
self.cancelButton.setImage(closeButtonImage(theme: self.presentationData.theme), for: .normal)
|
self.cancelButton.setImage(closeButtonImage(theme: self.presentationData.theme), for: .normal)
|
||||||
|
self.cancelButton.accessibilityLabel = presentationData.strings.Common_Close
|
||||||
|
self.cancelButton.accessibilityTraits = [.button]
|
||||||
|
|
||||||
self.terminateButton = SolidRoundedButtonNode(theme: SolidRoundedButtonTheme(backgroundColor: self.presentationData.theme.list.itemBlocksBackgroundColor, foregroundColor: self.presentationData.theme.list.itemDestructiveColor), font: .regular, height: 44.0, cornerRadius: 11.0, gloss: false)
|
self.terminateButton = SolidRoundedButtonNode(theme: SolidRoundedButtonTheme(backgroundColor: self.presentationData.theme.list.itemBlocksBackgroundColor, foregroundColor: self.presentationData.theme.list.itemDestructiveColor), font: .regular, height: 44.0, cornerRadius: 11.0, gloss: false)
|
||||||
|
|
||||||
@ -341,6 +348,9 @@ private class RecentSessionScreenNode: ViewControllerTracingNode, UIScrollViewDe
|
|||||||
self.secretChatsSwitchNode.isOn = session.flags.contains(.acceptsSecretChats)
|
self.secretChatsSwitchNode.isOn = session.flags.contains(.acceptsSecretChats)
|
||||||
self.incomingCallsSwitchNode.isOn = session.flags.contains(.acceptsIncomingCalls)
|
self.incomingCallsSwitchNode.isOn = session.flags.contains(.acceptsIncomingCalls)
|
||||||
|
|
||||||
|
self.secretChatsActivateAreaNode.accessibilityValue = self.secretChatsSwitchNode.isOn ? presentationData.strings.VoiceOver_Common_On : presentationData.strings.VoiceOver_Common_Off
|
||||||
|
self.incomingCallsActivateAreaNode.accessibilityValue = self.incomingCallsSwitchNode.isOn ? presentationData.strings.VoiceOver_Common_On : presentationData.strings.VoiceOver_Common_Off
|
||||||
|
|
||||||
if !session.flags.contains(.passwordPending) && session.apiId != 22 {
|
if !session.flags.contains(.passwordPending) && session.apiId != 22 {
|
||||||
hasIncomingCalls = true
|
hasIncomingCalls = true
|
||||||
if ![2040, 2496].contains(session.apiId) {
|
if ![2040, 2496].contains(session.apiId) {
|
||||||
@ -385,24 +395,42 @@ private class RecentSessionScreenNode: ViewControllerTracingNode, UIScrollViewDe
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.regular(30.0), textColor: textColor)
|
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.regular(30.0), textColor: textColor)
|
||||||
|
self.titleNode.accessibilityLabel = title
|
||||||
|
self.titleNode.isAccessibilityElement = true
|
||||||
|
|
||||||
self.textNode.attributedText = NSAttributedString(string: subtitle, font: Font.regular(17.0), textColor: subtitleActive ? accentColor : secondaryTextColor)
|
self.textNode.attributedText = NSAttributedString(string: subtitle, font: Font.regular(17.0), textColor: subtitleActive ? accentColor : secondaryTextColor)
|
||||||
|
self.textNode.accessibilityLabel = subtitle
|
||||||
|
self.textNode.isAccessibilityElement = true
|
||||||
|
|
||||||
self.deviceTitleNode.attributedText = NSAttributedString(string: deviceTitle, font: Font.regular(17.0), textColor: textColor)
|
self.deviceTitleNode.attributedText = NSAttributedString(string: deviceTitle, font: Font.regular(17.0), textColor: textColor)
|
||||||
self.deviceValueNode.attributedText = NSAttributedString(string: device, font: Font.regular(17.0), textColor: secondaryTextColor)
|
self.deviceValueNode.attributedText = NSAttributedString(string: device, font: Font.regular(17.0), textColor: secondaryTextColor)
|
||||||
|
self.deviceValueNode.accessibilityLabel = deviceTitle
|
||||||
|
self.deviceValueNode.accessibilityValue = device
|
||||||
|
self.deviceValueNode.isAccessibilityElement = true
|
||||||
|
|
||||||
self.firstSeparatorNode = ASDisplayNode()
|
self.firstSeparatorNode = ASDisplayNode()
|
||||||
self.firstSeparatorNode.backgroundColor = self.presentationData.theme.list.itemBlocksSeparatorColor
|
self.firstSeparatorNode.backgroundColor = self.presentationData.theme.list.itemBlocksSeparatorColor
|
||||||
|
|
||||||
self.ipTitleNode.attributedText = NSAttributedString(string: self.presentationData.strings.AuthSessions_View_IP, font: Font.regular(17.0), textColor: textColor)
|
self.ipTitleNode.attributedText = NSAttributedString(string: self.presentationData.strings.AuthSessions_View_IP, font: Font.regular(17.0), textColor: textColor)
|
||||||
self.ipValueNode.attributedText = NSAttributedString(string: ip, font: Font.regular(17.0), textColor: secondaryTextColor)
|
self.ipValueNode.attributedText = NSAttributedString(string: ip, font: Font.regular(17.0), textColor: secondaryTextColor)
|
||||||
|
self.ipValueNode.accessibilityLabel = self.presentationData.strings.AuthSessions_View_IP
|
||||||
|
self.ipValueNode.accessibilityValue = ip
|
||||||
|
self.ipValueNode.isAccessibilityElement = true
|
||||||
|
|
||||||
self.secondSeparatorNode = ASDisplayNode()
|
self.secondSeparatorNode = ASDisplayNode()
|
||||||
self.secondSeparatorNode.backgroundColor = self.presentationData.theme.list.itemBlocksSeparatorColor
|
self.secondSeparatorNode.backgroundColor = self.presentationData.theme.list.itemBlocksSeparatorColor
|
||||||
|
|
||||||
self.locationTitleNode.attributedText = NSAttributedString(string: self.presentationData.strings.AuthSessions_View_Location, font: Font.regular(17.0), textColor: textColor)
|
self.locationTitleNode.attributedText = NSAttributedString(string: self.presentationData.strings.AuthSessions_View_Location, font: Font.regular(17.0), textColor: textColor)
|
||||||
|
|
||||||
self.locationValueNode.attributedText = NSAttributedString(string: location, font: Font.regular(17.0), textColor: secondaryTextColor)
|
self.locationValueNode.attributedText = NSAttributedString(string: location, font: Font.regular(17.0), textColor: secondaryTextColor)
|
||||||
|
self.locationValueNode.accessibilityLabel = self.presentationData.strings.AuthSessions_View_Location
|
||||||
|
self.locationValueNode.accessibilityValue = location
|
||||||
|
self.locationValueNode.isAccessibilityElement = true
|
||||||
|
|
||||||
self.locationInfoNode.attributedText = NSAttributedString(string: self.presentationData.strings.AuthSessions_View_LocationInfo, font: Font.regular(13.0), textColor: secondaryTextColor)
|
self.locationInfoNode.attributedText = NSAttributedString(string: self.presentationData.strings.AuthSessions_View_LocationInfo, font: Font.regular(13.0), textColor: secondaryTextColor)
|
||||||
self.locationInfoNode.maximumNumberOfLines = 4
|
self.locationInfoNode.maximumNumberOfLines = 4
|
||||||
|
self.locationInfoNode.accessibilityLabel = self.presentationData.strings.AuthSessions_View_LocationInfo
|
||||||
|
self.locationInfoNode.isAccessibilityElement = true
|
||||||
|
|
||||||
self.acceptBackgroundNode = ASDisplayNode()
|
self.acceptBackgroundNode = ASDisplayNode()
|
||||||
self.acceptBackgroundNode.clipsToBounds = true
|
self.acceptBackgroundNode.clipsToBounds = true
|
||||||
@ -410,9 +438,18 @@ private class RecentSessionScreenNode: ViewControllerTracingNode, UIScrollViewDe
|
|||||||
self.acceptBackgroundNode.backgroundColor = self.presentationData.theme.list.itemBlocksBackgroundColor
|
self.acceptBackgroundNode.backgroundColor = self.presentationData.theme.list.itemBlocksBackgroundColor
|
||||||
|
|
||||||
self.acceptHeaderNode.attributedText = NSAttributedString(string: self.presentationData.strings.AuthSessions_View_AcceptTitle.uppercased(), font: Font.regular(17.0), textColor: textColor)
|
self.acceptHeaderNode.attributedText = NSAttributedString(string: self.presentationData.strings.AuthSessions_View_AcceptTitle.uppercased(), font: Font.regular(17.0), textColor: textColor)
|
||||||
|
self.acceptHeaderNode.accessibilityLabel = self.presentationData.strings.AuthSessions_View_AcceptTitle
|
||||||
|
self.acceptHeaderNode.isAccessibilityElement = true
|
||||||
|
|
||||||
self.secretChatsTitleNode.attributedText = NSAttributedString(string: self.presentationData.strings.AuthSessions_View_AcceptSecretChats, font: Font.regular(17.0), textColor: textColor)
|
self.secretChatsTitleNode.attributedText = NSAttributedString(string: self.presentationData.strings.AuthSessions_View_AcceptSecretChats, font: Font.regular(17.0), textColor: textColor)
|
||||||
self.incomingCallsTitleNode.attributedText = NSAttributedString(string: self.presentationData.strings.AuthSessions_View_AcceptIncomingCalls, font: Font.regular(17.0), textColor: textColor)
|
self.incomingCallsTitleNode.attributedText = NSAttributedString(string: self.presentationData.strings.AuthSessions_View_AcceptIncomingCalls, font: Font.regular(17.0), textColor: textColor)
|
||||||
|
|
||||||
|
self.secretChatsActivateAreaNode.accessibilityLabel = self.presentationData.strings.AuthSessions_View_AcceptSecretChats
|
||||||
|
self.secretChatsActivateAreaNode.accessibilityHint = self.presentationData.strings.VoiceOver_Common_SwitchHint
|
||||||
|
|
||||||
|
self.incomingCallsActivateAreaNode.accessibilityLabel = self.presentationData.strings.AuthSessions_View_AcceptIncomingCalls
|
||||||
|
self.incomingCallsActivateAreaNode.accessibilityHint = self.presentationData.strings.VoiceOver_Common_SwitchHint
|
||||||
|
|
||||||
self.acceptSeparatorNode = ASDisplayNode()
|
self.acceptSeparatorNode = ASDisplayNode()
|
||||||
self.acceptSeparatorNode.backgroundColor = self.presentationData.theme.list.itemBlocksSeparatorColor
|
self.acceptSeparatorNode.backgroundColor = self.presentationData.theme.list.itemBlocksSeparatorColor
|
||||||
|
|
||||||
@ -463,23 +500,49 @@ private class RecentSessionScreenNode: ViewControllerTracingNode, UIScrollViewDe
|
|||||||
if hasSecretChats {
|
if hasSecretChats {
|
||||||
self.contentContainerNode.addSubnode(self.secretChatsTitleNode)
|
self.contentContainerNode.addSubnode(self.secretChatsTitleNode)
|
||||||
self.contentContainerNode.addSubnode(self.secretChatsSwitchNode)
|
self.contentContainerNode.addSubnode(self.secretChatsSwitchNode)
|
||||||
|
self.contentContainerNode.addSubnode(self.secretChatsActivateAreaNode)
|
||||||
|
|
||||||
self.secretChatsSwitchNode.valueUpdated = { [weak self] value in
|
self.secretChatsSwitchNode.valueUpdated = { [weak self] value in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.updateAcceptSecretChats?(value)
|
strongSelf.updateAcceptSecretChats?(value)
|
||||||
|
|
||||||
|
strongSelf.secretChatsActivateAreaNode.accessibilityValue = value ? presentationData.strings.VoiceOver_Common_On : presentationData.strings.VoiceOver_Common_Off
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.secretChatsActivateAreaNode.activate = { [weak self] in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
let value = !strongSelf.secretChatsSwitchNode.isOn
|
||||||
|
strongSelf.updateAcceptSecretChats?(value)
|
||||||
|
strongSelf.secretChatsActivateAreaNode.accessibilityValue = value ? presentationData.strings.VoiceOver_Common_On : presentationData.strings.VoiceOver_Common_Off
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
self.contentContainerNode.addSubnode(self.acceptSeparatorNode)
|
self.contentContainerNode.addSubnode(self.acceptSeparatorNode)
|
||||||
}
|
}
|
||||||
self.contentContainerNode.addSubnode(self.incomingCallsTitleNode)
|
self.contentContainerNode.addSubnode(self.incomingCallsTitleNode)
|
||||||
self.contentContainerNode.addSubnode(self.incomingCallsSwitchNode)
|
self.contentContainerNode.addSubnode(self.incomingCallsSwitchNode)
|
||||||
|
self.contentContainerNode.addSubnode(self.incomingCallsActivateAreaNode)
|
||||||
|
|
||||||
self.incomingCallsSwitchNode.valueUpdated = { [weak self] value in
|
self.incomingCallsSwitchNode.valueUpdated = { [weak self] value in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.updateAcceptIncomingCalls?(value)
|
strongSelf.updateAcceptIncomingCalls?(value)
|
||||||
|
|
||||||
|
strongSelf.incomingCallsActivateAreaNode.accessibilityValue = value ? presentationData.strings.VoiceOver_Common_On : presentationData.strings.VoiceOver_Common_Off
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.incomingCallsActivateAreaNode.activate = { [weak self] in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
let value = !strongSelf.incomingCallsSwitchNode.isOn
|
||||||
|
strongSelf.updateAcceptIncomingCalls?(value)
|
||||||
|
strongSelf.incomingCallsActivateAreaNode.accessibilityValue = value ? presentationData.strings.VoiceOver_Common_On : presentationData.strings.VoiceOver_Common_Off
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.cancelButton.addTarget(self, action: #selector(self.cancelButtonPressed), forControlEvents: .touchUpInside)
|
self.cancelButton.addTarget(self, action: #selector(self.cancelButtonPressed), forControlEvents: .touchUpInside)
|
||||||
@ -800,6 +863,7 @@ private class RecentSessionScreenNode: ViewControllerTracingNode, UIScrollViewDe
|
|||||||
let switchSize = switchView.bounds.size
|
let switchSize = switchView.bounds.size
|
||||||
|
|
||||||
self.secretChatsSwitchNode.frame = CGRect(origin: CGPoint(x: fieldFrame.maxX - switchSize.width - inset, y: secretFrame.minY + floorToScreenPixels((fieldItemHeight - switchSize.height) / 2.0)), size: switchSize)
|
self.secretChatsSwitchNode.frame = CGRect(origin: CGPoint(x: fieldFrame.maxX - switchSize.width - inset, y: secretFrame.minY + floorToScreenPixels((fieldItemHeight - switchSize.height) / 2.0)), size: switchSize)
|
||||||
|
self.secretChatsActivateAreaNode.frame = CGRect(origin: CGPoint(x: secretFrame.minX, y: secretFrame.minY), size: CGSize(width: fieldFrame.width, height: fieldItemHeight))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -816,6 +880,7 @@ private class RecentSessionScreenNode: ViewControllerTracingNode, UIScrollViewDe
|
|||||||
let switchSize = switchView.bounds.size
|
let switchSize = switchView.bounds.size
|
||||||
|
|
||||||
self.incomingCallsSwitchNode.frame = CGRect(origin: CGPoint(x: fieldFrame.maxX - switchSize.width - inset, y: secretFrame.maxY - fieldItemHeight + floorToScreenPixels((fieldItemHeight - switchSize.height) / 2.0)), size: switchSize)
|
self.incomingCallsSwitchNode.frame = CGRect(origin: CGPoint(x: fieldFrame.maxX - switchSize.width - inset, y: secretFrame.maxY - fieldItemHeight + floorToScreenPixels((fieldItemHeight - switchSize.height) / 2.0)), size: switchSize)
|
||||||
|
self.incomingCallsActivateAreaNode.frame = CGRect(origin: CGPoint(x: secretFrame.minX, y: secretFrame.maxY - fieldItemHeight), size: CGSize(width: fieldFrame.width, height: fieldItemHeight))
|
||||||
}
|
}
|
||||||
|
|
||||||
if let _ = self.acceptBackgroundNode.supernode {
|
if let _ = self.acceptBackgroundNode.supernode {
|
||||||
@ -833,8 +898,10 @@ private class RecentSessionScreenNode: ViewControllerTracingNode, UIScrollViewDe
|
|||||||
if isCurrent {
|
if isCurrent {
|
||||||
contentHeight -= 68.0
|
contentHeight -= 68.0
|
||||||
self.terminateButton.isHidden = true
|
self.terminateButton.isHidden = true
|
||||||
|
self.terminateButton.isAccessibilityElement = false
|
||||||
} else {
|
} else {
|
||||||
self.terminateButton.isHidden = false
|
self.terminateButton.isHidden = false
|
||||||
|
self.terminateButton.isAccessibilityElement = true
|
||||||
}
|
}
|
||||||
|
|
||||||
let sideInset = floor((layout.size.width - width) / 2.0)
|
let sideInset = floor((layout.size.width - width) / 2.0)
|
||||||
|
@ -7202,8 +7202,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
|
|
||||||
mappedTransition = (ChatHistoryListViewTransition(historyView: transition.historyView, deleteItems: deleteItems, insertItems: insertItems, updateItems: transition.updateItems, options: options, scrollToItem: scrollToItem, stationaryItemRange: stationaryItemRange, initialData: transition.initialData, keyboardButtonsMessage: transition.keyboardButtonsMessage, cachedData: transition.cachedData, cachedDataMessages: transition.cachedDataMessages, readStateData: transition.readStateData, scrolledToIndex: transition.scrolledToIndex, scrolledToSomeIndex: transition.scrolledToSomeIndex, peerType: transition.peerType, networkType: transition.networkType, animateIn: false, reason: transition.reason, flashIndicators: transition.flashIndicators), updateSizeAndInsets)
|
mappedTransition = (ChatHistoryListViewTransition(historyView: transition.historyView, deleteItems: deleteItems, insertItems: insertItems, updateItems: transition.updateItems, options: options, scrollToItem: scrollToItem, stationaryItemRange: stationaryItemRange, initialData: transition.initialData, keyboardButtonsMessage: transition.keyboardButtonsMessage, cachedData: transition.cachedData, cachedDataMessages: transition.cachedDataMessages, readStateData: transition.readStateData, scrolledToIndex: transition.scrolledToIndex, scrolledToSomeIndex: transition.scrolledToSomeIndex, peerType: transition.peerType, networkType: transition.networkType, animateIn: false, reason: transition.reason, flashIndicators: transition.flashIndicators), updateSizeAndInsets)
|
||||||
}, updateExtraNavigationBarBackgroundHeight: { value, _ in
|
}, updateExtraNavigationBarBackgroundHeight: { value, hitTestSlop, _ in
|
||||||
strongSelf.additionalNavigationBarBackgroundHeight = value
|
strongSelf.additionalNavigationBarBackgroundHeight = value
|
||||||
|
strongSelf.additionalNavigationBarHitTestSlop = hitTestSlop
|
||||||
})
|
})
|
||||||
|
|
||||||
if let mappedTransition = mappedTransition {
|
if let mappedTransition = mappedTransition {
|
||||||
@ -11063,6 +11064,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
private var suspendNavigationBarLayout: Bool = false
|
private var suspendNavigationBarLayout: Bool = false
|
||||||
private var suspendedNavigationBarLayout: ContainerViewLayout?
|
private var suspendedNavigationBarLayout: ContainerViewLayout?
|
||||||
private var additionalNavigationBarBackgroundHeight: CGFloat = 0.0
|
private var additionalNavigationBarBackgroundHeight: CGFloat = 0.0
|
||||||
|
private var additionalNavigationBarHitTestSlop: CGFloat = 0.0
|
||||||
|
|
||||||
override public func updateNavigationBarLayout(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
override public func updateNavigationBarLayout(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||||
if self.suspendNavigationBarLayout {
|
if self.suspendNavigationBarLayout {
|
||||||
@ -11099,9 +11101,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
var navigationBarTransition = transition
|
var navigationBarTransition = transition
|
||||||
self.chatDisplayNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition, listViewTransaction: { updateSizeAndInsets, additionalScrollDistance, scrollToTop, completion in
|
self.chatDisplayNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition, listViewTransaction: { updateSizeAndInsets, additionalScrollDistance, scrollToTop, completion in
|
||||||
self.chatDisplayNode.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: additionalScrollDistance, scrollToTop: scrollToTop, completion: completion)
|
self.chatDisplayNode.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: additionalScrollDistance, scrollToTop: scrollToTop, completion: completion)
|
||||||
}, updateExtraNavigationBarBackgroundHeight: { value, extraNavigationTransition in
|
}, updateExtraNavigationBarBackgroundHeight: { value, hitTestSlop, extraNavigationTransition in
|
||||||
navigationBarTransition = extraNavigationTransition
|
navigationBarTransition = extraNavigationTransition
|
||||||
self.additionalNavigationBarBackgroundHeight = value
|
self.additionalNavigationBarBackgroundHeight = value
|
||||||
|
self.additionalNavigationBarHitTestSlop = hitTestSlop
|
||||||
})
|
})
|
||||||
|
|
||||||
if case .compact = layout.metrics.widthClass {
|
if case .compact = layout.metrics.widthClass {
|
||||||
@ -11122,6 +11125,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
self.suspendedNavigationBarLayout = suspendedNavigationBarLayout
|
self.suspendedNavigationBarLayout = suspendedNavigationBarLayout
|
||||||
self.applyNavigationBarLayout(suspendedNavigationBarLayout, navigationLayout: self.navigationLayout(layout: layout), additionalBackgroundHeight: self.additionalNavigationBarBackgroundHeight, transition: navigationBarTransition)
|
self.applyNavigationBarLayout(suspendedNavigationBarLayout, navigationLayout: self.navigationLayout(layout: layout), additionalBackgroundHeight: self.additionalNavigationBarBackgroundHeight, transition: navigationBarTransition)
|
||||||
}
|
}
|
||||||
|
self.navigationBar?.additionalContentNode.hitTestSlop = UIEdgeInsets(top: 0.0, left: 0.0, bottom: self.additionalNavigationBarHitTestSlop, right: 0.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateChatPresentationInterfaceState(animated: Bool = true, interactive: Bool, saveInterfaceState: Bool = false, _ f: (ChatPresentationInterfaceState) -> ChatPresentationInterfaceState, completion: @escaping (ContainedViewLayoutTransition) -> Void = { _ in }) {
|
func updateChatPresentationInterfaceState(animated: Bool = true, interactive: Bool, saveInterfaceState: Bool = false, _ f: (ChatPresentationInterfaceState) -> ChatPresentationInterfaceState, completion: @escaping (ContainedViewLayoutTransition) -> Void = { _ in }) {
|
||||||
@ -16702,100 +16706,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
self.present(actionSheet, in: .window(.root))
|
self.present(actionSheet, in: .window(.root))
|
||||||
}
|
}
|
||||||
|
|
||||||
func avatarPreviewingController(from sourceView: UIView) -> (UIViewController, CGRect)? {
|
|
||||||
guard let layout = self.validLayout else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
guard let buttonView = (self.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.avatarNode.view else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if let peer = self.presentationInterfaceState.renderedPeer?.chatMainPeer, peer.smallProfileImage != nil {
|
|
||||||
let galleryController = AvatarGalleryController(context: self.context, peer: peer, remoteEntries: nil, replaceRootController: { controller, ready in
|
|
||||||
}, synchronousLoad: true)
|
|
||||||
galleryController.setHintWillBePresentedInPreviewingContext(true)
|
|
||||||
galleryController.containerLayoutUpdated(ContainerViewLayout(size: CGSize(width: self.view.bounds.size.width, height: self.view.bounds.size.height), metrics: LayoutMetrics(), deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), additionalInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate)
|
|
||||||
return (galleryController, buttonView.convert(buttonView.bounds, to: sourceView))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func previewingController(from sourceView: UIView, for location: CGPoint) -> (UIViewController, CGRect)? {
|
|
||||||
guard let layout = self.validLayout, case .phone = layout.deviceMetrics.type, let view = self.chatDisplayNode.view.hitTest(location, with: nil), view.isDescendant(of: self.chatDisplayNode.historyNode.view) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
let historyPoint = sourceView.convert(location, to: self.chatDisplayNode.historyNode.view)
|
|
||||||
var result: (Message, ChatMessagePeekPreviewContent)?
|
|
||||||
self.chatDisplayNode.historyNode.forEachItemNode { itemNode in
|
|
||||||
if let itemNode = itemNode as? ChatMessageItemView {
|
|
||||||
if itemNode.frame.contains(historyPoint) {
|
|
||||||
if let value = itemNode.peekPreviewContent(at: self.chatDisplayNode.historyNode.view.convert(historyPoint, to: itemNode.view)) {
|
|
||||||
result = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let (message, content) = result {
|
|
||||||
switch content {
|
|
||||||
case let .media(media):
|
|
||||||
var selectedTransitionNode: (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?
|
|
||||||
self.chatDisplayNode.historyNode.forEachItemNode { itemNode in
|
|
||||||
if let itemNode = itemNode as? ChatMessageItemView {
|
|
||||||
if let result = itemNode.transitionNode(id: message.id, media: media) {
|
|
||||||
selectedTransitionNode = result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let selectedTransitionNode = selectedTransitionNode {
|
|
||||||
if let previewData = chatMessagePreviewControllerData(context: self.context, chatLocation: self.chatLocation, chatLocationContextHolder: self.chatLocationContextHolder, message: message, standalone: false, reverseMessageGalleryOrder: false, navigationController: self.effectiveNavigationController) {
|
|
||||||
switch previewData {
|
|
||||||
case let .gallery(gallery):
|
|
||||||
gallery.setHintWillBePresentedInPreviewingContext(true)
|
|
||||||
let rect = selectedTransitionNode.0.view.convert(selectedTransitionNode.0.bounds, to: sourceView)
|
|
||||||
let sourceRect = rect.insetBy(dx: -2.0, dy: -2.0)
|
|
||||||
gallery.containerLayoutUpdated(ContainerViewLayout(size: CGSize(width: self.view.bounds.size.width, height: self.view.bounds.size.height), metrics: LayoutMetrics(), deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), additionalInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate)
|
|
||||||
return (gallery, sourceRect)
|
|
||||||
case .instantPage:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case let .url(node, rect, string, concealed):
|
|
||||||
var parsedUrlValue: URL?
|
|
||||||
if let parsed = URL(string: string) {
|
|
||||||
parsedUrlValue = parsed
|
|
||||||
} else if let encoded = (string as NSString).addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed), let parsed = URL(string: encoded) {
|
|
||||||
parsedUrlValue = parsed
|
|
||||||
}
|
|
||||||
|
|
||||||
if let parsedUrlValue = parsedUrlValue {
|
|
||||||
if concealed, (parsedUrlValue.scheme == "http" || parsedUrlValue.scheme == "https"), !isConcealedUrlWhitelisted(parsedUrlValue) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
let targetRect = node.view.convert(rect, to: sourceView)
|
|
||||||
let sourceRect = CGRect(origin: CGPoint(x: floor(targetRect.midX), y: floor(targetRect.midY)), size: CGSize(width: 1.0, height: 1.0))
|
|
||||||
if let parsedUrl = parsedUrlValue {
|
|
||||||
if parsedUrl.scheme == "http" || parsedUrl.scheme == "https" {
|
|
||||||
if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
|
|
||||||
let controller = SFSafariViewController(url: parsedUrl)
|
|
||||||
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
|
|
||||||
controller.preferredBarTintColor = self.presentationData.theme.rootController.navigationBar.opaqueBackgroundColor
|
|
||||||
controller.preferredControlTintColor = self.presentationData.theme.rootController.navigationBar.accentTextColor
|
|
||||||
}
|
|
||||||
return (controller, sourceRect)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
private func presentBanMessageOptions(accountPeerId: PeerId, author: Peer, messageIds: Set<MessageId>, options: ChatAvailableMessageActionOptions) {
|
private func presentBanMessageOptions(accountPeerId: PeerId, author: Peer, messageIds: Set<MessageId>, options: ChatAvailableMessageActionOptions) {
|
||||||
guard let peerId = self.chatLocation.peerId else {
|
guard let peerId = self.chatLocation.peerId else {
|
||||||
return
|
return
|
||||||
|
@ -259,7 +259,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
|
|
||||||
if let (layout, navigationHeight) = self.validLayout {
|
if let (layout, navigationHeight) = self.validLayout {
|
||||||
self.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, transition: .immediate, listViewTransaction: { _, _, _, _ in
|
self.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, transition: .immediate, listViewTransaction: { _, _, _, _ in
|
||||||
}, updateExtraNavigationBarBackgroundHeight: { _, _ in
|
}, updateExtraNavigationBarBackgroundHeight: { _, _, _ in
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -829,7 +829,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition protoTransition: ContainedViewLayoutTransition, listViewTransaction: (ListViewUpdateSizeAndInsets, CGFloat, Bool, @escaping () -> Void) -> Void, updateExtraNavigationBarBackgroundHeight: (CGFloat, ContainedViewLayoutTransition) -> Void) {
|
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition protoTransition: ContainedViewLayoutTransition, listViewTransaction: (ListViewUpdateSizeAndInsets, CGFloat, Bool, @escaping () -> Void) -> Void, updateExtraNavigationBarBackgroundHeight: (CGFloat, CGFloat, ContainedViewLayoutTransition) -> Void) {
|
||||||
let transition: ContainedViewLayoutTransition
|
let transition: ContainedViewLayoutTransition
|
||||||
if let _ = self.scheduledAnimateInAsOverlayFromNode {
|
if let _ = self.scheduledAnimateInAsOverlayFromNode {
|
||||||
transition = .immediate
|
transition = .immediate
|
||||||
@ -1068,6 +1068,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
var immediatelyLayoutTitleAccessoryPanelNodeAndAnimateAppearance = false
|
var immediatelyLayoutTitleAccessoryPanelNodeAndAnimateAppearance = false
|
||||||
var titleAccessoryPanelHeight: CGFloat?
|
var titleAccessoryPanelHeight: CGFloat?
|
||||||
var titleAccessoryPanelBackgroundHeight: CGFloat?
|
var titleAccessoryPanelBackgroundHeight: CGFloat?
|
||||||
|
var titleAccessoryPanelHitTestSlop: CGFloat?
|
||||||
var extraTransition = transition
|
var extraTransition = transition
|
||||||
if let titleAccessoryPanelNode = titlePanelForChatPresentationInterfaceState(self.chatPresentationInterfaceState, context: self.context, currentPanel: self.titleAccessoryPanelNode, controllerInteraction: self.controllerInteraction, interfaceInteraction: self.interfaceInteraction) {
|
if let titleAccessoryPanelNode = titlePanelForChatPresentationInterfaceState(self.chatPresentationInterfaceState, context: self.context, currentPanel: self.titleAccessoryPanelNode, controllerInteraction: self.controllerInteraction, interfaceInteraction: self.interfaceInteraction) {
|
||||||
if self.titleAccessoryPanelNode != titleAccessoryPanelNode {
|
if self.titleAccessoryPanelNode != titleAccessoryPanelNode {
|
||||||
@ -1085,6 +1086,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
let layoutResult = titleAccessoryPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, transition: immediatelyLayoutTitleAccessoryPanelNodeAndAnimateAppearance ? .immediate : transition, interfaceState: self.chatPresentationInterfaceState)
|
let layoutResult = titleAccessoryPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, transition: immediatelyLayoutTitleAccessoryPanelNodeAndAnimateAppearance ? .immediate : transition, interfaceState: self.chatPresentationInterfaceState)
|
||||||
titleAccessoryPanelHeight = layoutResult.insetHeight
|
titleAccessoryPanelHeight = layoutResult.insetHeight
|
||||||
titleAccessoryPanelBackgroundHeight = layoutResult.backgroundHeight
|
titleAccessoryPanelBackgroundHeight = layoutResult.backgroundHeight
|
||||||
|
titleAccessoryPanelHitTestSlop = layoutResult.hitTestSlop
|
||||||
if immediatelyLayoutTitleAccessoryPanelNodeAndAnimateAppearance {
|
if immediatelyLayoutTitleAccessoryPanelNodeAndAnimateAppearance {
|
||||||
titleAccessoryPanelNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
titleAccessoryPanelNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||||
titleAccessoryPanelNode.subnodeTransform = CATransform3DMakeTranslation(0.0, -layoutResult.backgroundHeight, 0.0)
|
titleAccessoryPanelNode.subnodeTransform = CATransform3DMakeTranslation(0.0, -layoutResult.backgroundHeight, 0.0)
|
||||||
@ -1460,11 +1462,13 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
transition.updateFrame(node: self.inputContextOverTextPanelContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: layout.size.height)))
|
transition.updateFrame(node: self.inputContextOverTextPanelContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: layout.size.height)))
|
||||||
|
|
||||||
var extraNavigationBarHeight: CGFloat = 0.0
|
var extraNavigationBarHeight: CGFloat = 0.0
|
||||||
|
var extraNavigationBarHitTestSlop: CGFloat = 0.0
|
||||||
var titleAccessoryPanelFrame: CGRect?
|
var titleAccessoryPanelFrame: CGRect?
|
||||||
if let _ = self.titleAccessoryPanelNode, let panelHeight = titleAccessoryPanelHeight {
|
if let _ = self.titleAccessoryPanelNode, let panelHeight = titleAccessoryPanelHeight {
|
||||||
titleAccessoryPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: panelHeight))
|
titleAccessoryPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: panelHeight))
|
||||||
insets.top += panelHeight
|
insets.top += panelHeight
|
||||||
extraNavigationBarHeight += titleAccessoryPanelBackgroundHeight ?? 0.0
|
extraNavigationBarHeight += titleAccessoryPanelBackgroundHeight ?? 0.0
|
||||||
|
extraNavigationBarHitTestSlop = titleAccessoryPanelHitTestSlop ?? 0.0
|
||||||
}
|
}
|
||||||
|
|
||||||
var translationPanelFrame: CGRect?
|
var translationPanelFrame: CGRect?
|
||||||
@ -1474,7 +1478,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
extraNavigationBarHeight += panelHeight
|
extraNavigationBarHeight += panelHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
updateExtraNavigationBarBackgroundHeight(extraNavigationBarHeight, extraTransition)
|
updateExtraNavigationBarBackgroundHeight(extraNavigationBarHeight, extraNavigationBarHitTestSlop, extraTransition)
|
||||||
|
|
||||||
var importStatusPanelFrame: CGRect?
|
var importStatusPanelFrame: CGRect?
|
||||||
if let _ = self.chatImportStatusPanel, let panelHeight = importStatusPanelHeight {
|
if let _ = self.chatImportStatusPanel, let panelHeight = importStatusPanelHeight {
|
||||||
@ -3029,7 +3033,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
if let (layout, navigationHeight) = self.validLayout {
|
if let (layout, navigationHeight) = self.validLayout {
|
||||||
self.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, transition: transition, listViewTransaction: { updateSizeAndInsets, additionalScrollDistance, scrollToTop, completion in
|
self.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, transition: transition, listViewTransaction: { updateSizeAndInsets, additionalScrollDistance, scrollToTop, completion in
|
||||||
self.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: additionalScrollDistance, scrollToTop: scrollToTop, completion: completion)
|
self.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: additionalScrollDistance, scrollToTop: scrollToTop, completion: completion)
|
||||||
}, updateExtraNavigationBarBackgroundHeight: { _, _ in
|
}, updateExtraNavigationBarBackgroundHeight: { _, _, _ in
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1241,12 +1241,17 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||||||
|
|
||||||
var translateToLanguage: String?
|
var translateToLanguage: String?
|
||||||
if let translationState, isPremium && translationState.isEnabled {
|
if let translationState, isPremium && translationState.isEnabled {
|
||||||
translateToLanguage = translationState.toLang ?? presentationData.strings.baseLanguageCode
|
var languageCode = translationState.toLang ?? presentationData.strings.baseLanguageCode
|
||||||
if translateToLanguage == "nb" {
|
let rawSuffix = "-raw"
|
||||||
translateToLanguage = "no"
|
if languageCode.hasSuffix(rawSuffix) {
|
||||||
} else if translateToLanguage == "pt-br" {
|
languageCode = String(languageCode.dropLast(rawSuffix.count))
|
||||||
translateToLanguage = "pt"
|
|
||||||
}
|
}
|
||||||
|
if languageCode == "nb" {
|
||||||
|
languageCode = "no"
|
||||||
|
} else if languageCode == "pt-br" {
|
||||||
|
languageCode = "pt"
|
||||||
|
}
|
||||||
|
translateToLanguage = languageCode
|
||||||
}
|
}
|
||||||
|
|
||||||
let associatedData = extractAssociatedData(chatLocation: chatLocation, view: view, automaticDownloadNetworkType: networkType, animatedEmojiStickers: animatedEmojiStickers, additionalAnimatedEmojiStickers: additionalAnimatedEmojiStickers, subject: subject, currentlyPlayingMessageId: currentlyPlayingMessageIdAndType?.0, isCopyProtectionEnabled: isCopyProtectionEnabled, availableReactions: availableReactions, defaultReaction: defaultReaction, isPremium: isPremium, alwaysDisplayTranscribeButton: alwaysDisplayTranscribeButton, accountPeer: accountPeer, topicAuthorId: topicAuthorId, hasBots: chatHasBots, translateToLanguage: translateToLanguage)
|
let associatedData = extractAssociatedData(chatLocation: chatLocation, view: view, automaticDownloadNetworkType: networkType, animatedEmojiStickers: animatedEmojiStickers, additionalAnimatedEmojiStickers: additionalAnimatedEmojiStickers, subject: subject, currentlyPlayingMessageId: currentlyPlayingMessageIdAndType?.0, isCopyProtectionEnabled: isCopyProtectionEnabled, availableReactions: availableReactions, defaultReaction: defaultReaction, isPremium: isPremium, alwaysDisplayTranscribeButton: alwaysDisplayTranscribeButton, accountPeer: accountPeer, topicAuthorId: topicAuthorId, hasBots: chatHasBots, translateToLanguage: translateToLanguage)
|
||||||
|
@ -1062,15 +1062,21 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !messageText.isEmpty || resourceAvailable || diceEmoji != nil {
|
|
||||||
let isCopyProtected = chatPresentationInterfaceState.copyProtectionEnabled || message.isCopyProtected()
|
|
||||||
let message = messages[0]
|
let message = messages[0]
|
||||||
var isExpired = false
|
var isExpired = false
|
||||||
|
var isImage = false
|
||||||
for media in message.media {
|
for media in message.media {
|
||||||
if let _ = media as? TelegramMediaExpiredContent {
|
if let _ = media as? TelegramMediaExpiredContent {
|
||||||
isExpired = true
|
isExpired = true
|
||||||
}
|
}
|
||||||
|
if media is TelegramMediaImage {
|
||||||
|
isImage = true
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !messageText.isEmpty || (resourceAvailable && isImage) || diceEmoji != nil {
|
||||||
|
let isCopyProtected = chatPresentationInterfaceState.copyProtectionEnabled || message.isCopyProtected()
|
||||||
|
|
||||||
if !isExpired {
|
if !isExpired {
|
||||||
if !isPoll {
|
if !isPoll {
|
||||||
if !isCopyProtected {
|
if !isCopyProtected {
|
||||||
|
@ -52,6 +52,9 @@ func titlePanelForChatPresentationInterfaceState(_ chatPresentationInterfaceStat
|
|||||||
if inhibitTitlePanelDisplay, let selectedContextValue = selectedContext {
|
if inhibitTitlePanelDisplay, let selectedContextValue = selectedContext {
|
||||||
switch selectedContextValue {
|
switch selectedContextValue {
|
||||||
case .pinnedMessage:
|
case .pinnedMessage:
|
||||||
|
if case .peer = chatPresentationInterfaceState.chatLocation {
|
||||||
|
selectedContext = nil
|
||||||
|
}
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
selectedContext = nil
|
selectedContext = nil
|
||||||
|
@ -208,7 +208,7 @@ final class ChatInviteRequestsTitlePanelNode: ChatTitleAccessoryPanelNode {
|
|||||||
self.activateAreaNode.frame = CGRect(origin: .zero, size: CGSize(width: width, height: panelHeight))
|
self.activateAreaNode.frame = CGRect(origin: .zero, size: CGSize(width: width, height: panelHeight))
|
||||||
self.activateAreaNode.accessibilityLabel = interfaceState.strings.Conversation_RequestsToJoin(self.count)
|
self.activateAreaNode.accessibilityLabel = interfaceState.strings.Conversation_RequestsToJoin(self.count)
|
||||||
|
|
||||||
return LayoutResult(backgroundHeight: initialPanelHeight, insetHeight: panelHeight)
|
return LayoutResult(backgroundHeight: initialPanelHeight, insetHeight: panelHeight, hitTestSlop: 0.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func buttonPressed() {
|
@objc func buttonPressed() {
|
||||||
|
@ -232,6 +232,9 @@ final class ChatMessageAccessibilityData {
|
|||||||
}
|
}
|
||||||
} else if let file = media as? TelegramMediaFile {
|
} else if let file = media as? TelegramMediaFile {
|
||||||
var isSpecialFile = false
|
var isSpecialFile = false
|
||||||
|
|
||||||
|
let isVideo = file.isInstantVideo
|
||||||
|
|
||||||
for attribute in file.attributes {
|
for attribute in file.attributes {
|
||||||
switch attribute {
|
switch attribute {
|
||||||
case let .Sticker(displayText, _, _):
|
case let .Sticker(displayText, _, _):
|
||||||
@ -259,6 +262,9 @@ final class ChatMessageAccessibilityData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case let .Audio(isVoice, duration, title, performer, _):
|
case let .Audio(isVoice, duration, title, performer, _):
|
||||||
|
if isVideo {
|
||||||
|
continue
|
||||||
|
}
|
||||||
isSpecialFile = true
|
isSpecialFile = true
|
||||||
if isSelected == nil {
|
if isSelected == nil {
|
||||||
hint = item.presentationData.strings.VoiceOver_Chat_PlayHint
|
hint = item.presentationData.strings.VoiceOver_Chat_PlayHint
|
||||||
@ -573,6 +579,7 @@ final class ChatMessageAccessibilityData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (label, value) = dataForMessage(item.message, false)
|
var (label, value) = dataForMessage(item.message, false)
|
||||||
|
var replyValue: String?
|
||||||
|
|
||||||
for attribute in item.message.attributes {
|
for attribute in item.message.attributes {
|
||||||
if let attribute = attribute as? TextEntitiesMessageAttribute {
|
if let attribute = attribute as? TextEntitiesMessageAttribute {
|
||||||
@ -613,8 +620,8 @@ final class ChatMessageAccessibilityData {
|
|||||||
replyLabel = item.presentationData.strings.VoiceOver_Chat_ReplyToYourMessage
|
replyLabel = item.presentationData.strings.VoiceOver_Chat_ReplyToYourMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
// let (replyMessageLabel, replyMessageValue) = dataForMessage(replyMessage, true)
|
let (_, replyMessageValue) = dataForMessage(replyMessage, true)
|
||||||
// replyLabel += "\(replyLabel): \(replyMessageLabel), \(replyMessageValue)"
|
replyValue = replyMessageValue
|
||||||
|
|
||||||
label = "\(replyLabel) . \(label)"
|
label = "\(replyLabel) . \(label)"
|
||||||
}
|
}
|
||||||
@ -666,6 +673,10 @@ final class ChatMessageAccessibilityData {
|
|||||||
customActions.append(ChatMessageAccessibilityCustomAction(name: item.presentationData.strings.VoiceOver_MessageContextOpenMessageMenu, target: nil, selector: #selector(self.noop), action: .options))
|
customActions.append(ChatMessageAccessibilityCustomAction(name: item.presentationData.strings.VoiceOver_MessageContextOpenMessageMenu, target: nil, selector: #selector(self.noop), action: .options))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let replyValue {
|
||||||
|
value = "\(value). \(item.presentationData.strings.VoiceOver_Chat_ReplyingToMessage(replyValue).string)"
|
||||||
|
}
|
||||||
|
|
||||||
self.label = label
|
self.label = label
|
||||||
self.value = value
|
self.value = value
|
||||||
self.hint = hint
|
self.hint = hint
|
||||||
|
@ -522,7 +522,7 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
|||||||
}
|
}
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
return LayoutResult(backgroundHeight: panelHeight, insetHeight: panelHeight)
|
return LayoutResult(backgroundHeight: panelHeight, insetHeight: panelHeight, hitTestSlop: 0.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func enqueueTransition(width: CGFloat, panelHeight: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, animation: PinnedMessageAnimation?, pinnedMessage: ChatPinnedMessage, theme: PresentationTheme, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, accountPeerId: PeerId, firstTime: Bool, isReplyThread: Bool, translateToLanguage: String?) {
|
private func enqueueTransition(width: CGFloat, panelHeight: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, animation: PinnedMessageAnimation?, pinnedMessage: ChatPinnedMessage, theme: PresentationTheme, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, accountPeerId: PeerId, firstTime: Bool, isReplyThread: Bool, translateToLanguage: String?) {
|
||||||
|
@ -168,6 +168,14 @@ private final class ChatInfoTitlePanelInviteInfoNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||||
|
let result = super.hitTest(point, with: event)
|
||||||
|
if result == self.view {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
func update(width: CGFloat, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, chatPeer: Peer, invitedBy: Peer, transition: ContainedViewLayoutTransition) -> CGFloat {
|
func update(width: CGFloat, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, chatPeer: Peer, invitedBy: Peer, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||||
let primaryTextColor = serviceMessageColorComponents(theme: theme, wallpaper: wallpaper).primaryText
|
let primaryTextColor = serviceMessageColorComponents(theme: theme, wallpaper: wallpaper).primaryText
|
||||||
|
|
||||||
@ -603,6 +611,7 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode {
|
|||||||
if let renderedPeer = interfaceState.renderedPeer {
|
if let renderedPeer = interfaceState.renderedPeer {
|
||||||
chatPeer = renderedPeer.peers[renderedPeer.peerId]
|
chatPeer = renderedPeer.peers[renderedPeer.peerId]
|
||||||
}
|
}
|
||||||
|
var hitTestSlop: CGFloat = 0.0
|
||||||
if let chatPeer = chatPeer, (updatedButtons.contains(.block) || updatedButtons.contains(.reportSpam) || updatedButtons.contains(.reportUserSpam)), let invitedBy = interfaceState.contactStatus?.invitedBy {
|
if let chatPeer = chatPeer, (updatedButtons.contains(.block) || updatedButtons.contains(.reportSpam) || updatedButtons.contains(.reportUserSpam)), let invitedBy = interfaceState.contactStatus?.invitedBy {
|
||||||
var inviteInfoTransition = transition
|
var inviteInfoTransition = transition
|
||||||
let inviteInfoNode: ChatInfoTitlePanelInviteInfoNode
|
let inviteInfoNode: ChatInfoTitlePanelInviteInfoNode
|
||||||
@ -623,6 +632,7 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode {
|
|||||||
let inviteHeight = inviteInfoNode.update(width: width, theme: interfaceState.theme, strings: interfaceState.strings, wallpaper: interfaceState.chatWallpaper, chatPeer: chatPeer, invitedBy: invitedBy, transition: inviteInfoTransition)
|
let inviteHeight = inviteInfoNode.update(width: width, theme: interfaceState.theme, strings: interfaceState.strings, wallpaper: interfaceState.chatWallpaper, chatPeer: chatPeer, invitedBy: invitedBy, transition: inviteInfoTransition)
|
||||||
inviteInfoTransition.updateFrame(node: inviteInfoNode, frame: CGRect(origin: CGPoint(x: 0.0, y: panelHeight + panelInset), size: CGSize(width: width, height: inviteHeight)))
|
inviteInfoTransition.updateFrame(node: inviteInfoNode, frame: CGRect(origin: CGPoint(x: 0.0, y: panelHeight + panelInset), size: CGSize(width: width, height: inviteHeight)))
|
||||||
panelHeight += inviteHeight
|
panelHeight += inviteHeight
|
||||||
|
hitTestSlop = -inviteHeight
|
||||||
}
|
}
|
||||||
} else if let inviteInfoNode = self.inviteInfoNode {
|
} else if let inviteInfoNode = self.inviteInfoNode {
|
||||||
self.inviteInfoNode = nil
|
self.inviteInfoNode = nil
|
||||||
@ -658,7 +668,7 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode {
|
|||||||
peerNearbyInfoNode?.removeFromSupernode()
|
peerNearbyInfoNode?.removeFromSupernode()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return LayoutResult(backgroundHeight: initialPanelHeight, insetHeight: panelHeight + panelInset)
|
return LayoutResult(backgroundHeight: initialPanelHeight, insetHeight: panelHeight + panelInset, hitTestSlop: hitTestSlop)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func buttonPressed(_ view: UIButton) {
|
@objc func buttonPressed(_ view: UIButton) {
|
||||||
@ -698,6 +708,9 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if point.y > 40.0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return super.hitTest(point, with: event)
|
return super.hitTest(point, with: event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,6 +56,6 @@ final class ChatRequestInProgressTitlePanelNode: ChatTitleAccessoryPanelNode {
|
|||||||
self.activateAreaNode.frame = CGRect(origin: .zero, size: CGSize(width: width, height: panelHeight))
|
self.activateAreaNode.frame = CGRect(origin: .zero, size: CGSize(width: width, height: panelHeight))
|
||||||
self.activateAreaNode.accessibilityLabel = interfaceState.strings.Channel_NotificationLoading
|
self.activateAreaNode.accessibilityLabel = interfaceState.strings.Channel_NotificationLoading
|
||||||
|
|
||||||
return LayoutResult(backgroundHeight: panelHeight, insetHeight: panelHeight)
|
return LayoutResult(backgroundHeight: panelHeight, insetHeight: panelHeight, hitTestSlop: 0.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ class ChatTitleAccessoryPanelNode: ASDisplayNode {
|
|||||||
struct LayoutResult {
|
struct LayoutResult {
|
||||||
var backgroundHeight: CGFloat
|
var backgroundHeight: CGFloat
|
||||||
var insetHeight: CGFloat
|
var insetHeight: CGFloat
|
||||||
|
var hitTestSlop: CGFloat
|
||||||
}
|
}
|
||||||
|
|
||||||
var interfaceInteraction: ChatPanelInterfaceInteraction?
|
var interfaceInteraction: ChatPanelInterfaceInteraction?
|
||||||
|
@ -59,6 +59,6 @@ final class ChatToastAlertPanelNode: ChatTitleAccessoryPanelNode {
|
|||||||
self.activateAreaNode.frame = CGRect(origin: .zero, size: CGSize(width: width, height: panelHeight))
|
self.activateAreaNode.frame = CGRect(origin: .zero, size: CGSize(width: width, height: panelHeight))
|
||||||
self.activateAreaNode.accessibilityLabel = self.titleNode.attributedText?.string ?? ""
|
self.activateAreaNode.accessibilityLabel = self.titleNode.attributedText?.string ?? ""
|
||||||
|
|
||||||
return LayoutResult(backgroundHeight: panelHeight, insetHeight: panelHeight)
|
return LayoutResult(backgroundHeight: panelHeight, insetHeight: panelHeight, hitTestSlop: 0.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -634,13 +634,13 @@ private final class TranslationLanguagesContextMenuContent: ContextControllerIte
|
|||||||
|
|
||||||
if minVisibleIndex <= maxVisibleIndex {
|
if minVisibleIndex <= maxVisibleIndex {
|
||||||
for index in minVisibleIndex ... maxVisibleIndex {
|
for index in minVisibleIndex ... maxVisibleIndex {
|
||||||
|
if index < self.languages.count {
|
||||||
let height = self.languages[index].0.isEmpty ? separatorHeight : itemHeight
|
let height = self.languages[index].0.isEmpty ? separatorHeight : itemHeight
|
||||||
var itemFrame = CGRect(origin: CGPoint(x: 0.0, y: CGFloat(index) * itemHeight), size: CGSize(width: size.width, height: height))
|
var itemFrame = CGRect(origin: CGPoint(x: 0.0, y: CGFloat(index) * itemHeight), size: CGSize(width: size.width, height: height))
|
||||||
if index > separatorIndex {
|
if index > separatorIndex {
|
||||||
itemFrame.origin.y += separatorHeight - itemHeight
|
itemFrame.origin.y += separatorHeight - itemHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
if index < self.languages.count {
|
|
||||||
let (languageCode, displayTitle) = self.languages[index]
|
let (languageCode, displayTitle) = self.languages[index]
|
||||||
validIds.insert(index)
|
validIds.insert(index)
|
||||||
|
|
||||||
@ -690,7 +690,21 @@ private final class TranslationLanguagesContextMenuContent: ContextControllerIte
|
|||||||
|
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
|
|
||||||
let size = CGSize(width: constrainedSize.width, height: CGFloat(self.languages.count) * itemHeight)
|
var separatorIndex = 0
|
||||||
|
for i in 0 ..< self.languages.count {
|
||||||
|
if self.languages[i].0.isEmpty {
|
||||||
|
separatorIndex = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var contentHeight: CGFloat
|
||||||
|
if separatorIndex != 0 {
|
||||||
|
contentHeight = CGFloat(self.languages.count - 1) * itemHeight + separatorHeight
|
||||||
|
} else {
|
||||||
|
contentHeight = CGFloat(self.languages.count) * itemHeight
|
||||||
|
}
|
||||||
|
let size = CGSize(width: constrainedSize.width, height: contentHeight)
|
||||||
|
|
||||||
let containerSize = CGSize(width: size.width, height: min(constrainedSize.height, size.height))
|
let containerSize = CGSize(width: size.width, height: min(constrainedSize.height, size.height))
|
||||||
self.currentSize = containerSize
|
self.currentSize = containerSize
|
||||||
|
@ -884,17 +884,19 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
|
|||||||
|> take(1)
|
|> take(1)
|
||||||
|> map { sharedData, accessChallengeData -> WebBrowserSettings in
|
|> map { sharedData, accessChallengeData -> WebBrowserSettings in
|
||||||
let passcodeSettings = sharedData.entries[ApplicationSpecificSharedDataKeys.presentationPasscodeSettings]?.get(PresentationPasscodeSettings.self) ?? PresentationPasscodeSettings.defaultSettings
|
let passcodeSettings = sharedData.entries[ApplicationSpecificSharedDataKeys.presentationPasscodeSettings]?.get(PresentationPasscodeSettings.self) ?? PresentationPasscodeSettings.defaultSettings
|
||||||
if accessChallengeData.data.isLockable {
|
|
||||||
if passcodeSettings.autolockTimeout != nil {
|
|
||||||
return WebBrowserSettings(defaultWebBrowser: "Safari")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
var settings: WebBrowserSettings
|
||||||
if let current = sharedData.entries[ApplicationSpecificSharedDataKeys.webBrowserSettings]?.get(WebBrowserSettings.self) {
|
if let current = sharedData.entries[ApplicationSpecificSharedDataKeys.webBrowserSettings]?.get(WebBrowserSettings.self) {
|
||||||
return current
|
settings = current
|
||||||
} else {
|
} else {
|
||||||
return WebBrowserSettings.defaultSettings
|
settings = .defaultSettings
|
||||||
}
|
}
|
||||||
|
if accessChallengeData.data.isLockable {
|
||||||
|
if passcodeSettings.autolockTimeout != nil && settings.defaultWebBrowser == nil {
|
||||||
|
settings = WebBrowserSettings(defaultWebBrowser: "safari")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return settings
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = (settings
|
let _ = (settings
|
||||||
@ -902,20 +904,14 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
|
|||||||
if settings.defaultWebBrowser == nil {
|
if settings.defaultWebBrowser == nil {
|
||||||
// let controller = BrowserScreen(context: context, subject: .webPage(parsedUrl.absoluteString))
|
// let controller = BrowserScreen(context: context, subject: .webPage(parsedUrl.absoluteString))
|
||||||
// navigationController?.pushViewController(controller)
|
// navigationController?.pushViewController(controller)
|
||||||
if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
|
|
||||||
if let window = navigationController?.view.window {
|
if let window = navigationController?.view.window {
|
||||||
let controller = SFSafariViewController(url: parsedUrl)
|
let controller = SFSafariViewController(url: parsedUrl)
|
||||||
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
|
|
||||||
controller.preferredBarTintColor = presentationData.theme.rootController.navigationBar.opaqueBackgroundColor
|
controller.preferredBarTintColor = presentationData.theme.rootController.navigationBar.opaqueBackgroundColor
|
||||||
controller.preferredControlTintColor = presentationData.theme.rootController.navigationBar.accentTextColor
|
controller.preferredControlTintColor = presentationData.theme.rootController.navigationBar.accentTextColor
|
||||||
}
|
|
||||||
window.rootViewController?.present(controller, animated: true)
|
window.rootViewController?.present(controller, animated: true)
|
||||||
} else {
|
} else {
|
||||||
context.sharedContext.applicationBindings.openUrl(parsedUrl.absoluteString)
|
context.sharedContext.applicationBindings.openUrl(parsedUrl.absoluteString)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
context.sharedContext.applicationBindings.openUrl(url)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
let openInOptions = availableOpenInOptions(context: context, item: .url(url: url))
|
let openInOptions = availableOpenInOptions(context: context, item: .url(url: url))
|
||||||
if let option = openInOptions.first(where: { $0.identifier == settings.defaultWebBrowser }) {
|
if let option = openInOptions.first(where: { $0.identifier == settings.defaultWebBrowser }) {
|
||||||
|
@ -4649,13 +4649,13 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.botInfo == nil && data.isContact {
|
if user.botInfo == nil && data.isContact, let peer = strongSelf.data?.peer as? TelegramUser, let phone = peer.phone {
|
||||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Profile_ShareContactButton, icon: { theme in
|
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Profile_ShareContactButton, icon: { theme in
|
||||||
generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor)
|
generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor)
|
||||||
}, action: { [weak self] _, f in
|
}, action: { [weak self] _, f in
|
||||||
f(.dismissWithoutContent)
|
f(.dismissWithoutContent)
|
||||||
|
|
||||||
if let strongSelf = self, let peer = strongSelf.data?.peer as? TelegramUser, let phone = peer.phone {
|
if let strongSelf = self {
|
||||||
let contact = TelegramMediaContact(firstName: peer.firstName ?? "", lastName: peer.lastName ?? "", phoneNumber: phone, peerId: peer.id, vCardData: nil)
|
let contact = TelegramMediaContact(firstName: peer.firstName ?? "", lastName: peer.lastName ?? "", phoneNumber: phone, peerId: peer.id, vCardData: nil)
|
||||||
let shareController = ShareController(context: strongSelf.context, subject: .media(.standalone(media: contact)), updatedPresentationData: strongSelf.controller?.updatedPresentationData)
|
let shareController = ShareController(context: strongSelf.context, subject: .media(.standalone(media: contact)), updatedPresentationData: strongSelf.controller?.updatedPresentationData)
|
||||||
shareController.completed = { [weak self] peerIds in
|
shareController.completed = { [weak self] peerIds in
|
||||||
|
@ -958,8 +958,8 @@ public func resolveUrlImpl(context: AccountContext, peerId: PeerId?, url: String
|
|||||||
for scheme in schemes {
|
for scheme in schemes {
|
||||||
let basePrefix = scheme + basePath + "/"
|
let basePrefix = scheme + basePath + "/"
|
||||||
var url = url
|
var url = url
|
||||||
if (url.lowercased().hasPrefix(scheme) && url.lowercased().hasSuffix(".\(basePath)")) {
|
if (url.lowercased().hasPrefix(scheme) && (url.lowercased().hasSuffix(".\(basePath)") || url.lowercased().contains(".\(basePath)/"))) {
|
||||||
url = basePrefix + String(url[scheme.endIndex...]).replacingOccurrences(of: ".\(basePath)", with: "")
|
url = basePrefix + String(url[scheme.endIndex...]).replacingOccurrences(of: ".\(basePath)/", with: "").replacingOccurrences(of: ".\(basePath)", with: "")
|
||||||
}
|
}
|
||||||
if url.lowercased().hasPrefix(basePrefix) {
|
if url.lowercased().hasPrefix(basePrefix) {
|
||||||
if let internalUrl = parseInternalUrl(query: String(url[basePrefix.endIndex...])) {
|
if let internalUrl = parseInternalUrl(query: String(url[basePrefix.endIndex...])) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user