Skip to content

Commit

Permalink
feat: eula on registration and sign in
Browse files Browse the repository at this point in the history
  • Loading branch information
k1rill committed Feb 19, 2024
1 parent dedb010 commit ce6740e
Show file tree
Hide file tree
Showing 18 changed files with 303 additions and 69 deletions.
2 changes: 2 additions & 0 deletions app/src/main/java/org/openedx/app/di/AppModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import org.openedx.app.data.storage.PreferencesManager
import org.openedx.app.room.AppDatabase
import org.openedx.app.room.DATABASE_NAME
import org.openedx.app.system.notifier.AppNotifier
import org.openedx.auth.presentation.AgreementProvider
import org.openedx.auth.presentation.AuthAnalytics
import org.openedx.auth.presentation.AuthRouter
import org.openedx.auth.presentation.sso.FacebookAuthHelper
Expand Down Expand Up @@ -155,6 +156,7 @@ val appModule = module {
single<CourseAnalytics> { get<AnalyticsManager>() }
single<DiscussionAnalytics> { get<AnalyticsManager>() }

factory { AgreementProvider(get(), get()) }
factory { FacebookAuthHelper() }
factory { GoogleAuthHelper(get()) }
factory { MicrosoftAuthHelper() }
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/java/org/openedx/app/di/ScreenModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,14 @@ val screenModule = module {
get(),
get(),
get(),
get(),
courseId,
infoType,
)
}

viewModel { (courseId: String?, infoType: String?) ->
SignUpViewModel(get(), get(), get(), get(), get(), get(), get(), courseId, infoType)
SignUpViewModel(get(), get(), get(), get(), get(), get(), get(), get(), courseId, infoType)
}
viewModel { RestorePasswordViewModel(get(), get(), get(), get()) }

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package org.openedx.auth.presentation

import androidx.compose.ui.text.intl.Locale
import org.openedx.auth.R
import org.openedx.core.ApiConstants
import org.openedx.core.config.Config
import org.openedx.core.domain.model.RegistrationField
import org.openedx.core.domain.model.RegistrationFieldType
import org.openedx.core.system.ResourceManager

