mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-09 03:20:48 +00:00
Business fixes
This commit is contained in:
parent
23103a97d1
commit
1db0db3929
@ -23,6 +23,7 @@ import PlainButtonComponent
|
||||
import MultilineTextComponent
|
||||
import AttachmentUI
|
||||
import SearchBarNode
|
||||
import BalancedTextComponent
|
||||
|
||||
final class QuickReplySetupScreenComponent: Component {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
@ -474,6 +475,7 @@ final class QuickReplySetupScreenComponent: Component {
|
||||
final class View: UIView {
|
||||
private var emptyState: ComponentView<Empty>?
|
||||
private var contentListNode: ContentListNode?
|
||||
private var emptySearchState: ComponentView<Empty>?
|
||||
|
||||
private let navigationBarView = ComponentView<Empty>()
|
||||
private var navigationHeight: CGFloat?
|
||||
@ -1171,6 +1173,41 @@ final class QuickReplySetupScreenComponent: Component {
|
||||
}
|
||||
contentListNode.setEntries(entries: entries, animated: !transition.animation.isImmediate)
|
||||
|
||||
if !self.searchQuery.isEmpty && entries.isEmpty {
|
||||
var emptySearchStateTransition = transition
|
||||
let emptySearchState: ComponentView<Empty>
|
||||
if let current = self.emptySearchState {
|
||||
emptySearchState = current
|
||||
} else {
|
||||
emptySearchStateTransition = emptySearchStateTransition.withAnimation(.none)
|
||||
emptySearchState = ComponentView()
|
||||
self.emptySearchState = emptySearchState
|
||||
}
|
||||
let emptySearchStateSize = emptySearchState.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(BalancedTextComponent(
|
||||
text: .plain(NSAttributedString(string: environment.strings.Conversation_SearchNoResults, font: Font.regular(17.0), textColor: environment.theme.list.freeTextColor, paragraphAlignment: .center)),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 0
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - 16.0 * 2.0, height: availableSize.height)
|
||||
)
|
||||
var emptySearchStateBottomInset = listBottomInset
|
||||
emptySearchStateBottomInset = max(emptySearchStateBottomInset, environment.inputHeight)
|
||||
let emptySearchStateFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - emptySearchStateSize.width) * 0.5), y: navigationHeight + floor((availableSize.height - emptySearchStateBottomInset - navigationHeight) * 0.5)), size: emptySearchStateSize)
|
||||
if let emptySearchStateView = emptySearchState.view {
|
||||
if emptySearchStateView.superview == nil {
|
||||
self.addSubview(emptySearchStateView)
|
||||
}
|
||||
emptySearchStateTransition.containedViewLayoutTransition.updatePosition(layer: emptySearchStateView.layer, position: emptySearchStateFrame.center)
|
||||
emptySearchStateView.bounds = CGRect(origin: CGPoint(), size: emptySearchStateFrame.size)
|
||||
}
|
||||
} else if let emptySearchState = self.emptySearchState {
|
||||
self.emptySearchState = nil
|
||||
emptySearchState.view?.removeFromSuperview()
|
||||
}
|
||||
|
||||
if let shortcutMessageList = self.shortcutMessageList, !shortcutMessageList.items.isEmpty {
|
||||
contentListNode.isHidden = false
|
||||
} else {
|
||||
|
@ -27,7 +27,9 @@ private func wrappedMinuteRange(range: Range<Int>, dayIndexOffset: Int = 0) -> I
|
||||
|
||||
var result = IndexSet()
|
||||
if mappedRange.upperBound > 7 * 24 * 60 {
|
||||
result.insert(integersIn: mappedRange.lowerBound ..< 7 * 24 * 60)
|
||||
if mappedRange.lowerBound < 7 * 24 * 60 {
|
||||
result.insert(integersIn: mappedRange.lowerBound ..< 7 * 24 * 60)
|
||||
}
|
||||
result.insert(integersIn: 0 ..< (mappedRange.upperBound - 7 * 24 * 60))
|
||||
} else {
|
||||
result.insert(integersIn: mappedRange)
|
||||
|
@ -23,6 +23,8 @@ swift_library(
|
||||
"//submodules/PresentationDataUtils",
|
||||
"//submodules/SearchBarNode",
|
||||
"//submodules/TelegramUIPreferences",
|
||||
"//submodules/ComponentFlow",
|
||||
"//submodules/Components/BalancedTextComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -13,6 +13,8 @@ import AccountContext
|
||||
import SearchBarNode
|
||||
import SearchUI
|
||||
import TelegramUIPreferences
|
||||
import ComponentFlow
|
||||
import BalancedTextComponent
|
||||
|
||||
private struct TimezoneListEntry: Comparable, Identifiable {
|
||||
var id: String
|
||||
@ -60,6 +62,7 @@ private struct TimezoneListSearchContainerTransition {
|
||||
let insertions: [ListViewInsertItem]
|
||||
let updates: [ListViewUpdateItem]
|
||||
let isSearching: Bool
|
||||
let isEmptyResult: Bool
|
||||
}
|
||||
|
||||
private func preparedLanguageListSearchContainerTransition(presentationData: PresentationData, from fromEntries: [TimezoneListEntry], to toEntries: [TimezoneListEntry], action: @escaping (String) -> Void, isSearching: Bool, forceUpdate: Bool) -> TimezoneListSearchContainerTransition {
|
||||
@ -69,7 +72,7 @@ private func preparedLanguageListSearchContainerTransition(presentationData: Pre
|
||||
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(presentationData: presentationData, searchMode: true, action: action), directionHint: nil) }
|
||||
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(presentationData: presentationData, searchMode: true, action: action), directionHint: nil) }
|
||||
|
||||
return TimezoneListSearchContainerTransition(deletions: deletions, insertions: insertions, updates: updates, isSearching: isSearching)
|
||||
return TimezoneListSearchContainerTransition(deletions: deletions, insertions: insertions, updates: updates, isSearching: isSearching, isEmptyResult: isSearching && toEntries.isEmpty)
|
||||
}
|
||||
|
||||
private final class TimezoneListSearchContainerNode: SearchDisplayControllerContentNode {
|
||||
@ -77,6 +80,8 @@ private final class TimezoneListSearchContainerNode: SearchDisplayControllerCont
|
||||
private let dimNode: ASDisplayNode
|
||||
private let listNode: ListView
|
||||
|
||||
private var notFoundText: ComponentView<Empty>?
|
||||
|
||||
private var enqueuedTransitions: [TimezoneListSearchContainerTransition] = []
|
||||
private var hasValidLayout = false
|
||||
|
||||
@ -88,6 +93,9 @@ private final class TimezoneListSearchContainerNode: SearchDisplayControllerCont
|
||||
|
||||
private let presentationDataPromise: Promise<PresentationData>
|
||||
|
||||
private var isEmptyResult: Bool = false
|
||||
private var currentLayout: (layout: ContainerViewLayout, navigationBarHeight: CGFloat)?
|
||||
|
||||
public override var hasDim: Bool {
|
||||
return true
|
||||
}
|
||||
@ -220,14 +228,26 @@ private final class TimezoneListSearchContainerNode: SearchDisplayControllerCont
|
||||
options.insert(.PreferSynchronousDrawing)
|
||||
|
||||
let isSearching = transition.isSearching
|
||||
let isEmptyResult = transition.isEmptyResult
|
||||
|
||||
self.listNode.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, updateSizeAndInsets: nil, updateOpaqueState: nil, completion: { [weak self] _ in
|
||||
self?.listNode.isHidden = !isSearching
|
||||
self?.dimNode.isHidden = isSearching
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.listNode.isHidden = !isSearching
|
||||
self.dimNode.isHidden = isSearching
|
||||
self.isEmptyResult = isEmptyResult
|
||||
|
||||
if let currentLayout = self.currentLayout {
|
||||
self.containerLayoutUpdated(currentLayout.layout, navigationBarHeight: currentLayout.navigationBarHeight, transition: .immediate)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
self.currentLayout = (layout, navigationBarHeight)
|
||||
|
||||
super.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition)
|
||||
|
||||
let topInset = navigationBarHeight
|
||||
@ -244,6 +264,36 @@ private final class TimezoneListSearchContainerNode: SearchDisplayControllerCont
|
||||
self.dequeueTransitions()
|
||||
}
|
||||
}
|
||||
|
||||
if self.isEmptyResult {
|
||||
let notFoundText: ComponentView<Empty>
|
||||
if let current = self.notFoundText {
|
||||
notFoundText = current
|
||||
} else {
|
||||
notFoundText = ComponentView()
|
||||
self.notFoundText = notFoundText
|
||||
}
|
||||
let notFoundTextSize = notFoundText.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(BalancedTextComponent(
|
||||
text: .plain(NSAttributedString(string: self.presentationData.strings.Conversation_SearchNoResults, font: Font.regular(17.0), textColor: self.presentationData.theme.list.freeTextColor, paragraphAlignment: .center)),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 0
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: layout.size.width - 16.0 * 2.0, height: layout.size.height)
|
||||
)
|
||||
let notFoundTextFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - notFoundTextSize.width) * 0.5), y: navigationBarHeight + floor((layout.size.height - navigationBarHeight - notFoundTextSize.height) * 0.5)), size: notFoundTextSize)
|
||||
if let notFoundTextView = notFoundText.view {
|
||||
if notFoundTextView.superview == nil {
|
||||
self.view.addSubview(notFoundTextView)
|
||||
}
|
||||
notFoundTextView.frame = notFoundTextFrame
|
||||
}
|
||||
} else if let notFoundText = self.notFoundText {
|
||||
self.notFoundText = nil
|
||||
notFoundText.view?.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
@objc func dimTapGesture(_ recognizer: UITapGestureRecognizer) {
|
||||
|
@ -28,10 +28,6 @@ private func inputQueryResultPriority(_ result: ChatPresentationInputQueryResult
|
||||
}
|
||||
|
||||
func inputContextPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceState: ChatPresentationInterfaceState, context: AccountContext, currentPanel: ChatInputContextPanelNode?, controllerInteraction: ChatControllerInteraction, interfaceInteraction: ChatPanelInterfaceInteraction?, chatPresentationContext: ChatPresentationContext) -> ChatInputContextPanelNode? {
|
||||
guard let _ = chatPresentationInterfaceState.renderedPeer?.peer else {
|
||||
return nil
|
||||
}
|
||||
|
||||
if chatPresentationInterfaceState.showCommands, let renderedPeer = chatPresentationInterfaceState.renderedPeer {
|
||||
if let currentPanel = currentPanel as? CommandMenuChatInputContextPanelNode {
|
||||
return currentPanel
|
||||
|
@ -15,9 +15,6 @@ import ChatPresentationInterfaceState
|
||||
import ChatContextQuery
|
||||
|
||||
func contextQueryResultStateForChatInterfacePresentationState(_ chatPresentationInterfaceState: ChatPresentationInterfaceState, context: AccountContext, currentQueryStates: inout [ChatPresentationInputQueryKind: (ChatPresentationInputQuery, Disposable)], requestBotLocationStatus: @escaping (PeerId) -> Void) -> [ChatPresentationInputQueryKind: ChatContextQueryUpdate] {
|
||||
guard let peer = chatPresentationInterfaceState.renderedPeer?.peer else {
|
||||
return [:]
|
||||
}
|
||||
let inputQueries = inputContextQueriesForChatPresentationIntefaceState(chatPresentationInterfaceState).filter({ query in
|
||||
if chatPresentationInterfaceState.editMessageState != nil {
|
||||
switch query {
|
||||
@ -36,7 +33,7 @@ func contextQueryResultStateForChatInterfacePresentationState(_ chatPresentation
|
||||
for query in inputQueries {
|
||||
let previousQuery = currentQueryStates[query.kind]?.0
|
||||
if previousQuery != query {
|
||||
let signal = updatedContextQueryResultStateForQuery(context: context, peer: peer, chatLocation: chatPresentationInterfaceState.chatLocation, inputQuery: query, previousQuery: previousQuery, requestBotLocationStatus: requestBotLocationStatus)
|
||||
let signal = updatedContextQueryResultStateForQuery(context: context, peer: chatPresentationInterfaceState.renderedPeer?.peer, chatLocation: chatPresentationInterfaceState.chatLocation, inputQuery: query, previousQuery: previousQuery, requestBotLocationStatus: requestBotLocationStatus)
|
||||
updates[query.kind] = .update(query, signal)
|
||||
}
|
||||
}
|
||||
@ -57,7 +54,7 @@ func contextQueryResultStateForChatInterfacePresentationState(_ chatPresentation
|
||||
return updates
|
||||
}
|
||||
|
||||
private func updatedContextQueryResultStateForQuery(context: AccountContext, peer: Peer, chatLocation: ChatLocation, inputQuery: ChatPresentationInputQuery, previousQuery: ChatPresentationInputQuery?, requestBotLocationStatus: @escaping (PeerId) -> Void) -> Signal<(ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?, ChatContextQueryError> {
|
||||
private func updatedContextQueryResultStateForQuery(context: AccountContext, peer: Peer?, chatLocation: ChatLocation, inputQuery: ChatPresentationInputQuery, previousQuery: ChatPresentationInputQuery?, requestBotLocationStatus: @escaping (PeerId) -> Void) -> Signal<(ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?, ChatContextQueryError> {
|
||||
switch inputQuery {
|
||||
case let .emoji(query):
|
||||
var signal: Signal<(ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?, ChatContextQueryError> = .complete()
|
||||
@ -154,46 +151,64 @@ private func updatedContextQueryResultStateForQuery(context: AccountContext, pee
|
||||
|
||||
let inlineBots: Signal<[(EnginePeer, Double)], NoError> = types.contains(.contextBots) ? context.engine.peers.recentlyUsedInlineBots() : .single([])
|
||||
let strings = context.sharedContext.currentPresentationData.with({ $0 }).strings
|
||||
let participants = combineLatest(inlineBots, searchPeerMembers(context: context, peerId: peer.id, chatLocation: chatLocation, query: query, scope: .mention))
|
||||
|> map { inlineBots, peers -> (ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult? in
|
||||
let filteredInlineBots = inlineBots.sorted(by: { $0.1 > $1.1 }).filter { peer, rating in
|
||||
if rating < 0.14 {
|
||||
|
||||
let participants: Signal<(ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?, ChatContextQueryError>
|
||||
if let peer {
|
||||
participants = combineLatest(
|
||||
inlineBots,
|
||||
searchPeerMembers(context: context, peerId: peer.id, chatLocation: chatLocation, query: query, scope: .mention)
|
||||
)
|
||||
|> map { inlineBots, peers -> (ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult? in
|
||||
let filteredInlineBots = inlineBots.sorted(by: { $0.1 > $1.1 }).filter { peer, rating in
|
||||
if rating < 0.14 {
|
||||
return false
|
||||
}
|
||||
if peer.indexName.matchesByTokens(normalizedQuery) {
|
||||
return true
|
||||
}
|
||||
if let addressName = peer.addressName, addressName.lowercased().hasPrefix(normalizedQuery) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
if peer.indexName.matchesByTokens(normalizedQuery) {
|
||||
}.map { $0.0 }
|
||||
|
||||
let inlineBotPeerIds = Set(filteredInlineBots.map { $0.id })
|
||||
|
||||
let filteredPeers = peers.filter { peer in
|
||||
if inlineBotPeerIds.contains(peer.id) {
|
||||
return false
|
||||
}
|
||||
if !types.contains(.accountPeer) && peer.id == context.account.peerId {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
if let addressName = peer.addressName, addressName.lowercased().hasPrefix(normalizedQuery) {
|
||||
return true
|
||||
var sortedPeers = filteredInlineBots
|
||||
sortedPeers.append(contentsOf: filteredPeers.sorted(by: { lhs, rhs in
|
||||
let result = lhs.indexName.stringRepresentation(lastNameFirst: true).compare(rhs.indexName.stringRepresentation(lastNameFirst: true))
|
||||
return result == .orderedAscending
|
||||
}))
|
||||
sortedPeers = sortedPeers.filter { peer in
|
||||
return !peer.displayTitle(strings: strings, displayOrder: .firstLast).isEmpty
|
||||
}
|
||||
return false
|
||||
}.map { $0.0 }
|
||||
|
||||
let inlineBotPeerIds = Set(filteredInlineBots.map { $0.id })
|
||||
|
||||
let filteredPeers = peers.filter { peer in
|
||||
if inlineBotPeerIds.contains(peer.id) {
|
||||
return false
|
||||
}
|
||||
if !types.contains(.accountPeer) && peer.id == context.account.peerId {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return { _ in return .mentions(sortedPeers) }
|
||||
}
|
||||
var sortedPeers = filteredInlineBots
|
||||
sortedPeers.append(contentsOf: filteredPeers.sorted(by: { lhs, rhs in
|
||||
let result = lhs.indexName.stringRepresentation(lastNameFirst: true).compare(rhs.indexName.stringRepresentation(lastNameFirst: true))
|
||||
return result == .orderedAscending
|
||||
}))
|
||||
sortedPeers = sortedPeers.filter { peer in
|
||||
return !peer.displayTitle(strings: strings, displayOrder: .firstLast).isEmpty
|
||||
}
|
||||
return { _ in return .mentions(sortedPeers) }
|
||||
|> castError(ChatContextQueryError.self)
|
||||
} else {
|
||||
participants = .single({ _ in return nil })
|
||||
}
|
||||
|> castError(ChatContextQueryError.self)
|
||||
|
||||
return signal |> then(participants)
|
||||
case let .command(query):
|
||||
guard let peer else {
|
||||
return .single({ _ in return .commands(ChatInputQueryCommandsResult(
|
||||
commands: [],
|
||||
accountPeer: nil,
|
||||
hasShortcuts: false,
|
||||
query: ""
|
||||
)) })
|
||||
}
|
||||
|
||||
let normalizedQuery = query.lowercased()
|
||||
|
||||
var signal: Signal<(ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?, ChatContextQueryError> = .complete()
|
||||
@ -258,6 +273,9 @@ private func updatedContextQueryResultStateForQuery(context: AccountContext, pee
|
||||
|> castError(ChatContextQueryError.self)
|
||||
return signal |> then(commands)
|
||||
case let .contextRequest(addressName, query):
|
||||
guard let peer else {
|
||||
return .single({ _ in return .contextRequestResult(nil, nil) })
|
||||
}
|
||||
var delayRequest = true
|
||||
var signal: Signal<(ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?, ChatContextQueryError> = .complete()
|
||||
if let previousQuery = previousQuery {
|
||||
|
Loading…
x
Reference in New Issue
Block a user