Skip to content

Commit 00a4c89

Browse files
committed
[#102] Integrate implementation for no connection dialog
1 parent 2dd9657 commit 00a4c89

File tree

8 files changed

+92
-38
lines changed

8 files changed

+92
-38
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package co.nimblehq.compose.crypto.extension
2+
3+
import android.annotation.SuppressLint
4+
import androidx.compose.runtime.Composable
5+
import androidx.compose.runtime.LaunchedEffect
6+
import kotlinx.coroutines.flow.*
7+
import kotlin.coroutines.CoroutineContext
8+
import kotlin.coroutines.EmptyCoroutineContext
9+
10+
@SuppressLint("ComposableNaming")
11+
@Composable
12+
fun <T> Flow<T>.collectAsEffect(
13+
context: CoroutineContext = EmptyCoroutineContext,
14+
block: suspend (T) -> Unit,
15+
) {
16+
LaunchedEffect(key1 = Unit) {
17+
onEach(block).flowOn(context).launchIn(this)
18+
}
19+
}

app/src/main/java/co/nimblehq/compose/crypto/ui/navigation/AppDestination.kt

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ sealed class AppDestination(val route: String = "") {
1616

1717
object Home : AppDestination("home")
1818

19+
object NoNetwork : AppDestination("no_network")
20+
1921
/**
2022
* We can define route as "coin/details" without "coinId" parameter because we're passing it as argument already.
2123
* So either passing "coinId" via arguments or passing it via route.

app/src/main/java/co/nimblehq/compose/crypto/ui/navigation/AppNavigation.kt

+29
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,35 @@
11
package co.nimblehq.compose.crypto.ui.navigation
22

33
import androidx.compose.runtime.Composable
4+
import androidx.hilt.navigation.compose.hiltViewModel
45
import androidx.navigation.*
56
import androidx.navigation.compose.*
7+
import co.nimblehq.compose.crypto.R
8+
import co.nimblehq.compose.crypto.extension.collectAsEffect
9+
import co.nimblehq.compose.crypto.ui.common.AppDialogPopUp
10+
import co.nimblehq.compose.crypto.ui.screens.MainViewModel
611
import co.nimblehq.compose.crypto.ui.screens.detail.DetailScreen
712
import co.nimblehq.compose.crypto.ui.screens.home.HomeScreen
813

914
@Composable
1015
fun AppNavigation(
1116
navController: NavHostController = rememberNavController(),
17+
mainViewModel: MainViewModel = hiltViewModel(),
1218
startDestination: String = AppDestination.Home.destination
1319
) {
20+
21+
mainViewModel.isNetworkConnected.collectAsEffect { isNetworkConnected ->
22+
if (isNetworkConnected == false) {
23+
val destination = AppDestination.NoNetwork
24+
25+
val currentRoute = navController.currentBackStackEntry?.destination?.route
26+
if (currentRoute == AppDestination.NoNetwork.route) {
27+
navController.popBackStack()
28+
}
29+
30+
navController.navigate(destination)
31+
}
32+
}
1433
NavHost(
1534
navController = navController,
1635
startDestination = startDestination
@@ -27,6 +46,16 @@ fun AppNavigation(
2746
coinId = it.arguments?.getString(KEY_COIN_ID).orEmpty()
2847
)
2948
}
49+
50+
dialog(AppDestination.NoNetwork.route) {
51+
AppDialogPopUp(
52+
onDismiss = { navController.popBackStack() },
53+
onClick = { navController.popBackStack() },
54+
message = R.string.no_internet_message,
55+
actionText = android.R.string.ok,
56+
title = R.string.no_internet_title
57+
)
58+
}
3059
}
3160
}
3261

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package co.nimblehq.compose.crypto.ui.screens
2+
3+
import androidx.lifecycle.viewModelScope
4+
import co.nimblehq.compose.crypto.domain.usecase.GetConnectionStatusUseCase
5+
import co.nimblehq.compose.crypto.ui.base.*
6+
import co.nimblehq.compose.crypto.util.DispatchersProvider
7+
import dagger.hilt.android.lifecycle.HiltViewModel
8+
import kotlinx.coroutines.flow.*
9+
import javax.inject.Inject
10+
11+
interface Input : BaseInput
12+
13+
interface Output : BaseOutput {
14+
val isNetworkConnected: SharedFlow<Boolean?>
15+
}
16+
17+
@HiltViewModel
18+
class MainViewModel @Inject constructor(
19+
getConnectionStatusUseCase: GetConnectionStatusUseCase,
20+
dispatchersProvider: DispatchersProvider,
21+
) : BaseViewModel(dispatchersProvider), Input, Output {
22+
private val _isNetworkConnected = MutableSharedFlow<Boolean?>()
23+
override val isNetworkConnected: SharedFlow<Boolean?>
24+
get() = _isNetworkConnected
25+
26+
override val input: BaseInput = this
27+
override val output: BaseOutput = this
28+
29+
init {
30+
getConnectionStatusUseCase()
31+
.catch {
32+
_error.emit(it)
33+
}
34+
.onEach {
35+
_isNetworkConnected.emit(it)
36+
}
37+
.flowOn(dispatchersProvider.io)
38+
.launchIn(viewModelScope)
39+
}
40+
}

app/src/main/java/co/nimblehq/compose/crypto/ui/screens/home/HomeScreen.kt

-15
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import co.nimblehq.compose.crypto.R
2323
import co.nimblehq.compose.crypto.extension.boxShadow
2424
import co.nimblehq.compose.crypto.lib.IsLoading
2525
import co.nimblehq.compose.crypto.ui.base.LoadingState
26-
import co.nimblehq.compose.crypto.ui.common.AppDialogPopUp
2726
import co.nimblehq.compose.crypto.ui.navigation.AppDestination
2827
import co.nimblehq.compose.crypto.ui.preview.HomeScreenParams
2928
import co.nimblehq.compose.crypto.ui.preview.HomeScreenPreviewParameterProvider
@@ -57,9 +56,6 @@ fun HomeScreen(
5756
}
5857
}
5958

60-
// TODO remove in integration ticket
61-
val isNetworkConnected by viewModel.isNetworkConnected.collectAsState()
62-
6359
val showMyCoinsLoading: IsLoading by viewModel.output.showMyCoinsLoading.collectAsState()
6460
val showTrendingCoinsLoading: LoadingState by viewModel.output.showTrendingCoinsLoading.collectAsState()
6561
val myCoins: List<CoinItemUiModel> by viewModel.output.myCoins.collectAsState()
@@ -90,17 +86,6 @@ fun HomeScreen(
9086
onRefresh = { viewModel.input.loadData(isRefreshing = true) },
9187
onTrendingCoinsLoadMore = { viewModel.input.getTrendingCoins(loadMore = true) }
9288
)
93-
94-
// TODO remove in integration ticket
95-
if (isNetworkConnected == false) {
96-
AppDialogPopUp(
97-
onDismiss = { /*TODO*/ },
98-
onClick = { /*TODO*/ },
99-
message = stringResource(id = R.string.no_internet_message),
100-
actionText = stringResource(id = android.R.string.ok),
101-
title = stringResource(id = R.string.no_internet_title)
102-
)
103-
}
10489
}
10590

