diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/search/SearchAllPeopleScreen.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/search/SearchAllPeopleScreen.kt index 8179cf086f2..c20d256b3d6 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/search/SearchAllPeopleScreen.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/search/SearchAllPeopleScreen.kt @@ -18,14 +18,11 @@ package com.wire.android.ui.home.conversations.search -import androidx.annotation.StringRes import androidx.compose.animation.AnimatedContent -import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.lazy.LazyColumn @@ -37,26 +34,32 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import com.wire.android.R import com.wire.android.model.Clickable import com.wire.android.ui.common.button.WireSecondaryButton import com.wire.android.ui.common.dimensions -import com.wire.android.ui.common.progress.WireCircularProgressIndicator +import com.wire.android.ui.common.progress.CenteredCircularProgressBarIndicator import com.wire.android.ui.common.snackbar.LocalSnackbarHostState import com.wire.android.ui.home.conversations.search.widget.SearchFailureBox +import com.wire.android.ui.home.conversationslist.model.Membership import com.wire.android.ui.home.newconversation.SendConnectionRequestViewModel +import com.wire.android.ui.home.newconversation.SendConnectionRequestViewModelImpl +import com.wire.android.ui.home.newconversation.SendConnectionRequestViewModelPreview import com.wire.android.ui.home.newconversation.model.Contact import com.wire.android.ui.theme.WireTheme import com.wire.android.util.extension.folderWithElements import com.wire.android.util.ui.PreviewMultipleThemes +import com.wire.kalium.logic.data.user.ConnectionState import com.wire.kalium.logic.data.user.UserId import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableSet +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.persistentSetOf +import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.launch private const val DEFAULT_SEARCH_RESULT_ITEM_SIZE = 4 @@ -64,7 +67,6 @@ private const val DEFAULT_SEARCH_RESULT_ITEM_SIZE = 4 @Composable fun SearchAllPeopleScreen( searchQuery: String, - noneSearchSucceed: Boolean, contactsSearchResult: ImmutableList, publicSearchResult: ImmutableList, contactsAddedToGroup: ImmutableSet, @@ -74,26 +76,24 @@ fun SearchAllPeopleScreen( onOpenUserProfile: (Contact) -> Unit, lazyListState: LazyListState = rememberLazyListState() ) { - if (contactsSearchResult.isEmpty() && publicSearchResult.isEmpty()) { - EmptySearchQueryScreen() - } else { - if (noneSearchSucceed) { - SearchFailureBox(R.string.label_no_results_found) - } else { - Column { - SearchResult( - searchQuery = searchQuery, - publicSearchResult = publicSearchResult, - contactsSearchResult = contactsSearchResult, - contactsAddedToGroup = contactsAddedToGroup, - onChecked = onChecked, - onOpenUserProfile = onOpenUserProfile, - lazyListState = lazyListState, - isSearchActive = isSearchActive, - isLoading = isLoading - ) - } - } + val emptyResults = contactsSearchResult.isEmpty() && publicSearchResult.isEmpty() + when { + isLoading -> CenteredCircularProgressBarIndicator() + + searchQuery.isBlank() && emptyResults -> EmptySearchQueryScreen() + + searchQuery.isNotBlank() && emptyResults -> SearchFailureBox(R.string.label_no_results_found) + + else -> SearchResult( + searchQuery = searchQuery, + publicSearchResult = publicSearchResult, + contactsSearchResult = contactsSearchResult, + contactsAddedToGroup = contactsAddedToGroup, + onChecked = onChecked, + onOpenUserProfile = onOpenUserProfile, + lazyListState = lazyListState, + isSearchActive = isSearchActive, + ) } } @@ -102,12 +102,13 @@ private fun SearchResult( searchQuery: String, contactsSearchResult: ImmutableList, publicSearchResult: ImmutableList, - isLoading: Boolean, isSearchActive: Boolean, contactsAddedToGroup: ImmutableSet, onChecked: (Boolean, Contact) -> Unit, onOpenUserProfile: (Contact) -> Unit, - sendConnectionRequestViewModel: SendConnectionRequestViewModel = hiltViewModel(), + sendConnectionRequestViewModel: SendConnectionRequestViewModel = + if (LocalInspectionMode.current) SendConnectionRequestViewModelPreview + else hiltViewModel(), lazyListState: LazyListState = rememberLazyListState() ) { val searchPeopleScreenState = rememberSearchPeopleScreenState() @@ -141,10 +142,9 @@ private fun SearchResult( internalSearchResults( searchTitle = context.getString(R.string.label_contacts), searchQuery = searchQuery, - contactsAddedToGroup = contactsAddedToGroup, onChecked = onChecked, - isLoading = isLoading, - contactSearchResult = contactsSearchResult, + searchResult = contactsSearchResult, + contactsAddedToGroup = contactsAddedToGroup, showAllItems = !isSearchActive || searchPeopleScreenState.contactsAllResultsCollapsed, onShowAllButtonClicked = searchPeopleScreenState::toggleShowAllContactsResult, onOpenUserProfile = onOpenUserProfile, @@ -155,8 +155,7 @@ private fun SearchResult( externalSearchResults( searchTitle = context.getString(R.string.label_public_wire), searchQuery = searchQuery, - contactSearchResult = publicSearchResult, - isLoading = isLoading, + searchResult = publicSearchResult, showAllItems = searchPeopleScreenState.publicResultsCollapsed, onShowAllButtonClicked = searchPeopleScreenState::toggleShowAllPublicResult, onOpenUserProfile = onOpenUserProfile, @@ -169,68 +168,6 @@ private fun SearchResult( @Suppress("LongParameterList") private fun LazyListScope.internalSearchResults( - searchTitle: String, - searchQuery: String, - contactsAddedToGroup: ImmutableSet, - onChecked: (Boolean, Contact) -> Unit, - isLoading: Boolean, - contactSearchResult: ImmutableList, - showAllItems: Boolean, - onShowAllButtonClicked: () -> Unit, - onOpenUserProfile: (Contact) -> Unit -) { - when { - isLoading -> { - inProgressItem() - } - - else -> { - internalSuccessItem( - searchTitle = searchTitle, - showAllItems = showAllItems, - contactsAddedToGroup = contactsAddedToGroup, - onChecked = onChecked, - searchResult = contactSearchResult, - searchQuery = searchQuery, - onShowAllButtonClicked = onShowAllButtonClicked, - onOpenUserProfile = onOpenUserProfile - ) - } - } -} - -@Suppress("LongParameterList") -private fun LazyListScope.externalSearchResults( - searchTitle: String, - searchQuery: String, - contactSearchResult: ImmutableList, - isLoading: Boolean, - showAllItems: Boolean, - onShowAllButtonClicked: () -> Unit, - onOpenUserProfile: (Contact) -> Unit, - onAddContactClicked: (UserId) -> Unit -) { - when { - isLoading -> { - inProgressItem() - } - - else -> { - externalSuccessItem( - searchTitle = searchTitle, - showAllItems = showAllItems, - searchResult = contactSearchResult, - searchQuery = searchQuery, - onShowAllButtonClicked = onShowAllButtonClicked, - onOpenUserProfile = onOpenUserProfile, - onAddContactClicked = onAddContactClicked - ) - } - } -} - -@Suppress("LongParameterList") -private fun LazyListScope.internalSuccessItem( searchTitle: String, showAllItems: Boolean, contactsAddedToGroup: ImmutableSet, @@ -283,7 +220,7 @@ private fun LazyListScope.internalSuccessItem( } @Suppress("LongParameterList") -private fun LazyListScope.externalSuccessItem( +private fun LazyListScope.externalSearchResults( searchTitle: String, showAllItems: Boolean, searchResult: List, @@ -332,30 +269,6 @@ private fun LazyListScope.externalSuccessItem( } } -fun LazyListScope.inProgressItem() { - item { - Box( - Modifier - .fillMaxWidth() - .height(224.dp) - ) { - WireCircularProgressIndicator( - progressColor = Color.Black, - modifier = Modifier.align( - Alignment.Center - ) - ) - } - } -} - -fun LazyListScope.failureItem(@StringRes failureMessage: Int) { - item { - SearchFailureBox(failureMessage) - } -} - -@OptIn(ExperimentalAnimationApi::class) @Composable private fun ShowButton( isShownAll: Boolean, @@ -382,3 +295,47 @@ fun PreviewShowButton() { ShowButton(isShownAll = false, onShowButtonClicked = {}) } } + +@PreviewMultipleThemes +@Composable +fun PreviewSearchAllPeopleScreen_Loading() = WireTheme { + SearchAllPeopleScreen("Search query", persistentListOf(), persistentListOf(), persistentSetOf(), true, false, { _, _ -> }, {}) +} + +@PreviewMultipleThemes +@Composable +fun PreviewSearchAllPeopleScreen_InitialResults() = WireTheme { + val contacts = previewContactsList(count = 10, startIndex = 0, isContact = true).toPersistentList() + SearchAllPeopleScreen("", contacts, persistentListOf(), persistentSetOf(), false, false, { _, _ -> }, {}) +} + +@PreviewMultipleThemes +@Composable +fun PreviewSearchAllPeopleScreen_EmptyInitialResults() = WireTheme { + SearchAllPeopleScreen("", persistentListOf(), persistentListOf(), persistentSetOf(), false, false, { _, _ -> }, {}) +} + +@PreviewMultipleThemes +@Composable +fun PreviewSearchAllPeopleScreen_SearchResults() = WireTheme { + val contacts = previewContactsList(count = 10, startIndex = 0, isContact = true).toPersistentList() + val public = previewContactsList(count = 10, startIndex = 10, isContact = false).toPersistentList() + SearchAllPeopleScreen("Con", contacts, public, persistentSetOf(), false, true, { _, _ -> }, {}) +} + +@PreviewMultipleThemes +@Composable +fun PreviewSearchAllPeopleScreen_EmptySearchResults() = WireTheme { + SearchAllPeopleScreen("Con", persistentListOf(), persistentListOf(), persistentSetOf(), false, true, { _, _ -> }, {}) +} + +private fun previewContact(index: Int, isContact: Boolean) = Contact( + id = index.toString(), + domain = "wire.com", + name = "Contact nr $index", + connectionState = if (isContact) ConnectionState.ACCEPTED else ConnectionState.NOT_CONNECTED, + membership = Membership.Standard, +) + +private fun previewContactsList(count: Int, startIndex: Int = 0, isContact: Boolean): List = + buildList { repeat(count) { index -> add(previewContact(startIndex + index, isContact)) } } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/search/SearchAllServicesScreen.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/search/SearchAllServicesScreen.kt index 274983eab5c..0f5a46dd5ac 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/search/SearchAllServicesScreen.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/search/SearchAllServicesScreen.kt @@ -37,8 +37,14 @@ import com.wire.android.ui.common.UserProfileAvatar import com.wire.android.ui.common.dimensions import com.wire.android.ui.common.progress.CenteredCircularProgressBarIndicator import com.wire.android.ui.home.conversations.search.widget.SearchFailureBox +import com.wire.android.ui.home.conversationslist.model.Membership import com.wire.android.ui.home.newconversation.model.Contact +import com.wire.android.ui.theme.WireTheme +import com.wire.android.util.ui.PreviewMultipleThemes +import com.wire.kalium.logic.data.user.ConnectionState import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toPersistentList @Composable fun SearchAllServicesScreen( @@ -52,11 +58,10 @@ fun SearchAllServicesScreen( val lazyState = rememberLazyListState() SearchAllServicesContent( - searchQuery = searchQuery, + searchQuery = searchServicesViewModel.state.searchQuery, onServiceClicked = onServiceClicked, result = searchServicesViewModel.state.result, lazyListState = lazyState, - error = searchServicesViewModel.state.error, isLoading = searchServicesViewModel.state.isLoading ) } @@ -66,27 +71,23 @@ private fun SearchAllServicesContent( searchQuery: String, result: ImmutableList, isLoading: Boolean, - error: Boolean, onServiceClicked: (Contact) -> Unit, lazyListState: LazyListState = rememberLazyListState() ) { when { isLoading -> CenteredCircularProgressBarIndicator() - error -> SearchFailureBox(failureMessage = R.string.label_general_error) // TODO(user experience): what to do when user team has no services? - result.isEmpty() -> { - EmptySearchQueryScreen() - } + searchQuery.isBlank() && result.isEmpty() -> EmptySearchQueryScreen() - else -> { - SuccessServicesList( - searchQuery = searchQuery, - onServiceClicked = onServiceClicked, - services = result, - lazyListState = lazyListState - ) - } + searchQuery.isNotBlank() && result.isEmpty() -> SearchFailureBox(R.string.label_no_results_found) + + else -> SuccessServicesList( + searchQuery = searchQuery, + onServiceClicked = onServiceClicked, + services = result, + lazyListState = lazyListState + ) } } @@ -136,3 +137,43 @@ private fun SuccessServicesList( } } } + +@PreviewMultipleThemes +@Composable +fun PreviewSearchAllServicesScreen_Loading() = WireTheme { + SearchAllServicesContent("", persistentListOf(), true, {}) +} + +@PreviewMultipleThemes +@Composable +fun PreviewSearchAllServicesScreen_InitialResults() = WireTheme { + SearchAllServicesContent("", previewServiceList(count = 10).toPersistentList(), false, {}) +} + +@PreviewMultipleThemes +@Composable +fun PreviewSearchAllServicesScreen_EmptyInitialResults() = WireTheme { + SearchAllServicesContent("", persistentListOf(), false, {}) +} + +@PreviewMultipleThemes +@Composable +fun PreviewSearchAllServicesScreen_SearchResults() = WireTheme { + SearchAllServicesContent("Serv", previewServiceList(count = 10).toPersistentList(), false, {}) +} + +@PreviewMultipleThemes +@Composable +fun PreviewSearchAllServicesScreen_EmptySearchResults() = WireTheme { + SearchAllServicesContent("Serv", persistentListOf(), false, {}) +} + +private fun previewService(index: Int) = Contact( + id = index.toString(), + domain = "wire.com", + name = "Service nr $index", + connectionState = ConnectionState.NOT_CONNECTED, + membership = Membership.Service, +) + +private fun previewServiceList(count: Int): List = buildList { repeat(count) { index -> add(previewService(index)) } } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/search/SearchPeopleRouter.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/search/SearchPeopleRouter.kt index 42110004980..da91a870c5c 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/search/SearchPeopleRouter.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/search/SearchPeopleRouter.kt @@ -166,7 +166,6 @@ fun SearchUsersAndServicesScreen( onOpenUserProfile = onOpenUserProfile, onContactChecked = onContactChecked, isSearchActive = isSearchActive, - isLoading = false // TODO: update correctly ) } @@ -186,7 +185,6 @@ fun SearchUsersAndServicesScreen( onContactChecked = onContactChecked, onOpenUserProfile = onOpenUserProfile, isSearchActive = isSearchActive, - isLoading = false // TODO: update correctly ) } } @@ -230,7 +228,6 @@ enum class SearchPeopleScreenType { private fun SearchAllPeopleOrContactsScreen( searchQuery: String, contactsAddedToGroup: ImmutableSet, - isLoading: Boolean, isSearchActive: Boolean, onOpenUserProfile: (Contact) -> Unit, onContactChecked: (Boolean, Contact) -> Unit, @@ -243,8 +240,7 @@ private fun SearchAllPeopleOrContactsScreen( val lazyState = rememberLazyListState() SearchAllPeopleScreen( - searchQuery = searchQuery, - noneSearchSucceed = searchUserViewModel.state.noneSearchSucceeded, + searchQuery = searchUserViewModel.state.searchQuery, contactsSearchResult = searchUserViewModel.state.contactsResult, publicSearchResult = searchUserViewModel.state.publicResult, contactsAddedToGroup = contactsAddedToGroup, @@ -252,6 +248,6 @@ private fun SearchAllPeopleOrContactsScreen( onOpenUserProfile = onOpenUserProfile, lazyListState = lazyState, isSearchActive = isSearchActive, - isLoading = isLoading + isLoading = searchUserViewModel.state.isLoading, ) } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/search/SearchServicesViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/search/SearchServicesViewModel.kt index 1cab63cb0c7..0b0aa87535c 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/search/SearchServicesViewModel.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/search/SearchServicesViewModel.kt @@ -20,7 +20,6 @@ package com.wire.android.ui.home.conversations.search import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue -import androidx.compose.ui.text.input.TextFieldValue import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.wire.android.mapper.ContactMapper @@ -41,28 +40,23 @@ class SearchServicesViewModel @Inject constructor( private val contactMapper: ContactMapper, private val searchServicesByName: SearchServicesByNameUseCase, ) : ViewModel() { - var state: SearchServicesState by mutableStateOf(SearchServicesState()) + var state: SearchServicesState by mutableStateOf(SearchServicesState(isLoading = true)) private set fun search(query: String) { viewModelScope.launch { - if (query.isEmpty()) { - getAllServices().first().also { services -> - state = state.copy(result = services.map(contactMapper::fromService).toImmutableList()) - } + val result = if (query.isEmpty()) { + getAllServices().first() } else { - searchServicesByName(query).first().also { services -> - state = state.copy(result = services.map(contactMapper::fromService).toImmutableList()) - } + searchServicesByName(query).first() } + state = state.copy(isLoading = false, searchQuery = query, result = result.map(contactMapper::fromService).toImmutableList()) } } } data class SearchServicesState( val result: ImmutableList = persistentListOf(), - val searchQuery: TextFieldValue = TextFieldValue(), - val noneSearchSucceeded: Boolean = false, + val searchQuery: String = "", val isLoading: Boolean = false, - val error: Boolean = false ) diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/search/SearchUserViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/search/SearchUserViewModel.kt index d55b187d847..8df20210dbf 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/search/SearchUserViewModel.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/search/SearchUserViewModel.kt @@ -56,7 +56,7 @@ class SearchUserViewModel @Inject constructor( null } - var state: SearchUserState by mutableStateOf(SearchUserState()) + var state: SearchUserState by mutableStateOf(SearchUserState(isLoading = true)) private set fun search(query: String) = viewModelScope.launch { @@ -82,6 +82,8 @@ class SearchUserViewModel @Inject constructor( customDomain = domain ).also { userSearchEntities -> state = state.copy( + isLoading = false, + searchQuery = searchTerm, contactsResult = userSearchEntities.connected.map(contactMapper::fromSearchUserResult).toImmutableList(), publicResult = userSearchEntities.notConnected.map(contactMapper::fromSearchUserResult).toImmutableList() ) @@ -95,6 +97,8 @@ class SearchUserViewModel @Inject constructor( customDomain = domain ).also { userSearchEntities -> state = state.copy( + isLoading = false, + searchQuery = searchTerm, contactsResult = userSearchEntities.connected.map(contactMapper::fromSearchUserResult).toImmutableList(), publicResult = userSearchEntities.notConnected.map(contactMapper::fromSearchUserResult).toImmutableList() ) @@ -105,6 +109,6 @@ class SearchUserViewModel @Inject constructor( data class SearchUserState( val contactsResult: ImmutableList = persistentListOf(), val publicResult: ImmutableList = persistentListOf(), - val includeServices: Boolean = false, - val noneSearchSucceeded: Boolean = false + val searchQuery: String = "", + val isLoading: Boolean = false, ) diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/search/widget/SearchFailureWidget.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/search/widget/SearchFailureWidget.kt index 3765144738d..fadffceff3c 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/search/widget/SearchFailureWidget.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/search/widget/SearchFailureWidget.kt @@ -20,26 +20,21 @@ package com.wire.android.ui.home.conversations.search.widget import androidx.annotation.StringRes import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp +import com.wire.android.ui.theme.WireTheme import com.wire.android.ui.theme.wireColorScheme import com.wire.android.ui.theme.wireTypography +import com.wire.android.util.ui.PreviewMultipleThemes @Composable fun SearchFailureBox(@StringRes failureMessage: Int) { - Box( - Modifier - .fillMaxWidth() - .height(224.dp) - ) { + Box(Modifier.fillMaxSize()) { Text( stringResource(id = failureMessage), modifier = Modifier.align(Alignment.Center), @@ -48,8 +43,8 @@ fun SearchFailureBox(@StringRes failureMessage: Int) { } } -@Preview +@PreviewMultipleThemes @Composable -fun SearchFailureBoxPreview() { +fun SearchFailureBoxPreview() = WireTheme { SearchFailureBox(failureMessage = com.wire.android.R.string.label_no_results_found) } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/newconversation/SendConnectionRequestViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/home/newconversation/SendConnectionRequestViewModel.kt index 1e1583cc447..64817283d6c 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/newconversation/SendConnectionRequestViewModel.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/newconversation/SendConnectionRequestViewModel.kt @@ -29,12 +29,20 @@ import kotlinx.coroutines.Deferred import kotlinx.coroutines.async import javax.inject.Inject +interface SendConnectionRequestViewModel { + fun addContact(userId: UserId): Deferred +} + +object SendConnectionRequestViewModelPreview : SendConnectionRequestViewModel { + override fun addContact(userId: UserId): Deferred = TODO() +} + @HiltViewModel -class SendConnectionRequestViewModel @Inject constructor( +class SendConnectionRequestViewModelImpl @Inject constructor( private val sendConnectionRequest: SendConnectionRequestUseCase, -) : ViewModel() { +) : ViewModel(), SendConnectionRequestViewModel { - fun addContact(userId: UserId): Deferred = + override fun addContact(userId: UserId): Deferred = viewModelScope.async { when (sendConnectionRequest(userId)) { is SendConnectionRequestResult.Success -> {