Skip to content

Commit

Permalink
[#102] Integrate implementation for no connection dialog
Browse files Browse the repository at this point in the history
  • Loading branch information
kaungkhantsoe committed Aug 2, 2023
1 parent e0e0e42 commit fd3afad
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 38 deletions.
19 changes: 19 additions & 0 deletions app/src/main/java/co/nimblehq/compose/crypto/extension/FlowExt.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package co.nimblehq.compose.crypto.extension

import android.annotation.SuppressLint
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import kotlinx.coroutines.flow.*
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext

@SuppressLint("ComposableNaming")
@Composable
fun <T> Flow<T>.collectAsEffect(
context: CoroutineContext = EmptyCoroutineContext,
block: suspend (T) -> Unit,
) {
LaunchedEffect(key1 = Unit) {
onEach(block).flowOn(context).launchIn(this)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ sealed class AppDestination(val route: String = "") {

object Home : AppDestination("home")

object NoNetwork : AppDestination("no_network")

/**
* We can define route as "coin/details" without "coinId" parameter because we're passing it as argument already.
* So either passing "coinId" via arguments or passing it via route.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,35 @@
package co.nimblehq.compose.crypto.ui.navigation

import androidx.compose.runtime.Composable
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.*
import androidx.navigation.compose.*
import co.nimblehq.compose.crypto.R
import co.nimblehq.compose.crypto.extension.collectAsEffect
import co.nimblehq.compose.crypto.ui.common.AppDialogPopUp
import co.nimblehq.compose.crypto.ui.screens.MainViewModel
import co.nimblehq.compose.crypto.ui.screens.detail.DetailScreen
import co.nimblehq.compose.crypto.ui.screens.home.HomeScreen

@Composable
fun AppNavigation(
navController: NavHostController = rememberNavController(),
mainViewModel: MainViewModel = hiltViewModel(),
startDestination: String = AppDestination.Home.destination
) {

mainViewModel.isNetworkConnected.collectAsEffect { isNetworkConnected ->
if (isNetworkConnected == false) {
val destination = AppDestination.NoNetwork

val currentRoute = navController.currentBackStackEntry?.destination?.route
if (currentRoute == AppDestination.NoNetwork.route) {
navController.popBackStack()
}

navController.navigate(destination)
}
}
NavHost(
navController = navController,
startDestination = startDestination
Expand All @@ -27,6 +46,16 @@ fun AppNavigation(
coinId = it.arguments?.getString(KEY_COIN_ID).orEmpty()
)
}

dialog(AppDestination.NoNetwork.route) {
AppDialogPopUp(
onDismiss = { navController.popBackStack() },
onClick = { navController.popBackStack() },
message = R.string.no_internet_message,
actionText = android.R.string.ok,
title = R.string.no_internet_title
)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package co.nimblehq.compose.crypto.ui.screens

import androidx.lifecycle.viewModelScope
import co.nimblehq.compose.crypto.domain.usecase.GetConnectionStatusUseCase
import co.nimblehq.compose.crypto.ui.base.*
import co.nimblehq.compose.crypto.util.DispatchersProvider
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.*
import javax.inject.Inject

interface Input : BaseInput

interface Output : BaseOutput {
val isNetworkConnected: SharedFlow<Boolean?>
}

@HiltViewModel
class MainViewModel @Inject constructor(
getConnectionStatusUseCase: GetConnectionStatusUseCase,
dispatchersProvider: DispatchersProvider,
) : BaseViewModel(dispatchersProvider), Input, Output {
private val _isNetworkConnected = MutableSharedFlow<Boolean?>()
override val isNetworkConnected: SharedFlow<Boolean?>
get() = _isNetworkConnected

override val input: BaseInput = this
override val output: BaseOutput = this

init {
getConnectionStatusUseCase()
.catch {
_error.emit(it)
}
.onEach {
_isNetworkConnected.emit(it)
}
.flowOn(dispatchersProvider.io)
.launchIn(viewModelScope)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import co.nimblehq.compose.crypto.R
import co.nimblehq.compose.crypto.extension.boxShadow
import co.nimblehq.compose.crypto.lib.IsLoading
import co.nimblehq.compose.crypto.ui.base.LoadingState
import co.nimblehq.compose.crypto.ui.common.AppDialogPopUp
import co.nimblehq.compose.crypto.ui.navigation.AppDestination
import co.nimblehq.compose.crypto.ui.preview.HomeScreenParams
import co.nimblehq.compose.crypto.ui.preview.HomeScreenPreviewParameterProvider
Expand Down Expand Up @@ -57,9 +56,6 @@ fun HomeScreen(
}
}

// TODO remove in integration ticket
val hasConnection by viewModel.hasConnection.collectAsState()

val showMyCoinsLoading: IsLoading by viewModel.output.showMyCoinsLoading.collectAsState()
val showTrendingCoinsLoading: LoadingState by viewModel.output.showTrendingCoinsLoading.collectAsState()
val myCoins: List<CoinItemUiModel> by viewModel.output.myCoins.collectAsState()
Expand Down Expand Up @@ -90,17 +86,6 @@ fun HomeScreen(
onRefresh = { viewModel.input.loadData(isRefreshing = true) },
onTrendingCoinsLoadMore = { viewModel.input.getTrendingCoins(loadMore = true) }
)

// TODO remove in integration ticket
if (hasConnection == false) {
AppDialogPopUp(
onDismiss = { /*TODO*/ },
onClick = { /*TODO*/ },
message = R.string.no_internet_message,
actionText = android.R.string.ok,
title = R.string.no_internet_title
)
}
}

