feat: Пагинация списка входов и выходов, переименование некоторые классов, изменение эндпоинтов в запросах и добавление новых

This commit is contained in:
yastruckov 2025-02-19 11:30:57 +03:00
parent 5a490f37f4
commit 645c3e5f62
33 changed files with 250 additions and 155 deletions

View File

@ -13,6 +13,7 @@ class UserDataStoreManager(private val context: Context) {
companion object { companion object {
private val USERNAME_KEY = stringPreferencesKey("username") private val USERNAME_KEY = stringPreferencesKey("username")
private val ROLE_KEY = stringPreferencesKey("role") private val ROLE_KEY = stringPreferencesKey("role")
private val PASSWORD_KEY = stringPreferencesKey("password")
fun getInstance(context: Context): UserDataStoreManager { fun getInstance(context: Context): UserDataStoreManager {
return UserDataStoreManager(context.applicationContext) return UserDataStoreManager(context.applicationContext)
@ -22,12 +23,16 @@ class UserDataStoreManager(private val context: Context) {
val usernameFlow: Flow<String> = context.applicationContext.dataStore.data.map { prefs -> val usernameFlow: Flow<String> = context.applicationContext.dataStore.data.map { prefs ->
prefs[USERNAME_KEY] ?: "" prefs[USERNAME_KEY] ?: ""
} }
val passwordFlow: Flow<String> = context.applicationContext.dataStore.data.map { prefs ->
prefs[PASSWORD_KEY] ?: ""
}
val roleFlow: Flow<String> = context.applicationContext.dataStore.data.map{ prefs -> val roleFlow: Flow<String> = context.applicationContext.dataStore.data.map{ prefs ->
prefs[ROLE_KEY] ?: "" prefs[ROLE_KEY] ?: ""
} }
suspend fun saveUsername(username: String) { suspend fun saveCredentials(username: String, password: String) {
context.dataStore.edit { prefs -> context.dataStore.edit { prefs ->
prefs[USERNAME_KEY] = username prefs[USERNAME_KEY] = username
prefs[PASSWORD_KEY] = password
} }
} }
suspend fun saveRole(role: String){ suspend fun saveRole(role: String){
@ -35,7 +40,7 @@ class UserDataStoreManager(private val context: Context) {
prefs[ROLE_KEY] = role prefs[ROLE_KEY] = role
} }
} }
suspend fun clearUsername() { suspend fun clearCredentials() {
context.applicationContext.dataStore.edit { it.clear() } context.applicationContext.dataStore.edit { it.clear() }
} }
} }

View File

@ -2,18 +2,16 @@ package ru.myitschool.work.data.door
import android.content.Context import android.content.Context
import io.ktor.client.call.body import io.ktor.client.call.body
import io.ktor.client.request.basicAuth
import io.ktor.client.request.patch import io.ktor.client.request.patch
import io.ktor.client.request.setBody
import io.ktor.client.statement.bodyAsText import io.ktor.client.statement.bodyAsText
import io.ktor.http.ContentType
import io.ktor.http.HttpStatusCode import io.ktor.http.HttpStatusCode
import io.ktor.http.contentType import io.ktor.http.headers
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import ru.myitschool.work.core.Constants import ru.myitschool.work.core.Constants
import ru.myitschool.work.data.UserDataStoreManager import ru.myitschool.work.data.UserDataStoreManager
import ru.myitschool.work.data.dto.OpenRequestDTO
import ru.myitschool.work.utils.NetworkModule import ru.myitschool.work.utils.NetworkModule
class DoorNetworkDataSource( class DoorNetworkDataSource(
@ -21,12 +19,14 @@ class DoorNetworkDataSource(
) { ) {
private val client = NetworkModule.httpClient private val client = NetworkModule.httpClient
private val userDataStoreManager = UserDataStoreManager.getInstance(context) private val userDataStoreManager = UserDataStoreManager.getInstance(context)
suspend fun openDoor(openRequestDTO: OpenRequestDTO): Result<Unit> = withContext(Dispatchers.IO){ suspend fun openDoor(code : String): Result<Unit> = withContext(Dispatchers.IO){
runCatching { runCatching {
val username = userDataStoreManager.usernameFlow.first() val username = userDataStoreManager.usernameFlow.first()
val result = client.patch("${Constants.SERVER_ADDRESS}/api/$username/open"){ val password = userDataStoreManager.passwordFlow.first()
contentType(ContentType.Application.Json) val result = client.patch("${Constants.SERVER_ADDRESS}/api/employee/open?code=$code"){
setBody(openRequestDTO) headers{
basicAuth(username, password)
}
} }
if (result.status != HttpStatusCode.OK) { if (result.status != HttpStatusCode.OK) {
error("Status ${result.status}") error("Status ${result.status}")

View File

@ -1,12 +1,11 @@
package ru.myitschool.work.data.door package ru.myitschool.work.data.door
import ru.myitschool.work.domain.door.DoorRepo import ru.myitschool.work.domain.door.DoorRepo
import ru.myitschool.work.domain.entities.OpenEntity
class DoorRepoImpl( class DoorRepoImpl(
private val networkDataSource: DoorNetworkDataSource private val networkDataSource: DoorNetworkDataSource
) : DoorRepo { ) : DoorRepo {
override suspend fun openDoor(openEntity: OpenEntity): Result<Unit> { override suspend fun openDoor(code: String): Result<Unit> {
return networkDataSource.openDoor(openEntity.toDto()) return networkDataSource.openDoor(code)
} }
} }

View File

@ -1,9 +0,0 @@
package ru.myitschool.work.data.dto
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class OpenRequestDTO(
@SerialName("value") val value: Long
)

View File

@ -5,11 +5,11 @@ import kotlinx.serialization.Serializable
@Serializable @Serializable
data class UserDTO( data class UserDTO(
@SerialName("id") val id: Int, @SerialName("id") val id: Long?,
@SerialName("login") val login: String, @SerialName("login") val login: String?,
@SerialName("name") val name: String, @SerialName("name") val name: String?,
@SerialName("photo") val photo: String, @SerialName("authority") val authority : String?,
@SerialName("position") val position: String, @SerialName("photo") val photoUrl: String?,
@SerialName("lastVisit") val lastVisit: String @SerialName("position") val position: String?
) )

View File

@ -7,8 +7,7 @@ import java.util.Date
@Serializable @Serializable
data class VisitDTO( data class VisitDTO(
@SerialName("scanTime") @SerialName("scanTime") @Serializable(with = DateSerializer::class) val scanTime : Date?,
@Serializable(with = DateSerializer::class) val scanTime : Date?, @SerialName("readerId") val readerName: String?,
@SerialName("readerId") val readerId: Long?,
@SerialName("type") val type: String? @SerialName("type") val type: String?
) )

View File

@ -2,9 +2,11 @@ package ru.myitschool.work.data.info
import android.content.Context import android.content.Context
import io.ktor.client.call.body import io.ktor.client.call.body
import io.ktor.client.request.basicAuth
import io.ktor.client.request.get import io.ktor.client.request.get
import io.ktor.client.statement.bodyAsText import io.ktor.client.statement.bodyAsText
import io.ktor.http.HttpStatusCode import io.ktor.http.HttpStatusCode
import io.ktor.http.headers
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -22,8 +24,12 @@ class InfoNetworkDataSource(
suspend fun getInfo():Result<UserDTO> = withContext(Dispatchers.IO){ suspend fun getInfo():Result<UserDTO> = withContext(Dispatchers.IO){
runCatching { runCatching {
val username = userDataStoreManager.usernameFlow.first() val username = userDataStoreManager.usernameFlow.first()
val result = client.get("${Constants.SERVER_ADDRESS}/api/$username/info") val password = userDataStoreManager.passwordFlow.first()
val result = client.get("${Constants.SERVER_ADDRESS}/api/employee/profile"){
headers{
basicAuth(username, password)
}
}
if (result.status != HttpStatusCode.OK) { if (result.status != HttpStatusCode.OK) {
error("Status ${result.status}") error("Status ${result.status}")
} }

View File

@ -7,13 +7,16 @@ class InfoRepoImpl(
private val networkDataSource: InfoNetworkDataSource private val networkDataSource: InfoNetworkDataSource
): InfoRepo { ): InfoRepo {
override suspend fun getInfo(): Result<UserEntity> { override suspend fun getInfo(): Result<UserEntity> {
return networkDataSource.getInfo().map { dto -> return networkDataSource.getInfo().map { dto->
UserEntity( UserEntity(
name = dto.name, id = dto.id ?: 0,
position = dto.position, login = dto.login ?: "",
lastVisit = dto.lastVisit, name = dto.login ?: "",
photo = dto.photo authority = dto.authority ?: "",
photoUrl = dto.photoUrl,
position = dto.position ?: ""
) )
} }
} }
} }

View File

@ -1,9 +1,10 @@
package ru.myitschool.work.data.login package ru.myitschool.work.data.login
import io.ktor.client.call.body import io.ktor.client.call.body
import io.ktor.client.request.basicAuth
import io.ktor.client.request.get import io.ktor.client.request.get
import io.ktor.client.statement.bodyAsText
import io.ktor.http.HttpStatusCode import io.ktor.http.HttpStatusCode
import io.ktor.http.headers
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import ru.myitschool.work.core.Constants import ru.myitschool.work.core.Constants
@ -11,14 +12,16 @@ import ru.myitschool.work.utils.NetworkModule
class LoginNetworkDataSource { class LoginNetworkDataSource {
private val client = NetworkModule.httpClient private val client = NetworkModule.httpClient
suspend fun login(username: String):Result<Unit> = withContext(Dispatchers.IO){ suspend fun login(username: String, password: String):Result<Unit> = withContext(Dispatchers.IO){
runCatching { runCatching {
val result = client.get("${Constants.SERVER_ADDRESS}/api/$username/auth") val result = client.get("${Constants.SERVER_ADDRESS}/api/employee/login"){
headers{
basicAuth(username, password)
}
}
if (result.status != HttpStatusCode.OK) { if (result.status != HttpStatusCode.OK) {
error("Status ${result.status}") error("Status ${result.status}")
} }
println(result.bodyAsText())
result.body() result.body()
} }
} }

View File

@ -5,7 +5,7 @@ import ru.myitschool.work.domain.login.LoginRepo
class LoginRepoImpl( class LoginRepoImpl(
private val networkDataSource: LoginNetworkDataSource private val networkDataSource: LoginNetworkDataSource
) : LoginRepo { ) : LoginRepo {
override suspend fun login(username: String): Result<Unit> { override suspend fun login(username: String, password: String): Result<Unit> {
return networkDataSource.login(username) return networkDataSource.login(username, password)
} }
} }

View File

@ -1,21 +0,0 @@
package ru.myitschool.work.data.visitsList
import ru.myitschool.work.domain.entities.UserEntity
import ru.myitschool.work.domain.entities.VisitEntity
import ru.myitschool.work.domain.visitsList.VisitListRepo
class VisitListRepoImpl(
private val networkDataSource: VisitListNetworkDataSource
) : VisitListRepo {
override suspend fun getList(pageNum: Int, pageSize: Int): Result<List<VisitEntity>> {
return networkDataSource.getList(pageNum, pageSize).map { pagingDTO ->
pagingDTO.content?.mapNotNull { dto->
VisitEntity(
scanTime = dto.scanTime ?: return@mapNotNull null,
readerId = dto.readerId ?: return@mapNotNull null,
type = dto.type ?: return@mapNotNull null
)
}?: return Result.failure(IllegalStateException("List parse error"))
}
}
}

View File

@ -1,10 +1,11 @@
package ru.myitschool.work.data.visitsList package ru.myitschool.work.data.visitsList.employeeEntrances
import android.content.Context import android.content.Context
import io.ktor.client.call.body import io.ktor.client.call.body
import io.ktor.client.request.basicAuth
import io.ktor.client.request.get import io.ktor.client.request.get
import io.ktor.client.statement.bodyAsText
import io.ktor.http.HttpStatusCode import io.ktor.http.HttpStatusCode
import io.ktor.http.headers
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -13,7 +14,7 @@ import ru.myitschool.work.data.UserDataStoreManager
import ru.myitschool.work.data.dto.VisitListPagingDTO import ru.myitschool.work.data.dto.VisitListPagingDTO
import ru.myitschool.work.utils.NetworkModule import ru.myitschool.work.utils.NetworkModule
class VisitListNetworkDataSource( class EmployeeEntranceListNetworkDataSource(
context: Context context: Context
){ ){
private val client = NetworkModule.httpClient private val client = NetworkModule.httpClient
@ -21,7 +22,12 @@ class VisitListNetworkDataSource(
suspend fun getList(pageNum: Int, pageSize: Int):Result<VisitListPagingDTO> = withContext(Dispatchers.IO){ suspend fun getList(pageNum: Int, pageSize: Int):Result<VisitListPagingDTO> = withContext(Dispatchers.IO){
runCatching { runCatching {
val username = userDataStoreManager.usernameFlow.first() val username = userDataStoreManager.usernameFlow.first()
val result = client.get("${Constants.SERVER_ADDRESS}/api/$username/visits?page=$pageNum&size=$pageSize") val password = userDataStoreManager.passwordFlow.first()
val result = client.get("${Constants.SERVER_ADDRESS}/api/entrance?page=$pageNum&size=$pageSize"){
headers{
basicAuth(username, password)
}
}
if (result.status != HttpStatusCode.OK) { if (result.status != HttpStatusCode.OK) {
error("Status ${result.status}") error("Status ${result.status}")

View File

@ -0,0 +1,20 @@
package ru.myitschool.work.data.visitsList.employeeEntrances
import ru.myitschool.work.domain.entities.EmployeeEntranceEntity
import ru.myitschool.work.domain.visitsList.EmployeeEntranceListRepo
class EmployeeEntranceListRepoImpl(
private val networkDataSource: EmployeeEntranceListNetworkDataSource
) : EmployeeEntranceListRepo {
override suspend fun getList(pageNum: Int, pageSize: Int): Result<List<EmployeeEntranceEntity>> {
return networkDataSource.getList(pageNum, pageSize).map { pagingDTO ->
pagingDTO.content?.mapNotNull { dto->
EmployeeEntranceEntity(
scanTime = dto.scanTime ?: return@mapNotNull null,
readerName = dto.readerName ?: return@mapNotNull null,
type = dto.type ?: return@mapNotNull null
)
}?: return Result.failure(IllegalStateException("List parse error"))
}
}
}

View File

@ -1,7 +1,5 @@
package ru.myitschool.work.domain.door package ru.myitschool.work.domain.door
import ru.myitschool.work.domain.entities.OpenEntity
interface DoorRepo { interface DoorRepo {
suspend fun openDoor(openEntity: OpenEntity) : Result<Unit> suspend fun openDoor(code: String) : Result<Unit>
} }

View File

@ -1,11 +1,7 @@
package ru.myitschool.work.domain.door package ru.myitschool.work.domain.door
import ru.myitschool.work.domain.entities.OpenEntity
class OpenDoorUseCase( class OpenDoorUseCase(
private val repo: DoorRepo private val repo: DoorRepo
) { ) {
suspend operator fun invoke(openEntity: OpenEntity) = repo.openDoor( suspend operator fun invoke(code: String) = repo.openDoor(code)
openEntity = openEntity
)
} }

View File

@ -2,8 +2,8 @@ package ru.myitschool.work.domain.entities
import java.util.Date import java.util.Date
data class VisitEntity( data class EmployeeEntranceEntity(
val scanTime : Date, val scanTime : Date,
val readerId: Long, val readerName: String,
val type: String val type: String
) )

View File

@ -1,13 +0,0 @@
package ru.myitschool.work.domain.entities
import ru.myitschool.work.data.dto.OpenRequestDTO
data class OpenEntity(
val value: Long
){
fun toDto() : OpenRequestDTO{
return OpenRequestDTO(
value = value
)
}
}

View File

@ -1,8 +1,10 @@
package ru.myitschool.work.domain.entities package ru.myitschool.work.domain.entities
data class UserEntity ( data class UserEntity(
val id: Long,
val login: String,
val name: String, val name: String,
val photo: String, val authority: String,
val position: String, val photoUrl: String?,
val lastVisit: String val position: String
) )

View File

@ -1,5 +1,5 @@
package ru.myitschool.work.domain.login package ru.myitschool.work.domain.login
interface LoginRepo { interface LoginRepo {
suspend fun login(username: String): Result<Unit> suspend fun login(username: String, password: String): Result<Unit>
} }

View File

@ -3,5 +3,5 @@ package ru.myitschool.work.domain.login
class LoginUseCase( class LoginUseCase(
private val repo: LoginRepo private val repo: LoginRepo
) { ) {
suspend operator fun invoke(username : String) = repo.login(username) suspend operator fun invoke(username: String, password: String) = repo.login(username, password)
} }

View File

@ -0,0 +1,7 @@
package ru.myitschool.work.domain.visitsList
import ru.myitschool.work.domain.entities.EmployeeEntranceEntity
interface EmployeeEntranceListRepo {
suspend fun getList(pageNum : Int, pageSize: Int) : Result<List<EmployeeEntranceEntity>>
}

View File

@ -1,7 +1,7 @@
package ru.myitschool.work.domain.visitsList package ru.myitschool.work.domain.visitsList
class GetVisitListUseCase( class GetEmployeeEntranceListUseCase(
private val repo: VisitListRepo private val repo: EmployeeEntranceListRepo
) { ) {
suspend operator fun invoke(pageNum : Int, pageSize: Int) = repo.getList(pageNum, pageSize) suspend operator fun invoke(pageNum : Int, pageSize: Int) = repo.getList(pageNum, pageSize)
} }

View File

@ -1,8 +0,0 @@
package ru.myitschool.work.domain.visitsList
import ru.myitschool.work.domain.entities.UserEntity
import ru.myitschool.work.domain.entities.VisitEntity
interface VisitListRepo {
suspend fun getList(pageNum : Int, pageSize: Int) : Result<List<VisitEntity>>
}

View File

@ -29,14 +29,16 @@ class LoginFragment : Fragment(R.layout.fragment_login) {
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
val username = binding.username.text val username = binding.username.text
binding.loginBtn.isEnabled = username.length >= 3 && !username[0].isDigit() && username.matches(Regex("^[a-zA-Z0-9]*$")) val password = binding.password.text
binding.loginBtn.isEnabled = username.length >= 3 && !username[0].isDigit() && username.matches(Regex("^[a-zA-Z0-9]*$")) &&
password.length >= 6
} }
} }
binding.username.addTextChangedListener(textWatcher) binding.username.addTextChangedListener(textWatcher)
binding.loginBtn.isEnabled = false binding.loginBtn.isEnabled = false
binding.loginBtn.setOnClickListener{ binding.loginBtn.setOnClickListener{
viewModel.login(binding.username.text.toString()) viewModel.login(binding.username.text.toString(), binding.password.text.toString())
} }
lifecycleScope.launch { lifecycleScope.launch {

View File

@ -27,26 +27,27 @@ class LoginViewModel(
init { init {
viewModelScope.launch{ viewModelScope.launch{
val username = dataStoreManager.usernameFlow.first() val username = dataStoreManager.usernameFlow.first()
if(username != "") val password = dataStoreManager.passwordFlow.first()
login(username) if(username != "" && password != "")
login(username, password)
} }
} }
sealed class State { sealed class State {
object Idle : State() data object Idle : State()
object Loading : State() data object Loading : State()
object Success : State() data object Success : State()
data class Error(val message: String?) : State() data class Error(val message: String?) : State()
} }
fun login(username: String) { fun login(username: String, password: String) {
_state.value = State.Loading _state.value = State.Loading
viewModelScope.launch{ viewModelScope.launch{
useCase.invoke(username).fold( useCase.invoke(username, password).fold(
onSuccess = { data -> onSuccess = { _ ->
dataStoreManager.saveUsername(username) dataStoreManager.saveCredentials(username, password)
_state.value = State.Success _state.value = State.Success
}, },
onFailure = {e-> onFailure = {e->

View File

@ -0,0 +1,43 @@
package ru.myitschool.work.ui.main
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.paging.PagingDataAdapter
import androidx.recyclerview.widget.RecyclerView
import ru.myitschool.work.databinding.ItemVisitBinding
import ru.myitschool.work.domain.entities.EmployeeEntranceEntity
import ru.myitschool.work.utils.dateConverter
class EmployeeEntranceListAdapter : PagingDataAdapter<EmployeeEntranceEntity, EmployeeEntranceListAdapter.ViewHolder>(DiffUtil) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) : ViewHolder{
return ViewHolder(
ItemVisitBinding.inflate(
LayoutInflater.from(parent.context), parent, false
)
)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = getItem(position)
if (item != null) {
holder.bind(item)
}
}
inner class ViewHolder(
private val binding: ItemVisitBinding,
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: EmployeeEntranceEntity) {
binding.readerName.text = item.readerName
binding.timeVisit.text = dateConverter(item.scanTime)
}
}
object DiffUtil : androidx.recyclerview.widget.DiffUtil.ItemCallback<EmployeeEntranceEntity>() {
override fun areItemsTheSame(oldItem: EmployeeEntranceEntity, newItem: EmployeeEntranceEntity): Boolean {
return oldItem.scanTime == newItem.scanTime
}
override fun areContentsTheSame(oldItem: EmployeeEntranceEntity, newItem: EmployeeEntranceEntity): Boolean {
return oldItem == newItem
}
}
}

View File

@ -0,0 +1,35 @@
package ru.myitschool.work.ui.main
import androidx.paging.PagingSource
import androidx.paging.PagingState
import ru.myitschool.work.domain.entities.EmployeeEntranceEntity
class EmployeeEntranceListPagingSource(
private val request: suspend(pageNum: Int, pageSize: Int) ->Result<List<EmployeeEntranceEntity>>
) : PagingSource<Int, EmployeeEntranceEntity>() {
override fun getRefreshKey(state: PagingState<Int, EmployeeEntranceEntity>): 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, EmployeeEntranceEntity> {
val pageNum = params.key ?: 0
return request.invoke(
pageNum, params.loadSize
).fold(
onSuccess = { value ->
LoadResult.Page(
data = value,
prevKey = (pageNum - 1).takeIf { it >= 0 },
nextKey = (pageNum + 1).takeIf { value.size == params.loadSize }
)
},
onFailure = { e->
LoadResult.Error(e)
}
)
}
}

View File

@ -8,6 +8,7 @@ import androidx.fragment.app.setFragmentResultListener
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import com.squareup.picasso.Picasso import com.squareup.picasso.Picasso
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -57,6 +58,11 @@ class MainFragment : Fragment(R.layout.fragment_main) {
} }
} }
} }
binding.content.layoutManager = LinearLayoutManager(requireContext())
val adapter = EmployeeEntranceListAdapter()
binding.content.adapter = adapter
setFragmentResultListener(QrScanDestination.REQUEST_KEY) { _, bundle -> setFragmentResultListener(QrScanDestination.REQUEST_KEY) { _, bundle ->
val qrData = QrScanDestination.getDataIfExist(bundle) val qrData = QrScanDestination.getDataIfExist(bundle)
println(qrData) println(qrData)
@ -82,8 +88,7 @@ class MainFragment : Fragment(R.layout.fragment_main) {
binding.apply { binding.apply {
fullname.text = userEntity.name fullname.text = userEntity.name
position.text = userEntity.position position.text = userEntity.position
lastEntry.text = viewModel.formatDate(userEntity.lastVisit) Picasso.get().load(userEntity.photoUrl).into(photo)
Picasso.get().load(userEntity.photo).into(photo)
error.visibility = View.GONE error.visibility = View.GONE
setViewsVisibility(View.VISIBLE) setViewsVisibility(View.VISIBLE)
@ -98,7 +103,6 @@ class MainFragment : Fragment(R.layout.fragment_main) {
private fun setViewsVisibility(visibility: Int) { private fun setViewsVisibility(visibility: Int) {
binding.fullname.visibility = visibility binding.fullname.visibility = visibility
binding.position.visibility = visibility binding.position.visibility = visibility
binding.lastEntry.visibility = visibility
binding.photo.visibility = visibility binding.photo.visibility = visibility
binding.logout.visibility = visibility binding.logout.visibility = visibility
binding.scan.visibility = visibility binding.scan.visibility = visibility

View File

@ -6,6 +6,9 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.CreationExtras import androidx.lifecycle.viewmodel.CreationExtras
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.cachedIn
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
@ -14,16 +17,31 @@ import kotlinx.coroutines.withContext
import ru.myitschool.work.data.UserDataStoreManager import ru.myitschool.work.data.UserDataStoreManager
import ru.myitschool.work.data.info.InfoNetworkDataSource import ru.myitschool.work.data.info.InfoNetworkDataSource
import ru.myitschool.work.data.info.InfoRepoImpl import ru.myitschool.work.data.info.InfoRepoImpl
import ru.myitschool.work.data.visitsList.employeeEntrances.EmployeeEntranceListNetworkDataSource
import ru.myitschool.work.data.visitsList.employeeEntrances.EmployeeEntranceListRepoImpl
import ru.myitschool.work.domain.info.GetInfoUseCase import ru.myitschool.work.domain.info.GetInfoUseCase
import ru.myitschool.work.domain.visitsList.GetEmployeeEntranceListUseCase
import ru.myitschool.work.utils.UserState import ru.myitschool.work.utils.UserState
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
class MainViewModel( class MainViewModel(
private val useCase: GetInfoUseCase, private val infoUseCase: GetInfoUseCase,
private val listUseCase: GetEmployeeEntranceListUseCase,
application: Application application: Application
) : AndroidViewModel(application) { ) : AndroidViewModel(application) {
val listState = Pager(
config = PagingConfig(
pageSize = 10,
enablePlaceholders = false,
maxSize = 30
)
) {
println("Creating PagingSource")
EmployeeEntranceListPagingSource(listUseCase::invoke)
}.flow.cachedIn(viewModelScope)
private val _userState = MutableStateFlow<UserState>(UserState.Loading) private val _userState = MutableStateFlow<UserState>(UserState.Loading)
val userState: StateFlow<UserState> get() = _userState val userState: StateFlow<UserState> get() = _userState
@ -45,7 +63,7 @@ class MainViewModel(
fun getUserData() { fun getUserData() {
_userState.value = UserState.Loading _userState.value = UserState.Loading
viewModelScope.launch { viewModelScope.launch {
useCase.invoke().fold( infoUseCase.invoke().fold(
onSuccess = { data -> _userState.value = UserState.Success(data) }, onSuccess = { data -> _userState.value = UserState.Success(data) },
onFailure = { _userState.value = UserState.Error } onFailure = { _userState.value = UserState.Error }
) )
@ -55,7 +73,7 @@ class MainViewModel(
fun clearUsername() { fun clearUsername() {
viewModelScope.launch{ viewModelScope.launch{
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
dataStoreManager.clearUsername() dataStoreManager.clearCredentials()
} }
} }
@ -64,16 +82,22 @@ class MainViewModel(
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory { val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T { override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
val repoImpl = InfoRepoImpl( val infoRepoImpl = InfoRepoImpl(
networkDataSource = InfoNetworkDataSource( networkDataSource = InfoNetworkDataSource(
context = extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as Application context = extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as Application
) )
) )
val listInfoImpl = EmployeeEntranceListRepoImpl(
networkDataSource = EmployeeEntranceListNetworkDataSource(
context = extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as Application
)
)
val useCase = GetInfoUseCase(repoImpl) val infoUseCase = GetInfoUseCase(infoRepoImpl)
val listUseCase = GetEmployeeEntranceListUseCase(listInfoImpl)
return MainViewModel( return MainViewModel(
useCase, extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as Application infoUseCase, listUseCase, extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as Application
) as T ) as T
} }
} }

View File

@ -1,20 +0,0 @@
package ru.myitschool.work.ui.main
import androidx.paging.PagingSource
import androidx.paging.PagingState
import ru.myitschool.work.domain.entities.VisitEntity
class VisitListPagingSource(
private val request: suspend(pageNum: Int, pageSize: Int) ->Result<List<VisitEntity>>
) : PagingSource<Int, VisitEntity>() {
override fun getRefreshKey(state: PagingState<Int, VisitEntity>): 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, VisitEntity> {
TODO("Not yet implemented")
}
}

View File

@ -7,7 +7,6 @@ import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import ru.myitschool.work.R import ru.myitschool.work.R
import ru.myitschool.work.databinding.FragmentQrResultBinding import ru.myitschool.work.databinding.FragmentQrResultBinding
import ru.myitschool.work.domain.entities.OpenEntity
import ru.myitschool.work.utils.collectWhenStarted import ru.myitschool.work.utils.collectWhenStarted
class QrResultFragment : Fragment(R.layout.fragment_qr_result) { class QrResultFragment : Fragment(R.layout.fragment_qr_result) {
@ -19,7 +18,7 @@ class QrResultFragment : Fragment(R.layout.fragment_qr_result) {
_binding = FragmentQrResultBinding.bind(view) _binding = FragmentQrResultBinding.bind(view)
var qrData = arguments?.getString("qr_data") var qrData = arguments?.getString("qr_data")
if (qrData != null) { if (qrData != null) {
viewModel.openDoor(OpenEntity(qrData.toLong())) viewModel.openDoor(qrData)
} }
else{ else{
binding.result.text = getString(R.string.result_null_text) binding.result.text = getString(R.string.result_null_text)

View File

@ -13,7 +13,6 @@ import kotlinx.coroutines.launch
import ru.myitschool.work.data.door.DoorNetworkDataSource import ru.myitschool.work.data.door.DoorNetworkDataSource
import ru.myitschool.work.data.door.DoorRepoImpl import ru.myitschool.work.data.door.DoorRepoImpl
import ru.myitschool.work.domain.door.OpenDoorUseCase import ru.myitschool.work.domain.door.OpenDoorUseCase
import ru.myitschool.work.domain.entities.OpenEntity
class QrResultViewModel( class QrResultViewModel(
private val useCase: OpenDoorUseCase, private val useCase: OpenDoorUseCase,
@ -27,11 +26,11 @@ class QrResultViewModel(
data object Loading : State() data object Loading : State()
data object Error : State() data object Error : State()
} }
fun openDoor(openEntity: OpenEntity){ fun openDoor(code: String){
_state.value = State.Loading _state.value = State.Loading
viewModelScope.launch{ viewModelScope.launch{
useCase.invoke(openEntity).fold( useCase.invoke(code).fold(
onSuccess = { data-> onSuccess = { _ ->
_state.value = State.Success _state.value = State.Success
}, },
onFailure = { _ -> onFailure = { _ ->

View File

@ -0,0 +1,15 @@
package ru.myitschool.work.utils
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
fun dateConverter(date: Date?) : String {
if (date != null) {
val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
println(dateFormat.format(date).toString())
return dateFormat.format(date).toString()
}
return ""
}