mirror of
https://github.com/mollyim/monero-wallet-sdk.git
synced 2024-10-01 03:45:36 -04:00
demo: display account addresses in receive tab
This commit is contained in:
parent
2a26407258
commit
ec27aa42d4
@ -0,0 +1,9 @@
|
|||||||
|
package im.molly.monero.demo.data.model
|
||||||
|
|
||||||
|
import im.molly.monero.AccountAddress
|
||||||
|
|
||||||
|
data class WalletAddress(
|
||||||
|
val address: AccountAddress,
|
||||||
|
val used: Boolean,
|
||||||
|
val isLastForAccount: Boolean,
|
||||||
|
)
|
@ -0,0 +1,64 @@
|
|||||||
|
package im.molly.monero.demo.ui
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.alpha
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import im.molly.monero.demo.data.model.WalletAddress
|
||||||
|
import im.molly.monero.demo.ui.component.CopyableText
|
||||||
|
import im.molly.monero.demo.ui.theme.Blue40
|
||||||
|
import im.molly.monero.demo.ui.theme.Red40
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun AddressCardExpanded(
|
||||||
|
walletAddress: WalletAddress,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
onCreateSubAddressClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 16.dp, vertical = 8.dp)
|
||||||
|
) {
|
||||||
|
with(walletAddress.address) {
|
||||||
|
val used = walletAddress.used || isPrimaryAddress
|
||||||
|
if (isPrimaryAddress) {
|
||||||
|
Text(
|
||||||
|
text = "Account #$accountIndex Primary address",
|
||||||
|
style = MaterialTheme.typography.labelMedium,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Text(
|
||||||
|
text = "Account #$accountIndex Subaddress #$subAddressIndex",
|
||||||
|
style = MaterialTheme.typography.labelMedium,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
CopyableText(
|
||||||
|
text = address,
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
modifier = if (used) Modifier.alpha(0.5f) else Modifier,
|
||||||
|
)
|
||||||
|
if (walletAddress.isLastForAccount) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
) {
|
||||||
|
TextButton(onClick = onCreateSubAddressClick) {
|
||||||
|
Text(
|
||||||
|
text = "Add subaddress",
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package im.molly.monero.demo.ui
|
||||||
|
|
||||||
|
import androidx.compose.foundation.lazy.LazyListScope
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import im.molly.monero.demo.data.model.WalletAddress
|
||||||
|
|
||||||
|
fun LazyListScope.addressCardItems(
|
||||||
|
items: List<WalletAddress>,
|
||||||
|
onCreateSubAddressClick: (accountIndex: Int) -> Unit,
|
||||||
|
itemModifier: Modifier = Modifier,
|
||||||
|
) = items(
|
||||||
|
items = items,
|
||||||
|
key = { it.address },
|
||||||
|
itemContent = {
|
||||||
|
AddressCardExpanded(
|
||||||
|
walletAddress = it,
|
||||||
|
onClick = { },
|
||||||
|
onCreateSubAddressClick = {
|
||||||
|
onCreateSubAddressClick(it.address.accountIndex)
|
||||||
|
},
|
||||||
|
modifier = itemModifier,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
@ -9,7 +9,6 @@ import im.molly.monero.demo.AppModule
|
|||||||
import im.molly.monero.demo.common.Result
|
import im.molly.monero.demo.common.Result
|
||||||
import im.molly.monero.demo.common.asResult
|
import im.molly.monero.demo.common.asResult
|
||||||
import im.molly.monero.demo.data.WalletRepository
|
import im.molly.monero.demo.data.WalletRepository
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
@ -44,6 +44,10 @@ fun WalletRoute(
|
|||||||
uiState = uiState,
|
uiState = uiState,
|
||||||
onWalletConfigChange = { config -> viewModel.updateConfig(config) },
|
onWalletConfigChange = { config -> viewModel.updateConfig(config) },
|
||||||
onTransactionClick = onTransactionClick,
|
onTransactionClick = onTransactionClick,
|
||||||
|
onCreateAccountClick = { viewModel.createAccount() },
|
||||||
|
onCreateSubAddressClick = { accountIndex ->
|
||||||
|
viewModel.createSubAddress(accountIndex)
|
||||||
|
},
|
||||||
onBackClick = onBackClick,
|
onBackClick = onBackClick,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
@ -54,6 +58,8 @@ private fun WalletScreen(
|
|||||||
uiState: WalletUiState,
|
uiState: WalletUiState,
|
||||||
onWalletConfigChange: (WalletConfig) -> Unit,
|
onWalletConfigChange: (WalletConfig) -> Unit,
|
||||||
onTransactionClick: (String, Long) -> Unit,
|
onTransactionClick: (String, Long) -> Unit,
|
||||||
|
onCreateAccountClick: () -> Unit,
|
||||||
|
onCreateSubAddressClick: (Int) -> Unit,
|
||||||
onBackClick: () -> Unit,
|
onBackClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
@ -62,6 +68,8 @@ private fun WalletScreen(
|
|||||||
uiState = uiState,
|
uiState = uiState,
|
||||||
onWalletConfigChange = onWalletConfigChange,
|
onWalletConfigChange = onWalletConfigChange,
|
||||||
onTransactionClick = onTransactionClick,
|
onTransactionClick = onTransactionClick,
|
||||||
|
onCreateAccountClick = onCreateAccountClick,
|
||||||
|
onCreateSubAddressClick = onCreateSubAddressClick,
|
||||||
onBackClick = onBackClick,
|
onBackClick = onBackClick,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
@ -77,26 +85,32 @@ private fun WalletScreenLoaded(
|
|||||||
uiState: WalletUiState.Loaded,
|
uiState: WalletUiState.Loaded,
|
||||||
onWalletConfigChange: (WalletConfig) -> Unit,
|
onWalletConfigChange: (WalletConfig) -> Unit,
|
||||||
onTransactionClick: (String, Long) -> Unit,
|
onTransactionClick: (String, Long) -> Unit,
|
||||||
|
onCreateAccountClick: () -> Unit,
|
||||||
|
onCreateSubAddressClick: (Int) -> Unit,
|
||||||
onBackClick: () -> Unit,
|
onBackClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
var showRenameDialog by remember { mutableStateOf(false) }
|
var showRenameDialog by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
Scaffold(topBar = {
|
var selectedTabIndex by rememberSaveable { mutableStateOf(0) }
|
||||||
Toolbar(navigationIcon = {
|
|
||||||
IconButton(onClick = onBackClick) {
|
Scaffold(
|
||||||
Icon(
|
topBar = {
|
||||||
imageVector = AppIcons.ArrowBack,
|
Toolbar(navigationIcon = {
|
||||||
contentDescription = "Back",
|
IconButton(onClick = onBackClick) {
|
||||||
|
Icon(
|
||||||
|
imageVector = AppIcons.ArrowBack,
|
||||||
|
contentDescription = "Back",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}, actions = {
|
||||||
|
WalletKebabMenu(
|
||||||
|
onRenameClick = { showRenameDialog = true },
|
||||||
|
onDeleteClick = { },
|
||||||
)
|
)
|
||||||
}
|
})
|
||||||
}, actions = {
|
},
|
||||||
WalletKebabMenu(
|
) { padding ->
|
||||||
onRenameClick = { showRenameDialog = true },
|
|
||||||
onDeleteClick = { },
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}) { padding ->
|
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
@ -107,14 +121,13 @@ private fun WalletScreenLoaded(
|
|||||||
append(MoneroCurrency.SYMBOL + " ")
|
append(MoneroCurrency.SYMBOL + " ")
|
||||||
withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) {
|
withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) {
|
||||||
append(
|
append(
|
||||||
MoneroCurrency.Format(precision = 5).format(uiState.balance.confirmedAmount)
|
MoneroCurrency.Format(precision = 5)
|
||||||
|
.format(uiState.balance.confirmedAmount)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
Text(text = uiState.config.name, style = MaterialTheme.typography.headlineSmall)
|
Text(text = uiState.config.name, style = MaterialTheme.typography.headlineSmall)
|
||||||
|
|
||||||
var selectedTabIndex by rememberSaveable { mutableStateOf(0) }
|
|
||||||
|
|
||||||
WalletHeaderTabs(
|
WalletHeaderTabs(
|
||||||
titles = listOf("Balance", "Send", "Receive", "History"),
|
titles = listOf("Balance", "Send", "Receive", "History"),
|
||||||
selectedTabIndex = selectedTabIndex,
|
selectedTabIndex = selectedTabIndex,
|
||||||
@ -132,19 +145,31 @@ private fun WalletScreenLoaded(
|
|||||||
1 -> {} // TODO
|
1 -> {} // TODO
|
||||||
|
|
||||||
2 -> {
|
2 -> {
|
||||||
Column(
|
val scrollState = rememberLazyListState()
|
||||||
modifier = modifier
|
|
||||||
.fillMaxWidth()
|
LazyColumn(
|
||||||
.padding(16.dp)
|
state = scrollState,
|
||||||
) {
|
) {
|
||||||
Text(
|
addressCardItems(
|
||||||
text = "Primary address",
|
items = uiState.addresses,
|
||||||
style = MaterialTheme.typography.labelMedium,
|
onCreateSubAddressClick = onCreateSubAddressClick,
|
||||||
)
|
|
||||||
CopyableText(
|
|
||||||
text = uiState.config.publicAddress,
|
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
|
||||||
)
|
)
|
||||||
|
item {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
) {
|
||||||
|
OutlinedButton(
|
||||||
|
onClick = onCreateAccountClick,
|
||||||
|
modifier = modifier.padding(bottom = 16.dp),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "Create new account",
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,15 +311,19 @@ private fun WalletScreenPopulated(
|
|||||||
network = ledger.publicAddress.network,
|
network = ledger.publicAddress.network,
|
||||||
balance = ledger.balance,
|
balance = ledger.balance,
|
||||||
blockchainTime = ledger.checkedAt,
|
blockchainTime = ledger.checkedAt,
|
||||||
|
addresses = emptyList(),
|
||||||
transactions = emptyList(),
|
transactions = emptyList(),
|
||||||
),
|
),
|
||||||
onWalletConfigChange = {},
|
onWalletConfigChange = {},
|
||||||
onTransactionClick = { _: String, _: Long -> },
|
onTransactionClick = { _: String, _: Long -> },
|
||||||
|
onCreateAccountClick = {},
|
||||||
|
onCreateSubAddressClick = {},
|
||||||
onBackClick = {},
|
onBackClick = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class WalletScreenPreviewParameterProvider : PreviewParameterProvider<Ledger> {
|
private class WalletScreenPreviewParameterProvider :
|
||||||
|
PreviewParameterProvider<Ledger> {
|
||||||
override val values = sequenceOf(PreviewParameterData.ledger)
|
override val values = sequenceOf(PreviewParameterData.ledger)
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import im.molly.monero.demo.AppModule
|
|||||||
import im.molly.monero.demo.common.Result
|
import im.molly.monero.demo.common.Result
|
||||||
import im.molly.monero.demo.common.asResult
|
import im.molly.monero.demo.common.asResult
|
||||||
import im.molly.monero.demo.data.WalletRepository
|
import im.molly.monero.demo.data.WalletRepository
|
||||||
|
import im.molly.monero.demo.data.model.WalletAddress
|
||||||
import im.molly.monero.demo.data.model.WalletConfig
|
import im.molly.monero.demo.data.model.WalletConfig
|
||||||
import im.molly.monero.demo.data.model.WalletTransaction
|
import im.molly.monero.demo.data.model.WalletTransaction
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
@ -18,7 +19,7 @@ import kotlinx.coroutines.launch
|
|||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
class WalletViewModel(
|
class WalletViewModel(
|
||||||
walletId: Long,
|
private val walletId: Long,
|
||||||
private val walletRepository: WalletRepository = AppModule.walletRepository,
|
private val walletRepository: WalletRepository = AppModule.walletRepository,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
@ -46,6 +47,18 @@ class WalletViewModel(
|
|||||||
walletRepository.updateWalletConfig(config)
|
walletRepository.updateWalletConfig(config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun createAccount() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
walletRepository.getWallet(walletId).createAccount()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createSubAddress(accountIndex: Int) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
walletRepository.getWallet(walletId).createSubAddressForAccount(accountIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun walletUiState(
|
private fun walletUiState(
|
||||||
@ -61,14 +74,27 @@ private fun walletUiState(
|
|||||||
is Result.Success -> {
|
is Result.Success -> {
|
||||||
val config = result.data.first
|
val config = result.data.first
|
||||||
val ledger = result.data.second
|
val ledger = result.data.second
|
||||||
val balance = ledger.balance
|
val addresses =
|
||||||
val blockchainTime = ledger.checkedAt
|
ledger.accountAddresses.groupBy { it.accountIndex }.flatMap { (_, group) ->
|
||||||
|
group.sortedBy { it.subAddressIndex }.mapIndexed { index, address ->
|
||||||
|
WalletAddress(
|
||||||
|
address = address,
|
||||||
|
used = address.isAddressUsed(ledger.transactions.values),
|
||||||
|
isLastForAccount = index == group.size - 1,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
val transactions =
|
val transactions =
|
||||||
ledger.transactions
|
ledger.transactions.map { WalletTransaction(config.id, it.value) }
|
||||||
.map { WalletTransaction(config.id, it.value) }
|
|
||||||
.sortedByDescending { it.transaction.blockTimestamp ?: Instant.MAX }
|
.sortedByDescending { it.transaction.blockTimestamp ?: Instant.MAX }
|
||||||
val network = ledger.publicAddress.network
|
WalletUiState.Loaded(
|
||||||
WalletUiState.Loaded(config, network, blockchainTime, balance, transactions)
|
config = config,
|
||||||
|
network = ledger.publicAddress.network,
|
||||||
|
blockchainTime = ledger.checkedAt,
|
||||||
|
balance = ledger.balance,
|
||||||
|
addresses = addresses,
|
||||||
|
transactions = transactions,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
is Result.Loading -> {
|
is Result.Loading -> {
|
||||||
@ -88,6 +114,7 @@ sealed interface WalletUiState {
|
|||||||
val network: MoneroNetwork,
|
val network: MoneroNetwork,
|
||||||
val blockchainTime: BlockchainTime,
|
val blockchainTime: BlockchainTime,
|
||||||
val balance: Balance,
|
val balance: Balance,
|
||||||
|
val addresses: List<WalletAddress>,
|
||||||
val transactions: List<WalletTransaction>,
|
val transactions: List<WalletTransaction>,
|
||||||
) : WalletUiState
|
) : WalletUiState
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ import androidx.compose.ui.text.TextStyle
|
|||||||
fun CopyableText(
|
fun CopyableText(
|
||||||
text: String,
|
text: String,
|
||||||
style: TextStyle,
|
style: TextStyle,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val clipboardManager = LocalClipboardManager.current
|
val clipboardManager = LocalClipboardManager.current
|
||||||
@ -23,7 +24,7 @@ fun CopyableText(
|
|||||||
Text(
|
Text(
|
||||||
text = text,
|
text = text,
|
||||||
style = style,
|
style = style,
|
||||||
modifier = Modifier.combinedClickable(
|
modifier = modifier.combinedClickable(
|
||||||
onClick = {},
|
onClick = {},
|
||||||
onLongClick = {
|
onLongClick = {
|
||||||
clipboardManager.setText(AnnotatedString(text))
|
clipboardManager.setText(AnnotatedString(text))
|
||||||
|
@ -10,7 +10,7 @@ interface IWallet {
|
|||||||
String getPublicAddress();
|
String getPublicAddress();
|
||||||
void addBalanceListener(in IBalanceListener listener);
|
void addBalanceListener(in IBalanceListener listener);
|
||||||
void removeBalanceListener(in IBalanceListener listener);
|
void removeBalanceListener(in IBalanceListener listener);
|
||||||
oneway void getOrCreateAddress(int accountIndex, int subAddressIndex, in IWalletCallbacks callback);
|
oneway void addDetachedSubAddress(int accountIndex, int subAddressIndex, in IWalletCallbacks callback);
|
||||||
oneway void createAccount(in IWalletCallbacks callback);
|
oneway void createAccount(in IWalletCallbacks callback);
|
||||||
oneway void createSubAddressForAccount(int accountIndex, in IWalletCallbacks callback);
|
oneway void createSubAddressForAccount(int accountIndex, in IWalletCallbacks callback);
|
||||||
oneway void getAllAddresses(in IWalletCallbacks callback);
|
oneway void getAllAddresses(in IWalletCallbacks callback);
|
||||||
|
@ -134,6 +134,14 @@ std::string FormatAccountAddress(
|
|||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Wallet::addDetachedSubAddress(uint32_t index_major, uint32_t index_minor) {
|
||||||
|
return suspendRefreshAndRunLocked([&]() {
|
||||||
|
cryptonote::subaddress_index index = {index_major, index_minor};
|
||||||
|
m_wallet.create_one_off_subaddress(index);
|
||||||
|
return addSubaddressInternal(index);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
std::string Wallet::createSubAddressAccount() {
|
std::string Wallet::createSubAddressAccount() {
|
||||||
return suspendRefreshAndRunLocked([&]() {
|
return suspendRefreshAndRunLocked([&]() {
|
||||||
uint32_t index_major = m_wallet.get_num_subaddress_accounts();
|
uint32_t index_major = m_wallet.get_num_subaddress_accounts();
|
||||||
@ -150,14 +158,6 @@ std::string Wallet::createSubAddress(uint32_t index_major) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Wallet::addSubAddress(uint32_t index_major, uint32_t index_minor) {
|
|
||||||
return suspendRefreshAndRunLocked([&]() {
|
|
||||||
cryptonote::subaddress_index index = {index_major, index_minor};
|
|
||||||
m_wallet.create_one_off_subaddress(index);
|
|
||||||
return addSubaddressInternal(index);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Wallet::addSubaddressInternal(const cryptonote::subaddress_index& index) {
|
std::string Wallet::addSubaddressInternal(const cryptonote::subaddress_index& index) {
|
||||||
std::string subaddress = m_wallet.get_subaddress_as_str(index);
|
std::string subaddress = m_wallet.get_subaddress_as_str(index);
|
||||||
std::unique_lock<std::mutex> lock(m_subaddresses_mutex);
|
std::unique_lock<std::mutex> lock(m_subaddresses_mutex);
|
||||||
@ -704,7 +704,7 @@ Java_im_molly_monero_WalletNative_nativeGetPublicAddress(
|
|||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
JNIEXPORT jstring JNICALL
|
JNIEXPORT jstring JNICALL
|
||||||
Java_im_molly_monero_WalletNative_nativeAddSubAddress(
|
Java_im_molly_monero_WalletNative_nativeAddDetachedSubAddress(
|
||||||
JNIEnv* env,
|
JNIEnv* env,
|
||||||
jobject thiz,
|
jobject thiz,
|
||||||
jlong handle,
|
jlong handle,
|
||||||
@ -712,7 +712,7 @@ Java_im_molly_monero_WalletNative_nativeAddSubAddress(
|
|||||||
jint sub_address_minor) {
|
jint sub_address_minor) {
|
||||||
auto* wallet = reinterpret_cast<Wallet*>(handle);
|
auto* wallet = reinterpret_cast<Wallet*>(handle);
|
||||||
return NativeToJavaString(
|
return NativeToJavaString(
|
||||||
env, wallet->addSubAddress(sub_address_major, sub_address_minor));
|
env, wallet->addDetachedSubAddress(sub_address_major, sub_address_minor));
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
|
@ -94,9 +94,9 @@ class Wallet : i_wallet2_callback {
|
|||||||
void cancelRefresh();
|
void cancelRefresh();
|
||||||
void setRefreshSince(long height_or_timestamp);
|
void setRefreshSince(long height_or_timestamp);
|
||||||
|
|
||||||
|
std::string addDetachedSubAddress(uint32_t index_major, uint32_t index_minor);
|
||||||
std::string createSubAddressAccount();
|
std::string createSubAddressAccount();
|
||||||
std::string createSubAddress(uint32_t index_major);
|
std::string createSubAddress(uint32_t index_major);
|
||||||
std::string addSubAddress(uint32_t index_major, uint32_t index_minor);
|
|
||||||
|
|
||||||
std::unique_ptr<PendingTransfer> createPayment(
|
std::unique_ptr<PendingTransfer> createPayment(
|
||||||
const std::vector<std::string>& addresses,
|
const std::vector<std::string>& addresses,
|
||||||
|
@ -26,6 +26,16 @@ data class AccountAddress(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun isAddressUsed(transactions: Iterable<Transaction>): Boolean {
|
||||||
|
return transactions.any { tx ->
|
||||||
|
tx.sent.any { enote ->
|
||||||
|
enote.owner == this
|
||||||
|
} || tx.received.any { enote ->
|
||||||
|
enote.owner == this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun parseWithIndexes(addressString: String): AccountAddress {
|
fun parseWithIndexes(addressString: String): AccountAddress {
|
||||||
val parts = addressString.split("/")
|
val parts = addressString.split("/")
|
||||||
|
@ -5,9 +5,11 @@ import im.molly.monero.internal.consolidateTransactions
|
|||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.channels.awaitClose
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
import kotlinx.coroutines.channels.onFailure
|
import kotlinx.coroutines.channels.onFailure
|
||||||
|
import kotlinx.coroutines.channels.trySendBlocking
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.callbackFlow
|
import kotlinx.coroutines.flow.callbackFlow
|
||||||
|
import kotlinx.coroutines.flow.conflate
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.flow
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
@ -25,9 +27,9 @@ class MoneroWallet internal constructor(
|
|||||||
|
|
||||||
var dataStore by storageAdapter::dataStore
|
var dataStore by storageAdapter::dataStore
|
||||||
|
|
||||||
suspend fun getOrCreateAddress(accountIndex: Int, subAddressIndex: Int): AccountAddress =
|
suspend fun addDetachedSubAddress(accountIndex: Int, subAddressIndex: Int): AccountAddress =
|
||||||
suspendCancellableCoroutine { continuation ->
|
suspendCancellableCoroutine { continuation ->
|
||||||
wallet.getOrCreateAddress(
|
wallet.addDetachedSubAddress(
|
||||||
accountIndex,
|
accountIndex,
|
||||||
subAddressIndex,
|
subAddressIndex,
|
||||||
object : BaseWalletCallbacks() {
|
object : BaseWalletCallbacks() {
|
||||||
@ -91,14 +93,14 @@ class MoneroWallet internal constructor(
|
|||||||
accountAddresses = accountAddresses,
|
accountAddresses = accountAddresses,
|
||||||
blockchainContext = blockchainTime,
|
blockchainContext = blockchainTime,
|
||||||
)
|
)
|
||||||
lastKnownLedger = Ledger(
|
val ledger = Ledger(
|
||||||
publicAddress = publicAddress,
|
publicAddress = publicAddress,
|
||||||
accountAddresses = accountAddresses,
|
accountAddresses = accountAddresses,
|
||||||
transactions = txById,
|
transactions = txById,
|
||||||
enotes = enotes,
|
enotes = enotes,
|
||||||
checkedAt = blockchainTime,
|
checkedAt = blockchainTime,
|
||||||
)
|
)
|
||||||
sendLedger(lastKnownLedger)
|
sendLedger(ledger)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRefresh(blockchainTime: BlockchainTime) {
|
override fun onRefresh(blockchainTime: BlockchainTime) {
|
||||||
@ -114,16 +116,16 @@ class MoneroWallet internal constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun sendLedger(ledger: Ledger) {
|
private fun sendLedger(ledger: Ledger) {
|
||||||
trySend(ledger).onFailure {
|
lastKnownLedger = ledger
|
||||||
logger.e("Too many ledger updates, channel capacity exceeded", it)
|
// Shouldn't block as we conflate the flow.
|
||||||
}
|
trySendBlocking(ledger)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wallet.addBalanceListener(listener)
|
wallet.addBalanceListener(listener)
|
||||||
|
|
||||||
awaitClose { wallet.removeBalanceListener(listener) }
|
awaitClose { wallet.removeBalanceListener(listener) }
|
||||||
}
|
}.conflate()
|
||||||
|
|
||||||
suspend fun awaitRefresh(
|
suspend fun awaitRefresh(
|
||||||
ignoreMiningRewards: Boolean = true,
|
ignoreMiningRewards: Boolean = true,
|
||||||
|
@ -236,13 +236,13 @@ internal class WalletNative private constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getOrCreateAddress(
|
override fun addDetachedSubAddress(
|
||||||
accountIndex: Int,
|
accountIndex: Int,
|
||||||
subAddressIndex: Int,
|
subAddressIndex: Int,
|
||||||
callback: IWalletCallbacks?,
|
callback: IWalletCallbacks?,
|
||||||
) {
|
) {
|
||||||
scope.launch(ioDispatcher) {
|
scope.launch(ioDispatcher) {
|
||||||
val subAddress = nativeAddSubAddress(handle, accountIndex, subAddressIndex)
|
val subAddress = nativeAddDetachedSubAddress(handle, accountIndex, subAddressIndex)
|
||||||
notifyAddressCreation(subAddress, callback)
|
notifyAddressCreation(subAddress, callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -370,7 +370,7 @@ internal class WalletNative private constructor(
|
|||||||
const val REFRESH_ERROR: Int = 3
|
const val REFRESH_ERROR: Int = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
private external fun nativeAddSubAddress(
|
private external fun nativeAddDetachedSubAddress(
|
||||||
handle: Long,
|
handle: Long,
|
||||||
subAddressMajor: Int,
|
subAddressMajor: Int,
|
||||||
subAddressMinor: Int,
|
subAddressMinor: Int,
|
||||||
|
Loading…
Reference in New Issue
Block a user