From a30d949792107308a413e13c2f38b6a845019ba3 Mon Sep 17 00:00:00 2001
From: Konstantin Ivanov <54908981+konstantiniiv@users.noreply.github.com>
Date: Tue, 13 May 2025 10:29:37 +0200
Subject: [PATCH] DROID-3629 Onboarding | Email screen (#2395)
---
.../signup/OnboardingSoulCreationDI.kt | 12 +
.../ui/onboarding/OnboardingFragment.kt | 106 +++++++-
.../ui/onboarding/OnboardingNavigation.kt | 2 +-
.../ui/onboarding/OnboardingScreenContract.kt | 3 +-
.../screens/signup/OnboardingEmailScreen.kt | 240 ++++++++++++++++++
.../signup/OnboardingMnemonicPhraseScreen.kt | 1 -
.../signup/OnboardingSetProfileNameScreen.kt | 9 +-
.../anytypeio/anytype/core_models/Command.kt | 3 +-
.../anytype/core_ui/OnBoardingColors.kt | 6 +-
.../core_ui/views/TypographyCompose.kt | 12 +-
.../domain/payments/SetMembershipEmail.kt | 6 +-
.../resources/StringResourceProvider.kt | 1 +
localization/src/main/res/values/strings.xml | 11 +-
.../middleware/interactor/Middleware.kt | 3 +-
.../OnboardingSetProfileNameViewModel.kt | 89 +++++--
.../util/StringResourceProviderImpl.kt | 4 +
16 files changed, 474 insertions(+), 34 deletions(-)
create mode 100644 app/src/main/java/com/anytypeio/anytype/ui/onboarding/screens/signup/OnboardingEmailScreen.kt
diff --git a/app/src/main/java/com/anytypeio/anytype/di/feature/onboarding/signup/OnboardingSoulCreationDI.kt b/app/src/main/java/com/anytypeio/anytype/di/feature/onboarding/signup/OnboardingSoulCreationDI.kt
index 15575ac2e0..8411126173 100644
--- a/app/src/main/java/com/anytypeio/anytype/di/feature/onboarding/signup/OnboardingSoulCreationDI.kt
+++ b/app/src/main/java/com/anytypeio/anytype/di/feature/onboarding/signup/OnboardingSoulCreationDI.kt
@@ -3,6 +3,7 @@ package com.anytypeio.anytype.di.feature.onboarding.signup
import androidx.lifecycle.ViewModelProvider
import com.anytypeio.anytype.CrashReporter
import com.anytypeio.anytype.analytics.base.Analytics
+import com.anytypeio.anytype.core_utils.di.scope.PerScreen
import com.anytypeio.anytype.di.common.ComponentDependencies
import com.anytypeio.anytype.domain.account.AwaitAccountStartManager
import com.anytypeio.anytype.domain.auth.interactor.CreateAccount
@@ -16,7 +17,9 @@ import com.anytypeio.anytype.domain.device.PathProvider
import com.anytypeio.anytype.domain.misc.LocaleProvider
import com.anytypeio.anytype.domain.multiplayer.UserPermissionProvider
import com.anytypeio.anytype.domain.`object`.ImportGetStartedUseCase
+import com.anytypeio.anytype.domain.payments.SetMembershipEmail
import com.anytypeio.anytype.domain.platform.InitialParamsProvider
+import com.anytypeio.anytype.domain.resources.StringResourceProvider
import com.anytypeio.anytype.domain.search.RelationsSubscriptionManager
import com.anytypeio.anytype.domain.spaces.SpaceDeletedStatusWatcher
import com.anytypeio.anytype.domain.subscriptions.GlobalSubscriptionManager
@@ -94,6 +97,14 @@ object OnboardingSoulCreationModule {
dispatchers = dispatchers
)
+ @JvmStatic
+ @Provides
+ @SoulCreationScreenScope
+ fun provideSetMembershipEmail(
+ repo: BlockRepository,
+ dispatchers: AppCoroutineDispatchers
+ ): SetMembershipEmail = SetMembershipEmail(repo = repo, dispatchers = dispatchers)
+
@Module
interface Declarations {
@Binds
@@ -119,6 +130,7 @@ interface OnboardingSoulCreationDependencies : ComponentDependencies {
fun userPermissionProvider(): UserPermissionProvider
fun awaitAccountStartManager(): AwaitAccountStartManager
fun globalSubscriptionManager(): GlobalSubscriptionManager
+ fun stringResourceProvider(): StringResourceProvider
}
@Scope
diff --git a/app/src/main/java/com/anytypeio/anytype/ui/onboarding/OnboardingFragment.kt b/app/src/main/java/com/anytypeio/anytype/ui/onboarding/OnboardingFragment.kt
index 8d3fdc9bc8..db158000b0 100644
--- a/app/src/main/java/com/anytypeio/anytype/ui/onboarding/OnboardingFragment.kt
+++ b/app/src/main/java/com/anytypeio/anytype/ui/onboarding/OnboardingFragment.kt
@@ -66,7 +66,6 @@ import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.fragment.findNavController
import androidx.navigation.navArgument
-import androidx.navigation.navOptions
import com.anytypeio.anytype.BuildConfig.USE_EDGE_TO_EDGE
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_models.Id
@@ -74,7 +73,6 @@ import com.anytypeio.anytype.core_models.NO_VALUE
import com.anytypeio.anytype.core_ui.BuildConfig.LIBRARY_PACKAGE_NAME
import com.anytypeio.anytype.core_ui.MNEMONIC_WORD_COUNT
import com.anytypeio.anytype.core_ui.MnemonicPhrasePaletteColors
-import com.anytypeio.anytype.core_ui.foundation.GenericAlert
import com.anytypeio.anytype.core_ui.views.BaseAlertDialog
import com.anytypeio.anytype.core_utils.ext.argOrNull
import com.anytypeio.anytype.core_utils.ext.shareFirstFileFromPath
@@ -94,6 +92,7 @@ import com.anytypeio.anytype.ui.home.HomeScreenFragment
import com.anytypeio.anytype.ui.onboarding.screens.AuthScreenWrapper
import com.anytypeio.anytype.ui.onboarding.screens.signin.RecoveryScreenWrapper
import com.anytypeio.anytype.ui.onboarding.screens.signup.MnemonicPhraseScreenWrapper
+import com.anytypeio.anytype.ui.onboarding.screens.signup.SetEmailWrapper
import com.anytypeio.anytype.ui.onboarding.screens.signup.SetProfileNameWrapper
import com.anytypeio.anytype.ui.vault.VaultFragment
import com.google.android.exoplayer2.ExoPlayer
@@ -377,6 +376,40 @@ class OnboardingFragment : Fragment() {
)
BackHandler { onBackClicked() }
}
+ composable(
+ route = "${OnboardingNavigation.setEmail}?$ONBOARDING_NAME_PARAM={$ONBOARDING_NAME_PARAM}",
+ arguments = listOf(
+ navArgument(ONBOARDING_NAME_PARAM) {
+ type = NavType.StringType
+ defaultValue = ""
+ nullable = false
+ }
+ ),
+ enterTransition = {
+ fadeIn(tween(ANIMATION_LENGTH_FADE))
+ },
+ exitTransition = {
+ fadeOut(tween(ANIMATION_LENGTH_FADE))
+ }
+ ) {
+ val focus = LocalFocusManager.current
+ val onBackClicked : () -> Unit = {
+ val lastDestination = navController.currentBackStackEntry
+ if (lastDestination?.destination?.route?.startsWith(OnboardingNavigation.setEmail) == true) {
+ focus.clearFocus(true)
+ navController.popBackStack()
+ } else {
+ Timber.d("Skipping exit click...")
+ }
+ }
+ currentPage.value = OnboardingPage.SET_EMAIL
+ backButtonCallback.value = onBackClicked
+ AddEmail(
+ navController = navController,
+ onBackClicked = onBackClicked
+ )
+ BackHandler { onBackClicked() }
+ }
}
}
@@ -555,6 +588,16 @@ class OnboardingFragment : Fragment() {
is OnboardingSetProfileNameViewModel.Navigation.GoBack -> {
//
}
+
+ is OnboardingSetProfileNameViewModel.Navigation.NavigateToAddEmailScreen -> {
+ if (keyboardInsets.getBottom(density) > 0) {
+ focusManager.clearFocus(force = true)
+ delay(KEYBOARD_HIDE_DELAY)
+ }
+ navController.navigate(
+ route = "${OnboardingNavigation.setEmail}?$ONBOARDING_NAME_PARAM=${command.name}",
+ )
+ }
}
}
}
@@ -731,6 +774,63 @@ class OnboardingFragment : Fragment() {
}
}
+ @Composable
+ private fun AddEmail(
+ navController: NavHostController,
+ onBackClicked: () -> Unit
+ ) {
+ val component = componentManager().onboardingSoulCreationComponent
+ val vm = daggerViewModel { component.get().getViewModel() }
+
+ val focusManager = LocalFocusManager.current
+ val keyboardInsets = WindowInsets.ime
+ val density = LocalDensity.current
+
+ val name = navController.currentBackStackEntry?.arguments?.getString(ONBOARDING_NAME_PARAM) ?: ""
+
+ SetEmailWrapper(
+ viewModel = vm,
+ name = name,
+ onBackClicked = onBackClicked
+ )
+
+ LaunchedEffect(Unit) {
+ vm.navigation.collect { command ->
+ when (command) {
+ is OnboardingSetProfileNameViewModel.Navigation.NavigateToMnemonic -> {
+ if (keyboardInsets.getBottom(density) > 0) {
+ focusManager.clearFocus(force = true)
+ delay(KEYBOARD_HIDE_DELAY)
+ }
+ val space = command.space
+ val startingObject = command.startingObject
+ navController.navigate(
+ route = buildString {
+ append("${OnboardingNavigation.mnemonic}?$ONBOARDING_SPACE_PARAM=${space.id}")
+ startingObject?.let { append("&$ONBOARDING_STARTING_OBJECT_PARAM=${it}") }
+ }
+ )
+ }
+ is OnboardingSetProfileNameViewModel.Navigation.GoBack -> {
+ //
+ }
+
+ is OnboardingSetProfileNameViewModel.Navigation.NavigateToAddEmailScreen -> {
+ //do nothing
+ }
+ }
+ }
+ }
+ LaunchedEffect(Unit) {
+ vm.toasts.collect {
+ toast(it)
+ }
+ }
+ DisposableEffect(Unit) {
+ onDispose { component.release() }
+ }
+ }
+
private fun getVideoPlayer(context: Context, videoPath: Uri): Player {
val player = ExoPlayer.Builder(context).build()
val source = DefaultDataSource.Factory(
@@ -772,6 +872,8 @@ class OnboardingFragment : Fragment() {
private const val ONBOARDING_SPACE_PARAM = "space"
private const val ONBOARDING_STARTING_OBJECT_PARAM = "startingObject"
+
+ private const val ONBOARDING_NAME_PARAM = "name"
}
}
diff --git a/app/src/main/java/com/anytypeio/anytype/ui/onboarding/OnboardingNavigation.kt b/app/src/main/java/com/anytypeio/anytype/ui/onboarding/OnboardingNavigation.kt
index 98d850a282..62679e1237 100644
--- a/app/src/main/java/com/anytypeio/anytype/ui/onboarding/OnboardingNavigation.kt
+++ b/app/src/main/java/com/anytypeio/anytype/ui/onboarding/OnboardingNavigation.kt
@@ -6,5 +6,5 @@ object OnboardingNavigation {
const val void = "void"
const val mnemonic = "mnemonic"
const val setProfileName = "createSoul"
- const val enterTheVoid = "enterTheVoid"
+ const val setEmail = "setEmail"
}
\ No newline at end of file
diff --git a/app/src/main/java/com/anytypeio/anytype/ui/onboarding/OnboardingScreenContract.kt b/app/src/main/java/com/anytypeio/anytype/ui/onboarding/OnboardingScreenContract.kt
index c9f445a508..6acc0f34a4 100644
--- a/app/src/main/java/com/anytypeio/anytype/ui/onboarding/OnboardingScreenContract.kt
+++ b/app/src/main/java/com/anytypeio/anytype/ui/onboarding/OnboardingScreenContract.kt
@@ -7,5 +7,6 @@ enum class OnboardingPage(val num: Int, val visible: Boolean) {
SET_PROFILE_NAME(1, false),
SOUL_CREATION_ANIM(4, false),
RECOVERY(5, false),
- ENTER_THE_VOID(6, false)
+ ENTER_THE_VOID(6, false),
+ SET_EMAIL(3, false)
}
\ No newline at end of file
diff --git a/app/src/main/java/com/anytypeio/anytype/ui/onboarding/screens/signup/OnboardingEmailScreen.kt b/app/src/main/java/com/anytypeio/anytype/ui/onboarding/screens/signup/OnboardingEmailScreen.kt
new file mode 100644
index 0000000000..d034904c77
--- /dev/null
+++ b/app/src/main/java/com/anytypeio/anytype/ui/onboarding/screens/signup/OnboardingEmailScreen.kt
@@ -0,0 +1,240 @@
+package com.anytypeio.anytype.ui.onboarding.screens.signup
+
+import android.util.Patterns
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.imePadding
+import androidx.compose.foundation.layout.navigationBars
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.windowInsetsPadding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.text.KeyboardActions
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextFieldDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalSoftwareKeyboardController
+import androidx.compose.ui.res.colorResource
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.input.TextFieldValue
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.anytypeio.anytype.R
+import com.anytypeio.anytype.core_ui.common.DefaultPreviews
+import com.anytypeio.anytype.core_ui.foundation.noRippleClickable
+import com.anytypeio.anytype.core_ui.views.ButtonSize
+import com.anytypeio.anytype.core_ui.views.Caption1Regular
+import com.anytypeio.anytype.core_ui.views.HeadlineTitleSemibold
+import com.anytypeio.anytype.core_ui.views.OnBoardingButtonPrimary
+import com.anytypeio.anytype.core_ui.views.OnBoardingButtonSecondary
+import com.anytypeio.anytype.core_ui.views.PreviewTitle1Regular
+import com.anytypeio.anytype.core_ui.views.UXBody
+import com.anytypeio.anytype.presentation.onboarding.signup.OnboardingSetProfileNameViewModel
+import kotlin.also
+import kotlin.text.isNotEmpty
+
+@Composable
+fun SetEmailWrapper(
+ viewModel: OnboardingSetProfileNameViewModel,
+ name: String,
+ onBackClicked: () -> Unit,
+) {
+ OnboardingEmailScreen(
+ onContinueClicked = { email ->
+ viewModel.onEmailContinueClicked(
+ name = name,
+ email = email
+ )
+ },
+ onSkipClicked = {
+ viewModel.onEmailSkippedClicked(
+ name = name,
+ )
+ },
+ isLoading = viewModel.state
+ .collectAsStateWithLifecycle()
+ .value is OnboardingSetProfileNameViewModel.ScreenState.Loading,
+ onBackClicked = onBackClicked
+ )
+}
+
+@Composable
+fun OnboardingEmailScreen(
+ onContinueClicked: (String) -> Unit,
+ onSkipClicked: () -> Unit,
+ onBackClicked: () -> Unit,
+ isLoading: Boolean
+) {
+ var innerValue by remember { mutableStateOf(TextFieldValue()) }
+ var isError by remember { mutableStateOf(false) }
+ val focusRequester = remember { FocusRequester() }
+ val keyboardController = LocalSoftwareKeyboardController.current
+
+ fun isValidEmail(email: String): Boolean {
+ return Patterns.EMAIL_ADDRESS.matcher(email).matches()
+ }
+
+ fun validateAndSubmit() {
+ if (isValidEmail(innerValue.text)) {
+ isError = false
+ focusRequester.freeFocus()
+ keyboardController?.hide()
+ onContinueClicked(innerValue.text)
+ } else {
+ isError = true
+ }
+ }
+
+ Box(
+ modifier = Modifier
+ .windowInsetsPadding(WindowInsets.navigationBars)
+ .imePadding()
+ //.background(color = colorResource(id = R.color.black))
+ .fillMaxSize()
+ ) {
+ Column {
+ Spacer(modifier = Modifier.height(148.dp))
+ Text(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp),
+ text = stringResource(R.string.onboarding_email_add_title),
+ color = colorResource(id = R.color.text_white),
+ style = HeadlineTitleSemibold,
+ textAlign = TextAlign.Center
+ )
+ Spacer(modifier = Modifier.height(8.dp))
+ Text(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 20.dp),
+ text = stringResource(R.string.onboarding_email_add_description),
+ style = UXBody,
+ color = colorResource(id = R.color.text_white),
+ textAlign = TextAlign.Center
+ )
+ Spacer(modifier = Modifier.height(32.dp))
+ OutlinedTextField(
+ value = innerValue,
+ onValueChange = { input ->
+ innerValue = input
+ isError = false
+ },
+ shape = RoundedCornerShape(size = 16.dp),
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 20.dp)
+ .focusRequester(focusRequester),
+ placeholder = {
+ Text(
+ text = stringResource(id = R.string.onboarding_enter_email),
+ style = PreviewTitle1Regular,
+ color = colorResource(id = R.color.text_tertiary)
+ )
+ },
+ textStyle = PreviewTitle1Regular.copy(
+ color = Color(0xFFC2C2C2)
+ ),
+ singleLine = true,
+ isError = isError,
+ supportingText = {
+ if (isError) {
+ Text(
+ text = stringResource(id = R.string.onboarding_email_error),
+ color = colorResource(id = R.color.palette_system_red),
+ style = Caption1Regular
+ )
+ }
+ },
+ colors = TextFieldDefaults.colors(
+ disabledTextColor = colorResource(id = R.color.text_primary),
+ cursorColor = Color(0xFFC2C2C2),
+ focusedContainerColor = Color(0xFF212121),
+ unfocusedContainerColor = Color(0xFF212121),
+ errorContainerColor = Color(0xFF212121),
+ focusedIndicatorColor = Color.Transparent,
+ unfocusedIndicatorColor = Color.Transparent,
+ errorIndicatorColor = Color.Transparent
+ ),
+ keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
+ keyboardActions = KeyboardActions {
+ validateAndSubmit()
+ }
+ )
+ Spacer(modifier = Modifier.height(32.dp))
+ }
+ Image(
+ modifier = Modifier
+ .align(Alignment.TopStart)
+ .padding(top = 16.dp, start = 9.dp)
+ .noRippleClickable {
+ onBackClicked()
+ },
+ painter = painterResource(id = R.drawable.ic_back_onboarding_32),
+ contentDescription = stringResource(R.string.content_description_back_button_icon)
+ )
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(
+ start = 20.dp,
+ end = 20.dp,
+ bottom = 20.dp
+ )
+ .align(Alignment.BottomCenter)
+ ) {
+ OnBoardingButtonPrimary(
+ text = stringResource(id = R.string.onboarding_button_continue),
+ onClick = {
+ validateAndSubmit()
+ },
+ size = ButtonSize.Large,
+ modifier = Modifier.fillMaxWidth(),
+ isLoading = isLoading,
+ enabled = innerValue.text.isNotEmpty()
+ )
+ Spacer(modifier = Modifier.height(8.dp))
+ OnBoardingButtonSecondary(
+ text = stringResource(id = R.string.onboarding_button_skip),
+ onClick = {
+ onSkipClicked().also {
+ focusRequester.freeFocus()
+ }
+ },
+ textColor = colorResource(id = R.color.text_white),
+ size = ButtonSize.Large,
+ modifier = Modifier.fillMaxWidth(),
+ )
+ }
+ }
+}
+
+@DefaultPreviews
+@Composable
+private fun SetProfileNameScreenPreview() {
+ OnboardingEmailScreen(
+ onContinueClicked = {},
+ onBackClicked = {},
+ onSkipClicked = {},
+ isLoading = false
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/anytypeio/anytype/ui/onboarding/screens/signup/OnboardingMnemonicPhraseScreen.kt b/app/src/main/java/com/anytypeio/anytype/ui/onboarding/screens/signup/OnboardingMnemonicPhraseScreen.kt
index b6906bab5b..cb8dc378b7 100644
--- a/app/src/main/java/com/anytypeio/anytype/ui/onboarding/screens/signup/OnboardingMnemonicPhraseScreen.kt
+++ b/app/src/main/java/com/anytypeio/anytype/ui/onboarding/screens/signup/OnboardingMnemonicPhraseScreen.kt
@@ -42,7 +42,6 @@ import com.anytypeio.anytype.core_ui.extensions.conditional
import com.anytypeio.anytype.core_ui.foundation.noRippleClickable
import com.anytypeio.anytype.core_ui.views.BodyRegular
import com.anytypeio.anytype.core_ui.views.ButtonSize
-import com.anytypeio.anytype.core_ui.views.Caption1Regular
import com.anytypeio.anytype.core_ui.views.HeadlineHeading
import com.anytypeio.anytype.core_ui.views.HeadlineOnBoardingDescription
import com.anytypeio.anytype.core_ui.views.OnBoardingButtonPrimary
diff --git a/app/src/main/java/com/anytypeio/anytype/ui/onboarding/screens/signup/OnboardingSetProfileNameScreen.kt b/app/src/main/java/com/anytypeio/anytype/ui/onboarding/screens/signup/OnboardingSetProfileNameScreen.kt
index 99d8da14c6..720951e8cb 100644
--- a/app/src/main/java/com/anytypeio/anytype/ui/onboarding/screens/signup/OnboardingSetProfileNameScreen.kt
+++ b/app/src/main/java/com/anytypeio/anytype/ui/onboarding/screens/signup/OnboardingSetProfileNameScreen.kt
@@ -45,12 +45,13 @@ fun SetProfileNameWrapper(
viewModel: OnboardingSetProfileNameViewModel,
onBackClicked: () -> Unit,
) {
- val defaultSpaceName = stringResource(id = R.string.onboarding_my_first_space)
+ val name = remember { mutableStateOf("") }
+
SetProfileNameScreen(
- onNextClicked = { name ->
+ onNextClicked = { inputName ->
+ name.value = inputName
viewModel.onNextClicked(
- name = name,
- spaceName = defaultSpaceName
+ name = inputName
)
},
isLoading = viewModel.state
diff --git a/core-models/src/main/java/com/anytypeio/anytype/core_models/Command.kt b/core-models/src/main/java/com/anytypeio/anytype/core_models/Command.kt
index 5dea7b8728..2cc8b90d5c 100644
--- a/core-models/src/main/java/com/anytypeio/anytype/core_models/Command.kt
+++ b/core-models/src/main/java/com/anytypeio/anytype/core_models/Command.kt
@@ -550,7 +550,8 @@ sealed class Command {
data class Finalize(val name: String, val nameType: NameServiceNameType) : Membership()
data class GetVerificationEmail(
val email: String,
- val subscribeToNewsletter: Boolean
+ val subscribeToNewsletter: Boolean,
+ val isFromOnboarding: Boolean
) : Membership()
data class VerifyEmailCode(val code: String) : Membership()
diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/OnBoardingColors.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/OnBoardingColors.kt
index c5f84de3d7..48b6c97fac 100644
--- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/OnBoardingColors.kt
+++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/OnBoardingColors.kt
@@ -11,12 +11,12 @@ val OnboardingSubtitleColor = Color(0xFF505050)
val OnBoardingTextPrimaryColor = Color(0xFFF3F2EC)
val OnBoardingTextSecondaryColor = Color(0x80F3F2EC)
-val ColorButtonPrimaryActive = Color(0xFF252525)
+val ColorButtonPrimaryActive = Color(0xFF3A3A3A)
val ColorButtonPrimaryPressed = Color(0xFF464646)
val ColorButtonPrimaryText = Color(0xFFF2F3EC)
-val ColorButtonPrimaryInactive = Color(0xFF1F1E1D)
-val ColorButtonPrimaryInactiveText = Color(0xFF64635B)
+val ColorButtonPrimaryInactive = Color(0xFF1F1F1F)
+val ColorButtonPrimaryInactiveText = Color(0xFF646464)
val ColorButtonSecondaryActive = Color.Transparent
val ColorButtonSecondaryPressed = Color(0xFF1F1E1C)
diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/views/TypographyCompose.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/views/TypographyCompose.kt
index ed4907c9d0..4c3067a46c 100644
--- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/views/TypographyCompose.kt
+++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/views/TypographyCompose.kt
@@ -328,4 +328,14 @@ val ModalTitle = TextStyle(
val AvatarTitle = TextStyle(
fontFamily = fontInterRegular,
fontWeight = FontWeight.W600
-)
\ No newline at end of file
+)
+
+//Content/Headlines/Title Semibold
+val HeadlineTitleSemibold =
+ TextStyle(
+ fontFamily = fontInterSemibold,
+ fontWeight = FontWeight.W600,
+ fontSize = 28.sp,
+ lineHeight = 32.sp,
+ letterSpacing = (-0.017).em
+ )
\ No newline at end of file
diff --git a/domain/src/main/java/com/anytypeio/anytype/domain/payments/SetMembershipEmail.kt b/domain/src/main/java/com/anytypeio/anytype/domain/payments/SetMembershipEmail.kt
index 63713dc1d8..222d9dc5c8 100644
--- a/domain/src/main/java/com/anytypeio/anytype/domain/payments/SetMembershipEmail.kt
+++ b/domain/src/main/java/com/anytypeio/anytype/domain/payments/SetMembershipEmail.kt
@@ -14,13 +14,15 @@ class SetMembershipEmail @Inject constructor(
override suspend fun doWork(params: Params) {
val command = Command.Membership.GetVerificationEmail(
email = params.email,
- subscribeToNewsletter = params.subscribeToNewsletter
+ subscribeToNewsletter = params.subscribeToNewsletter,
+ isFromOnboarding = params.isFromOnboarding
)
repo.membershipGetVerificationEmail(command)
}
data class Params(
val email: String,
- val subscribeToNewsletter: Boolean
+ val subscribeToNewsletter: Boolean,
+ val isFromOnboarding: Boolean = false
)
}
\ No newline at end of file
diff --git a/domain/src/main/java/com/anytypeio/anytype/domain/resources/StringResourceProvider.kt b/domain/src/main/java/com/anytypeio/anytype/domain/resources/StringResourceProvider.kt
index 678d627414..de87cb36f7 100644
--- a/domain/src/main/java/com/anytypeio/anytype/domain/resources/StringResourceProvider.kt
+++ b/domain/src/main/java/com/anytypeio/anytype/domain/resources/StringResourceProvider.kt
@@ -9,4 +9,5 @@ interface StringResourceProvider {
fun getUntitledObjectTitle(): String
fun getSetOfObjectsTitle(): String
fun getPropertiesFormatPrettyString(format: RelationFormat): String
+ fun getDefaultSpaceName(): String
}
\ No newline at end of file
diff --git a/localization/src/main/res/values/strings.xml b/localization/src/main/res/values/strings.xml
index 6ece3a1a8d..ecd55eeb26 100644
--- a/localization/src/main/res/values/strings.xml
+++ b/localization/src/main/res/values/strings.xml
@@ -1132,8 +1132,6 @@
Skip
Not now
Copy to clipboard
- Set your name
- Only seen by people you share something with. There is no central registry of these names.
Anytype Identity
Creating your Identity…
Type your recovery phrase
@@ -2044,4 +2042,13 @@ Please provide specific details of your needs here.
Move to bin
Error while creating account: space is missing
+ Add Your Name
+ Only seen by people you share something with. There is no central registry for these names.
+ Stay in the loop
+ We’d love to share tips, tricks and product updates with you. Your email is never linked to your identity. We won’t share your data. Ever.
+ Enter your email
+ Continue
+ Skip
+ Incorrect email
+
\ No newline at end of file
diff --git a/middleware/src/main/java/com/anytypeio/anytype/middleware/interactor/Middleware.kt b/middleware/src/main/java/com/anytypeio/anytype/middleware/interactor/Middleware.kt
index 98bc62aa8f..3020a5e461 100644
--- a/middleware/src/main/java/com/anytypeio/anytype/middleware/interactor/Middleware.kt
+++ b/middleware/src/main/java/com/anytypeio/anytype/middleware/interactor/Middleware.kt
@@ -2658,7 +2658,8 @@ class Middleware @Inject constructor(
fun membershipGetVerificationEmail(command: Command.Membership.GetVerificationEmail) {
val request = Rpc.Membership.GetVerificationEmail.Request(
email = command.email,
- subscribeToNewsletter = command.subscribeToNewsletter
+ subscribeToNewsletter = command.subscribeToNewsletter,
+ isOnboardingList = command.isFromOnboarding
)
logRequestIfDebug(request)
val (response, time) = measureTimedValue { service.membershipGetVerificationEmail(request) }
diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/onboarding/signup/OnboardingSetProfileNameViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/onboarding/signup/OnboardingSetProfileNameViewModel.kt
index 1a428b1c9c..e6da606b8f 100644
--- a/presentation/src/main/java/com/anytypeio/anytype/presentation/onboarding/signup/OnboardingSetProfileNameViewModel.kt
+++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/onboarding/signup/OnboardingSetProfileNameViewModel.kt
@@ -19,9 +19,12 @@ import com.anytypeio.anytype.domain.device.PathProvider
import com.anytypeio.anytype.domain.misc.LocaleProvider
import com.anytypeio.anytype.domain.`object`.ImportGetStartedUseCase
import com.anytypeio.anytype.domain.`object`.SetObjectDetails
+import com.anytypeio.anytype.domain.payments.SetMembershipEmail
+import com.anytypeio.anytype.domain.resources.StringResourceProvider
import com.anytypeio.anytype.domain.spaces.SetSpaceDetails
import com.anytypeio.anytype.domain.subscriptions.GlobalSubscriptionManager
import com.anytypeio.anytype.domain.workspace.SpaceManager
+import com.anytypeio.anytype.presentation.BuildConfig
import com.anytypeio.anytype.presentation.common.BaseViewModel
import com.anytypeio.anytype.presentation.extension.proceedWithAccountEvent
import com.anytypeio.anytype.presentation.extension.sendAnalyticsOnboardingScreenEvent
@@ -46,7 +49,9 @@ class OnboardingSetProfileNameViewModel @Inject constructor(
private val crashReporter: CrashReporter,
private val localeProvider: LocaleProvider,
private val globalSubscriptionManager: GlobalSubscriptionManager,
- private val spaceManager: SpaceManager
+ private val spaceManager: SpaceManager,
+ private val stringProvider: StringResourceProvider,
+ private val setMembershipEmail: SetMembershipEmail,
) : BaseViewModel() {
init {
@@ -62,22 +67,23 @@ class OnboardingSetProfileNameViewModel @Inject constructor(
val navigation = MutableSharedFlow()
fun onNextClicked(
- name: String,
- spaceName: String
+ name: String
) {
if (state.value !is ScreenState.Loading) {
- proceedWithCreatingWallet(
- name = name,
- spaceName = spaceName
- )
+ viewModelScope.launch {
+ navigation.emit(
+ Navigation.NavigateToAddEmailScreen(
+ name = name
+ )
+ )
+ }
} else {
sendToast(LOADING_MSG)
}
}
private fun proceedWithCreatingWallet(
- name: String,
- spaceName: String
+ name: String
) {
state.value = ScreenState.Loading
setupWallet.invoke(
@@ -92,8 +98,7 @@ class OnboardingSetProfileNameViewModel @Inject constructor(
},
fnR = {
proceedWithCreatingAccount(
- name = name,
- spaceName = spaceName
+ name = name
)
}
)
@@ -101,9 +106,9 @@ class OnboardingSetProfileNameViewModel @Inject constructor(
}
private fun proceedWithCreatingAccount(
- name: String,
- spaceName: String
+ name: String
) {
+ val spaceName = stringProvider.getDefaultSpaceName()
val startTime = System.currentTimeMillis()
val params = CreateAccount.Params(
name = name,
@@ -248,6 +253,53 @@ class OnboardingSetProfileNameViewModel @Inject constructor(
}
}
+ //region Email screen
+ fun onEmailContinueClicked(
+ email: String,
+ name: String,
+ ) {
+ proceedWithSettingEmail(email = email)
+ if (state.value !is ScreenState.Loading) {
+ proceedWithCreatingWallet(
+ name = name
+ )
+ } else {
+ sendToast(LOADING_MSG)
+ }
+ }
+
+ fun onEmailSkippedClicked(
+ name: String
+ ) {
+ if (state.value !is ScreenState.Loading) {
+ proceedWithCreatingWallet(
+ name = name
+ )
+ } else {
+ sendToast(LOADING_MSG)
+ }
+ }
+
+ private fun proceedWithSettingEmail(email: String) {
+ val params = SetMembershipEmail.Params(
+ email = email,
+ subscribeToNewsletter = false,
+ isFromOnboarding = true
+ )
+ viewModelScope.launch {
+ setMembershipEmail.async(params).fold(
+ onSuccess = { Timber.d("Email set") },
+ onFailure = { error ->
+ if (BuildConfig.DEBUG) {
+ sendToast("Error setting email")
+ }
+ Timber.e("Error setting email: $error")
+ }
+ )
+ }
+ }
+ //endregion
+
class Factory @Inject constructor(
private val setObjectDetails: SetObjectDetails,
private val setSpaceDetails: SetSpaceDetails,
@@ -261,7 +313,9 @@ class OnboardingSetProfileNameViewModel @Inject constructor(
private val crashReporter: CrashReporter,
private val localeProvider: LocaleProvider,
private val globalSubscriptionManager: GlobalSubscriptionManager,
- private val spaceManager: SpaceManager
+ private val spaceManager: SpaceManager,
+ private val stringProvider: StringResourceProvider,
+ private val setMembershipEmail: SetMembershipEmail
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun create(modelClass: Class): T {
@@ -278,7 +332,9 @@ class OnboardingSetProfileNameViewModel @Inject constructor(
crashReporter = crashReporter,
localeProvider = localeProvider,
globalSubscriptionManager = globalSubscriptionManager,
- spaceManager = spaceManager
+ spaceManager = spaceManager,
+ stringProvider = stringProvider,
+ setMembershipEmail = setMembershipEmail
) as T
}
}
@@ -292,6 +348,9 @@ class OnboardingSetProfileNameViewModel @Inject constructor(
sealed class Navigation {
data class NavigateToMnemonic(val space: SpaceId, val startingObject: Id?): Navigation()
+ data class NavigateToAddEmailScreen(
+ val name: String
+ ) : Navigation()
data object GoBack: Navigation()
}
diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/util/StringResourceProviderImpl.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/util/StringResourceProviderImpl.kt
index fe9794d551..da1076bc65 100644
--- a/presentation/src/main/java/com/anytypeio/anytype/presentation/util/StringResourceProviderImpl.kt
+++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/util/StringResourceProviderImpl.kt
@@ -51,4 +51,8 @@ class StringResourceProviderImpl @Inject constructor(private val context: Contex
RelationFormat.UNDEFINED -> context.getString(R.string.undefined)
}
}
+
+ override fun getDefaultSpaceName(): String {
+ return context.getString(R.string.onboarding_my_first_space)
+ }
}
\ No newline at end of file