diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index cde3e19..7061a0d 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -49,6 +49,10 @@
+
+
+
+
diff --git a/core/src/main/java/com/codereview/core/theme/Color.kt b/core/src/main/java/com/codereview/core/theme/Color.kt
index 6e470a9..9fe005a 100644
--- a/core/src/main/java/com/codereview/core/theme/Color.kt
+++ b/core/src/main/java/com/codereview/core/theme/Color.kt
@@ -13,4 +13,7 @@ val Pink40 = Color(0xFF7D5260)
val WhiteSmoke = Color(0xF5F5F5F5)
val Whisper = Color(0xECECECEC)
+val LightGray = Color(0xFFE0E0E0)
+val OffWhite = Color(0xFFF5F5F5)
+
diff --git a/core/src/main/java/com/codereview/core/theme/ui/ModifierShimmerEffect.kt b/core/src/main/java/com/codereview/core/theme/ui/ModifierShimmerEffect.kt
new file mode 100644
index 0000000..fa528ce
--- /dev/null
+++ b/core/src/main/java/com/codereview/core/theme/ui/ModifierShimmerEffect.kt
@@ -0,0 +1,47 @@
+package com.codereview.core.theme.ui
+
+import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.RepeatMode
+import androidx.compose.animation.core.animateFloat
+import androidx.compose.animation.core.infiniteRepeatable
+import androidx.compose.animation.core.rememberInfiniteTransition
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.background
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.composed
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Brush
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.onGloballyPositioned
+import androidx.compose.ui.unit.IntSize
+
+fun Modifier.shimmerEffect(): Modifier = composed {
+ var size by remember { mutableStateOf(IntSize.Zero) }
+
+ val transition = rememberInfiniteTransition()
+ val startOffsetX by transition.animateFloat(
+ initialValue = -size.width.toFloat(),
+ targetValue = size.width.toFloat(),
+ animationSpec = infiniteRepeatable(
+ animation = tween(durationMillis = 1300, easing = LinearEasing),
+ repeatMode = RepeatMode.Restart
+ )
+ )
+ background(
+ brush = Brush.linearGradient(
+ colors = listOf(
+ Color(0xFFE0E0E0),
+ Color(0xFFF5F5F5),
+ Color(0xFFE0E0E0)
+ ),
+ start = Offset(startOffsetX, 0f),
+ end = Offset(startOffsetX + size.width.toFloat(), size.height.toFloat())
+ )
+ ).onGloballyPositioned {
+ size = it.size
+ }
+}
\ No newline at end of file
diff --git a/feature_jobs/src/main/java/com/codereview/feature_jobs/Homepage.kt b/feature_jobs/src/main/java/com/codereview/feature_jobs/Homepage.kt
index b68c576..3be57cb 100644
--- a/feature_jobs/src/main/java/com/codereview/feature_jobs/Homepage.kt
+++ b/feature_jobs/src/main/java/com/codereview/feature_jobs/Homepage.kt
@@ -1,5 +1,6 @@
package com.codereview.feature_jobs
+import android.util.Log
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
@@ -12,6 +13,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.lazy.grid.GridCells
@@ -20,16 +22,15 @@ import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Refresh
-import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
@@ -41,6 +42,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.window.core.layout.WindowSizeClass
import androidx.window.core.layout.WindowWidthSizeClass
+import com.codereview.core.theme.ui.shimmerEffect
import com.codereview.feature_jobs.state.HomeState
import com.codereview.repository.jobs_repository.JobSpec
import com.codereview.core.R as coreR
@@ -52,21 +54,26 @@ fun HomeScreen(
onNavigateToVacancies: (String) -> Unit
) {
val state = viewModel.state.collectAsStateWithLifecycle()
-
+ Log.d("HomeScreenS", "HomeScreen create")
when (val currentState = state.value) {
- is HomeState.Loading -> HomeLoading()
+ is HomeState.Loading -> {
+ Log.d("HomeScreenS", "State: Loading")
+ HomeLoading()
+ }
+
is HomeState.Ready -> {
+ Log.d("HomeScreenS", "State: Ready")
HomePage(
currentState.data,
onNavigateToVacancies
)
}
+
is HomeState.Error -> HomeError(
errorMessage = currentState.message,
onRefreshJobs = { }
)
}
-
}
@Composable
@@ -209,15 +216,32 @@ fun JobCard(
}
@Composable
-fun HomeLoading(
- modifier: Modifier = Modifier
-) {
- Box(
- modifier = modifier
+fun HomeLoading() {
+ Column(
+ modifier = Modifier
+ .padding(15.dp)
.fillMaxSize(),
- contentAlignment = Alignment.Center
+ verticalArrangement = Arrangement.spacedBy(15.dp),
+ horizontalAlignment = Alignment.CenterHorizontally
) {
- CircularProgressIndicator()
+ repeat(4) {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(start = 16.dp, end = 16.dp)
+ .height(24.dp)
+ .shimmerEffect()
+ )
+ }
+ Spacer(modifier = Modifier.height(15.dp))
+ repeat(5) {
+ Box(
+ modifier = Modifier
+ .size(width = 190.dp, height = 120.dp)
+ .clip(shape = RoundedCornerShape(16.dp))
+ .shimmerEffect()
+ )
+ }
}
}
diff --git a/feature_jobs/src/main/java/com/codereview/feature_jobs/HomepageViewModel.kt b/feature_jobs/src/main/java/com/codereview/feature_jobs/HomepageViewModel.kt
index a3eebec..0bb22b7 100644
--- a/feature_jobs/src/main/java/com/codereview/feature_jobs/HomepageViewModel.kt
+++ b/feature_jobs/src/main/java/com/codereview/feature_jobs/HomepageViewModel.kt
@@ -7,6 +7,7 @@ import com.codereview.feature_jobs.state.HomeUiState
import com.codereview.repository.jobs_repository.JobRepository
import com.codereview.repository.jobs_repository.JobSpec
import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
@@ -29,6 +30,7 @@ class HomepageViewModel @Inject constructor(
init {
viewModelScope.launch {
_state.value = HomeState.Loading
+ delay(2000)
repository.getJobList()
.catch {
_state.emit(HomeState.Error(it.message))
diff --git a/feature_vacansies/src/main/java/com/codereview/feature_vacansies/VacanciesViewModel.kt b/feature_vacansies/src/main/java/com/codereview/feature_vacansies/VacanciesViewModel.kt
index 537c4e8..686aacc 100644
--- a/feature_vacansies/src/main/java/com/codereview/feature_vacansies/VacanciesViewModel.kt
+++ b/feature_vacansies/src/main/java/com/codereview/feature_vacansies/VacanciesViewModel.kt
@@ -12,6 +12,7 @@ import com.codereview.repository.vacancy_repository.Vacancy
import com.codereview.repository.vacancy_repository.VacancyRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
+import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -37,6 +38,7 @@ class VacanciesViewModel @Inject constructor(
) {
viewModelScope.launch {
_state.value = VacanciesState.Loading
+ delay(2000L)
Log.d("VacanciesViewModel", "getVacancies: ${uiState.value.isLoading}")
repo.getVacancyList(
specialities = specialities
diff --git a/feature_vacansies/src/main/java/com/codereview/feature_vacansies/VacancyList.kt b/feature_vacansies/src/main/java/com/codereview/feature_vacansies/VacancyList.kt
index 69d9062..6a2e814 100644
--- a/feature_vacansies/src/main/java/com/codereview/feature_vacansies/VacancyList.kt
+++ b/feature_vacansies/src/main/java/com/codereview/feature_vacansies/VacancyList.kt
@@ -19,7 +19,6 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Refresh
-import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@@ -37,6 +36,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.codereview.core.theme.PurpleGrey80
import com.codereview.core.theme.Whisper
+import com.codereview.core.theme.ui.shimmerEffect
import com.codereview.feature_vacansies.state.VacanciesState
import com.codereview.repository.vacancy_repository.Vacancy
import com.codereview.core.R as coreR
@@ -145,17 +145,25 @@ fun VacancyItem(
}
@Composable
-fun VacanciesLoading(
- modifier: Modifier = Modifier
-) {
- Box(
- modifier = modifier
- .fillMaxSize()
- .background(color = PurpleGrey80)
+fun VacanciesLoading() {
+ Column(
+ modifier = Modifier
+ .padding(15.dp)
+ .fillMaxSize(),
+ verticalArrangement = Arrangement.spacedBy(15.dp),
+ horizontalAlignment = Alignment.CenterHorizontally
) {
- CircularProgressIndicator(
- modifier = Modifier.align(Alignment.Center)
- )
+ repeat(3) {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(300.dp)
+ .padding(start = 16.dp, end = 16.dp)
+ .clip(shape = RoundedCornerShape(32.dp))
+ .shimmerEffect()
+ )
+ Spacer(modifier = Modifier.height(8.dp))
+ }
}
}