From fdae5ffdf34110c823f33c3c64df8372803a6be6 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Sat, 18 Feb 2023 22:55:02 +0400 Subject: [PATCH] Cherry-pick some fixes --- .../Telegram-iOS/en.lproj/Localizable.strings | 45 +++++--- ...tionSequencePhoneEntryControllerNode.swift | 4 +- .../Source/Base/Transition.swift | 4 + .../Source/Components/Button.swift | 2 +- .../Sources/BlurredBackgroundComponent.swift | 10 +- .../Navigation/NavigationController.swift | 11 +- submodules/Display/Source/NavigationBar.swift | 2 +- .../Sources/MediaPickerGridItem.swift | 11 ++ .../Sources/MediaPickerScreen.swift | 7 ++ .../Sources/EmojiHeaderComponent.swift | 4 +- .../RecentSessionScreen.swift | 71 +++++++++++- .../TelegramUI/Sources/ChatController.swift | 102 ++---------------- .../Sources/ChatControllerNode.swift | 12 ++- .../Sources/ChatHistoryListNode.swift | 15 ++- .../ChatInterfaceStateContextMenus.swift | 22 ++-- .../ChatInterfaceTitlePanelNodes.swift | 3 + .../ChatInviteRequestsTitlePanelNode.swift | 2 +- .../Sources/ChatMessageItemView.swift | 15 ++- .../ChatPinnedMessageTitlePanelNode.swift | 2 +- .../ChatReportPeerTitlePanelNode.swift | 15 ++- .../ChatRequestInProgressTitlePanelNode.swift | 2 +- .../Sources/ChatTitleAccessoryPanelNode.swift | 1 + .../Sources/ChatToastAlertPanelNode.swift | 2 +- .../Sources/ChatTranslationPanelNode.swift | 28 +++-- submodules/TelegramUI/Sources/OpenUrl.swift | 36 +++---- .../Sources/PeerInfo/PeerInfoScreen.swift | 4 +- .../UrlHandling/Sources/UrlHandling.swift | 4 +- 27 files changed, 255 insertions(+), 181 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index afa80d1900..360145e7b5 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -384,7 +384,7 @@ "Tour.Text2" = "**Telegram** delivers messages\nfaster than any other application."; "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.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.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"; // 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.HaveNotReceivedCodeInternal" = "Haven't received the code?"; -"Login.CodeSentSms" = "We have sent you an SMS with the code"; +"Login.HaveNotReceivedCodeInternal" = "Didn't get the code?"; +"Login.CodeSentSms" = "We've sent you an SMS with the code"; "Login.Code" = "Code"; -"Login.WillCallYou" = "You can request a voice call in %@"; -"Login.CallRequestState2" = "Requesting a call from Telegram..."; +"Login.WillCallYou" = "Telegram will call you in %@"; +"Login.CallRequestState2" = "Requesting a call from Telegram…"; "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.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.InvalidCodeError" = "You have entered an invalid code. Please try again."; +"Login.UnknownError" = "An error occurred, please try again later."; +"Login.InvalidCodeError" = "Invalid code, please try again."; "Login.NetworkError" = "Please check your internet connection and try again."; -"Login.CodeExpiredError" = "Code expired. Please try again."; -"Login.CodeFloodError" = "Limit exceeded. Please try again later."; -"Login.InvalidPhoneError" = "Invalid phone number. Please try again."; -"Login.InvalidFirstNameError" = "Invalid first name. Please try again."; -"Login.InvalidLastNameError" = "Invalid last name. Please try again."; +"Login.CodeExpiredError" = "Code expired, please start over."; +"Login.CodeFloodError" = "Too many attempts, please try again later."; +"Login.InvalidPhoneError" = "Invalid phone number, please try again."; +"Login.InvalidFirstNameError" = "This first name is not allowed, please try another."; +"Login.InvalidLastNameError" = "Sorry, this last name can't be used."; "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$@"; @@ -438,8 +438,8 @@ "Login.InfoAvatarPhoto" = "photo"; "Login.InfoFirstNamePlaceholder" = "First Name"; "Login.InfoLastNamePlaceholder" = "Last Name"; -"Login.InfoDeletePhoto" = "Delete Photo"; -"Login.InfoHelp" = "Enter your name and add a profile picture."; +"Login.InfoDeletePhoto" = "Remove Photo"; +"Login.InfoHelp" = "Enter your name and add a profile photo."; // Login.SelectCountry "Login.SelectCountry.Title" = "Country"; @@ -8919,3 +8919,14 @@ Sorry for the inconvenience."; "ImportStickerPack.ImportingEmojis" = "Importing Emojis"; "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: %@"; diff --git a/submodules/AuthorizationUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift b/submodules/AuthorizationUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift index d28dfd9e8c..3aa3319f14 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift @@ -782,8 +782,8 @@ final class PhoneConfirmationController: ViewController { self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.4) self.backgroundNode = ASDisplayNode() - self.backgroundNode.backgroundColor = theme.list.plainBackgroundColor - self.backgroundNode.cornerRadius = 11.0 + self.backgroundNode.backgroundColor = theme.list.itemBlocksBackgroundColor + self.backgroundNode.cornerRadius = 24.0 self.textNode = ImmediateTextNode() self.textNode.displaysAsynchronously = false diff --git a/submodules/ComponentFlow/Source/Base/Transition.swift b/submodules/ComponentFlow/Source/Base/Transition.swift index 2e81ec8f4d..b6f58e0e06 100644 --- a/submodules/ComponentFlow/Source/Base/Transition.swift +++ b/submodules/ComponentFlow/Source/Base/Transition.swift @@ -119,6 +119,10 @@ public struct Transition { public static func easeInOut(duration: Double) -> Transition { 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) { self.animation = animation diff --git a/submodules/ComponentFlow/Source/Components/Button.swift b/submodules/ComponentFlow/Source/Components/Button.swift index ed1e57024e..9b64fffeda 100644 --- a/submodules/ComponentFlow/Source/Components/Button.swift +++ b/submodules/ComponentFlow/Source/Components/Button.swift @@ -126,7 +126,7 @@ public final class Button: Component { alpha = 1.0 } } else { - alpha = 0.4 + alpha = 0.3 } transition.setAlpha(view: self.contentView, alpha: alpha) } diff --git a/submodules/Components/BlurredBackgroundComponent/Sources/BlurredBackgroundComponent.swift b/submodules/Components/BlurredBackgroundComponent/Sources/BlurredBackgroundComponent.swift index df5f156d2a..ed008b2184 100644 --- a/submodules/Components/BlurredBackgroundComponent/Sources/BlurredBackgroundComponent.swift +++ b/submodules/Components/BlurredBackgroundComponent/Sources/BlurredBackgroundComponent.swift @@ -7,13 +7,16 @@ import ComponentDisplayAdapters public final class BlurredBackgroundComponent: Component { public let color: UIColor public let tintContainerView: UIView? + public let cornerRadius: CGFloat public init( color: UIColor, - tintContainerView: UIView? = nil + tintContainerView: UIView? = nil, + cornerRadius: CGFloat = 0.0 ) { self.color = color self.tintContainerView = tintContainerView + self.cornerRadius = cornerRadius } public static func ==(lhs: BlurredBackgroundComponent, rhs: BlurredBackgroundComponent) -> Bool { @@ -23,6 +26,9 @@ public final class BlurredBackgroundComponent: Component { if lhs.tintContainerView !== rhs.tintContainerView { return false } + if lhs.cornerRadius != rhs.cornerRadius { + return false + } return true } @@ -106,7 +112,7 @@ public final class BlurredBackgroundComponent: Component { 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 { transition.setFrame(view: tintContainerView, frame: CGRect(origin: CGPoint(), size: availableSize)) diff --git a/submodules/Display/Source/Navigation/NavigationController.swift b/submodules/Display/Source/Navigation/NavigationController.swift index 8bb2c46f06..ab7167d9be 100644 --- a/submodules/Display/Source/Navigation/NavigationController.swift +++ b/submodules/Display/Source/Navigation/NavigationController.swift @@ -1193,22 +1193,25 @@ open class NavigationController: UINavigationController, ContainableController, split.isInFocus = true } + var masterTopHasOpaque = topHasOpaque + var detailTopHasOpaque = topHasOpaque + if let controller = split.masterControllers.last { - if topHasOpaque { + if masterTopHasOpaque { controller.displayNode.accessibilityElementsHidden = true } else { if controller.isOpaqueWhenInOverlay || controller.blocksBackgroundWhenInOverlay { - topHasOpaque = true + masterTopHasOpaque = true } controller.displayNode.accessibilityElementsHidden = false } } if let controller = split.detailControllers.last { - if topHasOpaque { + if detailTopHasOpaque { controller.displayNode.accessibilityElementsHidden = true } else { if controller.isOpaqueWhenInOverlay || controller.blocksBackgroundWhenInOverlay { - topHasOpaque = true + detailTopHasOpaque = true } controller.displayNode.accessibilityElementsHidden = false } diff --git a/submodules/Display/Source/NavigationBar.swift b/submodules/Display/Source/NavigationBar.swift index fd839d4f14..797b72409f 100644 --- a/submodules/Display/Source/NavigationBar.swift +++ b/submodules/Display/Source/NavigationBar.swift @@ -9,7 +9,7 @@ open class SparseNode: ASDisplayNode { if self.alpha.isZero { return nil } - if !self.bounds.contains(point) { + if !self.bounds.inset(by: self.hitTestSlop).contains(point) { return nil } for view in self.view.subviews.reversed() { diff --git a/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift b/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift index 4d549e9b53..a1082e8001 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift @@ -87,6 +87,8 @@ final class MediaPickerGridItemNode: GridItemNode { private let typeIconNode: ASImageNode private let durationNode: ImmediateTextNode + private let activateAreaNode: AccessibilityAreaNode + private var interaction: MediaPickerInteraction? private var theme: PresentationTheme? @@ -114,10 +116,14 @@ final class MediaPickerGridItemNode: GridItemNode { self.typeIconNode.displayWithoutProcessing = true self.durationNode = ImmediateTextNode() + + self.activateAreaNode = AccessibilityAreaNode() + self.activateAreaNode.accessibilityTraits = [.image] super.init() self.addSubnode(self.imageNode) + self.addSubnode(self.activateAreaNode) self.imageNode.contentUpdated = { [weak self] image in self?.spoilerNode?.setImage(image) @@ -285,6 +291,10 @@ final class MediaPickerGridItemNode: GridItemNode { let editingContext = interaction.editingState 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 { subscriber in if let signal = editingContext.thumbnailImageSignal(forIdentifier: asset.localIdentifier) { let disposable = signal.start(next: { next in @@ -411,6 +421,7 @@ final class MediaPickerGridItemNode: GridItemNode { 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.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 { let durationSize = self.durationNode.updateLayout(self.bounds.size) diff --git a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift index 1a334237b8..3c8960f0c3 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift @@ -192,6 +192,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { private let backgroundNode: NavigationBackgroundNode private let gridNode: GridNode fileprivate var cameraView: TGAttachmentCameraView? + private var cameraActivateAreaNode: AccessibilityAreaNode private var placeholderNode: MediaPickerPlaceholderNode? private var manageNode: MediaPickerManageNode? private var scrollingArea: SparseItemGridScrollingArea @@ -233,6 +234,10 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { self.gridNode = GridNode() self.scrollingArea = SparseItemGridScrollingArea() + self.cameraActivateAreaNode = AccessibilityAreaNode() + self.cameraActivateAreaNode.accessibilityLabel = "Camera" + self.cameraActivateAreaNode.accessibilityTraits = [.button] + super.init() if case .assets(nil) = controller.subject { @@ -416,6 +421,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { cameraView.startPreview() self.gridNode.scrollView.addSubview(cameraView) + self.gridNode.addSubnode(self.cameraActivateAreaNode) } else { self.containerNode.clipsToBounds = true } @@ -1067,6 +1073,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { if let cameraView = self.cameraView { if let cameraRect = cameraRect { transition.updateFrame(view: cameraView, frame: cameraRect) + self.cameraActivateAreaNode.frame = cameraRect cameraView.isHidden = false } else { cameraView.isHidden = true diff --git a/submodules/PremiumUI/Sources/EmojiHeaderComponent.swift b/submodules/PremiumUI/Sources/EmojiHeaderComponent.swift index 03cc8821bb..cc28b6c698 100644 --- a/submodules/PremiumUI/Sources/EmojiHeaderComponent.swift +++ b/submodules/PremiumUI/Sources/EmojiHeaderComponent.swift @@ -110,7 +110,9 @@ class EmojiHeaderComponent: Component { } self.statusView.isHidden = false - containerView = containerView.subviews[1].subviews[1] + if containerView.subviews.count > 1 && containerView.subviews[1].subviews.count > 1 { + containerView = containerView.subviews[1].subviews[1] + } let initialPosition = self.statusView.center let targetPosition = self.statusView.superview!.convert(self.statusView.center, to: containerView) diff --git a/submodules/SettingsUI/Sources/Privacy and Security/RecentSessionScreen.swift b/submodules/SettingsUI/Sources/Privacy and Security/RecentSessionScreen.swift index f5a7c8aced..7847792e6e 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/RecentSessionScreen.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/RecentSessionScreen.swift @@ -187,8 +187,10 @@ private class RecentSessionScreenNode: ViewControllerTracingNode, UIScrollViewDe private let acceptHeaderNode: ImmediateTextNode private let secretChatsTitleNode: ImmediateTextNode private let secretChatsSwitchNode: SwitchNode + private let secretChatsActivateAreaNode: AccessibilityAreaNode private let incomingCallsTitleNode: ImmediateTextNode private let incomingCallsSwitchNode: SwitchNode + private let incomingCallsActivateAreaNode: AccessibilityAreaNode private let acceptSeparatorNode: ASDisplayNode private let cancelButton: HighlightableButtonNode @@ -264,8 +266,13 @@ private class RecentSessionScreenNode: ViewControllerTracingNode, UIScrollViewDe self.incomingCallsTitleNode = ImmediateTextNode() self.incomingCallsSwitchNode = SwitchNode() + self.secretChatsActivateAreaNode = AccessibilityAreaNode() + self.incomingCallsActivateAreaNode = AccessibilityAreaNode() + self.cancelButton = HighlightableButtonNode() 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) @@ -341,6 +348,9 @@ private class RecentSessionScreenNode: ViewControllerTracingNode, UIScrollViewDe self.secretChatsSwitchNode.isOn = session.flags.contains(.acceptsSecretChats) 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 { hasIncomingCalls = true 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.accessibilityLabel = title + self.titleNode.isAccessibilityElement = true + 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.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.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.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.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.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.maximumNumberOfLines = 4 + self.locationInfoNode.accessibilityLabel = self.presentationData.strings.AuthSessions_View_LocationInfo + self.locationInfoNode.isAccessibilityElement = true self.acceptBackgroundNode = ASDisplayNode() self.acceptBackgroundNode.clipsToBounds = true @@ -410,9 +438,18 @@ private class RecentSessionScreenNode: ViewControllerTracingNode, UIScrollViewDe 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.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.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.backgroundColor = self.presentationData.theme.list.itemBlocksSeparatorColor @@ -463,23 +500,49 @@ private class RecentSessionScreenNode: ViewControllerTracingNode, UIScrollViewDe if hasSecretChats { self.contentContainerNode.addSubnode(self.secretChatsTitleNode) self.contentContainerNode.addSubnode(self.secretChatsSwitchNode) + self.contentContainerNode.addSubnode(self.secretChatsActivateAreaNode) self.secretChatsSwitchNode.valueUpdated = { [weak self] value in if let strongSelf = self { 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.incomingCallsTitleNode) self.contentContainerNode.addSubnode(self.incomingCallsSwitchNode) + self.contentContainerNode.addSubnode(self.incomingCallsActivateAreaNode) self.incomingCallsSwitchNode.valueUpdated = { [weak self] value in if let strongSelf = self { 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) @@ -800,6 +863,7 @@ private class RecentSessionScreenNode: ViewControllerTracingNode, UIScrollViewDe 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.secretChatsActivateAreaNode.frame = CGRect(origin: CGPoint(x: secretFrame.minX, y: secretFrame.minY), size: CGSize(width: fieldFrame.width, height: fieldItemHeight)) } } @@ -815,7 +879,8 @@ private class RecentSessionScreenNode: ViewControllerTracingNode, UIScrollViewDe } 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 { @@ -833,8 +898,10 @@ private class RecentSessionScreenNode: ViewControllerTracingNode, UIScrollViewDe if isCurrent { contentHeight -= 68.0 self.terminateButton.isHidden = true + self.terminateButton.isAccessibilityElement = false } else { self.terminateButton.isHidden = false + self.terminateButton.isAccessibilityElement = true } let sideInset = floor((layout.size.width - width) / 2.0) diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index f1af62e80b..fe513a77cd 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -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) - }, updateExtraNavigationBarBackgroundHeight: { value, _ in + }, updateExtraNavigationBarBackgroundHeight: { value, hitTestSlop, _ in strongSelf.additionalNavigationBarBackgroundHeight = value + strongSelf.additionalNavigationBarHitTestSlop = hitTestSlop }) if let mappedTransition = mappedTransition { @@ -11063,6 +11064,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G private var suspendNavigationBarLayout: Bool = false private var suspendedNavigationBarLayout: ContainerViewLayout? private var additionalNavigationBarBackgroundHeight: CGFloat = 0.0 + private var additionalNavigationBarHitTestSlop: CGFloat = 0.0 override public func updateNavigationBarLayout(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { if self.suspendNavigationBarLayout { @@ -11099,9 +11101,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var navigationBarTransition = transition 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) - }, updateExtraNavigationBarBackgroundHeight: { value, extraNavigationTransition in + }, updateExtraNavigationBarBackgroundHeight: { value, hitTestSlop, extraNavigationTransition in navigationBarTransition = extraNavigationTransition self.additionalNavigationBarBackgroundHeight = value + self.additionalNavigationBarHitTestSlop = hitTestSlop }) if case .compact = layout.metrics.widthClass { @@ -11122,6 +11125,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.suspendedNavigationBarLayout = suspendedNavigationBarLayout 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 }) { @@ -16701,101 +16705,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.chatDisplayNode.dismissInput() 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, options: ChatAvailableMessageActionOptions) { guard let peerId = self.chatLocation.peerId else { return diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index a97d0f94f4..03b935eb6b 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -259,7 +259,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { if let (layout, navigationHeight) = self.validLayout { 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 if let _ = self.scheduledAnimateInAsOverlayFromNode { transition = .immediate @@ -1068,6 +1068,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { var immediatelyLayoutTitleAccessoryPanelNodeAndAnimateAppearance = false var titleAccessoryPanelHeight: CGFloat? var titleAccessoryPanelBackgroundHeight: CGFloat? + var titleAccessoryPanelHitTestSlop: CGFloat? var extraTransition = transition if let titleAccessoryPanelNode = titlePanelForChatPresentationInterfaceState(self.chatPresentationInterfaceState, context: self.context, currentPanel: self.titleAccessoryPanelNode, controllerInteraction: self.controllerInteraction, interfaceInteraction: self.interfaceInteraction) { 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) titleAccessoryPanelHeight = layoutResult.insetHeight titleAccessoryPanelBackgroundHeight = layoutResult.backgroundHeight + titleAccessoryPanelHitTestSlop = layoutResult.hitTestSlop if immediatelyLayoutTitleAccessoryPanelNodeAndAnimateAppearance { titleAccessoryPanelNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) 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))) var extraNavigationBarHeight: CGFloat = 0.0 + var extraNavigationBarHitTestSlop: CGFloat = 0.0 var titleAccessoryPanelFrame: CGRect? 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)) insets.top += panelHeight extraNavigationBarHeight += titleAccessoryPanelBackgroundHeight ?? 0.0 + extraNavigationBarHitTestSlop = titleAccessoryPanelHitTestSlop ?? 0.0 } var translationPanelFrame: CGRect? @@ -1474,7 +1478,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { extraNavigationBarHeight += panelHeight } - updateExtraNavigationBarBackgroundHeight(extraNavigationBarHeight, extraTransition) + updateExtraNavigationBarBackgroundHeight(extraNavigationBarHeight, extraNavigationBarHitTestSlop, extraTransition) var importStatusPanelFrame: CGRect? if let _ = self.chatImportStatusPanel, let panelHeight = importStatusPanelHeight { @@ -3029,7 +3033,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { if let (layout, navigationHeight) = self.validLayout { 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) - }, updateExtraNavigationBarBackgroundHeight: { _, _ in + }, updateExtraNavigationBarBackgroundHeight: { _, _, _ in }) } } diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index 9ff00cdb4f..43db38fa4d 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -1241,12 +1241,17 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { var translateToLanguage: String? if let translationState, isPremium && translationState.isEnabled { - translateToLanguage = translationState.toLang ?? presentationData.strings.baseLanguageCode - if translateToLanguage == "nb" { - translateToLanguage = "no" - } else if translateToLanguage == "pt-br" { - translateToLanguage = "pt" + var languageCode = translationState.toLang ?? presentationData.strings.baseLanguageCode + let rawSuffix = "-raw" + if languageCode.hasSuffix(rawSuffix) { + languageCode = String(languageCode.dropLast(rawSuffix.count)) } + 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) diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index 3e32ac62f4..ad12e51f40 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -1062,15 +1062,21 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState } } - if !messageText.isEmpty || resourceAvailable || diceEmoji != nil { - let isCopyProtected = chatPresentationInterfaceState.copyProtectionEnabled || message.isCopyProtected() - let message = messages[0] - var isExpired = false - for media in message.media { - if let _ = media as? TelegramMediaExpiredContent { - isExpired = true - } + let message = messages[0] + var isExpired = false + var isImage = false + for media in message.media { + if let _ = media as? TelegramMediaExpiredContent { + isExpired = true } + if media is TelegramMediaImage { + isImage = true + } + } + + if !messageText.isEmpty || (resourceAvailable && isImage) || diceEmoji != nil { + let isCopyProtected = chatPresentationInterfaceState.copyProtectionEnabled || message.isCopyProtected() + if !isExpired { if !isPoll { if !isCopyProtected { diff --git a/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift b/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift index 91eff93325..dd2d188c44 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift @@ -52,6 +52,9 @@ func titlePanelForChatPresentationInterfaceState(_ chatPresentationInterfaceStat if inhibitTitlePanelDisplay, let selectedContextValue = selectedContext { switch selectedContextValue { case .pinnedMessage: + if case .peer = chatPresentationInterfaceState.chatLocation { + selectedContext = nil + } break default: selectedContext = nil diff --git a/submodules/TelegramUI/Sources/ChatInviteRequestsTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatInviteRequestsTitlePanelNode.swift index c0baac19b2..50c9f7d6c3 100644 --- a/submodules/TelegramUI/Sources/ChatInviteRequestsTitlePanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatInviteRequestsTitlePanelNode.swift @@ -208,7 +208,7 @@ final class ChatInviteRequestsTitlePanelNode: ChatTitleAccessoryPanelNode { self.activateAreaNode.frame = CGRect(origin: .zero, size: CGSize(width: width, height: panelHeight)) 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() { diff --git a/submodules/TelegramUI/Sources/ChatMessageItemView.swift b/submodules/TelegramUI/Sources/ChatMessageItemView.swift index 5783330741..e0b8eeb2ad 100644 --- a/submodules/TelegramUI/Sources/ChatMessageItemView.swift +++ b/submodules/TelegramUI/Sources/ChatMessageItemView.swift @@ -232,6 +232,9 @@ final class ChatMessageAccessibilityData { } } else if let file = media as? TelegramMediaFile { var isSpecialFile = false + + let isVideo = file.isInstantVideo + for attribute in file.attributes { switch attribute { case let .Sticker(displayText, _, _): @@ -259,6 +262,9 @@ final class ChatMessageAccessibilityData { } } case let .Audio(isVoice, duration, title, performer, _): + if isVideo { + continue + } isSpecialFile = true if isSelected == nil { hint = item.presentationData.strings.VoiceOver_Chat_PlayHint @@ -573,6 +579,7 @@ final class ChatMessageAccessibilityData { } var (label, value) = dataForMessage(item.message, false) + var replyValue: String? for attribute in item.message.attributes { if let attribute = attribute as? TextEntitiesMessageAttribute { @@ -613,8 +620,8 @@ final class ChatMessageAccessibilityData { replyLabel = item.presentationData.strings.VoiceOver_Chat_ReplyToYourMessage } -// let (replyMessageLabel, replyMessageValue) = dataForMessage(replyMessage, true) -// replyLabel += "\(replyLabel): \(replyMessageLabel), \(replyMessageValue)" + let (_, replyMessageValue) = dataForMessage(replyMessage, true) + replyValue = replyMessageValue 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)) } + if let replyValue { + value = "\(value). \(item.presentationData.strings.VoiceOver_Chat_ReplyingToMessage(replyValue).string)" + } + self.label = label self.value = value self.hint = hint diff --git a/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift index 44e2e04fe3..0b7a23c3d7 100644 --- a/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift @@ -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?) { diff --git a/submodules/TelegramUI/Sources/ChatReportPeerTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatReportPeerTitlePanelNode.swift index 89b0544ab0..d04a66cebb 100644 --- a/submodules/TelegramUI/Sources/ChatReportPeerTitlePanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatReportPeerTitlePanelNode.swift @@ -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 { let primaryTextColor = serviceMessageColorComponents(theme: theme, wallpaper: wallpaper).primaryText @@ -603,6 +611,7 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode { if let renderedPeer = interfaceState.renderedPeer { 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 { var inviteInfoTransition = transition 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) inviteInfoTransition.updateFrame(node: inviteInfoNode, frame: CGRect(origin: CGPoint(x: 0.0, y: panelHeight + panelInset), size: CGSize(width: width, height: inviteHeight))) panelHeight += inviteHeight + hitTestSlop = -inviteHeight } } else if let inviteInfoNode = self.inviteInfoNode { self.inviteInfoNode = nil @@ -658,7 +668,7 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode { peerNearbyInfoNode?.removeFromSupernode() }) } - return LayoutResult(backgroundHeight: initialPanelHeight, insetHeight: panelHeight + panelInset) + return LayoutResult(backgroundHeight: initialPanelHeight, insetHeight: panelHeight + panelInset, hitTestSlop: hitTestSlop) } @objc func buttonPressed(_ view: UIButton) { @@ -698,6 +708,9 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode { return result } } + if point.y > 40.0 { + return nil + } return super.hitTest(point, with: event) } } diff --git a/submodules/TelegramUI/Sources/ChatRequestInProgressTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatRequestInProgressTitlePanelNode.swift index b716e487dc..9148b24047 100644 --- a/submodules/TelegramUI/Sources/ChatRequestInProgressTitlePanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatRequestInProgressTitlePanelNode.swift @@ -56,6 +56,6 @@ final class ChatRequestInProgressTitlePanelNode: ChatTitleAccessoryPanelNode { self.activateAreaNode.frame = CGRect(origin: .zero, size: CGSize(width: width, height: panelHeight)) self.activateAreaNode.accessibilityLabel = interfaceState.strings.Channel_NotificationLoading - return LayoutResult(backgroundHeight: panelHeight, insetHeight: panelHeight) + return LayoutResult(backgroundHeight: panelHeight, insetHeight: panelHeight, hitTestSlop: 0.0) } } diff --git a/submodules/TelegramUI/Sources/ChatTitleAccessoryPanelNode.swift b/submodules/TelegramUI/Sources/ChatTitleAccessoryPanelNode.swift index 5a07973883..f7973f8c49 100644 --- a/submodules/TelegramUI/Sources/ChatTitleAccessoryPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTitleAccessoryPanelNode.swift @@ -8,6 +8,7 @@ class ChatTitleAccessoryPanelNode: ASDisplayNode { struct LayoutResult { var backgroundHeight: CGFloat var insetHeight: CGFloat + var hitTestSlop: CGFloat } var interfaceInteraction: ChatPanelInterfaceInteraction? diff --git a/submodules/TelegramUI/Sources/ChatToastAlertPanelNode.swift b/submodules/TelegramUI/Sources/ChatToastAlertPanelNode.swift index 5020093f6c..0ab0c145fa 100644 --- a/submodules/TelegramUI/Sources/ChatToastAlertPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatToastAlertPanelNode.swift @@ -59,6 +59,6 @@ final class ChatToastAlertPanelNode: ChatTitleAccessoryPanelNode { self.activateAreaNode.frame = CGRect(origin: .zero, size: CGSize(width: width, height: panelHeight)) self.activateAreaNode.accessibilityLabel = self.titleNode.attributedText?.string ?? "" - return LayoutResult(backgroundHeight: panelHeight, insetHeight: panelHeight) + return LayoutResult(backgroundHeight: panelHeight, insetHeight: panelHeight, hitTestSlop: 0.0) } } diff --git a/submodules/TelegramUI/Sources/ChatTranslationPanelNode.swift b/submodules/TelegramUI/Sources/ChatTranslationPanelNode.swift index eec1aaf56e..919d13cd24 100644 --- a/submodules/TelegramUI/Sources/ChatTranslationPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTranslationPanelNode.swift @@ -634,13 +634,13 @@ private final class TranslationLanguagesContextMenuContent: ContextControllerIte if minVisibleIndex <= maxVisibleIndex { for index in minVisibleIndex ... maxVisibleIndex { - 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)) - if index > separatorIndex { - itemFrame.origin.y += separatorHeight - itemHeight - } - if index < self.languages.count { + 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)) + if index > separatorIndex { + itemFrame.origin.y += separatorHeight - itemHeight + } + let (languageCode, displayTitle) = self.languages[index] validIds.insert(index) @@ -690,7 +690,21 @@ private final class TranslationLanguagesContextMenuContent: ContextControllerIte 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)) self.currentSize = containerSize diff --git a/submodules/TelegramUI/Sources/OpenUrl.swift b/submodules/TelegramUI/Sources/OpenUrl.swift index 1cc5499d27..aff1160039 100644 --- a/submodules/TelegramUI/Sources/OpenUrl.swift +++ b/submodules/TelegramUI/Sources/OpenUrl.swift @@ -884,17 +884,19 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur |> take(1) |> map { sharedData, accessChallengeData -> WebBrowserSettings in let passcodeSettings = sharedData.entries[ApplicationSpecificSharedDataKeys.presentationPasscodeSettings]?.get(PresentationPasscodeSettings.self) ?? PresentationPasscodeSettings.defaultSettings + + var settings: WebBrowserSettings + if let current = sharedData.entries[ApplicationSpecificSharedDataKeys.webBrowserSettings]?.get(WebBrowserSettings.self) { + settings = current + } else { + settings = .defaultSettings + } if accessChallengeData.data.isLockable { - if passcodeSettings.autolockTimeout != nil { - return WebBrowserSettings(defaultWebBrowser: "Safari") + if passcodeSettings.autolockTimeout != nil && settings.defaultWebBrowser == nil { + settings = WebBrowserSettings(defaultWebBrowser: "safari") } } - - if let current = sharedData.entries[ApplicationSpecificSharedDataKeys.webBrowserSettings]?.get(WebBrowserSettings.self) { - return current - } else { - return WebBrowserSettings.defaultSettings - } + return settings } let _ = (settings @@ -902,19 +904,13 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur if settings.defaultWebBrowser == nil { // let controller = BrowserScreen(context: context, subject: .webPage(parsedUrl.absoluteString)) // navigationController?.pushViewController(controller) - if #available(iOSApplicationExtension 9.0, iOS 9.0, *) { - if let window = navigationController?.view.window { - let controller = SFSafariViewController(url: parsedUrl) - if #available(iOSApplicationExtension 10.0, iOS 10.0, *) { - controller.preferredBarTintColor = presentationData.theme.rootController.navigationBar.opaqueBackgroundColor - controller.preferredControlTintColor = presentationData.theme.rootController.navigationBar.accentTextColor - } - window.rootViewController?.present(controller, animated: true) - } else { - context.sharedContext.applicationBindings.openUrl(parsedUrl.absoluteString) - } + if let window = navigationController?.view.window { + let controller = SFSafariViewController(url: parsedUrl) + controller.preferredBarTintColor = presentationData.theme.rootController.navigationBar.opaqueBackgroundColor + controller.preferredControlTintColor = presentationData.theme.rootController.navigationBar.accentTextColor + window.rootViewController?.present(controller, animated: true) } else { - context.sharedContext.applicationBindings.openUrl(url) + context.sharedContext.applicationBindings.openUrl(parsedUrl.absoluteString) } } else { let openInOptions = availableOpenInOptions(context: context, item: .url(url: url)) diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 5c4c349eea..e1d31e5833 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -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 generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in 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 shareController = ShareController(context: strongSelf.context, subject: .media(.standalone(media: contact)), updatedPresentationData: strongSelf.controller?.updatedPresentationData) shareController.completed = { [weak self] peerIds in diff --git a/submodules/UrlHandling/Sources/UrlHandling.swift b/submodules/UrlHandling/Sources/UrlHandling.swift index 75d38106de..963614e1d9 100644 --- a/submodules/UrlHandling/Sources/UrlHandling.swift +++ b/submodules/UrlHandling/Sources/UrlHandling.swift @@ -958,8 +958,8 @@ public func resolveUrlImpl(context: AccountContext, peerId: PeerId?, url: String for scheme in schemes { let basePrefix = scheme + basePath + "/" var url = url - if (url.lowercased().hasPrefix(scheme) && url.lowercased().hasSuffix(".\(basePath)")) { - url = basePrefix + String(url[scheme.endIndex...]).replacingOccurrences(of: ".\(basePath)", with: "") + if (url.lowercased().hasPrefix(scheme) && (url.lowercased().hasSuffix(".\(basePath)") || url.lowercased().contains(".\(basePath)/"))) { + url = basePrefix + String(url[scheme.endIndex...]).replacingOccurrences(of: ".\(basePath)/", with: "").replacingOccurrences(of: ".\(basePath)", with: "") } if url.lowercased().hasPrefix(basePrefix) { if let internalUrl = parseInternalUrl(query: String(url[basePrefix.endIndex...])) {