feat: View profile use case done

This commit is contained in:
a1pha 2025-02-19 18:53:18 +03:00
parent 16199d485e
commit f76d744fd7
10 changed files with 282 additions and 2 deletions

View File

@ -0,0 +1,51 @@
package ru.myitschool.work.data
import ru.myitschool.work.data.dto.PassDto
import ru.myitschool.work.data.local.CredentialsLocalDataSource
import ru.myitschool.work.data.network.PassNetworkDataSource
import ru.myitschool.work.domain.entities.PassEntity
import ru.myitschool.work.domain.passes.PassRepository
class PassRepositoryImpl(
private val networkDataSource: PassNetworkDataSource,
private val credentialsLocalDataSource: CredentialsLocalDataSource
) : PassRepository {
override suspend fun getCurrentPasses(pageNum: Int, pageSize: Int): Result<List<PassEntity>> {
return map(
networkDataSource.getCurrentPasses(
pageNum,
pageSize,
credentialsLocalDataSource.getToken()
)
)
}
override suspend fun getUsersPasses(
pageNum: Int,
pageSize: Int,
login: String
): Result<List<PassEntity>> {
return map(
networkDataSource.getUsersPasses(
login = login,
pageNum = pageNum,
pageSize = pageSize,
token = credentialsLocalDataSource.getToken()
)
)
}
private fun map(listDto: Result<List<PassDto>>): Result<List<PassEntity>> {
return listDto.map { successListDto ->
successListDto.mapNotNull { dto ->
PassEntity(
type = dto.type ?: return@mapNotNull null,
name = dto.name ?: return@mapNotNull null,
time = dto.time ?: return@mapNotNull null
)
}
}
}
}

View File

@ -0,0 +1,15 @@
package ru.myitschool.work.data.dto
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class PassDto(
@SerialName("type")
val type: String?,
@SerialName("name")
val name: String?,
@SerialName("time")
val time: String?
)

View File

@ -0,0 +1,56 @@
package ru.myitschool.work.data.network
import io.ktor.client.call.body
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
import ru.myitschool.work.core.Constants.SERVER_ADDRESS
import ru.myitschool.work.data.dto.PassDto
object PassNetworkDataSource {
private val client = KtorClient.client
suspend fun getCurrentPasses(
pageNum: Int,
pageSize: Int,
token: String
): Result<List<PassDto>> =
withContext(Dispatchers.IO) {
runCatching {
val response =
client.get("$SERVER_ADDRESS/api/passes?pageNum=$pageNum&pageSize=$pageSize") {
headers {
append(HttpHeaders.Authorization, token)
}
}
if (response.status != HttpStatusCode.OK)
error("${response.status}")
response.body()
}
}
suspend fun getUsersPasses(
login: String,
pageNum: Int,
pageSize: Int,
token: String
): Result<List<PassDto>> = withContext(Dispatchers.IO) {
runCatching {
val response =
client.get("$SERVER_ADDRESS/api/passes?login=$login&pageNum=$pageNum&pageSize=$pageSize") {
headers {
append(HttpHeaders.Authorization, token)
}
}
if (response.status != HttpStatusCode.OK)
error("Status ${response.status}")
response.body()
}
}
}

View File

@ -0,0 +1,7 @@
package ru.myitschool.work.domain.entities
data class PassEntity(
val type: String,
val name: String,
val time: String
)

View File

@ -0,0 +1,11 @@
package ru.myitschool.work.domain.passes
import ru.myitschool.work.domain.entities.PassEntity
class GetCurrentPassesUseCase(
private val repository: PassRepository
) {
suspend operator fun invoke(pageNum: Int, pageSize: Int): Result<List<PassEntity>> =
repository.getCurrentPasses(pageNum, pageSize)
}

View File

@ -0,0 +1,9 @@
package ru.myitschool.work.domain.passes
import ru.myitschool.work.domain.entities.PassEntity
interface PassRepository {
suspend fun getCurrentPasses(pageNum: Int, pageSize: Int): Result<List<PassEntity>>
suspend fun getUsersPasses(pageNum: Int, pageSize: Int, login: String): Result<List<PassEntity>>
}

View File

