Skip to content

Commit

Permalink
Fixes #2624, fixes #2625 - Add user suggestions in the create room fl…
Browse files Browse the repository at this point in the history
…ows.
  • Loading branch information
stefanceriu committed Apr 3, 2024
1 parent a730689 commit 7a623bb
Show file tree
Hide file tree
Showing 14 changed files with 247 additions and 55 deletions.
7 changes: 4 additions & 3 deletions ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -985,11 +985,12 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
let selectedUsersSubject: CurrentValueSubject<[UserProfileProxy], Never> = .init([])

let stackCoordinator = NavigationStackCoordinator()
let inviteParameters = InviteUsersScreenCoordinatorParameters(selectedUsers: .init(selectedUsersSubject),
roomType: .room(roomProxy: roomProxy),
let inviteParameters = InviteUsersScreenCoordinatorParameters(clientProxy: userSession.clientProxy,
mediaProvider: userSession.mediaProvider,
userDiscoveryService: UserDiscoveryService(clientProxy: userSession.clientProxy),
userIndicatorController: userIndicatorController)
userIndicatorController: userIndicatorController,
selectedUsers: .init(selectedUsersSubject),
roomType: .room(roomProxy: roomProxy))

let coordinator = InviteUsersScreenCoordinator(parameters: inviteParameters)
stackCoordinator.setRootCoordinator(coordinator)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,10 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
if case .invitesScreen = stateMachine.state, availableInvitesCount == 1 {
dismissInvitesList(animated: true)
}

Task {
await userSession.clientProxy.trackRecentlyVisitedRoom(roomID)
}
}

