Feature: complete basket implementation

This commit is contained in:
ArtemEmelyanov 2023-11-25 22:37:59 +04:00
parent 52d4438a41
commit eb1eb47ce0
12 changed files with 158 additions and 28 deletions

View File

@ -96,7 +96,7 @@ fun CardSneaker(item: Sneaker, navController: NavHostController, basketViewModel
if(GlobalUser.getInstance().getUser() == null){
navController.navigate("login")
}else{
basketViewModel.addToBasket(BasketSneakers(GlobalUser.getInstance().getUser()?.userId!!, item.sneakerId!!))
basketViewModel.addToBasket(BasketSneakers(GlobalUser.getInstance().getUser()?.userId!!, item.sneakerId!!, 1))
}
},
modifier = Modifier

View File

@ -11,7 +11,14 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.KeyboardArrowDown
import androidx.compose.material.icons.filled.KeyboardArrowLeft
import androidx.compose.material.icons.filled.KeyboardArrowRight
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
@ -21,6 +28,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.android_programming.R

View File

@ -1,5 +1,6 @@
package com.example.android_programming.composeui.Screens.OrderScreen
import android.annotation.SuppressLint
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
@ -12,10 +13,17 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.KeyboardArrowLeft
import androidx.compose.material.icons.filled.KeyboardArrowRight
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@ -33,8 +41,11 @@ import com.example.android_programming.vmodel.AppViewModelProvider
import com.example.android_programming.vmodel.BasketViewModel
import com.example.android_programming.vmodel.OrderViewModel
@SuppressLint("UnrememberedMutableState")
@Composable
fun CardSneakerLike(item: Sneaker, basketViewModel: BasketViewModel = viewModel(factory = AppViewModelProvider.Factory)) {
val userId = GlobalUser.getInstance().getUser()?.userId!!
val quantityState by basketViewModel.getQuantityState(userId, item.sneakerId!!).collectAsState()
Row(
modifier = Modifier
.fillMaxWidth()
@ -63,6 +74,7 @@ fun CardSneakerLike(item: Sneaker, basketViewModel: BasketViewModel = viewModel(
Text(text = "${item.price} USD", color = Color.Red, fontSize = 16.sp)
}
Column {
Button(
colors = ButtonDefaults.buttonColors(
backgroundColor = colorResource(id = R.color.figma_blue),
@ -77,4 +89,17 @@ fun CardSneakerLike(item: Sneaker, basketViewModel: BasketViewModel = viewModel(
Icon(imageVector = Icons.Default.Delete, contentDescription = "delete")
}
}
Column {
Row {
IconButton(onClick = { basketViewModel.decrementQuantity(userId ,item.sneakerId!!) }) {
Icon(Icons.Default.KeyboardArrowLeft, contentDescription = "Decrease Quantity")
}
Text("$quantityState")
IconButton(onClick = { basketViewModel.incrementQuantity(userId, item.sneakerId!!) }) {
Icon(Icons.Default.KeyboardArrowRight, contentDescription = "Increase Quantity")
}
}
}
}
}

View File

@ -9,16 +9,21 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.android_programming.GlobalUser
import com.example.android_programming.R
import com.example.android_programming.vmodel.OrderViewModel
@Composable
fun SubTotal(orderViewModel: OrderViewModel) {
val userId = GlobalUser.getInstance().getUser()?.userId!!
orderViewModel.updateSubTotal(userId)
val subTotal = orderViewModel.subTotal.value
Column(
modifier = Modifier
.padding(16.dp)
@ -41,7 +46,7 @@ fun SubTotal(orderViewModel: OrderViewModel) {
modifier = Modifier.weight(1f),
horizontalArrangement = Arrangement.End
){
Text(text = "${orderViewModel.getSubTotal()} $", fontSize = 15.sp)
Text(text = "$subTotal $", fontSize = 15.sp)
}
}
Row(
@ -60,7 +65,7 @@ fun SubTotal(orderViewModel: OrderViewModel) {
modifier = Modifier.weight(1f),
horizontalArrangement = Arrangement.End
){
Text(text = "${"%.2f".format(orderViewModel.getSubTotal() * 0.05)} $", fontSize = 15.sp)
Text(text = "${"%.2f".format(subTotal * 0.05)} $", fontSize = 15.sp)
}
}
Row(
@ -79,7 +84,7 @@ fun SubTotal(orderViewModel: OrderViewModel) {
modifier = Modifier.weight(1f),
horizontalArrangement = Arrangement.End
){
Text(text = "${"%.2f".format(orderViewModel.getSubTotal() + orderViewModel.getSubTotal() * 0.05)} $", fontSize = 15.sp)
Text(text = "${"%.2f".format(subTotal + subTotal * 0.05)} $", fontSize = 15.sp)
}
}
}

View File

@ -28,4 +28,20 @@ interface BasketDao {
@Query("DELETE FROM 'BasketSneakers' WHERE basketId = :basketId AND sneakerId = :sneakerId")
suspend fun removeSneakerFromBasket(basketId: Int, sneakerId: Int)
@Query("UPDATE 'BasketSneakers' SET quantity = :quantity WHERE basketId = :basketId AND sneakerId = :sneakerId")
suspend fun updateSneakerQuantity(basketId: Int, sneakerId: Int, quantity: Int)
@Query("UPDATE 'BasketSneakers' SET quantity = quantity + 1 WHERE basketId = :basketId AND sneakerId = :sneakerId")
suspend fun incrementSneakerQuantity(basketId: Int, sneakerId: Int)
@Query("UPDATE 'BasketSneakers' SET quantity = quantity - 1 WHERE basketId = :basketId AND sneakerId = :sneakerId")
suspend fun decrementSneakerQuantity(basketId: Int, sneakerId: Int)
@Query("SELECT quantity FROM 'BasketSneakers' WHERE basketId = :basketId AND sneakerId = :sneakerId")
suspend fun getQuantity(basketId: Int, sneakerId: Int): Int?
@Query("SELECT * FROM 'BasketSneakers' WHERE basketId = :basketId AND sneakerId = :sneakerId")
suspend fun getSneaker(basketId: Int, sneakerId: Int): BasketSneakers?
@Query("SELECT SUM(price * quantity) FROM 'BasketSneakers' bs JOIN 'Sneaker' s ON bs.sneakerId = s.sneakerId WHERE bs.basketId = :userId")
suspend fun getTotalPriceForUser(userId: Int): Double?
}

View File

@ -7,4 +7,5 @@ import androidx.room.PrimaryKey
class BasketSneakers (
val basketId: Int,
val sneakerId: Int,
val quantity: Int
)

View File

@ -5,5 +5,6 @@ import androidx.room.Entity
@Entity(primaryKeys = ["orderId", "sneakerId"])
data class OrderSneaker(
val orderId: Int,
val sneakerId: Int
val sneakerId: Int,
val quantity: Int
)

View File

@ -9,8 +9,14 @@ import kotlinx.coroutines.flow.Flow
class BasketRepoImpl(private val basketDao: BasketDao): BasketRepository {
override suspend fun createBasket(basket: Basket): Long = basketDao.createBasket(basket)
override suspend fun removeSneakerFromBasket(basketId: Int, sneakerId: Int) = basketDao.removeSneakerFromBasket(basketId, sneakerId)
override suspend fun updateSneakerQuantity(basketId: Int, sneakerId: Int, quantity: Int) = basketDao.updateSneakerQuantity(basketId, sneakerId, quantity)
override suspend fun incrementSneakerQuantity(basketId: Int, sneakerId: Int) = basketDao.incrementSneakerQuantity(basketId, sneakerId)
override suspend fun decrementSneakerQuantity(basketId: Int, sneakerId: Int) = basketDao.decrementSneakerQuantity(basketId, sneakerId)
override suspend fun insertBasketSneaker(basketSneaker: BasketSneakers) = basketDao.insertBasketSneaker(basketSneaker)
override fun getBasketWithSneakers(id: Int): Flow<BasketWithSneakers> = basketDao.getBasketWithSneakers(id)
override fun getAllBasket(): Flow<List<Basket>> = basketDao.getAllBasket()
override suspend fun delete(basket: Basket) = basketDao.delete(basket)
override suspend fun getQuantity(basketId: Int, sneakerId: Int): Int? = basketDao.getQuantity(basketId, sneakerId)
override suspend fun getSneaker(basketId: Int, sneakerId: Int): BasketSneakers? = basketDao.getSneaker(basketId, sneakerId)
override suspend fun getTotalPriceForUser(userId: Int): Double? = basketDao.getTotalPriceForUser(userId)
}

View File

@ -15,4 +15,10 @@ interface BasketRepository {
suspend fun delete(basket: Basket)
suspend fun createBasket(basket: Basket):Long
suspend fun removeSneakerFromBasket(basketId: Int, sneakerId: Int)
suspend fun updateSneakerQuantity(basketId: Int, sneakerId: Int, quantity: Int)
suspend fun incrementSneakerQuantity(basketId: Int, sneakerId: Int)
suspend fun decrementSneakerQuantity(basketId: Int, sneakerId: Int)
suspend fun getQuantity(basketId: Int, sneakerId: Int): Int?
suspend fun getSneaker(basketId: Int, sneakerId: Int): BasketSneakers?
suspend fun getTotalPriceForUser(userId: Int): Double?
}

View File

@ -15,7 +15,7 @@ object AppViewModelProvider {
UserViewModel(app().container.userRepo)
}
initializer {
OrderViewModel(app().container.orderRepo)
OrderViewModel(app().container.orderRepo, app().container.basketRepo)
}
initializer {
BasketViewModel(app().container.basketRepo)

View File

@ -1,6 +1,8 @@
package com.example.android_programming.vmodel
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.android_programming.model.Basket
@ -9,13 +11,39 @@ import com.example.android_programming.model.BasketWithSneakers
import com.example.android_programming.model.Sneaker
import com.example.android_programming.repository.BasketRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
class BasketViewModel(private val basketRepository: BasketRepository): ViewModel() {
private val _quantityStateMap = mutableMapOf<Int, MutableStateFlow<Int>>()
fun getQuantityState(basketId: Int, sneakerId: Int): StateFlow<Int> {
val quantityStateFlow = _quantityStateMap.getOrPut(sneakerId) {
MutableStateFlow(0)
}
viewModelScope.launch {
val quantityFromDb = basketRepository.getQuantity(basketId, sneakerId)
quantityFromDb?.let { quantityStateFlow.value = it }
}
return quantityStateFlow
}
suspend fun isSneakerInBasket(basketId: Int, sneakerId: Int): Boolean {
return basketRepository.getSneaker(basketId, sneakerId) != null
}
fun addToBasket(basketSneakers: BasketSneakers) = viewModelScope.launch {
val isSneakerInBasket = isSneakerInBasket(basketSneakers.basketId, basketSneakers.sneakerId)
if (isSneakerInBasket) {
incrementQuantity(basketSneakers.basketId, basketSneakers.sneakerId)
} else {
basketRepository.insertBasketSneaker(basketSneakers)
}
}
fun getBasketSneakers(id: Int): Flow<BasketWithSneakers> {
return basketRepository.getBasketWithSneakers(id)
@ -24,4 +52,24 @@ class BasketViewModel(private val basketRepository: BasketRepository): ViewModel
fun deleteSneakerFromBasket(basketId: Int, sneakerId: Int) = viewModelScope.launch {
basketRepository.removeSneakerFromBasket(basketId, sneakerId)
}
fun incrementQuantity(basketId: Int, sneakerId: Int) {
val currentQuantity = _quantityStateMap[sneakerId]?.value ?: 1
_quantityStateMap[sneakerId]?.value = currentQuantity + 1
viewModelScope.launch {
basketRepository.incrementSneakerQuantity(basketId, sneakerId)
}
}
fun decrementQuantity(basketId: Int, sneakerId: Int) {
val currentQuantity = _quantityStateMap[sneakerId]?.value ?: 1
if (currentQuantity > 1) {
_quantityStateMap[sneakerId]?.value = currentQuantity - 1
viewModelScope.launch {
basketRepository.decrementSneakerQuantity(basketId, sneakerId)
}
}
}
}

View File

@ -1,5 +1,6 @@
package com.example.android_programming.vmodel
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
@ -23,12 +24,14 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import java.util.Date
class OrderViewModel(private val orderRepository: OrderRepository) : ViewModel() {
class OrderViewModel(private val orderRepository: OrderRepository, private val basketRepository: BasketRepository) : ViewModel() {
var city = mutableStateOf("")
val street = mutableStateOf("")
val house = mutableStateOf("")
private val _selectedItems = MutableLiveData<List<Sneaker>>()
private val _subTotal = mutableStateOf(0.0)
val subTotal: State<Double> get() = _subTotal
val selectedItems: LiveData<List<Sneaker>> get() = _selectedItems
fun updateSelectedItems(items: List<Sneaker>) {
@ -48,29 +51,40 @@ class OrderViewModel(private val orderRepository: OrderRepository) : ViewModel()
}
fun createOrder() = viewModelScope.launch {
val userId = GlobalUser.getInstance().getUser()?.userId!!
val order = Order(
date = Date().time,
city = city.value,
street = street.value,
house = house.value,
subtotal = getSubTotal(),
taxes = "%.2f".format(getSubTotal() * 0.05).toDouble(),
total = "%.2f".format(getSubTotal() * 0.05 + getSubTotal()).toDouble(),
subtotal = getSubTotal(userId),
taxes = "%.2f".format(getSubTotal(userId) * 0.05).toDouble(),
total = "%.2f".format(getSubTotal(userId) * 0.05 + getSubTotal(userId)).toDouble(),
creatorUserId = GlobalUser.getInstance().getUser()?.userId!!
)
val orderId = orderRepository.createOrder(order)
for (sneaker in selectedItems.value.orEmpty()) {
val orderSneaker = OrderSneaker( orderId.toInt(), sneaker.sneakerId!!)
val userId = GlobalUser.getInstance().getUser()?.userId!!
val orderSneaker = basketRepository.getQuantity(userId, sneaker.sneakerId!!)
?.let { OrderSneaker( orderId.toInt(), sneaker.sneakerId!!, it) }
if (orderSneaker != null) {
orderRepository.insertOrderSneaker(orderSneaker)
}
}
city.value = ""
street.value = ""
house.value = ""
}
fun getSubTotal(): Double {
return 0.0
fun updateSubTotal(userId: Int) {
viewModelScope.launch {
_subTotal.value = getSubTotal(userId)
}
}
suspend fun getSubTotal(userId: Int): Double {
return basketRepository.getTotalPriceForUser(userId) ?: 0.0
}
}