Skip to content

Commit

Permalink
Merge pull request #238 from JeonK1/feature/#166
Browse files Browse the repository at this point in the history
[Bug] 네트워크가 연결안된 상태로 컨트리뷰트 화면 진입시 앱이 종료 됨
  • Loading branch information
laco-dev authored Aug 26, 2023
2 parents d5be5e1 + 01465e8 commit 16e86b9
Show file tree
Hide file tree
Showing 17 changed files with 139 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
Expand All @@ -31,11 +32,19 @@ import com.droidknights.app2023.core.designsystem.theme.KnightsTheme
import com.droidknights.app2023.core.designsystem.theme.PaleGray
import com.droidknights.app2023.core.designsystem.theme.Purple01
import com.droidknights.app2023.core.designsystem.theme.surfaceDim
import kotlinx.coroutines.flow.collectLatest

@Composable
internal fun BookmarkRoute(viewModel: BookmarkViewModel = hiltViewModel()) {
internal fun BookmarkRoute(
onShowErrorSnackBar: (throwable: Throwable?) -> Unit,
viewModel: BookmarkViewModel = hiltViewModel()
) {
val bookmarkUiState by viewModel.bookmarkUiState.collectAsStateWithLifecycle()

LaunchedEffect(true) {
viewModel.errorFlow.collectLatest { onShowErrorSnackBar(it) }
}

Box(
modifier = Modifier
.fillMaxSize()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,5 @@ package com.droidknights.app2023.feature.bookmark

sealed interface BookmarkUiState {
object Loading : BookmarkUiState

data class Success(val isEditButtonSelected: Boolean = false, val bookmarks: List<BookmarkItemUiState> = listOf()) : BookmarkUiState
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.droidknights.app2023.core.domain.usecase.GetBookmarkedSessionsUseCase
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
import javax.inject.Inject
Expand All @@ -15,6 +18,9 @@ class BookmarkViewModel @Inject constructor(
private val getBookmarkedSessionsUseCase: GetBookmarkedSessionsUseCase
) : ViewModel() {

private val _errorFlow = MutableSharedFlow<Throwable>()
val errorFlow: SharedFlow<Throwable> get() = _errorFlow

private val _bookmarkUiState = MutableStateFlow<BookmarkUiState>(BookmarkUiState.Loading)
val bookmarkUiState: StateFlow<BookmarkUiState> = _bookmarkUiState

Expand Down Expand Up @@ -51,6 +57,8 @@ class BookmarkViewModel @Inject constructor(
)
}
}
}.catch { throwable ->
_errorFlow.emit(throwable)
}.collect { _bookmarkUiState.value = it }
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ fun NavController.navigateBookmark(navOptions: NavOptions) {
navigate(BookmarkRoute.route, navOptions)
}

fun NavGraphBuilder.bookmarkNavGraph() {
fun NavGraphBuilder.bookmarkNavGraph(
onShowErrorSnackBar: (throwable: Throwable?) -> Unit
) {
composable(route = BookmarkRoute.route) {
BookmarkRoute()
BookmarkRoute(onShowErrorSnackBar)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
Expand Down Expand Up @@ -48,15 +49,21 @@ import com.droidknights.app2023.core.designsystem.theme.surfaceDim
import com.droidknights.app2023.core.model.Contributor
import com.valentinilk.shimmer.shimmer
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.flow.collectLatest

@Composable
fun ContributorRoute(
onBackClick: () -> Unit,
onShowErrorSnackBar: (throwable: Throwable?) -> Unit,
modifier: Modifier = Modifier,
viewModel: ContributorViewModel = hiltViewModel(),
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()

LaunchedEffect(true) {
viewModel.errorFlow.collectLatest { throwable -> onShowErrorSnackBar(throwable) }
}

ContributorScreen(
uiState = uiState,
onBackClick = onBackClick,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ import androidx.lifecycle.viewModelScope
import com.droidknights.app2023.core.domain.usecase.GetContributorsUseCase
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.collections.immutable.toPersistentList
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
Expand All @@ -17,12 +20,16 @@ class ContributorViewModel @Inject constructor(
getContributorsUseCase: GetContributorsUseCase,
) : ViewModel() {

private val _errorFlow = MutableSharedFlow<Throwable>()
val errorFlow: SharedFlow<Throwable> get() = _errorFlow

val uiState: StateFlow<ContributorsUiState> =
flow { emit(getContributorsUseCase().toPersistentList()) }.map(
ContributorsUiState::Contributors,
).stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = ContributorsUiState.Loading,
)
flow { emit(getContributorsUseCase().toPersistentList()) }
.map(ContributorsUiState::Contributors)
.catch { throwable -> _errorFlow.emit(throwable) }
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = ContributorsUiState.Loading,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,15 @@ fun NavController.navigateContributor() {
this.navigate(ContributorRoute.route)
}

fun NavGraphBuilder.contributorNavGraph(onBackClick: () -> Unit) {
fun NavGraphBuilder.contributorNavGraph(
onBackClick: () -> Unit,
onShowErrorSnackBar: (throwable: Throwable?) -> Unit
) {
composable(route = ContributorRoute.route) {
ContributorRoute(onBackClick)
ContributorRoute(
onBackClick = onBackClick,
onShowErrorSnackBar = onShowErrorSnackBar
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,28 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import kotlinx.coroutines.flow.collectLatest

@Composable
internal fun HomeRoute(
padding: PaddingValues,
onSessionClick: () -> Unit,
onContributorClick: () -> Unit,
viewModel: HomeViewModel = hiltViewModel(),
onShowErrorSnackBar: (throwable: Throwable?) -> Unit,
viewModel: HomeViewModel = hiltViewModel()
) {
val sponsorsUiState by viewModel.sponsorsUiState.collectAsStateWithLifecycle()

LaunchedEffect(true) {
viewModel.errorFlow.collectLatest { throwable -> onShowErrorSnackBar(throwable) }
}

HomeScreen(
padding = padding,
sponsorsUiState = sponsorsUiState,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.droidknights.app2023.core.domain.usecase.GetSponsorsUseCase
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
Expand All @@ -16,6 +19,9 @@ class HomeViewModel @Inject constructor(
getSponsorsUseCase: GetSponsorsUseCase,
) : ViewModel() {

private val _errorFlow = MutableSharedFlow<Throwable>()
val errorFlow: SharedFlow<Throwable> get() = _errorFlow

val sponsorsUiState: StateFlow<SponsorsUiState> = flow { emit(getSponsorsUseCase()) }
.map { sponsors ->
if (sponsors.isNotEmpty()) {
Expand All @@ -24,6 +30,9 @@ class HomeViewModel @Inject constructor(
SponsorsUiState.Empty
}
}
.catch { throwable ->
_errorFlow.emit(throwable)
}
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ fun NavGraphBuilder.homeNavGraph(
padding: PaddingValues,
onSessionClick: () -> Unit,
onContributorClick: () -> Unit,
onShowErrorSnackBar: (throwable: Throwable?) -> Unit
) {
composable(route = HomeRoute.route) {
HomeRoute(padding, onSessionClick, onContributorClick)
HomeRoute(padding, onSessionClick, onContributorClick, onShowErrorSnackBar)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,14 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
Expand All @@ -41,12 +45,29 @@ import com.droidknights.app2023.feature.session.navigation.sessionNavGraph
import com.droidknights.app2023.feature.setting.navigation.settingNavGraph
import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.toPersistentList
import kotlinx.coroutines.launch
import java.net.UnknownHostException

@Composable
internal fun MainScreen(
navigator: MainNavigator = rememberMainNavigator(),
onChangeDarkTheme: (Boolean) -> Unit
) {
val snackBarHostState = remember { SnackbarHostState() }

val coroutineScope = rememberCoroutineScope()
val localContextResource = LocalContext.current.resources
val onShowErrorSnackBar: (throwable: Throwable?) -> Unit = { throwable ->
coroutineScope.launch {
snackBarHostState.showSnackbar(
when (throwable) {
is UnknownHostException -> localContextResource.getString(R.string.error_message_network)
else -> localContextResource.getString(R.string.error_message_unknown)
}
)
}
}

Scaffold(
content = { padding ->
Box(
Expand All @@ -62,21 +83,26 @@ internal fun MainScreen(
padding = padding,
onSessionClick = { navigator.navigateSession() },
onContributorClick = { navigator.navigateContributor() },
onShowErrorSnackBar = onShowErrorSnackBar
)
settingNavGraph(
padding = padding,
onChangeDarkTheme = onChangeDarkTheme
)

bookmarkNavGraph()
bookmarkNavGraph(
onShowErrorSnackBar = onShowErrorSnackBar
)

contributorNavGraph(
onBackClick = { navigator.popBackStack() }
onBackClick = { navigator.popBackStack() },
onShowErrorSnackBar = onShowErrorSnackBar
)

sessionNavGraph(
onBackClick = { navigator.popBackStack() },
onSessionClick = { navigator.navigateSessionDetail(it.id) }
onSessionClick = { navigator.navigateSessionDetail(it.id) },
onShowErrorSnackBar = onShowErrorSnackBar
)
}
}
Expand All @@ -89,6 +115,7 @@ internal fun MainScreen(
onTabSelected = { navigator.navigate(it) }
)
},
snackbarHost = { SnackbarHost(snackBarHostState) }
)
}

Expand Down
5 changes: 5 additions & 0 deletions feature/main/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="error_message_network">네트워크 연결이 원활하지 않습니다</string>
<string name="error_message_unknown">알 수 없는 오류가 발생하였습니다</string>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.Divider
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Dp
Expand All @@ -26,15 +27,24 @@ import com.droidknights.app2023.core.model.Room
import com.droidknights.app2023.core.model.Session
import com.droidknights.app2023.core.ui.RoomText
import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.flow.collectLatest

@Composable
internal fun SessionScreen(
onBackClick: () -> Unit,
onSessionClick: (Session) -> Unit,
onShowErrorSnackBar: (throwable: Throwable?) -> Unit,
sessionViewModel: SessionViewModel = hiltViewModel(),
) {
val sessionUiState by sessionViewModel.uiState.collectAsStateWithLifecycle()
val sessionState = rememberSessionState(sessions = sessionUiState.sessions)
val sessionState = (sessionUiState as? SessionUiState.Sessions)?.sessions?.let { sessions ->
rememberSessionState(sessions = sessions) // SessionUiState.Sessions
} ?: rememberSessionState(sessions = persistentListOf()) // SessionUiState.Loading, SessionUiState.Error

LaunchedEffect(true) {
sessionViewModel.errorFlow.collectLatest { throwable -> onShowErrorSnackBar(throwable) }
}

Box(modifier = Modifier.fillMaxSize()) {
SessionTopAppBar(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import com.droidknights.app2023.core.model.Session
import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.persistentListOf

data class SessionUiState(
val sessions: PersistentList<Session> = persistentListOf(),
)
sealed interface SessionUiState {
object Loading : SessionUiState
data class Sessions(
val sessions: PersistentList<Session> = persistentListOf(),
) : SessionUiState
}
Loading

0 comments on commit 16e86b9

Please sign in to comment.