diff --git a/submodules/ChatListUI/BUILD b/submodules/ChatListUI/BUILD index 726dfe65d9..3df609da76 100644 --- a/submodules/ChatListUI/BUILD +++ b/submodules/ChatListUI/BUILD @@ -56,6 +56,7 @@ swift_library( "//submodules/ShareController:ShareController", "//submodules/GridMessageSelectionNode:GridMessageSelectionNode", "//submodules/ChatListFilterSettingsHeaderItem:ChatListFilterSettingsHeaderItem", + "//submodules/TelegramStringFormatting:TelegramStringFormatting", ], visibility = [ "//visibility:public", diff --git a/submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift b/submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift index 2f4ffb40f8..64d5c04380 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift @@ -415,8 +415,8 @@ enum ChatListFilterTabEntry: Equatable { switch self { case .all: return .all - case let .filter(filter): - return .filter(filter.id) + case let .filter(id, _, _): + return .filter(id) } } @@ -424,8 +424,8 @@ enum ChatListFilterTabEntry: Equatable { switch self { case .all: return strings.ChatList_Tabs_AllChats - case let .filter(filter): - return filter.text + case let .filter(_, text, _): + return text } } @@ -433,8 +433,8 @@ enum ChatListFilterTabEntry: Equatable { switch self { case .all: return strings.ChatList_Tabs_All - case let .filter(filter): - return filter.text + case let .filter(_, text, _): + return text } } } @@ -700,8 +700,8 @@ final class ChatListFilterTabContainerNode: ASDisplayNode { strongSelf.scrollNode.view.panGestureRecognizer.isEnabled = true strongSelf.scrollNode.view.setContentOffset(strongSelf.scrollNode.view.contentOffset, animated: false) switch filter { - case let .filter(filter): - strongSelf.contextGesture?(filter.id, sourceNode, gesture) + case let .filter(id, _, _): + strongSelf.contextGesture?(id, sourceNode, gesture) default: strongSelf.contextGesture?(nil, sourceNode, gesture) } @@ -716,9 +716,9 @@ final class ChatListFilterTabContainerNode: ASDisplayNode { unreadCount = count unreadHasUnmuted = true isNoFilter = true - case let .filter(filter): - unreadCount = filter.unread.value - unreadHasUnmuted = filter.unread.hasUnmuted + case let .filter(_, _, unread): + unreadCount = unread.value + unreadHasUnmuted = unread.hasUnmuted } if !wasAdded && (itemNode.unreadCount != 0) != (unreadCount != 0) { badgeAnimations[filter.id] = (unreadCount != 0) ? .in : .out @@ -789,7 +789,7 @@ final class ChatListFilterTabContainerNode: ASDisplayNode { var longTitlesWidth: CGFloat = resolvedSideInset for i in 0 ..< tabSizes.count { - let (itemId, paneNodeSize, paneNodeShortSize, paneNode, wasAdded) = tabSizes[i] + let (_, paneNodeSize, _, _, _) = tabSizes[i] longTitlesWidth += paneNodeSize.width if i != tabSizes.count - 1 { longTitlesWidth += minSpacing diff --git a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift index f676bf2f04..1f191a7ffa 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift @@ -49,10 +49,10 @@ final class ChatListSearchInteraction { let peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)? let present: (ViewController, Any?) -> Void let dismissInput: () -> Void - let updateSuggestedPeers: ([Peer]) -> Void + let updateSuggestedPeers: ([Peer], ChatListSearchPaneKey) -> Void let getSelectedMessageIds: () -> Set? - init(openPeer: @escaping (Peer, Bool) -> Void, openDisabledPeer: @escaping (Peer) -> Void, openMessage: @escaping (Peer, MessageId) -> Void, openUrl: @escaping (String) -> Void, clearRecentSearch: @escaping () -> Void, addContact: @escaping (String) -> Void, toggleMessageSelection: @escaping (MessageId, Bool) -> Void, messageContextAction: @escaping ((Message, ASDisplayNode?, CGRect?, UIGestureRecognizer?) -> Void), mediaMessageContextAction: @escaping ((Message, ASDisplayNode?, CGRect?, UIGestureRecognizer?) -> Void), peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, updateSuggestedPeers: @escaping ([Peer]) -> Void, getSelectedMessageIds: @escaping () -> Set?) { + init(openPeer: @escaping (Peer, Bool) -> Void, openDisabledPeer: @escaping (Peer) -> Void, openMessage: @escaping (Peer, MessageId) -> Void, openUrl: @escaping (String) -> Void, clearRecentSearch: @escaping () -> Void, addContact: @escaping (String) -> Void, toggleMessageSelection: @escaping (MessageId, Bool) -> Void, messageContextAction: @escaping ((Message, ASDisplayNode?, CGRect?, UIGestureRecognizer?) -> Void), mediaMessageContextAction: @escaping ((Message, ASDisplayNode?, CGRect?, UIGestureRecognizer?) -> Void), peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, updateSuggestedPeers: @escaping ([Peer], ChatListSearchPaneKey) -> Void, getSelectedMessageIds: @escaping () -> Set?) { self.openPeer = openPeer self.openDisabledPeer = openDisabledPeer self.openMessage = openMessage @@ -215,8 +215,10 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo present(c, a) }, dismissInput: { [weak self] in self?.dismissInput() - }, updateSuggestedPeers: { [weak self] peers in - self?.suggestedPeers.set(.single(peers)) + }, updateSuggestedPeers: { [weak self] peers, key in + if let strongSelf = self, strongSelf.paneContainerNode.currentPaneKey == key { + strongSelf.suggestedPeers.set(.single(peers)) + } }, getSelectedMessageIds: { [weak self] () -> Set? in if let strongSelf = self { return strongSelf.stateValue.selectedMessageIds @@ -297,7 +299,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo self.suggestedFiltersDisposable.set((combineLatest(self.suggestedPeers.get(), self.suggestedDates.get()) |> mapToSignal { peers, dates -> Signal<([Peer], [(Date, String?)]), NoError> in - if peers.isEmpty && dates.isEmpty { + if (peers.isEmpty && dates.isEmpty) || peers.isEmpty { return .single((peers, dates)) } else { return (.complete() |> delay(0.2, queue: Queue.mainQueue())) diff --git a/submodules/ChatListUI/Sources/ChatListSearchFiltersContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchFiltersContainerNode.swift index 180733dcd3..1a85fa03dd 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchFiltersContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchFiltersContainerNode.swift @@ -226,7 +226,7 @@ final class ChatListSearchFiltersContainerNode: ASDisplayNode { } self.addSubnode(self.scrollNode) - self.addSubnode(self.selectedLineNode) + self.scrollNode.addSubnode(self.selectedLineNode) } func cancelAnimations() { @@ -254,6 +254,7 @@ final class ChatListSearchFiltersContainerNode: ASDisplayNode { transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: size)) + var hasSelection = false for i in 0 ..< filters.count { let filter = filters[i] if case let .filter(type) = filter { @@ -272,6 +273,7 @@ final class ChatListSearchFiltersContainerNode: ASDisplayNode { let selectionFraction: CGFloat if selectedFilter == filter.id { selectionFraction = 1.0 - abs(transitionFraction) + hasSelection = true } else if i != 0 && selectedFilter == filters[i - 1].id { selectionFraction = max(0.0, -transitionFraction) } else if i != filters.count - 1 && selectedFilter == filters[i + 1].id { @@ -323,7 +325,7 @@ final class ChatListSearchFiltersContainerNode: ASDisplayNode { } let minSpacing: CGFloat = 24.0 - let spacing = minSpacing + var spacing = minSpacing let resolvedSideInset: CGFloat = 16.0 + sideInset var leftOffset: CGFloat = resolvedSideInset @@ -340,6 +342,10 @@ final class ChatListSearchFiltersContainerNode: ASDisplayNode { } longTitlesWidth += resolvedSideInset + if longTitlesWidth < size.width && hasSelection { + spacing = (size.width - titlesWidth - resolvedSideInset * 2.0) / CGFloat(tabSizes.count - 1) + } + let verticalOffset: CGFloat = -3.0 for i in 0 ..< tabSizes.count { let (_, paneNodeSize, paneNode, wasAdded) = tabSizes[i] @@ -347,14 +353,25 @@ final class ChatListSearchFiltersContainerNode: ASDisplayNode { let paneFrame = CGRect(origin: CGPoint(x: leftOffset, y: floor((size.height - paneNodeSize.height) / 2.0) + verticalOffset), size: paneNodeSize) - if wasAdded { + var effectiveWasAdded = wasAdded + if !effectiveWasAdded && !self.bounds.intersects(self.scrollNode.convert(paneNode.frame, to: self)) && self.bounds.intersects(self.scrollNode.convert(paneFrame, to: self)) { + effectiveWasAdded = true + } + + if effectiveWasAdded { paneNode.frame = paneFrame paneNode.alpha = 0.0 paneNode.subnodeTransform = CATransform3DMakeScale(0.1, 0.1, 1.0) itemNodeTransition.updateSublayerTransformScale(node: paneNode, scale: 1.0) itemNodeTransition.updateAlpha(node: paneNode, alpha: 1.0) } else { - itemNodeTransition.updateFrameAdditive(node: paneNode, frame: paneFrame) + if self.bounds.intersects(self.scrollNode.convert(paneFrame, to: self)) { + itemNodeTransition.updateFrameAdditive(node: paneNode, frame: paneFrame) + } else { + paneNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.4) { [weak paneNode] _ in + paneNode?.frame = paneFrame + } + } } paneNode.updateArea(size: paneFrame.size, sideInset: spacing / 2.0, transition: itemNodeTransition) diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index 5d6959d04f..304b8e228a 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -634,6 +634,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { private let interaction: ChatListSearchInteraction private let peersFilter: ChatListNodePeersFilter private var presentationData: PresentationData + private let key: ChatListSearchPaneKey private let tagMask: MessageTags? private let navigationController: NavigationController? @@ -695,13 +696,30 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { private var hiddenMediaDisposable: Disposable? - init(context: AccountContext, interaction: ChatListSearchInteraction, tagMask: MessageTags?, peersFilter: ChatListNodePeersFilter, searchQuery: Signal, searchOptions: Signal, navigationController: NavigationController?) { + init(context: AccountContext, interaction: ChatListSearchInteraction, key: ChatListSearchPaneKey, peersFilter: ChatListNodePeersFilter, searchQuery: Signal, searchOptions: Signal, navigationController: NavigationController?) { self.context = context self.interaction = interaction + self.key = key self.peersFilter = peersFilter - self.tagMask = tagMask self.navigationController = navigationController + let tagMask: MessageTags? + switch key { + case .chats: + tagMask = nil + case .media: + tagMask = .photoOrVideo + case .links: + tagMask = .webPage + case .files: + tagMask = .file + case .music: + tagMask = .music + case .voice: + tagMask = .voiceOrInstantVideo + } + self.tagMask = tagMask + self.presentationData = context.sharedContext.currentPresentationData.with { $0 } self.presentationDataPromise.set(.single(ChatListPresentationData(theme: self.presentationData.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: self.presentationData.disableAnimations))) @@ -1190,8 +1208,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { return } switch item.content { - case let .peer(peer): - if let peer = peer.peer.peer { + case let .peer(_, peer, _, _, _, _, _, _, _, _, _, _): + if let peer = peer.peer { peerContextAction(peer, .search, node, gesture) } case .groupReference: @@ -1353,7 +1371,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { strongSelf.ready.set(.single(true)) strongSelf.didSetReady = true } else if tagMask != nil { - interaction.updateSuggestedPeers(Array(peers.prefix(8))) + interaction.updateSuggestedPeers(Array(peers.prefix(8)), strongSelf.key) } } })) diff --git a/submodules/ChatListUI/Sources/ChatListSearchMediaNode.swift b/submodules/ChatListUI/Sources/ChatListSearchMediaNode.swift index 2de228d537..f8fa5b5eee 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchMediaNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchMediaNode.swift @@ -117,7 +117,7 @@ private final class VisualMediaItemNode: ASDisplayNode { if case .ended = recognizer.state { if let (gesture, _) = recognizer.lastRecognizedGestureAndLocation { if case .tap = gesture { - if let (item, _, _, _) = self.item { + if let _ = self.item { var media: Media? for value in message.media { if let image = value as? TelegramMediaImage { @@ -225,7 +225,7 @@ private final class VisualMediaItemNode: ASDisplayNode { self.fetchStatusDisposable.set((messageMediaFileStatus(context: context, messageId: message.id, file: file) |> deliverOnMainQueue).start(next: { [weak self] status in - if let strongSelf = self, let (item, _, _, _) = strongSelf.item { + if let strongSelf = self, let _ = strongSelf.item { strongSelf.resourceStatus = status let isStreamable = isMediaStreamable(message: message, media: file) diff --git a/submodules/ChatListUI/Sources/ChatListSearchPaneContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchPaneContainerNode.swift index 40de98e859..dada51b135 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchPaneContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchPaneContainerNode.swift @@ -81,21 +81,7 @@ private final class ChatListSearchPendingPane { key: ChatListSearchPaneKey, hasBecomeReady: @escaping (ChatListSearchPaneKey) -> Void ) { - let paneNode: ChatListSearchPaneNode - switch key { - case .chats: - paneNode = ChatListSearchListPaneNode(context: context, interaction: interaction, tagMask: nil, peersFilter: peersFilter, searchQuery: searchQuery, searchOptions: searchOptions, navigationController: navigationController) - case .media: - paneNode = ChatListSearchListPaneNode(context: context, interaction: interaction, tagMask: .photoOrVideo, peersFilter: [], searchQuery: searchQuery, searchOptions: searchOptions, navigationController: navigationController) - case .files: - paneNode = ChatListSearchListPaneNode(context: context, interaction: interaction, tagMask: .file, peersFilter: [], searchQuery: searchQuery, searchOptions: searchOptions, navigationController: navigationController) - case .links: - paneNode = ChatListSearchListPaneNode(context: context, interaction: interaction, tagMask: .webPage, peersFilter: [], searchQuery: searchQuery, searchOptions: searchOptions, navigationController: navigationController) - case .voice: - paneNode = ChatListSearchListPaneNode(context: context, interaction: interaction, tagMask: .voiceOrInstantVideo, peersFilter: [], searchQuery: searchQuery, searchOptions: searchOptions, navigationController: navigationController) - case .music: - paneNode = ChatListSearchListPaneNode(context: context, interaction: interaction, tagMask: .music, peersFilter: [], searchQuery: searchQuery, searchOptions: searchOptions, navigationController: navigationController) - } + let paneNode = ChatListSearchListPaneNode(context: context, interaction: interaction, key: key, peersFilter: key == .chats ? peersFilter : [], searchQuery: searchQuery, searchOptions: searchOptions, navigationController: navigationController) self.pane = ChatListSearchPaneWrapper(key: key, node: paneNode) self.disposable = (paneNode.isReady @@ -493,7 +479,7 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, UIGestureRecognizerD transition.animateFrame(node: pane.node, from: paneFrame, to: paneFrame.offsetBy(dx: -paneSwitchAnimationOffset, dy: 0.0), completion: isAnimatingOut ? nil : { _ in paneCompletion() }) - } else if let previousPaneKey = previousPaneKey, key == self.currentPaneKey { + } else if let _ = previousPaneKey, key == self.currentPaneKey { pane.node.frame = adjustedFrame let isAnimatingOut = pane.isAnimatingOut pane.isAnimatingOut = true diff --git a/submodules/ChatListUI/Sources/DateSuggestion.swift b/submodules/ChatListUI/Sources/DateSuggestion.swift index 7a40eedf10..2c6f80468a 100644 --- a/submodules/ChatListUI/Sources/DateSuggestion.swift +++ b/submodules/ChatListUI/Sources/DateSuggestion.swift @@ -1,5 +1,6 @@ import Foundation import TelegramPresentationData +import TelegramStringFormatting private let telegramReleaseDate = Date(timeIntervalSince1970: 1376438400.0) @@ -106,35 +107,46 @@ func suggestDates(for string: String, strings: PresentationStrings, dateTimeForm } } - } else { - for (day, value) in weekDays { - let dayName = value.0.lowercased() - let shortDayName = value.1.lowercased() - if string == shortDayName || (string.count >= shortDayName.count && dayName.hasPrefix(string)) { - var nextDateComponent = calendar.dateComponents([.hour, .minute, .second], from: now) - nextDateComponent.weekday = day + calendar.firstWeekday - if let date = calendar.nextDate(after: now, matching: nextDateComponent, matchingPolicy: .nextTime, direction: .backward) { - let upperDate = getUpperDate(for: date) - for i in 0..<5 { - if let date = calendar.date(byAdding: .hour, value: -24 * 7 * i, to: upperDate) { - if calendar.isDate(date, equalTo: now, toGranularity: .weekOfYear) { - result.append((date, value.0)) - } else { - result.append((date, nil)) - } + } + + for (day, value) in weekDays { + let dayName = value.0.lowercased() + let shortDayName = value.1.lowercased() + if string == shortDayName || (string.count >= shortDayName.count && dayName.hasPrefix(string)) { + var nextDateComponent = calendar.dateComponents([.hour, .minute, .second], from: now) + nextDateComponent.weekday = day + calendar.firstWeekday + if let date = calendar.nextDate(after: now, matching: nextDateComponent, matchingPolicy: .nextTime, direction: .backward) { + let upperDate = getUpperDate(for: date) + for i in 0..<5 { + if let date = calendar.date(byAdding: .hour, value: -24 * 7 * i, to: upperDate) { + if calendar.isDate(date, equalTo: now, toGranularity: .weekOfYear) { + result.append((date, value.0)) + } else { + result.append((date, nil)) } } } } } - for (month, value) in months { - let monthName = value.0.lowercased() - let shortMonthName = value.1.lowercased() - if string == shortMonthName || (string.count >= shortMonthName.count && monthName.hasPrefix(string)) { + } + + let cleanString = string.trimmingCharacters(in: .decimalDigits).trimmingCharacters(in: .whitespacesAndNewlines) + let cleanDigits = string.trimmingCharacters(in: .letters).trimmingCharacters(in: .whitespacesAndNewlines) + + for (month, value) in months { + let monthName = value.0.lowercased() + let shortMonthName = value.1.lowercased() + if cleanString == shortMonthName || (cleanString.count >= shortMonthName.count && monthName.hasPrefix(cleanString)) { + if cleanDigits.count == 4, let year = Int(cleanDigits) { + let date = getUpperMonthDate(month: month, year: year) + if date <= now && date > telegramReleaseDate { + result.append((date, stringForMonth(strings: strings, month: Int32(month - 1), ofYear: Int32(year - 1900)))) + } + } else if cleanDigits.isEmpty { for i in (year - 7 ... year).reversed() { let date = getUpperMonthDate(month: month, year: i) if date <= now && date > telegramReleaseDate { - result.append((date, nil)) + result.append((date, stringForMonth(strings: strings, month: Int32(month - 1), ofYear: Int32(i - 1900)))) } } } diff --git a/submodules/DateSelectionUI/Sources/DateSelectionActionSheetController.swift b/submodules/DateSelectionUI/Sources/DateSelectionActionSheetController.swift index ee5ee5f509..cd1b21738f 100644 --- a/submodules/DateSelectionUI/Sources/DateSelectionActionSheetController.swift +++ b/submodules/DateSelectionUI/Sources/DateSelectionActionSheetController.swift @@ -110,6 +110,9 @@ private final class DateSelectionActionSheetItemNode: ActionSheetItemNode { self.pickerView.datePickerMode = .date self.pickerView.date = Date(timeIntervalSince1970: Double(roundDateToDays(currentValue))) self.pickerView.locale = localeWithStrings(strings) + if #available(iOS 13.4, *) { + self.pickerView.preferredDatePickerStyle = .wheels + } if let minimumDate = minimumDate { self.pickerView.minimumDate = minimumDate } diff --git a/submodules/GalleryUI/Sources/GalleryTitleView.swift b/submodules/GalleryUI/Sources/GalleryTitleView.swift index 53848f00a4..89ceb199e5 100644 --- a/submodules/GalleryUI/Sources/GalleryTitleView.swift +++ b/submodules/GalleryUI/Sources/GalleryTitleView.swift @@ -44,8 +44,8 @@ final class GalleryTitleView: UIView, NavigationBarTitleView { let leftInset: CGFloat = 0.0 let rightInset: CGFloat = 0.0 - let authorNameSize = self.authorNameNode.measure(CGSize(width: size.width - 44.0 * 2.0 - 8.0 * 2.0 - leftInset - rightInset, height: CGFloat.greatestFiniteMagnitude)) - let dateSize = self.dateNode.measure(CGSize(width: size.width - 44.0 * 2.0 - 8.0 * 2.0, height: CGFloat.greatestFiniteMagnitude)) + let authorNameSize = self.authorNameNode.measure(CGSize(width: size.width - 8.0 * 2.0 - leftInset - rightInset, height: CGFloat.greatestFiniteMagnitude)) + let dateSize = self.dateNode.measure(CGSize(width: size.width - 8.0 * 2.0, height: CGFloat.greatestFiniteMagnitude)) if authorNameSize.height.isZero { self.dateNode.frame = CGRect(origin: CGPoint(x: floor((size.width - dateSize.width) / 2.0), y: floor((size.height - dateSize.height) / 2.0)), size: dateSize) diff --git a/submodules/PeerInfoUI/Sources/PeerBanTimeoutController.swift b/submodules/PeerInfoUI/Sources/PeerBanTimeoutController.swift index b6a7459613..50c71a9558 100644 --- a/submodules/PeerInfoUI/Sources/PeerBanTimeoutController.swift +++ b/submodules/PeerInfoUI/Sources/PeerBanTimeoutController.swift @@ -101,6 +101,9 @@ private final class PeerBanTimeoutActionSheetItemNode: ActionSheetItemNode { self.pickerView.locale = localeWithStrings(strings) self.pickerView.minimumDate = Date() self.pickerView.maximumDate = Date(timeIntervalSince1970: Double(Int32.max - 1)) + if #available(iOS 13.4, *) { + self.pickerView.preferredDatePickerStyle = .wheels + } super.init(theme: theme) diff --git a/submodules/SearchBarNode/Sources/SearchBarNode.swift b/submodules/SearchBarNode/Sources/SearchBarNode.swift index cc9e516b7f..1b742bf801 100644 --- a/submodules/SearchBarNode/Sources/SearchBarNode.swift +++ b/submodules/SearchBarNode/Sources/SearchBarNode.swift @@ -56,6 +56,7 @@ public struct SearchBarToken { private final class TokenNode: ASDisplayNode { var theme: SearchBarNodeTheme let token: SearchBarToken + let containerNode: ASDisplayNode let iconNode: ASImageNode let titleNode: ASTextNode let backgroundNode: ASImageNode @@ -68,6 +69,8 @@ private final class TokenNode: ASDisplayNode { init(theme: SearchBarNodeTheme, token: SearchBarToken) { self.theme = theme self.token = token + self.containerNode = ASDisplayNode() + self.containerNode.clipsToBounds = true self.iconNode = ASImageNode() self.iconNode.displaysAsynchronously = false self.iconNode.displayWithoutProcessing = true @@ -82,8 +85,8 @@ private final class TokenNode: ASDisplayNode { super.init() self.clipsToBounds = true - - self.addSubnode(self.backgroundNode) + self.addSubnode(self.containerNode) + self.containerNode.addSubnode(self.backgroundNode) let backgroundColor = token.style?.backgroundColor ?? theme.inputIcon let strokeColor = token.style?.strokeColor ?? backgroundColor @@ -91,10 +94,10 @@ private final class TokenNode: ASDisplayNode { let foregroundColor = token.style?.foregroundColor ?? .white self.iconNode.image = generateTintedImage(image: token.icon, color: foregroundColor) - self.addSubnode(self.iconNode) + self.containerNode.addSubnode(self.iconNode) self.titleNode.attributedText = NSAttributedString(string: token.title, font: Font.regular(17.0), textColor: foregroundColor) - self.addSubnode(self.titleNode) + self.containerNode.addSubnode(self.titleNode) } override func didLoad() { @@ -108,8 +111,10 @@ private final class TokenNode: ASDisplayNode { } func animateIn() { - let targetFrame = self.frame - self.layer.animateFrame(from: CGRect(origin: targetFrame.origin, size: CGSize(width: 1.0, height: targetFrame.height)), to: targetFrame, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring) + let targetFrame = self.containerNode.frame + self.containerNode.layer.animateFrame(from: CGRect(origin: targetFrame.origin, size: CGSize(width: 1.0, height: targetFrame.height)), to: targetFrame, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring) + self.backgroundNode.layer.animateFrame(from: CGRect(origin: targetFrame.origin, size: CGSize(width: 1.0, height: targetFrame.height)), to: targetFrame, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring) + self.iconNode.layer.animateScale(from: 0.1, to: 1.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring) self.iconNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15) self.titleNode.layer.animateScale(from: 0.1, to: 1.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring) @@ -163,6 +168,7 @@ private final class TokenNode: ASDisplayNode { } let size = CGSize(width: self.isCollapsed ? height : width, height: height) + transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(), size: size)) transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: size)) transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: leftInset, y: floor((height - titleSize.height) / 2.0)), size: titleSize)) @@ -419,9 +425,14 @@ private class SearchBarTextField: UITextField, UIScrollViewDelegate { } } + private weak var _scrollView: UIScrollView? var scrollView: UIScrollView? { + if let scrollView = self._scrollView { + return scrollView + } for view in self.subviews { if let scrollView = view as? UIScrollView { + _scrollView = scrollView return scrollView } } @@ -459,8 +470,10 @@ private class SearchBarTextField: UITextField, UIScrollViewDelegate { return CGRect(origin: CGPoint(), size: CGSize()) } var rect = bounds.insetBy(dx: 7.0, dy: 4.0) - rect.origin.y += 1.0 - + if #available(iOS 14.0, *) { + } else { + rect.origin.y += 1.0 + } let prefixSize = self.measurePrefixLabel.updateLayout(CGSize(width: floor(bounds.size.width * 0.7), height: bounds.size.height)) if !prefixSize.width.isZero { let prefixOffset = prefixSize.width + 3.0 @@ -494,13 +507,19 @@ private class SearchBarTextField: UITextField, UIScrollViewDelegate { textOffset += 2.0 } + var placeholderOffset: CGFloat = 0.0 + if #available(iOS 14.0, *) { + placeholderOffset = 1.0 + } else { + } + let textRect = self.textRect(forBounds: bounds) let labelSize = self.placeholderLabel.updateLayout(textRect.size) - self.placeholderLabel.frame = CGRect(origin: CGPoint(x: textRect.minX, y: textRect.minY + textOffset), size: labelSize) + self.placeholderLabel.frame = CGRect(origin: CGPoint(x: textRect.minX, y: textRect.minY + textOffset + placeholderOffset), size: labelSize) let prefixSize = self.prefixLabel.updateLayout(CGSize(width: floor(bounds.size.width * 0.7), height: bounds.size.height)) let prefixBounds = bounds.insetBy(dx: 4.0, dy: 4.0) - self.prefixLabel.frame = CGRect(origin: CGPoint(x: prefixBounds.minX, y: prefixBounds.minY + textOffset), size: prefixSize) + self.prefixLabel.frame = CGRect(origin: CGPoint(x: prefixBounds.minX, y: prefixBounds.minY + textOffset + placeholderOffset), size: prefixSize) } override func deleteBackward() { @@ -1046,7 +1065,9 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate { public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { if let _ = self.textField.selectedTokenIndex { - self.textField.selectedTokenIndex = nil + if !string.isEmpty { + self.textField.selectedTokenIndex = nil + } if string.range(of: " ") != nil { return false } @@ -1090,21 +1111,19 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate { private func updateIsEmpty(animated: Bool = false) { let isEmpty = (self.textField.text?.isEmpty ?? true) && self.tokens.isEmpty - + let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.3, curve: .spring) : .immediate let placeholderTransition = !isEmpty ? .immediate : transition placeholderTransition.updateAlpha(node: self.textField.placeholderLabel, alpha: isEmpty ? 1.0 : 0.0) - + let clearIsHidden = isEmpty && self.prefixString == nil - transition.updateAlpha(node: self.clearButton, alpha: clearIsHidden ? 0.0 : 1.0) + transition.updateAlpha(node: self.clearButton.imageNode, alpha: clearIsHidden ? 0.0 : 1.0) transition.updateTransformScale(node: self.clearButton, scale: clearIsHidden ? 0.2 : 1.0) self.clearButton.isUserInteractionEnabled = !clearIsHidden } @objc private func cancelPressed() { - if let cancel = self.cancel { - cancel() - } + self.cancel?() } @objc private func clearPressed() { diff --git a/submodules/SettingsUI/Sources/Themes/ThemeAutoNightTimeSelectionActionSheet.swift b/submodules/SettingsUI/Sources/Themes/ThemeAutoNightTimeSelectionActionSheet.swift index 7bdbbc2c7f..3edd0efda9 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeAutoNightTimeSelectionActionSheet.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeAutoNightTimeSelectionActionSheet.swift @@ -105,6 +105,9 @@ private final class ThemeAutoNightTimeSelectionActionSheetItemNode: ActionSheetI self.pickerView.timeZone = TimeZone(secondsFromGMT: 0) self.pickerView.date = Date(timeIntervalSince1970: Double(currentValue)) self.pickerView.locale = Locale.current + if #available(iOS 13.4, *) { + self.pickerView.preferredDatePickerStyle = .wheels + } super.init(theme: theme) diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index 0a149bba96..aabe82ab8b 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -701,9 +701,9 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1449145777] = { return Api.upload.CdnFile.parse_cdnFile($0) } dict[1984136919] = { return Api.wallet.LiteResponse.parse_liteResponse($0) } dict[415997816] = { return Api.help.InviteText.parse_inviteText($0) } - dict[-1937807902] = { return Api.BotInlineMessage.parse_botInlineMessageText($0) } - dict[982505656] = { return Api.BotInlineMessage.parse_botInlineMessageMediaGeo($0) } dict[1984755728] = { return Api.BotInlineMessage.parse_botInlineMessageMediaAuto($0) } + dict[-1937807902] = { return Api.BotInlineMessage.parse_botInlineMessageText($0) } + dict[-1222451611] = { return Api.BotInlineMessage.parse_botInlineMessageMediaGeo($0) } dict[-1970903652] = { return Api.BotInlineMessage.parse_botInlineMessageMediaVenue($0) } dict[416402882] = { return Api.BotInlineMessage.parse_botInlineMessageMediaContact($0) } dict[-1673717362] = { return Api.InputPeerNotifySettings.parse_inputPeerNotifySettings($0) } diff --git a/submodules/TelegramApi/Sources/Api1.swift b/submodules/TelegramApi/Sources/Api1.swift index 1d381226c9..e12df7433c 100644 --- a/submodules/TelegramApi/Sources/Api1.swift +++ b/submodules/TelegramApi/Sources/Api1.swift @@ -19856,14 +19856,27 @@ public extension Api { } public enum BotInlineMessage: TypeConstructorDescription { - case botInlineMessageText(flags: Int32, message: String, entities: [Api.MessageEntity]?, replyMarkup: Api.ReplyMarkup?) - case botInlineMessageMediaGeo(flags: Int32, geo: Api.GeoPoint, replyMarkup: Api.ReplyMarkup?) case botInlineMessageMediaAuto(flags: Int32, message: String, entities: [Api.MessageEntity]?, replyMarkup: Api.ReplyMarkup?) + case botInlineMessageText(flags: Int32, message: String, entities: [Api.MessageEntity]?, replyMarkup: Api.ReplyMarkup?) + case botInlineMessageMediaGeo(flags: Int32, geo: Api.GeoPoint, period: Int32, replyMarkup: Api.ReplyMarkup?) case botInlineMessageMediaVenue(flags: Int32, geo: Api.GeoPoint, title: String, address: String, provider: String, venueId: String, venueType: String, replyMarkup: Api.ReplyMarkup?) case botInlineMessageMediaContact(flags: Int32, phoneNumber: String, firstName: String, lastName: String, vcard: String, replyMarkup: Api.ReplyMarkup?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { + case .botInlineMessageMediaAuto(let flags, let message, let entities, let replyMarkup): + if boxed { + buffer.appendInt32(1984755728) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeString(message, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(entities!.count)) + for item in entities! { + item.serialize(buffer, true) + }} + if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)} + break case .botInlineMessageText(let flags, let message, let entities, let replyMarkup): if boxed { buffer.appendInt32(-1937807902) @@ -19877,25 +19890,13 @@ public extension Api { }} if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)} break - case .botInlineMessageMediaGeo(let flags, let geo, let replyMarkup): + case .botInlineMessageMediaGeo(let flags, let geo, let period, let replyMarkup): if boxed { - buffer.appendInt32(982505656) + buffer.appendInt32(-1222451611) } serializeInt32(flags, buffer: buffer, boxed: false) geo.serialize(buffer, true) - if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)} - break - case .botInlineMessageMediaAuto(let flags, let message, let entities, let replyMarkup): - if boxed { - buffer.appendInt32(1984755728) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeString(message, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(entities!.count)) - for item in entities! { - item.serialize(buffer, true) - }} + serializeInt32(period, buffer: buffer, boxed: false) if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)} break case .botInlineMessageMediaVenue(let flags, let geo, let title, let address, let provider, let venueId, let venueType, let replyMarkup): @@ -19927,12 +19928,12 @@ public extension Api { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .botInlineMessageText(let flags, let message, let entities, let replyMarkup): - return ("botInlineMessageText", [("flags", flags), ("message", message), ("entities", entities), ("replyMarkup", replyMarkup)]) - case .botInlineMessageMediaGeo(let flags, let geo, let replyMarkup): - return ("botInlineMessageMediaGeo", [("flags", flags), ("geo", geo), ("replyMarkup", replyMarkup)]) case .botInlineMessageMediaAuto(let flags, let message, let entities, let replyMarkup): return ("botInlineMessageMediaAuto", [("flags", flags), ("message", message), ("entities", entities), ("replyMarkup", replyMarkup)]) + case .botInlineMessageText(let flags, let message, let entities, let replyMarkup): + return ("botInlineMessageText", [("flags", flags), ("message", message), ("entities", entities), ("replyMarkup", replyMarkup)]) + case .botInlineMessageMediaGeo(let flags, let geo, let period, let replyMarkup): + return ("botInlineMessageMediaGeo", [("flags", flags), ("geo", geo), ("period", period), ("replyMarkup", replyMarkup)]) case .botInlineMessageMediaVenue(let flags, let geo, let title, let address, let provider, let venueId, let venueType, let replyMarkup): return ("botInlineMessageMediaVenue", [("flags", flags), ("geo", geo), ("title", title), ("address", address), ("provider", provider), ("venueId", venueId), ("venueType", venueType), ("replyMarkup", replyMarkup)]) case .botInlineMessageMediaContact(let flags, let phoneNumber, let firstName, let lastName, let vcard, let replyMarkup): @@ -19940,6 +19941,30 @@ public extension Api { } } + public static func parse_botInlineMessageMediaAuto(_ reader: BufferReader) -> BotInlineMessage? { + var _1: Int32? + _1 = reader.readInt32() + var _2: String? + _2 = parseString(reader) + var _3: [Api.MessageEntity]? + if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self) + } } + var _4: Api.ReplyMarkup? + if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { + _4 = Api.parse(reader, signature: signature) as? Api.ReplyMarkup + } } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil + let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.BotInlineMessage.botInlineMessageMediaAuto(flags: _1!, message: _2!, entities: _3, replyMarkup: _4) + } + else { + return nil + } + } public static func parse_botInlineMessageText(_ reader: BufferReader) -> BotInlineMessage? { var _1: Int32? _1 = reader.readInt32() @@ -19971,39 +19996,18 @@ public extension Api { if let signature = reader.readInt32() { _2 = Api.parse(reader, signature: signature) as? Api.GeoPoint } - var _3: Api.ReplyMarkup? - if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { - _3 = Api.parse(reader, signature: signature) as? Api.ReplyMarkup - } } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = (Int(_1!) & Int(1 << 2) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.BotInlineMessage.botInlineMessageMediaGeo(flags: _1!, geo: _2!, replyMarkup: _3) - } - else { - return nil - } - } - public static func parse_botInlineMessageMediaAuto(_ reader: BufferReader) -> BotInlineMessage? { - var _1: Int32? - _1 = reader.readInt32() - var _2: String? - _2 = parseString(reader) - var _3: [Api.MessageEntity]? - if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self) - } } + var _3: Int32? + _3 = reader.readInt32() var _4: Api.ReplyMarkup? if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { _4 = Api.parse(reader, signature: signature) as? Api.ReplyMarkup } } let _c1 = _1 != nil let _c2 = _2 != nil - let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil + let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil if _c1 && _c2 && _c3 && _c4 { - return Api.BotInlineMessage.botInlineMessageMediaAuto(flags: _1!, message: _2!, entities: _3, replyMarkup: _4) + return Api.BotInlineMessage.botInlineMessageMediaGeo(flags: _1!, geo: _2!, period: _3!, replyMarkup: _4) } else { return nil diff --git a/submodules/TelegramCore/Sources/ChatContextResult.swift b/submodules/TelegramCore/Sources/ChatContextResult.swift index be86758086..ab703b262b 100644 --- a/submodules/TelegramCore/Sources/ChatContextResult.swift +++ b/submodules/TelegramCore/Sources/ChatContextResult.swift @@ -423,8 +423,8 @@ extension ChatContextResultMessage { parsedReplyMarkup = ReplyMarkupMessageAttribute(apiMarkup: replyMarkup) } self = .text(text: message, entities: parsedEntities, disableUrlPreview: (flags & (1 << 0)) != 0, replyMarkup: parsedReplyMarkup) - case let .botInlineMessageMediaGeo(_, geo, replyMarkup): - let media = telegramMediaMapFromApiGeoPoint(geo, title: nil, address: nil, provider: nil, venueId: nil, venueType: nil, liveBroadcastingTimeout: nil) + case let .botInlineMessageMediaGeo(_, geo, period, replyMarkup): + let media = telegramMediaMapFromApiGeoPoint(geo, title: nil, address: nil, provider: nil, venueId: nil, venueType: nil, liveBroadcastingTimeout: period) var parsedReplyMarkup: ReplyMarkupMessageAttribute? if let replyMarkup = replyMarkup { parsedReplyMarkup = ReplyMarkupMessageAttribute(apiMarkup: replyMarkup) diff --git a/submodules/TelegramCore/Sources/SearchMessages.swift b/submodules/TelegramCore/Sources/SearchMessages.swift index 69a70251ce..ce30fb2c06 100644 --- a/submodules/TelegramCore/Sources/SearchMessages.swift +++ b/submodules/TelegramCore/Sources/SearchMessages.swift @@ -238,7 +238,7 @@ public func searchMessages(account: Account, location: SearchMessagesLocation, q } else { let lowerBound = state?.main.messages.last.flatMap({ $0.index }) let signal: Signal - if peer.id.namespace == Namespaces.Peer.CloudChannel && query.isEmpty && tags == nil && minDate == nil && maxDate == nil { + if peer.id.namespace == Namespaces.Peer.CloudChannel && query.isEmpty && fromId == nil && tags == nil && minDate == nil && maxDate == nil { signal = account.network.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: lowerBound?.id.id ?? 0, offsetDate: 0, addOffset: 0, limit: limit, maxId: Int32.max - 1, minId: 0, hash: 0)) } else { signal = account.network.request(Api.functions.messages.search(flags: flags, peer: inputPeer, q: query, fromId: fromInputUser, topMsgId: topMsgId?.id, filter: filter, minDate: minDate ?? 0, maxDate: maxDate ?? (Int32.max - 1), offsetId: lowerBound?.id.id ?? 0, addOffset: 0, limit: limit, maxId: Int32.max - 1, minId: 0, hash: 0)) diff --git a/submodules/TelegramUI/Sources/ChatDateSelectionSheet.swift b/submodules/TelegramUI/Sources/ChatDateSelectionSheet.swift index 36a3ef2686..06d77d87a5 100644 --- a/submodules/TelegramUI/Sources/ChatDateSelectionSheet.swift +++ b/submodules/TelegramUI/Sources/ChatDateSelectionSheet.swift @@ -92,6 +92,10 @@ private final class ChatDateSelectorItemNode: ActionSheetItemNode { self.pickerView.minimumDate = Date(timeIntervalSince1970: 1376438400.0) self.pickerView.maximumDate = Date(timeIntervalSinceNow: 2.0) + if #available(iOS 13.4, *) { + self.pickerView.preferredDatePickerStyle = .wheels + } + super.init(theme: theme) self.view.addSubview(self.pickerView) diff --git a/submodules/TelegramUI/Sources/ChatScheduleTimeControllerNode.swift b/submodules/TelegramUI/Sources/ChatScheduleTimeControllerNode.swift index e9082e6f4d..b2520fa49c 100644 --- a/submodules/TelegramUI/Sources/ChatScheduleTimeControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatScheduleTimeControllerNode.swift @@ -188,6 +188,9 @@ class ChatScheduleTimeControllerNode: ViewControllerTracingNode, UIScrollViewDel pickerView.minuteInterval = 1 self.contentContainerNode.view.addSubview(pickerView) pickerView.addTarget(self, action: #selector(self.datePickerUpdated), for: .valueChanged) + if #available(iOS 13.4, *) { + pickerView.preferredDatePickerStyle = .wheels + } self.pickerView = pickerView self.updateMinimumDate(currentTime: currentTime)