If already logged in and an NFC tag shows up with ncryptsec, logs in as a transient account

This commit is contained in:
Vitor Pamplona 2024-09-12 15:57:35 -04:00
parent 2457429470
commit 9ee28e28b8
6 changed files with 106 additions and 48 deletions

View File

@ -26,7 +26,6 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
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.width
@ -40,11 +39,8 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
@ -59,8 +55,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import com.vitorpamplona.amethyst.AccountInfo
import com.vitorpamplona.amethyst.LocalPreferences
import com.vitorpamplona.amethyst.R
@ -69,11 +63,10 @@ import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
import com.vitorpamplona.amethyst.ui.note.ArrowBackIcon
import com.vitorpamplona.amethyst.ui.note.toShortenHex
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedOff.LoginOrSignupScreen
import com.vitorpamplona.amethyst.ui.screen.loggedOff.AddAccountDialog
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.AccountPictureModifier
import com.vitorpamplona.amethyst.ui.theme.Size10dp
@ -125,28 +118,7 @@ fun AccountSwitchBottomSheet(
}
if (popupExpanded) {
Dialog(
onDismissRequest = { popupExpanded = false },
properties = DialogProperties(usePlatformDefaultWidth = false),
) {
Surface(modifier = Modifier.fillMaxSize()) {
Box {
LoginOrSignupScreen(accountStateViewModel, isFirstLogin = false)
TopAppBar(
title = {
Text(text = stringRes(R.string.account_switch_add_account_dialog_title))
},
navigationIcon = {
IconButton(onClick = { popupExpanded = false }) { ArrowBackIcon() }
},
colors =
TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.surface,
),
)
}
}
}
AddAccountDialog(null, accountStateViewModel) { popupExpanded = false }
}
}

View File

@ -41,7 +41,6 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
import androidx.core.util.Consumer
import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import com.vitorpamplona.amethyst.R
@ -72,7 +71,9 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.SecurityFiltersScr
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.SettingsScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.threadview.ThreadScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.video.VideoScreen
import com.vitorpamplona.amethyst.ui.screen.loggedOff.AddAccountDialog
import com.vitorpamplona.amethyst.ui.uriToRoute
import com.vitorpamplona.quartz.encoders.Nip19Bech32
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.net.URLDecoder
@ -284,7 +285,7 @@ fun AppNavigation(
}
}
NavigateIfIntentRequested(nav.controller, accountViewModel)
NavigateIfIntentRequested(nav, accountViewModel, accountStateViewModel)
DisplayErrorMessages(accountViewModel)
DisplayNotifyMessages(accountViewModel, nav)
@ -292,10 +293,12 @@ fun AppNavigation(
@Composable
private fun NavigateIfIntentRequested(
navController: NavHostController,
nav: Nav,
accountViewModel: AccountViewModel,
accountStateViewModel: AccountStateViewModel,
) {
val activity = LocalContext.current.getActivity()
var newAccount by remember { mutableStateOf<String?>(null) }
var currentIntentNextPage by remember {
mutableStateOf(
@ -314,15 +317,19 @@ private fun NavigateIfIntentRequested(
LaunchedEffect(intentNextPage) {
if (actionableNextPage != null) {
actionableNextPage?.let {
val currentRoute = getRouteWithArguments(navController)
val currentRoute = getRouteWithArguments(nav.controller)
if (!isSameRoute(currentRoute, it)) {
navController.navigate(it) {
popUpTo(Route.Home.route)
launchSingleTop = true
}
nav.newStack(it)
}
actionableNextPage = null
}
} else if (intentNextPage.contains("ncryptsec1")) {
// login functions
Nip19Bech32.tryParseAndClean(intentNextPage)?.let {
newAccount = it
}
actionableNextPage = null
} else {
accountViewModel.toast(
R.string.invalid_nip19_uri,
@ -342,15 +349,18 @@ private fun NavigateIfIntentRequested(
Consumer<Intent> { intent ->
val uri = intent?.data?.toString()
if (!uri.isNullOrBlank()) {
// navigation functions
val newPage = uriToRoute(uri)
if (newPage != null) {
val currentRoute = getRouteWithArguments(navController)
val currentRoute = getRouteWithArguments(nav.controller)
if (!isSameRoute(currentRoute, newPage)) {
navController.navigate(newPage) {
popUpTo(Route.Home.route)
launchSingleTop = true
}
nav.newStack(newPage)
}
} else if (uri.contains("ncryptsec")) {
// login functions
Nip19Bech32.tryParseAndClean(uri)?.let {
newAccount = it
}
} else {
scope.launch {
@ -367,6 +377,10 @@ private fun NavigateIfIntentRequested(
activity.addOnNewIntentListener(consumer)
onDispose { activity.removeOnNewIntentListener(consumer) }
}
if (newAccount != null) {
AddAccountDialog(newAccount, accountStateViewModel) { newAccount = null }
}
}
fun Context.getActivity(): MainActivity {

View File

@ -88,7 +88,7 @@ fun AccountScreen(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background,
) {
LoginOrSignupScreen(accountStateViewModel, isFirstLogin = true)
LoginOrSignupScreen(null, accountStateViewModel, isFirstLogin = true)
}
}
is AccountState.LoggedIn -> {

View File

@ -0,0 +1,70 @@
/**
* Copyright (c) 2024 Vitor Pamplona
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.amethyst.ui.screen.loggedOff
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.note.ArrowBackIcon
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
import com.vitorpamplona.amethyst.ui.stringRes
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AddAccountDialog(
newAccountKey: String?,
accountStateViewModel: AccountStateViewModel,
onClose: () -> Unit,
) {
Dialog(
onDismissRequest = onClose,
properties = DialogProperties(usePlatformDefaultWidth = false),
) {
Surface(modifier = Modifier.fillMaxSize()) {
Box {
LoginOrSignupScreen(newAccountKey, accountStateViewModel, isFirstLogin = false)
TopAppBar(
title = {
Text(text = stringRes(R.string.account_switch_add_account_dialog_title))
},
navigationIcon = {
IconButton(onClick = onClose) { ArrowBackIcon() }
},
colors =
TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.surface,
),
)
}
}
}
}

View File

@ -30,7 +30,8 @@ import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
@Composable
fun LoginOrSignupScreen(
accountViewModel: AccountStateViewModel,
newAccountKey: String?,
accountStateViewModel: AccountStateViewModel,
isFirstLogin: Boolean,
) {
var wantsNewUser by remember {
@ -39,11 +40,11 @@ fun LoginOrSignupScreen(
Crossfade(wantsNewUser, label = "LoginOrSignupScreen") {
if (it) {
SignUpPage(accountStateViewModel = accountViewModel) {
SignUpPage(accountStateViewModel) {
wantsNewUser = false
}
} else {
LoginPage(accountStateViewModel = accountViewModel, isFirstLogin = isFirstLogin) {
LoginPage(accountStateViewModel, isFirstLogin, newAccountKey) {
wantsNewUser = true
}
}

View File

@ -129,9 +129,10 @@ fun LoginPage() {
fun LoginPage(
accountStateViewModel: AccountStateViewModel,
isFirstLogin: Boolean,
newAccountKey: String? = null,
onWantsToLogin: () -> Unit,
) {
val key = remember { mutableStateOf(TextFieldValue("")) }
val key = remember { mutableStateOf(TextFieldValue(newAccountKey ?: "")) }
var errorMessage by remember { mutableStateOf("") }
val acceptedTerms = remember { mutableStateOf(!isFirstLogin) }
var termsAcceptanceIsRequired by remember { mutableStateOf("") }