Сделал на 2 листа пагинацию, хохо

This commit is contained in:
Кашин Максим 2023-12-27 01:15:26 +04:00
parent c7f73c4d5a
commit 68d533c81b
19 changed files with 211 additions and 28 deletions

View File

@ -0,0 +1,5 @@
package com.example.myapplication.api
enum class ApiStatus {
LOADING, ERROR, DONE
}

View File

@ -29,6 +29,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@ -55,11 +56,15 @@ fun BikeList(
currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory) currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)
) { ) {
var getUser by remember { mutableStateOf(currentUserViewModel.user) } var getUser by remember { mutableStateOf(currentUserViewModel.user) }
val bikePagingItems = viewModel.bikePagingFlowState.flow.collectAsLazyPagingItems()
LaunchedEffect(getUser?.uid) {
viewModel.refresh()
}
// Проверяем, есть ли у пользователя uid // Проверяем, есть ли у пользователя uid
if (getUser?.uid != null) { if (getUser?.uid != null) {
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
val bikePagingItems = viewModel.bikeListUiState.collectAsLazyPagingItems()
Scaffold( Scaffold(
topBar = {}, topBar = {},

View File

@ -1,17 +1,37 @@
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.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.PagingData import androidx.paging.PagingData
import androidx.paging.cachedIn
import com.example.myapplication.database.entities.model.Bike import com.example.myapplication.database.entities.model.Bike
import com.example.myapplication.database.entities.repository.BikeRepository import com.example.myapplication.database.entities.repository.BikeRepository
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow
class BikeListViewModel( class BikeListViewModel(
private val bikeRepository: BikeRepository private val bikeRepository: BikeRepository
) : ViewModel() { ) : LoadingViewModel() {
val bikeListUiState: Flow<PagingData<Bike>> = bikeRepository.getAllBikes() var bikePagingFlowState by mutableStateOf(BikePagingFlowState())
private set
fun refresh() {
runInScope(actionSuccess = {
val pagingSource = bikeRepository.getAllBikes()
bikePagingFlowState = BikePagingFlowState(pagingSource.cachedIn(viewModelScope))
})
}
suspend fun deleteBike(bike: Bike) { suspend fun deleteBike(bike: Bike) {
bikeRepository.deleteBike(bike) runInScope(actionSuccess = {
bikeRepository.deleteBike(bike)
})
} }
} }
data class BikePagingFlowState(
val flow: Flow<PagingData<Bike>> = emptyFlow(),
)

View File

@ -34,6 +34,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp 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.api.ApiStatus
import com.example.myapplication.composeui.navigation.Screen import com.example.myapplication.composeui.navigation.Screen
import com.example.myapplication.database.entities.model.Bike import com.example.myapplication.database.entities.model.Bike
@ -50,7 +51,8 @@ fun BikeView(
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)
@ -148,3 +150,12 @@ fun BikeView(
} }
} }
} }
ApiStatus.LOADING -> LoadingPage()
else -> ErrorPage(
message = viewModel.apiError,
onBack = { navController.popBackStack() }
)
}
}

View File

