feat: last commit

This commit is contained in:
a1pha 2025-02-20 16:54:50 +03:00
parent 9d45790735
commit 5297d47340
14 changed files with 171 additions and 103 deletions

View File

@ -1,5 +1,6 @@
package ru.myitschool.work.data package ru.myitschool.work.data
import android.util.Log
import ru.myitschool.work.data.dto.PassDto import ru.myitschool.work.data.dto.PassDto
import ru.myitschool.work.data.local.CredentialsLocalDataSource import ru.myitschool.work.data.local.CredentialsLocalDataSource
import ru.myitschool.work.data.network.PassNetworkDataSource import ru.myitschool.work.data.network.PassNetworkDataSource

View File

@ -77,7 +77,7 @@ class UserRepositoryImpl(
override suspend fun getUserByLogin(login: String): Result<UserEntity> { override suspend fun getUserByLogin(login: String): Result<UserEntity> {
return networkDataSource.getUserByLogin(login, credentialsLocalDataSource.getToken()).fold( return networkDataSource.getUserByLogin(login, credentialsLocalDataSource.getToken()).fold(
onSuccess = { map(it) }, onFailure = { error(it) } onSuccess = { map(it) }, onFailure = { Result.failure(it) }
) )
} }
} }

View File

@ -6,9 +6,9 @@ import kotlinx.serialization.Serializable
@Serializable @Serializable
data class PassDto( data class PassDto(
@SerialName("time")
val time: String?,
@SerialName("localDateTime") @SerialName("localDateTime")
val time: String?,
@SerialName("terminal")
val terminal: TerminalDto? val terminal: TerminalDto?
) )

View File

