diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/attachments/AttachmentsScreen.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/attachments/AttachmentsScreen.kt index ff61fc1b06..9c50541d8f 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/attachments/AttachmentsScreen.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/attachments/AttachmentsScreen.kt @@ -1,6 +1,5 @@ package com.x8bit.bitwarden.ui.vault.feature.attachments -import android.widget.Toast import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.TopAppBarDefaults @@ -10,7 +9,6 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.input.nestedscroll.nestedScroll -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource import androidx.hilt.navigation.compose.hiltViewModel @@ -27,6 +25,8 @@ import com.x8bit.bitwarden.ui.platform.components.content.BitwardenLoadingConten import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenBasicDialog import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenLoadingDialog import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold +import com.x8bit.bitwarden.ui.platform.components.snackbar.BitwardenSnackbarHost +import com.x8bit.bitwarden.ui.platform.components.snackbar.rememberBitwardenSnackbarHostState import com.x8bit.bitwarden.ui.platform.composition.LocalIntentManager import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager import com.x8bit.bitwarden.ui.vault.feature.attachments.handlers.AttachmentsHandlers @@ -49,7 +49,7 @@ fun AttachmentsScreen( attachmentsHandlers.onFileChoose(it) } } - val context = LocalContext.current + val snackbarHostState = rememberBitwardenSnackbarHostState() EventsEffect(viewModel = viewModel) { event -> when (event) { AttachmentsEvent.NavigateBack -> onNavigateBack() @@ -60,11 +60,7 @@ fun AttachmentsScreen( ) } - is AttachmentsEvent.ShowToast -> { - Toast - .makeText(context, event.message(context.resources), Toast.LENGTH_SHORT) - .show() - } + is AttachmentsEvent.ShowSnackbar -> snackbarHostState.showSnackbar(event.data) } } @@ -96,6 +92,9 @@ fun AttachmentsScreen( }, ) }, + snackbarHost = { + BitwardenSnackbarHost(bitwardenHostState = snackbarHostState) + }, ) { when (val viewState = state.viewState) { is AttachmentsState.ViewState.Content -> AttachmentsContent( diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/attachments/AttachmentsViewModel.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/attachments/AttachmentsViewModel.kt index 6dfc638de7..be87b11c00 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/attachments/AttachmentsViewModel.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/attachments/AttachmentsViewModel.kt @@ -16,6 +16,7 @@ import com.x8bit.bitwarden.data.auth.repository.model.UserState import com.x8bit.bitwarden.data.vault.repository.VaultRepository import com.x8bit.bitwarden.data.vault.repository.model.CreateAttachmentResult import com.x8bit.bitwarden.data.vault.repository.model.DeleteAttachmentResult +import com.x8bit.bitwarden.ui.platform.components.snackbar.BitwardenSnackbarData import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager import com.x8bit.bitwarden.ui.vault.feature.attachments.util.toViewState import dagger.hilt.android.lifecycle.HiltViewModel @@ -281,7 +282,7 @@ class AttachmentsViewModel @Inject constructor( is CreateAttachmentResult.Success -> { mutableStateFlow.update { it.copy(dialogState = null) } - sendEvent(AttachmentsEvent.ShowToast(R.string.save_attachment_success.asText())) + sendEvent(AttachmentsEvent.ShowSnackbar(R.string.save_attachment_success.asText())) } } } @@ -302,7 +303,7 @@ class AttachmentsViewModel @Inject constructor( DeleteAttachmentResult.Success -> { mutableStateFlow.update { it.copy(dialogState = null) } - sendEvent(AttachmentsEvent.ShowToast(R.string.attachment_deleted.asText())) + sendEvent(AttachmentsEvent.ShowSnackbar(R.string.attachment_deleted.asText())) } } } @@ -430,11 +431,25 @@ sealed class AttachmentsEvent { data object ShowChooserSheet : AttachmentsEvent() /** - * Displays the given [message] as a toast. + * Displays the given [data] as a snackbar. */ - data class ShowToast( - val message: Text, - ) : AttachmentsEvent() + data class ShowSnackbar( + val data: BitwardenSnackbarData, + ) : AttachmentsEvent() { + constructor( + message: Text, + messageHeader: Text? = null, + actionLabel: Text? = null, + withDismissAction: Boolean = false, + ) : this( + data = BitwardenSnackbarData( + message = message, + messageHeader = messageHeader, + actionLabel = actionLabel, + withDismissAction = withDismissAction, + ), + ) + } } /** diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/attachments/AttachmentsViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/attachments/AttachmentsViewModelTest.kt index db8351f4e0..d807bab263 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/attachments/AttachmentsViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/attachments/AttachmentsViewModelTest.kt @@ -334,7 +334,7 @@ class AttachmentsViewModelTest : BaseViewModelTest() { } @Test - fun `SaveClick should send ShowToast when createAttachment succeeds`() = runTest { + fun `SaveClick should send ShowSnackbar when createAttachment succeeds`() = runTest { val cipherView = createMockCipherView(number = 1) val fileName = "test.png" val uri = mockk() @@ -373,7 +373,7 @@ class AttachmentsViewModelTest : BaseViewModelTest() { viewModel.eventFlow.test { viewModel.trySendAction(AttachmentsAction.SaveClick) assertEquals( - AttachmentsEvent.ShowToast(R.string.save_attachment_success.asText()), + AttachmentsEvent.ShowSnackbar(R.string.save_attachment_success.asText()), awaitItem(), ) } @@ -480,7 +480,7 @@ class AttachmentsViewModelTest : BaseViewModelTest() { } @Test - fun `DeleteClick with deleteCipherAttachment success should emit ShowToast`() = runTest { + fun `DeleteClick with deleteCipherAttachment success should emit ShowSnackbar`() = runTest { val cipherId = "mockId-1" val attachmentId = "mockId-1" val cipherView = createMockCipherView(number = 1) @@ -497,7 +497,7 @@ class AttachmentsViewModelTest : BaseViewModelTest() { viewModel.eventFlow.test { viewModel.trySendAction(AttachmentsAction.DeleteClick(cipherId)) assertEquals( - AttachmentsEvent.ShowToast(R.string.attachment_deleted.asText()), + AttachmentsEvent.ShowSnackbar(R.string.attachment_deleted.asText()), awaitItem(), ) }