Various improvements

This commit is contained in:
Ilya Laktyushin 2023-02-14 22:44:44 +04:00
parent 90e79d827e
commit 064fb7ae3e
51 changed files with 707 additions and 447 deletions

View File

@ -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";

View File

@ -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<PresentationData, NoError>)? = 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<PresentationData, NoError>)? = 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

View File

@ -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?) {

View File

@ -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<Bool>()
var ready: Signal<Bool, NoError> {
public var ready: Signal<Bool, NoError> {
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)

View File

@ -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

View File

@ -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)

View File

@ -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<EnginePeer?, NoError>
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

View File

@ -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

View File

@ -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)
}
}

View File

@ -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

View File

@ -507,11 +507,19 @@ public class Window1 {
}
}
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
var popoverDelta: CGFloat = 0.0
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,20 +533,24 @@ 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, *) {
if isWindowed, let _ = minKeyboardY {
screenHeight = strongSelf.windowLayout.size.height
} else {
screenHeight = UIScreen.main.bounds.height
}
} else {
screenHeight = strongSelf.windowLayout.size.height
}
} else {
if let _ = minKeyboardY {
screenHeight = strongSelf.windowLayout.size.height
} else {
if keyboardFrame.minX > 0.0 {
screenHeight = UIScreen.main.bounds.height
@ -546,21 +558,28 @@ public class Window1 {
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)
}
}

View File

@ -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)

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -1,10 +0,0 @@
precision highp float;
varying vec2 varTexcoord;
uniform sampler2D texture;
void main (void)
{
gl_FragColor = texture2D(texture, varTexcoord.st, 0.0);
}

View File

@ -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()) {

View File

@ -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

View File

@ -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))

View File

@ -48,18 +48,26 @@ final class HelloView: UIView, PhoneDemoDecorationView {
private var activePhrases = Set<Int>()
private var activePositions = Set<Int>()
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) {
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)
}
}

View File

@ -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)
}
}

View File

@ -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
}

View File

@ -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()
})
}

View File

@ -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)

View File

@ -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
@ -189,6 +189,8 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
super.init()
self.clipsToBounds = true
self.addSubnode(self.contentNode)
self.contentNode.addSubnode(self.backgroundNode)
@ -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
}

View File

@ -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))

View File

@ -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 {

View File

@ -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<ItemCollectionId>()
for (id, _, _) in view.collectionInfos {
installedCollectionIds.insert(id)
@ -7835,27 +7864,8 @@ 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?

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -544,14 +544,39 @@ 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 item.sending {
result += item.presentationData.strings.VoiceOver_Chat_Sending
} else if item.failed {
result += item.presentationData.strings.VoiceOver_Chat_Failed
} else {
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
} else {
value = ""

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -7927,7 +7927,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
func forwardMessages(messageIds: Set<MessageId>?) {
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

View File

@ -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 {

View File

@ -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
@ -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)
}
})
}
}

View File

@ -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,7 +64,8 @@ final class PeerSelectionControllerNode: ASDisplayNode {
private var forwardAccessoryPanelNode: ForwardAccessoryPanelNode?
var contactListNode: ContactListNode?
let chatListNode: ChatListNode
let chatListNode: ChatListNode?
let mainContainerNode: ChatListContainerNode?
private var contactListActive = false
@ -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,7 +201,19 @@ final class PeerSelectionControllerNode: ASDisplayNode {
chatListMode = .peers(filter: filter, isSelecting: false, additionalCategories: chatListCategories, chatListFilters: nil, displayAutoremoveTimeout: 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()
@ -208,46 +221,62 @@ final class PeerSelectionControllerNode: ASDisplayNode {
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,7 +287,12 @@ 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
@ -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)
}
}
}
}

View File

@ -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))