Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added auth state listener to directly navigate to home when logged in #50

Merged
merged 8 commits into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ dependencies {
implementation(project(":modules:education"))
implementation(project(":modules:onboarding"))

implementation(libs.firebase.firestore.ktx)

implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.lifecycle.view.model.ktx)
Expand All @@ -53,10 +55,5 @@ dependencies {
implementation(libs.navigation.compose)
implementation(libs.kotlinx.serialization.json)

implementation(libs.firebase.functions.ktx)
implementation(libs.firebase.auth.ktx)
implementation(libs.firebase.firestore.ktx)
implementation(libs.firebase.storage.ktx)

androidTestImplementation(project(":core:testing"))
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,13 @@ import edu.stanford.spezi.core.logging.speziLogger
import edu.stanford.spezi.core.navigation.NavigationEvent
import edu.stanford.spezi.core.navigation.Navigator
import edu.stanford.spezi.module.account.AccountEvents
import edu.stanford.spezi.module.account.manager.UserSessionManager
import edu.stanford.spezi.module.account.manager.UserState
import edu.stanford.spezi.module.onboarding.OnboardingNavigationEvent
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import javax.inject.Inject
Expand All @@ -23,6 +27,7 @@ import edu.stanford.spezi.core.design.R as DesignR
class MainActivityViewModel @Inject constructor(
private val accountEvents: AccountEvents,
private val navigator: Navigator,
private val userSessionManager: UserSessionManager,
) : ViewModel() {
private val logger by speziLogger()

Expand All @@ -36,6 +41,10 @@ class MainActivityViewModel @Inject constructor(
val uiState = _uiState.asStateFlow()

init {
startObserving()
}

private fun startObserving() {
viewModelScope.launch {
accountEvents.events.collect { event ->
when (event) {
Expand All @@ -49,6 +58,19 @@ class MainActivityViewModel @Inject constructor(
}
}
}

viewModelScope.launch {
userSessionManager.userState
.filterIsInstance<UserState.Registered>()
.collect { userState ->
val navigationEvent = if (userState.hasConsented) {
AppNavigationEvent.AppScreen
} else {
OnboardingNavigationEvent.ConsentScreen
}
navigator.navigateTo(event = navigationEvent)
}
}
}

fun onAction(action: Action) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package edu.stanford.bdh.engagehf.education

import com.google.firebase.firestore.FirebaseFirestore
import edu.stanford.spezi.module.onboarding.invitation.await
import edu.stanford.spezi.modules.education.videos.VideoSection
import edu.stanford.spezi.modules.education.videos.data.repository.EducationRepository
import kotlinx.coroutines.tasks.await
import javax.inject.Inject

class EngageEducationRepository @Inject constructor(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package edu.stanford.bdh.engagehf.education

import com.google.firebase.firestore.DocumentSnapshot
import edu.stanford.spezi.module.onboarding.invitation.await
import edu.stanford.spezi.modules.education.videos.Video
import edu.stanford.spezi.modules.education.videos.VideoSection
import kotlinx.coroutines.tasks.await
import java.util.Locale
import javax.inject.Inject

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package edu.stanford.bdh.engagehf.navigation.screens

import androidx.activity.ComponentActivity
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.HorizontalDivider
Expand All @@ -12,6 +13,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
Expand All @@ -27,7 +29,9 @@ import edu.stanford.spezi.modules.education.videos.EducationScreen

@Composable
fun AppScreen() {
val viewModel = hiltViewModel<MainActivityViewModel>()
val viewModel = hiltViewModel<MainActivityViewModel>(
viewModelStoreOwner = LocalContext.current as ComponentActivity
)
val uiState by viewModel.uiState.collectAsState()
AppScreen(
uiState = uiState,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,13 @@ package edu.stanford.bdh.engagehf.onboarding

import edu.stanford.bdh.engagehf.navigation.AppNavigationEvent
import edu.stanford.spezi.core.navigation.Navigator
import edu.stanford.spezi.core.utils.MessageNotifier
import edu.stanford.spezi.module.onboarding.consent.ConsentManager
import edu.stanford.spezi.module.onboarding.consent.ConsentUiState
import edu.stanford.spezi.module.onboarding.consent.PdfCreationService
import edu.stanford.spezi.module.onboarding.consent.PdfService
import javax.inject.Inject

class EngageConsentManager @Inject internal constructor(
private val pdfService: PdfService,
private val navigator: Navigator,
private val pdfCreationService: PdfCreationService,
private val messageNotifier: MessageNotifier,
) : ConsentManager {

override suspend fun getMarkdownText(): String {
Expand All @@ -23,12 +20,11 @@ class EngageConsentManager @Inject internal constructor(
""".trimIndent()
}

override suspend fun onConsented(uiState: ConsentUiState): Result<Unit> = runCatching {
val pdfBytes = pdfCreationService.createPdf(uiState)
if (pdfService.uploadPdf(pdfBytes).getOrThrow()) {
navigator.navigateTo(AppNavigationEvent.AppScreen)
} else {
error("Upload went wrong")
}
override suspend fun onConsented() {
navigator.navigateTo(AppNavigationEvent.AppScreen)
}

override suspend fun onConsentFailure(error: Throwable) {
messageNotifier.notify(message = "Something went wrong, failed to submit the consent!")
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,32 +1,157 @@
package edu.stanford.bdh.engagehf

import com.google.common.truth.Truth.assertThat
import edu.stanford.bdh.engagehf.navigation.AppNavigationEvent
import edu.stanford.spezi.core.navigation.NavigationEvent
import edu.stanford.spezi.core.navigation.Navigator
import edu.stanford.spezi.core.testing.CoroutineTestRule
import edu.stanford.spezi.core.testing.runTestUnconfined
import edu.stanford.spezi.module.account.AccountEvents
import edu.stanford.spezi.module.account.manager.UserSessionManager
import edu.stanford.spezi.module.account.manager.UserState
import edu.stanford.spezi.module.onboarding.OnboardingNavigationEvent
import io.mockk.Called
import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.ExperimentalCoroutinesApi
import io.mockk.verify
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.update
import org.junit.Before
import org.junit.Rule
import org.junit.Test

@ExperimentalCoroutinesApi
class MainActivityViewModelTest {

private var mockAccountEvents: AccountEvents = mockk(relaxed = true)
private var mockNavigator: Navigator = mockk(relaxed = true)

private lateinit var viewModel: MainActivityViewModel

@get:Rule
val coroutineTestRule = CoroutineTestRule()

private val accountEventsFlow = MutableSharedFlow<AccountEvents.Event>()
private val accountEvents: AccountEvents = mockk(relaxed = true)
private val navigator: Navigator = mockk(relaxed = true)
private val userStateFlow = MutableStateFlow<UserState>(UserState.NotInitialized)
private val userSessionManager: UserSessionManager = mockk()
private lateinit var viewModel: MainActivityViewModel

@Before
fun setup() {
viewModel = MainActivityViewModel(mockAccountEvents, mockNavigator)
fun setUp() {
every { accountEvents.events } returns accountEventsFlow
every { userSessionManager.userState } returns userStateFlow
viewModel = MainActivityViewModel(
accountEvents = accountEvents,
navigator = navigator,
userSessionManager = userSessionManager,
)
}

@Test
fun `it should start observing on init`() {
verify { accountEvents.events }
verify { userSessionManager.userState }
}

@Test
fun `it should navigate to app screen on SignUpSuccess event`() = runTestUnconfined {
// given
val event = AccountEvents.Event.SignUpSuccess

// when
accountEventsFlow.emit(event)

// then
verify { navigator.navigateTo(event = AppNavigationEvent.AppScreen) }
}

@Test
fun `it should navigate to app screen on SignInSuccess event`() = runTestUnconfined {
// given
val event = AccountEvents.Event.SignInSuccess

// when
accountEventsFlow.emit(event)

// then
verify { navigator.navigateTo(event = AppNavigationEvent.AppScreen) }
}

@Test
fun `it should not navigate on other account events`() = runTestUnconfined {
// given
val event = AccountEvents.Event.SignInFailure

// when
accountEventsFlow.emit(event)

// then
verify { navigator wasNot Called }
}

@Test
fun `it should return navigation events`() {
// given
val events: SharedFlow<NavigationEvent> = mockk()
every { navigator.events } returns events

// when
val result = viewModel.getNavigationEvents()

// then
assertThat(result).isEqualTo(events)
}

@Test
fun `it should navigate to app screen for registered user if consented`() =
runTestUnconfined {
// given
val userState = UserState.Registered(hasConsented = true)

// when
userStateFlow.update { userState }

// then
verify { navigator.navigateTo(event = AppNavigationEvent.AppScreen) }
}

@Test
fun `it should navigate to consent screen for registered user if not consented`() =
runTestUnconfined {
// given
val userState = UserState.Registered(hasConsented = false)

// when
userStateFlow.update { userState }

// then
verify { navigator.navigateTo(event = OnboardingNavigationEvent.ConsentScreen) }
}

@Test
fun `it should not navigate for not initialized users`() =
runTestUnconfined {
// given
val userState = UserState.NotInitialized

// when
userStateFlow.update { userState }

// then
verify { navigator wasNot Called }
}

@Test
fun `it should not navigate for anonymous users`() =
runTestUnconfined {
// given
val userState = UserState.Anonymous

// when
userStateFlow.update { userState }

// then
verify { navigator wasNot Called }
}

@Test
fun `given selectedItem when onAction UpdateSelectedItem then uiState selectedItem should be updated`() =
runTestUnconfined {
Expand Down
Loading
Loading