Skip to content
Draft
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
13 changes: 11 additions & 2 deletions app/src/main/java/to/bitkit/ui/ContentView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import to.bitkit.env.Env
import to.bitkit.models.NodeLifecycleState
import to.bitkit.models.Toast
import to.bitkit.models.WidgetType
import to.bitkit.repositories.ConnectivityState
import to.bitkit.ui.Routes.ExternalConnection
import to.bitkit.ui.components.AuthCheckScreen
import to.bitkit.ui.components.DrawerMenu
Expand Down Expand Up @@ -380,12 +381,14 @@ fun ContentView(

is Sheet.Receive -> {
val walletState by walletViewModel.walletState.collectAsStateWithLifecycle()
val connectivityState by appViewModel.isOnline.collectAsStateWithLifecycle()
ReceiveSheet(
walletState = walletState,
isOffline = connectivityState != ConnectivityState.CONNECTED,
navigateToExternalConnection = {
navController.navigateTo(ExternalConnection())
appViewModel.hideSheet()
}
},
)
}

Expand Down Expand Up @@ -574,7 +577,9 @@ private fun RootNavHost(
)
}
composableWithDefaultTransitions<Routes.SavingsConfirm> {
val connectivityState by appViewModel.isOnline.collectAsStateWithLifecycle()
SavingsConfirmScreen(
isOffline = connectivityState != ConnectivityState.CONNECTED,
onConfirm = { navController.navigateTo(Routes.SavingsProgress) },
onAdvancedClick = { navController.navigateTo(Routes.SavingsAdvanced) },
onBackClick = { navController.popBackStack() },
Expand Down Expand Up @@ -605,23 +610,27 @@ private fun RootNavHost(
)
}
composableWithDefaultTransitions<Routes.SpendingAmount> {
val connectivityState by appViewModel.isOnline.collectAsStateWithLifecycle()
SpendingAmountScreen(
viewModel = transferViewModel,
isOffline = connectivityState != ConnectivityState.CONNECTED,
onBackClick = { navController.popBackStack() },
onOrderCreated = { navController.navigateTo(Routes.SpendingConfirm) },
toastException = { appViewModel.toast(it) },
toast = { title, description ->
appViewModel.toast(
type = Toast.ToastType.ERROR,
title = title,
description = description
description = description,
)
},
)
}
composableWithDefaultTransitions<Routes.SpendingConfirm> {
val connectivityState by appViewModel.isOnline.collectAsStateWithLifecycle()
SpendingConfirmScreen(
viewModel = transferViewModel,
isOffline = connectivityState != ConnectivityState.CONNECTED,
onBackClick = { navController.popBackStack() },
onCloseClick = { navController.navigateToHome() },
onLearnMoreClick = { navController.navigateTo(Routes.TransferLiquidity) },
Expand Down
139 changes: 139 additions & 0 deletions app/src/main/java/to/bitkit/ui/components/ConnectionIssuesView.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package to.bitkit.ui.components

import androidx.compose.foundation.Canvas
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.PathEffect
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import to.bitkit.R
import to.bitkit.ui.scaffold.SheetTopBar
import to.bitkit.ui.shared.util.gradientBackground
import to.bitkit.ui.theme.AppThemeSurface
import to.bitkit.ui.theme.Colors
import to.bitkit.ui.utils.withAccent

@Composable
fun ConnectionIssuesView(
titleText: String,
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier
.fillMaxSize()
.gradientBackground()
.navigationBarsPadding()
.padding(horizontal = 16.dp)
.testTag("ConnectionIssueView"),
) {
SheetTopBar(titleText = titleText)

Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.fillMaxWidth()
.weight(1f),
) {
DashedRingsLayer(outerOnly = true)

Image(
painter = painterResource(R.drawable.phone),
contentDescription = null,
contentScale = ContentScale.Fit,
modifier = Modifier
.size(311.dp)
.align(Alignment.Center),
)

DashedRingsLayer(outerOnly = false)
}

Display(
text = stringResource(R.string.other__connection_issues_title)
.withAccent(accentColor = Colors.Yellow),
modifier = Modifier.fillMaxWidth(),
)

VerticalSpacer(8.dp)

BodyM(
text = stringResource(R.string.other__connection_issues_explain),
color = Colors.White64,
modifier = Modifier.fillMaxWidth(),
)

VerticalSpacer(24.dp)

Box(
contentAlignment = Alignment.Center,
modifier = Modifier.fillMaxWidth(),
) {
GradientCircularProgressIndicator(
strokeWidth = 1.dp,
modifier = Modifier.size(32.dp),
)
}

VerticalSpacer(16.dp)
}
}

private val outerRingRadii = listOf(200f)
private val innerRingRadii = listOf(150f, 100f, 50f)

@Composable
private fun DashedRingsLayer(outerOnly: Boolean, modifier: Modifier = Modifier) {
val radii = if (outerOnly) outerRingRadii else innerRingRadii
Canvas(modifier = modifier.fillMaxSize()) {
val center = Offset(size.width * 0.25f, size.height * 0.40f)
radii.forEach { radiusDp -> drawDashedGradientRing(radiusDp, center) }
}
}

private fun DrawScope.drawDashedGradientRing(radiusDp: Float, center: Offset) {
val radius = radiusDp.dp.toPx()
val brush = Brush.linearGradient(
colors = listOf(Color.Black, Colors.Yellow),
start = Offset(center.x - radius, center.y - radius),
end = Offset(center.x + radius, center.y + radius),
)
drawCircle(
brush = brush,
radius = radius,
center = center,
style = Stroke(
width = 1.dp.toPx(),
pathEffect = PathEffect.dashPathEffect(
floatArrayOf(8.dp.toPx(), 6.dp.toPx()),
),
),
)
}

@Preview(showSystemUi = true)
@Composable
private fun Preview() {
AppThemeSurface {
BottomSheetPreview {
ConnectionIssuesView(titleText = "Send Bitcoin")
}
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
package to.bitkit.ui.screens.transfer

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
Expand All @@ -30,6 +35,7 @@ import to.bitkit.ext.amountOnClose
import to.bitkit.ext.filterOpen
import to.bitkit.ui.components.ButtonSize
import to.bitkit.ui.components.Caption13Up
import to.bitkit.ui.components.ConnectionIssuesView
import to.bitkit.ui.components.Display
import to.bitkit.ui.components.MoneyDisplay
import to.bitkit.ui.components.PrimaryButton
Expand All @@ -46,6 +52,7 @@ import to.bitkit.ui.walletViewModel

@Composable
fun SavingsConfirmScreen(
isOffline: Boolean,
onConfirm: () -> Unit,
onAdvancedClick: () -> Unit,
onBackClick: () -> Unit,
Expand All @@ -70,19 +77,31 @@ fun SavingsConfirmScreen(

val amount = channels.sumOf { it.amountOnClose }

SavingsConfirmContent(
amount = amount,
hasMultiple = hasMultiple,
hasSelected = hasSelected,
onBackClick = onBackClick,
onAmountClick = { currency.switchUnit() },
onAdvancedClick = onAdvancedClick,
onSelectAllClick = { transfer.setSelectedChannelIds(emptySet()) },
onConfirm = {
transfer.onTransferToSavingsConfirm(channels)
onConfirm()
},
)
Box {
SavingsConfirmContent(
amount = amount,
hasMultiple = hasMultiple,
hasSelected = hasSelected,
onBackClick = onBackClick,
onAmountClick = { currency.switchUnit() },
onAdvancedClick = onAdvancedClick,
onSelectAllClick = { transfer.setSelectedChannelIds(emptySet()) },
onConfirm = {
transfer.onTransferToSavingsConfirm(channels)
onConfirm()
},
)
AnimatedVisibility(
visible = isOffline,
enter = fadeIn(),
exit = fadeOut(),
) {
ConnectionIssuesView(
titleText = stringResource(R.string.lightning__transfer__nav_title),
modifier = Modifier.statusBarsPadding(),
)
}
}
}

@Suppress("MagicNumber")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
package to.bitkit.ui.screens.transfer

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
Expand All @@ -23,6 +28,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import to.bitkit.R
import to.bitkit.repositories.CurrencyState
import to.bitkit.ui.LocalCurrencies
import to.bitkit.ui.components.ConnectionIssuesView
import to.bitkit.ui.components.Display
import to.bitkit.ui.components.FillHeight
import to.bitkit.ui.components.FillWidth
Expand Down Expand Up @@ -52,6 +58,7 @@ import kotlin.math.min
@Composable
fun SpendingAmountScreen(
viewModel: TransferViewModel,
isOffline: Boolean,
onBackClick: () -> Unit = {},
onOrderCreated: () -> Unit = {},
toastException: (Throwable) -> Unit,
Expand All @@ -64,7 +71,7 @@ fun SpendingAmountScreen(
val amountUiState by amountInputViewModel.uiState.collectAsStateWithLifecycle()
val context = LocalContext.current

LaunchedEffect(Unit) {
LaunchedEffect(isOffline) {
viewModel.updateLimits()
}

Expand All @@ -78,33 +85,45 @@ fun SpendingAmountScreen(
}
}

Content(
isNodeRunning = isNodeRunning,
uiState = uiState,
amountInputViewModel = amountInputViewModel,
currencies = currencies,
onBackClick = onBackClick,
onClickQuarter = {
val quarter = uiState.balanceAfterFeeQuarter()
val max = uiState.maxAllowedToSend
if (quarter > max) {
toast(
context.getString(R.string.lightning__spending_amount__error_max__title),
context.getString(R.string.lightning__spending_amount__error_max__description)
.replace("{amount}", "$max"),
)
}
val cappedQuarter = min(quarter, max)
viewModel.updateLimits(cappedQuarter)
amountInputViewModel.setSats(cappedQuarter, currencies)
},
onClickMaxAmount = {
val newAmountSats = uiState.maxAllowedToSend
viewModel.updateLimits(newAmountSats)
amountInputViewModel.setSats(newAmountSats, currencies)
},
onConfirmAmount = { viewModel.onConfirmAmount(amountUiState.sats) },
)
Box {
Content(
isNodeRunning = isNodeRunning,
uiState = uiState,
amountInputViewModel = amountInputViewModel,
currencies = currencies,
onBackClick = onBackClick,
onClickQuarter = {
val quarter = uiState.balanceAfterFeeQuarter()
val max = uiState.maxAllowedToSend
if (quarter > max) {
toast(
context.getString(R.string.lightning__spending_amount__error_max__title),
context.getString(R.string.lightning__spending_amount__error_max__description)
.replace("{amount}", "$max"),
)
}
val cappedQuarter = min(quarter, max)
viewModel.updateLimits(cappedQuarter)
amountInputViewModel.setSats(cappedQuarter, currencies)
},
onClickMaxAmount = {
val newAmountSats = uiState.maxAllowedToSend
viewModel.updateLimits(newAmountSats)
amountInputViewModel.setSats(newAmountSats, currencies)
},
onConfirmAmount = { viewModel.onConfirmAmount(amountUiState.sats) },
)
AnimatedVisibility(
visible = isOffline,
enter = fadeIn(),
exit = fadeOut(),
) {
ConnectionIssuesView(
titleText = stringResource(R.string.lightning__transfer__nav_title),
modifier = Modifier.statusBarsPadding(),
)
}
}
}

@Suppress("ViewModelForwarding")
Expand Down
Loading
Loading