некорректное отображение данных в корзине

This commit is contained in:
dasha 2023-12-20 16:26:43 +04:00
parent a14249d6ef
commit a66218c7ab
19 changed files with 243262 additions and 168 deletions

View File

@ -90,6 +90,11 @@ interface MyServerService {
@Query("userId") userId: Int,
): List<UserSessionWithSessionRemote>
@GET("userssessions")
suspend fun getUserSessions(
@Query("userId") userId: Int,
): List<UserSessionRemote>
@GET("userssessions?_expand=session")
suspend fun getUsersSessions(): List<UserSessionWithSessionRemote>

View File

@ -48,13 +48,13 @@ class RestCinemaRepository(
override suspend fun getCinema(uid: Int): CinemaWithSessions {
val cinemaWithSessions = service.getCinemaWithSessions(uid)
val orders = service.getOrders()
val sessions = cinemaWithSessions.sessions.map { sessionFromCinemaRemote ->
SessionFromCinema(
sessionFromCinemaRemote.id,
sessionFromCinemaRemote.dateTime,
sessionFromCinemaRemote.price,
sessionFromCinemaRemote.maxCount - service.getOrders().flatMap
sessionFromCinemaRemote.maxCount - orders.flatMap
{ order ->
order.sessions.filter { session ->
session.id == sessionFromCinemaRemote.id &&

View File

@ -11,7 +11,7 @@ class RestOrderSessionRepository(
private val dbOrderSessionRepository: OfflineOrderSessionRepository
) : OrderSessionRepository {
override suspend fun insertOrderSession(orderSessionCrossRef: OrderSessionCrossRef) {
var orderRemote = service.getOrder(orderSessionCrossRef.orderId)
val orderRemote = service.getOrder(orderSessionCrossRef.orderId)
val session = service.getSession(orderSessionCrossRef.sessionId)
val sessionFromOrder = SessionFromOrderRemote(
@ -23,10 +23,9 @@ class RestOrderSessionRepository(
session.cinema
)
val updatedSessions = orderRemote.sessions.toMutableList()
updatedSessions.add(sessionFromOrder)
orderRemote = orderRemote.copy(sessions = updatedSessions)
orderRemote.sessions = orderRemote.sessions.toMutableList().apply {
add(sessionFromOrder)
}
service.updateOrder(orderSessionCrossRef.orderId, orderRemote)
dbOrderSessionRepository.insertOrderSession(orderSessionCrossRef)
}

View File

@ -37,12 +37,13 @@ class RestUserRepository(
)
)
}
val orders = service.getOrders()
val sessions = cart.map { sessionFromCartRemote ->
SessionFromCart(
uid = sessionFromCartRemote.sessionId,
dateTime = sessionFromCartRemote.session.dateTime,
price = sessionFromCartRemote.session.price,
availableCount = sessionFromCartRemote.session.maxCount - service.getOrders()
availableCount = sessionFromCartRemote.session.maxCount - orders
.flatMap
{ order ->
order.sessions.filter { session ->

View File

@ -46,16 +46,20 @@ class RestUserSessionRepository(
val userSessionRemote = service.getUserSession(
userSessionCrossRef.userId,
userSessionCrossRef.sessionId
).first()
).firstOrNull() ?: return
service.deleteUserSession(userSessionRemote.id)
dbUserSessionRepository.deleteUserSession(userSessionCrossRef)
}
override suspend fun deleteUserSessions(userId: Int) {
val cart = service.getUserCart(userId)
val cart = service.getUserSessions(userId)
cart.forEach {
service.deleteUserSession(it.id)
}
dbUserSessionRepository.deleteUserSessions(userId)
}
override suspend fun deleteUserSessions(userSessionCrossRefs: List<UserSessionCrossRef>) {
userSessionCrossRefs.forEach { deleteUserSession(it) }
}
}

View File

@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
@ -16,6 +17,7 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
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.Icon
import androidx.compose.material3.IconButton
@ -33,8 +35,11 @@ import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.example.myapplication.LiveStore
import com.example.myapplication.R
import com.example.myapplication.api.ApiStatus
import com.example.myapplication.composeui.navigation.Screen
import com.example.myapplication.database.entities.composeui.AppViewModelProvider
import com.example.myapplication.database.entities.model.Session
import com.example.myapplication.database.entities.model.SessionFromCart
@ -44,6 +49,7 @@ import org.threeten.bp.format.DateTimeFormatter
@Composable
fun Cart(
navController: NavController,
viewModel: CartViewModel = viewModel(factory = AppViewModelProvider.Factory)
) {
val coroutineScope = rememberCoroutineScope()
@ -52,6 +58,8 @@ fun Cart(
LaunchedEffect(Unit) {
viewModel.refreshState()
}
when (viewModel.apiStatus) {
ApiStatus.DONE -> {
Cart(
cartUiState = cartUiState,
modifier = Modifier
@ -73,16 +81,38 @@ fun Cart(
coroutineScope.launch {
viewModel.addToOrder(sessions = sessions)
}
},
onDelete = { session: SessionFromCart ->
coroutineScope.launch {
viewModel.removeFromCart(
session = Session(
uid = session.uid,
dateTime = session.dateTime,
price = session.price,
maxCount = 0,
cinemaId = session.cinemaId
)
)
}
}
)
}
ApiStatus.LOADING -> LoadingPlaceholder()
else -> ErrorPlaceholder(
message = viewModel.apiError,
onBack = { navController.navigate(Screen.Report.route) }
)
}
}
@Composable
private fun Cart(
cartUiState: CartUiState,
modifier: Modifier,
onChangeCount: (SessionFromCart, Int) -> Unit,
onAddToOrder: (List<SessionFromCart>) -> Unit
onAddToOrder: (List<SessionFromCart>) -> Unit,
onDelete: (SessionFromCart) -> Unit
) {
LazyColumn(
modifier = modifier
@ -95,9 +125,13 @@ private fun Cart(
.padding(10.dp)
.clip(RoundedCornerShape(16.dp))
.background(MaterialTheme.colorScheme.secondary),
onChangeCount = onChangeCount
onChangeCount = onChangeCount,
onDelete = onDelete,
)
}
item {
Spacer(modifier = Modifier.height(48.dp))
}
}
val user = LiveStore.user.observeAsState()
if (user.value?.role == UserRole.USER) {
@ -107,7 +141,7 @@ private fun Cart(
Button(
onClick = { onAddToOrder(cartUiState.sessionList) },
modifier = Modifier
.padding(16.dp)
.padding(6.dp)
.fillMaxWidth()
) { Text("Купить") }
}
@ -119,25 +153,26 @@ private fun SessionListItem(
session: SessionFromCart,
modifier: Modifier = Modifier,
onChangeCount: (SessionFromCart, Int) -> Unit,
onDelete: (SessionFromCart) -> Unit
) {
val dateFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm")
val formattedDate = dateFormatter.format(session.dateTime)
Column {
Text(
text = formattedDate,
color = MaterialTheme.colorScheme.onBackground,
)
Column(modifier = modifier.fillMaxWidth()) {
Box(
modifier = modifier
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp),
.padding(8.dp)
) {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
if (session.cinema.image != null)
if (session.cinema.image != null) {
Image(
bitmap = BitmapFactory.decodeByteArray(
session.cinema.image,
@ -149,6 +184,7 @@ private fun SessionListItem(
.size(90.dp)
.padding(4.dp)
)
}
Column(
modifier = Modifier.weight(1f),
@ -157,38 +193,51 @@ private fun SessionListItem(
Text(
text = "${session.cinema.name}, ${session.cinema.year}\n" +
"Цена: ${session.price}\n" +
"${session.count}/${session.availableCount}",
if (session.availableCount == 0) "Недоступно" else "${session.count}/${session.availableCount}",
color = MaterialTheme.colorScheme.onSecondary
)
}
}
}
Box(
modifier = Modifier
.background(
color = MaterialTheme.colorScheme.background,
shape = RoundedCornerShape(10.dp)
) // Задаем фон для кнопок
) {
Row(
modifier = Modifier
.background(color = MaterialTheme.colorScheme.primary)
.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
IconButton(
onClick = { onDelete(session) }
) {
Icon(
imageVector = Icons.Default.Delete,
contentDescription = "Удалить",
tint = MaterialTheme.colorScheme.onPrimary
)
}
Spacer(modifier = Modifier.weight(1F))
if (session.availableCount != 0) {
IconButton(
enabled = session.count != 1,
onClick = { onChangeCount(session, session.count - 1) }
) {
Icon(
imageVector = ImageVector.vectorResource(id = R.drawable.minus),
contentDescription = "Уменьшить",
tint = MaterialTheme.colorScheme.onBackground,
modifier = Modifier.size(10.dp)
tint = MaterialTheme.colorScheme.onPrimary
)
}
Text(
text = "${session.count}",
color = MaterialTheme.colorScheme.onBackground
color = MaterialTheme.colorScheme.onPrimary
)
IconButton(
enabled = session.count != session.availableCount,
onClick = {
onChangeCount(
session,
@ -199,12 +248,10 @@ private fun SessionListItem(
Icon(
imageVector = Icons.Default.Add,
contentDescription = "Увеличить",
tint = MaterialTheme.colorScheme.onBackground,
modifier = Modifier.size(10.dp)
tint = MaterialTheme.colorScheme.onPrimary
)
}
}
}
}
}
}

View File

@ -3,6 +3,7 @@ package com.example.myapplication.composeui
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.room.Transaction
import com.example.myapplication.LiveStore
import com.example.myapplication.database.entities.model.Order
import com.example.myapplication.database.entities.model.OrderSessionCrossRef
@ -25,29 +26,32 @@ class CartViewModel(
var cartUiState by mutableStateOf(CartUiState())
private set
suspend fun refreshState() {
val userId: Int = LiveStore.user.value?.uid ?: 0
suspend fun refreshState(needLoadingScreen: Boolean = true) {
val userId: Int = LiveStore.user.value?.uid ?: return
runInScope(
actionSuccess = {
val cart = userRepository.getCartByUser(userId)
cartUiState = CartUiState(cart)
cartUiState = CartUiState(userRepository.getCartByUser(userId))
}, actionError = {
cartUiState = CartUiState()
}
},
needLoadingScreen = needLoadingScreen
)
}
@Transaction
suspend fun addToOrder(sessions: List<SessionFromCart>) {
if (isLoading)
return
isLoading = true
val userId: Int = LiveStore.user.value?.uid ?: return
if (sessions.isEmpty())
val cart = sessions.filter { it.availableCount != 0 }
if (cart.isEmpty())
return
runInScope(
actionSuccess = {
val orderId = orderRepository.insertOrder(Order(0, userId, LocalDateTime.now()))
sessions.forEach { session ->
cart.forEach { session ->
if (session.availableCount != 0) {
orderSessionRepository.insertOrderSession(
OrderSessionCrossRef(
orderId.toInt(),
@ -57,7 +61,10 @@ class CartViewModel(
)
)
}
userSessionRepository.deleteUserSessions(userId)
}
userSessionRepository.deleteUserSessions(cart.map {
UserSessionCrossRef(userId, it.uid, it.count)
})
refreshState()
}
)
@ -97,7 +104,9 @@ class CartViewModel(
)
)
refreshState()
}
},
actionError = { },
needLoadingScreen = false
)
}
}

View File

@ -1,6 +1,5 @@
package com.example.myapplication.composeui
import android.content.res.Configuration
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
@ -8,8 +7,6 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@ -17,12 +14,10 @@ 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.TextUnit
import androidx.compose.ui.unit.TextUnitType
import androidx.compose.ui.unit.dp
import com.example.myapplication.R
import com.example.myapplication.ui.theme.PmudemoTheme
@Composable
@ -68,29 +63,3 @@ fun LoadingPlaceholder() {
)
}
}
@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 ErrorPlaceholderPreview() {
PmudemoTheme {
Surface(
color = MaterialTheme.colorScheme.background
) {
ErrorPlaceholder("Error", onBack = {})
}
}
}
@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 LoadingPlaceholderPreview() {
PmudemoTheme {
Surface(
color = MaterialTheme.colorScheme.background
) {
LoadingPlaceholder()
}
}
}

View File

@ -25,8 +25,10 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.example.myapplication.api.ApiStatus
import com.example.myapplication.api.session.ReportRemote
import com.example.myapplication.composeui.navigation.Screen
import com.example.myapplication.database.entities.composeui.AppViewModelProvider
import kotlinx.coroutines.launch
import org.threeten.bp.format.DateTimeFormatter
@ -35,6 +37,7 @@ import java.util.Date
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Report(
navController: NavController,
viewModel: ReportViewModel = viewModel(factory = AppViewModelProvider.Factory)
) {
val dateStateStart = rememberDatePickerState(initialDisplayMode = DisplayMode.Input)
@ -87,7 +90,6 @@ fun Report(
Button(
onClick = { coroutineScope.launch { viewModel.getReport() } },
enabled = viewModel.reportUiState.isEntryValid,
shape = MaterialTheme.shapes.small,
modifier = Modifier.fillMaxWidth()
) {
Text(text = "Получить отчет")
@ -100,7 +102,7 @@ fun Report(
ApiStatus.LOADING -> LoadingPlaceholder()
else -> ErrorPlaceholder(
message = viewModel.apiError,
onBack = { }
onBack = { navController.navigate(Screen.Report.route) }
)
}
}

View File

@ -162,7 +162,7 @@ fun Navhost(
) {
composable(Screen.CinemaList.route) { CinemaList(navController) }
composable(Screen.OrderList.route) { OrderList(navController) }
composable(Screen.Cart.route) { Cart() }
composable(Screen.Cart.route) { Cart(navController) }
composable(Screen.UserProfile.route) { UserProfile(isDarkTheme, dataStore) }
composable(
Screen.CinemaEdit.route,
@ -189,7 +189,7 @@ fun Navhost(
) { backStackEntry ->
backStackEntry.arguments?.let { OrderView(navController) }
}
composable(Screen.Report.route) { Report() }
composable(Screen.Report.route) { Report(navController) }
}
}

View File

@ -113,7 +113,6 @@ fun SessionList(
IconButton(
onClick = {
coroutineScope.launch {
if (session.availableCount != 0)
viewModel.addSessionInCart(sessionId = session.uid)
}
},

View File

@ -29,7 +29,6 @@ class SessionListViewModel(
suspend fun addSessionInCart(sessionId: Int, count: Int = 1) {
val userId: Int = LiveStore.user.value?.uid ?: return
runInScope(actionSuccess = {
try {
userSessionRepository.insertUserSession(
UserSessionCrossRef(
userId,
@ -37,9 +36,6 @@ class SessionListViewModel(
count
)
)
} catch (_: Exception) {
}
})
}
}

View File

@ -76,7 +76,7 @@ fun UserProfile(
},
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
.padding(start = 2.dp, top = 10.dp)
) { Text("Выход") }
}
} else {
@ -160,7 +160,7 @@ fun UserProfile(
onClick = { coroutine.launch { viewModel.signUp() } },
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
.padding(start = 2.dp, top = 8.dp)
) {
Text("Регистрация")
}
@ -182,7 +182,7 @@ fun UserProfile(
},
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
.padding(start = 2.dp, top = 8.dp)
) {
Text("Вход")
}

View File

@ -111,7 +111,6 @@ private fun CinemaEdit(
Button(
onClick = onClick,
enabled = cinemaUiState.isEntryValid,
shape = MaterialTheme.shapes.small,
modifier = Modifier.fillMaxWidth()
) {
Text(text = stringResource(R.string.Save_button))

View File

@ -9,7 +9,6 @@ import androidx.compose.material3.Button
import androidx.compose.material3.DatePicker
import androidx.compose.material3.DisplayMode
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TimePicker
@ -153,7 +152,6 @@ private fun SessionEdit(
Button(
onClick = onClick,
enabled = sessionUiState.isEntryValid,
shape = MaterialTheme.shapes.small,
modifier = Modifier.fillMaxWidth()
) {
Text(text = stringResource(R.string.Save_button))

View File

@ -17,7 +17,7 @@ interface UserSessionCrossRefDao {
suspend fun update(userSessionCrossRef: UserSessionCrossRef)
@Delete
suspend fun delete(userSessionCrossRef: UserSessionCrossRef)
suspend fun delete(vararg userSessionCrossRef: UserSessionCrossRef)
@Query("DELETE FROM users_sessions where users_sessions.user_id = :userId")
suspend fun deleteByUserUid(userId: Int)

View File

@ -16,5 +16,8 @@ class OfflineUserSessionRepository(private val userSessionDao: UserSessionCrossR
override suspend fun deleteUserSessions(userId: Int) = userSessionDao.deleteByUserUid(userId)
override suspend fun deleteUserSessions(userSessionCrossRefs: List<UserSessionCrossRef>) =
userSessionDao.delete(*userSessionCrossRefs.toTypedArray())
suspend fun deleteSessionsByUid(sessionId: Int) = userSessionDao.deleteBySessionUid(sessionId)
}

View File

@ -7,4 +7,5 @@ interface UserSessionRepository {
suspend fun updateUserSession(userSessionCrossRef: UserSessionCrossRef)
suspend fun deleteUserSession(userSessionCrossRef: UserSessionCrossRef)
suspend fun deleteUserSessions(userId: Int)
suspend fun deleteUserSessions(userSessionCrossRefs: List<UserSessionCrossRef>)
}

File diff suppressed because it is too large Load Diff