Merge remote-tracking branch 'origin/master'

This commit is contained in:
Serafim_Pankin 2025-02-20 15:14:42 +03:00
commit 0ffd6b0ee6
14 changed files with 228 additions and 195 deletions

View File

@ -28,6 +28,7 @@ class MainActivity : AppCompatActivity() {
val toolbar = findViewById<Toolbar>(R.id.toolbar) val toolbar = findViewById<Toolbar>(R.id.toolbar)
setSupportActionBar(toolbar) setSupportActionBar(toolbar)
val navHostFragment = val navHostFragment =
supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as? NavHostFragment supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as? NavHostFragment
?: throw IllegalStateException("NavHostFragment is null") ?: throw IllegalStateException("NavHostFragment is null")
@ -38,6 +39,7 @@ class MainActivity : AppCompatActivity() {
) )
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration) NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration)
NavigationUI.setupWithNavController(navView, navController) NavigationUI.setupWithNavController(navView, navController)
supportActionBar!!.setDisplayHomeAsUpEnabled(false)
navController.addOnDestinationChangedListener { _, destination, _ -> navController.addOnDestinationChangedListener { _, destination, _ ->
Log.d("Navigate", "Navigate to " + destination.label) Log.d("Navigate", "Navigate to " + destination.label)
@ -45,8 +47,8 @@ class MainActivity : AppCompatActivity() {
val userDTO = getCachedUser() val userDTO = getCachedUser()
if (userDTO != null) { if (userDTO != null) {
if (!userDTO.roles.any { it.name == "ROLE_ADMIN" }) navView.menu.findItem(R.id.nav_admin) val adminButton = navView.menu.findItem(R.id.nav_admin)
.setVisible(false) else navView.menu.findItem(R.id.nav_admin).setVisible(true) if (!userDTO.roles.any { it.name == "ROLE_ADMIN" }) adminButton.setVisible(false) else adminButton.setVisible(true)
} }
} }
} }

View File