private func tearDownRoomFlow(animated: Bool) {
Expand Down
4 changes: 4 additions & 0 deletions ElementX/Sources/Mocks/ClientProxyMock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ extension ClientProxyMock {
ignoreUserReturnValue = .success(())
unignoreUserReturnValue = .success(())

trackRecentlyVisitedRoomReturnValue = .success(())
recentlyVisitedRoomsReturnValue = .success([])
recentConversationCounterpartsReturnValue = []

loadMediaContentForSourceThrowableError = ClientProxyError.failedLoadingMedia
loadMediaThumbnailForSourceWidthHeightThrowableError = ClientProxyError.failedLoadingMedia
loadMediaFileForSourceBodyThrowableError = ClientProxyError.failedLoadingMedia
Expand Down
57 changes: 56 additions & 1 deletion ElementX/Sources/Mocks/Generated/GeneratedMocks.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Generated using Sourcery 2.1.8 — https://github.com/krzysztofzablocki/Sourcery
// Generated using Sourcery 2.2.2 — https://github.com/krzysztofzablocki/Sourcery
// DO NOT EDIT

// swiftlint:disable all
Expand Down Expand Up @@ -1211,6 +1211,61 @@ class ClientProxyMock: ClientProxyProtocol {
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

var loadMediaContentForSourceThrowableError: Error?
Expand Down
43 changes: 42 additions & 1 deletion ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift
Original file line number Diff line number Diff line change
@@ -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

// swiftlint:disable all
Expand Down Expand Up @@ -344,6 +344,27 @@ class SDKClientMock: SDKClientProtocol {
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

public var getSessionVerificationControllerThrowableError: Error?
Expand Down Expand Up @@ -749,6 +770,26 @@ class SDKClientMock: SDKClientProtocol {
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

public var unignoreUserUserIdThrowableError: Error?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ import Combine
import SwiftUI

struct InviteUsersScreenCoordinatorParameters {
let selectedUsers: CurrentValuePublisher<[UserProfileProxy], Never>
let roomType: InviteUsersScreenRoomType
let clientProxy: ClientProxyProtocol
let mediaProvider: MediaProviderProtocol
let userDiscoveryService: UserDiscoveryServiceProtocol
let userIndicatorController: UserIndicatorControllerProtocol

let selectedUsers: CurrentValuePublisher<[UserProfileProxy], Never>
let roomType: InviteUsersScreenRoomType
}

enum InviteUsersScreenCoordinatorAction {
Expand All @@ -42,11 +44,12 @@ final class InviteUsersScreenCoordinator: CoordinatorProtocol {
}

init(parameters: InviteUsersScreenCoordinatorParameters) {
viewModel = InviteUsersScreenViewModel(selectedUsers: parameters.selectedUsers,
roomType: parameters.roomType,
viewModel = InviteUsersScreenViewModel(clientProxy: parameters.clientProxy,
mediaProvider: parameters.mediaProvider,
userDiscoveryService: parameters.userDiscoveryService,
userIndicatorController: parameters.userIndicatorController)
userIndicatorController: parameters.userIndicatorController,
selectedUsers: parameters.selectedUsers,
roomType: parameters.roomType)
}

func start() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,42 @@ import SwiftUI
typealias InviteUsersScreenViewModelType = StateStoreViewModel<InviteUsersScreenViewState, InviteUsersScreenViewAction>

class InviteUsersScreenViewModel: InviteUsersScreenViewModelType, InviteUsersScreenViewModelProtocol {
private let clientProxy: ClientProxyProtocol
private let roomType: InviteUsersScreenRoomType
private let userDiscoveryService: UserDiscoveryServiceProtocol
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> {
actionsSubject.eraseToAnyPublisher()
}

init(selectedUsers: CurrentValuePublisher<[UserProfileProxy], Never>,
roomType: InviteUsersScreenRoomType,
init(clientProxy: ClientProxyProtocol,
mediaProvider: MediaProviderProtocol,
userDiscoveryService: UserDiscoveryServiceProtocol,
userIndicatorController: UserIndicatorControllerProtocol) {
self.roomType = roomType
userIndicatorController: UserIndicatorControllerProtocol,
selectedUsers: CurrentValuePublisher<[UserProfileProxy], Never>,
roomType: InviteUsersScreenRoomType) {
self.clientProxy = clientProxy
self.userDiscoveryService = userDiscoveryService
self.userIndicatorController = userIndicatorController

self.roomType = roomType

super.init(initialViewState: InviteUsersScreenViewState(selectedUsers: selectedUsers.value, isCreatingRoom: roomType.isCreatingRoom), imageProvider: mediaProvider)

setupSubscriptions(selectedUsers: selectedUsers)
fetchMembersIfNeeded()

Task {
suggestedUsers = await clientProxy.recentConversationCounterparts()

if state.usersSection.type == .suggestions {
state.usersSection = .init(type: .suggestions, users: suggestedUsers)
}
}
}

// MARK: - Public
Expand Down Expand Up @@ -127,29 +141,28 @@ class InviteUsersScreenViewModel: InviteUsersScreenViewModelType, InviteUsersScr

private func fetchUsers() {
guard searchQuery.count >= 3 else {
state.usersSection = .init(type: .suggestions, users: [])
state.usersSection = .init(type: .suggestions, users: suggestedUsers)
return
}

state.isSearching = true

fetchUsersTask = Task {
let result = await userDiscoveryService.searchProfiles(with: searchQuery)

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 {
context.searchQuery
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,11 +160,12 @@ struct InviteUsersScreen_Previews: PreviewProvider, TestablePreview {
static let viewModel = {
let userDiscoveryService = UserDiscoveryServiceMock()
userDiscoveryService.searchProfilesWithReturnValue = .success([.mockAlice])
return InviteUsersScreenViewModel(selectedUsers: .init([]),
roomType: .draft,
return InviteUsersScreenViewModel(clientProxy: ClientProxyMock(.init()),
mediaProvider: MockMediaProvider(),
userDiscoveryService: userDiscoveryService,
userIndicatorController: UserIndicatorControllerMock())
userIndicatorController: UserIndicatorControllerMock(),
selectedUsers: .init([]),
roomType: .draft)
}()

static var previews: some View {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,12 @@ final class StartChatScreenCoordinator: CoordinatorProtocol {
// MARK: - Private

private func presentInviteUsersScreen() {
let inviteParameters = InviteUsersScreenCoordinatorParameters(selectedUsers: selectedUsersPublisher,
roomType: .draft,
let inviteParameters = InviteUsersScreenCoordinatorParameters(clientProxy: parameters.userSession.clientProxy,
mediaProvider: parameters.userSession.mediaProvider,
userDiscoveryService: parameters.userDiscoveryService,
userIndicatorController: parameters.userIndicatorController)
userIndicatorController: parameters.userIndicatorController,
selectedUsers: selectedUsersPublisher,
roomType: .draft)
let coordinator = InviteUsersScreenCoordinator(parameters: inviteParameters)
coordinator.actions.sink { [weak self] action in
guard let self else { return }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ class StartChatScreenViewModel: StartChatScreenViewModelType, StartChatScreenVie
private let userIndicatorController: UserIndicatorControllerProtocol
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> {
actionsSubject.eraseToAnyPublisher()
}
Expand All @@ -43,6 +44,14 @@ class StartChatScreenViewModel: StartChatScreenViewModelType, StartChatScreenVie
super.init(initialViewState: StartChatScreenViewState(userID: userSession.userID), imageProvider: userSession.mediaProvider)

setupBindings()

Task {
suggestedUsers = await userSession.clientProxy.recentConversationCounterparts()

if state.usersSection.type == .suggestions {
state.usersSection = .init(type: .suggestions, users: suggestedUsers)
}
}
}

// MARK: - Public
Expand Down Expand Up @@ -102,22 +111,21 @@ class StartChatScreenViewModel: StartChatScreenViewModelType, StartChatScreenVie

private func fetchUsers() {
guard searchQuery.count >= 3 else {
state.usersSection = .init(type: .suggestions, users: [])
state.usersSection = .init(type: .suggestions, users: suggestedUsers)
return
}

fetchUsersTask = Task {
let result = await userDiscoveryService.searchProfiles(with: searchQuery)

guard !Task.isCancelled else { return }
handleResult(for: .searchResult, result: result)
}
}

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:
break

switch result {
case .success(let users):
state.usersSection = .init(type: .searchResult, users: users)
case .failure:
break
}
}
}

Expand Down
Loading

0 comments on commit 7a623bb

Please sign in to comment.