Course work: implement full performance CRUD
This commit is contained in:
parent
a687ff002f
commit
2eca8db087
@ -67,7 +67,8 @@ dependencies {
|
|||||||
implementation("androidx.compose.ui:ui")
|
implementation("androidx.compose.ui:ui")
|
||||||
implementation("androidx.compose.ui:ui-graphics")
|
implementation("androidx.compose.ui:ui-graphics")
|
||||||
implementation("androidx.compose.ui:ui-tooling-preview")
|
implementation("androidx.compose.ui:ui-tooling-preview")
|
||||||
implementation("androidx.compose.material3:material3")
|
implementation("androidx.compose.material3:material3:1.1.2")
|
||||||
|
implementation("androidx.compose.material:material:1.4.3")
|
||||||
|
|
||||||
// Room
|
// Room
|
||||||
val room_version = "2.5.2"
|
val room_version = "2.5.2"
|
||||||
|
@ -60,6 +60,22 @@ interface MyServerService {
|
|||||||
@Body credentials: Credentials
|
@Body credentials: Credentials
|
||||||
): Token
|
): Token
|
||||||
|
|
||||||
|
@POST("api/performances")
|
||||||
|
suspend fun createPerformance(
|
||||||
|
@Body performance: PerformanceRemote,
|
||||||
|
)
|
||||||
|
|
||||||
|
@PUT("api/performances/{id}")
|
||||||
|
suspend fun updatePerformance(
|
||||||
|
@Path("id") id: Int,
|
||||||
|
@Body student: PerformanceRemote,
|
||||||
|
)
|
||||||
|
|
||||||
|
@DELETE("api/performances/{id}")
|
||||||
|
suspend fun deletePerformance(
|
||||||
|
@Path("id") id: Int,
|
||||||
|
)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val BASE_URL = "http://10.0.2.2:8000/"
|
private const val BASE_URL = "http://10.0.2.2:8000/"
|
||||||
|
|
||||||
|
@ -41,3 +41,13 @@ suspend fun PerformanceRemote.toPerformanceWithPeople(service: MyServerService):
|
|||||||
actorsList.toList()
|
actorsList.toList()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Performance.toPerformanceRemote(): PerformanceRemote = PerformanceRemote(
|
||||||
|
performance_uid!!,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
authorId!!,
|
||||||
|
directorId!!,
|
||||||
|
imageURL,
|
||||||
|
previewImageURL
|
||||||
|
)
|
@ -6,6 +6,7 @@ import androidx.paging.PagingConfig
|
|||||||
import androidx.paging.PagingData
|
import androidx.paging.PagingData
|
||||||
import com.example.mobile_labs.api.MyServerService
|
import com.example.mobile_labs.api.MyServerService
|
||||||
import com.example.mobile_labs.api.models.toPerformance
|
import com.example.mobile_labs.api.models.toPerformance
|
||||||
|
import com.example.mobile_labs.api.models.toPerformanceRemote
|
||||||
import com.example.mobile_labs.api.models.toPerformanceWithPeople
|
import com.example.mobile_labs.api.models.toPerformanceWithPeople
|
||||||
import com.example.mobile_labs.api.people.RestPersonRepository
|
import com.example.mobile_labs.api.people.RestPersonRepository
|
||||||
import com.example.mobile_labs.common.AppDataContainer
|
import com.example.mobile_labs.common.AppDataContainer
|
||||||
@ -50,4 +51,16 @@ class RestPerformanceRepository(
|
|||||||
|
|
||||||
override suspend fun getPerformance(uid: Int): PerformanceWithPeople =
|
override suspend fun getPerformance(uid: Int): PerformanceWithPeople =
|
||||||
service.getPerformance(uid).toPerformanceWithPeople(service)
|
service.getPerformance(uid).toPerformanceWithPeople(service)
|
||||||
|
|
||||||
|
override suspend fun insertPerformance(performance: Performance) {
|
||||||
|
service.createPerformance(performance.toPerformanceRemote())
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun updatePerformance(performance: Performance) {
|
||||||
|
service.updatePerformance(performance.performance_uid!!, performance.toPerformanceRemote())
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun deletePerformance(performance: Performance) {
|
||||||
|
service.deletePerformance(performance.performance_uid !!)
|
||||||
|
}
|
||||||
}
|
}
|
@ -64,6 +64,6 @@ class AppDataContainer(val context: Context) : AppContainer {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val TIMEOUT = 5000L
|
const val TIMEOUT = 5000L
|
||||||
const val LIMIT = 3
|
const val LIMIT = 20
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -8,7 +8,9 @@ import androidx.lifecycle.viewmodel.viewModelFactory
|
|||||||
import com.example.mobile_labs.TheatreApplication
|
import com.example.mobile_labs.TheatreApplication
|
||||||
import com.example.mobile_labs.ui.event.list.EventListViewModel
|
import com.example.mobile_labs.ui.event.list.EventListViewModel
|
||||||
import com.example.mobile_labs.ui.login.LoginViewModel
|
import com.example.mobile_labs.ui.login.LoginViewModel
|
||||||
|
import com.example.mobile_labs.ui.performance.list.AdminPerformanceListViewModel
|
||||||
import com.example.mobile_labs.ui.performance.list.PerformanceListViewModel
|
import com.example.mobile_labs.ui.performance.list.PerformanceListViewModel
|
||||||
|
import com.example.mobile_labs.ui.performance.view.AdminPerformanceViewModel
|
||||||
import com.example.mobile_labs.ui.performance.view.PerformanceViewModel
|
import com.example.mobile_labs.ui.performance.view.PerformanceViewModel
|
||||||
import com.example.mobile_labs.ui.person.list.PeopleListViewModel
|
import com.example.mobile_labs.ui.person.list.PeopleListViewModel
|
||||||
|
|
||||||
@ -29,6 +31,12 @@ object AppViewModelProvider {
|
|||||||
initializer {
|
initializer {
|
||||||
LoginViewModel()
|
LoginViewModel()
|
||||||
}
|
}
|
||||||
|
initializer {
|
||||||
|
AdminPerformanceListViewModel(theatreApplication().container.performanceRestRepository)
|
||||||
|
}
|
||||||
|
initializer {
|
||||||
|
AdminPerformanceViewModel(this.createSavedStateHandle(), theatreApplication().container.performanceRestRepository)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,4 +8,7 @@ import kotlinx.coroutines.flow.Flow
|
|||||||
interface PerformanceRepository {
|
interface PerformanceRepository {
|
||||||
fun getAllPerformances(): Flow<PagingData<Performance>>
|
fun getAllPerformances(): Flow<PagingData<Performance>>
|
||||||
suspend fun getPerformance(uid: Int): PerformanceWithPeople
|
suspend fun getPerformance(uid: Int): PerformanceWithPeople
|
||||||
|
suspend fun insertPerformance(performance: Performance)
|
||||||
|
suspend fun updatePerformance(performance: Performance)
|
||||||
|
suspend fun deletePerformance(performance: Performance)
|
||||||
}
|
}
|
@ -24,11 +24,11 @@ class OfflinePerformanceRepository(private val performanceDao: PerformanceDao) :
|
|||||||
).flow
|
).flow
|
||||||
override suspend fun getPerformance(uid: Int): PerformanceWithPeople = performanceDao.getByUid(uid).first();
|
override suspend fun getPerformance(uid: Int): PerformanceWithPeople = performanceDao.getByUid(uid).first();
|
||||||
|
|
||||||
suspend fun insertPerformance(performance: Performance) = performanceDao.insert(performance);
|
override suspend fun insertPerformance(performance: Performance) = performanceDao.insert(performance);
|
||||||
|
|
||||||
suspend fun updatePerformance(performance: Performance) = performanceDao.update(performance);
|
override suspend fun updatePerformance(performance: Performance) = performanceDao.update(performance);
|
||||||
|
|
||||||
suspend fun deletePerformance(performance: Performance) = performanceDao.delete(performance);
|
override suspend fun deletePerformance(performance: Performance) = performanceDao.delete(performance);
|
||||||
|
|
||||||
suspend fun clearPerformances() = performanceDao.deleteAll()
|
suspend fun clearPerformances() = performanceDao.deleteAll()
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.example.mobile_labs.ui.navigation
|
package com.example.mobile_labs.ui.navigation
|
||||||
|
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
|
import android.util.Log
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
@ -37,7 +38,9 @@ import com.example.mobile_labs.api.MyServerService
|
|||||||
import com.example.mobile_labs.ui.about.About
|
import com.example.mobile_labs.ui.about.About
|
||||||
import com.example.mobile_labs.ui.event.list.EventList
|
import com.example.mobile_labs.ui.event.list.EventList
|
||||||
import com.example.mobile_labs.ui.login.Login
|
import com.example.mobile_labs.ui.login.Login
|
||||||
|
import com.example.mobile_labs.ui.performance.list.AdminPerformanceList
|
||||||
import com.example.mobile_labs.ui.performance.list.PerformanceList
|
import com.example.mobile_labs.ui.performance.list.PerformanceList
|
||||||
|
import com.example.mobile_labs.ui.performance.view.AdminPerformanceView
|
||||||
import com.example.mobile_labs.ui.performance.view.PerformanceView
|
import com.example.mobile_labs.ui.performance.view.PerformanceView
|
||||||
import com.example.mobile_labs.ui.person.list.PeopleList
|
import com.example.mobile_labs.ui.person.list.PeopleList
|
||||||
|
|
||||||
@ -79,8 +82,26 @@ fun Navbar(
|
|||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
NavigationBar(modifier) {
|
NavigationBar(modifier) {
|
||||||
|
if (MyServerService.getToken().isBlank()) {
|
||||||
Screen.bottomBarItems.forEach { screen ->
|
Screen.bottomBarItems.forEach { screen ->
|
||||||
if (MyServerService.getToken().isBlank() || screen.route !== "login") {
|
NavigationBarItem(
|
||||||
|
icon = { Icon(screen.icon, contentDescription = null) },
|
||||||
|
label = { Text(stringResource(screen.resourceId)) },
|
||||||
|
selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true,
|
||||||
|
onClick = {
|
||||||
|
navController.navigate(screen.route) {
|
||||||
|
popUpTo(navController.graph.findStartDestination().id) {
|
||||||
|
saveState = false
|
||||||
|
}
|
||||||
|
launchSingleTop = true
|
||||||
|
restoreState = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Screen.adminBottomBarItems.forEach { screen ->
|
||||||
NavigationBarItem(
|
NavigationBarItem(
|
||||||
icon = { Icon(screen.icon, contentDescription = null) },
|
icon = { Icon(screen.icon, contentDescription = null) },
|
||||||
label = { Text(stringResource(screen.resourceId)) },
|
label = { Text(stringResource(screen.resourceId)) },
|
||||||
@ -113,8 +134,13 @@ fun Navhost(
|
|||||||
) {
|
) {
|
||||||
composable(Screen.Schedule.route) { EventList(navController) }
|
composable(Screen.Schedule.route) { EventList(navController) }
|
||||||
composable(Screen.Repertoire.route) {
|
composable(Screen.Repertoire.route) {
|
||||||
|
if (MyServerService.getToken().isNotBlank()) {
|
||||||
|
AdminPerformanceList(navController)
|
||||||
|
}
|
||||||
|
else {
|
||||||
PerformanceList(navController)
|
PerformanceList(navController)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
composable(Screen.PeopleList.route) { PeopleList() }
|
composable(Screen.PeopleList.route) { PeopleList() }
|
||||||
composable(Screen.About.route) { About() }
|
composable(Screen.About.route) { About() }
|
||||||
if (MyServerService.getToken().isBlank()) {
|
if (MyServerService.getToken().isBlank()) {
|
||||||
@ -124,10 +150,15 @@ fun Navhost(
|
|||||||
Screen.PerformanceView.route,
|
Screen.PerformanceView.route,
|
||||||
arguments = listOf(navArgument("id") { type = NavType.IntType })
|
arguments = listOf(navArgument("id") { type = NavType.IntType })
|
||||||
) { backStackEntry ->
|
) { backStackEntry ->
|
||||||
|
if (MyServerService.getToken().isNotBlank()) {
|
||||||
|
AdminPerformanceView(navController)
|
||||||
|
}
|
||||||
|
else {
|
||||||
backStackEntry.arguments?.let { PerformanceView() }
|
backStackEntry.arguments?.let { PerformanceView() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -38,16 +38,16 @@ enum class Screen(
|
|||||||
);
|
);
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val bottomBarItems = if (MyServerService.getToken().isBlank()) listOf(
|
val bottomBarItems = listOf(
|
||||||
Schedule,
|
Schedule,
|
||||||
Repertoire,
|
Repertoire,
|
||||||
PeopleList,
|
PeopleList,
|
||||||
About,
|
About,
|
||||||
Login
|
Login
|
||||||
) else listOf(
|
)
|
||||||
Schedule,
|
|
||||||
|
val adminBottomBarItems = listOf(
|
||||||
Repertoire,
|
Repertoire,
|
||||||
PeopleList,
|
|
||||||
About,
|
About,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -0,0 +1,261 @@
|
|||||||
|
package com.example.mobile_labs.ui.performance.list
|
||||||
|
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.core.spring
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Add
|
||||||
|
import androidx.compose.material.icons.filled.Delete
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.CardDefaults
|
||||||
|
import androidx.compose.material3.DismissDirection
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.DismissState
|
||||||
|
import androidx.compose.material3.DismissValue
|
||||||
|
import androidx.compose.material3.FloatingActionButton
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.SwipeToDismiss
|
||||||
|
import androidx.compose.material3.rememberDismissState
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
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.style.TextAlign
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.zIndex
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import androidx.paging.PagingData
|
||||||
|
import androidx.paging.compose.LazyPagingItems
|
||||||
|
import androidx.paging.compose.collectAsLazyPagingItems
|
||||||
|
import androidx.paging.compose.itemContentType
|
||||||
|
import androidx.paging.compose.itemKey
|
||||||
|
import coil.compose.AsyncImage
|
||||||
|
import com.example.mobile_labs.R
|
||||||
|
import com.example.mobile_labs.api.MyServerService
|
||||||
|
import com.example.mobile_labs.database.performance.model.Performance
|
||||||
|
import com.example.mobile_labs.common.AppViewModelProvider
|
||||||
|
import com.example.mobile_labs.ui.navigation.Screen
|
||||||
|
import com.example.mobile_labs.ui.theme.Mobile_LabsTheme
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun AdminPerformanceList(
|
||||||
|
navController: NavController,
|
||||||
|
viewModel: AdminPerformanceListViewModel = viewModel(factory = AppViewModelProvider.Factory)
|
||||||
|
) {
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
val performanceListUiState = viewModel.performanceListUiState.collectAsLazyPagingItems()
|
||||||
|
Scaffold(
|
||||||
|
topBar = {},
|
||||||
|
floatingActionButton = {
|
||||||
|
FloatingActionButton(
|
||||||
|
onClick = {
|
||||||
|
val route = Screen.PerformanceView.route.replace("{id}", 0.toString())
|
||||||
|
navController.navigate(route)
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
Icon(Icons.Filled.Add, "Добавить")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) { innerPadding ->
|
||||||
|
PerformanceList(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(innerPadding)
|
||||||
|
.fillMaxSize(),
|
||||||
|
performanceList = performanceListUiState,
|
||||||
|
onClick = { uid: Int ->
|
||||||
|
val route = Screen.PerformanceView.route.replace("{id}", uid.toString())
|
||||||
|
navController.navigate(route)
|
||||||
|
},
|
||||||
|
onSwipe = { performance: Performance ->
|
||||||
|
coroutineScope.launch {
|
||||||
|
viewModel.deleteStudent(performance)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun DismissBackground(dismissState: DismissState) {
|
||||||
|
val color = when (dismissState.dismissDirection) {
|
||||||
|
DismissDirection.StartToEnd -> Color.Transparent
|
||||||
|
DismissDirection.EndToStart -> Color(0xFFFF1744)
|
||||||
|
null -> Color.Transparent
|
||||||
|
}
|
||||||
|
val direction = dismissState.dismissDirection
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(color)
|
||||||
|
.padding(12.dp, 8.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.End
|
||||||
|
) {
|
||||||
|
if (direction == DismissDirection.EndToStart) {
|
||||||
|
Icon(
|
||||||
|
Icons.Default.Delete,
|
||||||
|
contentDescription = "delete",
|
||||||
|
tint = Color.White
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
private fun SwipeToDelete(
|
||||||
|
dismissState: DismissState,
|
||||||
|
performance: Performance,
|
||||||
|
onClick: (uid: Int) -> Unit
|
||||||
|
) {
|
||||||
|
SwipeToDismiss(
|
||||||
|
modifier = Modifier.zIndex(1f),
|
||||||
|
state = dismissState,
|
||||||
|
directions = setOf(
|
||||||
|
DismissDirection.EndToStart
|
||||||
|
),
|
||||||
|
background = {
|
||||||
|
DismissBackground(dismissState)
|
||||||
|
},
|
||||||
|
dismissContent = {
|
||||||
|
PerformanceListItem(performance = performance,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(vertical = 7.dp)
|
||||||
|
.clickable { onClick(performance.performance_uid!!) })
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
private fun PerformanceList(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
performanceList: LazyPagingItems<Performance>,
|
||||||
|
onClick: (uid: Int) -> Unit,
|
||||||
|
onSwipe: (performance: Performance) -> Unit
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = modifier
|
||||||
|
) {
|
||||||
|
if (performanceList.itemCount == 0) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.performance_missing_description),
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
style = MaterialTheme.typography.titleLarge
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
LazyColumn(modifier = Modifier.padding(all = 10.dp)) {
|
||||||
|
items(
|
||||||
|
count = performanceList.itemCount,
|
||||||
|
key = performanceList.itemKey(),
|
||||||
|
contentType = performanceList.itemContentType()
|
||||||
|
) { index ->
|
||||||
|
val performance = performanceList[index]
|
||||||
|
performance?.let {
|
||||||
|
var show by remember { mutableStateOf(true) }
|
||||||
|
val dismissState = rememberDismissState(
|
||||||
|
confirmValueChange = {
|
||||||
|
if (it == DismissValue.DismissedToStart ||
|
||||||
|
it == DismissValue.DismissedToEnd
|
||||||
|
) {
|
||||||
|
show = false
|
||||||
|
true
|
||||||
|
} else false
|
||||||
|
}, positionalThreshold = { 200.dp.toPx() }
|
||||||
|
)
|
||||||
|
|
||||||
|
AnimatedVisibility(
|
||||||
|
show, exit = fadeOut(spring())
|
||||||
|
) {
|
||||||
|
SwipeToDelete(
|
||||||
|
dismissState = dismissState,
|
||||||
|
performance = performance,
|
||||||
|
onClick = onClick
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(show) {
|
||||||
|
if (!show) {
|
||||||
|
delay(800)
|
||||||
|
onSwipe(performance)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun PerformanceListItem(
|
||||||
|
performance: Performance,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
Card(
|
||||||
|
modifier = modifier.fillMaxWidth(),
|
||||||
|
elevation = CardDefaults.cardElevation(defaultElevation = 2.dp)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = modifier.padding(all = 10.dp)
|
||||||
|
) {
|
||||||
|
AsyncImage(model = performance.imageURL, contentDescription = "")
|
||||||
|
Button(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(all = 10.dp),
|
||||||
|
onClick = { }) {
|
||||||
|
Text(performance.title)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 AdminSchedulePreview() {
|
||||||
|
Mobile_LabsTheme {
|
||||||
|
Surface(
|
||||||
|
color = MaterialTheme.colorScheme.background
|
||||||
|
) {
|
||||||
|
PerformanceList(
|
||||||
|
performanceList = MutableStateFlow(
|
||||||
|
PagingData.empty<Performance>()
|
||||||
|
).collectAsLazyPagingItems(),
|
||||||
|
onClick = {},
|
||||||
|
onSwipe = {}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package com.example.mobile_labs.ui.performance.list
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.paging.PagingData
|
||||||
|
import com.example.mobile_labs.common.PerformanceRepository
|
||||||
|
import com.example.mobile_labs.database.performance.model.Performance
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
class AdminPerformanceListViewModel(
|
||||||
|
private val performanceRepository: PerformanceRepository
|
||||||
|
) : ViewModel() {
|
||||||
|
val performanceListUiState: Flow<PagingData<Performance>> = performanceRepository.getAllPerformances()
|
||||||
|
|
||||||
|
suspend fun deleteStudent(performance: Performance) {
|
||||||
|
performanceRepository.deletePerformance(performance)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,129 @@
|
|||||||
|
package com.example.mobile_labs.ui.performance.view
|
||||||
|
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.OutlinedTextField
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import coil.compose.AsyncImage
|
||||||
|
import com.example.mobile_labs.R
|
||||||
|
import com.example.mobile_labs.common.AppViewModelProvider
|
||||||
|
import com.example.mobile_labs.ui.theme.Mobile_LabsTheme
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun AdminPerformanceView(
|
||||||
|
navController: NavController,
|
||||||
|
viewModel: AdminPerformanceViewModel = viewModel(factory = AppViewModelProvider.Factory)
|
||||||
|
) {
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
PerformanceEdit(
|
||||||
|
performanceUiState = viewModel.performanceUiState,
|
||||||
|
onClick = {
|
||||||
|
coroutineScope.launch {
|
||||||
|
viewModel.savePerformance()
|
||||||
|
navController.popBackStack()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onUpdate = viewModel::updateUiState,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
private fun PerformanceEdit(
|
||||||
|
performanceUiState: AdminPerformanceUiState,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
onUpdate: (AdminPerformanceDetails) -> Unit,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(all = 10.dp)
|
||||||
|
) {
|
||||||
|
OutlinedTextField(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
value = performanceUiState.performanceDetails.title,
|
||||||
|
onValueChange = { onUpdate(performanceUiState.performanceDetails.copy(title = it)) },
|
||||||
|
label = {Text("Название")},
|
||||||
|
singleLine = true
|
||||||
|
)
|
||||||
|
OutlinedTextField(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
value = performanceUiState.performanceDetails.description,
|
||||||
|
onValueChange = { onUpdate(performanceUiState.performanceDetails.copy(description = it)) },
|
||||||
|
label = {Text("Описание")},
|
||||||
|
singleLine = true
|
||||||
|
)
|
||||||
|
OutlinedTextField(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
value = performanceUiState.performanceDetails.imageURL,
|
||||||
|
onValueChange = { onUpdate(performanceUiState.performanceDetails.copy(imageURL = it)) },
|
||||||
|
label = { Text("URL изображения") },
|
||||||
|
singleLine = true,
|
||||||
|
)
|
||||||
|
OutlinedTextField(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
value = performanceUiState.performanceDetails.previewImageUrl,
|
||||||
|
onValueChange = { onUpdate(performanceUiState.performanceDetails.copy(previewImageUrl = it)) },
|
||||||
|
label = {Text("URL превью")},
|
||||||
|
singleLine = true
|
||||||
|
)
|
||||||
|
OutlinedTextField(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
value = performanceUiState.performanceDetails.authorId.toString(),
|
||||||
|
onValueChange = { onUpdate(performanceUiState.performanceDetails.copy(authorId = it.toInt())) },
|
||||||
|
label = {Text("Автор")},
|
||||||
|
singleLine = true,
|
||||||
|
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Phone)
|
||||||
|
)
|
||||||
|
OutlinedTextField(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
value = performanceUiState.performanceDetails.directorId.toString(),
|
||||||
|
onValueChange = { onUpdate(performanceUiState.performanceDetails.copy(directorId = it.toInt())) },
|
||||||
|
label = {Text("Режиссер")},
|
||||||
|
singleLine = true,
|
||||||
|
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Phone)
|
||||||
|
)
|
||||||
|
Button(
|
||||||
|
onClick = onClick,
|
||||||
|
enabled = performanceUiState.isEntryValid,
|
||||||
|
shape = MaterialTheme.shapes.small,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Text(text = "Сохранить")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 AdminPerformanceViewPreview() {
|
||||||
|
Mobile_LabsTheme {
|
||||||
|
Surface(
|
||||||
|
color = MaterialTheme.colorScheme.background
|
||||||
|
) {
|
||||||
|
PerformanceView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,103 @@
|
|||||||
|
package com.example.mobile_labs.ui.performance.view
|
||||||
|
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.lifecycle.SavedStateHandle
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.example.mobile_labs.database.performance.model.PerformanceWithPeople
|
||||||
|
import com.example.mobile_labs.common.PerformanceRepository
|
||||||
|
import com.example.mobile_labs.database.performance.model.Performance
|
||||||
|
import kotlinx.coroutines.flow.filterNotNull
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class AdminPerformanceViewModel(
|
||||||
|
savedStateHandle: SavedStateHandle,
|
||||||
|
private val performanceRepository: PerformanceRepository
|
||||||
|
) : ViewModel() {
|
||||||
|
|
||||||
|
var performanceUiState by mutableStateOf(AdminPerformanceUiState())
|
||||||
|
private set
|
||||||
|
|
||||||
|
private val performanceUid: Int = checkNotNull(savedStateHandle["id"])
|
||||||
|
|
||||||
|
init {
|
||||||
|
viewModelScope.launch {
|
||||||
|
if (performanceUid > 0) {
|
||||||
|
performanceUiState = performanceRepository.getPerformance(performanceUid)
|
||||||
|
.toUiStateAdmin()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateUiState(performanceDetails: AdminPerformanceDetails) {
|
||||||
|
performanceUiState = AdminPerformanceUiState(
|
||||||
|
performanceDetails = performanceDetails,
|
||||||
|
isEntryValid = validateInput(performanceDetails)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun savePerformance() {
|
||||||
|
if (validateInput()) {
|
||||||
|
if (performanceUid > 0) {
|
||||||
|
performanceRepository.updatePerformance(
|
||||||
|
performanceUiState.performanceDetails.toPerformance(performanceUid)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
performanceRepository.insertPerformance(
|
||||||
|
performanceUiState.performanceDetails.toPerformance()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun validateInput(uiState: AdminPerformanceDetails = performanceUiState.performanceDetails): Boolean {
|
||||||
|
return with(uiState) {
|
||||||
|
title.isNotBlank()
|
||||||
|
&& description.isNotBlank()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class AdminPerformanceUiState(
|
||||||
|
val performanceDetails: AdminPerformanceDetails = AdminPerformanceDetails(),
|
||||||
|
val isEntryValid: Boolean = false
|
||||||
|
)
|
||||||
|
|
||||||
|
data class AdminPerformanceDetails(
|
||||||
|
val title: String = "",
|
||||||
|
val authorName: String = "",
|
||||||
|
val actorsList: String = "",
|
||||||
|
val imageURL: String = "",
|
||||||
|
val description: String = "",
|
||||||
|
val authorId: Int = 0,
|
||||||
|
val directorId: Int = 0,
|
||||||
|
val previewImageUrl: String = "",
|
||||||
|
)
|
||||||
|
|
||||||
|
fun PerformanceWithPeople.toAdminDetails(): AdminPerformanceDetails = AdminPerformanceDetails(
|
||||||
|
title = performance.title,
|
||||||
|
authorName = String.format("%s %s", author.last_name, author.first_name),
|
||||||
|
actorsList = buildString { for (actor in actors) append(actor.last_name + " " + actor.first_name + "\n") },
|
||||||
|
imageURL = performance.imageURL,
|
||||||
|
description = performance.description,
|
||||||
|
authorId = performance.authorId!!,
|
||||||
|
directorId = performance.directorId!!,
|
||||||
|
previewImageUrl = performance.previewImageURL,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun PerformanceWithPeople.toUiStateAdmin(): AdminPerformanceUiState = AdminPerformanceUiState(
|
||||||
|
performanceDetails = this.toAdminDetails(),
|
||||||
|
)
|
||||||
|
|
||||||
|
fun AdminPerformanceDetails.toPerformance(uid: Int = 0): Performance = Performance(
|
||||||
|
performance_uid = uid,
|
||||||
|
title = title,
|
||||||
|
authorId = authorId,
|
||||||
|
directorId = directorId,
|
||||||
|
imageURL = imageURL,
|
||||||
|
description = description,
|
||||||
|
previewImageURL = previewImageUrl
|
||||||
|
)
|
Loading…
Reference in New Issue
Block a user