@ -1,12 +1,12 @@
package com.displaynone.acss.components.auth.models.user package com.displaynone.acss.components.auth.models.user
import android.content.Context import android.content.Context
import android.util.Log
import com.displaynone.acss.components.auth.internal_utils.AuthTokenManager import com.displaynone.acss.components.auth.internal_utils.AuthTokenManager
import com.displaynone.acss.components.auth.internal_utils.UserManager import com.displaynone.acss.components.auth.internal_utils.UserManager
import com.displaynone.acss.components.auth.models.AuthTokenPair import com.displaynone.acss.components.auth.models.AuthTokenPair
import com.displaynone.acss.components.auth.models.user.repository.UserRepository import com.displaynone.acss.components.auth.models.user.repository.UserRepository
import com.displaynone.acss.components.auth.models.user.repository.dto.UserDTO import com.displaynone.acss.components.auth.models.user.repository.dto.UserDTO
import com.displaynone.acss.components.acs.models.visit.repository.dto.VisitDto
class UserServiceST( class UserServiceST(
@ -34,69 +34,64 @@ class UserServiceST(
return instance ?: throw RuntimeException("null instance") return instance ?: throw RuntimeException("null instance")
} }
} }
suspend fun login(login: String, password: String): Result<Unit> { suspend fun login(login: String, password: String): Result<Unit> {
return runCatching { return runCatching {
userRepository.login(login = login, password).getOrThrow().let { data -> userRepository.login(login = login, password).getOrThrow().let { data ->
tokenManager.saveTokens(data) userManager.saveDto(data.user)
tokenManager.saveTokens(data.tokens)
} }
} }
} }
fun getTokenPair(): AuthTokenPair? { fun getTokenPair(): AuthTokenPair? {
return tokenManager.authTokenPair return tokenManager.authTokenPair
} }
fun hasTokens(): Boolean { fun hasTokens(): Boolean {
return tokenManager.hasTokens() return tokenManager.hasTokens()
} }
// suspend fun getMyLastVisits(pageNum: Int,
// pageSize: Int): Result<List<VisitDto>> {
// if (!tokenManager.hasTokens()) {
// throw RuntimeException("access token is null")
// }
//
// return userRepository.getMyLastVisits(
// pageNum = pageNum,
// pageSize = pageSize,
// token = tokenManager.authTokenPair!!.accessToken
// ).map { pagingDto -> pagingDto.content }
// }
// suspend fun getLastVisitsByLogin(pageNum: Int,
// pageSize: Int,
// login: String): Result<List<VisitDto>> {
// if (!tokenManager.hasTokens()) {
// throw RuntimeException("access token is null")
// }
// return userRepository.getLastVisitsByLogin(pageNum, pageSize, tokenManager.authTokenPair!!.accessToken, login).map { pagingDto -> pagingDto.content }
// }
fun logout() { fun logout() {
tokenManager.clear() tokenManager.clear()
} }
suspend fun changeRights(login: String, isACSBlocked: Boolean): Result<Unit> { suspend fun changeRights(login: String, isACSBlocked: Boolean): Result<Unit> {
return userRepository.changeRightsByLogin(login, tokenManager.authTokenPair!!.accessToken, isACSBlocked) return userRepository.changeRightsByLogin(
login,
tokenManager.authTokenPair!!.accessToken,
isACSBlocked
)
} }
suspend fun getInfo(): Result<UserDTO> { suspend fun getInfo(): Result<UserDTO> {
if (!tokenManager.hasTokens()) { if (!tokenManager.hasTokens()) {
throw RuntimeException("access token is null") throw RuntimeException("access token is null")
} }
val result = userRepository.getInfo(tokenManager.authTokenPair!!.accessToken) val result = userRepository.getInfo(tokenManager.authTokenPair!!.accessToken)
result.map { dto -> result.map { dto -> saveUserDTO(dto) }
saveUserDTO(dto)
}
return result return result
} }
suspend fun getInfoByLogin(login: String): Result<UserDTO> { suspend fun getInfoByLogin(login: String): Result<UserDTO> {
if (!tokenManager.hasTokens()) { if (!tokenManager.hasTokens()) {
throw RuntimeException("access token is null") throw RuntimeException("access token is null")
} }
return userRepository.getInfoByLogin(tokenManager.authTokenPair!!.accessToken, login) return userRepository.getInfoByLogin(tokenManager.authTokenPair!!.accessToken, login)
} }
suspend fun openDoor(code: String): Result<Int> { suspend fun openDoor(code: String): Result<Int> {
return userRepository.openDoor(tokenManager.authTokenPair!!.accessToken, code = code) return userRepository.openDoor(tokenManager.authTokenPair!!.accessToken, code = code)
} }
fun getUserDTO(): UserDTO? { fun getUserDTO(): UserDTO? {
return userManager.dto return userManager.dto
} }
fun saveUserDTO(userDTO: UserDTO){
return userManager.saveDto(userDTO)
fun saveUserDTO(userDTO: UserDTO) {
Log.d("USER", "UPDATE USER")
return userManager.saveDto(userDTO)
} }
} }

View File

