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.material3:material3:1.1.2")
|
||||
implementation("com.google.firebase:protolite-well-known-types:18.0.0")
|
||||
implementation("com.google.android.material:material:1.10.0")
|
||||
|
||||
// Room
|
||||
val room_version = "2.5.2"
|
||||
|
@ -1,14 +1,25 @@
|
||||
package ru.ulstu.`is`.pmu.ui.task.edit
|
||||
|
||||
import android.content.res.Configuration
|
||||
import android.view.ContextThemeWrapper
|
||||
import android.widget.CalendarView
|
||||
import androidx.compose.foundation.background
|
||||
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.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.KeyboardOptions
|
||||
import androidx.compose.material3.AlertDialog
|
||||
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.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
@ -18,6 +29,7 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
@ -25,14 +37,19 @@ 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
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
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.navigation.NavController
|
||||
import com.google.android.material.datepicker.MaterialDatePicker
|
||||
import java.util.Date
|
||||
import kotlinx.coroutines.launch
|
||||
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.ui.AppViewModelProvider
|
||||
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
|
||||
fun TaskEdit(
|
||||
@ -113,7 +135,6 @@ private fun UserDropDown(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun TaskEdit(
|
||||
@ -144,13 +165,33 @@ private fun TaskEdit(
|
||||
label = { Text(stringResource(id = R.string.task_lastname)) },
|
||||
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(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = taskUiState.taskDetails.endDate,
|
||||
onValueChange = { newDate -> onUpdate(taskUiState.taskDetails.copy(endDate = newDate)) },
|
||||
label = { Text(stringResource(id = R.string.task_endDate)) },
|
||||
singleLine = true,
|
||||
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number)
|
||||
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number),
|
||||
enabled = false
|
||||
)
|
||||
// UserDropDown(
|
||||
// userUiState = userUiState,
|
||||
@ -169,7 +210,6 @@ private fun TaskEdit(
|
||||
}
|
||||
},
|
||||
enabled = taskUiState.isEntryValid,
|
||||
shape = MaterialTheme.shapes.small,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(text = stringResource(R.string.task_save_button))
|
||||
@ -183,7 +223,7 @@ private fun TaskEdit(
|
||||
text = { Text("Введите дату по шаблону: 01.12.2023") },
|
||||
confirmButton = {
|
||||
Button(onClick = { showInvalidDateDialog = false }) {
|
||||
Text("ОК")
|
||||
Text("Подтвердить")
|
||||
}
|
||||
}
|
||||
)
|
||||
@ -195,22 +235,99 @@ fun isValidDate(date: String): Boolean {
|
||||
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
|
||||
fun TaskEditPreview() {
|
||||
PmudemoTheme {
|
||||
Surface(
|
||||
color = MaterialTheme.colorScheme.background
|
||||
) {
|
||||
TaskEdit(
|
||||
taskUiState = Task.getTask().toUiState(true),
|
||||
userUiState = User.DEMO_User.toUiState(),
|
||||
usersListUiState = UsersListUiState(listOf()),
|
||||
onClick = {},
|
||||
onUpdate = {},
|
||||
onUserUpdate = {}
|
||||
fun CustomCalendarView(onDateSelected: (Date) -> Unit) {
|
||||
AndroidView(
|
||||
modifier = Modifier.wrapContentSize(),
|
||||
factory = { context ->
|
||||
// Используем стандартный контекст, без применения кастомной темы
|
||||
CalendarView(context).apply {
|
||||
// Настройки CalendarView
|
||||
val calendar = Calendar.getInstance()
|
||||
|
||||
setOnDateChangeListener { _, year, month, dayOfMonth ->
|
||||
calendar.set(Calendar.YEAR, year)
|
||||
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 ->
|
||||
TaskListContent( // Изменил имя, чтобы избежать путаницы с функцией TaskList
|
||||
TaskListContent(
|
||||
modifier = Modifier
|
||||
.padding(innerPadding)
|
||||
.fillMaxSize(),
|
||||
@ -82,12 +82,12 @@ fun TaskList(
|
||||
val route = Screen.TaskEdit.route.replace("{id}", uid.toString())
|
||||
navController.navigate(route)
|
||||
},
|
||||
onSwipeLeft = { task: Task -> // Обработка свайпа влево (удаление)
|
||||
onSwipeLeft = { task: Task ->
|
||||
coroutineScope.launch {
|
||||
viewModel.deleteTask(task)
|
||||
}
|
||||
},
|
||||
onSwipeRight = { task: Task -> // Обработка свайпа вправо (добавить в избранное)
|
||||
onSwipeRight = { task: Task ->
|
||||
coroutineScope.launch {
|
||||
viewModel.favoriteTask(task)
|
||||
}
|
||||
@ -102,20 +102,20 @@ private fun SwipeToAction(
|
||||
dismissState: DismissState,
|
||||
task: Task,
|
||||
onClick: (uid: Int) -> Unit,
|
||||
onSwipeRight: (task: Task) -> Unit, // Для добавления в избранное
|
||||
onSwipeLeft: (task: Task) -> Unit // Для удаления
|
||||
onSwipeRight: (task: Task) -> Unit,
|
||||
onSwipeLeft: (task: Task) -> Unit
|
||||
) {
|
||||
SwipeToDismiss(
|
||||
state = dismissState,
|
||||
directions = setOf(
|
||||
DismissDirection.EndToStart, // Для удаления
|
||||
DismissDirection.StartToEnd // Для добавления в избранное
|
||||
DismissDirection.EndToStart,
|
||||
DismissDirection.StartToEnd
|
||||
),
|
||||
background = {
|
||||
val backgroundColor by animateColorAsState(
|
||||
when (dismissState.targetValue) {
|
||||
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
|
||||
}, label = ""
|
||||
)
|
||||
@ -132,7 +132,7 @@ private fun SwipeToAction(
|
||||
) {
|
||||
Icon(
|
||||
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",
|
||||
tint = Color.White
|
||||
)
|
||||
@ -160,7 +160,6 @@ private fun TaskListContent(
|
||||
modifier = modifier
|
||||
) {
|
||||
if (taskList.isEmpty()) {
|
||||
// Код для отображения пустого списка
|
||||
} else {
|
||||
LazyColumn(modifier = Modifier.padding(all = 10.dp)) {
|
||||
items(items = taskList, key = { it.uid }) { task ->
|
||||
@ -169,14 +168,14 @@ private fun TaskListContent(
|
||||
if (dismissState.isDismissed(DismissDirection.EndToStart)) {
|
||||
LaunchedEffect(task) {
|
||||
onSwipeLeft(task)
|
||||
dismissState.reset() // Сбросить состояние после обработки свайпа
|
||||
dismissState.reset()
|
||||
}
|
||||
}
|
||||
|
||||
if (dismissState.isDismissed(DismissDirection.StartToEnd)) {
|
||||
LaunchedEffect(task) {
|
||||
onSwipeRight(task)
|
||||
dismissState.reset() // Сбросить состояние после обработки свайпа
|
||||
dismissState.reset()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@ class TaskListViewModel(
|
||||
val taskListFavoriteUiState: StateFlow<TaskListUiState> = taskRepository.getAllTasks()
|
||||
.map { tasks ->
|
||||
val filteredTasks = tasks.filter { task ->
|
||||
task.favorite // Оставить только задачи, у которых favorite = true
|
||||
task.favorite
|
||||
}
|
||||
val sortedTasks = filteredTasks.sortedByDescending { task ->
|
||||
parseDate(task.endDate)?.timeInMillis ?: Long.MIN_VALUE
|
||||
@ -26,7 +26,6 @@ class TaskListViewModel(
|
||||
}
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(AppDataContainer.TIMEOUT), TaskListUiState())
|
||||
|
||||
// Для TaskListEndDate (Сортировка по endDate)
|
||||
val taskListEndDateUiState: StateFlow<TaskListUiState> = taskRepository.getAllTasks()
|
||||
.map { tasks ->
|
||||
val sortedTasks = tasks.sortedByDescending { task ->
|
||||
@ -36,7 +35,6 @@ class TaskListViewModel(
|
||||
}
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(AppDataContainer.TIMEOUT), TaskListUiState())
|
||||
|
||||
// Для TaskList (Сортировка по uid)
|
||||
val taskListUiState: StateFlow<TaskListUiState> = taskRepository.getAllTasks()
|
||||
.map { tasks ->
|
||||
val sortedTasks = tasks.sortedBy { task ->
|
||||
|
Loading…
Reference in New Issue
Block a user