Impl base schedule navigation; add internal logger
This commit is contained in:
@@ -8,7 +8,6 @@ import ru.fincode.tsudesk.core.network.model.NetworkResult
|
||||
import ru.fincode.tsudesk.core.network.model.map
|
||||
import ru.fincode.tsudesk.feature.schedule.data.datasource.ScheduleLocalDataSource
|
||||
import ru.fincode.tsudesk.feature.schedule.data.datasource.ScheduleRemoteDataSource
|
||||
import ru.fincode.tsudesk.feature.schedule.data.local.ScheduleCacheKey
|
||||
import ru.fincode.tsudesk.feature.schedule.data.remote.ScheduleNetworkMapper
|
||||
import ru.fincode.tsudesk.feature.schedule.domain.model.ScheduleEntity
|
||||
import ru.fincode.tsudesk.feature.schedule.domain.model.ScheduleType
|
||||
@@ -23,9 +22,11 @@ class ScheduleRepositoryImpl @Inject constructor(
|
||||
|
||||
override fun observeSchedule(type: ScheduleType): Flow<DataResult<ScheduleEntity>> = flow {
|
||||
val scheduleKey = type.toCacheKey()
|
||||
|
||||
val cached = local.observeSchedule(scheduleKey).first()
|
||||
cached?.let {
|
||||
emit(DataResult.Data(it, refreshedFromNetwork = false))
|
||||
if (cached != null) {
|
||||
emit(DataResult.Data(cached, refreshedFromNetwork = false))
|
||||
if ((System.currentTimeMillis() - cached.timestamp) < CACHE_TTL_MS) return@flow
|
||||
}
|
||||
|
||||
val networkResult = loadSchedule(type)
|
||||
@@ -45,11 +46,12 @@ class ScheduleRepositoryImpl @Inject constructor(
|
||||
|
||||
private suspend fun loadSchedule(type: ScheduleType): NetworkResult<ScheduleEntity> =
|
||||
when (type) {
|
||||
is ScheduleType.Group ->
|
||||
remote.loadScheduleByGroup(type.number).map(mapper::invoke)
|
||||
is ScheduleType.Group -> remote.loadScheduleByGroup(type.number).map(mapper::invoke)
|
||||
|
||||
is ScheduleType.Teacher ->
|
||||
remote.loadScheduleByTeacher(type.name).map(mapper::invoke)
|
||||
is ScheduleType.Teacher -> remote.loadScheduleByTeacher(type.name).map(mapper::invoke)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private const val CACHE_TTL_MS: Long = 1;//24L * 60L * 60L * 1000L // 1 day
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import ru.fincode.tsudesk.feature.schedule.domain.model.ScheduleType
|
||||
import ru.fincode.tsudesk.feature.schedule.domain.repository.ScheduleRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
class GetScheduleUseCase @Inject constructor(
|
||||
class ObserveScheduleUseCase @Inject constructor(
|
||||
private val repository: ScheduleRepository
|
||||
) {
|
||||
operator fun invoke(type: ScheduleType): Flow<DataResult<ScheduleEntity>> =
|
||||
@@ -0,0 +1,11 @@
|
||||
package ru.fincode.tsudesk.feature.schedule.presentation.model
|
||||
|
||||
import ru.fincode.tsudesk.feature.schedule.domain.model.ScheduleEntity
|
||||
|
||||
data class ScheduleUiState(
|
||||
val isLoading: Boolean = false,
|
||||
val data: ScheduleEntity? = null,
|
||||
val errorMessage: String? = null,
|
||||
val title: String = "Schedule",
|
||||
val refreshedFromNetwork: Boolean = false
|
||||
)
|
||||
@@ -0,0 +1,20 @@
|
||||
package ru.fincode.tsudesk.feature.schedule.presentation.navigation
|
||||
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.composable
|
||||
import ru.fincode.tsudesk.core.navigation.AppRoute
|
||||
import ru.fincode.tsudesk.feature.schedule.presentation.screen.ScheduleRoute
|
||||
|
||||
fun NavGraphBuilder.scheduleGraph(
|
||||
navController: NavHostController
|
||||
) {
|
||||
composable<AppRoute.Schedule> {
|
||||
ScheduleRoute()
|
||||
}
|
||||
|
||||
// composable<AppRoute.ScheduleDetails> { backStackEntry ->
|
||||
// val args = backStackEntry.toRoute<AppRoute.ScheduleDetails>()
|
||||
// ScheduleDetailsRoute(lessonId = args.lessonId)
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package ru.fincode.tsudesk.feature.schedule.presentation.screen
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import ru.fincode.tsudesk.feature.schedule.presentation.screen.ScheduleViewModel
|
||||
import ru.fincode.tsudesk.feature.schedule.presentation.ui.ScheduleScreen
|
||||
|
||||
|
||||
@Composable
|
||||
fun ScheduleRoute(
|
||||
viewModel: ScheduleViewModel = hiltViewModel()
|
||||
) {
|
||||
val state = viewModel.state.collectAsStateWithLifecycle().value
|
||||
ScheduleScreen(state = state)
|
||||
}
|
||||
@@ -2,21 +2,26 @@ package ru.fincode.tsudesk.feature.schedule.presentation.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import ru.fincode.tsudesk.feature.schedule.presentation.model.ScheduleUiState
|
||||
|
||||
@Composable
|
||||
fun ScheduleScreen() {
|
||||
fun ScheduleScreen(
|
||||
state: ScheduleUiState
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = "Schedule",
|
||||
color = Color.Black
|
||||
)
|
||||
if (state.isLoading) {
|
||||
CircularProgressIndicator()
|
||||
} else {
|
||||
Text(text = state.title, color = Color.Black)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package ru.fincode.tsudesk.feature.schedule.presentation.screen
|
||||
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import ru.fincode.tsudesk.core.common.log.logD
|
||||
import ru.fincode.tsudesk.core.common.model.DataResult
|
||||
import ru.fincode.tsudesk.feature.schedule.domain.model.ScheduleType
|
||||
import ru.fincode.tsudesk.feature.schedule.domain.usecase.ObserveScheduleUseCase
|
||||
import ru.fincode.tsudesk.feature.schedule.presentation.model.ScheduleUiState
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class ScheduleViewModel @Inject constructor(
|
||||
private val observeScheduleUseCase: ObserveScheduleUseCase
|
||||
) : ViewModel() {
|
||||
|
||||
private val scheduleType = ScheduleType.Group(number = "220631")
|
||||
|
||||
val state: StateFlow<ScheduleUiState> = observeScheduleUseCase(scheduleType).map { result ->
|
||||
when (result) {
|
||||
is DataResult.Data -> {
|
||||
logD("Data loaded from ${if (result.refreshedFromNetwork) "NETWORK" else "CACHE"}")
|
||||
ScheduleUiState(
|
||||
isLoading = false,
|
||||
data = result.data,
|
||||
refreshedFromNetwork = result.refreshedFromNetwork
|
||||
)
|
||||
}
|
||||
|
||||
is DataResult.Error -> {
|
||||
logD("Error loading schedule: ${result.error}")
|
||||
ScheduleUiState(isLoading = false, errorMessage = result.error.toString())
|
||||
}
|
||||
}
|
||||
}.onStart {
|
||||
logD("Start collecting schedule flow")
|
||||
emit(ScheduleUiState(isLoading = true))
|
||||
}.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.WhileSubscribed(5000),
|
||||
initialValue = ScheduleUiState(isLoading = true)
|
||||
)
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package ru.fincode.tsudesk.feature.schedule.ui.navigation
|
||||
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.composable
|
||||
import ru.fincode.tsudesk.core.navigation.AppRoute
|
||||
import ru.fincode.tsudesk.feature.schedule.presentation.ui.ScheduleScreen
|
||||
|
||||
fun NavGraphBuilder.scheduleGraph(
|
||||
navController: NavHostController
|
||||
) {
|
||||
composable<AppRoute.Schedule> {
|
||||
ScheduleScreen()
|
||||
}
|
||||
|
||||
// регистрация экрана детальной информации о занятии
|
||||
// composable<AppRoute.ScheduleDetails> { backStackEntry ->
|
||||
// // val args = backStackEntry.toRoute<AppRoute.ScheduleDetails>()
|
||||
// // ScheduleDetailsRoute(lessonId = args.lessonId)
|
||||
// }
|
||||
}
|
||||
Reference in New Issue
Block a user