diff --git a/app/src/main/java/com/sanaa/tudee_assistant/presentation/modifire/AnimationModifiers.kt b/app/src/main/java/com/sanaa/tudee_assistant/presentation/modifire/AnimationModifiers.kt new file mode 100644 index 00000000..fe7c7dcb --- /dev/null +++ b/app/src/main/java/com/sanaa/tudee_assistant/presentation/modifire/AnimationModifiers.kt @@ -0,0 +1,104 @@ +package com.sanaa.tudee_assistant.presentation.modifire + +import androidx.compose.animation.core.AnimationSpec +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.animation.core.tween +import androidx.compose.foundation.layout.offset +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.composed +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.platform.LocalLayoutDirection +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.LayoutDirection +import androidx.compose.ui.unit.dp +import kotlinx.coroutines.delay + +enum class SlideDirection { + Up, Down, End, Start, Left, Right +} + +fun Modifier.applyTaskCardAnimation( + delayMillis: Int, + durationMillis: Int = 600, + distance: Dp = 10.dp +): Modifier = this + .fadeAnimation(durationMillis = 400) + .slide( + direction = SlideDirection.Up, + delayMillis = delayMillis, + durationMillis = durationMillis, + distance = distance + ) + + +fun Modifier.slide( + direction: SlideDirection, + distance: Dp = 10.dp, + durationMillis: Int = 550, + delayMillis: Int = 0, + animationSpec: AnimationSpec = tween(durationMillis = durationMillis) +): Modifier = composed { + val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl + var isAnimated by remember { mutableStateOf(false) } + val offsetX by animateFloatAsState( + targetValue = if (isAnimated) { + 0f + } else { + when (direction) { + SlideDirection.Start -> if (isRtl) distance.value else -distance.value + SlideDirection.End -> if (isRtl) -distance.value else distance.value + SlideDirection.Left -> distance.value + SlideDirection.Right -> -distance.value + else -> 0f + } + }, + animationSpec = animationSpec, + label = "swipeAnimationX" + ) + val offsetY by animateFloatAsState( + targetValue = if (isAnimated) { + 0f + } else { + when (direction) { + SlideDirection.Up -> distance.value + SlideDirection.Down -> -distance.value + else -> 0f + } + }, + animationSpec = animationSpec, + label = "swipeAnimationY" + ) + + LaunchedEffect(key1 = Unit) { + delay(delayMillis.toLong()) + isAnimated = true + } + + this.offset(x = offsetX.dp, y = offsetY.dp) +} + +fun Modifier.fadeAnimation( + from: Float = 0f, + to: Float = 1f, + durationMillis: Int = 400 +): Modifier = composed { + var isAnimated by remember { mutableStateOf(false) } + val alpha by animateFloatAsState( + targetValue = if (isAnimated) to else from, + animationSpec = tween(durationMillis = durationMillis), + label = "fadeAnimation" + ) + + LaunchedEffect(Unit) { + isAnimated = true + } + + graphicsLayer { + this.alpha = alpha + } +} \ No newline at end of file diff --git a/app/src/main/java/com/sanaa/tudee_assistant/presentation/screen/category/CategoryScreen.kt b/app/src/main/java/com/sanaa/tudee_assistant/presentation/screen/category/CategoryScreen.kt index 35ab5709..26de4149 100644 --- a/app/src/main/java/com/sanaa/tudee_assistant/presentation/screen/category/CategoryScreen.kt +++ b/app/src/main/java/com/sanaa/tudee_assistant/presentation/screen/category/CategoryScreen.kt @@ -9,7 +9,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid -import androidx.compose.foundation.lazy.grid.items +import androidx.compose.foundation.lazy.grid.itemsIndexed import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -24,6 +24,8 @@ import com.sanaa.tudee_assistant.presentation.designSystem.component.CategoryCou import com.sanaa.tudee_assistant.presentation.designSystem.component.CategoryItem import com.sanaa.tudee_assistant.presentation.designSystem.component.button.FloatingActionButton import com.sanaa.tudee_assistant.presentation.designSystem.theme.Theme +import com.sanaa.tudee_assistant.presentation.modifire.SlideDirection +import com.sanaa.tudee_assistant.presentation.modifire.slide import com.sanaa.tudee_assistant.presentation.navigation.AppNavigation import com.sanaa.tudee_assistant.presentation.navigation.CategoryTasksScreenRoute import com.sanaa.tudee_assistant.presentation.shared.LocalSnackBarState @@ -102,8 +104,13 @@ fun CategoryScreenContent( verticalArrangement = Arrangement.spacedBy(24.dp), horizontalArrangement = Arrangement.spacedBy(8.dp) ) { - items(state.allCategories) { category -> + itemsIndexed(state.allCategories) { index, category -> CategoryItem( + modifier = Modifier + .slide( + direction = SlideDirection.Up, + delayMillis = (index * 100 / (index + 2)).coerceAtMost(1500) + ), category = category, topContent = { CategoryCount(category.tasksCount.toString()) }, onClick = { diff --git a/app/src/main/java/com/sanaa/tudee_assistant/presentation/screen/home/component/HomeOverviewCard.kt b/app/src/main/java/com/sanaa/tudee_assistant/presentation/screen/home/component/HomeOverviewCard.kt index b7578807..f0764e53 100644 --- a/app/src/main/java/com/sanaa/tudee_assistant/presentation/screen/home/component/HomeOverviewCard.kt +++ b/app/src/main/java/com/sanaa/tudee_assistant/presentation/screen/home/component/HomeOverviewCard.kt @@ -16,6 +16,7 @@ import com.sanaa.tudee_assistant.R import com.sanaa.tudee_assistant.presentation.designSystem.component.Slider import com.sanaa.tudee_assistant.presentation.designSystem.theme.Theme import com.sanaa.tudee_assistant.presentation.model.TaskUiStatus +import com.sanaa.tudee_assistant.presentation.modifire.applyTaskCardAnimation import com.sanaa.tudee_assistant.presentation.screen.home.HomeScreenUiState @Composable @@ -44,27 +45,36 @@ fun HomeOverviewCard(state: HomeScreenUiState) { color = Theme.color.title, style = Theme.textStyle.title.large ) + TaskStatusCardsRow(state) + } +} + +@Composable +fun TaskStatusCardsRow(state: HomeScreenUiState) { + val statuses = listOf( + TaskUiStatus.DONE, + TaskUiStatus.IN_PROGRESS, + TaskUiStatus.TODO + ) + + Row( + modifier = Modifier.padding( + top = Theme.dimension.small, + start = Theme.dimension.regular, + end = Theme.dimension.regular + ), + horizontalArrangement = Arrangement.spacedBy(Theme.dimension.small) + ) { + statuses.forEachIndexed { index, status -> + val count = state.tasks.count { it.status == status } - Row( - modifier = Modifier.padding( - top = Theme.dimension.small, - start = Theme.dimension.regular, - end = Theme.dimension.regular - ), - horizontalArrangement = Arrangement.spacedBy(Theme.dimension.small) - ) { - TaskCountByStatusCard( - count = state.tasks.filter { it.status == TaskUiStatus.DONE }.size, - taskUiStatus = TaskUiStatus.DONE, - ) - TaskCountByStatusCard( - count = state.tasks.filter { it.status == TaskUiStatus.IN_PROGRESS }.size, - taskUiStatus = TaskUiStatus.IN_PROGRESS, - ) TaskCountByStatusCard( - count = state.tasks.filter { it.status == TaskUiStatus.TODO }.size, - taskUiStatus = TaskUiStatus.TODO, + count = count, + taskUiStatus = status, + modifier = Modifier + .weight(1f) + .applyTaskCardAnimation(delayMillis = index * 50) ) } } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/sanaa/tudee_assistant/presentation/screen/home/component/TaskCountByStatusCard.kt b/app/src/main/java/com/sanaa/tudee_assistant/presentation/screen/home/component/TaskCountByStatusCard.kt index d247f97c..e6d8f525 100644 --- a/app/src/main/java/com/sanaa/tudee_assistant/presentation/screen/home/component/TaskCountByStatusCard.kt +++ b/app/src/main/java/com/sanaa/tudee_assistant/presentation/screen/home/component/TaskCountByStatusCard.kt @@ -9,7 +9,6 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.offset @@ -36,7 +35,7 @@ import com.sanaa.tudee_assistant.presentation.designSystem.theme.TudeeTheme import com.sanaa.tudee_assistant.presentation.model.TaskUiStatus @Composable -fun RowScope.TaskCountByStatusCard( +fun TaskCountByStatusCard( taskUiStatus: TaskUiStatus, count: Int, modifier: Modifier = Modifier, @@ -48,7 +47,6 @@ fun RowScope.TaskCountByStatusCard( Box( modifier - .weight(1f) .clip(RoundedCornerShape(20.dp)) .clipToBounds() .background(backgroundColor) @@ -171,9 +169,9 @@ private fun Preview() { .padding(Theme.dimension.medium), horizontalArrangement = Arrangement.spacedBy(Theme.dimension.medium) ) { - TaskCountByStatusCard(TaskUiStatus.DONE, 2) - TaskCountByStatusCard(TaskUiStatus.IN_PROGRESS, 16) - TaskCountByStatusCard(TaskUiStatus.TODO, 1) + TaskCountByStatusCard(TaskUiStatus.DONE, 2, Modifier.weight(1f)) + TaskCountByStatusCard(TaskUiStatus.IN_PROGRESS, 16, Modifier.weight(1f)) + TaskCountByStatusCard(TaskUiStatus.TODO, 1, Modifier.weight(1f)) } } } \ No newline at end of file