exception handling

This commit is contained in:
dasha 2023-12-18 01:43:48 +04:00
parent 42c21b0ce7
commit c0b94141a3
23 changed files with 493 additions and 75189 deletions

View File

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetDropDown">
<runningDeviceTargetSelectedWithDropDown>
<Target>
<type value="RUNNING_DEVICE_TARGET" />
<deviceKey>
<Key>
<type value="SERIAL_NUMBER" />
<value value="KFRSEQ6DTWWWQOE6" />
</Key>
</deviceKey>
</Target>
</runningDeviceTargetSelectedWithDropDown>
<timeTargetWasSelectedWithDropDown value="2023-12-17T14:14:18.887820Z" />
</component>
</project>

View File

@ -1,5 +1,6 @@
package com.example.myapplication package com.example.myapplication
import android.content.Context
import android.os.Bundle import android.os.Bundle
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
@ -20,6 +21,7 @@ class MainComposeActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
application.deleteDatabase("pmy-db") application.deleteDatabase("pmy-db")
appContext = applicationContext
setContent { setContent {
PmudemoTheme(darkTheme = isDarkTheme.value) { PmudemoTheme(darkTheme = isDarkTheme.value) {
LaunchedEffect(key1 = true) { LaunchedEffect(key1 = true) {
@ -40,4 +42,7 @@ class MainComposeActivity : ComponentActivity() {
} }
} }
} }
companion object {
lateinit var appContext: Context
}
} }

View File

