Регистрация, аутентификация, вывод книг по автору и по пользователю

This commit is contained in:
ityurner02@mail.ru 2023-12-24 20:16:50 +04:00
parent 3b17a15278
commit ef72894515
28 changed files with 527 additions and 129 deletions

View File

@ -1,11 +1,11 @@
{
"users": [
{
"id": 1,
"login": "Admin",
"password": "Admin",
"email": "admin@mail.ru",
"role": "ADMIN",
"id": 1
"role": "ADMIN"
},
{
"id": 2,
@ -13,6 +13,13 @@
"password": "123",
"email": "user1@mail.ru",
"role": "USER"
},
{
"id": 3,
"login": "User2",
"password": "123",
"email": "user2@gmail.ru",
"role": "USER"
}
],
"authors": [
@ -3957,7 +3964,7 @@
-39
],
"authorId": 2,
"userId": 2
"userId": 1
},
{
"title": "Вельд",
@ -7316,7 +7323,7 @@
-39
],
"authorId": 2,
"userId": 2,
"userId": 1,
"id": 4
},
{

View File

@ -89,6 +89,7 @@ dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
implementation("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0")
// LiveData | +
// LiveData | DataStore
implementation("androidx.compose.runtime:runtime-livedata:1.5.4")
implementation("androidx.datastore:datastore-preferences:1.0.0")
}

View File

@ -11,6 +11,7 @@ import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.example.myapplication.composeui.authenticator.Authenticator
import com.example.myapplication.composeui.navigation.MainNavbar
import com.example.myapplication.ui.theme.MyApplicationTheme
@ -24,7 +25,8 @@ class MainActivity : ComponentActivity() {
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
MainNavbar()
Authenticator()
MainNavbar();
}
}
}

View File

@ -1,14 +0,0 @@
package com.example.myapplication
class SingletonClass{
var _userId = 2
var _role = "USER"
fun getUserId(): Int{ return _userId }
fun setUserId(userId: Int){
_userId = userId
}
fun getRole(): String{ return _role }
fun setRole(role: String){
_role = role
}
}

View File

