diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..8ac4356 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +# Root .editorconfig file +root = true + +[*.{kt,kts}] +indent_style = space +max_line_length = 120 + +ktlint_standard_trailing-comma-on-call-site = disabled +ktlint_standard_multiline-expression-wrapping = disabled +ktlint_standard_string-template-indent = disabled +ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than = 5 +ktlint_function_naming_ignore_when_annotated_with=Composable +ktlint_standard_function-expression-body = disabled +ktlint_standard_class-signature = disabled diff --git a/.github/workflows/ktlint.yaml b/.github/workflows/ktlint.yaml new file mode 100644 index 0000000..fb181fc --- /dev/null +++ b/.github/workflows/ktlint.yaml @@ -0,0 +1,21 @@ +name: Ktlint Check + +on: [workflow_dispatch, pull_request] + +jobs: + ktlint: + name: "Run Ktlint Check" + runs-on: ubuntu-22.04 + + steps: + - name: "Checkout branch" + uses: actions/checkout@v4 + + - name: "Set up JDK 17" + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 17 + + - name: "Run Ktlint Check" + run: ./gradlew ktlintCheck diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ab38dfe..c44674f 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,9 +1,12 @@ +import org.jlleitschuh.gradle.ktlint.reporter.ReporterType + plugins { - id("com.android.application") - id("org.jetbrains.kotlin.android") - id("com.google.protobuf") - id("org.jetbrains.kotlin.plugin.compose") - id("org.jetbrains.kotlin.plugin.serialization") + id("com.android.application") version "8.7.1" + id("org.jetbrains.kotlin.android") version "2.1.10" + id("org.jetbrains.kotlin.plugin.compose") version "2.1.10" + id("org.jetbrains.kotlin.plugin.serialization") version "2.1.10" + id("com.google.protobuf") version "0.9.4" + id("org.jlleitschuh.gradle.ktlint") version "12.1.2" } // This is the version of the app that is displayed in the UI on the drawer. @@ -43,7 +46,6 @@ android { kotlinOptions { jvmTarget = "17" } - } dependencies { @@ -83,9 +85,9 @@ dependencies { implementation("com.google.zxing:core:3.4.1") // Tests - testImplementation ("junit:junit:4.13.2") - androidTestImplementation ("androidx.test.ext:junit:1.2.1") - androidTestImplementation ("androidx.test.espresso:espresso-core:3.6.1") + testImplementation("junit:junit:4.13.2") + androidTestImplementation("androidx.test.ext:junit:1.2.1") + androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1") } protobuf { @@ -103,3 +105,11 @@ protobuf { } } } + +ktlint { + version = "1.5.0" + ignoreFailures = false + reporters { + reporter(ReporterType.PLAIN).apply { outputToConsole = true } + } +} diff --git a/app/src/main/java/org/bitcoindevkit/devkitwallet/domain/BlockchainClient.kt b/app/src/main/java/org/bitcoindevkit/devkitwallet/domain/BlockchainClient.kt index f7d5549..520f80f 100644 --- a/app/src/main/java/org/bitcoindevkit/devkitwallet/domain/BlockchainClient.kt +++ b/app/src/main/java/org/bitcoindevkit/devkitwallet/domain/BlockchainClient.kt @@ -8,8 +8,8 @@ package org.bitcoindevkit.devkitwallet.domain import org.bitcoindevkit.FullScanRequest import org.bitcoindevkit.SyncRequest import org.bitcoindevkit.Transaction -import org.bitcoindevkit.EsploraClient as BdkEsploraClient import org.bitcoindevkit.Update +import org.bitcoindevkit.EsploraClient as BdkEsploraClient interface BlockchainClient { fun clientId(): String diff --git a/app/src/main/java/org/bitcoindevkit/devkitwallet/domain/BlockchainClientsConfig.kt b/app/src/main/java/org/bitcoindevkit/devkitwallet/domain/BlockchainClientsConfig.kt index 8973a15..5f50d87 100644 --- a/app/src/main/java/org/bitcoindevkit/devkitwallet/domain/BlockchainClientsConfig.kt +++ b/app/src/main/java/org/bitcoindevkit/devkitwallet/domain/BlockchainClientsConfig.kt @@ -17,7 +17,11 @@ class BlockchainClientsConfig { fun addClient(client: BlockchainClient, setDefault: Boolean) { allClients.forEach { - if (it.clientId() == client.clientId()) throw IllegalArgumentException("Client with url ${client.clientId()} already exists") + if (it.clientId() == client.clientId()) { + throw IllegalArgumentException( + "Client with url ${client.clientId()} already exists" + ) + } } if (allClients.size >= 8) throw IllegalArgumentException("Maximum number of clients (8) reached") allClients.add(client) diff --git a/app/src/main/java/org/bitcoindevkit/devkitwallet/domain/CurrencyUnit.kt b/app/src/main/java/org/bitcoindevkit/devkitwallet/domain/CurrencyUnit.kt index 9243458..8e82b91 100644 --- a/app/src/main/java/org/bitcoindevkit/devkitwallet/domain/CurrencyUnit.kt +++ b/app/src/main/java/org/bitcoindevkit/devkitwallet/domain/CurrencyUnit.kt @@ -7,5 +7,5 @@ package org.bitcoindevkit.devkitwallet.domain enum class CurrencyUnit { Bitcoin, - Satoshi + Satoshi, } diff --git a/app/src/main/java/org/bitcoindevkit/devkitwallet/domain/DwLogger.kt b/app/src/main/java/org/bitcoindevkit/devkitwallet/domain/DwLogger.kt index 6e095cf..7b65a32 100644 --- a/app/src/main/java/org/bitcoindevkit/devkitwallet/domain/DwLogger.kt +++ b/app/src/main/java/org/bitcoindevkit/devkitwallet/domain/DwLogger.kt @@ -1,3 +1,8 @@ +/* + * Copyright 2021-2025 thunderbiscuit and contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the ./LICENSE file. + */ + package org.bitcoindevkit.devkitwallet.domain object DwLogger { diff --git a/app/src/main/java/org/bitcoindevkit/devkitwallet/domain/ActiveWalletsRepository.kt b/app/src/main/java/org/bitcoindevkit/devkitwallet/domain/UserPreferencesRepository.kt similarity index 96% rename from app/src/main/java/org/bitcoindevkit/devkitwallet/domain/ActiveWalletsRepository.kt rename to app/src/main/java/org/bitcoindevkit/devkitwallet/domain/UserPreferencesRepository.kt index 46451b3..7d57a5c 100644 --- a/app/src/main/java/org/bitcoindevkit/devkitwallet/domain/ActiveWalletsRepository.kt +++ b/app/src/main/java/org/bitcoindevkit/devkitwallet/domain/UserPreferencesRepository.kt @@ -42,7 +42,8 @@ class UserPreferencesRepository( wallet } } - currentPreferences.toBuilder() + currentPreferences + .toBuilder() .clearWallets() .addAllWallets(updatedWalletsList) .build() diff --git a/app/src/main/java/org/bitcoindevkit/devkitwallet/domain/Wallet.kt b/app/src/main/java/org/bitcoindevkit/devkitwallet/domain/Wallet.kt index e78745d..9b274df 100644 --- a/app/src/main/java/org/bitcoindevkit/devkitwallet/domain/Wallet.kt +++ b/app/src/main/java/org/bitcoindevkit/devkitwallet/domain/Wallet.kt @@ -9,18 +9,14 @@ import android.util.Log import kotlinx.coroutines.runBlocking import org.bitcoindevkit.Address import org.bitcoindevkit.AddressInfo -import org.rustbitcoin.bitcoin.Amount import org.bitcoindevkit.CanonicalTx import org.bitcoindevkit.ChainPosition import org.bitcoindevkit.Connection import org.bitcoindevkit.Descriptor import org.bitcoindevkit.DescriptorSecretKey -import org.rustbitcoin.bitcoin.FeeRate import org.bitcoindevkit.KeychainKind import org.bitcoindevkit.Mnemonic -import org.rustbitcoin.bitcoin.Network import org.bitcoindevkit.Psbt -import org.rustbitcoin.bitcoin.Script import org.bitcoindevkit.TxBuilder import org.bitcoindevkit.Update import org.bitcoindevkit.WordCount @@ -34,8 +30,12 @@ import org.bitcoindevkit.devkitwallet.data.TxDetails import org.bitcoindevkit.devkitwallet.domain.utils.intoDomain import org.bitcoindevkit.devkitwallet.domain.utils.intoProto import org.bitcoindevkit.devkitwallet.presentation.viewmodels.mvi.Recipient -import org.bitcoindevkit.Wallet as BdkWallet +import org.rustbitcoin.bitcoin.Amount +import org.rustbitcoin.bitcoin.FeeRate +import org.rustbitcoin.bitcoin.Network +import org.rustbitcoin.bitcoin.Script import java.util.UUID +import org.bitcoindevkit.Wallet as BdkWallet private const val TAG = "Wallet" @@ -46,7 +46,7 @@ class Wallet private constructor( private var fullScanCompleted: Boolean, private val walletId: String, private val userPreferencesRepository: UserPreferencesRepository, - blockchainClientsConfig: BlockchainClientsConfig + blockchainClientsConfig: BlockchainClientsConfig, ) { private var currentBlockchainClient: BlockchainClient? = blockchainClientsConfig.getClient() @@ -54,11 +54,7 @@ class Wallet private constructor( return recoveryPhrase.split(" ") } - fun createTransaction( - recipientList: List, - feeRate: FeeRate, - opReturnMsg: String? - ): Psbt { + fun createTransaction(recipientList: List, feeRate: FeeRate, opReturnMsg: String?): Psbt { // technique 1 for adding a list of recipients to the TxBuilder // var txBuilder = TxBuilder() // for (recipient in recipientList) { @@ -67,11 +63,12 @@ class Wallet private constructor( // txBuilder = txBuilder.feeRate(satPerVbyte = fee_rate) // technique 2 for adding a list of recipients to the TxBuilder - var txBuilder = recipientList.fold(TxBuilder()) { builder, recipient -> - // val address = Address(recipient.address) - val scriptPubKey: Script = Address(recipient.address, Network.TESTNET).scriptPubkey() - builder.addRecipient(scriptPubKey, Amount.fromSat(recipient.amount)) - } + var txBuilder = + recipientList.fold(TxBuilder()) { builder, recipient -> + // val address = Address(recipient.address) + val scriptPubKey: Script = Address(recipient.address, Network.TESTNET).scriptPubkey() + builder.addRecipient(scriptPubKey, Amount.fromSat(recipient.amount)) + } // if (!opReturnMsg.isNullOrEmpty()) { // txBuilder = txBuilder.addData(opReturnMsg.toByteArray(charset = Charsets.UTF_8).asUByteArray().toList()) // } @@ -111,11 +108,13 @@ class Wallet private constructor( } fun broadcast(signedPsbt: Psbt): String { - currentBlockchainClient?.broadcast(signedPsbt.extractTx()) ?: throw IllegalStateException("Blockchain client not initialized") + currentBlockchainClient?.broadcast(signedPsbt.extractTx()) ?: throw IllegalStateException( + "Blockchain client not initialized" + ) return signedPsbt.extractTx().computeTxid() } - private fun getAllTransactions(): List = wallet.transactions() + private fun getAllTransactions(): List = wallet.transactions() fun getAllTxDetails(): List { val transactions = getAllTransactions() @@ -136,11 +135,27 @@ class Wallet private constructor( Log.e(TAG, "Error calculating fee for tx $txid: $e") } - val (confirmationBlock, confirmationTimestamp, pending) = when (val position = tx.chainPosition) { - is ChainPosition.Unconfirmed -> Triple(null, null, true) - is ChainPosition.Confirmed -> Triple(ConfirmationBlock(position.confirmationBlockTime.blockId.height), Timestamp(position.confirmationBlockTime.confirmationTime), false) - } - TxDetails(tx.transaction, txid, sent.toSat(), received.toSat(), fee?.toSat() ?: 0uL, feeRate, pending, confirmationBlock, confirmationTimestamp) + val (confirmationBlock, confirmationTimestamp, pending) = + when (val position = tx.chainPosition) { + is ChainPosition.Unconfirmed -> Triple(null, null, true) + is ChainPosition.Confirmed -> + Triple( + ConfirmationBlock(position.confirmationBlockTime.blockId.height), + Timestamp(position.confirmationBlockTime.confirmationTime), + false + ) + } + TxDetails( + tx.transaction, + txid, + sent.toSat(), + received.toSat(), + fee?.toSat() ?: 0uL, + feeRate, + pending, + confirmationBlock, + confirmationTimestamp + ) } } @@ -156,10 +171,11 @@ class Wallet private constructor( private fun fullScan() { val fullScanRequest = wallet.startFullScan().build() - val update: Update = currentBlockchainClient?.fullScan( - fullScanRequest = fullScanRequest, - stopGap = 20u, - ) ?: throw IllegalStateException("Blockchain client not initialized") + val update: Update = + currentBlockchainClient?.fullScan( + fullScanRequest = fullScanRequest, + stopGap = 20u, + ) ?: throw IllegalStateException("Blockchain client not initialized") wallet.applyUpdate(update) wallet.persist(connection) } @@ -175,9 +191,10 @@ class Wallet private constructor( } else { Log.i(TAG, "Just a normal sync!") val syncRequest = wallet.startSyncWithRevealedSpks().build() - val update = currentBlockchainClient?.sync( - syncRequest = syncRequest, - ) ?: throw IllegalStateException("Blockchain client not initialized") + val update = + currentBlockchainClient?.sync( + syncRequest = syncRequest, + ) ?: throw IllegalStateException("Blockchain client not initialized") wallet.applyUpdate(update) wallet.persist(connection) } @@ -188,7 +205,7 @@ class Wallet private constructor( fun getNewAddress(): AddressInfo = wallet.revealNextAddress(KeychainKind.EXTERNAL) fun getClientEndpoint(): String = currentBlockchainClient?.endpoint() ?: "No active client" - + // fun setElectrumSettings(electrumSettings: ElectrumSettings) { // when (electrumSettings) { // ElectrumSettings.DEFAULT -> electrumServer.useDefaultElectrum() @@ -204,42 +221,47 @@ class Wallet private constructor( ): Wallet { val mnemonic = Mnemonic(WordCount.WORDS12) val bip32ExtendedRootKey = DescriptorSecretKey(newWalletConfig.network, mnemonic, null) - val descriptor: Descriptor = createScriptAppropriateDescriptor( - newWalletConfig.scriptType, - bip32ExtendedRootKey, - newWalletConfig.network, - KeychainKind.EXTERNAL - ) - val changeDescriptor: Descriptor = createScriptAppropriateDescriptor( - newWalletConfig.scriptType, - bip32ExtendedRootKey, - newWalletConfig.network, - KeychainKind.INTERNAL - ) + val descriptor: Descriptor = + createScriptAppropriateDescriptor( + newWalletConfig.scriptType, + bip32ExtendedRootKey, + newWalletConfig.network, + KeychainKind.EXTERNAL + ) + val changeDescriptor: Descriptor = + createScriptAppropriateDescriptor( + newWalletConfig.scriptType, + bip32ExtendedRootKey, + newWalletConfig.network, + KeychainKind.INTERNAL + ) val walletId = UUID.randomUUID().toString() - val connection = Connection("$internalAppFilesPath/wallet-${walletId.take(8)}.sqlite3",) + val connection = Connection("$internalAppFilesPath/wallet-${walletId.take(8)}.sqlite3") // Create SingleWallet object for saving to datastore - val newWalletForDatastore: SingleWallet = SingleWallet.newBuilder() - .setId(walletId) - .setName(newWalletConfig.name) - .setNetwork(newWalletConfig.network.intoProto()) - .setScriptType(newWalletConfig.scriptType) - .setDescriptor(descriptor.toStringWithSecret()) - .setChangeDescriptor(changeDescriptor.toStringWithSecret()) - .setRecoveryPhrase(mnemonic.toString()) - .build() + val newWalletForDatastore: SingleWallet = + SingleWallet + .newBuilder() + .setId(walletId) + .setName(newWalletConfig.name) + .setNetwork(newWalletConfig.network.intoProto()) + .setScriptType(newWalletConfig.scriptType) + .setDescriptor(descriptor.toStringWithSecret()) + .setChangeDescriptor(changeDescriptor.toStringWithSecret()) + .setRecoveryPhrase(mnemonic.toString()) + .build() // TODO: launch this correctly, not on the main thread // Save the new wallet to the datastore runBlocking { userPreferencesRepository.updateActiveWallets(newWalletForDatastore) } - val bdkWallet = BdkWallet( - descriptor = descriptor, - changeDescriptor = changeDescriptor, - network = newWalletConfig.network, - connection = connection, - ) + val bdkWallet = + BdkWallet( + descriptor = descriptor, + changeDescriptor = changeDescriptor, + network = newWalletConfig.network, + connection = connection, + ) return Wallet( wallet = bdkWallet, @@ -260,11 +282,12 @@ class Wallet private constructor( val descriptor = Descriptor(activeWallet.descriptor, activeWallet.network.intoDomain()) val changeDescriptor = Descriptor(activeWallet.changeDescriptor, activeWallet.network.intoDomain()) val connection = Connection("$internalAppFilesPath/wallet-${activeWallet.id.take(8)}.sqlite3") - val bdkWallet = BdkWallet.load( - descriptor = descriptor, - changeDescriptor = changeDescriptor, - connection = connection, - ) + val bdkWallet = + BdkWallet.load( + descriptor = descriptor, + changeDescriptor = changeDescriptor, + connection = connection, + ) return Wallet( wallet = bdkWallet, @@ -284,42 +307,47 @@ class Wallet private constructor( ): Wallet { val mnemonic = Mnemonic.fromString(recoverWalletConfig.recoveryPhrase) val bip32ExtendedRootKey = DescriptorSecretKey(recoverWalletConfig.network, mnemonic, null) - val descriptor: Descriptor = createScriptAppropriateDescriptor( - recoverWalletConfig.scriptType, - bip32ExtendedRootKey, - recoverWalletConfig.network, - KeychainKind.EXTERNAL - ) - val changeDescriptor: Descriptor = createScriptAppropriateDescriptor( - recoverWalletConfig.scriptType, - bip32ExtendedRootKey, - recoverWalletConfig.network, - KeychainKind.INTERNAL - ) + val descriptor: Descriptor = + createScriptAppropriateDescriptor( + recoverWalletConfig.scriptType, + bip32ExtendedRootKey, + recoverWalletConfig.network, + KeychainKind.EXTERNAL + ) + val changeDescriptor: Descriptor = + createScriptAppropriateDescriptor( + recoverWalletConfig.scriptType, + bip32ExtendedRootKey, + recoverWalletConfig.network, + KeychainKind.INTERNAL + ) val walletId = UUID.randomUUID().toString() - val connection = Connection("$internalAppFilesPath/wallet-${walletId.take(8)}.sqlite3",) + val connection = Connection("$internalAppFilesPath/wallet-${walletId.take(8)}.sqlite3") // Create SingleWallet object for saving to datastore - val newWalletForDatastore: SingleWallet = SingleWallet.newBuilder() - .setId(walletId) - .setName(recoverWalletConfig.name) - .setNetwork(recoverWalletConfig.network.intoProto()) - .setScriptType(recoverWalletConfig.scriptType) - .setDescriptor(descriptor.toStringWithSecret()) - .setChangeDescriptor(changeDescriptor.toStringWithSecret()) - .setRecoveryPhrase(mnemonic.toString()) - .build() + val newWalletForDatastore: SingleWallet = + SingleWallet + .newBuilder() + .setId(walletId) + .setName(recoverWalletConfig.name) + .setNetwork(recoverWalletConfig.network.intoProto()) + .setScriptType(recoverWalletConfig.scriptType) + .setDescriptor(descriptor.toStringWithSecret()) + .setChangeDescriptor(changeDescriptor.toStringWithSecret()) + .setRecoveryPhrase(mnemonic.toString()) + .build() // TODO: launch this correctly, not on the main thread // Save the new wallet to the datastore runBlocking { userPreferencesRepository.updateActiveWallets(newWalletForDatastore) } - val bdkWallet = BdkWallet( - descriptor = descriptor, - changeDescriptor = changeDescriptor, - connection = connection, - network = recoverWalletConfig.network, - ) + val bdkWallet = + BdkWallet( + descriptor = descriptor, + changeDescriptor = changeDescriptor, + connection = connection, + network = recoverWalletConfig.network, + ) return Wallet( wallet = bdkWallet, @@ -338,7 +366,7 @@ fun createScriptAppropriateDescriptor( scriptType: ActiveWalletScriptType, bip32ExtendedRootKey: DescriptorSecretKey, network: Network, - keychain: KeychainKind + keychain: KeychainKind, ): Descriptor { return if (keychain == KeychainKind.EXTERNAL) { when (scriptType) { diff --git a/app/src/main/java/org/bitcoindevkit/devkitwallet/domain/utils/FormatInBtc.kt b/app/src/main/java/org/bitcoindevkit/devkitwallet/domain/utils/FormatInBtc.kt index 014c880..6304607 100644 --- a/app/src/main/java/org/bitcoindevkit/devkitwallet/domain/utils/FormatInBtc.kt +++ b/app/src/main/java/org/bitcoindevkit/devkitwallet/domain/utils/FormatInBtc.kt @@ -8,10 +8,11 @@ package org.bitcoindevkit.devkitwallet.domain.utils import java.text.DecimalFormat fun ULong?.formatInBtc(): String { - val balanceInSats = if (this == 0UL || this == null) { - 0F - } else { - this.toFloat().div(100_000_000) - } + val balanceInSats = + if (this == 0UL || this == null) { + 0F + } else { + this.toFloat().div(100_000_000) + } return DecimalFormat("0.00000000").format(balanceInSats) } diff --git a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/DevkitWalletActivity.kt b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/DevkitWalletActivity.kt index 5da0790..e716051 100644 --- a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/DevkitWalletActivity.kt +++ b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/DevkitWalletActivity.kt @@ -6,10 +6,10 @@ package org.bitcoindevkit.devkitwallet.presentation import android.content.Context -import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.util.Log import androidx.activity.compose.setContent +import androidx.appcompat.app.AppCompatActivity import androidx.datastore.core.DataStore import androidx.datastore.dataStore import androidx.lifecycle.lifecycleScope @@ -23,10 +23,10 @@ import org.bitcoindevkit.devkitwallet.data.UserPreferencesSerializer import org.bitcoindevkit.devkitwallet.domain.DwLogger import org.bitcoindevkit.devkitwallet.domain.UserPreferencesRepository import org.bitcoindevkit.devkitwallet.domain.Wallet -import org.bitcoindevkit.devkitwallet.presentation.navigation.HomeNavigation import org.bitcoindevkit.devkitwallet.presentation.navigation.CreateWalletNavigation -import org.bitcoindevkit.devkitwallet.presentation.ui.screens.intro.OnboardingScreen +import org.bitcoindevkit.devkitwallet.presentation.navigation.HomeNavigation import org.bitcoindevkit.devkitwallet.presentation.theme.DevkitTheme +import org.bitcoindevkit.devkitwallet.presentation.ui.screens.intro.OnboardingScreen private const val TAG = "DevkitWalletActivity" private val Context.userPreferencesStore: DataStore by dataStore( @@ -44,23 +44,27 @@ class DevkitWalletActivity : AppCompatActivity() { val userPreferencesRepository = UserPreferencesRepository(userPreferencesStore) val onBuildWalletButtonClicked: (WalletCreateType) -> Unit = { walletCreateType -> try { - val activeWallet = when (walletCreateType) { - is WalletCreateType.FROMSCRATCH -> Wallet.createWallet( - newWalletConfig = walletCreateType.newWalletConfig, - internalAppFilesPath = filesDir.absolutePath, - userPreferencesRepository = userPreferencesRepository, - ) - is WalletCreateType.LOADEXISTING -> Wallet.loadActiveWallet( - activeWallet = walletCreateType.activeWallet, - internalAppFilesPath = filesDir.absolutePath, - userPreferencesRepository = userPreferencesRepository, - ) - is WalletCreateType.RECOVER -> Wallet.recoverWallet( - recoverWalletConfig = walletCreateType.recoverWalletConfig, - internalAppFilesPath = filesDir.absolutePath, - userPreferencesRepository = userPreferencesRepository, - ) - } + val activeWallet = + when (walletCreateType) { + is WalletCreateType.FROMSCRATCH -> + Wallet.createWallet( + newWalletConfig = walletCreateType.newWalletConfig, + internalAppFilesPath = filesDir.absolutePath, + userPreferencesRepository = userPreferencesRepository, + ) + is WalletCreateType.LOADEXISTING -> + Wallet.loadActiveWallet( + activeWallet = walletCreateType.activeWallet, + internalAppFilesPath = filesDir.absolutePath, + userPreferencesRepository = userPreferencesRepository, + ) + is WalletCreateType.RECOVER -> + Wallet.recoverWallet( + recoverWalletConfig = walletCreateType.recoverWalletConfig, + internalAppFilesPath = filesDir.absolutePath, + userPreferencesRepository = userPreferencesRepository, + ) + } setContent { DevkitTheme { HomeNavigation(activeWallet) @@ -72,13 +76,15 @@ class DevkitWalletActivity : AppCompatActivity() { } lifecycleScope.launch { - val activeWallets = async { - userPreferencesRepository.fetchActiveWallets() - }.await() + val activeWallets = + async { + userPreferencesRepository.fetchActiveWallets() + }.await() - val onboardingDone = async { - userPreferencesRepository.fetchIntroDone() - }.await() + val onboardingDone = + async { + userPreferencesRepository.fetchIntroDone() + }.await() val onFinishOnboarding: () -> Unit = { lifecycleScope.launch { userPreferencesRepository.setIntroDone() } @@ -105,6 +111,8 @@ class DevkitWalletActivity : AppCompatActivity() { sealed class WalletCreateType { data class FROMSCRATCH(val newWalletConfig: NewWalletConfig) : WalletCreateType() + data class LOADEXISTING(val activeWallet: SingleWallet) : WalletCreateType() + data class RECOVER(val recoverWalletConfig: RecoverWalletConfig) : WalletCreateType() } diff --git a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/navigation/CreateWalletNavigation.kt b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/navigation/CreateWalletNavigation.kt index 2dbb219..2cf84d9 100644 --- a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/navigation/CreateWalletNavigation.kt +++ b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/navigation/CreateWalletNavigation.kt @@ -9,21 +9,18 @@ import androidx.compose.animation.AnimatedContentTransitionScope import androidx.compose.animation.core.tween import androidx.compose.runtime.Composable import androidx.navigation.NavHostController -import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable -import org.bitcoindevkit.devkitwallet.presentation.ui.screens.intro.WalletChoiceScreen -import org.bitcoindevkit.devkitwallet.presentation.ui.screens.intro.RecoverWalletScreen -import org.bitcoindevkit.devkitwallet.presentation.WalletCreateType +import androidx.navigation.compose.rememberNavController import org.bitcoindevkit.devkitwallet.data.SingleWallet +import org.bitcoindevkit.devkitwallet.presentation.WalletCreateType import org.bitcoindevkit.devkitwallet.presentation.ui.screens.intro.ActiveWalletsScreen import org.bitcoindevkit.devkitwallet.presentation.ui.screens.intro.CreateNewWalletScreen +import org.bitcoindevkit.devkitwallet.presentation.ui.screens.intro.RecoverWalletScreen +import org.bitcoindevkit.devkitwallet.presentation.ui.screens.intro.WalletChoiceScreen @Composable -fun CreateWalletNavigation( - onBuildWalletButtonClicked: (WalletCreateType) -> Unit, - activeWallets: List, -) { +fun CreateWalletNavigation(onBuildWalletButtonClicked: (WalletCreateType) -> Unit, activeWallets: List) { val navController: NavHostController = rememberNavController() val animationDuration = 400 @@ -33,55 +30,103 @@ fun CreateWalletNavigation( ) { composable( exitTransition = { - slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.Start, animationSpec = tween(animationDuration)) + slideOutOfContainer( + AnimatedContentTransitionScope.SlideDirection.Start, + animationSpec = tween(animationDuration) + ) }, popEnterTransition = { - slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.End, animationSpec = tween(animationDuration)) + slideIntoContainer( + AnimatedContentTransitionScope.SlideDirection.End, + animationSpec = tween(animationDuration) + ) }, ) { WalletChoiceScreen(navController = navController) } composable( enterTransition = { - slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.Start, animationSpec = tween(animationDuration)) + slideIntoContainer( + AnimatedContentTransitionScope.SlideDirection.Start, + animationSpec = tween(animationDuration) + ) }, exitTransition = { - slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.Start, animationSpec = tween(animationDuration)) + slideOutOfContainer( + AnimatedContentTransitionScope.SlideDirection.Start, + animationSpec = tween(animationDuration) + ) }, popEnterTransition = { - slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.End, animationSpec = tween(animationDuration)) + slideIntoContainer( + AnimatedContentTransitionScope.SlideDirection.End, + animationSpec = tween(animationDuration) + ) }, popExitTransition = { - slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.End, animationSpec = tween(animationDuration)) + slideOutOfContainer( + AnimatedContentTransitionScope.SlideDirection.End, + animationSpec = tween(animationDuration) + ) } - ) { ActiveWalletsScreen(activeWallets = activeWallets, navController = navController, onBuildWalletButtonClicked) } + ) { + ActiveWalletsScreen( + activeWallets = activeWallets, + navController = navController, + onBuildWalletButtonClicked + ) + } composable( enterTransition = { - slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.Start, animationSpec = tween(animationDuration)) + slideIntoContainer( + AnimatedContentTransitionScope.SlideDirection.Start, + animationSpec = tween(animationDuration) + ) }, exitTransition = { - slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.Start, animationSpec = tween(animationDuration)) + slideOutOfContainer( + AnimatedContentTransitionScope.SlideDirection.Start, + animationSpec = tween(animationDuration) + ) }, popEnterTransition = { - slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.End, animationSpec = tween(animationDuration)) + slideIntoContainer( + AnimatedContentTransitionScope.SlideDirection.End, + animationSpec = tween(animationDuration) + ) }, popExitTransition = { - slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.End, animationSpec = tween(animationDuration)) + slideOutOfContainer( + AnimatedContentTransitionScope.SlideDirection.End, + animationSpec = tween(animationDuration) + ) } ) { CreateNewWalletScreen(navController = navController, onBuildWalletButtonClicked) } composable( enterTransition = { - slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.Start, animationSpec = tween(animationDuration)) + slideIntoContainer( + AnimatedContentTransitionScope.SlideDirection.Start, + animationSpec = tween(animationDuration) + ) }, exitTransition = { - slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.Start, animationSpec = tween(animationDuration)) + slideOutOfContainer( + AnimatedContentTransitionScope.SlideDirection.Start, + animationSpec = tween(animationDuration) + ) }, popEnterTransition = { - slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.End, animationSpec = tween(animationDuration)) + slideIntoContainer( + AnimatedContentTransitionScope.SlideDirection.End, + animationSpec = tween(animationDuration) + ) }, popExitTransition = { - slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.End, animationSpec = tween(animationDuration)) + slideOutOfContainer( + AnimatedContentTransitionScope.SlideDirection.End, + animationSpec = tween(animationDuration) + ) } ) { RecoverWalletScreen(navController = navController, onBuildWalletButtonClicked) } } diff --git a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/navigation/Destinations.kt b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/navigation/Destinations.kt index 9fb76dd..0eeaaf2 100644 --- a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/navigation/Destinations.kt +++ b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/navigation/Destinations.kt @@ -10,35 +10,47 @@ import kotlinx.serialization.Serializable // Create wallet navigation destinations @Serializable object WalletChoiceScreen + @Serializable object ActiveWalletsScreen + @Serializable object CreateNewWalletScreen + @Serializable object WalletRecoveryScreen // Home navigation destinations @Serializable object WalletScreen + @Serializable object AboutScreen + @Serializable object RecoveryPhraseScreen + @Serializable object BlockchainClientScreen + @Serializable object LogsScreen // Wallet navigation destinations @Serializable object HomeScreen + @Serializable object ReceiveScreen + @Serializable object SendScreen + @Serializable object TransactionHistoryScreen + @Serializable data class TransactionScreen(val txid: String) + @Serializable data class RbfScreen(val txid: String) diff --git a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/navigation/HomeNavigation.kt b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/navigation/HomeNavigation.kt index 89e2b34..c1f3a21 100644 --- a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/navigation/HomeNavigation.kt +++ b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/navigation/HomeNavigation.kt @@ -9,9 +9,9 @@ import androidx.compose.animation.AnimatedContentTransitionScope import androidx.compose.animation.core.tween import androidx.compose.runtime.Composable import androidx.navigation.NavHostController -import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable +import androidx.navigation.compose.rememberNavController import org.bitcoindevkit.devkitwallet.domain.Wallet import org.bitcoindevkit.devkitwallet.presentation.ui.screens.WalletRoot import org.bitcoindevkit.devkitwallet.presentation.ui.screens.drawer.AboutScreen @@ -23,9 +23,7 @@ import org.bitcoindevkit.devkitwallet.presentation.viewmodels.WalletViewModel private const val ANIMATION_DURATION: Int = 400 @Composable -fun HomeNavigation( - activeWallet: Wallet -) { +fun HomeNavigation(activeWallet: Wallet) { val navController: NavHostController = rememberNavController() val walletViewModel = WalletViewModel(activeWallet) @@ -33,76 +31,131 @@ fun HomeNavigation( navController = navController, startDestination = WalletScreen, ) { - composable( exitTransition = { - slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.Start, animationSpec = tween(ANIMATION_DURATION)) + slideOutOfContainer( + AnimatedContentTransitionScope.SlideDirection.Start, + animationSpec = tween(ANIMATION_DURATION) + ) }, popEnterTransition = { - slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.End, animationSpec = tween(ANIMATION_DURATION)) + slideIntoContainer( + AnimatedContentTransitionScope.SlideDirection.End, + animationSpec = tween(ANIMATION_DURATION) + ) }, ) { WalletRoot(navController = navController, activeWallet = activeWallet, walletViewModel = walletViewModel) } composable( enterTransition = { - slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.Start, animationSpec = tween(ANIMATION_DURATION)) + slideIntoContainer( + AnimatedContentTransitionScope.SlideDirection.Start, + animationSpec = tween(ANIMATION_DURATION) + ) }, popEnterTransition = { - slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.Start, animationSpec = tween(ANIMATION_DURATION)) + slideIntoContainer( + AnimatedContentTransitionScope.SlideDirection.Start, + animationSpec = tween(ANIMATION_DURATION) + ) }, exitTransition = { - slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.Start, animationSpec = tween(ANIMATION_DURATION)) + slideOutOfContainer( + AnimatedContentTransitionScope.SlideDirection.Start, + animationSpec = tween(ANIMATION_DURATION) + ) }, popExitTransition = { - slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.Start, animationSpec = tween(ANIMATION_DURATION)) + slideOutOfContainer( + AnimatedContentTransitionScope.SlideDirection.Start, + animationSpec = tween(ANIMATION_DURATION) + ) }, ) { AboutScreen(navController = navController) } composable( enterTransition = { - slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.Start, animationSpec = tween(ANIMATION_DURATION)) + slideIntoContainer( + AnimatedContentTransitionScope.SlideDirection.Start, + animationSpec = tween(ANIMATION_DURATION) + ) }, popEnterTransition = { - slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.Start, animationSpec = tween(ANIMATION_DURATION)) + slideIntoContainer( + AnimatedContentTransitionScope.SlideDirection.Start, + animationSpec = tween(ANIMATION_DURATION) + ) }, exitTransition = { - slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.Start, animationSpec = tween(ANIMATION_DURATION)) + slideOutOfContainer( + AnimatedContentTransitionScope.SlideDirection.Start, + animationSpec = tween(ANIMATION_DURATION) + ) }, popExitTransition = { - slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.Start, animationSpec = tween(ANIMATION_DURATION)) + slideOutOfContainer( + AnimatedContentTransitionScope.SlideDirection.Start, + animationSpec = tween(ANIMATION_DURATION) + ) } ) { RecoveryPhraseScreen(activeWallet.getRecoveryPhrase(), navController = navController) } composable( enterTransition = { - slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.Start, animationSpec = tween(ANIMATION_DURATION)) + slideIntoContainer( + AnimatedContentTransitionScope.SlideDirection.Start, + animationSpec = tween(ANIMATION_DURATION) + ) }, popEnterTransition = { - slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.Start, animationSpec = tween(ANIMATION_DURATION)) + slideIntoContainer( + AnimatedContentTransitionScope.SlideDirection.Start, + animationSpec = tween(ANIMATION_DURATION) + ) }, exitTransition = { - slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.Start, animationSpec = tween(ANIMATION_DURATION)) + slideOutOfContainer( + AnimatedContentTransitionScope.SlideDirection.Start, + animationSpec = tween(ANIMATION_DURATION) + ) }, popExitTransition = { - slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.Start, animationSpec = tween(ANIMATION_DURATION)) + slideOutOfContainer( + AnimatedContentTransitionScope.SlideDirection.Start, + animationSpec = tween(ANIMATION_DURATION) + ) } - ) { BlockchainClientScreen( - state = walletViewModel.state, - navController = navController - ) } + ) { + BlockchainClientScreen( + state = walletViewModel.state, + navController = navController + ) + } composable( enterTransition = { - slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.Start, animationSpec = tween(ANIMATION_DURATION)) + slideIntoContainer( + AnimatedContentTransitionScope.SlideDirection.Start, + animationSpec = tween(ANIMATION_DURATION) + ) }, popEnterTransition = { - slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.Start, animationSpec = tween(ANIMATION_DURATION)) + slideIntoContainer( + AnimatedContentTransitionScope.SlideDirection.Start, + animationSpec = tween(ANIMATION_DURATION) + ) }, exitTransition = { - slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.Start, animationSpec = tween(ANIMATION_DURATION)) + slideOutOfContainer( + AnimatedContentTransitionScope.SlideDirection.Start, + animationSpec = tween(ANIMATION_DURATION) + ) }, popExitTransition = { - slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.Start, animationSpec = tween(ANIMATION_DURATION)) + slideOutOfContainer( + AnimatedContentTransitionScope.SlideDirection.Start, + animationSpec = tween(ANIMATION_DURATION) + ) } ) { LogsScreen(navController = navController) } } diff --git a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/navigation/WalletNavigation.kt b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/navigation/WalletNavigation.kt index 1ccd5e2..fa89c1d 100644 --- a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/navigation/WalletNavigation.kt +++ b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/navigation/WalletNavigation.kt @@ -7,19 +7,19 @@ package org.bitcoindevkit.devkitwallet.presentation.navigation import androidx.compose.animation.AnimatedContentTransitionScope import androidx.compose.animation.core.tween +import androidx.compose.material3.DrawerState import androidx.compose.runtime.Composable import androidx.navigation.NavHostController -import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable -import androidx.compose.material3.DrawerState +import androidx.navigation.compose.rememberNavController import androidx.navigation.toRoute import org.bitcoindevkit.devkitwallet.domain.Wallet import org.bitcoindevkit.devkitwallet.presentation.ui.screens.wallet.RBFScreen import org.bitcoindevkit.devkitwallet.presentation.ui.screens.wallet.ReceiveScreen import org.bitcoindevkit.devkitwallet.presentation.ui.screens.wallet.SendScreen -import org.bitcoindevkit.devkitwallet.presentation.ui.screens.wallet.TransactionScreen import org.bitcoindevkit.devkitwallet.presentation.ui.screens.wallet.TransactionHistoryScreen +import org.bitcoindevkit.devkitwallet.presentation.ui.screens.wallet.TransactionScreen import org.bitcoindevkit.devkitwallet.presentation.ui.screens.wallet.WalletHomeScreen import org.bitcoindevkit.devkitwallet.presentation.viewmodels.AddressViewModel import org.bitcoindevkit.devkitwallet.presentation.viewmodels.SendViewModel @@ -28,11 +28,7 @@ import org.bitcoindevkit.devkitwallet.presentation.viewmodels.WalletViewModel private const val ANIMATION_DURATION: Int = 400 @Composable -fun WalletNavigation( - drawerState: DrawerState, - activeWallet: Wallet, - walletViewModel: WalletViewModel -) { +fun WalletNavigation(drawerState: DrawerState, activeWallet: Wallet, walletViewModel: WalletViewModel) { val navController: NavHostController = rememberNavController() val addressViewModel = AddressViewModel(activeWallet) val sendViewModel = SendViewModel(activeWallet) @@ -43,25 +39,43 @@ fun WalletNavigation( ) { composable( exitTransition = { - slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.Start, animationSpec = tween(ANIMATION_DURATION)) + slideOutOfContainer( + AnimatedContentTransitionScope.SlideDirection.Start, + animationSpec = tween(ANIMATION_DURATION) + ) }, popEnterTransition = { - slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.End, animationSpec = tween(ANIMATION_DURATION)) + slideIntoContainer( + AnimatedContentTransitionScope.SlideDirection.End, + animationSpec = tween(ANIMATION_DURATION) + ) }, ) { WalletHomeScreen(navController, drawerState, walletViewModel) } composable( enterTransition = { - slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.Start, animationSpec = tween(ANIMATION_DURATION)) + slideIntoContainer( + AnimatedContentTransitionScope.SlideDirection.Start, + animationSpec = tween(ANIMATION_DURATION) + ) }, exitTransition = { - slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.End, animationSpec = tween(ANIMATION_DURATION)) + slideOutOfContainer( + AnimatedContentTransitionScope.SlideDirection.End, + animationSpec = tween(ANIMATION_DURATION) + ) }, popEnterTransition = { - slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.Start, animationSpec = tween(ANIMATION_DURATION)) + slideIntoContainer( + AnimatedContentTransitionScope.SlideDirection.Start, + animationSpec = tween(ANIMATION_DURATION) + ) }, popExitTransition = { - slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.End, animationSpec = tween(ANIMATION_DURATION)) + slideOutOfContainer( + AnimatedContentTransitionScope.SlideDirection.End, + animationSpec = tween(ANIMATION_DURATION) + ) } ) { ReceiveScreen( @@ -73,31 +87,55 @@ fun WalletNavigation( composable( enterTransition = { - slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.Start, animationSpec = tween(ANIMATION_DURATION)) + slideIntoContainer( + AnimatedContentTransitionScope.SlideDirection.Start, + animationSpec = tween(ANIMATION_DURATION) + ) }, exitTransition = { - slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.End, animationSpec = tween(ANIMATION_DURATION)) + slideOutOfContainer( + AnimatedContentTransitionScope.SlideDirection.End, + animationSpec = tween(ANIMATION_DURATION) + ) }, popEnterTransition = { - slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.Start, animationSpec = tween(ANIMATION_DURATION)) + slideIntoContainer( + AnimatedContentTransitionScope.SlideDirection.Start, + animationSpec = tween(ANIMATION_DURATION) + ) }, popExitTransition = { - slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.End, animationSpec = tween(ANIMATION_DURATION)) + slideOutOfContainer( + AnimatedContentTransitionScope.SlideDirection.End, + animationSpec = tween(ANIMATION_DURATION) + ) } ) { SendScreen(navController, sendViewModel) } composable( enterTransition = { - slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.Start, animationSpec = tween(ANIMATION_DURATION)) + slideIntoContainer( + AnimatedContentTransitionScope.SlideDirection.Start, + animationSpec = tween(ANIMATION_DURATION) + ) }, exitTransition = { - slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.End, animationSpec = tween(ANIMATION_DURATION)) + slideOutOfContainer( + AnimatedContentTransitionScope.SlideDirection.End, + animationSpec = tween(ANIMATION_DURATION) + ) }, popEnterTransition = { - slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.Start, animationSpec = tween(ANIMATION_DURATION)) + slideIntoContainer( + AnimatedContentTransitionScope.SlideDirection.Start, + animationSpec = tween(ANIMATION_DURATION) + ) }, popExitTransition = { - slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.End, animationSpec = tween(ANIMATION_DURATION)) + slideOutOfContainer( + AnimatedContentTransitionScope.SlideDirection.End, + animationSpec = tween(ANIMATION_DURATION) + ) } ) { val args = it.toRoute() @@ -106,31 +144,55 @@ fun WalletNavigation( composable( enterTransition = { - slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.Start, animationSpec = tween(ANIMATION_DURATION)) + slideIntoContainer( + AnimatedContentTransitionScope.SlideDirection.Start, + animationSpec = tween(ANIMATION_DURATION) + ) }, exitTransition = { - slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.End, animationSpec = tween(ANIMATION_DURATION)) + slideOutOfContainer( + AnimatedContentTransitionScope.SlideDirection.End, + animationSpec = tween(ANIMATION_DURATION) + ) }, popEnterTransition = { - slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.Start, animationSpec = tween(ANIMATION_DURATION)) + slideIntoContainer( + AnimatedContentTransitionScope.SlideDirection.Start, + animationSpec = tween(ANIMATION_DURATION) + ) }, popExitTransition = { - slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.End, animationSpec = tween(ANIMATION_DURATION)) + slideOutOfContainer( + AnimatedContentTransitionScope.SlideDirection.End, + animationSpec = tween(ANIMATION_DURATION) + ) } ) { TransactionHistoryScreen(navController, activeWallet) } composable( enterTransition = { - slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.Start, animationSpec = tween(ANIMATION_DURATION)) + slideIntoContainer( + AnimatedContentTransitionScope.SlideDirection.Start, + animationSpec = tween(ANIMATION_DURATION) + ) }, exitTransition = { - slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.End, animationSpec = tween(ANIMATION_DURATION)) + slideOutOfContainer( + AnimatedContentTransitionScope.SlideDirection.End, + animationSpec = tween(ANIMATION_DURATION) + ) }, popEnterTransition = { - slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.Start, animationSpec = tween(ANIMATION_DURATION)) + slideIntoContainer( + AnimatedContentTransitionScope.SlideDirection.Start, + animationSpec = tween(ANIMATION_DURATION) + ) }, popExitTransition = { - slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.End, animationSpec = tween(ANIMATION_DURATION)) + slideOutOfContainer( + AnimatedContentTransitionScope.SlideDirection.End, + animationSpec = tween(ANIMATION_DURATION) + ) } ) { val args = it.toRoute() diff --git a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/theme/DevkitWalletColors.kt b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/theme/DevkitWalletColors.kt index b99d1f3..4f87f32 100644 --- a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/theme/DevkitWalletColors.kt +++ b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/theme/DevkitWalletColors.kt @@ -7,12 +7,13 @@ package org.bitcoindevkit.devkitwallet.presentation.theme import androidx.compose.ui.graphics.Color +@Suppress("ktlint:standard:comment-spacing") object DevkitWalletColors { - val primaryDark: Color = Color(0xFF203B46) // App bar - val primary: Color = Color(0xFF264653) // Background + val primaryDark: Color = Color(0xFF203B46) // App bar + val primary: Color = Color(0xFF264653) // Background val primaryLight: Color = Color(0xFF335F70) // Behind balance primary light - val white: Color = Color(0xffffffff) // Most text - val secondary: Color = Color(0xFF2A9D8F) // Buttons - val accent1: Color = Color(0xFFE9C46A) // Receive button - val accent2: Color = Color(0xFFE76F51) // Send button + val white: Color = Color(0xffffffff) // Most text + val secondary: Color = Color(0xFF2A9D8F) // Buttons + val accent1: Color = Color(0xFFE9C46A) // Receive button + val accent2: Color = Color(0xFFE76F51) // Send button } diff --git a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/theme/Type.kt b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/theme/Type.kt index b9fccba..f4cd513 100644 --- a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/theme/Type.kt +++ b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/theme/Type.kt @@ -10,14 +10,16 @@ import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.sp -internal val devkitTypography = Typography( - labelLarge = TextStyle( - fontFamily = quattroRegular, - fontWeight = FontWeight.Normal, - fontSize = 16.sp, - lineHeight = 28.sp - ), -) +internal val devkitTypography = + Typography( + labelLarge = + TextStyle( + fontFamily = quattroRegular, + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + lineHeight = 28.sp + ), + ) // These are the default text styles used by Material3 components: // Buttons: labelLarge diff --git a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/components/LoadingAnimation.kt b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/components/LoadingAnimation.kt index 4be0583..ae0523f 100644 --- a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/components/LoadingAnimation.kt +++ b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/components/LoadingAnimation.kt @@ -31,7 +31,7 @@ fun LoadingAnimation( circleColor: Color = Color(0xffE9C46A), circleSize: Dp = 21.dp, animationDelay: Int = 800, - initialAlpha: Float = 0.3f + initialAlpha: Float = 0.3f, ) { val circles = listOf( remember { Animatable(initialValue = initialAlpha) }, @@ -66,7 +66,7 @@ fun LoadingAnimation( modifier = Modifier .size(size = circleSize) .clip(shape = CircleShape) - .background(circleColor.copy(alpha = animatable.value) ) + .background(circleColor.copy(alpha = animatable.value)) ) } } diff --git a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/components/NeutralButton.kt b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/components/NeutralButton.kt index ff3b493..a437ede 100644 --- a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/components/NeutralButton.kt +++ b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/components/NeutralButton.kt @@ -22,13 +22,15 @@ import org.bitcoindevkit.devkitwallet.presentation.theme.DevkitWalletColors fun NeutralButton(text: String, enabled: Boolean, modifier: Modifier? = null, onClick: () -> Unit) { Button( onClick = onClick, - colors = ButtonDefaults.buttonColors( - containerColor = DevkitWalletColors.secondary, - disabledContainerColor = DevkitWalletColors.secondary, - ), + colors = + ButtonDefaults.buttonColors( + containerColor = DevkitWalletColors.secondary, + disabledContainerColor = DevkitWalletColors.secondary, + ), shape = RoundedCornerShape(16.dp), enabled = enabled, - modifier = modifier ?: Modifier + modifier = + modifier ?: Modifier .height(80.dp) .fillMaxWidth(0.9f) .padding(vertical = 8.dp, horizontal = 8.dp) diff --git a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/components/SecondaryScreensAppBar.kt b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/components/SecondaryScreensAppBar.kt index 5b18589..cdea6a0 100644 --- a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/components/SecondaryScreensAppBar.kt +++ b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/components/SecondaryScreensAppBar.kt @@ -20,10 +20,7 @@ import org.bitcoindevkit.devkitwallet.presentation.theme.quattroRegular @OptIn(ExperimentalMaterial3Api::class) @Composable -internal fun SecondaryScreensAppBar( - title: String, - navigation: () -> Unit -) { +internal fun SecondaryScreensAppBar(title: String, navigation: () -> Unit) { TopAppBar( title = { Text( @@ -42,8 +39,9 @@ internal fun SecondaryScreensAppBar( ) } }, - colors = TopAppBarDefaults.topAppBarColors( - containerColor = DevkitWalletColors.primaryDark, - ) + colors = + TopAppBarDefaults.topAppBarColors( + containerColor = DevkitWalletColors.primaryDark, + ) ) } diff --git a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/components/TransactionCards.kt b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/components/TransactionCards.kt index 72f2dba..6011cfd 100644 --- a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/components/TransactionCards.kt +++ b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/components/TransactionCards.kt @@ -27,10 +27,10 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavController import org.bitcoindevkit.devkitwallet.data.TxDetails -import org.bitcoindevkit.devkitwallet.presentation.ui.screens.wallet.viewTransaction -import org.bitcoindevkit.devkitwallet.presentation.theme.DevkitWalletColors import org.bitcoindevkit.devkitwallet.domain.utils.timestampToString +import org.bitcoindevkit.devkitwallet.presentation.theme.DevkitWalletColors import org.bitcoindevkit.devkitwallet.presentation.theme.monoRegular +import org.bitcoindevkit.devkitwallet.presentation.ui.screens.wallet.viewTransaction private const val TAG = "TransactionCards" @@ -43,8 +43,7 @@ fun ConfirmedTransactionCard(details: TxDetails, navController: NavController) { .background( color = DevkitWalletColors.primaryLight, shape = RoundedCornerShape(16.dp) - ) - .clickable { viewTransaction(navController = navController, txid = details.txid) }, + ).clickable { viewTransaction(navController = navController, txid = details.txid) }, verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Absolute.SpaceBetween ) { @@ -57,12 +56,13 @@ fun ConfirmedTransactionCard(details: TxDetails, navController: NavController) { modifier = Modifier.padding(16.dp) ) Box( - modifier = Modifier - .padding(top = 16.dp, end = 16.dp) - .size(size = 24.dp) - .clip(shape = CircleShape) - .background(DevkitWalletColors.secondary) - .align(Alignment.Top) + modifier = + Modifier + .padding(top = 16.dp, end = 16.dp) + .size(size = 24.dp) + .clip(shape = CircleShape) + .background(DevkitWalletColors.secondary) + .align(Alignment.Top) ) } } @@ -76,13 +76,11 @@ fun PendingTransactionCard(details: TxDetails, navController: NavController) { .background( color = DevkitWalletColors.primaryLight, shape = RoundedCornerShape(16.dp) - ) - .border( + ).border( width = 2.dp, color = DevkitWalletColors.accent1, shape = RoundedCornerShape(16.dp) - ) - .clickable { + ).clickable { viewTransaction( navController = navController, txid = details.txid @@ -99,12 +97,13 @@ fun PendingTransactionCard(details: TxDetails, navController: NavController) { modifier = Modifier.padding(16.dp) ) Box( - modifier = Modifier - .padding(top = 16.dp, end = 16.dp) - .size(size = 24.dp) - .clip(shape = CircleShape) - .background(Color(0xffE9C46A)) - .align(Alignment.Top) + modifier = + Modifier + .padding(top = 16.dp, end = 16.dp) + .size(size = 24.dp) + .clip(shape = CircleShape) + .background(Color(0xffE9C46A)) + .align(Alignment.Top) ) } } diff --git a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/WalletRoot.kt b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/WalletRoot.kt index 0fc5ace..0fa6753 100644 --- a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/WalletRoot.kt +++ b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/WalletRoot.kt @@ -31,7 +31,6 @@ import androidx.compose.material3.rememberDrawerState import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import org.bitcoindevkit.devkitwallet.BuildConfig import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource @@ -43,37 +42,35 @@ import com.composables.icons.lucide.Info import com.composables.icons.lucide.Lucide import com.composables.icons.lucide.SatelliteDish import com.composables.icons.lucide.ScrollText -import org.bitcoindevkit.devkitwallet.presentation.theme.DevkitWalletColors -import org.bitcoindevkit.devkitwallet.presentation.navigation.WalletNavigation +import org.bitcoindevkit.devkitwallet.BuildConfig import org.bitcoindevkit.devkitwallet.R import org.bitcoindevkit.devkitwallet.domain.Wallet import org.bitcoindevkit.devkitwallet.presentation.navigation.AboutScreen import org.bitcoindevkit.devkitwallet.presentation.navigation.BlockchainClientScreen import org.bitcoindevkit.devkitwallet.presentation.navigation.LogsScreen import org.bitcoindevkit.devkitwallet.presentation.navigation.RecoveryPhraseScreen +import org.bitcoindevkit.devkitwallet.presentation.navigation.WalletNavigation +import org.bitcoindevkit.devkitwallet.presentation.theme.DevkitWalletColors import org.bitcoindevkit.devkitwallet.presentation.theme.quattroRegular import org.bitcoindevkit.devkitwallet.presentation.viewmodels.WalletViewModel @OptIn(androidx.compose.animation.ExperimentalAnimationApi::class) @Composable -internal fun WalletRoot( - navController: NavController, - activeWallet: Wallet, - walletViewModel: WalletViewModel -) { +internal fun WalletRoot(navController: NavController, activeWallet: Wallet, walletViewModel: WalletViewModel) { val drawerState = rememberDrawerState(DrawerValue.Closed) val items = listOf(Icons.Default.Favorite, Icons.Default.Face, Icons.Default.Email, Icons.Default.Face) val selectedItem = remember { mutableStateOf(items[0]) } - val navigationItemColors = colors( - selectedContainerColor = DevkitWalletColors.primary, - unselectedContainerColor = DevkitWalletColors.primary, - selectedTextColor = DevkitWalletColors.white, - unselectedTextColor = DevkitWalletColors.white - ) + val navigationItemColors = + colors( + selectedContainerColor = DevkitWalletColors.primary, + unselectedContainerColor = DevkitWalletColors.primary, + selectedTextColor = DevkitWalletColors.white, + unselectedTextColor = DevkitWalletColors.white + ) - ModalNavigationDrawer ( + ModalNavigationDrawer( drawerState = drawerState, drawerContent = { ModalDrawerSheet( @@ -91,7 +88,7 @@ internal fun WalletRoot( Image( painter = painterResource(id = R.drawable.ic_testnet_logo), contentDescription = "Bitcoin testnet logo", - Modifier + modifier = Modifier .size(90.dp) .padding(bottom = 16.dp) ) @@ -131,7 +128,13 @@ internal fun WalletRoot( modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding), ) NavigationDrawerItem( - icon = { Icon(Lucide.History, contentDescription = "Recovery Phrase", tint = DevkitWalletColors.white) }, + icon = { + Icon( + Lucide.History, + contentDescription = "Recovery Phrase", + tint = DevkitWalletColors.white + ) + }, label = { DrawerItemLabel("Recovery Phrase") }, selected = items[1] == selectedItem.value, onClick = { navController.navigate(RecoveryPhraseScreen) }, @@ -139,7 +142,13 @@ internal fun WalletRoot( modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding), ) NavigationDrawerItem( - icon = { Icon(Lucide.SatelliteDish, contentDescription = "Esplora Client", tint = DevkitWalletColors.white) }, + icon = { + Icon( + Lucide.SatelliteDish, + contentDescription = "Esplora Client", + tint = DevkitWalletColors.white + ) + }, label = { DrawerItemLabel("Esplora Client") }, selected = items[2] == selectedItem.value, onClick = { navController.navigate(BlockchainClientScreen) }, @@ -147,7 +156,13 @@ internal fun WalletRoot( modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding), ) NavigationDrawerItem( - icon = { Icon(Lucide.ScrollText, contentDescription = "Logs", tint = DevkitWalletColors.white) }, + icon = { + Icon( + Lucide.ScrollText, + contentDescription = "Logs", + tint = DevkitWalletColors.white + ) + }, label = { DrawerItemLabel("Logs") }, selected = items[3] == selectedItem.value, onClick = { navController.navigate(LogsScreen) }, diff --git a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/drawer/AboutScreen.kt b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/drawer/AboutScreen.kt index 9d47494..11d5f35 100644 --- a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/drawer/AboutScreen.kt +++ b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/drawer/AboutScreen.kt @@ -23,12 +23,20 @@ import androidx.compose.ui.tooling.preview.Devices import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.navigation.NavController -import org.bitcoindevkit.devkitwallet.R import androidx.navigation.compose.rememberNavController +import org.bitcoindevkit.devkitwallet.R import org.bitcoindevkit.devkitwallet.presentation.navigation.WalletScreen -import org.bitcoindevkit.devkitwallet.presentation.ui.components.SecondaryScreensAppBar import org.bitcoindevkit.devkitwallet.presentation.theme.DevkitWalletColors import org.bitcoindevkit.devkitwallet.presentation.theme.quattroRegular +import org.bitcoindevkit.devkitwallet.presentation.ui.components.SecondaryScreensAppBar + +private val message: String = """ + "This wallet is build for: + + 1. Developers interested in learning how to leverage the Bitcoin Development Kit on Android. + + 2. Any bitcoiner looking for a Signet/Testnet/Regtest wallet!" +""".trimIndent() @Composable internal fun AboutScreen(navController: NavController) { @@ -57,7 +65,7 @@ internal fun AboutScreen(navController: NavController) { ) Spacer(modifier = Modifier.padding(24.dp)) Text( - text = "This wallet is build for:\n\n1. Developers interested in learning how to leverage the Bitcoin Development Kit on Android.\n\n2. Any bitcoiner looking for a Signet/Testnet/Regtest wallet!", + text = message, color = DevkitWalletColors.white, textAlign = TextAlign.Start, fontFamily = quattroRegular, diff --git a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/drawer/BlockchainClientScreen.kt b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/drawer/BlockchainClientScreen.kt index 94e8c86..709146e 100644 --- a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/drawer/BlockchainClientScreen.kt +++ b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/drawer/BlockchainClientScreen.kt @@ -20,17 +20,14 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavController import org.bitcoindevkit.devkitwallet.presentation.navigation.WalletScreen -import org.bitcoindevkit.devkitwallet.presentation.ui.components.SecondaryScreensAppBar import org.bitcoindevkit.devkitwallet.presentation.theme.DevkitWalletColors import org.bitcoindevkit.devkitwallet.presentation.theme.quattroBold import org.bitcoindevkit.devkitwallet.presentation.theme.quattroRegular +import org.bitcoindevkit.devkitwallet.presentation.ui.components.SecondaryScreensAppBar import org.bitcoindevkit.devkitwallet.presentation.viewmodels.mvi.WalletScreenState @Composable -internal fun BlockchainClientScreen( - state: WalletScreenState, - navController: NavController -) { +internal fun BlockchainClientScreen(state: WalletScreenState, navController: NavController) { val focusManager = LocalFocusManager.current // val isBlockChainCreated = Wallet.isBlockChainCreated() val serverEndpoint: MutableState = remember { mutableStateOf("") } diff --git a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/drawer/LogsScreen.kt b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/drawer/LogsScreen.kt index d3c7af4..5df8ba6 100644 --- a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/drawer/LogsScreen.kt +++ b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/drawer/LogsScreen.kt @@ -13,24 +13,22 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.rememberScrollState import androidx.compose.material3.Scaffold -import androidx.compose.runtime.remember -import androidx.compose.ui.unit.sp -import org.bitcoindevkit.devkitwallet.domain.DwLogger import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import androidx.navigation.NavController +import org.bitcoindevkit.devkitwallet.domain.DwLogger import org.bitcoindevkit.devkitwallet.presentation.navigation.WalletScreen import org.bitcoindevkit.devkitwallet.presentation.theme.DevkitWalletColors import org.bitcoindevkit.devkitwallet.presentation.theme.quattroRegular import org.bitcoindevkit.devkitwallet.presentation.ui.components.SecondaryScreensAppBar @Composable -fun LogsScreen( - navController: NavController -) { +fun LogsScreen(navController: NavController) { val logs: List = remember { DwLogger.getLogs() } Scaffold( diff --git a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/drawer/RecoveryPhraseScreen.kt b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/drawer/RecoveryPhraseScreen.kt index cded24a..0a8e5e6 100644 --- a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/drawer/RecoveryPhraseScreen.kt +++ b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/drawer/RecoveryPhraseScreen.kt @@ -25,17 +25,14 @@ import androidx.compose.ui.unit.dp import androidx.navigation.NavController import androidx.navigation.compose.rememberNavController import org.bitcoindevkit.devkitwallet.presentation.navigation.WalletScreen -import org.bitcoindevkit.devkitwallet.presentation.ui.components.SecondaryScreensAppBar import org.bitcoindevkit.devkitwallet.presentation.theme.DevkitWalletColors import org.bitcoindevkit.devkitwallet.presentation.theme.monoRegular import org.bitcoindevkit.devkitwallet.presentation.theme.quattroRegular import org.bitcoindevkit.devkitwallet.presentation.ui.components.NeutralButton +import org.bitcoindevkit.devkitwallet.presentation.ui.components.SecondaryScreensAppBar @Composable -internal fun RecoveryPhraseScreen( - recoveryPhrase: List, - navController: NavController, -) { +internal fun RecoveryPhraseScreen(recoveryPhrase: List, navController: NavController) { val (currentIndex, setCurrentIndex) = remember { mutableIntStateOf(0) } Scaffold( @@ -47,7 +44,6 @@ internal fun RecoveryPhraseScreen( }, containerColor = DevkitWalletColors.primary ) { paddingValues -> - Crossfade( modifier = Modifier.padding(paddingValues), targetState = currentIndex, @@ -59,7 +55,7 @@ internal fun RecoveryPhraseScreen( ) { screen -> when (screen) { 0 -> WarningText(setCurrentIndex = setCurrentIndex) - 1 -> RecoveryPhrase(recoveryPhrase =recoveryPhrase) + 1 -> RecoveryPhrase(recoveryPhrase = recoveryPhrase) } } } @@ -111,12 +107,24 @@ fun RecoveryPhrase(recoveryPhrase: List) { } } - @Preview(device = Devices.PIXEL_4, showBackground = true) @Composable internal fun PreviewRecoveryPhraseScreen() { RecoveryPhraseScreen( - listOf("word1", "word2", "word3", "word4", "word5", "word6", "word7", "word8", "word9", "word10", "word11", "word12"), + listOf( + "word1", + "word2", + "word3", + "word4", + "word5", + "word6", + "word7", + "word8", + "word9", + "word10", + "word11", + "word12" + ), rememberNavController() ) } diff --git a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/intro/ActiveWalletsScreen.kt b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/intro/ActiveWalletsScreen.kt index bf6e1e2..ea16f43 100644 --- a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/intro/ActiveWalletsScreen.kt +++ b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/intro/ActiveWalletsScreen.kt @@ -24,11 +24,11 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavController -import org.bitcoindevkit.devkitwallet.presentation.WalletCreateType import org.bitcoindevkit.devkitwallet.data.SingleWallet -import org.bitcoindevkit.devkitwallet.presentation.ui.components.SecondaryScreensAppBar +import org.bitcoindevkit.devkitwallet.presentation.WalletCreateType import org.bitcoindevkit.devkitwallet.presentation.theme.DevkitWalletColors import org.bitcoindevkit.devkitwallet.presentation.theme.quattroRegular +import org.bitcoindevkit.devkitwallet.presentation.ui.components.SecondaryScreensAppBar private const val TAG = "ActiveWalletsScreen" @@ -36,7 +36,7 @@ private const val TAG = "ActiveWalletsScreen" internal fun ActiveWalletsScreen( activeWallets: List, navController: NavController, - onBuildWalletButtonClicked: (WalletCreateType) -> Unit + onBuildWalletButtonClicked: (WalletCreateType) -> Unit, ) { Scaffold( topBar = { diff --git a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/intro/CreateNewWallet.kt b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/intro/CreateNewWallet.kt index d695202..9324654 100644 --- a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/intro/CreateNewWallet.kt +++ b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/intro/CreateNewWallet.kt @@ -38,19 +38,19 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.constraintlayout.compose.ConstraintLayout import androidx.navigation.NavController -import org.bitcoindevkit.devkitwallet.presentation.WalletCreateType import org.bitcoindevkit.devkitwallet.data.ActiveWalletScriptType import org.bitcoindevkit.devkitwallet.data.NewWalletConfig -import org.bitcoindevkit.devkitwallet.presentation.ui.components.NeutralButton -import org.bitcoindevkit.devkitwallet.presentation.ui.components.SecondaryScreensAppBar +import org.bitcoindevkit.devkitwallet.presentation.WalletCreateType import org.bitcoindevkit.devkitwallet.presentation.theme.DevkitWalletColors import org.bitcoindevkit.devkitwallet.presentation.theme.monoRegular +import org.bitcoindevkit.devkitwallet.presentation.ui.components.NeutralButton +import org.bitcoindevkit.devkitwallet.presentation.ui.components.SecondaryScreensAppBar import org.rustbitcoin.bitcoin.Network @Composable internal fun CreateNewWalletScreen( navController: NavController, - onBuildWalletButtonClicked: (WalletCreateType) -> Unit + onBuildWalletButtonClicked: (WalletCreateType) -> Unit, ) { Scaffold( topBar = { @@ -70,7 +70,8 @@ internal fun CreateNewWalletScreen( val walletName: MutableState = remember { mutableStateOf("") } val selectedNetwork: MutableState = remember { mutableStateOf(Network.TESTNET) } val networks = listOf(Network.TESTNET, Network.SIGNET, Network.REGTEST) - val selectedScriptType: MutableState = remember { mutableStateOf(ActiveWalletScriptType.P2TR) } + val selectedScriptType: MutableState = + remember { mutableStateOf(ActiveWalletScriptType.P2TR) } val scriptTypes = listOf(ActiveWalletScriptType.P2TR, ActiveWalletScriptType.P2WPKH) Column( @@ -79,8 +80,7 @@ internal fun CreateNewWalletScreen( top.linkTo(parent.top) start.linkTo(parent.start) end.linkTo(parent.end) - } - .fillMaxSize() + }.fillMaxSize() .background(color = DevkitWalletColors.primary) .padding(horizontal = 32.dp) ) { @@ -118,8 +118,7 @@ internal fun CreateNewWalletScreen( bottom.linkTo(parent.bottom) start.linkTo(parent.start) end.linkTo(parent.end) - } - .fillMaxWidth() + }.fillMaxWidth() .padding(horizontal = 32.dp) ) { NeutralButton( @@ -155,8 +154,7 @@ fun NetworkOptionCard(networks: List, selectedNetwork: MutableState, selectedNetwork: MutableState RadioButtonWithLabel( @@ -185,7 +187,10 @@ fun NetworkOptionCard(networks: List, selectedNetwork: MutableState, selectedScriptType: MutableState) { +fun ScriptTypeOptionCard( + scriptTypes: List, + selectedScriptType: MutableState, +) { Column( Modifier .fillMaxWidth() @@ -193,8 +198,7 @@ fun ScriptTypeOptionCard(scriptTypes: List, selectedScri width = 2.dp, color = DevkitWalletColors.secondary, shape = RoundedCornerShape(16.dp) - ) - .background( + ).background( color = DevkitWalletColors.primaryLight, shape = RoundedCornerShape(16.dp) ), @@ -209,7 +213,11 @@ fun ScriptTypeOptionCard(scriptTypes: List, selectedScri modifier = Modifier.padding(top = 8.dp, start = 8.dp, bottom = 8.dp) ) - HorizontalDivider(color = DevkitWalletColors.secondary, thickness = 2.dp, modifier = Modifier.padding(bottom = 8.dp)) + HorizontalDivider( + color = DevkitWalletColors.secondary, + thickness = 2.dp, + modifier = Modifier.padding(bottom = 8.dp) + ) scriptTypes.forEachIndexed { index, it -> RadioButtonWithLabel( diff --git a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/intro/OnboardingScreen.kt b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/intro/OnboardingScreen.kt index cd384b2..037a187 100644 --- a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/intro/OnboardingScreen.kt +++ b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/intro/OnboardingScreen.kt @@ -36,6 +36,8 @@ import org.bitcoindevkit.devkitwallet.presentation.theme.devkitTypography @Composable fun OnboardingScreen(onFinishOnboarding: () -> Unit) { val (currentIndex, setCurrentIndex) = remember { mutableIntStateOf(1) } + + @Suppress("ktlint:standard:max-line-length") val messages = listOf( "Easter egg #1: \uD83E\uDD5A", "Welcome to the Devkit Wallet! This app is a playground for developers and bitcoin enthusiasts to experiment with bitcoin's test networks.", @@ -137,7 +139,7 @@ fun OnboardingScreen(onFinishOnboarding: () -> Unit) { .clickable( indication = null, interactionSource = remember { MutableInteractionSource() } - ) { setCurrentIndex((currentIndex - 1).coerceIn(0, 3)) }, + ) { setCurrentIndex((currentIndex - 1).coerceIn(0, 3)) }, color = DevkitWalletColors.white, style = devkitTypography.labelLarge ) @@ -148,7 +150,13 @@ fun OnboardingScreen(onFinishOnboarding: () -> Unit) { indication = null, interactionSource = remember { MutableInteractionSource() } ) { - if (currentIndex < 3) setCurrentIndex((currentIndex + 1).coerceIn(0, 3)) else onFinishOnboarding() + if (currentIndex < 3) { + setCurrentIndex( + (currentIndex + 1).coerceIn(0, 3) + ) + } else { + onFinishOnboarding() + } }, color = DevkitWalletColors.white, style = devkitTypography.labelLarge diff --git a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/intro/RecoverWalletScreen.kt b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/intro/RecoverWalletScreen.kt index fe14667..db080f0 100644 --- a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/intro/RecoverWalletScreen.kt +++ b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/intro/RecoverWalletScreen.kt @@ -14,9 +14,9 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.Scaffold import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextFieldDefaults +import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState @@ -36,19 +36,19 @@ import androidx.compose.ui.unit.sp import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.Dimension import androidx.navigation.NavController -import org.bitcoindevkit.devkitwallet.presentation.WalletCreateType import org.bitcoindevkit.devkitwallet.data.ActiveWalletScriptType import org.bitcoindevkit.devkitwallet.data.RecoverWalletConfig -import org.bitcoindevkit.devkitwallet.presentation.ui.components.NeutralButton -import org.bitcoindevkit.devkitwallet.presentation.ui.components.SecondaryScreensAppBar +import org.bitcoindevkit.devkitwallet.presentation.WalletCreateType import org.bitcoindevkit.devkitwallet.presentation.theme.DevkitWalletColors import org.bitcoindevkit.devkitwallet.presentation.theme.quattroRegular +import org.bitcoindevkit.devkitwallet.presentation.ui.components.NeutralButton +import org.bitcoindevkit.devkitwallet.presentation.ui.components.SecondaryScreensAppBar import org.rustbitcoin.bitcoin.Network @Composable internal fun RecoverWalletScreen( navController: NavController, - onBuildWalletButtonClicked: (WalletCreateType) -> Unit + onBuildWalletButtonClicked: (WalletCreateType) -> Unit, ) { Scaffold( topBar = { @@ -59,16 +59,26 @@ internal fun RecoverWalletScreen( // the screen is broken into 2 parts: the screen title and the body ConstraintLayout( - modifier = Modifier - .fillMaxHeight(1f) - .padding(paddingValues) + modifier = + Modifier + .fillMaxHeight(1f) + .padding(paddingValues) ) { - val (screenTitle, body) = createRefs() val emptyRecoveryPhrase: Map = mapOf( - 1 to "", 2 to "", 3 to "", 4 to "", 5 to "", 6 to "", - 7 to "", 8 to "", 9 to "", 10 to "", 11 to "", 12 to "" + 1 to "", + 2 to "", + 3 to "", + 4 to "", + 5 to "", + 6 to "", + 7 to "", + 8 to "", + 9 to "", + 10 to "", + 11 to "", + 12 to "" ) val (recoveryPhraseWordMap, setRecoveryPhraseWordMap) = remember { mutableStateOf(emptyRecoveryPhrase) } val walletName: MutableState = remember { mutableStateOf("") } @@ -80,16 +90,18 @@ internal fun RecoverWalletScreen( // the app name Column( horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier - .fillMaxWidth(1f) - .constrainAs(screenTitle) { - top.linkTo(parent.top) - } + modifier = + Modifier + .fillMaxWidth(1f) + .constrainAs(screenTitle) { + top.linkTo(parent.top) + } ) { Column { OutlinedTextField( - modifier = Modifier - .padding(vertical = 8.dp), + modifier = + Modifier + .padding(vertical = 8.dp), value = walletName.value, onValueChange = { walletName.value = it }, label = { @@ -100,11 +112,12 @@ internal fun RecoverWalletScreen( }, singleLine = true, textStyle = TextStyle(fontFamily = quattroRegular, color = DevkitWalletColors.white), - colors = OutlinedTextFieldDefaults.colors( - cursorColor = DevkitWalletColors.accent1, - focusedBorderColor = DevkitWalletColors.accent1, - unfocusedBorderColor = DevkitWalletColors.white, - ), + colors = + OutlinedTextFieldDefaults.colors( + cursorColor = DevkitWalletColors.accent1, + focusedBorderColor = DevkitWalletColors.accent1, + unfocusedBorderColor = DevkitWalletColors.white, + ), ) network.forEach { @@ -130,19 +143,21 @@ internal fun RecoverWalletScreen( MyList( recoveryPhraseWordMap, setRecoveryPhraseWordMap, - modifier = Modifier - .constrainAs(body) { - top.linkTo(screenTitle.bottom) - bottom.linkTo(parent.bottom) - height = Dimension.fillToConstraints - }, + modifier = + Modifier + .constrainAs(body) { + top.linkTo(screenTitle.bottom) + bottom.linkTo(parent.bottom) + height = Dimension.fillToConstraints + }, onClick = { - val recoverWalletConfig = RecoverWalletConfig( - name = walletName.value, - network = selectedNetwork, - scriptType = selectedScriptType, - recoveryPhrase = buildRecoveryPhrase(recoveryPhraseWordMap), - ) + val recoverWalletConfig = + RecoverWalletConfig( + name = walletName.value, + network = selectedNetwork, + scriptType = selectedScriptType, + recoveryPhrase = buildRecoveryPhrase(recoveryPhraseWordMap), + ) onBuildWalletButtonClicked(WalletCreateType.RECOVER(recoverWalletConfig)) } ) @@ -155,7 +170,7 @@ fun MyList( recoveryPhraseWordMap: Map, setRecoveryPhraseWordMap: (Map) -> Unit, modifier: Modifier, - onClick: () -> Unit + onClick: () -> Unit, ) { val scrollState = rememberScrollState() Column( @@ -182,7 +197,7 @@ fun WordField( wordNumber: Int, recoveryWordMap: Map, setRecoveryPhraseWordMap: (Map) -> Unit, - focusManager: FocusManager + focusManager: FocusManager, ) { OutlinedTextField( value = recoveryWordMap[wordNumber] ?: "elvis is here", @@ -199,25 +214,30 @@ fun WordField( color = DevkitWalletColors.white, ) }, - textStyle = TextStyle( - fontSize = 16.sp, - color = DevkitWalletColors.white - ), - colors = OutlinedTextFieldDefaults.colors( - cursorColor = DevkitWalletColors.accent1, - focusedBorderColor = DevkitWalletColors.accent1, - unfocusedBorderColor = DevkitWalletColors.white, - ), - modifier = Modifier - .padding(4.dp), - keyboardOptions = when (wordNumber) { - 12 -> KeyboardOptions(imeAction = ImeAction.Done) - else -> KeyboardOptions(imeAction = ImeAction.Next) - }, - keyboardActions = KeyboardActions( - onNext = { focusManager.moveFocus(FocusDirection.Down) }, - onDone = { focusManager.clearFocus() } - ), + textStyle = + TextStyle( + fontSize = 16.sp, + color = DevkitWalletColors.white + ), + colors = + OutlinedTextFieldDefaults.colors( + cursorColor = DevkitWalletColors.accent1, + focusedBorderColor = DevkitWalletColors.accent1, + unfocusedBorderColor = DevkitWalletColors.white, + ), + modifier = + Modifier + .padding(4.dp), + keyboardOptions = + when (wordNumber) { + 12 -> KeyboardOptions(imeAction = ImeAction.Done) + else -> KeyboardOptions(imeAction = ImeAction.Next) + }, + keyboardActions = + KeyboardActions( + onNext = { focusManager.moveFocus(FocusDirection.Down) }, + onDone = { focusManager.clearFocus() } + ), singleLine = true, // contentPadding = TextFieldDefaults.contentPaddingWithoutLabel( // start = 8.dp, @@ -232,7 +252,13 @@ fun WordField( private fun buildRecoveryPhrase(recoveryPhraseWordMap: Map): String { var recoveryPhrase = "" recoveryPhraseWordMap.values.forEach { - recoveryPhrase = recoveryPhrase.plus(it.trim().replace(" ", "").lowercase().plus(" ")) + recoveryPhrase = recoveryPhrase.plus( + it + .trim() + .replace(" ", "") + .lowercase() + .plus(" ") + ) } return recoveryPhrase.trim() } diff --git a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/intro/WalletChoiceScreen.kt b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/intro/WalletChoiceScreen.kt index 9d127e3..73d1fd1 100644 --- a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/intro/WalletChoiceScreen.kt +++ b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/intro/WalletChoiceScreen.kt @@ -36,26 +36,26 @@ import org.bitcoindevkit.devkitwallet.presentation.theme.DevkitWalletColors import org.bitcoindevkit.devkitwallet.presentation.theme.monoBold @Composable -internal fun WalletChoiceScreen( - navController: NavController, -) { +internal fun WalletChoiceScreen(navController: NavController) { Scaffold( containerColor = DevkitWalletColors.primary ) { paddingValues -> ConstraintLayout( - modifier = Modifier - .fillMaxSize() - .padding(paddingValues) + modifier = + Modifier + .fillMaxSize() + .padding(paddingValues) ) { val (logo, active, create, recover) = createRefs() Row( - modifier = Modifier - .fillMaxWidth() - .padding(top = 90.dp) - .constrainAs(logo) { - top.linkTo(parent.top) - }, + modifier = + Modifier + .fillMaxWidth() + .padding(top = 90.dp) + .constrainAs(logo) { + top.linkTo(parent.top) + }, horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically, ) { @@ -79,15 +79,16 @@ internal fun WalletChoiceScreen( colors = ButtonDefaults.buttonColors(DevkitWalletColors.secondary), shape = RoundedCornerShape(16.dp), enabled = true, - modifier = Modifier - .size(width = 300.dp, height = 150.dp) - .padding(vertical = 8.dp, horizontal = 8.dp) - .shadow(elevation = 8.dp, shape = RoundedCornerShape(16.dp)) - .constrainAs(active) { - bottom.linkTo(create.top) - start.linkTo(parent.start) - end.linkTo(parent.end) - } + modifier = + Modifier + .size(width = 300.dp, height = 150.dp) + .padding(vertical = 8.dp, horizontal = 8.dp) + .shadow(elevation = 8.dp, shape = RoundedCornerShape(16.dp)) + .constrainAs(active) { + bottom.linkTo(create.top) + start.linkTo(parent.start) + end.linkTo(parent.end) + } ) { Text( text = "Use an\nActive Wallet", @@ -101,15 +102,16 @@ internal fun WalletChoiceScreen( onClick = { navController.navigate(CreateNewWalletScreen) }, colors = ButtonDefaults.buttonColors(DevkitWalletColors.secondary), shape = RoundedCornerShape(16.dp), - modifier = Modifier - .size(width = 300.dp, height = 150.dp) - .padding(vertical = 8.dp, horizontal = 8.dp) - .shadow(elevation = 8.dp, shape = RoundedCornerShape(16.dp)) - .constrainAs(create) { - bottom.linkTo(recover.top) - start.linkTo(parent.start) - end.linkTo(parent.end) - } + modifier = + Modifier + .size(width = 300.dp, height = 150.dp) + .padding(vertical = 8.dp, horizontal = 8.dp) + .shadow(elevation = 8.dp, shape = RoundedCornerShape(16.dp)) + .constrainAs(create) { + bottom.linkTo(recover.top) + start.linkTo(parent.start) + end.linkTo(parent.end) + } ) { Text( text = "Create a\nNew Wallet", @@ -123,15 +125,16 @@ internal fun WalletChoiceScreen( onClick = { navController.navigate(WalletRecoveryScreen) }, colors = ButtonDefaults.buttonColors(DevkitWalletColors.secondary), shape = RoundedCornerShape(16.dp), - modifier = Modifier - .size(width = 300.dp, height = 150.dp) - .padding(vertical = 8.dp, horizontal = 8.dp) - .shadow(elevation = 8.dp, shape = RoundedCornerShape(16.dp)) - .constrainAs(recover) { - bottom.linkTo(parent.bottom, margin = 70.dp) - start.linkTo(parent.start) - end.linkTo(parent.end) - } + modifier = + Modifier + .size(width = 300.dp, height = 150.dp) + .padding(vertical = 8.dp, horizontal = 8.dp) + .shadow(elevation = 8.dp, shape = RoundedCornerShape(16.dp)) + .constrainAs(recover) { + bottom.linkTo(parent.bottom, margin = 70.dp) + start.linkTo(parent.start) + end.linkTo(parent.end) + } ) { Text( text = "Recover an\nExisting Wallet", diff --git a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/wallet/ReceiveScreen.kt b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/wallet/ReceiveScreen.kt index 6cbe741..a4052ce 100644 --- a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/wallet/ReceiveScreen.kt +++ b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/wallet/ReceiveScreen.kt @@ -48,18 +48,18 @@ import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.Dimension import androidx.core.graphics.createBitmap import androidx.navigation.NavController -import org.bitcoindevkit.devkitwallet.presentation.ui.components.SecondaryScreensAppBar -import org.bitcoindevkit.devkitwallet.presentation.theme.DevkitWalletColors +import com.composables.icons.lucide.ClipboardCopy +import com.composables.icons.lucide.Lucide import com.google.zxing.BarcodeFormat import com.google.zxing.common.BitMatrix import com.google.zxing.qrcode.QRCodeWriter import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch -import com.composables.icons.lucide.Lucide -import com.composables.icons.lucide.ClipboardCopy import org.bitcoindevkit.devkitwallet.presentation.navigation.HomeScreen +import org.bitcoindevkit.devkitwallet.presentation.theme.DevkitWalletColors import org.bitcoindevkit.devkitwallet.presentation.theme.monoRegular +import org.bitcoindevkit.devkitwallet.presentation.ui.components.SecondaryScreensAppBar import org.bitcoindevkit.devkitwallet.presentation.viewmodels.mvi.ReceiveScreenAction import org.bitcoindevkit.devkitwallet.presentation.viewmodels.mvi.ReceiveScreenState @@ -72,10 +72,12 @@ internal fun ReceiveScreen( navController: NavController, ) { Log.i(TAG, "We are recomposing the ReceiveScreen") - val snackbarHostState = remember { - SnackbarHostState() - } - Scaffold( snackbarHost = { SnackbarHost(snackbarHostState)}, + val snackbarHostState = + remember { + SnackbarHostState() + } + Scaffold( + snackbarHost = { SnackbarHost(snackbarHostState) }, topBar = { SecondaryScreensAppBar( title = "Receive Address", @@ -85,9 +87,10 @@ internal fun ReceiveScreen( containerColor = DevkitWalletColors.primary ) { paddingValues -> ConstraintLayout( - modifier = Modifier - .padding(paddingValues) - .fillMaxSize() + modifier = + Modifier + .padding(paddingValues) + .fillMaxSize() ) { val (QRCode, bottomButtons) = createRefs() val context = LocalContext.current @@ -95,41 +98,42 @@ internal fun ReceiveScreen( Column( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, - modifier = Modifier - .constrainAs(QRCode) { - top.linkTo(parent.top) - bottom.linkTo(bottomButtons.top) - start.linkTo(parent.start) - end.linkTo(parent.end) - height = Dimension.fillToConstraints - } - .padding(horizontal = 32.dp) + modifier = + Modifier + .constrainAs(QRCode) { + top.linkTo(parent.top) + bottom.linkTo(bottomButtons.top) + start.linkTo(parent.start) + end.linkTo(parent.end) + height = Dimension.fillToConstraints + }.padding(horizontal = 32.dp) ) { - val QR: ImageBitmap? = state.address?.let { addressToQR(it) } + val qr: ImageBitmap? = state.address?.let { addressToQR(it) } Log.i("ReceiveScreen", "New receive address is ${state.address}") - if (QR != null) { + if (qr != null) { Image( - bitmap = QR, + bitmap = qr, contentDescription = "Bitcoindevkit website QR code", Modifier.size(250.dp).clip(RoundedCornerShape(16.dp)) ) Spacer(modifier = Modifier.padding(vertical = 16.dp)) Box { SelectionContainer { - Text(modifier = Modifier - .clickable { - copyToClipboard( - state.address, - context, - scope, - snackbarHostState, - null - ) - } - .background( - color = DevkitWalletColors.primaryLight, - shape = RoundedCornerShape(16.dp) - ).padding(12.dp), + Text( + modifier = + Modifier + .clickable { + copyToClipboard( + state.address, + context, + scope, + snackbarHostState, + null + ) + }.background( + color = DevkitWalletColors.primaryLight, + shape = RoundedCornerShape(16.dp) + ).padding(12.dp), text = state.address.chunked(4).joinToString(" "), fontFamily = monoRegular, color = DevkitWalletColors.white @@ -139,10 +143,11 @@ internal fun ReceiveScreen( Lucide.ClipboardCopy, tint = Color.White, contentDescription = "Copy to clipboard", - modifier = Modifier - .padding(8.dp) - .size(20.dp) - .align(Alignment.BottomEnd) + modifier = + Modifier + .padding(8.dp) + .size(20.dp) + .align(Alignment.BottomEnd) ) } Spacer(modifier = Modifier.padding(vertical = 16.dp)) @@ -161,18 +166,18 @@ internal fun ReceiveScreen( bottom.linkTo(parent.bottom) start.linkTo(parent.start) end.linkTo(parent.end) - } - .padding(bottom = 24.dp) + }.padding(bottom = 24.dp) ) { Button( onClick = { onAction(ReceiveScreenAction.UpdateAddress) }, colors = ButtonDefaults.buttonColors(DevkitWalletColors.secondary), shape = RoundedCornerShape(16.dp), - modifier = Modifier - .height(80.dp) - .fillMaxWidth(0.9f) - .padding(vertical = 8.dp, horizontal = 8.dp) - .shadow(elevation = 4.dp, shape = RoundedCornerShape(16.dp)) + modifier = + Modifier + .height(80.dp) + .fillMaxWidth(0.9f) + .padding(vertical = 8.dp, horizontal = 8.dp) + .shadow(elevation = 4.dp, shape = RoundedCornerShape(16.dp)) ) { Text( text = "Generate address", @@ -206,7 +211,15 @@ private fun addressToQR(address: String): ImageBitmap? { return null } -fun copyToClipboard(content: String, context: Context, scope: CoroutineScope, snackbarHostState: SnackbarHostState, setCopyClicked: ((Boolean) -> Unit)?) { +fun copyToClipboard( + content: String, + context: Context, + scope: CoroutineScope, + snackbarHostState: SnackbarHostState, + setCopyClicked: ( + (Boolean) -> Unit + )?, +) { val clipboard: ClipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager val clip: ClipData = ClipData.newPlainText("", content) clipboard.setPrimaryClip(clip) diff --git a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/wallet/SendScreen.kt b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/wallet/SendScreen.kt index 7e08603..d34f5ea 100644 --- a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/wallet/SendScreen.kt +++ b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/wallet/SendScreen.kt @@ -69,10 +69,7 @@ private const val TAG = "SendScreen" @OptIn(ExperimentalMaterial3Api::class) @Composable -internal fun SendScreen( - navController: NavController, - sendViewModel: SendViewModel -) { +internal fun SendScreen(navController: NavController, sendViewModel: SendViewModel) { val onAction = sendViewModel::onAction val context = LocalContext.current @@ -80,7 +77,7 @@ internal fun SendScreen( val recipientList: MutableList = remember { mutableStateListOf(Recipient(address = "", amount = 0u)) } val feeRate: MutableState = rememberSaveable { mutableStateOf("") } - val (showDialog, setShowDialog) = rememberSaveable { mutableStateOf(false) } + val (showDialog, setShowDialog) = rememberSaveable { mutableStateOf(false) } val sendAll: MutableState = remember { mutableStateOf(false) } val rbfDisabled: MutableState = remember { mutableStateOf(false) } @@ -102,22 +99,24 @@ internal fun SendScreen( sheetPeekHeight = 0.dp, ) { paddingValues -> ConstraintLayout( - modifier = Modifier - .fillMaxSize() - .padding(paddingValues) - .background(DevkitWalletColors.primary) + modifier = + Modifier + .fillMaxSize() + .padding(paddingValues) + .background(DevkitWalletColors.primary) ) { val (transactionInputs, bottomButtons) = createRefs() Column( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, - modifier = Modifier.constrainAs(transactionInputs) { - top.linkTo(parent.top) - bottom.linkTo(bottomButtons.top) - start.linkTo(parent.start) - end.linkTo(parent.end) - height = Dimension.fillToConstraints - } + modifier = + Modifier.constrainAs(transactionInputs) { + top.linkTo(parent.top) + bottom.linkTo(bottomButtons.top) + start.linkTo(parent.start) + end.linkTo(parent.end) + height = Dimension.fillToConstraints + } ) { TransactionRecipientInput(recipientList = recipientList) TransactionAmountInput( @@ -143,18 +142,18 @@ internal fun SendScreen( bottom.linkTo(parent.bottom) start.linkTo(parent.start) end.linkTo(parent.end) - } - .padding(bottom = 32.dp) + }.padding(bottom = 32.dp) ) { Button( onClick = { setShowDialog(true) }, colors = ButtonDefaults.buttonColors(DevkitWalletColors.accent2), shape = RoundedCornerShape(16.dp), - modifier = Modifier - .height(80.dp) - .fillMaxWidth(0.9f) - .padding(vertical = 8.dp, horizontal = 8.dp) - .shadow(elevation = 4.dp, shape = RoundedCornerShape(16.dp)) + modifier = + Modifier + .height(80.dp) + .fillMaxWidth(0.9f) + .padding(vertical = 8.dp, horizontal = 8.dp) + .shadow(elevation = 4.dp, shape = RoundedCornerShape(16.dp)) ) { Text( text = "broadcast transaction", @@ -173,12 +172,13 @@ internal fun SendScreen( internal fun AdvancedOptions( sendAll: MutableState, opReturnMsg: MutableState, - recipientList: MutableList + recipientList: MutableList, ) { Column( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp, vertical = 8.dp) + modifier = + Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 8.dp) ) { Row( Modifier @@ -210,23 +210,27 @@ internal fun AdvancedOptions( checked = sendAll.value, onCheckedChange = { sendAll.value = !sendAll.value - while (recipientList.size > 1) { recipientList.removeLast() } + while (recipientList.size > 1) { + recipientList.removeLast() + } }, - colors = SwitchDefaults.colors( - uncheckedBorderColor = DevkitWalletColors.primaryDark, - uncheckedThumbColor = DevkitWalletColors.primaryDark, - uncheckedTrackColor = DevkitWalletColors.white, - checkedThumbColor = DevkitWalletColors.white, - checkedTrackColor = DevkitWalletColors.accent1, - ) + colors = + SwitchDefaults.colors( + uncheckedBorderColor = DevkitWalletColors.primaryDark, + uncheckedThumbColor = DevkitWalletColors.primaryDark, + uncheckedTrackColor = DevkitWalletColors.white, + checkedThumbColor = DevkitWalletColors.white, + checkedTrackColor = DevkitWalletColors.accent1, + ) ) } Row(verticalAlignment = Alignment.CenterVertically) { OutlinedTextField( - modifier = Modifier - .padding(vertical = 8.dp) - .weight(0.5f), + modifier = + Modifier + .padding(vertical = 8.dp) + .weight(0.5f), value = opReturnMsg.value ?: "", onValueChange = { opReturnMsg.value = it @@ -239,11 +243,12 @@ internal fun AdvancedOptions( }, singleLine = true, textStyle = TextStyle(color = DevkitWalletColors.white), - colors = OutlinedTextFieldDefaults.colors( - cursorColor = DevkitWalletColors.accent1, - focusedBorderColor = DevkitWalletColors.accent1, - unfocusedBorderColor = DevkitWalletColors.white, - ), + colors = + OutlinedTextFieldDefaults.colors( + cursorColor = DevkitWalletColors.accent1, + focusedBorderColor = DevkitWalletColors.accent1, + unfocusedBorderColor = DevkitWalletColors.white, + ), ) } @@ -266,7 +271,11 @@ internal fun AdvancedOptions( verticalAlignment = Alignment.CenterVertically ) { Button( - onClick = { if (recipientList.size > 1) { recipientList.removeLast() } }, + onClick = { + if (recipientList.size > 1) { + recipientList.removeLast() + } + }, enabled = !sendAll.value, colors = ButtonDefaults.buttonColors(DevkitWalletColors.accent2), shape = RoundedCornerShape(16.dp), @@ -298,17 +307,21 @@ internal fun AdvancedOptions( @Composable private fun TransactionRecipientInput(recipientList: MutableList) { - LazyColumn (modifier = Modifier - .fillMaxWidth(0.9f) - .heightIn(max = 100.dp)) { + LazyColumn( + modifier = + Modifier + .fillMaxWidth(0.9f) + .heightIn(max = 100.dp) + ) { itemsIndexed(recipientList) { index, _ -> val recipientAddress: MutableState = rememberSaveable { mutableStateOf("") } Row(verticalAlignment = Alignment.CenterVertically) { OutlinedTextField( - modifier = Modifier - .padding(vertical = 8.dp) - .weight(0.5f), + modifier = + Modifier + .padding(vertical = 8.dp) + .weight(0.5f), value = recipientAddress.value, onValueChange = { recipientAddress.value = it @@ -322,11 +335,12 @@ private fun TransactionRecipientInput(recipientList: MutableList) { }, singleLine = true, textStyle = TextStyle(color = DevkitWalletColors.white), - colors = OutlinedTextFieldDefaults.colors( - cursorColor = DevkitWalletColors.accent1, - focusedBorderColor = DevkitWalletColors.accent1, - unfocusedBorderColor = DevkitWalletColors.white, - ), + colors = + OutlinedTextFieldDefaults.colors( + cursorColor = DevkitWalletColors.accent1, + focusedBorderColor = DevkitWalletColors.accent1, + unfocusedBorderColor = DevkitWalletColors.white, + ), ) } } @@ -336,7 +350,7 @@ private fun TransactionRecipientInput(recipientList: MutableList) { fun checkRecipientList( recipientList: MutableList, feeRate: MutableState, - context: Context + context: Context, ): Boolean { if (recipientList.size > 4) { Toast.makeText(context, "Too many recipients", Toast.LENGTH_SHORT).show() @@ -357,17 +371,21 @@ fun checkRecipientList( @Composable private fun TransactionAmountInput(recipientList: MutableList, transactionType: TransactionType) { - LazyColumn (modifier = Modifier - .fillMaxWidth(0.9f) - .heightIn(max = 100.dp)) { + LazyColumn( + modifier = + Modifier + .fillMaxWidth(0.9f) + .heightIn(max = 100.dp) + ) { itemsIndexed(recipientList) { index, _ -> val amount: MutableState = rememberSaveable { mutableStateOf("") } Row(verticalAlignment = Alignment.CenterVertically) { OutlinedTextField( - modifier = Modifier - .padding(vertical = 8.dp) - .weight(0.5f), + modifier = + Modifier + .padding(vertical = 8.dp) + .weight(0.5f), value = amount.value, onValueChange = { amount.value = it @@ -391,16 +409,17 @@ private fun TransactionAmountInput(recipientList: MutableList, transa }, singleLine = true, textStyle = TextStyle(color = DevkitWalletColors.white), - colors = OutlinedTextFieldDefaults.colors( - cursorColor = DevkitWalletColors.accent1, - focusedBorderColor = DevkitWalletColors.accent1, - unfocusedBorderColor = DevkitWalletColors.white, - ), + colors = + OutlinedTextFieldDefaults.colors( + cursorColor = DevkitWalletColors.accent1, + focusedBorderColor = DevkitWalletColors.accent1, + unfocusedBorderColor = DevkitWalletColors.white, + ), enabled = ( - when (transactionType) { - TransactionType.SEND_ALL -> false - else -> true - } + when (transactionType) { + TransactionType.SEND_ALL -> false + else -> true + } ) ) } @@ -412,9 +431,10 @@ private fun TransactionAmountInput(recipientList: MutableList, transa private fun TransactionFeeInput(feeRate: MutableState) { Column(horizontalAlignment = Alignment.CenterHorizontally) { OutlinedTextField( - modifier = Modifier - .padding(vertical = 8.dp) - .fillMaxWidth(0.9f), + modifier = + Modifier + .padding(vertical = 8.dp) + .fillMaxWidth(0.9f), value = feeRate.value, onValueChange = { newValue: String -> feeRate.value = newValue.filter { it.isDigit() } @@ -427,11 +447,12 @@ private fun TransactionFeeInput(feeRate: MutableState) { color = DevkitWalletColors.white, ) }, - colors = OutlinedTextFieldDefaults.colors( - cursorColor = DevkitWalletColors.accent1, - focusedBorderColor = DevkitWalletColors.accent1, - unfocusedBorderColor = DevkitWalletColors.white, - ), + colors = + OutlinedTextFieldDefaults.colors( + cursorColor = DevkitWalletColors.accent1, + focusedBorderColor = DevkitWalletColors.accent1, + unfocusedBorderColor = DevkitWalletColors.white, + ), ) } } @@ -441,9 +462,10 @@ private fun TransactionFeeInput(feeRate: MutableState) { fun MoreOptions(coroutineScope: CoroutineScope, bottomSheetScaffoldState: BottomSheetScaffoldState) { Column( horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier - .padding(vertical = 8.dp) - .background(DevkitWalletColors.secondary) + modifier = + Modifier + .padding(vertical = 8.dp) + .background(DevkitWalletColors.secondary) ) { Button( onClick = { @@ -452,10 +474,11 @@ fun MoreOptions(coroutineScope: CoroutineScope, bottomSheetScaffoldState: Bottom } }, colors = ButtonDefaults.buttonColors(Color.Transparent), - modifier = Modifier - .height(60.dp) - .fillMaxWidth(fraction = 0.9f) - .padding(vertical = 8.dp) + modifier = + Modifier + .height(60.dp) + .fillMaxWidth(fraction = 0.9f) + .padding(vertical = 8.dp) ) { Text( text = "advanced options", @@ -480,7 +503,7 @@ private fun Dialog( ) { if (showDialog) { var confirmationText = "Confirm Transaction : \n" - recipientList.forEach { confirmationText += "${it.address}, ${it.amount}\n"} + recipientList.forEach { confirmationText += "${it.address}, ${it.amount}\n" } if (feeRate.value.isNotEmpty()) { confirmationText += "Fee Rate : ${feeRate.value.toULong()}" } @@ -506,12 +529,13 @@ private fun Dialog( TextButton( onClick = { if (checkRecipientList(recipientList = recipientList, feeRate = feeRate, context = context)) { - val txDataBundle = TxDataBundle( - recipients = recipientList, - feeRate = feeRate.value.toULong(), - transactionType = transactionType, - opReturnMsg = opReturnMsg - ) + val txDataBundle = + TxDataBundle( + recipients = recipientList, + feeRate = feeRate.value.toULong(), + transactionType = transactionType, + opReturnMsg = opReturnMsg + ) onAction(SendScreenAction.Broadcast(txDataBundle)) setShowDialog(false) } diff --git a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/wallet/TransactionHistoryScreen.kt b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/wallet/TransactionHistoryScreen.kt index 0517785..1a6b4fd 100644 --- a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/wallet/TransactionHistoryScreen.kt +++ b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/wallet/TransactionHistoryScreen.kt @@ -17,19 +17,16 @@ import androidx.compose.ui.unit.dp import androidx.navigation.NavController import org.bitcoindevkit.devkitwallet.domain.Wallet import org.bitcoindevkit.devkitwallet.presentation.navigation.HomeScreen +import org.bitcoindevkit.devkitwallet.presentation.navigation.TransactionScreen +import org.bitcoindevkit.devkitwallet.presentation.theme.DevkitWalletColors import org.bitcoindevkit.devkitwallet.presentation.ui.components.ConfirmedTransactionCard import org.bitcoindevkit.devkitwallet.presentation.ui.components.PendingTransactionCard import org.bitcoindevkit.devkitwallet.presentation.ui.components.SecondaryScreensAppBar -import org.bitcoindevkit.devkitwallet.presentation.theme.DevkitWalletColors -import org.bitcoindevkit.devkitwallet.presentation.navigation.TransactionScreen private const val TAG = "TransactionHistoryScreen" @Composable -internal fun TransactionHistoryScreen( - navController: NavController, - activeWallet: Wallet -) { +internal fun TransactionHistoryScreen(navController: NavController, activeWallet: Wallet) { val (pendingTransactions, confirmedTransactions) = activeWallet.getAllTxDetails().partition { it.pending } Scaffold( @@ -43,11 +40,12 @@ internal fun TransactionHistoryScreen( ) { paddingValues -> val scrollState = rememberScrollState() Column( - modifier = Modifier - .padding(paddingValues) - .fillMaxSize() - .padding(top = 6.dp) - .verticalScroll(state = scrollState) + modifier = + Modifier + .padding(paddingValues) + .fillMaxSize() + .padding(top = 6.dp) + .verticalScroll(state = scrollState) ) { if (pendingTransactions.isNotEmpty()) { pendingTransactions.forEach { diff --git a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/wallet/TransactionScreen.kt b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/wallet/TransactionScreen.kt index 66351f1..4508250 100644 --- a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/wallet/TransactionScreen.kt +++ b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/wallet/TransactionScreen.kt @@ -29,15 +29,12 @@ import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.Dimension import androidx.navigation.NavController import org.bitcoindevkit.devkitwallet.presentation.navigation.RbfScreen -import org.bitcoindevkit.devkitwallet.presentation.ui.components.SecondaryScreensAppBar import org.bitcoindevkit.devkitwallet.presentation.theme.DevkitWalletColors import org.bitcoindevkit.devkitwallet.presentation.theme.monoRegular +import org.bitcoindevkit.devkitwallet.presentation.ui.components.SecondaryScreensAppBar @Composable -internal fun TransactionScreen( - txid: String?, - navController: NavController, -) { +internal fun TransactionScreen(txid: String?, navController: NavController) { // val transaction = getTransaction(txid = txid) // if (transaction == null) { // navController.popBackStack() @@ -54,21 +51,22 @@ internal fun TransactionScreen( containerColor = DevkitWalletColors.primary ) { paddingValues -> ConstraintLayout( - modifier = Modifier - .fillMaxSize() - .background(DevkitWalletColors.primary) - .padding(paddingValues) + modifier = + Modifier + .fillMaxSize() + .background(DevkitWalletColors.primary) + .padding(paddingValues) ) { val (screenTitle, transactions, bottomButton) = createRefs() Column( - modifier = Modifier - .constrainAs(screenTitle) { - top.linkTo(parent.top) - start.linkTo(parent.start) - end.linkTo(parent.end) - } - .padding(top = 70.dp) + modifier = + Modifier + .constrainAs(screenTitle) { + top.linkTo(parent.top) + start.linkTo(parent.start) + end.linkTo(parent.end) + }.padding(top = 70.dp) ) { Text( text = "Transaction", @@ -87,17 +85,17 @@ internal fun TransactionScreen( // ) } - LazyColumn( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, - modifier = Modifier.constrainAs(transactions) { - top.linkTo(screenTitle.bottom) - bottom.linkTo(bottomButton.top) - start.linkTo(parent.start) - end.linkTo(parent.end) - height = Dimension.fillToConstraints - } + modifier = + Modifier.constrainAs(transactions) { + top.linkTo(screenTitle.bottom) + bottom.linkTo(bottomButton.top) + start.linkTo(parent.start) + end.linkTo(parent.end) + height = Dimension.fillToConstraints + } ) { // items(transactionDetail) { // Row( @@ -122,15 +120,16 @@ internal fun TransactionScreen( } Column( - modifier = Modifier - .fillMaxWidth(0.9f) - .padding(vertical = 8.dp, horizontal = 8.dp) - .shadow(elevation = 4.dp, shape = RoundedCornerShape(16.dp)) - .constrainAs(bottomButton) { - bottom.linkTo(parent.bottom) - start.linkTo(parent.start) - end.linkTo(parent.end) - } + modifier = + Modifier + .fillMaxWidth(0.9f) + .padding(vertical = 8.dp, horizontal = 8.dp) + .shadow(elevation = 4.dp, shape = RoundedCornerShape(16.dp)) + .constrainAs(bottomButton) { + bottom.linkTo(parent.bottom) + start.linkTo(parent.start) + end.linkTo(parent.end) + } ) { TransactionDetailButton( content = "increase fees", @@ -139,7 +138,6 @@ internal fun TransactionScreen( ) } } - } } @@ -158,9 +156,10 @@ fun TransactionDetailButton(content: String, navController: NavController, txid: }, colors = ButtonDefaults.buttonColors(DevkitWalletColors.secondary), shape = RoundedCornerShape(16.dp), - modifier = Modifier - .height(60.dp) - .fillMaxWidth() + modifier = + Modifier + .height(60.dp) + .fillMaxWidth() ) { Text( text = content, diff --git a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/wallet/WalletHomeScreen.kt b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/wallet/WalletHomeScreen.kt index 24aeb7a..b3f1358 100644 --- a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/wallet/WalletHomeScreen.kt +++ b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/ui/screens/wallet/WalletHomeScreen.kt @@ -23,6 +23,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.CurrencyBitcoin import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.CenterAlignedTopAppBar @@ -44,24 +45,23 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavHostController -import androidx.compose.material.icons.rounded.CurrencyBitcoin import com.composables.icons.lucide.Lucide import com.composables.icons.lucide.Menu -import org.bitcoindevkit.devkitwallet.presentation.ui.components.LoadingAnimation -import org.bitcoindevkit.devkitwallet.presentation.ui.components.NeutralButton -import org.bitcoindevkit.devkitwallet.presentation.theme.DevkitWalletColors -import org.bitcoindevkit.devkitwallet.domain.utils.formatInBtc -import org.bitcoindevkit.devkitwallet.presentation.viewmodels.WalletViewModel import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import org.bitcoindevkit.devkitwallet.domain.CurrencyUnit +import org.bitcoindevkit.devkitwallet.domain.utils.formatInBtc +import org.bitcoindevkit.devkitwallet.presentation.navigation.ReceiveScreen +import org.bitcoindevkit.devkitwallet.presentation.navigation.SendScreen import org.bitcoindevkit.devkitwallet.presentation.navigation.TransactionHistoryScreen +import org.bitcoindevkit.devkitwallet.presentation.theme.DevkitWalletColors import org.bitcoindevkit.devkitwallet.presentation.theme.monoRegular import org.bitcoindevkit.devkitwallet.presentation.theme.quattroBold +import org.bitcoindevkit.devkitwallet.presentation.ui.components.LoadingAnimation +import org.bitcoindevkit.devkitwallet.presentation.ui.components.NeutralButton +import org.bitcoindevkit.devkitwallet.presentation.viewmodels.WalletViewModel import org.bitcoindevkit.devkitwallet.presentation.viewmodels.mvi.WalletScreenAction import org.bitcoindevkit.devkitwallet.presentation.viewmodels.mvi.WalletScreenState -import org.bitcoindevkit.devkitwallet.presentation.navigation.ReceiveScreen -import org.bitcoindevkit.devkitwallet.presentation.navigation.SendScreen private const val TAG = "WalletHomeScreen" @@ -83,9 +83,10 @@ internal fun WalletHomeScreen( containerColor = DevkitWalletColors.primary ) { paddingValues -> Column( - modifier = Modifier - .fillMaxSize() - .padding(paddingValues), + modifier = + Modifier + .fillMaxSize() + .padding(paddingValues), horizontalAlignment = Alignment.CenterHorizontally, ) { Spacer(Modifier.padding(24.dp)) @@ -95,18 +96,16 @@ internal fun WalletHomeScreen( interactionSource, indication = null, onClick = { onAction(WalletScreenAction.SwitchUnit) } - ) - .fillMaxWidth(0.9f) + ).fillMaxWidth(0.9f) .padding(horizontal = 8.dp) .background( color = DevkitWalletColors.primaryLight, shape = RoundedCornerShape(16.dp) - ) - .height(100.dp), + ).height(100.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceEvenly ) { - when(state.unit) { + when (state.unit) { CurrencyUnit.Bitcoin -> { Icon( imageVector = Icons.Rounded.CurrencyBitcoin, @@ -176,56 +175,61 @@ internal fun WalletHomeScreen( Row( horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, - modifier = Modifier - .height(140.dp) - .fillMaxWidth(0.9f) + modifier = + Modifier + .height(140.dp) + .fillMaxWidth(0.9f) ) { Button( onClick = { navController.navigate(ReceiveScreen) }, colors = ButtonDefaults.buttonColors(DevkitWalletColors.accent1), shape = RoundedCornerShape(16.dp), - modifier = Modifier - .height(160.dp) - .padding(vertical = 8.dp, horizontal = 8.dp) - .shadow(elevation = 4.dp, shape = RoundedCornerShape(16.dp)) + modifier = + Modifier + .height(160.dp) + .padding(vertical = 8.dp, horizontal = 8.dp) + .shadow(elevation = 4.dp, shape = RoundedCornerShape(16.dp)) ) { Text( text = "receive", fontSize = 16.sp, textAlign = TextAlign.End, lineHeight = 28.sp, - modifier = Modifier - .fillMaxWidth(0.4f) - .align(Alignment.Bottom) + modifier = + Modifier + .fillMaxWidth(0.4f) + .align(Alignment.Bottom) ) } Button( onClick = { navController.navigate(SendScreen) }, - colors = ButtonDefaults.buttonColors( - containerColor = DevkitWalletColors.accent2, - disabledContainerColor = DevkitWalletColors.accent2, - ), + colors = + ButtonDefaults.buttonColors( + containerColor = DevkitWalletColors.accent2, + disabledContainerColor = DevkitWalletColors.accent2, + ), shape = RoundedCornerShape(16.dp), enabled = networkAvailable, - modifier = Modifier - .height(160.dp) - .padding(vertical = 8.dp, horizontal = 8.dp) - .shadow(elevation = 4.dp, shape = RoundedCornerShape(16.dp)) + modifier = + Modifier + .height(160.dp) + .padding(vertical = 8.dp, horizontal = 8.dp) + .shadow(elevation = 4.dp, shape = RoundedCornerShape(16.dp)) ) { Text( text = "send", fontSize = 16.sp, textAlign = TextAlign.End, lineHeight = 28.sp, - modifier = Modifier - .fillMaxWidth() - .align(Alignment.Bottom) + modifier = + Modifier + .fillMaxWidth() + .align(Alignment.Bottom) ) } } } - } } @@ -251,9 +255,10 @@ internal fun WalletAppBar(scope: CoroutineScope, drawerState: DrawerState) { ) } }, - colors = TopAppBarDefaults.topAppBarColors( - containerColor = DevkitWalletColors.primaryDark, - ) + colors = + TopAppBarDefaults.topAppBarColors( + containerColor = DevkitWalletColors.primaryDark, + ) ) } diff --git a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/viewmodels/SendViewModel.kt b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/viewmodels/SendViewModel.kt index e7188d5..bd5c698 100644 --- a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/viewmodels/SendViewModel.kt +++ b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/viewmodels/SendViewModel.kt @@ -17,7 +17,6 @@ import org.rustbitcoin.bitcoin.FeeRate private const val TAG = "SendViewModel" internal class SendViewModel(private val wallet: Wallet) : ViewModel() { - fun onAction(action: SendScreenAction) { when (action) { is SendScreenAction.Broadcast -> broadcast(action.txDataBundle) @@ -27,15 +26,17 @@ internal class SendViewModel(private val wallet: Wallet) : ViewModel() { private fun broadcast(txInfo: TxDataBundle) { try { // Create, sign, and broadcast - val psbt: Psbt = when (txInfo.transactionType) { - TransactionType.STANDARD -> wallet.createTransaction( - recipientList = txInfo.recipients, - feeRate = FeeRate.fromSatPerVb(txInfo.feeRate), - opReturnMsg = txInfo.opReturnMsg - ) - // TransactionType.SEND_ALL -> Wallet.createSendAllTransaction(recipientList[0].address, FeeRate.fromSatPerVb(feeRate), rbfEnabled, opReturnMsg) - TransactionType.SEND_ALL -> throw NotImplementedError("Send all not implemented") - } + val psbt: Psbt = + when (txInfo.transactionType) { + TransactionType.STANDARD -> + wallet.createTransaction( + recipientList = txInfo.recipients, + feeRate = FeeRate.fromSatPerVb(txInfo.feeRate), + opReturnMsg = txInfo.opReturnMsg + ) + // TransactionType.SEND_ALL -> Wallet.createSendAllTransaction(recipientList[0].address, FeeRate.fromSatPerVb(feeRate), rbfEnabled, opReturnMsg) + TransactionType.SEND_ALL -> throw NotImplementedError("Send all not implemented") + } val isSigned = wallet.sign(psbt) if (isSigned) { val txid: String = wallet.broadcast(psbt) diff --git a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/viewmodels/WalletViewModel.kt b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/viewmodels/WalletViewModel.kt index 0d32eea..692dfbb 100644 --- a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/viewmodels/WalletViewModel.kt +++ b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/viewmodels/WalletViewModel.kt @@ -11,20 +11,19 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import org.bitcoindevkit.devkitwallet.domain.Wallet import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.bitcoindevkit.devkitwallet.domain.CurrencyUnit +import org.bitcoindevkit.devkitwallet.domain.Wallet import org.bitcoindevkit.devkitwallet.presentation.viewmodels.mvi.WalletScreenAction import org.bitcoindevkit.devkitwallet.presentation.viewmodels.mvi.WalletScreenState private const val TAG = "WalletViewModel" class WalletViewModel( - private val wallet: Wallet + private val wallet: Wallet, ) : ViewModel() { - var state: WalletScreenState by mutableStateOf(WalletScreenState()) private set @@ -35,7 +34,7 @@ class WalletViewModel( fun onAction(action: WalletScreenAction) { when (action) { WalletScreenAction.UpdateBalance -> updateBalance() - WalletScreenAction.SwitchUnit -> switchUnit() + WalletScreenAction.SwitchUnit -> switchUnit() } } diff --git a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/viewmodels/mvi/MviReceiveScreen.kt b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/viewmodels/mvi/MviReceiveScreen.kt index 0bb9bda..fb3f954 100644 --- a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/viewmodels/mvi/MviReceiveScreen.kt +++ b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/viewmodels/mvi/MviReceiveScreen.kt @@ -7,7 +7,7 @@ package org.bitcoindevkit.devkitwallet.presentation.viewmodels.mvi data class ReceiveScreenState( val address: String? = null, - val addressIndex: UInt? = null + val addressIndex: UInt? = null, ) sealed interface ReceiveScreenAction { diff --git a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/viewmodels/mvi/MviWalletScreen.kt b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/viewmodels/mvi/MviWalletScreen.kt index e6b1a43..cef5212 100644 --- a/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/viewmodels/mvi/MviWalletScreen.kt +++ b/app/src/main/java/org/bitcoindevkit/devkitwallet/presentation/viewmodels/mvi/MviWalletScreen.kt @@ -11,10 +11,11 @@ data class WalletScreenState( val balance: ULong = 0u, val syncing: Boolean = false, val unit: CurrencyUnit = CurrencyUnit.Bitcoin, - val esploraEndpoint: String = "" + val esploraEndpoint: String = "", ) sealed interface WalletScreenAction { data object UpdateBalance : WalletScreenAction + data object SwitchUnit : WalletScreenAction } diff --git a/app/src/test/java/org/bitcoindevkit/devkitwallet/ExampleUnitTest.kt b/app/src/test/java/org/bitcoindevkit/devkitwallet/ExampleUnitTest.kt index fa2e130..636ec13 100644 --- a/app/src/test/java/org/bitcoindevkit/devkitwallet/ExampleUnitTest.kt +++ b/app/src/test/java/org/bitcoindevkit/devkitwallet/ExampleUnitTest.kt @@ -3,7 +3,6 @@ package org.bitcoindevkit.devkitwallet import org.junit.Assert.assertEquals import org.junit.Test - class ExampleUnitTest { @Test fun addition_isCorrect() { diff --git a/build.gradle.kts b/build.gradle.kts deleted file mode 100644 index 674dad1..0000000 --- a/build.gradle.kts +++ /dev/null @@ -1,7 +0,0 @@ -plugins { - id("com.android.application").version("8.7.1").apply(false) - id("org.jetbrains.kotlin.android").version("2.1.10").apply(false) - id("org.jetbrains.kotlin.plugin.compose").version("2.1.10").apply(false) - id("org.jetbrains.kotlin.plugin.serialization").version("2.1.10").apply(false) - id("com.google.protobuf").version("0.9.4").apply(false) -} diff --git a/gradle.properties b/gradle.properties index a69daa6..aef75d1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 android.useAndroidX=true -kotlin.code.style=official +#kotlin.code.style=official android.enableJetifier=true android.defaults.buildfeatures.buildconfig=true android.nonTransitiveRClass=false