mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-29 19:35:08 +00:00
Search filters fixes
This commit is contained in:
parent
5988ed6495
commit
d523ab842f
@ -56,6 +56,7 @@ swift_library(
|
|||||||
"//submodules/ShareController:ShareController",
|
"//submodules/ShareController:ShareController",
|
||||||
"//submodules/GridMessageSelectionNode:GridMessageSelectionNode",
|
"//submodules/GridMessageSelectionNode:GridMessageSelectionNode",
|
||||||
"//submodules/ChatListFilterSettingsHeaderItem:ChatListFilterSettingsHeaderItem",
|
"//submodules/ChatListFilterSettingsHeaderItem:ChatListFilterSettingsHeaderItem",
|
||||||
|
"//submodules/TelegramStringFormatting:TelegramStringFormatting",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
|||||||
@ -415,8 +415,8 @@ enum ChatListFilterTabEntry: Equatable {
|
|||||||
switch self {
|
switch self {
|
||||||
case .all:
|
case .all:
|
||||||
return .all
|
return .all
|
||||||
case let .filter(filter):
|
case let .filter(id, _, _):
|
||||||
return .filter(filter.id)
|
return .filter(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -424,8 +424,8 @@ enum ChatListFilterTabEntry: Equatable {
|
|||||||
switch self {
|
switch self {
|
||||||
case .all:
|
case .all:
|
||||||
return strings.ChatList_Tabs_AllChats
|
return strings.ChatList_Tabs_AllChats
|
||||||
case let .filter(filter):
|
case let .filter(_, text, _):
|
||||||
return filter.text
|
return text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -433,8 +433,8 @@ enum ChatListFilterTabEntry: Equatable {
|
|||||||
switch self {
|
switch self {
|
||||||
case .all:
|
case .all:
|
||||||
return strings.ChatList_Tabs_All
|
return strings.ChatList_Tabs_All
|
||||||
case let .filter(filter):
|
case let .filter(_, text, _):
|
||||||
return filter.text
|
return text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -700,8 +700,8 @@ final class ChatListFilterTabContainerNode: ASDisplayNode {
|
|||||||
strongSelf.scrollNode.view.panGestureRecognizer.isEnabled = true
|
strongSelf.scrollNode.view.panGestureRecognizer.isEnabled = true
|
||||||
strongSelf.scrollNode.view.setContentOffset(strongSelf.scrollNode.view.contentOffset, animated: false)
|
strongSelf.scrollNode.view.setContentOffset(strongSelf.scrollNode.view.contentOffset, animated: false)
|
||||||
switch filter {
|
switch filter {
|
||||||
case let .filter(filter):
|
case let .filter(id, _, _):
|
||||||
strongSelf.contextGesture?(filter.id, sourceNode, gesture)
|
strongSelf.contextGesture?(id, sourceNode, gesture)
|
||||||
default:
|
default:
|
||||||
strongSelf.contextGesture?(nil, sourceNode, gesture)
|
strongSelf.contextGesture?(nil, sourceNode, gesture)
|
||||||
}
|
}
|
||||||
@ -716,9 +716,9 @@ final class ChatListFilterTabContainerNode: ASDisplayNode {
|
|||||||
unreadCount = count
|
unreadCount = count
|
||||||
unreadHasUnmuted = true
|
unreadHasUnmuted = true
|
||||||
isNoFilter = true
|
isNoFilter = true
|
||||||
case let .filter(filter):
|
case let .filter(_, _, unread):
|
||||||
unreadCount = filter.unread.value
|
unreadCount = unread.value
|
||||||
unreadHasUnmuted = filter.unread.hasUnmuted
|
unreadHasUnmuted = unread.hasUnmuted
|
||||||
}
|
}
|
||||||
if !wasAdded && (itemNode.unreadCount != 0) != (unreadCount != 0) {
|
if !wasAdded && (itemNode.unreadCount != 0) != (unreadCount != 0) {
|
||||||
badgeAnimations[filter.id] = (unreadCount != 0) ? .in : .out
|
badgeAnimations[filter.id] = (unreadCount != 0) ? .in : .out
|
||||||
@ -789,7 +789,7 @@ final class ChatListFilterTabContainerNode: ASDisplayNode {
|
|||||||
|
|
||||||
var longTitlesWidth: CGFloat = resolvedSideInset
|
var longTitlesWidth: CGFloat = resolvedSideInset
|
||||||
for i in 0 ..< tabSizes.count {
|
for i in 0 ..< tabSizes.count {
|
||||||
let (itemId, paneNodeSize, paneNodeShortSize, paneNode, wasAdded) = tabSizes[i]
|
let (_, paneNodeSize, _, _, _) = tabSizes[i]
|
||||||
longTitlesWidth += paneNodeSize.width
|
longTitlesWidth += paneNodeSize.width
|
||||||
if i != tabSizes.count - 1 {
|
if i != tabSizes.count - 1 {
|
||||||
longTitlesWidth += minSpacing
|
longTitlesWidth += minSpacing
|
||||||
|
|||||||
@ -49,10 +49,10 @@ final class ChatListSearchInteraction {
|
|||||||
let peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?
|
let peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?
|
||||||
let present: (ViewController, Any?) -> Void
|
let present: (ViewController, Any?) -> Void
|
||||||
let dismissInput: () -> Void
|
let dismissInput: () -> Void
|
||||||
let updateSuggestedPeers: ([Peer]) -> Void
|
let updateSuggestedPeers: ([Peer], ChatListSearchPaneKey) -> Void
|
||||||
let getSelectedMessageIds: () -> Set<MessageId>?
|
let getSelectedMessageIds: () -> Set<MessageId>?
|
||||||
|
|
||||||
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<MessageId>?) {
|
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<MessageId>?) {
|
||||||
self.openPeer = openPeer
|
self.openPeer = openPeer
|
||||||
self.openDisabledPeer = openDisabledPeer
|
self.openDisabledPeer = openDisabledPeer
|
||||||
self.openMessage = openMessage
|
self.openMessage = openMessage
|
||||||
@ -215,8 +215,10 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
|||||||
present(c, a)
|
present(c, a)
|
||||||
}, dismissInput: { [weak self] in
|
}, dismissInput: { [weak self] in
|
||||||
self?.dismissInput()
|
self?.dismissInput()
|
||||||
}, updateSuggestedPeers: { [weak self] peers in
|
}, updateSuggestedPeers: { [weak self] peers, key in
|
||||||
self?.suggestedPeers.set(.single(peers))
|
if let strongSelf = self, strongSelf.paneContainerNode.currentPaneKey == key {
|
||||||
|
strongSelf.suggestedPeers.set(.single(peers))
|
||||||
|
}
|
||||||
}, getSelectedMessageIds: { [weak self] () -> Set<MessageId>? in
|
}, getSelectedMessageIds: { [weak self] () -> Set<MessageId>? in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
return strongSelf.stateValue.selectedMessageIds
|
return strongSelf.stateValue.selectedMessageIds
|
||||||
@ -297,7 +299,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
|||||||
|
|
||||||
self.suggestedFiltersDisposable.set((combineLatest(self.suggestedPeers.get(), self.suggestedDates.get())
|
self.suggestedFiltersDisposable.set((combineLatest(self.suggestedPeers.get(), self.suggestedDates.get())
|
||||||
|> mapToSignal { peers, dates -> Signal<([Peer], [(Date, String?)]), NoError> in
|
|> 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))
|
return .single((peers, dates))
|
||||||
} else {
|
} else {
|
||||||
return (.complete() |> delay(0.2, queue: Queue.mainQueue()))
|
return (.complete() |> delay(0.2, queue: Queue.mainQueue()))
|
||||||
|
|||||||
@ -226,7 +226,7 @@ final class ChatListSearchFiltersContainerNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.addSubnode(self.scrollNode)
|
self.addSubnode(self.scrollNode)
|
||||||
self.addSubnode(self.selectedLineNode)
|
self.scrollNode.addSubnode(self.selectedLineNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func cancelAnimations() {
|
func cancelAnimations() {
|
||||||
@ -254,6 +254,7 @@ final class ChatListSearchFiltersContainerNode: ASDisplayNode {
|
|||||||
|
|
||||||
transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: size))
|
transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||||
|
|
||||||
|
var hasSelection = false
|
||||||
for i in 0 ..< filters.count {
|
for i in 0 ..< filters.count {
|
||||||
let filter = filters[i]
|
let filter = filters[i]
|
||||||
if case let .filter(type) = filter {
|
if case let .filter(type) = filter {
|
||||||
@ -272,6 +273,7 @@ final class ChatListSearchFiltersContainerNode: ASDisplayNode {
|
|||||||
let selectionFraction: CGFloat
|
let selectionFraction: CGFloat
|
||||||
if selectedFilter == filter.id {
|
if selectedFilter == filter.id {
|
||||||
selectionFraction = 1.0 - abs(transitionFraction)
|
selectionFraction = 1.0 - abs(transitionFraction)
|
||||||
|
hasSelection = true
|
||||||
} else if i != 0 && selectedFilter == filters[i - 1].id {
|
} else if i != 0 && selectedFilter == filters[i - 1].id {
|
||||||
selectionFraction = max(0.0, -transitionFraction)
|
selectionFraction = max(0.0, -transitionFraction)
|
||||||
} else if i != filters.count - 1 && selectedFilter == filters[i + 1].id {
|
} 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 minSpacing: CGFloat = 24.0
|
||||||
let spacing = minSpacing
|
var spacing = minSpacing
|
||||||
|
|
||||||
let resolvedSideInset: CGFloat = 16.0 + sideInset
|
let resolvedSideInset: CGFloat = 16.0 + sideInset
|
||||||
var leftOffset: CGFloat = resolvedSideInset
|
var leftOffset: CGFloat = resolvedSideInset
|
||||||
@ -340,6 +342,10 @@ final class ChatListSearchFiltersContainerNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
longTitlesWidth += resolvedSideInset
|
longTitlesWidth += resolvedSideInset
|
||||||
|
|
||||||
|
if longTitlesWidth < size.width && hasSelection {
|
||||||
|
spacing = (size.width - titlesWidth - resolvedSideInset * 2.0) / CGFloat(tabSizes.count - 1)
|
||||||
|
}
|
||||||
|
|
||||||
let verticalOffset: CGFloat = -3.0
|
let verticalOffset: CGFloat = -3.0
|
||||||
for i in 0 ..< tabSizes.count {
|
for i in 0 ..< tabSizes.count {
|
||||||
let (_, paneNodeSize, paneNode, wasAdded) = tabSizes[i]
|
let (_, paneNodeSize, paneNode, wasAdded) = tabSizes[i]
|
||||||
|
|||||||
@ -634,6 +634,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
|||||||
private let interaction: ChatListSearchInteraction
|
private let interaction: ChatListSearchInteraction
|
||||||
private let peersFilter: ChatListNodePeersFilter
|
private let peersFilter: ChatListNodePeersFilter
|
||||||
private var presentationData: PresentationData
|
private var presentationData: PresentationData
|
||||||
|
private let key: ChatListSearchPaneKey
|
||||||
private let tagMask: MessageTags?
|
private let tagMask: MessageTags?
|
||||||
private let navigationController: NavigationController?
|
private let navigationController: NavigationController?
|
||||||
|
|
||||||
@ -695,13 +696,30 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
|||||||
|
|
||||||
private var hiddenMediaDisposable: Disposable?
|
private var hiddenMediaDisposable: Disposable?
|
||||||
|
|
||||||
init(context: AccountContext, interaction: ChatListSearchInteraction, tagMask: MessageTags?, peersFilter: ChatListNodePeersFilter, searchQuery: Signal<String?, NoError>, searchOptions: Signal<ChatListSearchOptions?, NoError>, navigationController: NavigationController?) {
|
init(context: AccountContext, interaction: ChatListSearchInteraction, key: ChatListSearchPaneKey, peersFilter: ChatListNodePeersFilter, searchQuery: Signal<String?, NoError>, searchOptions: Signal<ChatListSearchOptions?, NoError>, navigationController: NavigationController?) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.interaction = interaction
|
self.interaction = interaction
|
||||||
|
self.key = key
|
||||||
self.peersFilter = peersFilter
|
self.peersFilter = peersFilter
|
||||||
self.tagMask = tagMask
|
|
||||||
self.navigationController = navigationController
|
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.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)))
|
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
|
return
|
||||||
}
|
}
|
||||||
switch item.content {
|
switch item.content {
|
||||||
case let .peer(peer):
|
case let .peer(_, peer, _, _, _, _, _, _, _, _, _, _):
|
||||||
if let peer = peer.peer.peer {
|
if let peer = peer.peer {
|
||||||
peerContextAction(peer, .search, node, gesture)
|
peerContextAction(peer, .search, node, gesture)
|
||||||
}
|
}
|
||||||
case .groupReference:
|
case .groupReference:
|
||||||
@ -1353,7 +1371,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
|||||||
strongSelf.ready.set(.single(true))
|
strongSelf.ready.set(.single(true))
|
||||||
strongSelf.didSetReady = true
|
strongSelf.didSetReady = true
|
||||||
} else if tagMask != nil {
|
} else if tagMask != nil {
|
||||||
interaction.updateSuggestedPeers(Array(peers.prefix(8)))
|
interaction.updateSuggestedPeers(Array(peers.prefix(8)), strongSelf.key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|||||||
@ -117,7 +117,7 @@ private final class VisualMediaItemNode: ASDisplayNode {
|
|||||||
if case .ended = recognizer.state {
|
if case .ended = recognizer.state {
|
||||||
if let (gesture, _) = recognizer.lastRecognizedGestureAndLocation {
|
if let (gesture, _) = recognizer.lastRecognizedGestureAndLocation {
|
||||||
if case .tap = gesture {
|
if case .tap = gesture {
|
||||||
if let (item, _, _, _) = self.item {
|
if let _ = self.item {
|
||||||
var media: Media?
|
var media: Media?
|
||||||
for value in message.media {
|
for value in message.media {
|
||||||
if let image = value as? TelegramMediaImage {
|
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)
|
self.fetchStatusDisposable.set((messageMediaFileStatus(context: context, messageId: message.id, file: file)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] status in
|
|> 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
|
strongSelf.resourceStatus = status
|
||||||
|
|
||||||
let isStreamable = isMediaStreamable(message: message, media: file)
|
let isStreamable = isMediaStreamable(message: message, media: file)
|
||||||
|
|||||||
@ -81,21 +81,7 @@ private final class ChatListSearchPendingPane {
|
|||||||
key: ChatListSearchPaneKey,
|
key: ChatListSearchPaneKey,
|
||||||
hasBecomeReady: @escaping (ChatListSearchPaneKey) -> Void
|
hasBecomeReady: @escaping (ChatListSearchPaneKey) -> Void
|
||||||
) {
|
) {
|
||||||
let paneNode: ChatListSearchPaneNode
|
let paneNode = ChatListSearchListPaneNode(context: context, interaction: interaction, key: key, peersFilter: key == .chats ? peersFilter : [], searchQuery: searchQuery, searchOptions: searchOptions, navigationController: navigationController)
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
self.pane = ChatListSearchPaneWrapper(key: key, node: paneNode)
|
self.pane = ChatListSearchPaneWrapper(key: key, node: paneNode)
|
||||||
self.disposable = (paneNode.isReady
|
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
|
transition.animateFrame(node: pane.node, from: paneFrame, to: paneFrame.offsetBy(dx: -paneSwitchAnimationOffset, dy: 0.0), completion: isAnimatingOut ? nil : { _ in
|
||||||
paneCompletion()
|
paneCompletion()
|
||||||
})
|
})
|
||||||
} else if let previousPaneKey = previousPaneKey, key == self.currentPaneKey {
|
} else if let _ = previousPaneKey, key == self.currentPaneKey {
|
||||||
pane.node.frame = adjustedFrame
|
pane.node.frame = adjustedFrame
|
||||||
let isAnimatingOut = pane.isAnimatingOut
|
let isAnimatingOut = pane.isAnimatingOut
|
||||||
pane.isAnimatingOut = true
|
pane.isAnimatingOut = true
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
|
import TelegramStringFormatting
|
||||||
|
|
||||||
private let telegramReleaseDate = Date(timeIntervalSince1970: 1376438400.0)
|
private let telegramReleaseDate = Date(timeIntervalSince1970: 1376438400.0)
|
||||||
|
|
||||||
@ -106,7 +107,8 @@ func suggestDates(for string: String, strings: PresentationStrings, dateTimeForm
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
|
||||||
for (day, value) in weekDays {
|
for (day, value) in weekDays {
|
||||||
let dayName = value.0.lowercased()
|
let dayName = value.0.lowercased()
|
||||||
let shortDayName = value.1.lowercased()
|
let shortDayName = value.1.lowercased()
|
||||||
@ -127,14 +129,24 @@ func suggestDates(for string: String, strings: PresentationStrings, dateTimeForm
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let cleanString = string.trimmingCharacters(in: .decimalDigits).trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
let cleanDigits = string.trimmingCharacters(in: .letters).trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
|
||||||
for (month, value) in months {
|
for (month, value) in months {
|
||||||
let monthName = value.0.lowercased()
|
let monthName = value.0.lowercased()
|
||||||
let shortMonthName = value.1.lowercased()
|
let shortMonthName = value.1.lowercased()
|
||||||
if string == shortMonthName || (string.count >= shortMonthName.count && monthName.hasPrefix(string)) {
|
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() {
|
for i in (year - 7 ... year).reversed() {
|
||||||
let date = getUpperMonthDate(month: month, year: i)
|
let date = getUpperMonthDate(month: month, year: i)
|
||||||
if date <= now && date > telegramReleaseDate {
|
if date <= now && date > telegramReleaseDate {
|
||||||
result.append((date, nil))
|
result.append((date, stringForMonth(strings: strings, month: Int32(month - 1), ofYear: Int32(i - 1900))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -110,6 +110,9 @@ private final class DateSelectionActionSheetItemNode: ActionSheetItemNode {
|
|||||||
self.pickerView.datePickerMode = .date
|
self.pickerView.datePickerMode = .date
|
||||||
self.pickerView.date = Date(timeIntervalSince1970: Double(roundDateToDays(currentValue)))
|
self.pickerView.date = Date(timeIntervalSince1970: Double(roundDateToDays(currentValue)))
|
||||||
self.pickerView.locale = localeWithStrings(strings)
|
self.pickerView.locale = localeWithStrings(strings)
|
||||||
|
if #available(iOS 13.4, *) {
|
||||||
|
self.pickerView.preferredDatePickerStyle = .wheels
|
||||||
|
}
|
||||||
if let minimumDate = minimumDate {
|
if let minimumDate = minimumDate {
|
||||||
self.pickerView.minimumDate = minimumDate
|
self.pickerView.minimumDate = minimumDate
|
||||||
}
|
}
|
||||||
|
|||||||
@ -101,6 +101,9 @@ private final class PeerBanTimeoutActionSheetItemNode: ActionSheetItemNode {
|
|||||||
self.pickerView.locale = localeWithStrings(strings)
|
self.pickerView.locale = localeWithStrings(strings)
|
||||||
self.pickerView.minimumDate = Date()
|
self.pickerView.minimumDate = Date()
|
||||||
self.pickerView.maximumDate = Date(timeIntervalSince1970: Double(Int32.max - 1))
|
self.pickerView.maximumDate = Date(timeIntervalSince1970: Double(Int32.max - 1))
|
||||||
|
if #available(iOS 13.4, *) {
|
||||||
|
self.pickerView.preferredDatePickerStyle = .wheels
|
||||||
|
}
|
||||||
|
|
||||||
super.init(theme: theme)
|
super.init(theme: theme)
|
||||||
|
|
||||||
|
|||||||
@ -56,6 +56,7 @@ public struct SearchBarToken {
|
|||||||
private final class TokenNode: ASDisplayNode {
|
private final class TokenNode: ASDisplayNode {
|
||||||
var theme: SearchBarNodeTheme
|
var theme: SearchBarNodeTheme
|
||||||
let token: SearchBarToken
|
let token: SearchBarToken
|
||||||
|
let containerNode: ASDisplayNode
|
||||||
let iconNode: ASImageNode
|
let iconNode: ASImageNode
|
||||||
let titleNode: ASTextNode
|
let titleNode: ASTextNode
|
||||||
let backgroundNode: ASImageNode
|
let backgroundNode: ASImageNode
|
||||||
@ -68,6 +69,7 @@ private final class TokenNode: ASDisplayNode {
|
|||||||
init(theme: SearchBarNodeTheme, token: SearchBarToken) {
|
init(theme: SearchBarNodeTheme, token: SearchBarToken) {
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.token = token
|
self.token = token
|
||||||
|
self.containerNode = ASDisplayNode()
|
||||||
self.iconNode = ASImageNode()
|
self.iconNode = ASImageNode()
|
||||||
self.iconNode.displaysAsynchronously = false
|
self.iconNode.displaysAsynchronously = false
|
||||||
self.iconNode.displayWithoutProcessing = true
|
self.iconNode.displayWithoutProcessing = true
|
||||||
@ -82,8 +84,8 @@ private final class TokenNode: ASDisplayNode {
|
|||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
self.clipsToBounds = true
|
self.clipsToBounds = true
|
||||||
|
self.addSubnode(self.containerNode)
|
||||||
self.addSubnode(self.backgroundNode)
|
self.containerNode.addSubnode(self.backgroundNode)
|
||||||
|
|
||||||
let backgroundColor = token.style?.backgroundColor ?? theme.inputIcon
|
let backgroundColor = token.style?.backgroundColor ?? theme.inputIcon
|
||||||
let strokeColor = token.style?.strokeColor ?? backgroundColor
|
let strokeColor = token.style?.strokeColor ?? backgroundColor
|
||||||
@ -91,10 +93,10 @@ private final class TokenNode: ASDisplayNode {
|
|||||||
|
|
||||||
let foregroundColor = token.style?.foregroundColor ?? .white
|
let foregroundColor = token.style?.foregroundColor ?? .white
|
||||||
self.iconNode.image = generateTintedImage(image: token.icon, color: foregroundColor)
|
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.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() {
|
override func didLoad() {
|
||||||
@ -108,8 +110,8 @@ private final class TokenNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func animateIn() {
|
func animateIn() {
|
||||||
let targetFrame = self.frame
|
let targetFrame = self.containerNode.frame
|
||||||
self.layer.animateFrame(from: CGRect(origin: targetFrame.origin, size: CGSize(width: 1.0, height: targetFrame.height)), to: targetFrame, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
|
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.iconNode.layer.animateScale(from: 0.1, to: 1.0, 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.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)
|
self.titleNode.layer.animateScale(from: 0.1, to: 1.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
|
||||||
@ -163,6 +165,7 @@ private final class TokenNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let size = CGSize(width: self.isCollapsed ? height : width, height: height)
|
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.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))
|
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: leftInset, y: floor((height - titleSize.height) / 2.0)), size: titleSize))
|
||||||
|
|
||||||
@ -1046,7 +1049,9 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate {
|
|||||||
|
|
||||||
public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
|
public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
|
||||||
if let _ = self.textField.selectedTokenIndex {
|
if let _ = self.textField.selectedTokenIndex {
|
||||||
|
if !string.isEmpty {
|
||||||
self.textField.selectedTokenIndex = nil
|
self.textField.selectedTokenIndex = nil
|
||||||
|
}
|
||||||
if string.range(of: " ") != nil {
|
if string.range(of: " ") != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@ -105,6 +105,9 @@ private final class ThemeAutoNightTimeSelectionActionSheetItemNode: ActionSheetI
|
|||||||
self.pickerView.timeZone = TimeZone(secondsFromGMT: 0)
|
self.pickerView.timeZone = TimeZone(secondsFromGMT: 0)
|
||||||
self.pickerView.date = Date(timeIntervalSince1970: Double(currentValue))
|
self.pickerView.date = Date(timeIntervalSince1970: Double(currentValue))
|
||||||
self.pickerView.locale = Locale.current
|
self.pickerView.locale = Locale.current
|
||||||
|
if #available(iOS 13.4, *) {
|
||||||
|
self.pickerView.preferredDatePickerStyle = .wheels
|
||||||
|
}
|
||||||
|
|
||||||
super.init(theme: theme)
|
super.init(theme: theme)
|
||||||
|
|
||||||
|
|||||||
@ -92,6 +92,10 @@ private final class ChatDateSelectorItemNode: ActionSheetItemNode {
|
|||||||
self.pickerView.minimumDate = Date(timeIntervalSince1970: 1376438400.0)
|
self.pickerView.minimumDate = Date(timeIntervalSince1970: 1376438400.0)
|
||||||
self.pickerView.maximumDate = Date(timeIntervalSinceNow: 2.0)
|
self.pickerView.maximumDate = Date(timeIntervalSinceNow: 2.0)
|
||||||
|
|
||||||
|
if #available(iOS 13.4, *) {
|
||||||
|
self.pickerView.preferredDatePickerStyle = .wheels
|
||||||
|
}
|
||||||
|
|
||||||
super.init(theme: theme)
|
super.init(theme: theme)
|
||||||
|
|
||||||
self.view.addSubview(self.pickerView)
|
self.view.addSubview(self.pickerView)
|
||||||
|
|||||||
@ -188,6 +188,9 @@ class ChatScheduleTimeControllerNode: ViewControllerTracingNode, UIScrollViewDel
|
|||||||
pickerView.minuteInterval = 1
|
pickerView.minuteInterval = 1
|
||||||
self.contentContainerNode.view.addSubview(pickerView)
|
self.contentContainerNode.view.addSubview(pickerView)
|
||||||
pickerView.addTarget(self, action: #selector(self.datePickerUpdated), for: .valueChanged)
|
pickerView.addTarget(self, action: #selector(self.datePickerUpdated), for: .valueChanged)
|
||||||
|
if #available(iOS 13.4, *) {
|
||||||
|
pickerView.preferredDatePickerStyle = .wheels
|
||||||
|
}
|
||||||
self.pickerView = pickerView
|
self.pickerView = pickerView
|
||||||
|
|
||||||
self.updateMinimumDate(currentTime: currentTime)
|
self.updateMinimumDate(currentTime: currentTime)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user