mirror of
https://github.com/element-hq/element-x-ios.git
synced 2025-03-10 21:39:12 +00:00
Fix Suggestion Pattern when text is empty (#1943)
* fix + debounce improvement * comment * improved existing function to be more generic
This commit is contained in:
parent
2f57fbc77d
commit
4c84878f88
@ -25,16 +25,23 @@ extension Publisher where Self.Failure == Never {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Publisher where Output == String, Failure == Never {
|
extension Publisher where Output: Equatable, Failure == Never {
|
||||||
/// Debounce text queries and remove duplicates.
|
func debounceAndRemoveDuplicates<S: Scheduler>(on scheduler: S, delay: @escaping (Output) -> S.SchedulerTimeType.Stride) -> AnyPublisher<Output, Never> {
|
||||||
/// Clearing the text publishes the update immediately.
|
|
||||||
func debounceAndRemoveDuplicates() -> AnyPublisher<String, Never> {
|
|
||||||
map { query in
|
map { query in
|
||||||
let milliseconds = query.isEmpty ? 0 : 250
|
Just(query).delay(for: delay(query), scheduler: scheduler)
|
||||||
return Just(query).delay(for: .milliseconds(milliseconds), scheduler: DispatchQueue.main)
|
|
||||||
}
|
}
|
||||||
.switchToLatest()
|
.switchToLatest()
|
||||||
.removeDuplicates()
|
.removeDuplicates()
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension Publisher where Output == String, Failure == Never {
|
||||||
|
/// Debounce text queries and remove duplicates.
|
||||||
|
/// Clearing the text publishes the update immediately.
|
||||||
|
func debounceTextQueriesAndRemoveDuplicates() -> AnyPublisher<String, Never> {
|
||||||
|
debounceAndRemoveDuplicates(on: DispatchQueue.main) { query in
|
||||||
|
query.isEmpty ? .milliseconds(0) : .milliseconds(250)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -55,8 +55,10 @@ final class CompletionSuggestionService: CompletionSuggestionServiceProtocol {
|
|||||||
return membersSuggestion
|
return membersSuggestion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.debounce(for: 0.5, scheduler: DispatchQueue.main)
|
// We only debounce if the suggestion is nil
|
||||||
.eraseToAnyPublisher()
|
.debounceAndRemoveDuplicates(on: DispatchQueue.main) { [weak self] _ in
|
||||||
|
self?.suggestionTriggerSubject.value != nil ? .milliseconds(500) : .milliseconds(0)
|
||||||
|
}
|
||||||
|
|
||||||
Task {
|
Task {
|
||||||
switch await roomProxy.canUserTriggerRoomNotification(userID: roomProxy.ownUserID) {
|
switch await roomProxy.canUserTriggerRoomNotification(userID: roomProxy.ownUserID) {
|
||||||
@ -73,6 +75,10 @@ final class CompletionSuggestionService: CompletionSuggestionServiceProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static func isIncluded(searchText: String, userID: String, displayName: String?) -> Bool {
|
private static func isIncluded(searchText: String, userID: String, displayName: String?) -> Bool {
|
||||||
|
// If the search text is empty give back all the results
|
||||||
|
guard !searchText.isEmpty else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
let containedInUserID = userID.localizedStandardContains(searchText.lowercased())
|
let containedInUserID = userID.localizedStandardContains(searchText.lowercased())
|
||||||
|
|
||||||
let containedInDisplayName: Bool
|
let containedInDisplayName: Bool
|
||||||
|
@ -95,7 +95,7 @@ class InviteUsersScreenViewModel: InviteUsersScreenViewModelType, InviteUsersScr
|
|||||||
private func setupSubscriptions(selectedUsers: CurrentValuePublisher<[UserProfileProxy], Never>) {
|
private func setupSubscriptions(selectedUsers: CurrentValuePublisher<[UserProfileProxy], Never>) {
|
||||||
context.$viewState
|
context.$viewState
|
||||||
.map(\.bindings.searchQuery)
|
.map(\.bindings.searchQuery)
|
||||||
.debounceAndRemoveDuplicates()
|
.debounceTextQueriesAndRemoveDuplicates()
|
||||||
.sink { [weak self] _ in
|
.sink { [weak self] _ in
|
||||||
self?.fetchUsers()
|
self?.fetchUsers()
|
||||||
}
|
}
|
||||||
|
@ -92,7 +92,7 @@ class StartChatScreenViewModel: StartChatScreenViewModelType, StartChatScreenVie
|
|||||||
private func setupBindings() {
|
private func setupBindings() {
|
||||||
context.$viewState
|
context.$viewState
|
||||||
.map(\.bindings.searchQuery)
|
.map(\.bindings.searchQuery)
|
||||||
.debounceAndRemoveDuplicates()
|
.debounceTextQueriesAndRemoveDuplicates()
|
||||||
.sink { [weak self] _ in
|
.sink { [weak self] _ in
|
||||||
self?.fetchUsers()
|
self?.fetchUsers()
|
||||||
}
|
}
|
||||||
|
@ -87,4 +87,26 @@ final class CompletionSuggestionServiceTests: XCTestCase {
|
|||||||
service.setSuggestionTrigger(.init(type: .user, text: "every"))
|
service.setSuggestionTrigger(.init(type: .user, text: "every"))
|
||||||
try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testUserSuggestionsWithEmptyText() async throws {
|
||||||
|
let alice: RoomMemberProxyMock = .mockAlice
|
||||||
|
let bob: RoomMemberProxyMock = .mockBob
|
||||||
|
let members: [RoomMemberProxyMock] = [alice, bob, .mockMe]
|
||||||
|
let roomProxyMock = RoomProxyMock(with: .init(displayName: "test", members: members, canUserTriggerRoomNotification: true))
|
||||||
|
let service = CompletionSuggestionService(roomProxy: roomProxyMock, areSuggestionsEnabled: true)
|
||||||
|
|
||||||
|
var deferred = deferFulfillment(service.suggestionsPublisher) { suggestions in
|
||||||
|
suggestions == []
|
||||||
|
}
|
||||||
|
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
|
deferred = deferFulfillment(service.suggestionsPublisher) { suggestions in
|
||||||
|
suggestions == [.user(item: .init(id: alice.userID, displayName: alice.displayName, avatarURL: alice.avatarURL)),
|
||||||
|
.user(item: .init(id: bob.userID, displayName: bob.displayName, avatarURL: bob.avatarURL)),
|
||||||
|
.allUsers(item: .allUsersMention(roomAvatar: nil))]
|
||||||
|
}
|
||||||
|
service.setSuggestionTrigger(.init(type: .user, text: ""))
|
||||||
|
try await deferred.fulfill()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user