Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import com.wire.kalium.logic.feature.app.GetAppByIdUseCase
import com.wire.kalium.logic.feature.app.ObserveAllAppsUseCase
import com.wire.kalium.logic.feature.app.ObserveIsAppMemberUseCase
import com.wire.kalium.logic.feature.app.SearchAppsByNameUseCase
import com.wire.kalium.logic.feature.app.SyncAppsUseCase
import dev.zacsweers.metro.BindingContainer
import dev.zacsweers.metro.Provides

Expand Down Expand Up @@ -53,4 +54,8 @@ class AppsModule {
@Provides
fun provideObserveAllAppsUseCase(appScope: AppScope): ObserveAllAppsUseCase =
appScope.observeAllApps

@Provides
fun provideSyncAppsUseCase(appScope: AppScope): SyncAppsUseCase =
appScope.syncApps
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.stringResource
import com.wire.android.model.Contact
import com.wire.android.model.ItemActionType
import com.wire.android.search.apps.EmptySearchDisabledByConversationContent
import com.wire.android.search.apps.SearchAppsScreen
import com.wire.android.search.users.SearchAllPeopleScreen
import com.wire.android.search.users.SearchUserViewModel
Expand Down Expand Up @@ -229,13 +230,16 @@ fun SearchUsersAndAppsScreen(
}

SearchPeopleTabItem.SERVICES -> {
SearchAppsScreen(
protocolInfo = conversationProtocol,
searchQuery = searchBarState.searchQueryTextState.text.toString(),
onServiceClicked = onAppClicked,
lazyListState = lazyListStates[pageIndex],
isConversationAppsEnabled = isConversationAppsEnabled,
)
if (isConversationAppsEnabled) {
SearchAppsScreen(
protocolInfo = conversationProtocol,
searchQuery = searchBarState.searchQueryTextState.text.toString(),
onServiceClicked = onAppClicked,
lazyListState = lazyListStates[pageIndex],
)
} else {
EmptySearchDisabledByConversationContent()
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,4 @@ enum class AppsContentState {
EMPTY_INITIAL,
EMPTY_SEARCH,
SHOW_RESULTS,
APPS_NOT_ENABLED_FOR_CONVERSATION
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,12 @@
*/
package com.wire.android.search.apps

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
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.text.style.TextAlign
Expand All @@ -35,13 +32,10 @@ import com.wire.android.ui.theme.wireTypography

@Composable
fun EmptySearchDisabledByConversationContent(modifier: Modifier = Modifier) {
Column(
Box(
modifier = modifier
.fillMaxWidth()
.wrapContentHeight()
.fillMaxSize()
.padding(dimensions().spacing16x),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
Text(
text = stringResource(R.string.search_results_apps_empty_description_disabled_for_conversation),
Comment on lines +35 to 41

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the new Box doesn't specify any alignment, the text will move to the top-start. we could use contentAlignment = Alignment.Center here

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ fun SearchAppsScreen(
protocolInfo: Conversation.ProtocolInfo?,
searchQuery: String,
onServiceClicked: (Contact) -> Unit,
isConversationAppsEnabled: Boolean,
searchAppsViewModel: SearchAppsViewModel = searchAppsViewModel(protocolInfo),
lazyListState: LazyListState = rememberLazyListState()
) {
Expand All @@ -83,7 +82,6 @@ fun SearchAppsScreen(
appsAllowedResult = state.isTeamAllowedToUseApps,
isSelfATeamAdmin = state.isSelfATeamAdmin,
lazyListState = lazyListState,
isConversationAppsEnabled = isConversationAppsEnabled
)
}
}
Expand All @@ -96,11 +94,9 @@ private fun SearchAllAppsContent(
onServiceClicked: (Contact) -> Unit,
appsAllowedResult: AppsAllowedResult,
isSelfATeamAdmin: Boolean,
isConversationAppsEnabled: Boolean,
lazyListState: LazyListState = rememberLazyListState()
) {
val appsContentState by rememberAppsContentState(
isConversationAppsEnabled = isConversationAppsEnabled,
isLoading = isLoading,
appsAllowedResult = appsAllowedResult,
searchQuery = searchQuery,
Expand All @@ -125,10 +121,6 @@ private fun SearchAllAppsContent(
CenteredCircularProgressBarIndicator()
}

AppsContentState.APPS_NOT_ENABLED_FOR_CONVERSATION -> {
EmptySearchDisabledByConversationContent()
}

AppsContentState.TEAM_NOT_ALLOWED -> {
UpgradeToGetAppsBanner()
}
Expand Down Expand Up @@ -156,16 +148,14 @@ private fun SearchAllAppsContent(

@Composable
private fun rememberAppsContentState(
isConversationAppsEnabled: Boolean,
isLoading: Boolean,
appsAllowedResult: AppsAllowedResult,
searchQuery: String,
result: ImmutableList<Contact>
): State<AppsContentState> = remember(isConversationAppsEnabled, isLoading, appsAllowedResult, searchQuery, result) {
): State<AppsContentState> = remember(isLoading, appsAllowedResult, searchQuery, result) {
derivedStateOf {
if (isLoading) return@derivedStateOf AppsContentState.LOADING
if (appsAllowedResult is AppsAllowedResult.Disabled) return@derivedStateOf AppsContentState.TEAM_NOT_ALLOWED
if (!isConversationAppsEnabled) return@derivedStateOf AppsContentState.APPS_NOT_ENABLED_FOR_CONVERSATION

when {
searchQuery.isBlank() && result.isEmpty() -> AppsContentState.EMPTY_SEARCH
Expand Down Expand Up @@ -239,7 +229,6 @@ fun PreviewSearchAllServicesScreen_TeamNotEnabledForApps() = WireTheme {
onServiceClicked = {},
appsAllowedResult = AppsAllowedResult.Disabled,
isSelfATeamAdmin = true,
isConversationAppsEnabled = true
)
}

Expand All @@ -253,7 +242,6 @@ fun PreviewSearchAllServicesScreen_InitialResults() = WireTheme {
onServiceClicked = {},
appsAllowedResult = AppsAllowedResult.Enabled(protocol = AppsAllowedProtocol.MLS),
isSelfATeamAdmin = true,
isConversationAppsEnabled = true
)
}

Expand All @@ -267,7 +255,6 @@ fun PreviewSearchAllServicesScreen_EmptyInitialResults_TeamAdmin() = WireTheme {
onServiceClicked = {},
appsAllowedResult = AppsAllowedResult.Enabled(protocol = AppsAllowedProtocol.MLS),
isSelfATeamAdmin = true,
isConversationAppsEnabled = true
)
}

Expand All @@ -281,7 +268,6 @@ fun PreviewSearchAllServicesScreen_EmptyInitialResults_NonTeamAdmin() = WireThem
onServiceClicked = {},
appsAllowedResult = AppsAllowedResult.Enabled(protocol = AppsAllowedProtocol.MLS),
isSelfATeamAdmin = false,
isConversationAppsEnabled = true
)
}

Expand All @@ -295,7 +281,6 @@ fun PreviewSearchAllServicesScreen_SearchResults() = WireTheme {
onServiceClicked = {},
appsAllowedResult = AppsAllowedResult.Enabled(protocol = AppsAllowedProtocol.MLS),
isSelfATeamAdmin = true,
isConversationAppsEnabled = true
)
}

Expand All @@ -309,7 +294,6 @@ fun PreviewSearchAllServicesScreen_EmptySearchResults() = WireTheme {
onServiceClicked = {},
appsAllowedResult = AppsAllowedResult.Enabled(protocol = AppsAllowedProtocol.MLS),
isSelfATeamAdmin = true,
isConversationAppsEnabled = true
)
}

Expand All @@ -323,7 +307,6 @@ fun PreviewSearchAllServicesScreen_EmptySearchResultsDisabledInConversation() =
onServiceClicked = {},
appsAllowedResult = AppsAllowedResult.Enabled(protocol = AppsAllowedProtocol.MLS),
isSelfATeamAdmin = true,
isConversationAppsEnabled = false
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,19 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.wire.android.di.metro.WireAssistedViewModelBinding
import com.wire.android.mapper.ContactMapper
import com.wire.android.model.Contact
import com.wire.android.search.SearchManualViewModelFactoryGroup
import com.wire.android.ui.common.DEFAULT_SEARCH_QUERY_DEBOUNCE
import com.wire.android.util.AppsUtil
import com.wire.android.util.EMPTY
import com.wire.kalium.common.logger.kaliumLogger
import com.wire.kalium.logic.data.conversation.Conversation
import com.wire.kalium.logic.data.user.type.isTeamAdmin
import com.wire.kalium.logic.feature.app.ObserveAllAppsUseCase
import com.wire.kalium.logic.feature.app.SearchAppsByNameUseCase
import com.wire.kalium.logic.feature.app.SyncAppsUseCase
import com.wire.kalium.logic.feature.featureConfig.AppsAllowedResult
import com.wire.kalium.logic.feature.featureConfig.ObserveIsAppsAllowedForUsageUseCase
import com.wire.kalium.logic.feature.service.ObserveAllServicesUseCase
Expand All @@ -43,21 +47,23 @@ import dev.zacsweers.metro.AssistedInject
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch
import com.wire.android.di.metro.WireAssistedViewModelBinding
import com.wire.android.search.SearchManualViewModelFactoryGroup

@Suppress("LongParameterList")
@WireAssistedViewModelBinding(SearchManualViewModelFactoryGroup::class)
class SearchAppsViewModel @AssistedInject constructor(
@Assisted private val protocolInfo: Conversation.ProtocolInfo?,
private val getAllServices: ObserveAllServicesUseCase,
private val syncServices: SyncServicesUseCase,
private val syncApps: SyncAppsUseCase,
private val getAllApps: ObserveAllAppsUseCase,
private val contactMapper: ContactMapper,
private val searchServicesByName: SearchServicesByNameUseCase,
Expand All @@ -71,6 +77,7 @@ class SearchAppsViewModel @AssistedInject constructor(
}
private val searchQueryTextFlow = MutableStateFlow(String.EMPTY)
private var servicesSynced = false
private var appsSync: Deferred<SyncAppsUseCase.Result>? = null
var state: SearchServicesState by mutableStateOf(SearchServicesState(isLoading = true))
private set

Expand Down Expand Up @@ -111,29 +118,37 @@ class SearchAppsViewModel @AssistedInject constructor(
}
}

private fun search(query: String, appsAllowedResult: AppsAllowedResult.Enabled) {
viewModelScope.launch {
val showNewApps = AppsUtil.isAppsAllowed(
appsAllowedResult = appsAllowedResult,
conversationProtocol = protocolInfo
)
private suspend fun search(query: String, appsAllowedResult: AppsAllowedResult.Enabled) {
val showNewApps = AppsUtil.isAppsAllowed(
appsAllowedResult = appsAllowedResult,
conversationProtocol = protocolInfo
)

val result = if (showNewApps) {
if (query.isEmpty()) getAllApps() else searchAppsByName(query)
} else {
if (!servicesSynced) {
servicesSynced = true
launch { syncServices() }
val result = if (showNewApps) {
if (query.isEmpty() && appsSync == null) {
appsSync = viewModelScope.async {
syncApps().also { syncResult ->
if (syncResult is SyncAppsUseCase.Result.Failure) {
kaliumLogger.w("Failed to refresh apps; using the local cache: ${syncResult.error}")
}
}
}
if (query.isEmpty()) getAllServices() else searchServicesByName(query)
}

state = state.copy(
isLoading = false,
searchQuery = query,
result = result.first().map(contactMapper::fromService).toImmutableList()
)
appsSync?.await()
if (query.isEmpty()) getAllApps() else searchAppsByName(query)
} else {
if (!servicesSynced) {
servicesSynced = true
viewModelScope.launch { syncServices() }
}
if (query.isEmpty()) getAllServices() else searchServicesByName(query)
}

state = state.copy(
isLoading = false,
searchQuery = query,
result = result.first().map(contactMapper::fromService).toImmutableList()
)
}
}

Expand Down
Loading
Loading