From b92a46ee5814ef1a1b5e95f302cfbb98f05b6d68 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Sat, 4 Feb 2023 02:32:09 +0400 Subject: [PATCH 01/12] Check tmp dir size --- submodules/DebugSettingsUI/BUILD | 1 + .../Sources/DebugController.swift | 39 +++++++++++++++++-- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/submodules/DebugSettingsUI/BUILD b/submodules/DebugSettingsUI/BUILD index 60710882b0..a8ea3aa7b3 100644 --- a/submodules/DebugSettingsUI/BUILD +++ b/submodules/DebugSettingsUI/BUILD @@ -25,6 +25,7 @@ swift_library( "//submodules/GZip:GZip", "//third-party/ZipArchive:ZipArchive", "//submodules/InAppPurchaseManager:InAppPurchaseManager", + "//submodules/Utils/DarwinDirStat", ], visibility = [ "//visibility:public", diff --git a/submodules/DebugSettingsUI/Sources/DebugController.swift b/submodules/DebugSettingsUI/Sources/DebugController.swift index 8f2a768db3..46e8ae0508 100644 --- a/submodules/DebugSettingsUI/Sources/DebugController.swift +++ b/submodules/DebugSettingsUI/Sources/DebugController.swift @@ -16,6 +16,7 @@ import AppBundle import ZipArchive import WebKit import InAppPurchaseManager +import DarwinDirStat @objc private final class DebugControllerMailComposeDelegate: NSObject, MFMailComposeViewControllerDelegate { public func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) { @@ -947,10 +948,15 @@ private enum DebugControllerEntry: ItemListNodeEntry { case .resetTranslationStates: return ItemListActionItem(presentationData: presentationData, title: "Reset Translation States", kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: { if let context = arguments.context { - let _ = context.engine.itemCache.clear(collectionIds: [ - ApplicationSpecificItemCacheCollectionId.translationState - ]).start() + let size = statForDirectory(path: NSTemporaryDirectory()) + let controller = textAlertController(context: context, title: nil, text: "temp dir size \(size)", actions: [TextAlertAction(type: .genericAction, title: "OK", action: {})]) + arguments.presentController(controller, nil) } +// if let context = arguments.context { +// let _ = context.engine.itemCache.clear(collectionIds: [ +// ApplicationSpecificItemCacheCollectionId.translationState +// ]).start() +// } }) case .crash: return ItemListActionItem(presentationData: presentationData, title: "Crash", kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: { @@ -1539,3 +1545,30 @@ public func triggerDebugSendLogsUI(context: AccountContext, additionalInfo: Stri pushController(controller) }) } + +private func statForDirectory(path: String) -> Int64 { + if #available(macOS 10.13, *) { + var s = darwin_dirstat() + var result = dirstat_np(path, 1, &s, MemoryLayout.size) + if result != -1 { + return Int64(s.total_size) + } else { + result = dirstat_np(path, 0, &s, MemoryLayout.size) + if result != -1 { + return Int64(s.total_size) + } else { + return 0 + } + } + } else { + let fileManager = FileManager.default + let folderURL = URL(fileURLWithPath: path) + var folderSize: Int64 = 0 + if let files = try? fileManager.contentsOfDirectory(at: folderURL, includingPropertiesForKeys: nil, options: []) { + for file in files { + folderSize += (fileSize(file.path) ?? 0) + } + } + return folderSize + } +} From bf94ea75e9d901d1d3396cec471e794fd46cf09e Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Mon, 6 Feb 2023 00:55:17 +0400 Subject: [PATCH 02/12] Various fixes --- .../Telegram-iOS/en.lproj/Localizable.strings | 3 + .../AuthorizationSequenceController.swift | 5 +- .../Sources/ChatListSearchContainerNode.swift | 7 ++ .../Sources/Node/ChatListItem.swift | 2 +- submodules/DebugSettingsUI/BUILD | 1 - .../Sources/DebugController.swift | 39 +---------- .../Sources/InAppPurchaseManager.swift | 2 +- .../LegacyComponents/TGMediaVideoConverter.h | 6 +- .../Sources/TGMediaAssetsController.m | 14 ++++ .../Sources/TGMediaVideoConverter.m | 17 ++--- .../Sources/TGPhotoQualityController.m | 9 ++- .../Sources/MediaPickerScreen.swift | 2 +- .../SearchBarNode/Sources/SearchBarNode.swift | 5 +- .../DataAndStorageSettingsController.swift | 68 ++++++++++++++----- .../Sources/VoiceChatController.swift | 8 ++- .../Sources/MessageContentKind.swift | 2 +- .../Sources/BackgroundColorComponent.swift | 2 +- .../ChatMessageAnimatedStickerItemNode.swift | 2 +- .../ChatMessageAttachedContentNode.swift | 2 +- .../Sources/ChatMessageBubbleItemNode.swift | 2 +- .../ChatMessageCallBubbleContentNode.swift | 2 +- .../ChatMessageContactBubbleContentNode.swift | 2 +- .../ChatMessageInteractiveFileNode.swift | 2 +- ...atMessageInteractiveInstantVideoNode.swift | 2 +- .../ChatMessageMapBubbleContentNode.swift | 2 +- .../ChatMessageMediaBubbleContentNode.swift | 4 +- .../ChatMessagePollBubbleContentNode.swift | 2 +- ...atMessageRestrictedBubbleContentNode.swift | 2 +- .../Sources/ChatMessageStickerItemNode.swift | 2 +- .../ChatMessageTextBubbleContentNode.swift | 2 +- .../Sources/ChatTranslationPanelNode.swift | 65 +++++++++--------- .../Sources/CreateChannelController.swift | 8 ++- .../Sources/CreateGroupController.swift | 9 ++- .../Sources/FetchVideoMediaResource.swift | 12 +++- .../InstantVideoRadialStatusNode.swift | 3 + .../Sources/PeerInfo/PeerInfoHeaderNode.swift | 4 +- .../Sources/PeerInfo/PeerInfoScreen.swift | 9 ++- .../StringForMessageTimestampStatus.swift | 7 +- .../Sources/MediaInputSettings.swift | 6 +- 39 files changed, 202 insertions(+), 141 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 7fb7cb997e..8ad165c8d4 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -8886,3 +8886,6 @@ Sorry for the inconvenience."; "Translation.Language.ta" = "Tamil"; "Translation.Language.tr" = "Turkish"; "Translation.Language.uz" = "Uzbek"; + +"Settings.RaiseToListen" = "Raise to Listen"; +"Settings.RaiseToListenInfo" = "Raise to Listen allows you to quickly listen and reply to incoming audio messages by raising the phone to your ear."; diff --git a/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift b/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift index b9c6992c24..223da9bb54 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift @@ -954,7 +954,8 @@ public final class AuthorizationSequenceController: NavigationController, MFMail } } - let signal = TGMediaVideoConverter.convert(avatarAsset, adjustments: avatarAdjustments, watcher: nil, entityRenderer: entityRenderer)! + let tempFile = EngineTempBox.shared.tempFile(fileName: "video.mp4") + let signal = TGMediaVideoConverter.convert(avatarAsset, adjustments: avatarAdjustments, path: tempFile.path, watcher: nil, entityRenderer: entityRenderer)! let signalDisposable = signal.start(next: { next in if let result = next as? TGMediaVideoConversionResult { @@ -964,6 +965,8 @@ public final class AuthorizationSequenceController: NavigationController, MFMail let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) account.postbox.mediaBox.storeResourceData(resource.id, data: data, synchronous: true) subscriber.putNext(resource) + + EngineTempBox.shared.dispose(tempFile) } } subscriber.putCompletion() diff --git a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift index 23a1cbf1ab..c9cdd41346 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift @@ -126,6 +126,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo private var selectedFilterPromise = Promise() private var transitionFraction: CGFloat = 0.0 + private var appearanceTimestamp: Double? + private weak var copyProtectionTooltipController: TooltipController? private lazy var hapticFeedback = { HapticFeedback() }() @@ -306,6 +308,10 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo return } + if let appearanceTimestamp = strongSelf.appearanceTimestamp, CACurrentMediaTime() - appearanceTimestamp < 0.5 { + return + } + var key: ChatListSearchPaneKey? var date = strongSelf.currentSearchOptions.date var peer = strongSelf.currentSearchOptions.peer @@ -667,6 +673,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo if isFirstTime { self.filterContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + self.appearanceTimestamp = CACurrentMediaTime() } var bottomIntrinsicInset = layout.intrinsicInsets.bottom diff --git a/submodules/ChatListUI/Sources/Node/ChatListItem.swift b/submodules/ChatListUI/Sources/Node/ChatListItem.swift index e3271143bc..9166b6a686 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift @@ -1848,7 +1848,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { entities = translation.entities } - messageString = stringWithAppliedEntities(trimToLineCount(messageText, lineCount: authorAttributedString == nil ? 2 : 1), entities: entities, baseColor: theme.messageTextColor, linkColor: theme.messageTextColor, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: italicTextFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont, underlineLinks: false, message: message._asMessage()) + messageString = foldLineBreaks(stringWithAppliedEntities(messageText, entities: entities, baseColor: theme.messageTextColor, linkColor: theme.messageTextColor, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: italicTextFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont, underlineLinks: false, message: message._asMessage())) } else if spoilers != nil || customEmojiRanges != nil { let mutableString = NSMutableAttributedString(string: messageText, font: textFont, textColor: theme.messageTextColor) if let spoilers = spoilers { diff --git a/submodules/DebugSettingsUI/BUILD b/submodules/DebugSettingsUI/BUILD index a8ea3aa7b3..60710882b0 100644 --- a/submodules/DebugSettingsUI/BUILD +++ b/submodules/DebugSettingsUI/BUILD @@ -25,7 +25,6 @@ swift_library( "//submodules/GZip:GZip", "//third-party/ZipArchive:ZipArchive", "//submodules/InAppPurchaseManager:InAppPurchaseManager", - "//submodules/Utils/DarwinDirStat", ], visibility = [ "//visibility:public", diff --git a/submodules/DebugSettingsUI/Sources/DebugController.swift b/submodules/DebugSettingsUI/Sources/DebugController.swift index 46e8ae0508..8f2a768db3 100644 --- a/submodules/DebugSettingsUI/Sources/DebugController.swift +++ b/submodules/DebugSettingsUI/Sources/DebugController.swift @@ -16,7 +16,6 @@ import AppBundle import ZipArchive import WebKit import InAppPurchaseManager -import DarwinDirStat @objc private final class DebugControllerMailComposeDelegate: NSObject, MFMailComposeViewControllerDelegate { public func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) { @@ -948,15 +947,10 @@ private enum DebugControllerEntry: ItemListNodeEntry { case .resetTranslationStates: return ItemListActionItem(presentationData: presentationData, title: "Reset Translation States", kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: { if let context = arguments.context { - let size = statForDirectory(path: NSTemporaryDirectory()) - let controller = textAlertController(context: context, title: nil, text: "temp dir size \(size)", actions: [TextAlertAction(type: .genericAction, title: "OK", action: {})]) - arguments.presentController(controller, nil) + let _ = context.engine.itemCache.clear(collectionIds: [ + ApplicationSpecificItemCacheCollectionId.translationState + ]).start() } -// if let context = arguments.context { -// let _ = context.engine.itemCache.clear(collectionIds: [ -// ApplicationSpecificItemCacheCollectionId.translationState -// ]).start() -// } }) case .crash: return ItemListActionItem(presentationData: presentationData, title: "Crash", kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: { @@ -1545,30 +1539,3 @@ public func triggerDebugSendLogsUI(context: AccountContext, additionalInfo: Stri pushController(controller) }) } - -private func statForDirectory(path: String) -> Int64 { - if #available(macOS 10.13, *) { - var s = darwin_dirstat() - var result = dirstat_np(path, 1, &s, MemoryLayout.size) - if result != -1 { - return Int64(s.total_size) - } else { - result = dirstat_np(path, 0, &s, MemoryLayout.size) - if result != -1 { - return Int64(s.total_size) - } else { - return 0 - } - } - } else { - let fileManager = FileManager.default - let folderURL = URL(fileURLWithPath: path) - var folderSize: Int64 = 0 - if let files = try? fileManager.contentsOfDirectory(at: folderURL, includingPropertiesForKeys: nil, options: []) { - for file in files { - folderSize += (fileSize(file.path) ?? 0) - } - } - return folderSize - } -} diff --git a/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift b/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift index e8d9d6cc0a..99d606b32d 100644 --- a/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift +++ b/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift @@ -83,7 +83,7 @@ public final class InAppPurchaseManager: NSObject { } public func pricePerMonth(_ monthsCount: Int) -> String { - let price = self.skProduct.price.dividing(by: NSDecimalNumber(value: monthsCount)).prettyPrice().round(2) + let price = self.skProduct.price.dividing(by: NSDecimalNumber(value: monthsCount)).round(2) return self.numberFormatter.string(from: price) ?? "" } diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaVideoConverter.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaVideoConverter.h index 71a174799a..ed5638c23d 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaVideoConverter.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaVideoConverter.h @@ -16,11 +16,11 @@ @interface TGMediaVideoConverter : NSObject -+ (SSignal *)convertAVAsset:(AVAsset *)avAsset adjustments:(TGMediaVideoEditAdjustments *)adjustments watcher:(TGMediaVideoFileWatcher *)watcher entityRenderer:(id)entityRenderer; -+ (SSignal *)convertAVAsset:(AVAsset *)avAsset adjustments:(TGMediaVideoEditAdjustments *)adjustments watcher:(TGMediaVideoFileWatcher *)watcher inhibitAudio:(bool)inhibitAudio entityRenderer:(id)entityRenderer; ++ (SSignal *)convertAVAsset:(AVAsset *)avAsset adjustments:(TGMediaVideoEditAdjustments *)adjustments path:(NSString *)path watcher:(TGMediaVideoFileWatcher *)watcher entityRenderer:(id)entityRenderer; ++ (SSignal *)convertAVAsset:(AVAsset *)avAsset adjustments:(TGMediaVideoEditAdjustments *)adjustments path:(NSString *)path watcher:(TGMediaVideoFileWatcher *)watcher inhibitAudio:(bool)inhibitAudio entityRenderer:(id)entityRenderer; + (SSignal *)hashForAVAsset:(AVAsset *)avAsset adjustments:(TGMediaVideoEditAdjustments *)adjustments; -+ (SSignal *)renderUIImage:(UIImage *)image duration:(NSTimeInterval)duration adjustments:(TGMediaVideoEditAdjustments *)adjustments watcher:(TGMediaVideoFileWatcher *)watcher entityRenderer:(id)entityRenderer; ++ (SSignal *)renderUIImage:(UIImage *)image duration:(NSTimeInterval)duration adjustments:(TGMediaVideoEditAdjustments *)adjustments path:(NSString *)path watcher:(TGMediaVideoFileWatcher *)watcher entityRenderer:(id)entityRenderer; + (NSUInteger)estimatedSizeForPreset:(TGMediaVideoConversionPreset)preset duration:(NSTimeInterval)duration hasAudio:(bool)hasAudio; + (TGMediaVideoConversionPreset)bestAvailablePresetForDimensions:(CGSize)dimensions; diff --git a/submodules/LegacyComponents/Sources/TGMediaAssetsController.m b/submodules/LegacyComponents/Sources/TGMediaAssetsController.m index 92d6206d3d..6b2cd0c26e 100644 --- a/submodules/LegacyComponents/Sources/TGMediaAssetsController.m +++ b/submodules/LegacyComponents/Sources/TGMediaAssetsController.m @@ -1475,6 +1475,8 @@ } } + bool spoiler = [editingContext spoilerForItem:asset]; + if ([asset isKindOfClass:[UIImage class]]) { if (intent == TGMediaAssetsControllerSendFileIntent) { @@ -1513,6 +1515,10 @@ if (groupedId != nil && !hasAnyTimers) dict[@"groupedId"] = groupedId; + if (spoiler) { + dict[@"spoiler"] = @true; + } + id generatedItem = descriptionGenerator(dict, caption, nil, nil); return generatedItem; }]; @@ -1560,6 +1566,10 @@ if (groupedId != nil && !hasAnyTimers) dict[@"groupedId"] = groupedId; + if (spoiler) { + dict[@"spoiler"] = @true; + } + id generatedItem = descriptionGenerator(dict, caption, nil, nil); return generatedItem; }] catch:^SSignal *(__unused id error) @@ -1636,6 +1646,10 @@ else if (groupedId != nil && !hasAnyTimers) dict[@"groupedId"] = groupedId; + if (spoiler) { + dict[@"spoiler"] = @true; + } + id generatedItem = descriptionGenerator(dict, caption, nil, nil); return generatedItem; }]; diff --git a/submodules/LegacyComponents/Sources/TGMediaVideoConverter.m b/submodules/LegacyComponents/Sources/TGMediaVideoConverter.m index b8ec8db47a..4de1146338 100644 --- a/submodules/LegacyComponents/Sources/TGMediaVideoConverter.m +++ b/submodules/LegacyComponents/Sources/TGMediaVideoConverter.m @@ -101,12 +101,12 @@ @implementation TGMediaVideoConverter -+ (SSignal *)convertAVAsset:(AVAsset *)avAsset adjustments:(TGMediaVideoEditAdjustments *)adjustments watcher:(TGMediaVideoFileWatcher *)watcher entityRenderer:(id)entityRenderer ++ (SSignal *)convertAVAsset:(AVAsset *)avAsset adjustments:(TGMediaVideoEditAdjustments *)adjustments path:(NSString *)path watcher:(TGMediaVideoFileWatcher *)watcher entityRenderer:(id)entityRenderer { - return [self convertAVAsset:avAsset adjustments:adjustments watcher:watcher inhibitAudio:false entityRenderer:entityRenderer]; + return [self convertAVAsset:avAsset adjustments:adjustments path:path watcher:watcher inhibitAudio:false entityRenderer:entityRenderer]; } -+ (SSignal *)convertAVAsset:(AVAsset *)avAsset adjustments:(TGMediaVideoEditAdjustments *)adjustments watcher:(TGMediaVideoFileWatcher *)watcher inhibitAudio:(bool)inhibitAudio entityRenderer:(id)entityRenderer ++ (SSignal *)convertAVAsset:(AVAsset *)avAsset adjustments:(TGMediaVideoEditAdjustments *)adjustments path:(NSString *)path watcher:(TGMediaVideoFileWatcher *)watcher inhibitAudio:(bool)inhibitAudio entityRenderer:(id)entityRenderer { if ([avAsset isKindOfClass:[NSURL class]]) { avAsset = [[AVURLAsset alloc] initWithURL:(NSURL *)avAsset options:nil]; @@ -116,7 +116,7 @@ return [[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) { SAtomic *context = [[SAtomic alloc] initWithValue:[TGMediaVideoConversionContext contextWithQueue:queue subscriber:subscriber]]; - NSURL *outputUrl = [self _randomTemporaryURL]; + NSURL *outputUrl = [NSURL fileURLWithPath:path]; NSArray *requiredKeys = @[ @"tracks", @"duration" ]; [avAsset loadValuesAsynchronouslyForKeys:requiredKeys completionHandler:^ @@ -222,14 +222,14 @@ }]; } -+ (SSignal *)renderUIImage:(UIImage *)image duration:(NSTimeInterval)duration adjustments:(TGMediaVideoEditAdjustments *)adjustments watcher:(TGMediaVideoFileWatcher *)watcher entityRenderer:(id)entityRenderer ++ (SSignal *)renderUIImage:(UIImage *)image duration:(NSTimeInterval)duration adjustments:(TGMediaVideoEditAdjustments *)adjustments path:(NSString *)path watcher:(TGMediaVideoFileWatcher *)watcher entityRenderer:(id)entityRenderer { SQueue *queue = [[SQueue alloc] init]; return [[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) { SAtomic *context = [[SAtomic alloc] initWithValue:[TGMediaVideoConversionContext contextWithQueue:queue subscriber:subscriber]]; - NSURL *outputUrl = [self _randomTemporaryURL]; + NSURL *outputUrl = [NSURL fileURLWithPath:path]; NSString *path = TGComponentsPathForResource(@"blank", @"mp4"); AVAsset *avAsset = [[AVURLAsset alloc] initWithURL:[NSURL fileURLWithPath:path] options:nil]; @@ -895,11 +895,6 @@ return CGSizeMake(renderWidth, renderHeight); } -+ (NSURL *)_randomTemporaryURL -{ - return [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:[[NSString alloc] initWithFormat:@"%x.mp4", (int)arc4random()]]]; -} - + (NSUInteger)estimatedSizeForPreset:(TGMediaVideoConversionPreset)preset duration:(NSTimeInterval)duration hasAudio:(bool)hasAudio { NSInteger bitrate = [TGMediaVideoConversionPresetSettings _videoBitrateKbpsForPreset:preset]; diff --git a/submodules/LegacyComponents/Sources/TGPhotoQualityController.m b/submodules/LegacyComponents/Sources/TGPhotoQualityController.m index 30e065f806..0f1bd6a9e7 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoQualityController.m +++ b/submodules/LegacyComponents/Sources/TGPhotoQualityController.m @@ -648,6 +648,11 @@ const NSTimeInterval TGPhotoQualityPreviewDuration = 15.0f; [(TGPhotoEditorController *)self.parentViewController setInfoString:fileSize]; } ++ (NSString *)_randomTemporaryPath +{ + return [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSString alloc] initWithFormat:@"%x.mp4", (int)arc4random()]]; +} + - (void)generateVideoPreview { if (self.preset == _currentPreset) @@ -665,6 +670,8 @@ const NSTimeInterval TGPhotoQualityPreviewDuration = 15.0f; TGVideoEditAdjustments *adjustments = [self.photoEditor exportAdjustments]; adjustments = [adjustments editAdjustmentsWithPreset:self.preset maxDuration:TGPhotoQualityPreviewDuration]; + NSString *path = [TGPhotoQualityController _randomTemporaryPath]; + __block NSTimeInterval delay = 0.0; __weak TGPhotoQualityController *weakSelf = self; SSignal *convertSignal = [[assetSignal onNext:^(AVAsset *next) { @@ -680,7 +687,7 @@ const NSTimeInterval TGPhotoQualityPreviewDuration = 15.0f; { return [[[[[SSignal single:avAsset] delay:delay onQueue:[SQueue concurrentDefaultQueue]] mapToSignal:^SSignal *(AVAsset *avAsset) { - return [TGMediaVideoConverter convertAVAsset:avAsset adjustments:adjustments watcher:nil inhibitAudio:true entityRenderer:nil]; + return [TGMediaVideoConverter convertAVAsset:avAsset adjustments:adjustments path:path watcher:nil inhibitAudio:true entityRenderer:nil]; }] onError:^(__unused id error) { delay = 1.0; }] retryIf:^bool(__unused id error) diff --git a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift index 18f821da8a..1a334237b8 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift @@ -751,7 +751,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { case .assets: signals = TGMediaAssetsController.resultSignals(for: controller.interaction?.selectionState, editingContext: controller.interaction?.editingState, intent: asFile ? TGMediaAssetsControllerSendFileIntent : TGMediaAssetsControllerSendMediaIntent, currentItem: nil, storeAssets: true, convertToJpeg: convertToJpeg, descriptionGenerator: legacyAssetPickerItemGenerator(), saveEditedPhotos: controller.saveEditedPhotos) case .media: - signals = TGMediaAssetsController.pasteboardResultSignals(for: controller.interaction?.selectionState, editingContext: controller.interaction?.editingState, intent: asFile ? TGMediaAssetsControllerSendFileIntent : TGMediaAssetsControllerSendMediaIntent, currentItem: nil, descriptionGenerator: legacyAssetPickerItemGenerator()) + signals = TGMediaAssetsController.pasteboardResultSignals(for: controller.interaction?.selectionState, editingContext: controller.interaction?.editingState, intent: asFile ? TGMediaAssetsControllerSendFileIntent : TGMediaAssetsControllerSendMediaIntent, currentItem: nil, descriptionGenerator: legacyAssetPickerItemGenerator()) } guard let signals = signals else { return diff --git a/submodules/SearchBarNode/Sources/SearchBarNode.swift b/submodules/SearchBarNode/Sources/SearchBarNode.swift index 798792cd55..dedc477309 100644 --- a/submodules/SearchBarNode/Sources/SearchBarNode.swift +++ b/submodules/SearchBarNode/Sources/SearchBarNode.swift @@ -1067,6 +1067,8 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate { node.isHidden = true + self.textField.isUserInteractionEnabled = false + if !self.clearButton.isHidden { let xOffset = targetTextBackgroundFrame.width - self.textBackgroundNode.frame.width if !xOffset.isZero { @@ -1118,10 +1120,11 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate { var backgroundCompleted = false var separatorCompleted = false var textBackgroundCompleted = false - let intermediateCompletion: () -> Void = { [weak node] in + let intermediateCompletion: () -> Void = { [weak node, weak self] in if backgroundCompleted && separatorCompleted && textBackgroundCompleted { completion() node?.isHidden = false + self?.textField.isUserInteractionEnabled = true } } diff --git a/submodules/SettingsUI/Sources/Data and Storage/DataAndStorageSettingsController.swift b/submodules/SettingsUI/Sources/Data and Storage/DataAndStorageSettingsController.swift index 3c33297a21..5e6d8a8cee 100644 --- a/submodules/SettingsUI/Sources/Data and Storage/DataAndStorageSettingsController.swift +++ b/submodules/SettingsUI/Sources/Data and Storage/DataAndStorageSettingsController.swift @@ -30,6 +30,7 @@ private final class DataAndStorageControllerArguments { let openSaveIncoming: (AutomaticSaveIncomingPeerType) -> Void let toggleSaveEditedPhotos: (Bool) -> Void let togglePauseMusicOnRecording: (Bool) -> Void + let toggleRaiseToListen: (Bool) -> Void let toggleAutoplayGifs: (Bool) -> Void let toggleAutoplayVideos: (Bool) -> Void let toggleDownloadInBackground: (Bool) -> Void @@ -37,7 +38,7 @@ private final class DataAndStorageControllerArguments { let openIntents: () -> Void let toggleEnableSensitiveContent: (Bool) -> Void - init(openStorageUsage: @escaping () -> Void, openNetworkUsage: @escaping () -> Void, openProxy: @escaping () -> Void, openAutomaticDownloadConnectionType: @escaping (AutomaticDownloadConnectionType) -> Void, resetAutomaticDownload: @escaping () -> Void, toggleVoiceUseLessData: @escaping (Bool) -> Void, openSaveIncoming: @escaping (AutomaticSaveIncomingPeerType) -> Void, toggleSaveEditedPhotos: @escaping (Bool) -> Void, togglePauseMusicOnRecording: @escaping (Bool) -> Void, toggleAutoplayGifs: @escaping (Bool) -> Void, toggleAutoplayVideos: @escaping (Bool) -> Void, toggleDownloadInBackground: @escaping (Bool) -> Void, openBrowserSelection: @escaping () -> Void, openIntents: @escaping () -> Void, toggleEnableSensitiveContent: @escaping (Bool) -> Void) { + init(openStorageUsage: @escaping () -> Void, openNetworkUsage: @escaping () -> Void, openProxy: @escaping () -> Void, openAutomaticDownloadConnectionType: @escaping (AutomaticDownloadConnectionType) -> Void, resetAutomaticDownload: @escaping () -> Void, toggleVoiceUseLessData: @escaping (Bool) -> Void, openSaveIncoming: @escaping (AutomaticSaveIncomingPeerType) -> Void, toggleSaveEditedPhotos: @escaping (Bool) -> Void, togglePauseMusicOnRecording: @escaping (Bool) -> Void, toggleRaiseToListen: @escaping (Bool) -> Void, toggleAutoplayGifs: @escaping (Bool) -> Void, toggleAutoplayVideos: @escaping (Bool) -> Void, toggleDownloadInBackground: @escaping (Bool) -> Void, openBrowserSelection: @escaping () -> Void, openIntents: @escaping () -> Void, toggleEnableSensitiveContent: @escaping (Bool) -> Void) { self.openStorageUsage = openStorageUsage self.openNetworkUsage = openNetworkUsage self.openProxy = openProxy @@ -47,6 +48,7 @@ private final class DataAndStorageControllerArguments { self.openSaveIncoming = openSaveIncoming self.toggleSaveEditedPhotos = toggleSaveEditedPhotos self.togglePauseMusicOnRecording = togglePauseMusicOnRecording + self.toggleRaiseToListen = toggleRaiseToListen self.toggleAutoplayGifs = toggleAutoplayGifs self.toggleAutoplayVideos = toggleAutoplayVideos self.toggleDownloadInBackground = toggleDownloadInBackground @@ -74,6 +76,8 @@ public enum DataAndStorageEntryTag: ItemListItemTag, Equatable { case autoplayVideos case saveEditedPhotos case downloadInBackground + case pauseMusicOnRecording + case raiseToListen case autoSave(AutomaticSaveIncomingPeerType) public func isEqual(to other: ItemListItemTag) -> Bool { @@ -108,9 +112,11 @@ private enum DataAndStorageEntry: ItemListNodeEntry { case otherHeader(PresentationTheme, String) case shareSheet(PresentationTheme, String) case saveEditedPhotos(PresentationTheme, String, Bool) - case pauseMusicOnRecording(PresentationTheme, String, Bool) case openLinksIn(PresentationTheme, String, String) - + case pauseMusicOnRecording(PresentationTheme, String, Bool) + case raiseToListen(PresentationTheme, String, Bool) + case raiseToListenInfo(PresentationTheme, String) + case connectionHeader(PresentationTheme, String) case connectionProxy(PresentationTheme, String, String) case enableSensitiveContent(String, Bool) @@ -129,7 +135,7 @@ private enum DataAndStorageEntry: ItemListNodeEntry { return DataAndStorageSection.voiceCalls.rawValue case .autoplayHeader, .autoplayGifs, .autoplayVideos: return DataAndStorageSection.autoPlay.rawValue - case .otherHeader, .shareSheet, .saveEditedPhotos, .pauseMusicOnRecording, .openLinksIn: + case .otherHeader, .shareSheet, .saveEditedPhotos, .openLinksIn, .pauseMusicOnRecording, .raiseToListen, .raiseToListenInfo: return DataAndStorageSection.other.rawValue case .connectionHeader, .connectionProxy: return DataAndStorageSection.connection.rawValue @@ -152,14 +158,12 @@ private enum DataAndStorageEntry: ItemListNodeEntry { return 4 case .automaticDownloadReset: return 5 - case .autoSaveHeader: return 6 case let .autoSaveItem(index, _, _, _, _): return 7 + Int32(index) case .autoSaveInfo: return 20 - case .downloadInBackground: return 21 case .downloadInBackgroundInfo: @@ -180,16 +184,20 @@ private enum DataAndStorageEntry: ItemListNodeEntry { return 29 case .saveEditedPhotos: return 31 - case .pauseMusicOnRecording: - return 32 case .openLinksIn: + return 32 + case .pauseMusicOnRecording: return 33 - case .connectionHeader: + case .raiseToListen: return 34 - case .connectionProxy: + case .raiseToListenInfo: return 35 - case .enableSensitiveContent: + case .connectionHeader: return 36 + case .connectionProxy: + return 37 + case .enableSensitiveContent: + return 38 } } @@ -297,14 +305,26 @@ private enum DataAndStorageEntry: ItemListNodeEntry { } else { return false } + case let .openLinksIn(lhsTheme, lhsText, lhsValue): + if case let .openLinksIn(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue { + return true + } else { + return false + } case let .pauseMusicOnRecording(lhsTheme, lhsText, lhsValue): if case let .pauseMusicOnRecording(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue { return true } else { return false } - case let .openLinksIn(lhsTheme, lhsText, lhsValue): - if case let .openLinksIn(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue { + case let .raiseToListen(lhsTheme, lhsText, lhsValue): + if case let .raiseToListen(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue { + return true + } else { + return false + } + case let .raiseToListenInfo(lhsTheme, lhsText): + if case let .raiseToListenInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { return true } else { return false @@ -420,14 +440,20 @@ private enum DataAndStorageEntry: ItemListNodeEntry { return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in arguments.toggleSaveEditedPhotos(value) }, tag: DataAndStorageEntryTag.saveEditedPhotos) - case let .pauseMusicOnRecording(_, text, value): - return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in - arguments.togglePauseMusicOnRecording(value) - }, tag: DataAndStorageEntryTag.saveEditedPhotos) case let .openLinksIn(_, text, value): return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, action: { arguments.openBrowserSelection() }) + case let .pauseMusicOnRecording(_, text, value): + return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.togglePauseMusicOnRecording(value) + }, tag: DataAndStorageEntryTag.pauseMusicOnRecording) + case let .raiseToListen(_, text, value): + return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.toggleRaiseToListen(value) + }, tag: DataAndStorageEntryTag.raiseToListen) + case let .raiseToListenInfo(_, text): + return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section) case let .downloadInBackground(_, text, value): return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in arguments.toggleDownloadInBackground(value) @@ -646,8 +672,10 @@ private func dataAndStorageControllerEntries(state: DataAndStorageControllerStat entries.append(.shareSheet(presentationData.theme, presentationData.strings.ChatSettings_IntentsSettings)) } entries.append(.saveEditedPhotos(presentationData.theme, presentationData.strings.Settings_SaveEditedPhotos, data.generatedMediaStoreSettings.storeEditedPhotos)) - entries.append(.pauseMusicOnRecording(presentationData.theme, presentationData.strings.Settings_PauseMusicOnRecording, data.mediaInputSettings.pauseMusicOnRecording)) entries.append(.openLinksIn(presentationData.theme, presentationData.strings.ChatSettings_OpenLinksIn, defaultWebBrowser)) + entries.append(.pauseMusicOnRecording(presentationData.theme, presentationData.strings.Settings_PauseMusicOnRecording, data.mediaInputSettings.pauseMusicOnRecording)) + entries.append(.raiseToListen(presentationData.theme, presentationData.strings.Settings_RaiseToListen, data.mediaInputSettings.enableRaiseToSpeak)) + entries.append(.raiseToListenInfo(presentationData.theme, presentationData.strings.Settings_RaiseToListenInfo)) let proxyValue: String if let proxySettings = data.proxySettings, let activeServer = proxySettings.activeServer, proxySettings.enabled { @@ -878,6 +906,10 @@ public func dataAndStorageController(context: AccountContext, focusOnItemTag: Da let _ = updateMediaInputSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in return current.withUpdatedPauseMusicOnRecording(value) }).start() + }, toggleRaiseToListen: { value in + let _ = updateMediaInputSettingsInteractively(accountManager: context.sharedContext.accountManager, { + $0.withUpdatedEnableRaiseToSpeak(value) + }).start() }, toggleAutoplayGifs: { value in let _ = updateMediaDownloadSettingsInteractively(accountManager: context.sharedContext.accountManager, { settings in var settings = settings diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift index 10aa9a82f0..d97eca8d94 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift @@ -6296,6 +6296,8 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController return nil } } + + let tempFile = EngineTempBox.shared.tempFile(fileName: "video.mp4") let uploadInterface = LegacyLiveUploadInterface(context: context) let signal: SSignal if let url = asset as? URL, url.absoluteString.hasSuffix(".jpg"), let data = try? Data(contentsOf: url, options: [.mappedRead]), let image = UIImage(data: data), let entityRenderer = entityRenderer { @@ -6311,14 +6313,14 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController }) signal = durationSignal.map(toSignal: { duration -> SSignal in if let duration = duration as? Double { - return TGMediaVideoConverter.renderUIImage(image, duration: duration, adjustments: adjustments, watcher: nil, entityRenderer: entityRenderer)! + return TGMediaVideoConverter.renderUIImage(image, duration: duration, adjustments: adjustments, path: tempFile.path, watcher: nil, entityRenderer: entityRenderer)! } else { return SSignal.single(nil) } }) } else if let asset = asset as? AVAsset { - signal = TGMediaVideoConverter.convert(asset, adjustments: adjustments, watcher: uploadInterface, entityRenderer: entityRenderer)! + signal = TGMediaVideoConverter.convert(asset, adjustments: adjustments, path: tempFile.path, watcher: uploadInterface, entityRenderer: entityRenderer)! } else { signal = SSignal.complete() } @@ -6344,6 +6346,8 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController } account.postbox.mediaBox.storeResourceData(resource.id, data: data, synchronous: true) subscriber.putNext(resource) + + EngineTempBox.shared.dispose(tempFile) } } subscriber.putCompletion() diff --git a/submodules/TelegramStringFormatting/Sources/MessageContentKind.swift b/submodules/TelegramStringFormatting/Sources/MessageContentKind.swift index 600caaad12..990b07d8b0 100644 --- a/submodules/TelegramStringFormatting/Sources/MessageContentKind.swift +++ b/submodules/TelegramStringFormatting/Sources/MessageContentKind.swift @@ -418,7 +418,7 @@ public func foldLineBreaks(_ text: NSAttributedString) -> NSAttributedString { while true { if let range = remainingString.string.range(of: "\n") { let mappedRange = NSRange(range, in: remainingString.string) - lines.append(remainingString.attributedSubstring(from: NSRange(location: 0, length: mappedRange.upperBound))) + lines.append(remainingString.attributedSubstring(from: NSRange(location: 0, length: mappedRange.upperBound - 1))) remainingString.replaceCharacters(in: NSRange(location: 0, length: mappedRange.upperBound), with: "") } else { if lines.isEmpty { diff --git a/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/BackgroundColorComponent.swift b/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/BackgroundColorComponent.swift index 4272226fd6..d56e398b9e 100644 --- a/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/BackgroundColorComponent.swift +++ b/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/BackgroundColorComponent.swift @@ -77,7 +77,7 @@ final class BackgroundColorComponent: Component { let itemSize = CGSize(width: 30.0, height: 30.0) let sideInset: CGFloat = 12.0 let height: CGFloat = 50.0 - let delta = (availableSize.width - sideInset * 2.0 - CGFloat(values.count) * itemSize.width) / CGFloat(values.count - 1) + let delta = floorToScreenPixels((availableSize.width - sideInset * 2.0 - CGFloat(values.count) * itemSize.width) / CGFloat(values.count - 1)) for i in 0 ..< values.count { let view: ComponentView diff --git a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift index 23ec16609e..04b9b9f034 100644 --- a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -1136,7 +1136,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { } } - let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, format: .regular) + let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, format: .regular, associatedData: item.associatedData) var isReplyThread = false if case .replyThread = item.chatLocation { diff --git a/submodules/TelegramUI/Sources/ChatMessageAttachedContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageAttachedContentNode.swift index 3c61b94cf9..2ef2cd0fa2 100644 --- a/submodules/TelegramUI/Sources/ChatMessageAttachedContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageAttachedContentNode.swift @@ -391,7 +391,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode { } } - let dateText = stringForMessageTimestampStatus(accountPeerId: context.account.peerId, message: message, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, strings: presentationData.strings) + let dateText = stringForMessageTimestampStatus(accountPeerId: context.account.peerId, message: message, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, strings: presentationData.strings, associatedData: associatedData) var webpageGalleryMediaCount: Int? for media in message.media { diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift index 0bbe5642f3..7504cde026 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift @@ -1800,7 +1800,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode } else { dateFormat = .regular } - let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, format: dateFormat) + let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, format: dateFormat, associatedData: item.associatedData) let statusType: ChatMessageDateAndStatusType if incoming { diff --git a/submodules/TelegramUI/Sources/ChatMessageCallBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageCallBubbleContentNode.swift index 1b8016db8c..25aa0d6d85 100644 --- a/submodules/TelegramUI/Sources/ChatMessageCallBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageCallBubbleContentNode.swift @@ -167,7 +167,7 @@ class ChatMessageCallBubbleContentNode: ChatMessageBubbleContentNode { } } - let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings) + let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, associatedData: item.associatedData) let statusText: String if let callDuration = callDuration, callDuration > 1 { diff --git a/submodules/TelegramUI/Sources/ChatMessageContactBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageContactBubbleContentNode.swift index 4e2679138f..e1810b597c 100644 --- a/submodules/TelegramUI/Sources/ChatMessageContactBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageContactBubbleContentNode.swift @@ -188,7 +188,7 @@ class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode { } } - let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings) + let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, associatedData: item.associatedData) let statusType: ChatMessageDateAndStatusType? switch position { diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift index 8b9f9add4c..c1f0e3a66e 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift @@ -815,7 +815,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { edited = true } - let dateText = stringForMessageTimestampStatus(accountPeerId: arguments.context.account.peerId, message: arguments.message, dateTimeFormat: arguments.presentationData.dateTimeFormat, nameDisplayOrder: arguments.presentationData.nameDisplayOrder, strings: arguments.presentationData.strings) + let dateText = stringForMessageTimestampStatus(accountPeerId: arguments.context.account.peerId, message: arguments.message, dateTimeFormat: arguments.presentationData.dateTimeFormat, nameDisplayOrder: arguments.presentationData.nameDisplayOrder, strings: arguments.presentationData.strings, associatedData: arguments.associatedData) let displayReactionsInline = shouldDisplayInlineDateReactions(message: arguments.message, isPremium: arguments.associatedData.isPremium, forceInline: arguments.associatedData.forceInlineReactions) var reactionSettings: ChatMessageDateAndStatusNode.TrailingReactionSettings? diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift index 48768e4926..1f69e1a1b3 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift @@ -487,7 +487,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { } } - let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, format: .regular) + let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, format: .regular, associatedData: item.associatedData) let maxDateAndStatusWidth: CGFloat if case .bubble = statusDisplayType { diff --git a/submodules/TelegramUI/Sources/ChatMessageMapBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageMapBubbleContentNode.swift index e2709d08e8..b075a9b7e9 100644 --- a/submodules/TelegramUI/Sources/ChatMessageMapBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageMapBubbleContentNode.swift @@ -205,7 +205,7 @@ class ChatMessageMapBubbleContentNode: ChatMessageBubbleContentNode { } } - let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings) + let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, associatedData: item.associatedData) let statusType: ChatMessageDateAndStatusType? switch position { diff --git a/submodules/TelegramUI/Sources/ChatMessageMediaBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageMediaBubbleContentNode.swift index 3a5f317d6c..b212162374 100644 --- a/submodules/TelegramUI/Sources/ChatMessageMediaBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageMediaBubbleContentNode.swift @@ -237,8 +237,8 @@ class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode { } } } - - let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings) + + let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, associatedData: item.associatedData) let statusType: ChatMessageDateAndStatusType? switch preparePosition { diff --git a/submodules/TelegramUI/Sources/ChatMessagePollBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessagePollBubbleContentNode.swift index 692ee29ba9..84eab063fb 100644 --- a/submodules/TelegramUI/Sources/ChatMessagePollBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessagePollBubbleContentNode.swift @@ -1069,7 +1069,7 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode { } } - let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings) + let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, associatedData: item.associatedData) let statusType: ChatMessageDateAndStatusType? switch position { diff --git a/submodules/TelegramUI/Sources/ChatMessageRestrictedBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageRestrictedBubbleContentNode.swift index 2fe0f93251..437d1b8a93 100644 --- a/submodules/TelegramUI/Sources/ChatMessageRestrictedBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageRestrictedBubbleContentNode.swift @@ -71,7 +71,7 @@ class ChatMessageRestrictedBubbleContentNode: ChatMessageBubbleContentNode { } } - let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings) + let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, associatedData: item.associatedData) let statusType: ChatMessageDateAndStatusType? switch position { diff --git a/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift index a3f24aa215..d1740be7a9 100644 --- a/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift @@ -561,7 +561,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView { } } - let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, format: .regular) + let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, format: .regular, associatedData: item.associatedData) var isReplyThread = false if case .replyThread = item.chatLocation { diff --git a/submodules/TelegramUI/Sources/ChatMessageTextBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageTextBubbleContentNode.swift index b1642c0a50..ebac741e74 100644 --- a/submodules/TelegramUI/Sources/ChatMessageTextBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageTextBubbleContentNode.swift @@ -181,7 +181,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { } else { dateFormat = .regular } - let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, format: dateFormat) + let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, format: dateFormat, associatedData: item.associatedData) let statusType: ChatMessageDateAndStatusType? var displayStatus = false diff --git a/submodules/TelegramUI/Sources/ChatTranslationPanelNode.swift b/submodules/TelegramUI/Sources/ChatTranslationPanelNode.swift index a29233eb71..1ccf63ee3d 100644 --- a/submodules/TelegramUI/Sources/ChatTranslationPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTranslationPanelNode.swift @@ -226,8 +226,8 @@ final class ChatTranslationPanelNode: ASDisplayNode { } } - topLanguages.append(contentsOf: popularTranslationLanguages) - + topLanguages.append("") + var languages: [(String, String)] = [] let languageLocale = Locale(identifier: langCode) @@ -322,6 +322,8 @@ private final class TranslationContextReferenceContentSource: ContextReferenceCo } } +private let separatorHeight: CGFloat = 7.0 + private final class TranslationLanguagesContextMenuContent: ContextControllerItemsContent { private final class BackButtonNode: HighlightTrackingButtonNode { let highlightBackgroundNode: ASDisplayNode @@ -445,7 +447,7 @@ private final class TranslationLanguagesContextMenuContent: ContextControllerIte self.addSubnode(self.titleLabelNode) self.highligthedChanged = { [weak self] highlighted in - guard let strongSelf = self else { + guard let strongSelf = self, let language = strongSelf.language, !language.isEmpty else { return } if highlighted { @@ -461,6 +463,9 @@ private final class TranslationLanguagesContextMenuContent: ContextControllerIte } @objc private func pressed() { + guard let language = self.language, !language.isEmpty else { + return + } self.action() } @@ -476,7 +481,6 @@ private final class TranslationLanguagesContextMenuContent: ContextControllerIte } self.highlightBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor - self.separatorNode.backgroundColor = presentationData.theme.contextMenu.itemSeparatorColor self.highlightBackgroundNode.frame = CGRect(origin: CGPoint(), size: size) @@ -487,8 +491,15 @@ private final class TranslationLanguagesContextMenuContent: ContextControllerIte let titleFrame = CGRect(origin: CGPoint(x: sideInset, y: floor((size.height - titleSize.height) / 2.0)), size: titleSize) self.titleLabelNode.frame = titleFrame - self.separatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: size.height), size: CGSize(width: size.width, height: UIScreenPixel)) - self.separatorNode.isHidden = isLast + if language == "" { + self.separatorNode.backgroundColor = presentationData.theme.contextMenu.sectionSeparatorColor + self.separatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: separatorHeight)) + self.separatorNode.isHidden = false + } else { + self.separatorNode.backgroundColor = presentationData.theme.contextMenu.itemSeparatorColor + self.separatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: size.height), size: CGSize(width: size.width, height: UIScreenPixel)) + self.separatorNode.isHidden = isLast + } } } @@ -537,26 +548,6 @@ private final class TranslationLanguagesContextMenuContent: ContextControllerIte self.scrollNode.view.delegate = self self.clipsToBounds = true - -// self.stateDisposable = (self.listContext.state -// |> deliverOnMainQueue).start(next: { [weak self] state in -// guard let strongSelf = self else { -// return -// } -// let updatedState = ItemsState(listState: state, readStats: strongSelf.state.readStats) -// var animateIn = false -// if strongSelf.state.item(at: 0) == nil && updatedState.item(at: 0) != nil { -// animateIn = true -// } -// strongSelf.state = updatedState -// strongSelf.animateIn = true -// strongSelf.requestUpdate(strongSelf, animateIn ? .animated(duration: 0.2, curve: .easeInOut) : .immediate) -// if animateIn { -// for (_, itemNode) in strongSelf.itemNodes { -// itemNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) -// } -// } -// }) } func scrollViewDidScroll(_ scrollView: UIScrollView) { @@ -591,11 +582,23 @@ private final class TranslationLanguagesContextMenuContent: ContextControllerIte let minVisibleIndex = max(0, Int(floor(visibleBounds.minY / itemHeight))) let maxVisibleIndex = Int(ceil(visibleBounds.maxY / itemHeight)) - + + var separatorIndex = 0 + for i in 0 ..< self.languages.count { + if self.languages[i].0.isEmpty { + separatorIndex = i + break + } + } + if minVisibleIndex <= maxVisibleIndex { for index in minVisibleIndex ... maxVisibleIndex { - let itemFrame = CGRect(origin: CGPoint(x: 0.0, y: CGFloat(index) * itemHeight), size: CGSize(width: size.width, height: itemHeight)) - + let height = self.languages[index].0.isEmpty ? separatorHeight : itemHeight + var itemFrame = CGRect(origin: CGPoint(x: 0.0, y: CGFloat(index) * itemHeight), size: CGSize(width: size.width, height: height)) + if index > separatorIndex { + itemFrame.origin.y += separatorHeight - itemHeight + } + if index < self.languages.count { let (languageCode, displayTitle) = self.languages[index] validIds.insert(index) @@ -612,7 +615,7 @@ private final class TranslationLanguagesContextMenuContent: ContextControllerIte self.scrollNode.addSubnode(itemNode) } - itemNode.update(size: itemFrame.size, presentationData: presentationData, language: languageCode, displayTitle: displayTitle, isLast: index == self.languages.count - 1, syncronousLoad: syncronousLoad) + itemNode.update(size: itemFrame.size, presentationData: presentationData, language: languageCode, displayTitle: displayTitle, isLast: index == self.languages.count - 1 || index == separatorIndex - 1, syncronousLoad: syncronousLoad) itemNode.frame = itemFrame } } @@ -745,7 +748,7 @@ private final class TranslationLanguagesContextMenuContent: ContextControllerIte topContentHeight += backButtonFrame.height } if let separatorNode = self.separatorNode { - let separatorFrame = CGRect(origin: CGPoint(x: 0.0, y: topContentHeight), size: CGSize(width: constrainedSize.width, height: 7.0)) + let separatorFrame = CGRect(origin: CGPoint(x: 0.0, y: topContentHeight), size: CGSize(width: constrainedSize.width, height: separatorHeight)) separatorNode.backgroundColor = self.presentationData.theme.contextMenu.sectionSeparatorColor transition.updateFrame(node: separatorNode, frame: separatorFrame) topContentHeight += separatorFrame.height diff --git a/submodules/TelegramUI/Sources/CreateChannelController.swift b/submodules/TelegramUI/Sources/CreateChannelController.swift index bc7c6c6290..39ce3b3b2e 100644 --- a/submodules/TelegramUI/Sources/CreateChannelController.swift +++ b/submodules/TelegramUI/Sources/CreateChannelController.swift @@ -534,6 +534,8 @@ public func createChannelController(context: AccountContext, mode: CreateChannel return nil } } + + let tempFile = EngineTempBox.shared.tempFile(fileName: "video.mp4") let uploadInterface = LegacyLiveUploadInterface(context: context) let signal: SSignal if let url = asset as? URL, url.absoluteString.hasSuffix(".jpg"), let data = try? Data(contentsOf: url, options: [.mappedRead]), let image = UIImage(data: data), let entityRenderer = entityRenderer { @@ -549,14 +551,14 @@ public func createChannelController(context: AccountContext, mode: CreateChannel }) signal = durationSignal.map(toSignal: { duration -> SSignal in if let duration = duration as? Double { - return TGMediaVideoConverter.renderUIImage(image, duration: duration, adjustments: adjustments, watcher: nil, entityRenderer: entityRenderer)! + return TGMediaVideoConverter.renderUIImage(image, duration: duration, adjustments: adjustments, path: tempFile.path, watcher: nil, entityRenderer: entityRenderer)! } else { return SSignal.single(nil) } }) } else if let asset = asset as? AVAsset { - signal = TGMediaVideoConverter.convert(asset, adjustments: adjustments, watcher: uploadInterface, entityRenderer: entityRenderer)! + signal = TGMediaVideoConverter.convert(asset, adjustments: adjustments, path: tempFile.path, watcher: uploadInterface, entityRenderer: entityRenderer)! } else { signal = SSignal.complete() } @@ -582,6 +584,8 @@ public func createChannelController(context: AccountContext, mode: CreateChannel } context.account.postbox.mediaBox.storeResourceData(resource.id, data: data, synchronous: true) subscriber.putNext(resource) + + EngineTempBox.shared.dispose(tempFile) } } subscriber.putCompletion() diff --git a/submodules/TelegramUI/Sources/CreateGroupController.swift b/submodules/TelegramUI/Sources/CreateGroupController.swift index de1fca0a60..dae2f8f7bc 100644 --- a/submodules/TelegramUI/Sources/CreateGroupController.swift +++ b/submodules/TelegramUI/Sources/CreateGroupController.swift @@ -850,7 +850,6 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId] } let signal = Signal { subscriber in - let entityRenderer: LegacyPaintEntityRenderer? = adjustments.flatMap { adjustments in if let paintingData = adjustments.paintingData, paintingData.hasAnimation { return LegacyPaintEntityRenderer(account: context.account, adjustments: adjustments) @@ -858,6 +857,8 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId] return nil } } + + let tempFile = EngineTempBox.shared.tempFile(fileName: "video.mp4") let uploadInterface = LegacyLiveUploadInterface(context: context) let signal: SSignal if let url = asset as? URL, url.absoluteString.hasSuffix(".jpg"), let data = try? Data(contentsOf: url, options: [.mappedRead]), let image = UIImage(data: data), let entityRenderer = entityRenderer { @@ -873,14 +874,14 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId] }) signal = durationSignal.map(toSignal: { duration -> SSignal in if let duration = duration as? Double { - return TGMediaVideoConverter.renderUIImage(image, duration: duration, adjustments: adjustments, watcher: nil, entityRenderer: entityRenderer)! + return TGMediaVideoConverter.renderUIImage(image, duration: duration, adjustments: adjustments, path: tempFile.path, watcher: nil, entityRenderer: entityRenderer)! } else { return SSignal.single(nil) } }) } else if let asset = asset as? AVAsset { - signal = TGMediaVideoConverter.convert(asset, adjustments: adjustments, watcher: uploadInterface, entityRenderer: entityRenderer)! + signal = TGMediaVideoConverter.convert(asset, adjustments: adjustments, path: tempFile.path, watcher: uploadInterface, entityRenderer: entityRenderer)! } else { signal = SSignal.complete() } @@ -906,6 +907,8 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId] } context.account.postbox.mediaBox.storeResourceData(resource.id, data: data, synchronous: true) subscriber.putNext(resource) + + EngineTempBox.shared.dispose(tempFile) } } subscriber.putCompletion() diff --git a/submodules/TelegramUI/Sources/FetchVideoMediaResource.swift b/submodules/TelegramUI/Sources/FetchVideoMediaResource.swift index 315482de9b..6f35c73e81 100644 --- a/submodules/TelegramUI/Sources/FetchVideoMediaResource.swift +++ b/submodules/TelegramUI/Sources/FetchVideoMediaResource.swift @@ -265,7 +265,8 @@ public func fetchVideoLibraryMediaResource(account: Account, resource: VideoLibr return nil } } - let signal = TGMediaVideoConverter.convert(avAsset, adjustments: adjustments, watcher: VideoConversionWatcher(update: { path, size in + let tempFile = EngineTempBox.shared.tempFile(fileName: "video.mp4") + let signal = TGMediaVideoConverter.convert(avAsset, adjustments: adjustments, path: tempFile.path, watcher: VideoConversionWatcher(update: { path, size in var value = stat() if stat(path, &value) == 0 { if let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: [.mappedRead]) { @@ -298,6 +299,8 @@ public func fetchVideoLibraryMediaResource(account: Account, resource: VideoLibr subscriber.putError(.generic) } subscriber.putCompletion() + + EngineTempBox.shared.dispose(tempFile) } }, error: { _ in subscriber.putError(.generic) @@ -342,6 +345,7 @@ func fetchLocalFileVideoMediaResource(account: Account, resource: LocalFileVideo adjustments = TGVideoEditAdjustments(dictionary: dict) } } + let tempFile = EngineTempBox.shared.tempFile(fileName: "video.mp4") let updatedSize = Atomic(value: 0) let entityRenderer: LegacyPaintEntityRenderer? = adjustments.flatMap { adjustments in if let paintingData = adjustments.paintingData, paintingData.hasAnimation { @@ -366,7 +370,7 @@ func fetchLocalFileVideoMediaResource(account: Account, resource: LocalFileVideo signal = durationSignal.map(toSignal: { duration -> SSignal in if let duration = duration as? Double { - return TGMediaVideoConverter.renderUIImage(image, duration: duration, adjustments: adjustments, watcher: VideoConversionWatcher(update: { path, size in + return TGMediaVideoConverter.renderUIImage(image, duration: duration, adjustments: adjustments, path: tempFile.path, watcher: VideoConversionWatcher(update: { path, size in var value = stat() if stat(path, &value) == 0 { if let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: [.mappedRead]) { @@ -388,7 +392,7 @@ func fetchLocalFileVideoMediaResource(account: Account, resource: LocalFileVideo signal = SSignal.single(nil) } } else { - signal = TGMediaVideoConverter.convert(avAsset, adjustments: adjustments, watcher: VideoConversionWatcher(update: { path, size in + signal = TGMediaVideoConverter.convert(avAsset, adjustments: adjustments, path: tempFile.path, watcher: VideoConversionWatcher(update: { path, size in var value = stat() if stat(path, &value) == 0 { if let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: [.mappedRead]) { @@ -430,6 +434,8 @@ func fetchLocalFileVideoMediaResource(account: Account, resource: LocalFileVideo subscriber.putNext(.dataPart(resourceOffset: range!.lowerBound, data: data, range: range!, complete: false)) subscriber.putNext(.replaceHeader(data: data, range: 0 ..< 1024)) subscriber.putNext(.dataPart(resourceOffset: 0, data: Data(), range: 0 ..< 0, complete: true)) + + EngineTempBox.shared.dispose(tempFile) } } subscriber.putCompletion() diff --git a/submodules/TelegramUI/Sources/InstantVideoRadialStatusNode.swift b/submodules/TelegramUI/Sources/InstantVideoRadialStatusNode.swift index 2827f864c0..6357439f1f 100644 --- a/submodules/TelegramUI/Sources/InstantVideoRadialStatusNode.swift +++ b/submodules/TelegramUI/Sources/InstantVideoRadialStatusNode.swift @@ -5,6 +5,7 @@ import Display import SwiftSignalKit import UniversalMediaPlayer import LegacyComponents +import UIKitRuntimeUtils private final class InstantVideoRadialStatusNodeParameters: NSObject { let color: UIColor @@ -124,6 +125,8 @@ final class InstantVideoRadialStatusNode: ASDisplayNode, UIGestureRecognizerDele strongSelf.statusValue = status } }) + + self.view.disablesInteractiveTransitionGestureRecognizer = true } deinit { diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift index 250f353d63..e39997759f 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift @@ -2510,10 +2510,10 @@ final class PeerInfoHeaderNode: ASDisplayNode { func updateAvatarIsHidden(entry: AvatarGalleryEntry?) { if let entry = entry { self.avatarListNode.avatarContainerNode.containerNode.isHidden = entry == self.avatarListNode.listContainerNode.galleryEntries.first - self.editingContentNode.isHidden = entry == self.avatarListNode.listContainerNode.galleryEntries.first + self.editingContentNode.avatarNode.isHidden = entry == self.avatarListNode.listContainerNode.galleryEntries.first } else { self.avatarListNode.avatarContainerNode.containerNode.isHidden = false - self.editingContentNode.isHidden = false + self.editingContentNode.avatarNode.isHidden = false } self.avatarListNode.listContainerNode.updateEntryIsHidden(entry: entry) } diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 91aee0591f..5c4c349eea 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -7058,6 +7058,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate return nil } } + + let tempFile = EngineTempBox.shared.tempFile(fileName: "video.mp4") let uploadInterface = LegacyLiveUploadInterface(context: context) let signal: SSignal if let url = asset as? URL, url.absoluteString.hasSuffix(".jpg"), let data = try? Data(contentsOf: url, options: [.mappedRead]), let image = UIImage(data: data), let entityRenderer = entityRenderer { @@ -7073,14 +7075,13 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate }) signal = durationSignal.map(toSignal: { duration -> SSignal in if let duration = duration as? Double { - return TGMediaVideoConverter.renderUIImage(image, duration: duration, adjustments: adjustments, watcher: nil, entityRenderer: entityRenderer)! + return TGMediaVideoConverter.renderUIImage(image, duration: duration, adjustments: adjustments, path: tempFile.path, watcher: nil, entityRenderer: entityRenderer)! } else { return SSignal.single(nil) } }) - } else if let asset = asset as? AVAsset { - signal = TGMediaVideoConverter.convert(asset, adjustments: adjustments, watcher: uploadInterface, entityRenderer: entityRenderer)! + signal = TGMediaVideoConverter.convert(asset, adjustments: adjustments, path: tempFile.path, watcher: uploadInterface, entityRenderer: entityRenderer)! } else { signal = SSignal.complete() } @@ -7106,6 +7107,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate } account.postbox.mediaBox.storeResourceData(resource.id, data: data, synchronous: true) subscriber.putNext(resource) + + EngineTempBox.shared.dispose(tempFile) } } subscriber.putCompletion() diff --git a/submodules/TelegramUI/Sources/StringForMessageTimestampStatus.swift b/submodules/TelegramUI/Sources/StringForMessageTimestampStatus.swift index 23b31c06be..2769155594 100644 --- a/submodules/TelegramUI/Sources/StringForMessageTimestampStatus.swift +++ b/submodules/TelegramUI/Sources/StringForMessageTimestampStatus.swift @@ -5,6 +5,7 @@ import TelegramPresentationData import TelegramUIPreferences import TelegramStringFormatting import LocalizedPeerData +import AccountContext enum MessageTimestampStatusFormat { case regular @@ -28,7 +29,7 @@ private func dateStringForDay(strings: PresentationStrings, dateTimeFormat: Pres } } -func stringForMessageTimestampStatus(accountPeerId: PeerId, message: Message, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, strings: PresentationStrings, format: MessageTimestampStatusFormat = .regular) -> String { +func stringForMessageTimestampStatus(accountPeerId: PeerId, message: Message, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, strings: PresentationStrings, format: MessageTimestampStatusFormat = .regular, associatedData: ChatMessageItemAssociatedData) -> String { if let adAttribute = message.adAttribute { switch adAttribute.messageType { case .sponsored: @@ -86,6 +87,10 @@ func stringForMessageTimestampStatus(accountPeerId: PeerId, message: Message, da } } + if let subject = associatedData.subject, case .forwardedMessages = subject { + authorTitle = nil + } + if case .regular = format { if let authorTitle = authorTitle, !authorTitle.isEmpty { dateText = "\(authorTitle), \(dateText)" diff --git a/submodules/TelegramUIPreferences/Sources/MediaInputSettings.swift b/submodules/TelegramUIPreferences/Sources/MediaInputSettings.swift index b49f77ce31..85703b06da 100644 --- a/submodules/TelegramUIPreferences/Sources/MediaInputSettings.swift +++ b/submodules/TelegramUIPreferences/Sources/MediaInputSettings.swift @@ -8,7 +8,7 @@ public struct MediaInputSettings: Codable, Equatable { public let pauseMusicOnRecording: Bool public static var defaultSettings: MediaInputSettings { - return MediaInputSettings(enableRaiseToSpeak: true, pauseMusicOnRecording: false) + return MediaInputSettings(enableRaiseToSpeak: true, pauseMusicOnRecording: true) } public init(enableRaiseToSpeak: Bool, pauseMusicOnRecording: Bool) { @@ -20,14 +20,14 @@ public struct MediaInputSettings: Codable, Equatable { let container = try decoder.container(keyedBy: StringCodingKey.self) self.enableRaiseToSpeak = (try container.decode(Int32.self, forKey: "enableRaiseToSpeak")) != 0 - self.pauseMusicOnRecording = (try container.decodeIfPresent(Int32.self, forKey: "pauseMusicOnRecording") ?? 0) != 0 + self.pauseMusicOnRecording = (try container.decodeIfPresent(Int32.self, forKey: "pauseMusicOnRecording_v2") ?? 0) != 0 } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: StringCodingKey.self) try container.encode((self.enableRaiseToSpeak ? 1 : 0) as Int32, forKey: "enableRaiseToSpeak") - try container.encode((self.pauseMusicOnRecording ? 1 : 0) as Int32, forKey: "pauseMusicOnRecording") + try container.encode((self.pauseMusicOnRecording ? 1 : 0) as Int32, forKey: "pauseMusicOnRecording_v2") } public static func ==(lhs: MediaInputSettings, rhs: MediaInputSettings) -> Bool { From 8cc5fed85691cdd3e325f8b1cdf870e7be01a247 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Mon, 6 Feb 2023 02:49:21 +0400 Subject: [PATCH 03/12] Various fixes --- Telegram/Telegram-iOS/en.lproj/Localizable.strings | 2 ++ .../Sources/AuthorizationOptionText.swift | 4 ++-- .../TelegramEngine/Peers/PeerPhotoUpdater.swift | 10 ++++++++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 8ad165c8d4..6f6083f838 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -8889,3 +8889,5 @@ Sorry for the inconvenience."; "Settings.RaiseToListen" = "Raise to Listen"; "Settings.RaiseToListenInfo" = "Raise to Listen allows you to quickly listen and reply to incoming audio messages by raising the phone to your ear."; + +"Login.CodeSentCallText" = "Calling **%@** to dictate the code."; diff --git a/submodules/AuthorizationUtils/Sources/AuthorizationOptionText.swift b/submodules/AuthorizationUtils/Sources/AuthorizationOptionText.swift index 3f3bedffbe..b4b4b66532 100644 --- a/submodules/AuthorizationUtils/Sources/AuthorizationOptionText.swift +++ b/submodules/AuthorizationUtils/Sources/AuthorizationOptionText.swift @@ -22,9 +22,9 @@ public func authorizationCurrentOptionText(_ type: SentAuthorizationCodeType, ph let bold = MarkdownAttributeSet(font: Font.semibold(fontSize), textColor: primaryColor) return parseMarkdownIntoAttributedString(strings.Login_ShortCallTitle, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in nil }), textAlignment: .center) case .call: - return NSAttributedString(string: strings.Login_CodeSentCall, font: Font.regular(fontSize), textColor: primaryColor, paragraphAlignment: .center) + return parseMarkdownIntoAttributedString(strings.Login_CodeSentCallText(phoneNumber).string, attributes: attributes, textAlignment: .center) case .flashCall: - return NSAttributedString(string: strings.ChangePhoneNumberCode_Called, font: Font.regular(fontSize), textColor: primaryColor, paragraphAlignment: .center) + return parseMarkdownIntoAttributedString(strings.Login_CodeSentCallText(phoneNumber).string, attributes: attributes, textAlignment: .center) case .emailSetupRequired: return NSAttributedString(string: "", font: Font.regular(fontSize), textColor: primaryColor, paragraphAlignment: .center) case let .email(emailPattern, _, _, _, _): diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/PeerPhotoUpdater.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/PeerPhotoUpdater.swift index 77e74723b6..801b0fe16a 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/PeerPhotoUpdater.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/PeerPhotoUpdater.swift @@ -35,7 +35,13 @@ public enum SetCustomPeerPhotoMode { } func _internal_updateContactPhoto(account: Account, peerId: PeerId, resource: MediaResource?, videoResource: MediaResource?, videoStartTimestamp: Double?, markup: UploadPeerPhotoMarkup?, mode: SetCustomPeerPhotoMode, mapResourceToAvatarSizes: @escaping (MediaResource, [TelegramMediaImageRepresentation]) -> Signal<[Int: Data], NoError>) -> Signal { - return _internal_updatePeerPhoto(postbox: account.postbox, network: account.network, stateManager: account.stateManager, accountPeerId: account.peerId, peerId: peerId, photo: resource.flatMap({ _internal_uploadedPeerPhoto(postbox: account.postbox, network: account.network, resource: $0) }), video: videoResource.flatMap({ _internal_uploadedPeerVideo(postbox: account.postbox, network: account.network, messageMediaPreuploadManager: account.messageMediaPreuploadManager, resource: $0) |> map(Optional.init) }), videoStartTimestamp: videoStartTimestamp, markup: markup, customPeerPhotoMode: mode, mapResourceToAvatarSizes: mapResourceToAvatarSizes) + let photo: Signal? + if videoResource == nil && markup != nil, let resource = resource { + photo = .single(UploadedPeerPhotoData.withResource(resource)) + } else { + photo = resource.flatMap({ _internal_uploadedPeerPhoto(postbox: account.postbox, network: account.network, resource: $0) }) + } + return _internal_updatePeerPhoto(postbox: account.postbox, network: account.network, stateManager: account.stateManager, accountPeerId: account.peerId, peerId: peerId, photo: photo, video: videoResource.flatMap({ _internal_uploadedPeerVideo(postbox: account.postbox, network: account.network, messageMediaPreuploadManager: account.messageMediaPreuploadManager, resource: $0) |> map(Optional.init) }), videoStartTimestamp: videoStartTimestamp, markup: markup, customPeerPhotoMode: mode, mapResourceToAvatarSizes: mapResourceToAvatarSizes) } public struct UploadedPeerPhotoData { @@ -208,7 +214,7 @@ func _internal_updatePeerPhotoInternal(postbox: Postbox, network: Network, state if let _ = videoEmojiMarkup { flags |= (1 << 5) } - request = network.request(Api.functions.photos.uploadContactProfilePhoto(flags: flags, userId: inputUser, file: file, video: videoFile, videoStartTs: videoStartTimestamp, videoEmojiMarkup: videoEmojiMarkup)) + request = network.request(Api.functions.photos.uploadContactProfilePhoto(flags: flags, userId: inputUser, file: photoFile, video: videoFile, videoStartTs: videoStartTimestamp, videoEmojiMarkup: videoEmojiMarkup)) } else { request = .complete() } From 26ae313302e0efefad6b5b0d10f014fe86efa4a4 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Mon, 6 Feb 2023 13:35:03 +0400 Subject: [PATCH 04/12] Keep translation attribute if content haven't changed --- .../Sources/State/AccountStateManagementUtils.swift | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift index a4ae57f6dd..511563eefc 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift @@ -3513,6 +3513,7 @@ func replayFinalState( transaction.updateMessage(id, update: { previousMessage in var updatedFlags = message.flags var updatedLocalTags = message.localTags + var updatedAttributes = message.attributes if previousMessage.localTags.contains(.OutgoingLiveLocation) { updatedLocalTags.insert(.OutgoingLiveLocation) } @@ -3528,10 +3529,20 @@ func replayFinalState( return current }) + if previousMessage.text == message.text { + let previousEntities = previousMessage.textEntitiesAttribute?.entities ?? [] + let updatedEntities = (message.attributes.first(where: { $0 is TextEntitiesMessageAttribute }) as? TextEntitiesMessageAttribute)?.entities ?? [] + if previousEntities == updatedEntities, let translation = previousMessage.attributes.first(where: { $0 is TranslationMessageAttribute }) as? TranslationMessageAttribute { + if message.attributes.firstIndex(where: { $0 is TranslationMessageAttribute }) == nil { + updatedAttributes.append(translation) + } + } + } + if let message = locallyRenderedMessage(message: message, peers: peers) { generatedEvent = reactionGeneratedEvent(previousMessage.reactionsAttribute, message.reactionsAttribute, message: message, transaction: transaction) } - return .update(message.withUpdatedLocalTags(updatedLocalTags).withUpdatedFlags(updatedFlags)) + return .update(message.withUpdatedLocalTags(updatedLocalTags).withUpdatedFlags(updatedFlags).withUpdatedAttributes(updatedAttributes)) }) if let generatedEvent = generatedEvent { addedReactionEvents.append(generatedEvent) From a5bd95564333835bcd1f5cf595fac3a2773df629 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Mon, 6 Feb 2023 15:19:48 +0400 Subject: [PATCH 05/12] Various fixes --- .../HorizontalPeerItem/Sources/HorizontalPeerItem.swift | 4 +--- submodules/TelegramUI/Sources/ChatHistoryListNode.swift | 2 +- .../TelegramUIPreferences/Sources/MediaInputSettings.swift | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/submodules/HorizontalPeerItem/Sources/HorizontalPeerItem.swift b/submodules/HorizontalPeerItem/Sources/HorizontalPeerItem.swift index b35103ec43..db39d7e49c 100644 --- a/submodules/HorizontalPeerItem/Sources/HorizontalPeerItem.swift +++ b/submodules/HorizontalPeerItem/Sources/HorizontalPeerItem.swift @@ -217,17 +217,15 @@ public final class HorizontalPeerItemNode: ListViewItemNode { strongSelf.badgeBackgroundNode.isHidden = true } - var verticalOffset: CGFloat = 0.0 let state: RecentStatusOnlineIconState if case .actionSheet = item.mode { state = .panel - verticalOffset -= 9.0 } else { state = .regular } strongSelf.onlineNode.setImage(PresentationResourcesChatList.recentStatusOnlineIcon(item.theme, state: state), color: nil, transition: .immediate) - strongSelf.onlineNode.frame = CGRect(x: itemLayout.size.width - onlineLayout.width - 18.0, y: itemLayout.size.height - onlineLayout.height - 18.0 + verticalOffset, width: onlineLayout.width, height: onlineLayout.height) + strongSelf.onlineNode.frame = CGRect(x: itemLayout.size.width / 2.0 + 14.0, y: itemLayout.size.width - onlineLayout.height - 30.0, width: onlineLayout.width, height: onlineLayout.height) let _ = badgeApply() let _ = onlineApply(animateContent) diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index 508a8e7d72..92b2fef89c 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -1362,7 +1362,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { } }) } - if currentIsVisible { + if currentIsVisible && currentlyPlayingVideo { updatedScrollPosition = .index(index: .message(currentlyPlayingMessageId), position: .center(.bottom), directionHint: .Up, animated: true, highlight: true, displayLink: true) scrollAnimationCurve = .Spring(duration: 0.4) } diff --git a/submodules/TelegramUIPreferences/Sources/MediaInputSettings.swift b/submodules/TelegramUIPreferences/Sources/MediaInputSettings.swift index 85703b06da..bc85ba870d 100644 --- a/submodules/TelegramUIPreferences/Sources/MediaInputSettings.swift +++ b/submodules/TelegramUIPreferences/Sources/MediaInputSettings.swift @@ -20,7 +20,7 @@ public struct MediaInputSettings: Codable, Equatable { let container = try decoder.container(keyedBy: StringCodingKey.self) self.enableRaiseToSpeak = (try container.decode(Int32.self, forKey: "enableRaiseToSpeak")) != 0 - self.pauseMusicOnRecording = (try container.decodeIfPresent(Int32.self, forKey: "pauseMusicOnRecording_v2") ?? 0) != 0 + self.pauseMusicOnRecording = (try container.decodeIfPresent(Int32.self, forKey: "pauseMusicOnRecording_v2") ?? 1) != 0 } public func encode(to encoder: Encoder) throws { From 78b02192cf1bc855bb535408fb11b0364a5f1bcb Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Mon, 6 Feb 2023 18:28:18 +0400 Subject: [PATCH 06/12] Various improvements --- .../Telegram-iOS/en.lproj/Localizable.strings | 2 + .../Sources/InAppPurchaseManager.swift | 11 ++- .../Sources/Receipt.swift | 33 ++++++++- .../Sources/PremiumIntroScreen.swift | 59 ++++++++++++---- .../MediaNavigationAccessoryHeaderNode.swift | 6 +- .../Sources/TelegramBaseController.swift | 70 +++++++++---------- .../Sources/OverlayPlayerControlsNode.swift | 62 +++++++++++++--- 7 files changed, 180 insertions(+), 63 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 6f6083f838..292c5878d8 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -8891,3 +8891,5 @@ Sorry for the inconvenience."; "Settings.RaiseToListenInfo" = "Raise to Listen allows you to quickly listen and reply to incoming audio messages by raising the phone to your ear."; "Login.CodeSentCallText" = "Calling **%@** to dictate the code."; + +"Premium.Purchase.OnlyOneSubscriptionAllowed" = "You have already purchased Telegram Premium for another account. You can only have one Telegram Premium subscription on one Apple ID."; diff --git a/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift b/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift index 99d606b32d..adc0ba55c4 100644 --- a/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift +++ b/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift @@ -308,11 +308,17 @@ public final class InAppPurchaseManager: NSObject { return signal } - public func getValidTransactionIds() -> [String] { + public struct ReceiptPurchase: Equatable { + public let productId: String + public let transactionId: String + public let expirationDate: Date + } + + public func getReceiptPurchases() -> [ReceiptPurchase] { guard let data = getReceiptData(), let receipt = parseReceipt(data) else { return [] } - return receipt.purchases.map { $0.transactionId } + return receipt.purchases.map { ReceiptPurchase(productId: $0.productId, transactionId: $0.transactionId, expirationDate: $0.expirationDate) } } } @@ -359,7 +365,6 @@ extension InAppPurchaseManager: SKPaymentTransactionObserver { switch transaction.transactionState { case .purchased: Logger.shared.log("InAppPurchaseManager", "Account \(accountPeerId), transaction \(transaction.transactionIdentifier ?? ""), original transaction \(transaction.original?.transactionIdentifier ?? "none") purchased") - transactionState = .purchased(transactionId: transaction.transactionIdentifier) transactionsToAssign.append(transaction) case .restored: diff --git a/submodules/InAppPurchaseManager/Sources/Receipt.swift b/submodules/InAppPurchaseManager/Sources/Receipt.swift index 194e96c7b1..fbb5d1b9d4 100644 --- a/submodules/InAppPurchaseManager/Sources/Receipt.swift +++ b/submodules/InAppPurchaseManager/Sources/Receipt.swift @@ -7,6 +7,7 @@ private struct Asn1Tag { static let sequence: Int32 = 0x10 static let set: Int32 = 0x11 static let utf8String: Int32 = 0x0c + static let date: Int32 = 0x16 } private struct Asn1Entry { @@ -124,10 +125,12 @@ struct Receipt { fileprivate struct Tag { static let productIdentifier: Int32 = 1702 static let transactionIdentifier: Int32 = 1703 + static let expirationDate: Int32 = 1708 } let productId: String let transactionId: String + let expirationDate: Date } let purchases: [Purchase] @@ -192,6 +195,27 @@ func parseReceipt(_ data: Data) -> Receipt? { return Receipt(purchases: purchases) } +private func parseRfc3339Date(_ str: String) -> Date? { + let posixLocale = Locale(identifier: "en_US_POSIX") + + let formatter1 = DateFormatter() + formatter1.locale = posixLocale + formatter1.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ssX5" + formatter1.timeZone = TimeZone(secondsFromGMT: 0) + + let result = formatter1.date(from: str) + if result != nil { + return result + } + + let formatter2 = DateFormatter() + formatter2.locale = posixLocale + formatter2.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.SSSSSSX5" + formatter2.timeZone = TimeZone(secondsFromGMT: 0) + + return formatter2.date(from: str) +} + private func parsePurchaseAttributes(_ data: Data) -> Receipt.Purchase? { let root = parse(data) guard root.tag == Asn1Tag.set else { @@ -200,6 +224,7 @@ private func parsePurchaseAttributes(_ data: Data) -> Receipt.Purchase? { var productId: String? var transactionId: String? + var expirationDate: Date? let receiptAttributes = parseSequence(root.data) for attribute in receiptAttributes { @@ -219,12 +244,16 @@ private func parsePurchaseAttributes(_ data: Data) -> Receipt.Purchase? { let valEntry = parse(value) guard valEntry.tag == Asn1Tag.utf8String else { return nil } transactionId = String(bytes: valEntry.data, encoding: .utf8) + case Receipt.Purchase.Tag.expirationDate: + let valEntry = parse(value) + guard valEntry.tag == Asn1Tag.date else { return nil } + expirationDate = parseRfc3339Date(String(bytes: valEntry.data, encoding: .utf8) ?? "") default: break } } - guard let productId, let transactionId else { + guard let productId, let transactionId, let expirationDate else { return nil } - return Receipt.Purchase(productId: productId, transactionId: transactionId) + return Receipt.Purchase(productId: productId, transactionId: transactionId, expirationDate: expirationDate) } diff --git a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift index eac07cf23e..207ee5f4cc 100644 --- a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift @@ -1196,14 +1196,28 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { let otherPeerName: String? let products: [PremiumProduct]? let selectedProductId: String? - let validTransactionIds: [String] + let validPurchases: [InAppPurchaseManager.ReceiptPurchase] let promoConfiguration: PremiumPromoConfiguration? let present: (ViewController) -> Void let selectProduct: (String) -> Void let buy: () -> Void let updateIsFocused: (Bool) -> Void - init(context: AccountContext, source: PremiumSource, isPremium: Bool?, justBought: Bool, otherPeerName: String?, products: [PremiumProduct]?, selectedProductId: String?, validTransactionIds: [String], promoConfiguration: PremiumPromoConfiguration?, present: @escaping (ViewController) -> Void, selectProduct: @escaping (String) -> Void, buy: @escaping () -> Void, updateIsFocused: @escaping (Bool) -> Void) { + init( + context: AccountContext, + source: PremiumSource, + isPremium: Bool?, + justBought: Bool, + otherPeerName: String?, + products: [PremiumProduct]?, + selectedProductId: String?, + validPurchases: [InAppPurchaseManager.ReceiptPurchase], + promoConfiguration: PremiumPromoConfiguration?, + present: @escaping (ViewController) -> Void, + selectProduct: @escaping (String) -> Void, + buy: @escaping () -> Void, + updateIsFocused: @escaping (Bool) -> Void + ) { self.context = context self.source = source self.isPremium = isPremium @@ -1211,7 +1225,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { self.otherPeerName = otherPeerName self.products = products self.selectedProductId = selectedProductId - self.validTransactionIds = validTransactionIds + self.validPurchases = validPurchases self.promoConfiguration = promoConfiguration self.present = present self.selectProduct = selectProduct @@ -1241,7 +1255,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { if lhs.selectedProductId != rhs.selectedProductId { return false } - if lhs.validTransactionIds != rhs.validTransactionIds { + if lhs.validPurchases != rhs.validPurchases { return false } if lhs.promoConfiguration != rhs.promoConfiguration { @@ -1256,7 +1270,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { var products: [PremiumProduct]? var selectedProductId: String? - var validTransactionIds: [String] = [] + var validPurchases: [InAppPurchaseManager.ReceiptPurchase] = [] var isPremium: Bool? @@ -1276,7 +1290,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { var canUpgrade: Bool { if let products = self.products, let current = products.first(where: { $0.isCurrent }), let transactionId = current.transactionId { - if self.validTransactionIds.contains(transactionId) { + if self.validPurchases.contains(where: { $0.transactionId == transactionId }) { return products.first(where: { $0.months > current.months }) != nil } else { return false @@ -1373,7 +1387,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { let state = context.state state.products = context.component.products state.selectedProductId = context.component.selectedProductId - state.validTransactionIds = context.component.validTransactionIds + state.validPurchases = context.component.validPurchases state.isPremium = context.component.isPremium let theme = environment.theme @@ -1986,7 +2000,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent { private(set) var products: [PremiumProduct]? private(set) var selectedProductId: String? - fileprivate var validTransactionIds: [String] = [] + fileprivate var validPurchases: [InAppPurchaseManager.ReceiptPurchase] = [] var isPremium: Bool? var otherPeerName: String? @@ -2015,7 +2029,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent { var canUpgrade: Bool { if let products = self.products, let current = products.first(where: { $0.isCurrent }), let transactionId = current.transactionId { - if self.validTransactionIds.contains(transactionId) { + if self.validPurchases.contains(where: { $0.transactionId == transactionId }) { return products.first(where: { $0.months > current.months }) != nil } else { return false @@ -2036,7 +2050,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent { super.init() - self.validTransactionIds = context.inAppPurchaseManager?.getValidTransactionIds() ?? [] + self.validPurchases = context.inAppPurchaseManager?.getReceiptPurchases() ?? [] let availableProducts: Signal<[InAppPurchaseManager.Product], NoError> if let inAppPurchaseManager = context.inAppPurchaseManager { @@ -2136,7 +2150,28 @@ private final class PremiumIntroScreenComponent: CombinedComponent { let premiumProduct = self.products?.first(where: { $0.id == self.selectedProductId }), !self.inProgress else { return } + let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } + let isUpgrade = self.products?.first(where: { $0.isCurrent }) != nil + + var hasActiveSubsciption = false + if let data = self.context.currentAppConfiguration.with({ $0 }).data, let _ = data["ios_killswitch_disable_receipt_check"] { + + } else if !self.validPurchases.isEmpty && !isUpgrade { + let now = Date() + for purchase in self.validPurchases.reversed() { + if (purchase.productId.hasSuffix(".monthly") || purchase.productId.hasSuffix(".annual")) && purchase.expirationDate > now { + hasActiveSubsciption = true + } + } + } + + if hasActiveSubsciption { + let errorText = presentationData.strings.Premium_Purchase_OnlyOneSubscriptionAllowed + let alertController = textAlertController(context: self.context, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]) + self.present(alertController) + return + } addAppLogEvent(postbox: self.context.account.postbox, type: "premium.promo_screen_accept") @@ -2173,7 +2208,6 @@ private final class PremiumIntroScreenComponent: CombinedComponent { addAppLogEvent(postbox: strongSelf.context.account.postbox, type: "premium.promo_screen_fail") - let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } let errorText = presentationData.strings.Premium_Purchase_ErrorUnknown let alertController = textAlertController(context: strongSelf.context, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]) strongSelf.present(alertController) @@ -2198,7 +2232,6 @@ private final class PremiumIntroScreenComponent: CombinedComponent { strongSelf.updateInProgress(false) strongSelf.updated(transition: .immediate) - let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } var errorText: String? switch error { case .generic: @@ -2475,7 +2508,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent { otherPeerName: state.otherPeerName, products: state.products, selectedProductId: state.selectedProductId, - validTransactionIds: state.validTransactionIds, + validPurchases: state.validPurchases, promoConfiguration: state.promoConfiguration, present: context.component.present, selectProduct: { [weak state] productId in diff --git a/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryHeaderNode.swift b/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryHeaderNode.swift index fcdf028f53..4905cd1adf 100644 --- a/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryHeaderNode.swift +++ b/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryHeaderNode.swift @@ -188,7 +188,7 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi case .x0_5: self.rateButton.setContent(.image(optionsRateImage(rate: "0.5X", color: self.theme.rootController.navigationBar.accentTextColor))) case .x1: - self.rateButton.setContent(.image(optionsRateImage(rate: "2X", color: self.theme.rootController.navigationBar.controlColor))) + self.rateButton.setContent(.image(optionsRateImage(rate: "1X", color: self.theme.rootController.navigationBar.controlColor))) self.rateButton.accessibilityLabel = self.strings.VoiceOver_Media_PlaybackRate self.rateButton.accessibilityValue = self.strings.VoiceOver_Media_PlaybackRateNormal self.rateButton.accessibilityHint = self.strings.VoiceOver_Media_PlaybackRateChange @@ -378,7 +378,7 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi case .x0_5: self.rateButton.setContent(.image(optionsRateImage(rate: "0.5X", color: self.theme.rootController.navigationBar.accentTextColor))) case .x1: - self.rateButton.setContent(.image(optionsRateImage(rate: "2X", color: self.theme.rootController.navigationBar.controlColor))) + self.rateButton.setContent(.image(optionsRateImage(rate: "1X", color: self.theme.rootController.navigationBar.controlColor))) case .x1_5: self.rateButton.setContent(.image(optionsRateImage(rate: "1.5X", color: self.theme.rootController.navigationBar.accentTextColor))) case .x2: @@ -511,6 +511,8 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi if let rate = self.playbackBaseRate { switch rate { case .x1: + nextRate = .x1_5 + case .x1_5: nextRate = .x2 default: nextRate = .x1 diff --git a/submodules/TelegramBaseController/Sources/TelegramBaseController.swift b/submodules/TelegramBaseController/Sources/TelegramBaseController.swift index 349cbeaff8..9551940eec 100644 --- a/submodules/TelegramBaseController/Sources/TelegramBaseController.swift +++ b/submodules/TelegramBaseController/Sources/TelegramBaseController.swift @@ -687,41 +687,41 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder { } strongSelf.context.sharedContext.mediaManager.playlistControl(.setBaseRate(baseRate), type: type) - var hasTooltip = false - strongSelf.forEachController({ controller in - if let controller = controller as? UndoOverlayController { - hasTooltip = true - controller.dismissWithCommitAction() - } - return true - }) - - let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } - let slowdown: Bool? - if baseRate == .x1 { - slowdown = true - } else if baseRate == .x2 { - slowdown = false - } else { - slowdown = nil - } - if let slowdown = slowdown { - strongSelf.present( - UndoOverlayController( - presentationData: presentationData, - content: .audioRate( - slowdown: slowdown, - text: slowdown ? presentationData.strings.Conversation_AudioRateTooltipNormal : presentationData.strings.Conversation_AudioRateTooltipSpeedUp - ), - elevatedLayout: false, - animateInAsReplacement: hasTooltip, - action: { action in - return true - } - ), - in: .current - ) - } +// var hasTooltip = false +// strongSelf.forEachController({ controller in +// if let controller = controller as? UndoOverlayController { +// hasTooltip = true +// controller.dismissWithCommitAction() +// } +// return true +// }) +// +// let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } +// let slowdown: Bool? +// if baseRate == .x1 { +// slowdown = true +// } else if baseRate == .x2 { +// slowdown = false +// } else { +// slowdown = nil +// } +// if let slowdown = slowdown { +// strongSelf.present( +// UndoOverlayController( +// presentationData: presentationData, +// content: .audioRate( +// slowdown: slowdown, +// text: slowdown ? presentationData.strings.Conversation_AudioRateTooltipNormal : presentationData.strings.Conversation_AudioRateTooltipSpeedUp +// ), +// elevatedLayout: false, +// animateInAsReplacement: hasTooltip, +// action: { action in +// return true +// } +// ), +// in: .current +// ) +// } }) } mediaAccessoryPanel.togglePlayPause = { [weak self] in diff --git a/submodules/TelegramUI/Sources/OverlayPlayerControlsNode.swift b/submodules/TelegramUI/Sources/OverlayPlayerControlsNode.swift index cc3314ab97..41df961a91 100644 --- a/submodules/TelegramUI/Sources/OverlayPlayerControlsNode.swift +++ b/submodules/TelegramUI/Sources/OverlayPlayerControlsNode.swift @@ -35,6 +35,47 @@ private func generateCollapseIcon(theme: PresentationTheme) -> UIImage? { }) } +private func optionsRateImage(rate: String, color: UIColor = .white) -> UIImage? { + return generateImage(CGSize(width: 30.0, height: 16.0), rotatedContext: { size, context in + UIGraphicsPushContext(context) + + context.clear(CGRect(origin: CGPoint(), size: size)) + + let lineWidth = 1.0 + UIScreenPixel + context.setLineWidth(lineWidth) + context.setStrokeColor(color.cgColor) + + + let string = NSMutableAttributedString(string: rate, font: Font.with(size: 11.0, design: .round, weight: .bold), textColor: color) + + var offset = CGPoint(x: 1.0, y: 0.0) + var width: CGFloat + if rate.count >= 3 { + if rate == "0.5X" { + string.addAttribute(.kern, value: -0.8 as NSNumber, range: NSRange(string.string.startIndex ..< string.string.endIndex, in: string.string)) + offset.x += -0.5 + } else { + string.addAttribute(.kern, value: -0.5 as NSNumber, range: NSRange(string.string.startIndex ..< string.string.endIndex, in: string.string)) + offset.x += -0.3 + } + width = 29.0 + } else { + string.addAttribute(.kern, value: -0.5 as NSNumber, range: NSRange(string.string.startIndex ..< string.string.endIndex, in: string.string)) + width = 19.0 + offset.x += -0.3 + } + + let path = UIBezierPath(roundedRect: CGRect(x: floorToScreenPixels((size.width - width) / 2.0), y: 0.0, width: width, height: 16.0).insetBy(dx: lineWidth / 2.0, dy: lineWidth / 2.0), byRoundingCorners: .allCorners, cornerRadii: CGSize(width: 2.0, height: 2.0)) + context.addPath(path.cgPath) + context.strokePath() + + let boundingRect = string.boundingRect(with: size, options: [], context: nil) + string.draw(at: CGPoint(x: offset.x + floor((size.width - boundingRect.width) / 2.0), y: offset.y + UIScreenPixel + floor((size.height - boundingRect.height) / 2.0))) + + UIGraphicsPopContext() + }) +} + private let digitsSet = CharacterSet(charactersIn: "0123456789") private func timestampLabelWidthForDuration(_ timestamp: Double) -> CGFloat { let text: String @@ -375,8 +416,10 @@ final class OverlayPlayerControlsNode: ASDisplayNode { } let baseRate: AudioPlaybackRate - if !value.status.baseRate.isEqual(to: 1.0) { + if value.status.baseRate.isEqual(to: 2.0) { baseRate = .x2 + } else if value.status.baseRate.isEqual(to: 1.5) { + baseRate = .x1_5 } else { baseRate = .x1 } @@ -715,10 +758,9 @@ final class OverlayPlayerControlsNode: ASDisplayNode { } private func updateOrderButton(_ order: MusicPlaybackSettingsOrder) { - let baseColor = self.presentationData.theme.list.itemSecondaryTextColor switch order { case .regular: - self.orderButton.icon = generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/OrderReverse"), color: baseColor) + self.orderButton.icon = generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/OrderReverse"), color: self.presentationData.theme.list.itemSecondaryTextColor) case .reversed: self.orderButton.icon = generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/OrderReverse"), color: self.presentationData.theme.list.itemAccentColor) case .random: @@ -740,10 +782,12 @@ final class OverlayPlayerControlsNode: ASDisplayNode { private func updateRateButton(_ baseRate: AudioPlaybackRate) { switch baseRate { - case .x2: - self.rateButton.setImage(PresentationResourcesRootController.navigationPlayerMaximizedRateActiveIcon(self.presentationData.theme), for: []) - default: - self.rateButton.setImage(PresentationResourcesRootController.navigationPlayerMaximizedRateInactiveIcon(self.presentationData.theme), for: []) + case .x2: + self.rateButton.setImage(optionsRateImage(rate: "2X", color: self.presentationData.theme.list.itemAccentColor), for: []) + case .x1_5: + self.rateButton.setImage(optionsRateImage(rate: "1.5X", color: self.presentationData.theme.list.itemAccentColor), for: []) + default: + self.rateButton.setImage(optionsRateImage(rate: "1X", color: self.presentationData.theme.list.itemSecondaryTextColor), for: []) } } @@ -959,12 +1003,14 @@ final class OverlayPlayerControlsNode: ASDisplayNode { if let currentRate = self.currentRate { switch currentRate { case .x1: + nextRate = .x1_5 + case .x1_5: nextRate = .x2 default: nextRate = .x1 } } else { - nextRate = .x2 + nextRate = .x1_5 } self.control?(.setBaseRate(nextRate)) } From 7b746eb9b219c6e9088dffebc438e08f964b07f8 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Mon, 6 Feb 2023 19:58:06 +0400 Subject: [PATCH 07/12] Accessibility improvements --- .../Sources/ContactsController.swift | 2 ++ .../Sources/InstantPageNavigationBar.swift | 4 ++++ .../Items/ItemListDisclosureItem.swift | 4 ++-- .../PremiumUI/Sources/PremiumGiftScreen.swift | 4 ++++ .../Sources/PremiumIntroScreen.swift | 19 ++++++++++++++++--- .../Sources/SolidRoundedButtonNode.swift | 14 ++++++++++++++ .../ChatChannelSubscriberInputPanelNode.swift | 1 + .../TelegramUI/Sources/ChatQrCodeScreen.swift | 1 + .../Sources/PeerInfo/PeerInfoHeaderNode.swift | 12 +++++++++++- 9 files changed, 55 insertions(+), 6 deletions(-) diff --git a/submodules/ContactListUI/Sources/ContactsController.swift b/submodules/ContactListUI/Sources/ContactsController.swift index f9c170d984..e3f975a779 100644 --- a/submodules/ContactListUI/Sources/ContactsController.swift +++ b/submodules/ContactListUI/Sources/ContactsController.swift @@ -82,6 +82,8 @@ private final class SortHeaderButton: HighlightableButtonNode { self.containerNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: 44.0)) self.referenceNode.frame = self.containerNode.bounds + + self.accessibilityLabel = strings.Contacts_Sort } override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize { diff --git a/submodules/InstantPageUI/Sources/InstantPageNavigationBar.swift b/submodules/InstantPageUI/Sources/InstantPageNavigationBar.swift index 127327ec9b..b5cb174eaf 100644 --- a/submodules/InstantPageUI/Sources/InstantPageNavigationBar.swift +++ b/submodules/InstantPageUI/Sources/InstantPageNavigationBar.swift @@ -91,6 +91,7 @@ final class InstantPageNavigationBar: ASDisplayNode { self.moreButton = HighlightableButtonNode() self.actionButton = HighlightableButtonNode() self.scrollToTopButton = HighlightableButtonNode() + self.scrollToTopButton.isAccessibilityElement = false self.actionButton.setImage(actionImage, for: []) self.intrinsicActionSize = CGSize(width: 44.0, height: 44.0) @@ -128,6 +129,9 @@ final class InstantPageNavigationBar: ASDisplayNode { self.addSubnode(self.titleNode) self.addSubnode(self.progressNode) + self.actionButton.accessibilityLabel = strings.KeyCommand_Share + self.backButton.accessibilityLabel = strings.Common_Back + self.backButton.addTarget(self, action: #selector(self.backPressed), forControlEvents: .touchUpInside) self.actionButton.addTarget(self, action: #selector(self.actionPressed), forControlEvents: .touchUpInside) self.moreButton.addTarget(self, action: #selector(self.morePressed), forControlEvents: .touchUpInside) diff --git a/submodules/ItemListUI/Sources/Items/ItemListDisclosureItem.swift b/submodules/ItemListUI/Sources/Items/ItemListDisclosureItem.swift index 3b2ba21ecd..fbcd00ba92 100644 --- a/submodules/ItemListUI/Sources/Items/ItemListDisclosureItem.swift +++ b/submodules/ItemListUI/Sources/Items/ItemListDisclosureItem.swift @@ -430,9 +430,9 @@ public class ItemListDisclosureItemNode: ListViewItemNode, ItemListItemNode { strongSelf.activateArea.accessibilityLabel = item.title strongSelf.activateArea.accessibilityValue = item.label if item.enabled { - strongSelf.activateArea.accessibilityTraits = [] + strongSelf.activateArea.accessibilityTraits = [.button] } else { - strongSelf.activateArea.accessibilityTraits = .notEnabled + strongSelf.activateArea.accessibilityTraits = [.button, .notEnabled] } if let icon = item.icon { diff --git a/submodules/PremiumUI/Sources/PremiumGiftScreen.swift b/submodules/PremiumUI/Sources/PremiumGiftScreen.swift index 264a299943..96ffb99c67 100644 --- a/submodules/PremiumUI/Sources/PremiumGiftScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumGiftScreen.swift @@ -260,11 +260,13 @@ private final class PremiumGiftScreenContentComponent: CombinedComponent { let defaultPrice = product.storeProduct.defaultPrice(shortestOptionPrice.1, monthsCount: Int(product.months)) var subtitle = "" + var accessibilitySubtitle = "" var pricePerMonth = product.storeProduct.pricePerMonth(Int(product.months)) pricePerMonth = environment.strings.Premium_PricePerMonth(pricePerMonth).string if discountValue > 0 { subtitle = "**\(defaultPrice)** \(product.price)" + accessibilitySubtitle = product.price } items.append(SectionGroupComponent.Item( @@ -285,6 +287,7 @@ private final class PremiumGiftScreenContentComponent: CombinedComponent { ) ) ), + accessibilityLabel: "\(giftTitle). \(accessibilitySubtitle). \(pricePerMonth)", action: { component.selectProduct(product.id) }) @@ -358,6 +361,7 @@ private final class PremiumGiftScreenContentComponent: CombinedComponent { ) ) ), + accessibilityLabel: "\(perk.title(strings: strings)). \(perk.subtitle(strings: strings))", action: { [weak state] in var demoSubject: PremiumDemoScreen.Subject switch perk { diff --git a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift index 207ee5f4cc..c33bef1b67 100644 --- a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift @@ -842,11 +842,13 @@ private final class CheckComponent: Component { final class SectionGroupComponent: Component { public final class Item: Equatable { public let content: AnyComponentWithIdentity + public let accessibilityLabel: String public let isEnabled: Bool public let action: () -> Void - public init(_ content: AnyComponentWithIdentity, isEnabled: Bool = true, action: @escaping () -> Void) { + public init(_ content: AnyComponentWithIdentity, accessibilityLabel: String, isEnabled: Bool = true, action: @escaping () -> Void) { self.content = content + self.accessibilityLabel = accessibilityLabel self.isEnabled = isEnabled self.action = action } @@ -855,6 +857,9 @@ final class SectionGroupComponent: Component { if lhs.content != rhs.content { return false } + if lhs.accessibilityLabel != rhs.accessibilityLabel { + return false + } if lhs.isEnabled != rhs.isEnabled { return false } @@ -947,6 +952,7 @@ final class SectionGroupComponent: Component { self.buttonViews[item.content.id] = buttonView self.addSubview(buttonView) } + buttonView.accessibilityLabel = item.accessibilityLabel if let current = self.itemViews[item.content.id] { itemView = current @@ -1105,7 +1111,8 @@ final class PerkComponent: CombinedComponent { colors: component.iconBackgroundColors, cornerRadius: 7.0, gradientDirection: .vertical), - availableSize: iconSize, transition: context.transition + availableSize: iconSize, + transition: context.transition ) let icon = icon.update( @@ -1179,7 +1186,7 @@ final class PerkComponent: CombinedComponent { context.add(arrow .position(CGPoint(x: context.availableSize.width - 7.0 - arrow.size.width / 2.0, y: size.height / 2.0)) ) - + return size } } @@ -1557,14 +1564,17 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { let defaultPrice = product.storeProduct.defaultPrice(shortestOptionPrice.1, monthsCount: Int(product.months)) var subtitle = "" + var accessibilitySubtitle = "" var pricePerMonth = product.price if product.months > 1 { pricePerMonth = product.storeProduct.pricePerMonth(Int(product.months)) if discountValue > 0 { subtitle = "**\(defaultPrice)** \(product.price)" + accessibilitySubtitle = product.price if product.months == 12 { subtitle = environment.strings.Premium_PricePerYear(subtitle).string + accessibilitySubtitle = environment.strings.Premium_PricePerYear(accessibilitySubtitle).string } } else { subtitle = product.price @@ -1572,6 +1582,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { } if product.isCurrent { subtitle = environment.strings.Premium_CurrentPlan + accessibilitySubtitle = subtitle } pricePerMonth = environment.strings.Premium_PricePerMonth(pricePerMonth).string @@ -1594,6 +1605,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { ) ) ), + accessibilityLabel: "\(giftTitle). \(accessibilitySubtitle). \(pricePerMonth)", isEnabled: product.months > currentProductMonths, action: { selectProduct(product.id) @@ -1651,6 +1663,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { ) ) ), + accessibilityLabel: "\(perk.title(strings: strings)). \(perk.subtitle(strings: strings))", action: { [weak state] in var demoSubject: PremiumDemoScreen.Subject switch perk { diff --git a/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift b/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift index 5322b39083..7e56eb8946 100644 --- a/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift +++ b/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift @@ -106,6 +106,7 @@ public final class SolidRoundedButtonNode: ASDisplayNode { public var title: String? { didSet { + self.updateAccessibilityLabels() if let width = self.validLayout { _ = self.updateLayout(width: width, transition: .immediate) } @@ -114,6 +115,7 @@ public final class SolidRoundedButtonNode: ASDisplayNode { public var subtitle: String? { didSet { + self.updateAccessibilityLabels() if let width = self.validLayout { _ = self.updateLayout(width: width, previousSubtitle: oldValue, transition: .immediate) } @@ -126,6 +128,10 @@ public final class SolidRoundedButtonNode: ASDisplayNode { } } + private func updateAccessibilityLabels() { + self.accessibilityLabel = (self.title ?? "") + " " + (self.subtitle ?? "") + } + private var animationTimer: SwiftSignalKit.Timer? public var animation: String? { didSet { @@ -759,6 +765,7 @@ public final class SolidRoundedButtonView: UIView { public var title: String? { didSet { + self.updateAccessibilityLabels() if let width = self.validLayout { _ = self.updateLayout(width: width, transition: .immediate) } @@ -767,6 +774,7 @@ public final class SolidRoundedButtonView: UIView { public var label: String? { didSet { + self.updateAccessibilityLabels() if let width = self.validLayout { _ = self.updateLayout(width: width, transition: .immediate) } @@ -775,12 +783,18 @@ public final class SolidRoundedButtonView: UIView { public var subtitle: String? { didSet { + self.updateAccessibilityLabels() if let width = self.validLayout { _ = self.updateLayout(width: width, previousSubtitle: oldValue, transition: .immediate) } } } + private func updateAccessibilityLabels() { + self.accessibilityLabel = (self.title ?? "") + " " + (self.subtitle ?? "") + self.accessibilityValue = self.label + } + public var icon: UIImage? { didSet { self.iconNode.image = generateTintedImage(image: self.icon, color: self.theme.foregroundColor) diff --git a/submodules/TelegramUI/Sources/ChatChannelSubscriberInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatChannelSubscriberInputPanelNode.swift index b5c158d70a..d2b1b0e65c 100644 --- a/submodules/TelegramUI/Sources/ChatChannelSubscriberInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatChannelSubscriberInputPanelNode.swift @@ -312,6 +312,7 @@ final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode { } self.button.setTitle(title, with: Font.regular(17.0), with: color, for: []) + self.button.accessibilityLabel = title } else { self.action = nil } diff --git a/submodules/TelegramUI/Sources/ChatQrCodeScreen.swift b/submodules/TelegramUI/Sources/ChatQrCodeScreen.swift index 2e59902f73..ca44feaeb0 100644 --- a/submodules/TelegramUI/Sources/ChatQrCodeScreen.swift +++ b/submodules/TelegramUI/Sources/ChatQrCodeScreen.swift @@ -859,6 +859,7 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, UIScrollViewDeleg self.cancelButton = HighlightableButtonNode() self.cancelButton.setImage(closeButtonImage(theme: self.presentationData.theme), for: .normal) + self.cancelButton.accessibilityLabel = self.presentationData.strings.Common_Close self.switchThemeButton = HighlightTrackingButtonNode() self.animationContainerNode = ASDisplayNode() diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift index e39997759f..bdda287c7d 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift @@ -1322,6 +1322,7 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { self.theme = presentationData.theme let text: String + var accessibilityText: String var icon: UIImage? var isBold = false var isGestureEnabled = false @@ -1330,33 +1331,42 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { switch key { case .edit: text = presentationData.strings.Common_Edit + accessibilityText = text case .done, .cancel, .selectionDone: text = presentationData.strings.Common_Done + accessibilityText = text isBold = true case .select: text = presentationData.strings.Common_Select + accessibilityText = text case .search: text = "" + accessibilityText = presentationData.strings.Common_Search icon = nil// PresentationResourcesRootController.navigationCompactSearchIcon(presentationData.theme) isAnimation = true animationState = .search case .editPhoto: text = presentationData.strings.Settings_EditPhoto + accessibilityText = text case .editVideo: text = presentationData.strings.Settings_EditVideo + accessibilityText = text case .more: text = "" + accessibilityText = presentationData.strings.Common_More icon = nil// PresentationResourcesRootController.navigationMoreCircledIcon(presentationData.theme) isGestureEnabled = true isAnimation = true animationState = .more case .qrCode: text = "" + accessibilityText = presentationData.strings.PeerInfo_QRCode_Title icon = PresentationResourcesRootController.navigationQrCodeIcon(presentationData.theme) case .moreToSearch: text = "" + accessibilityText = "" } - self.accessibilityLabel = text + self.accessibilityLabel = accessibilityText self.containerNode.isGestureEnabled = isGestureEnabled let font: UIFont = isBold ? Font.semibold(17.0) : Font.regular(17.0) From f56320215b9babd253201d60ee788e81247c8553 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 7 Feb 2023 03:29:47 +0400 Subject: [PATCH 08/12] Various improvements --- .../Telegram-iOS/en.lproj/Localizable.strings | 4 ++ .../Sources/ContactsController.swift | 1 + submodules/Display/Source/NavigationBar.swift | 4 +- .../Sources/PasscodeEntryControllerNode.swift | 6 ++- .../Sources/PasscodeEntryKeyboardNode.swift | 3 ++ .../TwoFactorAuthDataInputScreen.swift | 2 +- .../Sources/TwoFactorAuthSplashScreen.swift | 2 +- .../QrCodeUI/Sources/QrCodeScanScreen.swift | 2 +- .../TermsOfServiceController.swift | 2 +- .../Sources/SolidRoundedButtonNode.swift | 4 ++ .../Sources/TabBarContollerNode.swift | 11 ++++-- .../TabBarUI/Sources/TabBarController.swift | 7 +++- submodules/TabBarUI/Sources/TabBarNode.swift | 17 ++++++++- .../Sources/CallControllerButtonsNode.swift | 37 +++++++++++++++++++ .../Sources/CallControllerNode.swift | 3 ++ .../Sources/CallControllerStatusNode.swift | 18 +++++++++ .../Sources/ComponentsThemes.swift | 2 +- .../Sources/ChatButtonKeyboardInputNode.swift | 3 ++ .../TelegramUI/Sources/ChatQrCodeScreen.swift | 1 + .../PeerInfoScreenLabeledValueItem.swift | 2 + .../Sources/PeerInfo/PeerInfoHeaderNode.swift | 2 + .../Sources/PeerSelectionController.swift | 26 ++++++++++++- .../WebUI/Sources/WebAppController.swift | 4 +- 23 files changed, 145 insertions(+), 18 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 292c5878d8..5aaded99a0 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -8893,3 +8893,7 @@ Sorry for the inconvenience."; "Login.CodeSentCallText" = "Calling **%@** to dictate the code."; "Premium.Purchase.OnlyOneSubscriptionAllowed" = "You have already purchased Telegram Premium for another account. You can only have one Telegram Premium subscription on one Apple ID."; + +"Call.VoiceOver.Minimize" = "Minimize Call"; + +"VoiceOver.Tab" = "Tab"; diff --git a/submodules/ContactListUI/Sources/ContactsController.swift b/submodules/ContactListUI/Sources/ContactsController.swift index e3f975a779..d1cb89b843 100644 --- a/submodules/ContactListUI/Sources/ContactsController.swift +++ b/submodules/ContactListUI/Sources/ContactsController.swift @@ -203,6 +203,7 @@ public class ContactsController: ViewController { self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) self.navigationItem.leftBarButtonItem = UIBarButtonItem(customDisplayNode: self.sortButton) + self.navigationItem.leftBarButtonItem?.accessibilityLabel = self.presentationData.strings.Contacts_Sort self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationAddIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.addPressed)) self.navigationItem.rightBarButtonItem?.accessibilityLabel = self.presentationData.strings.Contacts_VoiceOver_AddContact diff --git a/submodules/Display/Source/NavigationBar.swift b/submodules/Display/Source/NavigationBar.swift index 7752442d25..a3b935c357 100644 --- a/submodules/Display/Source/NavigationBar.swift +++ b/submodules/Display/Source/NavigationBar.swift @@ -93,10 +93,12 @@ public final class NavigationBarTheme { public final class NavigationBarStrings { public let back: String public let close: String + public let tab: String - public init(back: String, close: String) { + public init(back: String, close: String, tab: String) { self.back = back self.close = close + self.tab = tab } } diff --git a/submodules/PasscodeUI/Sources/PasscodeEntryControllerNode.swift b/submodules/PasscodeUI/Sources/PasscodeEntryControllerNode.swift index 57dca9563a..6ca0d5fc0f 100644 --- a/submodules/PasscodeUI/Sources/PasscodeEntryControllerNode.swift +++ b/submodules/PasscodeUI/Sources/PasscodeEntryControllerNode.swift @@ -125,8 +125,12 @@ final class PasscodeEntryControllerNode: ASDisplayNode { } self.cancelButtonNode.setTitle(strings.Common_Cancel, with: buttonFont, with: .white, for: .normal) + self.cancelButtonNode.accessibilityLabel = strings.Common_Cancel + self.cancelButtonNode.accessibilityTraits = [.button] self.deleteButtonNode.setTitle(strings.Common_Delete, with: buttonFont, with: .white, for: .normal) - + self.deleteButtonNode.accessibilityLabel = strings.Common_Delete + self.deleteButtonNode.accessibilityTraits = [.button] + if let biometricsType = self.biometricsType { switch biometricsType { case .touchId: diff --git a/submodules/PasscodeUI/Sources/PasscodeEntryKeyboardNode.swift b/submodules/PasscodeUI/Sources/PasscodeEntryKeyboardNode.swift index 649b98978d..5ccd1b07d3 100644 --- a/submodules/PasscodeUI/Sources/PasscodeEntryKeyboardNode.swift +++ b/submodules/PasscodeUI/Sources/PasscodeEntryKeyboardNode.swift @@ -128,6 +128,9 @@ final class PasscodeEntryButtonNode: HighlightTrackingButtonNode { super.init() + self.accessibilityLabel = title + self.accessibilityTraits = .keyboardKey + if let gradientBackgroundNode = self.gradientBackgroundNode { self.addSubnode(gradientBackgroundNode) } diff --git a/submodules/PasswordSetupUI/Sources/TwoFactorAuthDataInputScreen.swift b/submodules/PasswordSetupUI/Sources/TwoFactorAuthDataInputScreen.swift index 5a23069496..54c4115572 100644 --- a/submodules/PasswordSetupUI/Sources/TwoFactorAuthDataInputScreen.swift +++ b/submodules/PasswordSetupUI/Sources/TwoFactorAuthDataInputScreen.swift @@ -73,7 +73,7 @@ public final class TwoFactorDataInputScreen: ViewController { let defaultTheme = NavigationBarTheme(rootControllerTheme: self.presentationData.theme) let navigationBarTheme = NavigationBarTheme(buttonColor: defaultTheme.buttonColor, disabledButtonColor: defaultTheme.disabledButtonColor, primaryTextColor: defaultTheme.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: defaultTheme.badgeBackgroundColor, badgeStrokeColor: defaultTheme.badgeStrokeColor, badgeTextColor: defaultTheme.badgeTextColor) - super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(back: self.presentationData.strings.Common_Back, close: self.presentationData.strings.Common_Close))) + super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(back: self.presentationData.strings.Common_Back, close: self.presentationData.strings.Common_Close, tab: self.presentationData.strings.VoiceOver_Tab))) self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style self.navigationPresentation = presentation diff --git a/submodules/PasswordSetupUI/Sources/TwoFactorAuthSplashScreen.swift b/submodules/PasswordSetupUI/Sources/TwoFactorAuthSplashScreen.swift index 5d2912da32..0c986951c2 100644 --- a/submodules/PasswordSetupUI/Sources/TwoFactorAuthSplashScreen.swift +++ b/submodules/PasswordSetupUI/Sources/TwoFactorAuthSplashScreen.swift @@ -56,7 +56,7 @@ public final class TwoFactorAuthSplashScreen: ViewController { let defaultTheme = NavigationBarTheme(rootControllerTheme: self.presentationData.theme) let navigationBarTheme = NavigationBarTheme(buttonColor: defaultTheme.buttonColor, disabledButtonColor: defaultTheme.disabledButtonColor, primaryTextColor: defaultTheme.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: defaultTheme.badgeBackgroundColor, badgeStrokeColor: defaultTheme.badgeStrokeColor, badgeTextColor: defaultTheme.badgeTextColor) - super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(back: self.presentationData.strings.Common_Back, close: self.presentationData.strings.Common_Close))) + super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(back: self.presentationData.strings.Common_Back, close: self.presentationData.strings.Common_Close, tab: self.presentationData.strings.VoiceOver_Tab))) self.navigationPresentation = presentation diff --git a/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift b/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift index d38c8efc06..c944446ab5 100644 --- a/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift +++ b/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift @@ -112,7 +112,7 @@ public final class QrCodeScanScreen: ViewController { let navigationBarTheme = NavigationBarTheme(buttonColor: .white, disabledButtonColor: .white, primaryTextColor: .white, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear) - super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(back: self.presentationData.strings.Common_Back, close: self.presentationData.strings.Common_Close))) + super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(back: self.presentationData.strings.Common_Back, close: self.presentationData.strings.Common_Close, tab: self.presentationData.strings.VoiceOver_Tab))) self.statusBar.statusBarStyle = .White diff --git a/submodules/SettingsUI/Sources/Terms of Service/TermsOfServiceController.swift b/submodules/SettingsUI/Sources/Terms of Service/TermsOfServiceController.swift index 4e4db063d1..651c862ae7 100644 --- a/submodules/SettingsUI/Sources/Terms of Service/TermsOfServiceController.swift +++ b/submodules/SettingsUI/Sources/Terms of Service/TermsOfServiceController.swift @@ -78,7 +78,7 @@ public class TermsOfServiceController: ViewController, StandalonePresentableCont self.decline = decline self.openUrl = openUrl - super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: presentationData.theme), strings: NavigationBarStrings(back: presentationData.strings.Common_Back, close: presentationData.strings.Common_Close))) + super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: presentationData.theme), strings: NavigationBarStrings(back: presentationData.strings.Common_Back, close: presentationData.strings.Common_Close, tab: presentationData.strings.VoiceOver_Tab))) self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style diff --git a/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift b/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift index 7e56eb8946..d34ac8b555 100644 --- a/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift +++ b/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift @@ -251,6 +251,8 @@ public final class SolidRoundedButtonNode: ASDisplayNode { super.init() + self.isAccessibilityElement = true + self.addSubnode(self.buttonBackgroundNode) self.addSubnode(self.buttonNode) self.addSubnode(self.titleNode) @@ -931,6 +933,8 @@ public final class SolidRoundedButtonView: UIView { super.init(frame: CGRect()) + self.isAccessibilityElement = true + self.addSubview(self.buttonBackgroundNode) self.addSubview(self.buttonNode) self.addSubview(self.titleNode) diff --git a/submodules/TabBarUI/Sources/TabBarContollerNode.swift b/submodules/TabBarUI/Sources/TabBarContollerNode.swift index 39a56e395a..8d26ba1f6c 100644 --- a/submodules/TabBarUI/Sources/TabBarContollerNode.swift +++ b/submodules/TabBarUI/Sources/TabBarContollerNode.swift @@ -10,6 +10,7 @@ private extension ToolbarTheme { } final class TabBarControllerNode: ASDisplayNode { + private var navigationBarPresentationData: NavigationBarPresentationData private var theme: TabBarControllerTheme let tabBarNode: TabBarNode private let disabledOverlayNode: ASDisplayNode @@ -27,9 +28,10 @@ final class TabBarControllerNode: ASDisplayNode { } } - init(theme: TabBarControllerTheme, itemSelected: @escaping (Int, Bool, [ASDisplayNode]) -> Void, contextAction: @escaping (Int, ContextExtractedContentContainingNode, ContextGesture) -> Void, swipeAction: @escaping (Int, TabBarItemSwipeDirection) -> Void, toolbarActionSelected: @escaping (ToolbarActionOption) -> Void, disabledPressed: @escaping () -> Void) { + init(theme: TabBarControllerTheme, navigationBarPresentationData: NavigationBarPresentationData, itemSelected: @escaping (Int, Bool, [ASDisplayNode]) -> Void, contextAction: @escaping (Int, ContextExtractedContentContainingNode, ContextGesture) -> Void, swipeAction: @escaping (Int, TabBarItemSwipeDirection) -> Void, toolbarActionSelected: @escaping (ToolbarActionOption) -> Void, disabledPressed: @escaping () -> Void) { self.theme = theme - self.tabBarNode = TabBarNode(theme: theme, itemSelected: itemSelected, contextAction: contextAction, swipeAction: swipeAction) + self.navigationBarPresentationData = navigationBarPresentationData + self.tabBarNode = TabBarNode(theme: theme, tabString: navigationBarPresentationData.strings.tab, itemSelected: itemSelected, contextAction: contextAction, swipeAction: swipeAction) self.disabledOverlayNode = ASDisplayNode() self.disabledOverlayNode.backgroundColor = theme.backgroundColor.withAlphaComponent(0.5) self.disabledOverlayNode.alpha = 0.0 @@ -60,11 +62,12 @@ final class TabBarControllerNode: ASDisplayNode { } } - func updateTheme(_ theme: TabBarControllerTheme) { + func updateTheme(_ theme: TabBarControllerTheme, navigationBarPresentationData: NavigationBarPresentationData) { self.theme = theme + self.navigationBarPresentationData = navigationBarPresentationData self.backgroundColor = theme.backgroundColor - self.tabBarNode.updateTheme(theme) + self.tabBarNode.updateTheme(theme, tabString: navigationBarPresentationData.strings.tab) self.disabledOverlayNode.backgroundColor = theme.backgroundColor.withAlphaComponent(0.5) self.toolbarNode?.updateTheme(ToolbarTheme(tabBarTheme: theme)) } diff --git a/submodules/TabBarUI/Sources/TabBarController.swift b/submodules/TabBarUI/Sources/TabBarController.swift index b5381d47ca..5fdefb0a3a 100644 --- a/submodules/TabBarUI/Sources/TabBarController.swift +++ b/submodules/TabBarUI/Sources/TabBarController.swift @@ -127,9 +127,11 @@ open class TabBarControllerImpl: ViewController, TabBarController { private let pendingControllerDisposable = MetaDisposable() + private var navigationBarPresentationData: NavigationBarPresentationData private var theme: TabBarControllerTheme public init(navigationBarPresentationData: NavigationBarPresentationData, theme: TabBarControllerTheme) { + self.navigationBarPresentationData = navigationBarPresentationData self.theme = theme super.init(navigationBarPresentationData: nil) @@ -155,8 +157,9 @@ open class TabBarControllerImpl: ViewController, TabBarController { public func updateTheme(navigationBarPresentationData: NavigationBarPresentationData, theme: TabBarControllerTheme) { if self.theme !== theme { self.theme = theme + self.navigationBarPresentationData = navigationBarPresentationData if self.isNodeLoaded { - self.tabBarControllerNode.updateTheme(theme) + self.tabBarControllerNode.updateTheme(theme, navigationBarPresentationData: navigationBarPresentationData) } } } @@ -194,7 +197,7 @@ open class TabBarControllerImpl: ViewController, TabBarController { } override open func loadDisplayNode() { - self.displayNode = TabBarControllerNode(theme: self.theme, itemSelected: { [weak self] index, longTap, itemNodes in + self.displayNode = TabBarControllerNode(theme: self.theme, navigationBarPresentationData: self.navigationBarPresentationData, itemSelected: { [weak self] index, longTap, itemNodes in if let strongSelf = self { if longTap, let controller = strongSelf.controllers[index] as? TabBarContainedController { controller.presentTabBarPreviewingController(sourceNodes: itemNodes) diff --git a/submodules/TabBarUI/Sources/TabBarNode.swift b/submodules/TabBarUI/Sources/TabBarNode.swift index 5867b6b865..428a189c85 100644 --- a/submodules/TabBarUI/Sources/TabBarNode.swift +++ b/submodules/TabBarUI/Sources/TabBarNode.swift @@ -344,6 +344,7 @@ class TabBarNode: ASDisplayNode { private let swipeAction: (Int, TabBarItemSwipeDirection) -> Void private var theme: TabBarControllerTheme + private var tabString: String private var validLayout: (CGSize, CGFloat, CGFloat, UIEdgeInsets, CGFloat)? private var horizontal: Bool = false private var centered: Bool = false @@ -356,11 +357,12 @@ class TabBarNode: ASDisplayNode { private var tapRecognizer: TapLongTapOrDoubleTapGestureRecognizer? - init(theme: TabBarControllerTheme, itemSelected: @escaping (Int, Bool, [ASDisplayNode]) -> Void, contextAction: @escaping (Int, ContextExtractedContentContainingNode, ContextGesture) -> Void, swipeAction: @escaping (Int, TabBarItemSwipeDirection) -> Void) { + init(theme: TabBarControllerTheme, tabString: String, itemSelected: @escaping (Int, Bool, [ASDisplayNode]) -> Void, contextAction: @escaping (Int, ContextExtractedContentContainingNode, ContextGesture) -> Void, swipeAction: @escaping (Int, TabBarItemSwipeDirection) -> Void) { self.itemSelected = itemSelected self.contextAction = contextAction self.swipeAction = swipeAction self.theme = theme + self.tabString = tabString self.backgroundNode = NavigationBackgroundNode(color: theme.tabBarBackgroundColor) @@ -374,6 +376,7 @@ class TabBarNode: ASDisplayNode { super.init() self.isAccessibilityContainer = false + self.accessibilityTraits = [.tabBar] self.isOpaque = false self.backgroundColor = nil @@ -406,9 +409,10 @@ class TabBarNode: ASDisplayNode { } } - func updateTheme(_ theme: TabBarControllerTheme) { + func updateTheme(_ theme: TabBarControllerTheme, tabString: String) { if self.theme !== theme { self.theme = theme + self.tabString = tabString self.separatorNode.backgroundColor = theme.tabBarSeparatorColor self.backgroundNode.updateColor(color: theme.tabBarBackgroundColor, transition: .immediate) @@ -502,6 +506,8 @@ class TabBarNode: ASDisplayNode { node.contextTextImageNode.image = contextTextImage node.contextImageNode.image = contextImage node.accessibilityLabel = item.item.title + node.accessibilityTraits = [.button, .selected] + node.accessibilityHint = self.tabString node.contentWidth = max(contentWidth, imageContentWidth) node.isSelected = true } else { @@ -515,6 +521,8 @@ class TabBarNode: ASDisplayNode { node.textImageNode.image = textImage node.accessibilityLabel = item.item.title + node.accessibilityTraits = [.button] + node.accessibilityHint = self.tabString node.imageNode.image = image node.contextTextImageNode.image = contextTextImage node.contextImageNode.image = contextImage @@ -576,6 +584,8 @@ class TabBarNode: ASDisplayNode { let (contextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered) node.textImageNode.image = textImage node.accessibilityLabel = item.item.title + node.accessibilityTraits = [.button, .selected] + node.accessibilityHint = self.tabString node.imageNode.image = image node.contextTextImageNode.image = contextTextImage node.contextImageNode.image = contextImage @@ -608,6 +618,8 @@ class TabBarNode: ASDisplayNode { node.textImageNode.image = textImage node.accessibilityLabel = item.item.title + node.accessibilityTraits = [.button] + node.accessibilityHint = self.tabString node.imageNode.image = image node.contextTextImageNode.image = contextTextImage node.contextImageNode.image = contextImage @@ -706,6 +718,7 @@ class TabBarNode: ASDisplayNode { node.containerNode.frame = CGRect(origin: CGPoint(), size: nodeFrame.size) node.hitTestSlop = UIEdgeInsets(top: -3.0, left: -horizontalHitTestInset, bottom: -3.0, right: -horizontalHitTestInset) node.containerNode.hitTestSlop = UIEdgeInsets(top: -3.0, left: -horizontalHitTestInset, bottom: -3.0, right: -horizontalHitTestInset) + node.accessibilityFrame = nodeFrame.insetBy(dx: -horizontalHitTestInset, dy: 0.0).offsetBy(dx: 0.0, dy: size.height - nodeSize.height - bottomInset) if node.ringColor == nil { node.imageNode.frame = CGRect(origin: CGPoint(), size: nodeFrame.size) } diff --git a/submodules/TelegramCallsUI/Sources/CallControllerButtonsNode.swift b/submodules/TelegramCallsUI/Sources/CallControllerButtonsNode.swift index ad5346ffc7..4f8fec1299 100644 --- a/submodules/TelegramCallsUI/Sources/CallControllerButtonsNode.swift +++ b/submodules/TelegramCallsUI/Sources/CallControllerButtonsNode.swift @@ -436,6 +436,9 @@ final class CallControllerButtonsNode: ASDisplayNode { } let buttonContent: CallControllerButtonItemNode.Content let buttonText: String + var buttonAccessibilityLabel = "" + var buttonAccessibilityValue = "" + var buttonAccessibilityTraits: UIAccessibilityTraits = [.button] switch button.button { case .accept: buttonContent = CallControllerButtonItemNode.Content( @@ -443,6 +446,7 @@ final class CallControllerButtonsNode: ASDisplayNode { image: .accept ) buttonText = strings.Call_Accept + buttonAccessibilityLabel = buttonText case let .end(type): buttonContent = CallControllerButtonItemNode.Content( appearance: .color(.red), @@ -456,6 +460,11 @@ final class CallControllerButtonsNode: ASDisplayNode { case .end: buttonText = strings.Call_End } + if !buttonText.isEmpty { + buttonAccessibilityLabel = buttonText + } else { + buttonAccessibilityLabel = strings.Call_End + } case let .enableCamera(isActivated, isEnabled, isInitializing, isScreencastActive): buttonContent = CallControllerButtonItemNode.Content( appearance: .blurred(isFilled: isActivated), @@ -464,6 +473,13 @@ final class CallControllerButtonsNode: ASDisplayNode { hasProgress: isInitializing ) buttonText = strings.Call_Camera + buttonAccessibilityLabel = buttonText + if !isEnabled { + buttonAccessibilityTraits.insert(.notEnabled) + } + if isActivated { + buttonAccessibilityTraits.insert(.selected) + } case let .switchCamera(isEnabled): buttonContent = CallControllerButtonItemNode.Content( appearance: .blurred(isFilled: false), @@ -471,6 +487,10 @@ final class CallControllerButtonsNode: ASDisplayNode { isEnabled: isEnabled ) buttonText = strings.Call_Flip + buttonAccessibilityLabel = buttonText + if !isEnabled { + buttonAccessibilityTraits.insert(.notEnabled) + } case let .soundOutput(value): let image: CallControllerButtonItemNode.Content.Image var isFilled = false @@ -484,30 +504,43 @@ final class CallControllerButtonsNode: ASDisplayNode { case .bluetooth: image = .bluetooth title = strings.Call_Audio + buttonAccessibilityValue = "Bluetooth" case .airpods: image = .airpods title = strings.Call_Audio + buttonAccessibilityValue = "Airpods" case .airpodsPro: image = .airpodsPro title = strings.Call_Audio + buttonAccessibilityValue = "Airpods Pro" case .airpodsMax: image = .airpodsMax title = strings.Call_Audio + buttonAccessibilityValue = "Airpods Max" case .headphones: image = .headphones title = strings.Call_Audio + buttonAccessibilityValue = strings.Call_AudioRouteHeadphones } buttonContent = CallControllerButtonItemNode.Content( appearance: .blurred(isFilled: isFilled), image: image ) buttonText = title + buttonAccessibilityLabel = buttonText + if isFilled { + buttonAccessibilityTraits.insert(.selected) + } case let .mute(isMuted): buttonContent = CallControllerButtonItemNode.Content( appearance: .blurred(isFilled: isMuted), image: .mute ) buttonText = strings.Call_Mute + buttonAccessibilityLabel = buttonText + if isMuted { + buttonAccessibilityTraits.insert(.selected) + } } var buttonDelay = 0.0 if animatePositionsWithDelay { @@ -526,6 +559,10 @@ final class CallControllerButtonsNode: ASDisplayNode { } buttonTransition.updateFrame(node: buttonNode, frame: button.frame, delay: buttonDelay) buttonNode.update(size: button.frame.size, content: buttonContent, text: buttonText, transition: buttonTransition) + buttonNode.accessibilityLabel = buttonAccessibilityLabel + buttonNode.accessibilityValue = buttonAccessibilityValue + buttonNode.accessibilityTraits = buttonAccessibilityTraits + if animateButtonIn { buttonNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) } diff --git a/submodules/TelegramCallsUI/Sources/CallControllerNode.swift b/submodules/TelegramCallsUI/Sources/CallControllerNode.swift index e1818f74e9..f1d8d74b9c 100644 --- a/submodules/TelegramCallsUI/Sources/CallControllerNode.swift +++ b/submodules/TelegramCallsUI/Sources/CallControllerNode.swift @@ -501,6 +501,7 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro self.buttonsNode = CallControllerButtonsNode(strings: self.presentationData.strings) self.toastNode = CallControllerToastContainerNode(strings: self.presentationData.strings) self.keyButtonNode = CallControllerKeyButton() + self.keyButtonNode.accessibilityElementsHidden = false super.init() @@ -510,6 +511,8 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro self.containerTransformationNode.addSubnode(self.containerNode) self.backButtonNode.setTitle(presentationData.strings.Common_Back, with: Font.regular(17.0), with: .white, for: []) + self.backButtonNode.accessibilityLabel = presentationData.strings.Call_VoiceOver_Minimize + self.backButtonNode.accessibilityTraits = [.button] self.backButtonNode.hitTestSlop = UIEdgeInsets(top: -8.0, left: -20.0, bottom: -8.0, right: -8.0) self.backButtonNode.highligthedChanged = { [weak self] highlighted in if let strongSelf = self { diff --git a/submodules/TelegramCallsUI/Sources/CallControllerStatusNode.swift b/submodules/TelegramCallsUI/Sources/CallControllerStatusNode.swift index 9e84d73aaa..dc1ebe58d9 100644 --- a/submodules/TelegramCallsUI/Sources/CallControllerStatusNode.swift +++ b/submodules/TelegramCallsUI/Sources/CallControllerStatusNode.swift @@ -40,6 +40,9 @@ final class CallControllerStatusNode: ASDisplayNode { private let receptionNode: CallControllerReceptionNode private let logoNode: ASImageNode + private let titleActivateAreaNode: AccessibilityAreaNode + private let statusActivateAreaNode: AccessibilityAreaNode + var title: String = "" var subtitle: String = "" var status: CallControllerStatusValue = .text(string: "", displayLogo: false) { @@ -118,6 +121,12 @@ final class CallControllerStatusNode: ASDisplayNode { self.logoNode.image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallTitleLogo"), color: .white) self.logoNode.isHidden = true + self.titleActivateAreaNode = AccessibilityAreaNode() + self.titleActivateAreaNode.accessibilityTraits = .staticText + + self.statusActivateAreaNode = AccessibilityAreaNode() + self.statusActivateAreaNode.accessibilityTraits = [.staticText, .updatesFrequently] + super.init() self.isUserInteractionEnabled = false @@ -127,6 +136,9 @@ final class CallControllerStatusNode: ASDisplayNode { self.statusContainerNode.addSubnode(self.statusNode) self.statusContainerNode.addSubnode(self.receptionNode) self.statusContainerNode.addSubnode(self.logoNode) + + self.addSubnode(self.titleActivateAreaNode) + self.addSubnode(self.statusActivateAreaNode) } deinit { @@ -191,6 +203,9 @@ final class CallControllerStatusNode: ASDisplayNode { let _ = statusApply() let _ = statusMeasureApply() + self.titleActivateAreaNode.accessibilityLabel = self.title + self.statusActivateAreaNode.accessibilityLabel = statusText + self.titleNode.frame = CGRect(origin: CGPoint(x: floor((constrainedWidth - titleLayout.size.width) / 2.0), y: 0.0), size: titleLayout.size) self.statusContainerNode.frame = CGRect(origin: CGPoint(x: 0.0, y: titleLayout.size.height + spacing), size: CGSize(width: constrainedWidth, height: statusLayout.size.height)) self.statusNode.frame = CGRect(origin: CGPoint(x: floor((constrainedWidth - statusMeasureLayout.size.width) / 2.0) + statusOffset, y: 0.0), size: statusLayout.size) @@ -201,6 +216,9 @@ final class CallControllerStatusNode: ASDisplayNode { self.logoNode.frame = CGRect(origin: CGPoint(x: self.statusNode.frame.minX + firstLineOffset - image.size.width - 7.0, y: 5.0), size: image.size) } + self.titleActivateAreaNode.frame = self.titleNode.frame + self.statusActivateAreaNode.frame = self.statusContainerNode.frame + return titleLayout.size.height + spacing + statusLayout.size.height } } diff --git a/submodules/TelegramPresentationData/Sources/ComponentsThemes.swift b/submodules/TelegramPresentationData/Sources/ComponentsThemes.swift index f47d7ea5fc..569dce889c 100644 --- a/submodules/TelegramPresentationData/Sources/ComponentsThemes.swift +++ b/submodules/TelegramPresentationData/Sources/ComponentsThemes.swift @@ -54,7 +54,7 @@ public extension NavigationBarTheme { public extension NavigationBarStrings { convenience init(presentationStrings: PresentationStrings) { - self.init(back: presentationStrings.Common_Back, close: presentationStrings.Common_Close) + self.init(back: presentationStrings.Common_Back, close: presentationStrings.Common_Close, tab: presentationStrings.VoiceOver_Tab) } } diff --git a/submodules/TelegramUI/Sources/ChatButtonKeyboardInputNode.swift b/submodules/TelegramUI/Sources/ChatButtonKeyboardInputNode.swift index 2921481394..728a4c8214 100644 --- a/submodules/TelegramUI/Sources/ChatButtonKeyboardInputNode.swift +++ b/submodules/TelegramUI/Sources/ChatButtonKeyboardInputNode.swift @@ -65,6 +65,8 @@ private final class ChatButtonKeyboardInputButtonNode: HighlightTrackingButtonNo super.init() + self.accessibilityTraits = [.button] + self.addSubnode(self.backgroundContainerNode) self.backgroundContainerNode.addSubnode(self.backgroundColorNode) @@ -94,6 +96,7 @@ private final class ChatButtonKeyboardInputButtonNode: HighlightTrackingButtonNo override func setAttributedTitle(_ title: NSAttributedString, for state: UIControl.State) { self.textNode.attributedText = title + self.accessibilityLabel = title.string } private var absoluteRect: (CGRect, CGSize)? diff --git a/submodules/TelegramUI/Sources/ChatQrCodeScreen.swift b/submodules/TelegramUI/Sources/ChatQrCodeScreen.swift index ca44feaeb0..c510117192 100644 --- a/submodules/TelegramUI/Sources/ChatQrCodeScreen.swift +++ b/submodules/TelegramUI/Sources/ChatQrCodeScreen.swift @@ -860,6 +860,7 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, UIScrollViewDeleg self.cancelButton = HighlightableButtonNode() self.cancelButton.setImage(closeButtonImage(theme: self.presentationData.theme), for: .normal) self.cancelButton.accessibilityLabel = self.presentationData.strings.Common_Close + self.cancelButton.accessibilityTraits = [.button] self.switchThemeButton = HighlightTrackingButtonNode() self.animationContainerNode = ASDisplayNode() diff --git a/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenLabeledValueItem.swift b/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenLabeledValueItem.swift index ca88b0dd34..788cdfb4a1 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenLabeledValueItem.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenLabeledValueItem.swift @@ -373,9 +373,11 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode { self.iconNode.image = generateTintedImage(image: iconImage, color: presentationData.theme.list.itemAccentColor) self.iconNode.isHidden = false self.iconButtonNode.isHidden = false + self.iconButtonNode.accessibilityLabel = presentationData.strings.InviteLink_QRCode_Share } else { self.iconNode.isHidden = true self.iconButtonNode.isHidden = true + self.iconButtonNode.accessibilityLabel = nil } let additionalSideInset: CGFloat = !self.iconNode.isHidden ? 32.0 : 0.0 diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift index bdda287c7d..ebca5e5cee 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift @@ -1713,6 +1713,7 @@ final class PeerInfoHeaderSingleLineTextFieldNode: ASDisplayNode, PeerInfoHeader self.clearButtonNode = HighlightableButtonNode() self.clearButtonNode.isHidden = true + self.clearButtonNode.isAccessibilityElement = false self.topSeparator = ASDisplayNode() @@ -1873,6 +1874,7 @@ final class PeerInfoHeaderMultiLineTextFieldNode: ASDisplayNode, PeerInfoHeaderT self.clearButtonNode = HighlightableButtonNode() self.clearButtonNode.isHidden = true + self.clearButtonNode.isAccessibilityElement = false self.maskNode = ASImageNode() self.maskNode.isUserInteractionEnabled = false diff --git a/submodules/TelegramUI/Sources/PeerSelectionController.swift b/submodules/TelegramUI/Sources/PeerSelectionController.swift index d8767a7419..eaad689acc 100644 --- a/submodules/TelegramUI/Sources/PeerSelectionController.swift +++ b/submodules/TelegramUI/Sources/PeerSelectionController.swift @@ -240,7 +240,31 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon strongSelf.openMessageFromSearchDisposable.set((strongSelf.context.engine.peers.ensurePeerIsLocallyAvailable(peer: EnginePeer(peer)) |> deliverOnMainQueue).start(completed: { [weak strongSelf] in if let strongSelf = strongSelf, let peerSelected = strongSelf.peerSelected { - peerSelected(peer, threadId) + if let peer = peer as? TelegramChannel, peer.flags.contains(.isForum), threadId == nil, strongSelf.selectForumThreads { + let controller = PeerSelectionControllerImpl( + PeerSelectionControllerParams( + context: strongSelf.context, + updatedPresentationData: nil, + filter: strongSelf.filter, + forumPeerId: peer.id, + hasChatListSelector: false, + hasContactSelector: false, + hasGlobalSearch: false, + title: EnginePeer(peer).compactDisplayTitle, + attemptSelection: strongSelf.attemptSelection, + createNewGroup: nil, + pretendPresentedInModal: false, + multipleSelection: false, + forwardedMessageIds: [], + hasTypeHeaders: false, + selectForumThreads: false + ) + ) + controller.peerSelected = strongSelf.peerSelected + strongSelf.push(controller) + } else { + peerSelected(peer, threadId) + } } })) } diff --git a/submodules/WebUI/Sources/WebAppController.swift b/submodules/WebUI/Sources/WebAppController.swift index 7d7b62fcea..50501b0dad 100644 --- a/submodules/WebUI/Sources/WebAppController.swift +++ b/submodules/WebUI/Sources/WebAppController.swift @@ -1056,7 +1056,7 @@ public final class WebAppController: ViewController, AttachmentContainable { self.moreButtonNode = MoreButtonNode(theme: self.presentationData.theme) self.moreButtonNode.iconNode.enqueueState(.more, animated: false) - let navigationBarPresentationData = NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: self.presentationData.theme), strings: NavigationBarStrings(back: "", close: "")) + let navigationBarPresentationData = NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: self.presentationData.theme), strings: NavigationBarStrings(back: "", close: "", tab: "")) super.init(navigationBarPresentationData: navigationBarPresentationData) self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style @@ -1087,7 +1087,7 @@ public final class WebAppController: ViewController, AttachmentContainable { if let strongSelf = self { strongSelf.presentationData = presentationData - let navigationBarPresentationData = NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: presentationData.theme), strings: NavigationBarStrings(back: "", close: "")) + let navigationBarPresentationData = NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: presentationData.theme), strings: NavigationBarStrings(back: "", close: "", tab: "")) strongSelf.navigationBar?.updatePresentationData(navigationBarPresentationData) strongSelf.titleView?.theme = presentationData.theme From f1f4eefcbfea5c10d84312c7e5383d492619bb24 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 7 Feb 2023 05:09:34 +0400 Subject: [PATCH 09/12] Various fixes --- Telegram/Telegram-iOS/en.lproj/Localizable.strings | 2 -- submodules/Display/Source/NavigationBar.swift | 4 +--- .../Sources/TwoFactorAuthDataInputScreen.swift | 2 +- .../Sources/TwoFactorAuthSplashScreen.swift | 2 +- .../PremiumUI/Sources/PageIndicatorComponent.swift | 4 +--- .../PremiumUI/Sources/PremiumLimitsListScreen.swift | 2 +- submodules/QrCodeUI/Sources/QrCodeScanScreen.swift | 2 +- .../Terms of Service/TermsOfServiceController.swift | 2 +- submodules/TabBarUI/Sources/TabBarContollerNode.swift | 4 ++-- submodules/TabBarUI/Sources/TabBarNode.swift | 11 ++--------- .../Sources/ComponentsThemes.swift | 2 +- submodules/WebUI/Sources/WebAppController.swift | 4 ++-- 12 files changed, 14 insertions(+), 27 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 5aaded99a0..b547ea561f 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -8895,5 +8895,3 @@ Sorry for the inconvenience."; "Premium.Purchase.OnlyOneSubscriptionAllowed" = "You have already purchased Telegram Premium for another account. You can only have one Telegram Premium subscription on one Apple ID."; "Call.VoiceOver.Minimize" = "Minimize Call"; - -"VoiceOver.Tab" = "Tab"; diff --git a/submodules/Display/Source/NavigationBar.swift b/submodules/Display/Source/NavigationBar.swift index a3b935c357..7752442d25 100644 --- a/submodules/Display/Source/NavigationBar.swift +++ b/submodules/Display/Source/NavigationBar.swift @@ -93,12 +93,10 @@ public final class NavigationBarTheme { public final class NavigationBarStrings { public let back: String public let close: String - public let tab: String - public init(back: String, close: String, tab: String) { + public init(back: String, close: String) { self.back = back self.close = close - self.tab = tab } } diff --git a/submodules/PasswordSetupUI/Sources/TwoFactorAuthDataInputScreen.swift b/submodules/PasswordSetupUI/Sources/TwoFactorAuthDataInputScreen.swift index 54c4115572..5a23069496 100644 --- a/submodules/PasswordSetupUI/Sources/TwoFactorAuthDataInputScreen.swift +++ b/submodules/PasswordSetupUI/Sources/TwoFactorAuthDataInputScreen.swift @@ -73,7 +73,7 @@ public final class TwoFactorDataInputScreen: ViewController { let defaultTheme = NavigationBarTheme(rootControllerTheme: self.presentationData.theme) let navigationBarTheme = NavigationBarTheme(buttonColor: defaultTheme.buttonColor, disabledButtonColor: defaultTheme.disabledButtonColor, primaryTextColor: defaultTheme.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: defaultTheme.badgeBackgroundColor, badgeStrokeColor: defaultTheme.badgeStrokeColor, badgeTextColor: defaultTheme.badgeTextColor) - super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(back: self.presentationData.strings.Common_Back, close: self.presentationData.strings.Common_Close, tab: self.presentationData.strings.VoiceOver_Tab))) + super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(back: self.presentationData.strings.Common_Back, close: self.presentationData.strings.Common_Close))) self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style self.navigationPresentation = presentation diff --git a/submodules/PasswordSetupUI/Sources/TwoFactorAuthSplashScreen.swift b/submodules/PasswordSetupUI/Sources/TwoFactorAuthSplashScreen.swift index 0c986951c2..5d2912da32 100644 --- a/submodules/PasswordSetupUI/Sources/TwoFactorAuthSplashScreen.swift +++ b/submodules/PasswordSetupUI/Sources/TwoFactorAuthSplashScreen.swift @@ -56,7 +56,7 @@ public final class TwoFactorAuthSplashScreen: ViewController { let defaultTheme = NavigationBarTheme(rootControllerTheme: self.presentationData.theme) let navigationBarTheme = NavigationBarTheme(buttonColor: defaultTheme.buttonColor, disabledButtonColor: defaultTheme.disabledButtonColor, primaryTextColor: defaultTheme.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: defaultTheme.badgeBackgroundColor, badgeStrokeColor: defaultTheme.badgeStrokeColor, badgeTextColor: defaultTheme.badgeTextColor) - super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(back: self.presentationData.strings.Common_Back, close: self.presentationData.strings.Common_Close, tab: self.presentationData.strings.VoiceOver_Tab))) + super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(back: self.presentationData.strings.Common_Back, close: self.presentationData.strings.Common_Close))) self.navigationPresentation = presentation diff --git a/submodules/PremiumUI/Sources/PageIndicatorComponent.swift b/submodules/PremiumUI/Sources/PageIndicatorComponent.swift index d48c9e08b1..e8ea2d7380 100644 --- a/submodules/PremiumUI/Sources/PageIndicatorComponent.swift +++ b/submodules/PremiumUI/Sources/PageIndicatorComponent.swift @@ -81,7 +81,7 @@ public final class PageIndicatorComponent: Component { private final class PageIndicatorView: UIView { var displayCount: Int { - return min(11, self.pageCount) + return min(12, self.pageCount) } var dotSize: CGFloat = 8.0 var dotSpace: CGFloat = 10.0 @@ -403,5 +403,3 @@ private class ItemView: UIView { }) } } - - diff --git a/submodules/PremiumUI/Sources/PremiumLimitsListScreen.swift b/submodules/PremiumUI/Sources/PremiumLimitsListScreen.swift index 7508c05894..4ed0387240 100644 --- a/submodules/PremiumUI/Sources/PremiumLimitsListScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumLimitsListScreen.swift @@ -836,7 +836,7 @@ public class PremiumLimitsListScreen: ViewController { } else { topInset = max(0.0, panInitialTopInset + min(0.0, panOffset)) } - } else if let dismissOffset = self.dismissOffset { + } else if let dismissOffset = self.dismissOffset, !dismissOffset.isZero { topInset = edgeTopInset * dismissOffset } else { topInset = effectiveExpanded ? 0.0 : edgeTopInset diff --git a/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift b/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift index c944446ab5..d38c8efc06 100644 --- a/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift +++ b/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift @@ -112,7 +112,7 @@ public final class QrCodeScanScreen: ViewController { let navigationBarTheme = NavigationBarTheme(buttonColor: .white, disabledButtonColor: .white, primaryTextColor: .white, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear) - super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(back: self.presentationData.strings.Common_Back, close: self.presentationData.strings.Common_Close, tab: self.presentationData.strings.VoiceOver_Tab))) + super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(back: self.presentationData.strings.Common_Back, close: self.presentationData.strings.Common_Close))) self.statusBar.statusBarStyle = .White diff --git a/submodules/SettingsUI/Sources/Terms of Service/TermsOfServiceController.swift b/submodules/SettingsUI/Sources/Terms of Service/TermsOfServiceController.swift index 651c862ae7..4e4db063d1 100644 --- a/submodules/SettingsUI/Sources/Terms of Service/TermsOfServiceController.swift +++ b/submodules/SettingsUI/Sources/Terms of Service/TermsOfServiceController.swift @@ -78,7 +78,7 @@ public class TermsOfServiceController: ViewController, StandalonePresentableCont self.decline = decline self.openUrl = openUrl - super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: presentationData.theme), strings: NavigationBarStrings(back: presentationData.strings.Common_Back, close: presentationData.strings.Common_Close, tab: presentationData.strings.VoiceOver_Tab))) + super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: presentationData.theme), strings: NavigationBarStrings(back: presentationData.strings.Common_Back, close: presentationData.strings.Common_Close))) self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style diff --git a/submodules/TabBarUI/Sources/TabBarContollerNode.swift b/submodules/TabBarUI/Sources/TabBarContollerNode.swift index 8d26ba1f6c..02365f72e7 100644 --- a/submodules/TabBarUI/Sources/TabBarContollerNode.swift +++ b/submodules/TabBarUI/Sources/TabBarContollerNode.swift @@ -31,7 +31,7 @@ final class TabBarControllerNode: ASDisplayNode { init(theme: TabBarControllerTheme, navigationBarPresentationData: NavigationBarPresentationData, itemSelected: @escaping (Int, Bool, [ASDisplayNode]) -> Void, contextAction: @escaping (Int, ContextExtractedContentContainingNode, ContextGesture) -> Void, swipeAction: @escaping (Int, TabBarItemSwipeDirection) -> Void, toolbarActionSelected: @escaping (ToolbarActionOption) -> Void, disabledPressed: @escaping () -> Void) { self.theme = theme self.navigationBarPresentationData = navigationBarPresentationData - self.tabBarNode = TabBarNode(theme: theme, tabString: navigationBarPresentationData.strings.tab, itemSelected: itemSelected, contextAction: contextAction, swipeAction: swipeAction) + self.tabBarNode = TabBarNode(theme: theme, itemSelected: itemSelected, contextAction: contextAction, swipeAction: swipeAction) self.disabledOverlayNode = ASDisplayNode() self.disabledOverlayNode.backgroundColor = theme.backgroundColor.withAlphaComponent(0.5) self.disabledOverlayNode.alpha = 0.0 @@ -67,7 +67,7 @@ final class TabBarControllerNode: ASDisplayNode { self.navigationBarPresentationData = navigationBarPresentationData self.backgroundColor = theme.backgroundColor - self.tabBarNode.updateTheme(theme, tabString: navigationBarPresentationData.strings.tab) + self.tabBarNode.updateTheme(theme) self.disabledOverlayNode.backgroundColor = theme.backgroundColor.withAlphaComponent(0.5) self.toolbarNode?.updateTheme(ToolbarTheme(tabBarTheme: theme)) } diff --git a/submodules/TabBarUI/Sources/TabBarNode.swift b/submodules/TabBarUI/Sources/TabBarNode.swift index 428a189c85..9a7ecf52fc 100644 --- a/submodules/TabBarUI/Sources/TabBarNode.swift +++ b/submodules/TabBarUI/Sources/TabBarNode.swift @@ -344,7 +344,6 @@ class TabBarNode: ASDisplayNode { private let swipeAction: (Int, TabBarItemSwipeDirection) -> Void private var theme: TabBarControllerTheme - private var tabString: String private var validLayout: (CGSize, CGFloat, CGFloat, UIEdgeInsets, CGFloat)? private var horizontal: Bool = false private var centered: Bool = false @@ -357,12 +356,11 @@ class TabBarNode: ASDisplayNode { private var tapRecognizer: TapLongTapOrDoubleTapGestureRecognizer? - init(theme: TabBarControllerTheme, tabString: String, itemSelected: @escaping (Int, Bool, [ASDisplayNode]) -> Void, contextAction: @escaping (Int, ContextExtractedContentContainingNode, ContextGesture) -> Void, swipeAction: @escaping (Int, TabBarItemSwipeDirection) -> Void) { + init(theme: TabBarControllerTheme, itemSelected: @escaping (Int, Bool, [ASDisplayNode]) -> Void, contextAction: @escaping (Int, ContextExtractedContentContainingNode, ContextGesture) -> Void, swipeAction: @escaping (Int, TabBarItemSwipeDirection) -> Void) { self.itemSelected = itemSelected self.contextAction = contextAction self.swipeAction = swipeAction self.theme = theme - self.tabString = tabString self.backgroundNode = NavigationBackgroundNode(color: theme.tabBarBackgroundColor) @@ -409,10 +407,9 @@ class TabBarNode: ASDisplayNode { } } - func updateTheme(_ theme: TabBarControllerTheme, tabString: String) { + func updateTheme(_ theme: TabBarControllerTheme) { if self.theme !== theme { self.theme = theme - self.tabString = tabString self.separatorNode.backgroundColor = theme.tabBarSeparatorColor self.backgroundNode.updateColor(color: theme.tabBarBackgroundColor, transition: .immediate) @@ -507,7 +504,6 @@ class TabBarNode: ASDisplayNode { node.contextImageNode.image = contextImage node.accessibilityLabel = item.item.title node.accessibilityTraits = [.button, .selected] - node.accessibilityHint = self.tabString node.contentWidth = max(contentWidth, imageContentWidth) node.isSelected = true } else { @@ -522,7 +518,6 @@ class TabBarNode: ASDisplayNode { node.textImageNode.image = textImage node.accessibilityLabel = item.item.title node.accessibilityTraits = [.button] - node.accessibilityHint = self.tabString node.imageNode.image = image node.contextTextImageNode.image = contextTextImage node.contextImageNode.image = contextImage @@ -585,7 +580,6 @@ class TabBarNode: ASDisplayNode { node.textImageNode.image = textImage node.accessibilityLabel = item.item.title node.accessibilityTraits = [.button, .selected] - node.accessibilityHint = self.tabString node.imageNode.image = image node.contextTextImageNode.image = contextTextImage node.contextImageNode.image = contextImage @@ -619,7 +613,6 @@ class TabBarNode: ASDisplayNode { node.textImageNode.image = textImage node.accessibilityLabel = item.item.title node.accessibilityTraits = [.button] - node.accessibilityHint = self.tabString node.imageNode.image = image node.contextTextImageNode.image = contextTextImage node.contextImageNode.image = contextImage diff --git a/submodules/TelegramPresentationData/Sources/ComponentsThemes.swift b/submodules/TelegramPresentationData/Sources/ComponentsThemes.swift index 569dce889c..f47d7ea5fc 100644 --- a/submodules/TelegramPresentationData/Sources/ComponentsThemes.swift +++ b/submodules/TelegramPresentationData/Sources/ComponentsThemes.swift @@ -54,7 +54,7 @@ public extension NavigationBarTheme { public extension NavigationBarStrings { convenience init(presentationStrings: PresentationStrings) { - self.init(back: presentationStrings.Common_Back, close: presentationStrings.Common_Close, tab: presentationStrings.VoiceOver_Tab) + self.init(back: presentationStrings.Common_Back, close: presentationStrings.Common_Close) } } diff --git a/submodules/WebUI/Sources/WebAppController.swift b/submodules/WebUI/Sources/WebAppController.swift index 50501b0dad..7d7b62fcea 100644 --- a/submodules/WebUI/Sources/WebAppController.swift +++ b/submodules/WebUI/Sources/WebAppController.swift @@ -1056,7 +1056,7 @@ public final class WebAppController: ViewController, AttachmentContainable { self.moreButtonNode = MoreButtonNode(theme: self.presentationData.theme) self.moreButtonNode.iconNode.enqueueState(.more, animated: false) - let navigationBarPresentationData = NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: self.presentationData.theme), strings: NavigationBarStrings(back: "", close: "", tab: "")) + let navigationBarPresentationData = NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: self.presentationData.theme), strings: NavigationBarStrings(back: "", close: "")) super.init(navigationBarPresentationData: navigationBarPresentationData) self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style @@ -1087,7 +1087,7 @@ public final class WebAppController: ViewController, AttachmentContainable { if let strongSelf = self { strongSelf.presentationData = presentationData - let navigationBarPresentationData = NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: presentationData.theme), strings: NavigationBarStrings(back: "", close: "", tab: "")) + let navigationBarPresentationData = NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: presentationData.theme), strings: NavigationBarStrings(back: "", close: "")) strongSelf.navigationBar?.updatePresentationData(navigationBarPresentationData) strongSelf.titleView?.theme = presentationData.theme From 050418b0b843f6e7c1ce7d21a96f8c65f9e2719b Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 7 Feb 2023 16:21:02 +0400 Subject: [PATCH 10/12] Accessibility improvements --- .../Telegram-iOS/en.lproj/Localizable.strings | 11 +++++ .../Sources/AttachmentPanel.swift | 20 ++++++++ ...ationSequenceCodeEntryControllerNode.swift | 48 ++++++++++++++++++- ...nSequencePasswordEntryControllerNode.swift | 19 ++++++++ ...tionSequencePhoneEntryControllerNode.swift | 31 ++++++++++++ .../Sources/ChatListSearchListPaneNode.swift | 2 +- .../Sources/Node/ChatListNode.swift | 12 ++--- ...quenceCountrySelectionControllerNode.swift | 14 ++++-- .../Display/Source/NavigationButtonNode.swift | 4 +- .../ChatItemGalleryFooterContentNode.swift | 22 ++++++++- .../Sources/Items/ChatImageGalleryItem.swift | 3 ++ .../Items/UniversalVideoGalleryItem.swift | 3 ++ .../ItemListUI/Sources/ItemListItem.swift | 9 +++- .../Sources/ItemListCallListItem.swift | 9 ++++ .../Sources/SolidRoundedButtonNode.swift | 16 +++++++ .../Sources/ChatMessageItemView.swift | 1 + ...xtInputAudioRecordingCancelIndicator.swift | 2 + .../CommandChatInputContextPanelNode.swift | 14 +++--- .../Sources/CommandChatInputPanelItem.swift | 43 +++++++++++------ ...CommandMenuChatInputContextPanelNode.swift | 3 +- .../CommandMenuChatInputPanelItem.swift | 11 +++++ .../Sources/HashtagChatInputPanelItem.swift | 13 ++++- .../Sources/MentionChatInputPanelItem.swift | 17 ++++++- .../Sources/MultiScaleTextNode.swift | 2 + .../PeerInfoScreenCallListItem.swift | 13 +++++ .../PeerInfoScreenLabeledValueItem.swift | 1 - .../Sources/PeerInfo/PeerInfoHeaderNode.swift | 1 - 27 files changed, 299 insertions(+), 45 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index b547ea561f..165ccc3dec 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -8895,3 +8895,14 @@ Sorry for the inconvenience."; "Premium.Purchase.OnlyOneSubscriptionAllowed" = "You have already purchased Telegram Premium for another account. You can only have one Telegram Premium subscription on one Apple ID."; "Call.VoiceOver.Minimize" = "Minimize Call"; + +"Login.VoiceOver.PhoneCountryCode" = "Phone country code"; +"Login.VoiceOver.PhoneNumber" = "Phone number"; +"Login.VoiceOver.Password" = "Password"; + +"Gallery.VoiceOver.Delete" = "Delete"; +"Gallery.VoiceOver.Fullscreen" = "Fullscreen"; +"Gallery.VoiceOver.Share" = "Share"; +"Gallery.VoiceOver.Edit" = "Edit"; +"Gallery.VoiceOver.Stickers" = "Stickers"; +"Gallery.VoiceOver.PictureInPicture" = "Picture-in-Picture"; diff --git a/submodules/AttachmentUI/Sources/AttachmentPanel.swift b/submodules/AttachmentUI/Sources/AttachmentPanel.swift index 1640c9d4de..bcca2e3b1f 100644 --- a/submodules/AttachmentUI/Sources/AttachmentPanel.swift +++ b/submodules/AttachmentUI/Sources/AttachmentPanel.swift @@ -894,6 +894,26 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate { containerSize: CGSize(width: buttonWidth, height: buttonSize.height) ) buttonTransition.setFrame(view: buttonView, frame: buttonFrame) + var accessibilityTitle = "" + switch type { + case .gallery: + accessibilityTitle = self.presentationData.strings.Attachment_Gallery + case .file: + accessibilityTitle = self.presentationData.strings.Attachment_File + case .location: + accessibilityTitle = self.presentationData.strings.Attachment_Location + case .contact: + accessibilityTitle = self.presentationData.strings.Attachment_Contact + case .poll: + accessibilityTitle = self.presentationData.strings.Attachment_Poll + case let .app(_, appName, _): + accessibilityTitle = appName + case .standalone: + accessibilityTitle = "" + } + buttonView.isAccessibilityElement = true + buttonView.accessibilityLabel = accessibilityTitle + buttonView.accessibilityTraits = [.button] } } diff --git a/submodules/AuthorizationUI/Sources/AuthorizationSequenceCodeEntryControllerNode.swift b/submodules/AuthorizationUI/Sources/AuthorizationSequenceCodeEntryControllerNode.swift index 7fe7482657..bff27a5960 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationSequenceCodeEntryControllerNode.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationSequenceCodeEntryControllerNode.swift @@ -22,11 +22,14 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF private let animationNode: AnimatedStickerNode private let titleNode: ImmediateTextNode + private let titleActivateAreaNode: AccessibilityAreaNode private let titleIconNode: ASImageNode private let currentOptionNode: ImmediateTextNode + private let currentOptionActivateAreaNode: AccessibilityAreaNode private var dustNode: InvisibleInkDustNode? private let currentOptionInfoNode: ASTextNode + private let currentOptionInfoActivateAreaNode: AccessibilityAreaNode private let nextOptionTitleNode: ImmediateTextNode private let nextOptionButtonNode: HighlightableButtonNode @@ -91,6 +94,9 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF self.titleNode.isUserInteractionEnabled = false self.titleNode.displaysAsynchronously = false + self.titleActivateAreaNode = AccessibilityAreaNode() + self.titleActivateAreaNode.accessibilityTraits = .staticText + self.titleIconNode = ASImageNode() self.titleIconNode.isLayerBacked = true self.titleIconNode.displayWithoutProcessing = true @@ -102,10 +108,16 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF self.currentOptionNode.lineSpacing = 0.1 self.currentOptionNode.maximumNumberOfLines = 0 + self.currentOptionActivateAreaNode = AccessibilityAreaNode() + self.currentOptionActivateAreaNode.accessibilityTraits = .staticText + self.currentOptionInfoNode = ASTextNode() self.currentOptionInfoNode.isUserInteractionEnabled = false self.currentOptionInfoNode.displaysAsynchronously = false + self.currentOptionInfoActivateAreaNode = AccessibilityAreaNode() + self.currentOptionInfoActivateAreaNode.accessibilityTraits = .staticText + self.nextOptionTitleNode = ImmediateTextNode() self.nextOptionButtonNode = HighlightableButtonNode() @@ -113,6 +125,12 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF let (nextOptionText, nextOptionActive) = authorizationNextOptionText(currentType: .sms(length: 5), nextType: .call, timeout: 60, strings: self.strings, primaryColor: self.theme.list.itemPrimaryTextColor, accentColor: self.theme.list.itemAccentColor) self.nextOptionTitleNode.attributedText = nextOptionText self.nextOptionButtonNode.isUserInteractionEnabled = nextOptionActive + self.nextOptionButtonNode.accessibilityLabel = nextOptionText.string + if nextOptionActive { + self.nextOptionButtonNode.accessibilityTraits = [.button] + } else { + self.nextOptionButtonNode.accessibilityTraits = [.button, .notEnabled] + } self.nextOptionButtonNode.addSubnode(self.nextOptionTitleNode) self.codeInputView = CodeInputView() @@ -156,8 +174,10 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF self.addSubnode(self.codeInputView) self.addSubnode(self.titleNode) + self.addSubnode(self.titleActivateAreaNode) self.addSubnode(self.titleIconNode) self.addSubnode(self.currentOptionNode) + self.addSubnode(self.currentOptionActivateAreaNode) self.addSubnode(self.currentOptionInfoNode) self.addSubnode(self.nextOptionButtonNode) self.addSubnode(self.animationNode) @@ -266,10 +286,18 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF self.appleSignInAllowed = appleSignInAllowed self.currentOptionNode.attributedText = authorizationCurrentOptionText(codeType, phoneNumber: self.phoneNumber, email: self.email, strings: self.strings, primaryColor: self.theme.list.itemPrimaryTextColor, accentColor: self.theme.list.itemAccentColor) + self.currentOptionActivateAreaNode.accessibilityLabel = self.currentOptionNode.attributedText?.string ?? "" if case .missedCall = codeType { self.currentOptionInfoNode.attributedText = NSAttributedString(string: self.strings.Login_CodePhonePatternInfoText, font: Font.regular(17.0), textColor: self.theme.list.itemPrimaryTextColor, paragraphAlignment: .center) + self.currentOptionInfoActivateAreaNode.accessibilityLabel = self.currentOptionInfoNode.attributedText?.string ?? "" + if self.currentOptionInfoActivateAreaNode.supernode == nil { + self.addSubnode(self.currentOptionInfoActivateAreaNode) + } } else { self.currentOptionInfoNode.attributedText = NSAttributedString(string: "", font: Font.regular(17.0), textColor: self.theme.list.itemPrimaryTextColor) + if self.currentOptionInfoActivateAreaNode.supernode != nil { + self.currentOptionInfoActivateAreaNode.removeFromSupernode() + } } if let timeout = timeout { #if DEBUG @@ -283,7 +311,12 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF let (nextOptionText, nextOptionActive) = authorizationNextOptionText(currentType: codeType, nextType: nextType, timeout: strongSelf.currentTimeoutTime, strings: strongSelf.strings, primaryColor: strongSelf.theme.list.itemPrimaryTextColor, accentColor: strongSelf.theme.list.itemAccentColor) strongSelf.nextOptionTitleNode.attributedText = nextOptionText strongSelf.nextOptionButtonNode.isUserInteractionEnabled = nextOptionActive - + strongSelf.nextOptionButtonNode.accessibilityLabel = nextOptionText.string + if nextOptionActive { + strongSelf.nextOptionButtonNode.accessibilityTraits = [.button] + } else { + strongSelf.nextOptionButtonNode.accessibilityTraits = [.button, .notEnabled] + } if let layoutArguments = strongSelf.layoutArguments { strongSelf.containerLayoutUpdated(layoutArguments.0, navigationBarHeight: layoutArguments.1, transition: .immediate) } @@ -301,7 +334,12 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF let (nextOptionText, nextOptionActive) = authorizationNextOptionText(currentType: codeType, nextType: nextType, timeout: self.currentTimeoutTime, strings: self.strings, primaryColor: self.theme.list.itemPrimaryTextColor, accentColor: self.theme.list.itemAccentColor) self.nextOptionTitleNode.attributedText = nextOptionText self.nextOptionButtonNode.isUserInteractionEnabled = nextOptionActive - + self.nextOptionButtonNode.accessibilityLabel = nextOptionText.string + if nextOptionActive { + self.nextOptionButtonNode.accessibilityTraits = [.button] + } else { + self.nextOptionButtonNode.accessibilityTraits = [.button, .notEnabled] + } if let layoutArguments = self.layoutArguments { self.containerLayoutUpdated(layoutArguments.0, navigationBarHeight: layoutArguments.1, transition: .immediate) } @@ -347,6 +385,8 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF self.titleNode.attributedText = NSAttributedString(string: self.strings.Login_EnterCodeTelegramTitle, font: Font.semibold(40.0), textColor: self.theme.list.itemPrimaryTextColor) } + self.titleActivateAreaNode.accessibilityLabel = self.titleNode.attributedText?.string ?? "" + if let inputHeight = layout.inputHeight { if let codeType = self.codeType, case .email = codeType { insets.bottom = max(inputHeight, insets.bottom) @@ -525,6 +565,10 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF } self.nextOptionTitleNode.frame = self.nextOptionButtonNode.bounds + + self.titleActivateAreaNode.frame = self.titleNode.frame + self.currentOptionActivateAreaNode.frame = self.currentOptionNode.frame + self.currentOptionInfoActivateAreaNode.frame = self.currentOptionInfoNode.frame } func activateInput() { diff --git a/submodules/AuthorizationUI/Sources/AuthorizationSequencePasswordEntryControllerNode.swift b/submodules/AuthorizationUI/Sources/AuthorizationSequencePasswordEntryControllerNode.swift index d53a11d8f1..f67dfecd9c 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationSequencePasswordEntryControllerNode.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationSequencePasswordEntryControllerNode.swift @@ -15,7 +15,9 @@ final class AuthorizationSequencePasswordEntryControllerNode: ASDisplayNode, UIT private let animationNode: AnimatedStickerNode private let titleNode: ASTextNode + private let titleActivateAreaNode: AccessibilityAreaNode private let noticeNode: ASTextNode + private let noticeActivateAreaNode: AccessibilityAreaNode private let forgotNode: HighlightableButtonNode private let resetNode: HighlightableButtonNode private let proceedNode: SolidRoundedButtonNode @@ -68,15 +70,23 @@ final class AuthorizationSequencePasswordEntryControllerNode: ASDisplayNode, UIT self.titleNode.displaysAsynchronously = false self.titleNode.attributedText = NSAttributedString(string: strings.LoginPassword_Title, font: Font.semibold(28.0), textColor: self.theme.list.itemPrimaryTextColor) + self.titleActivateAreaNode = AccessibilityAreaNode() + self.titleActivateAreaNode.accessibilityTraits = .staticText + self.noticeNode = ASTextNode() self.noticeNode.isUserInteractionEnabled = false self.noticeNode.displaysAsynchronously = false self.noticeNode.lineSpacing = 0.1 self.noticeNode.attributedText = NSAttributedString(string: strings.TwoStepAuth_EnterPasswordHelp, font: Font.regular(17.0), textColor: self.theme.list.itemPrimaryTextColor, paragraphAlignment: .center) + self.noticeActivateAreaNode = AccessibilityAreaNode() + self.noticeActivateAreaNode.accessibilityTraits = .staticText + self.forgotNode = HighlightableButtonNode() self.forgotNode.displaysAsynchronously = false self.forgotNode.setAttributedTitle(NSAttributedString(string: self.strings.TwoStepAuth_EnterPasswordForgot, font: Font.regular(16.0), textColor: self.theme.list.itemAccentColor, paragraphAlignment: .center), for: []) + self.forgotNode.accessibilityLabel = self.strings.TwoStepAuth_EnterPasswordForgot + self.forgotNode.accessibilityTraits = [.button] self.resetNode = HighlightableButtonNode() self.resetNode.displaysAsynchronously = false @@ -95,6 +105,7 @@ final class AuthorizationSequencePasswordEntryControllerNode: ASDisplayNode, UIT self.codeField.textField.keyboardAppearance = self.theme.rootController.keyboardColor.keyboardAppearance self.codeField.textField.disableAutomaticKeyboardHandling = [.forward, .backward] self.codeField.textField.tintColor = self.theme.list.itemAccentColor + self.codeField.textField.accessibilityHint = self.strings.Login_VoiceOver_Password self.proceedNode = SolidRoundedButtonNode(title: self.strings.Login_Continue, theme: SolidRoundedButtonTheme(theme: self.theme), height: 50.0, cornerRadius: 11.0, gloss: false) self.proceedNode.progressType = .embedded @@ -114,9 +125,11 @@ final class AuthorizationSequencePasswordEntryControllerNode: ASDisplayNode, UIT self.addSubnode(self.codeSeparatorNode) self.addSubnode(self.codeField) self.addSubnode(self.titleNode) + self.addSubnode(self.titleActivateAreaNode) self.addSubnode(self.forgotNode) self.addSubnode(self.resetNode) self.addSubnode(self.noticeNode) + self.addSubnode(self.noticeActivateAreaNode) self.addSubnode(self.animationNode) self.addSubnode(self.proceedNode) @@ -214,6 +227,12 @@ final class AuthorizationSequencePasswordEntryControllerNode: ASDisplayNode, UIT self.animationNode.updateLayout(size: animationSize) let _ = layoutAuthorizationItems(bounds: CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: layout.size.width, height: layout.size.height - insets.top - insets.bottom - additionalBottomInset)), items: items, transition: transition, failIfDoesNotFit: false) + + self.titleActivateAreaNode.accessibilityLabel = self.titleNode.attributedText?.string ?? "" + self.noticeActivateAreaNode.accessibilityLabel = self.noticeNode.attributedText?.string ?? "" + + self.titleActivateAreaNode.frame = self.titleNode.frame + self.noticeActivateAreaNode.frame = self.noticeNode.frame } func activateInput() { diff --git a/submodules/AuthorizationUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift b/submodules/AuthorizationUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift index aaf572aa59..d28dfd9e8c 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift @@ -110,6 +110,8 @@ private final class PhoneAndCountryNode: ASDisplayNode { self.phoneInputNode.numberField.textField.textColor = theme.list.itemPrimaryTextColor self.phoneInputNode.countryCodeField.textField.tintColor = theme.list.itemAccentColor self.phoneInputNode.numberField.textField.tintColor = theme.list.itemAccentColor + self.phoneInputNode.countryCodeField.accessibilityHint = strings.Login_VoiceOver_PhoneCountryCode + self.phoneInputNode.numberField.accessibilityHint = strings.Login_VoiceOver_PhoneNumber self.phoneInputNode.countryCodeField.textField.tintColor = theme.list.itemAccentColor self.phoneInputNode.numberField.textField.tintColor = theme.list.itemAccentColor @@ -172,6 +174,9 @@ private final class PhoneAndCountryNode: ASDisplayNode { strongSelf.phoneInputNode.numberField.textField.attributedPlaceholder = NSAttributedString(string: strings.Login_PhonePlaceholder, font: Font.regular(20.0), textColor: theme.list.itemPlaceholderTextColor) } + strongSelf.countryButton.accessibilityLabel = strongSelf.countryButton.attributedTitle(for: .normal)?.string ?? "" + strongSelf.countryButton.accessibilityTraits = [.button] + if strongSelf.hasCountry { strongSelf.hasNumberUpdated?(!strongSelf.phoneInputNode.codeAndNumber.2.isEmpty) } else { @@ -289,7 +294,9 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode { private let animationNode: AnimatedStickerNode private let managedAnimationNode: ManagedPhoneAnimationNode private let titleNode: ASTextNode + private let titleActivateAreaNode: AccessibilityAreaNode private let noticeNode: ASTextNode + private let noticeActivateAreaNode: AccessibilityAreaNode private let phoneAndCountryNode: PhoneAndCountryNode private let contactSyncNode: ContactSyncNode private let proceedNode: SolidRoundedButtonNode @@ -378,12 +385,18 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode { self.titleNode.displaysAsynchronously = false self.titleNode.attributedText = NSAttributedString(string: account == nil ? strings.Login_NewNumber : strings.Login_PhoneTitle, font: Font.light(30.0), textColor: theme.list.itemPrimaryTextColor) + self.titleActivateAreaNode = AccessibilityAreaNode() + self.titleActivateAreaNode.accessibilityTraits = .staticText + self.noticeNode = ASTextNode() self.noticeNode.maximumNumberOfLines = 0 self.noticeNode.isUserInteractionEnabled = true self.noticeNode.displaysAsynchronously = false self.noticeNode.lineSpacing = 0.1 + self.noticeActivateAreaNode = AccessibilityAreaNode() + self.noticeActivateAreaNode.accessibilityTraits = .staticText + self.noticeNode.attributedText = NSAttributedString(string: account == nil ? strings.ChangePhoneNumberNumber_Help : strings.Login_PhoneAndCountryHelp, font: Font.regular(17.0), textColor: theme.list.itemPrimaryTextColor, paragraphAlignment: .center) self.contactSyncNode = ContactSyncNode(theme: theme, strings: strings) @@ -404,6 +417,8 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode { self.addSubnode(self.titleNode) self.addSubnode(self.noticeNode) + self.addSubnode(self.titleActivateAreaNode) + self.addSubnode(self.noticeActivateAreaNode) self.addSubnode(self.phoneAndCountryNode) self.addSubnode(self.contactSyncNode) self.addSubnode(self.proceedNode) @@ -534,6 +549,7 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode { let additionalBottomInset: CGFloat = layout.size.width > 320.0 ? 80.0 : 10.0 self.titleNode.attributedText = NSAttributedString(string: self.account == nil ? strings.Login_NewNumber : strings.Login_PhoneTitle, font: Font.bold(28.0), textColor: self.theme.list.itemPrimaryTextColor) + self.titleActivateAreaNode.accessibilityLabel = self.titleNode.attributedText?.string ?? "" let inset: CGFloat = 24.0 let maximumWidth: CGFloat = min(430.0, layout.size.width) @@ -587,6 +603,10 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode { let _ = layoutAuthorizationItems(bounds: CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: layout.size.width, height: layout.size.height - insets.top - insets.bottom - additionalBottomInset)), items: items, transition: transition, failIfDoesNotFit: false) transition.updateFrame(node: self.managedAnimationNode, frame: self.animationNode.frame) + + self.titleActivateAreaNode.frame = self.titleNode.frame + self.noticeActivateAreaNode.accessibilityLabel = self.noticeNode.attributedText?.string ?? "" + self.noticeActivateAreaNode.frame = self.noticeNode.frame } func activateInput() { @@ -726,6 +746,7 @@ final class PhoneConfirmationController: ViewController { class Node: ASDisplayNode { private let theme: PresentationTheme + private let strings: PresentationStrings private let code: String private let number: String @@ -740,6 +761,7 @@ final class PhoneConfirmationController: ViewController { private let phoneTargetNode: ImmediateTextNode private let textNode: ImmediateTextNode + private let textActivateAreaNode: AccessibilityAreaNode private let cancelButton: HighlightableButtonNode fileprivate let proceedNode: SolidRoundedButtonNode @@ -751,6 +773,7 @@ final class PhoneConfirmationController: ViewController { init(theme: PresentationTheme, strings: PresentationStrings, code: String, number: String) { self.theme = theme + self.strings = strings self.code = code self.number = number @@ -767,8 +790,13 @@ final class PhoneConfirmationController: ViewController { self.textNode.attributedText = NSAttributedString(string: strings.Login_PhoneNumberConfirmation, font: Font.regular(17.0), textColor: theme.list.itemPrimaryTextColor) self.textNode.textAlignment = .center + self.textActivateAreaNode = AccessibilityAreaNode() + self.textActivateAreaNode.accessibilityTraits = .staticText + self.cancelButton = HighlightableButtonNode() self.cancelButton.setTitle(strings.Login_Edit, with: Font.regular(19.0), with: theme.list.itemAccentColor, for: .normal) + self.cancelButton.accessibilityTraits = [.button] + self.cancelButton.accessibilityLabel = strings.Login_Edit self.proceedNode = SolidRoundedButtonNode(title: strings.Login_Continue, theme: SolidRoundedButtonTheme(theme: theme), height: 50.0, cornerRadius: 11.0, gloss: false) self.proceedNode.progressType = .embedded @@ -814,6 +842,7 @@ final class PhoneConfirmationController: ViewController { self.addSubnode(self.phoneTargetNode) self.addSubnode(self.textNode) + self.addSubnode(self.textActivateAreaNode) self.addSubnode(self.cancelButton) self.addSubnode(self.proceedNode) @@ -1009,6 +1038,8 @@ final class PhoneConfirmationController: ViewController { let textSize = self.textNode.updateLayout(backgroundSize) transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((backgroundSize.width - textSize.width) / 2.0), y: 88.0), size: textSize).offsetBy(dx: backgroundFrame.minX, dy: backgroundFrame.minY)) + self.textActivateAreaNode.frame = self.textNode.frame + self.textActivateAreaNode.accessibilityLabel = "\(self.code) \(self.number). \(self.strings.Login_PhoneNumberConfirmation)" let proceedWidth = backgroundSize.width - 16.0 * 2.0 let proceedHeight = self.proceedNode.updateLayout(width: proceedWidth, transition: transition) diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index 4ae0fc051c..d495ab1dbf 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -196,7 +196,7 @@ private enum ChatListRecentEntry: Comparable, Identifiable { } return ContactsPeerItem( - presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings), + presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder), sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, context: context, diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift index 0b3757cf34..99c8479b81 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNode.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNode.swift @@ -319,7 +319,7 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL } } return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListAdditionalCategoryItem( - presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings), + presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder), context: context, title: title, image: image, @@ -525,7 +525,7 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL } return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ContactsPeerItem( - presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings), + presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder), sortOrder: presentationData.nameSortOrder, displayOrder: presentationData.nameDisplayOrder, context: context, @@ -564,7 +564,7 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL let status: ContactsPeerItemStatus = .none return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ContactsPeerItem( - presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings), + presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder), sortOrder: presentationData.nameSortOrder, displayOrder: presentationData.nameDisplayOrder, context: context, @@ -778,7 +778,7 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL } return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ContactsPeerItem( - presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings), + presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder), sortOrder: presentationData.nameSortOrder, displayOrder: presentationData.nameDisplayOrder, context: context, @@ -817,7 +817,7 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL let status: ContactsPeerItemStatus = .none return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ContactsPeerItem( - presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings), + presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder), sortOrder: presentationData.nameSortOrder, displayOrder: presentationData.nameDisplayOrder, context: context, @@ -887,7 +887,7 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL } } return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListAdditionalCategoryItem( - presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings), + presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder), context: context, title: title, image: image, diff --git a/submodules/CountrySelectionUI/Sources/AuthorizationSequenceCountrySelectionControllerNode.swift b/submodules/CountrySelectionUI/Sources/AuthorizationSequenceCountrySelectionControllerNode.swift index ce30ca2f17..9acd1d0382 100644 --- a/submodules/CountrySelectionUI/Sources/AuthorizationSequenceCountrySelectionControllerNode.swift +++ b/submodules/CountrySelectionUI/Sources/AuthorizationSequenceCountrySelectionControllerNode.swift @@ -64,7 +64,7 @@ func localizedCountryNamesAndCodes(strings: PresentationStrings) -> [((String, S let locale = localeWithStrings(strings) var result: [((String, String), String, [Int])] = [] for country in AuthorizationSequenceCountrySelectionController.countries() { - if country.hidden { + if country.hidden || country.id == "FT" { continue } if let englishCountryName = usEnglishLocale.localizedString(forRegionCode: country.id), let countryName = locale.localizedString(forRegionCode: country.id) { @@ -362,20 +362,24 @@ final class AuthorizationSequenceCountrySelectionControllerNode: ASDisplayNode, } var countryName: String + var cleanCountryName: String let originalCountryName: String let code: String if tableView === self.tableView { - countryName = self.sections[indexPath.section].1[indexPath.row].0.1 - countryName = "\(emojiFlagForISOCountryCode(self.sections[indexPath.section].1[indexPath.row].1)) \(countryName)" + cleanCountryName = self.sections[indexPath.section].1[indexPath.row].0.1 + countryName = "\(emojiFlagForISOCountryCode(self.sections[indexPath.section].1[indexPath.row].1)) \(cleanCountryName)" originalCountryName = self.sections[indexPath.section].1[indexPath.row].0.0 code = "+\(self.sections[indexPath.section].1[indexPath.row].2)" } else { - countryName = self.searchResults[indexPath.row].0.1 - countryName = "\(emojiFlagForISOCountryCode(self.searchResults[indexPath.row].1)) \(countryName)" + cleanCountryName = self.searchResults[indexPath.row].0.1 + countryName = "\(emojiFlagForISOCountryCode(self.searchResults[indexPath.row].1)) \(cleanCountryName)" originalCountryName = self.searchResults[indexPath.row].0.0 code = "+\(self.searchResults[indexPath.row].2)" } + cell.accessibilityLabel = cleanCountryName + cell.accessibilityValue = code + cell.textLabel?.text = countryName cell.detailTextLabel?.text = originalCountryName if self.displayCodes, let label = cell.accessoryView as? UILabel { diff --git a/submodules/Display/Source/NavigationButtonNode.swift b/submodules/Display/Source/NavigationButtonNode.swift index b8528c798d..46a5ad84cc 100644 --- a/submodules/Display/Source/NavigationButtonNode.swift +++ b/submodules/Display/Source/NavigationButtonNode.swift @@ -48,7 +48,9 @@ private final class NavigationButtonItemNode: ImmediateTextNode { self.attributedText = NSAttributedString(string: text, attributes: self.attributesForCurrentState()) if _image == nil { - self.item?.accessibilityLabel = value + if self.item?.accessibilityLabel == nil { + self.item?.accessibilityLabel = value + } } } } diff --git a/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift b/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift index 8701104e79..7052f480ff 100644 --- a/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift +++ b/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift @@ -341,6 +341,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll self.authorNameNode.maximumNumberOfLines = 1 self.authorNameNode.isUserInteractionEnabled = false self.authorNameNode.displaysAsynchronously = false + self.dateNode = ASTextNode() self.dateNode.maximumNumberOfLines = 1 self.dateNode.isUserInteractionEnabled = false @@ -428,9 +429,20 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll self.contentNode.addSubnode(self.statusButtonNode) self.deleteButton.addTarget(self, action: #selector(self.deleteButtonPressed), for: [.touchUpInside]) + self.deleteButton.accessibilityTraits = [.button] + self.deleteButton.accessibilityLabel = presentationData.strings.Gallery_VoiceOver_Delete + self.fullscreenButton.addTarget(self, action: #selector(self.fullscreenButtonPressed), for: [.touchUpInside]) + self.fullscreenButton.accessibilityTraits = [.button] + self.fullscreenButton.accessibilityLabel = presentationData.strings.Gallery_VoiceOver_Fullscreen + self.actionButton.addTarget(self, action: #selector(self.actionButtonPressed), for: [.touchUpInside]) + self.actionButton.accessibilityTraits = [.button] + self.actionButton.accessibilityLabel = presentationData.strings.Gallery_VoiceOver_Share + self.editButton.addTarget(self, action: #selector(self.editButtonPressed), for: [.touchUpInside]) + self.editButton.accessibilityTraits = [.button] + self.editButton.accessibilityLabel = presentationData.strings.Gallery_VoiceOver_Edit self.backwardButton.addTarget(self, action: #selector(self.backwardButtonPressed), forControlEvents: .touchUpInside) self.forwardButton.addTarget(self, action: #selector(self.forwardButtonPressed), forControlEvents: .touchUpInside) @@ -595,12 +607,15 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll } else { self.authorNameNode.attributedText = nil } + self.authorNameNode.accessibilityLabel = self.authorNameNode.attributedText?.string + if let dateText = dateText { self.dateNode.attributedText = NSAttributedString(string: dateText, font: dateFont, textColor: .white) } else { self.dateNode.attributedText = nil } - + self.dateNode.accessibilityLabel = self.dateNode.attributedText?.string + self.requestLayout?(.immediate) } @@ -763,8 +778,11 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll } else { self.authorNameNode.attributedText = nil } + self.authorNameNode.accessibilityLabel = self.authorNameNode.attributedText?.string + self.dateNode.attributedText = NSAttributedString(string: dateText, font: dateFont, textColor: .white) - + self.dateNode.accessibilityLabel = self.dateNode.attributedText?.string + if canFullscreen { self.fullscreenButton.isHidden = false self.deleteButton.isHidden = true diff --git a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift index 3520026a4c..fad7948100 100644 --- a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift @@ -443,10 +443,12 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode { var barButtonItems: [UIBarButtonItem] = [] if imageReference.media.flags.contains(.hasStickers) { let rightBarButtonItem = UIBarButtonItem(image: generateTintedImage(image: UIImage(bundleImageName: "Media Gallery/Stickers"), color: .white), style: .plain, target: self, action: #selector(self.openStickersButtonPressed)) + rightBarButtonItem.accessibilityLabel = self.presentationData.strings.Gallery_VoiceOver_Stickers barButtonItems.append(rightBarButtonItem) } if self.message != nil { let moreMenuItem = UIBarButtonItem(customDisplayNode: self.moreBarButton)! + moreMenuItem.accessibilityLabel = self.presentationData.strings.Common_More barButtonItems.append(moreMenuItem) } self._rightBarButtonItems.set(.single(barButtonItems)) @@ -646,6 +648,7 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode { var barButtonItems: [UIBarButtonItem] = [] if self.message != nil { let moreMenuItem = UIBarButtonItem(customDisplayNode: self.moreBarButton)! + moreMenuItem.accessibilityLabel = self.presentationData.strings.Common_More barButtonItems.append(moreMenuItem) } self._rightBarButtonItems.set(.single(barButtonItems)) diff --git a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift index a5446f038f..d8dc50ca3d 100644 --- a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift @@ -1420,10 +1420,12 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { var barButtonItems: [UIBarButtonItem] = [] if hasLinkedStickers { let rightBarButtonItem = UIBarButtonItem(image: generateTintedImage(image: UIImage(bundleImageName: "Media Gallery/Stickers"), color: .white), style: .plain, target: self, action: #selector(self.openStickersButtonPressed)) + rightBarButtonItem.accessibilityLabel = self.presentationData.strings.Gallery_VoiceOver_Stickers barButtonItems.append(rightBarButtonItem) } if forceEnablePiP || (!isAnimated && !disablePlayerControls && !disablePictureInPicture) { let rightBarButtonItem = UIBarButtonItem(image: pictureInPictureButtonImage, style: .plain, target: self, action: #selector(self.pictureInPictureButtonPressed)) + rightBarButtonItem.accessibilityLabel = self.presentationData.strings.Gallery_VoiceOver_PictureInPicture self.pictureInPictureButton = rightBarButtonItem barButtonItems.append(rightBarButtonItem) self.hasPictureInPicture = true @@ -1452,6 +1454,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { if hasMoreButton { let moreMenuItem = UIBarButtonItem(customDisplayNode: self.moreBarButton)! + moreMenuItem.accessibilityLabel = self.presentationData.strings.Common_More barButtonItems.append(moreMenuItem) } } diff --git a/submodules/ItemListUI/Sources/ItemListItem.swift b/submodules/ItemListUI/Sources/ItemListItem.swift index 5ad5eab690..6fc5f3d183 100644 --- a/submodules/ItemListUI/Sources/ItemListItem.swift +++ b/submodules/ItemListUI/Sources/ItemListItem.swift @@ -159,11 +159,13 @@ public final class ItemListPresentationData: Equatable { public let theme: PresentationTheme public let fontSize: PresentationFontSize public let strings: PresentationStrings + public let nameDisplayOrder: PresentationPersonNameOrder - public init(theme: PresentationTheme, fontSize: PresentationFontSize, strings: PresentationStrings) { + public init(theme: PresentationTheme, fontSize: PresentationFontSize, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder) { self.theme = theme self.fontSize = fontSize self.strings = strings + self.nameDisplayOrder = nameDisplayOrder } public static func ==(lhs: ItemListPresentationData, rhs: ItemListPresentationData) -> Bool { @@ -176,6 +178,9 @@ public final class ItemListPresentationData: Equatable { if lhs.fontSize != rhs.fontSize { return false } + if lhs.nameDisplayOrder != rhs.nameDisplayOrder { + return false + } return true } } @@ -226,6 +231,6 @@ public extension PresentationFontSize { public extension ItemListPresentationData { convenience init(_ presentationData: PresentationData) { - self.init(theme: presentationData.theme, fontSize: presentationData.listsFontSize, strings: presentationData.strings) + self.init(theme: presentationData.theme, fontSize: presentationData.listsFontSize, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder) } } diff --git a/submodules/PeerInfoUI/Sources/ItemListCallListItem.swift b/submodules/PeerInfoUI/Sources/ItemListCallListItem.swift index 2e02353f11..47a0bdcb54 100644 --- a/submodules/PeerInfoUI/Sources/ItemListCallListItem.swift +++ b/submodules/PeerInfoUI/Sources/ItemListCallListItem.swift @@ -232,15 +232,20 @@ public class ItemListCallListItemNode: ListViewItemNode { insets = UIEdgeInsets() } + var accessibilityText = "" + let earliestMessage = item.messages.sorted(by: {$0.timestamp < $1.timestamp}).first! let titleText = stringForDate(timestamp: earliestMessage.timestamp, strings: item.presentationData.strings) let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: titleText, font: titleFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - params.rightInset - 20.0 - leftInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + accessibilityText.append(titleText) + accessibilityText.append(". ") contentHeight += titleLayout.size.height + 18.0 var index = 0 var nodesLayout: [(TextNodeLayout, TextNodeLayout)] = [] var nodesApply: [(() -> TextNode, () -> TextNode)] = [] + for message in item.messages { let makeTimeLayout = makeNodesLayout[index].0 let time = stringForMessageTimestamp(timestamp: message.timestamp, dateTimeFormat: item.dateTimeFormat) @@ -250,6 +255,8 @@ public class ItemListCallListItemNode: ListViewItemNode { let type = stringForCallType(message: message, strings: item.presentationData.strings) let (typeLayout, typeApply) = makeTypeLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: type, font: typeFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - params.rightInset - 20.0 - leftInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + accessibilityText.append("\(time) - \(type)") + nodesLayout.append((timeLayout, typeLayout)) nodesApply.append((timeApply, typeApply)) @@ -336,6 +343,8 @@ public class ItemListCallListItemNode: ListViewItemNode { index += 1 } + strongSelf.accessibilityArea.accessibilityLabel = accessibilityText + strongSelf.accessibilityArea.accessibilityTraits = .staticText strongSelf.accessibilityArea.frame = CGRect(origin: CGPoint(), size: layout.contentSize) } }) diff --git a/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift b/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift index d34ac8b555..1d7d2be654 100644 --- a/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift +++ b/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift @@ -101,6 +101,7 @@ public final class SolidRoundedButtonNode: ASDisplayNode { if self.isEnabled != oldValue { self.updateColors(animated: true) } + self.updateAccessibilityLabels() } } @@ -130,6 +131,11 @@ public final class SolidRoundedButtonNode: ASDisplayNode { private func updateAccessibilityLabels() { self.accessibilityLabel = (self.title ?? "") + " " + (self.subtitle ?? "") + if !self.isEnabled { + self.accessibilityTraits = [.button, .notEnabled] + } else { + self.accessibilityTraits = [.button] + } } private var animationTimer: SwiftSignalKit.Timer? @@ -289,6 +295,8 @@ public final class SolidRoundedButtonNode: ASDisplayNode { } } } + + self.updateAccessibilityLabels() } public override func didLoad() { @@ -795,6 +803,11 @@ public final class SolidRoundedButtonView: UIView { private func updateAccessibilityLabels() { self.accessibilityLabel = (self.title ?? "") + " " + (self.subtitle ?? "") self.accessibilityValue = self.label + if !self.isEnabled { + self.accessibilityTraits = [.button, .notEnabled] + } else { + self.accessibilityTraits = [.button] + } } public var icon: UIImage? { @@ -808,6 +821,7 @@ public final class SolidRoundedButtonView: UIView { if self.isEnabled != oldValue { self.titleNode.alpha = self.isEnabled ? 1.0 : 0.6 } + self.updateAccessibilityLabels() } } @@ -976,6 +990,8 @@ public final class SolidRoundedButtonView: UIView { if #available(iOS 13.0, *) { self.buttonBackgroundNode.layer.cornerCurve = .continuous } + + self.updateAccessibilityLabels() } required public init(coder: NSCoder) { diff --git a/submodules/TelegramUI/Sources/ChatMessageItemView.swift b/submodules/TelegramUI/Sources/ChatMessageItemView.swift index d582dce5e1..5783330741 100644 --- a/submodules/TelegramUI/Sources/ChatMessageItemView.swift +++ b/submodules/TelegramUI/Sources/ChatMessageItemView.swift @@ -214,6 +214,7 @@ final class ChatMessageAccessibilityData { loop: for media in message.media { if let _ = media as? TelegramMediaImage { + traits.insert(.image) if isIncoming { if announceIncomingAuthors, let authorName = authorName { label = item.presentationData.strings.VoiceOver_Chat_PhotoFrom(authorName).string diff --git a/submodules/TelegramUI/Sources/ChatTextInputAudioRecordingCancelIndicator.swift b/submodules/TelegramUI/Sources/ChatTextInputAudioRecordingCancelIndicator.swift index 7e0dcdb4c1..5d94685c16 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputAudioRecordingCancelIndicator.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputAudioRecordingCancelIndicator.swift @@ -32,6 +32,8 @@ final class ChatTextInputAudioRecordingCancelIndicator: ASDisplayNode { self.cancelButton = HighlightableButtonNode() self.cancelButton.setTitle(strings.Common_Cancel, with: cancelFont, with: theme.chat.inputPanel.panelControlAccentColor, for: []) self.cancelButton.alpha = 0.0 + self.cancelButton.accessibilityLabel = strings.Common_Cancel + self.cancelButton.accessibilityTraits = [.button] self.strings = strings diff --git a/submodules/TelegramUI/Sources/CommandChatInputContextPanelNode.swift b/submodules/TelegramUI/Sources/CommandChatInputContextPanelNode.swift index 8cb47032d5..09970ca2c7 100644 --- a/submodules/TelegramUI/Sources/CommandChatInputContextPanelNode.swift +++ b/submodules/TelegramUI/Sources/CommandChatInputContextPanelNode.swift @@ -10,6 +10,7 @@ import MergeLists import AccountContext import ChatPresentationInterfaceState import ChatControllerInteraction +import ItemListUI private struct CommandChatInputContextPanelEntryStableId: Hashable { let command: PeerCommand @@ -36,8 +37,8 @@ private struct CommandChatInputContextPanelEntry: Comparable, Identifiable { return lhs.index < rhs.index } - func item(context: AccountContext, fontSize: PresentationFontSize, commandSelected: @escaping (PeerCommand, Bool) -> Void) -> ListViewItem { - return CommandChatInputPanelItem(context: context, theme: self.theme, fontSize: fontSize, command: self.command, commandSelected: commandSelected) + func item(context: AccountContext, presentationData: PresentationData, commandSelected: @escaping (PeerCommand, Bool) -> Void) -> ListViewItem { + return CommandChatInputPanelItem(context: context, presentationData: ItemListPresentationData(presentationData), command: self.command, commandSelected: commandSelected) } } @@ -47,12 +48,12 @@ private struct CommandChatInputContextPanelTransition { let updates: [ListViewUpdateItem] } -private func preparedTransition(from fromEntries: [CommandChatInputContextPanelEntry], to toEntries: [CommandChatInputContextPanelEntry], context: AccountContext, fontSize: PresentationFontSize, commandSelected: @escaping (PeerCommand, Bool) -> Void) -> CommandChatInputContextPanelTransition { +private func preparedTransition(from fromEntries: [CommandChatInputContextPanelEntry], to toEntries: [CommandChatInputContextPanelEntry], context: AccountContext, presentationData: PresentationData, commandSelected: @escaping (PeerCommand, Bool) -> Void) -> CommandChatInputContextPanelTransition { let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) } - let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, fontSize: fontSize, commandSelected: commandSelected), directionHint: nil) } - let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, fontSize: fontSize, commandSelected: commandSelected), directionHint: nil) } + let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, commandSelected: commandSelected), directionHint: nil) } + let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, commandSelected: commandSelected), directionHint: nil) } return CommandChatInputContextPanelTransition(deletions: deletions, insertions: insertions, updates: updates) } @@ -101,7 +102,8 @@ final class CommandChatInputContextPanelNode: ChatInputContextPanelNode { private func prepareTransition(from: [CommandChatInputContextPanelEntry]? , to: [CommandChatInputContextPanelEntry]) { let firstTime = self.currentEntries == nil - let transition = preparedTransition(from: from ?? [], to: to, context: self.context, fontSize: self.fontSize, commandSelected: { [weak self] command, sendImmediately in + let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } + let transition = preparedTransition(from: from ?? [], to: to, context: self.context, presentationData: presentationData, commandSelected: { [weak self] command, sendImmediately in if let strongSelf = self, let interfaceInteraction = strongSelf.interfaceInteraction { if sendImmediately { interfaceInteraction.sendBotCommand(command.peer, "/" + command.command.text) diff --git a/submodules/TelegramUI/Sources/CommandChatInputPanelItem.swift b/submodules/TelegramUI/Sources/CommandChatInputPanelItem.swift index 4339046032..d92ac6b563 100644 --- a/submodules/TelegramUI/Sources/CommandChatInputPanelItem.swift +++ b/submodules/TelegramUI/Sources/CommandChatInputPanelItem.swift @@ -9,20 +9,19 @@ import TelegramPresentationData import TelegramUIPreferences import AvatarNode import AccountContext +import ItemListUI final class CommandChatInputPanelItem: ListViewItem { fileprivate let context: AccountContext - fileprivate let theme: PresentationTheme - fileprivate let fontSize: PresentationFontSize + fileprivate let presentationData: ItemListPresentationData fileprivate let command: PeerCommand fileprivate let commandSelected: (PeerCommand, Bool) -> Void let selectable: Bool = true - public init(context: AccountContext, theme: PresentationTheme, fontSize: PresentationFontSize, command: PeerCommand, commandSelected: @escaping (PeerCommand, Bool) -> Void) { + public init(context: AccountContext, presentationData: ItemListPresentationData, command: PeerCommand, commandSelected: @escaping (PeerCommand, Bool) -> Void) { self.context = context - self.theme = theme - self.fontSize = fontSize + self.presentationData = presentationData self.command = command self.commandSelected = commandSelected } @@ -92,6 +91,8 @@ final class CommandChatInputPanelItemNode: ListViewItemNode { private let highlightedBackgroundNode: ASDisplayNode private let arrowNode: ASButtonNode + private let activateAreaNode: AccessibilityAreaNode + init() { self.avatarNode = AvatarNode(font: avatarFont) self.textNode = TextNode() @@ -107,6 +108,9 @@ final class CommandChatInputPanelItemNode: ListViewItemNode { self.arrowNode = HighlightableButtonNode() + self.activateAreaNode = AccessibilityAreaNode() + self.activateAreaNode.accessibilityTraits = [.button] + super.init(layerBacked: false, dynamicBounce: false) self.addSubnode(self.topSeparatorNode) @@ -117,6 +121,8 @@ final class CommandChatInputPanelItemNode: ListViewItemNode { self.addSubnode(self.arrowNode) self.arrowNode.addTarget(self, action: #selector(self.arrowButtonPressed), forControlEvents: [.touchUpInside]) + + self.addSubnode(self.activateAreaNode) } override public func layoutForParams(_ params: ListViewItemLayoutParams, item: ListViewItem, previousItem: ListViewItem?, nextItem: ListViewItem?) { @@ -134,37 +140,40 @@ final class CommandChatInputPanelItemNode: ListViewItemNode { let makeTextLayout = TextNode.asyncLayout(self.textNode) return { [weak self] item, params, mergedTop, mergedBottom in - let textFont = Font.medium(floor(item.fontSize.baseDisplaySize * 14.0 / 17.0)) - let descriptionFont = Font.regular(floor(item.fontSize.baseDisplaySize * 14.0 / 17.0)) + let textFont = Font.medium(floor(item.presentationData.fontSize.baseDisplaySize * 14.0 / 17.0)) + let descriptionFont = Font.regular(floor(item.presentationData.fontSize.baseDisplaySize * 14.0 / 17.0)) let leftInset: CGFloat = 55.0 + params.leftInset let rightInset: CGFloat = 10.0 + params.rightInset + let peerName = EnginePeer(item.command.peer).displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder) + let commandString = NSMutableAttributedString() - commandString.append(NSAttributedString(string: "/" + item.command.command.text, font: textFont, textColor: item.theme.list.itemPrimaryTextColor)) + commandString.append(NSAttributedString(string: "/" + item.command.command.text, font: textFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor)) + let command = commandString.string if !item.command.command.description.isEmpty { - commandString.append(NSAttributedString(string: " " + item.command.command.description, font: descriptionFont, textColor: item.theme.list.itemSecondaryTextColor)) + commandString.append(NSAttributedString(string: " " + item.command.command.description, font: descriptionFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor)) } let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: commandString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - rightInset - 40.0, height: 100.0), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) let nodeLayout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: HashtagChatInputPanelItemNode.itemHeight), insets: UIEdgeInsets()) - let iconImage = PresentationResourcesChat.chatCommandPanelArrowImage(item.theme) + let iconImage = PresentationResourcesChat.chatCommandPanelArrowImage(item.presentationData.theme) return (nodeLayout, { _ in if let strongSelf = self { strongSelf.item = item - strongSelf.separatorNode.backgroundColor = item.theme.list.itemPlainSeparatorColor - strongSelf.topSeparatorNode.backgroundColor = item.theme.list.itemPlainSeparatorColor - strongSelf.backgroundColor = item.theme.list.plainBackgroundColor - strongSelf.highlightedBackgroundNode.backgroundColor = item.theme.list.itemHighlightedBackgroundColor + strongSelf.separatorNode.backgroundColor = item.presentationData.theme.list.itemPlainSeparatorColor + strongSelf.topSeparatorNode.backgroundColor = item.presentationData.theme.list.itemPlainSeparatorColor + strongSelf.backgroundColor = item.presentationData.theme.list.plainBackgroundColor + strongSelf.highlightedBackgroundNode.backgroundColor = item.presentationData.theme.list.itemHighlightedBackgroundColor strongSelf.arrowNode.setImage(iconImage, for: []) - strongSelf.avatarNode.setPeer(context: item.context, theme: item.theme, peer: EnginePeer(item.command.peer), emptyColor: item.theme.list.mediaPlaceholderColor) + strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: EnginePeer(item.command.peer), emptyColor: item.presentationData.theme.list.mediaPlaceholderColor) let _ = textApply() @@ -181,6 +190,10 @@ final class CommandChatInputPanelItemNode: ListViewItemNode { strongSelf.separatorNode.frame = CGRect(origin: CGPoint(x: leftInset, y: nodeLayout.contentSize.height - UIScreenPixel), size: CGSize(width: params.width - leftInset, height: UIScreenPixel)) strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: params.width, height: nodeLayout.size.height + UIScreenPixel)) + + strongSelf.activateAreaNode.accessibilityLabel = "\(peerName), \(command)" + strongSelf.activateAreaNode.accessibilityValue = item.command.command.description + strongSelf.activateAreaNode.frame = CGRect(origin: .zero, size: nodeLayout.size) } }) } diff --git a/submodules/TelegramUI/Sources/CommandMenuChatInputContextPanelNode.swift b/submodules/TelegramUI/Sources/CommandMenuChatInputContextPanelNode.swift index 250c514c00..293bc1aa91 100644 --- a/submodules/TelegramUI/Sources/CommandMenuChatInputContextPanelNode.swift +++ b/submodules/TelegramUI/Sources/CommandMenuChatInputContextPanelNode.swift @@ -72,6 +72,7 @@ final class CommandMenuChatInputContextPanelNode: ChatInputContextPanelNode { self.listView.clipsToBounds = false self.listView.isOpaque = false self.listView.stackFromBottom = true + self.listView.keepBottomItemOverscrollBackground = theme.list.plainBackgroundColor self.listView.limitHitTestToNodes = true self.listView.view.disablesInteractiveTransitionGestureRecognizer = true self.listView.accessibilityPageScrolledString = { row, count in @@ -234,7 +235,7 @@ final class CommandMenuChatInputContextPanelNode: ChatInputContextPanelNode { self.listView.keepBottomItemOverscrollBackground = self.theme.list.plainBackgroundColor let new = self.currentEntries?.map({$0.withUpdatedTheme(interfaceState.theme)}) ?? [] - prepareTransition(from: self.currentEntries, to: new) + self.prepareTransition(from: self.currentEntries, to: new) } } diff --git a/submodules/TelegramUI/Sources/CommandMenuChatInputPanelItem.swift b/submodules/TelegramUI/Sources/CommandMenuChatInputPanelItem.swift index d54c6e9b13..49855b427a 100644 --- a/submodules/TelegramUI/Sources/CommandMenuChatInputPanelItem.swift +++ b/submodules/TelegramUI/Sources/CommandMenuChatInputPanelItem.swift @@ -118,6 +118,8 @@ final class CommandMenuChatInputPanelItemNode: ListViewItemNode { private let backgroundNode: ASDisplayNode private let highlightedBackgroundNode: ASDisplayNode + private let activateAreaNode: AccessibilityAreaNode + init() { self.textNode = TextNode() self.commandNode = TextNode() @@ -139,6 +141,9 @@ final class CommandMenuChatInputPanelItemNode: ListViewItemNode { self.backgroundNode = ASDisplayNode() self.backgroundNode.clipsToBounds = true + self.activateAreaNode = AccessibilityAreaNode() + self.activateAreaNode.accessibilityTraits = [.button] + super.init(layerBacked: false, dynamicBounce: false) self.addSubnode(self.clippingNode) @@ -148,6 +153,8 @@ final class CommandMenuChatInputPanelItemNode: ListViewItemNode { self.backgroundNode.addSubnode(self.textNode) self.backgroundNode.addSubnode(self.commandNode) self.backgroundNode.addSubnode(self.separatorNode) + + self.addSubnode(self.activateAreaNode) } override func didLoad() { @@ -227,6 +234,10 @@ final class CommandMenuChatInputPanelItemNode: ListViewItemNode { strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: params.width, height: nodeLayout.size.height + UIScreenPixel)) + strongSelf.activateAreaNode.accessibilityLabel = textString.string + strongSelf.activateAreaNode.accessibilityValue = commandString.string + strongSelf.activateAreaNode.frame = CGRect(origin: .zero, size: nodeLayout.size) + if !mergedTop { strongSelf.shadowNode.isHidden = false strongSelf.shadowNode.frame = CGRect(origin: CGPoint(x: -shadowBlur, y: 0.0), size: CGSize(width: nodeLayout.size.width + shadowBlur * 2.0, height: backgroundCornerRadius + shadowBlur)) diff --git a/submodules/TelegramUI/Sources/HashtagChatInputPanelItem.swift b/submodules/TelegramUI/Sources/HashtagChatInputPanelItem.swift index e3279bc37a..e059fdb382 100644 --- a/submodules/TelegramUI/Sources/HashtagChatInputPanelItem.swift +++ b/submodules/TelegramUI/Sources/HashtagChatInputPanelItem.swift @@ -98,6 +98,8 @@ final class HashtagChatInputPanelItemNode: ListViewItemNode { private var recognizer: ItemListRevealOptionsGestureRecognizer? private var hapticFeedback: HapticFeedback? + private let activateAreaNode: AccessibilityAreaNode + private var item: HashtagChatInputPanelItem? private var validLayout: (CGSize, CGFloat, CGFloat)? @@ -114,11 +116,16 @@ final class HashtagChatInputPanelItemNode: ListViewItemNode { self.highlightedBackgroundNode = ASDisplayNode() self.highlightedBackgroundNode.isLayerBacked = true + self.activateAreaNode = AccessibilityAreaNode() + self.activateAreaNode.accessibilityTraits = [.button] + super.init(layerBacked: false, dynamicBounce: false) self.addSubnode(self.topSeparatorNode) self.addSubnode(self.separatorNode) self.addSubnode(self.textNode) + + self.addSubnode(self.activateAreaNode) } override func didLoad() { @@ -150,7 +157,8 @@ final class HashtagChatInputPanelItemNode: ListViewItemNode { let leftInset: CGFloat = 15.0 + params.leftInset - let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "#\(item.text)", font: textFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: baseWidth, height: 100.0), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + let title = "#\(item.text)" + let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: title, font: textFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: baseWidth, height: 100.0), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) let nodeLayout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: HashtagChatInputPanelItemNode.itemHeight), insets: UIEdgeInsets()) @@ -177,6 +185,9 @@ final class HashtagChatInputPanelItemNode: ListViewItemNode { strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: params.width, height: nodeLayout.size.height + UIScreenPixel)) + strongSelf.activateAreaNode.accessibilityLabel = title + strongSelf.activateAreaNode.frame = CGRect(origin: .zero, size: nodeLayout.size) + strongSelf.setRevealOptions([ItemListRevealOption(key: 0, title: item.presentationData.strings.Common_Delete, icon: .none, color: item.presentationData.theme.list.itemDisclosureActions.destructive.fillColor, textColor: item.presentationData.theme.list.itemDisclosureActions.destructive.foregroundColor)]) strongSelf.setRevealOptionsOpened(item.revealed, animated: animation.isAnimated) } diff --git a/submodules/TelegramUI/Sources/MentionChatInputPanelItem.swift b/submodules/TelegramUI/Sources/MentionChatInputPanelItem.swift index d7c4606ef0..d7ab86afb6 100644 --- a/submodules/TelegramUI/Sources/MentionChatInputPanelItem.swift +++ b/submodules/TelegramUI/Sources/MentionChatInputPanelItem.swift @@ -108,6 +108,8 @@ final class MentionChatInputPanelItemNode: ListViewItemNode { private var recognizer: ItemListRevealOptionsGestureRecognizer? private var hapticFeedback: HapticFeedback? + private let activateAreaNode: AccessibilityAreaNode + private var item: MentionChatInputPanelItem? private var validLayout: (CGSize, CGFloat, CGFloat)? @@ -125,6 +127,9 @@ final class MentionChatInputPanelItemNode: ListViewItemNode { self.highlightedBackgroundNode = ASDisplayNode() self.highlightedBackgroundNode.isLayerBacked = true + self.activateAreaNode = AccessibilityAreaNode() + self.activateAreaNode.accessibilityTraits = [.button] + super.init(layerBacked: false, dynamicBounce: false) self.addSubnode(self.topSeparatorNode) @@ -132,6 +137,8 @@ final class MentionChatInputPanelItemNode: ListViewItemNode { self.addSubnode(self.avatarNode) self.addSubnode(self.textNode) + + self.addSubnode(self.activateAreaNode) } override func didLoad() { @@ -171,10 +178,14 @@ final class MentionChatInputPanelItemNode: ListViewItemNode { updatedInverted = item.inverted } + + let title = EnginePeer(item.peer).displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder) + var username: String? let string = NSMutableAttributedString() - string.append(NSAttributedString(string: item.peer.debugDisplayTitle, font: primaryFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor)) + string.append(NSAttributedString(string: title, font: primaryFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor)) if let addressName = item.peer.addressName, !addressName.isEmpty { string.append(NSAttributedString(string: " @\(addressName)", font: secondaryFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor)) + username = "@\(addressName)" } let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: string, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - rightInset, height: 100.0), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) @@ -215,6 +226,10 @@ final class MentionChatInputPanelItemNode: ListViewItemNode { strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: params.width, height: nodeLayout.size.height + UIScreenPixel)) + strongSelf.activateAreaNode.accessibilityLabel = title + strongSelf.activateAreaNode.accessibilityValue = username + strongSelf.activateAreaNode.frame = CGRect(origin: .zero, size: nodeLayout.size) + if let peer = item.peer as? TelegramUser, let _ = peer.botInfo { strongSelf.setRevealOptions([ItemListRevealOption(key: 0, title: item.presentationData.strings.Common_Delete, icon: .none, color: item.presentationData.theme.list.itemDisclosureActions.destructive.fillColor, textColor: item.presentationData.theme.list.itemDisclosureActions.destructive.foregroundColor)]) strongSelf.setRevealOptionsOpened(item.revealed, animated: animation.isAnimated) diff --git a/submodules/TelegramUI/Sources/MultiScaleTextNode.swift b/submodules/TelegramUI/Sources/MultiScaleTextNode.swift index 37548d5d82..00526bc900 100644 --- a/submodules/TelegramUI/Sources/MultiScaleTextNode.swift +++ b/submodules/TelegramUI/Sources/MultiScaleTextNode.swift @@ -63,6 +63,8 @@ final class MultiScaleTextNode: ASDisplayNode { for (key, state) in states { if let node = self.stateNodes[key] { node.textNode.attributedText = NSAttributedString(string: text, font: state.attributes.font, textColor: state.attributes.color) + node.textNode.isAccessibilityElement = true + node.textNode.accessibilityLabel = text let nodeSize = node.textNode.updateLayout(state.constrainedSize) let nodeLayout = MultiScaleTextLayout(size: nodeSize) if key == mainState { diff --git a/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenCallListItem.swift b/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenCallListItem.swift index 5b2aa97897..40adb5bbea 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenCallListItem.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenCallListItem.swift @@ -30,6 +30,7 @@ final class PeerInfoScreenCallListItem: PeerInfoScreenItem { private final class PeerInfoScreenCallListItemNode: PeerInfoScreenItemNode { private let selectionNode: PeerInfoScreenSelectableBackgroundNode private let bottomSeparatorNode: ASDisplayNode + private let maskNode: ASImageNode private var item: PeerInfoScreenCallListItem? private var itemNode: ItemListCallListItemNode? @@ -42,6 +43,9 @@ private final class PeerInfoScreenCallListItemNode: PeerInfoScreenItemNode { self.bottomSeparatorNode = ASDisplayNode() self.bottomSeparatorNode.isLayerBacked = true + self.maskNode = ASImageNode() + self.maskNode.isUserInteractionEnabled = false + super.init() bringToFrontForHighlightImpl = { [weak self] in @@ -50,6 +54,7 @@ private final class PeerInfoScreenCallListItemNode: PeerInfoScreenItemNode { self.addSubnode(self.bottomSeparatorNode) self.addSubnode(self.selectionNode) + self.addSubnode(self.maskNode) } override func update(width: CGFloat, safeInsets: UIEdgeInsets, presentationData: PresentationData, item: PeerInfoScreenItem, topItem: PeerInfoScreenItem?, bottomItem: PeerInfoScreenItem?, hasCorners: Bool, transition: ContainedViewLayoutTransition) -> CGFloat { @@ -106,6 +111,14 @@ private final class PeerInfoScreenCallListItemNode: PeerInfoScreenItemNode { transition.updateFrame(node: self.bottomSeparatorNode, frame: CGRect(origin: CGPoint(x: sideInset, y: height - UIScreenPixel), size: CGSize(width: width - sideInset, height: UIScreenPixel))) transition.updateAlpha(node: self.bottomSeparatorNode, alpha: bottomItem == nil ? 0.0 : 1.0) + let hasCorners = hasCorners && (topItem == nil || bottomItem == nil) + let hasTopCorners = hasCorners && topItem == nil + let hasBottomCorners = hasCorners && bottomItem == nil + + self.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil + self.maskNode.frame = CGRect(origin: CGPoint(x: safeInsets.left, y: 0.0), size: CGSize(width: width - safeInsets.left - safeInsets.right, height: height)) + self.bottomSeparatorNode.isHidden = hasBottomCorners + return height } } diff --git a/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenLabeledValueItem.swift b/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenLabeledValueItem.swift index 788cdfb4a1..9aa643886c 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenLabeledValueItem.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenLabeledValueItem.swift @@ -527,7 +527,6 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode { self.activateArea.accessibilityLabel = item.label self.activateArea.accessibilityValue = item.text - let contentSize = CGSize(width: width, height: height) self.containerNode.frame = CGRect(origin: CGPoint(), size: contentSize) self.contextSourceNode.frame = CGRect(origin: CGPoint(), size: contentSize) diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift index ebca5e5cee..2fa0a79bec 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift @@ -2918,7 +2918,6 @@ final class PeerInfoHeaderNode: ASDisplayNode { TitleNodeStateRegular: MultiScaleTextState(attributes: titleAttributes, constrainedSize: titleConstrainedSize), TitleNodeStateExpanded: MultiScaleTextState(attributes: smallTitleAttributes, constrainedSize: titleConstrainedSize) ], mainState: TitleNodeStateRegular) - self.titleNode.accessibilityLabel = titleStringText let subtitleNodeLayout = self.subtitleNode.updateLayout(text: subtitleStringText, states: [ TitleNodeStateRegular: MultiScaleTextState(attributes: subtitleAttributes, constrainedSize: titleConstrainedSize), From 2ef1de9a8c54129f8596bf6ad33670a012a9ba1d Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 7 Feb 2023 17:15:56 +0400 Subject: [PATCH 11/12] Remove launch logo --- .../Telegram-iOS/Base.lproj/LaunchScreen.xib | 18 ++---------- .../platform/ios/RMIntroViewController.m | 2 ++ .../TelegramUI/Sources/AppDelegate.swift | 28 ++----------------- 3 files changed, 6 insertions(+), 42 deletions(-) diff --git a/Telegram/Telegram-iOS/Base.lproj/LaunchScreen.xib b/Telegram/Telegram-iOS/Base.lproj/LaunchScreen.xib index c8bc1ddd72..8548343621 100644 --- a/Telegram/Telegram-iOS/Base.lproj/LaunchScreen.xib +++ b/Telegram/Telegram-iOS/Base.lproj/LaunchScreen.xib @@ -1,6 +1,6 @@ - + @@ -10,27 +10,13 @@ - + - - - - - - - - - - - - - - diff --git a/submodules/RMIntro/Sources/platform/ios/RMIntroViewController.m b/submodules/RMIntro/Sources/platform/ios/RMIntroViewController.m index 249ca084ca..f698ca3f84 100644 --- a/submodules/RMIntro/Sources/platform/ios/RMIntroViewController.m +++ b/submodules/RMIntro/Sources/platform/ios/RMIntroViewController.m @@ -254,11 +254,13 @@ typedef enum { _glkView.transform = CGAffineTransformIdentity; } completion:nil]; + _glkView.alpha = 0.0; _pageScrollView.alpha = 0.0; _pageControl.alpha = 0.0; _startButton.alpha = 0.0; [UIView animateWithDuration:0.3 delay:0.15 options:kNilOptions animations:^{ + _glkView.alpha = 1.0; _pageScrollView.alpha = 1.0; _pageControl.alpha = 1.0; _startButton.alpha = 1.0; diff --git a/submodules/TelegramUI/Sources/AppDelegate.swift b/submodules/TelegramUI/Sources/AppDelegate.swift index 016445a072..f7d20548ae 100644 --- a/submodules/TelegramUI/Sources/AppDelegate.swift +++ b/submodules/TelegramUI/Sources/AppDelegate.swift @@ -337,12 +337,6 @@ private func extractAccountManagerState(records: AccountRecordsView deliverOnMainQueue).start(next: { [weak launchIconView] context in + |> deliverOnMainQueue).start(next: { context in print("Application: context took \(CFAbsoluteTimeGetCurrent() - startTime) to become available") var network: Network? @@ -1105,13 +1099,6 @@ private func extractAccountManagerState(records: AccountRecordsView deliverOnMainQueue).start(next: { [weak launchIconView] context in + |> deliverOnMainQueue).start(next: { context in var network: Network? if let context = context { network = context.account.network @@ -1211,17 +1198,6 @@ private func extractAccountManagerState(records: AccountRecordsView deliverOnMainQueue).start(next: { _ in progressDisposable.dispose() self.mainWindow.present(context.rootController, on: .root) - - if let launchIconView { - if context.rootController.topViewController is AuthorizationSequenceSplashController { - context.rootController.view.addSubview(launchIconView) - Queue.mainQueue().after(0.01, { - launchIconView.removeFromSuperview() - }) - } else { - launchIconView.removeFromSuperview() - } - } })) } else { authContextReadyDisposable.set(nil) From f80093608077ec9987c39fbe9a333412dd85dfa5 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 7 Feb 2023 21:41:49 +0400 Subject: [PATCH 12/12] Various fixes --- .../Telegram-iOS/en.lproj/Localizable.strings | 2 + .../Sources/AttachmentPanel.swift | 2 + .../ChatListFilterTabContainerNode.swift | 28 +-- .../ChatListSearchFiltersContainerNode.swift | 7 + .../Sources/Node/ChatListNode.swift | 3 + .../Sources/Node/ChatListNodeEntries.swift | 4 +- .../ChatPresentationInterfaceState.swift | 3 - .../ContextControllerActionsStackNode.swift | 1 + .../Source/AccessibilityAreaNode.swift | 10 + submodules/Display/Source/NavigationBar.swift | 7 + .../Sources/GalleryControllerNode.swift | 8 + .../TGPhotoEditorSliderView.h | 6 + .../Sources/TGPhotoEditorSliderView.m | 28 +++ submodules/PremiumUI/Sources/HelloView.swift | 14 +- .../BubbleSettingsController.swift | 6 + .../AutodownloadDataUsagePickerItem.swift | 41 ++++ .../Recent Sessions/ItemListWebsiteItem.swift | 28 +++ .../TextSizeSelectionController.swift | 6 + .../Sources/ThemeCarouselItem.swift | 16 ++ .../Themes/ThemeSettingsAppIconItem.swift | 17 +- .../Themes/ThemeSettingsThemeItem.swift | 17 +- .../Sources/ChatListHeaderComponent.swift | 3 + .../TelegramUI/Sources/ChatController.swift | 104 +------- .../Sources/ChatInfoTitlePanelNode.swift | 230 ------------------ .../ChatInterfaceTitlePanelNodes.swift | 10 +- .../ChatInviteRequestsTitlePanelNode.swift | 10 + .../ChatRequestInProgressTitlePanelNode.swift | 10 + .../Sources/ChatToastAlertPanelNode.swift | 8 + .../TranslateUI/Sources/ChatTranslation.swift | 7 +- 29 files changed, 265 insertions(+), 371 deletions(-) delete mode 100644 submodules/TelegramUI/Sources/ChatInfoTitlePanelNode.swift diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 165ccc3dec..99a1e37548 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -8906,3 +8906,5 @@ Sorry for the inconvenience."; "Gallery.VoiceOver.Edit" = "Edit"; "Gallery.VoiceOver.Stickers" = "Stickers"; "Gallery.VoiceOver.PictureInPicture" = "Picture-in-Picture"; + +"Appearance.VoiceOver.Theme" = "%@ Theme"; diff --git a/submodules/AttachmentUI/Sources/AttachmentPanel.swift b/submodules/AttachmentUI/Sources/AttachmentPanel.swift index bcca2e3b1f..ac8c8bf667 100644 --- a/submodules/AttachmentUI/Sources/AttachmentPanel.swift +++ b/submodules/AttachmentUI/Sources/AttachmentPanel.swift @@ -757,6 +757,8 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate { self.scrollNode.view.delegate = self self.scrollNode.view.showsHorizontalScrollIndicator = false self.scrollNode.view.showsVerticalScrollIndicator = false + + self.view.accessibilityTraits = .tabBar } @objc private func buttonPressed() { diff --git a/submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift b/submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift index ad2d5de24b..66ad27a098 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift @@ -76,9 +76,7 @@ private final class ItemNode: ASDisplayNode { private var deleteButtonNode: ItemNodeDeleteButtonNode? private let buttonNode: HighlightTrackingButtonNode - - private let activateArea: AccessibilityAreaNode - + private var selectionFraction: CGFloat = 0.0 private(set) var unreadCount: Int = 0 @@ -140,13 +138,9 @@ private final class ItemNode: ASDisplayNode { self.badgeBackgroundInactiveNode.displayWithoutProcessing = true self.buttonNode = HighlightTrackingButtonNode() - - self.activateArea = AccessibilityAreaNode() - + super.init() - - self.isAccessibilityElement = true - + self.extractedContainerNode.contentNode.addSubnode(self.extractedBackgroundNode) self.extractedContainerNode.contentNode.addSubnode(self.titleContainer) self.titleContainer.addSubnode(self.titleNode) @@ -163,9 +157,7 @@ private final class ItemNode: ASDisplayNode { self.containerNode.addSubnode(self.extractedContainerNode) self.containerNode.targetNodeForActivationProgress = self.extractedContainerNode.contentNode self.addSubnode(self.containerNode) - - self.addSubnode(self.activateArea) - + self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside) self.containerNode.activated = { [weak self] gesture, _ in @@ -212,11 +204,16 @@ private final class ItemNode: ASDisplayNode { self.badgeBackgroundInactiveNode.image = generateStretchableFilledCircleImage(diameter: 18.0, color: presentationData.theme.chatList.unreadBadgeInactiveBackgroundColor) } - self.activateArea.accessibilityLabel = title + self.buttonNode.accessibilityLabel = title if unreadCount > 0 { - self.activateArea.accessibilityValue = strings.VoiceOver_Chat_UnreadMessages(Int32(unreadCount)) + self.buttonNode.accessibilityValue = strings.VoiceOver_Chat_UnreadMessages(Int32(unreadCount)) } else { - self.activateArea.accessibilityValue = "" + self.buttonNode.accessibilityValue = "" + } + if selectionFraction == 1.0 { + self.buttonNode.accessibilityTraits = [.button, .selected] + } else { + self.buttonNode.accessibilityTraits = [.button] } self.containerNode.isGestureEnabled = !isEditing && !isReordering @@ -338,7 +335,6 @@ private final class ItemNode: ASDisplayNode { self.extractedContainerNode.contentNode.frame = CGRect(origin: CGPoint(), size: size) self.extractedContainerNode.contentRect = CGRect(origin: CGPoint(x: self.extractedBackgroundNode.frame.minX, y: 0.0), size: CGSize(width:self.extractedBackgroundNode.frame.width, height: size.height)) self.containerNode.frame = CGRect(origin: CGPoint(), size: size) - self.activateArea.frame = CGRect(origin: CGPoint(), size: size) self.hitTestSlop = UIEdgeInsets(top: 0.0, left: -sideInset, bottom: 0.0, right: -sideInset) self.extractedContainerNode.hitTestSlop = self.hitTestSlop diff --git a/submodules/ChatListUI/Sources/ChatListSearchFiltersContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchFiltersContainerNode.swift index 88e68b7335..25a99c9508 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchFiltersContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchFiltersContainerNode.swift @@ -126,6 +126,13 @@ private final class ItemNode: ASDisplayNode { transition.updateAlpha(node: self.titleNode, alpha: deselectionAlpha) transition.updateAlpha(node: self.titleActiveNode, alpha: selectionAlpha) + self.buttonNode.accessibilityLabel = title + if selectionFraction == 1.0 { + self.buttonNode.accessibilityTraits = [.button, .selected] + } else { + self.buttonNode.accessibilityTraits = [.button] + } + if self.theme !== presentationData.theme { self.theme = presentationData.theme self.iconNode.image = icon diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift index 99c8479b81..39fa90802e 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNode.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNode.swift @@ -1861,6 +1861,9 @@ public final class ChatListNode: ListView { return false } } + case .GroupReferenceEntry: + isEmpty = false + return true default: return true } diff --git a/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift b/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift index b371c384a3..95f8de2fdd 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift @@ -686,9 +686,9 @@ func chatListNodeEntriesForView(_ view: EngineChatList, state: ChatListNodeState } else if case let .peerType(type) = mode, !result.isEmpty { switch type { case .group: - result.append(.AdditionalCategory(index: 0, id: 0, title: "Create a New Group for This", image: PresentationResourcesItemList.createGroupIcon(state.presentationData.theme), appearance: .action, selected: false, presentationData: state.presentationData)) + result.append(.AdditionalCategory(index: 0, id: 0, title: state.presentationData.strings.RequestPeer_CreateNewGroup, image: PresentationResourcesItemList.createGroupIcon(state.presentationData.theme), appearance: .action, selected: false, presentationData: state.presentationData)) case .channel: - result.append(.AdditionalCategory(index: 0, id: 0, title: "Create a New Channel for This", image: PresentationResourcesItemList.createGroupIcon(state.presentationData.theme), appearance: .action, selected: false, presentationData: state.presentationData)) + result.append(.AdditionalCategory(index: 0, id: 0, title: state.presentationData.strings.RequestPeer_CreateNewChannel, image: PresentationResourcesItemList.createGroupIcon(state.presentationData.theme), appearance: .action, selected: false, presentationData: state.presentationData)) default: break } diff --git a/submodules/ChatPresentationInterfaceState/Sources/ChatPresentationInterfaceState.swift b/submodules/ChatPresentationInterfaceState/Sources/ChatPresentationInterfaceState.swift index 0e2ee1396e..3021294e44 100644 --- a/submodules/ChatPresentationInterfaceState/Sources/ChatPresentationInterfaceState.swift +++ b/submodules/ChatPresentationInterfaceState/Sources/ChatPresentationInterfaceState.swift @@ -103,7 +103,6 @@ public enum ChatInputMode: Equatable { public enum ChatTitlePanelContext: Equatable, Comparable { case pinnedMessage - case chatInfo case requestInProgress case toastAlert(String) case inviteRequests([EnginePeer], Int32) @@ -112,8 +111,6 @@ public enum ChatTitlePanelContext: Equatable, Comparable { switch self { case .pinnedMessage: return 0 - case .chatInfo: - return 1 case .requestInProgress: return 2 case .toastAlert: diff --git a/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift b/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift index 493722ee9a..0ac19f61d4 100644 --- a/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift +++ b/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift @@ -101,6 +101,7 @@ private final class ContextControllerActionsListActionItemNode: HighlightTrackin self.isAccessibilityElement = true self.accessibilityLabel = item.text + self.accessibilityTraits = [.button] self.addSubnode(self.highlightBackgroundNode) self.addSubnode(self.titleLabelNode) diff --git a/submodules/Display/Source/AccessibilityAreaNode.swift b/submodules/Display/Source/AccessibilityAreaNode.swift index 0079f46e3a..5975b2fa31 100644 --- a/submodules/Display/Source/AccessibilityAreaNode.swift +++ b/submodules/Display/Source/AccessibilityAreaNode.swift @@ -8,6 +8,8 @@ public protocol AccessibilityFocusableNode { public final class AccessibilityAreaNode: ASDisplayNode { public var activate: (() -> Bool)? + public var increment: (() -> Void)? + public var decrement: (() -> Void)? public var focused: (() -> Void)? override public init() { @@ -43,4 +45,12 @@ public final class AccessibilityAreaNode: ASDisplayNode { } } } + + override public func accessibilityIncrement() { + self.increment?() + } + + override public func accessibilityDecrement() { + self.decrement?() + } } diff --git a/submodules/Display/Source/NavigationBar.swift b/submodules/Display/Source/NavigationBar.swift index 7752442d25..fd839d4f14 100644 --- a/submodules/Display/Source/NavigationBar.swift +++ b/submodules/Display/Source/NavigationBar.swift @@ -697,9 +697,16 @@ open class NavigationBar: ASDisplayNode { if self.rightButtonNode.supernode != nil { addAccessibilityChildren(of: self.rightButtonNode, container: self, to: &accessibilityElements) } + if let customHeaderContentView = self.customHeaderContentView, customHeaderContentView.superview != nil { + customHeaderContentView.accessibilityFrame = UIAccessibility.convertToScreenCoordinates(customHeaderContentView.bounds, in: customHeaderContentView) + accessibilityElements.append(customHeaderContentView) + } if let contentNode = self.contentNode { addAccessibilityChildren(of: contentNode, container: self, to: &accessibilityElements) } + if let secondaryContentNode = self.secondaryContentNode { + addAccessibilityChildren(of: secondaryContentNode, container: self, to: &accessibilityElements) + } return accessibilityElements } set(value) { } diff --git a/submodules/GalleryUI/Sources/GalleryControllerNode.swift b/submodules/GalleryUI/Sources/GalleryControllerNode.swift index 4069f0fa68..20409a6720 100644 --- a/submodules/GalleryUI/Sources/GalleryControllerNode.swift +++ b/submodules/GalleryUI/Sources/GalleryControllerNode.swift @@ -528,4 +528,12 @@ open class GalleryControllerNode: ASDisplayNode, UIScrollViewDelegate, UIGesture break } } + + open override func accessibilityPerformEscape() -> Bool { + if let controller = self.galleryController() { + controller.dismiss(animated: true) + return true + } + return false + } } diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorSliderView.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorSliderView.h index 319a69076e..b72017d1e0 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorSliderView.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorSliderView.h @@ -43,6 +43,12 @@ - (void)setValue:(CGFloat)value animated:(BOOL)animated; +- (void)increase; +- (void)increaseBy:(CGFloat)delta; + +- (void)decrease; +- (void)decreaseBy:(CGFloat)delta; + @end extern const CGFloat TGPhotoEditorSliderViewMargin; diff --git a/submodules/LegacyComponents/Sources/TGPhotoEditorSliderView.m b/submodules/LegacyComponents/Sources/TGPhotoEditorSliderView.m index 39154218cd..a9215a4c68 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoEditorSliderView.m +++ b/submodules/LegacyComponents/Sources/TGPhotoEditorSliderView.m @@ -668,4 +668,32 @@ const CGFloat TGPhotoEditorSliderViewInternalMargin = 7.0f; self.interactionEnded(); } +- (void)increase { + self.value = MIN(self.maximumValue, self.value + 1); + + [self sendActionsForControlEvents:UIControlEventValueChanged]; + [self setNeedsLayout]; +} + +- (void)increaseBy:(CGFloat)delta { + self.value = MIN(self.maximumValue, self.value + delta); + + [self sendActionsForControlEvents:UIControlEventValueChanged]; + [self setNeedsLayout]; +} + +- (void)decrease { + self.value = MAX(self.minimumValue, self.value - 1); + + [self sendActionsForControlEvents:UIControlEventValueChanged]; + [self setNeedsLayout]; +} + +- (void)decreaseBy:(CGFloat)delta { + self.value = MAX(self.minimumValue, self.value - delta); + + [self sendActionsForControlEvents:UIControlEventValueChanged]; + [self setNeedsLayout]; +} + @end diff --git a/submodules/PremiumUI/Sources/HelloView.swift b/submodules/PremiumUI/Sources/HelloView.swift index f682a5cab7..41b06ae152 100644 --- a/submodules/PremiumUI/Sources/HelloView.swift +++ b/submodules/PremiumUI/Sources/HelloView.swift @@ -55,7 +55,7 @@ final class HelloView: UIView, PhoneDemoDecorationView { required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + func setupAnimations() { guard self.activePhrases.isEmpty else { return @@ -121,11 +121,13 @@ final class HelloView: UIView, PhoneDemoDecorationView { self.activePositions.insert(positionIndex) let duration: Double = Double.random(in: 1.75...2.25) - view.layer.animateKeyframes(values: [0.0, 1.0, 0.0] as [NSNumber], duration: duration, keyPath: "opacity", removeOnCompletion: false, completion: { [weak view] _ in - self.activePhrases.remove(index) - self.activePositions.remove(positionIndex) - view?.removeFromSuperview() - self.spawnNextPhrase() + view.layer.animateKeyframes(values: [0.0, 1.0, 0.0] as [NSNumber], duration: duration, keyPath: "opacity", removeOnCompletion: false, completion: { [weak view, weak self] _ in + if let self { + self.activePhrases.remove(index) + self.activePositions.remove(positionIndex) + view?.removeFromSuperview() + self.spawnNextPhrase() + } }) view.layer.animateScale(from: CGFloat.random(in: 0.4 ..< 0.6), to: CGFloat.random(in: 0.9 ..< 1.2), duration: duration, removeOnCompletion: false) diff --git a/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift b/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift index 1745f599e7..4f30925641 100644 --- a/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift +++ b/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift @@ -429,6 +429,9 @@ private final class BubbleSettingsToolbarNode: ASDisplayNode { super.init() + self.cancelButton.accessibilityTraits = [.button] + self.doneButton.accessibilityTraits = [.button] + self.addSubnode(self.switchItemNode) self.addSubnode(self.cornerRadiusItemNode) self.addSubnode(self.cancelButton) @@ -482,6 +485,9 @@ private final class BubbleSettingsToolbarNode: ASDisplayNode { self.cancelButton.setTitle(presentationData.strings.Common_Cancel, with: Font.regular(17.0), with: presentationData.theme.list.itemPrimaryTextColor, for: []) self.doneButton.setTitle(presentationData.strings.Wallpaper_Set, with: Font.regular(17.0), with: presentationData.theme.list.itemPrimaryTextColor, for: []) + + self.cancelButton.accessibilityLabel = presentationData.strings.Common_Cancel + self.doneButton.accessibilityLabel = presentationData.strings.Wallpaper_Set } func updatePresentationThemeSettings(presentationThemeSettings: PresentationThemeSettings) { diff --git a/submodules/SettingsUI/Sources/Data and Storage/AutodownloadDataUsagePickerItem.swift b/submodules/SettingsUI/Sources/Data and Storage/AutodownloadDataUsagePickerItem.swift index fc840584ef..51a0462c7f 100644 --- a/submodules/SettingsUI/Sources/Data and Storage/AutodownloadDataUsagePickerItem.swift +++ b/submodules/SettingsUI/Sources/Data and Storage/AutodownloadDataUsagePickerItem.swift @@ -95,6 +95,8 @@ private final class AutodownloadDataUsagePickerItemNode: ListViewItemNode { private let customTextNode: TextNode private var sliderView: TGPhotoEditorSliderView? + private let activateArea: AccessibilityAreaNode + private var item: AutodownloadDataUsagePickerItem? private var layoutParams: ListViewItemLayoutParams? @@ -126,12 +128,27 @@ private final class AutodownloadDataUsagePickerItemNode: ListViewItemNode { self.customTextNode.isUserInteractionEnabled = false self.customTextNode.displaysAsynchronously = false + self.activateArea = AccessibilityAreaNode() + super.init(layerBacked: false, dynamicBounce: false) self.addSubnode(self.lowTextNode) self.addSubnode(self.mediumTextNode) self.addSubnode(self.highTextNode) self.addSubnode(self.customTextNode) + self.addSubnode(self.activateArea) + + self.activateArea.increment = { [weak self] in + if let self { + self.sliderView?.increase() + } + } + + self.activateArea.decrement = { [weak self] in + if let self { + self.sliderView?.decrease() + } + } } func updateSliderView() { @@ -154,6 +171,8 @@ private final class AutodownloadDataUsagePickerItemNode: ListViewItemNode { sliderView.isUserInteractionEnabled = item.enabled sliderView.alpha = item.enabled ? 1.0 : 0.4 sliderView.layer.allowsGroupOpacity = !item.enabled + + self.updateAccessibilityLabels() } } @@ -323,11 +342,33 @@ private final class AutodownloadDataUsagePickerItemNode: ListViewItemNode { strongSelf.updateSliderView() } + strongSelf.activateArea.accessibilityLabel = item.strings.AutoDownloadSettings_DataUsage + + strongSelf.activateArea.frame = CGRect(origin: CGPoint(x: params.leftInset, y: 0.0), size: CGSize(width: params.width - params.leftInset - params.rightInset, height: layout.contentSize.height)) } }) } } + private func updateAccessibilityLabels() { + guard let item = self.item else { + return + } + var textNodes: [TextNode] = [self.lowTextNode, self.mediumTextNode, self.highTextNode] + if let customPosition = item.customPosition { + textNodes.insert(self.customTextNode, at: customPosition) + } + if let value = self.sliderView?.value { + self.activateArea.accessibilityValue = textNodes[Int(value)].cachedLayout?.attributedString?.string ?? "" + } + var accessibilityTraits: UIAccessibilityTraits = [.adjustable] + if item.enabled { + } else { + accessibilityTraits.insert(.notEnabled) + } + self.activateArea.accessibilityTraits = accessibilityTraits + } + override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) { self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4) } diff --git a/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/ItemListWebsiteItem.swift b/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/ItemListWebsiteItem.swift index 84c9408aa7..0618ba3b71 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/ItemListWebsiteItem.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/ItemListWebsiteItem.swift @@ -135,6 +135,8 @@ class ItemListWebsiteItemNode: ItemListRevealOptionsItemNode { return self.containerNode } + private let activateArea: AccessibilityAreaNode + private var layoutParams: (ItemListWebsiteItem, ListViewItemLayoutParams, ItemListNeighbors)? private var editableControlNode: ItemListEditableControlNode? @@ -184,6 +186,8 @@ class ItemListWebsiteItemNode: ItemListRevealOptionsItemNode { self.highlightedBackgroundNode = ASDisplayNode() self.highlightedBackgroundNode.isLayerBacked = true + self.activateArea = AccessibilityAreaNode() + super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) self.addSubnode(self.containerNode) @@ -191,6 +195,8 @@ class ItemListWebsiteItemNode: ItemListRevealOptionsItemNode { self.containerNode.addSubnode(self.titleNode) self.containerNode.addSubnode(self.appNode) self.containerNode.addSubnode(self.locationNode) + + self.addSubnode(self.activateArea) } func asyncLayout() -> (_ item: ItemListWebsiteItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, (Bool) -> Void) { @@ -288,6 +294,28 @@ class ItemListWebsiteItemNode: ItemListRevealOptionsItemNode { if let strongSelf = self { strongSelf.layoutParams = (item, params, neighbors) + strongSelf.activateArea.frame = CGRect(origin: CGPoint(x: params.leftInset, y: 0.0), size: CGSize(width: params.width - params.leftInset - params.rightInset, height: layout.contentSize.height)) + + strongSelf.activateArea.accessibilityLabel = titleAttributedString?.string ?? "" + + var value = "" + if let string = appAttributedString?.string { + value += string + } + if let string = locationAttributedString?.string { + if !value.isEmpty { + value += "\n" + } + value += string + } + strongSelf.activateArea.accessibilityValue = value + + if item.enabled { + strongSelf.activateArea.accessibilityTraits = [] + } else { + strongSelf.activateArea.accessibilityTraits = .notEnabled + } + if let _ = updatedTheme { strongSelf.topStripeNode.backgroundColor = item.presentationData.theme.list.itemBlocksSeparatorColor strongSelf.bottomStripeNode.backgroundColor = item.presentationData.theme.list.itemBlocksSeparatorColor diff --git a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift index ed7294f291..fca1b66abb 100644 --- a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift +++ b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift @@ -716,6 +716,9 @@ private final class TextSelectionToolbarNode: ASDisplayNode { super.init() + self.cancelButton.accessibilityTraits = [.button] + self.doneButton.accessibilityTraits = [.button] + self.addSubnode(self.switchItemNode) self.addSubnode(self.fontSizeItemNode) self.addSubnode(self.cancelButton) @@ -769,6 +772,9 @@ private final class TextSelectionToolbarNode: ASDisplayNode { self.cancelButton.setTitle(presentationData.strings.Common_Cancel, with: Font.regular(17.0), with: presentationData.theme.list.itemPrimaryTextColor, for: []) self.doneButton.setTitle(presentationData.strings.Wallpaper_Set, with: Font.regular(17.0), with: presentationData.theme.list.itemPrimaryTextColor, for: []) + + self.cancelButton.accessibilityLabel = presentationData.strings.Common_Cancel + self.doneButton.accessibilityLabel = presentationData.strings.Wallpaper_Set } func updatePresentationThemeSettings(presentationThemeSettings: PresentationThemeSettings) { diff --git a/submodules/SettingsUI/Sources/ThemeCarouselItem.swift b/submodules/SettingsUI/Sources/ThemeCarouselItem.swift index a03e7dbd82..7b96c101e7 100644 --- a/submodules/SettingsUI/Sources/ThemeCarouselItem.swift +++ b/submodules/SettingsUI/Sources/ThemeCarouselItem.swift @@ -203,6 +203,8 @@ private final class ThemeCarouselThemeItemIconNode : ListViewItemNode { private var placeholderNode: StickerShimmerEffectNode var snapshotView: UIView? + private let activateAreaNode: AccessibilityAreaNode + var item: ThemeCarouselThemeIconItem? override var visibility: ListViewItemNodeVisibility { @@ -246,6 +248,8 @@ private final class ThemeCarouselThemeItemIconNode : ListViewItemNode { self.emojiImageNode = TransformImageNode() self.placeholderNode = StickerShimmerEffectNode() + + self.activateAreaNode = AccessibilityAreaNode() super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) @@ -272,6 +276,8 @@ private final class ThemeCarouselThemeItemIconNode : ListViewItemNode { } firstTime = false } + + self.addSubnode(self.activateAreaNode) } deinit { @@ -442,6 +448,16 @@ private final class ThemeCarouselThemeItemIconNode : ListViewItemNode { animatedStickerNode.frame = emojiFrame animatedStickerNode.updateLayout(size: emojiFrame.size) } + + let presentationData = item.context.sharedContext.currentPresentationData.with { $0 } + strongSelf.activateAreaNode.accessibilityLabel = item.themeReference.emoticon.flatMap { presentationData.strings.Appearance_VoiceOver_Theme($0).string } + if item.selected { + strongSelf.activateAreaNode.accessibilityTraits = [.button, .selected] + } else { + strongSelf.activateAreaNode.accessibilityTraits = [.button] + } + + strongSelf.activateAreaNode.frame = CGRect(origin: .zero, size: itemLayout.size) } }) } diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsAppIconItem.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsAppIconItem.swift index 1175e83abb..b88e7e4a58 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsAppIconItem.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsAppIconItem.swift @@ -102,6 +102,8 @@ private final class ThemeSettingsAppIconNode : ASDisplayNode { private let textNode: ImmediateTextNode private var action: (() -> Void)? + private let activateAreaNode: AccessibilityAreaNode + private var locked = false override init() { @@ -122,12 +124,16 @@ private final class ThemeSettingsAppIconNode : ASDisplayNode { self.textNode.isUserInteractionEnabled = false self.textNode.displaysAsynchronously = false + self.activateAreaNode = AccessibilityAreaNode() + self.activateAreaNode.accessibilityTraits = [.button] + super.init() self.addSubnode(self.iconNode) self.addSubnode(self.overlayNode) self.addSubnode(self.textNode) self.addSubnode(self.lockNode) + self.addSubnode(self.activateAreaNode) } func setup(theme: PresentationTheme, icon: UIImage, title: NSAttributedString, locked: Bool, color: UIColor, bordered: Bool, selected: Bool, action: @escaping () -> Void) { @@ -140,6 +146,13 @@ private final class ThemeSettingsAppIconNode : ASDisplayNode { action() } + self.activateAreaNode.accessibilityLabel = title.string + if locked { + self.activateAreaNode.accessibilityTraits = [.button, .notEnabled] + } else { + self.activateAreaNode.accessibilityTraits = [.button] + } + self.setNeedsLayout() } @@ -171,6 +184,8 @@ private final class ThemeSettingsAppIconNode : ASDisplayNode { self.textNode.frame = textFrame self.lockNode.frame = CGRect(x: self.textNode.frame.minX - 10.0, y: 90.0, width: 6.0, height: 8.0) + + self.activateAreaNode.frame = self.bounds } } @@ -186,7 +201,7 @@ class ThemeSettingsAppIconItemNode: ListViewItemNode, ItemListItemNode { private let scrollNode: ASScrollNode private var nodes: [ThemeSettingsAppIconNode] = [] - + private var item: ThemeSettingsAppIconItem? private var layoutParams: ListViewItemLayoutParams? diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsThemeItem.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsThemeItem.swift index 81c8810792..6fa79773ef 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsThemeItem.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsThemeItem.swift @@ -207,6 +207,8 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode { private let titleNode: TextNode var snapshotView: UIView? + private let activateAreaNode: AccessibilityAreaNode + var item: ThemeSettingsThemeIconItem? init() { @@ -222,13 +224,17 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode { self.titleNode = TextNode() self.titleNode.isUserInteractionEnabled = false - + + self.activateAreaNode = AccessibilityAreaNode() + super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) self.addSubnode(self.containerNode) self.containerNode.addSubnode(self.imageNode) self.containerNode.addSubnode(self.overlayNode) self.containerNode.addSubnode(self.titleNode) + + self.addSubnode(self.activateAreaNode) self.containerNode.activated = { [weak self] gesture, _ in guard let strongSelf = self, let item = strongSelf.item else { @@ -308,6 +314,15 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode { strongSelf.overlayNode.frame = CGRect(origin: CGPoint(x: 9.0, y: 13.0), size: CGSize(width: 100.0, height: 64.0)) strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 88.0), size: CGSize(width: itemLayout.contentSize.width, height: 16.0)) + + strongSelf.activateAreaNode.accessibilityLabel = item.title + if item.selected { + strongSelf.activateAreaNode.accessibilityTraits = [.button, .selected] + } else { + strongSelf.activateAreaNode.accessibilityTraits = [.button] + } + + strongSelf.activateAreaNode.frame = CGRect(origin: .zero, size: itemLayout.size) } }) } diff --git a/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListHeaderComponent.swift b/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListHeaderComponent.swift index c6a978ee6a..20a1310511 100644 --- a/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListHeaderComponent.swift +++ b/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListHeaderComponent.swift @@ -237,6 +237,9 @@ public final class ChatListHeaderComponent: Component { self.titleView.attributedText = NSAttributedString(string: title, font: Font.regular(17.0), textColor: theme.rootController.navigationBar.accentTextColor) let titleSize = self.titleView.updateLayout(CGSize(width: 100.0, height: 44.0)) + self.accessibilityLabel = title + self.accessibilityTraits = [.button] + if self.currentColor != theme.rootController.navigationBar.accentTextColor { self.currentColor = theme.rootController.navigationBar.accentTextColor self.arrowView.image = NavigationBarTheme.generateBackArrowImage(color: theme.rootController.navigationBar.accentTextColor) diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 1fccb8b2b0..559552e153 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -4304,36 +4304,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.chatTitleView = ChatTitleView(context: self.context, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, animationCache: controllerInteraction.presentationContext.animationCache, animationRenderer: controllerInteraction.presentationContext.animationRenderer) self.navigationItem.titleView = self.chatTitleView - self.chatTitleView?.pressed = { [weak self] in - if let strongSelf = self { - if strongSelf.chatLocation == .peer(id: strongSelf.context.account.peerId) { - if let peer = strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer, let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: true, requestsContext: nil) { - strongSelf.effectiveNavigationController?.pushViewController(infoController) - } - } else { - strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { - return $0.updatedTitlePanelContext { - if let index = $0.firstIndex(where: { - switch $0 { - case .chatInfo: - return true - default: - return false - } - }) { - var updatedContexts = $0 - updatedContexts.remove(at: index) - return updatedContexts - } else { - var updatedContexts = $0 - updatedContexts.append(.chatInfo) - return updatedContexts.sorted() - } - } - }) - } - } - } self.chatTitleView?.longPressed = { [weak self] in if let strongSelf = self, let peerView = strongSelf.peerView, let peer = peerView.peers[peerView.peerId], peer.restrictionText(platform: "ios", contentSettings: strongSelf.context.currentContentSettings.with { $0 }) == nil && !strongSelf.presentationInterfaceState.isNotAccessible { strongSelf.interfaceInteraction?.beginMessageSearch(.everything, "") @@ -8317,22 +8287,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: interactive, { current in - return current.updatedTitlePanelContext { - if let index = $0.firstIndex(where: { - switch $0 { - case .chatInfo: - return true - default: - return false - } - }) { - var updatedContexts = $0 - updatedContexts.remove(at: index) - return updatedContexts - } else { - return $0 - } - }.updatedSearch(current.search == nil ? ChatSearchData(domain: domain).withUpdatedQuery(query) : current.search?.withUpdatedDomain(domain).withUpdatedQuery(query)) + return current.updatedSearch(current.search == nil ? ChatSearchData(domain: domain).withUpdatedQuery(query) : current.search?.withUpdatedDomain(domain).withUpdatedQuery(query)) }) strongSelf.updateItemNodesSearchTextHighlightStates() }) @@ -9613,17 +9568,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G guard let strongSelf = self, case let .peer(peerId) = strongSelf.chatLocation else { return } - strongSelf.updateChatPresentationInterfaceState(interactive: true, { state in - return state.updatedTitlePanelContext({ - $0.filter({ item in - if case .chatInfo = item { - return false - } else { - return true - } - }) - }) - }) let _ = (strongSelf.context.engine.peers.updatePeersGroupIdInteractively(peerIds: [peerId], groupId: .root) |> deliverOnMainQueue).start() }, openLinkEditing: { [weak self] in @@ -11070,30 +11014,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return interfaceState }).start() } - - override public func viewDidDisappear(_ animated: Bool) { - super.viewDidDisappear(animated) - self.updateChatPresentationInterfaceState(animated: false, interactive: false, { - $0.updatedTitlePanelContext { - if let index = $0.firstIndex(where: { - switch $0 { - case .chatInfo: - return true - default: - return false - } - }) { - var updatedContexts = $0 - updatedContexts.remove(at: index) - return updatedContexts - } else { - return $0 - } - } - }) - } - override public func viewWillLeaveNavigation() { self.chatDisplayNode.willNavigateAway() } @@ -11719,7 +11640,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G private func navigationButtonAction(_ action: ChatNavigationButtonAction) { switch action { - case .spacer: + case .spacer, .toggleInfoPanel: break case .cancelMessageSelection: self.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState { $0.withoutSelectionState() } }) @@ -12204,27 +12125,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G case .feed: break } - case .toggleInfoPanel: - self.updateChatPresentationInterfaceState(animated: true, interactive: true, { - return $0.updatedTitlePanelContext { - if let index = $0.firstIndex(where: { - switch $0 { - case .chatInfo: - return true - default: - return false - } - }) { - var updatedContexts = $0 - updatedContexts.remove(at: index) - return updatedContexts - } else { - var updatedContexts = $0 - updatedContexts.append(.chatInfo) - return updatedContexts.sorted() - } - } - }) } } diff --git a/submodules/TelegramUI/Sources/ChatInfoTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatInfoTitlePanelNode.swift deleted file mode 100644 index 05b056aaee..0000000000 --- a/submodules/TelegramUI/Sources/ChatInfoTitlePanelNode.swift +++ /dev/null @@ -1,230 +0,0 @@ -import Foundation -import UIKit -import Display -import AsyncDisplayKit -import Postbox -import TelegramCore -import TelegramPresentationData -import ChatPresentationInterfaceState - -private enum ChatInfoTitleButton { - case search - case info - case mute - case unmute - case call - case report - case unarchive - - func title(_ strings: PresentationStrings) -> String { - switch self { - case .search: - return strings.Common_Search - case .info: - return strings.Conversation_Info - case .mute: - return strings.Conversation_TitleMute - case .unmute: - return strings.Conversation_TitleUnmute - case .call: - return strings.Conversation_Call - case .report: - return strings.ReportPeer_Report - case .unarchive: - return strings.ChatList_UnarchiveAction - } - } - - func icon(_ theme: PresentationTheme) -> UIImage? { - switch self { - case .search: - return PresentationResourcesChat.chatTitlePanelSearchImage(theme) - case .info: - return PresentationResourcesChat.chatTitlePanelInfoImage(theme) - case .mute: - return PresentationResourcesChat.chatTitlePanelMuteImage(theme) - case .unmute: - return PresentationResourcesChat.chatTitlePanelUnmuteImage(theme) - case .call: - return PresentationResourcesChat.chatTitlePanelCallImage(theme) - case .report: - return PresentationResourcesChat.chatTitlePanelReportImage(theme) - case .unarchive: - return PresentationResourcesChat.chatTitlePanelUnarchiveImage(theme) - } - } -} - -private func peerButtons(_ peer: Peer, interfaceState: ChatPresentationInterfaceState) -> [ChatInfoTitleButton] { - let muteAction: ChatInfoTitleButton - if interfaceState.peerIsMuted { - muteAction = .unmute - } else { - muteAction = .mute - } - - let infoButton: ChatInfoTitleButton - if interfaceState.isArchived { - infoButton = .unarchive - } else { - infoButton = .info - } - - if let peer = peer as? TelegramUser { - var buttons: [ChatInfoTitleButton] = [.search, muteAction] - if peer.botInfo == nil && interfaceState.callsAvailable { - buttons.append(.call) - } - - buttons.append(infoButton) - return buttons - } else if let _ = peer as? TelegramSecretChat { - var buttons: [ChatInfoTitleButton] = [.search, muteAction] - buttons.append(.call) - buttons.append(.info) - return buttons - } else if let channel = peer as? TelegramChannel { - if channel.flags.contains(.isCreator) || channel.addressName == nil { - return [.search, muteAction, infoButton] - } else { - return [.search, .report, muteAction, infoButton] - } - } else if let group = peer as? TelegramGroup { - if case .creator = group.role { - return [.search, muteAction, infoButton] - } else { - return [.search, muteAction, infoButton] - } - } else { - return [.search, muteAction, infoButton] - } -} - -private let buttonFont = Font.medium(10.0) - -private final class ChatInfoTitlePanelButtonNode: HighlightableButtonNode { - init() { - super.init() - - self.displaysAsynchronously = false - self.imageNode.displayWithoutProcessing = true - self.imageNode.displaysAsynchronously = false - - self.titleNode.displaysAsynchronously = false - - self.laysOutHorizontally = false - } - - func setup(text: String, color: UIColor, icon: UIImage?) { - self.setTitle(text, with: buttonFont, with: color, for: []) - self.setImage(icon, for: []) - if let icon = icon { - self.contentSpacing = max(0.0, 32.0 - icon.size.height) - } - } -} - -final class ChatInfoTitlePanelNode: ChatTitleAccessoryPanelNode { - private var theme: PresentationTheme? - - private let separatorNode: ASDisplayNode - private var buttons: [(ChatInfoTitleButton, ChatInfoTitlePanelButtonNode)] = [] - - override init() { - self.separatorNode = ASDisplayNode() - self.separatorNode.isLayerBacked = true - - super.init() - - self.addSubnode(self.separatorNode) - } - - override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState) -> LayoutResult { - let themeUpdated = self.theme !== interfaceState.theme - self.theme = interfaceState.theme - - let panelHeight: CGFloat = 55.0 - - if themeUpdated { - self.separatorNode.backgroundColor = interfaceState.theme.rootController.navigationBar.separatorColor - } - - let updatedButtons: [ChatInfoTitleButton] - switch interfaceState.chatLocation { - case .peer: - if let peer = interfaceState.renderedPeer?.peer { - updatedButtons = peerButtons(peer, interfaceState: interfaceState) - } else { - updatedButtons = [] - } - case .replyThread, .feed: - updatedButtons = [] - } - - var buttonsUpdated = false - if self.buttons.count != updatedButtons.count { - buttonsUpdated = true - } else { - for i in 0 ..< updatedButtons.count { - if self.buttons[i].0 != updatedButtons[i] { - buttonsUpdated = true - break - } - } - } - - if buttonsUpdated || themeUpdated { - for (_, buttonNode) in self.buttons { - buttonNode.removeFromSupernode() - } - self.buttons.removeAll() - for button in updatedButtons { - let buttonNode = ChatInfoTitlePanelButtonNode() - buttonNode.laysOutHorizontally = false - - buttonNode.setup(text: button.title(interfaceState.strings), color: interfaceState.theme.chat.inputPanel.panelControlAccentColor, icon: button.icon(interfaceState.theme)) - - buttonNode.addTarget(self, action: #selector(self.buttonPressed(_:)), forControlEvents: [.touchUpInside]) - self.addSubnode(buttonNode) - self.buttons.append((button, buttonNode)) - } - } - - if !self.buttons.isEmpty { - let buttonWidth = floor((width - leftInset - rightInset) / CGFloat(self.buttons.count)) - var nextButtonOrigin: CGFloat = leftInset - for (_, buttonNode) in self.buttons { - buttonNode.frame = CGRect(origin: CGPoint(x: nextButtonOrigin, y: 0.0), size: CGSize(width: buttonWidth, height: panelHeight)) - nextButtonOrigin += buttonWidth - } - } - - transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: UIScreenPixel))) - - return LayoutResult(backgroundHeight: panelHeight, insetHeight: panelHeight) - } - - @objc func buttonPressed(_ node: HighlightableButtonNode) { - for (button, buttonNode) in self.buttons { - if buttonNode === node { - switch button { - case .info: - self.interfaceInteraction?.openPeerInfo() - case .mute: - self.interfaceInteraction?.togglePeerNotifications() - case .unmute: - self.interfaceInteraction?.togglePeerNotifications() - case .search: - self.interfaceInteraction?.beginMessageSearch(.everything, "") - case .call: - self.interfaceInteraction?.beginCall(false) - case .report: - self.interfaceInteraction?.reportPeer() - case .unarchive: - self.interfaceInteraction?.unarchiveChat() - } - break - } - } - } -} diff --git a/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift b/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift index eb4c315302..91eff93325 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift @@ -42,7 +42,7 @@ func titlePanelForChatPresentationInterfaceState(_ chatPresentationInterfaceStat break loop } } - case .chatInfo, .requestInProgress, .toastAlert, .inviteRequests: + case .requestInProgress, .toastAlert, .inviteRequests: selectedContext = context break loop } @@ -120,14 +120,6 @@ func titlePanelForChatPresentationInterfaceState(_ chatPresentationInterfaceStat panel.interfaceInteraction = interfaceInteraction return panel } - case .chatInfo: - if let currentPanel = currentPanel as? ChatInfoTitlePanelNode { - return currentPanel - } else { - let panel = ChatInfoTitlePanelNode() - panel.interfaceInteraction = interfaceInteraction - return panel - } case .requestInProgress: if let currentPanel = currentPanel as? ChatRequestInProgressTitlePanelNode { return currentPanel diff --git a/submodules/TelegramUI/Sources/ChatInviteRequestsTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatInviteRequestsTitlePanelNode.swift index 10e6c6ffcd..c0baac19b2 100644 --- a/submodules/TelegramUI/Sources/ChatInviteRequestsTitlePanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatInviteRequestsTitlePanelNode.swift @@ -119,6 +119,8 @@ final class ChatInviteRequestsTitlePanelNode: ChatTitleAccessoryPanelNode { private var avatarsContent: AnimatedAvatarSetContext.Content? private let avatarsNode: AnimatedAvatarSetNode + private let activateAreaNode: AccessibilityAreaNode + private var theme: PresentationTheme? private var peerId: PeerId? @@ -138,6 +140,9 @@ final class ChatInviteRequestsTitlePanelNode: ChatTitleAccessoryPanelNode { self.avatarsContext = AnimatedAvatarSetContext() self.avatarsNode = AnimatedAvatarSetNode() + self.activateAreaNode = AccessibilityAreaNode() + self.activateAreaNode.accessibilityTraits = .button + super.init() self.addSubnode(self.separatorNode) @@ -146,6 +151,8 @@ final class ChatInviteRequestsTitlePanelNode: ChatTitleAccessoryPanelNode { self.addSubnode(self.closeButton) self.addSubnode(self.avatarsNode) + + self.addSubnode(self.activateAreaNode) } @@ -198,6 +205,9 @@ final class ChatInviteRequestsTitlePanelNode: ChatTitleAccessoryPanelNode { transition.updateFrame(node: self.avatarsNode, frame: CGRect(origin: CGPoint(x: leftInset + 8.0, y: floor((panelHeight - avatarsSize.height) / 2.0)), size: avatarsSize)) } + 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) } diff --git a/submodules/TelegramUI/Sources/ChatRequestInProgressTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatRequestInProgressTitlePanelNode.swift index 3c99188165..b716e487dc 100644 --- a/submodules/TelegramUI/Sources/ChatRequestInProgressTitlePanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatRequestInProgressTitlePanelNode.swift @@ -9,6 +9,8 @@ final class ChatRequestInProgressTitlePanelNode: ChatTitleAccessoryPanelNode { private let separatorNode: ASDisplayNode private let titleNode: ImmediateTextNode + private let activateAreaNode: AccessibilityAreaNode + private var theme: PresentationTheme? private var strings: PresentationStrings? @@ -19,10 +21,15 @@ final class ChatRequestInProgressTitlePanelNode: ChatTitleAccessoryPanelNode { self.titleNode = ImmediateTextNode() self.titleNode.maximumNumberOfLines = 1 + self.activateAreaNode = AccessibilityAreaNode() + self.activateAreaNode.accessibilityTraits = .staticText + super.init() self.addSubnode(self.titleNode) self.addSubnode(self.separatorNode) + + self.addSubnode(self.activateAreaNode) } override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState) -> LayoutResult { @@ -46,6 +53,9 @@ final class ChatRequestInProgressTitlePanelNode: ChatTitleAccessoryPanelNode { transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: UIScreenPixel))) + 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) } } diff --git a/submodules/TelegramUI/Sources/ChatToastAlertPanelNode.swift b/submodules/TelegramUI/Sources/ChatToastAlertPanelNode.swift index 8e42a432af..5020093f6c 100644 --- a/submodules/TelegramUI/Sources/ChatToastAlertPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatToastAlertPanelNode.swift @@ -8,6 +8,8 @@ final class ChatToastAlertPanelNode: ChatTitleAccessoryPanelNode { private let separatorNode: ASDisplayNode private let titleNode: ImmediateTextNode + private let activateAreaNode: AccessibilityAreaNode + private var textColor: UIColor = .black { didSet { if !self.textColor.isEqual(oldValue) { @@ -34,6 +36,9 @@ final class ChatToastAlertPanelNode: ChatTitleAccessoryPanelNode { self.titleNode.maximumNumberOfLines = 1 self.titleNode.insets = UIEdgeInsets(top: 2.0, left: 2.0, bottom: 2.0, right: 2.0) + self.activateAreaNode = AccessibilityAreaNode() + self.activateAreaNode.accessibilityTraits = [.staticText] + super.init() self.addSubnode(self.titleNode) @@ -51,6 +56,9 @@ final class ChatToastAlertPanelNode: ChatTitleAccessoryPanelNode { let titleSize = self.titleNode.updateLayout(CGSize(width: width - leftInset - rightInset - 20.0, height: 100.0)) self.titleNode.frame = CGRect(origin: CGPoint(x: floor((width - titleSize.width) / 2.0), y: floor((panelHeight - titleSize.height) / 2.0)), size: titleSize) + 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) } } diff --git a/submodules/TranslateUI/Sources/ChatTranslation.swift b/submodules/TranslateUI/Sources/ChatTranslation.swift index c43060353b..f586036edb 100644 --- a/submodules/TranslateUI/Sources/ChatTranslation.swift +++ b/submodules/TranslateUI/Sources/ChatTranslation.swift @@ -155,7 +155,12 @@ public func chatTranslationState(context: AccountContext, peerId: EnginePeer.Id) return .single(nil) } if #available(iOS 12.0, *) { - let baseLang = context.sharedContext.currentPresentationData.with { $0 }.strings.baseLanguageCode + var baseLang = context.sharedContext.currentPresentationData.with { $0 }.strings.baseLanguageCode + let rawSuffix = "-raw" + if baseLang.hasSuffix(rawSuffix) { + baseLang = String(baseLang.dropLast(rawSuffix.count)) + } + return context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.translationSettings]) |> mapToSignal { sharedData in let settings = sharedData.entries[ApplicationSpecificSharedDataKeys.translationSettings]?.get(TranslationSettings.self) ?? TranslationSettings.defaultSettings