diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index 153e602200..907d71aa7b 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -923,6 +923,32 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { let searchStatePromise = self.searchStatePromise let selectionPromise = self.selectedMessagesPromise + let previousRecentlySearchedPeerOrder = Atomic<[EnginePeer.Id]>(value: []) + let fixedRecentlySearchedPeers = context.engine.peers.recentlySearchedPeers() + |> map { peers -> [RecentlySearchedPeer] in + var result: [RecentlySearchedPeer] = [] + let _ = previousRecentlySearchedPeerOrder.modify { current in + var updated: [EnginePeer.Id] = [] + for id in current { + inner: for peer in peers { + if peer.peer.peerId == id { + updated.append(id) + result.append(peer) + break inner + } + } + } + for peer in peers.reversed() { + if !updated.contains(peer.peer.peerId) { + updated.insert(peer.peer.peerId, at: 0) + result.insert(peer, at: 0) + } + } + return updated + } + return result + } + let downloadItems: Signal<(inProgressItems: [DownloadItem], doneItems: [RenderedRecentDownloadItem]), NoError> if key == .downloads { var firstTime = true @@ -1207,8 +1233,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { } }) - return combineLatest(accountPeer, foundLocalPeers, foundRemotePeers, foundRemoteMessages, presentationDataPromise.get(), searchStatePromise.get(), selectionPromise.get(), resolvedMessage) - |> map { accountPeer, foundLocalPeers, foundRemotePeers, foundRemoteMessages, presentationData, searchState, selectionState, resolvedMessage -> ([ChatListSearchEntry], Bool)? in + return combineLatest(accountPeer, foundLocalPeers, foundRemotePeers, foundRemoteMessages, presentationDataPromise.get(), searchStatePromise.get(), selectionPromise.get(), resolvedMessage, fixedRecentlySearchedPeers) + |> map { accountPeer, foundLocalPeers, foundRemotePeers, foundRemoteMessages, presentationData, searchState, selectionState, resolvedMessage, recentPeers -> ([ChatListSearchEntry], Bool)? in let isSearching = foundRemotePeers.2 || foundRemoteMessages.1 var entries: [ChatListSearchEntry] = [] var index = 0 @@ -1293,6 +1319,30 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { } } + if lowercasedQuery.count > 1 { + for peer in recentPeers { + if let peer = peer.peer.chatMainPeer, !existingPeerIds.contains(peer.id) { + let peer = EnginePeer(peer) + + var matches = false + if case let .user(user) = peer { + if let firstName = user.firstName, firstName.lowercased().hasPrefix(lowercasedQuery) { + matches = true + } else if let lastName = user.lastName, lastName.lowercased().hasPrefix(lowercasedQuery) { + matches = true + } + } else if peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder).lowercased().hasPrefix(lowercasedQuery) { + matches = true + } + + if matches { + existingPeerIds.insert(peer.id) + entries.append(.localPeer(peer, nil, nil, index, presentationData.theme, presentationData.strings, presentationData.nameSortOrder, presentationData.nameDisplayOrder, localExpandType)) + } + } + } + } + var numberOfLocalPeers = 0 for renderedPeer in foundLocalPeers.peers { if case .expand = localExpandType, numberOfLocalPeers >= 3 { @@ -1745,32 +1795,6 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { } |> distinctUntilChanged - let previousRecentlySearchedPeerOrder = Atomic<[EnginePeer.Id]>(value: []) - let fixedRecentlySearchedPeers = context.engine.peers.recentlySearchedPeers() - |> map { peers -> [RecentlySearchedPeer] in - var result: [RecentlySearchedPeer] = [] - let _ = previousRecentlySearchedPeerOrder.modify { current in - var updated: [EnginePeer.Id] = [] - for id in current { - inner: for peer in peers { - if peer.peer.peerId == id { - updated.append(id) - result.append(peer) - break inner - } - } - } - for peer in peers.reversed() { - if !updated.contains(peer.peer.peerId) { - updated.insert(peer.peer.peerId, at: 0) - result.insert(peer, at: 0) - } - } - return updated - } - return result - } - var recentItems = combineLatest(hasRecentPeers, fixedRecentlySearchedPeers, presentationDataPromise.get()) |> mapToSignal { hasRecentPeers, peers, presentationData -> Signal<[ChatListRecentEntry], NoError> in var entries: [ChatListRecentEntry] = [] diff --git a/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift b/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift index fcc0353ecc..0beaa3ea52 100644 --- a/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift +++ b/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift @@ -68,6 +68,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { var currentAccessibilityAreas: [AccessibilityAreaNode] = [] + private var previousContentOffset: CGPoint? private var isDeceleratingBecauseOfDragging = false private let hiddenMediaDisposable = MetaDisposable() @@ -392,6 +393,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { } self.scrollNode.view.contentOffset = contentOffset if didSetScrollOffset { + self.previousContentOffset = contentOffset self.updateNavigationBar() if self.currentLayout != nil { self.setupScrollOffsetOnLayout = false @@ -707,6 +709,8 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { func scrollViewDidScroll(_ scrollView: UIScrollView) { self.updateVisibleItems(visibleBounds: self.scrollNode.view.bounds) + self.updateNavigationBar() + self.previousContentOffset = self.scrollNode.view.contentOffset } func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { @@ -727,6 +731,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { } let bounds = self.scrollNode.view.bounds + let contentOffset = self.scrollNode.view.contentOffset let maxBarHeight: CGFloat let minBarHeight: CGFloat @@ -742,14 +747,55 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { minBarHeight = 20.0 } - let transition: ContainedViewLayoutTransition = .immediate + var pageProgress: CGFloat = 0.0 + if !self.scrollNode.view.contentSize.height.isZero { + let value = (contentOffset.y + self.scrollNode.view.contentInset.top) / (self.scrollNode.view.contentSize.height - bounds.size.height + self.scrollNode.view.contentInset.top) + pageProgress = max(0.0, min(1.0, value)) + } + + let delta: CGFloat + if self.setupScrollOffsetOnLayout { + delta = 0.0 + } else if let previousContentOffset = self.previousContentOffset { + delta = contentOffset.y - previousContentOffset.y + } else { + delta = 0.0 + } + self.previousContentOffset = contentOffset + + var transition: ContainedViewLayoutTransition = .immediate var navigationBarFrame = self.navigationBar.frame navigationBarFrame.size.width = bounds.size.width if navigationBarFrame.size.height.isZero { navigationBarFrame.size.height = maxBarHeight } - navigationBarFrame.size.height = maxBarHeight + if case .regular = containerLayout.metrics.widthClass { + navigationBarFrame.size.height = maxBarHeight + } else { + if forceState { + transition = .animated(duration: 0.3, curve: .spring) + + let transitionFactor = (navigationBarFrame.size.height - minBarHeight) / (maxBarHeight - minBarHeight) + + if contentOffset.y <= -self.scrollNode.view.contentInset.top || transitionFactor > 0.4 { + navigationBarFrame.size.height = maxBarHeight + } else { + navigationBarFrame.size.height = minBarHeight + } + } else { + if contentOffset.y <= -self.scrollNode.view.contentInset.top { + navigationBarFrame.size.height = maxBarHeight + } else { + navigationBarFrame.size.height -= delta + } + navigationBarFrame.size.height = max(minBarHeight, min(maxBarHeight, navigationBarFrame.size.height)) + } + + if self.setupScrollOffsetOnLayout { + navigationBarFrame.size.height = maxBarHeight + } + } let transitionFactor = (navigationBarFrame.size.height - minBarHeight) / (maxBarHeight - minBarHeight) @@ -768,7 +814,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { } transition.updateFrame(node: self.navigationBar, frame: navigationBarFrame) - self.navigationBar.updateLayout(size: navigationBarFrame.size, minHeight: minBarHeight, maxHeight: maxBarHeight, topInset: containerLayout.safeInsets.top, leftInset: containerLayout.safeInsets.left, rightInset: containerLayout.safeInsets.right, title: title, pageProgress: 0.0, transition: transition) + self.navigationBar.updateLayout(size: navigationBarFrame.size, minHeight: minBarHeight, maxHeight: maxBarHeight, topInset: containerLayout.safeInsets.top, leftInset: containerLayout.safeInsets.left, rightInset: containerLayout.safeInsets.right, title: title, pageProgress: pageProgress, transition: transition) transition.animateView { self.scrollNode.view.scrollIndicatorInsets = UIEdgeInsets(top: navigationBarFrame.size.height, left: 0.0, bottom: containerLayout.intrinsicInsets.bottom, right: 0.0) diff --git a/submodules/InstantPageUI/Sources/InstantPageNavigationBar.swift b/submodules/InstantPageUI/Sources/InstantPageNavigationBar.swift index 5fea9c5f6f..b2d6bb5283 100644 --- a/submodules/InstantPageUI/Sources/InstantPageNavigationBar.swift +++ b/submodules/InstantPageUI/Sources/InstantPageNavigationBar.swift @@ -169,7 +169,7 @@ final class InstantPageNavigationBar: ASDisplayNode { func updateLayout(size: CGSize, minHeight: CGFloat, maxHeight: CGFloat, topInset: CGFloat, leftInset: CGFloat, rightInset: CGFloat, title: String?, pageProgress: CGFloat, transition: ContainedViewLayoutTransition) { let progressHeight: CGFloat if !topInset.isZero { - progressHeight = size.height - topInset + 11.0 + progressHeight = size.height - topInset + 11.0 - UIScreenPixel } else { progressHeight = size.height } diff --git a/submodules/ItemListAvatarAndNameInfoItem/Sources/ItemListAvatarAndNameItem.swift b/submodules/ItemListAvatarAndNameInfoItem/Sources/ItemListAvatarAndNameItem.swift index c11d8955b7..116bb4845c 100644 --- a/submodules/ItemListAvatarAndNameInfoItem/Sources/ItemListAvatarAndNameItem.swift +++ b/submodules/ItemListAvatarAndNameInfoItem/Sources/ItemListAvatarAndNameItem.swift @@ -808,6 +808,7 @@ public class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNo if strongSelf.inputFirstField == nil { let inputFirstField = TextFieldNodeView() + inputFirstField.returnKeyType = .done inputFirstField.delegate = self inputFirstField.font = Font.regular(floor(item.presentationData.fontSize.itemListBaseFontSize * 19.0 / 17.0)) inputFirstField.autocorrectionType = .no diff --git a/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift b/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift index d404648dcb..3b9353eac5 100644 --- a/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift @@ -178,13 +178,13 @@ public func customizeDefaultDarkPresentationTheme(theme: PresentationTheme, edit reactionInactiveBackground: UIColor(rgb: 0xffffff, alpha: 0.07), reactionInactiveForeground: UIColor(rgb: 0xffffff), reactionActiveBackground: accentColor, - reactionActiveForeground: UIColor(rgb: 0xffffff) + reactionActiveForeground: monochrome ? UIColor(rgb: 0x000000) : UIColor(rgb: 0xffffff) ), withoutWallpaper: chat.message.incoming.bubble.withoutWallpaper.withUpdated( reactionInactiveBackground: UIColor(rgb: 0xffffff, alpha: 0.07), reactionInactiveForeground: UIColor(rgb: 0xffffff), reactionActiveBackground: accentColor, - reactionActiveForeground: UIColor(rgb: 0xffffff) + reactionActiveForeground: monochrome ? UIColor(rgb: 0x000000) : UIColor(rgb: 0xffffff) ) ), linkTextColor: accentColor, @@ -250,13 +250,13 @@ public func customizeDefaultDarkPresentationTheme(theme: PresentationTheme, edit reactionInactiveBackground: UIColor(rgb: 0xffffff, alpha: 0.12), reactionInactiveForeground: UIColor(rgb: 0xffffff), reactionActiveBackground: accentColor, - reactionActiveForeground: UIColor(rgb: 0xffffff) + reactionActiveForeground: monochrome ? UIColor(rgb: 0x000000) : UIColor(rgb: 0xffffff) ), withoutWallpaper: chat.message.freeform.withoutWallpaper.withUpdated( reactionInactiveBackground: chat.message.incoming.bubble.withoutWallpaper.fill.last, reactionInactiveForeground: UIColor(rgb: 0xffffff), reactionActiveBackground: accentColor, - reactionActiveForeground: UIColor(rgb: 0xffffff) + reactionActiveForeground: monochrome ? UIColor(rgb: 0x000000) : UIColor(rgb: 0xffffff) ) ), infoLinkTextColor: accentColor, diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 217ae1eb25..b74bb956ae 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -6853,13 +6853,24 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G media = .keep } - strongSelf.context.account.pendingUpdateMessageManager.add(messageId: editMessage.messageId, text: text.string, media: media, entities: entitiesAttribute, disableUrlPreview: disableUrlPreview) - - strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { state in - var state = state - state = state.updatedInterfaceState({ $0.withUpdatedEditMessage(nil) }) - state = state.updatedEditMessageState(nil) - return state + let _ = (strongSelf.context.account.postbox.messageAtId(editMessage.messageId) + |> deliverOnMainQueue) + .start(next: { [weak self] currentMessage in + if let strongSelf = self { + if let currentMessage = currentMessage { + let currentEntities = currentMessage.textEntitiesAttribute?.entities ?? [] + if currentMessage.text != text.string || currentEntities != entities { + strongSelf.context.account.pendingUpdateMessageManager.add(messageId: editMessage.messageId, text: text.string, media: media, entities: entitiesAttribute, disableUrlPreview: disableUrlPreview) + } + } + + strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { state in + var state = state + state = state.updatedInterfaceState({ $0.withUpdatedEditMessage(nil) }) + state = state.updatedEditMessageState(nil) + return state + }) + } }) } }, beginMessageSearch: { [weak self] domain, query in diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift index 1b2dd9f8b8..de6c081f85 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift @@ -182,8 +182,6 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio let factor: CGFloat = max(0.0, min(1.0, (scale - 1.0) * 8.0)) - transition.updateAlpha(node: strongSelf.dateAndStatusNode, alpha: 1.0 - factor) - if abs(scale - 1.0) > CGFloat.ulpOfOne { var highQualityImageNode: TransformImageNode? if let current = strongSelf.highQualityImageNode { @@ -241,6 +239,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio }) } + transition.updateAlpha(node: strongSelf.dateAndStatusNode, alpha: 1.0 - factor) if let badgeNode = strongSelf.badgeNode { transition.updateAlpha(node: badgeNode, alpha: 1.0 - factor) } @@ -1614,6 +1613,14 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio statusNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) } } + if self.dateAndStatusNode.isHidden != isHidden { + if isHidden { + self.dateAndStatusNode.isHidden = true + } else { + self.dateAndStatusNode.isHidden = false + self.dateAndStatusNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } + } } func transitionNode() -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? { @@ -1634,6 +1641,11 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio statusNodeHidden = statusNode.isHidden statusNode.isHidden = true } + var dateAndStatusNodeHidden: Bool? + if let dateAndStatusNode = self?.dateAndStatusNode { + dateAndStatusNodeHidden = dateAndStatusNode.isHidden + dateAndStatusNode.isHidden = true + } let view: UIView? if let strongSelf = self, strongSelf.imageNode.captureProtected { @@ -1658,6 +1670,9 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio if let statusNode = self?.statusNode, let statusNodeHidden = statusNodeHidden { statusNode.isHidden = statusNodeHidden } + if let dateAndStatusNode = self?.dateAndStatusNode, let dateAndStatusNodeHidden = dateAndStatusNodeHidden { + dateAndStatusNode.isHidden = dateAndStatusNodeHidden + } return (view, nil) }) } diff --git a/submodules/TelegramUI/Sources/CreateGroupController.swift b/submodules/TelegramUI/Sources/CreateGroupController.swift index ffc73fa0b3..1969a57922 100644 --- a/submodules/TelegramUI/Sources/CreateGroupController.swift +++ b/submodules/TelegramUI/Sources/CreateGroupController.swift @@ -235,6 +235,8 @@ private enum CreateGroupEntry: ItemListNodeEntry { case let .groupInfo(_, _, dateTimeFormat, peer, state, avatar): return ItemListAvatarAndNameInfoItem(accountContext: arguments.context, presentationData: presentationData, dateTimeFormat: dateTimeFormat, mode: .editSettings, peer: peer.flatMap(EnginePeer.init), presence: nil, memberCount: nil, state: state, sectionId: ItemListSectionId(self.section), style: .blocks(withTopInset: false, withExtendedBottomInset: false), editingNameUpdated: { editingName in arguments.updateEditingName(editingName) + }, editingNameCompleted: { + arguments.done() }, avatarTapped: { arguments.changeProfilePhoto() }, updatingImage: avatar, tag: CreateGroupEntryTag.info)