@ -1,22 +1,18 @@
package com.displaynone.acss.components.auth.models.user.repository package com.displaynone.acss.components.auth.models.user.repository
import android.util.Log import android.util.Log
import com.bumptech.glide.load.HttpException import com.displaynone.acss.components.auth.models.user.repository.dto.LoginDTO
import com.displaynone.acss.components.auth.models.AuthTokenPair import com.displaynone.acss.components.auth.models.user.repository.dto.LoginResponseDTO
import com.displaynone.acss.config.Constants.serverUrl
import com.displaynone.acss.config.Network
import com.displaynone.acss.components.auth.models.user.repository.dto.RegisterUserDto
import com.displaynone.acss.components.auth.models.user.repository.dto.UpdateUserDTO import com.displaynone.acss.components.auth.models.user.repository.dto.UpdateUserDTO
import com.displaynone.acss.components.auth.models.user.repository.dto.UserDTO import com.displaynone.acss.components.auth.models.user.repository.dto.UserDTO
import com.displaynone.acss.components.auth.models.user.repository.dto.UserLoginDto import com.displaynone.acss.config.Constants.serverUrl
import com.displaynone.acss.config.Network
import io.ktor.client.call.body import io.ktor.client.call.body
import io.ktor.client.request.get import io.ktor.client.request.get
import io.ktor.client.request.headers import io.ktor.client.request.headers
import io.ktor.client.request.patch import io.ktor.client.request.patch
import io.ktor.client.request.post import io.ktor.client.request.post
import io.ktor.client.request.setBody import io.ktor.client.request.setBody
import io.ktor.client.statement.bodyAsText
import io.ktor.http.ContentType import io.ktor.http.ContentType
import io.ktor.http.HttpHeaders import io.ktor.http.HttpHeaders
import io.ktor.http.HttpStatusCode import io.ktor.http.HttpStatusCode
@ -24,38 +20,25 @@ import io.ktor.http.contentType
import io.ktor.http.encodeURLPath import io.ktor.http.encodeURLPath
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json
class UserRepository( class UserRepository {
suspend fun login(login: String, password: String): Result<LoginResponseDTO> =
) { withContext(Dispatchers.IO) {
suspend fun isUserExist(login: String): Result<Boolean> = withContext(Dispatchers.IO) {
runCatching {
val encodedLogin = login.encodeURLPath()
val result = Network.client.get("$serverUrl/api/$encodedLogin/auth/")
result.status != HttpStatusCode.OK
}
}
suspend fun login(login: String, password: String): Result<AuthTokenPair> = withContext(Dispatchers.IO) {
runCatching { runCatching {
val result = Network.client.post("$serverUrl/api/auth/login") { val result = Network.client.post("$serverUrl/api/auth/login") {
headers { headers {
append(HttpHeaders.ContentType, ContentType.Application.Json.toString()) append(HttpHeaders.ContentType, ContentType.Application.Json.toString())
} }
contentType(ContentType.Application.Json) contentType(ContentType.Application.Json)
setBody(UserLoginDto(login, password)) setBody(LoginDTO(login, password))
} }
if (result.status != HttpStatusCode.OK) {
error(result.status) if (result.status != HttpStatusCode.OK) error(result.status)
}
// val gson = Gson() result.body()
// val tokenPair = gson.fromJson(result.bodyAsText(), AuthTokenPair::class.java)
Log.d("UserRepository", result.bodyAsText())
// result.body()
val tokenPair = Json.decodeFromString<AuthTokenPair>(result.bodyAsText())
tokenPair
} }
} }
suspend fun openDoor(token: String, code: String): Result<Int> = withContext(Dispatchers.IO) { suspend fun openDoor(token: String, code: String): Result<Int> = withContext(Dispatchers.IO) {
runCatching { runCatching {
val result = Network.client.post("$serverUrl/api/acs/open") { val result = Network.client.post("$serverUrl/api/acs/open") {
@ -65,13 +48,13 @@ class UserRepository(
contentType(ContentType.Application.Json) contentType(ContentType.Application.Json)
setBody("""{ "code": $code }""") setBody("""{ "code": $code }""")
} }
if (result.status != HttpStatusCode.OK) {
error("Status ${result.status}: ${result.body<String>()}") if (result.status != HttpStatusCode.OK) error("Status ${result.status}: ${result.body<String>()}")
}
Log.d("UserRepository", result.bodyAsText())
result.status.value result.status.value
} }
} }
suspend fun getInfo(token: String): Result<UserDTO> = withContext(Dispatchers.IO) { suspend fun getInfo(token: String): Result<UserDTO> = withContext(Dispatchers.IO) {
runCatching { runCatching {
val result = Network.client.get("$serverUrl/api/users/me") { val result = Network.client.get("$serverUrl/api/users/me") {
@ -79,14 +62,13 @@ class UserRepository(
append(HttpHeaders.Authorization, "Bearer $token") append(HttpHeaders.Authorization, "Bearer $token")
} }
} }
if (result.status != HttpStatusCode.OK) { if (result.status != HttpStatusCode.OK) error("Status ${result.status}: ${result.body<String>()}")
error("Status ${result.status}: ${result.body<String>()}")
}
Log.d("UserRepository", result.bodyAsText())
result.body() result.body()
} }
} }
suspend fun getInfoByLogin(token: String, login: String): Result<UserDTO> = withContext(Dispatchers.IO){
suspend fun getInfoByLogin(token: String, login: String): Result<UserDTO> =
withContext(Dispatchers.IO) {
runCatching { runCatching {
val encodedLogin = login.encodeURLPath() val encodedLogin = login.encodeURLPath()
val result = Network.client.get("$serverUrl/api/users/login/$encodedLogin") { val result = Network.client.get("$serverUrl/api/users/login/$encodedLogin") {
@ -95,14 +77,16 @@ class UserRepository(
} }
setBody("""{ "code": "$encodedLogin" }""") setBody("""{ "code": "$encodedLogin" }""")
} }
if (result.status != HttpStatusCode.OK) { if (result.status != HttpStatusCode.OK) error("Status ${result.status}: ${result.body<String>()}")
error("Status ${result.status}: ${result.body<String>()}")
}
Log.d("UserRepository", result.bodyAsText())
result.body() result.body()
} }
} }
suspend fun changeRightsByLogin(login: String, token: String, isACSBlocked: Boolean): Result<Unit> = withContext(Dispatchers.IO) {
suspend fun changeRightsByLogin(
login: String,
token: String,
isACSBlocked: Boolean
): Result<Unit> = withContext(Dispatchers.IO) {
runCatching { runCatching {
val encodedLogin = login.encodeURLPath() val encodedLogin = login.encodeURLPath()
val result = Network.client.patch("$serverUrl/api/users/login/$encodedLogin") { val result = Network.client.patch("$serverUrl/api/users/login/$encodedLogin") {
@ -110,13 +94,15 @@ class UserRepository(
append(HttpHeaders.Authorization, "Bearer $token") append(HttpHeaders.Authorization, "Bearer $token")
} }
contentType(ContentType.Application.Json) contentType(ContentType.Application.Json)
setBody(UpdateUserDTO( setBody(
UpdateUserDTO(
password = null, password = null,
name = null, name = null,
photo = null, photo = null,
position = null, position = null,
isACSBlocked = isACSBlocked isACSBlocked = isACSBlocked
)) )
)
} }
if (result.status != HttpStatusCode.OK) { if (result.status != HttpStatusCode.OK) {
Log.w("UserRepository", "Status: ${result.status}, Body: ${result.body<String>()}") Log.w("UserRepository", "Status: ${result.status}, Body: ${result.body<String>()}")

View File

@ -4,7 +4,7 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@Serializable @Serializable
data class UserLoginDto( data class LoginDTO(
@SerialName("login") @SerialName("login")
val login: String, val login: String,
@SerialName("password") @SerialName("password")

View File

@ -0,0 +1,13 @@
package com.displaynone.acss.components.auth.models.user.repository.dto
import com.displaynone.acss.components.auth.models.AuthTokenPair
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class LoginResponseDTO(
@SerialName("tokens")
val tokens: AuthTokenPair,
@SerialName("user")
val user: UserDTO,
)

View File

@ -9,9 +9,9 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import com.displaynone.acss.R import com.displaynone.acss.R
import com.displaynone.acss.databinding.FragmentAuthBinding import com.displaynone.acss.databinding.FragmentAuthBinding
import com.displaynone.acss.ui.auth.AuthViewModel.Action
import com.displaynone.acss.util.collectWithLifecycle import com.displaynone.acss.util.collectWithLifecycle
import com.displaynone.acss.util.navigateTo import com.displaynone.acss.util.navigateTo
import com.displaynone.acss.ui.auth.AuthViewModel.Action
class AuthFragment : Fragment(R.layout.fragment_auth) { class AuthFragment : Fragment(R.layout.fragment_auth) {
private var _binding: FragmentAuthBinding? = null private var _binding: FragmentAuthBinding? = null
@ -20,7 +20,6 @@ class AuthFragment: Fragment(R.layout.fragment_auth) {
private val viewModel: AuthViewModel by viewModels() private val viewModel: AuthViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
_binding = FragmentAuthBinding.bind(view) _binding = FragmentAuthBinding.bind(view)
@ -44,6 +43,7 @@ class AuthFragment: Fragment(R.layout.fragment_auth) {
onLoginButtonClicked(view) onLoginButtonClicked(view)
} }
} }
@SuppressLint("StringFormatMatches") @SuppressLint("StringFormatMatches")
private fun getErrorDescription(errorCode: String): String { private fun getErrorDescription(errorCode: String): String {
return when (errorCode) { return when (errorCode) {
@ -53,9 +53,11 @@ class AuthFragment: Fragment(R.layout.fragment_auth) {
else -> getString(R.string.error_unknown, errorCode) else -> getString(R.string.error_unknown, errorCode)
} }
} }
private fun blockLoginButton() { private fun blockLoginButton() {
binding.next.isEnabled = false binding.next.isEnabled = false
} }
private fun getPasswordValidError(password: String): String { private fun getPasswordValidError(password: String): String {
if (password.length < 8) { if (password.length < 8) {
return "LenError" return "LenError"
@ -76,6 +78,7 @@ class AuthFragment: Fragment(R.layout.fragment_auth) {
} }
} }
} }
private fun getLoginValidError(username: String): String { private fun getLoginValidError(username: String): String {
val alf = "^[a-zA-Z0-9_]+$".toRegex() val alf = "^[a-zA-Z0-9_]+$".toRegex()
if (username.isEmpty()) { if (username.isEmpty()) {
@ -109,41 +112,49 @@ private fun validateLoginAndSetError(username: String) {
"EmptyError" -> { "EmptyError" -> {
binding.login.error = getString(R.string.error_login_empty) binding.login.error = getString(R.string.error_login_empty)
} }
"LenError" -> { "LenError" -> {
binding.login.error = getString(R.string.error_login_too_short) binding.login.error = getString(R.string.error_login_too_short)
} }
"StartsWithDigitError" -> { "StartsWithDigitError" -> {
binding.login.error = getString(R.string.error_login_starts_with_digit) binding.login.error = getString(R.string.error_login_starts_with_digit)
} }
"InvalidCharactersError" -> { "InvalidCharactersError" -> {
binding.login.error = getString(R.string.error_login_invalid_characters) binding.login.error = getString(R.string.error_login_invalid_characters)
} }
"NoErrors" -> { "NoErrors" -> {
binding.login.error = null binding.login.error = null
} }
} }
} }
private fun onLoginButtonClicked(view: View) { private fun onLoginButtonClicked(view: View) {
val login = binding.login.text.toString() val login = binding.login.text.toString()
val password = binding.password.text.toString() val password = binding.password.text.toString()
if (getPasswordValidError(password) != "NoErrors") { if (getPasswordValidError(password) != "NoErrors") {
val color = R.color.secondary val color = R.color.secondary
binding.next.backgroundTintList = ContextCompat.getColorStateList(requireContext(), color) binding.next.backgroundTintList =
ContextCompat.getColorStateList(requireContext(), color)
validatePasswordAndSetError(password) validatePasswordAndSetError(password)
} else { } else {
val color = R.color.primary val color = R.color.primary
binding.next.backgroundTintList = ContextCompat.getColorStateList(requireContext(), color) binding.next.backgroundTintList =
ContextCompat.getColorStateList(requireContext(), color)
} }
// if (login.isEmpty()) return
if (getLoginValidError(login) != "NoErrors") { if (getLoginValidError(login) != "NoErrors") {
val color = R.color.secondary val color = R.color.secondary
binding.next.backgroundTintList = ContextCompat.getColorStateList(requireContext(), color) binding.next.backgroundTintList =
ContextCompat.getColorStateList(requireContext(), color)
validateLoginAndSetError(login) validateLoginAndSetError(login)
} else { } else {
val color = R.color.primary val color = R.color.primary
binding.next.backgroundTintList = ContextCompat.getColorStateList(requireContext(), color) binding.next.backgroundTintList =
ContextCompat.getColorStateList(requireContext(), color)
} }

View File

@ -4,7 +4,6 @@ import android.util.Log
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.displaynone.acss.components.auth.models.user.UserServiceST import com.displaynone.acss.components.auth.models.user.UserServiceST
import com.displaynone.acss.components.auth.models.user.repository.dto.RegisterUserDto
import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@ -12,9 +11,8 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlin.reflect.typeOf
class AuthViewModel(): ViewModel() { class AuthViewModel : ViewModel() {
private val _action = Channel<Action>( private val _action = Channel<Action>(
capacity = Channel.BUFFERED, capacity = Channel.BUFFERED,
onBufferOverflow = BufferOverflow.DROP_OLDEST, onBufferOverflow = BufferOverflow.DROP_OLDEST,
@ -40,6 +38,7 @@ class AuthViewModel(): ViewModel() {
} }
} }
} }
private fun openProfile() { private fun openProfile() {
viewModelScope.launch { viewModelScope.launch {
_action.send(Action.GotoProfile) _action.send(Action.GotoProfile)

View File

@ -12,7 +12,6 @@ import androidx.navigation.NavController
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import com.displaynone.acss.R import com.displaynone.acss.R
import com.displaynone.acss.components.auth.models.user.UserServiceST import com.displaynone.acss.components.auth.models.user.UserServiceST
import com.displaynone.acss.components.auth.models.user.repository.dto.UserDTO
import com.displaynone.acss.databinding.FragmentInitBinding import com.displaynone.acss.databinding.FragmentInitBinding
import com.displaynone.acss.util.collectWithLifecycle import com.displaynone.acss.util.collectWithLifecycle
import com.displaynone.acss.util.navigateTo import com.displaynone.acss.util.navigateTo
@ -29,12 +28,12 @@ class InitFragment : Fragment(R.layout.fragment_init) {
_binding = FragmentInitBinding.bind(view) _binding = FragmentInitBinding.bind(view)
if (!checkInternetConnection()) { if (!checkInternetConnection()) {
hideSpinner()
handleError(R.string.noInternet) handleError(R.string.noInternet)
return return
} }
pingServer() pingServer()
} }
private fun pingServer() { private fun pingServer() {
@ -42,11 +41,13 @@ class InitFragment : Fragment(R.layout.fragment_init) {
viewModel.state.collectWithLifecycle(this) { state -> viewModel.state.collectWithLifecycle(this) { state ->
if (state is InitFragmentViewModel.State.Show) { if (state is InitFragmentViewModel.State.Show) {
if (!state.item) { if (!state.item) {
hideSpinner()
handleError(R.string.serverIsUnabailable) handleError(R.string.serverIsUnabailable)
} else { } else {
val navController: NavController = findNavController() val navController: NavController = findNavController()
if (!isUserAuthenticated()) { if (!isUserAuthenticated()) {
hideSpinner()
navigateTo(navController, R.id.action_nav_init_to_nav_auth) navigateTo(navController, R.id.action_nav_init_to_nav_auth)
} else { } else {
updateUser(navController) updateUser(navController)
@ -59,13 +60,12 @@ class InitFragment : Fragment(R.layout.fragment_init) {
private fun updateUser(navController: NavController) { private fun updateUser(navController: NavController) {
viewModel.updateUserInfo() viewModel.updateUserInfo()
viewModel.state.collectWithLifecycle(this) { state -> viewModel.state.collectWithLifecycle(this) { state ->
hideSpinner()
if (state is InitFragmentViewModel.State.Update) { if (state is InitFragmentViewModel.State.Update) {
if (state.item == null) {
navigateTo(navController, R.id.action_nav_init_to_nav_auth)
} else{
navigateTo(navController, R.id.action_nav_init_to_nav_profile) navigateTo(navController, R.id.action_nav_init_to_nav_profile)
} }
}
if (state is InitFragmentViewModel.State.Error) { if (state is InitFragmentViewModel.State.Error) {
navigateTo(navController, R.id.action_nav_init_to_nav_auth) navigateTo(navController, R.id.action_nav_init_to_nav_auth)
} }
@ -95,8 +95,22 @@ class InitFragment : Fragment(R.layout.fragment_init) {
return false return false
} }
private fun getCachedUser(): UserDTO? { private fun hideSpinner() {
return UserServiceST.getInstance().getUserDTO(); binding.spinner.visibility = View.GONE
}
private fun unhideSpinner() {
binding.spinner.visibility = View.VISIBLE
}
private fun hideError() {
binding.errorImage.visibility = View.GONE
binding.error.visibility = View.GONE
}
private fun unhideError() {
binding.errorImage.visibility = View.VISIBLE
binding.error.visibility = View.VISIBLE
} }
private fun isUserAuthenticated(): Boolean { private fun isUserAuthenticated(): Boolean {
@ -104,11 +118,9 @@ class InitFragment : Fragment(R.layout.fragment_init) {
} }
private fun handleError(string: Int) { private fun handleError(string: Int) {
unhideError()
binding.error.text = requireContext().getString(string) binding.error.text = requireContext().getString(string)
} }
private fun handleError(errorMessage: String) {
binding.error.text = errorMessage
}
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()

View File

@ -6,11 +6,10 @@ import androidx.lifecycle.viewModelScope
import androidx.paging.Pager import androidx.paging.Pager
import androidx.paging.PagingConfig import androidx.paging.PagingConfig
import androidx.paging.cachedIn import androidx.paging.cachedIn
import com.displaynone.acss.components.auth.models.user.UserServiceST
import com.displaynone.acss.components.acs.models.visit.VisitListPagingSource import com.displaynone.acss.components.acs.models.visit.VisitListPagingSource
import com.displaynone.acss.components.acs.models.visit.VisitServiceST import com.displaynone.acss.components.acs.models.visit.VisitServiceST
import com.displaynone.acss.components.auth.models.user.UserServiceST
import com.displaynone.acss.components.auth.models.user.repository.dto.UserDTO import com.displaynone.acss.components.auth.models.user.repository.dto.UserDTO
import io.ktor.util.reflect.instanceOf
import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@ -23,16 +22,19 @@ class ProfileViewModel(): ViewModel() {
capacity = Channel.BUFFERED, capacity = Channel.BUFFERED,
onBufferOverflow = BufferOverflow.DROP_OLDEST, onBufferOverflow = BufferOverflow.DROP_OLDEST,
) )
private var login: String = "" private var login: String = ""
fun setLogin(login1: String) { fun setLogin(login1: String) {
login = login1 login = login1
} }
val action = _action.receiveAsFlow() val action = _action.receiveAsFlow()
val _state = MutableStateFlow<State>(State.Loading) val _state = MutableStateFlow<State>(State.Loading)
val state = _state.asStateFlow() val state = _state.asStateFlow()
val visitListState = Pager( val visitListState = Pager(
config = PagingConfig(pageSize = 20, config = PagingConfig(
pageSize = 20,
enablePlaceholders = false, enablePlaceholders = false,
maxSize = 100 maxSize = 100
) )
@ -52,12 +54,12 @@ class ProfileViewModel(): ViewModel() {
fun logout() { fun logout() {
UserServiceST.getInstance().logout() UserServiceST.getInstance().logout()
} }
fun getInfo() { fun getInfo() {
viewModelScope.launch { viewModelScope.launch {
UserServiceST.getInstance().getInfo().fold( UserServiceST.getInstance().getInfo().fold(
onSuccess = { data -> onSuccess = { data ->
_state.emit(State.Show(data)) _state.emit(State.Show(data))
UserServiceST.getInstance().saveUserDTO(data)
Log.d("Pvm", data.login) Log.d("Pvm", data.login)
}, },
onFailure = { error -> onFailure = { error ->
@ -67,6 +69,7 @@ class ProfileViewModel(): ViewModel() {
) )
} }
} }
fun changeRights(login: String, isACSBlocked: Boolean) { fun changeRights(login: String, isACSBlocked: Boolean) {
viewModelScope.launch { viewModelScope.launch {
UserServiceST.getInstance().changeRights(login, isACSBlocked).fold( UserServiceST.getInstance().changeRights(login, isACSBlocked).fold(
@ -80,11 +83,13 @@ class ProfileViewModel(): ViewModel() {
) )
} }
} }
fun openAuth() { fun openAuth() {
viewModelScope.launch { viewModelScope.launch {
_action.send(Action.GoToAuth) _action.send(Action.GoToAuth)
} }
} }
fun openScan() { fun openScan() {
viewModelScope.launch { viewModelScope.launch {
_action.send(Action.GoToScan) _action.send(Action.GoToScan)
@ -96,10 +101,12 @@ class ProfileViewModel(): ViewModel() {
data class Show( data class Show(
val item: UserDTO val item: UserDTO
) : State ) : State
data class Change( data class Change(
val item: Boolean val item: Boolean
) : State ) : State
} }
sealed interface Action { sealed interface Action {
data object GoToAuth : Action data object GoToAuth : Action
data object GoToScan : Action data object GoToScan : Action

View File

@ -1,35 +1,43 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" xmlns:tools="http://schemas.android.com/tools"
android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center_horizontal">
<ImageView <ImageView
android:visibility="gone"
android:id="@+id/error_image"
android:layout_width="250dp" android:layout_width="250dp"
android:layout_height="250dp"
android:layout_gravity="center" android:layout_gravity="center"
android:layout_margin="10dp" android:layout_margin="10dp"
android:layout_height="250dp" android:src="@drawable/dinosaur"
android:src="@drawable/lost_connection"/> android:textAlignment="center"/>
<TextView <TextView
android:visibility="gone"
android:id="@+id/error" android:id="@+id/error"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center"
android:textSize="24sp"
android:layout_margin="15dp" android:layout_margin="15dp"
android:letterSpacing="0.05"
android:background="?android:attr/colorBackground" android:background="?android:attr/colorBackground"
android:gravity="center"
android:letterSpacing="0.05"
android:textColor="?attr/colorOnBackground" android:textColor="?attr/colorOnBackground"
tools:text="@string/noInternet" android:textSize="24sp"
tools:ignore="MissingConstraints" /> tools:ignore="MissingConstraints"
<ImageView tools:text="@string/noInternet" />
android:layout_width="250dp" <ProgressBar
android:layout_gravity="center" android:id="@+id/spinner"
android:layout_margin="10dp" style="?android:attr/progressBarStyle"
android:layout_height="250dp" android:layout_width="wrap_content"
android:src="@drawable/dinosaur"/> android:layout_height="wrap_content"/>
</LinearLayout> </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -6,7 +6,7 @@
<fragment <fragment
android:id="@+id/nav_init" android:id="@+id/nav_init"
android:name="com.displaynone.acss.ui.init.InitFragment" android:name="com.displaynone.acss.ui.init.InitFragment"
android:label="InitFragment"> android:label="@string/app_name">
<action <action
android:id="@+id/action_nav_init_to_nav_profile" android:id="@+id/action_nav_init_to_nav_profile"
app:destination="@id/nav_profile" /> app:destination="@id/nav_profile" />

View File

@ -1,5 +1,5 @@
<resources> <resources>
<string name="app_name">AS</string> <string name="app_name">ACSS</string>
<string name="login">Enter Login</string> <string name="login">Enter Login</string>
<string name="password">Enter password</string> <string name="password">Enter password</string>
<string name="hello">Welcome!</string> <string name="hello">Welcome!</string>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="app_name">AS</string> <string name="app_name">ACSS</string>
<string name="login">Введите логин</string> <string name="login">Введите логин</string>
<string name="password">Введите пароль</string> <string name="password">Введите пароль</string>
<string name="hello">Здравствуйте!</string> <string name="hello">Здравствуйте!</string>

View File

@ -1,5 +1,5 @@
<resources> <resources>
<string name="app_name">AS</string> <string name="app_name">ACSS</string>
<string name="login">Enter Login</string> <string name="login">Enter Login</string>
<string name="password">Enter password</string> <string name="password">Enter password</string>
<string name="hello">Welcome!</string> <string name="hello">Welcome!</string>