mirror of
https://github.com/mollyim/monero-wallet-sdk.git
synced 2024-12-26 07:59:35 -05:00
demo: add wallet screen
This commit is contained in:
parent
518e9234fe
commit
41050d28ed
@ -42,7 +42,7 @@ class SyncService(
|
||||
while (isActive) {
|
||||
val result = wallet.awaitRefresh()
|
||||
if (result.isError()) {
|
||||
break;
|
||||
break
|
||||
}
|
||||
delay(10.seconds)
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ private fun FirstStepScreen(
|
||||
Scaffold(
|
||||
topBar = {
|
||||
Toolbar(
|
||||
titleRes = R.string.add_wallet,
|
||||
title = stringResource(R.string.add_wallet),
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onBackClick) {
|
||||
Icon(
|
||||
@ -125,9 +125,9 @@ private fun SecondStepScreen(
|
||||
) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
val title = if (showRestoreOptions) R.string.restore_wallet else R.string.new_wallet
|
||||
val titleRes = if (showRestoreOptions) R.string.restore_wallet else R.string.new_wallet
|
||||
Toolbar(
|
||||
titleRes = title,
|
||||
title = stringResource(titleRes),
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onBackClick) {
|
||||
Icon(
|
||||
|
@ -19,6 +19,7 @@ import im.molly.monero.demo.ui.component.Toolbar
|
||||
@Composable
|
||||
fun HomeRoute(
|
||||
navigateToAddWalletWizard: () -> Unit,
|
||||
navigateToWallet: (Long) -> Unit,
|
||||
viewModel: HomeViewModel = viewModel(),
|
||||
) {
|
||||
val walletListUiState: WalletListUiState by viewModel.walletListUiState.collectAsStateWithLifecycle()
|
||||
@ -26,6 +27,7 @@ fun HomeRoute(
|
||||
HomeScreen(
|
||||
walletListUiState = walletListUiState,
|
||||
onAddWalletClick = navigateToAddWalletWizard,
|
||||
onWalletClick = navigateToWallet,
|
||||
)
|
||||
}
|
||||
|
||||
@ -34,13 +36,14 @@ fun HomeRoute(
|
||||
private fun HomeScreen(
|
||||
walletListUiState: WalletListUiState,
|
||||
onAddWalletClick: () -> Unit,
|
||||
onWalletClick: (Long) -> Unit,
|
||||
) {
|
||||
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
|
||||
Scaffold(
|
||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
Toolbar(
|
||||
titleRes = R.string.monero_wallets,
|
||||
title = stringResource(R.string.monero_wallets),
|
||||
scrollBehavior = scrollBehavior,
|
||||
)
|
||||
},
|
||||
@ -57,20 +60,21 @@ private fun HomeScreen(
|
||||
.fillMaxSize()
|
||||
.padding(padding))
|
||||
{
|
||||
walletCards(walletListUiState)
|
||||
walletCards(walletListUiState, onWalletClick)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun LazyListScope.walletCards(
|
||||
walletListUiState: WalletListUiState,
|
||||
onWalletClick: (Long) -> Unit,
|
||||
) {
|
||||
when (walletListUiState) {
|
||||
WalletListUiState.Loading -> item {
|
||||
Text(text = "Loading wallet list...") // TODO
|
||||
}
|
||||
is WalletListUiState.Success -> {
|
||||
walletCardsItems(walletListUiState.ids)
|
||||
walletCardsItems(walletListUiState.ids, onWalletClick)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,7 @@
|
||||
package im.molly.monero.demo.ui
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
@ -41,9 +40,7 @@ fun RemoteNodeEditableList(
|
||||
onDeleteRemoteNode: (RemoteNode) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
remoteNodes.forEach { remoteNode ->
|
||||
RemoteNodeItem(
|
||||
remoteNode,
|
||||
@ -70,7 +67,6 @@ private fun RemoteNodeItem(
|
||||
headlineText = { Text(remoteNode.uri.toString()) },
|
||||
overlineText = { Text(remoteNode.network.name.uppercase()) },
|
||||
trailingContent = {
|
||||
Row {
|
||||
if (showCheckbox) {
|
||||
Checkbox(
|
||||
checked = checked,
|
||||
@ -78,18 +74,17 @@ private fun RemoteNodeItem(
|
||||
)
|
||||
}
|
||||
if (showMenu) {
|
||||
KebabMenu(
|
||||
WalletKebabMenu(
|
||||
onEditClick = { onEditClick(remoteNode) },
|
||||
onDeleteClick = { onDeleteClick(remoteNode) },
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun KebabMenu(
|
||||
private fun WalletKebabMenu(
|
||||
onEditClick: () -> Unit,
|
||||
onDeleteClick: () -> Unit,
|
||||
) {
|
||||
|
@ -1,13 +1,14 @@
|
||||
package im.molly.monero.demo.ui
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
@ -16,6 +17,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import im.molly.monero.demo.R
|
||||
import im.molly.monero.demo.data.model.RemoteNode
|
||||
import im.molly.monero.demo.ui.theme.AppIcons
|
||||
import im.molly.monero.demo.ui.theme.AppTheme
|
||||
|
||||
@Composable
|
||||
@ -46,19 +48,46 @@ private fun SettingsScreen(
|
||||
Column(
|
||||
modifier = modifier
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(all = 24.dp)
|
||||
) {
|
||||
Divider()
|
||||
SettingsSection(
|
||||
header = {
|
||||
SettingsSectionTitle(R.string.remote_nodes)
|
||||
TextButton(onClick = onAddRemoteNode) {
|
||||
Text(stringResource(R.string.add_remote_node))
|
||||
IconButton(onClick = onAddRemoteNode) {
|
||||
Icon(
|
||||
imageVector = AppIcons.AddRemoteWallet,
|
||||
contentDescription = stringResource(R.string.add_remote_node),
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
RemoteNodeEditableList(
|
||||
remoteNodes = remoteNodes,
|
||||
onEditRemoteNode = onEditRemoteNode,
|
||||
onDeleteRemoteNode = onDeleteRemoteNode,
|
||||
modifier = Modifier.padding(start = 24.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SettingsSection(
|
||||
modifier: Modifier = Modifier,
|
||||
header: @Composable() (RowScope.() -> Unit),
|
||||
content: @Composable() (RowScope.() -> Unit) = {},
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier.padding(horizontal = 24.dp),
|
||||
) {
|
||||
Divider(Modifier.padding(top = 24.dp))
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
content = header,
|
||||
)
|
||||
Row(
|
||||
content = content,
|
||||
)
|
||||
// Divider()
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,7 +96,6 @@ private fun SettingsSectionTitle(@StringRes titleRes: Int) {
|
||||
Text(
|
||||
text = stringResource(titleRes),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
modifier = Modifier.padding(top = 16.dp, bottom = 8.dp)
|
||||
)
|
||||
}
|
||||
|
||||
@ -75,8 +103,9 @@ private fun SettingsSectionTitle(@StringRes titleRes: Int) {
|
||||
@Composable
|
||||
private fun SettingsScreenPreview() {
|
||||
AppTheme {
|
||||
val aNode = RemoteNode.EMPTY.copy(uri = Uri.parse("http://node.monero"))
|
||||
SettingsScreen(
|
||||
remoteNodes = emptyList(),
|
||||
remoteNodes = listOf(aNode),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
@ -12,10 +13,12 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun WalletCard(
|
||||
walletId: Long,
|
||||
modifier: Modifier = Modifier,
|
||||
onClick: (Long) -> Unit,
|
||||
viewModel: WalletViewModel = viewModel(
|
||||
factory = WalletViewModel.factory(walletId),
|
||||
key = walletId.toString(),
|
||||
@ -24,6 +27,7 @@ fun WalletCard(
|
||||
val walletUiState: WalletUiState by viewModel.walletUiState.collectAsStateWithLifecycle()
|
||||
|
||||
Card(
|
||||
onClick = { onClick(walletId) },
|
||||
modifier = modifier
|
||||
.padding(8.dp)
|
||||
) {
|
||||
|
@ -6,6 +6,7 @@ import androidx.compose.ui.Modifier
|
||||
|
||||
fun LazyListScope.walletCardsItems(
|
||||
items: List<Long>,
|
||||
onItemClick: (Long) -> Unit,
|
||||
itemModifier: Modifier = Modifier,
|
||||
) = items(
|
||||
items = items,
|
||||
@ -13,6 +14,7 @@ fun LazyListScope.walletCardsItems(
|
||||
itemContent = {
|
||||
WalletCard(
|
||||
walletId = it,
|
||||
onClick = onItemClick,
|
||||
modifier = itemModifier,
|
||||
)
|
||||
},
|
||||
|
@ -0,0 +1,91 @@
|
||||
package im.molly.monero.demo.ui
|
||||
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import im.molly.monero.demo.R
|
||||
import im.molly.monero.demo.ui.component.Toolbar
|
||||
import im.molly.monero.demo.ui.theme.AppIcons
|
||||
|
||||
@Composable
|
||||
fun WalletRoute(
|
||||
walletId: Long,
|
||||
onBackClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
viewModel: WalletViewModel = viewModel(
|
||||
factory = WalletViewModel.factory(walletId),
|
||||
key = walletId.toString(),
|
||||
)
|
||||
) {
|
||||
val walletUiState: WalletUiState by viewModel.walletUiState.collectAsStateWithLifecycle()
|
||||
|
||||
WalletScreen(
|
||||
walletUiState = walletUiState,
|
||||
onBackClick = onBackClick,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun WalletScreen(
|
||||
walletUiState: WalletUiState,
|
||||
onBackClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
Toolbar(
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onBackClick) {
|
||||
Icon(
|
||||
imageVector = AppIcons.ArrowBack,
|
||||
contentDescription = stringResource(R.string.back),
|
||||
)
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
WalletKebabMenu({}, {})
|
||||
}
|
||||
)
|
||||
}
|
||||
) { padding ->
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun WalletKebabMenu(
|
||||
onRenameClick: () -> Unit,
|
||||
onDeleteClick: () -> Unit,
|
||||
) {
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
|
||||
IconButton(onClick = { expanded = true }) {
|
||||
Icon(
|
||||
imageVector = AppIcons.MoreVert,
|
||||
contentDescription = stringResource(R.string.open_menu),
|
||||
)
|
||||
}
|
||||
DropdownMenu(
|
||||
expanded = expanded,
|
||||
onDismissRequest = { expanded = false },
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(R.string.rename)) },
|
||||
onClick = {
|
||||
onRenameClick()
|
||||
expanded = false
|
||||
},
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(R.string.delete)) },
|
||||
onClick = {
|
||||
onDeleteClick()
|
||||
expanded = false
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
package im.molly.monero.demo.ui.component
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.material3.CenterAlignedTopAppBar
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
@ -8,19 +7,18 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun Toolbar(
|
||||
@StringRes titleRes: Int,
|
||||
modifier: Modifier = Modifier,
|
||||
title: String? = null,
|
||||
navigationIcon: @Composable () -> Unit = {},
|
||||
actions: @Composable RowScope.() -> Unit = {},
|
||||
scrollBehavior: TopAppBarScrollBehavior? = null,
|
||||
) {
|
||||
CenterAlignedTopAppBar(
|
||||
title = { Text(stringResource(id = titleRes), ) },
|
||||
title = { Text(title ?: "") },
|
||||
modifier = modifier,
|
||||
navigationIcon = navigationIcon,
|
||||
actions = actions,
|
||||
|
@ -14,10 +14,12 @@ fun NavController.navigateToHome(navOptions: NavOptions? = null) {
|
||||
|
||||
fun NavGraphBuilder.homeScreen(
|
||||
navigateToAddWalletWizard: () -> Unit,
|
||||
navigateToWallet: (Long) -> Unit,
|
||||
) {
|
||||
composable(route = homeNavRoute) {
|
||||
HomeRoute(
|
||||
navigateToAddWalletWizard = navigateToAddWalletWizard,
|
||||
navigateToWallet = navigateToWallet,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,9 @@ fun NavGraph(
|
||||
modifier,
|
||||
) {
|
||||
homeScreen(
|
||||
navigateToWallet = { walletId ->
|
||||
navController.navigateToWallet(walletId)
|
||||
},
|
||||
navigateToAddWalletWizard = {
|
||||
navController.navigateToAddWalletWizardGraph()
|
||||
},
|
||||
@ -28,6 +31,9 @@ fun NavGraph(
|
||||
navController.navigateToEditRemoteNode(remoteNodeId)
|
||||
},
|
||||
)
|
||||
walletScreen(
|
||||
onBackClick = onBackClick,
|
||||
)
|
||||
editRemoteNodeDialog(
|
||||
onBackClick = onBackClick,
|
||||
)
|
||||
|
@ -9,19 +9,16 @@ import im.molly.monero.demo.ui.SettingsRoute
|
||||
const val settingsNavRoute = "settings"
|
||||
const val settingsRemoteNodeNavRoute = "$settingsNavRoute/remote_node"
|
||||
|
||||
private const val idArg = "id"
|
||||
private const val queryId = "id"
|
||||
|
||||
fun NavController.navigateToSettings(navOptions: NavOptions? = null) {
|
||||
navigate(settingsNavRoute, navOptions)
|
||||
}
|
||||
|
||||
fun NavController.navigateToEditRemoteNode(remoteNodeId: Long?) {
|
||||
val route = if (remoteNodeId != null) {
|
||||
"$settingsRemoteNodeNavRoute?$idArg=$remoteNodeId"
|
||||
} else {
|
||||
settingsRemoteNodeNavRoute
|
||||
}
|
||||
this.navigate(route)
|
||||
val route = settingsRemoteNodeNavRoute +
|
||||
if (remoteNodeId != null) "?$queryId=$remoteNodeId" else ""
|
||||
navigate(route)
|
||||
}
|
||||
|
||||
fun NavGraphBuilder.settingsScreen(
|
||||
@ -38,16 +35,16 @@ fun NavGraphBuilder.editRemoteNodeDialog(
|
||||
onBackClick: () -> Unit,
|
||||
) {
|
||||
dialog(
|
||||
route = "$settingsRemoteNodeNavRoute?$idArg={$idArg}",
|
||||
route = "$settingsRemoteNodeNavRoute?$queryId={$queryId}",
|
||||
arguments = listOf(
|
||||
navArgument(idArg) {
|
||||
navArgument(queryId) {
|
||||
type = NavType.StringType
|
||||
nullable = true
|
||||
}
|
||||
)
|
||||
) {
|
||||
val arguments = requireNotNull(it.arguments)
|
||||
val remoteNodeId = arguments.getString(idArg)?.toLongOrNull()
|
||||
val remoteNodeId = arguments.getString(queryId)?.toLongOrNull()
|
||||
EditRemoteNodeRoute(
|
||||
remoteNodeId = remoteNodeId,
|
||||
onBackClick = onBackClick,
|
||||
|
@ -4,13 +4,22 @@ import androidx.navigation.*
|
||||
import androidx.navigation.compose.composable
|
||||
import im.molly.monero.demo.ui.AddWalletFirstStepRoute
|
||||
import im.molly.monero.demo.ui.AddWalletSecondStepRoute
|
||||
import im.molly.monero.demo.ui.WalletRoute
|
||||
|
||||
const val addWalletWizardNavRoute = "home/add_wallet_wizard"
|
||||
const val walletNavRoute = "wallet"
|
||||
const val addWalletWizardNavRoute = "add_wallet_wizard"
|
||||
|
||||
private const val walletIdArg = "id"
|
||||
|
||||
private const val startNavRoute = "$addWalletWizardNavRoute/start"
|
||||
private const val createNavRoute = "$addWalletWizardNavRoute/create"
|
||||
private const val restoreNavRoute = "$addWalletWizardNavRoute/restore"
|
||||
|
||||
fun NavController.navigateToWallet(walletId: Long) {
|
||||
val route = "$walletNavRoute/$walletId"
|
||||
navigate(route)
|
||||
}
|
||||
|
||||
fun NavController.navigateToAddWalletWizardGraph(navOptions: NavOptions? = null) {
|
||||
navigate(addWalletWizardNavRoute, navOptions)
|
||||
}
|
||||
@ -22,6 +31,24 @@ fun NavController.navigateToAddWalletSecondStep(restoreWallet: Boolean) {
|
||||
}
|
||||
}
|
||||
|
||||
fun NavGraphBuilder.walletScreen(
|
||||
onBackClick: () -> Unit,
|
||||
) {
|
||||
composable(
|
||||
route = "$walletNavRoute/{$walletIdArg}",
|
||||
arguments = listOf(
|
||||
navArgument(walletIdArg) { type = NavType.LongType }
|
||||
)
|
||||
) {
|
||||
val arguments = requireNotNull(it.arguments)
|
||||
val walletId = arguments.getLong(walletIdArg)
|
||||
WalletRoute(
|
||||
walletId = walletId,
|
||||
onBackClick = onBackClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun NavGraphBuilder.addWalletWizardGraph(
|
||||
navController: NavHostController,
|
||||
onBackClick: () -> Unit,
|
||||
|
@ -2,9 +2,7 @@ package im.molly.monero.demo.ui.theme
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.*
|
||||
import androidx.compose.material.icons.outlined.Home
|
||||
import androidx.compose.material.icons.outlined.List
|
||||
import androidx.compose.material.icons.outlined.Settings
|
||||
import androidx.compose.material.icons.outlined.*
|
||||
|
||||
object AppIcons {
|
||||
val ArrowBack = Icons.Filled.ArrowBack
|
||||
@ -16,4 +14,5 @@ object AppIcons {
|
||||
val Settings = Icons.Filled.Settings
|
||||
val SettingsOutlined = Icons.Outlined.Settings
|
||||
val AddWallet = Icons.Filled.Add
|
||||
val AddRemoteWallet = Icons.Outlined.AddCircle
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
<string name="settings">Settings</string>
|
||||
<string name="add_remote_node">Add remote node</string>
|
||||
<string name="edit">Edit</string>
|
||||
<string name="rename">Rename</string>
|
||||
<string name="open_menu">Open menu</string>
|
||||
<string name="delete">Delete</string>
|
||||
<string name="save">Save</string>
|
||||
|
Loading…
Reference in New Issue
Block a user