diff --git a/Telegram/SiriIntents/IntentHandler.swift b/Telegram/SiriIntents/IntentHandler.swift index 46395e888b..b0381122af 100644 --- a/Telegram/SiriIntents/IntentHandler.swift +++ b/Telegram/SiriIntents/IntentHandler.swift @@ -815,7 +815,7 @@ class DefaultIntentHandler: INExtension, INSendMessageIntentHandling, INSearchFo if let searchTerm = searchTerm { if !searchTerm.isEmpty { - for renderedPeer in transaction.searchPeers(query: searchTerm) { + for renderedPeer in transaction.searchPeers(query: searchTerm, predicate: nil) { if let peer = renderedPeer.peer, !(peer is TelegramSecretChat), !peer.isDeleted { peers.append(peer) } @@ -988,7 +988,7 @@ private final class WidgetIntentHandler { if let searchTerm = searchTerm { if !searchTerm.isEmpty { - for renderedPeer in transaction.searchPeers(query: searchTerm) { + for renderedPeer in transaction.searchPeers(query: searchTerm, predicate: nil) { if let peer = renderedPeer.peer, !(peer is TelegramSecretChat), !peer.isDeleted { peers.append(peer) } diff --git a/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift b/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift index bc640f69bf..70780df037 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift @@ -34,7 +34,7 @@ private enum InnerState: Equatable { public final class AuthorizationSequenceController: NavigationController, ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding { static func navigationBarTheme(_ theme: PresentationTheme) -> NavigationBarTheme { - return NavigationBarTheme(overallDarkAppearance: theme.overallDarkAppearance, buttonColor: theme.chat.inputPanel.panelControlColor, disabledButtonColor: theme.intro.disabledTextColor, primaryTextColor: theme.intro.primaryTextColor, backgroundColor: .clear, opaqueBackgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: theme.rootController.navigationBar.badgeBackgroundColor, badgeStrokeColor: theme.rootController.navigationBar.badgeStrokeColor, badgeTextColor: theme.rootController.navigationBar.badgeTextColor, edgeEffectColor: .clear, style: .glass) + return NavigationBarTheme(overallDarkAppearance: theme.overallDarkAppearance, buttonColor: theme.chat.inputPanel.panelControlColor, disabledButtonColor: theme.intro.disabledTextColor, primaryTextColor: theme.intro.primaryTextColor, backgroundColor: .clear, opaqueBackgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: theme.rootController.navigationBar.badgeBackgroundColor, badgeStrokeColor: theme.rootController.navigationBar.badgeStrokeColor, badgeTextColor: theme.rootController.navigationBar.badgeTextColor, edgeEffectColor: .clear, accentButtonColor: theme.list.itemCheckColors.fillColor, accentForegroundColor: theme.list.itemCheckColors.foregroundColor, style: .glass) } private let sharedContext: SharedAccountContext diff --git a/submodules/ChatListUI/Sources/ChatListControllerNode.swift b/submodules/ChatListUI/Sources/ChatListControllerNode.swift index 91c9505a72..8281e29585 100644 --- a/submodules/ChatListUI/Sources/ChatListControllerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListControllerNode.swift @@ -1949,7 +1949,21 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { //filter.insert(.excludeRecent) } - let contentNode = ChatListSearchContainerNode(context: self.context, animationCache: self.animationCache, animationRenderer: self.animationRenderer, filter: filter, requestPeerType: nil, location: effectiveLocation, displaySearchFilters: displaySearchFilters, hasDownloads: hasDownloads, initialFilter: initialFilter, openPeer: { [weak self] peer, _, threadId, dismissSearch in + var folder: (Int32, String)? + if let folders = self.controller?.tabContainerData?.0 { + switch self.effectiveContainerNode.currentItemFilter { + case .all: + break + case let .filter(id): + if let value = folders.first(where: { $0.id == .filter(id) }) { + if case let .filter(_, text, _) = value { + folder = (id, text.text) + } + } + } + } + + let contentNode = ChatListSearchContainerNode(context: self.context, animationCache: self.animationCache, animationRenderer: self.animationRenderer, filter: filter, requestPeerType: nil, location: effectiveLocation, folder: folder, displaySearchFilters: displaySearchFilters, hasDownloads: hasDownloads, initialFilter: initialFilter, openPeer: { [weak self] peer, _, threadId, dismissSearch in self?.requestOpenPeerFromSearch?(peer, threadId, dismissSearch) }, openDisabledPeer: { _, _, _ in }, openRecentPeerOptions: { [weak self] peer in diff --git a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift index 2495e589ee..b736750998 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift @@ -43,6 +43,7 @@ import ComponentDisplayAdapters private enum ChatListTokenId: Int32 { case archive + case folder case forum case filter case peer @@ -102,6 +103,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo private let peersFilter: ChatListNodePeersFilter private let requestPeerType: [ReplyMarkupButtonRequestPeerType]? private var location: ChatListControllerLocation + private var folder: (Int32, String)? private let displaySearchFilters: Bool private let hasDownloads: Bool private var interaction: ChatListSearchInteraction? @@ -165,7 +167,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo private var recentAppsDisposable: Disposable? private var refreshedGlobalPostSearchStateDisposable: Disposable? - public init(context: AccountContext, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, filter: ChatListNodePeersFilter, requestPeerType: [ReplyMarkupButtonRequestPeerType]?, location: ChatListControllerLocation, displaySearchFilters: Bool, hasDownloads: Bool, initialFilter: ChatListSearchFilter = .chats, openPeer originalOpenPeer: @escaping (EnginePeer, EnginePeer?, Int64?, Bool) -> Void, openDisabledPeer: @escaping (EnginePeer, Int64?, ChatListDisabledPeerReason) -> Void, openRecentPeerOptions: @escaping (EnginePeer) -> Void, openMessage originalOpenMessage: @escaping (EnginePeer, Int64?, EngineMessage.Id, Bool) -> Void, addContact: ((String) -> Void)?, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, navigationController: NavigationController?, parentController: @escaping () -> ViewController?) { + public init(context: AccountContext, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, filter: ChatListNodePeersFilter, requestPeerType: [ReplyMarkupButtonRequestPeerType]?, location: ChatListControllerLocation, folder: (Int32, String)?, displaySearchFilters: Bool, hasDownloads: Bool, initialFilter: ChatListSearchFilter = .chats, openPeer originalOpenPeer: @escaping (EnginePeer, EnginePeer?, Int64?, Bool) -> Void, openDisabledPeer: @escaping (EnginePeer, Int64?, ChatListDisabledPeerReason) -> Void, openRecentPeerOptions: @escaping (EnginePeer) -> Void, openMessage originalOpenMessage: @escaping (EnginePeer, Int64?, EngineMessage.Id, Bool) -> Void, addContact: ((String) -> Void)?, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, navigationController: NavigationController?, parentController: @escaping () -> ViewController?) { var initialFilter = initialFilter if case .chats = initialFilter, case .forum = location { initialFilter = .topics @@ -175,6 +177,9 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo self.peersFilter = filter self.requestPeerType = requestPeerType self.location = location + + self.folder = folder + self.displaySearchFilters = displaySearchFilters self.hasDownloads = hasDownloads self.navigationController = navigationController @@ -194,6 +199,11 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo self.paneContainerNode.clipsToBounds = true super.init() + + if let folder { + self.searchOptionsValue = self.currentSearchOptions.withUpdatedFolder(folder) + self.searchOptions.set(.single(self.currentSearchOptions)) + } self.backgroundColor = filter.contains(.excludeRecent) ? nil : self.presentationData.theme.chatList.backgroundColor @@ -588,7 +598,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo } private var currentSearchOptions: ChatListSearchOptions { - return self.searchOptionsValue ?? ChatListSearchOptions(peer: nil, date: nil) + return self.searchOptionsValue ?? ChatListSearchOptions(peer: nil, date: nil, folder: nil) } public override func searchTokensUpdated(tokens: [SearchBarToken]) { @@ -609,12 +619,19 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo if !tokensIdSet.contains(ChatListTokenId.peer.rawValue) && updatedOptions?.peer != nil { updatedOptions = updatedOptions?.withUpdatedPeer(nil) } + if !tokensIdSet.contains(ChatListTokenId.folder.rawValue) && updatedOptions?.folder != nil { + updatedOptions = updatedOptions?.withUpdatedFolder(nil) + self.folder = nil + } self.updateSearchOptions(updatedOptions) } private func updateSearchOptions(_ options: ChatListSearchOptions?, clearQuery: Bool = false) { var options = options var tokens: [SearchBarToken] = [] + if let folder = self.folder { + tokens.append(SearchBarToken(id: ChatListTokenId.folder.rawValue, icon: nil, iconOffset: 0.0, peer: nil, title: folder.1, permanent: false)) + } if case .chatList(.archive) = self.location { tokens.append(SearchBarToken(id: ChatListTokenId.archive.rawValue, icon: UIImage(bundleImageName: "Chat List/Search/Archive"), iconOffset: -1.0, title: self.presentationData.strings.ChatList_Archive, permanent: false)) } else if case .forum = self.location, let forumPeer = self.forumPeer { @@ -737,10 +754,13 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo key = .chats } self.paneContainerNode.requestSelectPane(key) - self.updateSearchOptions(nil) + self.updateSearchOptions(self.currentSearchOptions) self.searchTextUpdated(text: query ?? "") var tokens: [SearchBarToken] = [] + if let folder = self.folder { + tokens.append(SearchBarToken(id: ChatListTokenId.folder.rawValue, icon: nil, iconOffset: 0.0, peer: nil, title: folder.1, permanent: false)) + } if case .chatList(.archive) = self.location { tokens.append(SearchBarToken(id: ChatListTokenId.archive.rawValue, icon: UIImage(bundleImageName: "Chat List/Search/Archive"), iconOffset: -1.0, title: self.presentationData.strings.ChatList_Archive, permanent: false)) } else if case .forum = self.location, let forumPeer = self.forumPeer { diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index 7329c1f7ed..52b8ba2e16 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -1448,17 +1448,22 @@ public enum ChatListSearchContextActionSource { public struct ChatListSearchOptions { let peer: (EnginePeer.Id, Bool, String)? let date: (Int32?, Int32, String)? + let folder: (Int32, String)? var isEmpty: Bool { - return self.peer == nil && self.date == nil + return self.peer == nil && self.date == nil && self.folder == nil } func withUpdatedPeer(_ peerIdIsGroupAndName: (EnginePeer.Id, Bool, String)?) -> ChatListSearchOptions { - return ChatListSearchOptions(peer: peerIdIsGroupAndName, date: self.date) + return ChatListSearchOptions(peer: peerIdIsGroupAndName, date: self.date, folder: self.folder) } func withUpdatedDate(_ minDateMaxDateAndTitle: (Int32?, Int32, String)?) -> ChatListSearchOptions { - return ChatListSearchOptions(peer: self.peer, date: minDateMaxDateAndTitle) + return ChatListSearchOptions(peer: self.peer, date: minDateMaxDateAndTitle, folder: self.folder) + } + + func withUpdatedFolder(_ folder: (Int32, String)?) -> ChatListSearchOptions { + return ChatListSearchOptions(peer: self.peer, date: self.date, folder: folder) } } @@ -2065,7 +2070,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { var defaultFoundRemoteMessagesSignal: Signal<([FoundRemoteMessages], Bool), NoError> = .single(([FoundRemoteMessages(messages: [], readCounters: [:], threadsData: [:], totalCount: 0)], false)) if key == .globalPosts, let data = context.currentAppConfiguration.with({ $0 }).data, let value = data["ios_load_empty_global_posts"] as? Double, value != 0.0 { - let searchSignal = context.engine.messages.searchMessages(location: .general(scope: .globalPosts(allowPaidStars: nil), tags: nil, minDate: nil, maxDate: nil), query: "", state: nil, limit: 50) + let searchSignal = context.engine.messages.searchMessages(location: .general(scope: .globalPosts(allowPaidStars: nil), tags: nil, minDate: nil, maxDate: nil, folderId: nil), query: "", state: nil, limit: 50) |> map { resultData -> ChatListSearchMessagesResult in let (result, updatedState) = resultData @@ -2089,7 +2094,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { let foundItems: Signal<([ChatListSearchEntry], Bool, String?)?, NoError> = combineLatest(queue: .mainQueue(), searchQuery, self.approvedGlobalPostQueryState.get(), searchOptions, self.searchScopePromise.get(), downloadItems, globalPostSearchStateType, isPremium) |> debounceOnMainThread |> mapToSignal { [weak self] query, approvedGlobalPostQueryState, options, searchScope, downloadItems, _, _ -> Signal<([ChatListSearchEntry], Bool, String?)?, NoError> in - if query == nil && options == nil && [.chats, .topics, .channels, .apps].contains(key) { + if query == nil && (options == nil || options?.withUpdatedFolder(nil).isEmpty == true) && [.chats, .topics, .channels, .apps].contains(key) { let _ = currentRemotePeers.swap(nil) return .single(nil) } @@ -2244,7 +2249,24 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { return result } - let updatedLocalPeers = context.engine.contacts.searchLocalPeers(query: query.lowercased()) + var predicate: Signal = .single(nil) + if let folderId = options?.folder?.0 { + predicate = context.engine.peers.currentChatListFilters() + |> take(1) + |> map { filters -> ChatListFilterPredicate? in + guard let filter = filters.first(where: { $0.id == folderId }) else { + return nil + } + guard case let .filter(_, _, _, data) = filter else { + return nil + } + return chatListFilterPredicate(filter: data, accountPeerId: context.account.peerId) + } + } + + let updatedLocalPeers = predicate |> mapToSignal { predicate in + return context.engine.contacts.searchLocalPeers(query: query.lowercased(), predicate: predicate) + } |> mapToSignal { peers -> Signal<[EngineRenderedPeer], NoError> in return context.engine.data.subscribe( EngineDataMap(peers.map { peer in @@ -2552,7 +2574,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { if case .savedMessagesChats = location { foundRemotePeers = .single(([], [], [], false)) } else if let query = query, case .chats = key { - if query.hasPrefix("#") { + if query.hasPrefix("#") || options?.folder != nil { foundRemotePeers = .single(([], [], [], false)) } else { foundRemotePeers = ( @@ -2584,28 +2606,28 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { } let searchLocations: [SearchMessagesLocation] if key == .globalPosts { - searchLocations = [SearchMessagesLocation.general(scope: .globalPosts(allowPaidStars: approvedGlobalPostQueryState?.price), tags: nil, minDate: nil, maxDate: nil)] - } else if let options = options { + searchLocations = [SearchMessagesLocation.general(scope: .globalPosts(allowPaidStars: approvedGlobalPostQueryState?.price), tags: nil, minDate: nil, maxDate: nil, folderId: nil)] + } else if let options { if case let .forum(peerId) = location { - searchLocations = [.peer(peerId: peerId, fromId: nil, tags: tagMask, reactions: nil, threadId: nil, minDate: options.date?.0, maxDate: options.date?.1), .general(scope: .everywhere, tags: tagMask, minDate: options.date?.0, maxDate: options.date?.1)] + searchLocations = [.peer(peerId: peerId, fromId: nil, tags: tagMask, reactions: nil, threadId: nil, minDate: options.date?.0, maxDate: options.date?.1), .general(scope: .everywhere, tags: tagMask, minDate: options.date?.0, maxDate: options.date?.1, folderId: nil)] } else if let (peerId, _, _) = options.peer { searchLocations = [.peer(peerId: peerId, fromId: nil, tags: tagMask, reactions: nil, threadId: nil, minDate: options.date?.0, maxDate: options.date?.1)] } else { if case let .chatList(groupId) = location, case .archive = groupId { searchLocations = [.group(groupId: groupId._asGroup(), tags: tagMask, minDate: options.date?.0, maxDate: options.date?.1)] } else { - searchLocations = [.general(scope: searchScope, tags: tagMask, minDate: options.date?.0, maxDate: options.date?.1)] + searchLocations = [.general(scope: searchScope, tags: tagMask, minDate: options.date?.0, maxDate: options.date?.1, folderId: options.folder?.0)] } } } else { if case .channels = key { - searchLocations = [.general(scope: .channels, tags: tagMask, minDate: nil, maxDate: nil)] + searchLocations = [.general(scope: .channels, tags: tagMask, minDate: nil, maxDate: nil, folderId: nil)] } else if case let .forum(peerId) = location { - searchLocations = [.peer(peerId: peerId, fromId: nil, tags: tagMask, reactions: nil, threadId: nil, minDate: nil, maxDate: nil), .general(scope: .everywhere, tags: tagMask, minDate: nil, maxDate: nil)] + searchLocations = [.peer(peerId: peerId, fromId: nil, tags: tagMask, reactions: nil, threadId: nil, minDate: nil, maxDate: nil), .general(scope: .everywhere, tags: tagMask, minDate: nil, maxDate: nil, folderId: nil)] } else if case let .chatList(groupId) = location, case .archive = groupId { searchLocations = [.group(groupId: groupId._asGroup(), tags: tagMask, minDate: nil, maxDate: nil)] } else { - searchLocations = [.general(scope: searchScope, tags: tagMask, minDate: nil, maxDate: nil)] + searchLocations = [.general(scope: searchScope, tags: tagMask, minDate: nil, maxDate: nil, folderId: nil)] } } @@ -2704,7 +2726,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { let searchSignals: [Signal<(SearchMessagesResult, SearchMessagesState), NoError>] if key == .globalPosts { - searchSignals = [context.engine.messages.searchMessages(location: .general(scope: .globalPosts(allowPaidStars: approvedGlobalPostQueryState?.price), tags: nil, minDate: nil, maxDate: nil), query: finalQuery, state: nil, limit: 50)] + searchSignals = [context.engine.messages.searchMessages(location: .general(scope: .globalPosts(allowPaidStars: approvedGlobalPostQueryState?.price), tags: nil, minDate: nil, maxDate: nil, folderId: nil), query: finalQuery, state: nil, limit: 50)] } else { searchSignals = searchLocations.map { searchLocation in return context.engine.messages.searchMessages(location: searchLocation, query: finalQuery, state: nil, limit: 50) diff --git a/submodules/Components/PagerComponent/Sources/PagerComponent.swift b/submodules/Components/PagerComponent/Sources/PagerComponent.swift index 4b11ff0e5d..31f38da7cd 100644 --- a/submodules/Components/PagerComponent/Sources/PagerComponent.swift +++ b/submodules/Components/PagerComponent/Sources/PagerComponent.swift @@ -503,6 +503,20 @@ public final class PagerComponent UIView? { + if self.alpha.isZero { + return nil + } + for view in self.subviews.reversed() { + if let result = view.hitTest(self.convert(point, to: view), with: event), result.isUserInteractionEnabled { + return result + } + } + + let result = super.hitTest(point, with: event) + return result + } + func update(component: PagerComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { let previousPanelHideBehavior = self.component?.panelHideBehavior diff --git a/submodules/Display/Source/NavigationBar.swift b/submodules/Display/Source/NavigationBar.swift index 63f19d6cde..37137b665e 100644 --- a/submodules/Display/Source/NavigationBar.swift +++ b/submodules/Display/Source/NavigationBar.swift @@ -28,10 +28,12 @@ public final class NavigationBarTheme { public let badgeStrokeColor: UIColor public let badgeTextColor: UIColor public let edgeEffectColor: UIColor? + public let accentButtonColor: UIColor + public let accentForegroundColor: UIColor public let style: NavigationBar.Style public let glassStyle: NavigationBar.GlassStyle - public init(overallDarkAppearance: Bool, buttonColor: UIColor, disabledButtonColor: UIColor, primaryTextColor: UIColor, backgroundColor: UIColor, opaqueBackgroundColor: UIColor? = nil, enableBackgroundBlur: Bool, separatorColor: UIColor, badgeBackgroundColor: UIColor, badgeStrokeColor: UIColor, badgeTextColor: UIColor, edgeEffectColor: UIColor? = nil, style: NavigationBar.Style = .legacy, glassStyle: NavigationBar.GlassStyle = .default) { + public init(overallDarkAppearance: Bool, buttonColor: UIColor, disabledButtonColor: UIColor, primaryTextColor: UIColor, backgroundColor: UIColor, opaqueBackgroundColor: UIColor? = nil, enableBackgroundBlur: Bool, separatorColor: UIColor, badgeBackgroundColor: UIColor, badgeStrokeColor: UIColor, badgeTextColor: UIColor, edgeEffectColor: UIColor? = nil, accentButtonColor: UIColor, accentForegroundColor: UIColor, style: NavigationBar.Style = .legacy, glassStyle: NavigationBar.GlassStyle = .default) { self.overallDarkAppearance = overallDarkAppearance self.buttonColor = buttonColor self.disabledButtonColor = disabledButtonColor @@ -44,16 +46,18 @@ public final class NavigationBarTheme { self.badgeStrokeColor = badgeStrokeColor self.badgeTextColor = badgeTextColor self.edgeEffectColor = edgeEffectColor + self.accentButtonColor = accentButtonColor + self.accentForegroundColor = accentForegroundColor self.style = style self.glassStyle = glassStyle } public func withUpdatedBackgroundColor(_ color: UIColor) -> NavigationBarTheme { - return NavigationBarTheme(overallDarkAppearance: self.overallDarkAppearance, buttonColor: self.buttonColor, disabledButtonColor: self.disabledButtonColor, primaryTextColor: self.primaryTextColor, backgroundColor: color, opaqueBackgroundColor: self.opaqueBackgroundColor, enableBackgroundBlur: false, separatorColor: self.separatorColor, badgeBackgroundColor: self.badgeBackgroundColor, badgeStrokeColor: self.badgeStrokeColor, badgeTextColor: self.badgeTextColor, edgeEffectColor: self.edgeEffectColor, style: self.style, glassStyle: self.glassStyle) + return NavigationBarTheme(overallDarkAppearance: self.overallDarkAppearance, buttonColor: self.buttonColor, disabledButtonColor: self.disabledButtonColor, primaryTextColor: self.primaryTextColor, backgroundColor: color, opaqueBackgroundColor: self.opaqueBackgroundColor, enableBackgroundBlur: false, separatorColor: self.separatorColor, badgeBackgroundColor: self.badgeBackgroundColor, badgeStrokeColor: self.badgeStrokeColor, badgeTextColor: self.badgeTextColor, edgeEffectColor: self.edgeEffectColor, accentButtonColor: self.accentButtonColor, accentForegroundColor: self.accentForegroundColor, style: self.style, glassStyle: self.glassStyle) } public func withUpdatedSeparatorColor(_ color: UIColor) -> NavigationBarTheme { - return NavigationBarTheme(overallDarkAppearance: self.overallDarkAppearance, buttonColor: self.buttonColor, disabledButtonColor: self.disabledButtonColor, primaryTextColor: self.primaryTextColor, backgroundColor: self.backgroundColor, opaqueBackgroundColor: self.opaqueBackgroundColor, enableBackgroundBlur: self.enableBackgroundBlur, separatorColor: color, badgeBackgroundColor: self.badgeBackgroundColor, badgeStrokeColor: self.badgeStrokeColor, badgeTextColor: self.badgeTextColor, edgeEffectColor: self.edgeEffectColor, style: self.style, glassStyle: self.glassStyle) + return NavigationBarTheme(overallDarkAppearance: self.overallDarkAppearance, buttonColor: self.buttonColor, disabledButtonColor: self.disabledButtonColor, primaryTextColor: self.primaryTextColor, backgroundColor: self.backgroundColor, opaqueBackgroundColor: self.opaqueBackgroundColor, enableBackgroundBlur: self.enableBackgroundBlur, separatorColor: color, badgeBackgroundColor: self.badgeBackgroundColor, badgeStrokeColor: self.badgeStrokeColor, badgeTextColor: self.badgeTextColor, edgeEffectColor: self.edgeEffectColor, accentButtonColor: self.accentButtonColor, accentForegroundColor: self.accentForegroundColor, style: self.style, glassStyle: self.glassStyle) } } diff --git a/submodules/GalleryUI/Sources/GalleryController.swift b/submodules/GalleryUI/Sources/GalleryController.swift index ab140949c9..c4ae05271d 100644 --- a/submodules/GalleryUI/Sources/GalleryController.swift +++ b/submodules/GalleryUI/Sources/GalleryController.swift @@ -681,7 +681,7 @@ private func galleryEntriesForMessageHistoryEntries(_ entries: [MessageHistoryEn } public class GalleryController: ViewController, StandalonePresentableController, KeyShortcutResponder, GalleryControllerProtocol { - public static let darkNavigationTheme = NavigationBarTheme(overallDarkAppearance: true, buttonColor: .white, disabledButtonColor: UIColor(rgb: 0x525252), primaryTextColor: .white, backgroundColor: UIColor(white: 0.0, alpha: 0.6), enableBackgroundBlur: false, separatorColor: UIColor(white: 0.0, alpha: 0.8), badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear, edgeEffectColor: .clear, style: .glass) + public static let darkNavigationTheme = NavigationBarTheme(overallDarkAppearance: true, buttonColor: .white, disabledButtonColor: UIColor(rgb: 0x525252), primaryTextColor: .white, backgroundColor: UIColor(white: 0.0, alpha: 0.6), enableBackgroundBlur: false, separatorColor: UIColor(white: 0.0, alpha: 0.8), badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear, edgeEffectColor: .clear, accentButtonColor: .white, accentForegroundColor: .black, style: .glass) private var galleryNode: GalleryControllerNode { return self.displayNode as! GalleryControllerNode diff --git a/submodules/HashtagSearchUI/Sources/HashtagSearchGlobalChatContents.swift b/submodules/HashtagSearchUI/Sources/HashtagSearchGlobalChatContents.swift index 3eabc0345c..c9869932c8 100644 --- a/submodules/HashtagSearchUI/Sources/HashtagSearchGlobalChatContents.swift +++ b/submodules/HashtagSearchUI/Sources/HashtagSearchGlobalChatContents.swift @@ -54,7 +54,7 @@ final class HashtagSearchGlobalChatContents: ChatCustomContentsProtocol { if self.publicPosts { search = self.context.engine.messages.searchHashtagPosts(hashtag: self.query, state: nil) } else { - search = self.context.engine.messages.searchMessages(location: .general(scope: .everywhere, tags: nil, minDate: nil, maxDate: nil), query: self.query, state: nil) + search = self.context.engine.messages.searchMessages(location: .general(scope: .everywhere, tags: nil, minDate: nil, maxDate: nil, folderId: nil), query: self.query, state: nil) } self.isSearchingPromise.set(true) @@ -102,7 +102,7 @@ final class HashtagSearchGlobalChatContents: ChatCustomContentsProtocol { if self.publicPosts { search = self.context.engine.messages.searchHashtagPosts(hashtag: self.query, state: self.currentSearchState) } else { - search = self.context.engine.messages.searchMessages(location: .general(scope: .everywhere, tags: nil, minDate: nil, maxDate: nil), query: self.query, state: currentSearchState) + search = self.context.engine.messages.searchMessages(location: .general(scope: .everywhere, tags: nil, minDate: nil, maxDate: nil, folderId: nil), query: self.query, state: currentSearchState) } self.historyViewDisposable?.dispose() diff --git a/submodules/PasswordSetupUI/Sources/SetupTwoStepVerificationController.swift b/submodules/PasswordSetupUI/Sources/SetupTwoStepVerificationController.swift index e6c938946d..56f4938f09 100644 --- a/submodules/PasswordSetupUI/Sources/SetupTwoStepVerificationController.swift +++ b/submodules/PasswordSetupUI/Sources/SetupTwoStepVerificationController.swift @@ -39,7 +39,7 @@ public class SetupTwoStepVerificationController: ViewController { self.presentationData = self.context.sharedContext.currentPresentationData.with { $0 } - super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: NavigationBarTheme(overallDarkAppearance: self.presentationData.theme.overallDarkAppearance, buttonColor: self.presentationData.theme.rootController.navigationBar.accentTextColor, disabledButtonColor: self.presentationData.theme.rootController.navigationBar.disabledButtonColor, primaryTextColor: self.presentationData.theme.rootController.navigationBar.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear), strings: NavigationBarStrings(presentationStrings: self.presentationData.strings))) + super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: NavigationBarTheme(overallDarkAppearance: self.presentationData.theme.overallDarkAppearance, buttonColor: self.presentationData.theme.rootController.navigationBar.accentTextColor, disabledButtonColor: self.presentationData.theme.rootController.navigationBar.disabledButtonColor, primaryTextColor: self.presentationData.theme.rootController.navigationBar.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear, accentButtonColor: self.presentationData.theme.list.itemCheckColors.fillColor, accentForegroundColor: self.presentationData.theme.list.itemCheckColors.foregroundColor), strings: NavigationBarStrings(presentationStrings: self.presentationData.strings))) self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style diff --git a/submodules/PasswordSetupUI/Sources/TwoFactorAuthDataInputScreen.swift b/submodules/PasswordSetupUI/Sources/TwoFactorAuthDataInputScreen.swift index 54843c67d3..d42a2103b9 100644 --- a/submodules/PasswordSetupUI/Sources/TwoFactorAuthDataInputScreen.swift +++ b/submodules/PasswordSetupUI/Sources/TwoFactorAuthDataInputScreen.swift @@ -71,7 +71,7 @@ public final class TwoFactorDataInputScreen: ViewController { self.presentationData = self.sharedContext.currentPresentationData.with { $0 } let defaultTheme = NavigationBarTheme(rootControllerTheme: self.presentationData.theme) - let navigationBarTheme = NavigationBarTheme(overallDarkAppearance: defaultTheme.overallDarkAppearance, buttonColor: defaultTheme.buttonColor, disabledButtonColor: defaultTheme.disabledButtonColor, primaryTextColor: defaultTheme.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: defaultTheme.badgeBackgroundColor, badgeStrokeColor: defaultTheme.badgeStrokeColor, badgeTextColor: defaultTheme.badgeTextColor) + let navigationBarTheme = NavigationBarTheme(overallDarkAppearance: defaultTheme.overallDarkAppearance, buttonColor: defaultTheme.buttonColor, disabledButtonColor: defaultTheme.disabledButtonColor, primaryTextColor: defaultTheme.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: defaultTheme.badgeBackgroundColor, badgeStrokeColor: defaultTheme.badgeStrokeColor, badgeTextColor: defaultTheme.badgeTextColor, accentButtonColor: defaultTheme.accentButtonColor, accentForegroundColor: defaultTheme.accentForegroundColor) super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(back: self.presentationData.strings.Common_Back, close: self.presentationData.strings.Common_Close))) diff --git a/submodules/PasswordSetupUI/Sources/TwoFactorAuthSplashScreen.swift b/submodules/PasswordSetupUI/Sources/TwoFactorAuthSplashScreen.swift index 6b0209ed8c..f94f1ee2cf 100644 --- a/submodules/PasswordSetupUI/Sources/TwoFactorAuthSplashScreen.swift +++ b/submodules/PasswordSetupUI/Sources/TwoFactorAuthSplashScreen.swift @@ -57,7 +57,7 @@ public final class TwoFactorAuthSplashScreen: ViewController { self.presentationData = self.sharedContext.currentPresentationData.with { $0 } let defaultTheme = NavigationBarTheme(rootControllerTheme: self.presentationData.theme) - let navigationBarTheme = NavigationBarTheme(overallDarkAppearance: defaultTheme.overallDarkAppearance, buttonColor: defaultTheme.buttonColor, disabledButtonColor: defaultTheme.disabledButtonColor, primaryTextColor: defaultTheme.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: defaultTheme.badgeBackgroundColor, badgeStrokeColor: defaultTheme.badgeStrokeColor, badgeTextColor: defaultTheme.badgeTextColor) + let navigationBarTheme = NavigationBarTheme(overallDarkAppearance: defaultTheme.overallDarkAppearance, buttonColor: defaultTheme.buttonColor, disabledButtonColor: defaultTheme.disabledButtonColor, primaryTextColor: defaultTheme.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: defaultTheme.badgeBackgroundColor, badgeStrokeColor: defaultTheme.badgeStrokeColor, badgeTextColor: defaultTheme.badgeTextColor, accentButtonColor: defaultTheme.accentButtonColor, accentForegroundColor: defaultTheme.accentForegroundColor) super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(back: self.presentationData.strings.Common_Back, close: self.presentationData.strings.Common_Close))) diff --git a/submodules/Postbox/Sources/Postbox.swift b/submodules/Postbox/Sources/Postbox.swift index 5180199ff7..c058ccfba1 100644 --- a/submodules/Postbox/Sources/Postbox.swift +++ b/submodules/Postbox/Sources/Postbox.swift @@ -1213,9 +1213,9 @@ public final class Transaction { self.postbox?.reindexUnreadCounters(currentTransaction: self) } - public func searchPeers(query: String) -> [RenderedPeer] { + public func searchPeers(query: String, predicate: ChatListFilterPredicate?) -> [RenderedPeer] { assert(!self.disposed) - return self.postbox?.searchPeers(query: query) ?? [] + return self.postbox?.searchPeers(transaction: self, query: query, predicate: predicate) ?? [] } public func clearTimestampBasedAttribute(id: MessageId, tag: UInt16) { @@ -3800,13 +3800,13 @@ final class PostboxImpl { } |> switchToLatest } - public func searchPeers(query: String) -> Signal<[RenderedPeer], NoError> { + public func searchPeers(query: String, predicate: ChatListFilterPredicate?) -> Signal<[RenderedPeer], NoError> { return self.transaction { transaction -> Signal<[RenderedPeer], NoError> in - return .single(transaction.searchPeers(query: query)) + return .single(transaction.searchPeers(query: query, predicate: predicate)) } |> switchToLatest } - fileprivate func searchPeers(query: String) -> [RenderedPeer] { + fileprivate func searchPeers(transaction: Transaction, query: String, predicate: ChatListFilterPredicate?) -> [RenderedPeer] { var peerIds = Set() var chatPeers: [RenderedPeer] = [] @@ -3823,6 +3823,30 @@ final class PostboxImpl { } chatPeerIds.append(contentsOf: additionalChatPeerIds) + if let predicate { + let globalNotificationSettings = self.getGlobalNotificationSettings(transaction: transaction) + + let filterImpl: (PeerId) -> Bool = { peerId in + guard let peer = self.peerTable.get(peerId) else { + return false + } + let inclusion = self.chatListIndexTable.get(peerId: peerId) + let isUnread = self.readStateTable.getCombinedState(peerId)?.isUnread ?? false + let notificationsPeerId = peer.notificationSettingsPeerId ?? peerId + let isContact = self.contactsTable.isContact(peerId: notificationsPeerId) + let isRemovedFromTotalUnreadCount = resolvedIsRemovedFromTotalUnreadCount(globalSettings: globalNotificationSettings, peer: peer, peerSettings: self.peerNotificationSettingsTable.getEffective(notificationsPeerId)) + let messageTagSummaryResult = resolveChatListMessageTagSummaryResultCalculation(postbox: self, peerId: peer.id, threadId: nil, calculation: predicate.messageTagSummary) + if predicate.includes(peer: peer, groupId: inclusion.inclusion.groupId ?? .root, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, isUnread: isUnread, isContact: isContact, messageTagSummaryResult: messageTagSummaryResult) { + return true + } else { + return false + } + } + + chatPeerIds = chatPeerIds.filter(filterImpl) + contactPeerIds = contactPeerIds.filter(filterImpl) + } + for peerId in chatPeerIds { if let peer = self.peerTable.get(peerId) { var peers = SimpleDictionary() @@ -4929,12 +4953,12 @@ public class Postbox { } } - public func searchPeers(query: String) -> Signal<[RenderedPeer], NoError> { + public func searchPeers(query: String, predicate: ChatListFilterPredicate? = nil) -> Signal<[RenderedPeer], NoError> { return Signal { subscriber in let disposable = MetaDisposable() self.impl.with { impl in - disposable.set(impl.searchPeers(query: query).start(next: subscriber.putNext, error: subscriber.putError, completed: subscriber.putCompletion)) + disposable.set(impl.searchPeers(query: query, predicate: predicate).start(next: subscriber.putNext, error: subscriber.putError, completed: subscriber.putCompletion)) } return disposable diff --git a/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift b/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift index ceab808e4a..7ede3135e3 100644 --- a/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift +++ b/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift @@ -77,7 +77,7 @@ public final class QrCodeScanScreen: ViewController { self.presentationData = context.sharedContext.currentPresentationData.with { $0 } - let navigationBarTheme = NavigationBarTheme(overallDarkAppearance: self.presentationData.theme.overallDarkAppearance, buttonColor: .white, disabledButtonColor: .white, primaryTextColor: .white, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear) + let navigationBarTheme = NavigationBarTheme(overallDarkAppearance: self.presentationData.theme.overallDarkAppearance, buttonColor: .white, disabledButtonColor: .white, primaryTextColor: .white, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear, accentButtonColor: .white, accentForegroundColor: .black) super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(back: self.presentationData.strings.Common_Back, close: self.presentationData.strings.Common_Close))) diff --git a/submodules/SearchBarNode/Sources/SearchBarNode.swift b/submodules/SearchBarNode/Sources/SearchBarNode.swift index bed3d19c0e..e585e4c3fb 100644 --- a/submodules/SearchBarNode/Sources/SearchBarNode.swift +++ b/submodules/SearchBarNode/Sources/SearchBarNode.swift @@ -928,6 +928,9 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate { } set { self.textField.tokens = newValue self.updateIsEmpty(animated: true) + if let (boundingSize, leftInset, rightInset) = self.validLayout { + self.updateLayout(boundingSize: boundingSize, leftInset: leftInset, rightInset: rightInset, transition: .immediate) + } } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Contacts/TelegramEngineContacts.swift b/submodules/TelegramCore/Sources/TelegramEngine/Contacts/TelegramEngineContacts.swift index c3ecf5a4e2..aedbdbeae3 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Contacts/TelegramEngineContacts.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Contacts/TelegramEngineContacts.swift @@ -58,8 +58,8 @@ public extension TelegramEngine { return _internal_searchPeers(accountPeerId: self.account.peerId, postbox: self.account.postbox, network: self.account.network, query: query, scope: scope) } - public func searchLocalPeers(query: String, scope: TelegramSearchPeersScope = .everywhere) -> Signal<[EngineRenderedPeer], NoError> { - return self.account.postbox.searchPeers(query: query) + public func searchLocalPeers(query: String, scope: TelegramSearchPeersScope = .everywhere, predicate: ChatListFilterPredicate? = nil) -> Signal<[EngineRenderedPeer], NoError> { + return self.account.postbox.searchPeers(query: query, predicate: predicate) |> map { peers in switch scope { case .everywhere: diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SearchMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SearchMessages.swift index d89275a8ed..f385472317 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SearchMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SearchMessages.swift @@ -6,7 +6,7 @@ import MtProtoKit public enum SearchMessagesLocation: Equatable { - case general(scope: TelegramSearchPeersScope, tags: MessageTags?, minDate: Int32?, maxDate: Int32?) + case general(scope: TelegramSearchPeersScope, tags: MessageTags?, minDate: Int32?, maxDate: Int32?, folderId: Int32?) case group(groupId: PeerGroupId, tags: MessageTags?, minDate: Int32?, maxDate: Int32?) case peer(peerId: PeerId, fromId: PeerId?, tags: MessageTags?, reactions: [MessageReaction.Reaction]?, threadId: Int64?, minDate: Int32?, maxDate: Int32?) case sentMedia(tags: MessageTags?) @@ -471,7 +471,7 @@ func _internal_searchMessages(account: Account, location: SearchMessagesLocation } return combineLatest(peerMessages, additionalPeerMessages) } - case let .general(_, tags, minDate, maxDate), let .group(_, tags, minDate, maxDate): + case let .general(_, tags, minDate, maxDate, _), let .group(_, tags, minDate, maxDate): var flags: Int32 = 0 let folderId: Int32? if case let .group(groupId, _, _, _) = location { @@ -481,7 +481,7 @@ func _internal_searchMessages(account: Account, location: SearchMessagesLocation folderId = nil } - if case let .general(scope, _, _, _) = location, case let .globalPosts(allowPaidStars) = scope { + if case let .general(scope, _, _, _, _) = location, case let .globalPosts(allowPaidStars) = scope { remoteSearchResult = account.postbox.transaction { transaction -> (Int32, MessageIndex?, Api.InputPeer) in var lowerBound: MessageIndex? if let state = state, let message = state.main.messages.last { @@ -508,7 +508,7 @@ func _internal_searchMessages(account: Account, location: SearchMessagesLocation } } } else { - if case let .general(scope, _, _, _) = location { + if case let .general(scope, _, _, _, _) = location { switch scope { case .everywhere: break @@ -567,9 +567,9 @@ func _internal_searchMessages(account: Account, location: SearchMessagesLocation if state?.additional == nil { switch location { - case let .general(_, tags, minDate, maxDate), let .group(_, tags, minDate, maxDate): + case let .general(_, tags, minDate, maxDate, _), let .group(_, tags, minDate, maxDate): let secretMessages: [Message] - if case let .general(scope, _, _, _) = location, case .channels = scope { + if case let .general(scope, _, _, _, _) = location, case .channels = scope { secretMessages = [] } else { secretMessages = transaction.searchMessages(peerId: nil, query: query, tags: tags) diff --git a/submodules/TelegramNotices/Sources/Notices.swift b/submodules/TelegramNotices/Sources/Notices.swift index 731e31b609..7a7ac6d024 100644 --- a/submodules/TelegramNotices/Sources/Notices.swift +++ b/submodules/TelegramNotices/Sources/Notices.swift @@ -208,6 +208,7 @@ private enum ApplicationSpecificGlobalNotice: Int32 { case giftCraftingTips = 85 case copyProtectionTips = 86 case aiTextProcessingStyleSelectionTips = 87 + case savedMessagesChatListView = 88 var key: ValueBoxKey { let v = ValueBoxKey(length: 4) @@ -589,6 +590,10 @@ private struct ApplicationSpecificNoticeKeys { static func aiTextProcessingStyleSelectionTips() -> NoticeEntryKey { return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.aiTextProcessingStyleSelectionTips.key) } + + static func savedMessagesChatListView() -> NoticeEntryKey { + return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.savedMessagesChatListView.key) + } } public struct ApplicationSpecificNotice { @@ -2610,4 +2615,31 @@ public struct ApplicationSpecificNotice { return Int(previousValue) } } + + public static func getSavedMessagesChatListView(accountManager: AccountManager) -> Signal { + return accountManager.transaction { transaction -> Int32 in + if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.savedMessagesChatListView())?.get(ApplicationSpecificCounterNotice.self) { + return value.value + } else { + return 0 + } + } + } + + public static func incrementSavedMessagesChatListView(accountManager: AccountManager, count: Int = 1) -> Signal { + return accountManager.transaction { transaction -> Int in + var currentValue: Int32 = 0 + if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.savedMessagesChatListView())?.get(ApplicationSpecificCounterNotice.self) { + currentValue = value.value + } + let previousValue = currentValue + currentValue += Int32(count) + + if let entry = CodableEntry(ApplicationSpecificCounterNotice(value: currentValue)) { + transaction.setNotice(ApplicationSpecificNoticeKeys.savedMessagesChatListView(), entry) + } + + return Int(previousValue) + } + } } diff --git a/submodules/TelegramPresentationData/Sources/ComponentsThemes.swift b/submodules/TelegramPresentationData/Sources/ComponentsThemes.swift index 0f9f22faa0..8dbd3e82b6 100644 --- a/submodules/TelegramPresentationData/Sources/ComponentsThemes.swift +++ b/submodules/TelegramPresentationData/Sources/ComponentsThemes.swift @@ -69,7 +69,7 @@ public extension NavigationBarTheme { badgeTextColor = theme.badgeTextColor } - self.init(overallDarkAppearance: rootControllerTheme.overallDarkAppearance, buttonColor: buttonColor, disabledButtonColor: disabledButtonColor, primaryTextColor: theme.primaryTextColor, backgroundColor: hideBackground ? .clear : theme.blurredBackgroundColor, opaqueBackgroundColor: hideBackground ? .clear : theme.opaqueBackgroundColor, enableBackgroundBlur: enableBackgroundBlur, separatorColor: hideBackground || hideSeparator ? .clear : theme.separatorColor, badgeBackgroundColor: hideBadge ? .clear : badgeBackgroundColor, badgeStrokeColor: .clear, badgeTextColor: hideBadge ? .clear : badgeTextColor, edgeEffectColor: edgeEffectColor, style: style, glassStyle: glassStyle) + self.init(overallDarkAppearance: rootControllerTheme.overallDarkAppearance, buttonColor: buttonColor, disabledButtonColor: disabledButtonColor, primaryTextColor: theme.primaryTextColor, backgroundColor: hideBackground ? .clear : theme.blurredBackgroundColor, opaqueBackgroundColor: hideBackground ? .clear : theme.opaqueBackgroundColor, enableBackgroundBlur: enableBackgroundBlur, separatorColor: hideBackground || hideSeparator ? .clear : theme.separatorColor, badgeBackgroundColor: hideBadge ? .clear : badgeBackgroundColor, badgeStrokeColor: .clear, badgeTextColor: hideBadge ? .clear : badgeTextColor, edgeEffectColor: edgeEffectColor, accentButtonColor: rootControllerTheme.list.itemCheckColors.fillColor, accentForegroundColor: rootControllerTheme.list.itemCheckColors.foregroundColor, style: style, glassStyle: glassStyle) } } diff --git a/submodules/TelegramUI/Components/AttachmentFileController/Sources/AttachmentFileController.swift b/submodules/TelegramUI/Components/AttachmentFileController/Sources/AttachmentFileController.swift index d69c236735..e387d05658 100644 --- a/submodules/TelegramUI/Components/AttachmentFileController/Sources/AttachmentFileController.swift +++ b/submodules/TelegramUI/Components/AttachmentFileController/Sources/AttachmentFileController.swift @@ -756,7 +756,7 @@ public func makeAttachmentFileControllerImpl( case .audio: recentDocuments = .single(nil) |> then( - context.engine.messages.searchMessages(location: .general(scope: .everywhere, tags: [.music], minDate: nil, maxDate: nil), query: "", state: nil) + context.engine.messages.searchMessages(location: .general(scope: .everywhere, tags: [.music], minDate: nil, maxDate: nil, folderId: nil), query: "", state: nil) |> map { result -> [Message]? in return result.0.messages } diff --git a/submodules/TelegramUI/Components/AttachmentFileController/Sources/AttachmentFileSearchItem.swift b/submodules/TelegramUI/Components/AttachmentFileController/Sources/AttachmentFileSearchItem.swift index f3edb35f99..2e1463429e 100644 --- a/submodules/TelegramUI/Components/AttachmentFileController/Sources/AttachmentFileSearchItem.swift +++ b/submodules/TelegramUI/Components/AttachmentFileController/Sources/AttachmentFileSearchItem.swift @@ -530,7 +530,7 @@ public final class AttachmentFileSearchContainerNode: SearchDisplayControllerCon case .audio: shared = .single(nil) |> then( - context.engine.messages.searchMessages(location: .general(scope: .everywhere, tags: [.music], minDate: nil, maxDate: nil), query: query, state: nil) + context.engine.messages.searchMessages(location: .general(scope: .everywhere, tags: [.music], minDate: nil, maxDate: nil, folderId: nil), query: query, state: nil) |> delay(0.6, queue: Queue.mainQueue()) |> map { result -> [Message]? in return result.0.messages.filter { !$0.isRestricted(platform: "ios", contentSettings: context.currentContentSettings.with { $0 }) } diff --git a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift index a15805b502..2b3544686b 100644 --- a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift +++ b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift @@ -361,7 +361,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { private let context: AccountContext private let stateContext: StateContext? - private let entityKeyboardView: ComponentHostView + private let entityKeyboardView: ComponentView private let defaultToEmojiTab: Bool private var stableReorderableGroupOrder: [EntityKeyboardComponent.ReorderCategory: [ItemCollectionId]] = [:] @@ -449,7 +449,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { } public var canSwitchToTextInputAutomatically: Bool { - if let pagerView = self.entityKeyboardView.componentView as? EntityKeyboardComponent.View, let centralId = pagerView.centralId { + if let pagerView = self.entityKeyboardView.view as? EntityKeyboardComponent.View, let centralId = pagerView.centralId { if centralId == AnyHashable("emoji") { return false } @@ -496,11 +496,11 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { self.interaction = interaction - self.clippingView = UIView() + self.clippingView = SparseContainerView() self.clippingView.clipsToBounds = true self.clippingView.layer.cornerRadius = keyboardCornerRadius - self.entityKeyboardView = ComponentHostView() + self.entityKeyboardView = ComponentView() super.init() @@ -538,7 +538,6 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { ) } - self.clippingView.addSubview(self.entityKeyboardView) self.view.addSubview(self.clippingView) if let backgroundChromeView = self.backgroundChromeView { @@ -692,7 +691,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { for featuredStickerPack in stickerPacks { if featuredStickerPack.topItems.contains(where: { $0.file.fileId == file.fileId }) { - if let pagerView = self.entityKeyboardView.componentView as? EntityKeyboardComponent.View, let emojiInputInteraction = self.emojiInputInteraction { + if let pagerView = self.entityKeyboardView.view as? EntityKeyboardComponent.View, let emojiInputInteraction = self.emojiInputInteraction { pagerView.openCustomSearch(content: EmojiSearchContent( context: self.context, forceTheme: self.interaction?.forceTheme, @@ -1326,7 +1325,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { )) }, openSearch: { [weak self] in - if let strongSelf = self, let pagerView = strongSelf.entityKeyboardView.componentView as? EntityKeyboardComponent.View { + if let strongSelf = self, let pagerView = strongSelf.entityKeyboardView.view as? EntityKeyboardComponent.View { pagerView.openSearch() } }, @@ -1630,7 +1629,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { var transition: ComponentTransition = .immediate var useAnimation = false - if let pagerView = strongSelf.entityKeyboardView.componentView as? EntityKeyboardComponent.View, let centralId = pagerView.centralId { + if let pagerView = strongSelf.entityKeyboardView.view as? EntityKeyboardComponent.View, let centralId = pagerView.centralId { if centralId == AnyHashable("emoji") { useAnimation = strongSelf.currentInputData.emoji != inputData.emoji } else if centralId == AnyHashable("stickers"), strongSelf.currentInputData.stickers != nil, inputData.stickers != nil { @@ -1710,7 +1709,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { gifContext.loadMore(token: token) }, openSearch: { [weak self] in - if let strongSelf = self, let pagerView = strongSelf.entityKeyboardView.componentView as? EntityKeyboardComponent.View { + if let strongSelf = self, let pagerView = strongSelf.entityKeyboardView.view as? EntityKeyboardComponent.View { pagerView.openSearch() } }, @@ -1798,8 +1797,13 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { } override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { - if let result = super.hitTest(point, with: event) { - return result + if self.alpha.isZero || !self.view.isUserInteractionEnabled { + return nil + } + for subview in self.view.subviews.reversed() { + if let result = subview.hitTest(self.view.convert(point, to: subview), with: event), result.isUserInteractionEnabled { + return result + } } if let backgroundView = self.backgroundView, backgroundView.frame.contains(point) { @@ -2025,7 +2029,12 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { entityKeyboardSizeFrame.origin.y += self.topBackgroundExtension } - transition.updateFrame(view: self.entityKeyboardView, frame: entityKeyboardSizeFrame) + if let entityKeyboardComponentView = self.entityKeyboardView.view { + if entityKeyboardComponentView.superview == nil { + self.clippingView.addSubview(entityKeyboardComponentView) + } + transition.updateFrame(view: entityKeyboardComponentView, frame: entityKeyboardSizeFrame) + } transition.updateFrame(view: self.clippingView, frame: clippingFrame) @@ -2366,7 +2375,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { } public func scrollToGroupEmoji() { - if let pagerView = self.entityKeyboardView.componentView as? EntityKeyboardComponent.View { + if let pagerView = self.entityKeyboardView.view as? EntityKeyboardComponent.View { pagerView.scrollToItemGroup(contentId: "emoji", groupId: "peerSpecific", subgroupId: nil) } } diff --git a/submodules/TelegramUI/Components/ChatScheduleTimeController/Sources/ChatScheduleTimeScreen.swift b/submodules/TelegramUI/Components/ChatScheduleTimeController/Sources/ChatScheduleTimeScreen.swift index c49ef78263..782a1ff862 100644 --- a/submodules/TelegramUI/Components/ChatScheduleTimeController/Sources/ChatScheduleTimeScreen.swift +++ b/submodules/TelegramUI/Components/ChatScheduleTimeController/Sources/ChatScheduleTimeScreen.swift @@ -157,7 +157,8 @@ private final class ChatScheduleTimeSheetContentComponent: Component { self.environment = environment if self.component == nil { - if case .format = component.mode { + switch component.mode { + case .format, .search: self.minDate = Date(timeIntervalSince1970: 0.0) self.maxDate = Date(timeIntervalSince1970: Double(Int32.max - 1)) if let currentTime = component.currentTime { @@ -167,7 +168,7 @@ private final class ChatScheduleTimeSheetContentComponent: Component { } else { self.date = Date() } - } else { + default: self.updateMinimumDate(currentTime: component.currentTime, minimalTime: component.minimalTime) } self.repeatPeriod = component.currentRepeatPeriod @@ -227,6 +228,9 @@ private final class ChatScheduleTimeSheetContentComponent: Component { title = strings.Conversation_FormatDate_Title case .poll: title = strings.CreatePoll_Deadline_Title + case .search: + //TODO:localize + title = "Search" } let titleSize = self.title.update( transition: transition, @@ -386,6 +390,8 @@ private final class ChatScheduleTimeSheetContentComponent: Component { var repeatValueFrame = CGRect() if case .format = component.mode { contentHeight += 8.0 + } else if case .search = component.mode { + contentHeight += 8.0 } else if case .poll = component.mode { contentHeight += 8.0 } else { @@ -497,6 +503,9 @@ private final class ChatScheduleTimeSheetContentComponent: Component { buttonTitle = component.currentTime != nil ? strings.Conversation_FormatDate_EditDate : strings.Conversation_FormatDate_AddDate case .poll: buttonTitle = strings.CreatePoll_Deadline_SetDeadline + case .search: + //TODO:localize + buttonTitle = "Done" } let buttonSideInset: CGFloat = 30.0 @@ -539,7 +548,13 @@ private final class ChatScheduleTimeSheetContentComponent: Component { } contentHeight += buttonSize.height - if case .format = component.mode, component.currentTime != nil { + var isFormatOrSearch = false + if case .format = component.mode { + isFormatOrSearch = true + } else if case .search = component.mode { + isFormatOrSearch = true + } + if isFormatOrSearch && component.currentTime != nil { contentHeight += 8.0 let buttonSize = self.secondaryButton.update( @@ -947,6 +962,7 @@ public class ChatScheduleTimeScreen: ViewControllerComponentContainer { case reminders case format case poll + case search } public struct Result { diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboard.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboard.swift index 12ec0cfb7f..c525591808 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboard.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboard.swift @@ -272,7 +272,7 @@ public final class EntityKeyboardComponent: Component { public final class View: UIView { private let tintContainerView: UIView - private let pagerView: ComponentHostView + private let pagerView: ComponentView private var component: EntityKeyboardComponent? public private(set) weak var state: EmptyComponentState? @@ -295,20 +295,32 @@ public final class EntityKeyboardComponent: Component { override init(frame: CGRect) { self.tintContainerView = UIView() - self.pagerView = ComponentHostView() + self.pagerView = ComponentView() super.init(frame: frame) //self.clipsToBounds = true self.disablesInteractiveTransitionGestureRecognizer = true - - self.addSubview(self.pagerView) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + if self.alpha.isZero { + return nil + } + for view in self.subviews.reversed() { + if let result = view.hitTest(self.convert(point, to: view), with: event), result.isUserInteractionEnabled { + return result + } + } + + let result = super.hitTest(point, with: event) + return result + } + func update(component: EntityKeyboardComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { self.state = state @@ -787,7 +799,12 @@ public final class EntityKeyboardComponent: Component { forceUpdate: forceUpdate, containerSize: availableSize ) - transition.setFrame(view: self.pagerView, frame: CGRect(origin: CGPoint(), size: pagerSize)) + if let pagerComponentView = self.pagerView.view { + if pagerComponentView.superview == nil { + self.insertSubview(pagerComponentView, at: 0) + } + transition.setFrame(view: pagerComponentView, frame: CGRect(origin: CGPoint(), size: pagerSize)) + } let accountContext = component.emojiContent?.context ?? component.stickerContent?.context if let searchComponent = self.searchComponent, let accountContext = accountContext { diff --git a/submodules/TelegramUI/Components/NavigationBarImpl/Sources/NavigationBarImpl.swift b/submodules/TelegramUI/Components/NavigationBarImpl/Sources/NavigationBarImpl.swift index bf2eaacaa4..8639fa514d 100644 --- a/submodules/TelegramUI/Components/NavigationBarImpl/Sources/NavigationBarImpl.swift +++ b/submodules/TelegramUI/Components/NavigationBarImpl/Sources/NavigationBarImpl.swift @@ -441,9 +441,7 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar { self.leftButtonNodeImpl.view.removeFromSuperview() var backTitle: String? - if case .glass = self.presentationData.theme.style { - backTitle = "" - } else if let customBackButtonText = self.customBackButtonText { + if let customBackButtonText = self.customBackButtonText { backTitle = customBackButtonText } else if let leftBarButtonItem = item.leftBarButtonItem, leftBarButtonItem.backButtonAppearance { backTitle = leftBarButtonItem.title @@ -460,6 +458,12 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar { } } + if backTitle != nil { + if case .glass = self.presentationData.theme.style { + backTitle = "" + } + } + if let backTitle { self.backButtonNodeImpl.updateManualText(backTitle, isBack: true) if self.backButtonNodeImpl.supernode == nil { @@ -631,10 +635,6 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar { self.backButtonNodeImpl.color = self.presentationData.theme.buttonColor self.backButtonNodeImpl.disabledColor = self.presentationData.theme.disabledButtonColor - self.leftButtonNodeImpl.color = self.presentationData.theme.buttonColor - self.leftButtonNodeImpl.disabledColor = self.presentationData.theme.disabledButtonColor - self.rightButtonNodeImpl.color = self.presentationData.theme.buttonColor - self.rightButtonNodeImpl.disabledColor = self.presentationData.theme.disabledButtonColor self.backButtonArrow.image = presentationData.theme.style == .glass ? generateTintedImage(image: glassBackArrowImage, color: self.presentationData.theme.buttonColor) : navigationBarBackArrowImage(color: self.presentationData.theme.buttonColor) if let title = self.title { self.titleNode.attributedText = NSAttributedString(string: title, font: NavigationBarImpl.titleFont, textColor: self.presentationData.theme.primaryTextColor) @@ -753,10 +753,6 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar { self.backButtonNodeImpl.color = self.presentationData.theme.buttonColor self.backButtonNodeImpl.disabledColor = self.presentationData.theme.disabledButtonColor - self.leftButtonNodeImpl.color = self.presentationData.theme.buttonColor - self.leftButtonNodeImpl.disabledColor = self.presentationData.theme.disabledButtonColor - self.rightButtonNodeImpl.color = self.presentationData.theme.buttonColor - self.rightButtonNodeImpl.disabledColor = self.presentationData.theme.disabledButtonColor self.backButtonArrow.image = self.presentationData.theme.style == .glass ? generateTintedImage(image: glassBackArrowImage, color: self.presentationData.theme.buttonColor) : navigationBarBackArrowImage(color: self.presentationData.theme.buttonColor) if let title = self.title { self.titleNode.attributedText = NSAttributedString(string: title, font: NavigationBarImpl.titleFont, textColor: self.presentationData.theme.primaryTextColor) @@ -906,6 +902,15 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar { self.badgeNode.alpha = 1.0 } } else if self.leftButtonNodeImpl.view.superview != nil { + switch self.leftButtonNodeImpl.commonContentType { + case .accent: + self.leftButtonNodeImpl.color = self.presentationData.theme.accentForegroundColor + self.leftButtonNodeImpl.disabledColor = self.presentationData.theme.accentForegroundColor.withMultipliedAlpha(0.5) + case .generic: + self.leftButtonNodeImpl.color = self.presentationData.theme.buttonColor + self.leftButtonNodeImpl.disabledColor = self.presentationData.theme.disabledButtonColor + } + let leftButtonSize = self.leftButtonNodeImpl.updateLayout(constrainedSize: CGSize(width: size.width, height: 44.0), isLandscape: isLandscape, isLeftAligned: true) leftTitleInset = leftButtonSize.width + leftButtonInset + 1.0 @@ -937,6 +942,15 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar { var rightButtonsWidth: CGFloat = 0.0 if self.rightButtonNodeImpl.view.superview != nil { + switch self.rightButtonNodeImpl.commonContentType { + case .accent: + self.rightButtonNodeImpl.color = self.presentationData.theme.accentForegroundColor + self.rightButtonNodeImpl.disabledColor = self.presentationData.theme.accentForegroundColor.withMultipliedAlpha(0.5) + case .generic: + self.rightButtonNodeImpl.color = self.presentationData.theme.buttonColor + self.rightButtonNodeImpl.disabledColor = self.presentationData.theme.disabledButtonColor + } + let rightButtonSize = self.rightButtonNodeImpl.updateLayout(constrainedSize: (CGSize(width: size.width, height: 44.0)), isLandscape: isLandscape, isLeftAligned: false) if !self.rightButtonNodeImpl.isEmpty { rightButtonsWidth += rightButtonSize.width @@ -975,7 +989,16 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar { leftButtonsBackgroundTransition.setBounds(view: leftButtonsBackgroundView.background, bounds: CGRect(origin: CGPoint(), size: leftButtonsBackgroundFrame.size)) leftButtonsBackgroundTransition.setFrame(view: leftButtonsBackgroundView.container, frame: CGRect(origin: CGPoint(), size: leftButtonsBackgroundFrame.size)) ComponentTransition(transition).setAlpha(view: leftButtonsBackgroundView.background, alpha: leftButtonsWidth == 0.0 ? 0.0 : 1.0) - leftButtonsBackgroundView.background.update(size: leftButtonsBackgroundFrame.size, cornerRadius: leftButtonsBackgroundFrame.height * 0.5, isDark: self.presentationData.theme.overallDarkAppearance, tintColor: .init(kind: self.presentationData.theme.glassStyle == .clear ? .clear : .panel), isInteractive: true, isVisible: leftButtonsWidth != 0.0, transition: leftButtonsBackgroundTransition) + + var leftButtonsColor: GlassBackgroundView.TintColor = .init(kind: self.presentationData.theme.glassStyle == .clear ? .clear : .panel) + switch self.leftButtonNodeImpl.commonContentType { + case .accent: + leftButtonsColor = .init(kind: .custom(style: self.presentationData.theme.glassStyle == .clear ? .clear : .default, color: self.presentationData.theme.accentButtonColor)) + case .generic: + break + } + + leftButtonsBackgroundView.background.update(size: leftButtonsBackgroundFrame.size, cornerRadius: leftButtonsBackgroundFrame.height * 0.5, isDark: self.presentationData.theme.overallDarkAppearance, tintColor: leftButtonsColor, isInteractive: true, isVisible: leftButtonsWidth != 0.0, transition: leftButtonsBackgroundTransition) } if let rightButtonsBackgroundView = self.rightButtonsBackgroundView { @@ -1001,8 +1024,16 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar { }) } + var rightButtonsColor: GlassBackgroundView.TintColor = .init(kind: self.presentationData.theme.glassStyle == .clear ? .clear : .panel) + switch self.rightButtonNodeImpl.commonContentType { + case .accent: + rightButtonsColor = .init(kind: .custom(style: self.presentationData.theme.glassStyle == .clear ? .clear : .default, color: self.presentationData.theme.accentButtonColor)) + case .generic: + break + } + rightButtonsBackgroundView.background.isHidden = false - rightButtonsBackgroundView.background.update(size: rightButtonsBackgroundFrame.size, cornerRadius: rightButtonsBackgroundFrame.height * 0.5, isDark: self.presentationData.theme.overallDarkAppearance, tintColor: .init(kind: self.presentationData.theme.glassStyle == .clear ? .clear : .panel), isInteractive: true, transition: rightButtonsBackgroundTransition) + rightButtonsBackgroundView.background.update(size: rightButtonsBackgroundFrame.size, cornerRadius: rightButtonsBackgroundFrame.height * 0.5, isDark: self.presentationData.theme.overallDarkAppearance, tintColor: rightButtonsColor, isInteractive: true, transition: rightButtonsBackgroundTransition) } else { rightButtonsBackgroundView.background.isHidden = true } @@ -1038,7 +1069,7 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar { titleViewTransition = .immediate } - let titleSize = titleView.updateLayout(availableSize: CGSize(width: size.width - max(leftTitleInset, rightTitleInset) * 2.0, height: nominalHeight), transition: titleViewTransition) + let titleSize = titleView.updateLayout(availableSize: CGSize(width: size.width - leftTitleInset - rightTitleInset, height: nominalHeight), transition: titleViewTransition) var titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) * 0.5), y: contentVerticalOrigin + floorToScreenPixels((nominalHeight - titleSize.height) / 2.0)), size: titleSize) if titleFrame.origin.x + titleFrame.width > size.width - rightTitleInset { @@ -1050,8 +1081,14 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar { titleViewTransition.updateFrame(view: titleView, frame: titleFrame) } else { - let titleSize = CGSize(width: max(1.0, size.width - max(leftTitleInset, rightTitleInset) * 2.0), height: nominalHeight) - let titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: contentVerticalOrigin + floorToScreenPixels((nominalHeight - titleSize.height) / 2.0)), size: titleSize) + let titleSize = CGSize(width: max(1.0, size.width - leftTitleInset - rightTitleInset), height: nominalHeight) + var titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: contentVerticalOrigin + floorToScreenPixels((nominalHeight - titleSize.height) / 2.0)), size: titleSize) + if titleFrame.origin.x + titleFrame.width > size.width - rightTitleInset { + titleFrame.origin.x = size.width - rightTitleInset - titleFrame.width + } + if titleFrame.origin.x < leftTitleInset { + titleFrame.origin.x = leftTitleInset + floorToScreenPixels((size.width - leftTitleInset - rightTitleInset - titleFrame.width) * 0.5) + } var titleViewTransition = transition if titleView.frame.isEmpty { titleViewTransition = .immediate diff --git a/submodules/TelegramUI/Components/NavigationBarImpl/Sources/NavigationButtonNode.swift b/submodules/TelegramUI/Components/NavigationBarImpl/Sources/NavigationButtonNode.swift index 0446f196a0..5c5982341f 100644 --- a/submodules/TelegramUI/Components/NavigationBarImpl/Sources/NavigationButtonNode.swift +++ b/submodules/TelegramUI/Components/NavigationBarImpl/Sources/NavigationButtonNode.swift @@ -261,7 +261,7 @@ private final class NavigationButtonItemNode: ImmediateTextNode { } } - private(set) var imageNode: ASImageNode? + private(set) var imageView: UIImageView? private var _image: UIImage? public var image: UIImage? { @@ -271,23 +271,21 @@ private final class NavigationButtonItemNode: ImmediateTextNode { _image = value if let _ = value { - if self.imageNode == nil { - let imageNode = ASImageNode() - imageNode.displayWithoutProcessing = true - imageNode.displaysAsynchronously = false - self.imageNode = imageNode + if self.imageView == nil { + let imageView = UIImageView() + self.imageView = imageView - self.addSubnode(imageNode) + self.view.addSubview(imageView) } - self.imageNode?.image = image - if self.imageNode?.image?.renderingMode == .alwaysTemplate { - self.imageNode?.tintColor = self.color + self.imageView?.image = image + if self.imageView?.image?.renderingMode == .alwaysTemplate { + self.imageView?.tintColor = self.color } else { - self.imageNode?.tintColor = nil + self.imageView?.tintColor = nil } - } else if let imageNode = self.imageNode { - imageNode.removeFromSupernode() - self.imageNode = nil + } else if let imageView = self.imageView { + imageView.removeFromSuperview() + self.imageView = nil } self.invalidateCalculatedLayout() @@ -311,11 +309,15 @@ private final class NavigationButtonItemNode: ImmediateTextNode { public var color: UIColor = UIColor(rgb: 0x0088ff) { didSet { - if self.imageNode?.image?.renderingMode == .alwaysTemplate { - self.imageNode?.tintColor = self.color - } - if let text = self._text { - self.attributedText = NSAttributedString(string: text, attributes: self.attributesForCurrentState()) + if self.color != oldValue { + if self.imageView?.image?.renderingMode == .alwaysTemplate { + self.imageView?.tintColor = self.color + } else { + self.image = self.image + } + if let text = self._text { + self.attributedText = NSAttributedString(string: text, attributes: self.attributesForCurrentState()) + } } } } @@ -422,11 +424,11 @@ private final class NavigationButtonItemNode: ImmediateTextNode { let size = CGSize(width: max(nodeSize.width, superSize.width), height: max(nodeSize.height, superSize.height)) node.frame = CGRect(origin: CGPoint(), size: nodeSize) return size - } else if let imageNode = self.imageNode { - let nodeSize = imageNode.image?.size ?? CGSize() + } else if let imageView = self.imageView { + let nodeSize = imageView.image?.size ?? CGSize() let size = CGSize(width: max(nodeSize.width, superSize.width), height: max(44.0, max(nodeSize.height, superSize.height))) let imageFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - nodeSize.width) / 2.0), y: floorToScreenPixels((size.height - nodeSize.height) / 2.0)), size: nodeSize) - imageNode.frame = imageFrame + imageView.frame = imageFrame return size } else { superSize.height = max(44.0, superSize.height) @@ -524,9 +526,15 @@ private final class NavigationButtonItemNode: ImmediateTextNode { public final class NavigationButtonNodeImpl: ContextControllerSourceNode, NavigationButtonNode { + enum ContentType { + case accent + case generic + } + private let isGlass: Bool private var isBack: Bool = false + private var items: [UIBarButtonItem] = [] private var nodes: [NavigationButtonItemNode] = [] private var disappearingNodes: [(frame: CGRect, size: CGSize, node: NavigationButtonItemNode)] = [] @@ -648,6 +656,8 @@ public final class NavigationButtonNodeImpl: ContextControllerSourceNode, Naviga } public func updateItems(_ items: [UIBarButtonItem], animated: Bool) { + self.items = items + for i in 0 ..< items.count { let node: NavigationButtonItemNode if self.nodes.count > i { @@ -676,8 +686,11 @@ public final class NavigationButtonNodeImpl: ContextControllerSourceNode, Naviga node.alpha = self.manualAlpha node.item = items[i] if items[i].title == "___close" { - //node.image = glassCloseImage - node.image = generateTintedImage(image: UIImage(bundleImageName: "Navigation/Close"), color: self.color) + node.image = generateTintedImage(image: UIImage(bundleImageName: "Navigation/Close"), color: .white)?.withRenderingMode(.alwaysTemplate) + } else if items[i].title == "___clear" { + node.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: .white)?.withRenderingMode(.alwaysTemplate) + } else if items[i].title == "___done" { + node.image = generateTintedImage(image: UIImage(bundleImageName: "Navigation/Done"), color: .white)?.withRenderingMode(.alwaysTemplate) } else { node.image = items[i].image node.text = items[i].title ?? "" @@ -749,7 +762,7 @@ public final class NavigationButtonNodeImpl: ContextControllerSourceNode, Naviga nodeOrigin.x += 16.0 } - if !self.isGlass && node.node == nil && node.imageNode != nil && i == self.nodes.count - 1 { + if !self.isGlass && node.node == nil && node.imageView != nil && i == self.nodes.count - 1 { nodeOrigin.x -= 5.0 } } @@ -791,4 +804,26 @@ public final class NavigationButtonNodeImpl: ContextControllerSourceNode, Naviga } return true } + + var commonContentType: ContentType { + var commonType: ContentType? + for item in self.items { + var nodeContentType: ContentType = .generic + if item.title == "___close" { + } else if item.title == "___clear" { + } else if item.title == "___done" { + nodeContentType = .accent + } + + if commonType == nil { + commonType = nodeContentType + } else { + if commonType != nodeContentType { + return .generic + } + } + } + + return commonType ?? .generic + } } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index 256f17800c..ab03540a15 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -4949,7 +4949,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro self?.deactivateSearch() }, fieldStyle: .glass) } else if let currentPaneKey = self.paneContainerNode.currentPaneKey, case .savedMessagesChats = currentPaneKey { - let contentNode = ChatListSearchContainerNode(context: self.context, animationCache: self.context.animationCache, animationRenderer: self.context.animationRenderer, filter: [.removeSearchHeader], requestPeerType: nil, location: .savedMessagesChats(peerId: self.context.account.peerId), displaySearchFilters: false, hasDownloads: false, initialFilter: .chats, openPeer: { [weak self] peer, _, _, _ in + let contentNode = ChatListSearchContainerNode(context: self.context, animationCache: self.context.animationCache, animationRenderer: self.context.animationRenderer, filter: [.removeSearchHeader], requestPeerType: nil, location: .savedMessagesChats(peerId: self.context.account.peerId), folder: nil, displaySearchFilters: false, hasDownloads: false, initialFilter: .chats, openPeer: { [weak self] peer, _, _, _ in guard let self else { return } @@ -6460,7 +6460,9 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc separatorColor: .clear, badgeBackgroundColor: baseNavigationBarPresentationData.theme.badgeBackgroundColor, badgeStrokeColor: baseNavigationBarPresentationData.theme.badgeStrokeColor, - badgeTextColor: baseNavigationBarPresentationData.theme.badgeTextColor + badgeTextColor: baseNavigationBarPresentationData.theme.badgeTextColor, + accentButtonColor: baseNavigationBarPresentationData.theme.accentButtonColor, + accentForegroundColor: baseNavigationBarPresentationData.theme.accentForegroundColor ), strings: baseNavigationBarPresentationData.strings)) self._hasGlassStyle = true diff --git a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift index aedd7a31fa..b438e2e511 100644 --- a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift +++ b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift @@ -1330,6 +1330,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { filter: self.filter, requestPeerType: self.requestPeerType, location: chatListLocation, + folder: nil, displaySearchFilters: false, hasDownloads: false, openPeer: { [weak self] peer, chatPeer, threadId, _ in diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift index 568d6bf79b..1a5ff0c4bb 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift @@ -1172,7 +1172,7 @@ extension ChatControllerImpl { } return updatedState }) - self.searchResult.set(.single((results, state, .general(scope: .channels, tags: nil, minDate: nil, maxDate: nil)))) + self.searchResult.set(.single((results, state, .general(scope: .channels, tags: nil, minDate: nil, maxDate: nil, folderId: nil)))) } } @@ -2412,7 +2412,7 @@ extension ChatControllerImpl { } } }, openCalendarSearch: { [weak self] in - self?.openCalendarSearch(timestamp: Int32(Date().timeIntervalSince1970)) + self?.openCalendarSearch(timestamp: Int32(Date().timeIntervalSince1970), isMedia: false) }, toggleMembersSearch: { [weak self] value in if let strongSelf = self { strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { state in diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 3269558e13..b7293406a9 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -3468,7 +3468,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G switch strongSelf.chatLocation { case let .peer(peerId): if alreadyThere { - strongSelf.openCalendarSearch(timestamp: timestamp) + strongSelf.openCalendarSearch(timestamp: timestamp, isMedia: true) } else { strongSelf.navigateToMessage(from: nil, to: .index(MessageIndex(id: MessageId(peerId: peerId, namespace: 0, id: 0), timestamp: timestamp - Int32(NSTimeZone.local.secondsFromGMT()))), scrollPosition: .bottom(0.0), rememberInStack: false, animated: true, completion: nil) } diff --git a/submodules/TelegramUI/Sources/ChatControllerContentData.swift b/submodules/TelegramUI/Sources/ChatControllerContentData.swift index c7661a559a..0d6d1b2b6d 100644 --- a/submodules/TelegramUI/Sources/ChatControllerContentData.swift +++ b/submodules/TelegramUI/Sources/ChatControllerContentData.swift @@ -14,6 +14,7 @@ import ChatListUI import EmojiStatusComponent import TelegramUIPreferences import TranslateUI +import TelegramNotices extension ChatControllerImpl { final class ContentData { @@ -507,6 +508,25 @@ extension ChatControllerImpl { messageOptionsTitleInfo = .single(nil) } + var savedMessagesChatsTip: Signal = .single(false) + if case .peer(context.account.peerId) = chatLocation { + let hasSavedChats = context.engine.messages.savedMessagesHasPeersOtherThanSaved() + if chatLocation.threadId == nil { + savedMessagesChatsTip = hasSavedChats + |> distinctUntilChanged + |> mapToSignal { value -> Signal in + if !value { + return .single(false) + } + return ApplicationSpecificNotice.getSavedMessagesChatListView(accountManager: context.sharedContext.accountManager) + |> map { value -> Bool in + return value < 5 + } + } + |> distinctUntilChanged + } + } + self.titleDisposable = (combineLatest( queue: Queue.mainQueue(), peerView.get(), @@ -515,9 +535,10 @@ extension ChatControllerImpl { subtitleTextSignal, configuration, hasPeerInfo, - messageOptionsTitleInfo + messageOptionsTitleInfo, + savedMessagesChatsTip ) - |> deliverOnMainQueue).startStrict(next: { [weak self] peerView, onlineMemberCount, displayedCount, subtitleText, configuration, hasPeerInfo, messageOptionsTitleInfo in + |> deliverOnMainQueue).startStrict(next: { [weak self] peerView, onlineMemberCount, displayedCount, subtitleText, configuration, hasPeerInfo, messageOptionsTitleInfo, savedMessagesChatsTip in guard let strongSelf = self else { return } @@ -586,7 +607,13 @@ extension ChatControllerImpl { strongSelf.state.chatTitleContent = .custom(title: [ChatTitleContent.TitleTextItem(id: AnyHashable(0), content: .text(channel.debugDisplayTitle))], subtitle: nil, isEnabled: true) } } else { - strongSelf.state.chatTitleContent = .peer(peerView: ChatTitleContent.PeerData(peerView: peerView), customTitle: nil, customSubtitle: nil, onlineMemberCount: onlineMemberCount, isScheduledMessages: isScheduledMessages, isMuted: nil, customMessageCount: nil, isEnabled: hasPeerInfo) + var customSubtitle: String? + if savedMessagesChatsTip { + //TODO:localize + customSubtitle = "Tap to view as chats" + } + + strongSelf.state.chatTitleContent = .peer(peerView: ChatTitleContent.PeerData(peerView: peerView), customTitle: nil, customSubtitle: customSubtitle, onlineMemberCount: onlineMemberCount, isScheduledMessages: isScheduledMessages, isMuted: nil, customMessageCount: nil, isEnabled: hasPeerInfo) let imageOverride: AvatarNodeImageOverride? if context.account.peerId == peer.id { diff --git a/submodules/TelegramUI/Sources/ChatControllerOpenCalendarSearch.swift b/submodules/TelegramUI/Sources/ChatControllerOpenCalendarSearch.swift index 81ead39eac..821488a047 100644 --- a/submodules/TelegramUI/Sources/ChatControllerOpenCalendarSearch.swift +++ b/submodules/TelegramUI/Sources/ChatControllerOpenCalendarSearch.swift @@ -10,83 +10,48 @@ import ChatControllerInteraction import Display import UIKit import UndoUI +import ChatScheduleTimeController extension ChatControllerImpl { - func openCalendarSearch(timestamp: Int32) { + func openCalendarSearch(timestamp: Int32, isMedia: Bool) { guard let peerId = self.chatLocation.peerId else { return } self.chatDisplayNode.dismissInput() - - let initialTimestamp = timestamp - var dismissCalendarScreen: (() -> Void)? - var selectDay: ((Int32) -> Void)? - var openClearHistory: ((Int32) -> Void)? - - let enableMessageRangeDeletion: Bool = peerId.namespace == Namespaces.Peer.CloudUser - let displayMedia = self.presentationInterfaceState.historyFilter == nil - - let calendarScreen = CalendarMessageScreen( - context: self.context, - peerId: peerId, - calendarSource: self.context.engine.messages.sparseMessageCalendar(peerId: peerId, threadId: self.chatLocation.threadId, tag: .photoOrVideo, displayMedia: displayMedia), - initialTimestamp: initialTimestamp, - enableMessageRangeDeletion: enableMessageRangeDeletion, - canNavigateToEmptyDays: true, - navigateToDay: { [weak self] c, index, timestamp in - guard let strongSelf = self else { - c.dismiss() - return - } - - strongSelf.alwaysShowSearchResultsAsList = false - strongSelf.chatDisplayNode.alwaysShowSearchResultsAsList = false - strongSelf.updateChatPresentationInterfaceState(animated: false, interactive: false, { state in - return state.updatedDisplayHistoryFilterAsList(false).updatedSearch(nil) - }) - - c.dismiss() - - strongSelf.loadingMessage.set(.single(.generic)) - - let peerId: PeerId - let threadId: Int64? - switch strongSelf.chatLocation { - case let .peer(peerIdValue): - peerId = peerIdValue - threadId = nil - case let .replyThread(replyThreadMessage): - peerId = replyThreadMessage.peerId - threadId = replyThreadMessage.threadId - case .customChatContents: - return - } - - strongSelf.messageIndexDisposable.set((strongSelf.context.engine.messages.searchMessageIdByTimestamp(peerId: peerId, threadId: threadId, timestamp: timestamp) |> deliverOnMainQueue).startStrict(next: { messageId in - if let strongSelf = self { - strongSelf.loadingMessage.set(.single(nil)) - if let messageId = messageId { - strongSelf.navigateToMessage(from: nil, to: .id(messageId, NavigateToMessageParams(timestamp: nil, quote: nil)), forceInCurrentChat: true) - } + if isMedia { + let initialTimestamp = timestamp + var dismissCalendarScreen: (() -> Void)? + var selectDay: ((Int32) -> Void)? + var openClearHistory: ((Int32) -> Void)? + + let enableMessageRangeDeletion: Bool = peerId.namespace == Namespaces.Peer.CloudUser + + let displayMedia = self.presentationInterfaceState.historyFilter == nil + + let calendarScreen = CalendarMessageScreen( + context: self.context, + peerId: peerId, + calendarSource: self.context.engine.messages.sparseMessageCalendar(peerId: peerId, threadId: self.chatLocation.threadId, tag: .photoOrVideo, displayMedia: displayMedia), + initialTimestamp: initialTimestamp, + enableMessageRangeDeletion: enableMessageRangeDeletion, + canNavigateToEmptyDays: true, + navigateToDay: { [weak self] c, index, timestamp in + guard let strongSelf = self else { + c.dismiss() + return } - })) - }, - previewDay: { [weak self] timestamp, _, sourceNode, sourceRect, gesture in - guard let strongSelf = self else { - return - } - - var items: [ContextMenuItem] = [] - - items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Chat_JumpToDate, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/GoToMessage"), color: theme.contextMenu.primaryColor) - }, action: { _, f in - f(.dismissWithoutContent) - dismissCalendarScreen?() - + + strongSelf.alwaysShowSearchResultsAsList = false + strongSelf.chatDisplayNode.alwaysShowSearchResultsAsList = false + strongSelf.updateChatPresentationInterfaceState(animated: false, interactive: false, { state in + return state.updatedDisplayHistoryFilterAsList(false).updatedSearch(nil) + }) + + c.dismiss() + strongSelf.loadingMessage.set(.single(.generic)) - + let peerId: PeerId let threadId: Int64? switch strongSelf.chatLocation { @@ -99,7 +64,7 @@ extension ChatControllerImpl { case .customChatContents: return } - + strongSelf.messageIndexDisposable.set((strongSelf.context.engine.messages.searchMessageIdByTimestamp(peerId: peerId, threadId: threadId, timestamp: timestamp) |> deliverOnMainQueue).startStrict(next: { messageId in if let strongSelf = self { strongSelf.loadingMessage.set(.single(nil)) @@ -108,84 +73,168 @@ extension ChatControllerImpl { } } })) - }))) - - if enableMessageRangeDeletion && (peerId.namespace == Namespaces.Peer.CloudUser || peerId.namespace == Namespaces.Peer.SecretChat) { - items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.DialogList_ClearHistoryConfirmation, textColor: .destructive, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) + }, + previewDay: { [weak self] timestamp, _, sourceNode, sourceRect, gesture in + guard let strongSelf = self else { + return + } + + var items: [ContextMenuItem] = [] + + items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Chat_JumpToDate, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/GoToMessage"), color: theme.contextMenu.primaryColor) }, action: { _, f in f(.dismissWithoutContent) - openClearHistory?(timestamp) - }))) - - items.append(.separator) - - items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Common_Select, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Select"), color: theme.contextMenu.primaryColor) - }, action: { _, f in - f(.dismissWithoutContent) - selectDay?(timestamp) - }))) - } - - let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peerId), subject: .message(id: .timestamp(timestamp), highlight: nil, timecode: nil, setupReply: false), botStart: nil, mode: .standard(.previewing), params: nil) - chatController.canReadHistory.set(false) - - strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts() - - let contextController = makeContextController(presentationData: strongSelf.presentationData, source: .controller(ChatContextControllerContentSourceImpl(controller: chatController, sourceNode: sourceNode, sourceRect: sourceRect, passthroughTouches: true)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) - strongSelf.presentInGlobalOverlay(contextController) - } - ) - - calendarScreen.completedWithRemoveMessagesInRange = { [weak self] range, type, dayCount, calendarSource in - guard let strongSelf = self else { - return - } - - let statusText: String - switch type { - case .forEveryone: - statusText = strongSelf.presentationData.strings.Chat_MessageRangeDeleted_ForBothSides(Int32(dayCount)) - default: - statusText = strongSelf.presentationData.strings.Chat_MessageRangeDeleted_ForMe(Int32(dayCount)) - } - - strongSelf.chatDisplayNode.historyNode.ignoreMessagesInTimestampRange = range - - strongSelf.present(UndoOverlayController(presentationData: strongSelf.context.sharedContext.currentPresentationData.with { $0 }, content: .removedChat(context: strongSelf.context, title: NSAttributedString(string: statusText), text: nil), elevatedLayout: false, action: { value in - guard let strongSelf = self else { - return false - } - - if value == .commit { - let _ = calendarSource.removeMessagesInRange(minTimestamp: range.lowerBound, maxTimestamp: range.upperBound, type: type, completion: { - Queue.mainQueue().after(1.0, { - guard let strongSelf = self else { - return + dismissCalendarScreen?() + + strongSelf.loadingMessage.set(.single(.generic)) + + let peerId: PeerId + let threadId: Int64? + switch strongSelf.chatLocation { + case let .peer(peerIdValue): + peerId = peerIdValue + threadId = nil + case let .replyThread(replyThreadMessage): + peerId = replyThreadMessage.peerId + threadId = replyThreadMessage.threadId + case .customChatContents: + return + } + + strongSelf.messageIndexDisposable.set((strongSelf.context.engine.messages.searchMessageIdByTimestamp(peerId: peerId, threadId: threadId, timestamp: timestamp) |> deliverOnMainQueue).startStrict(next: { messageId in + if let strongSelf = self { + strongSelf.loadingMessage.set(.single(nil)) + if let messageId = messageId { + strongSelf.navigateToMessage(from: nil, to: .id(messageId, NavigateToMessageParams(timestamp: nil, quote: nil)), forceInCurrentChat: true) + } } - strongSelf.chatDisplayNode.historyNode.ignoreMessagesInTimestampRange = nil - }) - }) - return true - } else if value == .undo { - strongSelf.chatDisplayNode.historyNode.ignoreMessagesInTimestampRange = nil - return true + })) + }))) + + if enableMessageRangeDeletion && (peerId.namespace == Namespaces.Peer.CloudUser || peerId.namespace == Namespaces.Peer.SecretChat) { + items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.DialogList_ClearHistoryConfirmation, textColor: .destructive, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) + }, action: { _, f in + f(.dismissWithoutContent) + openClearHistory?(timestamp) + }))) + + items.append(.separator) + + items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Common_Select, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Select"), color: theme.contextMenu.primaryColor) + }, action: { _, f in + f(.dismissWithoutContent) + selectDay?(timestamp) + }))) + } + + let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peerId), subject: .message(id: .timestamp(timestamp), highlight: nil, timecode: nil, setupReply: false), botStart: nil, mode: .standard(.previewing), params: nil) + chatController.canReadHistory.set(false) + + strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts() + + let contextController = makeContextController(presentationData: strongSelf.presentationData, source: .controller(ChatContextControllerContentSourceImpl(controller: chatController, sourceNode: sourceNode, sourceRect: sourceRect, passthroughTouches: true)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + strongSelf.presentInGlobalOverlay(contextController) } - return false - }), in: .current) - } - - self.effectiveNavigationController?.pushViewController(calendarScreen) - - dismissCalendarScreen = { [weak calendarScreen] in - calendarScreen?.dismiss(completion: nil) - } - selectDay = { [weak calendarScreen] timestamp in - calendarScreen?.selectDay(timestamp: timestamp) - } - openClearHistory = { [weak calendarScreen] timestamp in - calendarScreen?.openClearHistory(timestamp: timestamp) + ) + + calendarScreen.completedWithRemoveMessagesInRange = { [weak self] range, type, dayCount, calendarSource in + guard let strongSelf = self else { + return + } + + let statusText: String + switch type { + case .forEveryone: + statusText = strongSelf.presentationData.strings.Chat_MessageRangeDeleted_ForBothSides(Int32(dayCount)) + default: + statusText = strongSelf.presentationData.strings.Chat_MessageRangeDeleted_ForMe(Int32(dayCount)) + } + + strongSelf.chatDisplayNode.historyNode.ignoreMessagesInTimestampRange = range + + strongSelf.present(UndoOverlayController(presentationData: strongSelf.context.sharedContext.currentPresentationData.with { $0 }, content: .removedChat(context: strongSelf.context, title: NSAttributedString(string: statusText), text: nil), elevatedLayout: false, action: { value in + guard let strongSelf = self else { + return false + } + + if value == .commit { + let _ = calendarSource.removeMessagesInRange(minTimestamp: range.lowerBound, maxTimestamp: range.upperBound, type: type, completion: { + Queue.mainQueue().after(1.0, { + guard let strongSelf = self else { + return + } + strongSelf.chatDisplayNode.historyNode.ignoreMessagesInTimestampRange = nil + }) + }) + return true + } else if value == .undo { + strongSelf.chatDisplayNode.historyNode.ignoreMessagesInTimestampRange = nil + return true + } + return false + }), in: .current) + } + + self.effectiveNavigationController?.pushViewController(calendarScreen) + + dismissCalendarScreen = { [weak calendarScreen] in + calendarScreen?.dismiss(completion: nil) + } + selectDay = { [weak calendarScreen] timestamp in + calendarScreen?.selectDay(timestamp: timestamp) + } + openClearHistory = { [weak calendarScreen] timestamp in + calendarScreen?.openClearHistory(timestamp: timestamp) + } + } else { + let controller = ChatScheduleTimeScreen( + context: self.context, + mode: .search, + currentTime: nil, + currentRepeatPeriod: nil, + minimalTime: nil, + isDark: false, + completion: { [weak self] result in + guard let self else { + return + } + + self.alwaysShowSearchResultsAsList = false + self.chatDisplayNode.alwaysShowSearchResultsAsList = false + self.updateChatPresentationInterfaceState(animated: false, interactive: false, { state in + return state.updatedDisplayHistoryFilterAsList(false).updatedSearch(nil) + }) + + self.loadingMessage.set(.single(.generic)) + + let peerId: PeerId + let threadId: Int64? + switch self.chatLocation { + case let .peer(peerIdValue): + peerId = peerIdValue + threadId = nil + case let .replyThread(replyThreadMessage): + peerId = replyThreadMessage.peerId + threadId = replyThreadMessage.threadId + case .customChatContents: + return + } + + self.messageIndexDisposable.set((self.context.engine.messages.searchMessageIdByTimestamp(peerId: peerId, threadId: threadId, timestamp: result.time) |> deliverOnMainQueue).startStrict(next: { [weak self] messageId in + guard let self else { + return + } + self.loadingMessage.set(.single(nil)) + if let messageId { + self.navigateToMessage(from: nil, to: .id(messageId, NavigateToMessageParams(timestamp: nil, quote: nil)), forceInCurrentChat: true) + } + })) + } + ) + self.push(controller) } } } diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateNavigationButtons.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateNavigationButtons.swift index 82c58604fa..803f089285 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateNavigationButtons.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateNavigationButtons.swift @@ -47,10 +47,14 @@ func leftNavigationButtonForChatInterfaceState(_ presentationInterfaceState: Cha } if canClear { - return ChatNavigationButton(action: .clearHistory, buttonItem: UIBarButtonItem(title: title, style: .plain, target: target, action: selector)) + let buttonItem = UIBarButtonItem(title: "___clear", style: .plain, target: target, action: selector) + buttonItem.accessibilityLabel = title + return ChatNavigationButton(action: .clearHistory, buttonItem: buttonItem) } else { title = strings.Conversation_ClearCache - return ChatNavigationButton(action: .clearCache, buttonItem: UIBarButtonItem(title: title, style: .plain, target: target, action: selector)) + let buttonItem = UIBarButtonItem(title: "___clear", style: .plain, target: target, action: selector) + buttonItem.accessibilityLabel = title + return ChatNavigationButton(action: .clearCache, buttonItem: buttonItem) } } } @@ -88,7 +92,7 @@ func rightNavigationButtonForChatInterfaceState(context: AccountContext, present if let currentButton = currentButton, currentButton.action == .cancelMessageSelection { return currentButton } else { - let buttonItem = UIBarButtonItem(title: strings.Common_Cancel, style: .plain, target: target, action: selector) + let buttonItem = UIBarButtonItem(title: "___done", style: .plain, target: target, action: selector) buttonItem.accessibilityLabel = strings.Common_Cancel return ChatNavigationButton(action: .cancelMessageSelection, buttonItem: buttonItem) } diff --git a/submodules/WebSearchUI/Sources/WebSearchGalleryController.swift b/submodules/WebSearchUI/Sources/WebSearchGalleryController.swift index b6d8b61f14..48266cf949 100644 --- a/submodules/WebSearchUI/Sources/WebSearchGalleryController.swift +++ b/submodules/WebSearchUI/Sources/WebSearchGalleryController.swift @@ -60,7 +60,7 @@ final class WebSearchGalleryControllerPresentationArguments { } class WebSearchGalleryController: ViewController { - private static let navigationTheme = NavigationBarTheme(overallDarkAppearance: false, buttonColor: .white, disabledButtonColor: UIColor(rgb: 0x525252), primaryTextColor: .white, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear) + private static let navigationTheme = NavigationBarTheme(overallDarkAppearance: false, buttonColor: .white, disabledButtonColor: UIColor(rgb: 0x525252), primaryTextColor: .white, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear, accentButtonColor: .white, accentForegroundColor: .black) private var galleryNode: GalleryControllerNode { return self.displayNode as! GalleryControllerNode diff --git a/submodules/WebUI/Sources/WebAppController.swift b/submodules/WebUI/Sources/WebAppController.swift index 7e0ce945ea..7c217e478c 100644 --- a/submodules/WebUI/Sources/WebAppController.swift +++ b/submodules/WebUI/Sources/WebAppController.swift @@ -3800,7 +3800,9 @@ public final class WebAppController: ViewController, AttachmentContainable { separatorColor: UIColor(rgb: 0x000000, alpha: 0.25), badgeBackgroundColor: .clear, badgeStrokeColor: .clear, - badgeTextColor: .clear + badgeTextColor: .clear, + accentButtonColor: self.presentationData.theme.list.itemCheckColors.fillColor, + accentForegroundColor: self.presentationData.theme.list.itemCheckColors.foregroundColor ), strings: NavigationBarStrings(back: "", close: "") )