@ -100,7 +100,7 @@ interface ServerService{
suspend fun getUserByLoginPass(
@Query("login") login: String,
@Query("password") password: String
): UserRemote
): List<UserRemote>
@POST("users")
suspend fun createUser(
@Body user: UserRemote,
@ -117,7 +117,7 @@ interface ServerService{
@Path("id") id: Int,
): UserRemote
companion object {
private const val BASE_URL = "http://89.239.172.45:8079/"
private const val BASE_URL = "http://89.239.174.67:8079/"
@Volatile
private var INSTANCE: ServerService? = null

View File

@ -28,7 +28,7 @@ class RestUserRepository(
}
override suspend fun tryLogin(login: String, password: String): User? {
return service.getUserByLoginPass(login, password).toUser()
return service.getUserByLoginPass(login, password).firstOrNull()?.toUser()
}
override suspend fun insert(user: User) {

View File

@ -6,15 +6,19 @@ import androidx.lifecycle.viewmodel.CreationExtras
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.example.myapplication.MyApplication
import com.example.myapplication.composeui.ViewModel.AuthorBooksViewModel
import com.example.myapplication.composeui.ViewModel.AuthorDropDownViewModel
import com.example.myapplication.composeui.ViewModel.AuthorEditViewModel
import com.example.myapplication.composeui.ViewModel.AuthorListViewModel
import com.example.myapplication.composeui.ViewModel.BookEditViewModel
import com.example.myapplication.composeui.ViewModel.BookListViewModel
import com.example.myapplication.composeui.ViewModel.BookPageViewModel
import com.example.myapplication.composeui.ViewModel.LoginViewModel
import com.example.myapplication.composeui.ViewModel.SearchPageViewModel
import com.example.myapplication.composeui.ViewModel.UserBooksViewModel
import com.example.myapplication.composeui.ViewModel.UserEditViewModel
import com.example.myapplication.composeui.ViewModel.UserPageViewModel
import com.example.myapplication.composeui.authenticator.AuthenticatorViewModel
object AppViewModelProvider {
val Factory = viewModelFactory {
@ -69,6 +73,21 @@ object AppViewModelProvider {
myApplication().container.authorRestRepository
)
}
initializer {
AuthorBooksViewModel(
this.createSavedStateHandle(),
myApplication().container.bookRestRepository
)
}
initializer {
UserBooksViewModel(myApplication().container.bookRestRepository)
}
initializer {
AuthenticatorViewModel(myApplication().container.userRestRepository)
}
initializer {
LoginViewModel(myApplication().container.userRestRepository)
}
}
}

View File

@ -0,0 +1,52 @@
package com.example.myapplication.composeui
import android.content.res.Configuration
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import androidx.navigation.compose.rememberNavController
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
import com.example.myapplication.composeui.ViewModel.AuthorBooksViewModel
import com.example.myapplication.db.model.Book
import com.example.myapplication.ui.theme.MyApplicationTheme
import kotlinx.coroutines.launch
@Composable
fun AuthorBooks(navController: NavController, authorId: Int, viewModel: AuthorBooksViewModel = viewModel(factory = AppViewModelProvider.Factory)) {
val coroutineScope = rememberCoroutineScope()
val authorBooksUiState = viewModel.authorBooksUiState
val pagingBook: LazyPagingItems<Book> = viewModel.bookPagedData.collectAsLazyPagingItems()
LaunchedEffect(Unit) {
viewModel.refreshState()
}
fun refresh(){
coroutineScope.launch { viewModel.refreshState() }
}
LazyVerticalGrid(columns = GridCells.Adaptive(minSize = 180.dp)){
items (pagingBook.itemCount){
index -> BookCell(navController = navController, book = pagingBook[index]!!.copy())
}
}
}
@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 AuthorBooksPreview() {
MyApplicationTheme {
Surface(
color = MaterialTheme.colorScheme.background
) {
AuthorBooks(navController = rememberNavController(), 0)
}
}
}

View File

@ -27,14 +27,17 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.example.myapplication.MainActivity
import com.example.myapplication.R
import com.example.myapplication.composeui.ViewModel.AuthorListViewModel
import com.example.myapplication.composeui.navigation.Screen
import com.example.myapplication.db.database.AppDatabase
import com.example.myapplication.db.model.Author
import com.example.myapplication.store.LiveStore
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.launch
@ -46,7 +49,7 @@ fun AuthorCell(navController: NavController?, author: Author, viewModel: AuthorL
val context = LocalContext.current
Column(modifier = Modifier
.padding(all = 5.dp)
.requiredSize(170.dp, 100.dp)
.requiredSize(170.dp, 150.dp)
.clip(RoundedCornerShape(10.dp))
.border(1.dp, Color.LightGray, shape = RoundedCornerShape(10.dp))
) {
@ -83,6 +86,14 @@ fun AuthorCell(navController: NavController?, author: Author, viewModel: AuthorL
)
}
}
Button(
modifier = Modifier
.fillMaxWidth()
.padding(all = 10.dp),
onClick = {
navController?.navigate(Screen.AuthorBooks.route.replace("{id}", author.id.toString()))
}) {
Text(stringResource(id = R.string.books))
}
}
}

View File

@ -33,7 +33,6 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.example.myapplication.R
import com.example.myapplication.SingletonClass
import com.example.myapplication.composeui.ViewModel.AuthorsListUiState
import com.example.myapplication.composeui.ViewModel.AuthorsUiState
import com.example.myapplication.composeui.ViewModel.AuthorDetails

View File

