Skip to content

Commit

Permalink
feat: refreshing 기능 추가
Browse files Browse the repository at this point in the history
  • Loading branch information
HyomK committed Feb 19, 2024
1 parent 0cea0e9 commit d7fba30
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 21 deletions.
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ dependencies {
implementation("com.google.accompanist:accompanist-permissions:${Versions.permission}")
implementation("androidx.core:core-splashscreen:${Versions.splash}")
implementation("com.airbnb.android:lottie-compose:${Versions.lottie}")
implementation("androidx.compose.material:material:${Versions.refresh}")
}

fun getProperty(propertyKey: String): String {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,19 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.GridItemSpan
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Divider
import androidx.compose.material3.MaterialTheme
Expand All @@ -34,19 +40,27 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.BiasAlignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.lerp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import nexters.hyomk.domain.utils.calculateDDay
import nexters.hyomk.domain.utils.toFormatString
import nexters.hyomk.dontforget.R
Expand All @@ -57,6 +71,7 @@ import nexters.hyomk.dontforget.presentation.component.card.AnniversaryCard
import nexters.hyomk.dontforget.presentation.component.card.getCardProperties
import nexters.hyomk.dontforget.presentation.compositionlocal.GuideCompositionLocal
import nexters.hyomk.dontforget.presentation.feature.detail.CustomSnackBar
import nexters.hyomk.dontforget.presentation.feature.splash.OnLifecycleEvent
import nexters.hyomk.dontforget.presentation.utils.conditional
import nexters.hyomk.dontforget.presentation.utils.noRippleClickable
import nexters.hyomk.dontforget.presentation.utils.pixelsToDp
Expand All @@ -83,6 +98,7 @@ fun HomeScreen(
)

val context = LocalContext.current
val coroutine = rememberCoroutineScope()

val displayMetrics = context.resources.displayMetrics
val maxHeightPx = displayMetrics.heightPixels
Expand All @@ -92,6 +108,18 @@ fun HomeScreen(
homeViewModel.getAnniversaryList()
}

OnLifecycleEvent { owner, event ->
when (event) {
Lifecycle.Event.ON_RESUME -> {
coroutine.launch {
homeViewModel.getAnniversaryList()
}
}

else -> {}
}
}

when (uiState) {
is HomeUiState.Success -> {
Scaffold(
Expand All @@ -116,7 +144,8 @@ fun HomeScreen(
Surface(
shape = RoundedCornerShape(bottomStart = 32.dp, bottomEnd = 32.dp),
modifier = Modifier
.background(Gray900).padding(bottom = 40.dp)
.background(Gray900)
.padding(bottom = 40.dp)
.noRippleClickable {
navHostController.navigate(NavigationItem.Detail.route + "/${anniversarys.first().eventId}")
},
Expand Down Expand Up @@ -145,7 +174,9 @@ fun HomeScreen(
.padding(horizontal = 36.dp),
) {
Column(
modifier = Modifier.fillMaxHeight().padding(top = 100.dp),
modifier = Modifier
.fillMaxHeight()
.padding(top = 100.dp),
) {
Text(
text = main.solarDate.toFormatString(),
Expand Down Expand Up @@ -197,7 +228,8 @@ fun HomeScreen(
index % 2 == 1,
) {
padding(end = 16.dp, start = 8.dp)
}.conditional(index % 2 == 0) {
}
.conditional(index % 2 == 0) {
padding(start = 16.dp, end = 8.dp)
},
) {
Expand Down Expand Up @@ -225,7 +257,9 @@ fun HomeScreen(
}
items(count = 1, span = { GridItemSpan(2) }) {
Box(
Modifier.height(24.dp).fillMaxWidth(),
Modifier
.height(24.dp)
.fillMaxWidth(),
)
}
},
Expand All @@ -247,16 +281,35 @@ fun HomeScreen(
}

is HomeUiState.Fail -> {
FailContent()
FailContent(refresh = homeViewModel::getAnniversaryList)
}

else -> {}
}
}

@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
@OptIn(ExperimentalLayoutApi::class)
@OptIn(ExperimentalLayoutApi::class, ExperimentalMaterialApi::class)
@Composable
fun FailContent() {
fun FailContent(refresh: suspend () -> Unit) {
val snackState = remember { SnackbarHostState() }
val refreshScope = rememberCoroutineScope()
var refreshing by remember { mutableStateOf(false) }

val scrollState = rememberScrollState()

val state = rememberPullRefreshState(
refreshing = refreshing,
onRefresh = {
refreshing = true
refreshScope.launch {
refresh()
delay(2000)
refreshing = false
}
},
)

LaunchedEffect(Unit) {
snackState.showSnackbar("네트워크 연결이 불안정합니다")
}
Expand All @@ -282,6 +335,35 @@ fun FailContent() {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.BottomCenter) {
CustomSnackBar(snackState = snackState, modifier = Modifier.align(Alignment.BottomCenter))
}
Column(
modifier = Modifier
.fillMaxSize()
.pullRefresh(state) // state 적용
.verticalScroll(scrollState),
) {
Box(
modifier = Modifier
.fillMaxWidth()
.background(Color.Transparent)
.height(
if (refreshing) { // 새로고침 중이면 높이 고정
140.dp
} else { // 당기기 정도에 따라 0~140dp까지 크기가 늘어남
lerp(0.dp, 140.dp, state.progress.coerceIn(0f..1f))
},
),
) {
if (refreshing) {
CircularProgressIndicator(
modifier = Modifier
.size(30.dp)
.align(Alignment.Center),
color = Color.White,
strokeWidth = 1.dp,
)
}
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ class HomeViewModel @Inject constructor(
} else {
getDetailAnniversaryUseCase(list.first().eventId).catch {
Timber.e("api result fail detail $it")

_uiState.emit(HomeUiState.Fail)
}.collectLatest {
_uiState.emit(HomeUiState.Success(list, it))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.NavHostController
import com.airbnb.lottie.compose.LottieAnimation
import com.airbnb.lottie.compose.LottieCompositionSpec
Expand All @@ -53,6 +54,7 @@ import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.isGranted
import com.google.accompanist.permissions.rememberPermissionState
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import nexters.hyomk.dontforget.R
import nexters.hyomk.dontforget.navigation.NavigationItem
Expand Down Expand Up @@ -89,28 +91,33 @@ fun SplashScreen(
)
val lottieAnimatable = rememberLottieAnimatable()

val lifecycle = LocalLifecycleOwner.current

LaunchedEffect(Unit) {
lottieAnimatable.animate(
composition,
)
}

OnLifecycleEvent { owner, event ->
when (event) {
Lifecycle.Event.ON_RESUME -> {
if (permissionRequestState.requestPermission) {
if (deviceId.isNotBlank()) {
navHostController.navigate(
NavigationItem.Home.route,
) {
launchSingleTop = true
popUpTo(NavigationItem.Splash.route) { inclusive = true }
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
splashViewModel.events.collectLatest {
when (it) {
SplashViewModel.SplashEvent.Success -> {
if (permissionRequestState.requestPermission) {
if (deviceId.isNotBlank()) {
navHostController.navigate(
NavigationItem.Home.route,
) {
launchSingleTop = true
popUpTo(NavigationItem.Splash.route) { inclusive = true }
}
}
}
}
SplashViewModel.SplashEvent.Fail->{

}
}
}

else -> {}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collectLatest
Expand All @@ -27,6 +28,9 @@ class SplashViewModel @Inject constructor(
private val _deviceId = MutableStateFlow("")
val deviceId get() = _deviceId

private val _events = MutableSharedFlow<SplashEvent>()
val events get() = _events

init {
viewModelScope.launch {
getDeviceInfoUseCase().collectLatest { deviceId ->
Expand All @@ -48,9 +52,16 @@ class SplashViewModel @Inject constructor(
FcmInfo(token = fcmToken, deviceId = deviceId.value, status = AlarmStatus.ON),
).catch {
Timber.e(it)
_events.emit(SplashEvent.Fail)
}.collectLatest {
Timber.d(it.toString())
_events.emit(SplashEvent.Success)
}
}
}

sealed class SplashEvent {
object Success : SplashEvent()
object Fail : SplashEvent()
}
}
1 change: 1 addition & 0 deletions buildSrc/src/main/kotlin/Versions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,5 @@ object Versions {
const val fcm_direct = "20.2.0"
const val splash = "1.0.0-beta02"
const val lottie = "6.3.0"
const val refresh = "1.3.1"
}

0 comments on commit d7fba30

Please sign in to comment.