Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Add Alarm Selection in the Add Task Bottom Sheet #659

Merged
merged 1 commit into from
Apr 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ val taskModule = module {
viewModelDefinition {
AddTaskViewModel(
addTaskUseCase = get(),
alarmIntervalMapper = get(),
applicationScope = get(),
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,34 @@ package com.escodro.task.presentation.add
import com.escodro.coroutines.AppCoroutineScope
import com.escodro.domain.model.Task
import com.escodro.domain.usecase.task.AddTask
import com.escodro.task.mapper.AlarmIntervalMapper
import com.escodro.task.model.AlarmInterval
import com.escodro.task.presentation.detail.main.CategoryId
import dev.icerock.moko.mvvm.viewmodel.ViewModel
import kotlinx.datetime.LocalDateTime

internal class AddTaskViewModel(
private val addTaskUseCase: AddTask,
private val alarmIntervalMapper: AlarmIntervalMapper,
private val applicationScope: AppCoroutineScope,
) : ViewModel() {

fun addTask(title: String, categoryId: CategoryId?) {
fun addTask(
title: String,
categoryId: CategoryId?,
dueDate: LocalDateTime?,
alarmInterval: AlarmInterval = AlarmInterval.NEVER,
) {
if (title.isBlank()) return

val interval = alarmIntervalMapper.toDomain(alarmInterval)
applicationScope.launch {
val task = Task(title = title, categoryId = categoryId?.value)
val task = Task(
title = title,
dueDate = dueDate,
categoryId = categoryId?.value,
alarmInterval = interval,
)
addTaskUseCase.invoke(task)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package com.escodro.task.presentation.add

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
Expand All @@ -21,31 +19,37 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.unit.dp
import com.escodro.alarmapi.AlarmPermission
import com.escodro.categoryapi.presentation.CategoryListViewModel
import com.escodro.categoryapi.presentation.CategoryState
import com.escodro.designsystem.components.AlkaaInputTextField
import com.escodro.resources.MR
import com.escodro.task.model.AlarmInterval
import com.escodro.task.presentation.category.CategorySelection
import com.escodro.task.presentation.detail.alarm.AlarmSelection
import com.escodro.task.presentation.detail.main.CategoryId
import dev.icerock.moko.resources.compose.stringResource
import kotlinx.coroutines.delay
import kotlinx.datetime.LocalDateTime
import org.koin.compose.koinInject

@Composable
internal fun AddTaskBottomSheet(
addTaskViewModel: AddTaskViewModel = koinInject(),
categoryViewModel: CategoryListViewModel = koinInject(),
alarmPermission: AlarmPermission = koinInject(),
onHideBottomSheet: () -> Unit,
) {
Column(
modifier = Modifier
.fillMaxWidth()
.height(256.dp)
.background(MaterialTheme.colorScheme.surface) // Accompanist does not support M3 yet
.fillMaxWidth(0.5f)
.padding(16.dp),
verticalArrangement = Arrangement.SpaceAround,
) {
var taskInputText by rememberSaveable { mutableStateOf("") }
var taskInputText: String by rememberSaveable { mutableStateOf("") }
var taskDueDate: LocalDateTime? by rememberSaveable { mutableStateOf(null) }
var alarmInterval: AlarmInterval by rememberSaveable { mutableStateOf(AlarmInterval.NEVER) }
val categoryState by remember(categoryViewModel) {
categoryViewModel
}.loadCategories().collectAsState(initial = CategoryState.Empty)
Expand All @@ -72,12 +76,28 @@ internal fun AddTaskBottomSheet(
onCategoryChange = { categoryId -> currentCategory = categoryId },
)

AlarmSelection(
calendar = taskDueDate,
interval = alarmInterval,
onAlarmUpdate = { dateTime -> taskDueDate = dateTime },
onIntervalSelect = { interval -> alarmInterval = interval },
hasExactAlarmPermission = { alarmPermission.hasExactAlarmPermission() },
openExactAlarmPermissionScreen = { alarmPermission.openExactAlarmPermissionScreen() },
openAppSettingsScreen = { alarmPermission.openAppSettings() },
)

Button(
modifier = Modifier
.padding(top = 8.dp, bottom = 16.dp)
.fillMaxWidth()
.height(48.dp),
onClick = {
addTaskViewModel.addTask(taskInputText, currentCategory)
addTaskViewModel.addTask(
title = taskInputText,
categoryId = currentCategory,
dueDate = taskDueDate,
alarmInterval = alarmInterval,
)
taskInputText = ""
onHideBottomSheet()
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package com.escodro.task.presentation.add

import com.escodro.coroutines.AppCoroutineScope
import com.escodro.task.mapper.AlarmIntervalMapper
import com.escodro.task.model.AlarmInterval
import com.escodro.task.presentation.detail.main.CategoryId
import com.escodro.task.presentation.fake.AddTaskFake
import com.escodro.test.rule.CoroutinesTestDispatcher
import com.escodro.test.rule.CoroutinesTestDispatcherImpl
import kotlinx.datetime.LocalDateTime
import org.junit.Assert
import org.junit.Before
import org.junit.Test
Expand All @@ -13,8 +16,11 @@ internal class AddTaskViewModelTest : CoroutinesTestDispatcher by CoroutinesTest

private val addTask = AddTaskFake()

private val alarmIntervalMapper = AlarmIntervalMapper()

private val viewModel = AddTaskViewModel(
addTaskUseCase = addTask,
alarmIntervalMapper = alarmIntervalMapper,
applicationScope = AppCoroutineScope(context = testDispatcher()),
)

Expand All @@ -27,17 +33,61 @@ internal class AddTaskViewModelTest : CoroutinesTestDispatcher by CoroutinesTest
fun `test if when a task is created when function is called`() {
val taskTitle = "Rendez-vous"

viewModel.addTask(taskTitle, CategoryId(null))
viewModel.addTask(
title = taskTitle,
categoryId = CategoryId(null),
dueDate = null,
alarmInterval = AlarmInterval.NEVER,
)

Assert.assertTrue(addTask.wasTaskCreated(taskTitle))
Assert.assertEquals(taskTitle, addTask.createdTask?.title)
}

@Test
fun `test if when a task is not created when title is empty`() {
val taskTitle = ""

viewModel.addTask(taskTitle, CategoryId(null))
viewModel.addTask(
title = taskTitle,
categoryId = CategoryId(value = null),
dueDate = null,
alarmInterval = AlarmInterval.MONTHLY,
)

Assert.assertTrue(addTask.createdTask == null)
}

@Test
fun `test if due date is created in the task`() {
val taskTitle = "Voulez-vous?"
val dueDate = LocalDateTime(2022, 1, 1, 12, 0)

viewModel.addTask(
title = taskTitle,
categoryId = CategoryId(null),
dueDate = dueDate,
alarmInterval = AlarmInterval.NEVER,
)

Assert.assertEquals(taskTitle, addTask.createdTask?.title)
Assert.assertEquals(dueDate, addTask.createdTask?.dueDate)
}

@Test
fun `test if alarm interval is created in the task`() {
val taskTitle = "Coucher avec moi?"
val alarmInterval = AlarmInterval.WEEKLY

viewModel.addTask(
title = taskTitle,
categoryId = CategoryId(null),
dueDate = null,
alarmInterval = alarmInterval,
)

val assertAlarmInterval = alarmIntervalMapper.toDomain(alarmInterval)

Assert.assertFalse(addTask.wasTaskCreated(taskTitle))
Assert.assertEquals(taskTitle, addTask.createdTask?.title)
Assert.assertEquals(assertAlarmInterval, addTask.createdTask?.alarmInterval)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,13 @@ import com.escodro.domain.usecase.task.AddTask

internal class AddTaskFake : AddTask {

private val updatedList: MutableList<Task> = mutableListOf()
var createdTask: Task? = null

override suspend fun invoke(task: Task) {
updatedList.add(task)
createdTask = task
}

fun clear() =
updatedList.clear()

fun wasTaskCreated(title: String): Boolean =
updatedList.any { it.title == title }
fun clear() {
createdTask = null
}
}