feat: Получение данных пользователя по логину + фиксы багов
This commit is contained in:
parent
0446bddcc9
commit
041db2955f
@ -1,59 +0,0 @@
|
||||
package ru.myitschool.work
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.Fragment
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
|
||||
// TODO: Rename parameter arguments, choose names that match
|
||||
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
|
||||
private const val ARG_PARAM1 = "param1"
|
||||
private const val ARG_PARAM2 = "param2"
|
||||
|
||||
/**
|
||||
* A simple [Fragment] subclass.
|
||||
* Use the [admin.newInstance] factory method to
|
||||
* create an instance of this fragment.
|
||||
*/
|
||||
class admin : Fragment() {
|
||||
// TODO: Rename and change types of parameters
|
||||
private var param1: String? = null
|
||||
private var param2: String? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
arguments?.let {
|
||||
param1 = it.getString(ARG_PARAM1)
|
||||
param2 = it.getString(ARG_PARAM2)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
// Inflate the layout for this fragment
|
||||
return inflater.inflate(R.layout.fragment_admin, container, false)
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Use this factory method to create a new instance of
|
||||
* this fragment using the provided parameters.
|
||||
*
|
||||
* @param param1 Parameter 1.
|
||||
* @param param2 Parameter 2.
|
||||
* @return A new instance of fragment admin.
|
||||
*/
|
||||
// TODO: Rename and change types and number of parameters
|
||||
@JvmStatic
|
||||
fun newInstance(param1: String, param2: String) =
|
||||
admin().apply {
|
||||
arguments = Bundle().apply {
|
||||
putString(ARG_PARAM1, param1)
|
||||
putString(ARG_PARAM2, param2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -28,7 +28,7 @@ class LastEntranceNetworkDataSource(
|
||||
basicAuth(username, password)
|
||||
}
|
||||
}
|
||||
if (result.status != HttpStatusCode.OK) {
|
||||
if (result.status != HttpStatusCode.OK && result.status != HttpStatusCode.NoContent) {
|
||||
error("Status ${result.status}")
|
||||
}
|
||||
result.body()
|
||||
|
@ -3,6 +3,7 @@ package ru.myitschool.work.data.profile.admin
|
||||
import android.content.Context
|
||||
import io.ktor.client.call.body
|
||||
import io.ktor.client.request.basicAuth
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.client.request.post
|
||||
import io.ktor.client.statement.bodyAsText
|
||||
import io.ktor.http.HttpStatusCode
|
||||
@ -10,28 +11,43 @@ import io.ktor.http.headers
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.withContext
|
||||
import ru.myitschool.work.R
|
||||
import ru.myitschool.work.core.Constants
|
||||
import ru.myitschool.work.data.UserDataStoreManager
|
||||
import ru.myitschool.work.dto.EmployeeDTO
|
||||
import ru.myitschool.work.utils.NetworkModule
|
||||
|
||||
class EmployeeNetworkDataSource(
|
||||
context: Context
|
||||
class EmployeeInfoNetworkDataSource(
|
||||
private val context: Context
|
||||
) {
|
||||
private val client = NetworkModule.httpClient
|
||||
private val userDataStoreManager = UserDataStoreManager.getInstance(context)
|
||||
suspend fun getProfile(login : String):Result<EmployeeDTO> = withContext(Dispatchers.IO){
|
||||
runCatching {
|
||||
println(login)
|
||||
val username = userDataStoreManager.usernameFlow.first()
|
||||
println(username)
|
||||
val password = userDataStoreManager.passwordFlow.first()
|
||||
val result = client.post("${Constants.SERVER_ADDRESS}/api/employee/$login"){
|
||||
val result = client.get("${Constants.SERVER_ADDRESS}/api/employee/$login"){
|
||||
headers{
|
||||
basicAuth(username, password)
|
||||
}
|
||||
}
|
||||
if (result.status != HttpStatusCode.OK) {
|
||||
error("Status ${result.status}")
|
||||
when(result.status){
|
||||
HttpStatusCode.Unauthorized -> {context.getString(R.string.admin_unauthorized)}
|
||||
HttpStatusCode.Forbidden -> error(context.getString(R.string.admin_forbidden))
|
||||
HttpStatusCode.NotFound -> error(context.getString(R.string.not_found))
|
||||
HttpStatusCode.OK -> result.body()
|
||||
}
|
||||
if(result.status == HttpStatusCode.Unauthorized){
|
||||
error(context.getString(R.string.admin_unauthorized))
|
||||
}
|
||||
if (result.status != HttpStatusCode.OK) {
|
||||
println(result.status)
|
||||
error("Status ${result.status}")
|
||||
|
||||
}
|
||||
|
||||
println(result.bodyAsText())
|
||||
result.body()
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
package ru.myitschool.work.data.profile.admin
|
||||
|
||||
import ru.myitschool.work.entities.EmployeeEntity
|
||||
import ru.myitschool.work.domain.profile.admin.EmployeeProfileRepo
|
||||
import ru.myitschool.work.domain.profile.admin.EmployeeInfoRepo
|
||||
|
||||
class EmployeeProfileRepoImpl(
|
||||
private val networkDataSource: EmployeeNetworkDataSource
|
||||
) : EmployeeProfileRepo {
|
||||
class EmployeeInfoRepoImpl(
|
||||
private val networkDataSource: EmployeeInfoNetworkDataSource
|
||||
) : EmployeeInfoRepo {
|
||||
override suspend fun getInfo(login : String): Result<EmployeeEntity> {
|
||||
return networkDataSource.getProfile(login).map { dto ->
|
||||
EmployeeEntity(
|
@ -2,6 +2,6 @@ package ru.myitschool.work.domain.profile.admin
|
||||
|
||||
import ru.myitschool.work.entities.EmployeeEntity
|
||||
|
||||
interface EmployeeProfileRepo {
|
||||
interface EmployeeInfoRepo {
|
||||
suspend fun getInfo(login : String): Result<EmployeeEntity>
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package ru.myitschool.work.domain.profile.admin
|
||||
|
||||
class GetEmployeeProfileUseCase(
|
||||
private val repo : EmployeeProfileRepo
|
||||
class GetEmployeeInfoUseCase(
|
||||
private val repo : EmployeeInfoRepo
|
||||
) {
|
||||
suspend operator fun invoke(login : String) = repo.getInfo(login)
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
package ru.myitschool.work.ui.admin
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.View
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import ru.myitschool.work.R
|
||||
import ru.myitschool.work.databinding.FragmentAdminBinding
|
||||
import ru.myitschool.work.entities.EmployeeEntity
|
||||
import ru.myitschool.work.ui.login.LoginViewModel
|
||||
import ru.myitschool.work.utils.collectWithLifecycle
|
||||
|
||||
class AdminFragment : Fragment(R.layout.fragment_admin) {
|
||||
private var _binding: FragmentAdminBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
private val viewModel: AdminViewModel by viewModels{ AdminViewModel.Factory }
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
_binding = FragmentAdminBinding.bind(view)
|
||||
binding.searchBtn.setOnClickListener {
|
||||
viewModel.searchUser(
|
||||
binding.search.text.toString()
|
||||
)
|
||||
}
|
||||
|
||||
val textWatcher = object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
|
||||
|
||||
}
|
||||
|
||||
override fun afterTextChanged(s: Editable?) {}
|
||||
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
||||
val search = binding.search.text
|
||||
val isEnabled =
|
||||
search.length >= 3 && !search[0].isDigit() && search.matches(Regex("^[a-zA-Z0-9]*$"))
|
||||
binding.searchBtn.isEnabled = isEnabled
|
||||
if (isEnabled) {
|
||||
binding.searchBtn.setBackgroundColor(resources.getColor(R.color.accent_color))
|
||||
binding.searchBtn.imageTintList = ColorStateList.valueOf(
|
||||
ContextCompat.getColor(
|
||||
requireContext(),
|
||||
R.color.white
|
||||
))
|
||||
} else {
|
||||
binding.searchBtn.setBackgroundColor(resources.getColor(R.color.bg_color))
|
||||
binding.searchBtn.imageTintList = ColorStateList.valueOf(
|
||||
ContextCompat.getColor(
|
||||
requireContext(),
|
||||
R.color.secondary_text_color
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
binding.search.addTextChangedListener(textWatcher)
|
||||
viewModel.infoState.collectWithLifecycle(this){ state ->
|
||||
when(state){
|
||||
is AdminViewModel.SearchState.Error -> {
|
||||
binding.error.visibility = View.VISIBLE
|
||||
binding.error.text = state.message
|
||||
|
||||
}
|
||||
AdminViewModel.SearchState.Loading -> {
|
||||
binding.error.visibility = View.GONE
|
||||
binding
|
||||
}
|
||||
is AdminViewModel.SearchState.Success -> {
|
||||
binding.error.visibility = View.GONE
|
||||
showUserData(state.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private fun showUserData(user: EmployeeEntity){
|
||||
binding.userName.text = user.name
|
||||
binding.position.text = user.position
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package ru.myitschool.work.ui.admin
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.viewmodel.CreationExtras
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import ru.myitschool.work.data.profile.admin.EmployeeInfoNetworkDataSource
|
||||
import ru.myitschool.work.data.profile.admin.EmployeeInfoRepoImpl
|
||||
import ru.myitschool.work.domain.profile.admin.EmployeeInfoRepo
|
||||
import ru.myitschool.work.domain.profile.admin.GetEmployeeInfoUseCase
|
||||
import ru.myitschool.work.entities.EmployeeEntity
|
||||
|
||||
class AdminViewModel(
|
||||
private val getInfoUseCase: GetEmployeeInfoUseCase,
|
||||
application: Application
|
||||
) : AndroidViewModel(application) {
|
||||
private val _infoState = MutableStateFlow<SearchState>(SearchState.Loading)
|
||||
val infoState: StateFlow<SearchState> = _infoState.asStateFlow()
|
||||
|
||||
sealed class SearchState {
|
||||
data object Loading : SearchState()
|
||||
data class Success(val data: EmployeeEntity) : SearchState()
|
||||
data class Error(val message: String?) : SearchState()
|
||||
}
|
||||
fun searchUser(login : String){
|
||||
_infoState.value = SearchState.Loading
|
||||
viewModelScope.launch {
|
||||
getInfoUseCase.invoke(login).fold(
|
||||
onSuccess = { data ->
|
||||
_infoState.value = SearchState.Success(data)
|
||||
},
|
||||
onFailure = { e ->
|
||||
_infoState.value = SearchState.Error(e.message)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
companion object {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
|
||||
val infoRepoImpl = EmployeeInfoRepoImpl(
|
||||
networkDataSource = EmployeeInfoNetworkDataSource(
|
||||
context = extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as Application
|
||||
)
|
||||
)
|
||||
val useCase = GetEmployeeInfoUseCase(infoRepoImpl)
|
||||
|
||||
return AdminViewModel(
|
||||
useCase, extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as Application
|
||||
) as T
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -23,6 +23,7 @@ class LoginFragment : Fragment(R.layout.fragment_login) {
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
_binding = FragmentLoginBinding.bind(view)
|
||||
|
||||
val textWatcher = object : TextWatcher{
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
|
||||
|
||||
@ -37,9 +38,11 @@ class LoginFragment : Fragment(R.layout.fragment_login) {
|
||||
binding.loginBtn.isEnabled = isEnabled
|
||||
if(isEnabled){
|
||||
binding.loginBtn.backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.accent_color))
|
||||
binding.loginBtn.setTextColor(ContextCompat.getColor(requireContext(), R.color.white))
|
||||
}
|
||||
else{
|
||||
binding.loginBtn.backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.bg_color))
|
||||
binding.loginBtn.setTextColor(ContextCompat.getColor(requireContext(), R.color.secondary_text_color))
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
package ru.myitschool.work.ui.main
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.setFragmentResultListener
|
||||
@ -9,6 +12,7 @@ import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.paging.LoadState
|
||||
import androidx.paging.map
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.squareup.picasso.Picasso
|
||||
import kotlinx.coroutines.delay
|
||||
@ -36,7 +40,7 @@ class MainFragment : Fragment(R.layout.fragment_main) {
|
||||
|
||||
viewModel.getUserData()
|
||||
viewModel.getLastEntryDate()
|
||||
|
||||
findNavController().navigate(R.id.admin)
|
||||
binding.logout.setOnClickListener { logout() }
|
||||
binding.scan.setOnClickListener { onScanClick() }
|
||||
viewModel.userState.collectWhenStarted(this) { state ->
|
||||
@ -125,15 +129,27 @@ class MainFragment : Fragment(R.layout.fragment_main) {
|
||||
|
||||
}
|
||||
private fun showUserData(employeeEntity: EmployeeEntity) {
|
||||
binding.apply {
|
||||
fullname.text = employeeEntity.name
|
||||
println(employeeEntity.name)
|
||||
position.text = employeeEntity.position
|
||||
Picasso.get().load(employeeEntity.photoUrl).into(photo)
|
||||
|
||||
error.visibility = View.GONE
|
||||
setViewsVisibility(View.VISIBLE)
|
||||
binding.fullname.text = employeeEntity.name
|
||||
println(employeeEntity.name)
|
||||
binding.position.text = employeeEntity.position
|
||||
Picasso.get().load(employeeEntity.photoUrl).into(binding.photo)
|
||||
|
||||
binding.error.visibility = View.GONE
|
||||
setViewsVisibility(View.VISIBLE)
|
||||
|
||||
binding.scan.isEnabled = employeeEntity.qrEnabled
|
||||
when(employeeEntity.qrEnabled){
|
||||
true -> {
|
||||
binding.scan.backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.accent_color))
|
||||
binding.scan.setTextColor(ContextCompat.getColor(requireContext(), R.color.white))
|
||||
}
|
||||
false -> {
|
||||
binding.scan.backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.bg_color))
|
||||
binding.scan.setTextColor(ContextCompat.getColor(requireContext(), R.color.secondary_text_color))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun showError() {
|
||||
@ -147,6 +163,8 @@ class MainFragment : Fragment(R.layout.fragment_main) {
|
||||
binding.photo.visibility = visibility
|
||||
binding.logout.visibility = visibility
|
||||
binding.scan.visibility = visibility
|
||||
binding.blockMain.visibility = visibility
|
||||
binding.blockHistory.visibility = visibility
|
||||
}
|
||||
|
||||
|
||||
|
@ -12,6 +12,9 @@
|
||||
<action
|
||||
android:id="@+id/action_loginFragment_to_mainFragment"
|
||||
app:destination="@id/mainFragment" />
|
||||
<action
|
||||
android:id="@+id/action_loginFragment_to_admin"
|
||||
app:destination="@id/admin" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/mainFragment"
|
||||
@ -21,6 +24,9 @@
|
||||
<action
|
||||
android:id="@+id/action_mainFragment_to_qrScanFragment"
|
||||
app:destination="@id/qrScanFragment" />
|
||||
<action
|
||||
android:id="@+id/action_mainFragment_to_admin"
|
||||
app:destination="@id/admin" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/qrScanFragment"
|
||||
@ -40,4 +46,9 @@
|
||||
android:id="@+id/action_qrResultFragment_to_mainFragment"
|
||||
app:destination="@id/mainFragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/admin"
|
||||
android:name="ru.myitschool.work.ui.admin.AdminFragment"
|
||||
android:label="fragment_admin"
|
||||
tools:layout="@layout/fragment_admin" />
|
||||
</navigation>
|
@ -18,4 +18,8 @@
|
||||
<string name="history_title">Visit history</string>
|
||||
<string name="login_unauthorized">Incorrect login or password</string>
|
||||
<string name="search_hint">Enter the employee\'s username</string>
|
||||
<string name="admin_unauthorized">Unauthorized</string>
|
||||
<string name="admin_forbidden">Forbidden \n
|
||||
How did you get here?</string>
|
||||
<string name="not_found"> 404 Not Found</string>
|
||||
</resources>
|
@ -20,6 +20,10 @@
|
||||
<string name="history_title">История посещений</string>
|
||||
<string name="login_unauthorized">Неправильное имя пользователя или пароль</string>
|
||||
<string name="search_hint">Введите логин сотрудника</string>
|
||||
<string name="admin_unauthorized">Вы не авторизованы</string>
|
||||
<string name="admin_forbidden">Не достаточно прав \n
|
||||
Как ты сюда попал?</string>
|
||||
<string name="not_found">404 Не найдено</string>
|
||||
<!-- TODO: Remove or change this placeholder text -->
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user