fix: say sign in is not available yet, instead of offering a flow alpha cannot finish

The sign in screen asked for an nsec or npub, walked through a confirmation
step and reported an error when the sign in failed. None of that can succeed
while the app is in alpha testing, so the screen is now the notice and nothing
else, centred in the window because the message is the only thing on it.

The screen no longer reads any state, so it takes no arguments and the
navigation host stops handing it the repository.

SignInToProfileViewModel, SignInToProfileUIState and SignInToProfileFormState
are left where they are. Nothing references them now, but they are the
implementation to restore when sign in ships, rather than something to write
again.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kgothatso Ngako
2026-09-08 09:28:11 +02:00
parent b942e74449
commit 64181bfcaa
3 changed files with 19 additions and 264 deletions

View File

@@ -2,273 +2,42 @@ package press.mantra.compose.ui.composable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Create
import androidx.compose.material.icons.filled.NavigateNext
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
import androidx.compose.material3.LoadingIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.quartz.nip19Bech32.entities.NPub
import com.vitorpamplona.quartz.nip19Bech32.entities.NSec
import press.mantra.compose.ui.theme.spacing
import mantra.composeapp.generated.resources.Res
import org.jetbrains.compose.resources.stringResource
import mantra.composeapp.generated.resources.be_sure_to_keep_this_nsec_safe
import mantra.composeapp.generated.resources.enter_the_nsec_or_npub_read_only_that_you
import mantra.composeapp.generated.resources.next
import mantra.composeapp.generated.resources.nsec_npub_nip_05_static_address
import mantra.composeapp.generated.resources.sign_in
import mantra.composeapp.generated.resources.sign_in_to_nsec
import mantra.composeapp.generated.resources.sign_in_with_an_npub
import mantra.composeapp.generated.resources.something_went_wrong_and_we_were_unable_to
import mantra.composeapp.generated.resources.this_will_give_you_read_only_access_to_the
import mantra.composeapp.generated.resources.this_will_give_you_write_access_to_the
import androidx.compose.material3.SnackbarHost
import mantra.composeapp.generated.resources.sign_in_is_not_yet_available_while_mantra_is
import press.mantra.compose.ui.composable.widgets.LocalSnackbarHostState
import press.mantra.compose.ui.theme.readableContent
import press.mantra.compose.ui.theme.ConformancePreviews
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
// The message is the whole screen while sign in is held back, so it is centred in
// the window rather than given the leading edge the readable column normally keeps.
@Composable
fun SignInToProfileScreen(
initialSignInToProfileUIState: press.mantra.compose.ui.view.state.SignInToProfileUIState = press.mantra.compose.ui.view.state.SignInToProfileUIState.InputPrompt,
nostrRepository: press.mantra.compose.repository.NostrRepository
) {
val signInToProfileViewModel: press.mantra.compose.ui.view.model.SignInToProfileViewModel = viewModel (
factory = press.mantra.compose.ui.view.model.SignInToProfileViewModel.factory(
initialSignInToProfileUIState,
nostrRepository
)
)
// imePadding: this screen has a text field, and without it the software
// keyboard covers whatever is being typed into.
Scaffold(
snackbarHost = { SnackbarHost(LocalSnackbarHostState.current) },
modifier = Modifier.imePadding()) { innerPadding ->
fun SignInToProfileScreen() {
Scaffold(snackbarHost = { SnackbarHost(LocalSnackbarHostState.current) }) { innerPadding ->
Column(
modifier = Modifier.padding(innerPadding)
modifier = Modifier.fillMaxSize().padding(innerPadding)
.readableContent()
.padding(MaterialTheme.spacing.screenMargin),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Column(
modifier = Modifier.fillMaxWidth().padding(
MaterialTheme.spacing.space125
),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.space250)
) {
when (val signInToProfileUIState = signInToProfileViewModel.signInToProfileUIState.value) {
is press.mantra.compose.ui.view.state.SignInToProfileUIState.InputPrompt -> {
Text(
stringResource(Res.string.sign_in),
style = MaterialTheme.typography.headlineSmall
)
TextField(
modifier = Modifier.fillMaxWidth(),
state = signInToProfileViewModel.signInToProfileFormState.textFieldForm.textFieldState,
isError = signInToProfileViewModel.signInToProfileFormState.textFieldForm.errorMessage.value != null,
supportingText = signInToProfileViewModel.signInToProfileFormState.textFieldForm.errorMessage.value?.let {
{
Text(
text = it,
modifier = Modifier.padding(
bottom = MaterialTheme.spacing.compactPadding
)
)
}
},
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Password,
imeAction = ImeAction.Done
),
label = {
Text(
text = stringResource(Res.string.nsec_npub_nip_05_static_address),
maxLines = 1,
)
},
placeholder = {
Text(
text = stringResource(Res.string.enter_the_nsec_or_npub_read_only_that_you),
maxLines = 1,
)
},
leadingIcon = {
Icon(
Icons.Default.Create,
contentDescription = "Write note"
)
}
)
Button(
onClick = {
signInToProfileViewModel.signInToProfileUIState.value = signInToProfileViewModel.validateCredential().let { credentialEntity ->
when (credentialEntity) {
is NSec -> {
press.mantra.compose.ui.view.state.SignInToProfileUIState.ConfirmNsecSignIn(
nsec = signInToProfileViewModel.signInToProfileFormState.textFieldForm.textFieldState.text.toString(),
hexKey = credentialEntity.hex
)
}
is NPub -> {
press.mantra.compose.ui.view.state.SignInToProfileUIState.ConfirmNpubSignIn(
npub = signInToProfileViewModel.signInToProfileFormState.textFieldForm.textFieldState.text.toString(),
hexKey = credentialEntity.hex
)
}
else -> {
press.mantra.compose.ui.view.state.SignInToProfileUIState.Error
}
}
}
},
) {
Text(
stringResource(Res.string.next)
)
Icon(
Icons.Default.NavigateNext,
contentDescription = "Next"
)
}
}
is press.mantra.compose.ui.view.state.SignInToProfileUIState.ConfirmNpubSignIn-> {
Spacer(
modifier = Modifier.weight(1f)
)
Text(
text = stringResource(Res.string.sign_in_with_an_npub),
style = MaterialTheme.typography.headlineMedium
)
Text(
text = signInToProfileUIState.npub,
style = MaterialTheme.typography.bodyLarge,
textAlign = TextAlign.Center
)
Text(
text = signInToProfileUIState.hexKey,
style = MaterialTheme.typography.labelMedium,
textAlign = TextAlign.Center
)
Spacer(
modifier = Modifier.weight(4f)
)
Text(
text = stringResource(Res.string.this_will_give_you_read_only_access_to_the),
style = MaterialTheme.typography.labelSmall,
textAlign = TextAlign.Center
)
if (signInToProfileViewModel.isActionPending.value) {
LoadingIndicator(
modifier = Modifier
)
} else {
Button(
onClick = {
signInToProfileViewModel.signInToAccount()
}
) {
Text(
text = stringResource(Res.string.sign_in)
)
}
}
}
is press.mantra.compose.ui.view.state.SignInToProfileUIState.ConfirmNsecSignIn-> {
Spacer(
modifier = Modifier.weight(1f)
)
Text(
text = stringResource(Res.string.sign_in_to_nsec),
style = MaterialTheme.typography.headlineMedium
)
Text(
text = stringResource(Res.string.be_sure_to_keep_this_nsec_safe),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.bodyMedium
)
Spacer(
modifier = Modifier.weight(4f)
)
Text(
text = stringResource(Res.string.this_will_give_you_write_access_to_the),
style = MaterialTheme.typography.labelSmall,
textAlign = TextAlign.Center
)
if (signInToProfileViewModel.isActionPending.value) {
LoadingIndicator(
modifier = Modifier
)
} else {
Button(
onClick = {
signInToProfileViewModel.signInToAccount()
}
) {
Text(
text = stringResource(Res.string.sign_in)
)
}
}
}
is press.mantra.compose.ui.view.state.SignInToProfileUIState.Error -> {
Spacer(
modifier = Modifier.weight(1f)
)
Text(
text = stringResource(Res.string.something_went_wrong_and_we_were_unable_to),
style = MaterialTheme.typography.bodyLarge,
textAlign = TextAlign.Center
)
Spacer(
modifier = Modifier.weight(1f)
)
}
}
}
Text(
text = stringResource(Res.string.sign_in_is_not_yet_available_while_mantra_is),
style = MaterialTheme.typography.bodyLarge,
textAlign = TextAlign.Center
)
}
}
}
@@ -280,20 +49,7 @@ private fun SignInToProfileScreenPreview() {
Surface(
modifier = Modifier.fillMaxSize()
) {
SignInToProfileScreen(
initialSignInToProfileUIState =
press.mantra.compose.ui.view.state.SignInToProfileUIState.InputPrompt,
// SignInToProfileUIState.Error,
// SignInToProfileUIState.ConfirmNpubSignIn(
// npub = "npub1uh366qnj68a7atvwgwdy4569xd6v5jtferftapqztwjwlzh9553q47pqsx",
// hexKey = "e5e3ad0272d1fbeead8e439a4ad3453374ca4969c8d2be84025ba4ef8ae5a522"
// ),
// SignInToProfileUIState.ConfirmNsecSignIn(
// nsec = "nsecthingsaretobekeptsecret",
// hexKey = "e5e3ad0272d1fbeead8e439a4ad3453374ca4969c8d2be84025ba4ef8ae5a522"
// ),
nostrRepository = press.mantra.compose.repository.NostrRepository.NO_OP_NOSTR_REPOSITORY
)
SignInToProfileScreen()
}
}
}
}

View File

@@ -628,9 +628,7 @@ fun MantraNavHost(
)
}
composable<SignInRoute> {
SignInToProfileScreen(
nostrRepository = databaseNostrRepository
)
SignInToProfileScreen()
}
composable<UnsignedProfileRoute> { backStackEntry ->
backStackEntry.toRoute<UnsignedProfileRoute>()