@ -15,21 +15,17 @@ fun Authenticator(
viewModel: AuthenticatorViewModel = viewModel(factory = AppViewModelProvider.Factory) viewModel: AuthenticatorViewModel = viewModel(factory = AppViewModelProvider.Factory)
) { ) {
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val login = dataStoreManager.getLogin().collectAsState(initial = "").value val login = dataStoreManager.getLogin().collectAsState(initial = "").value
LiveStore.user.value = viewModel.authUiState.user
fun synchronize() { fun synchronize() {
scope.launch { scope.launch {
if (login == "") { if (login == "") {
LiveStore.user.value = null LiveStore.user.value = null
return@launch return@launch
} }
val overlap = viewModel.findUserByLogin(login) viewModel.findUserByLogin(login)
if (overlap == null) {
dataStoreManager.setLogin("")
return@launch
}
LiveStore.user.value = overlap
} }
} }

View File

@ -1,18 +1,27 @@
package com.example.myapplication.composeui package com.example.myapplication.composeui
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import com.example.myapplication.database.entities.model.User import com.example.myapplication.database.entities.model.User
import com.example.myapplication.database.entities.repository.UserRepository import com.example.myapplication.database.entities.repository.UserRepository
class AuthenticatorViewModel( class AuthenticatorViewModel(
private val userRepository: UserRepository private val userRepository: UserRepository
) : MyViewModel() { ) : MyViewModel() {
suspend fun findUserByLogin(login: String): User? { var authUiState by mutableStateOf(AuthenticatorUiState())
var user: User? = null private set
suspend fun findUserByLogin(login: String) {
runInScope( runInScope(
actionSuccess = { actionSuccess = {
user = userRepository.getUser(login) authUiState = AuthenticatorUiState(userRepository.getUser(login))
},
actionError = {
authUiState = AuthenticatorUiState()
} }
) )
return user
} }
} }
data class AuthenticatorUiState(val user: User? = null)

View File

@ -60,7 +60,6 @@ fun Cart(
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
viewModel.refreshState() viewModel.refreshState()
} }
Cart( Cart(
cartUiState = cartUiState, cartUiState = cartUiState,
modifier = Modifier modifier = Modifier

View File

@ -3,7 +3,6 @@ package com.example.myapplication.composeui
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import com.example.myapplication.LiveStore import com.example.myapplication.LiveStore
import com.example.myapplication.database.entities.model.Order import com.example.myapplication.database.entities.model.Order
import com.example.myapplication.database.entities.model.OrderSessionCrossRef import com.example.myapplication.database.entities.model.OrderSessionCrossRef
@ -21,15 +20,21 @@ class CartViewModel(
private val orderRepository: OrderRepository, private val orderRepository: OrderRepository,
private val orderSessionRepository: OrderSessionRepository, private val orderSessionRepository: OrderSessionRepository,
private val userRepository: UserRepository, private val userRepository: UserRepository,
) : ViewModel() { ) : MyViewModel() {
private var isLoading: Boolean = false private var isLoading: Boolean = false
var cartUiState by mutableStateOf(CartUiState()) var cartUiState by mutableStateOf(CartUiState())
private set private set
suspend fun refreshState() { suspend fun refreshState() {
val userId: Int = LiveStore.user.value?.uid ?: 0 val userId: Int = LiveStore.user.value?.uid ?: 0
runInScope(
actionSuccess = {
val cart = userRepository.getCartByUser(userId) val cart = userRepository.getCartByUser(userId)
cartUiState = CartUiState(cart) cartUiState = CartUiState(cart)
}, actionError = {
cartUiState = CartUiState()
}
)
} }
suspend fun addToOrder(sessions: List<SessionFromCart>) { suspend fun addToOrder(sessions: List<SessionFromCart>) {
@ -39,6 +44,8 @@ class CartViewModel(
val userId: Int = LiveStore.user.value?.uid ?: return val userId: Int = LiveStore.user.value?.uid ?: return
if (sessions.isEmpty()) if (sessions.isEmpty())
return return
runInScope(
actionSuccess = {
val orderId = orderRepository.insertOrder(Order(0, userId, LocalDateTime.now())) val orderId = orderRepository.insertOrder(Order(0, userId, LocalDateTime.now()))
sessions.forEach { session -> sessions.forEach { session ->
orderSessionRepository.insertOrderSession( orderSessionRepository.insertOrderSession(
@ -53,24 +60,45 @@ class CartViewModel(
userSessionRepository.deleteUserSessions(userId) userSessionRepository.deleteUserSessions(userId)
refreshState() refreshState()
} }
)
}
suspend fun removeFromCart(session: Session, count: Int = 0) { suspend fun removeFromCart(session: Session, count: Int = 0) {
val userId: Int = LiveStore.user.value?.uid ?: return val userId: Int = LiveStore.user.value?.uid ?: return
userSessionRepository.deleteUserSession(UserSessionCrossRef(userId, session.uid, count)) runInScope(
actionSuccess = {
userSessionRepository.deleteUserSession(
UserSessionCrossRef(
userId,
session.uid,
count
)
)
refreshState() refreshState()
} }
)
}
suspend fun updateFromCart(session: Session, count: Int, availableCount: Int): Boolean { suspend fun updateFromCart(session: Session, count: Int, availableCount: Int) {
val userId: Int = LiveStore.user.value?.uid ?: return false val userId: Int = LiveStore.user.value?.uid ?: return
if (count == 0) { if (count == 0) {
removeFromCart(session, count) removeFromCart(session, count)
return false return
} }
if (count > availableCount) if (count > availableCount)
return false return
userSessionRepository.updateUserSession(UserSessionCrossRef(userId, session.uid, count)) runInScope(
actionSuccess = {
userSessionRepository.updateUserSession(
UserSessionCrossRef(
userId,
session.uid,
count
)
)
refreshState() refreshState()
return true }
)
} }
} }

View File

@ -25,6 +25,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.myapplication.api.ApiStatus
import com.example.myapplication.api.session.ReportRemote import com.example.myapplication.api.session.ReportRemote
import com.example.myapplication.database.entities.composeui.AppViewModelProvider import com.example.myapplication.database.entities.composeui.AppViewModelProvider
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -39,6 +40,9 @@ fun Report(
val dateStateStart = rememberDatePickerState(initialDisplayMode = DisplayMode.Input) val dateStateStart = rememberDatePickerState(initialDisplayMode = DisplayMode.Input)
val dateStateEnd = rememberDatePickerState(initialDisplayMode = DisplayMode.Input) val dateStateEnd = rememberDatePickerState(initialDisplayMode = DisplayMode.Input)
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
when (viewModel.apiStatus) {
ApiStatus.DONE -> {
Column( Column(
modifier = Modifier modifier = Modifier
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
@ -93,6 +97,14 @@ fun Report(
} }
} }
ApiStatus.LOADING -> LoadingPlaceholder()
else -> ErrorPlaceholder(
message = viewModel.apiError,
onBack = { }
)
}
}
@Composable @Composable
fun CardScreen(reportData: List<ReportRemote>) { fun CardScreen(reportData: List<ReportRemote>) {
val dateFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm") val dateFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm")

View File

@ -3,12 +3,11 @@ package com.example.myapplication.composeui
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import com.example.myapplication.api.session.ReportRemote import com.example.myapplication.api.session.ReportRemote
import com.example.myapplication.api.session.RestSessionRepository import com.example.myapplication.api.session.RestSessionRepository
import java.util.Date import java.util.Date
class ReportViewModel(private val serialRepository: RestSessionRepository) : ViewModel() { class ReportViewModel(private val serialRepository: RestSessionRepository) : MyViewModel() {
var reportUiState by mutableStateOf(ReportUiState()) var reportUiState by mutableStateOf(ReportUiState())
private set private set
@ -32,11 +31,17 @@ class ReportViewModel(private val serialRepository: RestSessionRepository) : Vie
suspend fun getReport() { suspend fun getReport() {
if (validateInput()) { if (validateInput()) {
runInScope(
actionSuccess = {
val temp = serialRepository.getReport( val temp = serialRepository.getReport(
reportUiState.reportDetails.startDate, reportUiState.reportDetails.startDate,
reportUiState.reportDetails.endDate reportUiState.reportDetails.endDate
) )
reportResultUiState = ReportResultUiState(temp) reportResultUiState = ReportResultUiState(temp)
}, actionError = {
reportResultUiState = ReportResultUiState()
}
)
} }
} }
} }

View File

@ -93,10 +93,10 @@ fun Topbar(
Spacer(modifier = Modifier.width(16.dp)) Spacer(modifier = Modifier.width(16.dp))
if (currentScreen?.route == Screen.CinemaList.route) {
Search( Search(
initValue = LiveStore.searchRequest.value ?: "", initValue = LiveStore.searchRequest.value ?: "",
onDone = { onDone = {
navController.navigate(Screen.CinemaList.route)
LiveStore.searchRequest.value = it LiveStore.searchRequest.value = it
}, },
modifier = Modifier modifier = Modifier
@ -111,7 +111,6 @@ fun Topbar(
} }
} }
} }
}
@Composable @Composable
fun Navbar( fun Navbar(
@ -188,7 +187,7 @@ fun Navhost(
Screen.OrderView.route, Screen.OrderView.route,
arguments = listOf(navArgument("id") { type = NavType.IntType }) arguments = listOf(navArgument("id") { type = NavType.IntType })
) { backStackEntry -> ) { backStackEntry ->
backStackEntry.arguments?.let { OrderView(it.getInt("id")) } backStackEntry.arguments?.let { OrderView(navController) }
} }
composable(Screen.Report.route) { Report() } composable(Screen.Report.route) { Report() }
} }

View File

@ -56,7 +56,6 @@ fun CinemaList(
LaunchedEffect(searchPattern.value) { LaunchedEffect(searchPattern.value) {
viewModel.refresh() viewModel.refresh()
} }
Scaffold( Scaffold(
topBar = {}, topBar = {},
floatingActionButton = { floatingActionButton = {
@ -99,6 +98,7 @@ fun CinemaList(
} }
} }
@Composable @Composable
private fun CinemaList( private fun CinemaList(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,

View File

@ -21,12 +21,16 @@ class CinemaListViewModel(
fun refresh() { fun refresh() {
val name = "%${LiveStore.searchRequest.value}%" val name = "%${LiveStore.searchRequest.value}%"
runInScope(actionSuccess = {
val pagingSource = cinemaRepository.getAllCinemas(name) val pagingSource = cinemaRepository.getAllCinemas(name)
cinemaPagingFlow = CinemaPagingFlowState(pagingSource.cachedIn(viewModelScope)) cinemaPagingFlow = CinemaPagingFlowState(pagingSource.cachedIn(viewModelScope))
})
} }
suspend fun deleteCinema(cinema: Cinema) { suspend fun deleteCinema(cinema: Cinema) {
runInScope(actionSuccess = {
cinemaRepository.deleteCinema(cinema) cinemaRepository.deleteCinema(cinema)
})
} }
} }

View File

@ -31,6 +31,9 @@ import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController import androidx.navigation.NavController
import com.example.myapplication.LiveStore import com.example.myapplication.LiveStore
import com.example.myapplication.api.ApiStatus
import com.example.myapplication.composeui.ErrorPlaceholder
import com.example.myapplication.composeui.LoadingPlaceholder
import com.example.myapplication.composeui.navigation.Screen import com.example.myapplication.composeui.navigation.Screen
import com.example.myapplication.database.entities.model.Cinema import com.example.myapplication.database.entities.model.Cinema
import com.example.myapplication.database.entities.model.UserRole import com.example.myapplication.database.entities.model.UserRole
@ -42,11 +45,11 @@ fun CinemaView(
) { ) {
val cinemaUiState = viewModel.cinemaUiState val cinemaUiState = viewModel.cinemaUiState
val user = LiveStore.user.observeAsState() val user = LiveStore.user.observeAsState()
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
viewModel.refreshState() viewModel.refreshState()
} }
when (viewModel.apiStatus) {
ApiStatus.DONE -> {
Column( Column(
modifier = Modifier modifier = Modifier
.padding(16.dp) .padding(16.dp)
@ -143,3 +146,11 @@ fun CinemaView(
} }
} }
} }
ApiStatus.LOADING -> LoadingPlaceholder()
else -> ErrorPlaceholder(
message = viewModel.apiError,
onBack = { navController.popBackStack() }
)
}
}

View File

@ -4,13 +4,13 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel import com.example.myapplication.composeui.MyViewModel
import com.example.myapplication.database.entities.model.CinemaWithSessions import com.example.myapplication.database.entities.model.CinemaWithSessions
import com.example.myapplication.database.entities.repository.CinemaRepository import com.example.myapplication.database.entities.repository.CinemaRepository
class CinemaViewModel( class CinemaViewModel(
savedStateHandle: SavedStateHandle, private val cinemaRepository: CinemaRepository savedStateHandle: SavedStateHandle, private val cinemaRepository: CinemaRepository
) : ViewModel() { ) : MyViewModel() {
private val cinemaUid: Int = checkNotNull(savedStateHandle["id"]) private val cinemaUid: Int = checkNotNull(savedStateHandle["id"])
var cinemaUiState by mutableStateOf(CinemaUiState()) var cinemaUiState by mutableStateOf(CinemaUiState())
@ -18,7 +18,11 @@ class CinemaViewModel(
suspend fun refreshState() { suspend fun refreshState() {
if (cinemaUid > 0) { if (cinemaUid > 0) {
runInScope(actionSuccess = {
cinemaUiState = CinemaUiState(cinemaRepository.getCinema(cinemaUid)) cinemaUiState = CinemaUiState(cinemaRepository.getCinema(cinemaUid))
}, actionError = {
cinemaUiState = CinemaUiState()
})
} }
} }
} }

View File

@ -1,7 +1,7 @@
package com.example.myapplication.database.entities.composeui package com.example.myapplication.database.entities.composeui
import androidx.lifecycle.ViewModel
import androidx.paging.PagingData import androidx.paging.PagingData
import com.example.myapplication.composeui.MyViewModel
import com.example.myapplication.database.entities.model.Order import com.example.myapplication.database.entities.model.Order
import com.example.myapplication.database.entities.repository.OrderRepository import com.example.myapplication.database.entities.repository.OrderRepository
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@ -9,10 +9,12 @@ import kotlinx.coroutines.flow.emptyFlow
class OrderListViewModel( class OrderListViewModel(
private val orderRepository: OrderRepository private val orderRepository: OrderRepository
) : ViewModel() { ) : MyViewModel() {
var orderListUiState: Flow<PagingData<Order>> = emptyFlow() var orderListUiState: Flow<PagingData<Order>> = emptyFlow()
fun refreshState(userId: Int = 0) { fun refreshState(userId: Int = 0) {
runInScope(actionSuccess = {
orderListUiState = orderRepository.getAllOrders(userId) orderListUiState = orderRepository.getAllOrders(userId)
})
} }
} }

View File

@ -1,6 +1,5 @@
package com.example.myapplication.database.entities.composeui package com.example.myapplication.database.entities.composeui
import android.content.res.Configuration
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
@ -15,37 +14,41 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.myapplication.ui.theme.PmudemoTheme import androidx.navigation.NavController
import com.example.myapplication.api.ApiStatus
import com.example.myapplication.composeui.ErrorPlaceholder
import com.example.myapplication.composeui.LoadingPlaceholder
import org.threeten.bp.format.DateTimeFormatter import org.threeten.bp.format.DateTimeFormatter
@Composable @Composable
fun OrderView( fun OrderView(
id: Int, navController: NavController,
viewModel: OrderViewModel = viewModel(factory = AppViewModelProvider.Factory) viewModel: OrderViewModel = viewModel(factory = AppViewModelProvider.Factory)
) { ) {
val coroutineScope = rememberCoroutineScope() val orderUiState = viewModel.orderUiState
val orderUiState by viewModel.orderUiState.collectAsState()
LaunchedEffect(Unit) {
viewModel.refreshState()
}
when (viewModel.apiStatus) {
ApiStatus.DONE -> {
LazyColumn( LazyColumn(
modifier = Modifier modifier = Modifier
.padding(10.dp) .padding(10.dp)
) { ) {
items(orderUiState.sessionList) { session -> items(orderUiState.sessionList) { session ->
val count = remember { mutableStateOf(session.count) } val count = remember { mutableIntStateOf(session.count) }
val dateFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm") val dateFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm")
val formattedDate = dateFormatter.format(session.dateTime) val formattedDate = dateFormatter.format(session.dateTime)
@ -89,7 +92,7 @@ fun OrderView(
Text( Text(
text = "${session.cinema.name}, ${session.cinema.year}\n" + text = "${session.cinema.name}, ${session.cinema.year}\n" +
"Цена: ${session.frozenPrice}\n" + "Цена: ${session.frozenPrice}\n" +
"Количество: ${count.value}", "Количество: $count",
color = MaterialTheme.colorScheme.onSecondary color = MaterialTheme.colorScheme.onSecondary
) )
} }
@ -99,16 +102,10 @@ fun OrderView(
} }
} }
ApiStatus.LOADING -> LoadingPlaceholder()
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO) else -> ErrorPlaceholder(
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES) message = viewModel.apiError,
@Composable onBack = { navController.popBackStack() }
fun OrderViewPreview() { )
PmudemoTheme {
Surface(
color = MaterialTheme.colorScheme.background
) {
OrderView(id = 1)
}
} }
} }

View File

@ -1,31 +1,29 @@
package com.example.myapplication.database.entities.composeui package com.example.myapplication.database.entities.composeui
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel import com.example.myapplication.composeui.MyViewModel
import androidx.lifecycle.viewModelScope
import com.example.myapplication.database.AppContainer
import com.example.myapplication.database.entities.model.SessionFromOrder import com.example.myapplication.database.entities.model.SessionFromOrder
import com.example.myapplication.database.entities.repository.OrderRepository import com.example.myapplication.database.entities.repository.OrderRepository
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
class OrderViewModel( class OrderViewModel(
savedStateHandle: SavedStateHandle, savedStateHandle: SavedStateHandle,
private val orderRepository: OrderRepository private val orderRepository: OrderRepository
) : ViewModel() { ) : MyViewModel() {
private val orderUid: Int = checkNotNull(savedStateHandle["id"]) private val orderUid: Int = checkNotNull(savedStateHandle["id"])
val orderUiState: StateFlow<OrderUiState> = var orderUiState by mutableStateOf(OrderUiState())
flow { emit(orderRepository.getOrder(orderUid)) }.map { private set
OrderUiState(it)
}.stateIn( suspend fun refreshState() {
scope = viewModelScope, runInScope(actionSuccess = {
started = SharingStarted.WhileSubscribed(stopTimeoutMillis = AppContainer.TIMEOUT), orderUiState = OrderUiState(orderRepository.getOrder(orderUid))
initialValue = OrderUiState() }, actionError = {
) orderUiState = OrderUiState()
})
}
} }
data class OrderUiState(val sessionList: List<SessionFromOrder> = listOf()) data class OrderUiState(val sessionList: List<SessionFromOrder> = listOf())

View File

@ -1,7 +1,7 @@
package com.example.myapplication.database.entities.composeui package com.example.myapplication.database.entities.composeui
import androidx.lifecycle.ViewModel
import com.example.myapplication.LiveStore import com.example.myapplication.LiveStore
import com.example.myapplication.composeui.MyViewModel
import com.example.myapplication.database.entities.model.Session import com.example.myapplication.database.entities.model.Session
import com.example.myapplication.database.entities.model.SessionFromCinema import com.example.myapplication.database.entities.model.SessionFromCinema
import com.example.myapplication.database.entities.model.UserSessionCrossRef import com.example.myapplication.database.entities.model.UserSessionCrossRef
@ -11,8 +11,9 @@ import com.example.myapplication.database.entities.repository.UserSessionReposit
class SessionListViewModel( class SessionListViewModel(
private val sessionRepository: SessionRepository, private val sessionRepository: SessionRepository,
private val userSessionRepository: UserSessionRepository private val userSessionRepository: UserSessionRepository
) : ViewModel() { ) : MyViewModel() {
suspend fun deleteSession(session: SessionFromCinema) { suspend fun deleteSession(session: SessionFromCinema) {
runInScope(actionSuccess = {
sessionRepository.deleteSession( sessionRepository.deleteSession(
Session( Session(
uid = session.uid, uid = session.uid,
@ -22,14 +23,23 @@ class SessionListViewModel(
cinemaId = 0 cinemaId = 0
) )
) )
})
} }
suspend fun addSessionInCart(sessionId: Int, count: Int = 1) { suspend fun addSessionInCart(sessionId: Int, count: Int = 1) {
try {
val userId: Int = LiveStore.user.value?.uid ?: return val userId: Int = LiveStore.user.value?.uid ?: return
userSessionRepository.insertUserSession(UserSessionCrossRef(userId, sessionId, count)) runInScope(actionSuccess = {
try {
userSessionRepository.insertUserSession(
UserSessionCrossRef(
userId,
sessionId,
count
)
)
} catch (_: Exception) { } catch (_: Exception) {
} }
})
} }
} }

View File

@ -34,6 +34,7 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController import androidx.navigation.NavController
import com.example.myapplication.LiveStore import com.example.myapplication.LiveStore
import com.example.myapplication.composeui.navigation.Screen
import com.example.myapplication.datastore.DataStoreManager import com.example.myapplication.datastore.DataStoreManager
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -47,11 +48,15 @@ fun UserProfile(
var isRegistration by remember { mutableStateOf(false) } var isRegistration by remember { mutableStateOf(false) }
val coroutine = rememberCoroutineScope() val coroutine = rememberCoroutineScope()
val errorStringId: Int? = viewModel.userUiState.errorId val errorStringId: Int? = viewModel.userUiState.errorId
val errorMessage = if (errorStringId == null) "" else stringResource(errorStringId) val errorMessage =
if (errorStringId == null || errorStringId == 0) "" else stringResource(errorStringId)
val user = LiveStore.user.observeAsState() val user = LiveStore.user.observeAsState()
LazyColumn { LazyColumn {
item { item {
if (errorStringId == 0) {
navController.navigate(Screen.CinemaList.route)
}
if (user.value != null) { if (user.value != null) {
Column( Column(
modifier = Modifier modifier = Modifier
@ -151,7 +156,7 @@ fun UserProfile(
if (isRegistration) { if (isRegistration) {
Button( Button(
onClick = { coroutine.launch { isRegistration = !viewModel.signUp() } }, onClick = { coroutine.launch { viewModel.signUp() } },
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(8.dp) .padding(8.dp)
@ -169,13 +174,7 @@ fun UserProfile(
) )
} else { } else {
Button( Button(
onClick = { onClick = { coroutine.launch { viewModel.signIn(dataStoreManager) } },
coroutine.launch {
if (viewModel.signIn(dataStoreManager)) {
navController.popBackStack()
}
}
},
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(8.dp) .padding(8.dp)

View File

@ -1,17 +1,19 @@
package com.example.myapplication.database.entities.composeui package com.example.myapplication.database.entities.composeui
import android.widget.Toast
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel import com.example.myapplication.MainComposeActivity
import com.example.myapplication.R import com.example.myapplication.R
import com.example.myapplication.composeui.MyViewModel
import com.example.myapplication.database.entities.model.User import com.example.myapplication.database.entities.model.User
import com.example.myapplication.database.entities.repository.UserRepository import com.example.myapplication.database.entities.repository.UserRepository
import com.example.myapplication.datastore.DataStoreManager import com.example.myapplication.datastore.DataStoreManager
class UserProfileViewModel( class UserProfileViewModel(
private val userRepository: UserRepository private val userRepository: UserRepository
) : ViewModel() { ) : MyViewModel() {
var userUiState by mutableStateOf(UserUiState()) var userUiState by mutableStateOf(UserUiState())
private set private set
@ -22,28 +24,36 @@ class UserProfileViewModel(
) )
} }
suspend fun signIn(dataStoreManager: DataStoreManager): Boolean { suspend fun signIn(dataStoreManager: DataStoreManager) {
userUiState.details.passwordConfirm = userUiState.details.password userUiState.details.passwordConfirm = userUiState.details.password
var errorId: Int? = validateInput(userUiState.details) var errorId: Int? = validateInput(userUiState.details)
runInScope(
actionSuccess = {
if (errorId == null) { if (errorId == null) {
val overlap = userRepository.getUser(userUiState.details.login) val overlap: User? = userRepository.getUser(userUiState.details.login)
if (overlap == null || userUiState.details.password != overlap.password) { if (overlap == null || userUiState.details.password != overlap.password) {
errorId = R.string.err_04 errorId = R.string.err_04
} else {
dataStoreManager.setLogin(userUiState.details.login)
errorId = 0
} }
} }
userUiState = UserUiState( userUiState = UserUiState(
details = userUiState.details, details = userUiState.details,
errorId = errorId errorId = errorId
) )
if (errorId == null) { }, actionError = {
dataStoreManager.setLogin(userUiState.details.login) errorId = R.string.err_06
return true userUiState = UserUiState(
} details = userUiState.details,
return false errorId = errorId
)
})
} }
suspend fun signUp(): Boolean { suspend fun signUp() {
var errorId: Int? = validateInput(userUiState.details) var errorId: Int? = validateInput(userUiState.details)
runInScope(actionSuccess = {
if (errorId == null) { if (errorId == null) {
val overlap = userRepository.getUser(userUiState.details.login) val overlap = userRepository.getUser(userUiState.details.login)
if (overlap != null) { if (overlap != null) {
@ -56,9 +66,20 @@ class UserProfileViewModel(
) )
if (errorId == null) { if (errorId == null) {
userRepository.insertUser(userUiState.details.toUser()) userRepository.insertUser(userUiState.details.toUser())
return true val toast = Toast.makeText(
MainComposeActivity.appContext,
"Вы зарегистрированы",
Toast.LENGTH_SHORT
)
toast.show()
} }
return false }, actionError = {
errorId = R.string.err_06
userUiState = UserUiState(
details = userUiState.details,
errorId = errorId
)
})
} }
private fun validateInput(details: UserDetails = userUiState.details): Int? { private fun validateInput(details: UserDetails = userUiState.details): Int? {

View File

@ -22,6 +22,9 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController import androidx.navigation.NavController
import com.example.myapplication.R import com.example.myapplication.R
import com.example.myapplication.api.ApiStatus
import com.example.myapplication.composeui.ErrorPlaceholder
import com.example.myapplication.composeui.LoadingPlaceholder
import com.example.myapplication.database.entities.composeui.AppViewModelProvider import com.example.myapplication.database.entities.composeui.AppViewModelProvider
import com.example.myapplication.ui.theme.PmudemoTheme import com.example.myapplication.ui.theme.PmudemoTheme
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -32,7 +35,8 @@ fun CinemaEdit(
viewModel: CinemaEditViewModel = viewModel(factory = AppViewModelProvider.Factory), viewModel: CinemaEditViewModel = viewModel(factory = AppViewModelProvider.Factory),
) { ) {
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
when (viewModel.apiStatus) {
ApiStatus.DONE -> {
CinemaEdit( CinemaEdit(
cinemaUiState = viewModel.cinemaUiState, cinemaUiState = viewModel.cinemaUiState,
onClick = { onClick = {
@ -44,6 +48,13 @@ fun CinemaEdit(
onUpdate = viewModel::updateUiState, onUpdate = viewModel::updateUiState,
) )
} }
ApiStatus.LOADING -> LoadingPlaceholder()
else -> ErrorPlaceholder(
message = viewModel.apiError,
onBack = { navController.popBackStack() }
)
}
}
@Composable @Composable
private fun CinemaEdit( private fun CinemaEdit(

View File

@ -24,6 +24,9 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController import androidx.navigation.NavController
import com.example.myapplication.R import com.example.myapplication.R
import com.example.myapplication.api.ApiStatus
import com.example.myapplication.composeui.ErrorPlaceholder
import com.example.myapplication.composeui.LoadingPlaceholder
import com.example.myapplication.database.entities.composeui.AppViewModelProvider import com.example.myapplication.database.entities.composeui.AppViewModelProvider
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.threeten.bp.Instant import org.threeten.bp.Instant
@ -39,6 +42,8 @@ fun SessionEdit(
viewModel: SessionEditViewModel = viewModel(factory = AppViewModelProvider.Factory), viewModel: SessionEditViewModel = viewModel(factory = AppViewModelProvider.Factory),
) { ) {
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
when (viewModel.apiStatus) {
ApiStatus.DONE -> {
SessionEdit( SessionEdit(
sessionUiState = viewModel.sessionUiState, sessionUiState = viewModel.sessionUiState,
onClick = { onClick = {
@ -51,6 +56,14 @@ fun SessionEdit(
) )
} }
ApiStatus.LOADING -> LoadingPlaceholder()
else -> ErrorPlaceholder(
message = viewModel.apiError,
onBack = { navController.popBackStack() }
)
}
}
fun Long.toLocalDate(): org.threeten.bp.LocalDate { fun Long.toLocalDate(): org.threeten.bp.LocalDate {
val instant = Instant.ofEpochMilli(this) val instant = Instant.ofEpochMilli(this)
return instant.atZone(ZoneId.systemDefault()).toLocalDate() return instant.atZone(ZoneId.systemDefault()).toLocalDate()

View File

@ -25,6 +25,7 @@
<string name="err_03">Логин занят</string> <string name="err_03">Логин занят</string>
<string name="err_04">Неверный логин или пароль</string> <string name="err_04">Неверный логин или пароль</string>
<string name="err_05">Не совпадают пароли</string> <string name="err_05">Не совпадают пароли</string>
<string name="err_06">Ошибка сети</string>
<string name="back">Назад</string> <string name="back">Назад</string>
<string name="loading">Загрузка…</string> <string name="loading">Загрузка…</string>
</resources> </resources>

File diff suppressed because it is too large Load Diff