diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index e3781ad4d9..95822ab013 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -600,7 +600,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController |> take(1) |> deliverOnMainQueue).start(next: { [weak self] greetingSticker in if let strongSelf = self { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer.id), activateInput: activateInput, scrollToEndIfExists: scrollToEndIfExists, greetingData: greetingSticker.flatMap({ ChatGreetingData(sticker: $0) }), animated: !scrollToEndIfExists, options: strongSelf.groupId == PeerGroupId.root ? [.removeOnMasterDetails] : [], parentGroupId: strongSelf.groupId, completion: { [weak self] controller in + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer.id), activateInput: activateInput && !peer.isDeleted, scrollToEndIfExists: scrollToEndIfExists, greetingData: greetingSticker.flatMap({ ChatGreetingData(sticker: $0) }), animated: !scrollToEndIfExists, options: strongSelf.groupId == PeerGroupId.root ? [.removeOnMasterDetails] : [], parentGroupId: strongSelf.groupId, completion: { [weak self] controller in self?.chatListDisplayNode.containerNode.currentItemNode.clearHighlightAnimated(true) if let promoInfo = promoInfo { switch promoInfo { diff --git a/submodules/Display/Source/NavigationButtonNode.swift b/submodules/Display/Source/NavigationButtonNode.swift index 1d614e0202..43b7f473c2 100644 --- a/submodules/Display/Source/NavigationButtonNode.swift +++ b/submodules/Display/Source/NavigationButtonNode.swift @@ -217,7 +217,6 @@ private final class NavigationButtonItemNode: ImmediateTextNode { override func updateLayout(_ constrainedSize: CGSize) -> CGSize { var superSize = super.updateLayout(constrainedSize) - superSize.height = max(44.0, superSize.height) if let node = self.node { let nodeSize = node.measure(constrainedSize) @@ -231,6 +230,8 @@ private final class NavigationButtonItemNode: ImmediateTextNode { imageNode.frame = imageFrame self.imageRippleNode.frame = imageFrame return size + } else { + superSize.height = max(44.0, superSize.height) } return superSize } diff --git a/submodules/PasscodeUI/Sources/PasscodeEntryControllerNode.swift b/submodules/PasscodeUI/Sources/PasscodeEntryControllerNode.swift index 5927a1bd54..6272a4c17b 100644 --- a/submodules/PasscodeUI/Sources/PasscodeEntryControllerNode.swift +++ b/submodules/PasscodeUI/Sources/PasscodeEntryControllerNode.swift @@ -128,6 +128,8 @@ final class PasscodeEntryControllerNode: ASDisplayNode { override func didLoad() { super.didLoad() + self.view.disablesInteractiveKeyboardGestureRecognizer = true + self.view.insertSubview(self.effectView, at: 0) if self.arguments.cancel != nil { diff --git a/submodules/PasscodeUI/Sources/PasscodeSetupControllerNode.swift b/submodules/PasscodeUI/Sources/PasscodeSetupControllerNode.swift index 6d2221e05b..78e857ab8d 100644 --- a/submodules/PasscodeUI/Sources/PasscodeSetupControllerNode.swift +++ b/submodules/PasscodeUI/Sources/PasscodeSetupControllerNode.swift @@ -161,6 +161,11 @@ final class PasscodeSetupControllerNode: ASDisplayNode { transition.updateFrame(node: self.modeButtonNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - insets.bottom - 53.0), size: CGSize(width: layout.size.width, height: 44.0))) } + override func didLoad() { + super.didLoad() + self.view.disablesInteractiveKeyboardGestureRecognizer = true + } + func updateMode(_ mode: PasscodeSetupControllerMode) { self.mode = mode self.inputFieldNode.reset() diff --git a/submodules/PeerInfoUI/Sources/ChannelAdminController.swift b/submodules/PeerInfoUI/Sources/ChannelAdminController.swift index 9ba77bc238..fe07260469 100644 --- a/submodules/PeerInfoUI/Sources/ChannelAdminController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelAdminController.swift @@ -1039,6 +1039,10 @@ public func channelAdminController(context: AccountContext, peerId: PeerId, admi return current.withUpdatedUpdating(true) } updateRightsDisposable.set((context.peerChannelMemberCategoriesContextsManager.updateMemberAdminRights(account: context.account, peerId: peerId, memberId: adminId, adminRights: TelegramChatAdminRights(rights: updateFlags ?? []), rank: effectiveRank) |> deliverOnMainQueue).start(error: { error in + updateState { current in + return current.withUpdatedUpdating(false) + } + let presentationData = context.sharedContext.currentPresentationData.with { $0 } var text = presentationData.strings.Login_UnknownError switch error { diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index ac19261fe8..d73e1c648e 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -1382,6 +1382,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } }, openInstantPage: { [weak self] message, associatedData in if let strongSelf = self, strongSelf.isNodeLoaded, let navigationController = strongSelf.effectiveNavigationController, let message = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(message.id) { + strongSelf.chatDisplayNode.dismissInput() openChatInstantPage(context: strongSelf.context, message: message, sourcePeerType: associatedData?.automaticDownloadPeerType, navigationController: navigationController) if case .overlay = strongSelf.presentationInterfaceState.mode { @@ -11642,12 +11643,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var latestNode: (Int32, ASDisplayNode)? self.chatDisplayNode.historyNode.forEachVisibleItemNode { itemNode in if let itemNode = itemNode as? ChatMessageItemView, let item = itemNode.item, let statusNode = itemNode.getStatusNode() { - if let (latestTimestamp, _) = latestNode { - if item.message.timestamp > latestTimestamp { + if !item.content.effectivelyIncoming(self.context.account.peerId) { + if let (latestTimestamp, _) = latestNode { + if item.message.timestamp > latestTimestamp { + latestNode = (item.message.timestamp, statusNode) + } + } else { latestNode = (item.message.timestamp, statusNode) } - } else { - latestNode = (item.message.timestamp, statusNode) } } } diff --git a/submodules/TelegramUI/Sources/ChatMessageItemView.swift b/submodules/TelegramUI/Sources/ChatMessageItemView.swift index 3b0b9a8652..d4a3177ae2 100644 --- a/submodules/TelegramUI/Sources/ChatMessageItemView.swift +++ b/submodules/TelegramUI/Sources/ChatMessageItemView.swift @@ -184,8 +184,6 @@ final class ChatMessageAccessibilityData { let singleUrl: String? init(item: ChatMessageItem, isSelected: Bool?) { - var label: String = "" - let value: String var hint: String? var traits: UIAccessibilityTraits = [] var singleUrl: String? @@ -202,368 +200,377 @@ final class ChatMessageAccessibilityData { } } - let authorName = item.message.author?.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder) - - if let chatPeer = item.message.peers[item.message.id.peerId] { - let (_, _, messageText) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, messages: [item.message], chatPeer: RenderedPeer(peer: chatPeer), accountPeerId: item.context.account.peerId) + let dataForMessage: (Message, Bool) -> (String, String) = { message, isReply -> (String, String) in + var label: String = "" + var value: String = "" - var text = messageText - - loop: for media in item.message.media { - if let _ = media as? TelegramMediaImage { - if isIncoming { - if announceIncomingAuthors, let authorName = authorName { - label = item.presentationData.strings.VoiceOver_Chat_PhotoFrom(authorName).0 - } else { - label = item.presentationData.strings.VoiceOver_Chat_Photo - } - } else { - label = item.presentationData.strings.VoiceOver_Chat_YourPhoto - } - text = "" - if !item.message.text.isEmpty { - text.append("\n") - - text.append(item.presentationData.strings.VoiceOver_Chat_Caption(item.message.text).0) - } - } else if let file = media as? TelegramMediaFile { - var isSpecialFile = false - for attribute in file.attributes { - switch attribute { - case let .Sticker(displayText, packReference, _): - isSpecialFile = true - text = displayText - if file.mimeType == "application/x-tgsticker" { - if isIncoming { - if announceIncomingAuthors, let authorName = authorName { - label = item.presentationData.strings.VoiceOver_Chat_AnimatedStickerFrom(authorName).0 - } else { - label = item.presentationData.strings.VoiceOver_Chat_AnimatedSticker - } - } else { - label = item.presentationData.strings.VoiceOver_Chat_YourAnimatedSticker - } - } else { - if isIncoming { - if announceIncomingAuthors, let authorName = authorName { - label = item.presentationData.strings.VoiceOver_Chat_StickerFrom(authorName).0 - } else { - label = item.presentationData.strings.VoiceOver_Chat_Sticker - } - } else { - label = item.presentationData.strings.VoiceOver_Chat_YourSticker - } - } - case let .Audio(audio): - isSpecialFile = true - if isSelected == nil { - hint = item.presentationData.strings.VoiceOver_Chat_PlayHint - } - traits.insert(.startsMediaSession) - if audio.isVoice { - let durationString = voiceMessageDurationFormatter.string(from: Double(audio.duration)) ?? "" - if isIncoming { - if announceIncomingAuthors, let authorName = authorName { - label = item.presentationData.strings.VoiceOver_Chat_VoiceMessageFrom(authorName).0 - } else { - label = item.presentationData.strings.VoiceOver_Chat_VoiceMessage - } - } else { - label = item.presentationData.strings.VoiceOver_Chat_YourVoiceMessage - } - text = item.presentationData.strings.VoiceOver_Chat_Duration(durationString).0 - } else { - let durationString = musicDurationFormatter.string(from: Double(audio.duration)) ?? "" - if isIncoming { - if announceIncomingAuthors, let authorName = authorName { - label = item.presentationData.strings.VoiceOver_Chat_MusicFrom(authorName).0 - } else { - label = item.presentationData.strings.VoiceOver_Chat_Music - } - } else { - label = item.presentationData.strings.VoiceOver_Chat_YourMusic - } - let performer = audio.performer ?? "Unknown" - let title = audio.title ?? "Unknown" - - text = item.presentationData.strings.VoiceOver_Chat_MusicTitle(title, performer).0 - text.append(item.presentationData.strings.VoiceOver_Chat_Duration(durationString).0) - } - case let .Video(video): - isSpecialFile = true - if isSelected == nil { - hint = item.presentationData.strings.VoiceOver_Chat_PlayHint - } - traits.insert(.startsMediaSession) - let durationString = voiceMessageDurationFormatter.string(from: Double(video.duration)) ?? "" - if video.flags.contains(.instantRoundVideo) { - if isIncoming { - if announceIncomingAuthors, let authorName = authorName { - label = item.presentationData.strings.VoiceOver_Chat_VideoMessageFrom(authorName).0 - } else { - label = item.presentationData.strings.VoiceOver_Chat_VideoMessage - } - } else { - label = item.presentationData.strings.VoiceOver_Chat_YourVideoMessage - } - } else { - if isIncoming { - if announceIncomingAuthors, let authorName = authorName { - label = item.presentationData.strings.VoiceOver_Chat_VideoFrom(authorName).0 - } else { - label = item.presentationData.strings.VoiceOver_Chat_Video - } - } else { - label = item.presentationData.strings.VoiceOver_Chat_YourVideo - } - } - text = item.presentationData.strings.VoiceOver_Chat_Duration(durationString).0 - default: - break - } - } - if !isSpecialFile { - if isSelected == nil { - hint = item.presentationData.strings.VoiceOver_Chat_OpenHint - } - let sizeString = fileSizeFormatter.string(fromByteCount: Int64(file.size ?? 0)) + if let chatPeer = message.peers[item.message.id.peerId] { + let authorName = message.author?.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder) + + let (_, _, messageText) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, messages: [message], chatPeer: RenderedPeer(peer: chatPeer), accountPeerId: item.context.account.peerId) + + var text = messageText + + loop: for media in message.media { + if let _ = media as? TelegramMediaImage { if isIncoming { if announceIncomingAuthors, let authorName = authorName { - label = item.presentationData.strings.VoiceOver_Chat_FileFrom(authorName).0 + label = item.presentationData.strings.VoiceOver_Chat_PhotoFrom(authorName).0 } else { - label = item.presentationData.strings.VoiceOver_Chat_File + label = item.presentationData.strings.VoiceOver_Chat_Photo } } else { - label = item.presentationData.strings.VoiceOver_Chat_YourFile + label = item.presentationData.strings.VoiceOver_Chat_YourPhoto } - text = "\(file.fileName ?? ""). " - text.append(item.presentationData.strings.VoiceOver_Chat_Size(sizeString).0) - } - if !item.message.text.isEmpty { - text.append("\n") - text.append(item.presentationData.strings.VoiceOver_Chat_Caption(item.message.text).0) - } - break loop - } else if let webpage = media as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content { - var contentText = item.presentationData.strings.VoiceOver_Chat_PagePreview + ". " - if let title = content.title, !title.isEmpty { - contentText.append(item.presentationData.strings.VoiceOver_Chat_Title(title).0) - contentText.append(". ") - } - if let text = content.text, !text.isEmpty { - contentText.append(text) - } - text = "\(item.message.text)\n\(contentText)" - } else if let contact = media as? TelegramMediaContact { - if isIncoming { - if announceIncomingAuthors, let authorName = authorName { - label = item.presentationData.strings.VoiceOver_Chat_ContactFrom(authorName).0 - } else { - label = item.presentationData.strings.VoiceOver_Chat_Contact + text = "" + if !message.text.isEmpty { + text.append("\n") + + text.append(item.presentationData.strings.VoiceOver_Chat_Caption(message.text).0) } - } else { - label = item.presentationData.strings.VoiceOver_Chat_YourContact - } - var displayName = "" - if !contact.firstName.isEmpty { - displayName.append(contact.firstName) - } - if !contact.lastName.isEmpty { - if !displayName.isEmpty { - displayName.append(" ") + } else if let file = media as? TelegramMediaFile { + var isSpecialFile = false + for attribute in file.attributes { + switch attribute { + case let .Sticker(displayText, packReference, _): + isSpecialFile = true + text = displayText + if file.mimeType == "application/x-tgsticker" { + if isIncoming { + if announceIncomingAuthors, let authorName = authorName { + label = item.presentationData.strings.VoiceOver_Chat_AnimatedStickerFrom(authorName).0 + } else { + label = item.presentationData.strings.VoiceOver_Chat_AnimatedSticker + } + } else { + label = item.presentationData.strings.VoiceOver_Chat_YourAnimatedSticker + } + } else { + if isIncoming { + if announceIncomingAuthors, let authorName = authorName { + label = item.presentationData.strings.VoiceOver_Chat_StickerFrom(authorName).0 + } else { + label = item.presentationData.strings.VoiceOver_Chat_Sticker + } + } else { + label = item.presentationData.strings.VoiceOver_Chat_YourSticker + } + } + case let .Audio(audio): + isSpecialFile = true + if isSelected == nil { + hint = item.presentationData.strings.VoiceOver_Chat_PlayHint + } + traits.insert(.startsMediaSession) + if audio.isVoice { + let durationString = voiceMessageDurationFormatter.string(from: Double(audio.duration)) ?? "" + if isIncoming { + if announceIncomingAuthors, let authorName = authorName { + label = item.presentationData.strings.VoiceOver_Chat_VoiceMessageFrom(authorName).0 + } else { + label = item.presentationData.strings.VoiceOver_Chat_VoiceMessage + } + } else { + label = item.presentationData.strings.VoiceOver_Chat_YourVoiceMessage + } + text = item.presentationData.strings.VoiceOver_Chat_Duration(durationString).0 + } else { + let durationString = musicDurationFormatter.string(from: Double(audio.duration)) ?? "" + if isIncoming { + if announceIncomingAuthors, let authorName = authorName { + label = item.presentationData.strings.VoiceOver_Chat_MusicFrom(authorName).0 + } else { + label = item.presentationData.strings.VoiceOver_Chat_Music + } + } else { + label = item.presentationData.strings.VoiceOver_Chat_YourMusic + } + let performer = audio.performer ?? "Unknown" + let title = audio.title ?? "Unknown" + + text = item.presentationData.strings.VoiceOver_Chat_MusicTitle(title, performer).0 + text.append(item.presentationData.strings.VoiceOver_Chat_Duration(durationString).0) + } + case let .Video(video): + isSpecialFile = true + if isSelected == nil { + hint = item.presentationData.strings.VoiceOver_Chat_PlayHint + } + traits.insert(.startsMediaSession) + let durationString = voiceMessageDurationFormatter.string(from: Double(video.duration)) ?? "" + if video.flags.contains(.instantRoundVideo) { + if isIncoming { + if announceIncomingAuthors, let authorName = authorName { + label = item.presentationData.strings.VoiceOver_Chat_VideoMessageFrom(authorName).0 + } else { + label = item.presentationData.strings.VoiceOver_Chat_VideoMessage + } + } else { + label = item.presentationData.strings.VoiceOver_Chat_YourVideoMessage + } + } else { + if isIncoming { + if announceIncomingAuthors, let authorName = authorName { + label = item.presentationData.strings.VoiceOver_Chat_VideoFrom(authorName).0 + } else { + label = item.presentationData.strings.VoiceOver_Chat_Video + } + } else { + label = item.presentationData.strings.VoiceOver_Chat_YourVideo + } + } + text = item.presentationData.strings.VoiceOver_Chat_Duration(durationString).0 + default: + break + } } - displayName.append(contact.lastName) - } - var phoneNumbersString = "" - var phoneNumberCount = 0 - var emailAddressesString = "" - var emailAddressCount = 0 - var organizationString = "" - if let vCard = contact.vCardData, let vCardData = vCard.data(using: .utf8), let contactData = DeviceContactExtendedData(vcard: vCardData) { - if displayName.isEmpty && !contactData.organization.isEmpty { - displayName = contactData.organization - } - if !contactData.basicData.phoneNumbers.isEmpty { - for phone in contactData.basicData.phoneNumbers { - if !phoneNumbersString.isEmpty { - phoneNumbersString.append(", ") + if !isSpecialFile { + if isSelected == nil { + hint = item.presentationData.strings.VoiceOver_Chat_OpenHint + } + let sizeString = fileSizeFormatter.string(fromByteCount: Int64(file.size ?? 0)) + if isIncoming { + if announceIncomingAuthors, let authorName = authorName { + label = item.presentationData.strings.VoiceOver_Chat_FileFrom(authorName).0 + } else { + label = item.presentationData.strings.VoiceOver_Chat_File } - for c in phone.value { + } else { + label = item.presentationData.strings.VoiceOver_Chat_YourFile + } + text = "\(file.fileName ?? ""). " + text.append(item.presentationData.strings.VoiceOver_Chat_Size(sizeString).0) + } + if !message.text.isEmpty { + text.append("\n") + text.append(item.presentationData.strings.VoiceOver_Chat_Caption(message.text).0) + } + break loop + } else if let webpage = media as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content { + var contentText = item.presentationData.strings.VoiceOver_Chat_PagePreview + ". " + if let title = content.title, !title.isEmpty { + contentText.append(item.presentationData.strings.VoiceOver_Chat_Title(title).0) + contentText.append(". ") + } + if let text = content.text, !text.isEmpty { + contentText.append(text) + } + text = "\(message.text)\n\(contentText)" + } else if let contact = media as? TelegramMediaContact { + if isIncoming { + if announceIncomingAuthors, let authorName = authorName { + label = item.presentationData.strings.VoiceOver_Chat_ContactFrom(authorName).0 + } else { + label = item.presentationData.strings.VoiceOver_Chat_Contact + } + } else { + label = item.presentationData.strings.VoiceOver_Chat_YourContact + } + var displayName = "" + if !contact.firstName.isEmpty { + displayName.append(contact.firstName) + } + if !contact.lastName.isEmpty { + if !displayName.isEmpty { + displayName.append(" ") + } + displayName.append(contact.lastName) + } + var phoneNumbersString = "" + var phoneNumberCount = 0 + var emailAddressesString = "" + var emailAddressCount = 0 + var organizationString = "" + if let vCard = contact.vCardData, let vCardData = vCard.data(using: .utf8), let contactData = DeviceContactExtendedData(vcard: vCardData) { + if displayName.isEmpty && !contactData.organization.isEmpty { + displayName = contactData.organization + } + if !contactData.basicData.phoneNumbers.isEmpty { + for phone in contactData.basicData.phoneNumbers { + if !phoneNumbersString.isEmpty { + phoneNumbersString.append(", ") + } + for c in phone.value { + phoneNumbersString.append(c) + phoneNumbersString.append(" ") + } + phoneNumberCount += 1 + } + } else { + for c in contact.phoneNumber { phoneNumbersString.append(c) phoneNumbersString.append(" ") } phoneNumberCount += 1 } - } else { - for c in contact.phoneNumber { - phoneNumbersString.append(c) - phoneNumbersString.append(" ") + + for email in contactData.emailAddresses { + if !emailAddressesString.isEmpty { + emailAddressesString.append(", ") + } + emailAddressesString.append("\(email.value)") + emailAddressCount += 1 } - phoneNumberCount += 1 + if !contactData.organization.isEmpty && displayName != contactData.organization { + organizationString = contactData.organization + } + } else { + phoneNumbersString.append("\(contact.phoneNumber)") + } + text = "\(displayName)." + if !phoneNumbersString.isEmpty { + if phoneNumberCount > 1 { + text.append(item.presentationData.strings.VoiceOver_Chat_ContactPhoneNumberCount(Int32(phoneNumberCount))) + text.append(": ") + } else { + text.append(item.presentationData.strings.VoiceOver_Chat_ContactPhoneNumber) + } + text.append("\(phoneNumbersString). ") + } + if !emailAddressesString.isEmpty { + if emailAddressCount > 1 { + text.append(item.presentationData.strings.VoiceOver_Chat_ContactEmailCount(Int32(emailAddressCount))) + text.append(": ") + } else { + text.append(item.presentationData.strings.VoiceOver_Chat_ContactEmail) + text.append(": ") + } + text.append("\(emailAddressesString). ") + } + if !organizationString.isEmpty { + text.append(item.presentationData.strings.VoiceOver_Chat_ContactOrganization(organizationString).0) + text.append(".") + } + } else if let poll = media as? TelegramMediaPoll { + if isIncoming { + if announceIncomingAuthors, let authorName = authorName { + label = item.presentationData.strings.VoiceOver_Chat_AnonymousPollFrom(authorName).0 + } else { + label = item.presentationData.strings.VoiceOver_Chat_AnonymousPoll + } + } else { + label = item.presentationData.strings.VoiceOver_Chat_YourAnonymousPoll } - for email in contactData.emailAddresses { - if !emailAddressesString.isEmpty { - emailAddressesString.append(", ") + var optionVoterCount: [Int: Int32] = [:] + var maxOptionVoterCount: Int32 = 0 + var totalVoterCount: Int32 = 0 + let voters: [TelegramMediaPollOptionVoters]? + if poll.isClosed { + voters = poll.results.voters ?? [] + } else { + voters = poll.results.voters + } + var selectedOptionId: Data? + if let voters = voters, let totalVoters = poll.results.totalVoters { + var didVote = false + for voter in voters { + if voter.selected { + didVote = true + selectedOptionId = voter.opaqueIdentifier + } } - emailAddressesString.append("\(email.value)") - emailAddressCount += 1 - } - if !contactData.organization.isEmpty && displayName != contactData.organization { - organizationString = contactData.organization - } - } else { - phoneNumbersString.append("\(contact.phoneNumber)") - } - text = "\(displayName)." - if !phoneNumbersString.isEmpty { - if phoneNumberCount > 1 { - text.append(item.presentationData.strings.VoiceOver_Chat_ContactPhoneNumberCount(Int32(phoneNumberCount))) - text.append(": ") - } else { - text.append(item.presentationData.strings.VoiceOver_Chat_ContactPhoneNumber) - } - text.append("\(phoneNumbersString). ") - } - if !emailAddressesString.isEmpty { - if emailAddressCount > 1 { - text.append(item.presentationData.strings.VoiceOver_Chat_ContactEmailCount(Int32(emailAddressCount))) - text.append(": ") - } else { - text.append(item.presentationData.strings.VoiceOver_Chat_ContactEmail) - text.append(": ") - } - text.append("\(emailAddressesString). ") - } - if !organizationString.isEmpty { - text.append(item.presentationData.strings.VoiceOver_Chat_ContactOrganization(organizationString).0) - text.append(".") - } - } else if let poll = media as? TelegramMediaPoll { - if isIncoming { - if announceIncomingAuthors, let authorName = authorName { - label = item.presentationData.strings.VoiceOver_Chat_AnonymousPollFrom(authorName).0 - } else { - label = item.presentationData.strings.VoiceOver_Chat_AnonymousPoll - } - } else { - label = item.presentationData.strings.VoiceOver_Chat_YourAnonymousPoll - } - - var optionVoterCount: [Int: Int32] = [:] - var maxOptionVoterCount: Int32 = 0 - var totalVoterCount: Int32 = 0 - let voters: [TelegramMediaPollOptionVoters]? - if poll.isClosed { - voters = poll.results.voters ?? [] - } else { - voters = poll.results.voters - } - var selectedOptionId: Data? - if let voters = voters, let totalVoters = poll.results.totalVoters { - var didVote = false - for voter in voters { - if voter.selected { - didVote = true - selectedOptionId = voter.opaqueIdentifier - } - } - totalVoterCount = totalVoters - if didVote || poll.isClosed { - for i in 0 ..< poll.options.count { - inner: for optionVoters in voters { - if optionVoters.opaqueIdentifier == poll.options[i].opaqueIdentifier { - optionVoterCount[i] = optionVoters.count - maxOptionVoterCount = max(maxOptionVoterCount, optionVoters.count) - break inner + totalVoterCount = totalVoters + if didVote || poll.isClosed { + for i in 0 ..< poll.options.count { + inner: for optionVoters in voters { + if optionVoters.opaqueIdentifier == poll.options[i].opaqueIdentifier { + optionVoterCount[i] = optionVoters.count + maxOptionVoterCount = max(maxOptionVoterCount, optionVoters.count) + break inner + } } } } } - } - - var optionVoterCounts: [Int] - if totalVoterCount != 0 { - optionVoterCounts = countNicePercent(votes: (0 ..< poll.options.count).map({ Int(optionVoterCount[$0] ?? 0) }), total: Int(totalVoterCount)) - } else { - optionVoterCounts = Array(repeating: 0, count: poll.options.count) - } - - text = item.presentationData.strings.VoiceOver_Chat_Title(poll.text).0 - text.append(". ") - - text.append(item.presentationData.strings.VoiceOver_Chat_PollOptionCount(Int32(poll.options.count))) - text.append(": ") - var optionsText = "" - for i in 0 ..< poll.options.count { - let option = poll.options[i] - if !optionsText.isEmpty { - optionsText.append(", ") - } - optionsText.append(option.text) - if let selectedOptionId = selectedOptionId, selectedOptionId == option.opaqueIdentifier { - optionsText.append(", ") - optionsText.append(item.presentationData.strings.VoiceOver_Chat_OptionSelected) + var optionVoterCounts: [Int] + if totalVoterCount != 0 { + optionVoterCounts = countNicePercent(votes: (0 ..< poll.options.count).map({ Int(optionVoterCount[$0] ?? 0) }), total: Int(totalVoterCount)) + } else { + optionVoterCounts = Array(repeating: 0, count: poll.options.count) } - if let _ = optionVoterCount[i] { - if maxOptionVoterCount != 0 && totalVoterCount != 0 { - optionsText.append(", \(optionVoterCounts[i])%") + text = item.presentationData.strings.VoiceOver_Chat_Title(poll.text).0 + text.append(". ") + + text.append(item.presentationData.strings.VoiceOver_Chat_PollOptionCount(Int32(poll.options.count))) + text.append(": ") + var optionsText = "" + for i in 0 ..< poll.options.count { + let option = poll.options[i] + + if !optionsText.isEmpty { + optionsText.append(", ") + } + optionsText.append(option.text) + if let selectedOptionId = selectedOptionId, selectedOptionId == option.opaqueIdentifier { + optionsText.append(", ") + optionsText.append(item.presentationData.strings.VoiceOver_Chat_OptionSelected) + } + + if let _ = optionVoterCount[i] { + if maxOptionVoterCount != 0 && totalVoterCount != 0 { + optionsText.append(", \(optionVoterCounts[i])%") + } } } - } - text.append("\(optionsText). ") - if totalVoterCount != 0 { - text.append(item.presentationData.strings.VoiceOver_Chat_PollVotes(Int32(totalVoterCount))) - } else { - text.append(item.presentationData.strings.VoiceOver_Chat_PollNoVotes) - } - if poll.isClosed { - text.append(item.presentationData.strings.VoiceOver_Chat_PollFinalResults) + text.append("\(optionsText). ") + if totalVoterCount != 0 { + text.append(item.presentationData.strings.VoiceOver_Chat_PollVotes(Int32(totalVoterCount))) + } else { + text.append(item.presentationData.strings.VoiceOver_Chat_PollNoVotes) + } + if poll.isClosed { + text.append(item.presentationData.strings.VoiceOver_Chat_PollFinalResults) + } } } - } - - var result = "" - - if let isSelected = isSelected { - if isSelected { - result += item.presentationData.strings.VoiceOver_Chat_Selected + + var result = "" + + if let isSelected = isSelected { + if isSelected { + result += item.presentationData.strings.VoiceOver_Chat_Selected + result += "\n" + } + traits.insert(.startsMediaSession) + } + + result += "\(text)" + + let dateString = DateFormatter.localizedString(from: Date(timeIntervalSince1970: Double(message.timestamp)), dateStyle: .medium, timeStyle: .short) + + result += "\n\(dateString)" + if !isIncoming && item.read && !isReply { result += "\n" + if announceIncomingAuthors { + result += item.presentationData.strings.VoiceOver_Chat_SeenByRecipients + } else { + result += item.presentationData.strings.VoiceOver_Chat_SeenByRecipient + } } - traits.insert(.startsMediaSession) + value = result + } else { + value = "" } - result += "\(text)" - - let dateString = DateFormatter.localizedString(from: Date(timeIntervalSince1970: Double(item.message.timestamp)), dateStyle: .medium, timeStyle: .short) - - result += "\n\(dateString)" - if !isIncoming && item.read { - result += "\n" - if announceIncomingAuthors { - result += item.presentationData.strings.VoiceOver_Chat_SeenByRecipients + if label.isEmpty { + if let author = message.author { + if isIncoming { + label = author.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder) + } else { + label = item.presentationData.strings.VoiceOver_Chat_YourMessage + } } else { - result += item.presentationData.strings.VoiceOver_Chat_SeenByRecipient + label = item.presentationData.strings.VoiceOver_Chat_Message } } - value = result - } else { - value = "" + + return (label, value) } - if label.isEmpty { - if let author = item.message.author { - if isIncoming { - label = author.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder) - } else { - label = item.presentationData.strings.VoiceOver_Chat_YourMessage - } - } else { - label = item.presentationData.strings.VoiceOver_Chat_Message - } - } + var (label, value) = dataForMessage(item.message, false) for attribute in item.message.attributes { if let attribute = attribute as? TextEntitiesMessageAttribute { @@ -593,7 +600,7 @@ final class ChatMessageAccessibilityData { } } } else if let attribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[attribute.messageId] { - let replyLabel: String + var replyLabel: String if replyMessage.flags.contains(.Incoming) { if let author = replyMessage.author { replyLabel = item.presentationData.strings.VoiceOver_Chat_ReplyFrom(author.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)).0 @@ -603,6 +610,10 @@ final class ChatMessageAccessibilityData { } else { replyLabel = item.presentationData.strings.VoiceOver_Chat_ReplyToYourMessage } + + let (replyMessageLabel, replyMessageValue) = dataForMessage(replyMessage, true) + replyLabel += "\(replyLabel): \(replyMessageLabel), \(replyMessageValue)" + label = "\(replyLabel) . \(label)" } } diff --git a/submodules/TelegramUI/Sources/ChatTitleView.swift b/submodules/TelegramUI/Sources/ChatTitleView.swift index acae3a4ac8..fc9cefb143 100644 --- a/submodules/TelegramUI/Sources/ChatTitleView.swift +++ b/submodules/TelegramUI/Sources/ChatTitleView.swift @@ -378,7 +378,9 @@ final class ChatTitleView: UIView, NavigationBarTitleView { let string = NSAttributedString(string: "", font: Font.regular(13.0), textColor: titleTheme.rootController.navigationBar.secondaryTextColor) state = .info(string, .generic) } else if let user = peer as? TelegramUser { - if servicePeer { + if user.isDeleted { + state = .none + } else if servicePeer { let string = NSAttributedString(string: "", font: Font.regular(13.0), textColor: titleTheme.rootController.navigationBar.secondaryTextColor) state = .info(string, .generic) } else if user.flags.contains(.isSupport) { diff --git a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoVisualMediaPaneNode.swift b/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoVisualMediaPaneNode.swift index 05fe77e053..4680fbd7ba 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoVisualMediaPaneNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoVisualMediaPaneNode.swift @@ -826,7 +826,7 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro self.scrollNode.view.delaysContentTouches = false self.scrollNode.view.canCancelContentTouches = true - self.scrollNode.view.showsVerticalScrollIndicator = true + self.scrollNode.view.showsVerticalScrollIndicator = false if #available(iOS 11.0, *) { self.scrollNode.view.contentInsetAdjustmentBehavior = .never } diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift index e48252786c..163cb4b3ef 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift @@ -993,6 +993,7 @@ func peerInfoHeaderButtons(peer: Peer?, cachedData: CachedPeerData?, isOpenedFro var canViewStats = false var hasDiscussion = false var hasVoiceChat = false + var canStartVoiceChat = false if let cachedChannelData = cachedData as? CachedChannelData { canViewStats = cachedChannelData.flags.contains(.canViewStats) } @@ -1012,6 +1013,9 @@ func peerInfoHeaderButtons(peer: Peer?, cachedData: CachedPeerData?, isOpenedFro if channel.flags.contains(.hasVoiceChat) { hasVoiceChat = true } + if !hasVoiceChat && (channel.flags.contains(.isCreator) || channel.hasPermission(.manageCalls)) { + canStartVoiceChat = true + } } switch channel.participationStatus { case .member: @@ -1043,7 +1047,7 @@ func peerInfoHeaderButtons(peer: Peer?, cachedData: CachedPeerData?, isOpenedFro if channel.isVerified || channel.adminRights != nil || channel.flags.contains(.isCreator) { canReport = false } - if !canReport && !canViewStats { + if !canReport && !canViewStats && !canStartVoiceChat { displayMore = false } if displayMore {