class AgreementProvider(
private val config: Config,
private val resourceManager: ResourceManager,
) {
internal fun getAgreement(isSignIn: Boolean): RegistrationField? {
val agreementConfig = config.getAgreement(Locale.current.language)
if (agreementConfig.eulaUrl.isBlank()) return null
val agreementRes = if (isSignIn) {
R.string.auth_agreement_signin_in
} else {
R.string.auth_agreement_creating_account
}
val eula = resourceManager.getString(
R.string.auth_cdata_template,
agreementConfig.eulaUrl,
resourceManager.getString(R.string.auth_agreement_eula)
)
val tos = resourceManager.getString(
R.string.auth_cdata_template,
agreementConfig.tosUrl,
resourceManager.getString(R.string.auth_agreement_tos)
)
val privacy = resourceManager.getString(
R.string.auth_cdata_template,
agreementConfig.privacyPolicyUrl,
resourceManager.getString(R.string.auth_agreement_privacy)
)
val text = resourceManager.getString(
agreementRes,
eula,
tos,
config.getPlatformName(),
privacy,
)
return RegistrationField(
name = ApiConstants.RegistrationFields.HONOR_CODE,
label = text,
type = RegistrationFieldType.PLAINTEXT,
placeholder = "",
instructions = "",
exposed = false,
required = false,
restrictions = RegistrationField.Restrictions(),
options = emptyList(),
errorInstructions = ""
)
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.openedx.auth.presentation.signin

import org.openedx.core.domain.model.RegistrationField

/**
* Data class to store UI state of the SignIn screen
*
Expand All @@ -18,4 +20,5 @@ internal data class SignInUIState(
val isLogistrationEnabled: Boolean = false,
val showProgress: Boolean = false,
val loginSuccess: Boolean = false,
val agreement: RegistrationField? = null,
)
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import org.openedx.auth.R
import org.openedx.auth.data.model.AuthType
import org.openedx.auth.domain.interactor.AuthInteractor
import org.openedx.auth.domain.model.SocialAuthResponse
import org.openedx.auth.presentation.AgreementProvider
import org.openedx.auth.presentation.AuthAnalytics
import org.openedx.auth.presentation.sso.OAuthHelper
import org.openedx.core.BaseViewModel
Expand All @@ -38,6 +39,7 @@ class SignInViewModel(
private val appUpgradeNotifier: AppUpgradeNotifier,
private val analytics: AuthAnalytics,
private val oAuthHelper: OAuthHelper,
agreementProvider: AgreementProvider,
config: Config,
val courseId: String?,
val infoType: String?,
Expand All @@ -52,6 +54,7 @@ class SignInViewModel(
isMicrosoftAuthEnabled = config.getMicrosoftConfig().isEnabled(),
isSocialAuthEnabled = config.isSocialAuthEnabled(),
isLogistrationEnabled = config.isPreLoginExperienceEnabled(),
agreement = agreementProvider.getAgreement(isSignIn = true),
)
)
internal val uiState: StateFlow<SignInUIState> = _uiState
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,10 @@ import org.openedx.auth.presentation.signin.SignInUIState
import org.openedx.auth.presentation.ui.LoginTextField
import org.openedx.auth.presentation.ui.SocialAuthView
import org.openedx.core.UIMessage
import org.openedx.core.extension.TextConverter
import org.openedx.core.ui.BackBtn
import org.openedx.core.ui.HandleUIMessage
import org.openedx.core.ui.HyperlinkText
import org.openedx.core.ui.OpenEdXButton
import org.openedx.core.ui.WindowSize
import org.openedx.core.ui.WindowType
Expand Down Expand Up @@ -185,6 +187,17 @@ internal fun LoginScreen(
state,
onEvent,
)
state.agreement?.let {
Spacer(modifier = Modifier.height(24.dp))
val linkedText =
TextConverter.htmlTextToLinkedText(state.agreement.label)
HyperlinkText(
modifier = Modifier.testTag("txt_${state.agreement.name}"),
fullText = linkedText.text,
hyperLinks = linkedText.links,
linkTextColor = MaterialTheme.appColors.primary
)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import org.openedx.core.system.notifier.AppUpgradeEvent

data class SignUpUIState(
val allFields: List<RegistrationField> = emptyList(),
val requiredFields: List<RegistrationField> = emptyList(),
val optionalFields: List<RegistrationField> = emptyList(),
val agreementFields: List<RegistrationField> = emptyList(),
val isFacebookAuthEnabled: Boolean = false,
val isGoogleAuthEnabled: Boolean = false,
val isMicrosoftAuthEnabled: Boolean = false,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.openedx.auth.presentation.signup

import androidx.compose.ui.text.intl.Locale
import androidx.fragment.app.Fragment
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
Expand All @@ -14,11 +15,11 @@ import kotlinx.coroutines.withContext
import org.openedx.auth.data.model.AuthType
import org.openedx.auth.domain.interactor.AuthInteractor
import org.openedx.auth.domain.model.SocialAuthResponse
import org.openedx.auth.presentation.AgreementProvider
import org.openedx.auth.presentation.AuthAnalytics
import org.openedx.auth.presentation.sso.OAuthHelper
import org.openedx.core.ApiConstants
import org.openedx.core.BaseViewModel
import org.openedx.core.R
import org.openedx.core.UIMessage
import org.openedx.core.config.Config
import org.openedx.core.data.storage.CorePreferences
Expand All @@ -28,13 +29,15 @@ import org.openedx.core.extension.isInternetError
import org.openedx.core.system.ResourceManager
import org.openedx.core.system.notifier.AppUpgradeNotifier
import org.openedx.core.utils.Logger
import org.openedx.core.R as coreR

class SignUpViewModel(
private val interactor: AuthInteractor,
private val resourceManager: ResourceManager,
private val analytics: AuthAnalytics,
private val preferencesManager: CorePreferences,
private val appUpgradeNotifier: AppUpgradeNotifier,
private val agreementProvider: AgreementProvider,
private val oAuthHelper: OAuthHelper,
private val config: Config,
val courseId: String?,
Expand Down Expand Up @@ -69,35 +72,62 @@ class SignUpViewModel(
_uiState.update { it.copy(isLoading = true) }
viewModelScope.launch {
try {
val allFields = interactor.getRegistrationFields()
updateFields(interactor.getRegistrationFields())
_uiState.update { state ->
state.copy(
allFields = allFields,
isLoading = false,
)
state.copy(isLoading = false)
}
} catch (e: Exception) {
if (e.isInternetError()) {
_uiMessage.emit(
UIMessage.SnackBarMessage(
resourceManager.getString(R.string.core_error_no_connection)
resourceManager.getString(coreR.string.core_error_no_connection)
)
)
} else {
_uiMessage.emit(
UIMessage.SnackBarMessage(
resourceManager.getString(R.string.core_error_unknown_error)
resourceManager.getString(coreR.string.core_error_unknown_error)
)
)
}
}
}
}

private fun updateFields(allFields: List<RegistrationField>) {
val mutableAllFields = allFields.toMutableList()
val requiredFields = mutableListOf<RegistrationField>()
val optionalFields = mutableListOf<RegistrationField>()
val agreementFields = mutableListOf<RegistrationField>()
val agreement = agreementProvider.getAgreement(isSignIn = false)
if (agreement != null) {
val honourCode = allFields.find { it.name == ApiConstants.RegistrationFields.HONOR_CODE }
val marketingEmails = allFields.find { it.name == ApiConstants.RegistrationFields.MARKETING_EMAILS }
mutableAllFields.remove(honourCode)
requiredFields.addAll(mutableAllFields.filter { it.required })
optionalFields.addAll(mutableAllFields.filter { !it.required })
requiredFields.remove(marketingEmails)
optionalFields.remove(marketingEmails)
marketingEmails?.let { agreementFields.add(it) }
agreementFields.add(agreement)
} else {
requiredFields.addAll(mutableAllFields.filter { it.required })
optionalFields.addAll(mutableAllFields.filter { !it.required })
}
_uiState.update { state ->
state.copy(
allFields = mutableAllFields,
requiredFields = requiredFields,
optionalFields = optionalFields,
agreementFields = agreementFields,
)
}
}

fun register() {
analytics.createAccountClickedEvent("")
val mapFields = uiState.value.allFields.associate { it.name to it.placeholder } +
mapOf(ApiConstants.HONOR_CODE to true.toString())
mapOf(ApiConstants.RegistrationFields.HONOR_CODE to true.toString())
val resultMap = mapFields.toMutableMap()
uiState.value.allFields.filter { !it.required }.forEach { (k, _) ->
if (mapFields[k].isNullOrEmpty()) {
Expand Down Expand Up @@ -137,13 +167,13 @@ class SignUpViewModel(
if (e.isInternetError()) {
_uiMessage.emit(
UIMessage.SnackBarMessage(
resourceManager.getString(R.string.core_error_no_connection)
resourceManager.getString(coreR.string.core_error_no_connection)
)
)
} else {
_uiMessage.emit(
UIMessage.SnackBarMessage(
resourceManager.getString(R.string.core_error_unknown_error)
resourceManager.getString(coreR.string.core_error_unknown_error)
)
)
}
Expand Down Expand Up @@ -178,18 +208,18 @@ class SignUpViewModel(
runCatching {
interactor.loginSocial(socialAuth.accessToken, socialAuth.authType)
}.onFailure {
val fields = uiState.value.allFields.toMutableList()
.filter { field -> field.type != RegistrationFieldType.PASSWORD }
updateField(ApiConstants.NAME, socialAuth.name)
updateField(ApiConstants.EMAIL, socialAuth.email)
setErrorInstructions(emptyMap())
_uiState.update {
val fields = it.allFields.toMutableList()
.filter { field -> field.type != RegistrationFieldType.PASSWORD }
updateField(ApiConstants.NAME, socialAuth.name)
updateField(ApiConstants.EMAIL, socialAuth.email)
setErrorInstructions(emptyMap())
it.copy(
isLoading = false,
socialAuth = socialAuth,
allFields = fields
)
}
updateFields(fields)
}.onSuccess {
setUserId()
analytics.userLoginEvent(socialAuth.authType.methodName)
Expand All @@ -208,12 +238,8 @@ class SignUpViewModel(
updatedFields.add(it.copy(errorInstructions = ""))
}
}
_uiState.update { state ->
state.copy(
allFields = updatedFields,
isLoading = false,
)
}
updateFields(updatedFields)
_uiState.update { it.copy(isLoading = false) }
}

private fun collectAppUpgradeEvent() {
Expand All @@ -231,15 +257,13 @@ class SignUpViewModel(
}

fun updateField(key: String, value: String) {
_uiState.update {
val updatedFields = uiState.value.allFields.toMutableList().map { field ->
if (field.name == key) {
field.copy(placeholder = value)
} else {
field
}
val updatedFields = uiState.value.allFields.toMutableList().map { field ->
if (field.name == key) {
field.copy(placeholder = value)
} else {
field
}
it.copy(allFields = updatedFields)
}
updateFields(updatedFields)
}
}
Loading

0 comments on commit ce6740e

Please sign in to comment.