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