package com.suno.android.ui.screens.onboarding.login import android.net.Uri import androidx.compose.foundation.background import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.TextLayoutResult import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.suno.android.common_ui.components.buttons.AuthenticationOption import com.suno.android.common_ui.components.loading.SunoSpinner import com.suno.android.common_ui.theme.ExtendedTheme import com.suno.android.common_res.R as CommonResR @Composable fun LoginScreen( vm: LoginScreenVM = hiltViewModel(), onOauth: (Boolean, Uri) -> Unit, onPhoneAuthClicked: (Boolean) -> Unit, onTermsClicked: () -> Unit, onPrivacyPolicyClicked: () -> Unit, ) { LaunchedEffect(Unit) { vm.effects.collect { effect -> when (effect) { is LoginScreenEffect.OnOauthClicked -> { onOauth.invoke(effect.isSignUp, effect.redirectUrl) } is LoginScreenEffect.OnPhoneNumberClicked -> { onPhoneAuthClicked.invoke(effect.isSignUp) } } } } val viewState = vm.state.collectAsStateWithLifecycle().value val authLoading = viewState.authLoading Column( modifier = Modifier .background(ExtendedTheme.colors.backgroundPrimary) .fillMaxSize() .padding(32.dp), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.SpaceEvenly, ) { Text( text = if (viewState.isSignUp) { stringResource(CommonResR.string.sign_up_title) } else { stringResource(CommonResR.string.log_in_to_suno) }, textAlign = TextAlign.Center, style = ExtendedTheme.typography.largeHeadline, color = ExtendedTheme.colors.textPrimary, ) Text( text = if (viewState.isSignUp) { stringResource(CommonResR.string.sign_up_subtitle) } else { stringResource(CommonResR.string.or_create_free_account) }, style = ExtendedTheme.typography.body, color = ExtendedTheme.colors.textPrimary, ) Column( verticalArrangement = Arrangement.spacedBy(8.dp), ) { viewState.authenticationOptions.forEach { AuthenticationOption( imageIcon = it.imageRes, displayOptionText = stringResource(it.textRes), onClick = { vm.sendEvent(it.loginMethodEvent) }, ) } } val termsText = stringResource(CommonResR.string.terms_of_service) val privacyText = stringResource(CommonResR.string.privacy_policy) val formattedText = stringResource( CommonResR.string.terms_and_privacy_agreement, termsText, privacyText, ) val annotatedString = buildAnnotatedString { val termsStart = formattedText.indexOf(termsText) val termsEnd = termsStart + termsText.length val privacyStart = formattedText.indexOf(privacyText) val privacyEnd = privacyStart + privacyText.length append(formattedText) addStringAnnotation( tag = "TERMS", annotation = "terms", start = termsStart, end = termsEnd, ) addStyle( style = SpanStyle( color = ExtendedTheme.colors.sunoPink, fontWeight = FontWeight.Bold, ), start = termsStart, end = termsEnd, ) addStringAnnotation( tag = "PRIVACY", annotation = "privacy", start = privacyStart, end = privacyEnd, ) addStyle( style = SpanStyle( color = ExtendedTheme.colors.sunoPink, fontWeight = FontWeight.Bold, ), start = privacyStart, end = privacyEnd, ) } var layoutResult: TextLayoutResult? = null Text( modifier = Modifier .fillMaxWidth() .pointerInput(Unit) { detectTapGestures { tapOffset -> layoutResult?.let { textLayoutResult -> val position = textLayoutResult.getOffsetForPosition(tapOffset) annotatedString.getStringAnnotations(start = position, end = position).firstOrNull() ?.let { annotation -> when (annotation.tag) { "TERMS" -> { if (!authLoading) { onTermsClicked() } } "PRIVACY" -> { if (!authLoading) { onPrivacyPolicyClicked() } } } } } } }, onTextLayout = { layoutResult = it }, text = annotatedString, style = ExtendedTheme.typography.smallBody, textAlign = TextAlign.Center, color = ExtendedTheme.colors.textPrimary, ) } if (authLoading) { Box( modifier = Modifier .background(color = Color.Black.copy(alpha = 0.5f)) .fillMaxSize(), contentAlignment = Alignment.Center, ) { SunoSpinner( modifier = Modifier.size(64.dp), ) } } }