lab4
This commit is contained in:
parent
c5b54562d3
commit
7ed6a7e9b9
Binary file not shown.
BIN
.vs/Lection5/v17/.wsuo
Normal file
BIN
.vs/Lection5/v17/.wsuo
Normal file
Binary file not shown.
3
.vs/ProjectSettings.json
Normal file
3
.vs/ProjectSettings.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"CurrentProjectSetting": null
|
||||||
|
}
|
6
.vs/VSWorkspaceState.json
Normal file
6
.vs/VSWorkspaceState.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"ExpandedNodes": [
|
||||||
|
""
|
||||||
|
],
|
||||||
|
"PreviewInSolutionExplorer": false
|
||||||
|
}
|
BIN
.vs/slnx.sqlite
Normal file
BIN
.vs/slnx.sqlite
Normal file
Binary file not shown.
@ -67,6 +67,7 @@ dependencies {
|
|||||||
implementation("androidx.compose.ui:ui-tooling-preview")
|
implementation("androidx.compose.ui:ui-tooling-preview")
|
||||||
implementation("androidx.compose.material3:material3:1.1.2")
|
implementation("androidx.compose.material3:material3:1.1.2")
|
||||||
implementation("com.google.firebase:protolite-well-known-types:18.0.0")
|
implementation("com.google.firebase:protolite-well-known-types:18.0.0")
|
||||||
|
implementation("com.google.android.material:material:1.10.0")
|
||||||
|
|
||||||
// Room
|
// Room
|
||||||
val room_version = "2.5.2"
|
val room_version = "2.5.2"
|
||||||
|
@ -1,14 +1,25 @@
|
|||||||
package ru.ulstu.`is`.pmu.ui.task.edit
|
package ru.ulstu.`is`.pmu.ui.task.edit
|
||||||
|
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
|
import android.view.ContextThemeWrapper
|
||||||
|
import android.widget.CalendarView
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.defaultMinSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.wrapContentSize
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.text.KeyboardActions
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.DatePickerDefaults
|
||||||
|
import androidx.compose.material3.DatePickerFormatter
|
||||||
|
import androidx.compose.material3.DatePickerState
|
||||||
import androidx.compose.material3.DropdownMenu
|
import androidx.compose.material3.DropdownMenu
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
@ -18,6 +29,7 @@ import androidx.compose.material3.MaterialTheme
|
|||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.TextField
|
import androidx.compose.material3.TextField
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@ -25,14 +37,19 @@ import androidx.compose.runtime.mutableStateOf
|
|||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
|
import androidx.compose.ui.window.Dialog
|
||||||
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
|
import com.google.android.material.datepicker.MaterialDatePicker
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import ru.ulstu.`is`.pmu.R
|
import ru.ulstu.`is`.pmu.R
|
||||||
@ -40,6 +57,11 @@ import ru.ulstu.`is`.pmu.database.task.model.User
|
|||||||
import ru.ulstu.`is`.pmu.database.task.model.Task
|
import ru.ulstu.`is`.pmu.database.task.model.Task
|
||||||
import ru.ulstu.`is`.pmu.ui.AppViewModelProvider
|
import ru.ulstu.`is`.pmu.ui.AppViewModelProvider
|
||||||
import ru.ulstu.`is`.pmu.ui.theme.PmudemoTheme
|
import ru.ulstu.`is`.pmu.ui.theme.PmudemoTheme
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.time.LocalDate
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
import java.util.Calendar
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TaskEdit(
|
fun TaskEdit(
|
||||||
@ -113,7 +135,6 @@ private fun UserDropDown(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun TaskEdit(
|
private fun TaskEdit(
|
||||||
@ -144,13 +165,33 @@ private fun TaskEdit(
|
|||||||
label = { Text(stringResource(id = R.string.task_lastname)) },
|
label = { Text(stringResource(id = R.string.task_lastname)) },
|
||||||
singleLine = true
|
singleLine = true
|
||||||
)
|
)
|
||||||
|
var showDatePicker by remember { mutableStateOf(false) }
|
||||||
|
if (showDatePicker) {
|
||||||
|
DatePicker(
|
||||||
|
onDateSelected = { selectedDate ->
|
||||||
|
onUpdate(taskUiState.taskDetails.copy(endDate = SimpleDateFormat("dd.MM.yyyy").format(selectedDate)))
|
||||||
|
showDatePicker = false
|
||||||
|
},
|
||||||
|
onDismissRequest = {
|
||||||
|
showDatePicker = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Button(
|
||||||
|
onClick = { showDatePicker = true },
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Text("Выбрать дату окончания")
|
||||||
|
}
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
value = taskUiState.taskDetails.endDate,
|
value = taskUiState.taskDetails.endDate,
|
||||||
onValueChange = { newDate -> onUpdate(taskUiState.taskDetails.copy(endDate = newDate)) },
|
onValueChange = { newDate -> onUpdate(taskUiState.taskDetails.copy(endDate = newDate)) },
|
||||||
label = { Text(stringResource(id = R.string.task_endDate)) },
|
label = { Text(stringResource(id = R.string.task_endDate)) },
|
||||||
singleLine = true,
|
singleLine = true,
|
||||||
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number)
|
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number),
|
||||||
|
enabled = false
|
||||||
)
|
)
|
||||||
// UserDropDown(
|
// UserDropDown(
|
||||||
// userUiState = userUiState,
|
// userUiState = userUiState,
|
||||||
@ -169,7 +210,6 @@ private fun TaskEdit(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
enabled = taskUiState.isEntryValid,
|
enabled = taskUiState.isEntryValid,
|
||||||
shape = MaterialTheme.shapes.small,
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
) {
|
) {
|
||||||
Text(text = stringResource(R.string.task_save_button))
|
Text(text = stringResource(R.string.task_save_button))
|
||||||
@ -183,7 +223,7 @@ private fun TaskEdit(
|
|||||||
text = { Text("Введите дату по шаблону: 01.12.2023") },
|
text = { Text("Введите дату по шаблону: 01.12.2023") },
|
||||||
confirmButton = {
|
confirmButton = {
|
||||||
Button(onClick = { showInvalidDateDialog = false }) {
|
Button(onClick = { showInvalidDateDialog = false }) {
|
||||||
Text("ОК")
|
Text("Подтвердить")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -195,22 +235,99 @@ fun isValidDate(date: String): Boolean {
|
|||||||
return regex.matches(date)
|
return regex.matches(date)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
|
|
||||||
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TaskEditPreview() {
|
fun CustomCalendarView(onDateSelected: (Date) -> Unit) {
|
||||||
PmudemoTheme {
|
AndroidView(
|
||||||
Surface(
|
modifier = Modifier.wrapContentSize(),
|
||||||
color = MaterialTheme.colorScheme.background
|
factory = { context ->
|
||||||
) {
|
// Используем стандартный контекст, без применения кастомной темы
|
||||||
TaskEdit(
|
CalendarView(context).apply {
|
||||||
taskUiState = Task.getTask().toUiState(true),
|
// Настройки CalendarView
|
||||||
userUiState = User.DEMO_User.toUiState(),
|
val calendar = Calendar.getInstance()
|
||||||
usersListUiState = UsersListUiState(listOf()),
|
|
||||||
onClick = {},
|
setOnDateChangeListener { _, year, month, dayOfMonth ->
|
||||||
onUpdate = {},
|
calendar.set(Calendar.YEAR, year)
|
||||||
onUserUpdate = {}
|
calendar.set(Calendar.MONTH, month)
|
||||||
|
calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth)
|
||||||
|
onDateSelected(calendar.time)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun DatePicker(onDateSelected: (Date) -> Unit, onDismissRequest: () -> Unit) {
|
||||||
|
val selDate = remember { mutableStateOf(Date()) }
|
||||||
|
|
||||||
|
//todo - add strings to resource after POC
|
||||||
|
Dialog(onDismissRequest = { onDismissRequest() }, properties = DialogProperties()) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.wrapContentSize()
|
||||||
|
.background(
|
||||||
|
color = Color.White
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
Modifier
|
||||||
|
.defaultMinSize(minHeight = 72.dp)
|
||||||
|
.fillMaxWidth()
|
||||||
|
.background(
|
||||||
|
color = MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
.padding(16.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "Выберите дату"
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.size(24.dp))
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = SimpleDateFormat("MMM d, yyyy", Locale.getDefault()).format(selDate.value)
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.size(16.dp))
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomCalendarView(onDateSelected = {
|
||||||
|
selDate.value = it
|
||||||
|
})
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.size(8.dp))
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.align(Alignment.End)
|
||||||
|
.padding(bottom = 16.dp, end = 16.dp)
|
||||||
|
.background(
|
||||||
|
color = Color.White,
|
||||||
|
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
TextButton(
|
||||||
|
onClick = onDismissRequest
|
||||||
|
) {
|
||||||
|
//TODO - hardcode string
|
||||||
|
Text(
|
||||||
|
text = "Отмена"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
onDateSelected(selDate.value)
|
||||||
|
onDismissRequest()
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
//TODO - hardcode string
|
||||||
|
Text(
|
||||||
|
text = "Подтвердить"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -73,7 +73,7 @@ fun TaskList(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
) { innerPadding ->
|
) { innerPadding ->
|
||||||
TaskListContent( // Изменил имя, чтобы избежать путаницы с функцией TaskList
|
TaskListContent(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(innerPadding)
|
.padding(innerPadding)
|
||||||
.fillMaxSize(),
|
.fillMaxSize(),
|
||||||
@ -82,12 +82,12 @@ fun TaskList(
|
|||||||
val route = Screen.TaskEdit.route.replace("{id}", uid.toString())
|
val route = Screen.TaskEdit.route.replace("{id}", uid.toString())
|
||||||
navController.navigate(route)
|
navController.navigate(route)
|
||||||
},
|
},
|
||||||
onSwipeLeft = { task: Task -> // Обработка свайпа влево (удаление)
|
onSwipeLeft = { task: Task ->
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
viewModel.deleteTask(task)
|
viewModel.deleteTask(task)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onSwipeRight = { task: Task -> // Обработка свайпа вправо (добавить в избранное)
|
onSwipeRight = { task: Task ->
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
viewModel.favoriteTask(task)
|
viewModel.favoriteTask(task)
|
||||||
}
|
}
|
||||||
@ -102,20 +102,20 @@ private fun SwipeToAction(
|
|||||||
dismissState: DismissState,
|
dismissState: DismissState,
|
||||||
task: Task,
|
task: Task,
|
||||||
onClick: (uid: Int) -> Unit,
|
onClick: (uid: Int) -> Unit,
|
||||||
onSwipeRight: (task: Task) -> Unit, // Для добавления в избранное
|
onSwipeRight: (task: Task) -> Unit,
|
||||||
onSwipeLeft: (task: Task) -> Unit // Для удаления
|
onSwipeLeft: (task: Task) -> Unit
|
||||||
) {
|
) {
|
||||||
SwipeToDismiss(
|
SwipeToDismiss(
|
||||||
state = dismissState,
|
state = dismissState,
|
||||||
directions = setOf(
|
directions = setOf(
|
||||||
DismissDirection.EndToStart, // Для удаления
|
DismissDirection.EndToStart,
|
||||||
DismissDirection.StartToEnd // Для добавления в избранное
|
DismissDirection.StartToEnd
|
||||||
),
|
),
|
||||||
background = {
|
background = {
|
||||||
val backgroundColor by animateColorAsState(
|
val backgroundColor by animateColorAsState(
|
||||||
when (dismissState.targetValue) {
|
when (dismissState.targetValue) {
|
||||||
DismissedToStart -> Color.Red.copy(alpha = 0.8f)
|
DismissedToStart -> Color.Red.copy(alpha = 0.8f)
|
||||||
DismissValue.DismissedToEnd -> Color.Green.copy(alpha = 0.8f) // Цвет фона для избранного
|
DismissValue.DismissedToEnd -> Color.Green.copy(alpha = 0.8f)
|
||||||
else -> Color.White
|
else -> Color.White
|
||||||
}, label = ""
|
}, label = ""
|
||||||
)
|
)
|
||||||
@ -132,7 +132,7 @@ private fun SwipeToAction(
|
|||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
modifier = Modifier.scale(iconScale),
|
modifier = Modifier.scale(iconScale),
|
||||||
imageVector = if (dismissState.targetValue == DismissedToStart) Icons.Outlined.Delete else Icons.Default.Star, // Иконка для избранного
|
imageVector = if (dismissState.targetValue == DismissedToStart) Icons.Outlined.Delete else Icons.Default.Star,
|
||||||
contentDescription = if (dismissState.targetValue == DismissedToStart) "Delete" else "Favorite",
|
contentDescription = if (dismissState.targetValue == DismissedToStart) "Delete" else "Favorite",
|
||||||
tint = Color.White
|
tint = Color.White
|
||||||
)
|
)
|
||||||
@ -160,7 +160,6 @@ private fun TaskListContent(
|
|||||||
modifier = modifier
|
modifier = modifier
|
||||||
) {
|
) {
|
||||||
if (taskList.isEmpty()) {
|
if (taskList.isEmpty()) {
|
||||||
// Код для отображения пустого списка
|
|
||||||
} else {
|
} else {
|
||||||
LazyColumn(modifier = Modifier.padding(all = 10.dp)) {
|
LazyColumn(modifier = Modifier.padding(all = 10.dp)) {
|
||||||
items(items = taskList, key = { it.uid }) { task ->
|
items(items = taskList, key = { it.uid }) { task ->
|
||||||
@ -169,14 +168,14 @@ private fun TaskListContent(
|
|||||||
if (dismissState.isDismissed(DismissDirection.EndToStart)) {
|
if (dismissState.isDismissed(DismissDirection.EndToStart)) {
|
||||||
LaunchedEffect(task) {
|
LaunchedEffect(task) {
|
||||||
onSwipeLeft(task)
|
onSwipeLeft(task)
|
||||||
dismissState.reset() // Сбросить состояние после обработки свайпа
|
dismissState.reset()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dismissState.isDismissed(DismissDirection.StartToEnd)) {
|
if (dismissState.isDismissed(DismissDirection.StartToEnd)) {
|
||||||
LaunchedEffect(task) {
|
LaunchedEffect(task) {
|
||||||
onSwipeRight(task)
|
onSwipeRight(task)
|
||||||
dismissState.reset() // Сбросить состояние после обработки свайпа
|
dismissState.reset()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ class TaskListViewModel(
|
|||||||
val taskListFavoriteUiState: StateFlow<TaskListUiState> = taskRepository.getAllTasks()
|
val taskListFavoriteUiState: StateFlow<TaskListUiState> = taskRepository.getAllTasks()
|
||||||
.map { tasks ->
|
.map { tasks ->
|
||||||
val filteredTasks = tasks.filter { task ->
|
val filteredTasks = tasks.filter { task ->
|
||||||
task.favorite // Оставить только задачи, у которых favorite = true
|
task.favorite
|
||||||
}
|
}
|
||||||
val sortedTasks = filteredTasks.sortedByDescending { task ->
|
val sortedTasks = filteredTasks.sortedByDescending { task ->
|
||||||
parseDate(task.endDate)?.timeInMillis ?: Long.MIN_VALUE
|
parseDate(task.endDate)?.timeInMillis ?: Long.MIN_VALUE
|
||||||
@ -26,7 +26,6 @@ class TaskListViewModel(
|
|||||||
}
|
}
|
||||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(AppDataContainer.TIMEOUT), TaskListUiState())
|
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(AppDataContainer.TIMEOUT), TaskListUiState())
|
||||||
|
|
||||||
// Для TaskListEndDate (Сортировка по endDate)
|
|
||||||
val taskListEndDateUiState: StateFlow<TaskListUiState> = taskRepository.getAllTasks()
|
val taskListEndDateUiState: StateFlow<TaskListUiState> = taskRepository.getAllTasks()
|
||||||
.map { tasks ->
|
.map { tasks ->
|
||||||
val sortedTasks = tasks.sortedByDescending { task ->
|
val sortedTasks = tasks.sortedByDescending { task ->
|
||||||
@ -36,7 +35,6 @@ class TaskListViewModel(
|
|||||||
}
|
}
|
||||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(AppDataContainer.TIMEOUT), TaskListUiState())
|
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(AppDataContainer.TIMEOUT), TaskListUiState())
|
||||||
|
|
||||||
// Для TaskList (Сортировка по uid)
|
|
||||||
val taskListUiState: StateFlow<TaskListUiState> = taskRepository.getAllTasks()
|
val taskListUiState: StateFlow<TaskListUiState> = taskRepository.getAllTasks()
|
||||||
.map { tasks ->
|
.map { tasks ->
|
||||||
val sortedTasks = tasks.sortedBy { task ->
|
val sortedTasks = tasks.sortedBy { task ->
|
||||||
|
Loading…
Reference in New Issue
Block a user