diff --git a/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/transferdetails/TransferDetailsScreen.kt b/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/transferdetails/TransferDetailsScreen.kt index f9788ed62a..31df3cf827 100644 --- a/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/transferdetails/TransferDetailsScreen.kt +++ b/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/transferdetails/TransferDetailsScreen.kt @@ -48,6 +48,8 @@ import com.infomaniak.swisstransfer.ui.images.icons.QrCode import com.infomaniak.swisstransfer.ui.images.icons.Share import com.infomaniak.swisstransfer.ui.previewparameter.TransferUiListPreviewParameter import com.infomaniak.swisstransfer.ui.screen.main.components.SmallWindowTopAppBarScaffold +import com.infomaniak.swisstransfer.ui.screen.main.transferdetails.TransferDetailsViewModel.TransferDetailsUiState.Loading +import com.infomaniak.swisstransfer.ui.screen.main.transferdetails.TransferDetailsViewModel.TransferDetailsUiState.Success import com.infomaniak.swisstransfer.ui.screen.main.transferdetails.components.PasswordBottomSheet import com.infomaniak.swisstransfer.ui.screen.main.transferdetails.components.QrCodeBottomSheet import com.infomaniak.swisstransfer.ui.screen.main.transferdetails.components.TransferInfo @@ -63,8 +65,8 @@ fun TransferDetailsScreen( navigateBack: (() -> Unit)?, transferDetailsViewModel: TransferDetailsViewModel = hiltViewModel(), ) { - val transfer by transferDetailsViewModel.transfer.collectAsStateWithLifecycle() - val isLoading by remember { derivedStateOf { transfer == null } } + val uiState by transferDetailsViewModel.transferDetailsUiState.collectAsStateWithLifecycle() + val isLoading by remember { derivedStateOf { uiState is Loading } } LaunchedEffect(transferUuid) { transferDetailsViewModel.loadTransfer(transferUuid) @@ -75,7 +77,7 @@ fun TransferDetailsScreen( transferUrl = transferDetailsViewModel.getTransferUrl(transferUuid), direction = direction, navigateBack = navigateBack, - getTransfer = { transfer }, + getTransfer = { (uiState as Success).transfer }, getCheckedFiles = { transferDetailsViewModel.checkedFiles }, clearCheckedFiles = { transferDetailsViewModel.checkedFiles.clear() }, setFileCheckStatus = { fileUid, isChecked -> @@ -90,14 +92,13 @@ private fun TransferDetailsScreen( transferUrl: String, direction: TransferDirection, navigateBack: (() -> Unit)?, - getTransfer: () -> TransferUi?, + getTransfer: () -> TransferUi, getCheckedFiles: () -> SnapshotStateMap, clearCheckedFiles: () -> Unit, setFileCheckStatus: (String, Boolean) -> Unit, ) { val context = LocalContext.current - val transfer = getTransfer() ?: return val transferRecipients: List = emptyList() // TODO: Use real data val transferPassword = "toto42" // TODO: Use real data @@ -108,7 +109,7 @@ private fun TransferDetailsScreen( SmallWindowTopAppBarScaffold( smallWindowTopAppBar = { SwissTransferTopAppBar( - title = transfer.createdDateTimestamp.toDateFromSeconds().format(FORMAT_DATE_FULL), + title = getTransfer().createdDateTimestamp.toDateFromSeconds().format(FORMAT_DATE_FULL), navigationMenu = TopAppBarButton.backButton(navigateBack ?: {}), TopAppBarButton.downloadButton { /* TODO */ }, ) @@ -117,7 +118,7 @@ private fun TransferDetailsScreen( ) { Column { - FilesList(transfer, transferRecipients, isMultiselectOn, getCheckedFiles, setFileCheckStatus) + FilesList(getTransfer, transferRecipients, isMultiselectOn, getCheckedFiles, setFileCheckStatus) BottomBar( direction = direction, @@ -156,7 +157,7 @@ private fun TransferDetailsScreen( @Composable private fun ColumnScope.FilesList( - transfer: TransferUi, + getTransfer: () -> TransferUi, transferRecipients: List, isMultiselectOn: Boolean, getCheckedFiles: () -> SnapshotStateMap, @@ -164,13 +165,13 @@ private fun ColumnScope.FilesList( ) { val shouldDisplayRecipients = transferRecipients.isNotEmpty() - val shouldDisplayMessage = transfer.message != null + val shouldDisplayMessage = getTransfer().message != null FileItemList( modifier = Modifier .weight(1.0f) .padding(horizontal = Margin.Medium), - files = transfer.files, + files = getTransfer().files, isRemoveButtonVisible = false, isCheckboxVisible = { isMultiselectOn }, isUidChecked = { fileUid -> getCheckedFiles()[fileUid] ?: false }, @@ -178,11 +179,11 @@ private fun ColumnScope.FilesList( header = { Column { Spacer(modifier = Modifier.height(Margin.Large)) - TransferInfo(transfer) + TransferInfo(getTransfer) Spacer(modifier = Modifier.height(Margin.Large)) if (shouldDisplayRecipients) TransferRecipients(transferRecipients) if (shouldDisplayRecipients && shouldDisplayMessage) Spacer(modifier = Modifier.height(Margin.Mini)) - if (shouldDisplayMessage) TransferMessage(transfer.message!!) + if (shouldDisplayMessage) TransferMessage(getTransfer().message!!) if (shouldDisplayRecipients || shouldDisplayMessage) Spacer(modifier = Modifier.height(Margin.Large)) TransferContentHeader() } @@ -298,7 +299,7 @@ private fun Preview(@PreviewParameter(TransferUiListPreviewParameter::class) tra transferUrl = "", direction = TransferDirection.SENT, navigateBack = null, - getTransfer = { transfers.first() }, + getTransfer = { Success(transfers.first()).transfer }, getCheckedFiles = { mutableStateMapOf() }, clearCheckedFiles = {}, setFileCheckStatus = { _, _ -> }, diff --git a/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/transferdetails/TransferDetailsViewModel.kt b/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/transferdetails/TransferDetailsViewModel.kt index b3b2e994cc..7197df8f5a 100644 --- a/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/transferdetails/TransferDetailsViewModel.kt +++ b/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/transferdetails/TransferDetailsViewModel.kt @@ -17,18 +17,19 @@ */ package com.infomaniak.swisstransfer.ui.screen.main.transferdetails +import androidx.compose.runtime.Immutable import androidx.compose.runtime.mutableStateMapOf import androidx.compose.runtime.snapshots.SnapshotStateMap import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.infomaniak.multiplatform_swisstransfer.SharedApiUrlCreator +import com.infomaniak.multiplatform_swisstransfer.common.interfaces.ui.TransferUi import com.infomaniak.multiplatform_swisstransfer.managers.TransferManager +import com.infomaniak.swisstransfer.ui.screen.main.transferdetails.TransferDetailsViewModel.TransferDetailsUiState.Loading +import com.infomaniak.swisstransfer.ui.screen.main.transferdetails.TransferDetailsViewModel.TransferDetailsUiState.Success import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import javax.inject.Inject @@ -41,10 +42,21 @@ class TransferDetailsViewModel @Inject constructor( private val _transferUuidFlow = MutableSharedFlow(1) @OptIn(ExperimentalCoroutinesApi::class) - val transfer = _transferUuidFlow - .flatMapLatest { transferManager.getTransferFlow(it) } + private val _transfer = _transferUuidFlow + .flatMapLatest { + transferManager.getTransferFlow(it) + } .stateIn(viewModelScope, SharingStarted.Eagerly, null) + val transferDetailsUiState = _transfer + .flatMapLatest { transfer -> + when (transfer) { + null -> flow { emit(Loading(null)) } + else -> flow { emit(Success(transfer)) } + } + } + .stateIn(viewModelScope, SharingStarted.Eagerly, Loading(null)) + val checkedFiles: SnapshotStateMap = mutableStateMapOf() fun loadTransfer(transferUuid: String) { @@ -54,4 +66,16 @@ class TransferDetailsViewModel @Inject constructor( } fun getTransferUrl(transferUuid: String): String = sharedApiUrlCreator.shareTransferUrl(transferUuid) + + sealed class TransferDetailsUiState(open val transfer: TransferUi?) { + + @Immutable + data class Success(override val transfer: TransferUi) : TransferDetailsUiState(transfer) + + @Immutable + data class Loading(override val transfer: TransferUi?) : TransferDetailsUiState(null) + + @Immutable + data class Error(override val transfer: TransferUi?) : TransferDetailsUiState(null) //TODO Handle error case + } } diff --git a/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/transferdetails/components/TransferInfo.kt b/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/transferdetails/components/TransferInfo.kt index 838002d61e..96f2b0275a 100644 --- a/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/transferdetails/components/TransferInfo.kt +++ b/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/transferdetails/components/TransferInfo.kt @@ -47,10 +47,10 @@ import com.infomaniak.swisstransfer.ui.theme.SwissTransferTheme import com.infomaniak.swisstransfer.ui.utils.PreviewLightAndDark @Composable -fun TransferInfo(transfer: TransferUi) { +fun TransferInfo(transfer: () -> TransferUi) { - val filesCount = transfer.files.count() - val downloadedCount by remember { derivedStateOf { transfer.downloadLimit - transfer.downloadLeft } } + val filesCount by remember { derivedStateOf { transfer().files.count() } } + val downloadedCount by remember { derivedStateOf { transfer().downloadLimit - transfer().downloadLeft } } Row(verticalAlignment = Alignment.CenterVertically) { Icon( @@ -61,7 +61,7 @@ fun TransferInfo(transfer: TransferUi) { Spacer(Modifier.width(Margin.Mini)) TextDotText( firstText = { pluralStringResource(R.plurals.filesCount, filesCount, filesCount) }, - secondText = { Formatter.formatShortFileSize(LocalContext.current, transfer.sizeUploaded) }, + secondText = { Formatter.formatShortFileSize(LocalContext.current, transfer().sizeUploaded) }, color = SwissTransferTheme.colors.primaryTextColor, ) } @@ -70,14 +70,14 @@ fun TransferInfo(transfer: TransferUi) { IconText( icon = AppIcons.Clock, - text = stringResource(R.string.expiresIn, transfer.expiresInDays), + text = stringResource(R.string.expiresIn, transfer().expiresInDays), ) HorizontalDivider(modifier = Modifier.padding(vertical = Margin.Medium)) IconText( icon = AppIcons.ArrowDownFile, - text = stringResource(R.string.downloadedTransferLabel, downloadedCount, transfer.downloadLimit), + text = stringResource(R.string.downloadedTransferLabel, downloadedCount, transfer().downloadLimit), ) } @@ -105,7 +105,7 @@ private fun Preview(@PreviewParameter(TransferUiListPreviewParameter::class) tra SwissTransferTheme { Surface { Column { - TransferInfo(transfers.first()) + TransferInfo { transfers.first() } } } }