Business fixes

This commit is contained in:
Isaac 2024-03-01 15:18:46 +04:00
parent 23103a97d1
commit 1db0db3929
6 changed files with 149 additions and 44 deletions

View File

@ -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 {

View File

@ -27,7 +27,9 @@ private func wrappedMinuteRange(range: Range<Int>, dayIndexOffset: Int = 0) -> I
var result = IndexSet()
if mappedRange.upperBound > 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)

View File

@ -23,6 +23,8 @@ swift_library(
"//submodules/PresentationDataUtils",
"//submodules/SearchBarNode",
"//submodules/TelegramUIPreferences",
"//submodules/ComponentFlow",
"//submodules/Components/BalancedTextComponent",
],
visibility = [
"//visibility:public",

View File

@ -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) {

View File

@ -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

View File

@ -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,7 +151,13 @@ 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))
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 {
@ -191,9 +194,21 @@ private func updatedContextQueryResultStateForQuery(context: AccountContext, pee
return { _ in return .mentions(sortedPeers) }
}
|> castError(ChatContextQueryError.self)
} else {
participants = .single({ _ in return nil })
}
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 {