@ -15,7 +15,7 @@ object AdminNetworkDataSource {
suspend fun blockUser(login: String, token: String): Result<Unit> = suspend fun blockUser(login: String, token: String): Result<Unit> =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
runCatching { runCatching {
val response = client.patch("$SERVER_ADDRESS/api/employees/block?login=$login") { val response = client.patch("$SERVER_ADDRESS/api/users/block?username=$login") {
headers { headers {
append(HttpHeaders.Authorization, token) append(HttpHeaders.Authorization, token)
} }
@ -30,7 +30,7 @@ object AdminNetworkDataSource {
suspend fun unblockUser(login: String, token: String): Result<Unit> = suspend fun unblockUser(login: String, token: String): Result<Unit> =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
runCatching { runCatching {
val response = client.patch("$SERVER_ADDRESS/api/employees/unblock?login=$login") { val response = client.patch("$SERVER_ADDRESS/api/users/unblock?username=$login") {
headers { headers {
append(HttpHeaders.Authorization, token) append(HttpHeaders.Authorization, token)
} }

View File

@ -52,13 +52,13 @@ object UserNetworkDataSource {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
runCatching { runCatching {
val response = val response =
KtorClient.client.get("${Constants.SERVER_ADDRESS}/api/employees/find?${login}") { KtorClient.client.get("${Constants.SERVER_ADDRESS}/api/users/get?username=${login}") {
headers { headers {
append(HttpHeaders.Authorization, token) append(HttpHeaders.Authorization, token)
} }
} }
if (response.status == HttpStatusCode.OK) if (response.status != HttpStatusCode.OK)
error("Status ${response.status}") error("Status ${response.status}")
response.body() response.body()
} }

View File

@ -4,6 +4,7 @@ import android.os.Bundle
import android.view.View import android.view.View
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import ru.myitschool.work.R import ru.myitschool.work.R
import ru.myitschool.work.databinding.FragmentFindEmployeeBinding import ru.myitschool.work.databinding.FragmentFindEmployeeBinding
import ru.myitschool.work.utils.collectWithLifecycle import ru.myitschool.work.utils.collectWithLifecycle
@ -23,7 +24,7 @@ class AdminFragment : Fragment(R.layout.fragment_find_employee) {
when (state) { when (state) {
is AdminViewModel.State.Error -> binding.error.text = state.errorMessage is AdminViewModel.State.Error -> binding.error.text = state.errorMessage
is AdminViewModel.State.Loading -> Unit is AdminViewModel.State.Loading -> Unit
is AdminViewModel.State.Show -> {} is AdminViewModel.State.Show -> findNavController().navigate(R.id.action_adminFragment_to_viewUserAsAdminFragment)
is AdminViewModel.State.Waiting -> Unit is AdminViewModel.State.Waiting -> Unit
} }
} }
@ -31,7 +32,6 @@ class AdminFragment : Fragment(R.layout.fragment_find_employee) {
binding.find.setOnClickListener { binding.find.setOnClickListener {
viewModel.onFind(binding.username.text.toString()) viewModel.onFind(binding.username.text.toString())
} }
} }
override fun onDestroy() { override fun onDestroy() {

View File

@ -38,10 +38,10 @@ class AdminViewModel(
private val _state = MutableStateFlow<State>(State.Waiting) private val _state = MutableStateFlow<State>(State.Waiting)
val state = _state.asStateFlow() val state = _state.asStateFlow()
private var _listState: Flow<PagingData<PassEntity>>? = null // private var _listState: Flow<PagingData<PassEntity>>? = null
val listState: Flow<PagingData<PassEntity>> get() = _listState!! // val listState: Flow<PagingData<PassEntity>> get() = _listState!!
private var currentLogin: String? = null private var currentLogin: String = "pivanov"
fun onFind(login: String) { fun onFind(login: String) {
viewModelScope.launch { viewModelScope.launch {
@ -72,16 +72,16 @@ class AdminViewModel(
} }
private fun setUpPager(login: String) { private fun setUpPager(login: String) {
_listState = Pager( // _listState = Pager(
config = PagingConfig( // config = PagingConfig(
pageSize = 10, // pageSize = 10,
enablePlaceholders = false, // enablePlaceholders = false,
maxSize = 50 // maxSize = 50
) // )
) { // ) {
UsersPassesPagingSource(getUsersPassesUseCase::invoke, login) // UsersPassesPagingSource(getUsersPassesUseCase::invoke, login)
}.flow // }.flow
.cachedIn(viewModelScope) // .cachedIn(viewModelScope)
} }
fun onBlock() { fun onBlock() {

View File

@ -49,27 +49,28 @@ class ViewUserAsAdminFragment : Fragment(R.layout.fragment_user) {
is AdminViewModel.State.Error -> binding.error.text = state.errorMessage is AdminViewModel.State.Error -> binding.error.text = state.errorMessage
is AdminViewModel.State.Waiting -> Unit is AdminViewModel.State.Waiting -> Unit
} }
viewModel.listState.collectWithLifecycle(this) { listState ->
adapter.submitData(listState)
}
adapter.loadStateFlow.collectWithLifecycle(this) { data ->
val dataState = data.refresh
binding.refresh.isRefreshing = dataState is LoadState.Loading
binding.error.visibleOrGone(dataState is LoadState.Error)
if (dataState is LoadState.Error) {
binding.error.text = dataState.error.toString()
}
}
binding.refresh.setOnRefreshListener {
viewModel.onRefresh()
adapter.refresh()
}
} }
// viewModel.listState.collectWithLifecycle(this) { listState ->
// adapter.submitData(listState)
// }
// adapter.loadStateFlow.collectWithLifecycle(this) { data ->
// val dataState = data.refresh
// binding.refresh.isRefreshing = dataState is LoadState.Loading
// binding.error.visibleOrGone(dataState is LoadState.Error)
//
// if (dataState is LoadState.Error) {
// binding.error.text = dataState.error.toString()
// }
// }
binding.refresh.setOnRefreshListener {
viewModel.onRefresh()
// adapter.refresh()
}
binding.block.setOnClickListener { binding.block.setOnClickListener {
AlertDialog.Builder(requireContext()) AlertDialog.Builder(requireContext())
.setTitle("Блокировка доступа") .setTitle("Блокировка доступа")

View File

@ -1,15 +1,17 @@
package ru.myitschool.work.ui.profile package ru.myitschool.work.ui.profile
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.paging.PagingDataAdapter import androidx.paging.PagingDataAdapter
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import ru.myitschool.work.databinding.PassItemBinding import ru.myitschool.work.databinding.PassItemBinding
import ru.myitschool.work.domain.entities.PassEntity import ru.myitschool.work.domain.entities.PassEntity
class PassesListAdapter: class PassesListAdapter:
PagingDataAdapter<PassEntity, PassesListAdapter.ViewHolder>(CenterDiff) { ListAdapter<PassEntity, PassesListAdapter.ViewHolder>(CenterDiff) {
class ViewHolder( class ViewHolder(
private val binding: PassItemBinding, private val binding: PassItemBinding,
@ -38,12 +40,6 @@ class PassesListAdapter:
} }
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind( holder.bind(getItem(position))
getItem(position) ?: PassEntity(
type = "Загрузка ...",
name = "Терминал №...",
time = "Давным-давно..."
)
)
} }
} }

View File

@ -1,5 +1,6 @@
package ru.myitschool.work.ui.profile package ru.myitschool.work.ui.profile
import android.util.Log
import androidx.paging.PagingSource import androidx.paging.PagingSource
import androidx.paging.PagingState import androidx.paging.PagingState
import ru.myitschool.work.domain.entities.PassEntity import ru.myitschool.work.domain.entities.PassEntity
@ -15,12 +16,12 @@ class PassesPagingSource(
} }
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, PassEntity> { override suspend fun load(params: LoadParams<Int>): LoadResult<Int, PassEntity> {
val pageNum = params.key ?: 1 val pageNum = params.key ?: 0
return request.invoke(pageNum, params.loadSize).fold( return request.invoke(pageNum, params.loadSize).fold(
onSuccess = { value -> onSuccess = { value ->
LoadResult.Page( LoadResult.Page(
data = value, data = value,
prevKey = (pageNum - 1).takeIf { it >= 0 }, prevKey = (pageNum - 1).takeIf { it > 0 },
nextKey = (pageNum + 1).takeIf { value.size == params.loadSize } nextKey = (pageNum + 1).takeIf { value.size == params.loadSize }
) )
}, },

View File

@ -7,7 +7,6 @@ import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.paging.LoadState
import com.squareup.picasso.Picasso import com.squareup.picasso.Picasso
import ru.myitschool.work.R import ru.myitschool.work.R
import ru.myitschool.work.databinding.FragmentUserBinding import ru.myitschool.work.databinding.FragmentUserBinding
@ -33,10 +32,10 @@ class UserFragment : Fragment(R.layout.fragment_user) {
viewModel.state.collectWithLifecycle(this) { state -> viewModel.state.collectWithLifecycle(this) { state ->
binding.refresh.isRefreshing = state is UserViewModel.State.Loading binding.refresh.isRefreshing = state is UserViewModel.State.Loading
binding.content.visibleOrGone(state is UserViewModel.State.Show) binding.content.visibleOrGone(state is UserViewModel.State.Show)
Log.d("info", state.toString())
when (state) { when (state) {
is UserViewModel.State.Loading -> Unit is UserViewModel.State.Loading -> Unit
is UserViewModel.State.Show -> { is UserViewModel.State.Show -> {
adapter.submitList(state.passes)
val user = state.userEntity val user = state.userEntity
binding.scan.visibleOrGone(!user.isCardBlocked) binding.scan.visibleOrGone(!user.isCardBlocked)
binding.findUser.visibleOrGone(user.isAdmin) binding.findUser.visibleOrGone(user.isAdmin)
@ -46,53 +45,39 @@ class UserFragment : Fragment(R.layout.fragment_user) {
Picasso.get().load(user.photoUrl).into(binding.photo) Picasso.get().load(user.photoUrl).into(binding.photo)
} }
} }
viewModel.listState.collectWithLifecycle(this) { listState ->
adapter.submitData(listState)
}
adapter.loadStateFlow.collectWithLifecycle(this) { data ->
val dataState = data.refresh
binding.refresh.isRefreshing = dataState is LoadState.Loading
binding.error.visibleOrGone(dataState is LoadState.Error)
if (dataState is LoadState.Error) {
binding.error.text = dataState.error.toString()
}
}
binding.refresh.setOnRefreshListener {
viewModel.onRefresh()
adapter.refresh()
}
binding.logout.setOnClickListener {
AlertDialog.Builder(requireContext())
.setTitle("Выход")
.setMessage("Вы уверены, что хотите выйти из аккаунта?")
.setIcon(android.R.drawable.ic_dialog_alert)
.setPositiveButton(android.R.string.ok) { _, _ ->
viewModel.onLogout()
findNavController().navigate(R.id.action_userFragment_to_loginFragment)
}
.show()
}
binding.findUser.setOnClickListener {
findNavController().navigate(R.id.find_user)
}
binding.scan.setOnClickListener {
findNavController().navigate(R.id.action_userFragment_to_qrScanFragment)
}
parentFragmentManager.setFragmentResultListener(
QrScanDestination.REQUEST_KEY, this
) { _, result ->
parentFragmentManager.setFragmentResult(RESPONSE_KEY, result)
findNavController().navigate(R.id.action_userFragment_to_qrResultFragment)
}
} }
binding.refresh.setOnRefreshListener {
viewModel.onRefresh()
}
binding.logout.setOnClickListener {
AlertDialog.Builder(requireContext())
.setTitle("Выход")
.setMessage("Вы уверены, что хотите выйти из аккаунта?")
.setIcon(android.R.drawable.ic_dialog_alert)
.setPositiveButton(android.R.string.ok) { _, _ ->
viewModel.onLogout()
findNavController().navigate(R.id.action_userFragment_to_loginFragment)
}
.show()
}
binding.findUser.setOnClickListener {
findNavController().navigate(R.id.action_userFragment_to_adminFragment)
}
binding.scan.setOnClickListener {
findNavController().navigate(R.id.action_userFragment_to_qrScanFragment)
}
parentFragmentManager.setFragmentResultListener(
QrScanDestination.REQUEST_KEY, this
) { _, result ->
parentFragmentManager.setFragmentResult(RESPONSE_KEY, result)
findNavController().navigate(R.id.action_userFragment_to_qrResultFragment)
}
} }
override fun onDestroy() { override fun onDestroy() {

View File

@ -1,5 +1,6 @@
package ru.myitschool.work.ui.profile package ru.myitschool.work.ui.profile
import android.util.Log
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
@ -16,6 +17,7 @@ import ru.myitschool.work.data.local.CredentialsLocalDataSource
import ru.myitschool.work.data.local.UserLocalDataSource import ru.myitschool.work.data.local.UserLocalDataSource
import ru.myitschool.work.data.network.PassNetworkDataSource import ru.myitschool.work.data.network.PassNetworkDataSource
import ru.myitschool.work.data.network.UserNetworkDataSource import ru.myitschool.work.data.network.UserNetworkDataSource
import ru.myitschool.work.domain.entities.PassEntity
import ru.myitschool.work.domain.entities.UserEntity import ru.myitschool.work.domain.entities.UserEntity
import ru.myitschool.work.domain.login.LogoutUseCase import ru.myitschool.work.domain.login.LogoutUseCase
import ru.myitschool.work.domain.passes.GetCurrentPassesUseCase import ru.myitschool.work.domain.passes.GetCurrentPassesUseCase
@ -48,7 +50,14 @@ class UserViewModel(
private fun updateState() { private fun updateState() {
viewModelScope.launch { viewModelScope.launch {
_state.emit(State.Loading) _state.emit(State.Loading)
_state.emit(State.Show(getCurrentUserUseCase()))
_state.emit(
State.Show(
getCurrentUserUseCase(),
getCurrentPassesUseCase(0, 30).getOrNull()!!
)
)
} }
} }
@ -62,7 +71,7 @@ class UserViewModel(
sealed interface State { sealed interface State {
data object Loading : State data object Loading : State
data class Show(val userEntity: UserEntity) : State data class Show(val userEntity: UserEntity, val passes: List<PassEntity>) : State
} }
companion object { companion object {

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<vector
android:height="108dp"
android:width="108dp"
android:viewportHeight="108"
android:viewportWidth="108"
xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z"/>
<path android:fillColor="#00000000" android:pathData="M9,0L9,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,0L19,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,0L29,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,0L39,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,0L49,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,0L59,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,0L69,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,0L79,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M89,0L89,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M99,0L99,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,9L108,9"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,19L108,19"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,29L108,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,39L108,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,49L108,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,59L108,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,69L108,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,79L108,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,89L108,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,99L108,99"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,29L89,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,39L89,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,49L89,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,59L89,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,69L89,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,79L89,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,19L29,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,19L39,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,19L49,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,19L59,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,19L69,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,19L79,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
</vector>

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background"/> <background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground"/> <foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon> </adaptive-icon>