@OptIn(ExperimentalMaterialApi::class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ class HomeViewModel @Inject constructor(
dispatchers: DispatchersProvider,
private val getMyCoinsUseCase: GetMyCoinsUseCase,
private val getTrendingCoinsUseCase: GetTrendingCoinsUseCase,
private val getConnectionStatusUseCase: GetConnectionStatusUseCase,
) : BaseViewModel(dispatchers), Input, Output {

override val input = this
Expand Down Expand Up @@ -84,19 +83,8 @@ class HomeViewModel @Inject constructor(

private var trendingCoinsPage = MY_COINS_INITIAL_PAGE

// TODO remove in integration ticket
private val _hasConnection = MutableStateFlow<Boolean?>(null)
val hasConnection: StateFlow<Boolean?> = _hasConnection

init {
loadData()
// TODO remove in integration ticket
execute {
getConnectionStatusUseCase()
.collect {
_hasConnection.emit(it)
}
}
}

override fun loadData(isRefreshing: Boolean) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,12 @@ class HomeScreenTest : BaseViewModelTest() {
private val mockGetMyCoinsUseCase = mockk<GetMyCoinsUseCase>()
private val mockGetTrendingCoinsUseCase = mockk<GetTrendingCoinsUseCase>()

// TODO remove in integration ticket
private val mockGetConnectionStatusUseCase = mockk<GetConnectionStatusUseCase>()

private lateinit var viewModel: HomeViewModel

private var appDestination: AppDestination? = null

@Before
fun setUp() {
every { mockGetConnectionStatusUseCase() } returns flowOf(null)
composeAndroidTestRule.activity.setContent {
HomeScreen(
viewModel = viewModel,
Expand Down Expand Up @@ -219,8 +215,7 @@ class HomeScreenTest : BaseViewModelTest() {
viewModel = HomeViewModel(
dispatchers = testDispatcherProvider,
getMyCoinsUseCase = mockGetMyCoinsUseCase,
getTrendingCoinsUseCase = mockGetTrendingCoinsUseCase,
getConnectionStatusUseCase = mockGetConnectionStatusUseCase
getTrendingCoinsUseCase = mockGetTrendingCoinsUseCase
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@ class HomeViewModelTest : BaseViewModelTest() {
private val mockGetTrendingCoinsUseCase = mockk<GetTrendingCoinsUseCase>()
private lateinit var viewModel: HomeViewModel

// TODO remove in integration ticket
private val mockGetConnectionStatusUseCase = mockk<GetConnectionStatusUseCase>()

@Before
fun setUp() {
every { mockGetMyCoinsUseCase.execute(any()) } returns flowOf(MockUtil.myCoins)
Expand Down Expand Up @@ -145,8 +142,7 @@ class HomeViewModelTest : BaseViewModelTest() {
viewModel = HomeViewModel(
testDispatcherProvider,
mockGetMyCoinsUseCase,
mockGetTrendingCoinsUseCase,
mockGetConnectionStatusUseCase
mockGetTrendingCoinsUseCase
)
}
}

0 comments on commit fd3afad

Please sign in to comment.