diff --git a/feature/home/src/main/java/com/kolown/home/component/RandomImageList.kt b/feature/home/src/main/java/com/kolown/home/component/RandomImageList.kt index 5fa27267..4ee17ee7 100644 --- a/feature/home/src/main/java/com/kolown/home/component/RandomImageList.kt +++ b/feature/home/src/main/java/com/kolown/home/component/RandomImageList.kt @@ -24,6 +24,7 @@ import androidx.compose.material3.CardDefaults import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Icon import androidx.compose.material3.IconButton +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -242,15 +243,14 @@ private fun RandomImage( } if (isError) { - Column { - Icon( - imageVector = Icons.Default.Warning, - contentDescription = null, - tint = Color.Gray, - modifier = Modifier.size(60.dp) + Box( + modifier = Modifier.fillMaxSize() + ) { + Text( + modifier = Modifier.align(Alignment.Center), + text = "이미지를 로드할 수 없음. 다시 시도해주세요." ) } - } } diff --git a/feature/my/src/main/java/com/kolown/my/MyScreen.kt b/feature/my/src/main/java/com/kolown/my/MyScreen.kt index 145ce637..e189dec8 100644 --- a/feature/my/src/main/java/com/kolown/my/MyScreen.kt +++ b/feature/my/src/main/java/com/kolown/my/MyScreen.kt @@ -1,30 +1,58 @@ package com.kolown.my +import android.content.Context +import android.net.ConnectivityManager +import android.net.NetworkCapabilities +import android.util.Log +import androidx.compose.foundation.Image +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Spacer 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.lazy.staggeredgrid.LazyStaggeredGridState import androidx.compose.foundation.lazy.staggeredgrid.LazyVerticalStaggeredGrid import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridItemSpan import androidx.compose.foundation.lazy.staggeredgrid.rememberLazyStaggeredGridState +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Warning +import androidx.compose.material3.CircularProgressIndicator +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.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.paging.LoadState +import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.collectAsLazyPagingItems +import com.kolown.model.PostContentModel +import com.kolown.model.UiState import com.kolown.my.component.DeleteDialog import com.kolown.my.component.GalleryItem import com.kolown.my.component.MyAppBar import com.kolown.my.component.PageItemFooter import com.kolown.my.component.RestrictedLoginContent +import kotlinx.coroutines.delay @Composable internal fun MyRoute( @@ -33,7 +61,7 @@ internal fun MyRoute( navigateToSetting: () -> Unit, padding: PaddingValues = PaddingValues(), ) { - if(isLoggedIn) { + if (isLoggedIn) { MyScreen( navigateToSetting = navigateToSetting, padding = padding @@ -65,32 +93,107 @@ fun MyScreen( MyAppBar( onSettingClicked = navigateToSetting ) - LazyVerticalStaggeredGrid( - columns = StaggeredGridCells.Fixed(2), - modifier = Modifier - .fillMaxSize(), - state = listState, - contentPadding = PaddingValues(8.dp), - horizontalArrangement = Arrangement.spacedBy(8.dp), - verticalItemSpacing = 8.dp, - content = { - items(pagingItems.itemCount) { index -> - pagingItems[index]?.let { - GalleryItem(it, width) { - viewModel.deletePost(it.postId) + StateLazyGrid( + listState = listState, + pagingItems = pagingItems, + width = width, + deletePost = viewModel::deletePost + ) + } +} + +@Composable +fun StateLazyGrid( + listState: LazyStaggeredGridState, + pagingItems: LazyPagingItems, + width: Dp, + deletePost: (String) -> Unit, +) { + var showErrorScreen by remember { mutableStateOf(false) } + + LaunchedEffect(pagingItems.loadState.refresh) { + if (pagingItems.loadState.refresh == LoadState.Loading) { + delay(5000) + showErrorScreen = true + } else { + showErrorScreen = false + } + } + + + when { + showErrorScreen -> { + ErrorScreen() + } + pagingItems.loadState.refresh is LoadState.Error -> { + Log.d("LazyVertical", "실패") + showErrorScreen = false + ErrorScreen() + } + pagingItems.loadState.refresh is LoadState.Loading -> { + Log.d("LazyVertical", "로딩") + showErrorScreen = false + Box( + modifier = Modifier.fillMaxSize(), + ) { + CircularProgressIndicator( + modifier = Modifier + .size(36.dp) + .align(Alignment.Center), + ) + } + } + pagingItems.loadState.refresh is LoadState.NotLoading -> { + Log.d("LazyVertical", "성공 ${pagingItems.itemCount}") + showErrorScreen = false + LazyVerticalStaggeredGrid( + columns = StaggeredGridCells.Fixed(2), + modifier = Modifier + .fillMaxSize(), + state = listState, + contentPadding = PaddingValues(8.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalItemSpacing = 8.dp, + content = { + items(pagingItems.itemCount) { index -> + pagingItems[index]?.let { + GalleryItem(it, width) { + deletePost(it.postId) + } } } - } - if (pagingItems.loadState.append !is LoadState.NotLoading) { - item(key = "", span = StaggeredGridItemSpan.FullLine) { - PageItemFooter(loadState = pagingItems.loadState.append) { - pagingItems.retry() + if (pagingItems.loadState.append !is LoadState.NotLoading) { + item(key = "", span = StaggeredGridItemSpan.FullLine) { + PageItemFooter(loadState = pagingItems.loadState.append) { + pagingItems.retry() + } } } } - } - ) + ) + } + } +} + +@Composable +private fun ErrorScreen() { + Box( + modifier = Modifier + .fillMaxSize() + ) { + Column( + modifier = Modifier.align(Alignment.Center), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Image( + imageVector = Icons.Default.Warning, contentDescription = null + ) + Spacer(modifier = Modifier.height(16.dp)) + Text( + text = "오류가 발생하였습니다!", color = Color.Red + ) + } } } diff --git a/feature/my/src/main/java/com/kolown/my/component/GalleryItem.kt b/feature/my/src/main/java/com/kolown/my/component/GalleryItem.kt index d5fd673a..e2554985 100644 --- a/feature/my/src/main/java/com/kolown/my/component/GalleryItem.kt +++ b/feature/my/src/main/java/com/kolown/my/component/GalleryItem.kt @@ -2,10 +2,12 @@ package com.kolown.my.component import android.util.Log import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -15,9 +17,12 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Warning import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text @@ -25,8 +30,10 @@ import androidx.compose.material3.TextButton import androidx.compose.material3.TextField import androidx.compose.material3.TextFieldDefaults import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -42,7 +49,9 @@ import androidx.compose.ui.window.DialogProperties import coil3.compose.AsyncImage import com.kolown.data.mock.MockDataProvider import com.kolown.designsystem.Error +import com.kolown.designsystem.Gray import com.kolown.designsystem.Primary +import com.kolown.designsystem.Surface2 import com.kolown.model.PostContentModel import kotlin.random.Random @@ -53,40 +62,74 @@ fun GalleryItem( width: Dp, onLongClickImage: () -> Unit = {} ) { + var isLoading by remember { mutableStateOf(true) } + var isError by remember { mutableStateOf(false) } + //비율은 그냥 테스트 val heightNum = postContentModel.postId.filter { it.isDigit() }.toInt() val height = if (heightNum % 2 == 0) (width.value * 1.4).dp else width + 20.dp val isDialogVisible = remember { mutableStateOf(false) } - AsyncImage( - modifier = Modifier - .fillMaxWidth() + Box( + modifier = Modifier.fillMaxWidth() .height(height) .clip(RoundedCornerShape(10.dp)) - .combinedClickable ( - onClick = { - // todo navigateToDetail - }, - onLongClick = { - isDialogVisible.value = true - Log.d("GalleryItem", "GalleryItem: LongClick") - } - ), - model = postContentModel.imageUrl, - contentDescription = null, - contentScale = ContentScale.Crop - ) - - if(isDialogVisible.value) { - DeleteDialog( - onClickCancel = { isDialogVisible.value = false }, - onClickConfirm = { - onLongClickImage() - isDialogVisible.value = false + .background(Surface2) + ) { + AsyncImage( + modifier = Modifier + .fillMaxSize() + .combinedClickable ( + onClick = { + // todo navigateToDetail + }, + onLongClick = { + isDialogVisible.value = true + Log.d("GalleryItem", "GalleryItem: LongClick") + } + ), + model = postContentModel.imageUrl, + contentDescription = null, + contentScale = ContentScale.Crop, + onLoading = { + isLoading = true + isError = false + }, + onSuccess = { + isLoading = false + isError = false + }, + onError = { + isLoading = false + isError = true } ) - } + if (isLoading) { + CircularProgressIndicator( + color = Color.Gray, + modifier = Modifier.size(48.dp).align(Alignment.Center) + ) + } + + if (isError) { + Text( + modifier = Modifier.align(Alignment.Center), + text = "이미지를 로드할 수 없음.\n 다시 시도해주세요.", + style = MaterialTheme.typography.labelMedium, + ) + } + + if(isDialogVisible.value) { + DeleteDialog( + onClickCancel = { isDialogVisible.value = false }, + onClickConfirm = { + onLongClickImage() + isDialogVisible.value = false + } + ) + } + } } @Preview