Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ name: Build

# Verifies that the app compiles.

on: [workflow_dispatch, push, pull_request]
on:
push:
branches: [master]
pull_request:
workflow_dispatch:

permissions: {}

Expand Down
8 changes: 7 additions & 1 deletion .github/workflows/format-check.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
name: Format Check

on: [pull_request, workflow_dispatch]
# Runs the ktfmt formatter and ensures the source files do not need formatting.

on:
push:
branches: [master]
pull_request:
workflow_dispatch:

jobs:
format_check:
Expand Down
14 changes: 14 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,17 @@ format:
exit 1
fi
ktfmt --kotlinlang-style --enable-editorconfig src/

# Check formatting using ktfmt 0.64 without modifying files
format-check:
#!/usr/bin/env bash
set -euo pipefail
version=$(ktfmt --version | grep -oE '[0-9]+\.[0-9]+')
if [ "$version" != "0.64" ]; then
echo "Error: ktfmt 0.64 is required, but found $version" >&2
exit 1
fi
if ! ktfmt --kotlinlang-style --enable-editorconfig --dry-run --set-exit-if-changed src/ > /dev/null; then
echo "Error: formatting issues found. Run 'just format' to fix them." >&2
exit 1
fi
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ fun AppNavigation(
WalletHomeScreen(
state = walletViewModel!!.state,
onAction = walletViewModel::onAction,
snackbarMessages = walletViewModel.snackbarMessages,
navController = navController,
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ fun OnboardingScreen(onFinishOnboarding: () -> Unit) {

val messages =
listOf(
"Easter egg #1: \uD83E\uDD5A",
"\uD83E\uDD5A\nEaster egg #1",
"Welcome to the Devkit Wallet! This app is a playground for developers and bitcoin enthusiasts to experiment with bitcoin's test networks.",
"It is developed with the Bitcoin Dev Kit, a powerful set of libraries produced and maintained by the Bitcoin Dev Kit Foundation.\n\nThis version of the app is using Compact Block Filters to sync its wallets.",
"The Foundation maintains this app as a way to showcase the capabilities of the Bitcoin Dev Kit and to provide a starting point for developers to build their own apps.\n\nIt is not a production application, and only works for testnet3, testnet4, signet, and regtest. Have fun!",
"The Foundation maintains this app as a way to showcase the capabilities of the Bitcoin Dev Kit and to provide a starting point for developers to build their own apps.\n\nIt is not a production application and only works for the Testnet3, Testnet4, Signet, and Regtest networks. Have fun!",
)

Column(
Expand All @@ -67,38 +67,25 @@ fun OnboardingScreen(onFinishOnboarding: () -> Unit) {

// Logo
Box(
modifier =
Modifier.size(100.dp)
.border(
width = 2.dp,
color = accent.copy(alpha = 0.20f),
shape = CircleShape,
),
modifier = Modifier.size(200.dp),
contentAlignment = Alignment.Center,
) {
Image(
painter = painterResource(id = R.drawable.bdk_logo),
contentDescription = "Bitcoin Dev Kit logo",
modifier = Modifier.size(56.dp),
modifier = Modifier.size(120.dp),
)
}

Spacer(Modifier.height(16.dp))
Spacer(Modifier.height(32.dp))

Text(
text = "Devkit Wallet",
fontFamily = inter,
fontSize = 24.sp,
fontSize = 28.sp,
fontWeight = FontWeight.Light,
color = onSurface,
)
Text(
text = "BITCOIN DEVELOPMENT KIT",
fontFamily = inter,
fontSize = 11.sp,
color = subtle,
letterSpacing = 1.5.sp,
)

Spacer(Modifier.height(48.dp))

Expand All @@ -110,7 +97,7 @@ fun OnboardingScreen(onFinishOnboarding: () -> Unit) {
Text(
text = messages[page],
fontFamily = inter,
fontSize = 15.sp,
fontSize = 16.sp,
lineHeight = 24.sp,
color = onSurface.copy(alpha = 0.85f),
textAlign = TextAlign.Center,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
Expand All @@ -60,8 +59,7 @@ import com.composables.icons.lucide.Lucide
import com.composables.icons.lucide.Monitor
import com.composables.icons.lucide.Settings
import com.composables.icons.lucide.Shield
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.flow.Flow
import org.bitcoindevkit.devkitwallet.domain.CurrencyUnit
import org.bitcoindevkit.devkitwallet.domain.utils.formatInBtc
import org.bitcoindevkit.devkitwallet.presentation.navigation.BlockchainClientScreen
Expand All @@ -83,18 +81,24 @@ private const val TAG = "WalletHomeScreen"
internal fun WalletHomeScreen(
state: WalletScreenState,
onAction: (WalletScreenAction) -> Unit,
snackbarMessages: Flow<String>,
navController: NavHostController,
) {
val snackbarHostState = remember { SnackbarHostState() }
val networkAvailable: Boolean = isOnline(LocalContext.current)
val interactionSource = remember { MutableInteractionSource() }
val scope: CoroutineScope = rememberCoroutineScope()
val colorScheme = MaterialTheme.colorScheme

LaunchedEffect(Unit) {
onAction(WalletScreenAction.UpdateBalance)
}

LaunchedEffect(Unit) {
snackbarMessages.collect { message ->
snackbarHostState.showSnackbar(message)
}
}

Scaffold(
topBar = { WalletAppBar(onSettingsClick = { navController.navigate(SettingsScreen) }) },
snackbarHost = {
Expand All @@ -103,18 +107,6 @@ internal fun WalletHomeScreen(
}
},
) { paddingValues ->

// If a new snackbar has be triggered, show it
state.snackbarMessage?.let { message ->
Log.i("WalletHomeScreen", "Showing snackbar: $message")
LaunchedEffect(message) {
scope.launch {
snackbarHostState.showSnackbar(message)
onAction(WalletScreenAction.ClearSnackbar)
}
}
}

Column(
modifier =
Modifier.fillMaxSize()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch
import org.bitcoindevkit.devkitwallet.data.Kyoto
import org.bitcoindevkit.devkitwallet.domain.CurrencyUnit
Expand All @@ -31,24 +35,18 @@ internal class WalletViewModel(private val wallet: Wallet) : ViewModel() {
private val kyotoCoroutineScope: CoroutineScope = CoroutineScope(Dispatchers.IO)
private var kyoto: Kyoto? = null

private val snackbarChannel = Channel<String>(capacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
val snackbarMessages: Flow<String> = snackbarChannel.receiveAsFlow()

fun onAction(action: WalletScreenAction) {
when (action) {
WalletScreenAction.SwitchUnit -> switchUnit()
WalletScreenAction.UpdateBalance -> updateBalance()
WalletScreenAction.ActivateCbfNode -> activateKyoto()
WalletScreenAction.StopKyotoNode -> stopKyotoNode()
WalletScreenAction.ClearSnackbar -> clearSnackbar()
}
}

private fun showSnackbar(message: String) {
state = state.copy(snackbarMessage = message)
}

private fun clearSnackbar() {
state = state.copy(snackbarMessage = null)
}

private fun switchUnit() {
state =
when (state.unit) {
Expand Down Expand Up @@ -78,11 +76,19 @@ internal class WalletViewModel(private val wallet: Wallet) : ViewModel() {
this.kyoto = Kyoto.create(wallet.wallet, dataDir, wallet.network)
val updatesFlow = kyoto!!.start()
kyotoCoroutineScope.launch {
var previousHeight: UInt = wallet.bestBlock()

updatesFlow.collect {
Log.i(TAG, "Collecting a flow update")
wallet.applyUpdate(it)
updateBalance()
updateBestBlock()

val newHeight = state.bestBlockHeight
if (newHeight > previousHeight) {
snackbarChannel.send("New block: $newHeight")
}
previousHeight = newHeight
}
}
kyoto!!.logToLogcat()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ data class WalletScreenState(
val balance: ULong = 0u,
val unit: CurrencyUnit = CurrencyUnit.Bitcoin,
val bestBlockHeight: UInt = 0u,
val snackbarMessage: String? = null,
val kyotoNodeStatus: CbfNodeStatus = CbfNodeStatus.Stopped,
)

Expand All @@ -23,8 +22,6 @@ sealed interface WalletScreenAction {
data object ActivateCbfNode : WalletScreenAction

data object StopKyotoNode : WalletScreenAction

data object ClearSnackbar : WalletScreenAction
}

enum class CbfNodeStatus {
Expand Down
Loading