diff --git a/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/di/TaskListModule.kt b/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/di/TaskListModule.kt index ae1ba863..b42539c4 100644 --- a/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/di/TaskListModule.kt +++ b/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/di/TaskListModule.kt @@ -9,6 +9,8 @@ import br.com.sailboat.todozy.feature.task.list.impl.data.repository.TaskReposit import br.com.sailboat.todozy.feature.task.list.impl.domain.usecase.CompleteTaskUseCase import br.com.sailboat.todozy.feature.task.list.impl.domain.usecase.CompleteTaskUseCaseImpl import br.com.sailboat.todozy.feature.task.list.impl.domain.usecase.GetTasksUseCaseImpl +import br.com.sailboat.todozy.feature.task.list.impl.domain.usecase.MarkTaskDoneForTodayUseCase +import br.com.sailboat.todozy.feature.task.list.impl.domain.usecase.MarkTaskDoneForTodayUseCaseImpl import br.com.sailboat.todozy.feature.task.list.impl.presentation.factory.TaskListUiModelFactory import br.com.sailboat.todozy.feature.task.list.impl.presentation.mapper.TaskCategoryToStringMapper import br.com.sailboat.todozy.feature.task.list.impl.presentation.mapper.TaskToTaskUiModelMapper @@ -33,6 +35,7 @@ private val presentation = getTaskMetricsUseCase = get(), getTaskProgressUseCase = get(), completeTaskUseCase = get(), + markTaskDoneForTodayUseCase = get(), taskListUiModelFactory = get(), logService = get(), ) @@ -43,6 +46,7 @@ private val domain = module { factory { GetTasksUseCaseImpl(get()) } factory { CompleteTaskUseCaseImpl(get(), get(), get(), get(), get()) } + factory { MarkTaskDoneForTodayUseCaseImpl(get(), get()) } } private val data = diff --git a/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/domain/usecase/MarkTaskDoneForTodayUseCase.kt b/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/domain/usecase/MarkTaskDoneForTodayUseCase.kt new file mode 100644 index 00000000..cfb9e6cb --- /dev/null +++ b/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/domain/usecase/MarkTaskDoneForTodayUseCase.kt @@ -0,0 +1,5 @@ +package br.com.sailboat.todozy.feature.task.list.impl.domain.usecase + +internal interface MarkTaskDoneForTodayUseCase { + suspend operator fun invoke(taskId: Long): Result +} diff --git a/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/domain/usecase/MarkTaskDoneForTodayUseCaseImpl.kt b/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/domain/usecase/MarkTaskDoneForTodayUseCaseImpl.kt new file mode 100644 index 00000000..aedc4903 --- /dev/null +++ b/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/domain/usecase/MarkTaskDoneForTodayUseCaseImpl.kt @@ -0,0 +1,22 @@ +package br.com.sailboat.todozy.feature.task.list.impl.domain.usecase + +import br.com.sailboat.todozy.domain.model.TaskStatus +import br.com.sailboat.todozy.feature.task.details.domain.usecase.GetTaskUseCase +import br.com.sailboat.todozy.feature.task.history.domain.usecase.AddHistoryUseCase + +internal class MarkTaskDoneForTodayUseCaseImpl( + private val getTaskUseCase: GetTaskUseCase, + private val addHistoryUseCase: AddHistoryUseCase, +) : MarkTaskDoneForTodayUseCase { + override suspend operator fun invoke(taskId: Long): Result { + val task = getTaskUseCase(taskId).getOrElse { throwable -> + return Result.failure(throwable) + } + + addHistoryUseCase(task, TaskStatus.DONE).getOrElse { throwable -> + return Result.failure(throwable) + } + + return Result.success(Unit) + } +} diff --git a/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/TaskListFragment.kt b/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/TaskListFragment.kt index 98e4eb43..868aea22 100644 --- a/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/TaskListFragment.kt +++ b/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/TaskListFragment.kt @@ -102,6 +102,10 @@ internal class TaskListFragment : Fragment() { ), ) }, + onMarkDoneForToday = { taskId -> + haptics.performHapticFeedback(HapticFeedbackType.LongPress) + viewModel.dispatchViewIntent(TaskListViewIntent.OnMarkDoneForToday(taskId)) + }, onNewTask = { haptics.performHapticFeedback(HapticFeedbackType.LongPress) viewModel.dispatchViewIntent(TaskListViewIntent.OnClickNewTask) diff --git a/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/TaskListScreen.kt b/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/TaskListScreen.kt index 085c2e85..7698607a 100644 --- a/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/TaskListScreen.kt +++ b/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/TaskListScreen.kt @@ -29,6 +29,8 @@ import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.DismissDirection import androidx.compose.material.DismissValue +import androidx.compose.material.DropdownMenu +import androidx.compose.material.DropdownMenuItem import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.ExtendedFloatingActionButton import androidx.compose.material.Icon @@ -102,6 +104,7 @@ internal fun TaskListScreen( onTaskClick: (Long) -> Unit, onTaskSwipe: (taskId: Long, TaskStatus) -> Unit, onTaskUndo: (Long, TaskStatus) -> Unit, + onMarkDoneForToday: (Long) -> Unit, onNewTask: () -> Unit, onSearch: (String) -> Unit, ) { @@ -112,6 +115,7 @@ internal fun TaskListScreen( val metricsToShow = if (tasksLoading.isTrue()) null else taskMetrics val listState = rememberLazyListState() var pendingScrollToTop by rememberSaveable { mutableStateOf(false) } + var contextMenuTaskId by rememberSaveable { mutableStateOf(null) } LaunchedEffect(tasksLoading, taskProgressLoading) { if (tasksLoading || taskProgressLoading) { @@ -191,14 +195,27 @@ internal fun TaskListScreen( is TaskUiModel -> SwipeableTaskItem( task = item, - onClick = onTaskClick, + onClick = { taskId -> + contextMenuTaskId = null + onTaskClick(taskId) + }, onUndoClick = onTaskUndo, onSwipe = { status -> + contextMenuTaskId = null onTaskSwipe( item.taskId, status, ) }, + isContextMenuVisible = contextMenuTaskId == item.taskId, + onOpenContextMenu = { taskId -> + contextMenuTaskId = taskId + }, + onDismissContextMenu = { contextMenuTaskId = null }, + onMarkDoneForToday = { taskId -> + contextMenuTaskId = null + onMarkDoneForToday(taskId) + }, modifier = Modifier.animateItem(), ) @@ -341,6 +358,10 @@ private fun SwipeableTaskItem( onClick: (Long) -> Unit, onUndoClick: (Long, TaskStatus) -> Unit, onSwipe: (TaskStatus) -> Unit, + isContextMenuVisible: Boolean, + onOpenContextMenu: (Long) -> Unit, + onDismissContextMenu: () -> Unit, + onMarkDoneForToday: (Long) -> Unit, modifier: Modifier = Modifier, ) { val spacing = LocalTodozySpacing.current @@ -355,6 +376,10 @@ private fun SwipeableTaskItem( onSwipe = onSwipe, onClick = onClick, onUndoClick = onUndoClick, + isContextMenuVisible = isContextMenuVisible, + onOpenContextMenu = onOpenContextMenu, + onDismissContextMenu = onDismissContextMenu, + onMarkDoneForToday = onMarkDoneForToday, enableSwipe = task.showInlineMetrics.not(), ) } @@ -401,6 +426,10 @@ private fun AnchoredSwipeToDismiss( onSwipe: (TaskStatus) -> Unit, onClick: (Long) -> Unit, onUndoClick: (Long, TaskStatus) -> Unit, + isContextMenuVisible: Boolean, + onOpenContextMenu: (Long) -> Unit, + onDismissContextMenu: () -> Unit, + onMarkDoneForToday: (Long) -> Unit, enableSwipe: Boolean, ) { var widthPx by remember { mutableFloatStateOf(0f) } @@ -511,6 +540,7 @@ private fun AnchoredSwipeToDismiss( task = task, onClick = onClick, onUndoClick = onUndoClick, + onLongClick = { onOpenContextMenu(task.taskId) }, modifier = Modifier .fillMaxWidth() @@ -522,5 +552,16 @@ private fun AnchoredSwipeToDismiss( flingBehavior = flingBehavior, ), ) + + DropdownMenu( + expanded = isContextMenuVisible, + onDismissRequest = onDismissContextMenu, + ) { + DropdownMenuItem( + onClick = { onMarkDoneForToday(task.taskId) }, + ) { + Text(text = stringResource(id = R.string.task_menu_mark_done_for_today)) + } + } } } diff --git a/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/viewmodel/TaskListViewIntent.kt b/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/viewmodel/TaskListViewIntent.kt index 6c946f28..ec314a93 100644 --- a/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/viewmodel/TaskListViewIntent.kt +++ b/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/viewmodel/TaskListViewIntent.kt @@ -11,6 +11,7 @@ internal sealed class TaskListViewIntent { object OnClickMenuHistory : TaskListViewIntent() object OnClickNewTask : TaskListViewIntent() data class OnClickTask(val taskId: Long) : TaskListViewIntent() + data class OnMarkDoneForToday(val taskId: Long) : TaskListViewIntent() data class OnSubmitSearchTerm(val term: String) : TaskListViewIntent() data class OnSwipeTask(val taskId: Long, val status: TaskStatus) : TaskListViewIntent() data class OnClickUndoTask(val taskId: Long, val status: TaskStatus) : TaskListViewIntent() diff --git a/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/viewmodel/TaskListViewModel.kt b/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/viewmodel/TaskListViewModel.kt index 34d09438..1136e619 100644 --- a/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/viewmodel/TaskListViewModel.kt +++ b/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/viewmodel/TaskListViewModel.kt @@ -15,6 +15,7 @@ import br.com.sailboat.todozy.feature.task.history.domain.model.TaskProgressFilt import br.com.sailboat.todozy.feature.task.history.domain.usecase.GetTaskProgressUseCase import br.com.sailboat.todozy.feature.task.list.domain.usecase.GetTasksUseCase import br.com.sailboat.todozy.feature.task.list.impl.domain.usecase.CompleteTaskUseCase +import br.com.sailboat.todozy.feature.task.list.impl.domain.usecase.MarkTaskDoneForTodayUseCase import br.com.sailboat.todozy.feature.task.list.impl.presentation.factory.TaskListUiModelFactory import br.com.sailboat.todozy.feature.task.list.impl.presentation.viewmodel.TaskListViewIntent.OnClickMenuAbout import br.com.sailboat.todozy.feature.task.list.impl.presentation.viewmodel.TaskListViewIntent.OnClickMenuHistory @@ -22,6 +23,7 @@ import br.com.sailboat.todozy.feature.task.list.impl.presentation.viewmodel.Task import br.com.sailboat.todozy.feature.task.list.impl.presentation.viewmodel.TaskListViewIntent.OnClickNewTask import br.com.sailboat.todozy.feature.task.list.impl.presentation.viewmodel.TaskListViewIntent.OnClickTask import br.com.sailboat.todozy.feature.task.list.impl.presentation.viewmodel.TaskListViewIntent.OnClickUndoTask +import br.com.sailboat.todozy.feature.task.list.impl.presentation.viewmodel.TaskListViewIntent.OnMarkDoneForToday import br.com.sailboat.todozy.feature.task.list.impl.presentation.viewmodel.TaskListViewIntent.OnSelectProgressRange import br.com.sailboat.todozy.feature.task.list.impl.presentation.viewmodel.TaskListViewIntent.OnStart import br.com.sailboat.todozy.feature.task.list.impl.presentation.viewmodel.TaskListViewIntent.OnSubmitSearchTerm @@ -55,6 +57,7 @@ internal class TaskListViewModel( private val getTaskMetricsUseCase: GetTaskMetricsUseCase, private val getTaskProgressUseCase: GetTaskProgressUseCase, private val completeTaskUseCase: CompleteTaskUseCase, + private val markTaskDoneForTodayUseCase: MarkTaskDoneForTodayUseCase, private val taskListUiModelFactory: TaskListUiModelFactory, private val logService: LogService, private val dispatcherProvider: DispatcherProvider = DefaultDispatcherProvider, @@ -89,6 +92,7 @@ internal class TaskListViewModel( is OnClickMenuHistory -> onClickMenuHistory() is OnClickNewTask -> onClickNewTask() is OnClickTask -> onClickTask(viewIntent.taskId) + is OnMarkDoneForToday -> onMarkDoneForToday(viewIntent.taskId) is OnSubmitSearchTerm -> onSubmitSearchTerm(viewIntent.term) is OnClickUndoTask -> onClickUndoTask(viewIntent.taskId) is OnSwipeTask -> onSwipeTask(viewIntent.taskId, viewIntent.status) @@ -158,6 +162,19 @@ internal class TaskListViewModel( viewState.viewAction.tryEmit(TaskListViewAction.NavigateToTaskDetails(taskId = taskId)) } + private fun onMarkDoneForToday(taskId: Long) = viewModelScope.launch { + try { + withContext(dispatcherProvider.io()) { + markTaskDoneForTodayUseCase(taskId).getOrThrow() + } + updateTodayProgress(TaskStatus.DONE) + loadTaskMetrics() + } catch (e: Exception) { + logService.error(e) + viewState.viewAction.tryEmit(TaskListViewAction.ShowErrorCompletingTask) + } + } + private fun onSubmitSearchTerm(term: String) { searchJob?.cancel() searchJob = viewModelScope.launch { diff --git a/feature/task-list/impl/src/main/res/values/strings.xml b/feature/task-list/impl/src/main/res/values/strings.xml index 82b1b177..e2b6374e 100644 --- a/feature/task-list/impl/src/main/res/values/strings.xml +++ b/feature/task-list/impl/src/main/res/values/strings.xml @@ -1,4 +1,5 @@ New Task + Mark done for today diff --git a/feature/task-list/impl/src/test/java/br/com/sailboat/todozy/feature/task/list/impl/domain/usecase/MarkTaskDoneForTodayUseCaseImplTest.kt b/feature/task-list/impl/src/test/java/br/com/sailboat/todozy/feature/task/list/impl/domain/usecase/MarkTaskDoneForTodayUseCaseImplTest.kt new file mode 100644 index 00000000..feca76aa --- /dev/null +++ b/feature/task-list/impl/src/test/java/br/com/sailboat/todozy/feature/task/list/impl/domain/usecase/MarkTaskDoneForTodayUseCaseImplTest.kt @@ -0,0 +1,43 @@ +package br.com.sailboat.todozy.feature.task.list.impl.domain.usecase + +import br.com.sailboat.todozy.domain.model.Task +import br.com.sailboat.todozy.domain.model.TaskStatus +import br.com.sailboat.todozy.feature.task.details.domain.usecase.GetTaskUseCase +import br.com.sailboat.todozy.feature.task.history.domain.usecase.AddHistoryUseCase +import io.mockk.coEvery +import io.mockk.coVerify +import io.mockk.confirmVerified +import io.mockk.mockk +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertTrue + +internal class MarkTaskDoneForTodayUseCaseImplTest { + private val getTaskUseCase: GetTaskUseCase = mockk(relaxed = true) + private val addHistoryUseCase: AddHistoryUseCase = mockk(relaxed = true) + + private val markTaskDoneForTodayUseCase = + MarkTaskDoneForTodayUseCaseImpl( + getTaskUseCase = getTaskUseCase, + addHistoryUseCase = addHistoryUseCase, + ) + + @Test + fun `should add history for task id`() = runBlocking { + val task = + Task( + id = 42L, + name = "Task Name", + notes = "Some notes", + ) + coEvery { getTaskUseCase(task.id) } returns Result.success(task) + coEvery { addHistoryUseCase(task, TaskStatus.DONE) } returns Result.success(Unit) + + val result = markTaskDoneForTodayUseCase(task.id) + + assertTrue(result.isSuccess) + coVerify { getTaskUseCase(task.id) } + coVerify { addHistoryUseCase(task, TaskStatus.DONE) } + confirmVerified(getTaskUseCase, addHistoryUseCase) + } +} diff --git a/feature/task-list/impl/src/test/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/viewmodel/TaskListViewModelTest.kt b/feature/task-list/impl/src/test/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/viewmodel/TaskListViewModelTest.kt index b877710e..cc65c5ba 100644 --- a/feature/task-list/impl/src/test/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/viewmodel/TaskListViewModelTest.kt +++ b/feature/task-list/impl/src/test/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/viewmodel/TaskListViewModelTest.kt @@ -14,6 +14,7 @@ import br.com.sailboat.todozy.feature.task.history.domain.model.TaskProgressFilt import br.com.sailboat.todozy.feature.task.history.domain.usecase.GetTaskProgressUseCase import br.com.sailboat.todozy.feature.task.list.domain.usecase.GetTasksUseCase import br.com.sailboat.todozy.feature.task.list.impl.domain.usecase.CompleteTaskUseCase +import br.com.sailboat.todozy.feature.task.list.impl.domain.usecase.MarkTaskDoneForTodayUseCase import br.com.sailboat.todozy.feature.task.list.impl.presentation.factory.TaskListUiModelFactory import br.com.sailboat.todozy.utility.kotlin.LogService import br.com.sailboat.todozy.utility.kotlin.model.Entity @@ -52,6 +53,7 @@ internal class TaskListViewModelTest { private val getTaskMetricsUseCase: GetTaskMetricsUseCase = mockk(relaxed = true) private val getTaskProgressUseCase: GetTaskProgressUseCase = mockk(relaxed = true) private val completeTaskUseCase: CompleteTaskUseCase = mockk(relaxed = true) + private val markTaskDoneForTodayUseCase: MarkTaskDoneForTodayUseCase = mockk(relaxed = true) private val taskListUiModelFactory: TaskListUiModelFactory = mockk(relaxed = true) private val logService: LogService = mockk(relaxed = true) @@ -66,6 +68,7 @@ internal class TaskListViewModelTest { getTaskMetricsUseCase = getTaskMetricsUseCase, getTaskProgressUseCase = getTaskProgressUseCase, completeTaskUseCase = completeTaskUseCase, + markTaskDoneForTodayUseCase = markTaskDoneForTodayUseCase, taskListUiModelFactory = taskListUiModelFactory, logService = logService, dispatcherProvider = coroutinesTestRule.dispatcherProvider, @@ -203,6 +206,17 @@ internal class TaskListViewModelTest { assertEquals(TaskListViewAction.NavigateToHistory, latestAction()) } + @Test + fun `should call markTaskDoneForTodayUseCase when dispatchViewAction is called with OnMarkDoneForToday`() = + runTest(coroutinesTestRule.dispatcher) { + prepareScenario() + + viewModel.dispatchViewIntent(TaskListViewIntent.OnMarkDoneForToday(taskId = 42L)) + advanceUntilIdle() + + coVerify { markTaskDoneForTodayUseCase(42L) } + } + @Test fun `should navigate to settings screen when dispatchViewAction is called with OnClickMenuSettings`() { prepareScenario() @@ -585,6 +599,7 @@ internal class TaskListViewModelTest { coEvery { getTaskProgressUseCase(any()) } returns Result.success(emptyList()) coEvery { getTaskMetricsUseCase(any()) } returns Result.success(TaskMetrics(0, 0, 0)) coEvery { completeTaskUseCase(any(), any()) } returns Result.success(Unit) + coEvery { markTaskDoneForTodayUseCase(any()) } returns Result.success(Unit) } private fun latestAction(): TaskListViewAction? = viewModel.viewState.viewAction.replayCache.lastOrNull() diff --git a/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/task/TaskItem.kt b/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/task/TaskItem.kt index e30c461d..ff4824e5 100644 --- a/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/task/TaskItem.kt +++ b/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/task/TaskItem.kt @@ -9,7 +9,11 @@ import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.animation.shrinkVertically import androidx.compose.animation.togetherWith +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.LocalIndication import androidx.compose.foundation.background +import androidx.compose.foundation.combinedClickable +import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row @@ -24,7 +28,6 @@ import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.ZeroCornerSize import androidx.compose.material.ButtonDefaults import androidx.compose.material.Card -import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.Icon import androidx.compose.material.MaterialTheme import androidx.compose.material.Surface @@ -64,16 +67,18 @@ private val TASK_ROW_MIN_HEIGHT = 64.dp @Composable @Suppress("FunctionName") -@OptIn(ExperimentalMaterialApi::class) +@OptIn(ExperimentalFoundationApi::class) fun TaskItem( task: TaskUiModel, modifier: Modifier = Modifier, onClick: (Long) -> Unit, + onLongClick: (Long) -> Unit = {}, onUndoClick: (Long, TaskStatus) -> Unit, ) { val context = LocalContext.current val spacing = LocalTodozySpacing.current val inlineElevation = 6.dp + val interactionSource = remember { MutableInteractionSource() } val contentPadding = PaddingValues( start = spacing.medium, @@ -87,13 +92,19 @@ fun TaskItem( val alarmColor = task.alarmColor?.let { Color(it) } ?: MaterialTheme.colors.primary Card( - modifier = modifier - .fillMaxWidth() - .animateContentSize(), + modifier = + modifier + .fillMaxWidth() + .animateContentSize() + .combinedClickable( + interactionSource = interactionSource, + indication = LocalIndication.current, + onClick = { onClick(task.taskId) }, + onLongClick = { onLongClick(task.taskId) }, + ), backgroundColor = MaterialTheme.colors.surface, elevation = if (task.showInlineMetrics) inlineElevation else 0.dp, shape = MaterialTheme.shapes.medium.copy(all = ZeroCornerSize), - onClick = { onClick(task.taskId) }, ) { AnimatedContent( targetState = task.showInlineMetrics,