10691
@OptIn(ExperimentalMaterialApi::class)

app/src/main/java/co/nimblehq/compose/crypto/ui/screens/home/HomeViewModel.kt

-12
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ class HomeViewModel @Inject constructor(
5252
dispatchers: DispatchersProvider,
5353
private val getMyCoinsUseCase: GetMyCoinsUseCase,
5454
private val getTrendingCoinsUseCase: GetTrendingCoinsUseCase,
55-
private val isNetworkConnectedUseCase: IsNetworkConnectedUseCase,
5655
) : BaseViewModel(dispatchers), Input, Output {
5756

5857
override val input = this
@@ -84,19 +83,8 @@ class HomeViewModel @Inject constructor(
8483

8584
private var trendingCoinsPage = MY_COINS_INITIAL_PAGE
8685

87-
// TODO remove in integration ticket
88-
private val _isNetworkConnected = MutableStateFlow<Boolean?>(null)
89-
val isNetworkConnected: StateFlow<Boolean?> = _isNetworkConnected
90-
9186
init {
9287
loadData()
93-
// TODO remove in integration ticket
94-
execute {
95-
isNetworkConnectedUseCase()
96-
.collect {
97-
_isNetworkConnected.emit(it)
98-
}
99-
}
10088
}
10189

10290
override fun loadData(isRefreshing: Boolean) {

app/src/test/java/co/nimblehq/compose/crypto/ui/screens/home/HomeScreenTest.kt

+1-6
Original file line numberDiff line numberDiff line change
@@ -54,16 +54,12 @@ class HomeScreenTest : BaseViewModelTest() {
5454
private val mockGetMyCoinsUseCase = mockk<GetMyCoinsUseCase>()
5555
private val mockGetTrendingCoinsUseCase = mockk<GetTrendingCoinsUseCase>()
5656

57-
// TODO remove in integration ticket
58-
private val mockIsNetworkConnectedUseCase = mockk<IsNetworkConnectedUseCase>()
59-
6057
private lateinit var viewModel: HomeViewModel
6158

6259
private var appDestination: AppDestination? = null
6360

6461
@Before
6562
fun setUp() {
66-
every { mockIsNetworkConnectedUseCase() } returns flowOf(null)
6763
composeAndroidTestRule.activity.setContent {
6864
HomeScreen(
6965
viewModel = viewModel,
@@ -219,8 +215,7 @@ class HomeScreenTest : BaseViewModelTest() {
219215
viewModel = HomeViewModel(
220216
dispatchers = testDispatcherProvider,
221217
getMyCoinsUseCase = mockGetMyCoinsUseCase,
222-
getTrendingCoinsUseCase = mockGetTrendingCoinsUseCase,
223-
isNetworkConnectedUseCase = mockIsNetworkConnectedUseCase
218+
getTrendingCoinsUseCase = mockGetTrendingCoinsUseCase
224219
)
225220
}
226221
}

app/src/test/java/co/nimblehq/compose/crypto/ui/screens/home/HomeViewModelTest.kt

+1-5
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,6 @@ class HomeViewModelTest : BaseViewModelTest() {
2424
private val mockGetTrendingCoinsUseCase = mockk<GetTrendingCoinsUseCase>()
2525
private lateinit var viewModel: HomeViewModel
2626

27-
// TODO remove in integration ticket
28-
private val mockIsNetworkConnectedUseCase = mockk<IsNetworkConnectedUseCase>()
29-
3027
@Before
3128
fun setUp() {
3229
every { mockGetMyCoinsUseCase.execute(any()) } returns flowOf(MockUtil.myCoins)
@@ -145,8 +142,7 @@ class HomeViewModelTest : BaseViewModelTest() {
145142
viewModel = HomeViewModel(
146143
testDispatcherProvider,
147144
mockGetMyCoinsUseCase,
148-
mockGetTrendingCoinsUseCase,
149-
mockIsNetworkConnectedUseCase
145+
mockGetTrendingCoinsUseCase
150146
)
151147
}
152148
}

0 commit comments

Comments
 (0)