@ -8,15 +8,12 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
@ -33,7 +30,6 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.example.myapplication.R
import com.example.myapplication.SingletonClass
import com.example.myapplication.composeui.ViewModel.AuthorDropDownViewModel
import com.example.myapplication.composeui.ViewModel.AuthorsListUiState
import com.example.myapplication.composeui.ViewModel.AuthorsUiState
@ -42,6 +38,7 @@ import com.example.myapplication.composeui.ViewModel.BookEditViewModel
import com.example.myapplication.composeui.ViewModel.BookUiState
import com.example.myapplication.composeui.ViewModel.ImageUploader
import com.example.myapplication.db.model.Author
import com.example.myapplication.store.LiveStore
import kotlinx.coroutines.launch
@Composable
@ -124,8 +121,7 @@ private fun BookEdit(
)
)
}
var SingletonClass = SingletonClass()
onUpdate(bookUiState.bookDetails.copy(userId = SingletonClass.getUserId()))
onUpdate(bookUiState.bookDetails.copy(userId = LiveStore.getUserId()))
Column(
Modifier
.fillMaxWidth()

View File

@ -6,14 +6,19 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
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.LaunchedEffect
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
@ -25,41 +30,52 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.compose.rememberNavController
import com.example.myapplication.MainActivity
import com.example.myapplication.SingletonClass
import com.example.myapplication.api.ServerService
import com.example.myapplication.composeui.ViewModel.LoginViewModel
import com.example.myapplication.composeui.navigation.Screen
import com.example.myapplication.db.database.AppDatabase
import com.example.myapplication.db.model.User
import com.example.myapplication.store.PreferencesStore
import com.example.myapplication.ui.theme.MyApplicationTheme
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.coroutines.launch
import kotlinx.coroutines.GlobalScope
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Enter(navController: NavController) {
var SingletonClass = SingletonClass()
fun Enter(navController: NavController, viewModel: LoginViewModel = viewModel(factory = AppViewModelProvider.Factory)) {
val scope = rememberCoroutineScope()
val context = LocalContext.current
val service: ServerService
var login by remember{mutableStateOf("")}
var password by remember{mutableStateOf("")}
val user = viewModel.signInUserState
Column(
Modifier
.fillMaxWidth()
.padding(all = 40.dp)) {
Text(stringResource(id = R.string.login))
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
value = login, onValueChange = { login = it },
value = user.details.login, onValueChange = { viewModel.updateState(user.details.copy(login = it)) },
)
Spacer(Modifier.padding(bottom = 10.dp))
Text(stringResource(id = R.string.password))
var passwordVisible by rememberSaveable { mutableStateOf(false) }
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
value = password, onValueChange = { password = it },
value = user.details.password, onValueChange = { viewModel.updateState(user.details.copy(password = it)) },
visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
trailingIcon = {
val image = if (passwordVisible)
Icons.Filled.Close
else Icons.Filled.Check
val description = if (passwordVisible) "Hide password" else "Show password"
IconButton(onClick = { passwordVisible = !passwordVisible }) {
Icon(imageVector = image, description)
}
}
)
Spacer(Modifier.padding(bottom = 20.dp))
Button(
@ -68,15 +84,13 @@ fun Enter(navController: NavController) {
.padding(all = 10.dp),
onClick = {
scope.launch {
//if( AppDatabase.getInstance(context).userDao().tryLogin(login.toString(), password.toString()) != null){
// SingletonClass.setUserId(AppDatabase.getInstance(context).userDao().tryLogin(login.toString(), password.toString())?.uid!!)
//SingletonClass.setRole(AppDatabase.getInstance(context).userDao().tryLogin(login.toString(), password.toString())?.role!!)
if(viewModel.signIn(context) != null) {
navController?.navigate(Screen.Profile.route)
//}
//else{
// val toast = Toast.makeText(MainActivity.appContext, "Неверный логин или пароль", Toast.LENGTH_SHORT)
// toast.show()
//}
}
else{
val toast = Toast.makeText(MainActivity.appContext, "Неверный логин или пароль", Toast.LENGTH_SHORT)
toast.show()
}
}
}) {
Text(stringResource(id = R.string.enter))

View File

@ -1,66 +1,33 @@
package com.example.myapplication.composeui
import android.annotation.SuppressLint
import android.content.res.Configuration
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Create
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.outlined.Clear
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import androidx.navigation.compose.rememberNavController
import com.example.myapplication.composeui.navigation.Screen
import com.example.myapplication.db.database.AppDatabase
import com.example.myapplication.db.model.User
import com.example.myapplication.R
import com.example.myapplication.SingletonClass
import com.example.myapplication.composeui.ViewModel.UserPageViewModel
import com.example.myapplication.store.LiveStore
import com.example.myapplication.ui.theme.MyApplicationTheme
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@Composable
fun Profile(navController: NavController) {
var SingletonClass = SingletonClass()
Column(Modifier.padding(all = 40.dp)) {
Image(
painter = painterResource(id = R.drawable.user),
@ -70,17 +37,22 @@ fun Profile(navController: NavController) {
.clickable(
enabled = true,
onClick = {
navController?.navigate(
Screen.ProfileEdit.route.replace(
"{id}",
SingletonClass.getUserId().toString()
)
)
navController?.navigate(Screen.ProfileEdit.route.replace("{id}", LiveStore.getUserId().toString()))
}
)
)
Spacer (Modifier.padding(bottom = 10.dp))
Text("Открыть профиль", modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.Center)
Spacer(Modifier.padding(bottom = 20.dp))
Button(
modifier = Modifier
.fillMaxWidth()
.padding(all = 10.dp),
onClick = {
navController?.navigate(Screen.UserBooks.route.replace("{id}", LiveStore.getUserId().toString()))
}) {
Text(stringResource(id = R.string.my_books))
}
}
}

View File

@ -5,8 +5,14 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface
@ -22,49 +28,83 @@ import androidx.compose.runtime.setValue
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.compose.rememberNavController
import com.example.myapplication.composeui.navigation.Screen
import com.example.myapplication.ui.theme.MyApplicationTheme
import com.example.myapplication.R
import com.example.myapplication.db.database.AppDatabase
import com.example.myapplication.db.model.User
import com.example.myapplication.composeui.ViewModel.UserDetails
import com.example.myapplication.composeui.ViewModel.UserEditViewModel
import com.example.myapplication.composeui.ViewModel.UserUiState
import com.example.myapplication.store.LiveStore
import kotlinx.coroutines.launch
@Composable
fun Registration(navController: NavController, viewModel: UserEditViewModel = viewModel(factory = AppViewModelProvider.Factory)){
val scope = rememberCoroutineScope()
Registration(
navController = navController,
userUiState = viewModel.userUiState,
onClick = {
scope.launch {
viewModel.saveUser()
navController?.navigate(Screen.Enter.route)
}
},
onUpdate = viewModel::updateUiState
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Registration(navController: NavController) {
val scope = rememberCoroutineScope()
val context = LocalContext.current
private fun Registration(
navController: NavController,
userUiState: UserUiState,
onClick: () -> Unit,
onUpdate: (UserDetails) -> Unit
) {
onUpdate(userUiState.userDetails.copy(role = "USER"))
Column(Modifier.fillMaxWidth().padding(all = 40.dp)) {
var mail by remember{mutableStateOf("")}
Text(stringResource(id = R.string.email))
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
value = mail, onValueChange = { mail = it },
value = userUiState.userDetails.email, onValueChange = { onUpdate(userUiState.userDetails.copy(email = it)) },
)
Spacer(Modifier.padding(bottom = 10.dp))
var login by remember{mutableStateOf("")}
Text(stringResource(id = R.string.login))
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
value = login, onValueChange = { login = it },
value = userUiState.userDetails.login, onValueChange = { onUpdate(userUiState.userDetails.copy(login = it)) },
)
Spacer(Modifier.padding(bottom = 10.dp))
var password by remember{mutableStateOf("")}
var passwordVisible by rememberSaveable { mutableStateOf(false) }
Text(stringResource(id = R.string.password))
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
value = password, onValueChange = { password = it },
value = userUiState.userDetails.password, onValueChange = { onUpdate(userUiState.userDetails.copy(password = it)) },
visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
trailingIcon = {
val image = if (passwordVisible)
Icons.Filled.Close
else Icons.Filled.Check
val description = if (passwordVisible) "Hide password" else "Show password"
IconButton(onClick = { passwordVisible = !passwordVisible }) {
Icon(imageVector = image, description)
}
}
)
Spacer(Modifier.padding(bottom = 20.dp))
Button(
modifier = Modifier
.fillMaxWidth()
.padding(all = 10.dp),
onClick = {
scope.launch{
//AppDatabase.getInstance(context).userDao().insert(User(0, login.toString(), password.toString(), mail.toString(), "USER"))
}
navController?.navigate(Screen.Enter.route)
}) {
onClick = onClick
) {
Text(stringResource(id = R.string.create_acc))
}
}

View File

@ -0,0 +1,52 @@
package com.example.myapplication.composeui
import android.content.res.Configuration
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import androidx.navigation.compose.rememberNavController
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
import com.example.myapplication.composeui.ViewModel.UserBooksViewModel
import com.example.myapplication.db.model.Book
import com.example.myapplication.ui.theme.MyApplicationTheme
import kotlinx.coroutines.launch
@Composable
fun UserBooks(navController: NavController, userId: Int, viewModel: UserBooksViewModel = viewModel(factory = AppViewModelProvider.Factory)) {
val coroutineScope = rememberCoroutineScope()
val userBooksUiState = viewModel.userBooksUiState
val pagingBook: LazyPagingItems<Book> = viewModel.bookPagedData.collectAsLazyPagingItems()
LaunchedEffect(Unit) {
viewModel.refreshState()
}
fun refresh(){
coroutineScope.launch { viewModel.refreshState() }
}
LazyVerticalGrid(columns = GridCells.Adaptive(minSize = 180.dp)){
items (pagingBook.itemCount){
index -> BookCell(navController = navController, book = pagingBook[index]!!.copy())
}
}
}
@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 UserBooksPreview() {
MyApplicationTheme {
Surface(
color = MaterialTheme.colorScheme.background
) {
UserBooks(navController = rememberNavController(), 0)
}
}
}

View File

@ -0,0 +1,37 @@
package com.example.myapplication.composeui.ViewModel
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 androidx.paging.PagingData
import androidx.paging.filter
import com.example.myapplication.db.model.Book
import com.example.myapplication.db.respository.BookRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
class AuthorBooksViewModel(savedStateHandle: SavedStateHandle, private val bookRepository: BookRepository) : ViewModel(){
private val authorUid: Int = checkNotNull(savedStateHandle["id"])
var authorBooksUiState by mutableStateOf(AuthorBooksUiState())
private set
init {
viewModelScope.launch {
refreshState()
}
}
suspend fun refreshState() {
authorBooksUiState = AuthorBooksUiState(bookRepository.getByUserId(authorUid))
}
val bookPagedData: Flow<PagingData<Book>> = bookRepository.loadAllBooksPaged().map{
x -> x.filter{
y-> (y.authorId == authorUid)
}
}
}
data class AuthorBooksUiState(val bookList: List<Book> = listOf())

View File

@ -0,0 +1,51 @@
package com.example.myapplication.composeui.ViewModel
import android.annotation.SuppressLint
import android.content.Context
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import com.example.myapplication.db.model.User
import com.example.myapplication.db.respository.UserRepository
import com.example.myapplication.store.LiveStore
import com.example.myapplication.store.PreferencesStore
class LoginViewModel (
private val userRepository: UserRepository
) : ViewModel() {
var signInUserState by mutableStateOf(SignInUserState())
private set
fun updateState(signInUserDetails: SignInUserDetails) {
signInUserState = SignInUserState(
details = signInUserDetails
)
}
@SuppressLint("SuspiciousIndentation")
suspend fun signIn(context: Context): User? {
val user: User? = userRepository.tryLogin(signInUserState.details.login, signInUserState.details.password)
if(user != null) {
val store = PreferencesStore(context)
store.setUid(user.uid.toString())
LiveStore.user.value = user.copy()
}
else{
return null
}
signInUserState = SignInUserState(
details = signInUserState.details,
)
return user
}
}
data class SignInUserDetails(
val login: String = "",
val password: String = ""
)
data class SignInUserState(
val details: SignInUserDetails = SignInUserDetails(),
)

View File

@ -0,0 +1,37 @@
package com.example.myapplication.composeui.ViewModel
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.PagingData
import androidx.paging.filter
import com.example.myapplication.db.model.Book
import com.example.myapplication.db.respository.BookRepository
import com.example.myapplication.store.LiveStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
class UserBooksViewModel(private val bookRepository: BookRepository) : ViewModel(){
private val userUid: Int = LiveStore.user.value?.uid ?: 0
var userBooksUiState by mutableStateOf(UserBooksUiState())
private set
init {
viewModelScope.launch {
refreshState()
}
}
suspend fun refreshState() {
userBooksUiState = UserBooksUiState(bookRepository.getByUserId(userUid))
}
val bookPagedData: Flow<PagingData<Book>> = bookRepository.loadAllBooksPaged().map{
x -> x.filter{
y-> (y.userId == userUid)
}
}
}
data class UserBooksUiState(val bookList: List<Book> = listOf())

View File

@ -9,6 +9,7 @@ import androidx.lifecycle.ViewModel
import com.example.myapplication.db.respository.UserRepository
import androidx.lifecycle.viewModelScope
import com.example.myapplication.db.model.User
import com.example.myapplication.store.LiveStore
import kotlinx.coroutines.launch
class UserEditViewModel(savedStateHandle: SavedStateHandle,
@ -17,7 +18,7 @@ class UserEditViewModel(savedStateHandle: SavedStateHandle,
var userUiState by mutableStateOf(UserUiState())
private set
private val userUid: Int = checkNotNull(savedStateHandle["id"])
private val userUid: Int = LiveStore.user.value?.uid ?: 0
init {
viewModelScope.launch {
if (userUid > 0) {

View File

@ -9,21 +9,21 @@ import androidx.lifecycle.SavedStateHandle
import com.example.myapplication.db.model.User
import kotlinx.coroutines.launch
import com.example.myapplication.db.respository.UserRepository
import com.example.myapplication.store.LiveStore
class UserPageViewModel(savedStateHandle: SavedStateHandle, private val userRepository: UserRepository) : ViewModel(){
private val userId: Int = checkNotNull(savedStateHandle["id"])
var userPageUiState by mutableStateOf(UserPageUiState())
private set
init {
viewModelScope.launch {
if (userId > 0) {
if (LiveStore.user.value != null) {
refreshState()
}
}
}
suspend fun refreshState() {
userPageUiState = UserPageUiState(userRepository.getByUid(userId))
userPageUiState = UserPageUiState(LiveStore.user.value)
}
}

View File

@ -0,0 +1,38 @@
package com.example.myapplication.composeui.authenticator
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.myapplication.store.LiveStore
import com.example.myapplication.store.PreferencesStore
import com.example.myapplication.composeui.AppViewModelProvider
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
@Composable
fun Authenticator(
viewModel: AuthenticatorViewModel = viewModel(factory = AppViewModelProvider.Factory)
) {
val context = LocalContext.current
val store = PreferencesStore(context)
val scope = rememberCoroutineScope()
val uid = store.getUid().collectAsState(initial = "").value
fun synchronize() {
scope.launch {
if (uid == "") {
LiveStore.user.value = null
return@launch
}
try {
viewModel.findUserByUid(uid.toInt())
}
catch (_: Exception){}
}
}
synchronize()
}

View File

@ -0,0 +1,21 @@
package com.example.myapplication.composeui.authenticator
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import com.example.myapplication.db.model.User
import com.example.myapplication.db.respository.UserRepository
class AuthenticatorViewModel(
private val userRepository: UserRepository
) : ViewModel() {
var authUiState by mutableStateOf(AuthenticatorUiState())
private set
suspend fun findUserByUid(uid: Int) {
authUiState = AuthenticatorUiState(userRepository.getByUid(uid))
}
}
data class AuthenticatorUiState(val user: User? = null)

View File

@ -36,6 +36,7 @@ import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import com.example.myapplication.ui.theme.MyApplicationTheme
import com.example.myapplication.R
import com.example.myapplication.composeui.AuthorBooks
import com.example.myapplication.composeui.Main
import com.example.myapplication.composeui.Registration
import com.example.myapplication.composeui.Enter
@ -49,6 +50,7 @@ import com.example.myapplication.composeui.BookEdit
import com.example.myapplication.composeui.BookSearch
import com.example.myapplication.composeui.ListAuthors
import com.example.myapplication.composeui.AuthorEdit
import com.example.myapplication.composeui.UserBooks
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@ -163,6 +165,18 @@ fun Navhost(
) { backStackEntry ->
backStackEntry.arguments?.let { BookSearch(navController = navController ,it.getString ("searchStr", "")) }
}
composable(
Screen.AuthorBooks.route,
arguments = listOf(navArgument("id") { type = NavType.IntType })
) { backStackEntry ->
backStackEntry.arguments?.let { AuthorBooks(navController = navController, it.getInt("id")) }
}
composable(
Screen.UserBooks.route,
arguments = listOf(navArgument("id") { type = NavType.IntType })
) { backStackEntry ->
backStackEntry.arguments?.let { UserBooks(navController = navController, it.getInt("id")) }
}
}
}

View File

@ -46,6 +46,12 @@ enum class Screen(
BookSearch(
"book-search/{searchStr}", R.string.search, showInBottomBar = false
),
AuthorBooks(
"author-books/{id}", R.string.authors_books, showInBottomBar = false
),
UserBooks(
"user-books/{id}", R.string.my_books, showInBottomBar = false
),
BookView(
"book-view/{id}", R.string.book_view_title, showInBottomBar = false
),

View File

@ -0,0 +1,16 @@
package com.example.myapplication.store
import androidx.lifecycle.MutableLiveData
import com.example.myapplication.db.model.User
class LiveStore{
companion object{
val user = MutableLiveData<User?>(null)
fun getUserId(): Int{
return user.value?.uid ?: 0
}
fun getRole(): String{
return user.value?.role ?: "USER"
}
}
}

View File

@ -0,0 +1,34 @@
package com.example.myapplication.store
import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
class PreferencesStore(private val context: Context) {
companion object {
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore("Store")
val UID = stringPreferencesKey("uid")
}
fun getUid(): Flow<String> {
return context.dataStore.data
.map { preferences ->
preferences[UID] ?: ""
}
}
private suspend fun saveStringValue(key: Preferences.Key<String>, value: String) {
context.dataStore.edit { preferences ->
preferences[key] = value
}
}
suspend fun setUid(uid: String) {
saveStringValue(UID, uid)
}
}

View File

@ -12,21 +12,14 @@
<string name="listauthors">Писатели</string>
<string name="author">Писатель</string>
<string name="book_view_title">Книга</string>
<string name="books">Книги</string>
<string name="my_books">Мои книги</string>
<string name="authors_books">Книги автора</string>
<string name="enter">Войти</string>
<string name="create_acc">Создать аккаунт</string>
<string name="email">Почта</string>
<string name="login">Логин</string>
<string name="password">Пароль</string>
<string name="stats">
<p>Пользователь</p>\n
<p>User1</p>\n\n
<p>Дата регистрации</p>\n
<p>20.09.2023</p>\n\n
<p>Прочитано книг</p>\n
<p>7</p>\n\n
<p>Загружено книг</p>\n
<p>0</p>
</string>
<string name="book_title">Название</string>
<string name="author_name">Автор</string>
<string name="description">Содержание</string>
@ -37,5 +30,4 @@
<string name="load_book">Загрузить</string>
<string name="add_book">Добавить</string>
<string name="read_book">Читать</string>
<string name="help_me_pls">Вы можете помочь нашей библиотеке, загрузив свою книгу.</string>
</resources>

View File

@ -3,6 +3,6 @@
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">100.87.48.148</domain>
<domain includeSubdomains="true">192.168.43.198</domain>
<domain includeSubdomains="true">89.239.172.45</domain>
<domain includeSubdomains="true">89.239.174.67</domain>
</domain-config>
</network-security-config>