From 6a78060f2545e7d77d7598265c500078c483a0b7 Mon Sep 17 00:00:00 2001 From: Izlydov Date: Thu, 20 Feb 2025 12:22:54 +0300 Subject: [PATCH] added init logic, updated userDTO, added change rights logic in admin panel, some fixes --- .../java/com/displaynone/ACSSApplication.kt | 2 + .../acs/models/visit/VisitAdapter.kt | 3 +- .../components/auth/models/user/UserMapper.kt | 1 + .../auth/models/user/UserServiceST.kt | 3 + .../models/user/repository/UserRepository.kt | 39 +++++++----- .../user/repository/dto/UpdateUserDTO.kt | 18 ++++++ .../models/user/repository/dto/UserDTO.kt | 3 +- .../components/init/models/InitServiceST.kt | 25 ++++++++ .../init/models/repository/InitRepository.kt | 31 ++++++++++ .../acss/components/main/utils/utils.kt | 22 ------- .../displaynone/acss/ui/init/InitFragment.kt | 62 ++++++++++++------- .../acss/ui/init/InitFragmentViewModel.kt | 47 ++++++++++++-- .../acss/ui/profile/ProfileFragment.kt | 6 ++ .../acss/ui/profile/ProfileViewModel.kt | 17 +++++ 14 files changed, 212 insertions(+), 67 deletions(-) create mode 100644 app/src/main/java/com/displaynone/acss/components/auth/models/user/repository/dto/UpdateUserDTO.kt create mode 100644 app/src/main/java/com/displaynone/acss/components/init/models/InitServiceST.kt create mode 100644 app/src/main/java/com/displaynone/acss/components/init/models/repository/InitRepository.kt delete mode 100644 app/src/main/java/com/displaynone/acss/components/main/utils/utils.kt diff --git a/app/src/main/java/com/displaynone/ACSSApplication.kt b/app/src/main/java/com/displaynone/ACSSApplication.kt index 04dde97..0206c5c 100644 --- a/app/src/main/java/com/displaynone/ACSSApplication.kt +++ b/app/src/main/java/com/displaynone/ACSSApplication.kt @@ -4,6 +4,7 @@ import android.app.Application import com.displaynone.acss.components.acs.models.gate.GateServiceST import com.displaynone.acss.components.acs.models.visit.VisitServiceST import com.displaynone.acss.components.auth.models.user.UserServiceST +import com.displaynone.acss.components.init.models.InitServiceST class ACSSApplication : Application() { override fun onCreate() { @@ -11,5 +12,6 @@ class ACSSApplication : Application() { UserServiceST.createInstance(this) GateServiceST.createInstance() VisitServiceST.createInstance() + InitServiceST.createInstance() } } diff --git a/app/src/main/java/com/displaynone/acss/components/acs/models/visit/VisitAdapter.kt b/app/src/main/java/com/displaynone/acss/components/acs/models/visit/VisitAdapter.kt index 70ebf0a..8a5f698 100644 --- a/app/src/main/java/com/displaynone/acss/components/acs/models/visit/VisitAdapter.kt +++ b/app/src/main/java/com/displaynone/acss/components/acs/models/visit/VisitAdapter.kt @@ -5,6 +5,7 @@ import android.view.ViewGroup import androidx.paging.PagingDataAdapter import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView +import com.displaynone.acss.R import com.displaynone.acss.components.acs.models.visit.repository.dto.VisitDto import com.displaynone.acss.databinding.ItemScannerViewBinding @@ -37,7 +38,7 @@ class VisitAdapter: PagingDataAdapter(VisitDi ): RecyclerView.ViewHolder(binding.root) { fun bind(item: VisitDto){ binding.scanTime.text = item.createdAt - binding.scannerId.text = item.gateId.toString() + binding.scannerId.text = "Id " + item.gateId.toString() } } diff --git a/app/src/main/java/com/displaynone/acss/components/auth/models/user/UserMapper.kt b/app/src/main/java/com/displaynone/acss/components/auth/models/user/UserMapper.kt index d6a11ce..344297d 100644 --- a/app/src/main/java/com/displaynone/acss/components/auth/models/user/UserMapper.kt +++ b/app/src/main/java/com/displaynone/acss/components/auth/models/user/UserMapper.kt @@ -23,6 +23,7 @@ class UserMapper { photo = userEntity.photo, position = userEntity.position, roles = emptyList(), + isACSBlocked = false // lastVisit = userEntity.lastVisit, ) return userDto diff --git a/app/src/main/java/com/displaynone/acss/components/auth/models/user/UserServiceST.kt b/app/src/main/java/com/displaynone/acss/components/auth/models/user/UserServiceST.kt index 083734c..4064041 100644 --- a/app/src/main/java/com/displaynone/acss/components/auth/models/user/UserServiceST.kt +++ b/app/src/main/java/com/displaynone/acss/components/auth/models/user/UserServiceST.kt @@ -70,6 +70,9 @@ class UserServiceST( fun logout(){ tokenManager.clear() } + suspend fun changeRights(login: String, isACSBlocked: Boolean): Result { + return userRepository.changeRightsByLogin(login, tokenManager.authTokenPair!!.accessToken, isACSBlocked) + } suspend fun getInfo(): Result{ if (!tokenManager.hasTokens()) { throw RuntimeException("access token is null") diff --git a/app/src/main/java/com/displaynone/acss/components/auth/models/user/repository/UserRepository.kt b/app/src/main/java/com/displaynone/acss/components/auth/models/user/repository/UserRepository.kt index 16a8a1a..e251dd5 100644 --- a/app/src/main/java/com/displaynone/acss/components/auth/models/user/repository/UserRepository.kt +++ b/app/src/main/java/com/displaynone/acss/components/auth/models/user/repository/UserRepository.kt @@ -5,11 +5,13 @@ import com.displaynone.acss.components.auth.models.AuthTokenPair 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.UserDTO import com.displaynone.acss.components.auth.models.user.repository.dto.UserLoginDto import io.ktor.client.call.body import io.ktor.client.request.get import io.ktor.client.request.headers +import io.ktor.client.request.patch import io.ktor.client.request.post import io.ktor.client.request.setBody import io.ktor.client.statement.bodyAsText @@ -101,25 +103,28 @@ class UserRepository( result.body() } } - - suspend fun register(login: String, password: String): Result = - withContext(Dispatchers.IO) { - runCatching { - val result = Network.client.post("$serverUrl/api/person/register") { - contentType(ContentType.Application.Json) - setBody( - RegisterUserDto( - login = login, - password = password, - ) - ) + suspend fun changeRightsByLogin(login: String, token: String, isACSBlocked: Boolean): Result = withContext(Dispatchers.IO) { + runCatching { + val encodedLogin = login.encodeURLPath() + val result = Network.client.patch("$serverUrl/api/users/login/$encodedLogin") { + headers{ + append(HttpHeaders.Authorization, "Bearer $token") } - if (result.status != HttpStatusCode.Created) { - Log.w("UserRepository", "Status: ${result.status}, Body: ${result.body()}") - error("Status ${result.status}: ${result.body()}") - } - Unit + contentType(ContentType.Application.Json) + setBody(UpdateUserDTO( + password = null, + name = null, + photo = null, + position = null, + isACSBlocked = isACSBlocked + )) } + if (result.status != HttpStatusCode.OK) { + Log.w("UserRepository", "Status: ${result.status}, Body: ${result.body()}") + error("Status ${result.status}: ${result.body()}") + } + Unit } + } } \ No newline at end of file diff --git a/app/src/main/java/com/displaynone/acss/components/auth/models/user/repository/dto/UpdateUserDTO.kt b/app/src/main/java/com/displaynone/acss/components/auth/models/user/repository/dto/UpdateUserDTO.kt new file mode 100644 index 0000000..52d9266 --- /dev/null +++ b/app/src/main/java/com/displaynone/acss/components/auth/models/user/repository/dto/UpdateUserDTO.kt @@ -0,0 +1,18 @@ +package com.displaynone.acss.components.auth.models.user.repository.dto + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class UpdateUserDTO ( + @SerialName("password") + val password: String?, + @SerialName("name") + val name: String?, + @SerialName("photo") + val photo: String?, + @SerialName("position") + val position: String?, + @SerialName("isACSBlocked") + val isACSBlocked: Boolean, +) \ No newline at end of file diff --git a/app/src/main/java/com/displaynone/acss/components/auth/models/user/repository/dto/UserDTO.kt b/app/src/main/java/com/displaynone/acss/components/auth/models/user/repository/dto/UserDTO.kt index 141492a..3cb866b 100644 --- a/app/src/main/java/com/displaynone/acss/components/auth/models/user/repository/dto/UserDTO.kt +++ b/app/src/main/java/com/displaynone/acss/components/auth/models/user/repository/dto/UserDTO.kt @@ -22,7 +22,8 @@ data class UserDTO ( val position: String, @SerialName("roles") val roles: List, - + @SerialName("acsblocked") + val isACSBlocked: Boolean // @SerialName("lastVisit") // val lastVisit: String, ) : java.io.Serializable \ No newline at end of file diff --git a/app/src/main/java/com/displaynone/acss/components/init/models/InitServiceST.kt b/app/src/main/java/com/displaynone/acss/components/init/models/InitServiceST.kt new file mode 100644 index 0000000..82126c5 --- /dev/null +++ b/app/src/main/java/com/displaynone/acss/components/init/models/InitServiceST.kt @@ -0,0 +1,25 @@ +package com.displaynone.acss.components.init.models + +import com.displaynone.acss.components.acs.models.visit.VisitServiceST +import com.displaynone.acss.components.auth.models.user.UserServiceST +import com.displaynone.acss.components.init.models.repository.InitRepository + +class InitServiceST { + private val initRepository = InitRepository() + companion object { + private var instance: InitServiceST? = null + + fun createInstance() { + if (instance == null) { + instance = InitServiceST() + } + } + + fun getInstance(): InitServiceST { + return instance ?: throw RuntimeException("null instance") + } + } + suspend fun ping(): Result { + return initRepository.ping(UserServiceST.getInstance().getTokenPair().accessToken) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/displaynone/acss/components/init/models/repository/InitRepository.kt b/app/src/main/java/com/displaynone/acss/components/init/models/repository/InitRepository.kt new file mode 100644 index 0000000..fc3efdd --- /dev/null +++ b/app/src/main/java/com/displaynone/acss/components/init/models/repository/InitRepository.kt @@ -0,0 +1,31 @@ +package com.displaynone.acss.components.init.models.repository + +import android.util.Log +import com.displaynone.acss.components.auth.models.user.repository.dto.UserDTO +import com.displaynone.acss.config.Constants.serverUrl +import com.displaynone.acss.config.Network +import io.ktor.client.call.body +import io.ktor.client.request.get +import io.ktor.client.request.headers +import io.ktor.client.request.setBody +import io.ktor.client.statement.bodyAsText +import io.ktor.http.HttpHeaders +import io.ktor.http.HttpStatusCode +import io.ktor.http.encodeURLPath +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +class InitRepository { + suspend fun ping(token: String): Result = withContext( + Dispatchers.IO){ + runCatching { + val result = Network.client.get("$serverUrl/api/utils/ping") { + headers { + append(HttpHeaders.Authorization, "Bearer $token") + } + } + + result.status == HttpStatusCode.OK + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/displaynone/acss/components/main/utils/utils.kt b/app/src/main/java/com/displaynone/acss/components/main/utils/utils.kt deleted file mode 100644 index e0584b3..0000000 --- a/app/src/main/java/com/displaynone/acss/components/main/utils/utils.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.displaynone.acss.components.main.utils - -import com.displaynone.acss.config.Constants.serverUrl -import com.displaynone.acss.config.Network -import io.ktor.client.request.get -import io.ktor.client.request.headers -import io.ktor.http.HttpHeaders -import io.ktor.http.HttpStatusCode -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext - -suspend fun pingServer(token: String): Result = withContext(Dispatchers.IO) { - runCatching { - val result = Network.client.get("$serverUrl/api/ping") { - headers { - append(HttpHeaders.Authorization, "Bearer $token") - } - } - - result.status == HttpStatusCode.OK - } -} \ No newline at end of file diff --git a/app/src/main/java/com/displaynone/acss/ui/init/InitFragment.kt b/app/src/main/java/com/displaynone/acss/ui/init/InitFragment.kt index a08fedd..6f7bc43 100644 --- a/app/src/main/java/com/displaynone/acss/ui/init/InitFragment.kt +++ b/app/src/main/java/com/displaynone/acss/ui/init/InitFragment.kt @@ -7,18 +7,23 @@ import android.os.Bundle import android.util.Log import android.view.View import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels import androidx.navigation.NavController import androidx.navigation.fragment.findNavController import com.displaynone.acss.R 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.util.collectWithLifecycle import com.displaynone.acss.util.navigateTo class InitFragment : Fragment(R.layout.fragment_init) { private var _binding: FragmentInitBinding? = null private val binding: FragmentInitBinding get() = _binding!! + private val viewModel: InitFragmentViewModel by viewModels() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) _binding = FragmentInitBinding.bind(view) @@ -28,33 +33,43 @@ class InitFragment : Fragment(R.layout.fragment_init) { return } - if (!pingServer()) { - handleError(R.string.serverIsUnabailable) - return - } + pingServer() - val navController: NavController = findNavController() - - if (!isUserAuthenticated()) { - navigateTo(navController, R.id.action_nav_init_to_nav_auth) - return - } - - val user: UserDTO? = updateUser() - if (user == null) { - navigateTo(navController, R.id.action_nav_init_to_nav_auth) - return - } - - navigateTo(navController, R.id.action_nav_init_to_nav_profile) } - private fun pingServer(): Boolean { - return true + private fun pingServer() { + viewModel.pingServer() + viewModel.state.collectWithLifecycle(this) { state -> + if (state is InitFragmentViewModel.State.Show){ + if(!state.item){ + handleError(R.string.serverIsUnabailable) + } else{ + val navController: NavController = findNavController() + + if (!isUserAuthenticated()) { + navigateTo(navController, R.id.action_nav_init_to_nav_auth) + } else { + updateUser(navController) + } + } + } + } } - private fun updateUser(): UserDTO? { - return null + private fun updateUser(navController: NavController) { + viewModel.updateUserInfo() + viewModel.state.collectWithLifecycle(this){ state -> + 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) + } + } + if (state is InitFragmentViewModel.State.Error){ + navigateTo(navController, R.id.action_nav_init_to_nav_auth) + } + } } private fun checkInternetConnection(): Boolean { @@ -91,4 +106,7 @@ class InitFragment : Fragment(R.layout.fragment_init) { private fun handleError(string: Int) { binding.error.text = requireContext().getString(string) } + private fun handleError(errorMessage: String) { + binding.error.text = errorMessage + } } \ No newline at end of file diff --git a/app/src/main/java/com/displaynone/acss/ui/init/InitFragmentViewModel.kt b/app/src/main/java/com/displaynone/acss/ui/init/InitFragmentViewModel.kt index d295983..28be599 100644 --- a/app/src/main/java/com/displaynone/acss/ui/init/InitFragmentViewModel.kt +++ b/app/src/main/java/com/displaynone/acss/ui/init/InitFragmentViewModel.kt @@ -1,14 +1,53 @@ package com.displaynone.acss.ui.init +import android.util.Log import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +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.init.models.InitServiceST +import com.displaynone.acss.ui.profile.ProfileViewModel.State +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch class InitFragmentViewModel: ViewModel() { - private fun pingServer(): Boolean { - + val _state = MutableStateFlow(State.Loading) + val state = _state.asStateFlow() + fun pingServer() { + viewModelScope.launch { + InitServiceST.getInstance().ping().fold( + onSuccess = { _state.emit(State.Show(true)) }, + onFailure = { + _state.emit(State.Show(false)) + Log.d("InitFragmentViewModel", "failed to ping") } + ) + } } - private fun updateUser(): UserDTO? { - + fun updateUserInfo() { + viewModelScope.launch { + UserServiceST.getInstance().getInfo().fold( + onSuccess = { data -> + _state.emit(State.Update(data)) + }, + onFailure = { error -> + _state.emit(State.Error(error.message.toString())) + Log.d("InitFragmentViewModel", "failed to update user info ${error.message}") + } + ) + } + } + sealed interface State { + data object Loading : State + data class Show( + val item: Boolean + ) : State + data class Update( + val item: UserDTO + ) : State + data class Error( + val errorMessage: String + ) : State } } \ No newline at end of file diff --git a/app/src/main/java/com/displaynone/acss/ui/profile/ProfileFragment.kt b/app/src/main/java/com/displaynone/acss/ui/profile/ProfileFragment.kt index 57fee69..09e5295 100644 --- a/app/src/main/java/com/displaynone/acss/ui/profile/ProfileFragment.kt +++ b/app/src/main/java/com/displaynone/acss/ui/profile/ProfileFragment.kt @@ -57,6 +57,7 @@ class ProfileFragment: Fragment(R.layout.fragment_profile) { waitForQRScanResult() } else{ showData(getUserDto()!!) + Log.d("ProfileFragment", "set login") viewModel.visitListStateFromLogin.collectWithLifecycle(this) { data -> adapter.submitData(data) @@ -92,6 +93,7 @@ class ProfileFragment: Fragment(R.layout.fragment_profile) { binding.logout.visibility = View.GONE binding.scan.visibility = View.GONE binding.buttonSearch.visibility = View.GONE + binding.changeRights.visibility = View.VISIBLE } fun showMyData(userDTO: UserDTO){ binding.fio.text = userDTO.name @@ -101,7 +103,11 @@ class ProfileFragment: Fragment(R.layout.fragment_profile) { fun showData(userDTO: UserDTO){ binding.fio.text = userDTO.name binding.position.text = userDTO.position + binding.changeRights.text = if(userDTO.isACSBlocked) "Разблокировать пользователя" else "Заблокировать пользователя" viewModel.setLogin(login1 = userDTO.login) + binding.changeRights.setOnClickListener { + viewModel.changeRights(userDTO.login, !userDTO.isACSBlocked) + } Log.d("ProfileFragment", userDTO.login) // binding.lastEntry.text = userDTO.lastVisit diff --git a/app/src/main/java/com/displaynone/acss/ui/profile/ProfileViewModel.kt b/app/src/main/java/com/displaynone/acss/ui/profile/ProfileViewModel.kt index d58d9b3..9c1e44a 100644 --- a/app/src/main/java/com/displaynone/acss/ui/profile/ProfileViewModel.kt +++ b/app/src/main/java/com/displaynone/acss/ui/profile/ProfileViewModel.kt @@ -10,6 +10,7 @@ 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.VisitServiceST 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.Channel import kotlinx.coroutines.flow.MutableStateFlow @@ -66,6 +67,19 @@ class ProfileViewModel(): ViewModel() { ) } } + fun changeRights(login: String, isACSBlocked: Boolean){ + viewModelScope.launch { + UserServiceST.getInstance().changeRights(login, isACSBlocked).fold( + onSuccess = { + Log.d("ProfileViewModel", "changed rights") +// _state.emit(State.Change(isACSBlocked)) + }, + onFailure = { error -> + Log.d("ProfileViewModel", "failed to change rights ${error.message}") + } + ) + } + } fun openAuth(){ viewModelScope.launch { _action.send(Action.GoToAuth) @@ -82,6 +96,9 @@ class ProfileViewModel(): ViewModel() { data class Show( val item: UserDTO ) : State + data class Change( + val item: Boolean + ) } sealed interface Action { data object GoToAuth: Action