diff --git a/app/src/main/java/org/dianqk/ruslin/ui/component/SuspendConfirmAlertDialog.kt b/app/src/main/java/org/dianqk/ruslin/ui/component/SuspendConfirmAlertDialog.kt new file mode 100644 index 0000000..d1ea00d --- /dev/null +++ b/app/src/main/java/org/dianqk/ruslin/ui/component/SuspendConfirmAlertDialog.kt @@ -0,0 +1,73 @@ +package org.dianqk.ruslin.ui.component + +import androidx.compose.animation.core.* +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.res.stringResource +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import org.dianqk.ruslin.R + +@Composable +fun SuspendConfirmAlertDialog( + scope: CoroutineScope = rememberCoroutineScope(), + icon: @Composable (() -> Unit)? = null, + inProgressIcon: @Composable ((Modifier) -> Unit)? = null, + title: @Composable (() -> Unit)? = null, + text: @Composable (() -> Unit)? = null, + onDismissRequest: () -> Unit, + onConfirm: suspend () -> Unit, + onConfirmFinished: () -> Unit, +) { + var inProgress by remember { mutableStateOf(false) } + val inProgressAnimation by rememberInfiniteTransition().animateFloat( + initialValue = 1f, + targetValue = 0f, + animationSpec = infiniteRepeatable( + animation = tween(300, easing = LinearEasing) + ) + ) + AlertDialog( + onDismissRequest = { + if (!inProgress) { + onDismissRequest() + } + }, + confirmButton = { + TextButton(enabled = !inProgress, onClick = { + inProgress = true + scope.launch { + onConfirm() + inProgress = false + onConfirmFinished() + } + }) { + Text(text = stringResource(id = R.string.confirm)) + } + }, + dismissButton = { + TextButton(enabled = !inProgress, onClick = { + onDismissRequest() + }) { + Text(text = stringResource(id = R.string.cancel)) + } + }, + icon = { + if (inProgress) { + if (inProgressIcon != null) { + inProgressIcon(Modifier.alpha(inProgressAnimation)) + } + } else { + if (icon != null) { + icon() + } + } + }, + title = title, + text = text + ) +} \ No newline at end of file diff --git a/app/src/main/java/org/dianqk/ruslin/ui/page/notes/NotesDrawerSheet.kt b/app/src/main/java/org/dianqk/ruslin/ui/page/notes/NotesDrawerSheet.kt index 8326efa..3b1bb2d 100644 --- a/app/src/main/java/org/dianqk/ruslin/ui/page/notes/NotesDrawerSheet.kt +++ b/app/src/main/java/org/dianqk/ruslin/ui/page/notes/NotesDrawerSheet.kt @@ -1,12 +1,14 @@ package org.dianqk.ruslin.ui.page.notes import androidx.compose.animation.* +import androidx.compose.animation.core.* import androidx.compose.foundation.* import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.CreateNewFolder +import androidx.compose.material.icons.filled.DeleteSweep import androidx.compose.material.icons.filled.Edit import androidx.compose.material.icons.filled.Warning import androidx.compose.material.icons.outlined.* @@ -29,6 +31,7 @@ import org.dianqk.ruslin.R import org.dianqk.ruslin.ui.component.CombinedClickableSurface import org.dianqk.ruslin.ui.component.OutlinedButtonWithIcon import org.dianqk.ruslin.ui.component.SubTitle +import org.dianqk.ruslin.ui.component.SuspendConfirmAlertDialog import org.dianqk.ruslin.ui.theme.Shape32 import uniffi.ruslin.FfiFolder @@ -44,7 +47,7 @@ fun NotesDrawerSheet( openCreateFolderDialog: Boolean, onCreateFolder: (String) -> Unit, onRenameFolder: (FfiFolder) -> Unit, - onDeleteFolder: (FfiFolder) -> Unit, + onDeleteFolder: suspend (FfiFolder) -> Unit, onChangeOpenCreateFolderDialogVisible: (Boolean) -> Unit, onShowSettingsPage: () -> Unit ) { @@ -75,27 +78,22 @@ fun NotesDrawerSheet( } openDeleteFolderAlertDialog?.let { deleteFolder -> - AlertDialog( - onDismissRequest = { - openDeleteFolderAlertDialog = null - }, - confirmButton = { - TextButton(onClick = { - onDeleteFolder(deleteFolder.ffiFolder) - openDeleteFolderAlertDialog = null - }) { - Text(text = stringResource(id = R.string.confirm)) - } - }, - dismissButton = { - TextButton(onClick = { - openDeleteFolderAlertDialog = null - }) { - Text(text = stringResource(id = R.string.cancel)) - } + SuspendConfirmAlertDialog( + onDismissRequest = { openDeleteFolderAlertDialog = null }, + inProgressIcon = { + Icon( + modifier = it, + imageVector = Icons.Default.DeleteSweep, + contentDescription = null, + tint = MaterialTheme.colorScheme.tertiary + ) }, icon = { - Icon(imageVector = Icons.Default.Warning, contentDescription = null) + Icon( + imageVector = Icons.Default.Warning, + contentDescription = null, + tint = MaterialTheme.colorScheme.error + ) }, title = { Text( @@ -107,7 +105,12 @@ fun NotesDrawerSheet( }, text = { Text(text = stringResource(id = R.string.ask_delete_folder_description)) - }) + }, + onConfirm = { + onDeleteFolder(deleteFolder.ffiFolder) + }) { + openDeleteFolderAlertDialog = null + } } if (openCreateFolderDialog) { diff --git a/app/src/main/java/org/dianqk/ruslin/ui/page/notes/NotesPage.kt b/app/src/main/java/org/dianqk/ruslin/ui/page/notes/NotesPage.kt index a871920..b7646a3 100644 --- a/app/src/main/java/org/dianqk/ruslin/ui/page/notes/NotesPage.kt +++ b/app/src/main/java/org/dianqk/ruslin/ui/page/notes/NotesPage.kt @@ -7,14 +7,12 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items -import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.* import androidx.compose.material.icons.outlined.DeleteForever import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.rotate import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp @@ -24,6 +22,7 @@ import kotlinx.coroutines.launch import org.dianqk.ruslin.R import org.dianqk.ruslin.ui.component.ContentEmptyState import org.dianqk.ruslin.ui.component.ContentLoadingState +import org.dianqk.ruslin.ui.component.SuspendConfirmAlertDialog @OptIn( ExperimentalMaterial3Api::class, @@ -221,60 +220,22 @@ fun NotesPage( ) if (showRemoveMultipleItemsDialog) { - val deletingAnimation by rememberInfiniteTransition().animateFloat( - initialValue = 1f, - targetValue = 0f, - animationSpec = infiniteRepeatable( - animation = tween(300, easing = LinearEasing) - ) - ) - var isDeleting by remember { mutableStateOf(false) } - - AlertDialog( - onDismissRequest = { - if (!isDeleting) { - showRemoveMultipleItemsDialog = false - } - }, - confirmButton = { - TextButton(enabled = !isDeleting, onClick = { - isDeleting = true - scope.launch { - viewModel.deleteNotes(selectedItemIds) - .onFailure { e -> - Log.d(TAG, "$e") - } - isDeleting = false - firstSelectedItemId = null - showRemoveMultipleItemsDialog = false - } - }) { - Text(text = stringResource(id = R.string.confirm)) - } - }, - dismissButton = { - TextButton(enabled = !isDeleting, onClick = { - showRemoveMultipleItemsDialog = false - }) { - Text(text = stringResource(id = R.string.cancel)) - } + SuspendConfirmAlertDialog( + onDismissRequest = { showRemoveMultipleItemsDialog = false }, + inProgressIcon = { + Icon( + modifier = it, + imageVector = Icons.Default.DeleteSweep, + contentDescription = null, + tint = MaterialTheme.colorScheme.tertiary + ) }, icon = { - if (isDeleting) { - Icon( - modifier = Modifier.alpha(deletingAnimation), - imageVector = Icons.Default.DeleteSweep, - contentDescription = null, - tint = MaterialTheme.colorScheme.tertiary - ) - } else { - Icon( - imageVector = Icons.Default.Warning, - contentDescription = null, - tint = MaterialTheme.colorScheme.error - ) - } - + Icon( + imageVector = Icons.Default.Warning, + contentDescription = null, + tint = MaterialTheme.colorScheme.error + ) }, title = { Text( @@ -282,7 +243,16 @@ fun NotesPage( id = R.string.ask_delete_selected_notes ) ) - }) + }, + onConfirm = { + viewModel.deleteNotes(selectedItemIds) + .onFailure { e -> + Log.d(TAG, "$e") + } + }) { + firstSelectedItemId = null + showRemoveMultipleItemsDialog = false + } } LaunchedEffect(Unit) { diff --git a/app/src/main/java/org/dianqk/ruslin/ui/page/notes/NotesViewModel.kt b/app/src/main/java/org/dianqk/ruslin/ui/page/notes/NotesViewModel.kt index 85ef1ab..b6034b8 100644 --- a/app/src/main/java/org/dianqk/ruslin/ui/page/notes/NotesViewModel.kt +++ b/app/src/main/java/org/dianqk/ruslin/ui/page/notes/NotesViewModel.kt @@ -150,11 +150,9 @@ class NotesViewModel @Inject constructor( } } - fun deleteFolder(folder: FfiFolder) { - viewModelScope.launch { - notesRepository.deleteFolder(folder.id) - loadFoldersFromRepo() - } + suspend fun deleteFolder(folder: FfiFolder) { + notesRepository.deleteFolder(folder.id) + loadFoldersFromRepo() } fun createFolder(title: String) {