Skip to content

Commit

Permalink
Merge pull request #102 from CrisisCleanup/accept-tos
Browse files Browse the repository at this point in the history
Accept tos
  • Loading branch information
hueachilles authored Feb 16, 2024
2 parents f1b31cb + 60eacce commit 9cac72a
Show file tree
Hide file tree
Showing 50 changed files with 796 additions and 241 deletions.
1 change: 0 additions & 1 deletion .maestro/.gitignore

This file was deleted.

4 changes: 3 additions & 1 deletion .maestro/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ List devices

Full test command
`MAESTRO_APP_ID=com.crisiscleanup.demo.debug maestro --device emulator-5554 test auth-tests`
`MAESTRO_APP_ID=com.crisiscleanup.dev maestro --device device-uuid-from-list test auth-tests`
`MAESTRO_APP_ID=com.crisiscleanup.dev maestro --device device-uuid-from-list test auth-tests`

[Flow file structure](https://maestro.mobile.dev/api-reference/configuration/flow-configuration)
2 changes: 1 addition & 1 deletion .maestro/auth-tests/login-password-toggle.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ env:
- assertVisible:
id: ccuLogo
- assertVisible:
id: loginHeaderText
id: loginEmailHeaderText
- assertVisible:
id: loginEmailTextField
- assertVisible:
Expand Down
4 changes: 2 additions & 2 deletions .maestro/auth-tests/login-view.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ tags:
- assertVisible:
id: ccuLogo
- assertVisible:
id: loginHeaderText
id: loginEmailHeaderText
- assertVisible:
id: loginEmailTextField
- assertVisible:
id: loginPasswordTextField
- assertVisible:
id: loginLoginBtn
id: loginLoginAction
2 changes: 1 addition & 1 deletion .maestro/auth-tests/login.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ tags:
id: loginPasswordTextField
- inputText: ${MAESTRO_APP_PASSWORD}
- tapOn:
id: loginLoginBtn
id: loginLoginAction
- assertVisible:
id: navItem_Work
- assertVisible:
Expand Down
2 changes: 1 addition & 1 deletion .maestro/auth-tests/logout.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ onFlowStart:
- assertVisible:
id: ccuLogo
- assertVisible:
id: loginHeaderText
id: loginEmailHeaderText
12 changes: 0 additions & 12 deletions .maestro/screenshot-tests/auth-screens.yaml

This file was deleted.

98 changes: 94 additions & 4 deletions app/src/main/java/com/crisiscleanup/MainActivityViewModel.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
package com.crisiscleanup

import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.crisiscleanup.core.common.AppEnv
import com.crisiscleanup.core.common.AppSettingsProvider
import com.crisiscleanup.core.common.AppVersionProvider
import com.crisiscleanup.core.common.KeyResourceTranslator
import com.crisiscleanup.core.common.NetworkMonitor
import com.crisiscleanup.core.common.event.AuthEventBus
import com.crisiscleanup.core.common.event.ExternalEventBus
import com.crisiscleanup.core.common.event.UserPersistentInvite
import com.crisiscleanup.core.common.log.AppLogger
Expand All @@ -14,9 +19,11 @@ import com.crisiscleanup.core.common.log.Logger
import com.crisiscleanup.core.common.network.CrisisCleanupDispatchers.IO
import com.crisiscleanup.core.common.network.Dispatcher
import com.crisiscleanup.core.common.sync.SyncPuller
import com.crisiscleanup.core.common.throttleLatest
import com.crisiscleanup.core.data.IncidentSelector
import com.crisiscleanup.core.data.repository.AccountDataRefresher
import com.crisiscleanup.core.data.repository.AccountDataRepository
import com.crisiscleanup.core.data.repository.AccountUpdateRepository
import com.crisiscleanup.core.data.repository.AppDataManagementRepository
import com.crisiscleanup.core.data.repository.LocalAppMetricsRepository
import com.crisiscleanup.core.data.repository.LocalAppPreferencesRepository
Expand All @@ -31,6 +38,7 @@ import com.crisiscleanup.core.model.data.UserData
import com.google.firebase.analytics.FirebaseAnalytics
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
Expand All @@ -40,9 +48,11 @@ import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.datetime.Clock
import java.util.concurrent.atomic.AtomicReference
import javax.inject.Inject
Expand All @@ -55,14 +65,18 @@ class MainActivityViewModel @Inject constructor(
accountDataRepository: AccountDataRepository,
incidentSelector: IncidentSelector,
appDataRepository: AppDataManagementRepository,
accountDataRefresher: AccountDataRefresher,
private val accountDataRefresher: AccountDataRefresher,
private val accountUpdateRepository: AccountUpdateRepository,
val translator: KeyResourceTranslator,
private val syncPuller: SyncPuller,
private val appVersionProvider: AppVersionProvider,
appSettingsProvider: AppSettingsProvider,
private val appEnv: AppEnv,
firebaseAnalytics: FirebaseAnalytics,
externalEventBus: ExternalEventBus,
@Dispatcher(IO) ioDispatcher: CoroutineDispatcher,
private val authEventBus: AuthEventBus,
private val networkMonitor: NetworkMonitor,
@Dispatcher(IO) private val ioDispatcher: CoroutineDispatcher,
@Logger(CrisisCleanupLoggers.App) private val logger: AppLogger,
) : ViewModel() {
/**
Expand Down Expand Up @@ -92,12 +106,33 @@ class MainActivityViewModel @Inject constructor(
/**
* API account tokens need re-issuing
*/
var isAccountExpired = mutableStateOf(false)
var isAccountExpired by mutableStateOf(false)
private set

val termsOfServiceUrl = "${appSettingsProvider.baseUrl}/terms?view=plain"
var hasAcceptedTerms by mutableStateOf(false)
private set
var isAcceptingTerms by mutableStateOf(false)
val isFetchingTermsAcceptance = MutableStateFlow(false)
private val isUpdatingTermsAcceptance = MutableStateFlow(false)
val isLoadingTermsAcceptance = combine(
isFetchingTermsAcceptance,
isUpdatingTermsAcceptance,
::Pair,
)
.map { (b0, b1) -> b0 || b1 }
.stateIn(
scope = viewModelScope,
initialValue = false,
started = SharingStarted.WhileSubscribed(),
)
var acceptTermsErrorMessage by mutableStateOf("")
private set

val authState = accountDataRepository.accountData
.map {
isAccountExpired.value = !it.areTokensValid
isAccountExpired = !it.areTokensValid
hasAcceptedTerms = it.hasAcceptedTerms

if (it.hasAuthenticated) {
AuthState.Authenticated(it)
Expand Down Expand Up @@ -179,6 +214,24 @@ class MainActivityViewModel @Inject constructor(
syncPuller.appPullLanguage()
syncPuller.appPullStatuses()

accountDataRepository.accountData
.mapLatest { it.hasAcceptedTerms }
.filter { !it }
.throttleLatest(250)
.onEach {
isAcceptingTerms = false

withContext(ioDispatcher) {
isFetchingTermsAcceptance.value = true
try {
accountDataRefresher.updateAcceptedTerms()
} finally {
isFetchingTermsAcceptance.value = false
}
}
}
.launchIn(viewModelScope)

val switchProductionApiManager = SwitchProductionApiManager(
appMetricsRepository,
appDataRepository,
Expand Down Expand Up @@ -207,6 +260,43 @@ class MainActivityViewModel @Inject constructor(
}
}
}

fun onRejectTerms() {
acceptTermsErrorMessage = ""
authEventBus.onLogout()
}

fun onAcceptTerms() {
acceptTermsErrorMessage = ""

if (!isAcceptingTerms) {
acceptTermsErrorMessage =
translator("~~You must check the box accepting the terms of service.")
return
}

if (isUpdatingTermsAcceptance.value) {
return
}
isUpdatingTermsAcceptance.value = true
viewModelScope.launch(ioDispatcher) {
try {
val isAccepted = accountUpdateRepository.acceptTerms()
if (isAccepted) {
accountDataRefresher.updateAcceptedTerms()
} else {
val errorMessage = if (networkMonitor.isOnline.first()) {
translator("~~Something went wrong. Please try again later")
} else {
translator("~~Connect to the internet and try again.")
}
acceptTermsErrorMessage = errorMessage
}
} finally {
isUpdatingTermsAcceptance.value = false
}
}
}
}

sealed interface MainActivityUiState {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import androidx.compose.ui.Modifier
import androidx.navigation.NavController
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import com.crisiscleanup.core.appnav.RouteConstant
import com.crisiscleanup.core.appnav.RouteConstant.authGraphRoutePattern
import com.crisiscleanup.core.appnav.RouteConstant.authRoute
import com.crisiscleanup.feature.authentication.navigation.authGraph
import com.crisiscleanup.feature.authentication.navigation.emailLoginLinkScreen
import com.crisiscleanup.feature.authentication.navigation.forgotPasswordScreen
Expand All @@ -28,10 +28,7 @@ import com.crisiscleanup.feature.authentication.navigation.resetPasswordScreen
import com.crisiscleanup.feature.authentication.navigation.volunteerOrgScreen

private fun NavController.popToAuth() {
popBackStack()
while (currentBackStackEntry?.destination?.route?.let { it != RouteConstant.authRoute } == true) {
popBackStack()
}
popBackStack(authRoute, false, saveState = false)
}

@Composable
Expand All @@ -43,6 +40,7 @@ fun CrisisCleanupAuthNavHost(
modifier: Modifier = Modifier,
startDestination: String = authGraphRoutePattern,
) {
val navToAuth = remember(navController) { { navController.popToAuth() } }
val navToLoginWithEmail =
remember(navController) {
{
Expand All @@ -67,6 +65,7 @@ fun CrisisCleanupAuthNavHost(
nestedGraphs = {
loginWithEmailScreen(
onBack = onBack,
onAuthenticated = navToAuth,
closeAuthentication = closeAuthentication,
openForgotPassword = navToForgotPassword,
openEmailMagicLink = navToEmailMagicLink,
Expand All @@ -81,6 +80,7 @@ fun CrisisCleanupAuthNavHost(
)
loginWithPhoneScreen(
onBack = onBack,
onAuthenticated = navToAuth,
closeAuthentication = closeAuthentication,
)
volunteerOrgScreen(
Expand All @@ -102,6 +102,7 @@ fun CrisisCleanupAuthNavHost(
)
magicLinkLoginScreen(
onBack = onBack,
onAuthenticated = navToAuth,
closeAuthentication = closeAuthentication,
)
requestAccessScreen(
Expand Down
Loading

0 comments on commit 9cac72a

Please sign in to comment.