diff --git a/app/src/main/java/com/anytypeio/anytype/di/feature/DateObjectDI.kt b/app/src/main/java/com/anytypeio/anytype/di/feature/DateObjectDI.kt index 267957d429..72b30bdc44 100644 --- a/app/src/main/java/com/anytypeio/anytype/di/feature/DateObjectDI.kt +++ b/app/src/main/java/com/anytypeio/anytype/di/feature/DateObjectDI.kt @@ -18,6 +18,7 @@ import com.anytypeio.anytype.domain.misc.UrlBuilder import com.anytypeio.anytype.domain.multiplayer.UserPermissionProvider import com.anytypeio.anytype.domain.`object`.GetObject import com.anytypeio.anytype.domain.`object`.SetObjectDetails +import com.anytypeio.anytype.domain.objects.SetObjectListIsArchived import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes import com.anytypeio.anytype.domain.objects.StoreOfRelations import com.anytypeio.anytype.domain.page.CreateObject @@ -135,6 +136,14 @@ object DateObjectModule { dispatchers: AppCoroutineDispatchers ): SetObjectDetails = SetObjectDetails(repository, dispatchers) + @JvmStatic + @PerScreen + @Provides + fun getSetObjectListIsArchived( + repo: BlockRepository, + dispatchers: AppCoroutineDispatchers + ): SetObjectListIsArchived = SetObjectListIsArchived(repo, dispatchers) + @Module interface Declarations { @PerScreen diff --git a/app/src/main/java/com/anytypeio/anytype/ui/date/DateObjectFragment.kt b/app/src/main/java/com/anytypeio/anytype/ui/date/DateObjectFragment.kt index 69e68f6f18..6235fd341d 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/date/DateObjectFragment.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/date/DateObjectFragment.kt @@ -154,6 +154,9 @@ class DateObjectFragment : BaseComposeFragment(), ObjectTypeSelectionListener { is DateObjectCommand.SendToast.UnexpectedLayout -> { toast("Unexpected layout") } + is DateObjectCommand.SendToast.Error -> { + toast(effect.message) + } DateObjectCommand.TypeSelectionScreen -> { val dialog = ObjectTypeSelectionFragment.new(space = space) dialog.show(childFragmentManager, null) @@ -198,6 +201,7 @@ class DateObjectFragment : BaseComposeFragment(), ObjectTypeSelectionListener { canPaginate = vm.canPaginate.collectAsStateWithLifecycle().value, uiCalendarState = vm.uiCalendarState.collectAsStateWithLifecycle().value, uiSyncStatusState = vm.uiSyncStatusWidgetState.collectAsStateWithLifecycle().value, + uiSnackbarState = vm.uiSnackbarState.collectAsStateWithLifecycle().value, onDateEvent = vm::onDateEvent ) } diff --git a/feature-date/src/main/java/com/anytypeio/anytype/feature_date/mapping/DateObjectModelsExt.kt b/feature-date/src/main/java/com/anytypeio/anytype/feature_date/mapping/DateObjectModelsExt.kt index 1fb1536f4c..636be5787c 100644 --- a/feature-date/src/main/java/com/anytypeio/anytype/feature_date/mapping/DateObjectModelsExt.kt +++ b/feature-date/src/main/java/com/anytypeio/anytype/feature_date/mapping/DateObjectModelsExt.kt @@ -57,7 +57,8 @@ fun ObjectWrapper.Basic.toUiObjectsListItem( space: SpaceId, urlBuilder: UrlBuilder, objectTypes: List, - fieldParser: FieldParser + fieldParser: FieldParser, + isOwnerOrEditor: Boolean ): UiObjectsListItem { val obj = this val typeUrl = obj.getProperType() @@ -76,6 +77,7 @@ fun ObjectWrapper.Basic.toUiObjectsListItem( } }?.name, layout = layout, - icon = obj.objectIcon(builder = urlBuilder) + icon = obj.objectIcon(builder = urlBuilder), + isPossibleToDelete = isOwnerOrEditor ) } \ No newline at end of file diff --git a/feature-date/src/main/java/com/anytypeio/anytype/feature_date/ui/MainScreen.kt b/feature-date/src/main/java/com/anytypeio/anytype/feature_date/ui/MainScreen.kt index 08c5cb8170..abe6b02e8b 100644 --- a/feature-date/src/main/java/com/anytypeio/anytype/feature_date/ui/MainScreen.kt +++ b/feature-date/src/main/java/com/anytypeio/anytype/feature_date/ui/MainScreen.kt @@ -14,15 +14,22 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.statusBars import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.layout.windowInsetsTopHeight -import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarDuration +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState +import androidx.compose.material3.SnackbarResult import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.colorResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import com.anytypeio.anytype.core_models.Id import com.anytypeio.anytype.core_ui.foundation.components.BottomNavigationMenu import com.anytypeio.anytype.core_ui.syncstatus.SpaceSyncStatusScreen import com.anytypeio.anytype.core_utils.insets.EDGE_TO_EDGE_MIN_SDK @@ -36,8 +43,11 @@ import com.anytypeio.anytype.feature_date.viewmodel.UiFieldsState import com.anytypeio.anytype.feature_date.viewmodel.UiHeaderState import com.anytypeio.anytype.feature_date.viewmodel.UiNavigationWidget import com.anytypeio.anytype.feature_date.viewmodel.UiObjectsListState +import com.anytypeio.anytype.feature_date.viewmodel.UiSnackbarState import com.anytypeio.anytype.feature_date.viewmodel.UiSyncStatusBadgeState import com.anytypeio.anytype.feature_date.viewmodel.UiSyncStatusWidgetState +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -52,12 +62,31 @@ fun DateMainScreen( uiSyncStatusState: UiSyncStatusWidgetState, uiCalendarState: UiCalendarState, uiContentState: UiContentState, + uiSnackbarState: UiSnackbarState, canPaginate: Boolean, onDateEvent: (DateEvent) -> Unit ) { val scope = rememberCoroutineScope() + val snackBarHostState = remember { SnackbarHostState() } + + val snackBarText = stringResource(R.string.all_content_snackbar_title) + val undoText = stringResource(R.string.undo) + + LaunchedEffect(key1 = uiSnackbarState) { + if (uiSnackbarState is UiSnackbarState.Visible) { + showMoveToBinSnackbar( + message = "'${uiSnackbarState.message}' $snackBarText", + undo = undoText, + scope = this, + snackBarHostState = snackBarHostState, + objectId = uiSnackbarState.objId, + onDateEvent = onDateEvent + ) + } + } + Scaffold( modifier = Modifier.fillMaxSize(), containerColor = colorResource(id = R.color.background_primary), @@ -152,6 +181,9 @@ fun DateMainScreen( isOwnerOrEditor = uiNavigationWidget is UiNavigationWidget.Editor ) } + }, + snackbarHost = { + SnackbarHost(hostState = snackBarHostState) } ) if (uiSyncStatusState is UiSyncStatusWidgetState.Visible) { @@ -179,4 +211,32 @@ fun DateMainScreen( onDateEvent = onDateEvent ) } +} + +private fun showMoveToBinSnackbar( + objectId: Id, + message: String, + undo: String, + scope: CoroutineScope, + snackBarHostState: SnackbarHostState, + onDateEvent: (DateEvent) -> Unit +) { + scope.launch { + val result = snackBarHostState + .showSnackbar( + message = message, + actionLabel = undo, + duration = SnackbarDuration.Short, + withDismissAction = true + ) + when (result) { + SnackbarResult.ActionPerformed -> { + onDateEvent(DateEvent.Snackbar.UndoMoveToBin(objectId)) + } + + SnackbarResult.Dismissed -> { + onDateEvent(DateEvent.Snackbar.OnSnackbarDismiss) + } + } + } } \ No newline at end of file diff --git a/feature-date/src/main/java/com/anytypeio/anytype/feature_date/ui/ObjectsScreen.kt b/feature-date/src/main/java/com/anytypeio/anytype/feature_date/ui/ObjectsScreen.kt index 9ff10a7fa8..e0022ffb17 100644 --- a/feature-date/src/main/java/com/anytypeio/anytype/feature_date/ui/ObjectsScreen.kt +++ b/feature-date/src/main/java/com/anytypeio/anytype/feature_date/ui/ObjectsScreen.kt @@ -1,6 +1,10 @@ package com.anytypeio.anytype.feature_date.ui +import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.animation.core.tween +import androidx.compose.animation.fadeOut +import androidx.compose.animation.shrinkVertically import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.Row @@ -16,6 +20,9 @@ import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.Text import androidx.compose.material3.ListItem import androidx.compose.material3.ListItemDefaults +import androidx.compose.material3.SwipeToDismissBox +import androidx.compose.material3.SwipeToDismissBoxValue +import androidx.compose.material3.rememberSwipeToDismissBoxState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.derivedStateOf @@ -24,6 +31,7 @@ import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.graphicsLayer @@ -34,6 +42,8 @@ import androidx.compose.ui.unit.dp import com.anytypeio.anytype.core_ui.common.DefaultPreviews import com.anytypeio.anytype.core_ui.common.ShimmerEffect import com.anytypeio.anytype.core_ui.extensions.swapList +import com.anytypeio.anytype.core_ui.foundation.DismissBackground +import com.anytypeio.anytype.core_ui.foundation.Divider import com.anytypeio.anytype.core_ui.foundation.noRippleThrottledClickable import com.anytypeio.anytype.core_ui.views.ButtonSize import com.anytypeio.anytype.core_ui.views.PreviewTitle2Regular @@ -47,6 +57,7 @@ import com.anytypeio.anytype.feature_date.ui.models.StubVerticalItems import com.anytypeio.anytype.feature_date.viewmodel.UiContentState import com.anytypeio.anytype.feature_date.viewmodel.UiObjectsListItem import com.anytypeio.anytype.feature_date.viewmodel.UiObjectsListState +import kotlinx.coroutines.delay import kotlinx.coroutines.launch @Composable @@ -100,15 +111,18 @@ fun ObjectsScreen( val item = items[index] when (item) { is UiObjectsListItem.Item -> { - ListItem( + SwipeToDismissListItems( modifier = Modifier + .fillMaxWidth() + .animateItem() .noRippleThrottledClickable { onDateEvent(DateEvent.ObjectsList.OnObjectClicked(item)) }, - item = item + item = item, + onDateEvent = onDateEvent ) + Divider(paddingStart = 16.dp, paddingEnd = 16.dp) } - is UiObjectsListItem.Loading -> { ListItemLoading(modifier = Modifier) } @@ -229,6 +243,71 @@ private fun ListItemLoading( ) } +@Composable +fun SwipeToDismissListItems( + item: UiObjectsListItem.Item, + modifier: Modifier, + animationDuration: Int = 500, + onDateEvent: (DateEvent) -> Unit, +) { + var isRemoved by remember { mutableStateOf(false) } + val dismissState = rememberSwipeToDismissBoxState( + initialValue = SwipeToDismissBoxValue.Settled, + confirmValueChange = { value -> + if (value == SwipeToDismissBoxValue.EndToStart) { + isRemoved = true + true + } else { + false + } + return@rememberSwipeToDismissBoxState true + }, + positionalThreshold = { it * .5f } + ) + + if (dismissState.currentValue != SwipeToDismissBoxValue.Settled) { + LaunchedEffect(Unit) { + dismissState.snapTo(SwipeToDismissBoxValue.Settled) + } + } + + LaunchedEffect(key1 = isRemoved) { + if (isRemoved) { + delay(animationDuration.toLong()) + onDateEvent(DateEvent.ObjectsList.OnObjectMoveToBin(item)) + } + } + AnimatedVisibility( + visible = !isRemoved, + exit = shrinkVertically( + animationSpec = tween(durationMillis = animationDuration), + shrinkTowards = Alignment.Top + ) + fadeOut() + ) { + SwipeToDismissBox( + modifier = modifier, + state = dismissState, + enableDismissFromEndToStart = item.isPossibleToDelete, + enableDismissFromStartToEnd = false, + backgroundContent = { + DismissBackground( + actionText = stringResource(R.string.move_to_bin), + dismissState = dismissState + ) + }, + content = { + ListItem( + modifier = Modifier + .noRippleThrottledClickable { + onDateEvent(DateEvent.ObjectsList.OnObjectClicked(item)) + }, + item = item + ) + } + ) + } +} + @Composable private fun BoxScope.LoadingState() { val loadingAlpha by animateFloatAsState(targetValue = 1f, label = "") diff --git a/feature-date/src/main/java/com/anytypeio/anytype/feature_date/ui/models/DateEvent.kt b/feature-date/src/main/java/com/anytypeio/anytype/feature_date/ui/models/DateEvent.kt index 5903aff660..7051eadfc4 100644 --- a/feature-date/src/main/java/com/anytypeio/anytype/feature_date/ui/models/DateEvent.kt +++ b/feature-date/src/main/java/com/anytypeio/anytype/feature_date/ui/models/DateEvent.kt @@ -42,6 +42,7 @@ sealed class DateEvent { sealed class ObjectsList : DateEvent() { data class OnObjectClicked(val item: UiObjectsListItem) : ObjectsList() + data class OnObjectMoveToBin(val item: UiObjectsListItem.Item) : ObjectsList() data object OnLoadMore : ObjectsList() } @@ -53,4 +54,9 @@ sealed class DateEvent { sealed class SyncStatusWidget : DateEvent() { data object OnSyncStatusDismiss : SyncStatusWidget() } + + sealed class Snackbar : DateEvent() { + data object OnSnackbarDismiss : Snackbar() + data class UndoMoveToBin(val objectId: String) : Snackbar() + } } \ No newline at end of file diff --git a/feature-date/src/main/java/com/anytypeio/anytype/feature_date/viewmodel/DateModels.kt b/feature-date/src/main/java/com/anytypeio/anytype/feature_date/viewmodel/DateModels.kt index a9740019b9..859ddef08b 100644 --- a/feature-date/src/main/java/com/anytypeio/anytype/feature_date/viewmodel/DateModels.kt +++ b/feature-date/src/main/java/com/anytypeio/anytype/feature_date/viewmodel/DateModels.kt @@ -137,7 +137,8 @@ sealed class UiObjectsListItem { val typeName: String? = null, val createdBy: String? = null, val layout: ObjectType.Layout? = null, - val icon: ObjectIcon = ObjectIcon.None + val icon: ObjectIcon = ObjectIcon.None, + val isPossibleToDelete: Boolean = false ) : UiObjectsListItem() } @@ -179,3 +180,8 @@ sealed class UiErrorState { data class Other(val msg: String) : Reason() } } + +sealed class UiSnackbarState { + data object Hidden : UiSnackbarState() + data class Visible(val message: String, val objId: Id) : UiSnackbarState() +} diff --git a/feature-date/src/main/java/com/anytypeio/anytype/feature_date/viewmodel/DateObjectCommand.kt b/feature-date/src/main/java/com/anytypeio/anytype/feature_date/viewmodel/DateObjectCommand.kt index 37c403413a..5803bb59c1 100644 --- a/feature-date/src/main/java/com/anytypeio/anytype/feature_date/viewmodel/DateObjectCommand.kt +++ b/feature-date/src/main/java/com/anytypeio/anytype/feature_date/viewmodel/DateObjectCommand.kt @@ -11,6 +11,7 @@ sealed class DateObjectCommand { data object TypeSelectionScreen : DateObjectCommand() data object ExitToSpaceWidgets : DateObjectCommand() sealed class SendToast : DateObjectCommand() { + data class Error(val message: String) : SendToast() data class UnexpectedLayout(val layout: String) : SendToast() } data object OpenGlobalSearch : DateObjectCommand() diff --git a/feature-date/src/main/java/com/anytypeio/anytype/feature_date/viewmodel/DateObjectVMFactory.kt b/feature-date/src/main/java/com/anytypeio/anytype/feature_date/viewmodel/DateObjectVMFactory.kt index 9b7b0702ec..4d515fa780 100644 --- a/feature-date/src/main/java/com/anytypeio/anytype/feature_date/viewmodel/DateObjectVMFactory.kt +++ b/feature-date/src/main/java/com/anytypeio/anytype/feature_date/viewmodel/DateObjectVMFactory.kt @@ -9,6 +9,7 @@ import com.anytypeio.anytype.domain.misc.DateProvider import com.anytypeio.anytype.domain.misc.UrlBuilder import com.anytypeio.anytype.domain.multiplayer.UserPermissionProvider import com.anytypeio.anytype.domain.`object`.GetObject +import com.anytypeio.anytype.domain.objects.SetObjectListIsArchived import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes import com.anytypeio.anytype.domain.objects.StoreOfRelations import com.anytypeio.anytype.domain.page.CreateObject @@ -31,7 +32,8 @@ class DateObjectVMFactory @Inject constructor( private val dateProvider: DateProvider, private val spaceSyncAndP2PStatusProvider: SpaceSyncAndP2PStatusProvider, private val createObject: CreateObject, - private val fieldParser: FieldParser + private val fieldParser: FieldParser, + private val setObjectListIsArchived: SetObjectListIsArchived ) : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") @@ -50,6 +52,8 @@ class DateObjectVMFactory @Inject constructor( dateProvider = dateProvider, spaceSyncAndP2PStatusProvider = spaceSyncAndP2PStatusProvider, createObject = createObject, - fieldParser = fieldParser + fieldParser = fieldParser, + setObjectListIsArchived = setObjectListIsArchived + ) as T } \ No newline at end of file diff --git a/feature-date/src/main/java/com/anytypeio/anytype/feature_date/viewmodel/DateObjectViewModel.kt b/feature-date/src/main/java/com/anytypeio/anytype/feature_date/viewmodel/DateObjectViewModel.kt index 57350e8029..51a81c2b9c 100644 --- a/feature-date/src/main/java/com/anytypeio/anytype/feature_date/viewmodel/DateObjectViewModel.kt +++ b/feature-date/src/main/java/com/anytypeio/anytype/feature_date/viewmodel/DateObjectViewModel.kt @@ -21,6 +21,7 @@ import com.anytypeio.anytype.domain.misc.DateProvider import com.anytypeio.anytype.domain.misc.UrlBuilder import com.anytypeio.anytype.domain.multiplayer.UserPermissionProvider import com.anytypeio.anytype.domain.`object`.GetObject +import com.anytypeio.anytype.domain.objects.SetObjectListIsArchived import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes import com.anytypeio.anytype.domain.objects.StoreOfRelations import com.anytypeio.anytype.domain.page.CreateObject @@ -41,6 +42,7 @@ import com.anytypeio.anytype.presentation.search.GlobalSearchViewModel.Companion import com.anytypeio.anytype.presentation.search.ObjectSearchConstants.defaultKeys import com.anytypeio.anytype.presentation.sync.toSyncStatusWidgetState import kotlin.collections.map +import kotlin.text.take import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.Flow @@ -82,7 +84,8 @@ class DateObjectViewModel( private val dateProvider: DateProvider, private val spaceSyncAndP2PStatusProvider: SpaceSyncAndP2PStatusProvider, private val createObject: CreateObject, - private val fieldParser: FieldParser + private val fieldParser: FieldParser, + private val setObjectListIsArchived: SetObjectListIsArchived ) : ViewModel(), AnalyticSpaceHelperDelegate by analyticSpaceHelperDelegate { val uiCalendarIconState = MutableStateFlow(UiCalendarIconState.Hidden) @@ -97,6 +100,7 @@ class DateObjectViewModel( val uiCalendarState = MutableStateFlow(UiCalendarState.Hidden) val uiSyncStatusWidgetState = MutableStateFlow(UiSyncStatusWidgetState.Hidden) + val uiSnackbarState = MutableStateFlow(UiSnackbarState.Hidden) val effects = MutableSharedFlow() val errorState = MutableStateFlow(UiErrorState.Hidden) @@ -397,7 +401,8 @@ class DateObjectViewModel( space = vmParams.spaceId, urlBuilder = urlBuilder, objectTypes = storeOfObjectTypes.getAll(), - fieldParser = fieldParser + fieldParser = fieldParser, + isOwnerOrEditor = permission.value?.isOwnerOrEditor() == true ) } uiContentState.value = if (items.isEmpty()) { @@ -640,6 +645,18 @@ class DateObjectViewModel( is DateEvent.NavigationWidget -> onNavigationWidgetEvent(event) is DateEvent.ObjectsList -> onObjectsListEvent(event) is DateEvent.SyncStatusWidget -> onSyncStatusWidgetEvent(event) + is DateEvent.Snackbar -> onSnackbarEvent(event) + } + } + + private fun onSnackbarEvent(event: DateEvent.Snackbar) { + when (event) { + DateEvent.Snackbar.OnSnackbarDismiss -> { + proceedWithDismissSnackbar() + } + is DateEvent.Snackbar.UndoMoveToBin -> { + proceedWithUndoMoveToBin(event.objectId) + } } } @@ -666,6 +683,7 @@ class DateObjectViewModel( when (event) { DateEvent.ObjectsList.OnLoadMore -> updateLimit() is DateEvent.ObjectsList.OnObjectClicked -> onItemClicked(event.item) + is DateEvent.ObjectsList.OnObjectMoveToBin -> proceedWithMoveToBin(event.item) } } @@ -797,6 +815,48 @@ class DateObjectViewModel( } } } + + fun proceedWithMoveToBin(item: UiObjectsListItem.Item) { + val params = SetObjectListIsArchived.Params( + targets = listOf(item.id), + isArchived = true + ) + viewModelScope.launch { + setObjectListIsArchived.async(params).fold( + onSuccess = { ids -> + Timber.d("Successfully archived object: $ids") + val name = item.name + uiSnackbarState.value = UiSnackbarState.Visible( + message = name.take(10), + objId = item.id + ) + }, + onFailure = { e -> + Timber.e(e, "Error while archiving object") + effects.emit(DateObjectCommand.SendToast.Error("Error while archiving object")) + } + ) + } + } + + fun proceedWithUndoMoveToBin(objectId: Id) { + val params = SetObjectListIsArchived.Params( + targets = listOf(objectId), + isArchived = false + ) + viewModelScope.launch { + setObjectListIsArchived.async(params).fold( + onSuccess = { ids -> + Timber.d("Successfully archived object: $ids") + uiSnackbarState.value = UiSnackbarState.Hidden + }, + onFailure = { e -> + Timber.e(e, "Error while un-archiving object") + effects.emit(DateObjectCommand.SendToast.Error("Error while un-archiving object")) + } + ) + } + } //endregion //region Ui State @@ -847,6 +907,10 @@ class DateObjectViewModel( ) } } + + fun proceedWithDismissSnackbar() { + uiSnackbarState.value = UiSnackbarState.Hidden + } //endregion companion object { diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/widgets/collection/CollectionViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/widgets/collection/CollectionViewModel.kt index d7aa7375c8..e3da9f5ea7 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/widgets/collection/CollectionViewModel.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/widgets/collection/CollectionViewModel.kt @@ -656,9 +656,22 @@ class CollectionViewModel( } private fun changeObjectListBinStatus(ids: List, isArchived: Boolean) { - launch { - setObjectListIsArchived.stream(SetObjectListIsArchived.Params(ids, isArchived)) - .collect { it.progressiveFold() } + viewModelScope.launch { + val params = SetObjectListIsArchived.Params(ids, isArchived) + setObjectListIsArchived.async(params) + .fold( + onSuccess = { + if (isArchived) { + toasts.emit("Objects moved to bin") + } else { + toasts.emit("Objects restored") + } + }, + onFailure = { + toasts.emit("Error while moving files to bin") + Timber.e(it, "Error while moving files to bin") + } + ) } }