diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index afa80d1900..5741361dc1 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -8919,3 +8919,12 @@ Sorry for the inconvenience."; "ImportStickerPack.ImportingEmojis" = "Importing Emojis"; "ImportStickerPack.CreateNewEmojiPack" = "Create a New Emoji Pack"; + +"VoiceOver.Chat.Sending" = "Sending"; +"VoiceOver.Chat.Failed" = "Failed to send"; + +"VoiceOver.Chat.PlayedByRecipient" = "Played by recipient"; +"VoiceOver.Chat.PlayedByRecipients" = "Played by recipients"; + +"VoiceOver.Chat.NotPlayedByRecipient" = "Not played by recipient"; +"VoiceOver.Chat.NotPlayedByRecipients" = "Not played by recipients"; diff --git a/submodules/AccountContext/Sources/PeerSelectionController.swift b/submodules/AccountContext/Sources/PeerSelectionController.swift index 6c5efa1bfc..3efb9d152a 100644 --- a/submodules/AccountContext/Sources/PeerSelectionController.swift +++ b/submodules/AccountContext/Sources/PeerSelectionController.swift @@ -43,6 +43,7 @@ public final class PeerSelectionControllerParams { public let filter: ChatListNodePeersFilter public let requestPeerType: ReplyMarkupButtonRequestPeerType? public let forumPeerId: EnginePeer.Id? + public let hasFilters: Bool public let hasChatListSelector: Bool public let hasContactSelector: Bool public let hasGlobalSearch: Bool @@ -55,12 +56,13 @@ public final class PeerSelectionControllerParams { public let hasTypeHeaders: Bool public let selectForumThreads: Bool - public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, filter: ChatListNodePeersFilter = [.onlyWriteable], requestPeerType: ReplyMarkupButtonRequestPeerType? = nil, forumPeerId: EnginePeer.Id? = nil, hasChatListSelector: Bool = true, hasContactSelector: Bool = true, hasGlobalSearch: Bool = true, title: String? = nil, attemptSelection: ((Peer, Int64?) -> Void)? = nil, createNewGroup: (() -> Void)? = nil, pretendPresentedInModal: Bool = false, multipleSelection: Bool = false, forwardedMessageIds: [EngineMessage.Id] = [], hasTypeHeaders: Bool = false, selectForumThreads: Bool = false) { + public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, filter: ChatListNodePeersFilter = [.onlyWriteable], requestPeerType: ReplyMarkupButtonRequestPeerType? = nil, forumPeerId: EnginePeer.Id? = nil, hasFilters: Bool = false, hasChatListSelector: Bool = true, hasContactSelector: Bool = true, hasGlobalSearch: Bool = true, title: String? = nil, attemptSelection: ((Peer, Int64?) -> Void)? = nil, createNewGroup: (() -> Void)? = nil, pretendPresentedInModal: Bool = false, multipleSelection: Bool = false, forwardedMessageIds: [EngineMessage.Id] = [], hasTypeHeaders: Bool = false, selectForumThreads: Bool = false) { self.context = context self.updatedPresentationData = updatedPresentationData self.filter = filter self.requestPeerType = requestPeerType self.forumPeerId = forumPeerId + self.hasFilters = hasFilters self.hasChatListSelector = hasChatListSelector self.hasContactSelector = hasContactSelector self.hasGlobalSearch = hasGlobalSearch diff --git a/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift b/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift index d80d2ad40b..be35d4fdfb 100644 --- a/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift +++ b/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift @@ -448,11 +448,11 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS public var focusUpdated: ((Bool) -> Void)? public var heightUpdated: ((Bool) -> Void)? - public func updateLayoutSize(_ size: CGSize, sideInset: CGFloat) -> CGFloat { + public func updateLayoutSize(_ size: CGSize, sideInset: CGFloat, animated: Bool) -> CGFloat { guard let presentationInterfaceState = self.presentationInterfaceState else { return 0.0 } - return self.updateLayout(width: size.width, leftInset: sideInset, rightInset: sideInset, bottomInset: 0.0, additionalSideInsets: UIEdgeInsets(), maxHeight: size.height, isSecondary: false, transition: .immediate, interfaceState: presentationInterfaceState, metrics: LayoutMetrics(widthClass: .compact, heightClass: .compact), isMediaInputExpanded: false) + return self.updateLayout(width: size.width, leftInset: sideInset, rightInset: sideInset, bottomInset: 0.0, additionalSideInsets: UIEdgeInsets(), maxHeight: size.height, isSecondary: false, transition: animated ? .animated(duration: 0.2, curve: .easeInOut) : .immediate, interfaceState: presentationInterfaceState, metrics: LayoutMetrics(widthClass: .compact, heightClass: .compact), isMediaInputExpanded: false) } public func setCaption(_ caption: NSAttributedString?) { diff --git a/submodules/ChatListUI/Sources/ChatListControllerNode.swift b/submodules/ChatListUI/Sources/ChatListControllerNode.swift index b9a3938c50..9253408bb4 100644 --- a/submodules/ChatListUI/Sources/ChatListControllerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListControllerNode.swift @@ -14,11 +14,11 @@ import ContextUI import AnimationCache import MultiAnimationRenderer -enum ChatListContainerNodeFilter: Equatable { +public enum ChatListContainerNodeFilter: Equatable { case all case filter(ChatListFilter) - var id: ChatListFilterTabEntryId { + public var id: ChatListFilterTabEntryId { switch self { case .all: return .all @@ -27,7 +27,7 @@ enum ChatListContainerNodeFilter: Equatable { } } - var filter: ChatListFilter? { + public var filter: ChatListFilter? { switch self { case .all: return nil @@ -333,7 +333,7 @@ private final class ChatListContainerItemNode: ASDisplayNode { private(set) var validLayout: (size: CGSize, insets: UIEdgeInsets, visualNavigationHeight: CGFloat, originalNavigationHeight: CGFloat, inlineNavigationLocation: ChatListControllerLocation?, inlineNavigationTransitionFraction: CGFloat)? - init(context: AccountContext, location: ChatListControllerLocation, filter: ChatListFilter?, previewing: Bool, isInlineMode: Bool, controlsHistoryPreload: Bool, presentationData: PresentationData, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, becameEmpty: @escaping (ChatListFilter?) -> Void, emptyAction: @escaping (ChatListFilter?) -> Void, secondaryEmptyAction: @escaping () -> Void) { + init(context: AccountContext, location: ChatListControllerLocation, filter: ChatListFilter?, chatListMode: ChatListNodeMode, previewing: Bool, isInlineMode: Bool, controlsHistoryPreload: Bool, presentationData: PresentationData, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, becameEmpty: @escaping (ChatListFilter?) -> Void, emptyAction: @escaping (ChatListFilter?) -> Void, secondaryEmptyAction: @escaping () -> Void) { self.context = context self.animationCache = animationCache self.animationRenderer = animationRenderer @@ -343,7 +343,7 @@ private final class ChatListContainerItemNode: ASDisplayNode { self.secondaryEmptyAction = secondaryEmptyAction self.isInlineMode = isInlineMode - self.listNode = ChatListNode(context: context, location: location, chatListFilter: filter, previewing: previewing, fillPreloadItems: controlsHistoryPreload, mode: .chatList, theme: presentationData.theme, fontSize: presentationData.listsFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameSortOrder: presentationData.nameSortOrder, nameDisplayOrder: presentationData.nameDisplayOrder, animationCache: animationCache, animationRenderer: animationRenderer, disableAnimations: true, isInlineMode: isInlineMode) + self.listNode = ChatListNode(context: context, location: location, chatListFilter: filter, previewing: previewing, fillPreloadItems: controlsHistoryPreload, mode: chatListMode, theme: presentationData.theme, fontSize: presentationData.listsFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameSortOrder: presentationData.nameSortOrder, nameDisplayOrder: presentationData.nameDisplayOrder, animationCache: animationCache, animationRenderer: animationRenderer, disableAnimations: true, isInlineMode: isInlineMode) super.init() @@ -492,9 +492,10 @@ private final class ChatListContainerItemNode: ASDisplayNode { } } -final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate { +public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate { private let context: AccountContext let location: ChatListControllerLocation + private let chatListMode: ChatListNodeMode private let previewing: Bool private let isInlineMode: Bool private let controlsHistoryPreload: Bool @@ -515,7 +516,7 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate { private var filtersLimit: Int32? = nil private var selectedId: ChatListFilterTabEntryId - private(set) var transitionFraction: CGFloat = 0.0 + public private(set) var transitionFraction: CGFloat = 0.0 private var transitionFractionOffset: CGFloat = 0.0 private var disableItemNodeOperationsWhileAnimating: Bool = false private var validLayout: (layout: ContainerViewLayout, navigationBarHeight: CGFloat, visualNavigationHeight: CGFloat, originalNavigationHeight: CGFloat, cleanNavigationBarHeight: CGFloat, insets: UIEdgeInsets, isReorderingFilters: Bool, isEditing: Bool, inlineNavigationLocation: ChatListControllerLocation?, inlineNavigationTransitionFraction: CGFloat)? @@ -527,7 +528,7 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate { let leftSeparatorLayer: SimpleLayer private let _ready = Promise() - var ready: Signal { + public var ready: Signal { return _ready.get() } @@ -537,7 +538,7 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate { } private var currentItemNodeValue: ChatListContainerItemNode? - var currentItemNode: ChatListNode { + public var currentItemNode: ChatListNode { return self.currentItemNodeValue!.listNode } @@ -546,8 +547,8 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate { return self.currentItemStateValue.get() } - var currentItemFilterUpdated: ((ChatListFilterTabEntryId, CGFloat, ContainedViewLayoutTransition, Bool) -> Void)? - var currentItemFilter: ChatListFilterTabEntryId { + public var currentItemFilterUpdated: ((ChatListFilterTabEntryId, CGFloat, ContainedViewLayoutTransition, Bool) -> Void)? + public var currentItemFilter: ChatListFilterTabEntryId { return self.currentItemNode.chatListFilter.flatMap { .filter($0.id) } ?? .all } @@ -662,7 +663,7 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate { } } - var activateSearch: (() -> Void)? + public var activateSearch: (() -> Void)? var presentAlert: ((String) -> Void)? var present: ((ViewController) -> Void)? var push: ((ViewController) -> Void)? @@ -673,19 +674,20 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate { var setPeerThreadStopped: ((EnginePeer.Id, Int64, Bool) -> Void)? var setPeerThreadPinned: ((EnginePeer.Id, Int64, Bool) -> Void)? var setPeerThreadHidden: ((EnginePeer.Id, Int64, Bool) -> Void)? - var peerSelected: ((EnginePeer, Int64?, Bool, Bool, ChatListNodeEntryPromoInfo?) -> Void)? + public var peerSelected: ((EnginePeer, Int64?, Bool, Bool, ChatListNodeEntryPromoInfo?) -> Void)? var groupSelected: ((EngineChatList.Group) -> Void)? var updatePeerGrouping: ((EnginePeer.Id, Bool) -> Void)? - fileprivate var contentOffsetChanged: ((ListViewVisibleContentOffset) -> Void)? - fileprivate var contentScrollingEnded: ((ListView) -> Bool)? + public var contentOffsetChanged: ((ListViewVisibleContentOffset) -> Void)? + public var contentScrollingEnded: ((ListView) -> Bool)? var activateChatPreview: ((ChatListItem, Int64?, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)? var addedVisibleChatsWithPeerIds: (([EnginePeer.Id]) -> Void)? var didBeginSelectingChats: (() -> Void)? var displayFilterLimit: (() -> Void)? - init(context: AccountContext, location: ChatListControllerLocation, previewing: Bool, controlsHistoryPreload: Bool, isInlineMode: Bool, presentationData: PresentationData, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, filterBecameEmpty: @escaping (ChatListFilter?) -> Void, filterEmptyAction: @escaping (ChatListFilter?) -> Void, secondaryEmptyAction: @escaping () -> Void) { + public init(context: AccountContext, location: ChatListControllerLocation, chatListMode: ChatListNodeMode = .chatList, previewing: Bool, controlsHistoryPreload: Bool, isInlineMode: Bool, presentationData: PresentationData, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, filterBecameEmpty: @escaping (ChatListFilter?) -> Void, filterEmptyAction: @escaping (ChatListFilter?) -> Void, secondaryEmptyAction: @escaping () -> Void) { self.context = context self.location = location + self.chatListMode = chatListMode self.previewing = previewing self.isInlineMode = isInlineMode self.filterBecameEmpty = filterBecameEmpty @@ -707,7 +709,7 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate { self.backgroundColor = presentationData.theme.chatList.backgroundColor - let itemNode = ChatListContainerItemNode(context: self.context, location: self.location, filter: nil, previewing: self.previewing, isInlineMode: self.isInlineMode, controlsHistoryPreload: self.controlsHistoryPreload, presentationData: presentationData, animationCache: self.animationCache, animationRenderer: self.animationRenderer, becameEmpty: { [weak self] filter in + let itemNode = ChatListContainerItemNode(context: self.context, location: self.location, filter: nil, chatListMode: chatListMode, previewing: self.previewing, isInlineMode: self.isInlineMode, controlsHistoryPreload: self.controlsHistoryPreload, presentationData: presentationData, animationCache: self.animationCache, animationRenderer: self.animationRenderer, becameEmpty: { [weak self] filter in self?.filterBecameEmpty(filter) }, emptyAction: { [weak self] filter in self?.filterEmptyAction(filter) @@ -752,11 +754,11 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate { self.pendingItemNode?.2.dispose() } - func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { + public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { return false } - func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool { + public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool { if let _ = otherGestureRecognizer as? InteractiveTransitionGestureRecognizer { return false } @@ -892,7 +894,7 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate { self.currentItemNode.fixContentOffset(offset: offset) } - func updatePresentationData(_ presentationData: PresentationData) { + public func updatePresentationData(_ presentationData: PresentationData) { self.presentationData = presentationData if let validLayout = self.validLayout { @@ -920,7 +922,7 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate { } } - func scrollToTop() { + public func scrollToTop() { if let itemNode = self.itemNodes[self.selectedId] { itemNode.listNode.scrollToPosition(.top) } @@ -951,7 +953,7 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate { } } - func updateAvailableFilters(_ availableFilters: [ChatListContainerNodeFilter], limit: Int32?) { + public func updateAvailableFilters(_ availableFilters: [ChatListContainerNodeFilter], limit: Int32?) { if self.availableFilters != availableFilters { let apply: () -> Void = { [weak self] in guard let strongSelf = self else { @@ -973,7 +975,7 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate { } } - func updateEnableAdjacentFilterLoading(_ value: Bool) { + public func updateEnableAdjacentFilterLoading(_ value: Bool) { if value != self.enableAdjacentFilterLoading { self.enableAdjacentFilterLoading = value @@ -983,7 +985,7 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate { } } - func switchToFilter(id: ChatListFilterTabEntryId, animated: Bool = true, completion: (() -> Void)? = nil) { + public func switchToFilter(id: ChatListFilterTabEntryId, animated: Bool = true, completion: (() -> Void)? = nil) { self.onFilterSwitch?() if id != self.selectedId, let index = self.availableFilters.firstIndex(where: { $0.id == id }) { if let itemNode = self.itemNodes[id] { @@ -1001,7 +1003,7 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate { itemNode.emptyNode?.restartAnimation() completion?() } else if self.pendingItemNode == nil { - let itemNode = ChatListContainerItemNode(context: self.context, location: self.location, filter: self.availableFilters[index].filter, previewing: self.previewing, isInlineMode: self.isInlineMode, controlsHistoryPreload: self.controlsHistoryPreload, presentationData: self.presentationData, animationCache: self.animationCache, animationRenderer: self.animationRenderer, becameEmpty: { [weak self] filter in + let itemNode = ChatListContainerItemNode(context: self.context, location: self.location, filter: self.availableFilters[index].filter, chatListMode: self.chatListMode, previewing: self.previewing, isInlineMode: self.isInlineMode, controlsHistoryPreload: self.controlsHistoryPreload, presentationData: self.presentationData, animationCache: self.animationCache, animationRenderer: self.animationRenderer, becameEmpty: { [weak self] filter in self?.filterBecameEmpty(filter) }, emptyAction: { [weak self] filter in self?.filterEmptyAction(filter) @@ -1094,7 +1096,7 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate { } } - func update(layout: ContainerViewLayout, navigationBarHeight: CGFloat, visualNavigationHeight: CGFloat, originalNavigationHeight: CGFloat, cleanNavigationBarHeight: CGFloat, insets: UIEdgeInsets, isReorderingFilters: Bool, isEditing: Bool, inlineNavigationLocation: ChatListControllerLocation?, inlineNavigationTransitionFraction: CGFloat, transition: ContainedViewLayoutTransition) { + public func update(layout: ContainerViewLayout, navigationBarHeight: CGFloat, visualNavigationHeight: CGFloat, originalNavigationHeight: CGFloat, cleanNavigationBarHeight: CGFloat, insets: UIEdgeInsets, isReorderingFilters: Bool, isEditing: Bool, inlineNavigationLocation: ChatListControllerLocation?, inlineNavigationTransitionFraction: CGFloat, transition: ContainedViewLayoutTransition) { self.validLayout = (layout, navigationBarHeight, visualNavigationHeight, originalNavigationHeight, cleanNavigationBarHeight, insets, isReorderingFilters, isEditing, inlineNavigationLocation, inlineNavigationTransitionFraction) self._validLayoutReady.set(.single(true)) @@ -1119,7 +1121,7 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate { validNodeIds.append(id) if self.itemNodes[id] == nil && self.enableAdjacentFilterLoading && !self.disableItemNodeOperationsWhileAnimating { - let itemNode = ChatListContainerItemNode(context: self.context, location: self.location, filter: self.availableFilters[i].filter, previewing: self.previewing, isInlineMode: self.isInlineMode, controlsHistoryPreload: self.controlsHistoryPreload, presentationData: self.presentationData, animationCache: self.animationCache, animationRenderer: self.animationRenderer, becameEmpty: { [weak self] filter in + let itemNode = ChatListContainerItemNode(context: self.context, location: self.location, filter: self.availableFilters[i].filter, chatListMode: self.chatListMode, previewing: self.previewing, isInlineMode: self.isInlineMode, controlsHistoryPreload: self.controlsHistoryPreload, presentationData: self.presentationData, animationCache: self.animationCache, animationRenderer: self.animationRenderer, becameEmpty: { [weak self] filter in self?.filterBecameEmpty(filter) }, emptyAction: { [weak self] filter in self?.filterEmptyAction(filter) diff --git a/submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift b/submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift index 66ad27a098..8918c05f3d 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift @@ -419,21 +419,26 @@ private final class ItemNode: ASDisplayNode { } } -enum ChatListFilterTabEntryId: Hashable { +public enum ChatListFilterTabEntryId: Hashable { case all case filter(Int32) } -struct ChatListFilterTabEntryUnreadCount: Equatable { +public struct ChatListFilterTabEntryUnreadCount: Equatable { let value: Int let hasUnmuted: Bool + + public init(value: Int, hasUnmuted: Bool) { + self.value = value + self.hasUnmuted = hasUnmuted + } } -enum ChatListFilterTabEntry: Equatable { +public enum ChatListFilterTabEntry: Equatable { case all(unreadCount: Int) case filter(id: Int32, text: String, unread: ChatListFilterTabEntryUnreadCount) - var id: ChatListFilterTabEntryId { + public var id: ChatListFilterTabEntryId { switch self { case .all: return .all @@ -461,12 +466,12 @@ enum ChatListFilterTabEntry: Equatable { } } -final class ChatListFilterTabContainerNode: ASDisplayNode { +public final class ChatListFilterTabContainerNode: ASDisplayNode { private let scrollNode: ASScrollNode private let selectedLineNode: ASImageNode private var itemNodes: [ChatListFilterTabEntryId: ItemNode] = [:] - var tabSelected: ((ChatListFilterTabEntryId, Bool) -> Void)? + public var tabSelected: ((ChatListFilterTabEntryId, Bool) -> Void)? var tabRequestedDeletion: ((ChatListFilterTabEntryId) -> Void)? var addFilter: (() -> Void)? var contextGesture: ((Int32?, ContextExtractedContentContainingNode, ContextGesture, Bool) -> Void)? @@ -510,7 +515,7 @@ final class ChatListFilterTabContainerNode: ASDisplayNode { } } - override init() { + public override init() { self.scrollNode = ASScrollNode() self.selectedLineNode = ASImageNode() @@ -656,12 +661,12 @@ final class ChatListFilterTabContainerNode: ASDisplayNode { private var previousSelectedAbsFrame: CGRect? private var previousSelectedFrame: CGRect? - func cancelAnimations() { + public func cancelAnimations() { self.selectedLineNode.layer.removeAllAnimations() self.scrollNode.layer.removeAllAnimations() } - func update(size: CGSize, sideInset: CGFloat, filters: [ChatListFilterTabEntry], selectedFilter: ChatListFilterTabEntryId?, isReordering: Bool, isEditing: Bool, canReorderAllChats: Bool, filtersLimit: Int32?, transitionFraction: CGFloat, presentationData: PresentationData, transition proposedTransition: ContainedViewLayoutTransition) { + public func update(size: CGSize, sideInset: CGFloat, filters: [ChatListFilterTabEntry], selectedFilter: ChatListFilterTabEntryId?, isReordering: Bool, isEditing: Bool, canReorderAllChats: Bool, filtersLimit: Int32?, transitionFraction: CGFloat, presentationData: PresentationData, transition proposedTransition: ContainedViewLayoutTransition) { let isFirstTime = self.currentParams == nil let transition: ContainedViewLayoutTransition = isFirstTime ? .immediate : proposedTransition diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index f21543054f..ebbb4515fe 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -2658,7 +2658,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { let panelFrame = CGRect(origin: CGPoint(x: 0.0, y: topPanelHeight - panelHeight), size: CGSize(width: size.width, height: panelHeight)) if let (mediaAccessoryPanel, mediaType) = self.mediaAccessoryPanel, mediaType == type { transition.updateFrame(layer: mediaAccessoryPanel.layer, frame: panelFrame) - mediaAccessoryPanel.updateLayout(size: panelFrame.size, leftInset: sideInset, rightInset: sideInset, transition: transition) + mediaAccessoryPanel.updateLayout(size: panelFrame.size, leftInset: sideInset, rightInset: sideInset, isHidden: false, transition: transition) switch order { case .regular: mediaAccessoryPanel.containerNode.headerNode.playbackItems = (item, previousItem, nextItem) @@ -2873,7 +2873,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { self.mediaAccessoryPanelContainer.addSubnode(mediaAccessoryPanel) } self.mediaAccessoryPanel = (mediaAccessoryPanel, type) - mediaAccessoryPanel.updateLayout(size: panelFrame.size, leftInset: sideInset, rightInset: sideInset, transition: .immediate) + mediaAccessoryPanel.updateLayout(size: panelFrame.size, leftInset: sideInset, rightInset: sideInset, isHidden: false, transition: .immediate) switch order { case .regular: mediaAccessoryPanel.containerNode.headerNode.playbackItems = (item, previousItem, nextItem) diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift index d5c665113e..64c764c79a 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNode.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNode.swift @@ -998,7 +998,7 @@ public final class ChatListNode: ListView { } private var currentLocation: ChatListNodeLocation? - private(set) var chatListFilter: ChatListFilter? { + public private(set) var chatListFilter: ChatListFilter? { didSet { self.chatListFilterValue.set(.single(self.chatListFilter)) @@ -1409,7 +1409,7 @@ public final class ChatListNode: ListView { let currentRemovingItemId = self.currentRemovingItemId let savedMessagesPeer: Signal - if case let .peers(filter, _, _, _, _) = mode, filter.contains(.onlyWriteable), case .chatList = location { + if case let .peers(filter, _, _, _, _) = mode, filter.contains(.onlyWriteable), case .chatList = location, self.chatListFilter == nil { savedMessagesPeer = context.account.postbox.loadedPeerWithId(context.account.peerId) |> map(Optional.init) |> map { peer in diff --git a/submodules/ChatListUI/Sources/TabBarChatListFilterController.swift b/submodules/ChatListUI/Sources/TabBarChatListFilterController.swift index a522e50c3d..cdc335ae8e 100644 --- a/submodules/ChatListUI/Sources/TabBarChatListFilterController.swift +++ b/submodules/ChatListUI/Sources/TabBarChatListFilterController.swift @@ -9,7 +9,7 @@ import Postbox import TelegramUIPreferences import TelegramCore -func chatListFilterItems(context: AccountContext) -> Signal<(Int, [(ChatListFilter, Int, Bool)]), NoError> { +public func chatListFilterItems(context: AccountContext) -> Signal<(Int, [(ChatListFilter, Int, Bool)]), NoError> { return context.engine.peers.updatedChatListFilters() |> distinctUntilChanged |> mapToSignal { filters -> Signal<(Int, [(ChatListFilter, Int, Bool)]), NoError> in diff --git a/submodules/Display/Source/Navigation/NavigationController.swift b/submodules/Display/Source/Navigation/NavigationController.swift index 8bb2c46f06..a30c4304a9 100644 --- a/submodules/Display/Source/Navigation/NavigationController.swift +++ b/submodules/Display/Source/Navigation/NavigationController.swift @@ -361,7 +361,7 @@ open class NavigationController: UINavigationController, ContainableController, } } - private func updateContainers(layout rawLayout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { + private func updateContainers(layout rawLayout: ContainerViewLayout, transition: ContainedViewLayoutTransition, completion: @escaping () -> Void = {}) { self.isUpdatingContainers = true var layout = rawLayout @@ -1447,7 +1447,9 @@ open class NavigationController: UINavigationController, ContainableController, return controller } if let layout = self.validLayout { - self.updateContainers(layout: layout, transition: animated ? .animated(duration: 0.5, curve: .spring) : .immediate) + self.updateContainers(layout: layout, transition: animated ? .animated(duration: 0.5, curve: .spring) : .immediate, completion: { [weak self] in + self?.notifyAccessibilityScreenChanged() + }) } self._viewControllersPromise.set(self.viewControllers) } @@ -1705,4 +1707,8 @@ open class NavigationController: UINavigationController, ContainableController, } return hidden } + + private func notifyAccessibilityScreenChanged() { + UIAccessibility.post(notification: UIAccessibility.Notification.screenChanged, argument: nil) + } } diff --git a/submodules/Display/Source/TextAlertController.swift b/submodules/Display/Source/TextAlertController.swift index 471c993101..28b339166e 100644 --- a/submodules/Display/Source/TextAlertController.swift +++ b/submodules/Display/Source/TextAlertController.swift @@ -120,6 +120,8 @@ public final class TextAlertContentActionNode: HighlightableButtonNode { break } self.setAttributedTitle(NSAttributedString(string: self.action.title, font: font, textColor: color, paragraphAlignment: .center), for: []) + self.accessibilityLabel = self.action.title + self.accessibilityTraits = [.button] } @objc func pressed() { @@ -193,6 +195,7 @@ public final class TextAlertContentNode: AlertContentNode { titleNode.maximumNumberOfLines = 4 titleNode.truncationType = .end titleNode.isAccessibilityElement = true + titleNode.accessibilityLabel = title.string self.titleNode = titleNode } else { self.titleNode = nil diff --git a/submodules/Display/Source/WindowContent.swift b/submodules/Display/Source/WindowContent.swift index f2adb8f223..772fa8ac45 100644 --- a/submodules/Display/Source/WindowContent.swift +++ b/submodules/Display/Source/WindowContent.swift @@ -507,11 +507,19 @@ public class Window1 { } } - - var popoverDelta: CGFloat = 0.0 + var minKeyboardY: CGFloat? + if #available(iOSApplicationExtension 16.1, iOS 16.1, *), let screen = notification.object as? UIScreen, let keyboardFrameEnd = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect { + let fromCoordinateSpace = screen.coordinateSpace + let toCoordinateSpace: UICoordinateSpace = strongSelf.hostView.eventView + + let convertedKeyboardFrameEnd = fromCoordinateSpace.convert(keyboardFrameEnd, to: toCoordinateSpace) + minKeyboardY = convertedKeyboardFrameEnd.minY + } + + var windowedHeightDifference: CGFloat = 0.0 let screenHeight: CGFloat - var inPopover = false + var isWindowed = false if keyboardFrame.width.isEqual(to: UIScreen.main.bounds.width) { let screenSize = UIScreen.main.bounds.size var portraitScreenSize = UIScreen.main.bounds.size @@ -525,42 +533,53 @@ public class Window1 { if strongSelf.windowLayout.size.height != screenSize.height { let heightDelta = screenSize.height - strongSelf.windowLayout.size.height - - let heightDeltaValid = heightDelta > 0.0 && heightDelta < 100.0 - - if heightDeltaValid { - inPopover = true - popoverDelta = heightDelta / 2.0 - } + //if heightDelta > 0.0 && heightDelta < 200.0 { + isWindowed = true + windowedHeightDifference = heightDelta / 2.0 + //} } if #available(iOSApplicationExtension 13.0, iOS 13.0, *) { - screenHeight = UIScreen.main.bounds.height + if isWindowed, let _ = minKeyboardY { + screenHeight = strongSelf.windowLayout.size.height + } else { + screenHeight = UIScreen.main.bounds.height + } } else { screenHeight = strongSelf.windowLayout.size.height } } else { - if keyboardFrame.minX > 0.0 { - screenHeight = UIScreen.main.bounds.height + if let _ = minKeyboardY { + screenHeight = strongSelf.windowLayout.size.height } else { - screenHeight = UIScreen.main.bounds.width + if keyboardFrame.minX > 0.0 { + screenHeight = UIScreen.main.bounds.height + } else { + screenHeight = UIScreen.main.bounds.width + } } } var keyboardHeight: CGFloat if keyboardFrame.isEmpty || keyboardFrame.maxY < screenHeight { - if inPopover || (isTablet && screenHeight - keyboardFrame.maxY < 5.0) { + if isWindowed || (isTablet && screenHeight - keyboardFrame.maxY < 5.0) { + if let minKeyboardY { + keyboardFrame.origin.y = minKeyboardY + } keyboardHeight = max(0.0, screenHeight - keyboardFrame.minY) - if inPopover && !keyboardHeight.isZero { - keyboardHeight = max(0.0, keyboardHeight - popoverDelta) + if isWindowed && !keyboardHeight.isZero, minKeyboardY == nil { + keyboardHeight = max(0.0, keyboardHeight - windowedHeightDifference) } } else { keyboardHeight = 0.0 } } else { + if let minKeyboardY { + keyboardFrame.origin.y = minKeyboardY + } keyboardHeight = max(0.0, screenHeight - keyboardFrame.minY) - if inPopover && !keyboardHeight.isZero { - keyboardHeight = max(0.0, keyboardHeight - popoverDelta) + if isWindowed && !keyboardHeight.isZero, minKeyboardY == nil { + keyboardHeight = max(0.0, keyboardHeight - windowedHeightDifference) } } diff --git a/submodules/ItemListUI/Sources/Items/ItemListMultilineInputItem.swift b/submodules/ItemListUI/Sources/Items/ItemListMultilineInputItem.swift index 138f9d20fd..c83ebd5a1c 100644 --- a/submodules/ItemListUI/Sources/Items/ItemListMultilineInputItem.swift +++ b/submodules/ItemListUI/Sources/Items/ItemListMultilineInputItem.swift @@ -148,6 +148,7 @@ public class ItemListMultilineInputItemNode: ListViewItemNode, ASEditableTextNod self.measureTextNode = TextNode() self.limitTextNode = TextNode() + self.limitTextNode.displaysAsynchronously = false super.init(layerBacked: false, dynamicBounce: false) diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoPaintStickersContext.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoPaintStickersContext.h index 61bfa2e36d..6b0d0586e7 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoPaintStickersContext.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoPaintStickersContext.h @@ -30,7 +30,7 @@ @property (nonatomic, copy) void(^ _Nullable focusUpdated)(BOOL focused); @property (nonatomic, copy) void(^ _Nullable heightUpdated)(BOOL animated); -- (CGFloat)updateLayoutSize:(CGSize)size sideInset:(CGFloat)sideInset; +- (CGFloat)updateLayoutSize:(CGSize)size sideInset:(CGFloat)sideInset animated:(bool)animated; - (CGFloat)baseHeight; @end diff --git a/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_Blit.fsh b/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_Blit.fsh deleted file mode 100755 index a3b4c6d24c..0000000000 --- a/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_Blit.fsh +++ /dev/null @@ -1,11 +0,0 @@ -precision highp float; - -varying vec2 varTexcoord; - -uniform sampler2D texture; - -void main (void) -{ - gl_FragColor = texture2D(texture, varTexcoord.st, 0.0); - gl_FragColor.rgb *= gl_FragColor.a; -} diff --git a/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_Blit.vsh b/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_Blit.vsh deleted file mode 100755 index bb6535aa6a..0000000000 --- a/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_Blit.vsh +++ /dev/null @@ -1,13 +0,0 @@ -precision highp float; - -uniform mat4 mvpMatrix; - -attribute vec4 inPosition; -attribute vec2 inTexcoord; -varying vec2 varTexcoord; - -void main (void) -{ - gl_Position = mvpMatrix * inPosition; - varTexcoord = inTexcoord; -} diff --git a/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_BlitWithEraseMask.fsh b/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_BlitWithEraseMask.fsh deleted file mode 100755 index 8af5d2bcb9..0000000000 --- a/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_BlitWithEraseMask.fsh +++ /dev/null @@ -1,17 +0,0 @@ -precision highp float; - -varying vec2 varTexcoord; - -uniform sampler2D texture; -uniform sampler2D mask; - -void main (void) -{ - vec4 dst = texture2D(texture, varTexcoord.st, 0.0); - float srcAlpha = 1.0 - texture2D(mask, varTexcoord.st, 0.0).a; - - float outAlpha = dst.a * srcAlpha; - - gl_FragColor.rgb = dst.rgb * outAlpha; - gl_FragColor.a = outAlpha; -} diff --git a/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_BlitWithMask.fsh b/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_BlitWithMask.fsh deleted file mode 100755 index 2db02d9a85..0000000000 --- a/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_BlitWithMask.fsh +++ /dev/null @@ -1,20 +0,0 @@ -precision highp float; - -varying vec2 varTexcoord; - -uniform sampler2D texture; -uniform sampler2D mask; -uniform vec4 color; - -void main (void) -{ - vec4 dst = texture2D(texture, varTexcoord.st, 0.0); - float srcAlpha = color.a * texture2D(mask, varTexcoord.st, 0.0).a; - - float outAlpha = srcAlpha + dst.a * (1.0 - srcAlpha); - - gl_FragColor.rgb = (color.rgb * srcAlpha + dst.rgb * dst.a * (1.0 - srcAlpha)) / outAlpha; - gl_FragColor.a = outAlpha; - - gl_FragColor.rgb *= gl_FragColor.a; -} \ No newline at end of file diff --git a/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_BlitWithMaskLight.fsh b/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_BlitWithMaskLight.fsh deleted file mode 100644 index d44696d48b..0000000000 --- a/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_BlitWithMaskLight.fsh +++ /dev/null @@ -1,26 +0,0 @@ -precision highp float; - -varying vec2 varTexcoord; - -uniform sampler2D texture; -uniform sampler2D mask; -uniform vec4 color; - -void main (void) -{ - vec4 dst = texture2D(texture, varTexcoord.st, 0.0); - vec3 maskColor = texture2D(mask, varTexcoord.st, 0.0).rgb; - - float srcAlpha = clamp(0.78 * maskColor.r + maskColor.b + maskColor.g, 0.0, 1.0); - - vec3 borderColor = mix(color.rgb, vec3(1.0, 1.0, 1.0), 0.86); - vec3 finalColor = mix(color.rgb, borderColor, maskColor.g); - finalColor = mix(finalColor.rgb, vec3(1.0, 1.0, 1.0), maskColor.b); - - float outAlpha = srcAlpha + dst.a * (1.0 - srcAlpha); - - gl_FragColor.rgb = (finalColor * srcAlpha + dst.rgb * dst.a * (1.0 - srcAlpha)) / outAlpha; - gl_FragColor.a = outAlpha; - - gl_FragColor.rgb *= gl_FragColor.a; -} diff --git a/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_Brush.fsh b/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_Brush.fsh deleted file mode 100755 index d6effd7b91..0000000000 --- a/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_Brush.fsh +++ /dev/null @@ -1,14 +0,0 @@ -precision highp float; - -varying vec2 varTexcoord; -varying float varIntensity; - -uniform sampler2D texture; - -void main (void) -{ - float f = texture2D(texture, varTexcoord.st, 0.0).a; - float v = varIntensity * f; - - gl_FragColor = vec4(0, 0, 0, v); -} diff --git a/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_Brush.vsh b/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_Brush.vsh deleted file mode 100755 index b9b6fe4fb1..0000000000 --- a/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_Brush.vsh +++ /dev/null @@ -1,16 +0,0 @@ -precision highp float; - -uniform mat4 mvpMatrix; - -attribute vec4 inPosition; -attribute vec2 inTexcoord; -attribute float alpha; -varying vec2 varTexcoord; -varying float varIntensity; - -void main (void) -{ - gl_Position = mvpMatrix * inPosition; - varTexcoord = inTexcoord; - varIntensity = alpha; -} diff --git a/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_BrushLight.fsh b/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_BrushLight.fsh deleted file mode 100644 index 9a94a278d0..0000000000 --- a/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_BrushLight.fsh +++ /dev/null @@ -1,12 +0,0 @@ -precision highp float; - -varying vec2 varTexcoord; -varying float varIntensity; - -uniform sampler2D texture; - -void main (void) -{ - vec4 f = texture2D(texture, varTexcoord.st, 0.0); - gl_FragColor = vec4(f.r * varIntensity, f.g, f.b, 0.0); -} diff --git a/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_BrushLightPreview.fsh b/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_BrushLightPreview.fsh deleted file mode 100644 index 506a4f975b..0000000000 --- a/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_BrushLightPreview.fsh +++ /dev/null @@ -1,21 +0,0 @@ -precision highp float; - -varying vec2 varTexcoord; - -uniform sampler2D mask; -uniform vec4 color; - -void main (void) -{ - vec3 maskColor = texture2D(mask, varTexcoord.st, 0.0).rgb; - float srcAlpha = color.a * clamp(maskColor.r + maskColor.b, 0.0, 1.0); - - vec3 borderColor = vec3(1.0, 1.0, 1.0); - //vec3 finalColor = mix(color.rgb, borderColor, maskColor.g); - vec3 finalColor = mix(color.rgb, vec3(1.0, 1.0, 1.0), maskColor.b); - - gl_FragColor.rgb = (finalColor * srcAlpha) / srcAlpha; - gl_FragColor.a = srcAlpha; - - gl_FragColor.rgb *= gl_FragColor.a; -} diff --git a/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_CompositeWithEraseMask.fsh b/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_CompositeWithEraseMask.fsh deleted file mode 100755 index 1d2e7f189b..0000000000 --- a/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_CompositeWithEraseMask.fsh +++ /dev/null @@ -1,13 +0,0 @@ -precision highp float; - -varying vec2 varTexcoord; - -uniform sampler2D texture; -uniform sampler2D mask; - -void main (void) -{ - gl_FragColor = texture2D(texture, varTexcoord.st, 0.0); - float srcAlpha = 1.0 - texture2D(mask, varTexcoord.st, 0.0).a; - gl_FragColor.a *= srcAlpha; -} diff --git a/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_CompositeWithMask.fsh b/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_CompositeWithMask.fsh deleted file mode 100755 index 3f0b4d26ea..0000000000 --- a/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_CompositeWithMask.fsh +++ /dev/null @@ -1,18 +0,0 @@ -precision highp float; - -varying vec2 varTexcoord; - -uniform sampler2D texture; -uniform sampler2D mask; -uniform vec4 color; - -void main (void) -{ - vec4 dst = texture2D(texture, varTexcoord.st, 0.0); - float srcAlpha = color.a * texture2D(mask, varTexcoord.st, 0.0).a; - - float outAlpha = srcAlpha + dst.a * (1.0 - srcAlpha); - - gl_FragColor.rgb = (color.rgb * srcAlpha + dst.rgb * dst.a * (1.0 - srcAlpha)) / outAlpha; - gl_FragColor.a = outAlpha; -} diff --git a/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_CompositeWithMaskLight.fsh b/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_CompositeWithMaskLight.fsh deleted file mode 100644 index aeee1dcf09..0000000000 --- a/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_CompositeWithMaskLight.fsh +++ /dev/null @@ -1,24 +0,0 @@ -precision highp float; - -varying vec2 varTexcoord; - -uniform sampler2D texture; -uniform sampler2D mask; -uniform vec4 color; - -void main (void) -{ - vec4 dst = texture2D(texture, varTexcoord.st, 0.0); - vec3 maskColor = texture2D(mask, varTexcoord.st, 0.0).rgb; - - float srcAlpha = clamp(0.78 * maskColor.r + maskColor.b + maskColor.g, 0.0, 1.0); - - vec3 borderColor = mix(color.rgb, vec3(1.0, 1.0, 1.0), 0.86); - vec3 finalColor = mix(color.rgb, borderColor, maskColor.g); - finalColor = mix(finalColor.rgb, vec3(1.0, 1.0, 1.0), maskColor.b); - - float outAlpha = srcAlpha + dst.a * (1.0 - srcAlpha); - - gl_FragColor.rgb = (finalColor * srcAlpha + dst.rgb * dst.a * (1.0 - srcAlpha)) / outAlpha; - gl_FragColor.a = outAlpha; -} diff --git a/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_NonPremultipliedBlit.fsh b/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_NonPremultipliedBlit.fsh deleted file mode 100755 index fe2a39f049..0000000000 --- a/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/Paint_NonPremultipliedBlit.fsh +++ /dev/null @@ -1,10 +0,0 @@ -precision highp float; - -varying vec2 varTexcoord; - -uniform sampler2D texture; - -void main (void) -{ - gl_FragColor = texture2D(texture, varTexcoord.st, 0.0); -} \ No newline at end of file diff --git a/submodules/LegacyComponents/Sources/TGPhotoCaptionInputMixin.m b/submodules/LegacyComponents/Sources/TGPhotoCaptionInputMixin.m index d9cd29dbd7..98f1744ecb 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoCaptionInputMixin.m +++ b/submodules/LegacyComponents/Sources/TGPhotoCaptionInputMixin.m @@ -204,7 +204,7 @@ CGRect frame = _currentFrame; UIEdgeInsets edgeInsets = _currentEdgeInsets; - CGFloat panelHeight = [_inputPanel updateLayoutSize:frame.size sideInset:0.0]; + CGFloat panelHeight = [_inputPanel updateLayoutSize:frame.size sideInset:0.0 animated:false]; [UIView animateWithDuration:duration delay:0.0f options:(curve << 16) animations:^{ _inputPanelView.frame = CGRectMake(edgeInsets.left, frame.size.height - panelHeight - MAX(edgeInsets.bottom, _keyboardHeight), frame.size.width, panelHeight); @@ -224,7 +224,7 @@ _currentFrame = frame; _currentEdgeInsets = edgeInsets; - CGFloat panelHeight = [_inputPanel updateLayoutSize:frame.size sideInset:0.0]; + CGFloat panelHeight = [_inputPanel updateLayoutSize:frame.size sideInset:0.0 animated:animated]; CGFloat y = 0.0; if (frame.size.width > frame.size.height && !TGIsPad()) { diff --git a/submodules/ListSectionHeaderNode/Sources/ListSectionHeaderNode.swift b/submodules/ListSectionHeaderNode/Sources/ListSectionHeaderNode.swift index 52a172d518..d737a6a77f 100644 --- a/submodules/ListSectionHeaderNode/Sources/ListSectionHeaderNode.swift +++ b/submodules/ListSectionHeaderNode/Sources/ListSectionHeaderNode.swift @@ -23,6 +23,7 @@ public final class ListSectionHeaderNode: ASDisplayNode { public var title: String? { didSet { self.label.attributedText = NSAttributedString(string: self.title ?? "", font: titleFont, textColor: self.theme.chatList.sectionHeaderTextColor) + self.label.accessibilityLabel = self.title if let (size, leftInset, rightInset) = self.validLayout { self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset) @@ -72,6 +73,8 @@ public final class ListSectionHeaderNode: ASDisplayNode { actionColor = self.theme.list.itemDestructiveColor } self.actionButtonLabel?.attributedText = NSAttributedString(string: action, font: actionFont, textColor: actionColor) + self.actionButton?.accessibilityLabel = action + self.actionButton?.accessibilityTraits = [.button] } if let (size, leftInset, rightInset) = self.validLayout { @@ -86,9 +89,11 @@ public final class ListSectionHeaderNode: ASDisplayNode { self.label = ImmediateTextNode() self.label.isUserInteractionEnabled = false + self.label.isAccessibilityElement = true super.init() + self.addSubnode(self.label) self.backgroundColor = theme.chatList.sectionHeaderFillColor diff --git a/submodules/PeerInfoUI/Sources/ItemListCallListItem.swift b/submodules/PeerInfoUI/Sources/ItemListCallListItem.swift index 47a0bdcb54..9ed20538e0 100644 --- a/submodules/PeerInfoUI/Sources/ItemListCallListItem.swift +++ b/submodules/PeerInfoUI/Sources/ItemListCallListItem.swift @@ -255,7 +255,7 @@ 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)") + accessibilityText.append("\(time) - \(type).\n") nodesLayout.append((timeLayout, typeLayout)) nodesApply.append((timeApply, typeApply)) diff --git a/submodules/PremiumUI/Sources/HelloView.swift b/submodules/PremiumUI/Sources/HelloView.swift index 41b06ae152..bc2553575d 100644 --- a/submodules/PremiumUI/Sources/HelloView.swift +++ b/submodules/PremiumUI/Sources/HelloView.swift @@ -48,18 +48,26 @@ final class HelloView: UIView, PhoneDemoDecorationView { private var activePhrases = Set() private var activePositions = Set() + private var containerView: UIView + override init(frame: CGRect) { + self.containerView = UIView() + super.init(frame: frame) + + self.addSubview(self.containerView) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + private var didSetup = false func setupAnimations() { - guard self.activePhrases.isEmpty else { + guard self.activePhrases.isEmpty, self.visible else { return } + self.didSetup = true var ids: [Int] = [] for i in 0 ..< phrases.count { ids.append(i) @@ -131,7 +139,7 @@ final class HelloView: UIView, PhoneDemoDecorationView { }) view.layer.animateScale(from: CGFloat.random(in: 0.4 ..< 0.6), to: CGFloat.random(in: 0.9 ..< 1.2), duration: duration, removeOnCompletion: false) - self.addSubview(view) + self.containerView.addSubview(view) } func positionForIndex(_ index: Int) -> CGPoint { @@ -142,11 +150,36 @@ final class HelloView: UIView, PhoneDemoDecorationView { return position } + private var visible = false func setVisible(_ visible: Bool) { - self.setupAnimations() + guard self.visible != visible else { + return + } + self.visible = visible + + if visible { + self.setupAnimations() + } else { + self.didSetup = false + } + + let transition = ContainedViewLayoutTransition.animated(duration: 0.3, curve: .linear) + transition.updateAlpha(layer: self.containerView.layer, alpha: visible ? 1.0 : 0.0, completion: { [weak self] finished in + if let strongSelf = self, finished && !visible && !strongSelf.visible { + for view in strongSelf.containerView.subviews { + view.removeFromSuperview() + } + } + }) } func resetAnimation() { } + + override func layoutSubviews() { + super.layoutSubviews() + + self.containerView.frame = CGRect(origin: .zero, size: self.frame.size) + } } diff --git a/submodules/PremiumUI/Sources/SwirlStarsView.swift b/submodules/PremiumUI/Sources/SwirlStarsView.swift index bf8a510522..d7ba345bf7 100644 --- a/submodules/PremiumUI/Sources/SwirlStarsView.swift +++ b/submodules/PremiumUI/Sources/SwirlStarsView.swift @@ -78,6 +78,6 @@ final class SwirlStarsView: UIView, PhoneDemoDecorationView { override func layoutSubviews() { super.layoutSubviews() - self.sceneView.frame = CGRect(origin: .zero, size: frame.size) + self.sceneView.frame = CGRect(origin: .zero, size: self.frame.size) } } diff --git a/submodules/TelegramBaseController/Sources/LocationBroadcastNavigationAccessoryPanel.swift b/submodules/TelegramBaseController/Sources/LocationBroadcastNavigationAccessoryPanel.swift index 708806b2a4..82d17c6d76 100644 --- a/submodules/TelegramBaseController/Sources/LocationBroadcastNavigationAccessoryPanel.swift +++ b/submodules/TelegramBaseController/Sources/LocationBroadcastNavigationAccessoryPanel.swift @@ -36,7 +36,7 @@ final class LocationBroadcastNavigationAccessoryPanel: ASDisplayNode { private let closeButton: HighlightableButtonNode private let separatorNode: ASDisplayNode - private var validLayout: (CGSize, CGFloat, CGFloat)? + private var validLayout: (CGSize, CGFloat, CGFloat, Bool)? private var peersAndMode: ([EnginePeer], LocationBroadcastNavigationAccessoryPanelMode, Bool)? init(accountPeerId: EnginePeer.Id, theme: PresentationTheme, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, tapAction: @escaping () -> Void, close: @escaping () -> Void) { @@ -75,6 +75,8 @@ final class LocationBroadcastNavigationAccessoryPanel: ASDisplayNode { super.init() + self.clipsToBounds = true + self.addSubnode(self.contentNode) self.contentNode.addSubnode(self.iconNode) @@ -105,10 +107,11 @@ final class LocationBroadcastNavigationAccessoryPanel: ASDisplayNode { self.separatorNode.backgroundColor = theme.rootController.navigationBar.separatorColor } - func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) { - self.validLayout = (size, leftInset, rightInset) + func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, isHidden: Bool, transition: ContainedViewLayoutTransition) { + self.validLayout = (size, leftInset, rightInset, isHidden) - transition.updateFrame(node: self.contentNode, frame: CGRect(origin: CGPoint(), size: size)) + transition.updateFrame(node: self.contentNode, frame: CGRect(origin: CGPoint(x: 0.0, y: isHidden ? -size.height : 0.0), size: size)) + transition.updateAlpha(node: self.contentNode, alpha: isHidden ? 0.0 : 1.0) let titleString = NSAttributedString(string: self.strings.Conversation_LiveLocation, font: titleFont, textColor: self.theme.rootController.navigationBar.primaryTextColor) var subtitleString: NSAttributedString? @@ -179,20 +182,17 @@ final class LocationBroadcastNavigationAccessoryPanel: ASDisplayNode { func update(peers: [EnginePeer], mode: LocationBroadcastNavigationAccessoryPanelMode, canClose: Bool) { self.peersAndMode = (peers, mode, canClose) - if let layout = validLayout { - self.updateLayout(size: layout.0, leftInset: layout.1, rightInset: layout.2, transition: .immediate) + if let (size, leftInset, rightInset, isHidden) = self.validLayout { + self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, isHidden: isHidden, transition: .immediate) } } func animateIn(_ transition: ContainedViewLayoutTransition) { - self.clipsToBounds = true let contentPosition = self.contentNode.layer.position - transition.animatePosition(node: self.contentNode, from: CGPoint(x: contentPosition.x, y: contentPosition.y - 37.0), completion: { [weak self] _ in - self?.clipsToBounds = false - }) + transition.animatePosition(node: self.contentNode, from: CGPoint(x: contentPosition.x, y: contentPosition.y - 37.0)) - guard let (size, _, _) = self.validLayout else { + guard let (size, _, _, _) = self.validLayout else { return } @@ -200,14 +200,12 @@ final class LocationBroadcastNavigationAccessoryPanel: ASDisplayNode { } func animateOut(_ transition: ContainedViewLayoutTransition, completion: @escaping () -> Void) { - self.clipsToBounds = true let contentPosition = self.contentNode.layer.position - transition.animatePosition(node: self.contentNode, to: CGPoint(x: contentPosition.x, y: contentPosition.y - 37.0), removeOnCompletion: false, completion: { [weak self] _ in - self?.clipsToBounds = false + transition.animatePosition(node: self.contentNode, to: CGPoint(x: contentPosition.x, y: contentPosition.y - 37.0), removeOnCompletion: false, completion: { _ in completion() }) - guard let (size, _, _) = self.validLayout else { + guard let (size, _, _, _) = self.validLayout else { return } diff --git a/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryPanel.swift b/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryPanel.swift index a10974bed6..c13c820f4d 100644 --- a/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryPanel.swift +++ b/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryPanel.swift @@ -25,6 +25,8 @@ public final class MediaNavigationAccessoryPanel: ASDisplayNode { super.init() + self.clipsToBounds = true + self.addSubnode(self.containerNode) self.containerNode.headerNode.close = { [weak self] in @@ -71,30 +73,26 @@ public final class MediaNavigationAccessoryPanel: ASDisplayNode { } } - public func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) { - transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(), size: size)) + public func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, isHidden: Bool, transition: ContainedViewLayoutTransition) { + transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: isHidden ? -size.height : 0.0), size: size)) + transition.updateAlpha(node: self.containerNode, alpha: isHidden ? 0.0 : 1.0) self.containerNode.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: transition) } public func animateIn(transition: ContainedViewLayoutTransition) { - self.clipsToBounds = true let contentPosition = self.containerNode.layer.position self.containerNode.animateIn(transition: transition) - transition.animatePosition(node: self.containerNode, from: CGPoint(x: contentPosition.x, y: contentPosition.y - 37.0), completion: { [weak self] _ in - self?.clipsToBounds = false - }) + transition.animatePosition(node: self.containerNode, from: CGPoint(x: contentPosition.x, y: contentPosition.y - 37.0)) } public func animateOut(transition: ContainedViewLayoutTransition, completion: @escaping () -> Void) { - self.clipsToBounds = true let contentPosition = self.containerNode.layer.position self.containerNode.animateOut(transition: transition) - transition.animatePosition(node: self.containerNode, to: CGPoint(x: contentPosition.x, y: contentPosition.y - 37.0), removeOnCompletion: false, completion: { [weak self] _ in - self?.clipsToBounds = false + transition.animatePosition(node: self.containerNode, to: CGPoint(x: contentPosition.x, y: contentPosition.y - 37.0), removeOnCompletion: false, completion: { _ in completion() }) } diff --git a/submodules/TelegramBaseController/Sources/TelegramBaseController.swift b/submodules/TelegramBaseController/Sources/TelegramBaseController.swift index a575233266..787cb214bf 100644 --- a/submodules/TelegramBaseController/Sources/TelegramBaseController.swift +++ b/submodules/TelegramBaseController/Sources/TelegramBaseController.swift @@ -392,10 +392,10 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder { super.containerLayoutUpdated(layout, transition: transition) - var navigationHeight = super.navigationLayout(layout: layout).navigationFrame.maxY - self.additionalNavigationBarHeight - if !self.displayNavigationBar { - navigationHeight = 0.0 - } + let navigationHeight = super.navigationLayout(layout: layout).navigationFrame.height - self.additionalNavigationBarHeight +// if !self.displayNavigationBar { +// navigationHeight = 0.0 +// } var additionalHeight: CGFloat = 0.0 @@ -408,7 +408,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder { if let current = self.groupCallAccessoryPanel { groupCallAccessoryPanel = current transition.updateFrame(node: groupCallAccessoryPanel, frame: panelFrame) - groupCallAccessoryPanel.updateLayout(size: panelFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, transition: transition) + groupCallAccessoryPanel.updateLayout(size: panelFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, isHidden: !self.displayNavigationBar, transition: transition) } else { let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } groupCallAccessoryPanel = GroupCallNavigationAccessoryPanel(context: self.context, presentationData: presentationData, tapAction: { [weak self] in @@ -426,7 +426,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder { groupCallAccessoryPanel.frame = panelFrame groupCallAccessoryPanel.update(data: groupCallPanelData) - groupCallAccessoryPanel.updateLayout(size: panelFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, transition: .immediate) + groupCallAccessoryPanel.updateLayout(size: panelFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, isHidden: !self.displayNavigationBar, transition: .immediate) if transition.isAnimated { groupCallAccessoryPanel.animateIn(transition) } @@ -451,7 +451,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder { if let current = self.locationBroadcastAccessoryPanel { locationBroadcastAccessoryPanel = current transition.updateFrame(node: locationBroadcastAccessoryPanel, frame: panelFrame) - locationBroadcastAccessoryPanel.updateLayout(size: panelFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, transition: transition) + locationBroadcastAccessoryPanel.updateLayout(size: panelFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, isHidden: !self.displayNavigationBar, transition: transition) } else { let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } locationBroadcastAccessoryPanel = LocationBroadcastNavigationAccessoryPanel(accountPeerId: self.context.account.peerId, theme: presentationData.theme, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder, tapAction: { [weak self] in @@ -583,7 +583,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder { } locationBroadcastAccessoryPanel.update(peers: locationBroadcastPeers, mode: locationBroadcastMode, canClose: canClose) - locationBroadcastAccessoryPanel.updateLayout(size: panelFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, transition: .immediate) + locationBroadcastAccessoryPanel.updateLayout(size: panelFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, isHidden: !self.displayNavigationBar, transition: .immediate) if transition.isAnimated { locationBroadcastAccessoryPanel.animateIn(transition) } @@ -614,7 +614,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder { let panelFrame = CGRect(origin: CGPoint(x: 0.0, y: navigationHeight.isZero ? -panelHeight : (navigationHeight + additionalHeight)), size: CGSize(width: layout.size.width, height: panelHeight)) if let (mediaAccessoryPanel, mediaType) = self.mediaAccessoryPanel, mediaType == type { transition.updateFrame(layer: mediaAccessoryPanel.layer, frame: panelFrame) - mediaAccessoryPanel.updateLayout(size: panelFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, transition: transition) + mediaAccessoryPanel.updateLayout(size: panelFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, isHidden: !self.displayNavigationBar, transition: transition) switch order { case .regular: mediaAccessoryPanel.containerNode.headerNode.playbackItems = (item, previousItem, nextItem) @@ -827,7 +827,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder { self.navigationBar?.additionalContentNode.addSubnode(mediaAccessoryPanel) } self.mediaAccessoryPanel = (mediaAccessoryPanel, type) - mediaAccessoryPanel.updateLayout(size: panelFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, transition: .immediate) + mediaAccessoryPanel.updateLayout(size: panelFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, isHidden: !self.displayNavigationBar, transition: .immediate) switch order { case .regular: mediaAccessoryPanel.containerNode.headerNode.playbackItems = (item, previousItem, nextItem) diff --git a/submodules/TelegramCallsUI/Sources/GroupCallNavigationAccessoryPanel.swift b/submodules/TelegramCallsUI/Sources/GroupCallNavigationAccessoryPanel.swift index 07ca7617ec..768fd82654 100644 --- a/submodules/TelegramCallsUI/Sources/GroupCallNavigationAccessoryPanel.swift +++ b/submodules/TelegramCallsUI/Sources/GroupCallNavigationAccessoryPanel.swift @@ -149,7 +149,7 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode { private let hapticFeedback = HapticFeedback() private var currentData: GroupCallPanelData? - private var validLayout: (CGSize, CGFloat, CGFloat)? + private var validLayout: (CGSize, CGFloat, CGFloat, Bool)? public init(context: AccountContext, presentationData: PresentationData, tapAction: @escaping () -> Void) { self.context = context @@ -188,6 +188,8 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode { self.separatorNode.isLayerBacked = true super.init() + + self.clipsToBounds = true self.addSubnode(self.contentNode) @@ -302,8 +304,8 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode { self.updateJoinButton() - if let (size, leftInset, rightInset) = self.validLayout { - self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: .immediate) + if let (size, leftInset, rightInset, isHidden) = self.validLayout { + self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, isHidden: isHidden, transition: .immediate) } } @@ -409,8 +411,8 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode { strongSelf.avatarsContent = strongSelf.avatarsContext.update(peers: summaryState.topParticipants.map { EnginePeer($0.peer) }, animated: false) } - if let (size, leftInset, rightInset) = strongSelf.validLayout { - strongSelf.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: .immediate) + if let (size, leftInset, rightInset, isHidden) = strongSelf.validLayout { + strongSelf.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, isHidden: isHidden, transition: .immediate) } })) @@ -427,8 +429,8 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode { strongSelf.callState = callState - if let (size, leftInset, rightInset) = strongSelf.validLayout { - strongSelf.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: transition) + if let (size, leftInset, rightInset, isHidden) = strongSelf.validLayout { + strongSelf.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, isHidden: isHidden, transition: transition) } })) @@ -548,16 +550,16 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode { } } self.previewImage = image - if let (size, leftInset, rightInset) = self.validLayout { - self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: .animated(duration: 0.2, curve: .easeInOut)) + if let (size, leftInset, rightInset, isHidden) = self.validLayout { + self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, isHidden: isHidden, transition: .animated(duration: 0.2, curve: .easeInOut)) } }) } } #endif - if let (size, leftInset, rightInset) = self.validLayout { - self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: .animated(duration: 0.2, curve: .easeInOut)) + if let (size, leftInset, rightInset, isHidden) = self.validLayout { + self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, isHidden: isHidden, transition: .animated(duration: 0.2, curve: .easeInOut)) } if updateAudioLevels { @@ -598,14 +600,15 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode { self.avatarsNode.updateAudioLevels(color: self.theme.chat.inputPanel.actionControlFillColor, backgroundColor: self.theme.chat.inputPanel.actionControlFillColor, levels: levels) } - public func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) { - self.validLayout = (size, leftInset, rightInset) + public func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, isHidden: Bool, transition: ContainedViewLayoutTransition) { + self.validLayout = (size, leftInset, rightInset, isHidden) let staticTransition: ContainedViewLayoutTransition = .immediate let panelHeight = size.height - transition.updateFrame(node: self.contentNode, frame: CGRect(origin: CGPoint(), size: size)) + transition.updateFrame(node: self.contentNode, frame: CGRect(origin: CGPoint(x: 0.0, y: isHidden ? -size.height : 0.0), size: size)) + transition.updateAlpha(node: self.contentNode, alpha: isHidden ? 0.0 : 1.0) self.tapButton.frame = CGRect(origin: CGPoint(), size: CGSize(width: size.width - 7.0 - 36.0 - 7.0, height: panelHeight)) @@ -649,8 +652,8 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode { if self.updateTimer == nil { let timer = SwiftSignalKit.Timer(timeout: 0.5, repeat: true, completion: { [weak self] in - if let strongSelf = self, let (size, leftInset, rightInset) = strongSelf.validLayout { - strongSelf.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: .immediate) + if let strongSelf = self, let (size, leftInset, rightInset, isHidden) = strongSelf.validLayout { + strongSelf.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, isHidden: isHidden, transition: .immediate) } }, queue: Queue.mainQueue()) self.updateTimer = timer @@ -691,7 +694,7 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode { previewImageNode.cornerRadius = 8.0 previewImageNode.contentMode = .scaleAspectFill self.previewImageNode = previewImageNode - self.addSubnode(previewImageNode) + self.contentNode.addSubnode(previewImageNode) } previewImageNode.image = previewImage let previewSize = CGSize(width: 40.0, height: 40.0) @@ -757,13 +760,10 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode { } public func animateIn(_ transition: ContainedViewLayoutTransition) { - self.clipsToBounds = true let contentPosition = self.contentNode.layer.position - transition.animatePosition(node: self.contentNode, from: CGPoint(x: contentPosition.x, y: contentPosition.y - 50.0), completion: { [weak self] _ in - self?.clipsToBounds = false - }) + transition.animatePosition(node: self.contentNode, from: CGPoint(x: contentPosition.x, y: contentPosition.y - 50.0)) - guard let (size, _, _) = self.validLayout else { + guard let (size, _, _, _) = self.validLayout else { return } @@ -771,14 +771,12 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode { } public func animateOut(_ transition: ContainedViewLayoutTransition, completion: @escaping () -> Void) { - self.clipsToBounds = true let contentPosition = self.contentNode.layer.position - transition.animatePosition(node: self.contentNode, to: CGPoint(x: contentPosition.x, y: contentPosition.y - 50.0), removeOnCompletion: false, completion: { [weak self] _ in - self?.clipsToBounds = false + transition.animatePosition(node: self.contentNode, to: CGPoint(x: contentPosition.x, y: contentPosition.y - 50.0), removeOnCompletion: false, completion: { _ in completion() }) - guard let (size, _, _) = self.validLayout else { + guard let (size, _, _, _) = self.validLayout else { return } diff --git a/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift b/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift index 51ea2c13e1..3a715fe788 100644 --- a/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift +++ b/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift @@ -127,7 +127,7 @@ private func sendMessageContent(account: Account, peerId: PeerId, attributes: [M case let .media(inputMedia, text): sendMessageRequest = account.network.request(Api.functions.messages.sendMedia(flags: flags, peer: inputPeer, replyToMsgId: replyMessageId, topMsgId: nil, media: inputMedia, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, sendAs: sendAsInputPeer)) |> `catch` { _ -> Signal in - return .complete() + return .complete() } } diff --git a/submodules/TelegramPermissionsUI/Sources/PermissionContentNode.swift b/submodules/TelegramPermissionsUI/Sources/PermissionContentNode.swift index e11718cc1a..14c1cc419a 100644 --- a/submodules/TelegramPermissionsUI/Sources/PermissionContentNode.swift +++ b/submodules/TelegramPermissionsUI/Sources/PermissionContentNode.swift @@ -91,16 +91,19 @@ public final class PermissionContentNode: ASDisplayNode { self.titleNode.textAlignment = .center self.titleNode.isUserInteractionEnabled = false self.titleNode.displaysAsynchronously = false + self.titleNode.isAccessibilityElement = true self.subtitleNode = ImmediateTextNode() self.subtitleNode.maximumNumberOfLines = 1 self.subtitleNode.textAlignment = .center self.subtitleNode.isUserInteractionEnabled = false self.subtitleNode.displaysAsynchronously = false + self.subtitleNode.isAccessibilityElement = true self.textNode = ImmediateTextNode() self.textNode.maximumNumberOfLines = 0 self.textNode.displaysAsynchronously = false + self.textNode.isAccessibilityElement = true self.actionButton = SolidRoundedButtonNode(theme: SolidRoundedButtonTheme(theme: theme), height: 52.0, cornerRadius: 9.0, gloss: true) @@ -108,9 +111,11 @@ public final class PermissionContentNode: ASDisplayNode { self.footerNode.textAlignment = .center self.footerNode.maximumNumberOfLines = 0 self.footerNode.displaysAsynchronously = false + self.footerNode.isAccessibilityElement = true self.privacyPolicyButton = HighlightableButtonNode() self.privacyPolicyButton.setTitle(secondaryButtonTitle ?? strings.Permissions_PrivacyPolicy, with: Font.regular(17.0), with: theme.list.itemAccentColor, for: .normal) + self.privacyPolicyButton.accessibilityLabel = secondaryButtonTitle ?? strings.Permissions_PrivacyPolicy super.init() @@ -133,10 +138,12 @@ public final class PermissionContentNode: ASDisplayNode { if let subtitle = subtitle { self.subtitleNode.attributedText = NSAttributedString(string: subtitle, font: Font.regular(13.0), textColor: theme.list.freeTextColor, paragraphAlignment: .center) + self.subtitleNode.accessibilityLabel = subtitle } if let footerText = footerText { self.footerNode.attributedText = NSAttributedString(string: footerText, font: Font.regular(13.0), textColor: theme.list.freeTextColor, paragraphAlignment: .center) + self.footerNode.accessibilityLabel = footerText } self.addSubnode(self.iconNode) @@ -165,12 +172,15 @@ public final class PermissionContentNode: ASDisplayNode { let body = MarkdownAttributeSet(font: Font.regular(16.0), textColor: theme.list.itemPrimaryTextColor) let link = MarkdownAttributeSet(font: Font.regular(16.0), textColor: theme.list.itemAccentColor, additionalAttributes: [TelegramTextAttributes.URL: ""]) self.textNode.attributedText = parseMarkdownIntoAttributedString(self.text.replacingOccurrences(of: "]", with: "]()"), attributes: MarkdownAttributes(body: body, bold: body, link: link, linkAttribute: { _ in nil }), textAlignment: .center) + self.textNode.accessibilityLabel = self.textNode.attributedText?.string if let subtitle = self.subtitleNode.attributedText?.string { self.subtitleNode.attributedText = NSAttributedString(string: subtitle, font: Font.regular(13.0), textColor: theme.list.freeTextColor, paragraphAlignment: .center) + self.subtitleNode.accessibilityLabel = subtitle } if let footerText = self.footerNode.attributedText?.string { self.footerNode.attributedText = NSAttributedString(string: footerText, font: Font.regular(13.0), textColor: theme.list.freeTextColor, paragraphAlignment: .center) + self.footerNode.accessibilityLabel = footerText } if let privacyPolicyTitle = self.privacyPolicyButton.attributedTitle(for: .normal)?.string { @@ -203,6 +213,7 @@ public final class PermissionContentNode: ASDisplayNode { let smallerSidePadding: CGFloat = 20.0 + insets.left self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.bold(fontSize), textColor: self.theme.list.itemPrimaryTextColor) + self.titleNode.accessibilityLabel = self.title let titleSize = self.titleNode.updateLayout(CGSize(width: size.width - sidePadding * 2.0, height: .greatestFiniteMagnitude)) let subtitleSize = self.subtitleNode.updateLayout(CGSize(width: size.width - smallerSidePadding * 2.0, height: .greatestFiniteMagnitude)) diff --git a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift index 304b90882b..47e5b2efe4 100644 --- a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift +++ b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift @@ -1913,9 +1913,14 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { var updatedGroups: [EmojiPagerContentComponent.ItemGroup] = [] + var staticIsFirst = false + if let first = itemGroups.first, first.groupId == AnyHashable("static") { + staticIsFirst = true + } + for group in itemGroups { if !(group.groupId.base is ItemCollectionId) { - if group.groupId != AnyHashable("static") { + if group.groupId != AnyHashable("static") || staticIsFirst { updatedGroups.append(group) } } else { diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift index 9f0013fb96..210a716254 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift @@ -7048,6 +7048,35 @@ public final class EmojiPagerContentComponent: Component { var itemGroups: [ItemGroup] = [] var itemGroupIndexById: [AnyHashable: Int] = [:] + let appendUnicodeEmoji = { + if areUnicodeEmojiEnabled { + for (subgroupId, list) in staticEmojiMapping { + let groupId: AnyHashable = "static" + for emojiString in list { + let resultItem = EmojiPagerContentComponent.Item( + animationData: nil, + content: .staticEmoji(emojiString), + itemFile: nil, + subgroupId: subgroupId.rawValue, + icon: .none, + tintMode: .none + ) + + if let groupIndex = itemGroupIndexById[groupId] { + itemGroups[groupIndex].items.append(resultItem) + } else { + itemGroupIndexById[groupId] = itemGroups.count + itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: strings.EmojiInput_SectionTitleEmoji, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: nil, isClearable: false, headerItem: nil, items: [resultItem])) + } + } + } + } + } + + if !hasPremium { + appendUnicodeEmoji() + } + var installedCollectionIds = Set() for (id, _, _) in view.collectionInfos { installedCollectionIds.insert(id) @@ -7835,29 +7864,10 @@ public final class EmojiPagerContentComponent: Component { } } - if areUnicodeEmojiEnabled { - for (subgroupId, list) in staticEmojiMapping { - let groupId: AnyHashable = "static" - for emojiString in list { - let resultItem = EmojiPagerContentComponent.Item( - animationData: nil, - content: .staticEmoji(emojiString), - itemFile: nil, - subgroupId: subgroupId.rawValue, - icon: .none, - tintMode: .none - ) - - if let groupIndex = itemGroupIndexById[groupId] { - itemGroups[groupIndex].items.append(resultItem) - } else { - itemGroupIndexById[groupId] = itemGroups.count - itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: strings.EmojiInput_SectionTitleEmoji, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: nil, isClearable: false, headerItem: nil, items: [resultItem])) - } - } - } + if hasPremium { + appendUnicodeEmoji() } - + var displaySearchWithPlaceholder: String? let searchInitiallyHidden = true if hasSearch { diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index f1af62e80b..3144e4ca9e 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -15822,7 +15822,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } var attemptSelectionImpl: ((Peer) -> Void)? - let controller = self.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: self.context, updatedPresentationData: self.updatedPresentationData, filter: filter, attemptSelection: { peer, _ in + let controller = self.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: self.context, updatedPresentationData: self.updatedPresentationData, filter: filter, hasFilters: true, attemptSelection: { peer, _ in attemptSelectionImpl?(peer) }, multipleSelection: true, forwardedMessageIds: messages.map { $0.id }, selectForumThreads: true)) let context = self.context diff --git a/submodules/TelegramUI/Sources/ChatMessageInstantVideoBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageInstantVideoBubbleContentNode.swift index afc5c9fd49..d187dfaeff 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInstantVideoBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInstantVideoBubbleContentNode.swift @@ -331,12 +331,7 @@ class ChatMessageInstantVideoBubbleContentNode: ChatMessageBubbleContentNode { ) animation.animator.updateFrame(layer: strongSelf.backdropMaskForeground, frame: backdropMaskFrame, completion: nil) - let videoLayoutData: ChatMessageInstantVideoItemLayoutData - if incoming { - videoLayoutData = .constrained(left: 0.0, right: 0.0) //max(0.0, availableContentWidth - videoFrame.width)) - } else { - videoLayoutData = .constrained(left: 0.0, right: 0.0) - } + let videoLayoutData: ChatMessageInstantVideoItemLayoutData = .constrained(left: 0.0, right: 0.0) var videoAnimation = animation var fileAnimation = animation diff --git a/submodules/TelegramUI/Sources/ChatMessageItem.swift b/submodules/TelegramUI/Sources/ChatMessageItem.swift index a878a6dd5e..fd012872ba 100644 --- a/submodules/TelegramUI/Sources/ChatMessageItem.swift +++ b/submodules/TelegramUI/Sources/ChatMessageItem.swift @@ -287,6 +287,33 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible { } } + var unsent: Bool { + switch self.content { + case let .message(message, _, _, _, _): + return message.flags.contains(.Unsent) + case let .group(messages): + return messages[0].0.flags.contains(.Unsent) + } + } + + var sending: Bool { + switch self.content { + case let .message(message, _, _, _, _): + return message.flags.contains(.Sending) + case let .group(messages): + return messages[0].0.flags.contains(.Sending) + } + } + + var failed: Bool { + switch self.content { + case let .message(message, _, _, _, _): + return message.flags.contains(.Failed) + case let .group(messages): + return messages[0].0.flags.contains(.Failed) + } + } + public init(presentationData: ChatPresentationData, context: AccountContext, chatLocation: ChatLocation, associatedData: ChatMessageItemAssociatedData, controllerInteraction: ChatControllerInteraction, content: ChatMessageItemContent, disableDate: Bool = false, additionalContent: ChatMessageItemAdditionalContent? = nil) { self.presentationData = presentationData self.context = context diff --git a/submodules/TelegramUI/Sources/ChatMessageItemView.swift b/submodules/TelegramUI/Sources/ChatMessageItemView.swift index 5783330741..2b0ac8e7b7 100644 --- a/submodules/TelegramUI/Sources/ChatMessageItemView.swift +++ b/submodules/TelegramUI/Sources/ChatMessageItemView.swift @@ -544,12 +544,37 @@ final class ChatMessageAccessibilityData { let dateString = DateFormatter.localizedString(from: Date(timeIntervalSince1970: Double(message.timestamp)), dateStyle: .medium, timeStyle: .short) result += "\n\(dateString)" - if !isIncoming && item.read && !isReply { + if !isIncoming && !isReply { result += "\n" - if announceIncomingAuthors { - result += item.presentationData.strings.VoiceOver_Chat_SeenByRecipients + if item.sending { + result += item.presentationData.strings.VoiceOver_Chat_Sending + } else if item.failed { + result += item.presentationData.strings.VoiceOver_Chat_Failed } else { - result += item.presentationData.strings.VoiceOver_Chat_SeenByRecipient + if item.read { + if announceIncomingAuthors { + result += item.presentationData.strings.VoiceOver_Chat_SeenByRecipients + } else { + result += item.presentationData.strings.VoiceOver_Chat_SeenByRecipient + } + } + for attribute in message.attributes { + if let attribute = attribute as? ConsumableContentMessageAttribute { + if !attribute.consumed { + if announceIncomingAuthors { + result += item.presentationData.strings.VoiceOver_Chat_NotPlayedByRecipients + } else { + result += item.presentationData.strings.VoiceOver_Chat_NotPlayedByRecipient + } + } else { + if announceIncomingAuthors { + result += item.presentationData.strings.VoiceOver_Chat_PlayedByRecipients + } else { + result += item.presentationData.strings.VoiceOver_Chat_PlayedByRecipient + } + } + } + } } } value = result diff --git a/submodules/TelegramUI/Sources/ChatScheduleTimeControllerNode.swift b/submodules/TelegramUI/Sources/ChatScheduleTimeControllerNode.swift index c1fe018cdc..d6f0206005 100644 --- a/submodules/TelegramUI/Sources/ChatScheduleTimeControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatScheduleTimeControllerNode.swift @@ -102,9 +102,13 @@ class ChatScheduleTimeControllerNode: ViewControllerTracingNode, UIScrollViewDel self.titleNode = ASTextNode() self.titleNode.attributedText = NSAttributedString(string: title, font: Font.bold(17.0), textColor: textColor) + self.titleNode.accessibilityLabel = title + self.titleNode.accessibilityTraits = [.staticText] self.cancelButton = HighlightableButtonNode() self.cancelButton.setTitle(self.presentationData.strings.Common_Cancel, with: Font.regular(17.0), with: accentColor, for: .normal) + self.cancelButton.accessibilityLabel = self.presentationData.strings.Common_Cancel + self.cancelButton.accessibilityTraits = [.button] self.doneButton = SolidRoundedButtonNode(theme: SolidRoundedButtonTheme(theme: self.presentationData.theme), height: 52.0, cornerRadius: 11.0, gloss: false) diff --git a/submodules/TelegramUI/Sources/EditableTokenListNode.swift b/submodules/TelegramUI/Sources/EditableTokenListNode.swift index dd74061201..7e6ae6f971 100644 --- a/submodules/TelegramUI/Sources/EditableTokenListNode.swift +++ b/submodules/TelegramUI/Sources/EditableTokenListNode.swift @@ -451,14 +451,11 @@ final class EditableTokenListNode: ASDisplayNode, UITextFieldDelegate { transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: width, height: nodeHeight))) if !abs(previousContentHeight - contentHeight).isLess(than: CGFloat.ulpOfOne) { - let contentOffset = CGPoint(x: 0, y: max(0, contentHeight - nodeHeight)) + let contentOffset = CGPoint(x: 0.0, y: max(0.0, contentHeight - nodeHeight)) if case .immediate = transition { self.scrollNode.view.contentOffset = contentOffset - } - else { - UIView.animate(withDuration: 0.2) { - self.scrollNode.view.contentOffset = contentOffset - } + } else { + transition.updateBounds(node: self.scrollNode, bounds: CGRect(origin: CGPoint(x: 0.0, y: contentOffset.y), size: self.scrollNode.bounds.size)) } } self.scrollNode.view.contentSize = CGSize(width: width, height: contentHeight) diff --git a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoListPaneNode.swift b/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoListPaneNode.swift index af6d37211a..f409765387 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoListPaneNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoListPaneNode.swift @@ -207,7 +207,7 @@ final class PeerInfoListPaneNode: ASDisplayNode, PeerInfoPaneNode { let panelFrame = CGRect(origin: CGPoint(x: 0.0, y: topPanelHeight - panelHeight), size: CGSize(width: size.width, height: panelHeight)) if let (mediaAccessoryPanel, mediaType) = self.mediaAccessoryPanel, mediaType == type { transition.updateFrame(layer: mediaAccessoryPanel.layer, frame: panelFrame) - mediaAccessoryPanel.updateLayout(size: panelFrame.size, leftInset: sideInset, rightInset: sideInset, transition: transition) + mediaAccessoryPanel.updateLayout(size: panelFrame.size, leftInset: sideInset, rightInset: sideInset, isHidden: false, transition: transition) switch order { case .regular: mediaAccessoryPanel.containerNode.headerNode.playbackItems = (item, previousItem, nextItem) @@ -410,7 +410,7 @@ final class PeerInfoListPaneNode: ASDisplayNode, PeerInfoPaneNode { self.mediaAccessoryPanelContainer.addSubnode(mediaAccessoryPanel) } self.mediaAccessoryPanel = (mediaAccessoryPanel, type) - mediaAccessoryPanel.updateLayout(size: panelFrame.size, leftInset: sideInset, rightInset: sideInset, transition: .immediate) + mediaAccessoryPanel.updateLayout(size: panelFrame.size, leftInset: sideInset, rightInset: sideInset, isHidden: false, transition: .immediate) switch order { case .regular: mediaAccessoryPanel.containerNode.headerNode.playbackItems = (item, previousItem, nextItem) diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 5c4c349eea..d3594563d6 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -7927,7 +7927,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate func forwardMessages(messageIds: Set?) { if let messageIds = messageIds ?? self.state.selectedMessageIds, !messageIds.isEmpty { - let peerSelectionController = self.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, filter: [.onlyWriteable, .excludeDisabled], multipleSelection: true, selectForumThreads: true)) + let peerSelectionController = self.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, filter: [.onlyWriteable, .excludeDisabled], hasFilters: true, multipleSelection: true, selectForumThreads: true)) peerSelectionController.multiplePeersSelected = { [weak self, weak peerSelectionController] peers, peerMap, messageText, mode, forwardOptions in guard let strongSelf = self, let strongController = peerSelectionController else { return diff --git a/submodules/TelegramUI/Sources/PeerInfoScreenMultilineInputtem.swift b/submodules/TelegramUI/Sources/PeerInfoScreenMultilineInputtem.swift index 878ef1b051..9d04b079de 100644 --- a/submodules/TelegramUI/Sources/PeerInfoScreenMultilineInputtem.swift +++ b/submodules/TelegramUI/Sources/PeerInfoScreenMultilineInputtem.swift @@ -111,7 +111,7 @@ final class PeerInfoScreenMultilineInputItemNode: PeerInfoScreenItemNode { 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)) + transition.updateFrame(node: 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 if self.maskNode.supernode == nil { diff --git a/submodules/TelegramUI/Sources/PeerSelectionController.swift b/submodules/TelegramUI/Sources/PeerSelectionController.swift index eaad689acc..2d10245531 100644 --- a/submodules/TelegramUI/Sources/PeerSelectionController.swift +++ b/submodules/TelegramUI/Sources/PeerSelectionController.swift @@ -58,6 +58,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon private let hasChatListSelector: Bool private let hasContactSelector: Bool + private let hasFilters: Bool private let hasGlobalSearch: Bool private let pretendPresentedInModal: Bool private let forwardedMessageIds: [EngineMessage.Id] @@ -79,11 +80,18 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon } private var searchContentNode: NavigationBarSearchContentNode? + var tabContainerNode: ChatListFilterTabContainerNode? + private var tabContainerData: ([ChatListFilterTabEntry], Bool, Int32?)? + + private let filterDisposable = MetaDisposable() + + private var validLayout: ContainerViewLayout? public init(_ params: PeerSelectionControllerParams) { self.context = params.context self.filter = params.filter self.forumPeerId = params.forumPeerId + self.hasFilters = params.hasFilters self.hasChatListSelector = params.hasChatListSelector self.hasContactSelector = params.hasContactSelector self.hasGlobalSearch = params.hasGlobalSearch @@ -135,7 +143,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon } self.presentationDataDisposable = ((params.updatedPresentationData?.signal ?? self.context.sharedContext.presentationData) - |> deliverOnMainQueue).start(next: { [weak self] presentationData in + |> deliverOnMainQueue).start(next: { [weak self] presentationData in if let strongSelf = self { let previousTheme = strongSelf.presentationData.theme let previousStrings = strongSelf.presentationData.strings @@ -156,6 +164,36 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon if params.multipleSelection { self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Select, style: .plain, target: self, action: #selector(self.beginSelection)) } + + if params.hasFilters { + self.tabContainerNode = ChatListFilterTabContainerNode() + self.navigationBar?.setSecondaryContentNode(self.tabContainerNode, animated: false) + self.reloadFilters() + + self.peerSelectionNode.mainContainerNode?.currentItemFilterUpdated = { [weak self] filter, fraction, transition, force in + guard let strongSelf = self else { + return + } + guard let layout = strongSelf.validLayout else { + return + } + guard let tabContainerData = strongSelf.tabContainerData else { + return + } + if force { + strongSelf.tabContainerNode?.cancelAnimations() + } + strongSelf.tabContainerNode?.update(size: CGSize(width: layout.size.width, height: 46.0), sideInset: layout.safeInsets.left, filters: tabContainerData.0, selectedFilter: filter, isReordering: false, isEditing: false, canReorderAllChats: false, filtersLimit: tabContainerData.2, transitionFraction: fraction, presentationData: strongSelf.presentationData, transition: transition) + } + + self.tabContainerNode?.tabSelected = { [weak self] id, _ in + guard let strongSelf = self else { + return + } + + strongSelf.selectTab(id: id) + } + } } required public init(coder aDecoder: NSCoder) { @@ -165,6 +203,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon deinit { self.openMessageFromSearchDisposable.dispose() self.presentationDataDisposable?.dispose() + self.filterDisposable.dispose() } private func updateThemeAndStrings() { @@ -177,7 +216,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon } override public func loadDisplayNode() { - self.displayNode = PeerSelectionControllerNode(context: self.context, controller: self, presentationData: self.presentationData, filter: self.filter, forumPeerId: self.forumPeerId, hasChatListSelector: self.hasChatListSelector, hasContactSelector: self.hasContactSelector, hasGlobalSearch: self.hasGlobalSearch, forwardedMessageIds: self.forwardedMessageIds, hasTypeHeaders: self.hasTypeHeaders, requestPeerType: self.requestPeerType, createNewGroup: self.createNewGroup, present: { [weak self] c, a in + self.displayNode = PeerSelectionControllerNode(context: self.context, controller: self, presentationData: self.presentationData, filter: self.filter, forumPeerId: self.forumPeerId, hasFilters: self.hasFilters, hasChatListSelector: self.hasChatListSelector, hasContactSelector: self.hasContactSelector, hasGlobalSearch: self.hasGlobalSearch, forwardedMessageIds: self.forwardedMessageIds, hasTypeHeaders: self.hasTypeHeaders, requestPeerType: self.requestPeerType, createNewGroup: self.createNewGroup, present: { [weak self] c, a in self?.present(c, in: .window(.root), with: a) }, presentInGlobalOverlay: { [weak self] c, a in self?.presentInGlobalOverlay(c, with: a) @@ -208,6 +247,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon updatedPresentationData: nil, filter: strongSelf.filter, forumPeerId: peer.id, + hasFilters: false, hasChatListSelector: false, hasContactSelector: false, hasGlobalSearch: false, @@ -247,6 +287,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon updatedPresentationData: nil, filter: strongSelf.filter, forumPeerId: peer.id, + hasFilters: false, hasChatListSelector: false, hasContactSelector: false, hasGlobalSearch: false, @@ -295,10 +336,25 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon self._ready.set(self.peerSelectionNode.ready) } + public override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + self.peerSelectionNode.mainContainerNode?.updateEnableAdjacentFilterLoading(true) + } + override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { + self.validLayout = layout + super.containerLayoutUpdated(layout, transition: transition) self.peerSelectionNode.containerLayoutUpdated(layout, navigationBarHeight: self.cleanNavigationHeight, actualNavigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition) + + if let tabContainerNode = self.tabContainerNode, let mainContainerNode = self.peerSelectionNode.mainContainerNode { + let tabContainerOffset: CGFloat = 0.0 + let navigationBarHeight = self.navigationBar?.frame.maxY ?? 0.0 + transition.updateFrame(node: tabContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: navigationBarHeight - self.additionalNavigationBarHeight - 46.0 + tabContainerOffset), size: CGSize(width: layout.size.width, height: 46.0))) + tabContainerNode.update(size: CGSize(width: layout.size.width, height: 46.0), sideInset: layout.safeInsets.left, filters: self.tabContainerData?.0 ?? [], selectedFilter: mainContainerNode.currentItemFilter, isReordering: false, isEditing: false, canReorderAllChats: false, filtersLimit: self.tabContainerData?.2, transitionFraction: mainContainerNode.transitionFraction, presentationData: self.presentationData, transition: .animated(duration: 0.4, curve: .spring)) + } } @objc private func beginSelection() { @@ -328,10 +384,178 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon private func deactivateSearch() { if !self.displayNavigationBar { - self.setDisplayNavigationBar(true, transition: .animated(duration: 0.5, curve: .spring)) if let searchContentNode = self.searchContentNode { self.peerSelectionNode.deactivateSearch(placeholderNode: searchContentNode.placeholderNode) } } } + + private var initializedFilters = false + private func reloadFilters(firstUpdate: (() -> Void)? = nil) { + let filterItems = chatListFilterItems(context: self.context) + var notifiedFirstUpdate = false + self.filterDisposable.set((combineLatest(queue: .mainQueue(), + filterItems, + self.context.account.postbox.peerView(id: self.context.account.peerId), + self.context.engine.data.get(TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: false)) + ) + |> deliverOnMainQueue).start(next: { [weak self] countAndFilterItems, peerView, limits in + guard let strongSelf = self else { + return + } + + let isPremium = peerView.peers[peerView.peerId]?.isPremium + + let (_, items) = countAndFilterItems + var filterItems: [ChatListFilterTabEntry] = [] + + for (filter, unreadCount, hasUnmutedUnread) in items { + switch filter { + case .allChats: + if let isPremium = isPremium, !isPremium && filterItems.count > 0 { + filterItems.insert(.all(unreadCount: 0), at: 0) + } else { + filterItems.append(.all(unreadCount: 0)) + } + case let .filter(id, title, _, _): + filterItems.append(.filter(id: id, text: title, unread: ChatListFilterTabEntryUnreadCount(value: unreadCount, hasUnmuted: hasUnmutedUnread))) + } + } + + let resolvedItems = filterItems + + var wasEmpty = false + if let tabContainerData = strongSelf.tabContainerData { + wasEmpty = tabContainerData.0.count <= 1 || tabContainerData.1 + } else { + wasEmpty = true + } + + let firstItem = countAndFilterItems.1.first?.0 ?? .allChats + let firstItemEntryId: ChatListFilterTabEntryId + switch firstItem { + case .allChats: + firstItemEntryId = .all + case let .filter(id, _, _, _): + firstItemEntryId = .filter(id) + } + + var selectedEntryId = !strongSelf.initializedFilters ? firstItemEntryId : (strongSelf.peerSelectionNode.mainContainerNode?.currentItemFilter ?? .all) + var resetCurrentEntry = false + if !resolvedItems.contains(where: { $0.id == selectedEntryId }) { + resetCurrentEntry = true + if let tabContainerData = strongSelf.tabContainerData { + var found = false + if let index = tabContainerData.0.firstIndex(where: { $0.id == selectedEntryId }) { + for i in (0 ..< index - 1).reversed() { + if resolvedItems.contains(where: { $0.id == tabContainerData.0[i].id }) { + selectedEntryId = tabContainerData.0[i].id + found = true + break + } + } + } + if !found { + selectedEntryId = .all + } + } else { + selectedEntryId = .all + } + } + let filtersLimit = isPremium == false ? limits.maxFoldersCount : nil + strongSelf.tabContainerData = (resolvedItems, false, filtersLimit) + var availableFilters: [ChatListContainerNodeFilter] = [] + var hasAllChats = false + for item in items { + switch item.0 { + case .allChats: + hasAllChats = true + if let isPremium = isPremium, !isPremium && availableFilters.count > 0 { + availableFilters.insert(.all, at: 0) + } else { + availableFilters.append(.all) + } + case .filter: + availableFilters.append(.filter(item.0)) + } + } + if !hasAllChats { + availableFilters.insert(.all, at: 0) + } + strongSelf.peerSelectionNode.mainContainerNode?.updateAvailableFilters(availableFilters, limit: filtersLimit) + +// if isPremium == nil && items.isEmpty { +// strongSelf.ready.set(strongSelf.chatListDisplayNode.mainContainerNode.currentItemNode.ready) +// } else if !strongSelf.initializedFilters { +// if selectedEntryId != strongSelf.chatListDisplayNode.mainContainerNode.currentItemFilter { +// strongSelf.chatListDisplayNode.mainContainerNode.switchToFilter(id: selectedEntryId, animated: false, completion: { [weak self] in +// if let strongSelf = self { +// strongSelf.ready.set(strongSelf.chatListDisplayNode.mainContainerNode.currentItemNode.ready) +// } +// }) +// } else { +// strongSelf.ready.set(strongSelf.chatListDisplayNode.mainContainerNode.currentItemNode.ready) +// } +// strongSelf.initializedFilters = true +// } + strongSelf.initializedFilters = true + + let isEmpty = resolvedItems.count <= 1 + + if wasEmpty != isEmpty, strongSelf.displayNavigationBar { + strongSelf.navigationBar?.setSecondaryContentNode(isEmpty ? nil : strongSelf.tabContainerNode, animated: false) + } + + if let layout = strongSelf.validLayout { + if wasEmpty != isEmpty { + strongSelf.containerLayoutUpdated(layout, transition: .immediate) + } else { + strongSelf.tabContainerNode?.update(size: CGSize(width: layout.size.width, height: 46.0), sideInset: layout.safeInsets.left, filters: resolvedItems, selectedFilter: selectedEntryId, isReordering: false, isEditing: false, canReorderAllChats: false, filtersLimit: filtersLimit, transitionFraction: 0.0, presentationData: strongSelf.presentationData, transition: .animated(duration: 0.4, curve: .spring)) + } + } + + if !notifiedFirstUpdate { + notifiedFirstUpdate = true + firstUpdate?() + } + + if resetCurrentEntry { + //strongSelf.selectTab(id: selectedEntryId) + } + })) + } + + private func selectTab(id: ChatListFilterTabEntryId) { + let _ = (self.context.engine.peers.currentChatListFilters() + |> deliverOnMainQueue).start(next: { [weak self] filters in + guard let strongSelf = self else { + return + } + let updatedFilter: ChatListFilter? + switch id { + case .all: + updatedFilter = nil + case let .filter(id): + var found = false + var foundValue: ChatListFilter? + for filter in filters { + if filter.id == id { + foundValue = filter + found = true + break + } + } + if found { + updatedFilter = foundValue + } else { + updatedFilter = nil + } + } + if strongSelf.peerSelectionNode.mainContainerNode?.currentItemNode.chatListFilter?.id == updatedFilter?.id { + strongSelf.scrollToTop?() + } else { + strongSelf.peerSelectionNode.mainContainerNode?.switchToFilter(id: updatedFilter.flatMap { .filter($0.id) } ?? .all) + } + }) + } } diff --git a/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift b/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift index c0900cf727..ceac7166a2 100644 --- a/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift +++ b/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift @@ -24,7 +24,7 @@ import SolidRoundedButtonNode final class PeerSelectionControllerNode: ASDisplayNode { private let context: AccountContext - private weak var controller: PeerSelectionController? + private weak var controller: PeerSelectionControllerImpl? private let present: (ViewController, Any?) -> Void private let presentInGlobalOverlay: (ViewController, Any?) -> Void private let dismiss: () -> Void @@ -64,8 +64,9 @@ final class PeerSelectionControllerNode: ASDisplayNode { private var forwardAccessoryPanelNode: ForwardAccessoryPanelNode? var contactListNode: ContactListNode? - let chatListNode: ChatListNode - + let chatListNode: ChatListNode? + let mainContainerNode: ChatListContainerNode? + private var contactListActive = false private var searchDisplayController: SearchDisplayController? @@ -104,7 +105,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { return (self.presentationData, self.presentationDataPromise.get()) } - init(context: AccountContext, controller: PeerSelectionController, presentationData: PresentationData, filter: ChatListNodePeersFilter, forumPeerId: EnginePeer.Id?, hasChatListSelector: Bool, hasContactSelector: Bool, hasGlobalSearch: Bool, forwardedMessageIds: [EngineMessage.Id], hasTypeHeaders: Bool, requestPeerType: ReplyMarkupButtonRequestPeerType?, createNewGroup: (() -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, dismiss: @escaping () -> Void) { + init(context: AccountContext, controller: PeerSelectionControllerImpl, presentationData: PresentationData, filter: ChatListNodePeersFilter, forumPeerId: EnginePeer.Id?, hasFilters: Bool, hasChatListSelector: Bool, hasContactSelector: Bool, hasGlobalSearch: Bool, forwardedMessageIds: [EngineMessage.Id], hasTypeHeaders: Bool, requestPeerType: ReplyMarkupButtonRequestPeerType?, createNewGroup: (() -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, dismiss: @escaping () -> Void) { self.context = context self.controller = controller self.present = present @@ -200,54 +201,82 @@ final class PeerSelectionControllerNode: ASDisplayNode { chatListMode = .peers(filter: filter, isSelecting: false, additionalCategories: chatListCategories, chatListFilters: nil, displayAutoremoveTimeout: false) } - self.chatListNode = ChatListNode(context: context, location: chatListLocation, previewing: false, fillPreloadItems: false, mode: chatListMode, theme: self.presentationData.theme, fontSize: presentationData.listsFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameSortOrder: presentationData.nameSortOrder, nameDisplayOrder: presentationData.nameDisplayOrder, animationCache: self.animationCache, animationRenderer: self.animationRenderer, disableAnimations: true, isInlineMode: false) - + if hasFilters { + self.mainContainerNode = ChatListContainerNode(context: context, location: chatListLocation, chatListMode: chatListMode, previewing: false, controlsHistoryPreload: false, isInlineMode: false, presentationData: presentationData, animationCache: self.animationCache, animationRenderer: self.animationRenderer, filterBecameEmpty: { _ in + //filterBecameEmpty?(filter) + }, filterEmptyAction: { _ in + //filterEmptyAction?(filter) + }, secondaryEmptyAction: { + + }) + self.chatListNode = nil + } else { + self.mainContainerNode = nil + self.chatListNode = ChatListNode(context: context, location: chatListLocation, previewing: false, fillPreloadItems: false, mode: chatListMode, theme: self.presentationData.theme, fontSize: presentationData.listsFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameSortOrder: presentationData.nameSortOrder, nameDisplayOrder: presentationData.nameDisplayOrder, animationCache: self.animationCache, animationRenderer: self.animationRenderer, disableAnimations: true, isInlineMode: false) + } + super.init() self.setViewBlock({ return UITracingLayerView() }) - self.chatListNode.additionalCategorySelected = { _ in + self.chatListNode?.additionalCategorySelected = { _ in createNewGroup?() } - + self.backgroundColor = self.presentationData.theme.chatList.backgroundColor - self.chatListNode.selectionCountChanged = { [weak self] count in + self.chatListNode?.selectionCountChanged = { [weak self] count in self?.textInputPanelNode?.updateSendButtonEnabled(count > 0, animated: true) } - self.chatListNode.accessibilityPageScrolledString = { row, count in + self.chatListNode?.accessibilityPageScrolledString = { row, count in return presentationData.strings.VoiceOver_ScrollStatus(row, count).string } - self.chatListNode.activateSearch = { [weak self] in + self.chatListNode?.activateSearch = { [weak self] in + self?.requestActivateSearch?() + } + self.mainContainerNode?.activateSearch = { [weak self] in self?.requestActivateSearch?() } - self.chatListNode.peerSelected = { [weak self] peer, threadId, _, _, _ in - self?.chatListNode.clearHighlightAnimated(true) + self.chatListNode?.peerSelected = { [weak self] peer, threadId, _, _, _ in + self?.chatListNode?.clearHighlightAnimated(true) + self?.requestOpenPeer?(peer._asPeer(), threadId) + } + self.mainContainerNode?.peerSelected = { [weak self] peer, threadId, _, _, _ in + self?.chatListNode?.clearHighlightAnimated(true) self?.requestOpenPeer?(peer._asPeer(), threadId) } - self.chatListNode.disabledPeerSelected = { [weak self] peer, threadId in + self.chatListNode?.disabledPeerSelected = { [weak self] peer, threadId in self?.requestOpenDisabledPeer?(peer._asPeer(), threadId) } - self.chatListNode.contentOffsetChanged = { [weak self] offset in + self.chatListNode?.contentOffsetChanged = { [weak self] offset in guard let strongSelf = self else { return } - if strongSelf.chatListNode.supernode != nil { + if strongSelf.chatListNode?.supernode != nil { strongSelf.contentOffsetChanged?(offset) } } - self.chatListNode.contentScrollingEnded = { [weak self] listView in + self.mainContainerNode?.contentOffsetChanged = { [weak self] offset in + guard let strongSelf = self else { + return + } + if strongSelf.chatListNode?.supernode != nil { + strongSelf.contentOffsetChanged?(offset) + } + } + + self.chatListNode?.contentScrollingEnded = { [weak self] listView in return self?.contentScrollingEnded?(listView) ?? false } - self.chatListNode.isEmptyUpdated = { [weak self] state, _, _ in + self.chatListNode?.isEmptyUpdated = { [weak self] state, _, _ in guard let strongSelf = self else { return } @@ -258,8 +287,13 @@ final class PeerSelectionControllerNode: ASDisplayNode { } } - self.addSubnode(self.chatListNode) - + if let mainContainerNode = self.mainContainerNode { + self.addSubnode(mainContainerNode) + } + if let chatListNode = self.chatListNode { + self.addSubnode(chatListNode) + } + if hasChatListSelector && hasContactSelector { self.segmentedControlNode!.selectedIndexChanged = { [weak self] index in self?.indexChanged(index) @@ -271,9 +305,9 @@ final class PeerSelectionControllerNode: ASDisplayNode { } if let requirementsBackgroundNode = self.requirementsBackgroundNode, let requirementsSeparatorNode = self.requirementsSeparatorNode, let requirementsTextNode = self.requirementsTextNode { - self.chatListNode.addSubnode(requirementsBackgroundNode) - self.chatListNode.addSubnode(requirementsSeparatorNode) - self.chatListNode.addSubnode(requirementsTextNode) + self.chatListNode?.addSubnode(requirementsBackgroundNode) + self.chatListNode?.addSubnode(requirementsSeparatorNode) + self.chatListNode?.addSubnode(requirementsTextNode) self.addSubnode(self.emptyAnimationNode) self.addSubnode(self.emptyTitleNode) @@ -464,12 +498,18 @@ final class PeerSelectionControllerNode: ASDisplayNode { return nil }, statuses: nil) - self.readyValue.set(self.chatListNode.ready) + if let mainContainerNode = self.mainContainerNode { + self.readyValue.set(mainContainerNode.ready) + } + if let chatListNode = self.chatListNode { + self.readyValue.set(chatListNode.ready) + } } func updatePresentationData(_ presentationData: PresentationData) { self.presentationData = presentationData self.updateThemeAndStrings() + self.mainContainerNode?.updatePresentationData(presentationData) } private func updateChatPresentationInterfaceState(animated: Bool = true, _ f: (ChatPresentationInterfaceState) -> ChatPresentationInterfaceState, completion: @escaping (ContainedViewLayoutTransition) -> Void = { _ in }) { @@ -529,7 +569,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { } else { var selectedPeerIds: [PeerId] = [] var selectedPeerMap: [PeerId: Peer] = [:] - strongSelf.chatListNode.updateState { state in + strongSelf.chatListNode?.updateState { state in selectedPeerIds = Array(state.selectedPeerIds) selectedPeerMap = state.selectedPeerMap.mapValues({ $0._asPeer() }) return state @@ -558,7 +598,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { return ContactListNodeGroupSelectionState() }) } else { - self.chatListNode.updateState { state in + self.chatListNode?.updateState { state in var state = state state.editing = true return state @@ -569,7 +609,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { private func updateThemeAndStrings() { self.backgroundColor = self.presentationData.theme.chatList.backgroundColor self.searchDisplayController?.updatePresentationData(self.presentationData) - self.chatListNode.updateThemeAndStrings(theme: self.presentationData.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true) + self.chatListNode?.updateThemeAndStrings(theme: self.presentationData.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true) self.updateChatPresentationInterfaceState({ $0.updatedTheme(self.presentationData.theme) }) @@ -661,12 +701,19 @@ final class PeerSelectionControllerNode: ASDisplayNode { headerInsets.left += layout.safeInsets.left headerInsets.right += layout.safeInsets.right - self.chatListNode.bounds = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: layout.size.height) - self.chatListNode.position = CGPoint(x: layout.size.width / 2.0, y: layout.size.height / 2.0) + if let chatListNode = self.chatListNode { + chatListNode.bounds = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: layout.size.height) + chatListNode.position = CGPoint(x: layout.size.width / 2.0, y: layout.size.height / 2.0) + } + + if let mainContainerNode = self.mainContainerNode { + transition.updateFrame(node: mainContainerNode, frame: CGRect(origin: CGPoint(), size: layout.size)) + mainContainerNode.update(layout: layout, navigationBarHeight: navigationBarHeight, visualNavigationHeight: actualNavigationBarHeight, originalNavigationHeight: navigationBarHeight, cleanNavigationBarHeight: navigationBarHeight, insets: insets, isReorderingFilters: false, isEditing: false, inlineNavigationLocation: nil, inlineNavigationTransitionFraction: 0.0, transition: transition) + } if let requestPeerType = self.requestPeerType { if self.isEmpty { - self.chatListNode.isHidden = true + self.chatListNode?.isHidden = true self.requirementsBackgroundNode?.isHidden = true self.requirementsTextNode?.isHidden = true self.requirementsSeparatorNode?.isHidden = true @@ -774,7 +821,9 @@ final class PeerSelectionControllerNode: ASDisplayNode { let (duration, curve) = listViewAnimationDurationAndCurve(transition: transition) let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: layout.size, insets: insets, headerInsets: headerInsets, duration: duration, curve: curve) - self.chatListNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, visibleTopInset: updateSizeAndInsets.insets.top, originalTopInset: updateSizeAndInsets.insets.top, inlineNavigationLocation: nil, inlineNavigationTransitionFraction: 0.0) + if let chatListNode = self.chatListNode { + chatListNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, visibleTopInset: updateSizeAndInsets.insets.top, originalTopInset: updateSizeAndInsets.insets.top, inlineNavigationLocation: nil, inlineNavigationTransitionFraction: 0.0) + } if let contactListNode = self.contactListNode { contactListNode.bounds = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: layout.size.height) @@ -795,7 +844,12 @@ final class PeerSelectionControllerNode: ASDisplayNode { return } - if self.chatListNode.supernode != nil { + self.navigationBar?.setSecondaryContentNode(nil, animated: true) + + if self.chatListNode?.supernode != nil || self.mainContainerNode?.supernode != nil { + self.chatListNode?.accessibilityElementsHidden = true + self.mainContainerNode?.accessibilityElementsHidden = true + let chatListLocation: ChatListControllerLocation if let forumPeerId = self.forumPeerId { chatListLocation = .forum(peerId: forumPeerId) @@ -821,7 +875,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { } var updated = false var count = 0 - strongSelf.chatListNode.updateState { state in + strongSelf.chatListNode?.updateState { state in if state.editing { updated = true var state = state @@ -898,6 +952,8 @@ final class PeerSelectionControllerNode: ASDisplayNode { }, placeholder: placeholderNode) } else if let contactListNode = self.contactListNode, contactListNode.supernode != nil { + contactListNode.accessibilityElementsHidden = true + var categories: ContactsSearchCategories = [.cloudContacts] if self.hasGlobalSearch { categories.insert(.global) @@ -968,10 +1024,19 @@ final class PeerSelectionControllerNode: ASDisplayNode { func deactivateSearch(placeholderNode: SearchBarPlaceholderNode) { if let searchDisplayController = self.searchDisplayController { - if self.chatListNode.supernode != nil { + if self.chatListNode?.supernode != nil || self.mainContainerNode?.supernode != nil { + self.chatListNode?.accessibilityElementsHidden = false + self.mainContainerNode?.accessibilityElementsHidden = false + + self.navigationBar?.setSecondaryContentNode(self.controller?.tabContainerNode, animated: true) + self.controller?.setDisplayNavigationBar(true, transition: .animated(duration: 0.5, curve: .spring)) + searchDisplayController.deactivate(placeholder: placeholderNode) self.searchDisplayController = nil } else if let contactListNode = self.contactListNode, contactListNode.supernode != nil { + contactListNode.accessibilityElementsHidden = false + + self.controller?.setDisplayNavigationBar(true, transition: .animated(duration: 0.5, curve: .spring)) searchDisplayController.deactivate(placeholder: placeholderNode) self.searchDisplayController = nil } @@ -979,8 +1044,10 @@ final class PeerSelectionControllerNode: ASDisplayNode { } func scrollToTop() { - if self.chatListNode.supernode != nil { - self.chatListNode.scrollToPosition(.top) + if self.mainContainerNode?.supernode != nil { + self.mainContainerNode?.scrollToTop() + } else if self.chatListNode?.supernode != nil { + self.chatListNode?.scrollToPosition(.top) } else if let contactListNode = self.contactListNode, contactListNode.supernode != nil { //contactListNode.scrollToTop() } @@ -992,10 +1059,20 @@ final class PeerSelectionControllerNode: ASDisplayNode { self.contactListActive = contactListActive if contactListActive { if let contactListNode = self.contactListNode { - self.insertSubnode(contactListNode, aboveSubnode: self.chatListNode) - self.chatListNode.removeFromSupernode() + self.navigationBar?.setSecondaryContentNode(nil, animated: false) + if let chatListNode = self.chatListNode, chatListNode.supernode != nil { + self.insertSubnode(contactListNode, aboveSubnode: chatListNode) + chatListNode.removeFromSupernode() + } else if let mainContainerNode = self.mainContainerNode, mainContainerNode.supernode != nil { + self.insertSubnode(contactListNode, aboveSubnode: mainContainerNode) + mainContainerNode.removeFromSupernode() + } self.recursivelyEnsureDisplaySynchronously(true) contactListNode.enableUpdates = true + + if let (layout, _, _) = self.containerLayout { + self.controller?.containerLayoutUpdated(layout, transition: .immediate) + } } else { let contactListNode = ContactListNode(context: self.context, updatedPresentationData: self.updatedPresentationData, presentation: .single(.natural(options: [], includeChatList: false))) self.contactListNode = contactListNode @@ -1039,26 +1116,54 @@ final class PeerSelectionControllerNode: ASDisplayNode { let _ = (contactListNode.ready |> deliverOnMainQueue).start(next: { [weak self] _ in if let strongSelf = self { + strongSelf.navigationBar?.setSecondaryContentNode(nil, animated: false) if let contactListNode = strongSelf.contactListNode { - strongSelf.insertSubnode(contactListNode, aboveSubnode: strongSelf.chatListNode) + if let chatListNode = strongSelf.chatListNode, chatListNode.supernode != nil { + strongSelf.insertSubnode(contactListNode, aboveSubnode: chatListNode) + chatListNode.removeFromSupernode() + } else if let mainContainerNode = strongSelf.mainContainerNode, mainContainerNode.supernode != nil { + strongSelf.insertSubnode(contactListNode, aboveSubnode: mainContainerNode) + mainContainerNode.removeFromSupernode() + } } - strongSelf.chatListNode.removeFromSupernode() strongSelf.recursivelyEnsureDisplaySynchronously(true) + + if let (layout, _, _) = strongSelf.containerLayout { + strongSelf.controller?.containerLayoutUpdated(layout, transition: .immediate) + } } }) } else { - if let contactListNode = self.contactListNode { - self.insertSubnode(contactListNode, aboveSubnode: self.chatListNode) + self.navigationBar?.setSecondaryContentNode(nil, animated: false) + if let chatListNode = self.chatListNode { + self.insertSubnode(contactListNode, aboveSubnode: chatListNode) + chatListNode.removeFromSupernode() + } else if let mainContainerNode = self.mainContainerNode { + self.insertSubnode(contactListNode, aboveSubnode: mainContainerNode) + mainContainerNode.removeFromSupernode() } - self.chatListNode.removeFromSupernode() self.recursivelyEnsureDisplaySynchronously(true) + + if let (layout, _, _) = self.containerLayout { + self.controller?.containerLayoutUpdated(layout, transition: .immediate) + } } } } else if let contactListNode = self.contactListNode { + self.navigationBar?.setSecondaryContentNode(self.controller?.tabContainerNode, animated: false) contactListNode.enableUpdates = false - self.insertSubnode(self.chatListNode, aboveSubnode: contactListNode) + if let mainContainerNode = self.mainContainerNode { + self.insertSubnode(mainContainerNode, aboveSubnode: contactListNode) + } + if let chatListNode = self.chatListNode { + self.insertSubnode(chatListNode, aboveSubnode: contactListNode) + } contactListNode.removeFromSupernode() + + if let (layout, _, _) = self.containerLayout { + self.controller?.containerLayoutUpdated(layout, transition: .immediate) + } } } } diff --git a/submodules/TelegramUI/Sources/TransformOutgoingMessageMedia.swift b/submodules/TelegramUI/Sources/TransformOutgoingMessageMedia.swift index 2b20f1e525..e9cf655b0f 100644 --- a/submodules/TelegramUI/Sources/TransformOutgoingMessageMedia.swift +++ b/submodules/TelegramUI/Sources/TransformOutgoingMessageMedia.swift @@ -49,13 +49,6 @@ public func transformOutgoingMessageMedia(postbox: Postbox, network: Network, me context.setBlendMode(.copy) drawImage(context: context, image: image.cgImage!, orientation: image.imageOrientation, in: CGRect(origin: CGPoint(), size: size)) }, scale: 1.0), let thumbnailData = scaledImage.jpegData(compressionQuality: 0.6) { - /*if #available(iOSApplicationExtension 11.0, iOS 11.0, *) { - #if DEBUG - if true, let heicData = compressImage(scaledImage, quality: 0.7) { - print("data \(thumbnailData.count), heicData \(heicData.count)") - } - #endif - }*/ let imageDimensions = CGSize(width: image.size.width * image.scale, height: image.size.height * image.scale) let thumbnailResource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))