Account State is more than just a view model.
This commit is contained in:
@@ -55,6 +55,7 @@ import com.vitorpamplona.amethyst.service.relayClient.notifyCommand.model.Notify
|
|||||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.RelaySubscriptionsCoordinator
|
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.RelaySubscriptionsCoordinator
|
||||||
import com.vitorpamplona.amethyst.service.relayClient.speedLogger.RelaySpeedLogger
|
import com.vitorpamplona.amethyst.service.relayClient.speedLogger.RelaySpeedLogger
|
||||||
import com.vitorpamplona.amethyst.service.uploads.nip95.Nip95CacheFactory
|
import com.vitorpamplona.amethyst.service.uploads.nip95.Nip95CacheFactory
|
||||||
|
import com.vitorpamplona.amethyst.ui.screen.AccountSessionManager
|
||||||
import com.vitorpamplona.amethyst.ui.screen.UiSettingsState
|
import com.vitorpamplona.amethyst.ui.screen.UiSettingsState
|
||||||
import com.vitorpamplona.amethyst.ui.tor.TorManager
|
import com.vitorpamplona.amethyst.ui.tor.TorManager
|
||||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||||
@@ -231,6 +232,15 @@ class AppModules(
|
|||||||
client = client,
|
client = client,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val sessionManager =
|
||||||
|
AccountSessionManager(
|
||||||
|
accountsCache = accountsCache,
|
||||||
|
nip05Client = nip05Client,
|
||||||
|
client = client,
|
||||||
|
localPreferences = LocalPreferences,
|
||||||
|
scope = applicationIOScope,
|
||||||
|
)
|
||||||
|
|
||||||
// Organizes cache clearing
|
// Organizes cache clearing
|
||||||
val trimmingService = MemoryTrimmingService(cache)
|
val trimmingService = MemoryTrimmingService(cache)
|
||||||
|
|
||||||
@@ -274,6 +284,7 @@ class AppModules(
|
|||||||
applicationIOScope.launch {
|
applicationIOScope.launch {
|
||||||
// loads main account quickly.
|
// loads main account quickly.
|
||||||
LocalPreferences.loadAccountConfigFromEncryptedStorage()
|
LocalPreferences.loadAccountConfigFromEncryptedStorage()
|
||||||
|
sessionManager.loginWithDefaultAccountIfLoggedOff()
|
||||||
}
|
}
|
||||||
|
|
||||||
// forces initialization of uiPrefs in the main thread to avoid blinking themes
|
// forces initialization of uiPrefs in the main thread to avoid blinking themes
|
||||||
|
|||||||
@@ -26,8 +26,6 @@ import androidx.activity.compose.setContent
|
|||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
||||||
import com.vitorpamplona.amethyst.Amethyst
|
import com.vitorpamplona.amethyst.Amethyst
|
||||||
import com.vitorpamplona.amethyst.debugState
|
import com.vitorpamplona.amethyst.debugState
|
||||||
import com.vitorpamplona.amethyst.model.Account
|
import com.vitorpamplona.amethyst.model.Account
|
||||||
@@ -38,7 +36,6 @@ import com.vitorpamplona.amethyst.service.playback.pip.BackgroundMedia
|
|||||||
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor
|
import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor
|
||||||
import com.vitorpamplona.amethyst.ui.screen.AccountScreen
|
import com.vitorpamplona.amethyst.ui.screen.AccountScreen
|
||||||
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
|
|
||||||
import com.vitorpamplona.amethyst.ui.theme.AmethystTheme
|
import com.vitorpamplona.amethyst.ui.theme.AmethystTheme
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent
|
import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent
|
||||||
import com.vitorpamplona.quartz.nip19Bech32.Nip19Parser
|
import com.vitorpamplona.quartz.nip19Bech32.Nip19Parser
|
||||||
@@ -68,13 +65,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
setContent {
|
setContent {
|
||||||
StringResSetup()
|
StringResSetup()
|
||||||
AmethystTheme {
|
AmethystTheme {
|
||||||
val accountStateViewModel: AccountStateViewModel = viewModel()
|
AccountScreen(Amethyst.instance.sessionManager)
|
||||||
|
|
||||||
LaunchedEffect(key1 = Unit) {
|
|
||||||
accountStateViewModel.loginWithDefaultAccountIfLoggedOff()
|
|
||||||
}
|
|
||||||
|
|
||||||
AccountScreen(accountStateViewModel)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ private tailrec fun Context.getActivityWindow(): Window? =
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun getActivity(): Activity? = LocalContext.current.getActivity()
|
fun getActivity(): Activity = LocalContext.current.getActivity()
|
||||||
|
|
||||||
tailrec fun Context.getActivity(): ComponentActivity =
|
tailrec fun Context.getActivity(): ComponentActivity =
|
||||||
when (this) {
|
when (this) {
|
||||||
@@ -55,6 +55,13 @@ tailrec fun Context.getActivity(): ComponentActivity =
|
|||||||
else -> throw IllegalStateException("Requires a ComponentActivity to run")
|
else -> throw IllegalStateException("Requires a ComponentActivity to run")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Context.getActivityOrNull(): ComponentActivity? =
|
||||||
|
when (this) {
|
||||||
|
is ComponentActivity -> this
|
||||||
|
is ContextWrapper -> baseContext.getActivity()
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun amethystApp(): Amethyst = LocalContext.current.asAmethystApp()
|
fun amethystApp(): Amethyst = LocalContext.current.asAmethystApp()
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ import com.vitorpamplona.amethyst.ui.navigation.routes.isSameRoute
|
|||||||
import com.vitorpamplona.amethyst.ui.note.PayViaIntentScreen
|
import com.vitorpamplona.amethyst.ui.note.PayViaIntentScreen
|
||||||
import com.vitorpamplona.amethyst.ui.note.UpdateReactionTypeScreen
|
import com.vitorpamplona.amethyst.ui.note.UpdateReactionTypeScreen
|
||||||
import com.vitorpamplona.amethyst.ui.note.nip22Comments.ReplyCommentPostScreen
|
import com.vitorpamplona.amethyst.ui.note.nip22Comments.ReplyCommentPostScreen
|
||||||
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
|
import com.vitorpamplona.amethyst.ui.screen.AccountSessionManager
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountSwitcherAndLeftDrawerLayout
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountSwitcherAndLeftDrawerLayout
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.bookmarkgroups.default.BookmarkListScreen
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.bookmarkgroups.default.BookmarkListScreen
|
||||||
@@ -128,11 +128,11 @@ import java.net.URI
|
|||||||
@Composable
|
@Composable
|
||||||
fun AppNavigation(
|
fun AppNavigation(
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
accountStateViewModel: AccountStateViewModel,
|
accountSessionManager: AccountSessionManager,
|
||||||
) {
|
) {
|
||||||
val nav = rememberNav()
|
val nav = rememberNav()
|
||||||
|
|
||||||
AccountSwitcherAndLeftDrawerLayout(accountViewModel, accountStateViewModel, nav) {
|
AccountSwitcherAndLeftDrawerLayout(accountViewModel, accountSessionManager, nav) {
|
||||||
NavHost(
|
NavHost(
|
||||||
navController = nav.controller,
|
navController = nav.controller,
|
||||||
startDestination = Route.Home,
|
startDestination = Route.Home,
|
||||||
@@ -320,7 +320,7 @@ fun AppNavigation(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NavigateIfIntentRequested(nav, accountViewModel, accountStateViewModel)
|
NavigateIfIntentRequested(nav, accountViewModel, accountSessionManager)
|
||||||
|
|
||||||
DisplayErrorMessages(accountViewModel.toastManager, accountViewModel, nav)
|
DisplayErrorMessages(accountViewModel.toastManager, accountViewModel, nav)
|
||||||
DisplayNotifyMessages(accountViewModel, nav)
|
DisplayNotifyMessages(accountViewModel, nav)
|
||||||
@@ -332,7 +332,7 @@ fun AppNavigation(
|
|||||||
private fun NavigateIfIntentRequested(
|
private fun NavigateIfIntentRequested(
|
||||||
nav: Nav,
|
nav: Nav,
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
accountStateViewModel: AccountStateViewModel,
|
accountSessionManager: AccountSessionManager,
|
||||||
) {
|
) {
|
||||||
accountViewModel.firstRoute?.let { newRoute ->
|
accountViewModel.firstRoute?.let { newRoute ->
|
||||||
accountViewModel.firstRoute = null
|
accountViewModel.firstRoute = null
|
||||||
@@ -386,8 +386,8 @@ private fun NavigateIfIntentRequested(
|
|||||||
if (actionableNextPage != null) {
|
if (actionableNextPage != null) {
|
||||||
actionableNextPage?.let { nextRoute ->
|
actionableNextPage?.let { nextRoute ->
|
||||||
val npub = runCatching { URI(intentNextPage.removePrefix("nostr:")).findParameterValue("account") }.getOrNull()
|
val npub = runCatching { URI(intentNextPage.removePrefix("nostr:")).findParameterValue("account") }.getOrNull()
|
||||||
if (npub != null && accountStateViewModel.currentAccountNPub() != npub) {
|
if (npub != null && accountSessionManager.currentAccountNPub() != npub) {
|
||||||
accountStateViewModel.checkAndSwitchUserSync(npub, nextRoute)
|
accountSessionManager.checkAndSwitchUserSync(npub, nextRoute)
|
||||||
} else {
|
} else {
|
||||||
val currentRoute = getRouteWithArguments(nextRoute::class, nav.controller)
|
val currentRoute = getRouteWithArguments(nextRoute::class, nav.controller)
|
||||||
if (!isSameRoute(currentRoute, nextRoute)) {
|
if (!isSameRoute(currentRoute, nextRoute)) {
|
||||||
@@ -442,8 +442,8 @@ private fun NavigateIfIntentRequested(
|
|||||||
if (newPage != null) {
|
if (newPage != null) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
val npub = runCatching { URI(uri.removePrefix("nostr:")).findParameterValue("account") }.getOrNull()
|
val npub = runCatching { URI(uri.removePrefix("nostr:")).findParameterValue("account") }.getOrNull()
|
||||||
if (npub != null && accountStateViewModel.currentAccountNPub() != npub) {
|
if (npub != null && accountSessionManager.currentAccountNPub() != npub) {
|
||||||
accountStateViewModel.checkAndSwitchUserSync(npub, newPage)
|
accountSessionManager.checkAndSwitchUserSync(npub, newPage)
|
||||||
} else {
|
} else {
|
||||||
val currentRoute = getRouteWithArguments(newPage::class, nav.controller)
|
val currentRoute = getRouteWithArguments(newPage::class, nav.controller)
|
||||||
if (!isSameRoute(currentRoute, newPage)) {
|
if (!isSameRoute(currentRoute, newPage)) {
|
||||||
@@ -474,7 +474,7 @@ private fun NavigateIfIntentRequested(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (newAccount != null) {
|
if (newAccount != null) {
|
||||||
AddAccountDialog(newAccount, accountStateViewModel) { newAccount = null }
|
AddAccountDialog(newAccount, accountSessionManager) { newAccount = null }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-11
@@ -63,7 +63,7 @@ import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUse
|
|||||||
import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji
|
import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji
|
||||||
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
|
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
|
||||||
import com.vitorpamplona.amethyst.ui.note.toShortDisplay
|
import com.vitorpamplona.amethyst.ui.note.toShortDisplay
|
||||||
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
|
import com.vitorpamplona.amethyst.ui.screen.AccountSessionManager
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedOff.AddAccountDialog
|
import com.vitorpamplona.amethyst.ui.screen.loggedOff.AddAccountDialog
|
||||||
import com.vitorpamplona.amethyst.ui.stringRes
|
import com.vitorpamplona.amethyst.ui.stringRes
|
||||||
@@ -78,7 +78,7 @@ import kotlinx.coroutines.launch
|
|||||||
@Composable
|
@Composable
|
||||||
fun AccountSwitchBottomSheet(
|
fun AccountSwitchBottomSheet(
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
accountStateViewModel: AccountStateViewModel,
|
accountSessionManager: AccountSessionManager,
|
||||||
) {
|
) {
|
||||||
var popupExpanded by remember { mutableStateOf(false) }
|
var popupExpanded by remember { mutableStateOf(false) }
|
||||||
val scrollState = rememberScrollState()
|
val scrollState = rememberScrollState()
|
||||||
@@ -94,7 +94,7 @@ fun AccountSwitchBottomSheet(
|
|||||||
) {
|
) {
|
||||||
Text(stringRes(R.string.account_switch_select_account), fontWeight = FontWeight.Bold)
|
Text(stringRes(R.string.account_switch_select_account), fontWeight = FontWeight.Bold)
|
||||||
}
|
}
|
||||||
DisplayAllAccounts(accountViewModel, accountStateViewModel)
|
DisplayAllAccounts(accountViewModel, accountSessionManager)
|
||||||
Row(
|
Row(
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
@@ -110,24 +110,24 @@ fun AccountSwitchBottomSheet(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (popupExpanded) {
|
if (popupExpanded) {
|
||||||
AddAccountDialog(null, accountStateViewModel) { popupExpanded = false }
|
AddAccountDialog(null, accountSessionManager) { popupExpanded = false }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun DisplayAllAccounts(
|
private fun DisplayAllAccounts(
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
accountStateViewModel: AccountStateViewModel,
|
accountSessionManager: AccountSessionManager,
|
||||||
) {
|
) {
|
||||||
val accounts by LocalPreferences.accountsFlow().collectAsStateWithLifecycle()
|
val accounts by LocalPreferences.accountsFlow().collectAsStateWithLifecycle()
|
||||||
accounts?.forEach { acc -> DisplayAccount(acc, accountViewModel, accountStateViewModel) }
|
accounts?.forEach { acc -> DisplayAccount(acc, accountViewModel, accountSessionManager) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun DisplayAccount(
|
fun DisplayAccount(
|
||||||
acc: AccountInfo,
|
acc: AccountInfo,
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
accountStateViewModel: AccountStateViewModel,
|
accountSessionManager: AccountSessionManager,
|
||||||
) {
|
) {
|
||||||
var baseUser by remember(acc) {
|
var baseUser by remember(acc) {
|
||||||
mutableStateOf<User?>(
|
mutableStateOf<User?>(
|
||||||
@@ -153,7 +153,7 @@ fun DisplayAccount(
|
|||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.clickable { accountStateViewModel.switchUser(acc) }
|
.clickable { accountSessionManager.switchUser(acc) }
|
||||||
.padding(16.dp, 16.dp),
|
.padding(16.dp, 16.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
@@ -179,7 +179,7 @@ fun DisplayAccount(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LogoutButton(acc, accountStateViewModel)
|
LogoutButton(acc, accountSessionManager)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -247,7 +247,7 @@ private fun AccountName(
|
|||||||
@Composable
|
@Composable
|
||||||
private fun LogoutButton(
|
private fun LogoutButton(
|
||||||
acc: AccountInfo,
|
acc: AccountInfo,
|
||||||
accountStateViewModel: AccountStateViewModel,
|
accountSessionManager: AccountSessionManager,
|
||||||
) {
|
) {
|
||||||
var logoutDialog by remember { mutableStateOf(false) }
|
var logoutDialog by remember { mutableStateOf(false) }
|
||||||
if (logoutDialog) {
|
if (logoutDialog) {
|
||||||
@@ -259,7 +259,7 @@ private fun LogoutButton(
|
|||||||
TextButton(
|
TextButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
logoutDialog = false
|
logoutDialog = false
|
||||||
accountStateViewModel.logOff(acc)
|
accountSessionManager.logOff(acc)
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
Text(text = stringRes(R.string.log_out))
|
Text(text = stringRes(R.string.log_out))
|
||||||
|
|||||||
@@ -41,14 +41,14 @@ import com.vitorpamplona.amethyst.ui.stringRes
|
|||||||
import com.vitorpamplona.quartz.utils.Log
|
import com.vitorpamplona.quartz.utils.Log
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AccountScreen(accountStateViewModel: AccountStateViewModel) {
|
fun AccountScreen(accountSessionManager: AccountSessionManager) {
|
||||||
// Pauses relay services when the app pauses
|
// Pauses relay services when the app pauses
|
||||||
ManageRelayServices()
|
ManageRelayServices()
|
||||||
ManageWebOkHttp()
|
ManageWebOkHttp()
|
||||||
|
|
||||||
val accountState by accountStateViewModel.accountContent.collectAsStateWithLifecycle()
|
val accountState by accountSessionManager.accountContent.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
Log.d("ActivityLifecycle", "AccountScreen $accountState $accountStateViewModel")
|
Log.d("ActivityLifecycle", "AccountScreen $accountState $accountSessionManager")
|
||||||
|
|
||||||
Crossfade(
|
Crossfade(
|
||||||
targetState = accountState,
|
targetState = accountState,
|
||||||
@@ -56,8 +56,8 @@ fun AccountScreen(accountStateViewModel: AccountStateViewModel) {
|
|||||||
) { state ->
|
) { state ->
|
||||||
when (state) {
|
when (state) {
|
||||||
is AccountState.Loading -> LoadingSetup()
|
is AccountState.Loading -> LoadingSetup()
|
||||||
is AccountState.LoggedOff -> LoggedOffSetup(accountStateViewModel)
|
is AccountState.LoggedOff -> LoggedOffSetup(accountSessionManager)
|
||||||
is AccountState.LoggedIn -> LoggedInSetup(state, accountStateViewModel)
|
is AccountState.LoggedIn -> LoggedInSetup(state, accountSessionManager)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -94,25 +94,25 @@ fun LoadingSetup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun LoggedOffSetup(accountStateViewModel: AccountStateViewModel) {
|
fun LoggedOffSetup(accountSessionManager: AccountSessionManager) {
|
||||||
Surface(
|
Surface(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
color = MaterialTheme.colorScheme.background,
|
color = MaterialTheme.colorScheme.background,
|
||||||
) {
|
) {
|
||||||
LoginOrSignupScreen(null, accountStateViewModel, isFirstLogin = true)
|
LoginOrSignupScreen(null, accountSessionManager, isFirstLogin = true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun LoggedInSetup(
|
fun LoggedInSetup(
|
||||||
state: AccountState.LoggedIn,
|
state: AccountState.LoggedIn,
|
||||||
accountStateViewModel: AccountStateViewModel,
|
accountSessionManager: AccountSessionManager,
|
||||||
) {
|
) {
|
||||||
SetAccountCentricViewModelStore(state) {
|
SetAccountCentricViewModelStore(state) {
|
||||||
LoggedInPage(
|
LoggedInPage(
|
||||||
account = state.account,
|
account = state.account,
|
||||||
route = state.route,
|
route = state.route,
|
||||||
accountStateViewModel = accountStateViewModel,
|
accountSessionManager = accountSessionManager,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+36
-32
@@ -21,10 +21,7 @@
|
|||||||
package com.vitorpamplona.amethyst.ui.screen
|
package com.vitorpamplona.amethyst.ui.screen
|
||||||
|
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import com.vitorpamplona.amethyst.AccountInfo
|
import com.vitorpamplona.amethyst.AccountInfo
|
||||||
import com.vitorpamplona.amethyst.Amethyst
|
|
||||||
import com.vitorpamplona.amethyst.LocalPreferences
|
import com.vitorpamplona.amethyst.LocalPreferences
|
||||||
import com.vitorpamplona.amethyst.model.AccountSettings
|
import com.vitorpamplona.amethyst.model.AccountSettings
|
||||||
import com.vitorpamplona.amethyst.model.DefaultChannels
|
import com.vitorpamplona.amethyst.model.DefaultChannels
|
||||||
@@ -33,14 +30,17 @@ import com.vitorpamplona.amethyst.model.DefaultIndexerRelayList
|
|||||||
import com.vitorpamplona.amethyst.model.DefaultNIP65List
|
import com.vitorpamplona.amethyst.model.DefaultNIP65List
|
||||||
import com.vitorpamplona.amethyst.model.DefaultNIP65RelaySet
|
import com.vitorpamplona.amethyst.model.DefaultNIP65RelaySet
|
||||||
import com.vitorpamplona.amethyst.model.DefaultSearchRelayList
|
import com.vitorpamplona.amethyst.model.DefaultSearchRelayList
|
||||||
|
import com.vitorpamplona.amethyst.model.accountsCache.AccountCacheState
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.hexToByteArray
|
import com.vitorpamplona.quartz.nip01Core.core.hexToByteArray
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.toHexKey
|
import com.vitorpamplona.quartz.nip01Core.core.toHexKey
|
||||||
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
||||||
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
|
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerSync
|
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerSync
|
||||||
import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent
|
import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent
|
||||||
import com.vitorpamplona.quartz.nip02FollowList.tags.ContactTag
|
import com.vitorpamplona.quartz.nip02FollowList.tags.ContactTag
|
||||||
|
import com.vitorpamplona.quartz.nip05DnsIdentifiers.Nip05Client
|
||||||
import com.vitorpamplona.quartz.nip05DnsIdentifiers.Nip05Id
|
import com.vitorpamplona.quartz.nip05DnsIdentifiers.Nip05Id
|
||||||
import com.vitorpamplona.quartz.nip06KeyDerivation.Nip06
|
import com.vitorpamplona.quartz.nip06KeyDerivation.Nip06
|
||||||
import com.vitorpamplona.quartz.nip17Dm.settings.ChatMessageRelayListEvent
|
import com.vitorpamplona.quartz.nip17Dm.settings.ChatMessageRelayListEvent
|
||||||
@@ -63,9 +63,8 @@ import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent
|
|||||||
import com.vitorpamplona.quartz.utils.Hex
|
import com.vitorpamplona.quartz.utils.Hex
|
||||||
import com.vitorpamplona.quartz.utils.Log
|
import com.vitorpamplona.quartz.utils.Log
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
@@ -77,21 +76,27 @@ import java.util.regex.Pattern
|
|||||||
val EMAIL_PATTERN: Pattern = Pattern.compile(".+@.+\\.[a-z]+")
|
val EMAIL_PATTERN: Pattern = Pattern.compile(".+@.+\\.[a-z]+")
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
class AccountStateViewModel : ViewModel() {
|
class AccountSessionManager(
|
||||||
|
val accountsCache: AccountCacheState,
|
||||||
|
val nip05Client: Nip05Client,
|
||||||
|
val client: INostrClient,
|
||||||
|
val localPreferences: LocalPreferences,
|
||||||
|
val scope: CoroutineScope,
|
||||||
|
) {
|
||||||
private val _accountContent = MutableStateFlow<AccountState>(AccountState.Loading)
|
private val _accountContent = MutableStateFlow<AccountState>(AccountState.Loading)
|
||||||
val accountContent = _accountContent.asStateFlow()
|
val accountContent = _accountContent.asStateFlow()
|
||||||
|
|
||||||
fun loginWithDefaultAccountIfLoggedOff() {
|
fun loginWithDefaultAccountIfLoggedOff() {
|
||||||
// pulls account from storage.
|
// pulls account from storage.
|
||||||
if (_accountContent.value !is AccountState.LoggedIn) {
|
if (_accountContent.value !is AccountState.LoggedIn) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
scope.launch(Dispatchers.IO) {
|
||||||
loginWithDefaultAccount()
|
loginWithDefaultAccount()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun loginWithDefaultAccount(route: Route? = null) {
|
private suspend fun loginWithDefaultAccount(route: Route? = null) {
|
||||||
val accountSettings = LocalPreferences.loadAccountConfigFromEncryptedStorage()
|
val accountSettings = localPreferences.loadAccountConfigFromEncryptedStorage()
|
||||||
|
|
||||||
if (accountSettings != null) {
|
if (accountSettings != null) {
|
||||||
startUI(accountSettings, route)
|
startUI(accountSettings, route)
|
||||||
@@ -155,7 +160,7 @@ class AccountStateViewModel : ViewModel() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalPreferences.setDefaultAccount(accountSettings)
|
localPreferences.setDefaultAccount(accountSettings)
|
||||||
|
|
||||||
startUI(accountSettings)
|
startUI(accountSettings)
|
||||||
}
|
}
|
||||||
@@ -164,7 +169,7 @@ class AccountStateViewModel : ViewModel() {
|
|||||||
accountSettings: AccountSettings,
|
accountSettings: AccountSettings,
|
||||||
route: Route? = null,
|
route: Route? = null,
|
||||||
) {
|
) {
|
||||||
val account = Amethyst.instance.accountsCache.loadAccount(accountSettings)
|
val account = accountsCache.loadAccount(accountSettings)
|
||||||
_accountContent.update {
|
_accountContent.update {
|
||||||
AccountState.LoggedIn(account, route)
|
AccountState.LoggedIn(account, route)
|
||||||
}
|
}
|
||||||
@@ -187,7 +192,7 @@ class AccountStateViewModel : ViewModel() {
|
|||||||
packageName: String = "",
|
packageName: String = "",
|
||||||
onError: (String?) -> Unit,
|
onError: (String?) -> Unit,
|
||||||
) {
|
) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
scope.launch(Dispatchers.IO) {
|
||||||
if (key.startsWith("ncryptsec")) {
|
if (key.startsWith("ncryptsec")) {
|
||||||
val newKey =
|
val newKey =
|
||||||
try {
|
try {
|
||||||
@@ -214,7 +219,7 @@ class AccountStateViewModel : ViewModel() {
|
|||||||
onError("Could not parse nip05 address: $nip05")
|
onError("Could not parse nip05 address: $nip05")
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
val pubkeyInfo = Amethyst.instance.nip05Client.get(nip05)
|
val pubkeyInfo = nip05Client.get(nip05)
|
||||||
if (pubkeyInfo == null) {
|
if (pubkeyInfo == null) {
|
||||||
onError("User not found in the nip05 server: $nip05")
|
onError("User not found in the nip05 server: $nip05")
|
||||||
} else {
|
} else {
|
||||||
@@ -238,7 +243,7 @@ class AccountStateViewModel : ViewModel() {
|
|||||||
packageName: String = "",
|
packageName: String = "",
|
||||||
onError: (String) -> Unit,
|
onError: (String) -> Unit,
|
||||||
) {
|
) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
scope.launch(Dispatchers.IO) {
|
||||||
loginSync(key, transientAccount, loginWithExternalSigner, packageName, onError)
|
loginSync(key, transientAccount, loginWithExternalSigner, packageName, onError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -263,7 +268,7 @@ class AccountStateViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun newKey(name: String? = null) {
|
fun newKey(name: String? = null) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
scope.launch(Dispatchers.IO) {
|
||||||
if (_accountContent.value is AccountState.LoggedIn) {
|
if (_accountContent.value is AccountState.LoggedIn) {
|
||||||
prepareLogoutOrSwitch()
|
prepareLogoutOrSwitch()
|
||||||
}
|
}
|
||||||
@@ -272,22 +277,21 @@ class AccountStateViewModel : ViewModel() {
|
|||||||
|
|
||||||
val accountSettings = createNewAccount(name)
|
val accountSettings = createNewAccount(name)
|
||||||
|
|
||||||
LocalPreferences.setDefaultAccount(accountSettings)
|
localPreferences.setDefaultAccount(accountSettings)
|
||||||
|
|
||||||
startUI(accountSettings)
|
startUI(accountSettings)
|
||||||
|
|
||||||
@OptIn(DelicateCoroutinesApi::class)
|
scope.launch(Dispatchers.IO) {
|
||||||
GlobalScope.launch(Dispatchers.IO) {
|
|
||||||
delay(2000) // waits for the new user to connect to the new relays.
|
delay(2000) // waits for the new user to connect to the new relays.
|
||||||
|
|
||||||
val toPost = accountSettings.backupNIP65RelayList?.writeRelaysNorm()?.toSet() ?: DefaultNIP65RelaySet
|
val toPost = accountSettings.backupNIP65RelayList?.writeRelaysNorm()?.toSet() ?: DefaultNIP65RelaySet
|
||||||
|
|
||||||
accountSettings.backupUserMetadata?.let { Amethyst.instance.client.send(it, toPost) }
|
accountSettings.backupUserMetadata?.let { client.send(it, toPost) }
|
||||||
accountSettings.backupContactList?.let { Amethyst.instance.client.send(it, toPost) }
|
accountSettings.backupContactList?.let { client.send(it, toPost) }
|
||||||
accountSettings.backupNIP65RelayList?.let { Amethyst.instance.client.send(it, toPost) }
|
accountSettings.backupNIP65RelayList?.let { client.send(it, toPost) }
|
||||||
accountSettings.backupDMRelayList?.let { Amethyst.instance.client.send(it, toPost) }
|
accountSettings.backupDMRelayList?.let { client.send(it, toPost) }
|
||||||
accountSettings.backupSearchRelayList?.let { Amethyst.instance.client.send(it, toPost) }
|
accountSettings.backupSearchRelayList?.let { client.send(it, toPost) }
|
||||||
accountSettings.backupIndexRelayList?.let { Amethyst.instance.client.send(it, toPost) }
|
accountSettings.backupIndexRelayList?.let { client.send(it, toPost) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -315,7 +319,7 @@ class AccountStateViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun switchUser(accountInfo: AccountInfo) {
|
fun switchUser(accountInfo: AccountInfo) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
scope.launch(Dispatchers.IO) {
|
||||||
switchUserSync(accountInfo)
|
switchUserSync(accountInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -324,8 +328,8 @@ class AccountStateViewModel : ViewModel() {
|
|||||||
npub: String,
|
npub: String,
|
||||||
route: Route,
|
route: Route,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
if (npub != LocalPreferences.currentAccount()) {
|
if (npub != localPreferences.currentAccount()) {
|
||||||
val account = LocalPreferences.allSavedAccounts().firstOrNull { it.npub == npub }
|
val account = localPreferences.allSavedAccounts().firstOrNull { it.npub == npub }
|
||||||
if (account != null) {
|
if (account != null) {
|
||||||
switchUserSync(account, route)
|
switchUserSync(account, route)
|
||||||
return true
|
return true
|
||||||
@@ -339,7 +343,7 @@ class AccountStateViewModel : ViewModel() {
|
|||||||
route: Route? = null,
|
route: Route? = null,
|
||||||
) {
|
) {
|
||||||
prepareLogoutOrSwitch()
|
prepareLogoutOrSwitch()
|
||||||
LocalPreferences.switchToAccount(accountInfo)
|
localPreferences.switchToAccount(accountInfo)
|
||||||
loginWithDefaultAccount(route)
|
loginWithDefaultAccount(route)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -357,17 +361,17 @@ class AccountStateViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun logOff(accountInfo: AccountInfo) {
|
fun logOff(accountInfo: AccountInfo) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
scope.launch(Dispatchers.IO) {
|
||||||
if (accountInfo.npub == currentAccountNPub()) {
|
if (accountInfo.npub == currentAccountNPub()) {
|
||||||
// log off and relogin with the 0 account
|
// log off and relogin with the 0 account
|
||||||
prepareLogoutOrSwitch()
|
prepareLogoutOrSwitch()
|
||||||
LocalPreferences.deleteAccount(accountInfo)
|
localPreferences.deleteAccount(accountInfo)
|
||||||
Amethyst.instance.accountsCache.removeAccount(accountInfo.npub.bechToBytes().toHexKey())
|
accountsCache.removeAccount(accountInfo.npub.bechToBytes().toHexKey())
|
||||||
loginWithDefaultAccount()
|
loginWithDefaultAccount()
|
||||||
} else {
|
} else {
|
||||||
// delete without switching logins
|
// delete without switching logins
|
||||||
LocalPreferences.deleteAccount(accountInfo)
|
localPreferences.deleteAccount(accountInfo)
|
||||||
Amethyst.instance.accountsCache.removeAccount(accountInfo.npub.bechToBytes().toHexKey())
|
accountsCache.removeAccount(accountInfo.npub.bechToBytes().toHexKey())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+3
-3
@@ -40,14 +40,14 @@ import androidx.compose.ui.platform.LocalConfiguration
|
|||||||
import com.vitorpamplona.amethyst.ui.navigation.drawer.AccountSwitchBottomSheet
|
import com.vitorpamplona.amethyst.ui.navigation.drawer.AccountSwitchBottomSheet
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.drawer.DrawerContent
|
import com.vitorpamplona.amethyst.ui.navigation.drawer.DrawerContent
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||||
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
|
import com.vitorpamplona.amethyst.ui.screen.AccountSessionManager
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun AccountSwitcherAndLeftDrawerLayout(
|
fun AccountSwitcherAndLeftDrawerLayout(
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
accountStateViewModel: AccountStateViewModel,
|
accountSessionManager: AccountSessionManager,
|
||||||
nav: INav,
|
nav: INav,
|
||||||
content: @Composable () -> Unit,
|
content: @Composable () -> Unit,
|
||||||
) {
|
) {
|
||||||
@@ -106,7 +106,7 @@ fun AccountSwitcherAndLeftDrawerLayout(
|
|||||||
) {
|
) {
|
||||||
AccountSwitchBottomSheet(
|
AccountSwitchBottomSheet(
|
||||||
accountViewModel = accountViewModel,
|
accountViewModel = accountViewModel,
|
||||||
accountStateViewModel = accountStateViewModel,
|
accountSessionManager = accountSessionManager,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -46,7 +46,7 @@ import com.vitorpamplona.amethyst.service.relayClient.authCommand.compose.RelayA
|
|||||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.account.AccountFilterAssemblerSubscription
|
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.account.AccountFilterAssemblerSubscription
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.AppNavigation
|
import com.vitorpamplona.amethyst.ui.navigation.AppNavigation
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
||||||
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
|
import com.vitorpamplona.amethyst.ui.screen.AccountSessionManager
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.datasource.ChatroomListFilterAssemblerSubscription
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.datasource.ChatroomListFilterAssemblerSubscription
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.datasource.DiscoveryFilterAssemblerSubscription
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.datasource.DiscoveryFilterAssemblerSubscription
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.datasource.HomeFilterAssemblerSubscription
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.datasource.HomeFilterAssemblerSubscription
|
||||||
@@ -59,7 +59,7 @@ import kotlinx.coroutines.launch
|
|||||||
fun LoggedInPage(
|
fun LoggedInPage(
|
||||||
account: Account,
|
account: Account,
|
||||||
route: Route?,
|
route: Route?,
|
||||||
accountStateViewModel: AccountStateViewModel,
|
accountSessionManager: AccountSessionManager,
|
||||||
) {
|
) {
|
||||||
val accountViewModel: AccountViewModel =
|
val accountViewModel: AccountViewModel =
|
||||||
viewModel(
|
viewModel(
|
||||||
@@ -99,7 +99,7 @@ fun LoggedInPage(
|
|||||||
|
|
||||||
AppNavigation(
|
AppNavigation(
|
||||||
accountViewModel = accountViewModel,
|
accountViewModel = accountViewModel,
|
||||||
accountStateViewModel = accountStateViewModel,
|
accountSessionManager = accountSessionManager,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -35,14 +35,14 @@ import androidx.compose.ui.window.Dialog
|
|||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import com.vitorpamplona.amethyst.R
|
import com.vitorpamplona.amethyst.R
|
||||||
import com.vitorpamplona.amethyst.ui.note.ArrowBackIcon
|
import com.vitorpamplona.amethyst.ui.note.ArrowBackIcon
|
||||||
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
|
import com.vitorpamplona.amethyst.ui.screen.AccountSessionManager
|
||||||
import com.vitorpamplona.amethyst.ui.stringRes
|
import com.vitorpamplona.amethyst.ui.stringRes
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun AddAccountDialog(
|
fun AddAccountDialog(
|
||||||
newAccountKey: String?,
|
newAccountKey: String?,
|
||||||
accountStateViewModel: AccountStateViewModel,
|
accountSessionManager: AccountSessionManager,
|
||||||
onClose: () -> Unit,
|
onClose: () -> Unit,
|
||||||
) {
|
) {
|
||||||
Dialog(
|
Dialog(
|
||||||
@@ -51,7 +51,7 @@ fun AddAccountDialog(
|
|||||||
) {
|
) {
|
||||||
Surface(modifier = Modifier.fillMaxSize()) {
|
Surface(modifier = Modifier.fillMaxSize()) {
|
||||||
Box {
|
Box {
|
||||||
LoginOrSignupScreen(newAccountKey, accountStateViewModel, isFirstLogin = false)
|
LoginOrSignupScreen(newAccountKey, accountSessionManager, isFirstLogin = false)
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
title = {
|
title = {
|
||||||
Text(text = stringRes(R.string.account_switch_add_account_dialog_title))
|
Text(text = stringRes(R.string.account_switch_add_account_dialog_title))
|
||||||
|
|||||||
+4
-4
@@ -26,14 +26,14 @@ import androidx.compose.runtime.getValue
|
|||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
|
import com.vitorpamplona.amethyst.ui.screen.AccountSessionManager
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedOff.login.LoginPage
|
import com.vitorpamplona.amethyst.ui.screen.loggedOff.login.LoginPage
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedOff.signup.SignUpPage
|
import com.vitorpamplona.amethyst.ui.screen.loggedOff.signup.SignUpPage
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun LoginOrSignupScreen(
|
fun LoginOrSignupScreen(
|
||||||
newAccountKey: String?,
|
newAccountKey: String?,
|
||||||
accountStateViewModel: AccountStateViewModel,
|
accountSessionManager: AccountSessionManager,
|
||||||
isFirstLogin: Boolean,
|
isFirstLogin: Boolean,
|
||||||
) {
|
) {
|
||||||
var wantsNewUser by remember {
|
var wantsNewUser by remember {
|
||||||
@@ -42,11 +42,11 @@ fun LoginOrSignupScreen(
|
|||||||
|
|
||||||
Crossfade(wantsNewUser, label = "LoginOrSignupScreen") {
|
Crossfade(wantsNewUser, label = "LoginOrSignupScreen") {
|
||||||
if (it) {
|
if (it) {
|
||||||
SignUpPage(accountStateViewModel) {
|
SignUpPage(accountSessionManager) {
|
||||||
wantsNewUser = false
|
wantsNewUser = false
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LoginPage(accountStateViewModel, isFirstLogin, newAccountKey) {
|
LoginPage(accountSessionManager, isFirstLogin, newAccountKey) {
|
||||||
wantsNewUser = true
|
wantsNewUser = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-6
@@ -78,7 +78,7 @@ import com.vitorpamplona.amethyst.Amethyst
|
|||||||
import com.vitorpamplona.amethyst.R
|
import com.vitorpamplona.amethyst.R
|
||||||
import com.vitorpamplona.amethyst.commons.hashtags.Amethyst
|
import com.vitorpamplona.amethyst.commons.hashtags.Amethyst
|
||||||
import com.vitorpamplona.amethyst.commons.hashtags.CustomHashTagIcons
|
import com.vitorpamplona.amethyst.commons.hashtags.CustomHashTagIcons
|
||||||
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
|
import com.vitorpamplona.amethyst.ui.screen.AccountSessionManager
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedOff.AcceptTerms
|
import com.vitorpamplona.amethyst.ui.screen.loggedOff.AcceptTerms
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedOff.TorSettingsSetup
|
import com.vitorpamplona.amethyst.ui.screen.loggedOff.TorSettingsSetup
|
||||||
import com.vitorpamplona.amethyst.ui.stringRes
|
import com.vitorpamplona.amethyst.ui.stringRes
|
||||||
@@ -87,6 +87,7 @@ import com.vitorpamplona.amethyst.ui.theme.Size20dp
|
|||||||
import com.vitorpamplona.amethyst.ui.theme.Size40dp
|
import com.vitorpamplona.amethyst.ui.theme.Size40dp
|
||||||
import com.vitorpamplona.amethyst.ui.theme.ThemeComparisonRow
|
import com.vitorpamplona.amethyst.ui.theme.ThemeComparisonRow
|
||||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||||
|
import com.vitorpamplona.amethyst.ui.tor.TorSettingsFlow
|
||||||
import com.vitorpamplona.quartz.nip55AndroidSigner.client.isExternalSignerInstalled
|
import com.vitorpamplona.quartz.nip55AndroidSigner.client.isExternalSignerInstalled
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -94,24 +95,26 @@ import kotlinx.coroutines.launch
|
|||||||
@Preview(device = "spec:width=2160px,height=2340px,dpi=440")
|
@Preview(device = "spec:width=2160px,height=2340px,dpi=440")
|
||||||
@Composable
|
@Composable
|
||||||
fun LoginPagePreview() {
|
fun LoginPagePreview() {
|
||||||
val accountViewModel: AccountStateViewModel = viewModel()
|
val loginViewModel: LoginViewModel = viewModel()
|
||||||
|
loginViewModel.init(TorSettingsFlow())
|
||||||
|
|
||||||
ThemeComparisonRow(
|
ThemeComparisonRow(
|
||||||
toPreview = {
|
toPreview = {
|
||||||
LoginPage(accountViewModel, true) {}
|
LoginPage(loginViewModel) {}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun LoginPage(
|
fun LoginPage(
|
||||||
accountStateViewModel: AccountStateViewModel,
|
accountSessionManager: AccountSessionManager,
|
||||||
isFirstLogin: Boolean,
|
isFirstLogin: Boolean,
|
||||||
newAccountKey: String? = null,
|
newAccountKey: String? = null,
|
||||||
onWantsToLogin: () -> Unit,
|
onWantsToLogin: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val loginViewModel: LoginViewModel = viewModel()
|
val loginViewModel: LoginViewModel = viewModel()
|
||||||
loginViewModel.init(accountStateViewModel)
|
loginViewModel.init(accountSessionManager)
|
||||||
|
loginViewModel.init(Amethyst.instance.torPrefs.value)
|
||||||
|
|
||||||
LaunchedEffect(loginViewModel, isFirstLogin, newAccountKey) {
|
LaunchedEffect(loginViewModel, isFirstLogin, newAccountKey) {
|
||||||
loginViewModel.load(isFirstLogin, newAccountKey)
|
loginViewModel.load(isFirstLogin, newAccountKey)
|
||||||
@@ -180,7 +183,7 @@ fun LoginPage(
|
|||||||
Spacer(modifier = Modifier.height(10.dp))
|
Spacer(modifier = Modifier.height(10.dp))
|
||||||
|
|
||||||
TorSettingsSetup(
|
TorSettingsSetup(
|
||||||
torSettingsFlow = Amethyst.instance.torPrefs.value,
|
torSettingsFlow = loginViewModel.torSettings,
|
||||||
onError = {
|
onError = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
Toast
|
Toast
|
||||||
|
|||||||
+12
-6
@@ -28,11 +28,13 @@ import androidx.compose.runtime.setValue
|
|||||||
import androidx.compose.ui.text.input.TextFieldValue
|
import androidx.compose.ui.text.input.TextFieldValue
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import com.vitorpamplona.amethyst.R
|
import com.vitorpamplona.amethyst.R
|
||||||
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
|
import com.vitorpamplona.amethyst.ui.screen.AccountSessionManager
|
||||||
|
import com.vitorpamplona.amethyst.ui.tor.TorSettingsFlow
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
class LoginViewModel : ViewModel() {
|
class LoginViewModel : ViewModel() {
|
||||||
lateinit var accountStateViewModel: AccountStateViewModel
|
lateinit var accountSessionManager: AccountSessionManager
|
||||||
|
lateinit var torSettings: TorSettingsFlow
|
||||||
|
|
||||||
val errorManager = LoginErrorManager()
|
val errorManager = LoginErrorManager()
|
||||||
|
|
||||||
@@ -52,8 +54,12 @@ class LoginViewModel : ViewModel() {
|
|||||||
|
|
||||||
var isFirstLogin by mutableStateOf(false)
|
var isFirstLogin by mutableStateOf(false)
|
||||||
|
|
||||||
fun init(accountStateViewModel: AccountStateViewModel) {
|
fun init(accountSessionManager: AccountSessionManager) {
|
||||||
this.accountStateViewModel = accountStateViewModel
|
this.accountSessionManager = accountSessionManager
|
||||||
|
}
|
||||||
|
|
||||||
|
fun init(torSettings: TorSettingsFlow) {
|
||||||
|
this.torSettings = torSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
fun load(
|
fun load(
|
||||||
@@ -132,7 +138,7 @@ class LoginViewModel : ViewModel() {
|
|||||||
fun login() {
|
fun login() {
|
||||||
if (checkCanLogin()) {
|
if (checkCanLogin()) {
|
||||||
processingLogin = true
|
processingLogin = true
|
||||||
accountStateViewModel.login(
|
accountSessionManager.login(
|
||||||
key = key.text,
|
key = key.text,
|
||||||
password = password.text,
|
password = password.text,
|
||||||
transientAccount = isTemporary,
|
transientAccount = isTemporary,
|
||||||
@@ -150,7 +156,7 @@ class LoginViewModel : ViewModel() {
|
|||||||
fun loginWithExternalSigner(packageName: String) {
|
fun loginWithExternalSigner(packageName: String) {
|
||||||
if (checkCanLogin()) {
|
if (checkCanLogin()) {
|
||||||
processingLogin = true
|
processingLogin = true
|
||||||
accountStateViewModel.login(
|
accountSessionManager.login(
|
||||||
key = key.text,
|
key = key.text,
|
||||||
transientAccount = isTemporary,
|
transientAccount = isTemporary,
|
||||||
loginWithExternalSigner = true,
|
loginWithExternalSigner = true,
|
||||||
|
|||||||
+2
-2
@@ -33,7 +33,7 @@ import androidx.compose.runtime.setValue
|
|||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.core.util.Consumer
|
import androidx.core.util.Consumer
|
||||||
import com.vitorpamplona.amethyst.R
|
import com.vitorpamplona.amethyst.R
|
||||||
import com.vitorpamplona.amethyst.ui.components.getActivity
|
import com.vitorpamplona.amethyst.ui.components.getActivityOrNull
|
||||||
import com.vitorpamplona.amethyst.ui.stringRes
|
import com.vitorpamplona.amethyst.ui.stringRes
|
||||||
import com.vitorpamplona.quartz.nip19Bech32.Nip19Parser
|
import com.vitorpamplona.quartz.nip19Bech32.Nip19Parser
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
@@ -42,7 +42,7 @@ import kotlinx.coroutines.launch
|
|||||||
@Composable
|
@Composable
|
||||||
fun OpenURIIfNotLoggedIn(onNewNIP19: suspend (String) -> Unit) {
|
fun OpenURIIfNotLoggedIn(onNewNIP19: suspend (String) -> Unit) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val activity = context.getActivity()
|
val activity = context.getActivityOrNull() ?: return
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
var currentIntentNextPage by remember {
|
var currentIntentNextPage by remember {
|
||||||
|
|||||||
+9
-6
@@ -53,7 +53,7 @@ import com.vitorpamplona.amethyst.Amethyst
|
|||||||
import com.vitorpamplona.amethyst.R
|
import com.vitorpamplona.amethyst.R
|
||||||
import com.vitorpamplona.amethyst.commons.hashtags.Amethyst
|
import com.vitorpamplona.amethyst.commons.hashtags.Amethyst
|
||||||
import com.vitorpamplona.amethyst.commons.hashtags.CustomHashTagIcons
|
import com.vitorpamplona.amethyst.commons.hashtags.CustomHashTagIcons
|
||||||
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
|
import com.vitorpamplona.amethyst.ui.screen.AccountSessionManager
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedOff.AcceptTerms
|
import com.vitorpamplona.amethyst.ui.screen.loggedOff.AcceptTerms
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedOff.TorSettingsSetup
|
import com.vitorpamplona.amethyst.ui.screen.loggedOff.TorSettingsSetup
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedOff.login.LoginErrorManager
|
import com.vitorpamplona.amethyst.ui.screen.loggedOff.login.LoginErrorManager
|
||||||
@@ -63,27 +63,30 @@ import com.vitorpamplona.amethyst.ui.theme.Size20dp
|
|||||||
import com.vitorpamplona.amethyst.ui.theme.Size40dp
|
import com.vitorpamplona.amethyst.ui.theme.Size40dp
|
||||||
import com.vitorpamplona.amethyst.ui.theme.ThemeComparisonRow
|
import com.vitorpamplona.amethyst.ui.theme.ThemeComparisonRow
|
||||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||||
|
import com.vitorpamplona.amethyst.ui.tor.TorSettingsFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@Preview(device = "spec:width=2160px,height=2340px,dpi=440")
|
@Preview(device = "spec:width=2160px,height=2340px,dpi=440")
|
||||||
@Composable
|
@Composable
|
||||||
fun SignUpPagePreview() {
|
fun SignUpPagePreview() {
|
||||||
val accountViewModel: AccountStateViewModel = viewModel()
|
val signUpViewModel: SignUpViewModel = viewModel()
|
||||||
|
signUpViewModel.init(TorSettingsFlow())
|
||||||
|
|
||||||
ThemeComparisonRow(
|
ThemeComparisonRow(
|
||||||
toPreview = {
|
toPreview = {
|
||||||
SignUpPage(accountViewModel) {}
|
SignUpPage(signUpViewModel) {}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SignUpPage(
|
fun SignUpPage(
|
||||||
accountStateViewModel: AccountStateViewModel,
|
accountSessionManager: AccountSessionManager,
|
||||||
onWantsToLogin: () -> Unit,
|
onWantsToLogin: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val signUpViewModel: SignUpViewModel = viewModel()
|
val signUpViewModel: SignUpViewModel = viewModel()
|
||||||
signUpViewModel.init(accountStateViewModel)
|
signUpViewModel.init(accountSessionManager)
|
||||||
|
signUpViewModel.init(Amethyst.instance.torPrefs.value)
|
||||||
|
|
||||||
SignUpPage(signUpViewModel, onWantsToLogin)
|
SignUpPage(signUpViewModel, onWantsToLogin)
|
||||||
}
|
}
|
||||||
@@ -171,7 +174,7 @@ fun SignUpPage(
|
|||||||
Spacer(modifier = Modifier.height(10.dp))
|
Spacer(modifier = Modifier.height(10.dp))
|
||||||
|
|
||||||
TorSettingsSetup(
|
TorSettingsSetup(
|
||||||
torSettingsFlow = Amethyst.instance.torPrefs.value,
|
torSettingsFlow = signUpViewModel.torSettings,
|
||||||
onError = {
|
onError = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
Toast
|
Toast
|
||||||
|
|||||||
+13
-5
@@ -20,17 +20,21 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.amethyst.ui.screen.loggedOff.signup
|
package com.vitorpamplona.amethyst.ui.screen.loggedOff.signup
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.text.input.TextFieldValue
|
import androidx.compose.ui.text.input.TextFieldValue
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import com.vitorpamplona.amethyst.R
|
import com.vitorpamplona.amethyst.R
|
||||||
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
|
import com.vitorpamplona.amethyst.ui.screen.AccountSessionManager
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedOff.login.LoginErrorManager
|
import com.vitorpamplona.amethyst.ui.screen.loggedOff.login.LoginErrorManager
|
||||||
|
import com.vitorpamplona.amethyst.ui.tor.TorSettingsFlow
|
||||||
|
|
||||||
|
@Stable
|
||||||
class SignUpViewModel : ViewModel() {
|
class SignUpViewModel : ViewModel() {
|
||||||
lateinit var accountStateViewModel: AccountStateViewModel
|
lateinit var accountSessionManager: AccountSessionManager
|
||||||
|
lateinit var torSettings: TorSettingsFlow
|
||||||
|
|
||||||
val errorManager = LoginErrorManager()
|
val errorManager = LoginErrorManager()
|
||||||
|
|
||||||
@@ -39,8 +43,12 @@ class SignUpViewModel : ViewModel() {
|
|||||||
var acceptedTerms by mutableStateOf(false)
|
var acceptedTerms by mutableStateOf(false)
|
||||||
var termsAcceptanceIsRequiredError by mutableStateOf(false)
|
var termsAcceptanceIsRequiredError by mutableStateOf(false)
|
||||||
|
|
||||||
fun init(accountStateViewModel: AccountStateViewModel) {
|
fun init(accountSessionManager: AccountSessionManager) {
|
||||||
this.accountStateViewModel = accountStateViewModel
|
this.accountSessionManager = accountSessionManager
|
||||||
|
}
|
||||||
|
|
||||||
|
fun init(torSettings: TorSettingsFlow) {
|
||||||
|
this.torSettings = torSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateDisplayName(value: TextFieldValue) {
|
fun updateDisplayName(value: TextFieldValue) {
|
||||||
@@ -73,7 +81,7 @@ class SignUpViewModel : ViewModel() {
|
|||||||
|
|
||||||
fun signup() {
|
fun signup() {
|
||||||
if (checkCanSignup()) {
|
if (checkCanSignup()) {
|
||||||
accountStateViewModel.newKey(displayName.text)
|
accountSessionManager.newKey(displayName.text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user