Completion Suggestion view for user mentions (#1859)

* created the list but I need to find a way to overlay it

* make the list able to have an intrinsic height

* best solution so far but does not work with expansion

* needs testing

* more scalable solution

* tests completed

* changelog

* injecting the media provider

* fix tests

* pr suggestions

* better testing
This commit is contained in:
Mauro
2023-10-06 15:47:31 +02:00
committed by GitHub
parent 72a96b6850
commit 8270a868a7
24 changed files with 605 additions and 31 deletions
@@ -0,0 +1,53 @@
//
// Copyright 2023 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Combine
import XCTest
@testable import ElementX
final class CompletionSuggestionServiceTests: XCTestCase {
private var cancellables = Set<AnyCancellable>()
override func setUp() {
cancellables.removeAll()
}
func testUserSuggestons() async throws {
let alice: RoomMemberProxyMock = .mockAlice
let members: [RoomMemberProxyMock] = [alice, .mockBob, .mockCharlie, .mockMe]
let roomProxyMock = RoomProxyMock(with: .init(displayName: "test", members: members))
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))]
}
service.setSuggestionTrigger(.init(type: .user, text: "ali"))
try await deferred.fulfill()
deferred = deferFulfillment(service.suggestionsPublisher) { suggestions in
suggestions == []
}
service.setSuggestionTrigger(.init(type: .user, text: "me"))
try await deferred.fulfill()
}
}
@@ -14,15 +14,18 @@
// limitations under the License.
//
import Combine
@testable import ElementX
import WysiwygComposer
import XCTest
import WysiwygComposer
@MainActor
class ComposerToolbarViewModelTests: XCTestCase {
private var appSettings: AppSettings!
private var wysiwygViewModel: WysiwygComposerViewModel!
private var viewModel: ComposerToolbarViewModel!
private var completionSuggestionServiceMock: CompletionSuggestionServiceMock!
override func setUp() {
AppSettings.reset()
@@ -30,7 +33,10 @@ class ComposerToolbarViewModelTests: XCTestCase {
appSettings.richTextEditorEnabled = true
ServiceLocator.shared.register(appSettings: appSettings)
wysiwygViewModel = WysiwygComposerViewModel()
viewModel = ComposerToolbarViewModel(wysiwygViewModel: wysiwygViewModel)
completionSuggestionServiceMock = CompletionSuggestionServiceMock(configuration: .init())
viewModel = ComposerToolbarViewModel(wysiwygViewModel: wysiwygViewModel,
completionSuggestionService: completionSuggestionServiceMock,
mediaProvider: MockMediaProvider())
}
func testComposerFocus() {
@@ -92,4 +98,23 @@ class ComposerToolbarViewModelTests: XCTestCase {
viewModel.process(viewAction: .composerAction(action: .link))
XCTAssertNotNil(viewModel.state.bindings.alertInfo)
}
func testSuggestions() {
let suggestions: [SuggestionItem] = [.user(item: MentionSuggestionItem(id: "@user_mention_1:matrix.org", displayName: "User 1", avatarURL: nil)),
.user(item: MentionSuggestionItem(id: "@user_mention_2:matrix.org", displayName: "User 2", avatarURL: URL.documentsDirectory))]
let mockCompletionSuggestionService = CompletionSuggestionServiceMock(configuration: .init(suggestions: suggestions))
viewModel = ComposerToolbarViewModel(wysiwygViewModel: wysiwygViewModel,
completionSuggestionService: mockCompletionSuggestionService,
mediaProvider: MockMediaProvider())
XCTAssertEqual(viewModel.state.suggestions, suggestions)
}
func testSuggestionTrigger() {
wysiwygViewModel.setMarkdownContent("@test")
wysiwygViewModel.setMarkdownContent("#not_implemented_yey")
// The first one is nil because when initialised the view model is empty
XCTAssertEqual(completionSuggestionServiceMock.setSuggestionTriggerReceivedInvocations, [nil, .init(type: .user, text: "test"), nil])
}
}
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1053b6b8361e483fdbfc3cab89610b5e6ba9642ee0c659a90880dc05779a0ee1
size 87735
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7bcd00bc4f83ce36c67954aa301858ad9ea1e35eb04c735e596f6481e75070eb
size 77023
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c9b4ab3c5f407fd0cd1086e74b8b8097ce26c6a980e009b03ee2bbcf6009d088
size 99560
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:dc30346f730c3ceaf00311d6d1f50c7ebb97879d658424c8fc6624e8f78c80ee
size 63677
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:792706480d9eb6b51fb407226f5ffa729ac54fc967c8f9925f2e30a6e3fe9ef2
size 60057