mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various improvements
This commit is contained in:
parent
90e79d827e
commit
064fb7ae3e
@ -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";
|
||||
|
@ -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
|
||||
|
@ -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?) {
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -507,11 +507,19 @@ public class Window1 {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var popoverDelta: CGFloat = 0.0
|
||||
var minKeyboardY: CGFloat?
|
||||
if #available(iOSApplicationExtension 16.1, iOS 16.1, *), let screen = notification.object as? UIScreen, let keyboardFrameEnd = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect {
|
||||
let fromCoordinateSpace = screen.coordinateSpace
|
||||
let toCoordinateSpace: UICoordinateSpace = strongSelf.hostView.eventView
|
||||
|
||||
let convertedKeyboardFrameEnd = fromCoordinateSpace.convert(keyboardFrameEnd, to: toCoordinateSpace)
|
||||
minKeyboardY = convertedKeyboardFrameEnd.minY
|
||||
}
|
||||
|
||||
var windowedHeightDifference: CGFloat = 0.0
|
||||
|
||||
let screenHeight: CGFloat
|
||||
var inPopover = false
|
||||
var isWindowed = false
|
||||
if keyboardFrame.width.isEqual(to: UIScreen.main.bounds.width) {
|
||||
let screenSize = UIScreen.main.bounds.size
|
||||
var portraitScreenSize = UIScreen.main.bounds.size
|
||||
@ -525,42 +533,53 @@ public class Window1 {
|
||||
|
||||
if strongSelf.windowLayout.size.height != screenSize.height {
|
||||
let heightDelta = screenSize.height - strongSelf.windowLayout.size.height
|
||||
|
||||
let heightDeltaValid = heightDelta > 0.0 && heightDelta < 100.0
|
||||
|
||||
if heightDeltaValid {
|
||||
inPopover = true
|
||||
popoverDelta = heightDelta / 2.0
|
||||
}
|
||||
//if heightDelta > 0.0 && heightDelta < 200.0 {
|
||||
isWindowed = true
|
||||
windowedHeightDifference = heightDelta / 2.0
|
||||
//}
|
||||
}
|
||||
|
||||
if #available(iOSApplicationExtension 13.0, iOS 13.0, *) {
|
||||
screenHeight = UIScreen.main.bounds.height
|
||||
if isWindowed, let _ = minKeyboardY {
|
||||
screenHeight = strongSelf.windowLayout.size.height
|
||||
} else {
|
||||
screenHeight = UIScreen.main.bounds.height
|
||||
}
|
||||
} else {
|
||||
screenHeight = strongSelf.windowLayout.size.height
|
||||
}
|
||||
} else {
|
||||
if keyboardFrame.minX > 0.0 {
|
||||
screenHeight = UIScreen.main.bounds.height
|
||||
if let _ = minKeyboardY {
|
||||
screenHeight = strongSelf.windowLayout.size.height
|
||||
} else {
|
||||
screenHeight = UIScreen.main.bounds.width
|
||||
if keyboardFrame.minX > 0.0 {
|
||||
screenHeight = UIScreen.main.bounds.height
|
||||
} else {
|
||||
screenHeight = UIScreen.main.bounds.width
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var keyboardHeight: CGFloat
|
||||
if keyboardFrame.isEmpty || keyboardFrame.maxY < screenHeight {
|
||||
if inPopover || (isTablet && screenHeight - keyboardFrame.maxY < 5.0) {
|
||||
if isWindowed || (isTablet && screenHeight - keyboardFrame.maxY < 5.0) {
|
||||
if let minKeyboardY {
|
||||
keyboardFrame.origin.y = minKeyboardY
|
||||
}
|
||||
keyboardHeight = max(0.0, screenHeight - keyboardFrame.minY)
|
||||
if inPopover && !keyboardHeight.isZero {
|
||||
keyboardHeight = max(0.0, keyboardHeight - popoverDelta)
|
||||
if isWindowed && !keyboardHeight.isZero, minKeyboardY == nil {
|
||||
keyboardHeight = max(0.0, keyboardHeight - windowedHeightDifference)
|
||||
}
|
||||
} else {
|
||||
keyboardHeight = 0.0
|
||||
}
|
||||
} else {
|
||||
if let minKeyboardY {
|
||||
keyboardFrame.origin.y = minKeyboardY
|
||||
}
|
||||
keyboardHeight = max(0.0, screenHeight - keyboardFrame.minY)
|
||||
if inPopover && !keyboardHeight.isZero {
|
||||
keyboardHeight = max(0.0, keyboardHeight - popoverDelta)
|
||||
if isWindowed && !keyboardHeight.isZero, minKeyboardY == nil {
|
||||
keyboardHeight = max(0.0, keyboardHeight - windowedHeightDifference)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
precision highp float;
|
||||
|
||||
varying vec2 varTexcoord;
|
||||
|
||||
uniform sampler2D texture;
|
||||
|
||||
void main (void)
|
||||
{
|
||||
gl_FragColor = texture2D(texture, varTexcoord.st, 0.0);
|
||||
}
|
@ -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()) {
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
@ -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) {
|
||||
self.setupAnimations()
|
||||
guard self.visible != visible else {
|
||||
return
|
||||
}
|
||||
self.visible = visible
|
||||
|
||||
if visible {
|
||||
self.setupAnimations()
|
||||
} else {
|
||||
self.didSetup = false
|
||||
}
|
||||
|
||||
let transition = ContainedViewLayoutTransition.animated(duration: 0.3, curve: .linear)
|
||||
transition.updateAlpha(layer: self.containerView.layer, alpha: visible ? 1.0 : 0.0, completion: { [weak self] finished in
|
||||
if let strongSelf = self, finished && !visible && !strongSelf.visible {
|
||||
for view in strongSelf.containerView.subviews {
|
||||
view.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func resetAnimation() {
|
||||
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
self.containerView.frame = CGRect(origin: .zero, size: self.frame.size)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
})
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -149,7 +149,7 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
|
||||
private let hapticFeedback = HapticFeedback()
|
||||
|
||||
private var currentData: GroupCallPanelData?
|
||||
private var validLayout: (CGSize, CGFloat, CGFloat)?
|
||||
private var validLayout: (CGSize, CGFloat, CGFloat, Bool)?
|
||||
|
||||
public init(context: AccountContext, presentationData: PresentationData, tapAction: @escaping () -> Void) {
|
||||
self.context = context
|
||||
@ -188,6 +188,8 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
|
||||
self.separatorNode.isLayerBacked = true
|
||||
|
||||
super.init()
|
||||
|
||||
self.clipsToBounds = true
|
||||
|
||||
self.addSubnode(self.contentNode)
|
||||
|
||||
@ -302,8 +304,8 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
|
||||
|
||||
self.updateJoinButton()
|
||||
|
||||
if let (size, leftInset, rightInset) = self.validLayout {
|
||||
self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: .immediate)
|
||||
if let (size, leftInset, rightInset, isHidden) = self.validLayout {
|
||||
self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, isHidden: isHidden, transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
@ -409,8 +411,8 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
|
||||
strongSelf.avatarsContent = strongSelf.avatarsContext.update(peers: summaryState.topParticipants.map { EnginePeer($0.peer) }, animated: false)
|
||||
}
|
||||
|
||||
if let (size, leftInset, rightInset) = strongSelf.validLayout {
|
||||
strongSelf.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: .immediate)
|
||||
if let (size, leftInset, rightInset, isHidden) = strongSelf.validLayout {
|
||||
strongSelf.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, isHidden: isHidden, transition: .immediate)
|
||||
}
|
||||
}))
|
||||
|
||||
@ -427,8 +429,8 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
|
||||
|
||||
strongSelf.callState = callState
|
||||
|
||||
if let (size, leftInset, rightInset) = strongSelf.validLayout {
|
||||
strongSelf.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: transition)
|
||||
if let (size, leftInset, rightInset, isHidden) = strongSelf.validLayout {
|
||||
strongSelf.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, isHidden: isHidden, transition: transition)
|
||||
}
|
||||
}))
|
||||
|
||||
@ -548,16 +550,16 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
self.previewImage = image
|
||||
if let (size, leftInset, rightInset) = self.validLayout {
|
||||
self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: .animated(duration: 0.2, curve: .easeInOut))
|
||||
if let (size, leftInset, rightInset, isHidden) = self.validLayout {
|
||||
self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, isHidden: isHidden, transition: .animated(duration: 0.2, curve: .easeInOut))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if let (size, leftInset, rightInset) = self.validLayout {
|
||||
self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: .animated(duration: 0.2, curve: .easeInOut))
|
||||
if let (size, leftInset, rightInset, isHidden) = self.validLayout {
|
||||
self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, isHidden: isHidden, transition: .animated(duration: 0.2, curve: .easeInOut))
|
||||
}
|
||||
|
||||
if updateAudioLevels {
|
||||
@ -598,14 +600,15 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
|
||||
self.avatarsNode.updateAudioLevels(color: self.theme.chat.inputPanel.actionControlFillColor, backgroundColor: self.theme.chat.inputPanel.actionControlFillColor, levels: levels)
|
||||
}
|
||||
|
||||
public func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = (size, leftInset, rightInset)
|
||||
public func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, isHidden: Bool, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = (size, leftInset, rightInset, isHidden)
|
||||
|
||||
let staticTransition: ContainedViewLayoutTransition = .immediate
|
||||
|
||||
let panelHeight = size.height
|
||||
|
||||
transition.updateFrame(node: self.contentNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||
transition.updateFrame(node: self.contentNode, frame: CGRect(origin: CGPoint(x: 0.0, y: isHidden ? -size.height : 0.0), size: size))
|
||||
transition.updateAlpha(node: self.contentNode, alpha: isHidden ? 0.0 : 1.0)
|
||||
|
||||
self.tapButton.frame = CGRect(origin: CGPoint(), size: CGSize(width: size.width - 7.0 - 36.0 - 7.0, height: panelHeight))
|
||||
|
||||
@ -649,8 +652,8 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
|
||||
|
||||
if self.updateTimer == nil {
|
||||
let timer = SwiftSignalKit.Timer(timeout: 0.5, repeat: true, completion: { [weak self] in
|
||||
if let strongSelf = self, let (size, leftInset, rightInset) = strongSelf.validLayout {
|
||||
strongSelf.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: .immediate)
|
||||
if let strongSelf = self, let (size, leftInset, rightInset, isHidden) = strongSelf.validLayout {
|
||||
strongSelf.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, isHidden: isHidden, transition: .immediate)
|
||||
}
|
||||
}, queue: Queue.mainQueue())
|
||||
self.updateTimer = timer
|
||||
@ -691,7 +694,7 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
|
||||
previewImageNode.cornerRadius = 8.0
|
||||
previewImageNode.contentMode = .scaleAspectFill
|
||||
self.previewImageNode = previewImageNode
|
||||
self.addSubnode(previewImageNode)
|
||||
self.contentNode.addSubnode(previewImageNode)
|
||||
}
|
||||
previewImageNode.image = previewImage
|
||||
let previewSize = CGSize(width: 40.0, height: 40.0)
|
||||
@ -757,13 +760,10 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
|
||||
}
|
||||
|
||||
public func animateIn(_ transition: ContainedViewLayoutTransition) {
|
||||
self.clipsToBounds = true
|
||||
let contentPosition = self.contentNode.layer.position
|
||||
transition.animatePosition(node: self.contentNode, from: CGPoint(x: contentPosition.x, y: contentPosition.y - 50.0), completion: { [weak self] _ in
|
||||
self?.clipsToBounds = false
|
||||
})
|
||||
transition.animatePosition(node: self.contentNode, from: CGPoint(x: contentPosition.x, y: contentPosition.y - 50.0))
|
||||
|
||||
guard let (size, _, _) = self.validLayout else {
|
||||
guard let (size, _, _, _) = self.validLayout else {
|
||||
return
|
||||
}
|
||||
|
||||
@ -771,14 +771,12 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
|
||||
}
|
||||
|
||||
public func animateOut(_ transition: ContainedViewLayoutTransition, completion: @escaping () -> Void) {
|
||||
self.clipsToBounds = true
|
||||
let contentPosition = self.contentNode.layer.position
|
||||
transition.animatePosition(node: self.contentNode, to: CGPoint(x: contentPosition.x, y: contentPosition.y - 50.0), removeOnCompletion: false, completion: { [weak self] _ in
|
||||
self?.clipsToBounds = false
|
||||
transition.animatePosition(node: self.contentNode, to: CGPoint(x: contentPosition.x, y: contentPosition.y - 50.0), removeOnCompletion: false, completion: { _ in
|
||||
completion()
|
||||
})
|
||||
|
||||
guard let (size, _, _) = self.validLayout else {
|
||||
guard let (size, _, _, _) = self.validLayout else {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -127,7 +127,7 @@ private func sendMessageContent(account: Account, peerId: PeerId, attributes: [M
|
||||
case let .media(inputMedia, text):
|
||||
sendMessageRequest = account.network.request(Api.functions.messages.sendMedia(flags: flags, peer: inputPeer, replyToMsgId: replyMessageId, topMsgId: nil, media: inputMedia, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, sendAs: sendAsInputPeer))
|
||||
|> `catch` { _ -> Signal<Api.Updates, NoError> in
|
||||
return .complete()
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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 {
|
||||
|
@ -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,29 +7864,10 @@ public final class EmojiPagerContentComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
if areUnicodeEmojiEnabled {
|
||||
for (subgroupId, list) in staticEmojiMapping {
|
||||
let groupId: AnyHashable = "static"
|
||||
for emojiString in list {
|
||||
let resultItem = EmojiPagerContentComponent.Item(
|
||||
animationData: nil,
|
||||
content: .staticEmoji(emojiString),
|
||||
itemFile: nil,
|
||||
subgroupId: subgroupId.rawValue,
|
||||
icon: .none,
|
||||
tintMode: .none
|
||||
)
|
||||
|
||||
if let groupIndex = itemGroupIndexById[groupId] {
|
||||
itemGroups[groupIndex].items.append(resultItem)
|
||||
} else {
|
||||
itemGroupIndexById[groupId] = itemGroups.count
|
||||
itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: strings.EmojiInput_SectionTitleEmoji, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: nil, isClearable: false, headerItem: nil, items: [resultItem]))
|
||||
}
|
||||
}
|
||||
}
|
||||
if hasPremium {
|
||||
appendUnicodeEmoji()
|
||||
}
|
||||
|
||||
|
||||
var displaySearchWithPlaceholder: String?
|
||||
let searchInitiallyHidden = true
|
||||
if hasSearch {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -544,12 +544,37 @@ final class ChatMessageAccessibilityData {
|
||||
let dateString = DateFormatter.localizedString(from: Date(timeIntervalSince1970: Double(message.timestamp)), dateStyle: .medium, timeStyle: .short)
|
||||
|
||||
result += "\n\(dateString)"
|
||||
if !isIncoming && item.read && !isReply {
|
||||
if !isIncoming && !isReply {
|
||||
result += "\n"
|
||||
if announceIncomingAuthors {
|
||||
result += item.presentationData.strings.VoiceOver_Chat_SeenByRecipients
|
||||
if item.sending {
|
||||
result += item.presentationData.strings.VoiceOver_Chat_Sending
|
||||
} else if item.failed {
|
||||
result += item.presentationData.strings.VoiceOver_Chat_Failed
|
||||
} else {
|
||||
result += item.presentationData.strings.VoiceOver_Chat_SeenByRecipient
|
||||
if item.read {
|
||||
if announceIncomingAuthors {
|
||||
result += item.presentationData.strings.VoiceOver_Chat_SeenByRecipients
|
||||
} else {
|
||||
result += item.presentationData.strings.VoiceOver_Chat_SeenByRecipient
|
||||
}
|
||||
}
|
||||
for attribute in message.attributes {
|
||||
if let attribute = attribute as? ConsumableContentMessageAttribute {
|
||||
if !attribute.consumed {
|
||||
if announceIncomingAuthors {
|
||||
result += item.presentationData.strings.VoiceOver_Chat_NotPlayedByRecipients
|
||||
} else {
|
||||
result += item.presentationData.strings.VoiceOver_Chat_NotPlayedByRecipient
|
||||
}
|
||||
} else {
|
||||
if announceIncomingAuthors {
|
||||
result += item.presentationData.strings.VoiceOver_Chat_PlayedByRecipients
|
||||
} else {
|
||||
result += item.presentationData.strings.VoiceOver_Chat_PlayedByRecipient
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
value = result
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -58,6 +58,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
|
||||
|
||||
private let hasChatListSelector: Bool
|
||||
private let hasContactSelector: Bool
|
||||
private let hasFilters: Bool
|
||||
private let hasGlobalSearch: Bool
|
||||
private let pretendPresentedInModal: Bool
|
||||
private let forwardedMessageIds: [EngineMessage.Id]
|
||||
@ -79,11 +80,18 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
|
||||
}
|
||||
|
||||
private var searchContentNode: NavigationBarSearchContentNode?
|
||||
var tabContainerNode: ChatListFilterTabContainerNode?
|
||||
private var tabContainerData: ([ChatListFilterTabEntry], Bool, Int32?)?
|
||||
|
||||
private let filterDisposable = MetaDisposable()
|
||||
|
||||
private var validLayout: ContainerViewLayout?
|
||||
|
||||
public init(_ params: PeerSelectionControllerParams) {
|
||||
self.context = params.context
|
||||
self.filter = params.filter
|
||||
self.forumPeerId = params.forumPeerId
|
||||
self.hasFilters = params.hasFilters
|
||||
self.hasChatListSelector = params.hasChatListSelector
|
||||
self.hasContactSelector = params.hasContactSelector
|
||||
self.hasGlobalSearch = params.hasGlobalSearch
|
||||
@ -135,7 +143,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
|
||||
}
|
||||
|
||||
self.presentationDataDisposable = ((params.updatedPresentationData?.signal ?? self.context.sharedContext.presentationData)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
|
||||
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
|
||||
if let strongSelf = self {
|
||||
let previousTheme = strongSelf.presentationData.theme
|
||||
let previousStrings = strongSelf.presentationData.strings
|
||||
@ -156,6 +164,36 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
|
||||
if params.multipleSelection {
|
||||
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Select, style: .plain, target: self, action: #selector(self.beginSelection))
|
||||
}
|
||||
|
||||
if params.hasFilters {
|
||||
self.tabContainerNode = ChatListFilterTabContainerNode()
|
||||
self.navigationBar?.setSecondaryContentNode(self.tabContainerNode, animated: false)
|
||||
self.reloadFilters()
|
||||
|
||||
self.peerSelectionNode.mainContainerNode?.currentItemFilterUpdated = { [weak self] filter, fraction, transition, force in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
guard let layout = strongSelf.validLayout else {
|
||||
return
|
||||
}
|
||||
guard let tabContainerData = strongSelf.tabContainerData else {
|
||||
return
|
||||
}
|
||||
if force {
|
||||
strongSelf.tabContainerNode?.cancelAnimations()
|
||||
}
|
||||
strongSelf.tabContainerNode?.update(size: CGSize(width: layout.size.width, height: 46.0), sideInset: layout.safeInsets.left, filters: tabContainerData.0, selectedFilter: filter, isReordering: false, isEditing: false, canReorderAllChats: false, filtersLimit: tabContainerData.2, transitionFraction: fraction, presentationData: strongSelf.presentationData, transition: transition)
|
||||
}
|
||||
|
||||
self.tabContainerNode?.tabSelected = { [weak self] id, _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.selectTab(id: id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
required public init(coder aDecoder: NSCoder) {
|
||||
@ -165,6 +203,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
|
||||
deinit {
|
||||
self.openMessageFromSearchDisposable.dispose()
|
||||
self.presentationDataDisposable?.dispose()
|
||||
self.filterDisposable.dispose()
|
||||
}
|
||||
|
||||
private func updateThemeAndStrings() {
|
||||
@ -177,7 +216,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
self.displayNode = PeerSelectionControllerNode(context: self.context, controller: self, presentationData: self.presentationData, filter: self.filter, forumPeerId: self.forumPeerId, hasChatListSelector: self.hasChatListSelector, hasContactSelector: self.hasContactSelector, hasGlobalSearch: self.hasGlobalSearch, forwardedMessageIds: self.forwardedMessageIds, hasTypeHeaders: self.hasTypeHeaders, requestPeerType: self.requestPeerType, createNewGroup: self.createNewGroup, present: { [weak self] c, a in
|
||||
self.displayNode = PeerSelectionControllerNode(context: self.context, controller: self, presentationData: self.presentationData, filter: self.filter, forumPeerId: self.forumPeerId, hasFilters: self.hasFilters, hasChatListSelector: self.hasChatListSelector, hasContactSelector: self.hasContactSelector, hasGlobalSearch: self.hasGlobalSearch, forwardedMessageIds: self.forwardedMessageIds, hasTypeHeaders: self.hasTypeHeaders, requestPeerType: self.requestPeerType, createNewGroup: self.createNewGroup, present: { [weak self] c, a in
|
||||
self?.present(c, in: .window(.root), with: a)
|
||||
}, presentInGlobalOverlay: { [weak self] c, a in
|
||||
self?.presentInGlobalOverlay(c, with: a)
|
||||
@ -208,6 +247,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
|
||||
updatedPresentationData: nil,
|
||||
filter: strongSelf.filter,
|
||||
forumPeerId: peer.id,
|
||||
hasFilters: false,
|
||||
hasChatListSelector: false,
|
||||
hasContactSelector: false,
|
||||
hasGlobalSearch: false,
|
||||
@ -247,6 +287,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
|
||||
updatedPresentationData: nil,
|
||||
filter: strongSelf.filter,
|
||||
forumPeerId: peer.id,
|
||||
hasFilters: false,
|
||||
hasChatListSelector: false,
|
||||
hasContactSelector: false,
|
||||
hasGlobalSearch: false,
|
||||
@ -295,10 +336,25 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
|
||||
self._ready.set(self.peerSelectionNode.ready)
|
||||
}
|
||||
|
||||
public override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
self.peerSelectionNode.mainContainerNode?.updateEnableAdjacentFilterLoading(true)
|
||||
}
|
||||
|
||||
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = layout
|
||||
|
||||
super.containerLayoutUpdated(layout, transition: transition)
|
||||
|
||||
self.peerSelectionNode.containerLayoutUpdated(layout, navigationBarHeight: self.cleanNavigationHeight, actualNavigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition)
|
||||
|
||||
if let tabContainerNode = self.tabContainerNode, let mainContainerNode = self.peerSelectionNode.mainContainerNode {
|
||||
let tabContainerOffset: CGFloat = 0.0
|
||||
let navigationBarHeight = self.navigationBar?.frame.maxY ?? 0.0
|
||||
transition.updateFrame(node: tabContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: navigationBarHeight - self.additionalNavigationBarHeight - 46.0 + tabContainerOffset), size: CGSize(width: layout.size.width, height: 46.0)))
|
||||
tabContainerNode.update(size: CGSize(width: layout.size.width, height: 46.0), sideInset: layout.safeInsets.left, filters: self.tabContainerData?.0 ?? [], selectedFilter: mainContainerNode.currentItemFilter, isReordering: false, isEditing: false, canReorderAllChats: false, filtersLimit: self.tabContainerData?.2, transitionFraction: mainContainerNode.transitionFraction, presentationData: self.presentationData, transition: .animated(duration: 0.4, curve: .spring))
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func beginSelection() {
|
||||
@ -328,10 +384,178 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
|
||||
|
||||
private func deactivateSearch() {
|
||||
if !self.displayNavigationBar {
|
||||
self.setDisplayNavigationBar(true, transition: .animated(duration: 0.5, curve: .spring))
|
||||
if let searchContentNode = self.searchContentNode {
|
||||
self.peerSelectionNode.deactivateSearch(placeholderNode: searchContentNode.placeholderNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var initializedFilters = false
|
||||
private func reloadFilters(firstUpdate: (() -> Void)? = nil) {
|
||||
let filterItems = chatListFilterItems(context: self.context)
|
||||
var notifiedFirstUpdate = false
|
||||
self.filterDisposable.set((combineLatest(queue: .mainQueue(),
|
||||
filterItems,
|
||||
self.context.account.postbox.peerView(id: self.context.account.peerId),
|
||||
self.context.engine.data.get(TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: false))
|
||||
)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] countAndFilterItems, peerView, limits in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
let isPremium = peerView.peers[peerView.peerId]?.isPremium
|
||||
|
||||
let (_, items) = countAndFilterItems
|
||||
var filterItems: [ChatListFilterTabEntry] = []
|
||||
|
||||
for (filter, unreadCount, hasUnmutedUnread) in items {
|
||||
switch filter {
|
||||
case .allChats:
|
||||
if let isPremium = isPremium, !isPremium && filterItems.count > 0 {
|
||||
filterItems.insert(.all(unreadCount: 0), at: 0)
|
||||
} else {
|
||||
filterItems.append(.all(unreadCount: 0))
|
||||
}
|
||||
case let .filter(id, title, _, _):
|
||||
filterItems.append(.filter(id: id, text: title, unread: ChatListFilterTabEntryUnreadCount(value: unreadCount, hasUnmuted: hasUnmutedUnread)))
|
||||
}
|
||||
}
|
||||
|
||||
let resolvedItems = filterItems
|
||||
|
||||
var wasEmpty = false
|
||||
if let tabContainerData = strongSelf.tabContainerData {
|
||||
wasEmpty = tabContainerData.0.count <= 1 || tabContainerData.1
|
||||
} else {
|
||||
wasEmpty = true
|
||||
}
|
||||
|
||||
let firstItem = countAndFilterItems.1.first?.0 ?? .allChats
|
||||
let firstItemEntryId: ChatListFilterTabEntryId
|
||||
switch firstItem {
|
||||
case .allChats:
|
||||
firstItemEntryId = .all
|
||||
case let .filter(id, _, _, _):
|
||||
firstItemEntryId = .filter(id)
|
||||
}
|
||||
|
||||
var selectedEntryId = !strongSelf.initializedFilters ? firstItemEntryId : (strongSelf.peerSelectionNode.mainContainerNode?.currentItemFilter ?? .all)
|
||||
var resetCurrentEntry = false
|
||||
if !resolvedItems.contains(where: { $0.id == selectedEntryId }) {
|
||||
resetCurrentEntry = true
|
||||
if let tabContainerData = strongSelf.tabContainerData {
|
||||
var found = false
|
||||
if let index = tabContainerData.0.firstIndex(where: { $0.id == selectedEntryId }) {
|
||||
for i in (0 ..< index - 1).reversed() {
|
||||
if resolvedItems.contains(where: { $0.id == tabContainerData.0[i].id }) {
|
||||
selectedEntryId = tabContainerData.0[i].id
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
selectedEntryId = .all
|
||||
}
|
||||
} else {
|
||||
selectedEntryId = .all
|
||||
}
|
||||
}
|
||||
let filtersLimit = isPremium == false ? limits.maxFoldersCount : nil
|
||||
strongSelf.tabContainerData = (resolvedItems, false, filtersLimit)
|
||||
var availableFilters: [ChatListContainerNodeFilter] = []
|
||||
var hasAllChats = false
|
||||
for item in items {
|
||||
switch item.0 {
|
||||
case .allChats:
|
||||
hasAllChats = true
|
||||
if let isPremium = isPremium, !isPremium && availableFilters.count > 0 {
|
||||
availableFilters.insert(.all, at: 0)
|
||||
} else {
|
||||
availableFilters.append(.all)
|
||||
}
|
||||
case .filter:
|
||||
availableFilters.append(.filter(item.0))
|
||||
}
|
||||
}
|
||||
if !hasAllChats {
|
||||
availableFilters.insert(.all, at: 0)
|
||||
}
|
||||
strongSelf.peerSelectionNode.mainContainerNode?.updateAvailableFilters(availableFilters, limit: filtersLimit)
|
||||
|
||||
// if isPremium == nil && items.isEmpty {
|
||||
// strongSelf.ready.set(strongSelf.chatListDisplayNode.mainContainerNode.currentItemNode.ready)
|
||||
// } else if !strongSelf.initializedFilters {
|
||||
// if selectedEntryId != strongSelf.chatListDisplayNode.mainContainerNode.currentItemFilter {
|
||||
// strongSelf.chatListDisplayNode.mainContainerNode.switchToFilter(id: selectedEntryId, animated: false, completion: { [weak self] in
|
||||
// if let strongSelf = self {
|
||||
// strongSelf.ready.set(strongSelf.chatListDisplayNode.mainContainerNode.currentItemNode.ready)
|
||||
// }
|
||||
// })
|
||||
// } else {
|
||||
// strongSelf.ready.set(strongSelf.chatListDisplayNode.mainContainerNode.currentItemNode.ready)
|
||||
// }
|
||||
// strongSelf.initializedFilters = true
|
||||
// }
|
||||
strongSelf.initializedFilters = true
|
||||
|
||||
let isEmpty = resolvedItems.count <= 1
|
||||
|
||||
if wasEmpty != isEmpty, strongSelf.displayNavigationBar {
|
||||
strongSelf.navigationBar?.setSecondaryContentNode(isEmpty ? nil : strongSelf.tabContainerNode, animated: false)
|
||||
}
|
||||
|
||||
if let layout = strongSelf.validLayout {
|
||||
if wasEmpty != isEmpty {
|
||||
strongSelf.containerLayoutUpdated(layout, transition: .immediate)
|
||||
} else {
|
||||
strongSelf.tabContainerNode?.update(size: CGSize(width: layout.size.width, height: 46.0), sideInset: layout.safeInsets.left, filters: resolvedItems, selectedFilter: selectedEntryId, isReordering: false, isEditing: false, canReorderAllChats: false, filtersLimit: filtersLimit, transitionFraction: 0.0, presentationData: strongSelf.presentationData, transition: .animated(duration: 0.4, curve: .spring))
|
||||
}
|
||||
}
|
||||
|
||||
if !notifiedFirstUpdate {
|
||||
notifiedFirstUpdate = true
|
||||
firstUpdate?()
|
||||
}
|
||||
|
||||
if resetCurrentEntry {
|
||||
//strongSelf.selectTab(id: selectedEntryId)
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
private func selectTab(id: ChatListFilterTabEntryId) {
|
||||
let _ = (self.context.engine.peers.currentChatListFilters()
|
||||
|> deliverOnMainQueue).start(next: { [weak self] filters in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let updatedFilter: ChatListFilter?
|
||||
switch id {
|
||||
case .all:
|
||||
updatedFilter = nil
|
||||
case let .filter(id):
|
||||
var found = false
|
||||
var foundValue: ChatListFilter?
|
||||
for filter in filters {
|
||||
if filter.id == id {
|
||||
foundValue = filter
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if found {
|
||||
updatedFilter = foundValue
|
||||
} else {
|
||||
updatedFilter = nil
|
||||
}
|
||||
}
|
||||
if strongSelf.peerSelectionNode.mainContainerNode?.currentItemNode.chatListFilter?.id == updatedFilter?.id {
|
||||
strongSelf.scrollToTop?()
|
||||
} else {
|
||||
strongSelf.peerSelectionNode.mainContainerNode?.switchToFilter(id: updatedFilter.flatMap { .filter($0.id) } ?? .all)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ import SolidRoundedButtonNode
|
||||
|
||||
final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
private let context: AccountContext
|
||||
private weak var controller: PeerSelectionController?
|
||||
private weak var controller: PeerSelectionControllerImpl?
|
||||
private let present: (ViewController, Any?) -> Void
|
||||
private let presentInGlobalOverlay: (ViewController, Any?) -> Void
|
||||
private let dismiss: () -> Void
|
||||
@ -64,8 +64,9 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
private var forwardAccessoryPanelNode: ForwardAccessoryPanelNode?
|
||||
|
||||
var contactListNode: ContactListNode?
|
||||
let chatListNode: ChatListNode
|
||||
|
||||
let chatListNode: ChatListNode?
|
||||
let mainContainerNode: ChatListContainerNode?
|
||||
|
||||
private var contactListActive = false
|
||||
|
||||
private var searchDisplayController: SearchDisplayController?
|
||||
@ -104,7 +105,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
return (self.presentationData, self.presentationDataPromise.get())
|
||||
}
|
||||
|
||||
init(context: AccountContext, controller: PeerSelectionController, presentationData: PresentationData, filter: ChatListNodePeersFilter, forumPeerId: EnginePeer.Id?, hasChatListSelector: Bool, hasContactSelector: Bool, hasGlobalSearch: Bool, forwardedMessageIds: [EngineMessage.Id], hasTypeHeaders: Bool, requestPeerType: ReplyMarkupButtonRequestPeerType?, createNewGroup: (() -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, dismiss: @escaping () -> Void) {
|
||||
init(context: AccountContext, controller: PeerSelectionControllerImpl, presentationData: PresentationData, filter: ChatListNodePeersFilter, forumPeerId: EnginePeer.Id?, hasFilters: Bool, hasChatListSelector: Bool, hasContactSelector: Bool, hasGlobalSearch: Bool, forwardedMessageIds: [EngineMessage.Id], hasTypeHeaders: Bool, requestPeerType: ReplyMarkupButtonRequestPeerType?, createNewGroup: (() -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, dismiss: @escaping () -> Void) {
|
||||
self.context = context
|
||||
self.controller = controller
|
||||
self.present = present
|
||||
@ -200,54 +201,82 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
chatListMode = .peers(filter: filter, isSelecting: false, additionalCategories: chatListCategories, chatListFilters: nil, displayAutoremoveTimeout: false)
|
||||
}
|
||||
|
||||
self.chatListNode = ChatListNode(context: context, location: chatListLocation, previewing: false, fillPreloadItems: false, mode: chatListMode, theme: self.presentationData.theme, fontSize: presentationData.listsFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameSortOrder: presentationData.nameSortOrder, nameDisplayOrder: presentationData.nameDisplayOrder, animationCache: self.animationCache, animationRenderer: self.animationRenderer, disableAnimations: true, isInlineMode: false)
|
||||
|
||||
if hasFilters {
|
||||
self.mainContainerNode = ChatListContainerNode(context: context, location: chatListLocation, chatListMode: chatListMode, previewing: false, controlsHistoryPreload: false, isInlineMode: false, presentationData: presentationData, animationCache: self.animationCache, animationRenderer: self.animationRenderer, filterBecameEmpty: { _ in
|
||||
//filterBecameEmpty?(filter)
|
||||
}, filterEmptyAction: { _ in
|
||||
//filterEmptyAction?(filter)
|
||||
}, secondaryEmptyAction: {
|
||||
|
||||
})
|
||||
self.chatListNode = nil
|
||||
} else {
|
||||
self.mainContainerNode = nil
|
||||
self.chatListNode = ChatListNode(context: context, location: chatListLocation, previewing: false, fillPreloadItems: false, mode: chatListMode, theme: self.presentationData.theme, fontSize: presentationData.listsFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameSortOrder: presentationData.nameSortOrder, nameDisplayOrder: presentationData.nameDisplayOrder, animationCache: self.animationCache, animationRenderer: self.animationRenderer, disableAnimations: true, isInlineMode: false)
|
||||
}
|
||||
|
||||
super.init()
|
||||
|
||||
self.setViewBlock({
|
||||
return UITracingLayerView()
|
||||
})
|
||||
|
||||
self.chatListNode.additionalCategorySelected = { _ in
|
||||
self.chatListNode?.additionalCategorySelected = { _ in
|
||||
createNewGroup?()
|
||||
}
|
||||
|
||||
|
||||
self.backgroundColor = self.presentationData.theme.chatList.backgroundColor
|
||||
|
||||
self.chatListNode.selectionCountChanged = { [weak self] count in
|
||||
self.chatListNode?.selectionCountChanged = { [weak self] count in
|
||||
self?.textInputPanelNode?.updateSendButtonEnabled(count > 0, animated: true)
|
||||
}
|
||||
self.chatListNode.accessibilityPageScrolledString = { row, count in
|
||||
self.chatListNode?.accessibilityPageScrolledString = { row, count in
|
||||
return presentationData.strings.VoiceOver_ScrollStatus(row, count).string
|
||||
}
|
||||
|
||||
self.chatListNode.activateSearch = { [weak self] in
|
||||
self.chatListNode?.activateSearch = { [weak self] in
|
||||
self?.requestActivateSearch?()
|
||||
}
|
||||
self.mainContainerNode?.activateSearch = { [weak self] in
|
||||
self?.requestActivateSearch?()
|
||||
}
|
||||
|
||||
self.chatListNode.peerSelected = { [weak self] peer, threadId, _, _, _ in
|
||||
self?.chatListNode.clearHighlightAnimated(true)
|
||||
self.chatListNode?.peerSelected = { [weak self] peer, threadId, _, _, _ in
|
||||
self?.chatListNode?.clearHighlightAnimated(true)
|
||||
self?.requestOpenPeer?(peer._asPeer(), threadId)
|
||||
}
|
||||
self.mainContainerNode?.peerSelected = { [weak self] peer, threadId, _, _, _ in
|
||||
self?.chatListNode?.clearHighlightAnimated(true)
|
||||
self?.requestOpenPeer?(peer._asPeer(), threadId)
|
||||
}
|
||||
|
||||
self.chatListNode.disabledPeerSelected = { [weak self] peer, threadId in
|
||||
self.chatListNode?.disabledPeerSelected = { [weak self] peer, threadId in
|
||||
self?.requestOpenDisabledPeer?(peer._asPeer(), threadId)
|
||||
}
|
||||
|
||||
self.chatListNode.contentOffsetChanged = { [weak self] offset in
|
||||
self.chatListNode?.contentOffsetChanged = { [weak self] offset in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if strongSelf.chatListNode.supernode != nil {
|
||||
if strongSelf.chatListNode?.supernode != nil {
|
||||
strongSelf.contentOffsetChanged?(offset)
|
||||
}
|
||||
}
|
||||
|
||||
self.chatListNode.contentScrollingEnded = { [weak self] listView in
|
||||
self.mainContainerNode?.contentOffsetChanged = { [weak self] offset in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if strongSelf.chatListNode?.supernode != nil {
|
||||
strongSelf.contentOffsetChanged?(offset)
|
||||
}
|
||||
}
|
||||
|
||||
self.chatListNode?.contentScrollingEnded = { [weak self] listView in
|
||||
return self?.contentScrollingEnded?(listView) ?? false
|
||||
}
|
||||
|
||||
self.chatListNode.isEmptyUpdated = { [weak self] state, _, _ in
|
||||
self.chatListNode?.isEmptyUpdated = { [weak self] state, _, _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
@ -258,8 +287,13 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
self.addSubnode(self.chatListNode)
|
||||
|
||||
if let mainContainerNode = self.mainContainerNode {
|
||||
self.addSubnode(mainContainerNode)
|
||||
}
|
||||
if let chatListNode = self.chatListNode {
|
||||
self.addSubnode(chatListNode)
|
||||
}
|
||||
|
||||
if hasChatListSelector && hasContactSelector {
|
||||
self.segmentedControlNode!.selectedIndexChanged = { [weak self] index in
|
||||
self?.indexChanged(index)
|
||||
@ -271,9 +305,9 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
if let requirementsBackgroundNode = self.requirementsBackgroundNode, let requirementsSeparatorNode = self.requirementsSeparatorNode, let requirementsTextNode = self.requirementsTextNode {
|
||||
self.chatListNode.addSubnode(requirementsBackgroundNode)
|
||||
self.chatListNode.addSubnode(requirementsSeparatorNode)
|
||||
self.chatListNode.addSubnode(requirementsTextNode)
|
||||
self.chatListNode?.addSubnode(requirementsBackgroundNode)
|
||||
self.chatListNode?.addSubnode(requirementsSeparatorNode)
|
||||
self.chatListNode?.addSubnode(requirementsTextNode)
|
||||
|
||||
self.addSubnode(self.emptyAnimationNode)
|
||||
self.addSubnode(self.emptyTitleNode)
|
||||
@ -464,12 +498,18 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
return nil
|
||||
}, statuses: nil)
|
||||
|
||||
self.readyValue.set(self.chatListNode.ready)
|
||||
if let mainContainerNode = self.mainContainerNode {
|
||||
self.readyValue.set(mainContainerNode.ready)
|
||||
}
|
||||
if let chatListNode = self.chatListNode {
|
||||
self.readyValue.set(chatListNode.ready)
|
||||
}
|
||||
}
|
||||
|
||||
func updatePresentationData(_ presentationData: PresentationData) {
|
||||
self.presentationData = presentationData
|
||||
self.updateThemeAndStrings()
|
||||
self.mainContainerNode?.updatePresentationData(presentationData)
|
||||
}
|
||||
|
||||
private func updateChatPresentationInterfaceState(animated: Bool = true, _ f: (ChatPresentationInterfaceState) -> ChatPresentationInterfaceState, completion: @escaping (ContainedViewLayoutTransition) -> Void = { _ in }) {
|
||||
@ -529,7 +569,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
} else {
|
||||
var selectedPeerIds: [PeerId] = []
|
||||
var selectedPeerMap: [PeerId: Peer] = [:]
|
||||
strongSelf.chatListNode.updateState { state in
|
||||
strongSelf.chatListNode?.updateState { state in
|
||||
selectedPeerIds = Array(state.selectedPeerIds)
|
||||
selectedPeerMap = state.selectedPeerMap.mapValues({ $0._asPeer() })
|
||||
return state
|
||||
@ -558,7 +598,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
return ContactListNodeGroupSelectionState()
|
||||
})
|
||||
} else {
|
||||
self.chatListNode.updateState { state in
|
||||
self.chatListNode?.updateState { state in
|
||||
var state = state
|
||||
state.editing = true
|
||||
return state
|
||||
@ -569,7 +609,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
private func updateThemeAndStrings() {
|
||||
self.backgroundColor = self.presentationData.theme.chatList.backgroundColor
|
||||
self.searchDisplayController?.updatePresentationData(self.presentationData)
|
||||
self.chatListNode.updateThemeAndStrings(theme: self.presentationData.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true)
|
||||
self.chatListNode?.updateThemeAndStrings(theme: self.presentationData.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true)
|
||||
|
||||
self.updateChatPresentationInterfaceState({ $0.updatedTheme(self.presentationData.theme) })
|
||||
|
||||
@ -661,12 +701,19 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
headerInsets.left += layout.safeInsets.left
|
||||
headerInsets.right += layout.safeInsets.right
|
||||
|
||||
self.chatListNode.bounds = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: layout.size.height)
|
||||
self.chatListNode.position = CGPoint(x: layout.size.width / 2.0, y: layout.size.height / 2.0)
|
||||
if let chatListNode = self.chatListNode {
|
||||
chatListNode.bounds = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: layout.size.height)
|
||||
chatListNode.position = CGPoint(x: layout.size.width / 2.0, y: layout.size.height / 2.0)
|
||||
}
|
||||
|
||||
if let mainContainerNode = self.mainContainerNode {
|
||||
transition.updateFrame(node: mainContainerNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
mainContainerNode.update(layout: layout, navigationBarHeight: navigationBarHeight, visualNavigationHeight: actualNavigationBarHeight, originalNavigationHeight: navigationBarHeight, cleanNavigationBarHeight: navigationBarHeight, insets: insets, isReorderingFilters: false, isEditing: false, inlineNavigationLocation: nil, inlineNavigationTransitionFraction: 0.0, transition: transition)
|
||||
}
|
||||
|
||||
if let requestPeerType = self.requestPeerType {
|
||||
if self.isEmpty {
|
||||
self.chatListNode.isHidden = true
|
||||
self.chatListNode?.isHidden = true
|
||||
self.requirementsBackgroundNode?.isHidden = true
|
||||
self.requirementsTextNode?.isHidden = true
|
||||
self.requirementsSeparatorNode?.isHidden = true
|
||||
@ -774,7 +821,9 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
let (duration, curve) = listViewAnimationDurationAndCurve(transition: transition)
|
||||
let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: layout.size, insets: insets, headerInsets: headerInsets, duration: duration, curve: curve)
|
||||
|
||||
self.chatListNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, visibleTopInset: updateSizeAndInsets.insets.top, originalTopInset: updateSizeAndInsets.insets.top, inlineNavigationLocation: nil, inlineNavigationTransitionFraction: 0.0)
|
||||
if let chatListNode = self.chatListNode {
|
||||
chatListNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, visibleTopInset: updateSizeAndInsets.insets.top, originalTopInset: updateSizeAndInsets.insets.top, inlineNavigationLocation: nil, inlineNavigationTransitionFraction: 0.0)
|
||||
}
|
||||
|
||||
if let contactListNode = self.contactListNode {
|
||||
contactListNode.bounds = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: layout.size.height)
|
||||
@ -795,7 +844,12 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
return
|
||||
}
|
||||
|
||||
if self.chatListNode.supernode != nil {
|
||||
self.navigationBar?.setSecondaryContentNode(nil, animated: true)
|
||||
|
||||
if self.chatListNode?.supernode != nil || self.mainContainerNode?.supernode != nil {
|
||||
self.chatListNode?.accessibilityElementsHidden = true
|
||||
self.mainContainerNode?.accessibilityElementsHidden = true
|
||||
|
||||
let chatListLocation: ChatListControllerLocation
|
||||
if let forumPeerId = self.forumPeerId {
|
||||
chatListLocation = .forum(peerId: forumPeerId)
|
||||
@ -821,7 +875,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
}
|
||||
var updated = false
|
||||
var count = 0
|
||||
strongSelf.chatListNode.updateState { state in
|
||||
strongSelf.chatListNode?.updateState { state in
|
||||
if state.editing {
|
||||
updated = true
|
||||
var state = state
|
||||
@ -898,6 +952,8 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
}, placeholder: placeholderNode)
|
||||
|
||||
} else if let contactListNode = self.contactListNode, contactListNode.supernode != nil {
|
||||
contactListNode.accessibilityElementsHidden = true
|
||||
|
||||
var categories: ContactsSearchCategories = [.cloudContacts]
|
||||
if self.hasGlobalSearch {
|
||||
categories.insert(.global)
|
||||
@ -968,10 +1024,19 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
|
||||
func deactivateSearch(placeholderNode: SearchBarPlaceholderNode) {
|
||||
if let searchDisplayController = self.searchDisplayController {
|
||||
if self.chatListNode.supernode != nil {
|
||||
if self.chatListNode?.supernode != nil || self.mainContainerNode?.supernode != nil {
|
||||
self.chatListNode?.accessibilityElementsHidden = false
|
||||
self.mainContainerNode?.accessibilityElementsHidden = false
|
||||
|
||||
self.navigationBar?.setSecondaryContentNode(self.controller?.tabContainerNode, animated: true)
|
||||
self.controller?.setDisplayNavigationBar(true, transition: .animated(duration: 0.5, curve: .spring))
|
||||
|
||||
searchDisplayController.deactivate(placeholder: placeholderNode)
|
||||
self.searchDisplayController = nil
|
||||
} else if let contactListNode = self.contactListNode, contactListNode.supernode != nil {
|
||||
contactListNode.accessibilityElementsHidden = false
|
||||
|
||||
self.controller?.setDisplayNavigationBar(true, transition: .animated(duration: 0.5, curve: .spring))
|
||||
searchDisplayController.deactivate(placeholder: placeholderNode)
|
||||
self.searchDisplayController = nil
|
||||
}
|
||||
@ -979,8 +1044,10 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
func scrollToTop() {
|
||||
if self.chatListNode.supernode != nil {
|
||||
self.chatListNode.scrollToPosition(.top)
|
||||
if self.mainContainerNode?.supernode != nil {
|
||||
self.mainContainerNode?.scrollToTop()
|
||||
} else if self.chatListNode?.supernode != nil {
|
||||
self.chatListNode?.scrollToPosition(.top)
|
||||
} else if let contactListNode = self.contactListNode, contactListNode.supernode != nil {
|
||||
//contactListNode.scrollToTop()
|
||||
}
|
||||
@ -992,10 +1059,20 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
self.contactListActive = contactListActive
|
||||
if contactListActive {
|
||||
if let contactListNode = self.contactListNode {
|
||||
self.insertSubnode(contactListNode, aboveSubnode: self.chatListNode)
|
||||
self.chatListNode.removeFromSupernode()
|
||||
self.navigationBar?.setSecondaryContentNode(nil, animated: false)
|
||||
if let chatListNode = self.chatListNode, chatListNode.supernode != nil {
|
||||
self.insertSubnode(contactListNode, aboveSubnode: chatListNode)
|
||||
chatListNode.removeFromSupernode()
|
||||
} else if let mainContainerNode = self.mainContainerNode, mainContainerNode.supernode != nil {
|
||||
self.insertSubnode(contactListNode, aboveSubnode: mainContainerNode)
|
||||
mainContainerNode.removeFromSupernode()
|
||||
}
|
||||
self.recursivelyEnsureDisplaySynchronously(true)
|
||||
contactListNode.enableUpdates = true
|
||||
|
||||
if let (layout, _, _) = self.containerLayout {
|
||||
self.controller?.containerLayoutUpdated(layout, transition: .immediate)
|
||||
}
|
||||
} else {
|
||||
let contactListNode = ContactListNode(context: self.context, updatedPresentationData: self.updatedPresentationData, presentation: .single(.natural(options: [], includeChatList: false)))
|
||||
self.contactListNode = contactListNode
|
||||
@ -1039,26 +1116,54 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
|
||||
let _ = (contactListNode.ready |> deliverOnMainQueue).start(next: { [weak self] _ in
|
||||
if let strongSelf = self {
|
||||
strongSelf.navigationBar?.setSecondaryContentNode(nil, animated: false)
|
||||
if let contactListNode = strongSelf.contactListNode {
|
||||
strongSelf.insertSubnode(contactListNode, aboveSubnode: strongSelf.chatListNode)
|
||||
if let chatListNode = strongSelf.chatListNode, chatListNode.supernode != nil {
|
||||
strongSelf.insertSubnode(contactListNode, aboveSubnode: chatListNode)
|
||||
chatListNode.removeFromSupernode()
|
||||
} else if let mainContainerNode = strongSelf.mainContainerNode, mainContainerNode.supernode != nil {
|
||||
strongSelf.insertSubnode(contactListNode, aboveSubnode: mainContainerNode)
|
||||
mainContainerNode.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
strongSelf.chatListNode.removeFromSupernode()
|
||||
strongSelf.recursivelyEnsureDisplaySynchronously(true)
|
||||
|
||||
if let (layout, _, _) = strongSelf.containerLayout {
|
||||
strongSelf.controller?.containerLayoutUpdated(layout, transition: .immediate)
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
if let contactListNode = self.contactListNode {
|
||||
self.insertSubnode(contactListNode, aboveSubnode: self.chatListNode)
|
||||
self.navigationBar?.setSecondaryContentNode(nil, animated: false)
|
||||
if let chatListNode = self.chatListNode {
|
||||
self.insertSubnode(contactListNode, aboveSubnode: chatListNode)
|
||||
chatListNode.removeFromSupernode()
|
||||
} else if let mainContainerNode = self.mainContainerNode {
|
||||
self.insertSubnode(contactListNode, aboveSubnode: mainContainerNode)
|
||||
mainContainerNode.removeFromSupernode()
|
||||
}
|
||||
self.chatListNode.removeFromSupernode()
|
||||
self.recursivelyEnsureDisplaySynchronously(true)
|
||||
|
||||
if let (layout, _, _) = self.containerLayout {
|
||||
self.controller?.containerLayoutUpdated(layout, transition: .immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let contactListNode = self.contactListNode {
|
||||
self.navigationBar?.setSecondaryContentNode(self.controller?.tabContainerNode, animated: false)
|
||||
contactListNode.enableUpdates = false
|
||||
|
||||
self.insertSubnode(self.chatListNode, aboveSubnode: contactListNode)
|
||||
if let mainContainerNode = self.mainContainerNode {
|
||||
self.insertSubnode(mainContainerNode, aboveSubnode: contactListNode)
|
||||
}
|
||||
if let chatListNode = self.chatListNode {
|
||||
self.insertSubnode(chatListNode, aboveSubnode: contactListNode)
|
||||
}
|
||||
contactListNode.removeFromSupernode()
|
||||
|
||||
if let (layout, _, _) = self.containerLayout {
|
||||
self.controller?.containerLayoutUpdated(layout, transition: .immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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))
|
||||
|
Loading…
x
Reference in New Issue
Block a user