@ -0,0 +1,27 @@
package ru.myitschool.work.ui
import android.os.Bundle
import android.view.View
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import ru.myitschool.work.R
import ru.myitschool.work.databinding.FragmentNoInternetNotificationBinding
import ru.myitschool.work.utils.isOnline
class NoInternetNotificationFragment: BottomSheetDialogFragment(R.layout.fragment_no_internet_notification) {
private var _binding: FragmentNoInternetNotificationBinding? = null
private val binding: FragmentNoInternetNotificationBinding get() = _binding!!
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
_binding = FragmentNoInternetNotificationBinding.bind(view)
binding.close.setOnClickListener {
if (isOnline(requireActivity())) dismiss()
}
}
override fun onDestroy() {
_binding = null
super.onDestroy()
}
}

View File

@ -0,0 +1,50 @@
package ru.myitschool.work.ui.profile
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.paging.PagingDataAdapter
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import ru.myitschool.work.databinding.PassItemBinding
import ru.myitschool.work.domain.entities.PassEntity
class PassesListAdapter:
PagingDataAdapter<PassEntity, PassesListAdapter.ViewHolder>(CenterDiff) {
class ViewHolder(
private val binding: PassItemBinding,
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: PassEntity) {
binding.time.text = item.time
binding.name.text = item.name
binding.type.text = item.type
}
}
object CenterDiff : DiffUtil.ItemCallback<PassEntity>() {
override fun areContentsTheSame(oldItem: PassEntity, newItem: PassEntity): Boolean {
return oldItem.name == newItem.name
}
override fun areItemsTheSame(oldItem: PassEntity, newItem: PassEntity): Boolean {
return oldItem == newItem
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
PassItemBinding.inflate(LayoutInflater.from(parent.context), parent, false),
)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(
getItem(position) ?: PassEntity(
type = "Загрузка ...",
name = "Терминал №...",
time = "Давным-давно..."
)
)
}
}

View File

@ -0,0 +1,30 @@
package ru.myitschool.work.ui.profile
import androidx.paging.PagingSource
import androidx.paging.PagingState
import ru.myitschool.work.domain.entities.PassEntity
class PassesPagingSource(
private val request: suspend (pageNum: Int, pageSize: Int) -> Result<List<PassEntity>>
) : 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).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) }
)
}
}

View File

@ -4,25 +4,43 @@ 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.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import ru.myitschool.work.data.PassRepositoryImpl
import ru.myitschool.work.data.UserRepositoryImpl import ru.myitschool.work.data.UserRepositoryImpl
import ru.myitschool.work.data.local.CredentialsLocalDataSource 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.UserNetworkDataSource import ru.myitschool.work.data.network.UserNetworkDataSource
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.user.GetCurrentUserUseCase import ru.myitschool.work.domain.user.GetCurrentUserUseCase
class UserViewModel( class UserViewModel(
private val getCurrentUserUseCase: GetCurrentUserUseCase, private val getCurrentUserUseCase: GetCurrentUserUseCase,
private val logoutUseCase: LogoutUseCase private val logoutUseCase: LogoutUseCase,
private val getCurrentPassesUseCase: GetCurrentPassesUseCase
) : ViewModel() { ) : ViewModel() {
private val _state = MutableStateFlow<State>(State.Loading) private val _state = MutableStateFlow<State>(State.Loading)
val state = _state.asStateFlow() val state = _state.asStateFlow()
val listState = Pager(
config = PagingConfig(
pageSize = 10,
enablePlaceholders = false,
maxSize = 50
)
) {
PassesPagingSource(getCurrentPassesUseCase::invoke)
}.flow
.cachedIn(viewModelScope)
init { init {
updateState() updateState()
} }
@ -57,7 +75,13 @@ class UserViewModel(
) )
return UserViewModel( return UserViewModel(
getCurrentUserUseCase = GetCurrentUserUseCase(repository = repository), getCurrentUserUseCase = GetCurrentUserUseCase(repository = repository),
logoutUseCase = LogoutUseCase(repository = repository) logoutUseCase = LogoutUseCase(repository = repository),
getCurrentPassesUseCase = GetCurrentPassesUseCase(
repository = PassRepositoryImpl(
networkDataSource = PassNetworkDataSource,
credentialsLocalDataSource = CredentialsLocalDataSource.getInstance()
)
)
) as T ) as T
} }
} }