diff --git a/tv/src/main/java/au/com/shiftyjelly/pocketcasts/TvApplication.kt b/tv/src/main/java/au/com/shiftyjelly/pocketcasts/TvApplication.kt index 7ab67b2bad2..e86b461f9ef 100644 --- a/tv/src/main/java/au/com/shiftyjelly/pocketcasts/TvApplication.kt +++ b/tv/src/main/java/au/com/shiftyjelly/pocketcasts/TvApplication.kt @@ -5,6 +5,7 @@ import androidx.hilt.work.HiltWorkerFactory import androidx.work.Configuration import au.com.shiftyjelly.pocketcasts.repositories.playback.PlaybackManager import au.com.shiftyjelly.pocketcasts.utils.TimberDebugTree +import au.com.shiftyjelly.pocketcasts.utils.log.RxJavaUncaughtExceptionHandling import dagger.hilt.android.HiltAndroidApp import javax.inject.Inject import kotlinx.coroutines.CoroutineScope @@ -29,6 +30,7 @@ class TvApplication : if (BuildConfig.DEBUG) { Timber.plant(TimberDebugTree()) } + RxJavaUncaughtExceptionHandling.setUp() // setup() subscribes the Up Next queue's sync pipeline itself, so there must be no // separate UpNextQueue.setupBlocking() call on TV. applicationScope.launch { diff --git a/tv/src/main/java/au/com/shiftyjelly/pocketcasts/home/TvScaffold.kt b/tv/src/main/java/au/com/shiftyjelly/pocketcasts/home/TvScaffold.kt index 996fde364ad..93be96baf5e 100644 --- a/tv/src/main/java/au/com/shiftyjelly/pocketcasts/home/TvScaffold.kt +++ b/tv/src/main/java/au/com/shiftyjelly/pocketcasts/home/TvScaffold.kt @@ -42,6 +42,7 @@ import au.com.shiftyjelly.pocketcasts.upnext.TvUpNextScreen fun TvScaffold( onLogIn: () -> Unit, onCreateAccount: () -> Unit, + onSignedOut: () -> Unit, modifier: Modifier = Modifier, viewModel: TvScaffoldViewModel = hiltViewModel(), ) { @@ -131,6 +132,7 @@ fun TvScaffold( onLogOut = { isProfileModalVisible = false viewModel.signOut() + onSignedOut() }, ) } diff --git a/tv/src/main/java/au/com/shiftyjelly/pocketcasts/onboarding/TvOnboardingNavHost.kt b/tv/src/main/java/au/com/shiftyjelly/pocketcasts/onboarding/TvOnboardingNavHost.kt index 2301283a4b4..95957e575f0 100644 --- a/tv/src/main/java/au/com/shiftyjelly/pocketcasts/onboarding/TvOnboardingNavHost.kt +++ b/tv/src/main/java/au/com/shiftyjelly/pocketcasts/onboarding/TvOnboardingNavHost.kt @@ -28,6 +28,11 @@ fun TvOnboardingNavHost( viewModel: TvOnboardingViewModel = hiltViewModel(), ) { val navController = rememberNavController() + val navigateClearingBackStack: (String) -> Unit = { route -> + navController.navigate(route) { + popUpTo(navController.graph.id) { inclusive = true } + } + } val toastHostState = remember { TvToastHostState() } CompositionLocalProvider(LocalTvToastHostState provides toastHostState) { Box(modifier = modifier.fillMaxSize()) { @@ -41,7 +46,6 @@ fun TvOnboardingNavHost( onSignIn = { navController.navigate(TvOnboardingRoutes.SIGN_IN) }, onCreateAccount = { navController.navigate(TvOnboardingRoutes.CREATE_ACCOUNT) }, onContinueWithoutAccount = { - viewModel.completeOnboarding() navController.navigate(TvOnboardingRoutes.HOME) { popUpTo(TvOnboardingRoutes.LANDING) { inclusive = true } } @@ -55,17 +59,12 @@ fun TvOnboardingNavHost( } composable(TvOnboardingRoutes.SIGN_IN) { TvSignInScreen( - onSignInComplete = { - navController.navigate(TvOnboardingRoutes.SYNCING) { - popUpTo(navController.graph.id) { inclusive = true } - } - }, + onSignInComplete = { navigateClearingBackStack(TvOnboardingRoutes.SYNCING) }, ) } composable(TvOnboardingRoutes.SYNCING) { TvSyncingScreen( onSyncComplete = { - viewModel.completeOnboarding() navController.navigate(TvOnboardingRoutes.HOME) { popUpTo(TvOnboardingRoutes.SYNCING) { inclusive = true } } @@ -76,6 +75,7 @@ fun TvOnboardingNavHost( TvScaffold( onLogIn = { navController.navigate(TvOnboardingRoutes.SIGN_IN) }, onCreateAccount = { navController.navigate(TvOnboardingRoutes.CREATE_ACCOUNT) }, + onSignedOut = { navigateClearingBackStack(TvOnboardingRoutes.LANDING) }, ) } } diff --git a/tv/src/main/java/au/com/shiftyjelly/pocketcasts/onboarding/TvOnboardingViewModel.kt b/tv/src/main/java/au/com/shiftyjelly/pocketcasts/onboarding/TvOnboardingViewModel.kt index 6760a3844e3..966e2b962d7 100644 --- a/tv/src/main/java/au/com/shiftyjelly/pocketcasts/onboarding/TvOnboardingViewModel.kt +++ b/tv/src/main/java/au/com/shiftyjelly/pocketcasts/onboarding/TvOnboardingViewModel.kt @@ -2,20 +2,18 @@ package au.com.shiftyjelly.pocketcasts.onboarding import androidx.lifecycle.ViewModel import au.com.shiftyjelly.pocketcasts.preferences.Settings +import au.com.shiftyjelly.pocketcasts.repositories.sync.SyncManager import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject @HiltViewModel class TvOnboardingViewModel @Inject constructor( - private val settings: Settings, + syncManager: SyncManager, + settings: Settings, ) : ViewModel() { - val startDestination: String = if (settings.hasCompletedOnboarding()) { + val startDestination: String = if (syncManager.isLoggedIn() && !settings.getFullySignedOut()) { TvOnboardingRoutes.HOME } else { TvOnboardingRoutes.LANDING } - - fun completeOnboarding() { - settings.setHasDoneInitialOnboarding() - } } diff --git a/tv/src/test/java/au/com/shiftyjelly/pocketcasts/onboarding/TvOnboardingViewModelTest.kt b/tv/src/test/java/au/com/shiftyjelly/pocketcasts/onboarding/TvOnboardingViewModelTest.kt index bd6319e27f7..f8328928d29 100644 --- a/tv/src/test/java/au/com/shiftyjelly/pocketcasts/onboarding/TvOnboardingViewModelTest.kt +++ b/tv/src/test/java/au/com/shiftyjelly/pocketcasts/onboarding/TvOnboardingViewModelTest.kt @@ -1,34 +1,37 @@ package au.com.shiftyjelly.pocketcasts.onboarding import au.com.shiftyjelly.pocketcasts.preferences.Settings +import au.com.shiftyjelly.pocketcasts.repositories.sync.SyncManager import org.junit.Assert.assertEquals import org.junit.Test import org.mockito.kotlin.mock -import org.mockito.kotlin.verify import org.mockito.kotlin.whenever class TvOnboardingViewModelTest { + private val syncManager = mock() private val settings = mock() @Test - fun `start destination is landing when onboarding not completed`() { - whenever(settings.hasCompletedOnboarding()).thenReturn(false) - val viewModel = TvOnboardingViewModel(settings) - assertEquals(TvOnboardingRoutes.LANDING, viewModel.startDestination) + fun `start destination is landing when signed out`() { + whenever(syncManager.isLoggedIn()).thenReturn(false) + whenever(settings.getFullySignedOut()).thenReturn(true) + assertEquals(TvOnboardingRoutes.LANDING, viewModel().startDestination) } @Test - fun `start destination is home when onboarding completed`() { - whenever(settings.hasCompletedOnboarding()).thenReturn(true) - val viewModel = TvOnboardingViewModel(settings) - assertEquals(TvOnboardingRoutes.HOME, viewModel.startDestination) + fun `start destination is home when signed in and not fully signed out`() { + whenever(syncManager.isLoggedIn()).thenReturn(true) + whenever(settings.getFullySignedOut()).thenReturn(false) + assertEquals(TvOnboardingRoutes.HOME, viewModel().startDestination) } @Test - fun `complete onboarding persists to settings`() { - val viewModel = TvOnboardingViewModel(settings) - viewModel.completeOnboarding() - verify(settings).setHasDoneInitialOnboarding() + fun `start destination is landing when logged in but sign-out is pending`() { + whenever(syncManager.isLoggedIn()).thenReturn(true) + whenever(settings.getFullySignedOut()).thenReturn(true) + assertEquals(TvOnboardingRoutes.LANDING, viewModel().startDestination) } + + private fun viewModel() = TvOnboardingViewModel(syncManager, settings) }