@ -10,7 +10,7 @@ import com.example.myapplication.database.entities.repository.BikeRepository
class BikeViewModel( class BikeViewModel(
savedStateHandle: SavedStateHandle, private val bikeRepository: BikeRepository savedStateHandle: SavedStateHandle, private val bikeRepository: BikeRepository
) : ViewModel() { ) : LoadingViewModel() {
private val bikeUid: Int = checkNotNull(savedStateHandle["id"]) private val bikeUid: Int = checkNotNull(savedStateHandle["id"])
var bikeUiState by mutableStateOf(BikeUiState()) var bikeUiState by mutableStateOf(BikeUiState())
@ -18,7 +18,11 @@ class BikeViewModel(
suspend fun refreshState() { suspend fun refreshState() {
if (bikeUid > 0) { if (bikeUid > 0) {
bikeUiState = BikeUiState(bikeRepository.getBike(bikeUid)) runInScope(actionSuccess = {
bikeUiState = BikeUiState(bikeRepository.getBike(bikeUid))
}, actionError = {
bikeUiState = BikeUiState()
})
} }
} }

View File

@ -20,7 +20,7 @@ class CartViewModel(
private val rentRepository: RentRepository, private val rentRepository: RentRepository,
private val rentItemRepository: RentItemRepository, private val rentItemRepository: RentItemRepository,
private val userRepository: UserRepository, private val userRepository: UserRepository,
) : ViewModel() { ) : LoadingViewModel() {
//private val userUid: Int = 1 //private val userUid: Int = 1
var cartUiState by mutableStateOf(CartUiState()) var cartUiState by mutableStateOf(CartUiState())
private set private set

View File

@ -9,7 +9,7 @@ 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 kotlinx.coroutines.launch import kotlinx.coroutines.launch
class CurrentUserViewModel(private val userRepository: UserRepository) : ViewModel(){ class CurrentUserViewModel(private val userRepository: UserRepository) : LoadingViewModel(){
val argument = mutableStateOf<String?>(null) val argument = mutableStateOf<String?>(null)
private val userid = mutableStateOf<Int?>(null) private val userid = mutableStateOf<Int?>(null)
var user by mutableStateOf<User?>(null) var user by mutableStateOf<User?>(null)

View File

@ -9,7 +9,7 @@ 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 kotlinx.coroutines.launch import kotlinx.coroutines.launch
class EntryUserViewModel(private val userRepository: UserRepository) : ViewModel() { class EntryUserViewModel(private val userRepository: UserRepository) : LoadingViewModel() {
var userList by mutableStateOf<List<User>>(emptyList()) var userList by mutableStateOf<List<User>>(emptyList())
fun setUserList() { fun setUserList() {

View File

@ -10,7 +10,7 @@ import com.example.myapplication.database.entities.repository.UserItemRepository
class ItemListViewModel( class ItemListViewModel(
private val itemRepository: ItemRepository, private val itemRepository: ItemRepository,
private val userItemRepository: UserItemRepository private val userItemRepository: UserItemRepository
) : ViewModel() { ) : LoadingViewModel() {
suspend fun deleteItem(item: ItemFromBike) { suspend fun deleteItem(item: ItemFromBike) {
itemRepository.deleteItem( itemRepository.deleteItem(
Item( Item(

View File

@ -0,0 +1,64 @@
package com.example.myapplication.database.entities.composeui
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
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.Text
import androidx.compose.runtime.Composable
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.unit.TextUnit
import androidx.compose.ui.unit.TextUnitType
import androidx.compose.ui.unit.dp
import com.example.myapplication.R
@Composable
fun LoadingPage() {
Column(
modifier = Modifier
.fillMaxSize()
.padding(10.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center,
fontSize = TextUnit(value = 25F, type = TextUnitType.Sp),
text = stringResource(id = R.string.loading)
)
}
}
@Composable
fun ErrorPage(message: String, onBack: () -> Unit) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(10.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center,
fontSize = TextUnit(value = 20F, type = TextUnitType.Sp),
text = message,
color = Color(0xFFFF1744)
)
Spacer(modifier = Modifier.padding(bottom = 10.dp))
Button(
modifier = Modifier.fillMaxWidth(),
onClick = { onBack() }
) {
Text(stringResource(id = R.string.back))
}
}
}

View File

@ -0,0 +1,50 @@
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.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.myapplication.api.ApiStatus
import kotlinx.coroutines.launch
import retrofit2.HttpException
import java.io.IOException
open class LoadingViewModel : ViewModel() {
var apiStatus by mutableStateOf(ApiStatus.DONE)
private set
var apiError by mutableStateOf("")
private set
fun runInScope(
actionSuccess: suspend () -> Unit,
actionError: suspend () -> Unit,
needLoadingScreen: Boolean = true,
) {
viewModelScope.launch {
if (needLoadingScreen)
apiStatus = ApiStatus.LOADING
runCatching {
actionSuccess()
apiStatus = ApiStatus.DONE
apiError = ""
}.onFailure { e: Throwable ->
when (e) {
is IOException,
is HttpException -> {
actionError()
apiStatus = ApiStatus.ERROR
apiError = e.localizedMessage ?: e.toString()
}
else -> throw e
}
}
}
}
fun runInScope(actionSuccess: suspend () -> Unit) {
runInScope(actionSuccess, actionError = {})
}
}

View File

@ -8,7 +8,7 @@ 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 kotlinx.coroutines.launch import kotlinx.coroutines.launch
class RegisterUserViewModel(private val userRepository: UserRepository) : ViewModel() { class RegisterUserViewModel(private val userRepository: UserRepository) : LoadingViewModel() {
private val _users = MutableLiveData<List<User>>() private val _users = MutableLiveData<List<User>>()
val users: LiveData<List<User>> get() = _users val users: LiveData<List<User>> get() = _users

View File

@ -11,7 +11,7 @@ import kotlinx.coroutines.flow.flatMapLatest
class RentListViewModel( class RentListViewModel(
private val rentRepository: RentRepository private val rentRepository: RentRepository
) : ViewModel() { ) : LoadingViewModel() {
private val _userId = MutableStateFlow<Int?>(null) private val _userId = MutableStateFlow<Int?>(null)
val userId: StateFlow<Int?> get() = _userId val userId: StateFlow<Int?> get() = _userId

View File

@ -15,7 +15,7 @@ import kotlinx.coroutines.flow.stateIn
class RentViewModel( class RentViewModel(
savedStateHandle: SavedStateHandle, savedStateHandle: SavedStateHandle,
private val rentRepository: RentRepository private val rentRepository: RentRepository
) : ViewModel() { ) : LoadingViewModel() {
private val rentUid: Int = checkNotNull(savedStateHandle["id"]) private val rentUid: Int = checkNotNull(savedStateHandle["id"])
val rentUiState: StateFlow<RentUiState> = flow{emit(rentRepository.getRent(rentUid))} .map { val rentUiState: StateFlow<RentUiState> = flow{emit(rentRepository.getRent(rentUid))} .map {

View File

@ -15,7 +15,7 @@ import java.text.SimpleDateFormat
import java.util.Date import java.util.Date
import java.util.Locale import java.util.Locale
class ReportViewModel(private val itemRespository: RestItemRepository): ViewModel() { class ReportViewModel(private val itemRespository: RestItemRepository): LoadingViewModel() {
var reportPageUiState by mutableStateOf(ReportPageUiState()) var reportPageUiState by mutableStateOf(ReportPageUiState())
private set private set

View File

@ -19,7 +19,10 @@ 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.database.entities.composeui.AppViewModelProvider import com.example.myapplication.database.entities.composeui.AppViewModelProvider
import com.example.myapplication.database.entities.composeui.ErrorPage
import com.example.myapplication.database.entities.composeui.LoadingPage
import com.example.myapplication.ui.theme.PmudemoTheme import com.example.myapplication.ui.theme.PmudemoTheme
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -29,7 +32,8 @@ fun BikeEdit(
viewModel: BikeEditViewModel = viewModel(factory = AppViewModelProvider.Factory), viewModel: BikeEditViewModel = viewModel(factory = AppViewModelProvider.Factory),
) { ) {
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
when (viewModel.apiStatus) {
ApiStatus.DONE -> {
BikeEdit( BikeEdit(
bikeUiState = viewModel.bikeUiState, bikeUiState = viewModel.bikeUiState,
onClick = { onClick = {
@ -40,6 +44,13 @@ fun BikeEdit(
}, },
onUpdate = viewModel::updateUiState, onUpdate = viewModel::updateUiState,
) )
}
ApiStatus.LOADING -> LoadingPage()
else -> ErrorPage(
message = viewModel.apiError,
onBack = { navController.popBackStack() }
)
}
} }
@Composable @Composable

View File

@ -6,6 +6,7 @@ import androidx.compose.runtime.setValue
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.example.myapplication.database.entities.composeui.LoadingViewModel
import com.example.myapplication.database.entities.model.Bike import com.example.myapplication.database.entities.model.Bike
import com.example.myapplication.database.entities.model.BikeWithItems import com.example.myapplication.database.entities.model.BikeWithItems
import com.example.myapplication.database.entities.model.ItemFromBike import com.example.myapplication.database.entities.model.ItemFromBike
@ -15,7 +16,7 @@ import kotlinx.coroutines.launch
class BikeEditViewModel( class BikeEditViewModel(
savedStateHandle: SavedStateHandle, savedStateHandle: SavedStateHandle,
private val bikeRepository: BikeRepository private val bikeRepository: BikeRepository
) : ViewModel() { ) : LoadingViewModel() {
var bikeUiState by mutableStateOf(BikeUiState()) var bikeUiState by mutableStateOf(BikeUiState())
private set private set
@ -23,10 +24,15 @@ class BikeEditViewModel(
init { init {
viewModelScope.launch { viewModelScope.launch {
if (bikeUid > 0) { runInScope(
bikeUiState = bikeRepository.getBike(bikeUid) actionSuccess = {
.toUiState(true) bikeUiState = bikeRepository.getBike(bikeUid)
} .toUiState(true)
},
actionError = {
bikeUiState = BikeUiState()
}
)
} }
} }
@ -39,11 +45,15 @@ class BikeEditViewModel(
suspend fun saveBike() { suspend fun saveBike() {
if (validateInput()) { if (validateInput()) {
if (bikeUid > 0) { runInScope(
bikeRepository.updateBike(bikeUiState.bikeDetails.toBike(bikeUid)) actionSuccess = {
} else { if (bikeUid > 0) {
bikeRepository.insertBike(bikeUiState.bikeDetails.toBike()) bikeRepository.updateBike(bikeUiState.bikeDetails.toBike(bikeUid))
} } else {
bikeRepository.insertBike(bikeUiState.bikeDetails.toBike())
}
}
)
} }
} }

View File

@ -6,6 +6,7 @@ import androidx.compose.runtime.setValue
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.example.myapplication.database.entities.composeui.LoadingViewModel
import com.example.myapplication.database.entities.model.Item import com.example.myapplication.database.entities.model.Item
import com.example.myapplication.database.entities.repository.ItemRepository import com.example.myapplication.database.entities.repository.ItemRepository
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -14,7 +15,7 @@ import org.threeten.bp.LocalDateTime
class ItemEditViewModel( class ItemEditViewModel(
savedStateHandle: SavedStateHandle, savedStateHandle: SavedStateHandle,
private val itemRepository: ItemRepository private val itemRepository: ItemRepository
) : ViewModel() { ) : LoadingViewModel() {
var itemUiState by mutableStateOf(ItemUiState()) var itemUiState by mutableStateOf(ItemUiState())
private set private set

View File

@ -21,4 +21,6 @@
<string name="item_bike_not_select">Велосипед не указан</string> <string name="item_bike_not_select">Велосипед не указан</string>
<string name="size">Размер загруженного изображения: %1$dx%2$d</string> <string name="size">Размер загруженного изображения: %1$dx%2$d</string>
<string name="not_uploaded">Загрузите изображение</string> <string name="not_uploaded">Загрузите изображение</string>
<string name="loading">Загрузка…</string>
<string name="back">Назад</string>
</resources> </resources>