Skip to content
Merged
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
33 changes: 33 additions & 0 deletions core/ui-common/stability/ui-common-debug.stability
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,13 @@ public fun com.wire.android.ui.common.ProtocolLabel(protocolName: kotlin.String,
- protocolName: STABLE (String is immutable)
- modifier: STABLE (marked @Stable or @Immutable)

@Composable
private fun com.wire.android.ui.common.RadioButtonInnerDot(color: androidx.compose.ui.graphics.Color): kotlin.Unit
skippable: true
restartable: true
params:
- color: STABLE (marked @Stable or @Immutable)

@Composable
public fun com.wire.android.ui.common.SearchBarInput(placeholderText: kotlin.String, leadingIcon: @[Composable] androidx.compose.runtime.internal.ComposableFunction0<kotlin.Unit>, textState: androidx.compose.foundation.text.input.TextFieldState, modifier: androidx.compose.ui.Modifier, inputModifier: androidx.compose.ui.Modifier, clearButtonModifier: androidx.compose.ui.Modifier, placeholderTextStyle: androidx.compose.ui.text.TextStyle, placeholderAlignment: androidx.compose.ui.Alignment.Horizontal, interactionSource: androidx.compose.foundation.interaction.MutableInteractionSource, textStyle: androidx.compose.ui.text.TextStyle, textFieldState: com.wire.android.ui.common.textfield.WireTextFieldState, isLoading: kotlin.Boolean, inputEnabled: kotlin.Boolean, semanticDescription: kotlin.String?, onTap: kotlin.Function0<kotlin.Unit>?): kotlin.Unit
skippable: true
Expand Down Expand Up @@ -455,6 +462,32 @@ public fun com.wire.android.ui.common.WirePromotionDialog(title: kotlin.String,
- shape: STABLE (marked @Stable or @Immutable)
- contentPadding: STABLE (marked @Stable or @Immutable)

@Composable
public fun com.wire.android.ui.common.WireRadioButton(checked: kotlin.Boolean, modifier: androidx.compose.ui.Modifier, onButtonChecked: kotlin.Function0<kotlin.Unit>?, enabled: kotlin.Boolean): kotlin.Unit
skippable: true
restartable: true
params:
- checked: STABLE (primitive type)
- modifier: STABLE (marked @Stable or @Immutable)
- onButtonChecked: STABLE (function type)
- enabled: STABLE (primitive type)

@Composable
private fun com.wire.android.ui.common.WireRadioButtonIndicator(checked: kotlin.Boolean, enabled: kotlin.Boolean, focused: kotlin.Boolean, selectedColor: androidx.compose.ui.graphics.Color, selectedContentColor: androidx.compose.ui.graphics.Color, unselectedColor: androidx.compose.ui.graphics.Color, unselectedBackgroundColor: androidx.compose.ui.graphics.Color, disabledSelectedColor: androidx.compose.ui.graphics.Color, disabledUnselectedColor: androidx.compose.ui.graphics.Color, modifier: androidx.compose.ui.Modifier): kotlin.Unit
skippable: true
restartable: true
params:
- checked: STABLE (primitive type)
- enabled: STABLE (primitive type)
- focused: STABLE (primitive type)
- selectedColor: STABLE (marked @Stable or @Immutable)
- selectedContentColor: STABLE (marked @Stable or @Immutable)
- unselectedColor: STABLE (marked @Stable or @Immutable)
- unselectedBackgroundColor: STABLE (marked @Stable or @Immutable)
- disabledSelectedColor: STABLE (marked @Stable or @Immutable)
- disabledUnselectedColor: STABLE (marked @Stable or @Immutable)
- modifier: STABLE (marked @Stable or @Immutable)

@Composable
public fun com.wire.android.ui.common.WireTabRow(tabs: kotlin.collections.List<com.wire.android.ui.common.TabItem>, selectedTabIndex: kotlin.Int, onTabChange: kotlin.Function1<kotlin.Int, kotlin.Unit>, modifier: androidx.compose.ui.Modifier, tabModifiers: kotlin.collections.List<androidx.compose.ui.Modifier>, containerColor: androidx.compose.ui.graphics.Color, divider: @[Composable] androidx.compose.runtime.internal.ComposableFunction0<kotlin.Unit>, upperCaseTitles: kotlin.Boolean): kotlin.Unit
skippable: false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ package com.wire.android.feature.cells.ui
import androidx.compose.animation.AnimatedContent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.text.input.rememberTextFieldState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
Expand All @@ -38,6 +40,7 @@ import com.wire.android.feature.cells.ui.audioplayer.AudioPlayerNavArgs
import com.wire.android.feature.cells.ui.common.OfflineBanner
import com.wire.android.feature.cells.ui.imageviewer.CellImageViewerNavArgs
import com.wire.android.feature.cells.ui.search.DriveSearchScreenType
import com.wire.android.feature.cells.ui.search.sort.SortRowWithMenu
import com.wire.android.feature.cells.ui.videoplayer.VideoViewerNavArgs
import com.wire.android.navigation.NavigationCommand
import com.wire.android.navigation.WireNavigator
Expand All @@ -59,28 +62,42 @@ fun AllFilesScreen(
val isOnlineState by viewModel.isOnline.collectAsState()
// When offline files are disabled, never enter offline mode so all offline UI stays hidden.
val isOnline = isOnlineState || !viewModel.offlineFilesEnabled
val sortingCriteria by viewModel.sortingCriteria.collectAsState()

val lazyListState = rememberLazyListState()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

On "Drive" screen, the search top bar doesn't disappear when user scrolls, like on conversation list or archive. To fix that inconsistency, we need to use the search bar from HomeScreen instead of creating yet another one in the content of cells screen, and use LazyListState from homeStateHolder.lazyListStateFor(HomeDestination.Cells) instead.

LaunchedEffect(sortingCriteria) {
lazyListState.animateScrollToItem(0)
}

WireScaffold(
modifier = modifier,
topBar = {
Column {
AnimatedContent(isOnline) {
if (it) {
SearchTopBar(
modifier = Modifier,
isSearchActive = false,
searchBarHint = stringResource(R.string.search_label),
searchQueryTextState = rememberTextFieldState(),
onTap = {
navigator.navigate(
NavigationCommand(
SearchScreenDestination(
screenType = DriveSearchScreenType.DRIVE,
Column {
SearchTopBar(
modifier = Modifier,
isSearchActive = false,
searchBarHint = stringResource(R.string.search_label),
searchQueryTextState = rememberTextFieldState(),
onTap = {
navigator.navigate(
NavigationCommand(
SearchScreenDestination(
screenType = DriveSearchScreenType.DRIVE,
)
)
)
)
},
)
},
)
SortRowWithMenu(
sortingCriteria = sortingCriteria,
screenType = DriveSearchScreenType.DRIVE,
onSortByClicked = { viewModel.setSortBy(it) },
onOrderClicked = { viewModel.setSorting(it) },
)
}
} else {
OfflineBanner()
}
Expand All @@ -90,6 +107,7 @@ fun AllFilesScreen(
) { innerPadding ->
CellScreenContent(
modifier = Modifier.padding(innerPadding),
lazyListState = lazyListState,
actionsFlow = viewModel.actions,
pagingListItems = pagingListItems,
sendIntent = { viewModel.sendIntent(it) },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ import com.wire.android.feature.cells.ui.model.localFileAvailable
import com.wire.android.feature.cells.ui.model.toUiModel
import com.wire.android.feature.cells.ui.search.DriveSearchScreenType
import com.wire.android.feature.cells.ui.search.SearchNavArgs
import com.wire.android.feature.cells.ui.search.defaultCriteriaFor
import com.wire.android.feature.cells.ui.search.sort.SortBy
import com.wire.android.feature.cells.ui.search.sort.SortingCriteria
import com.wire.android.feature.cells.ui.search.sort.toKaliumCriteria
import com.wire.android.feature.cells.util.FileHelper
Expand Down Expand Up @@ -156,13 +158,14 @@ class CellViewModel(

// AllFiles context (no conversationId, not recycle bin) defaults to newest-first;
// ConversationFiles and RecycleBin default to folders-first.
private val _defaultSortingCriteria = MutableStateFlow(
if (navArgs.conversationId == null && !(navArgs.isRecycleBin ?: false)) {
SortingCriteria.ByDate.NewestFirst
} else {
SortingCriteria.FoldersFirst
}
)
val defaultSortingCriteria: SortingCriteria = if (navArgs.conversationId == null && !(navArgs.isRecycleBin ?: false)) {
SortingCriteria.ByDate.NewestFirst
} else {
SortingCriteria.FoldersFirst
}

private val _sortingCriteria = MutableStateFlow(defaultSortingCriteria)
val sortingCriteria: StateFlow<SortingCriteria> = _sortingCriteria.asStateFlow()

val isOnline: StateFlow<Boolean> = networkStateObserver.observeNetworkState()
.map { it is NetworkState.ConnectedWithInternet }
Expand Down Expand Up @@ -196,7 +199,7 @@ class CellViewModel(
}

refreshTrigger.flatMapLatest {
_defaultSortingCriteria.flatMapLatest { sortingCriteria ->
_sortingCriteria.flatMapLatest { sortingCriteria ->
combine(
getCellFilesPaged(
conversationId = navArgs.conversationId,
Expand Down Expand Up @@ -381,6 +384,16 @@ class CellViewModel(
cancelOpenDownload(uuid)
}

fun setSortBy(by: SortBy) {
_sortingCriteria.update { current ->
if (current.by == by) current else defaultCriteriaFor(by)
}
}

fun setSorting(criteria: SortingCriteria) {
_sortingCriteria.value = criteria
}

@Suppress("ReturnCount")
private fun openFileContentUrl(file: CellNodeUi.File) {
when (file.assetType) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
Expand Down Expand Up @@ -67,6 +68,10 @@ import com.wire.android.feature.cells.ui.dialog.CellsNewActionBottomSheet
import com.wire.android.feature.cells.ui.dialog.CellsOptionsBottomSheet
import com.wire.android.feature.cells.ui.imageviewer.CellImageViewerNavArgs
import com.wire.android.feature.cells.ui.model.CellNodeUi
import com.wire.android.feature.cells.ui.search.DriveSearchScreenType
import com.wire.android.feature.cells.ui.search.sort.SortBy
import com.wire.android.feature.cells.ui.search.sort.SortRowWithMenu
import com.wire.android.feature.cells.ui.search.sort.SortingCriteria
import com.wire.android.feature.cells.ui.videoplayer.VideoViewerNavArgs
import com.wire.android.navigation.BackStackMode
import com.wire.android.navigation.NavigationCommand
Expand Down Expand Up @@ -132,6 +137,9 @@ fun ConversationFilesScreen(
onRefresh = viewModel::onPullToRefresh,
retryEditNodeError = viewModel::editNode,
fileReadyFlow = viewModel.fileReadyFlow,
sortingCriteria = viewModel.sortingCriteria.collectAsState().value,
onSortByClicked = viewModel::setSortBy,
onSortOrderClicked = viewModel::setSorting,
)

LaunchedEffect(Unit) {
Expand Down Expand Up @@ -162,6 +170,9 @@ internal fun ConversationFilesScreenContent(
isOnline: Boolean = true,
breadcrumbs: Array<String>? = emptyArray(),
fileReadyFlow: Flow<CellNodeUi.File> = emptyFlow(),
sortingCriteria: SortingCriteria = SortingCriteria.FoldersFirst,
onSortByClicked: (SortBy) -> Unit = {},
onSortOrderClicked: (SortingCriteria) -> Unit = {},
) {
val sharedScope = LocalSharedTransitionScope.current

Expand All @@ -176,6 +187,11 @@ internal fun ConversationFilesScreenContent(
else -> true
}

val lazyListState = rememberLazyListState()
LaunchedEffect(sortingCriteria) {
lazyListState.animateScrollToItem(0)
}

CellsNewActionBottomSheet(
sheetState = newActionBottomSheetState,
onDismiss = {
Expand Down Expand Up @@ -264,6 +280,14 @@ internal fun ConversationFilesScreenContent(
}
},
)
if (!isRecycleBin) {
SortRowWithMenu(
sortingCriteria = sortingCriteria,
screenType = DriveSearchScreenType.SHARED_DRIVE,
onSortByClicked = onSortByClicked,
onOrderClicked = onSortOrderClicked,
)
}
} else {
OfflineBanner()
}
Expand Down Expand Up @@ -300,6 +324,7 @@ internal fun ConversationFilesScreenContent(
) { innerPadding ->
CellScreenContent(
modifier = Modifier.padding(innerPadding),
lazyListState = lazyListState,
actionsFlow = actions,
pagingListItems = pagingListItems,
sendIntent = sendIntent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,8 @@ fun ConversationFilesWithSlideInTransitionScreen(
onRefresh = viewModel::onPullToRefresh,
retryEditNodeError = viewModel::editNode,
fileReadyFlow = viewModel.fileReadyFlow,
sortingCriteria = viewModel.sortingCriteria.collectAsState().value,
onSortByClicked = viewModel::setSortBy,
onSortOrderClicked = viewModel::setSorting,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ enum class NodeBottomSheetAction(
val isHighlighted: Boolean = false
) {
OPEN(R.string.open_label, R.drawable.ic_open),
SHARE(R.string.share_label, R.drawable.ic_share),
SHARE(R.string.share_label, R.drawable.ic_file_share),
PUBLIC_LINK(R.string.public_link, R.drawable.ic_link),
ADD_REMOVE_TAGS(R.string.add_remove_tags_label, R.drawable.ic_tags),
MOVE(R.string.move_label, R.drawable.ic_folder),
Expand All @@ -38,7 +38,7 @@ enum class NodeBottomSheetAction(
CANCEL_LOADING(R.string.cancel_loading_label, com.wire.android.ui.common.R.drawable.ic_close, true),
CANCEL_DOWNLOAD(R.string.cancel_download_label, com.wire.android.ui.common.R.drawable.ic_close, true),
MAKE_AVAILABLE_OFFLINE(R.string.make_available_offline_label, R.drawable.ic_arrow_down_circle),
REMOVE_OFFLINE_ACCESS(R.string.remove_offline_access_label, R.drawable.ic_cross_in_circle, true),
REMOVE_OFFLINE_ACCESS(R.string.remove_offline_access_label, R.drawable.ic_cross_in_circle),
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ import com.wire.android.feature.cells.ui.model.CellNodeUi
import com.wire.android.feature.cells.ui.model.OpenLoadState
import com.wire.android.feature.cells.ui.model.toUiModel
import com.wire.android.feature.cells.ui.search.SearchNavArgs
import com.wire.android.feature.cells.ui.search.sort.SortBy
import com.wire.android.feature.cells.ui.search.sort.SortingCriteria
import com.wire.android.feature.cells.util.FileHelper
import com.wire.android.feature.cells.util.FileNameResolver
import com.wire.kalium.cells.domain.model.Node
Expand Down Expand Up @@ -413,6 +415,56 @@ class CellViewModelTest {
}
}

@Test
fun `GIVEN AllFiles context WHEN setSortBy called with Name THEN sortingCriteria changes to ByName AtoZ`() = runTest {
val (_, viewModel) = Arrangement()
.withLoadSuccess()
.arrange()

assertEquals(SortingCriteria.ByDate.NewestFirst, viewModel.sortingCriteria.value)

viewModel.setSortBy(SortBy.Name)

assertEquals(SortingCriteria.ByName.AtoZ, viewModel.sortingCriteria.value)
}

@Test
fun `GIVEN AllFiles context WHEN setSorting called THEN sortingCriteria updates`() = runTest {
val (_, viewModel) = Arrangement()
.withLoadSuccess()
.arrange()

viewModel.setSorting(SortingCriteria.ByName.ZtoA)

assertEquals(SortingCriteria.ByName.ZtoA, viewModel.sortingCriteria.value)
}

@Test
fun `GIVEN same sortBy WHEN setSortBy called with same criteria THEN sortingCriteria unchanged`() = runTest {
val (_, viewModel) = Arrangement()
.withLoadSuccess()
.arrange()

val initial = viewModel.sortingCriteria.value
viewModel.setSortBy(SortBy.Modified)

assertEquals(initial, viewModel.sortingCriteria.value)
}

@Test
fun `GIVEN conversation context WHEN setSortBy called with Size THEN sortingCriteria changes to BySize SmallestFirst`() = runTest {
val (_, viewModel) = Arrangement()
.withLoadSuccess()
.withConversationId("conversationId")
.arrange()

assertEquals(SortingCriteria.FoldersFirst, viewModel.sortingCriteria.value)

viewModel.setSortBy(SortBy.Size)

assertEquals(SortingCriteria.BySize.SmallestFirst, viewModel.sortingCriteria.value)
}

private class Arrangement(
private var conversationId: String? = null,
private var inAppImageViewerEnabled: Boolean = false,
Expand Down Expand Up @@ -550,6 +602,9 @@ class CellViewModelTest {

fun withConversationId(conversationId: String) = apply {
this.conversationId = conversationId
every { ConversationFilesScreenDestination.argsFrom(savedStateHandle) } returns CellFilesNavArgs(
conversationId = conversationId
)
every { savedStateHandle.get<String>(any()) } returns conversationId
every { savedStateHandle.get<String>("conversationId") } returns conversationId
}
Expand Down
Loading
Loading