mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-07 21:08:28 +00:00
This commit is contained in:
committed by
Stefan Ceriu
parent
c85c1593ee
commit
97d9189077
@@ -7176,7 +7176,7 @@
|
|||||||
repositoryURL = "https://github.com/matrix-org/matrix-rust-components-swift";
|
repositoryURL = "https://github.com/matrix-org/matrix-rust-components-swift";
|
||||||
requirement = {
|
requirement = {
|
||||||
kind = exactVersion;
|
kind = exactVersion;
|
||||||
version = 1.1.55;
|
version = 1.1.56;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
821C67C9A7F8CC3FD41B28B4 /* XCRemoteSwiftPackageReference "emojibase-bindings" */ = {
|
821C67C9A7F8CC3FD41B28B4 /* XCRemoteSwiftPackageReference "emojibase-bindings" */ = {
|
||||||
|
|||||||
@@ -139,8 +139,8 @@
|
|||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/matrix-org/matrix-rust-components-swift",
|
"location" : "https://github.com/matrix-org/matrix-rust-components-swift",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "407ad3da9444148ec71a775029c69c25e4a8bb2e",
|
"revision" : "1d47d496ca46e123e102e64d45635fab73de3d78",
|
||||||
"version" : "1.1.55"
|
"version" : "1.1.56"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -263,7 +263,7 @@
|
|||||||
{
|
{
|
||||||
"identity" : "swiftui-introspect",
|
"identity" : "swiftui-introspect",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/siteline/SwiftUI-Introspect",
|
"location" : "https://github.com/siteline/SwiftUI-Introspect.git",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "b94da693e57eaf79d16464b8b7c90d09cba4e290",
|
"revision" : "b94da693e57eaf79d16464b8b7c90d09cba4e290",
|
||||||
"version" : "0.9.2"
|
"version" : "0.9.2"
|
||||||
|
|||||||
@@ -1009,11 +1009,12 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
let selectedUsersSubject: CurrentValueSubject<[UserProfileProxy], Never> = .init([])
|
let selectedUsersSubject: CurrentValueSubject<[UserProfileProxy], Never> = .init([])
|
||||||
|
|
||||||
let stackCoordinator = NavigationStackCoordinator()
|
let stackCoordinator = NavigationStackCoordinator()
|
||||||
let inviteParameters = InviteUsersScreenCoordinatorParameters(selectedUsers: .init(selectedUsersSubject),
|
let inviteParameters = InviteUsersScreenCoordinatorParameters(clientProxy: userSession.clientProxy,
|
||||||
roomType: .room(roomProxy: roomProxy),
|
|
||||||
mediaProvider: userSession.mediaProvider,
|
mediaProvider: userSession.mediaProvider,
|
||||||
userDiscoveryService: UserDiscoveryService(clientProxy: userSession.clientProxy),
|
userDiscoveryService: UserDiscoveryService(clientProxy: userSession.clientProxy),
|
||||||
userIndicatorController: userIndicatorController)
|
userIndicatorController: userIndicatorController,
|
||||||
|
selectedUsers: .init(selectedUsersSubject),
|
||||||
|
roomType: .room(roomProxy: roomProxy))
|
||||||
|
|
||||||
let coordinator = InviteUsersScreenCoordinator(parameters: inviteParameters)
|
let coordinator = InviteUsersScreenCoordinator(parameters: inviteParameters)
|
||||||
stackCoordinator.setRootCoordinator(coordinator)
|
stackCoordinator.setRootCoordinator(coordinator)
|
||||||
|
|||||||
@@ -460,6 +460,10 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
if case .invitesScreen = stateMachine.state, availableInvitesCount == 1 {
|
if case .invitesScreen = stateMachine.state, availableInvitesCount == 1 {
|
||||||
dismissInvitesList(animated: true)
|
dismissInvitesList(animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Task {
|
||||||
|
await userSession.clientProxy.trackRecentlyVisitedRoom(roomID)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func tearDownRoomFlow(animated: Bool) {
|
private func tearDownRoomFlow(animated: Bool) {
|
||||||
|
|||||||
@@ -73,6 +73,10 @@ extension ClientProxyMock {
|
|||||||
ignoreUserReturnValue = .success(())
|
ignoreUserReturnValue = .success(())
|
||||||
unignoreUserReturnValue = .success(())
|
unignoreUserReturnValue = .success(())
|
||||||
|
|
||||||
|
trackRecentlyVisitedRoomReturnValue = .success(())
|
||||||
|
recentlyVisitedRoomsReturnValue = .success([])
|
||||||
|
recentConversationCounterpartsReturnValue = []
|
||||||
|
|
||||||
loadMediaContentForSourceThrowableError = ClientProxyError.sdkError(ClientProxyMockError.generic)
|
loadMediaContentForSourceThrowableError = ClientProxyError.sdkError(ClientProxyMockError.generic)
|
||||||
loadMediaThumbnailForSourceWidthHeightThrowableError = ClientProxyError.sdkError(ClientProxyMockError.generic)
|
loadMediaThumbnailForSourceWidthHeightThrowableError = ClientProxyError.sdkError(ClientProxyMockError.generic)
|
||||||
loadMediaFileForSourceBodyThrowableError = ClientProxyError.sdkError(ClientProxyMockError.generic)
|
loadMediaFileForSourceBodyThrowableError = ClientProxyError.sdkError(ClientProxyMockError.generic)
|
||||||
|
|||||||
@@ -1211,6 +1211,61 @@ class ClientProxyMock: ClientProxyProtocol {
|
|||||||
return unignoreUserReturnValue
|
return unignoreUserReturnValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//MARK: - trackRecentlyVisitedRoom
|
||||||
|
|
||||||
|
var trackRecentlyVisitedRoomCallsCount = 0
|
||||||
|
var trackRecentlyVisitedRoomCalled: Bool {
|
||||||
|
return trackRecentlyVisitedRoomCallsCount > 0
|
||||||
|
}
|
||||||
|
var trackRecentlyVisitedRoomReceivedRoomID: String?
|
||||||
|
var trackRecentlyVisitedRoomReceivedInvocations: [String] = []
|
||||||
|
var trackRecentlyVisitedRoomReturnValue: Result<Void, ClientProxyError>!
|
||||||
|
var trackRecentlyVisitedRoomClosure: ((String) async -> Result<Void, ClientProxyError>)?
|
||||||
|
|
||||||
|
func trackRecentlyVisitedRoom(_ roomID: String) async -> Result<Void, ClientProxyError> {
|
||||||
|
trackRecentlyVisitedRoomCallsCount += 1
|
||||||
|
trackRecentlyVisitedRoomReceivedRoomID = roomID
|
||||||
|
trackRecentlyVisitedRoomReceivedInvocations.append(roomID)
|
||||||
|
if let trackRecentlyVisitedRoomClosure = trackRecentlyVisitedRoomClosure {
|
||||||
|
return await trackRecentlyVisitedRoomClosure(roomID)
|
||||||
|
} else {
|
||||||
|
return trackRecentlyVisitedRoomReturnValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//MARK: - recentlyVisitedRooms
|
||||||
|
|
||||||
|
var recentlyVisitedRoomsCallsCount = 0
|
||||||
|
var recentlyVisitedRoomsCalled: Bool {
|
||||||
|
return recentlyVisitedRoomsCallsCount > 0
|
||||||
|
}
|
||||||
|
var recentlyVisitedRoomsReturnValue: Result<[String], ClientProxyError>!
|
||||||
|
var recentlyVisitedRoomsClosure: (() async -> Result<[String], ClientProxyError>)?
|
||||||
|
|
||||||
|
func recentlyVisitedRooms() async -> Result<[String], ClientProxyError> {
|
||||||
|
recentlyVisitedRoomsCallsCount += 1
|
||||||
|
if let recentlyVisitedRoomsClosure = recentlyVisitedRoomsClosure {
|
||||||
|
return await recentlyVisitedRoomsClosure()
|
||||||
|
} else {
|
||||||
|
return recentlyVisitedRoomsReturnValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//MARK: - recentConversationCounterparts
|
||||||
|
|
||||||
|
var recentConversationCounterpartsCallsCount = 0
|
||||||
|
var recentConversationCounterpartsCalled: Bool {
|
||||||
|
return recentConversationCounterpartsCallsCount > 0
|
||||||
|
}
|
||||||
|
var recentConversationCounterpartsReturnValue: [UserProfileProxy]!
|
||||||
|
var recentConversationCounterpartsClosure: (() async -> [UserProfileProxy])?
|
||||||
|
|
||||||
|
func recentConversationCounterparts() async -> [UserProfileProxy] {
|
||||||
|
recentConversationCounterpartsCallsCount += 1
|
||||||
|
if let recentConversationCounterpartsClosure = recentConversationCounterpartsClosure {
|
||||||
|
return await recentConversationCounterpartsClosure()
|
||||||
|
} else {
|
||||||
|
return recentConversationCounterpartsReturnValue
|
||||||
|
}
|
||||||
|
}
|
||||||
//MARK: - loadMediaContentForSource
|
//MARK: - loadMediaContentForSource
|
||||||
|
|
||||||
var loadMediaContentForSourceThrowableError: Error?
|
var loadMediaContentForSourceThrowableError: Error?
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Generated using Sourcery 2.1.7 — https://github.com/krzysztofzablocki/Sourcery
|
// Generated using Sourcery 2.1.8 — https://github.com/krzysztofzablocki/Sourcery
|
||||||
// DO NOT EDIT
|
// DO NOT EDIT
|
||||||
|
|
||||||
// swiftlint:disable all
|
// swiftlint:disable all
|
||||||
@@ -344,6 +344,27 @@ class SDKClientMock: SDKClientProtocol {
|
|||||||
return getProfileUserIdReturnValue
|
return getProfileUserIdReturnValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//MARK: - getRecentlyVisitedRooms
|
||||||
|
|
||||||
|
public var getRecentlyVisitedRoomsThrowableError: Error?
|
||||||
|
public var getRecentlyVisitedRoomsCallsCount = 0
|
||||||
|
public var getRecentlyVisitedRoomsCalled: Bool {
|
||||||
|
return getRecentlyVisitedRoomsCallsCount > 0
|
||||||
|
}
|
||||||
|
public var getRecentlyVisitedRoomsReturnValue: [String]!
|
||||||
|
public var getRecentlyVisitedRoomsClosure: (() async throws -> [String])?
|
||||||
|
|
||||||
|
public func getRecentlyVisitedRooms() async throws -> [String] {
|
||||||
|
if let error = getRecentlyVisitedRoomsThrowableError {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
getRecentlyVisitedRoomsCallsCount += 1
|
||||||
|
if let getRecentlyVisitedRoomsClosure = getRecentlyVisitedRoomsClosure {
|
||||||
|
return try await getRecentlyVisitedRoomsClosure()
|
||||||
|
} else {
|
||||||
|
return getRecentlyVisitedRoomsReturnValue
|
||||||
|
}
|
||||||
|
}
|
||||||
//MARK: - getSessionVerificationController
|
//MARK: - getSessionVerificationController
|
||||||
|
|
||||||
public var getSessionVerificationControllerThrowableError: Error?
|
public var getSessionVerificationControllerThrowableError: Error?
|
||||||
@@ -749,6 +770,26 @@ class SDKClientMock: SDKClientProtocol {
|
|||||||
return syncServiceReturnValue
|
return syncServiceReturnValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//MARK: - trackRecentlyVisitedRoom
|
||||||
|
|
||||||
|
public var trackRecentlyVisitedRoomRoomThrowableError: Error?
|
||||||
|
public var trackRecentlyVisitedRoomRoomCallsCount = 0
|
||||||
|
public var trackRecentlyVisitedRoomRoomCalled: Bool {
|
||||||
|
return trackRecentlyVisitedRoomRoomCallsCount > 0
|
||||||
|
}
|
||||||
|
public var trackRecentlyVisitedRoomRoomReceivedRoom: String?
|
||||||
|
public var trackRecentlyVisitedRoomRoomReceivedInvocations: [String] = []
|
||||||
|
public var trackRecentlyVisitedRoomRoomClosure: ((String) async throws -> Void)?
|
||||||
|
|
||||||
|
public func trackRecentlyVisitedRoom(room: String) async throws {
|
||||||
|
if let error = trackRecentlyVisitedRoomRoomThrowableError {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
trackRecentlyVisitedRoomRoomCallsCount += 1
|
||||||
|
trackRecentlyVisitedRoomRoomReceivedRoom = room
|
||||||
|
trackRecentlyVisitedRoomRoomReceivedInvocations.append(room)
|
||||||
|
try await trackRecentlyVisitedRoomRoomClosure?(room)
|
||||||
|
}
|
||||||
//MARK: - unignoreUser
|
//MARK: - unignoreUser
|
||||||
|
|
||||||
public var unignoreUserUserIdThrowableError: Error?
|
public var unignoreUserUserIdThrowableError: Error?
|
||||||
|
|||||||
@@ -18,11 +18,13 @@ import Combine
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct InviteUsersScreenCoordinatorParameters {
|
struct InviteUsersScreenCoordinatorParameters {
|
||||||
let selectedUsers: CurrentValuePublisher<[UserProfileProxy], Never>
|
let clientProxy: ClientProxyProtocol
|
||||||
let roomType: InviteUsersScreenRoomType
|
|
||||||
let mediaProvider: MediaProviderProtocol
|
let mediaProvider: MediaProviderProtocol
|
||||||
let userDiscoveryService: UserDiscoveryServiceProtocol
|
let userDiscoveryService: UserDiscoveryServiceProtocol
|
||||||
let userIndicatorController: UserIndicatorControllerProtocol
|
let userIndicatorController: UserIndicatorControllerProtocol
|
||||||
|
|
||||||
|
let selectedUsers: CurrentValuePublisher<[UserProfileProxy], Never>
|
||||||
|
let roomType: InviteUsersScreenRoomType
|
||||||
}
|
}
|
||||||
|
|
||||||
enum InviteUsersScreenCoordinatorAction {
|
enum InviteUsersScreenCoordinatorAction {
|
||||||
@@ -42,11 +44,12 @@ final class InviteUsersScreenCoordinator: CoordinatorProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
init(parameters: InviteUsersScreenCoordinatorParameters) {
|
init(parameters: InviteUsersScreenCoordinatorParameters) {
|
||||||
viewModel = InviteUsersScreenViewModel(selectedUsers: parameters.selectedUsers,
|
viewModel = InviteUsersScreenViewModel(clientProxy: parameters.clientProxy,
|
||||||
roomType: parameters.roomType,
|
|
||||||
mediaProvider: parameters.mediaProvider,
|
mediaProvider: parameters.mediaProvider,
|
||||||
userDiscoveryService: parameters.userDiscoveryService,
|
userDiscoveryService: parameters.userDiscoveryService,
|
||||||
userIndicatorController: parameters.userIndicatorController)
|
userIndicatorController: parameters.userIndicatorController,
|
||||||
|
selectedUsers: parameters.selectedUsers,
|
||||||
|
roomType: parameters.roomType)
|
||||||
}
|
}
|
||||||
|
|
||||||
func start() {
|
func start() {
|
||||||
|
|||||||
@@ -21,28 +21,42 @@ import SwiftUI
|
|||||||
typealias InviteUsersScreenViewModelType = StateStoreViewModel<InviteUsersScreenViewState, InviteUsersScreenViewAction>
|
typealias InviteUsersScreenViewModelType = StateStoreViewModel<InviteUsersScreenViewState, InviteUsersScreenViewAction>
|
||||||
|
|
||||||
class InviteUsersScreenViewModel: InviteUsersScreenViewModelType, InviteUsersScreenViewModelProtocol {
|
class InviteUsersScreenViewModel: InviteUsersScreenViewModelType, InviteUsersScreenViewModelProtocol {
|
||||||
|
private let clientProxy: ClientProxyProtocol
|
||||||
private let roomType: InviteUsersScreenRoomType
|
private let roomType: InviteUsersScreenRoomType
|
||||||
private let userDiscoveryService: UserDiscoveryServiceProtocol
|
private let userDiscoveryService: UserDiscoveryServiceProtocol
|
||||||
private let userIndicatorController: UserIndicatorControllerProtocol
|
private let userIndicatorController: UserIndicatorControllerProtocol
|
||||||
|
|
||||||
private let actionsSubject: PassthroughSubject<InviteUsersScreenViewModelAction, Never> = .init()
|
private var suggestedUsers = [UserProfileProxy]()
|
||||||
|
|
||||||
|
private let actionsSubject: PassthroughSubject<InviteUsersScreenViewModelAction, Never> = .init()
|
||||||
var actions: AnyPublisher<InviteUsersScreenViewModelAction, Never> {
|
var actions: AnyPublisher<InviteUsersScreenViewModelAction, Never> {
|
||||||
actionsSubject.eraseToAnyPublisher()
|
actionsSubject.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
init(selectedUsers: CurrentValuePublisher<[UserProfileProxy], Never>,
|
init(clientProxy: ClientProxyProtocol,
|
||||||
roomType: InviteUsersScreenRoomType,
|
|
||||||
mediaProvider: MediaProviderProtocol,
|
mediaProvider: MediaProviderProtocol,
|
||||||
userDiscoveryService: UserDiscoveryServiceProtocol,
|
userDiscoveryService: UserDiscoveryServiceProtocol,
|
||||||
userIndicatorController: UserIndicatorControllerProtocol) {
|
userIndicatorController: UserIndicatorControllerProtocol,
|
||||||
self.roomType = roomType
|
selectedUsers: CurrentValuePublisher<[UserProfileProxy], Never>,
|
||||||
|
roomType: InviteUsersScreenRoomType) {
|
||||||
|
self.clientProxy = clientProxy
|
||||||
self.userDiscoveryService = userDiscoveryService
|
self.userDiscoveryService = userDiscoveryService
|
||||||
self.userIndicatorController = userIndicatorController
|
self.userIndicatorController = userIndicatorController
|
||||||
|
|
||||||
|
self.roomType = roomType
|
||||||
|
|
||||||
super.init(initialViewState: InviteUsersScreenViewState(selectedUsers: selectedUsers.value, isCreatingRoom: roomType.isCreatingRoom), imageProvider: mediaProvider)
|
super.init(initialViewState: InviteUsersScreenViewState(selectedUsers: selectedUsers.value, isCreatingRoom: roomType.isCreatingRoom), imageProvider: mediaProvider)
|
||||||
|
|
||||||
setupSubscriptions(selectedUsers: selectedUsers)
|
setupSubscriptions(selectedUsers: selectedUsers)
|
||||||
fetchMembersIfNeeded()
|
fetchMembersIfNeeded()
|
||||||
|
|
||||||
|
Task {
|
||||||
|
suggestedUsers = await clientProxy.recentConversationCounterparts()
|
||||||
|
|
||||||
|
if state.usersSection.type == .suggestions {
|
||||||
|
state.usersSection = .init(type: .suggestions, users: suggestedUsers)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Public
|
// MARK: - Public
|
||||||
@@ -127,29 +141,28 @@ class InviteUsersScreenViewModel: InviteUsersScreenViewModelType, InviteUsersScr
|
|||||||
|
|
||||||
private func fetchUsers() {
|
private func fetchUsers() {
|
||||||
guard searchQuery.count >= 3 else {
|
guard searchQuery.count >= 3 else {
|
||||||
state.usersSection = .init(type: .suggestions, users: [])
|
state.usersSection = .init(type: .suggestions, users: suggestedUsers)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
state.isSearching = true
|
state.isSearching = true
|
||||||
|
|
||||||
fetchUsersTask = Task {
|
fetchUsersTask = Task {
|
||||||
let result = await userDiscoveryService.searchProfiles(with: searchQuery)
|
let result = await userDiscoveryService.searchProfiles(with: searchQuery)
|
||||||
|
|
||||||
guard !Task.isCancelled else { return }
|
guard !Task.isCancelled else { return }
|
||||||
handleResult(for: .searchResult, result: result)
|
|
||||||
|
state.isSearching = false
|
||||||
|
|
||||||
|
switch result {
|
||||||
|
case .success(let users):
|
||||||
|
state.usersSection = .init(type: .searchResult, users: users)
|
||||||
|
case .failure:
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func handleResult(for sectionType: UserDiscoverySectionType, result: Result<[UserProfileProxy], UserDiscoveryErrorType>) {
|
|
||||||
state.isSearching = false
|
|
||||||
|
|
||||||
switch result {
|
|
||||||
case .success(let users):
|
|
||||||
state.usersSection = .init(type: sectionType, users: users)
|
|
||||||
case .failure:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var searchQuery: String {
|
private var searchQuery: String {
|
||||||
context.searchQuery
|
context.searchQuery
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -160,11 +160,12 @@ struct InviteUsersScreen_Previews: PreviewProvider, TestablePreview {
|
|||||||
static let viewModel = {
|
static let viewModel = {
|
||||||
let userDiscoveryService = UserDiscoveryServiceMock()
|
let userDiscoveryService = UserDiscoveryServiceMock()
|
||||||
userDiscoveryService.searchProfilesWithReturnValue = .success([.mockAlice])
|
userDiscoveryService.searchProfilesWithReturnValue = .success([.mockAlice])
|
||||||
return InviteUsersScreenViewModel(selectedUsers: .init([]),
|
return InviteUsersScreenViewModel(clientProxy: ClientProxyMock(.init()),
|
||||||
roomType: .draft,
|
|
||||||
mediaProvider: MockMediaProvider(),
|
mediaProvider: MockMediaProvider(),
|
||||||
userDiscoveryService: userDiscoveryService,
|
userDiscoveryService: userDiscoveryService,
|
||||||
userIndicatorController: UserIndicatorControllerMock())
|
userIndicatorController: UserIndicatorControllerMock(),
|
||||||
|
selectedUsers: .init([]),
|
||||||
|
roomType: .draft)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
|
|||||||
@@ -84,11 +84,12 @@ final class StartChatScreenCoordinator: CoordinatorProtocol {
|
|||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
|
|
||||||
private func presentInviteUsersScreen() {
|
private func presentInviteUsersScreen() {
|
||||||
let inviteParameters = InviteUsersScreenCoordinatorParameters(selectedUsers: selectedUsersPublisher,
|
let inviteParameters = InviteUsersScreenCoordinatorParameters(clientProxy: parameters.userSession.clientProxy,
|
||||||
roomType: .draft,
|
|
||||||
mediaProvider: parameters.userSession.mediaProvider,
|
mediaProvider: parameters.userSession.mediaProvider,
|
||||||
userDiscoveryService: parameters.userDiscoveryService,
|
userDiscoveryService: parameters.userDiscoveryService,
|
||||||
userIndicatorController: parameters.userIndicatorController)
|
userIndicatorController: parameters.userIndicatorController,
|
||||||
|
selectedUsers: selectedUsersPublisher,
|
||||||
|
roomType: .draft)
|
||||||
let coordinator = InviteUsersScreenCoordinator(parameters: inviteParameters)
|
let coordinator = InviteUsersScreenCoordinator(parameters: inviteParameters)
|
||||||
coordinator.actions.sink { [weak self] action in
|
coordinator.actions.sink { [weak self] action in
|
||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
|
|||||||
@@ -25,8 +25,9 @@ class StartChatScreenViewModel: StartChatScreenViewModelType, StartChatScreenVie
|
|||||||
private let userIndicatorController: UserIndicatorControllerProtocol
|
private let userIndicatorController: UserIndicatorControllerProtocol
|
||||||
private let userDiscoveryService: UserDiscoveryServiceProtocol
|
private let userDiscoveryService: UserDiscoveryServiceProtocol
|
||||||
|
|
||||||
private let actionsSubject: PassthroughSubject<StartChatScreenViewModelAction, Never> = .init()
|
private var suggestedUsers = [UserProfileProxy]()
|
||||||
|
|
||||||
|
private let actionsSubject: PassthroughSubject<StartChatScreenViewModelAction, Never> = .init()
|
||||||
var actions: AnyPublisher<StartChatScreenViewModelAction, Never> {
|
var actions: AnyPublisher<StartChatScreenViewModelAction, Never> {
|
||||||
actionsSubject.eraseToAnyPublisher()
|
actionsSubject.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
@@ -43,6 +44,14 @@ class StartChatScreenViewModel: StartChatScreenViewModelType, StartChatScreenVie
|
|||||||
super.init(initialViewState: StartChatScreenViewState(userID: userSession.userID), imageProvider: userSession.mediaProvider)
|
super.init(initialViewState: StartChatScreenViewState(userID: userSession.userID), imageProvider: userSession.mediaProvider)
|
||||||
|
|
||||||
setupBindings()
|
setupBindings()
|
||||||
|
|
||||||
|
Task {
|
||||||
|
suggestedUsers = await userSession.clientProxy.recentConversationCounterparts()
|
||||||
|
|
||||||
|
if state.usersSection.type == .suggestions {
|
||||||
|
state.usersSection = .init(type: .suggestions, users: suggestedUsers)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Public
|
// MARK: - Public
|
||||||
@@ -89,25 +98,24 @@ class StartChatScreenViewModel: StartChatScreenViewModelType, StartChatScreenVie
|
|||||||
|
|
||||||
private func fetchUsers() {
|
private func fetchUsers() {
|
||||||
guard context.searchQuery.count >= 3 else {
|
guard context.searchQuery.count >= 3 else {
|
||||||
state.usersSection = .init(type: .suggestions, users: [])
|
state.usersSection = .init(type: .suggestions, users: suggestedUsers)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchUsersTask = Task {
|
fetchUsersTask = Task {
|
||||||
let result = await userDiscoveryService.searchProfiles(with: context.searchQuery)
|
let result = await userDiscoveryService.searchProfiles(with: context.searchQuery)
|
||||||
|
|
||||||
guard !Task.isCancelled else { return }
|
guard !Task.isCancelled else { return }
|
||||||
handleResult(for: .searchResult, result: result)
|
|
||||||
|
switch result {
|
||||||
|
case .success(let users):
|
||||||
|
state.usersSection = .init(type: .searchResult, users: users)
|
||||||
|
case .failure:
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func handleResult(for sectionType: UserDiscoverySectionType, result: Result<[UserProfileProxy], UserDiscoveryErrorType>) {
|
|
||||||
switch result {
|
|
||||||
case .success(let users):
|
|
||||||
state.usersSection = .init(type: sectionType, users: users)
|
|
||||||
case .failure:
|
|
||||||
state.bindings.alertInfo = AlertInfo(id: .unknown)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func createDirectRoom(with user: UserProfileProxy) async {
|
private func createDirectRoom(with user: UserProfileProxy) async {
|
||||||
defer {
|
defer {
|
||||||
hideLoadingIndicator()
|
hideLoadingIndicator()
|
||||||
|
|||||||
@@ -574,7 +574,7 @@ class ClientProxy: ClientProxyProtocol {
|
|||||||
RoomDirectorySearchProxy(roomDirectorySearch: client.roomDirectorySearch())
|
RoomDirectorySearchProxy(roomDirectorySearch: client.roomDirectorySearch())
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Ignored users
|
// MARK: Ignored users
|
||||||
|
|
||||||
func ignoreUser(_ userID: String) async -> Result<Void, ClientProxyError> {
|
func ignoreUser(_ userID: String) async -> Result<Void, ClientProxyError> {
|
||||||
do {
|
do {
|
||||||
@@ -596,7 +596,58 @@ class ClientProxy: ClientProxyProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Private
|
// MARK: Recently visited rooms
|
||||||
|
|
||||||
|
func trackRecentlyVisitedRoom(_ roomID: String) async -> Result<Void, ClientProxyError> {
|
||||||
|
do {
|
||||||
|
try await client.trackRecentlyVisitedRoom(room: roomID)
|
||||||
|
return .success(())
|
||||||
|
} catch {
|
||||||
|
MXLog.error("Failed tracking recently visited room: \(roomID) with error: \(error)")
|
||||||
|
return .failure(.sdkError(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func recentlyVisitedRooms() async -> Result<[String], ClientProxyError> {
|
||||||
|
do {
|
||||||
|
let result = try await client.getRecentlyVisitedRooms()
|
||||||
|
return .success(result)
|
||||||
|
} catch {
|
||||||
|
MXLog.error("Failed retrieving recently visited rooms with error: \(error)")
|
||||||
|
return .failure(.sdkError(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func recentConversationCounterparts() async -> [UserProfileProxy] {
|
||||||
|
let maxResultsToReturn = 5
|
||||||
|
|
||||||
|
guard case let .success(roomIdentifiers) = await recentlyVisitedRooms() else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
var users = [UserProfileProxy]()
|
||||||
|
|
||||||
|
for roomID in roomIdentifiers {
|
||||||
|
guard let room = await roomForIdentifier(roomID),
|
||||||
|
room.isDirect,
|
||||||
|
let members = await room.members() else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for member in members where member.isActive && member.userID != userID {
|
||||||
|
users.append(.init(userID: member.userID, displayName: member.displayName, avatarURL: member.avatarURL))
|
||||||
|
|
||||||
|
// Return early to avoid unnecessary work
|
||||||
|
if users.count >= maxResultsToReturn {
|
||||||
|
return users
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return users
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private
|
||||||
|
|
||||||
private func loadUserAvatarURLFromCache() {
|
private func loadUserAvatarURLFromCache() {
|
||||||
loadCachedAvatarURLTask = Task {
|
loadCachedAvatarURLTask = Task {
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ enum ClientProxyLoadingState {
|
|||||||
|
|
||||||
enum ClientProxyError: Error {
|
enum ClientProxyError: Error {
|
||||||
case sdkError(Error)
|
case sdkError(Error)
|
||||||
|
|
||||||
case invalidMedia
|
case invalidMedia
|
||||||
case failedUploadingMedia(Error, MatrixErrorCode)
|
case failedUploadingMedia(Error, MatrixErrorCode)
|
||||||
}
|
}
|
||||||
@@ -147,4 +148,12 @@ protocol ClientProxyProtocol: AnyObject, MediaLoaderProtocol {
|
|||||||
func ignoreUser(_ userID: String) async -> Result<Void, ClientProxyError>
|
func ignoreUser(_ userID: String) async -> Result<Void, ClientProxyError>
|
||||||
|
|
||||||
func unignoreUser(_ userID: String) async -> Result<Void, ClientProxyError>
|
func unignoreUser(_ userID: String) async -> Result<Void, ClientProxyError>
|
||||||
|
|
||||||
|
// MARK: - Recently visited rooms
|
||||||
|
|
||||||
|
func trackRecentlyVisitedRoom(_ roomID: String) async -> Result<Void, ClientProxyError>
|
||||||
|
|
||||||
|
func recentlyVisitedRooms() async -> Result<[String], ClientProxyError>
|
||||||
|
|
||||||
|
func recentConversationCounterparts() async -> [UserProfileProxy]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,10 +98,12 @@ class InviteUsersScreenViewModelTests: XCTestCase {
|
|||||||
userDiscoveryService = UserDiscoveryServiceMock()
|
userDiscoveryService = UserDiscoveryServiceMock()
|
||||||
userDiscoveryService.searchProfilesWithReturnValue = .success([])
|
userDiscoveryService.searchProfilesWithReturnValue = .success([])
|
||||||
usersSubject.send([])
|
usersSubject.send([])
|
||||||
let viewModel = InviteUsersScreenViewModel(selectedUsers: usersSubject.asCurrentValuePublisher(),
|
let viewModel = InviteUsersScreenViewModel(clientProxy: ClientProxyMock(.init()),
|
||||||
roomType: roomType, mediaProvider: MockMediaProvider(),
|
mediaProvider: MockMediaProvider(),
|
||||||
userDiscoveryService: userDiscoveryService,
|
userDiscoveryService: userDiscoveryService,
|
||||||
userIndicatorController: UserIndicatorControllerMock())
|
userIndicatorController: UserIndicatorControllerMock(),
|
||||||
|
selectedUsers: usersSubject.asCurrentValuePublisher(),
|
||||||
|
roomType: roomType)
|
||||||
viewModel.state.usersSection = .init(type: .suggestions, users: [.mockAlice, .mockBob, .mockCharlie])
|
viewModel.state.usersSection = .init(type: .suggestions, users: [.mockAlice, .mockBob, .mockCharlie])
|
||||||
self.viewModel = viewModel
|
self.viewModel = viewModel
|
||||||
|
|
||||||
|
|||||||
@@ -222,10 +222,9 @@ class LoggingTests: XCTestCase {
|
|||||||
let rustEmoteMessage = EmoteMessageContent(body: emoteString,
|
let rustEmoteMessage = EmoteMessageContent(body: emoteString,
|
||||||
formatted: FormattedBody(format: .html, body: "<b>\(emoteString)</b>"))
|
formatted: FormattedBody(format: .html, body: "<b>\(emoteString)</b>"))
|
||||||
|
|
||||||
let pointer = Unmanaged.passRetained(NSURL(fileURLWithPath: "/tmp/file")).toOpaque()
|
let rustImageMessage = ImageMessageContent(body: "ImageString", formatted: nil, filename: nil, source: MediaSource(noPointer: .init()), info: nil)
|
||||||
let rustImageMessage = ImageMessageContent(body: "ImageString", formatted: nil, filename: nil, source: MediaSource(unsafeFromRawPointer: pointer), info: nil)
|
let rustVideoMessage = VideoMessageContent(body: "VideoString", formatted: nil, filename: nil, source: MediaSource(noPointer: .init()), info: nil)
|
||||||
let rustVideoMessage = VideoMessageContent(body: "VideoString", formatted: nil, filename: nil, source: MediaSource(unsafeFromRawPointer: pointer), info: nil)
|
let rustFileMessage = FileMessageContent(body: "FileString", formatted: nil, filename: "FileName", source: MediaSource(noPointer: .init()), info: nil)
|
||||||
let rustFileMessage = FileMessageContent(body: "FileString", formatted: nil, filename: "FileName", source: MediaSource(unsafeFromRawPointer: pointer), info: nil)
|
|
||||||
|
|
||||||
// When logging that value
|
// When logging that value
|
||||||
MXLog.info(rustTextMessage)
|
MXLog.info(rustTextMessage)
|
||||||
|
|||||||
+1
-1
@@ -48,7 +48,7 @@ packages:
|
|||||||
# Element/Matrix dependencies
|
# Element/Matrix dependencies
|
||||||
MatrixRustSDK:
|
MatrixRustSDK:
|
||||||
url: https://github.com/matrix-org/matrix-rust-components-swift
|
url: https://github.com/matrix-org/matrix-rust-components-swift
|
||||||
exactVersion: 1.1.55
|
exactVersion: 1.1.56
|
||||||
# path: ../matrix-rust-sdk
|
# path: ../matrix-rust-sdk
|
||||||
Compound:
|
Compound:
|
||||||
url: https://github.com/element-hq/compound-ios
|
url: https://github.com/element-hq/compound-ios
|
||||||
|
|||||||
Reference in New Issue
Block a user