diff --git a/app/src/main/java/com/example/tudee/presentation/components/CategoryTask.kt b/app/src/main/java/com/example/tudee/presentation/components/CategoryTask.kt index 45dd728c..966f3030 100644 --- a/app/src/main/java/com/example/tudee/presentation/components/CategoryTask.kt +++ b/app/src/main/java/com/example/tudee/presentation/components/CategoryTask.kt @@ -2,7 +2,6 @@ package com.example.tudee.presentation.components import androidx.compose.foundation.background import androidx.compose.foundation.border -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -39,8 +38,7 @@ fun CategoryTaskComponent( priorityBackgroundColor: Color = TudeeTheme.color.statusColors.yellowAccent, taskIcon: @Composable () -> Unit, priorityIcon: Painter, - dateText: String? = null, - onClick: () -> Unit, + dateText: String? = null, onClick: () -> Unit = {}, ) { Column( modifier = modifier @@ -48,7 +46,7 @@ fun CategoryTaskComponent( .clip(RoundedCornerShape(16.dp)) .clickWithRipple(onClick = onClick) .border(1.dp, TudeeTheme.color.surfaceHigh, RoundedCornerShape(16.dp)) - .background( TudeeTheme.color.surfaceHigh,RoundedCornerShape(16.dp)) + .background(TudeeTheme.color.surfaceHigh, RoundedCornerShape(16.dp)) .padding(top = 4.dp, start = 4.dp, end = 12.dp), verticalArrangement = Arrangement.spacedBy(2.dp) ) { diff --git a/app/src/main/java/com/example/tudee/presentation/screen/category/tasks/CategoryTasksScreen.kt b/app/src/main/java/com/example/tudee/presentation/screen/category/tasks/CategoryTasksScreen.kt index a8cde8ff..27fa80b1 100644 --- a/app/src/main/java/com/example/tudee/presentation/screen/category/tasks/CategoryTasksScreen.kt +++ b/app/src/main/java/com/example/tudee/presentation/screen/category/tasks/CategoryTasksScreen.kt @@ -1,5 +1,14 @@ package com.example.tudee.presentation.screen.category.tasks +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.Spring +import androidx.compose.animation.core.spring +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically import androidx.compose.foundation.border import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -8,6 +17,7 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.RoundedCornerShape @@ -22,6 +32,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf 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 @@ -33,13 +44,16 @@ import androidx.navigation.compose.rememberNavController import com.example.tudee.R import com.example.tudee.designsystem.theme.TudeeTheme import com.example.tudee.presentation.components.CategoryTaskComponent +import com.example.tudee.presentation.components.SnackBarComponent import com.example.tudee.presentation.components.TabBarComponent import com.example.tudee.presentation.components.TopAppBar import com.example.tudee.presentation.components.TudeeScaffold +import com.example.tudee.presentation.components.buttons.ButtonState import com.example.tudee.presentation.screen.category.component.CategorySheet import com.example.tudee.presentation.screen.category.model.CategoryData import com.example.tudee.presentation.screen.category.model.CategorySheetState import com.example.tudee.presentation.screen.category.model.UiImage +import com.example.tudee.presentation.screen.task_screen.component.DeleteConfirmationBottomSheet import com.example.tudee.presentation.screen.task_screen.ui.NotTaskForTodayDialogue import kotlinx.coroutines.delay import org.koin.androidx.compose.koinViewModel @@ -51,20 +65,37 @@ fun CategoryTasksScreen( viewModel: CategoryTasksViewModel = koinViewModel() ) { val uiState by viewModel.categoryTasksUiState.collectAsState() - val showSnackBar = remember { mutableStateOf(false) } - var selectedTabIndex by remember { mutableIntStateOf(0) } - var isEditCategorySheetVisible by remember { mutableStateOf(false) } + var showSnackBar by remember { mutableStateOf(false) } + var snackBarMessageId by remember { mutableIntStateOf(0) } + var snackBarIconId by remember { mutableIntStateOf(0) } val allTasks by viewModel.allTasks.collectAsState() + val coroutineScope = rememberCoroutineScope() LaunchedEffect(Unit) { viewModel.snackBarEvent.collect { event -> - when (event) { - is SnackBarEvent.ShowError -> { - showSnackBar.value = true - delay(3000L) - showSnackBar.value = false + showSnackBar = when (event) { + CategoryTasksSnackBarEvent.ShowDeleteError -> { + snackBarMessageId = R.string.failed_to_delete_category + snackBarIconId = R.drawable.ic_error + true } - SnackBarEvent.ShowSuccess -> {} + CategoryTasksSnackBarEvent.ShowDeleteSuccess -> { + snackBarMessageId = R.string.delete_category_successfully + snackBarIconId = R.drawable.ic_success + true + } + + CategoryTasksSnackBarEvent.ShowEditError -> { + snackBarMessageId = R.string.failed_to_edit_category + snackBarIconId = R.drawable.ic_error + true + } + + CategoryTasksSnackBarEvent.ShowEditSuccess -> { + snackBarMessageId = R.string.edited_category_successfully + snackBarIconId = R.drawable.ic_success + true + } } } } @@ -79,9 +110,8 @@ fun CategoryTasksScreen( uiState.categoryTasksUiModel?.isPredefined?.let { if (!(it)) { IconButton( - onClick = { - isEditCategorySheetVisible = true - } + onClick = viewModel::showEditCategorySheet + ) { Icon( modifier = Modifier @@ -107,23 +137,38 @@ fun CategoryTasksScreen( CategoryTasksContent( modifier = modifier.padding(paddingValues), categoryTaskUIState = uiState, - isEditCategorySheetVisible = isEditCategorySheetVisible, + isEditCategorySheetVisible = uiState.isEditCategorySheetVisible, + isDeleteCategorySheetVisible = uiState.isDeleteCategorySheetVisible, allTasks = allTasks, onDeleteCategory = viewModel::deleteCategory, onSaveButtonClicked = { categoryData -> viewModel.editCategory(categoryData) }, onTabSelected = { index -> - selectedTabIndex = index viewModel.updateSelectedIndex(index) }, - onBottomSheetDismissed = { isEditCategorySheetVisible = false }, - onCancelButtonClicked = { isEditCategorySheetVisible = false }, + onBottomSheetDismissed = viewModel::hideEditCategorySheet, + onCancelButtonClicked = viewModel::hideEditCategorySheet, + showDeleteCategorySheet = { + viewModel.hideEditCategorySheet() + viewModel.showDeleteCategorySheet() + }, + hideDeleteCategorySheet = { + viewModel.hideDeleteCategorySheet() + viewModel.showEditCategorySheet() + } ) } else if (uiState.loading) { LoadingState(modifier = modifier) } } + + CategoryTasksSnackBar( + isSnackBarVisible = showSnackBar, + hideSnackBar = { showSnackBar = false }, + snackBarStringId = snackBarMessageId, + snackBarIconId = snackBarIconId, + ) } @@ -132,7 +177,10 @@ fun CategoryTasksContent( modifier: Modifier = Modifier, categoryTaskUIState: CategoryTasksUiState, isEditCategorySheetVisible: Boolean, + isDeleteCategorySheetVisible: Boolean, allTasks: List, + showDeleteCategorySheet: () -> Unit, + hideDeleteCategorySheet: () -> Unit, onDeleteCategory: () -> Unit, onSaveButtonClicked: (CategoryData) -> Unit, onTabSelected: (Int) -> Unit, @@ -152,8 +200,11 @@ fun CategoryTasksContent( allTasks = allTasks, onTabSelected = { onTabSelected(it) }, isEditCategorySheetVisible = isEditCategorySheetVisible, - onBottomSheetDismissed = { onBottomSheetDismissed() }, - onCancelButtonClicked = { onCancelButtonClicked() } + isDeleteCategorySheetVisible = isDeleteCategorySheetVisible, + onBottomSheetDismissed = onBottomSheetDismissed, + onCancelButtonClicked = onCancelButtonClicked, + showDeleteCategorySheet = showDeleteCategorySheet, + hideDeleteCategorySheet = hideDeleteCategorySheet ) } } @@ -175,6 +226,9 @@ private fun SuccessState( categoryTasks: List, isEditCategorySheetVisible: Boolean, allTasks: List, + isDeleteCategorySheetVisible: Boolean, + showDeleteCategorySheet: () -> Unit, + hideDeleteCategorySheet: () -> Unit, onDeleteCategory: () -> Unit, onSaveButtonClicked: (CategoryData) -> Unit, onTabSelected: (Int) -> Unit, @@ -236,10 +290,7 @@ private fun SuccessState( tint = TudeeTheme.color.statusColors.purpleAccent ) }, - priorityIcon = painterResource(categoryTask.priority.drawableRes), - onClick = { - //TODO() - } + priorityIcon = painterResource(categoryTask.priority.drawableRes) ) } } @@ -255,8 +306,61 @@ private fun SuccessState( onDismiss = onBottomSheetDismissed, onConfirm = { onSaveButtonClicked(it) }, onCancel = onCancelButtonClicked, - onDelete = onDeleteCategory + onDelete = showDeleteCategorySheet ) + + DeleteConfirmationBottomSheet( + isBottomSheetVisible = isDeleteCategorySheetVisible, + title = stringResource(R.string.delete_category), + subtitle = stringResource(R.string.delete_category_confirmation), + deleteButtonUiState = ButtonState.IDLE, + cancelButtonUiState = ButtonState.IDLE, + onBottomSheetDismissed = hideDeleteCategorySheet, + onDeleteButtonClicked = onDeleteCategory, + onCancelButtonClicked = hideDeleteCategorySheet + ) + } + +} + +@Composable +private fun CategoryTasksSnackBar( + isSnackBarVisible: Boolean, + hideSnackBar: () -> Unit, + @StringRes snackBarStringId: Int, + @DrawableRes snackBarIconId: Int +) { + LaunchedEffect(isSnackBarVisible) { + delay(3000) + hideSnackBar() } + AnimatedVisibility( + visible = isSnackBarVisible, enter = slideInVertically( + initialOffsetY = { fullHeight -> -fullHeight }, animationSpec = spring( + stiffness = Spring.StiffnessLow, dampingRatio = Spring.DampingRatioMediumBouncy + ) + ) + fadeIn(), + + exit = slideOutVertically( + targetOffsetY = { fullHeight -> fullHeight }, animationSpec = spring( + stiffness = Spring.StiffnessMedium, dampingRatio = Spring.DampingRatioNoBouncy + ) + ) + fadeOut() + ) { + Box( + Modifier + .statusBarsPadding() + .padding(top = 16.dp) + .padding(horizontal = 16.dp) + ) { + SnackBarComponent( + message = stringResource(snackBarStringId), + iconPainter = painterResource(id = snackBarIconId), + iconDescription = stringResource(snackBarStringId), + iconBackgroundColor = TudeeTheme.color.statusColors.greenVariant, + iconTint = TudeeTheme.color.statusColors.greenAccent + ) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/example/tudee/presentation/screen/category/tasks/CategoryTasksUiState.kt b/app/src/main/java/com/example/tudee/presentation/screen/category/tasks/CategoryTasksUiState.kt index 37765fe7..9d8bf9f9 100644 --- a/app/src/main/java/com/example/tudee/presentation/screen/category/tasks/CategoryTasksUiState.kt +++ b/app/src/main/java/com/example/tudee/presentation/screen/category/tasks/CategoryTasksUiState.kt @@ -11,6 +11,8 @@ import com.example.tudee.presentation.screen.category.model.UiImage data class CategoryTasksUiState( val loading: Boolean = false, + val isEditCategorySheetVisible: Boolean = false, + val isDeleteCategorySheetVisible: Boolean = false, val categoryTasksUiModel: CategoryTasksUiModel? = null ) diff --git a/app/src/main/java/com/example/tudee/presentation/screen/category/tasks/CategoryTasksViewModel.kt b/app/src/main/java/com/example/tudee/presentation/screen/category/tasks/CategoryTasksViewModel.kt index b7205ead..605ce92a 100644 --- a/app/src/main/java/com/example/tudee/presentation/screen/category/tasks/CategoryTasksViewModel.kt +++ b/app/src/main/java/com/example/tudee/presentation/screen/category/tasks/CategoryTasksViewModel.kt @@ -30,12 +30,36 @@ class CategoryTasksViewModel( private val _categoryTasksUiState = MutableStateFlow(CategoryTasksUiState()) val categoryTasksUiState: StateFlow = _categoryTasksUiState - private val _snackBarEvent = MutableSharedFlow() + private val _snackBarEvent = MutableSharedFlow() val snackBarEvent = _snackBarEvent.asSharedFlow() private val _allTasks = MutableStateFlow>(mutableListOf()) val allTasks = _allTasks.asStateFlow() + fun showEditCategorySheet() { + _categoryTasksUiState.update { currentState -> + currentState.copy(isEditCategorySheetVisible = true) + } + } + + fun hideEditCategorySheet() { + _categoryTasksUiState.update { currentState -> + currentState.copy(isEditCategorySheetVisible = false) + } + } + + fun showDeleteCategorySheet() { + _categoryTasksUiState.update { currentState -> + currentState.copy(isDeleteCategorySheetVisible = true) + } + } + + fun hideDeleteCategorySheet() { + _categoryTasksUiState.update { currentState -> + currentState.copy(isDeleteCategorySheetVisible = false) + } + } + init { viewModelScope.launch { _categoryTasksUiState.update { it.copy(loading = true) } @@ -55,8 +79,7 @@ class CategoryTasksViewModel( ) } updateSelectedIndex(0) - } catch (_: Exception) { - _snackBarEvent.emit(SnackBarEvent.ShowError) + } catch (e: Exception) { _categoryTasksUiState.update { currentState -> currentState.copy( loading = false, @@ -119,7 +142,15 @@ class CategoryTasksViewModel( fun deleteCategory() { _categoryTasksUiState.value.categoryTasksUiModel?.let { viewModelScope.launch { - taskCategoryService.deleteCategory(it.id) + try { + taskCategoryService.deleteCategory(it.id) + _snackBarEvent.emit(CategoryTasksSnackBarEvent.ShowDeleteSuccess) + + hideDeleteCategorySheet() + hideEditCategorySheet() + } catch (_: Exception) { + _snackBarEvent.emit(CategoryTasksSnackBarEvent.ShowDeleteError) + } } } } @@ -127,15 +158,28 @@ class CategoryTasksViewModel( fun editCategory(category: CategoryData) { _categoryTasksUiState.value.categoryTasksUiModel?.let { viewModelScope.launch { - taskCategoryService.editCategory( - TaskCategory( - id = it.id, - title = category.name, - image = category.uiImage?.asString()!!, - isPredefined = it.isPredefined + try { + taskCategoryService.editCategory( + TaskCategory( + id = it.id, + title = category.name, + image = category.uiImage?.asString()!!, + isPredefined = it.isPredefined + ) ) - ) + hideEditCategorySheet() + _snackBarEvent.emit(CategoryTasksSnackBarEvent.ShowEditSuccess) + } catch (_: Exception) { + _snackBarEvent.emit(CategoryTasksSnackBarEvent.ShowEditError) + } } } } +} + +sealed class CategoryTasksSnackBarEvent() { + data object ShowEditError : CategoryTasksSnackBarEvent() + data object ShowEditSuccess : CategoryTasksSnackBarEvent() + data object ShowDeleteError : CategoryTasksSnackBarEvent() + data object ShowDeleteSuccess : CategoryTasksSnackBarEvent() } \ No newline at end of file diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 76465858..0e2eadae 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -99,4 +99,10 @@ تعديل الصورة اضافة صورة صوره صفحة البدايه + حدث حطأ, لم يتم حذف الصنف. + تم حذف العنصر بنجاح. + حدث حطأ, لم يتم تعديل الصنف. + تم تعديل الصنف بنجاح. + حذف صنف + هل تريد المواصلة ؟ \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 52501245..04079ebd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -111,7 +111,7 @@ Tudee is proud of you Previous week Next week Open calendar - SAVE +e Save Added category successfully. No tasks in %1$s category! Category not found! @@ -119,4 +119,10 @@ Tudee is proud of you Edit image Add image Image for On boarding page + Failed to delete category. + Deleted category successfully. + Failed to edit category. + Edited category successfully. + Delete Category + Are you sure to continue? \ No newline at end of file