android, desktop: onboarding design changes (#4631)

* android, desktop: onboarding design changes

* changes

* onboardingState + status bar color

* paddings, update texts

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
This commit is contained in:
Stanislav Dmitrenko
2024-08-09 04:25:56 +09:00
committed by GitHub
parent 6a7e573b42
commit 38d46891a1
16 changed files with 319 additions and 249 deletions
@@ -276,12 +276,12 @@ class SimplexApp: Application(), LifecycleEventObserver {
@Suppress("DEPRECATION")
val windowInsetController = ViewCompat.getWindowInsetsController(window.decorView)
val statusBar = (if (hasTop) {
val statusBar = (if (hasTop && appPrefs.onboardingStage.get() == OnboardingStage.OnboardingComplete) {
backgroundColor.mixWith(CurrentColors.value.colors.onBackground, 0.97f)
} else {
backgroundColor
}).toArgb()
val navBar = (if (hasBottom) {
val navBar = (if (hasBottom && appPrefs.onboardingStage.get() == OnboardingStage.OnboardingComplete) {
backgroundColor.mixWith(CurrentColors.value.colors.onBackground, 0.97f)
} else {
backgroundColor
@@ -1,6 +1,8 @@
package chat.simplex.common.views.onboarding
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import chat.simplex.common.model.SharedPreference
import chat.simplex.common.model.User
import chat.simplex.res.MR
@@ -8,8 +10,8 @@ import chat.simplex.res.MR
@Composable
actual fun OnboardingActionButton(user: User?, onboardingStage: SharedPreference<OnboardingStage>, onclick: (() -> Unit)?) {
if (user == null) {
OnboardingActionButton(MR.strings.create_your_profile, onboarding = OnboardingStage.Step2_CreateProfile, true, onclick = onclick)
OnboardingActionButton(Modifier.fillMaxWidth(), labelId = MR.strings.create_your_profile, onboarding = OnboardingStage.Step2_CreateProfile, onclick = onclick)
} else {
OnboardingActionButton(MR.strings.make_private_connection, onboarding = OnboardingStage.OnboardingComplete, true, onclick = onclick)
OnboardingActionButton(Modifier.fillMaxWidth(), labelId = MR.strings.make_private_connection, onboarding = OnboardingStage.OnboardingComplete, onclick = onclick)
}
}
@@ -1,5 +1,7 @@
package chat.simplex.common
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.SizeTransform
import androidx.compose.animation.core.Animatable
import androidx.compose.foundation.*
import androidx.compose.foundation.interaction.MutableInteractionSource
@@ -29,6 +31,8 @@ import chat.simplex.common.views.chat.ChatView
import chat.simplex.common.views.chatlist.*
import chat.simplex.common.views.database.DatabaseErrorView
import chat.simplex.common.views.helpers.*
import chat.simplex.common.views.helpers.ModalManager.Companion.fromEndToStartTransition
import chat.simplex.common.views.helpers.ModalManager.Companion.fromStartToEndTransition
import chat.simplex.common.views.localauth.VerticalDivider
import chat.simplex.common.views.onboarding.*
import chat.simplex.common.views.usersettings.*
@@ -148,17 +152,30 @@ fun MainScreen() {
}
}
}
onboarding == OnboardingStage.Step1_SimpleXInfo -> {
SimpleXInfo(chatModel, onboarding = true)
if (appPlatform.isDesktop) {
ModalManager.fullscreen.showInView()
else -> AnimatedContent(targetState = onboarding,
transitionSpec = {
if (targetState > initialState) {
fromEndToStartTransition()
} else {
fromStartToEndTransition()
}.using(SizeTransform(clip = false))
}
) { state ->
when (state) {
OnboardingStage.OnboardingComplete -> { /* handled out of AnimatedContent block */}
OnboardingStage.Step1_SimpleXInfo -> {
SimpleXInfo(chatModel, onboarding = true)
if (appPlatform.isDesktop) {
ModalManager.fullscreen.showInView()
}
}
OnboardingStage.Step2_CreateProfile -> CreateFirstProfile(chatModel) {}
OnboardingStage.LinkAMobile -> LinkAMobile()
OnboardingStage.Step2_5_SetupDatabasePassphrase -> SetupDatabasePassphrase(chatModel)
OnboardingStage.Step3_CreateSimpleXAddress -> CreateSimpleXAddress(chatModel, null)
OnboardingStage.Step4_SetNotificationsMode -> SetNotificationsMode(chatModel)
}
}
onboarding == OnboardingStage.Step2_CreateProfile -> CreateFirstProfile(chatModel) {}
onboarding == OnboardingStage.LinkAMobile -> LinkAMobile()
onboarding == OnboardingStage.Step2_5_SetupDatabasePassphrase -> SetupDatabasePassphrase(chatModel)
onboarding == OnboardingStage.Step3_CreateSimpleXAddress -> CreateSimpleXAddress(chatModel, null)
onboarding == OnboardingStage.Step4_SetNotificationsMode -> SetNotificationsMode(chatModel)
}
if (appPlatform.isAndroid) {
ModalManager.fullscreen.showInView()
@@ -2,8 +2,8 @@ package chat.simplex.common.views
import SectionTextFooter
import androidx.compose.foundation.*
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.material.*
import androidx.compose.material.MaterialTheme.colors
@@ -17,11 +17,12 @@ import androidx.compose.ui.graphics.SolidColor
import dev.icerock.moko.resources.compose.painterResource
import dev.icerock.moko.resources.compose.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.style.*
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import chat.simplex.common.model.*
import chat.simplex.common.model.ChatController.appPrefs
import chat.simplex.common.model.ChatModel.controller
import chat.simplex.common.platform.*
import chat.simplex.common.ui.theme.*
@@ -112,47 +113,46 @@ fun CreateFirstProfile(chatModel: ChatModel, close: () -> Unit) {
var savedKeyboardState by remember { mutableStateOf(keyboardState) }
ProvideWindowInsets(windowInsetsAnimationsEnabled = true) {
Box(
Column(
modifier = Modifier
.fillMaxSize()
.padding(top = 20.dp)
.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally
) {
val displayName = rememberSaveable { mutableStateOf("") }
val focusRequester = remember { FocusRequester() }
CloseSheetBar(close = {
if (chatModel.users.none { !it.user.hidden }) {
appPrefs.onboardingStage.set(OnboardingStage.Step1_SimpleXInfo)
} else {
close()
}
})
BackHandler(onBack = {
appPrefs.onboardingStage.set(OnboardingStage.Step1_SimpleXInfo)
})
ColumnWithScrollBar(
modifier = Modifier.fillMaxSize()
modifier = if (appPlatform.isAndroid) Modifier.fillMaxSize() else Modifier.widthIn(max = 600.dp).fillMaxHeight(),
) {
/*CloseSheetBar(close = {
if (chatModel.users.isEmpty()) {
chatModel.onboardingStage.value = OnboardingStage.Step1_SimpleXInfo
} else {
close()
}
})*/
val displayName = rememberSaveable { mutableStateOf("") }
val focusRequester = remember { FocusRequester() }
Column(Modifier.padding(horizontal = DEFAULT_PADDING)) {
AppBarTitle(stringResource(MR.strings.create_profile), bottomPadding = DEFAULT_PADDING)
ReadableText(MR.strings.your_profile_is_stored_on_your_device, TextAlign.Center, padding = PaddingValues(), style = MaterialTheme.typography.body1.copy(color = MaterialTheme.colors.secondary))
ReadableText(MR.strings.profile_is_only_shared_with_your_contacts, TextAlign.Center, style = MaterialTheme.typography.body1.copy(color = MaterialTheme.colors.secondary))
ProfileNameField(displayName, stringResource(MR.strings.display_name), { it.trim() == mkValidName(it) }, focusRequester)
Spacer(Modifier.height(DEFAULT_PADDING))
Row(Modifier.padding(bottom = DEFAULT_PADDING_HALF).fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
Text(
stringResource(MR.strings.display_name),
fontSize = 16.sp
)
val name = displayName.value.trim()
val validName = mkValidName(name)
Spacer(Modifier.height(20.dp))
if (name != validName) {
IconButton({ showInvalidNameAlert(mkValidName(displayName.value), displayName) }, Modifier.size(20.dp)) {
Icon(painterResource(MR.images.ic_info), null, tint = MaterialTheme.colors.error)
}
}
}
ProfileNameField(displayName, "", { it.trim() == mkValidName(it) }, focusRequester)
ReadableText(MR.strings.your_profile_is_stored_on_your_device, TextAlign.Start, padding = PaddingValues(), style = MaterialTheme.typography.body1.copy(color = MaterialTheme.colors.secondary))
ReadableText(MR.strings.profile_is_only_shared_with_your_contacts, TextAlign.Start, style = MaterialTheme.typography.body1.copy(color = MaterialTheme.colors.secondary))
}
Spacer(Modifier.fillMaxHeight().weight(1f))
OnboardingButtons(displayName, close)
Column(Modifier.widthIn(max = if (appPlatform.isAndroid) 450.dp else 1000.dp).align(Alignment.CenterHorizontally), horizontalAlignment = Alignment.CenterHorizontally) {
OnboardingActionButton(
if (appPlatform.isAndroid) Modifier.padding(horizontal = DEFAULT_PADDING * 2).fillMaxWidth() else Modifier.widthIn(min = 300.dp),
labelId = MR.strings.create_profile_button,
onboarding = null,
enabled = canCreateProfile(displayName.value),
onclick = { createProfileOnboarding(chat.simplex.common.platform.chatModel, displayName.value, close) }
)
// Reserve space
TextButtonBelowOnboardingButton("", null)
}
LaunchedEffect(Unit) {
delay(300)
@@ -230,28 +230,6 @@ fun createProfileOnboarding(chatModel: ChatModel, displayName: String, close: ()
}
}
@Composable
fun OnboardingButtons(displayName: MutableState<String>, close: () -> Unit) {
Row {
SimpleButtonDecorated(
text = stringResource(MR.strings.about_simplex),
icon = painterResource(MR.images.ic_arrow_back_ios_new),
textDecoration = TextDecoration.None,
fontWeight = FontWeight.Medium
) { chatModel.controller.appPrefs.onboardingStage.set(OnboardingStage.Step1_SimpleXInfo) }
Spacer(Modifier.fillMaxWidth().weight(1f))
val enabled = canCreateProfile(displayName.value)
val createModifier: Modifier = Modifier.clickable(enabled) { createProfileOnboarding(chatModel, displayName.value, close) }.padding(8.dp)
val createColor: Color = if (enabled) MaterialTheme.colors.primary else MaterialTheme.colors.secondary
Surface(shape = RoundedCornerShape(20.dp), color = Color.Transparent, contentColor = LocalContentColor.current) {
Row(verticalAlignment = Alignment.CenterVertically, modifier = createModifier) {
Text(stringResource(MR.strings.create_profile_button), style = MaterialTheme.typography.caption, color = createColor, fontWeight = FontWeight.Medium)
Icon(painterResource(MR.images.ic_arrow_forward_ios), stringResource(MR.strings.create_profile_button), tint = createColor)
}
}
}
}
@Composable
fun ProfileNameField(name: MutableState<String>, placeholder: String = "", isValid: (String) -> Boolean = { true }, focusRequester: FocusRequester? = null) {
var valid by rememberSaveable { mutableStateOf(true) }
@@ -269,15 +247,13 @@ fun ProfileNameField(name: MutableState<String>, placeholder: String = "", isVal
}
val modifier = Modifier
.fillMaxWidth()
.padding(horizontal = DEFAULT_PADDING)
.heightIn(min = 50.dp)
.navigationBarsWithImePadding()
.onFocusChanged { focused = it.isFocused }
Box(
Column(
Modifier
.fillMaxWidth()
.height(52.dp)
.border(border = BorderStroke(1.dp, strokeColor), shape = RoundedCornerShape(50)),
contentAlignment = Alignment.Center
.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
BasicTextField(
value = name.value,
@@ -285,8 +261,31 @@ fun ProfileNameField(name: MutableState<String>, placeholder: String = "", isVal
modifier = if (focusRequester == null) modifier else modifier.focusRequester(focusRequester),
textStyle = TextStyle(fontSize = 18.sp, color = colors.onBackground),
singleLine = true,
cursorBrush = SolidColor(MaterialTheme.colors.secondary)
cursorBrush = SolidColor(MaterialTheme.colors.secondary),
decorationBox = @Composable { innerTextField ->
TextFieldDefaults.TextFieldDecorationBox(
value = name.value,
innerTextField = innerTextField,
placeholder = if (placeholder != "") {{ Text(placeholder, style = MaterialTheme.typography.body1.copy(color = MaterialTheme.colors.secondary, lineHeight = 22.sp)) }} else null,
contentPadding = PaddingValues(),
label = null,
visualTransformation = VisualTransformation.None,
leadingIcon = null,
trailingIcon = if (!valid && placeholder != "") {
{
IconButton({ showInvalidNameAlert(mkValidName(name.value), name) }, Modifier.size(20.dp)) {
Icon(painterResource(MR.images.ic_info), null, tint = MaterialTheme.colors.error)
}
}
} else null,
singleLine = true,
enabled = true,
isError = false,
interactionSource = remember { MutableInteractionSource() },
)
}
)
Divider(color = strokeColor)
}
LaunchedEffect(Unit) {
snapshotFlow { name.value }
@@ -174,28 +174,6 @@ class ModalManager(private val placement: ModalPlacement? = null) {
block()
atomicBoolean.set(false)
}
@OptIn(ExperimentalAnimationApi::class)
private fun fromStartToEndTransition() =
slideInHorizontally(
initialOffsetX = { fullWidth -> -fullWidth },
animationSpec = animationSpec()
) with slideOutHorizontally(
targetOffsetX = { fullWidth -> fullWidth },
animationSpec = animationSpec()
)
@OptIn(ExperimentalAnimationApi::class)
private fun fromEndToStartTransition() =
slideInHorizontally(
initialOffsetX = { fullWidth -> fullWidth },
animationSpec = animationSpec()
) with slideOutHorizontally(
targetOffsetX = { fullWidth -> -fullWidth },
animationSpec = animationSpec()
)
private fun <T> animationSpec() = tween<T>(durationMillis = 250, easing = FastOutSlowInEasing)
// private fun <T> animationSpecFromStart() = tween<T>(durationMillis = 150, easing = FastOutLinearInEasing)
// private fun <T> animationSpecFromEnd() = tween<T>(durationMillis = 100, easing = FastOutSlowInEasing)
@@ -212,5 +190,27 @@ private fun <T> animationSpec() = tween<T>(durationMillis = 250, easing = FastOu
end.closeModals()
fullscreen.closeModals()
}
@OptIn(ExperimentalAnimationApi::class)
fun fromStartToEndTransition() =
slideInHorizontally(
initialOffsetX = { fullWidth -> -fullWidth },
animationSpec = animationSpec()
) with slideOutHorizontally(
targetOffsetX = { fullWidth -> fullWidth },
animationSpec = animationSpec()
)
@OptIn(ExperimentalAnimationApi::class)
fun fromEndToStartTransition() =
slideInHorizontally(
initialOffsetX = { fullWidth -> fullWidth },
animationSpec = animationSpec()
) with slideOutHorizontally(
targetOffsetX = { fullWidth -> -fullWidth },
animationSpec = animationSpec()
)
private fun <T> animationSpec() = tween<T>(durationMillis = 250, easing = FastOutSlowInEasing)
}
}
@@ -1,8 +1,10 @@
package chat.simplex.common.views.helpers
import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import dev.icerock.moko.resources.compose.painterResource
@@ -16,6 +18,7 @@ import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.dp
import chat.simplex.common.platform.appPlatform
import chat.simplex.common.ui.theme.SimpleXTheme
import chat.simplex.res.MR
@@ -1,15 +1,16 @@
package chat.simplex.common.views.onboarding
import SectionBottomSpacer
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.text.font.FontWeight
import dev.icerock.moko.resources.compose.painterResource
import dev.icerock.moko.resources.compose.stringResource
import androidx.compose.ui.text.style.TextAlign
@@ -76,78 +77,94 @@ private fun CreateSimpleXAddressLayout(
nextStep: () -> Unit,
) {
ColumnWithScrollBar(
Modifier.fillMaxSize().padding(top = DEFAULT_PADDING),
Modifier
.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
CloseSheetBar(showClose = false, close = {})
AppBarTitle(stringResource(MR.strings.simplex_address))
Spacer(Modifier.weight(1f))
if (userAddress != null) {
SimpleXLinkQRCode(userAddress.connReqContact)
ShareAddressButton { share(simplexChatLink(userAddress.connReqContact)) }
Spacer(Modifier.height(DEFAULT_PADDING_HALF))
Row {
ShareAddressButton { share(simplexChatLink(userAddress.connReqContact)) }
Spacer(Modifier.width(DEFAULT_PADDING * 2))
ShareViaEmailButton { sendEmail(userAddress) }
}
Spacer(Modifier.height(DEFAULT_PADDING))
Spacer(Modifier.weight(1f))
ShareViaEmailButton { sendEmail(userAddress) }
Spacer(Modifier.weight(1f))
ContinueButton(nextStep)
Column(Modifier.widthIn(max = if (appPlatform.isAndroid) 450.dp else 1000.dp).align(Alignment.CenterHorizontally), horizontalAlignment = Alignment.CenterHorizontally) {
OnboardingActionButton(
modifier = if (appPlatform.isAndroid) Modifier.padding(horizontal = DEFAULT_PADDING * 2).fillMaxWidth() else Modifier.widthIn(min = 300.dp),
labelId = MR.strings.continue_to_next_step,
onboarding = null,
onclick = nextStep
)
// Reserve space
TextButtonBelowOnboardingButton("", null)
}
} else {
CreateAddressButton(createAddress)
TextBelowButton(stringResource(MR.strings.you_can_make_address_visible_via_settings))
Button(createAddress, Modifier, shape = CircleShape, contentPadding = PaddingValues()) {
Icon(painterResource(MR.images.ic_mail_filled), null, Modifier.size(100.dp).background(MaterialTheme.colors.primary, CircleShape).padding(25.dp), tint = Color.White)
}
Spacer(Modifier.height(DEFAULT_PADDING))
Spacer(Modifier.weight(1f))
Text(stringResource(MR.strings.create_simplex_address), style = MaterialTheme.typography.h3, fontWeight = FontWeight.Bold)
TextBelowButton(stringResource(MR.strings.you_can_make_address_visible_via_settings))
Spacer(Modifier.height(DEFAULT_PADDING))
Spacer(Modifier.weight(1f))
SkipButton(nextStep)
}
SectionBottomSpacer()
}
}
@Composable
private fun CreateAddressButton(onClick: () -> Unit) {
TextButton(onClick) {
Text(stringResource(MR.strings.create_simplex_address), style = MaterialTheme.typography.h2, color = MaterialTheme.colors.primary)
Column(Modifier.widthIn(max = if (appPlatform.isAndroid) 450.dp else 1000.dp).align(Alignment.CenterHorizontally), horizontalAlignment = Alignment.CenterHorizontally) {
OnboardingActionButton(
modifier = if (appPlatform.isAndroid) Modifier.padding(horizontal = DEFAULT_PADDING * 2).fillMaxWidth() else Modifier.widthIn(min = 300.dp),
labelId = MR.strings.create_address_button,
onboarding = null,
onclick = createAddress
)
TextButtonBelowOnboardingButton(stringResource(MR.strings.dont_create_address), nextStep)
}
}
}
}
@Composable
fun ShareAddressButton(onClick: () -> Unit) {
SimpleButtonFrame(onClick) {
Icon(
painterResource(MR.images.ic_share_filled), generalGetString(MR.strings.share_verb), tint = MaterialTheme.colors.primary,
modifier = Modifier.padding(end = 8.dp).size(18.dp)
)
Text(stringResource(MR.strings.share_verb), style = MaterialTheme.typography.caption, color = MaterialTheme.colors.primary)
Column(horizontalAlignment = Alignment.CenterHorizontally) {
IconButton(onClick, Modifier.padding(bottom = DEFAULT_PADDING_HALF).border(1.dp, MaterialTheme.colors.secondary.copy(0.1f), CircleShape)) {
Icon(
painterResource(MR.images.ic_share_filled), generalGetString(MR.strings.share_verb), tint = MaterialTheme.colors.primary,
modifier = Modifier.size(50.dp).padding(DEFAULT_PADDING_HALF)
)
}
Text(stringResource(MR.strings.share_verb))
}
}
@Composable
fun ShareViaEmailButton(onClick: () -> Unit) {
SimpleButtonFrame(onClick) {
Icon(
painterResource(MR.images.ic_mail), generalGetString(MR.strings.share_verb), tint = MaterialTheme.colors.primary,
modifier = Modifier.padding(end = 8.dp).size(30.dp)
)
Text(stringResource(MR.strings.invite_friends), style = MaterialTheme.typography.h6, color = MaterialTheme.colors.primary)
Column(horizontalAlignment = Alignment.CenterHorizontally) {
IconButton(onClick, Modifier.padding(bottom = DEFAULT_PADDING_HALF).border(1.dp, MaterialTheme.colors.secondary.copy(0.1f), CircleShape)) {
Icon(
painterResource(MR.images.ic_mail), generalGetString(MR.strings.share_verb), tint = MaterialTheme.colors.primary,
modifier = Modifier.size(50.dp).padding(DEFAULT_PADDING_HALF)
)
}
Text(stringResource(MR.strings.invite_friends_short))
}
}
@Composable
private fun ContinueButton(onClick: () -> Unit) {
SimpleButtonIconEnded(stringResource(MR.strings.continue_to_next_step), painterResource(MR.images.ic_chevron_right), click = onClick)
}
@Composable
private fun SkipButton(onClick: () -> Unit) {
SimpleButtonIconEnded(stringResource(MR.strings.dont_create_address), painterResource(MR.images.ic_chevron_right), click = onClick)
TextBelowButton(stringResource(MR.strings.you_can_create_it_later))
}
@Composable
private fun TextBelowButton(text: String) {
Text(
text,
Modifier
.fillMaxWidth()
.padding(horizontal = DEFAULT_PADDING * 3),
.padding(horizontal = DEFAULT_PADDING * 3, vertical = DEFAULT_PADDING_HALF),
style = MaterialTheme.typography.subtitle1,
color = MaterialTheme.colors.secondary,
textAlign = TextAlign.Center,
)
}
@@ -25,7 +25,7 @@ import dev.icerock.moko.resources.StringResource
fun HowItWorks(user: User?, onboardingStage: SharedPreference<OnboardingStage>? = null) {
ColumnWithScrollBar(Modifier
.fillMaxWidth()
.padding(horizontal = DEFAULT_PADDING),
.padding(DEFAULT_PADDING),
) {
AppBarTitle(stringResource(MR.strings.how_simplex_works), withPadding = false)
ReadableText(MR.strings.many_people_asked_how_can_it_deliver)
@@ -46,6 +46,7 @@ fun HowItWorks(user: User?, onboardingStage: SharedPreference<OnboardingStage>?
}
Spacer(Modifier.fillMaxHeight().weight(1f))
}
Spacer(Modifier.height(DEFAULT_PADDING))
}
}
@@ -10,7 +10,9 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.dp
import chat.simplex.common.model.ChatController.appPrefs
import chat.simplex.common.model.ChatModel
import chat.simplex.common.platform.BackHandler
import chat.simplex.common.platform.chatModel
import chat.simplex.common.ui.theme.DEFAULT_PADDING
import chat.simplex.common.views.helpers.*
@@ -56,7 +58,13 @@ private fun LinkAMobileLayout(
staleQrCode: MutableState<Boolean>,
updateDeviceName: (String) -> Unit,
) {
Column(Modifier.padding(top = 20.dp)) {
Column {
CloseSheetBar(close = {
appPrefs.onboardingStage.set(OnboardingStage.Step1_SimpleXInfo)
})
BackHandler(onBack = {
appPrefs.onboardingStage.set(OnboardingStage.Step1_SimpleXInfo)
})
AppBarTitle(stringResource(if (remember { chatModel.remoteHosts }.isEmpty()) MR.strings.link_a_mobile else MR.strings.linked_mobiles))
Row(Modifier.weight(1f).padding(horizontal = DEFAULT_PADDING * 2), verticalAlignment = Alignment.CenterVertically) {
Column(
@@ -82,11 +90,5 @@ private fun LinkAMobileLayout(
}
}
}
SimpleButtonDecorated(
text = stringResource(MR.strings.about_simplex),
icon = painterResource(MR.images.ic_arrow_back_ios_new),
textDecoration = TextDecoration.None,
fontWeight = FontWeight.Medium
) { chatModel.controller.appPrefs.onboardingStage.set(OnboardingStage.Step1_SimpleXInfo) }
}
}
@@ -17,20 +17,19 @@ import androidx.compose.ui.unit.sp
import chat.simplex.common.model.ChatModel
import chat.simplex.common.model.NotificationsMode
import chat.simplex.common.platform.ColumnWithScrollBar
import chat.simplex.common.platform.appPlatform
import chat.simplex.common.ui.theme.*
import chat.simplex.common.views.helpers.*
import chat.simplex.common.views.usersettings.changeNotificationsMode
import chat.simplex.res.MR
import dev.icerock.moko.resources.StringResource
@Composable
fun SetNotificationsMode(m: ChatModel) {
ColumnWithScrollBar(
modifier = Modifier
.fillMaxSize()
.padding(vertical = 14.dp)
) {
//CloseSheetBar(null)
CloseSheetBar(showClose = false, close = {})
AppBarTitle(stringResource(MR.strings.onboarding_notifications_mode_title))
val currentMode = rememberSaveable { mutableStateOf(NotificationsMode.default) }
Column(Modifier.padding(horizontal = DEFAULT_PADDING * 1f)) {
@@ -47,12 +46,18 @@ fun SetNotificationsMode(m: ChatModel) {
}
}
Spacer(Modifier.fillMaxHeight().weight(1f))
Box(Modifier.fillMaxWidth().padding(bottom = DEFAULT_PADDING_HALF), contentAlignment = Alignment.Center) {
OnboardingActionButton(MR.strings.use_chat, OnboardingStage.OnboardingComplete, false) {
changeNotificationsMode(currentMode.value, m)
}
Column(Modifier.widthIn(max = if (appPlatform.isAndroid) 450.dp else 1000.dp).align(Alignment.CenterHorizontally), horizontalAlignment = Alignment.CenterHorizontally) {
OnboardingActionButton(
modifier = if (appPlatform.isAndroid) Modifier.padding(horizontal = DEFAULT_PADDING * 2).fillMaxWidth() else Modifier,
labelId = MR.strings.use_chat,
onboarding = OnboardingStage.OnboardingComplete,
onclick = {
changeNotificationsMode(currentMode.value, m)
}
)
// Reserve space
TextButtonBelowOnboardingButton("", null)
}
Spacer(Modifier.fillMaxHeight().weight(1f))
}
SetNotificationsModeAdditions()
}
@@ -1,11 +1,8 @@
package chat.simplex.common.views.onboarding
import SectionBottomSpacer
import SectionTextFooter
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
@@ -20,6 +17,7 @@ import dev.icerock.moko.resources.compose.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import chat.simplex.common.model.*
import chat.simplex.common.model.ChatController.appPrefs
import chat.simplex.common.platform.*
import chat.simplex.common.ui.theme.*
import chat.simplex.common.views.database.*
@@ -107,9 +105,10 @@ private fun SetupDatabasePassphraseLayout(
nextStep: () -> Unit,
) {
ColumnWithScrollBar(
Modifier.fillMaxSize().padding(top = DEFAULT_PADDING),
Modifier.fillMaxSize().padding(bottom = DEFAULT_PADDING * 2),
horizontalAlignment = Alignment.CenterHorizontally,
) {
CloseSheetBar(showClose = false, close = {})
AppBarTitle(stringResource(MR.strings.setup_database_passphrase))
Spacer(Modifier.weight(1f))
@@ -184,8 +183,6 @@ private fun SetupDatabasePassphraseLayout(
chatModel.desktopOnboardingRandomPassword.value = true
nextStep()
}
SectionBottomSpacer()
}
}
@@ -209,7 +206,8 @@ private fun SkipButton(disabled: Boolean, onClick: () -> Unit) {
stringResource(MR.strings.you_can_change_it_later),
Modifier
.fillMaxWidth()
.padding(horizontal = DEFAULT_PADDING * 3),
.padding(horizontal = DEFAULT_PADDING * 3)
.padding(top = DEFAULT_PADDING, bottom = DEFAULT_PADDING - 5.dp),
style = MaterialTheme.typography.subtitle1,
color = MaterialTheme.colors.secondary,
textAlign = TextAlign.Center,
@@ -1,23 +1,26 @@
package chat.simplex.common.views.onboarding
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.layout.ContentScale
import dev.icerock.moko.resources.compose.painterResource
import dev.icerock.moko.resources.compose.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.*
import chat.simplex.common.model.*
import chat.simplex.common.platform.ColumnWithScrollBar
import chat.simplex.common.platform.chatModel
import chat.simplex.common.model.ChatController.appPrefs
import chat.simplex.common.platform.*
import chat.simplex.common.ui.theme.*
import chat.simplex.common.views.helpers.*
import chat.simplex.common.views.migration.MigrateToDeviceView
@@ -27,64 +30,67 @@ import dev.icerock.moko.resources.StringResource
@Composable
fun SimpleXInfo(chatModel: ChatModel, onboarding: Boolean = true) {
SimpleXInfoLayout(
user = chatModel.currentUser.value,
onboardingStage = if (onboarding) chatModel.controller.appPrefs.onboardingStage else null,
showModal = { modalView -> { if (onboarding) ModalManager.fullscreen.showModal { modalView(chatModel) } else ModalManager.start.showModal { modalView(chatModel) } } },
)
if (onboarding) {
ModalView({}, showClose = false, endButtons = {
IconButton({ ModalManager.fullscreen.showModal { HowItWorks(chatModel.currentUser.value, null) }}) {
Icon(painterResource(MR.images.ic_info), null, Modifier.size(28.dp), tint = MaterialTheme.colors.primary)
}
}) {
SimpleXInfoLayout(
user = chatModel.currentUser.value,
onboardingStage = chatModel.controller.appPrefs.onboardingStage
)
}
} else {
SimpleXInfoLayout(
user = chatModel.currentUser.value,
onboardingStage = null
)
}
}
@Composable
fun SimpleXInfoLayout(
user: User?,
onboardingStage: SharedPreference<OnboardingStage>?,
showModal: (@Composable (ChatModel) -> Unit) -> (() -> Unit),
onboardingStage: SharedPreference<OnboardingStage>?
) {
ColumnWithScrollBar(
Modifier
.fillMaxSize()
.padding(start = DEFAULT_PADDING , end = DEFAULT_PADDING, top = DEFAULT_PADDING),
.padding(horizontal = 16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Box(Modifier.fillMaxWidth().padding(top = 8.dp, bottom = 10.dp), contentAlignment = Alignment.Center) {
Box(Modifier.widthIn(max = if (appPlatform.isAndroid) 250.dp else 500.dp).padding(top = DEFAULT_PADDING + 8.dp), contentAlignment = Alignment.Center) {
SimpleXLogo()
}
Text(stringResource(MR.strings.next_generation_of_private_messaging), style = MaterialTheme.typography.h2, modifier = Modifier.padding(bottom = 48.dp).padding(horizontal = 36.dp), textAlign = TextAlign.Center)
Spacer(Modifier.weight(1f))
Column {
InfoRow(painterResource(MR.images.privacy), MR.strings.privacy_redefined, MR.strings.first_platform_without_user_ids, width = 80.dp)
InfoRow(painterResource(MR.images.shield), MR.strings.immune_to_spam_and_abuse, MR.strings.people_can_connect_only_via_links_you_share)
Text(
stringResource(MR.strings.next_generation_of_private_messaging),
style = MaterialTheme.typography.h3,
color = MaterialTheme.colors.secondary,
textAlign = TextAlign.Center
)
Spacer(Modifier.weight(1f))
Column(Modifier.fillMaxWidth()) {
InfoRow(painterResource(MR.images.privacy), MR.strings.privacy_redefined, MR.strings.first_platform_without_user_ids, width = 60.dp)
InfoRow(painterResource(MR.images.shield), MR.strings.immune_to_spam_and_abuse, MR.strings.people_can_connect_only_via_links_you_share, width = 46.dp)
InfoRow(painterResource(if (isInDarkTheme()) MR.images.decentralized_light else MR.images.decentralized), MR.strings.decentralized, MR.strings.opensource_protocol_and_code_anybody_can_run_servers)
}
Spacer(Modifier.fillMaxHeight().weight(1f))
if (onboardingStage != null) {
Box(Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
Column(Modifier.padding(horizontal = DEFAULT_PADDING).widthIn(max = if (appPlatform.isAndroid) 450.dp else 1000.dp).align(Alignment.CenterHorizontally), horizontalAlignment = Alignment.CenterHorizontally) {
OnboardingActionButton(user, onboardingStage)
TextButtonBelowOnboardingButton(stringResource(MR.strings.migrate_from_another_device)) {
chatModel.migrationState.value = MigrationToState.PasteOrScanLink
ModalManager.fullscreen.showCustomModal { close -> MigrateToDeviceView(close) }
}
}
Spacer(Modifier.fillMaxHeight().weight(1f))
Box(
Modifier
.fillMaxWidth()
.padding(top = DEFAULT_PADDING), contentAlignment = Alignment.Center
) {
SimpleButtonDecorated(text = stringResource(MR.strings.migrate_from_another_device), icon = painterResource(MR.images.ic_download),
click = {
chatModel.migrationState.value = MigrationToState.PasteOrScanLink
ModalManager.fullscreen.showCustomModal { close -> MigrateToDeviceView(close) } })
}
}
Box(
Modifier
.fillMaxWidth()
.padding(bottom = DEFAULT_PADDING.times(1.5f), top = if (onboardingStage == null) DEFAULT_PADDING else 0.dp), contentAlignment = Alignment.Center
) {
SimpleButtonDecorated(text = stringResource(MR.strings.how_it_works), icon = painterResource(MR.images.ic_info),
click = showModal { HowItWorks(user, onboardingStage) })
}
}
LaunchedEffect(Unit) {
@@ -99,21 +105,23 @@ fun SimpleXLogo() {
Image(
painter = painterResource(if (isInDarkTheme()) MR.images.logo_light else MR.images.logo),
contentDescription = stringResource(MR.strings.image_descr_simplex_logo),
contentScale = ContentScale.FillWidth,
modifier = Modifier
.padding(vertical = DEFAULT_PADDING)
.fillMaxWidth(0.60f)
.fillMaxWidth()
)
}
@Composable
private fun InfoRow(icon: Painter, titleId: StringResource, textId: StringResource, width: Dp = 76.dp) {
private fun InfoRow(icon: Painter, titleId: StringResource, textId: StringResource, width: Dp = 58.dp) {
Row(Modifier.padding(bottom = 27.dp), verticalAlignment = Alignment.Top) {
Spacer(Modifier.width((18.dp + 58.dp - width) / 2))
Image(icon, contentDescription = null, modifier = Modifier
.width(width)
.padding(top = 8.dp, start = 8.dp, end = 24.dp))
Column {
.width(width))
Spacer(Modifier.width((18.dp + 58.dp - width) / 2 + DEFAULT_PADDING_HALF))
Column(Modifier.padding(top = 4.dp), verticalArrangement = Arrangement.spacedBy(DEFAULT_PADDING_HALF)) {
Text(stringResource(titleId), fontWeight = FontWeight.Bold, style = MaterialTheme.typography.h3, lineHeight = 24.sp)
Text(stringResource(textId), lineHeight = 24.sp, style = MaterialTheme.typography.body1)
Text(stringResource(textId), lineHeight = 24.sp, style = MaterialTheme.typography.body1, color = MaterialTheme.colors.secondary)
}
}
}
@@ -123,38 +131,53 @@ expect fun OnboardingActionButton(user: User?, onboardingStage: SharedPreference
@Composable
fun OnboardingActionButton(
modifier: Modifier = Modifier,
labelId: StringResource,
onboarding: OnboardingStage?,
border: Boolean,
enabled: Boolean = true,
icon: Painter? = null,
iconColor: Color = MaterialTheme.colors.primary,
iconColor: Color = Color.White,
onclick: (() -> Unit)?
) {
val modifier = if (border) {
Modifier
.border(border = BorderStroke(1.dp, MaterialTheme.colors.primary), shape = RoundedCornerShape(50))
.padding(
horizontal = if (icon == null) DEFAULT_PADDING * 2 else DEFAULT_PADDING_HALF,
vertical = 4.dp
)
} else {
Modifier
}
SimpleButtonFrame(click = {
onclick?.invoke()
if (onboarding != null) {
ChatController.appPrefs.onboardingStage.set(onboarding)
}
}, modifier) {
Button(
onClick = {
onclick?.invoke()
if (onboarding != null) {
appPrefs.onboardingStage.set(onboarding)
}
},
modifier = modifier,
shape = CircleShape,
enabled = enabled,
// elevation = ButtonDefaults.elevation(defaultElevation = 0.dp, focusedElevation = 0.dp, pressedElevation = 0.dp, hoveredElevation = 0.dp),
contentPadding = PaddingValues(horizontal = if (icon == null) DEFAULT_PADDING * 2 else DEFAULT_PADDING * 1.5f, vertical = DEFAULT_PADDING),
colors = ButtonDefaults.buttonColors(MaterialTheme.colors.primary, disabledBackgroundColor = MaterialTheme.colors.secondary)
) {
if (icon != null) {
Icon(icon, stringResource(labelId), Modifier.padding(end = DEFAULT_PADDING_HALF), tint = iconColor)
}
Text(stringResource(labelId), style = MaterialTheme.typography.h2, color = MaterialTheme.colors.primary, fontSize = 20.sp)
Icon(
painterResource(MR.images.ic_arrow_forward_ios), "next stage", tint = MaterialTheme.colors.primary,
modifier = Modifier.padding(start = DEFAULT_PADDING.div(4)).size(20.dp)
)
Text(stringResource(labelId), style = MaterialTheme.typography.h2, color = Color.White, fontSize = 18.sp, fontWeight = FontWeight.Medium)
}
}
@Composable
fun TextButtonBelowOnboardingButton(text: String, onClick: (() -> Unit)?) {
val state = getKeyboardState()
val topPadding by animateDpAsState(if (appPlatform.isAndroid && state.value == KeyboardState.Opened) 0.dp else DEFAULT_PADDING)
val bottomPadding by animateDpAsState(if (appPlatform.isAndroid && state.value == KeyboardState.Opened) 0.dp else DEFAULT_PADDING * 2)
if ((appPlatform.isAndroid && state.value == KeyboardState.Closed) || topPadding > 0.dp) {
TextButton({ onClick?.invoke() }, Modifier.padding(top = topPadding, bottom = bottomPadding).clip(CircleShape), enabled = onClick != null) {
Text(
text,
Modifier.padding(start = DEFAULT_PADDING_HALF, end = DEFAULT_PADDING_HALF, bottom = 5.dp),
color = MaterialTheme.colors.primary,
fontWeight = FontWeight.Medium,
textAlign = TextAlign.Center
)
}
} else {
// Hide from view when keyboard is open and move the view down
Spacer(Modifier.height(DEFAULT_PADDING * 2))
}
}
@@ -168,8 +191,7 @@ fun PreviewSimpleXInfo() {
SimpleXTheme {
SimpleXInfoLayout(
user = null,
onboardingStage = null,
showModal = { {} }
onboardingStage = null
)
}
}
@@ -870,6 +870,7 @@
<string name="dont_create_address">Don\'t create address</string>
<string name="you_can_create_it_later">You can create it later</string>
<string name="you_can_make_address_visible_via_settings">You can make it visible to your SimpleX contacts via Settings.</string>
<string name="invite_friends_short">Invite</string>
<!-- User profile details - UserProfileView.kt -->
<string name="display_name__field">Profile name:</string>
@@ -904,6 +905,7 @@
<string name="display_name">Enter your name:</string>
<string name="create_profile_button">Create</string>
<string name="create_another_profile_button">Create profile</string>
<string name="create_address_button">Create</string>
<string name="invalid_name">Invalid name!</string>
<string name="correct_name_to">Correct name to %s?</string>
<string name="about_simplex">About SimpleX</string>
@@ -956,13 +958,13 @@
<string name="error_initializing_web_view">Error initializing WebView. Update your system to the new version. Please contact developers.\nError: %s</string>
<!-- SimpleXInfo -->
<string name="next_generation_of_private_messaging">The next generation of private messaging</string>
<string name="next_generation_of_private_messaging">The next generation\nof private messaging</string>
<string name="privacy_redefined">Privacy redefined</string>
<string name="first_platform_without_user_ids">The 1st platform without any user identifiers – private by design.</string>
<string name="immune_to_spam_and_abuse">Immune to spam and abuse</string>
<string name="people_can_connect_only_via_links_you_share">People can connect to you only via the links you share.</string>
<string name="first_platform_without_user_ids">No user identifiers.</string>
<string name="immune_to_spam_and_abuse">Immune to spam</string>
<string name="people_can_connect_only_via_links_you_share">You decide who can connect.</string>
<string name="decentralized">Decentralized</string>
<string name="opensource_protocol_and_code_anybody_can_run_servers">Open-source protocol and code – anybody can run the servers.</string>
<string name="opensource_protocol_and_code_anybody_can_run_servers">Anybody can host servers.</string>
<string name="create_your_profile">Create your profile</string>
<string name="make_private_connection">Make a private connection</string>
<string name="migrate_from_another_device">Migrate from another device</string>
@@ -985,8 +987,8 @@
<string name="onboarding_notifications_mode_periodic">Periodic</string>
<string name="onboarding_notifications_mode_service">Instant</string>
<string name="onboarding_notifications_mode_off_desc"><![CDATA[<b>Best for battery</b>. You will receive notifications only when the app is running (NO background service).]]></string>
<string name="onboarding_notifications_mode_periodic_desc"><![CDATA[<b>Good for battery</b>. Background service checks messages every 10 minutes. You may miss calls or urgent messages.]]></string>
<string name="onboarding_notifications_mode_service_desc"><![CDATA[<b>Uses more battery</b>! Background service always runs – notifications are shown as soon as messages are available.]]></string>
<string name="onboarding_notifications_mode_periodic_desc"><![CDATA[<b>Good for battery</b>. App checks messages every 10 minutes. You may miss calls or urgent messages.]]></string>
<string name="onboarding_notifications_mode_service_desc"><![CDATA[<b>Uses more battery</b>! App always runs in background – notifications are shown instantly.]]></string>
<!-- SetupDatabasePassphrase.kt -->
<string name="setup_database_passphrase">Setup database passphrase</string>
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5f6368"><path d="M142.5-164.5q-22.97 0-40.23-17.27Q85-199.03 85-222v-516q0-22.97 17.27-40.23 17.26-17.27 40.23-17.27h675q22.97 0 40.23 17.27Q875-760.97 875-738v516q0 22.97-17.27 40.23-17.26 17.27-40.23 17.27h-675ZM480-464l337.5-221.5V-738L480-521.5 142.5-738v52.5L480-464Z"/></svg>

After

Width:  |  Height:  |  Size: 380 B

@@ -463,13 +463,13 @@
<string name="callstate_connected">соединено</string>
<string name="callstate_ended">завершен</string>
<!-- SimpleXInfo -->
<string name="next_generation_of_private_messaging">Новое поколение приватных сообщений</string>
<string name="next_generation_of_private_messaging">Новое поколение\nприватных сообщений</string>
<string name="privacy_redefined">Более конфиденциальный</string>
<string name="first_platform_without_user_ids">Первая в мире платформа без идентификаторов пользователей.</string>
<string name="first_platform_without_user_ids">Без идентификаторов пользователей.</string>
<string name="immune_to_spam_and_abuse">Защищен от спама</string>
<string name="people_can_connect_only_via_links_you_share">С Вами можно соединиться только через созданные Вами ссылки.</string>
<string name="people_can_connect_only_via_links_you_share">Вы определяете, кто может соединиться.</string>
<string name="decentralized">Децентрализованный</string>
<string name="opensource_protocol_and_code_anybody_can_run_servers">Открытый протокол и код - кто угодно может запустить сервер.</string>
<string name="opensource_protocol_and_code_anybody_can_run_servers">Кто угодно может запустить сервер.</string>
<string name="create_your_profile">Создать профиль</string>
<string name="make_private_connection">Добавьте контакт</string>
<string name="how_it_works">Как это работает</string>
@@ -1,8 +1,9 @@
package chat.simplex.common.views.onboarding
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import chat.simplex.common.model.ChatModel.controller
import chat.simplex.common.model.SharedPreference
import chat.simplex.common.model.User
@@ -15,10 +16,10 @@ import dev.icerock.moko.resources.compose.painterResource
actual fun OnboardingActionButton(user: User?, onboardingStage: SharedPreference<OnboardingStage>, onclick: (() -> Unit)?) {
if (user == null) {
Row(horizontalArrangement = Arrangement.spacedBy(DEFAULT_PADDING * 2.5f)) {
OnboardingActionButton(MR.strings.link_a_mobile, onboarding = if (controller.appPrefs.initialRandomDBPassphrase.get() && !chatModel.desktopOnboardingRandomPassword.value) OnboardingStage.Step2_5_SetupDatabasePassphrase else OnboardingStage.LinkAMobile, true, icon = painterResource(MR.images.ic_smartphone_300), onclick = onclick)
OnboardingActionButton(MR.strings.create_your_profile, onboarding = OnboardingStage.Step2_CreateProfile, true, icon = painterResource(MR.images.ic_desktop), onclick = onclick)
OnboardingActionButton(labelId = MR.strings.link_a_mobile, onboarding = if (controller.appPrefs.initialRandomDBPassphrase.get() && !chatModel.desktopOnboardingRandomPassword.value) OnboardingStage.Step2_5_SetupDatabasePassphrase else OnboardingStage.LinkAMobile, icon = painterResource(MR.images.ic_smartphone_300), onclick = onclick)
OnboardingActionButton(labelId = MR.strings.create_your_profile, onboarding = OnboardingStage.Step2_CreateProfile, icon = painterResource(MR.images.ic_desktop), onclick = onclick)
}
} else {
OnboardingActionButton(MR.strings.make_private_connection, onboarding = OnboardingStage.OnboardingComplete, true, onclick = onclick)
OnboardingActionButton(Modifier.widthIn(min = 300.dp), labelId = MR.strings.make_private_connection, onboarding = OnboardingStage.OnboardingComplete, onclick = onclick)
}
}