feat: admin section done
This commit is contained in:
parent
f76d744fd7
commit
9393cb89b0
@ -0,0 +1,14 @@
|
||||
package ru.myitschool.work.data
|
||||
|
||||
import ru.myitschool.work.data.local.CredentialsLocalDataSource
|
||||
import ru.myitschool.work.data.network.AdminNetworkDataSource
|
||||
import ru.myitschool.work.domain.admin.AdminRepository
|
||||
|
||||
class AdminRepositoryImpl(
|
||||
private val networkDataSource: AdminNetworkDataSource,
|
||||
private val localCredentialsLocalDataSource: CredentialsLocalDataSource
|
||||
): AdminRepository {
|
||||
override suspend fun blockUser(login: String): Result<Unit> {
|
||||
return networkDataSource.blockUser(login, localCredentialsLocalDataSource.getToken())
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package ru.myitschool.work.data.network
|
||||
|
||||
import io.ktor.client.request.headers
|
||||
import io.ktor.client.request.patch
|
||||
import io.ktor.http.HttpHeaders
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import ru.myitschool.work.core.Constants.SERVER_ADDRESS
|
||||
|
||||
object AdminNetworkDataSource {
|
||||
|
||||
private val client = KtorClient.client
|
||||
|
||||
suspend fun blockUser(login: String, token: String): Result<Unit> =
|
||||
withContext(Dispatchers.IO) {
|
||||
runCatching {
|
||||
val response = client.patch("$SERVER_ADDRESS/api/employees/block&login=${login}") {
|
||||
headers {
|
||||
append(HttpHeaders.Authorization, token)
|
||||
}
|
||||
}
|
||||
|
||||
if (response.status != HttpStatusCode.OK)
|
||||
error("Status ${response.status}")
|
||||
Unit
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package ru.myitschool.work.domain.admin
|
||||
|
||||
interface AdminRepository {
|
||||
suspend fun blockUser(login: String): Result<Unit>
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package ru.myitschool.work.domain.admin
|
||||
|
||||
class BlockUserUseCase(
|
||||
private val repository: AdminRepository
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(login: String) = repository.blockUser(login)
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package ru.myitschool.work.domain.passes
|
||||
|
||||
class GetUsersPassesUseCase(
|
||||
private val repository: PassRepository
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(pageNum: Int, pageSize: Int, login: String) =
|
||||
repository.getUsersPasses(pageNum, pageSize, login)
|
||||
}
|
@ -4,13 +4,18 @@ import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import ru.myitschool.work.R
|
||||
import ru.myitschool.work.utils.isOnline
|
||||
|
||||
// НЕ ИЗМЕНЯЙТЕ НАЗВАНИЕ КЛАССА!
|
||||
@AndroidEntryPoint
|
||||
class RootActivity : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_root)
|
||||
|
||||
if (!isOnline(this)) {
|
||||
val dialog = NoInternetNotificationFragment()
|
||||
dialog.isCancelable = false
|
||||
dialog.show(supportFragmentManager, "NO_INTERNET")
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package ru.myitschool.work.ui.admin.search
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import ru.myitschool.work.R
|
||||
import ru.myitschool.work.databinding.FragmentFindEmployeeBinding
|
||||
import ru.myitschool.work.utils.collectWithLifecycle
|
||||
|
||||
class AdminFragment : Fragment(R.layout.fragment_find_employee) {
|
||||
|
||||
private var _binding: FragmentFindEmployeeBinding? = null
|
||||
private val binding: FragmentFindEmployeeBinding get() = _binding!!
|
||||
|
||||
private val viewModel by viewModels<AdminViewModel> { AdminViewModel.Factory }
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
_binding = FragmentFindEmployeeBinding.bind(view)
|
||||
|
||||
viewModel.state.collectWithLifecycle(this) { state ->
|
||||
binding.username.isEnabled = state !is AdminViewModel.State.Loading
|
||||
when (state) {
|
||||
is AdminViewModel.State.Error -> binding.error.text = state.errorMessage
|
||||
is AdminViewModel.State.Loading -> Unit
|
||||
is AdminViewModel.State.Show -> {}
|
||||
is AdminViewModel.State.Waiting -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
binding.find.setOnClickListener {
|
||||
viewModel.onFind(binding.username.text.toString())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
_binding = null
|
||||
super.onDestroy()
|
||||
}
|
||||
}
|
@ -0,0 +1,127 @@
|
||||
package ru.myitschool.work.ui.admin.search
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.viewmodel.CreationExtras
|
||||
import androidx.paging.Pager
|
||||
import androidx.paging.PagingConfig
|
||||
import androidx.paging.PagingData
|
||||
import androidx.paging.cachedIn
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import ru.myitschool.work.data.AdminRepositoryImpl
|
||||
import ru.myitschool.work.data.PassRepositoryImpl
|
||||
import ru.myitschool.work.data.UserRepositoryImpl
|
||||
import ru.myitschool.work.data.local.CredentialsLocalDataSource
|
||||
import ru.myitschool.work.data.local.UserLocalDataSource
|
||||
import ru.myitschool.work.data.network.AdminNetworkDataSource
|
||||
import ru.myitschool.work.data.network.PassNetworkDataSource
|
||||
import ru.myitschool.work.data.network.UserNetworkDataSource
|
||||
import ru.myitschool.work.domain.admin.BlockUserUseCase
|
||||
import ru.myitschool.work.domain.entities.PassEntity
|
||||
import ru.myitschool.work.domain.entities.UserEntity
|
||||
import ru.myitschool.work.domain.passes.GetUsersPassesUseCase
|
||||
import ru.myitschool.work.domain.user.GetUserByLoginUseCase
|
||||
import ru.myitschool.work.ui.admin.view.UsersPassesPagingSource
|
||||
|
||||
class AdminViewModel(
|
||||
private val getUserByLoginUseCase: GetUserByLoginUseCase,
|
||||
private val getUsersPassesUseCase: GetUsersPassesUseCase,
|
||||
private val blockUserUseCase: BlockUserUseCase
|
||||
) : ViewModel() {
|
||||
|
||||
private val _state = MutableStateFlow<State>(State.Waiting)
|
||||
val state = _state.asStateFlow()
|
||||
|
||||
private var _listState: Flow<PagingData<PassEntity>>? = null
|
||||
val listState: Flow<PagingData<PassEntity>> get() = _listState!!
|
||||
|
||||
private var currentLogin: String? = null
|
||||
|
||||
fun onFind(login: String) {
|
||||
viewModelScope.launch {
|
||||
_state.emit(State.Loading)
|
||||
getUserByLoginUseCase(login).fold(
|
||||
onSuccess = { data ->
|
||||
currentLogin = login
|
||||
_state.emit(State.Show(data))
|
||||
setUpPager(login)
|
||||
},
|
||||
onFailure = { _state.emit(State.Error(it.message.toString())) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun onRefresh() {
|
||||
updateState()
|
||||
}
|
||||
|
||||
private fun updateState() {
|
||||
viewModelScope.launch {
|
||||
_state.emit(State.Loading)
|
||||
getUserByLoginUseCase(currentLogin!!).fold(
|
||||
onSuccess = { _state.emit(State.Show(it)) },
|
||||
onFailure = { _state.emit(State.Error(it.message.toString())) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpPager(login: String) {
|
||||
_listState = Pager(
|
||||
config = PagingConfig(
|
||||
pageSize = 10,
|
||||
enablePlaceholders = false,
|
||||
maxSize = 50
|
||||
)
|
||||
) {
|
||||
UsersPassesPagingSource(getUsersPassesUseCase::invoke, login)
|
||||
}.flow
|
||||
.cachedIn(viewModelScope)
|
||||
}
|
||||
|
||||
fun onBlock() {
|
||||
viewModelScope.launch {
|
||||
blockUserUseCase(currentLogin!!)
|
||||
}
|
||||
}
|
||||
|
||||
sealed interface State {
|
||||
data class Show(val user: UserEntity) : State
|
||||
data object Waiting : State
|
||||
data object Loading : State
|
||||
data class Error(val errorMessage: String) : State
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
|
||||
return AdminViewModel(
|
||||
getUserByLoginUseCase = GetUserByLoginUseCase(
|
||||
repository = UserRepositoryImpl(
|
||||
credentialsLocalDataSource = CredentialsLocalDataSource.getInstance(),
|
||||
userLocalDataSource = UserLocalDataSource,
|
||||
networkDataSource = UserNetworkDataSource
|
||||
)
|
||||
),
|
||||
getUsersPassesUseCase = GetUsersPassesUseCase(
|
||||
repository = PassRepositoryImpl(
|
||||
networkDataSource = PassNetworkDataSource,
|
||||
credentialsLocalDataSource = CredentialsLocalDataSource.getInstance()
|
||||
)
|
||||
),
|
||||
blockUserUseCase = BlockUserUseCase(
|
||||
repository = AdminRepositoryImpl(
|
||||
networkDataSource = AdminNetworkDataSource,
|
||||
localCredentialsLocalDataSource = CredentialsLocalDataSource.getInstance()
|
||||
)
|
||||
)
|
||||
) as T
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package ru.myitschool.work.ui.admin.view
|
||||
|
||||
import androidx.paging.PagingSource
|
||||
import androidx.paging.PagingState
|
||||
import ru.myitschool.work.domain.entities.PassEntity
|
||||
|
||||
class UsersPassesPagingSource(
|
||||
private val request: suspend (pageNum: Int, pageSize: Int, login: String) -> Result<List<PassEntity>>,
|
||||
private val login: String
|
||||
) : PagingSource<Int, PassEntity>() {
|
||||
override fun getRefreshKey(state: PagingState<Int, PassEntity>): Int? {
|
||||
return state.anchorPosition?.let {
|
||||
state.closestPageToPosition(it)?.prevKey?.plus(1)
|
||||
?: state.closestPageToPosition(it)?.nextKey?.minus(1)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, PassEntity> {
|
||||
val pageNum = params.key ?: 0
|
||||
return request.invoke(pageNum, params.loadSize, login).fold(
|
||||
onSuccess = { value ->
|
||||
LoadResult.Page(
|
||||
data = value,
|
||||
prevKey = (pageNum - 1).takeIf { it >= 0 },
|
||||
nextKey = (pageNum + 1).takeIf { value.size == params.loadSize }
|
||||
)
|
||||
},
|
||||
onFailure = { error -> LoadResult.Error(error) }
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
package ru.myitschool.work.ui.admin.view
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.paging.LoadState
|
||||
import com.squareup.picasso.Picasso
|
||||
import ru.myitschool.work.R
|
||||
import ru.myitschool.work.databinding.FragmentUserBinding
|
||||
import ru.myitschool.work.ui.admin.search.AdminViewModel
|
||||
import ru.myitschool.work.ui.profile.PassesListAdapter
|
||||
import ru.myitschool.work.utils.collectWithLifecycle
|
||||
import ru.myitschool.work.utils.visibleOrGone
|
||||
|
||||
class ViewUserAsAdminFragment : Fragment(R.layout.fragment_user) {
|
||||
|
||||
private var _binding: FragmentUserBinding? = null
|
||||
private val binding: FragmentUserBinding get() = _binding!!
|
||||
|
||||
private val viewModel by viewModels<AdminViewModel> { AdminViewModel.Factory }
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
_binding = FragmentUserBinding.bind(view)
|
||||
|
||||
binding.findUser.visibleOrGone(false)
|
||||
binding.logout.visibleOrGone(false)
|
||||
binding.block.visibleOrGone(true)
|
||||
|
||||
val adapter = PassesListAdapter()
|
||||
binding.passes.adapter = adapter
|
||||
|
||||
viewModel.state.collectWithLifecycle(this) { state ->
|
||||
binding.refresh.isRefreshing = state is AdminViewModel.State.Loading
|
||||
binding.content?.visibleOrGone(state is AdminViewModel.State.Show)
|
||||
|
||||
when (state) {
|
||||
is AdminViewModel.State.Loading -> Unit
|
||||
is AdminViewModel.State.Show -> {
|
||||
val user = state.user
|
||||
binding.findUser.visibleOrGone(user.isAdmin)
|
||||
binding.fullname.text = user.name
|
||||
binding.position.text = user.position
|
||||
binding.lastEntry.text = user.lastVisit
|
||||
Picasso.get().load(user.photoUrl).into(binding.photo)
|
||||
}
|
||||
|
||||
is AdminViewModel.State.Error -> binding.error.text = state.errorMessage
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
binding.block.setOnClickListener {
|
||||
AlertDialog.Builder(requireContext())
|
||||
.setTitle("Блокировка доступа")
|
||||
.setMessage("Вы уверены, что хотите заблокировать доступ работнику: ${binding.fullname}?")
|
||||
.setIcon(android.R.drawable.ic_dialog_alert)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ -> viewModel.onBlock() }
|
||||
.show()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
_binding = null
|
||||
super.onDestroy()
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user