From a26b38bbd6f46c6d687dcad8e60673f65bba93c6 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Mon, 14 Jul 2025 16:21:18 +0200 Subject: [PATCH 01/18] chore: cleanup unused deps from BlocktankNotificationsService --- .../java/to/bitkit/services/BlocktankNotificationsService.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/src/main/java/to/bitkit/services/BlocktankNotificationsService.kt b/app/src/main/java/to/bitkit/services/BlocktankNotificationsService.kt index 4a4b57b34..f4c80ec3b 100644 --- a/app/src/main/java/to/bitkit/services/BlocktankNotificationsService.kt +++ b/app/src/main/java/to/bitkit/services/BlocktankNotificationsService.kt @@ -1,10 +1,8 @@ package to.bitkit.services import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.flow.first import kotlinx.coroutines.withContext import to.bitkit.async.ServiceQueue -import to.bitkit.data.SettingsStore import to.bitkit.data.keychain.Keychain import to.bitkit.data.keychain.Keychain.Key import to.bitkit.di.BgDispatcher @@ -24,7 +22,6 @@ class BlocktankNotificationsService @Inject constructor( private val lightningService: LightningService, private val keychain: Keychain, private val crypto: Crypto, - private val settingsStore: SettingsStore, ) { suspend fun registerDevice(deviceToken: String) = withContext(bgDispatcher) { From cee11927f5def460695350f8c4fda84e34168863 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Mon, 14 Jul 2025 17:18:29 +0200 Subject: [PATCH 02/18] feat: custom http logging disabled --- app/src/main/java/to/bitkit/di/HttpModule.kt | 39 ++++++++++++++------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/to/bitkit/di/HttpModule.kt b/app/src/main/java/to/bitkit/di/HttpModule.kt index e9cb290b4..01bf6c30b 100644 --- a/app/src/main/java/to/bitkit/di/HttpModule.kt +++ b/app/src/main/java/to/bitkit/di/HttpModule.kt @@ -6,18 +6,20 @@ import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import io.ktor.client.HttpClient import io.ktor.client.plugins.HttpTimeout +import io.ktor.client.plugins.HttpTimeoutConfig import io.ktor.client.plugins.contentnegotiation.ContentNegotiation import io.ktor.client.plugins.defaultRequest -import io.ktor.client.plugins.logging.ANDROID import io.ktor.client.plugins.logging.LogLevel -import io.ktor.client.plugins.logging.Logger import io.ktor.client.plugins.logging.Logging +import io.ktor.client.plugins.logging.LoggingConfig import io.ktor.http.ContentType import io.ktor.http.contentType import io.ktor.serialization.kotlinx.json.json import kotlinx.serialization.json.Json +import to.bitkit.utils.Logger import javax.inject.Qualifier import javax.inject.Singleton +import io.ktor.client.plugins.logging.Logger as KtorLogger @Qualifier @Retention(AnnotationRetention.BINARY) @@ -26,18 +28,16 @@ annotation class ProtoClient @Module @InstallIn(SingletonComponent::class) object HttpModule { + @Provides @Singleton fun provideHttpClient(json: Json): HttpClient { return HttpClient { install(HttpTimeout) { - requestTimeoutMillis = 60_000 - connectTimeoutMillis = 30_000 - socketTimeoutMillis = 30_000 + this@install.defaultTimeoutConfig() } install(Logging) { - logger = Logger.ANDROID - level = LogLevel.INFO + this@install.defaultLoggingConfig() } install(ContentNegotiation) { json(json = json) @@ -54,14 +54,29 @@ object HttpModule { fun provideProtoHttpClient(): HttpClient { return HttpClient { install(HttpTimeout) { - requestTimeoutMillis = 60_000 - connectTimeoutMillis = 30_000 - socketTimeoutMillis = 30_000 + this@install.defaultTimeoutConfig() } install(Logging) { - logger = Logger.ANDROID - level = LogLevel.INFO + this@install.defaultLoggingConfig() } } } + + private fun HttpTimeoutConfig.defaultTimeoutConfig() { + requestTimeoutMillis = 60_000 + connectTimeoutMillis = 30_000 + socketTimeoutMillis = 30_000 + } + + private fun LoggingConfig.defaultLoggingConfig() { + logger = KtorLogger.APP + level = LogLevel.NONE + } } + +private val KtorLogger.Companion.APP + get() = object : KtorLogger { + override fun log(message: String) { + Logger.debug(message) + } + } From 7c230788dcf7b5c39430a7c2456ea9517bd1a4a7 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Mon, 14 Jul 2025 18:05:44 +0200 Subject: [PATCH 03/18] chore: upgrade agp --- gradle/libs.versions.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 27b8a26e2..f6b1e5c66 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] accompanistPermissions = "0.36.0" activityCompose = "1.10.1" -agp = "8.11.0" +agp = "8.11.1" appcompat = "1.7.0" barcodeScanning = "17.3.0" biometric = "1.4.0-alpha02" @@ -81,7 +81,7 @@ ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" } ktor-client-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktor" } ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" } ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" } -#ldk-node-android = { module = "org.lightningdevkit:ldk-node-android", version = "0.5.0" } # upstream +#ldk-node-android = { module = "org.lightningdevkit:ldk-node-android", version = "0.6.1" } # upstream ldk-node-android = { module = "com.github.synonymdev:ldk-node", version = "v0.6.1-rc.2" } # fork lifecycle-process = { group = "androidx.lifecycle", name = "lifecycle-process", version.ref = "lifecycle" } lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "lifecycle" } From 4304acc1b88163574ac40d3589b7c37c637c0a3c Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Mon, 14 Jul 2025 18:15:05 +0200 Subject: [PATCH 04/18] refactor: remove redundant app shapes --- .../java/to/bitkit/ui/components/OutlinedColorButton.kt | 2 +- .../main/java/to/bitkit/ui/components/RectangleButton.kt | 2 +- .../screens/transfer/external/ExternalConnectionScreen.kt | 6 +++--- app/src/main/java/to/bitkit/ui/theme/Shape.kt | 2 -- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/to/bitkit/ui/components/OutlinedColorButton.kt b/app/src/main/java/to/bitkit/ui/components/OutlinedColorButton.kt index 33715e7d6..166ee3d30 100644 --- a/app/src/main/java/to/bitkit/ui/components/OutlinedColorButton.kt +++ b/app/src/main/java/to/bitkit/ui/components/OutlinedColorButton.kt @@ -35,7 +35,7 @@ fun OutlinedColorButton( disabledContentColor = color, ), enabled = enabled, - shape = AppShapes.smallButton, + shape = AppShapes.small, contentPadding = PaddingValues(8.dp, 4.dp), border = BorderStroke(1.dp, color), ) { diff --git a/app/src/main/java/to/bitkit/ui/components/RectangleButton.kt b/app/src/main/java/to/bitkit/ui/components/RectangleButton.kt index 2d7d04cbe..66786b6e4 100644 --- a/app/src/main/java/to/bitkit/ui/components/RectangleButton.kt +++ b/app/src/main/java/to/bitkit/ui/components/RectangleButton.kt @@ -40,7 +40,7 @@ fun RectangleButton( containerColor = Colors.White10, ), enabled = enabled, - shape = AppShapes.smallButton, + shape = AppShapes.small, contentPadding = PaddingValues(24.dp), modifier = modifier .alpha(if (enabled) 1f else 0.5f) diff --git a/app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt b/app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt index 36546083e..83de4e0f6 100644 --- a/app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt @@ -126,7 +126,7 @@ private fun ExternalConnectionContent( onValueChange = { nodeId = it }, singleLine = false, colors = AppTextFieldDefaults.semiTransparent, - shape = AppShapes.smallInput, + shape = AppShapes.small, keyboardOptions = KeyboardOptions( autoCorrectEnabled = false, imeAction = ImeAction.Done, @@ -144,7 +144,7 @@ private fun ExternalConnectionContent( onValueChange = { host = it }, singleLine = true, colors = AppTextFieldDefaults.semiTransparent, - shape = AppShapes.smallInput, + shape = AppShapes.small, keyboardOptions = KeyboardOptions( autoCorrectEnabled = false, imeAction = ImeAction.Done, @@ -162,7 +162,7 @@ private fun ExternalConnectionContent( onValueChange = { port = it }, singleLine = true, colors = AppTextFieldDefaults.semiTransparent, - shape = AppShapes.smallInput, + shape = AppShapes.small, keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number, autoCorrectEnabled = false, diff --git a/app/src/main/java/to/bitkit/ui/theme/Shape.kt b/app/src/main/java/to/bitkit/ui/theme/Shape.kt index ccf0d656c..cad12ba8c 100644 --- a/app/src/main/java/to/bitkit/ui/theme/Shape.kt +++ b/app/src/main/java/to/bitkit/ui/theme/Shape.kt @@ -15,6 +15,4 @@ val Shapes = Shapes( object AppShapes { val sheet = RoundedCornerShape(topStart = 32.dp, topEnd = 32.dp) val small = RoundedCornerShape(8.dp) - val smallButton = small - val smallInput = small } From bfc3e021934eea145fb7208d8445dc0766167360 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Mon, 14 Jul 2025 18:40:18 +0200 Subject: [PATCH 05/18] refactor: TextInput style and preview --- .../main/java/to/bitkit/ui/components/Text.kt | 13 ++--- .../java/to/bitkit/ui/components/TextInput.kt | 55 ++++++++++++++----- app/src/main/java/to/bitkit/ui/theme/Type.kt | 14 +++++ 3 files changed, 60 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/to/bitkit/ui/components/Text.kt b/app/src/main/java/to/bitkit/ui/components/Text.kt index 52056ac73..0ac1275ed 100644 --- a/app/src/main/java/to/bitkit/ui/components/Text.kt +++ b/app/src/main/java/to/bitkit/ui/components/Text.kt @@ -13,6 +13,7 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.toUpperCase import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.sp +import to.bitkit.ui.theme.AppTextStyles import to.bitkit.ui.theme.Colors import to.bitkit.ui.theme.InterFontFamily @@ -201,14 +202,10 @@ fun BodyMSB( ) { Text( text = text, - style = TextStyle( - fontWeight = FontWeight.SemiBold, - fontSize = 17.sp, - lineHeight = 22.sp, - letterSpacing = 0.4.sp, - fontFamily = InterFontFamily, - color = color, - textAlign = TextAlign.Start, + style = AppTextStyles.BodyMSB.merge( + TextStyle( + color = color, + ) ), maxLines = maxLines, overflow = overflow, diff --git a/app/src/main/java/to/bitkit/ui/components/TextInput.kt b/app/src/main/java/to/bitkit/ui/components/TextInput.kt index d3a0fadef..ae4f5c929 100644 --- a/app/src/main/java/to/bitkit/ui/components/TextInput.kt +++ b/app/src/main/java/to/bitkit/ui/components/TextInput.kt @@ -1,19 +1,21 @@ package to.bitkit.ui.components +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.TextField import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.VisualTransformation -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.sp +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import to.bitkit.ui.scaffold.ScreenColumn import to.bitkit.ui.theme.AppShapes import to.bitkit.ui.theme.AppTextFieldDefaults +import to.bitkit.ui.theme.AppTextStyles +import to.bitkit.ui.theme.AppThemeSurface import to.bitkit.ui.theme.Colors -import to.bitkit.ui.theme.InterFontFamily @Composable fun TextInput( @@ -43,14 +45,7 @@ fun TextInput( } else null }, isError = isError, - textStyle = LocalTextStyle.current.copy( - fontWeight = FontWeight.SemiBold, - fontSize = 17.sp, - lineHeight = 22.sp, - letterSpacing = 0.4.sp, - fontFamily = InterFontFamily, - textAlign = TextAlign.Start, - ), + textStyle = AppTextStyles.BodyMSB, value = value, onValueChange = onValueChange, maxLines = maxLines, @@ -65,6 +60,38 @@ fun TextInput( suffix = suffix, supportingText = supportingText, visualTransformation = visualTransformation, - modifier = modifier + modifier = modifier, ) } + +@Preview(showSystemUi = true) +@Composable +private fun Preview() { + AppThemeSurface { + ScreenColumn( + modifier = Modifier.padding(vertical = 24.dp, horizontal = 16.dp) + ) { + TextInput( + value = "Input text value", + onValueChange = {}, + modifier = Modifier.fillMaxWidth(), + ) + + VerticalSpacer(12.dp) + TextInput( + value = "", + onValueChange = {}, + placeholder = "Placeholder text", + modifier = Modifier.fillMaxWidth(), + ) + + VerticalSpacer(12.dp) + TextInput( + value = "Error text", + onValueChange = {}, + isError = true, + modifier = Modifier.fillMaxWidth(), + ) + } + } +} diff --git a/app/src/main/java/to/bitkit/ui/theme/Type.kt b/app/src/main/java/to/bitkit/ui/theme/Type.kt index 3ef2e8ae9..5f04ec49e 100644 --- a/app/src/main/java/to/bitkit/ui/theme/Type.kt +++ b/app/src/main/java/to/bitkit/ui/theme/Type.kt @@ -1,10 +1,12 @@ package to.bitkit.ui.theme import androidx.compose.material3.Typography +import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.Font import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.sp import to.bitkit.R @@ -47,3 +49,15 @@ val Typography = Typography( letterSpacing = 0.4.sp, ), ) + +object AppTextStyles { + val BodyMSB = TextStyle( + fontWeight = FontWeight.SemiBold, + fontSize = 17.sp, + lineHeight = 22.sp, + letterSpacing = 0.4.sp, + fontFamily = InterFontFamily, + textAlign = TextAlign.Start, + color = Color.Unspecified, + ) +} From 886d057defbbf773e08d2a6de1c50286fb484984 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Mon, 14 Jul 2025 18:55:21 +0200 Subject: [PATCH 06/18] fix: TextInput font size --- app/src/main/java/to/bitkit/ui/components/Text.kt | 12 ++---------- .../main/java/to/bitkit/ui/components/TextInput.kt | 6 +++--- app/src/main/java/to/bitkit/ui/theme/Type.kt | 9 +++++++++ 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/to/bitkit/ui/components/Text.kt b/app/src/main/java/to/bitkit/ui/components/Text.kt index 0ac1275ed..f1e7bfc54 100644 --- a/app/src/main/java/to/bitkit/ui/components/Text.kt +++ b/app/src/main/java/to/bitkit/ui/components/Text.kt @@ -203,9 +203,7 @@ fun BodyMSB( Text( text = text, style = AppTextStyles.BodyMSB.merge( - TextStyle( - color = color, - ) + color = color, ), maxLines = maxLines, overflow = overflow, @@ -302,14 +300,8 @@ fun BodySSB( ) { Text( text = text, - style = TextStyle( - fontWeight = FontWeight.SemiBold, - fontSize = 15.sp, - lineHeight = 20.sp, - letterSpacing = 0.4.sp, - fontFamily = InterFontFamily, + style = AppTextStyles.BodySSB.merge( color = color, - textAlign = TextAlign.Start, ), modifier = modifier, maxLines = maxLines, diff --git a/app/src/main/java/to/bitkit/ui/components/TextInput.kt b/app/src/main/java/to/bitkit/ui/components/TextInput.kt index ae4f5c929..c4ff4e96c 100644 --- a/app/src/main/java/to/bitkit/ui/components/TextInput.kt +++ b/app/src/main/java/to/bitkit/ui/components/TextInput.kt @@ -38,14 +38,14 @@ fun TextInput( TextField( placeholder = { if (!placeholder.isNullOrEmpty()) { - BodyMSB( + BodySSB( placeholder, - color = Colors.White64 + color = Colors.White64, ) } else null }, isError = isError, - textStyle = AppTextStyles.BodyMSB, + textStyle = AppTextStyles.BodySSB, value = value, onValueChange = onValueChange, maxLines = maxLines, diff --git a/app/src/main/java/to/bitkit/ui/theme/Type.kt b/app/src/main/java/to/bitkit/ui/theme/Type.kt index 5f04ec49e..fd0b4223e 100644 --- a/app/src/main/java/to/bitkit/ui/theme/Type.kt +++ b/app/src/main/java/to/bitkit/ui/theme/Type.kt @@ -60,4 +60,13 @@ object AppTextStyles { textAlign = TextAlign.Start, color = Color.Unspecified, ) + val BodySSB = TextStyle( + fontWeight = FontWeight.SemiBold, + fontSize = 15.sp, + lineHeight = 20.sp, + letterSpacing = 0.4.sp, + fontFamily = InterFontFamily, + color = Color.Unspecified, + textAlign = TextAlign.Start, + ) } From d7cf06a49e0f5f05051e9ec932c91223d42e948e Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Mon, 14 Jul 2025 18:56:51 +0200 Subject: [PATCH 07/18] refactor: use TextInput component --- .../external/ExternalConnectionScreen.kt | 23 ++++++------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt b/app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt index 83de4e0f6..6c5eb4d5e 100644 --- a/app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt @@ -14,8 +14,6 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Icon -import androidx.compose.material3.Text -import androidx.compose.material3.TextField import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState @@ -41,11 +39,10 @@ import to.bitkit.ui.components.Caption13Up import to.bitkit.ui.components.Display import to.bitkit.ui.components.PrimaryButton import to.bitkit.ui.components.SecondaryButton +import to.bitkit.ui.components.TextInput import to.bitkit.ui.scaffold.AppTopBar import to.bitkit.ui.scaffold.CloseNavIcon import to.bitkit.ui.scaffold.ScreenColumn -import to.bitkit.ui.theme.AppShapes -import to.bitkit.ui.theme.AppTextFieldDefaults import to.bitkit.ui.theme.AppThemeSurface import to.bitkit.ui.theme.Colors import to.bitkit.ui.utils.withAccent @@ -120,13 +117,11 @@ private fun ExternalConnectionContent( Spacer(modifier = Modifier.height(16.dp)) Caption13Up(text = stringResource(R.string.lightning__external_manual__node_id), color = Colors.White64) Spacer(modifier = Modifier.height(8.dp)) - TextField( - placeholder = { Text("00000000000000000000000000000000000000000000000000000000000000") }, + TextInput( + placeholder = "00000000000000000000000000000000000000000000000000000000000000", value = nodeId, onValueChange = { nodeId = it }, singleLine = false, - colors = AppTextFieldDefaults.semiTransparent, - shape = AppShapes.small, keyboardOptions = KeyboardOptions( autoCorrectEnabled = false, imeAction = ImeAction.Done, @@ -138,13 +133,11 @@ private fun ExternalConnectionContent( Spacer(modifier = Modifier.height(16.dp)) Caption13Up(text = stringResource(R.string.lightning__external_manual__host), color = Colors.White64) Spacer(modifier = Modifier.height(8.dp)) - TextField( - placeholder = { Text("00.00.00.00") }, + TextInput( + placeholder = "00.00.00.00", value = host, onValueChange = { host = it }, singleLine = true, - colors = AppTextFieldDefaults.semiTransparent, - shape = AppShapes.small, keyboardOptions = KeyboardOptions( autoCorrectEnabled = false, imeAction = ImeAction.Done, @@ -156,13 +149,11 @@ private fun ExternalConnectionContent( Spacer(modifier = Modifier.height(16.dp)) Caption13Up(text = stringResource(R.string.lightning__external_manual__port), color = Colors.White64) Spacer(modifier = Modifier.height(8.dp)) - TextField( - placeholder = { Text("9735") }, + TextInput( + placeholder ="9735", value = port, onValueChange = { port = it }, singleLine = true, - colors = AppTextFieldDefaults.semiTransparent, - shape = AppShapes.small, keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number, autoCorrectEnabled = false, From 9cdf036826ebefb6b7256a5442faa8d510e93072 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Mon, 14 Jul 2025 19:00:47 +0200 Subject: [PATCH 08/18] fix: use non-deprecated clipboard manager --- app/src/main/java/to/bitkit/ext/Context.kt | 3 ++- .../screens/transfer/external/ExternalConnectionScreen.kt | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/to/bitkit/ext/Context.kt b/app/src/main/java/to/bitkit/ext/Context.kt index f594c5429..dfee9aab6 100644 --- a/app/src/main/java/to/bitkit/ext/Context.kt +++ b/app/src/main/java/to/bitkit/ext/Context.kt @@ -12,6 +12,7 @@ import android.content.ContextWrapper import android.content.pm.PackageManager.PERMISSION_GRANTED import androidx.core.app.NotificationManagerCompat import androidx.core.content.ContextCompat +import to.bitkit.R import to.bitkit.utils.Logger import java.io.File import java.io.FileOutputStream @@ -63,7 +64,7 @@ fun Context.findActivity(): Activity? = } // Clipboard -fun Context.setClipboardText(text: String, label: String = "") { +fun Context.setClipboardText(text: String, label: String = getString(R.string.app_name)) { this.clipboardManager.setPrimaryClip( ClipData.newPlainText(label, text) ) diff --git a/app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt b/app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt index 6c5eb4d5e..957bbd9cc 100644 --- a/app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt @@ -23,6 +23,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalClipboardManager +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction @@ -31,6 +32,7 @@ import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import to.bitkit.R +import to.bitkit.ext.getClipboardText import to.bitkit.models.LnPeer import to.bitkit.ui.appViewModel import to.bitkit.ui.components.BodyM @@ -58,7 +60,7 @@ fun ExternalConnectionScreen( onCloseClick: () -> Unit, ) { val app = appViewModel ?: return - val clipboard = LocalClipboardManager.current + val context = LocalContext.current val uiState by viewModel.uiState.collectAsState() LaunchedEffect(viewModel, onNodeConnected) { @@ -74,7 +76,7 @@ fun ExternalConnectionScreen( uiState = uiState, onContinueClick = { peer -> viewModel.onConnectionContinue(peer) }, onScanClick = { app.toast(Exception("Coming soon")) }, - onPasteClick = { viewModel.onConnectionPaste(clipboardText = clipboard.getText()?.text.orEmpty()) }, + onPasteClick = { viewModel.onConnectionPaste(clipboardText = context.getClipboardText().orEmpty()) }, onBackClick = onBackClick, onCloseClick = onCloseClick, ) From b40d1c0f73e9560ba0e3f5a482fb87b4bf493f83 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Mon, 14 Jul 2025 19:10:12 +0200 Subject: [PATCH 09/18] feat: nav to scanner --- app/src/main/java/to/bitkit/ui/ContentView.kt | 1 + .../screens/transfer/external/ExternalConnectionScreen.kt | 8 +++----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/to/bitkit/ui/ContentView.kt b/app/src/main/java/to/bitkit/ui/ContentView.kt index ec66ea53b..6ca243006 100644 --- a/app/src/main/java/to/bitkit/ui/ContentView.kt +++ b/app/src/main/java/to/bitkit/ui/ContentView.kt @@ -574,6 +574,7 @@ private fun RootNavHost( ExternalConnectionScreen( viewModel = viewModel, onNodeConnected = { navController.navigate(Routes.ExternalAmount) }, + onScanClick = { navController.navigateToQrScanner() }, onBackClick = { navController.popBackStack() }, onCloseClick = { navController.popBackStack(inclusive = true) }, ) diff --git a/app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt b/app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt index 957bbd9cc..6262c7364 100644 --- a/app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt @@ -22,7 +22,6 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource @@ -34,7 +33,6 @@ import androidx.compose.ui.unit.dp import to.bitkit.R import to.bitkit.ext.getClipboardText import to.bitkit.models.LnPeer -import to.bitkit.ui.appViewModel import to.bitkit.ui.components.BodyM import to.bitkit.ui.components.ButtonSize import to.bitkit.ui.components.Caption13Up @@ -56,10 +54,10 @@ import to.bitkit.viewmodels.ExternalNodeViewModel fun ExternalConnectionScreen( viewModel: ExternalNodeViewModel, onNodeConnected: () -> Unit, + onScanClick: () -> Unit, onBackClick: () -> Unit, onCloseClick: () -> Unit, ) { - val app = appViewModel ?: return val context = LocalContext.current val uiState by viewModel.uiState.collectAsState() @@ -75,8 +73,8 @@ fun ExternalConnectionScreen( ExternalConnectionContent( uiState = uiState, onContinueClick = { peer -> viewModel.onConnectionContinue(peer) }, - onScanClick = { app.toast(Exception("Coming soon")) }, onPasteClick = { viewModel.onConnectionPaste(clipboardText = context.getClipboardText().orEmpty()) }, + onScanClick = onScanClick, onBackClick = onBackClick, onCloseClick = onCloseClick, ) @@ -152,7 +150,7 @@ private fun ExternalConnectionContent( Caption13Up(text = stringResource(R.string.lightning__external_manual__port), color = Colors.White64) Spacer(modifier = Modifier.height(8.dp)) TextInput( - placeholder ="9735", + placeholder = "9735", value = port, onValueChange = { port = it }, singleLine = true, From 5e21f259436f45ba6ec530c2e0bb680e88c67472 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Mon, 14 Jul 2025 20:16:28 +0200 Subject: [PATCH 10/18] feat: on scan node uri nav to external connection --- app/src/main/java/to/bitkit/models/LnPeer.kt | 10 +- app/src/main/java/to/bitkit/ui/ContentView.kt | 186 +++++++++--------- .../external/ExternalConnectionScreen.kt | 10 +- .../java/to/bitkit/viewmodels/AppViewModel.kt | 14 +- .../viewmodels/ExternalNodeViewModel.kt | 4 +- 5 files changed, 120 insertions(+), 104 deletions(-) diff --git a/app/src/main/java/to/bitkit/models/LnPeer.kt b/app/src/main/java/to/bitkit/models/LnPeer.kt index e4fe50a5c..1c523a732 100644 --- a/app/src/main/java/to/bitkit/models/LnPeer.kt +++ b/app/src/main/java/to/bitkit/models/LnPeer.kt @@ -26,15 +26,15 @@ data class LnPeer( address = address, ) - fun parseUri(string: String): Result { - val uri = string.split("@") - val nodeId = uri[0] + fun parseUri(uriString: String): Result { + val uriComponents = uriString.split("@") + val nodeId = uriComponents[0] - if (uri.size != 2) { + if (uriComponents.size != 2) { return Result.failure(Exception("Invalid peer uri")) } - val address = uri[1].split(":") + val address = uriComponents[1].split(":") if (address.size < 2) { return Result.failure(Exception("Invalid peer uri")) diff --git a/app/src/main/java/to/bitkit/ui/ContentView.kt b/app/src/main/java/to/bitkit/ui/ContentView.kt index 6ca243006..b184a3978 100644 --- a/app/src/main/java/to/bitkit/ui/ContentView.kt +++ b/app/src/main/java/to/bitkit/ui/ContentView.kt @@ -207,7 +207,7 @@ fun ContentView( LaunchedEffect(appViewModel) { appViewModel.mainScreenEffect.collect { when (it) { - is MainScreenEffect.NavigateActivityDetail -> navController.navigate(Routes.ActivityDetail(it.activityId)) + is MainScreenEffect.Navigate -> navController.navigate(it.route) is MainScreenEffect.ProcessClipboardAutoRead -> { val isOnHome = navController.currentDestination?.hasRoute() == true if (!isOnHome) { @@ -565,13 +565,15 @@ private fun RootNavHost( ) } navigation( - startDestination = Routes.ExternalConnection, + startDestination = Routes.ExternalConnection(), ) { composable { val parentEntry = remember(it) { navController.getBackStackEntry(Routes.ExternalNav) } + val route = it.toRoute() val viewModel = hiltViewModel(parentEntry) ExternalConnectionScreen( + route = route, viewModel = viewModel, onNodeConnected = { navController.navigate(Routes.ExternalAmount) }, onScanClick = { navController.navigateToQrScanner() }, @@ -1365,66 +1367,66 @@ fun NavController.navigateToAboutSettings() = navigate( ) // endregion -object Routes { +sealed interface Routes { @Serializable - data object Home + data object Home : Routes @Serializable - data object Settings + data object Settings : Routes @Serializable - data object NodeInfo + data object NodeInfo : Routes @Serializable - data object GeneralSettings + data object GeneralSettings : Routes @Serializable - data object TransactionSpeedSettings + data object TransactionSpeedSettings : Routes @Serializable - data object WidgetsSettings + data object WidgetsSettings : Routes @Serializable - data object TagsSettings + data object TagsSettings : Routes @Serializable - data object AdvancedSettings + data object AdvancedSettings : Routes @Serializable - data object CoinSelectPreference + data object CoinSelectPreference : Routes @Serializable - data object ElectrumConfig + data object ElectrumConfig : Routes @Serializable - data object RgsServer + data object RgsServer : Routes @Serializable - data object AddressViewer + data object AddressViewer : Routes @Serializable - data object AboutSettings + data object AboutSettings : Routes @Serializable - data object CustomFeeSettings + data object CustomFeeSettings : Routes @Serializable - data object SecuritySettings + data object SecuritySettings : Routes @Serializable - data object DisablePin + data object DisablePin : Routes @Serializable - data object ChangePin + data object ChangePin : Routes @Serializable - data object ChangePinNew + data object ChangePinNew : Routes @Serializable - data class ChangePinConfirm(val newPin: String) + data class ChangePinConfirm(val newPin: String) : Routes @Serializable - data object ChangePinResult + data object ChangePinResult : Routes @Serializable data class AuthCheck( @@ -1432,209 +1434,209 @@ object Routes { val requirePin: Boolean = false, val requireBiometrics: Boolean = false, val onSuccessActionId: String, - ) + ) : Routes @Serializable - data object DefaultUnitSettings + data object DefaultUnitSettings : Routes @Serializable - data object LocalCurrencySettings + data object LocalCurrencySettings : Routes @Serializable - data object BackupSettings + data object BackupSettings : Routes @Serializable - data object ResetAndRestoreSettings + data object ResetAndRestoreSettings : Routes @Serializable - data object ChannelOrdersSettings + data object ChannelOrdersSettings : Routes @Serializable - data object Logs + data object Logs : Routes @Serializable - data class LogDetail(val fileName: String) + data class LogDetail(val fileName: String) : Routes @Serializable - data class OrderDetail(val id: String) + data class OrderDetail(val id: String) : Routes @Serializable - data class CjitDetail(val id: String) + data class CjitDetail(val id: String) : Routes @Serializable - data object ConnectionsNav + data object ConnectionsNav : Routes @Serializable - data object LightningConnections + data object LightningConnections : Routes @Serializable - data object ChannelDetail + data object ChannelDetail : Routes @Serializable - data object CloseConnection + data object CloseConnection : Routes @Serializable - data object DevSettings + data object DevSettings : Routes @Serializable - data object RegtestSettings + data object RegtestSettings : Routes @Serializable - data object TransferRoot + data object TransferRoot : Routes @Serializable - data object TransferIntro + data object TransferIntro : Routes @Serializable - data object SpendingIntro + data object SpendingIntro : Routes @Serializable - data object SpendingAmount + data object SpendingAmount : Routes @Serializable - data object SpendingConfirm + data object SpendingConfirm : Routes @Serializable - data object SpendingAdvanced + data object SpendingAdvanced : Routes @Serializable - data object TransferLiquidity + data object TransferLiquidity : Routes @Serializable - data object SettingUp + data object SettingUp : Routes @Serializable - data object SavingsIntro + data object SavingsIntro : Routes @Serializable - data object SavingsAvailability + data object SavingsAvailability : Routes @Serializable - data object SavingsConfirm + data object SavingsConfirm : Routes @Serializable - data object SavingsAdvanced + data object SavingsAdvanced : Routes @Serializable - data object SavingsProgress + data object SavingsProgress : Routes @Serializable - data object Funding + data object Funding : Routes @Serializable - data object FundingAdvanced + data object FundingAdvanced : Routes @Serializable - data object ExternalNav + data object ExternalNav : Routes @Serializable - data object ExternalConnection + data class ExternalConnection(val scannedNodeUri: String? = null) : Routes @Serializable - data object ExternalAmount + data object ExternalAmount : Routes @Serializable - data object ExternalConfirm + data object ExternalConfirm : Routes @Serializable - data object ExternalSuccess + data object ExternalSuccess : Routes @Serializable - data object ExternalFeeCustom + data object ExternalFeeCustom : Routes @Serializable - data class ActivityDetail(val id: String) + data class ActivityDetail(val id: String) : Routes @Serializable - data class ActivityExplore(val id: String) + data class ActivityExplore(val id: String) : Routes @Serializable - data object QrScanner + data object QrScanner : Routes @Serializable - data object BuyIntro + data object BuyIntro : Routes @Serializable - data object Support + data object Support : Routes @Serializable - data object ReportIssue + data object ReportIssue : Routes @Serializable - data object ReportIssueSuccess + data object ReportIssueSuccess : Routes @Serializable - data object ReportIssueFailure + data object ReportIssueFailure : Routes @Serializable - data object QuickPayIntro + data object QuickPayIntro : Routes @Serializable - data object QuickPaySettings + data object QuickPaySettings : Routes @Serializable - data object ProfileIntro + data object ProfileIntro : Routes @Serializable - data object CreateProfile + data object CreateProfile : Routes @Serializable - data object ShopIntro + data object ShopIntro : Routes @Serializable - data object ShopDiscover + data object ShopDiscover : Routes @Serializable - data object WidgetsIntro + data object WidgetsIntro : Routes @Serializable - data object AddWidget + data object AddWidget : Routes @Serializable - data object Headlines + data object Headlines : Routes @Serializable - data object HeadlinesPreview + data object HeadlinesPreview : Routes @Serializable - data object HeadlinesEdit + data object HeadlinesEdit : Routes @Serializable - data object Facts + data object Facts : Routes @Serializable - data object FactsPreview + data object FactsPreview : Routes @Serializable - data object FactsEdit + data object FactsEdit : Routes @Serializable - data object Blocks + data object Blocks : Routes @Serializable - data object BlocksPreview + data object BlocksPreview : Routes @Serializable - data object BlocksEdit + data object BlocksEdit : Routes @Serializable - data object Weather + data object Weather : Routes @Serializable - data object WeatherPreview + data object WeatherPreview : Routes @Serializable - data object WeatherEdit + data object WeatherEdit : Routes @Serializable - data object Price + data object Price : Routes @Serializable - data object PricePreview + data object PricePreview : Routes @Serializable - data object PriceEdit + data object PriceEdit : Routes @Serializable - data object CalculatorPreview + data object CalculatorPreview : Routes } diff --git a/app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt b/app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt index 6262c7364..5f5cf3ef2 100644 --- a/app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt @@ -33,6 +33,7 @@ import androidx.compose.ui.unit.dp import to.bitkit.R import to.bitkit.ext.getClipboardText import to.bitkit.models.LnPeer +import to.bitkit.ui.Routes import to.bitkit.ui.components.BodyM import to.bitkit.ui.components.ButtonSize import to.bitkit.ui.components.Caption13Up @@ -52,6 +53,7 @@ import to.bitkit.viewmodels.ExternalNodeViewModel @Composable fun ExternalConnectionScreen( + route: Routes.ExternalConnection, viewModel: ExternalNodeViewModel, onNodeConnected: () -> Unit, onScanClick: () -> Unit, @@ -61,6 +63,12 @@ fun ExternalConnectionScreen( val context = LocalContext.current val uiState by viewModel.uiState.collectAsState() + LaunchedEffect(route.scannedNodeUri) { + if (route.scannedNodeUri != null) { + viewModel.parseNodeUri(route.scannedNodeUri) + } + } + LaunchedEffect(viewModel, onNodeConnected) { viewModel.effects.collect { when (it) { @@ -73,7 +81,7 @@ fun ExternalConnectionScreen( ExternalConnectionContent( uiState = uiState, onContinueClick = { peer -> viewModel.onConnectionContinue(peer) }, - onPasteClick = { viewModel.onConnectionPaste(clipboardText = context.getClipboardText().orEmpty()) }, + onPasteClick = { viewModel.parseNodeUri(context.getClipboardText().orEmpty()) }, onScanClick = onScanClick, onBackClick = onBackClick, onCloseClick = onCloseClick, diff --git a/app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt b/app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt index 64b143491..4f2e677a3 100644 --- a/app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt +++ b/app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt @@ -14,7 +14,6 @@ import com.synonym.bitkitcore.Scanner import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow @@ -54,6 +53,7 @@ import to.bitkit.services.LdkNodeEventBus import to.bitkit.services.ScannerService import to.bitkit.services.hasLightingParam import to.bitkit.services.lightningParam +import to.bitkit.ui.Routes import to.bitkit.ui.components.BottomSheetType import to.bitkit.ui.screens.wallets.send.SendRoute import to.bitkit.ui.shared.toast.ToastEventBus @@ -493,6 +493,11 @@ class AppViewModel @Inject constructor( } } + is Scanner.NodeId -> { + val nextRoute = Routes.ExternalConnection(scan.url) + mainScreenEffect(MainScreenEffect.Navigate(nextRoute)) + } + null -> { toast( type = Toast.ToastType.ERROR, @@ -644,7 +649,7 @@ class AppViewModel @Inject constructor( val filter = newTransaction.type.toActivityFilter() val paymentType = newTransaction.direction.toTxType() - viewModelScope.launch(Dispatchers.IO) { + viewModelScope.launch(bgDispatcher) { val activity = coreService.activity.get(filter = filter, txType = paymentType, limit = 1u).firstOrNull() if (activity == null) { @@ -652,7 +657,8 @@ class AppViewModel @Inject constructor( return@launch } - mainScreenEffect(MainScreenEffect.NavigateActivityDetail(activity.rawId())) + val nextRoute = Routes.ActivityDetail(activity.rawId()) + mainScreenEffect(MainScreenEffect.Navigate(nextRoute)) } } @@ -933,7 +939,7 @@ sealed class SendEffect { } sealed class MainScreenEffect { - data class NavigateActivityDetail(val activityId: String) : MainScreenEffect() + data class Navigate(val route: Routes) : MainScreenEffect() data object WipeWallet : MainScreenEffect() data class ProcessClipboardAutoRead(val data: String) : MainScreenEffect() } diff --git a/app/src/main/java/to/bitkit/viewmodels/ExternalNodeViewModel.kt b/app/src/main/java/to/bitkit/viewmodels/ExternalNodeViewModel.kt index f9beb9391..019e36066 100644 --- a/app/src/main/java/to/bitkit/viewmodels/ExternalNodeViewModel.kt +++ b/app/src/main/java/to/bitkit/viewmodels/ExternalNodeViewModel.kt @@ -58,9 +58,9 @@ class ExternalNodeViewModel @Inject constructor( } } - fun onConnectionPaste(clipboardText: String) { + fun parseNodeUri(uriString: String) { viewModelScope.launch { - val result = LnPeer.parseUri(clipboardText) + val result = LnPeer.parseUri(uriString) if (result.isSuccess) { _uiState.update { it.copy(peer = result.getOrNull()) } From 153bab534b177af09393b4d0e57ba92e9865c0b1 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Mon, 14 Jul 2025 20:18:19 +0200 Subject: [PATCH 11/18] refactor: rename to navigateToScanner --- app/src/main/java/to/bitkit/ui/ContentView.kt | 6 +++--- .../main/java/to/bitkit/ui/screens/wallets/HomeScreen.kt | 4 ++-- .../to/bitkit/ui/settings/advanced/ElectrumConfigScreen.kt | 4 ++-- .../java/to/bitkit/ui/settings/advanced/RgsServerScreen.kt | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/to/bitkit/ui/ContentView.kt b/app/src/main/java/to/bitkit/ui/ContentView.kt index b184a3978..8ca1dcf3b 100644 --- a/app/src/main/java/to/bitkit/ui/ContentView.kt +++ b/app/src/main/java/to/bitkit/ui/ContentView.kt @@ -558,7 +558,7 @@ private fun RootNavHost( } composable { FundingAdvancedScreen( - onLnUrl = { navController.navigateToQrScanner() }, + onLnUrl = { navController.navigateToScanner() }, onManual = { navController.navigate(Routes.ExternalNav) }, onBackClick = { navController.popBackStack() }, onCloseClick = { navController.popBackStack(inclusive = true) }, @@ -576,7 +576,7 @@ private fun RootNavHost( route = route, viewModel = viewModel, onNodeConnected = { navController.navigate(Routes.ExternalAmount) }, - onScanClick = { navController.navigateToQrScanner() }, + onScanClick = { navController.navigateToScanner() }, onBackClick = { navController.popBackStack() }, onCloseClick = { navController.popBackStack(inclusive = true) }, ) @@ -1327,7 +1327,7 @@ fun NavController.navigateToActivityExplore(id: String) = navigate( route = Routes.ActivityExplore(id), ) -fun NavController.navigateToQrScanner(isCalledForResult: Boolean = false) { +fun NavController.navigateToScanner(isCalledForResult: Boolean = false) { if (isCalledForResult) { currentBackStackEntry?.savedStateHandle?.set(SCAN_REQUEST_KEY, true) } diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/HomeScreen.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/HomeScreen.kt index c7cde8613..3de63adab 100644 --- a/app/src/main/java/to/bitkit/ui/screens/wallets/HomeScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/wallets/HomeScreen.kt @@ -88,7 +88,7 @@ import to.bitkit.ui.components.VerticalSpacer import to.bitkit.ui.components.WalletBalanceView import to.bitkit.ui.currencyViewModel import to.bitkit.ui.navigateToActivityItem -import to.bitkit.ui.navigateToQrScanner +import to.bitkit.ui.navigateToScanner import to.bitkit.ui.navigateToSettings import to.bitkit.ui.navigateToTransferFunding import to.bitkit.ui.navigateToTransferIntro @@ -331,7 +331,7 @@ fun HomeScreen( TabBar( onSendClick = { appViewModel.showSheet(BottomSheetType.Send()) }, onReceiveClick = { appViewModel.showSheet(BottomSheetType.Receive) }, - onScanClick = { rootNavController.navigateToQrScanner() }, + onScanClick = { rootNavController.navigateToScanner() }, modifier = Modifier .align(Alignment.BottomCenter) .systemBarsPadding() diff --git a/app/src/main/java/to/bitkit/ui/settings/advanced/ElectrumConfigScreen.kt b/app/src/main/java/to/bitkit/ui/settings/advanced/ElectrumConfigScreen.kt index 2cf178afa..c002ace73 100644 --- a/app/src/main/java/to/bitkit/ui/settings/advanced/ElectrumConfigScreen.kt +++ b/app/src/main/java/to/bitkit/ui/settings/advanced/ElectrumConfigScreen.kt @@ -38,7 +38,7 @@ import to.bitkit.ui.components.TextInput import to.bitkit.ui.components.VerticalSpacer import to.bitkit.ui.components.settings.SettingsButtonRow import to.bitkit.ui.components.settings.SettingsButtonValue -import to.bitkit.ui.navigateToQrScanner +import to.bitkit.ui.navigateToScanner import to.bitkit.ui.scaffold.AppTopBar import to.bitkit.ui.scaffold.ScanNavIcon import to.bitkit.ui.scaffold.ScreenColumn @@ -91,7 +91,7 @@ fun ElectrumConfigScreen( Content( uiState = uiState, onBack = { navController.popBackStack() }, - onScan = { navController.navigateToQrScanner(isCalledForResult = true) }, + onScan = { navController.navigateToScanner(isCalledForResult = true) }, onChangeHost = viewModel::setHost, onChangePort = viewModel::setPort, onChangeProtocol = viewModel::setProtocol, diff --git a/app/src/main/java/to/bitkit/ui/settings/advanced/RgsServerScreen.kt b/app/src/main/java/to/bitkit/ui/settings/advanced/RgsServerScreen.kt index c55fc2aed..5667c9975 100644 --- a/app/src/main/java/to/bitkit/ui/settings/advanced/RgsServerScreen.kt +++ b/app/src/main/java/to/bitkit/ui/settings/advanced/RgsServerScreen.kt @@ -32,7 +32,7 @@ import to.bitkit.ui.components.PrimaryButton import to.bitkit.ui.components.SecondaryButton import to.bitkit.ui.components.TextInput import to.bitkit.ui.components.VerticalSpacer -import to.bitkit.ui.navigateToQrScanner +import to.bitkit.ui.navigateToScanner import to.bitkit.ui.scaffold.AppTopBar import to.bitkit.ui.scaffold.ScanNavIcon import to.bitkit.ui.scaffold.ScreenColumn @@ -83,7 +83,7 @@ fun RgsServerScreen( Content( uiState = uiState, onBack = { navController.popBackStack() }, - onScan = { navController.navigateToQrScanner(isCalledForResult = true) }, + onScan = { navController.navigateToScanner(isCalledForResult = true) }, onChangeUrl = viewModel::setRgsUrl, onClickReset = viewModel::resetToDefault, onClickConnect = viewModel::onClickConnect, From de0eec164e318b04102452327b09d9055467f74c Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Mon, 14 Jul 2025 20:22:41 +0200 Subject: [PATCH 12/18] refactor: move popBackStack to inner handler --- app/src/main/java/to/bitkit/ui/ContentView.kt | 1 - .../main/java/to/bitkit/ui/screens/scanner/QrScanningScreen.kt | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/app/src/main/java/to/bitkit/ui/ContentView.kt b/app/src/main/java/to/bitkit/ui/ContentView.kt index 8ca1dcf3b..0931b5a25 100644 --- a/app/src/main/java/to/bitkit/ui/ContentView.kt +++ b/app/src/main/java/to/bitkit/ui/ContentView.kt @@ -954,7 +954,6 @@ private fun NavGraphBuilder.qrScanner( exitTransition = { screenSlideOut }, ) { QrScanningScreen(navController = navController) { qrCode -> - navController.popBackStack() appViewModel.onScanSuccess( data = qrCode, onResultDelay = 650 // slight delay for nav transition before showing send sheet diff --git a/app/src/main/java/to/bitkit/ui/screens/scanner/QrScanningScreen.kt b/app/src/main/java/to/bitkit/ui/screens/scanner/QrScanningScreen.kt index e9ddd9bb3..ca037014c 100644 --- a/app/src/main/java/to/bitkit/ui/screens/scanner/QrScanningScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/scanner/QrScanningScreen.kt @@ -17,7 +17,6 @@ import androidx.camera.core.ImageAnalysis import androidx.camera.core.Preview import androidx.camera.lifecycle.ProcessCameraProvider import androidx.camera.view.PreviewView -import androidx.compose.animation.core.AnimationConstants import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -71,7 +70,6 @@ import to.bitkit.ui.shared.util.gradientBackground import to.bitkit.ui.theme.Colors import to.bitkit.utils.Logger import java.util.concurrent.Executors -import kotlin.time.Duration.Companion.milliseconds const val SCAN_REQUEST_KEY = "SCAN_REQUEST" const val SCAN_RESULT_KEY = "SCAN_RESULT" @@ -99,6 +97,7 @@ fun QrScanningScreen( navController.popBackStack() backStackEntry.savedStateHandle.remove(SCAN_REQUEST_KEY) } else { + navController.popBackStack() onScanSuccess(qrCode) } From 590b820dca3e1dbc3a2bc2f3e3784d3e9ad2f656 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Mon, 14 Jul 2025 20:29:02 +0200 Subject: [PATCH 13/18] refactor: remove redundant ResourceProvider --- .../java/to/bitkit/utils/ResourceProvider.kt | 17 ----------------- .../viewmodels/ExternalNodeViewModel.kt | 19 +++++++++---------- 2 files changed, 9 insertions(+), 27 deletions(-) delete mode 100644 app/src/main/java/to/bitkit/utils/ResourceProvider.kt diff --git a/app/src/main/java/to/bitkit/utils/ResourceProvider.kt b/app/src/main/java/to/bitkit/utils/ResourceProvider.kt deleted file mode 100644 index b1ba730f2..000000000 --- a/app/src/main/java/to/bitkit/utils/ResourceProvider.kt +++ /dev/null @@ -1,17 +0,0 @@ -package to.bitkit.utils - -import android.content.Context -import androidx.annotation.StringRes -import dagger.hilt.android.qualifiers.ApplicationContext -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class ResourceProvider @Inject constructor( - @ApplicationContext private val context: Context, -) { - - fun getString(@StringRes resId: Int): String { - return context.getString(resId) - } -} diff --git a/app/src/main/java/to/bitkit/viewmodels/ExternalNodeViewModel.kt b/app/src/main/java/to/bitkit/viewmodels/ExternalNodeViewModel.kt index 019e36066..5b4a1b12c 100644 --- a/app/src/main/java/to/bitkit/viewmodels/ExternalNodeViewModel.kt +++ b/app/src/main/java/to/bitkit/viewmodels/ExternalNodeViewModel.kt @@ -1,8 +1,10 @@ package to.bitkit.viewmodels +import android.content.Context import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asSharedFlow @@ -19,14 +21,13 @@ import to.bitkit.models.Toast import to.bitkit.services.LdkNodeEventBus import to.bitkit.services.LightningService import to.bitkit.ui.shared.toast.ToastEventBus -import to.bitkit.utils.ResourceProvider import to.bitkit.viewmodels.ExternalNodeContract.SideEffect import to.bitkit.viewmodels.ExternalNodeContract.UiState import javax.inject.Inject @HiltViewModel class ExternalNodeViewModel @Inject constructor( - private val resourceProvider: ResourceProvider, + @ApplicationContext private val context: Context, private val lightningService: LightningService, private val ldkNodeEventBus: LdkNodeEventBus, ) : ViewModel() { @@ -51,8 +52,8 @@ class ExternalNodeViewModel @Inject constructor( } else { ToastEventBus.send( type = Toast.ToastType.ERROR, - title = resourceProvider.getString(R.string.lightning__error_add_title), - description = resourceProvider.getString(R.string.lightning__error_add), + title = context.getString(R.string.lightning__error_add_title), + description = context.getString(R.string.lightning__error_add), ) } } @@ -67,7 +68,7 @@ class ExternalNodeViewModel @Inject constructor( } else { ToastEventBus.send( type = Toast.ToastType.ERROR, - title = resourceProvider.getString(R.string.lightning__error_add_uri), + title = context.getString(R.string.lightning__error_add_uri), ) } } @@ -103,15 +104,13 @@ class ExternalNodeViewModel @Inject constructor( } } - private suspend fun failConfirm(errorMessage: String) { + private suspend fun failConfirm(error: String) { _uiState.update { it.copy(isLoading = false) } ToastEventBus.send( type = Toast.ToastType.ERROR, - title = resourceProvider.getString(R.string.lightning__error_channel_purchase), - description = resourceProvider - .getString(R.string.lightning__error_channel_setup_msg) - .replace("{raw}", errorMessage), + title = context.getString(R.string.lightning__error_channel_purchase), + description = context.getString(R.string.lightning__error_channel_setup_msg).replace("{raw}", error), ) } From dbb194388ab515a5058d4af583a96bb99ed8b957 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Mon, 14 Jul 2025 20:38:54 +0200 Subject: [PATCH 14/18] fix: navigate home on close tap from funding screens --- app/src/main/java/to/bitkit/ui/ContentView.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/to/bitkit/ui/ContentView.kt b/app/src/main/java/to/bitkit/ui/ContentView.kt index 0931b5a25..72eba96c0 100644 --- a/app/src/main/java/to/bitkit/ui/ContentView.kt +++ b/app/src/main/java/to/bitkit/ui/ContentView.kt @@ -553,7 +553,7 @@ private fun RootNavHost( }, onAdvanced = { navController.navigate(Routes.FundingAdvanced) }, onBackClick = { navController.popBackStack() }, - onCloseClick = { navController.navigateUp() }, + onCloseClick = { navController.navigateToHome() }, ) } composable { @@ -561,7 +561,7 @@ private fun RootNavHost( onLnUrl = { navController.navigateToScanner() }, onManual = { navController.navigate(Routes.ExternalNav) }, onBackClick = { navController.popBackStack() }, - onCloseClick = { navController.popBackStack(inclusive = true) }, + onCloseClick = { navController.navigateToHome() }, ) } navigation( @@ -578,7 +578,7 @@ private fun RootNavHost( onNodeConnected = { navController.navigate(Routes.ExternalAmount) }, onScanClick = { navController.navigateToScanner() }, onBackClick = { navController.popBackStack() }, - onCloseClick = { navController.popBackStack(inclusive = true) }, + onCloseClick = { navController.navigateToHome() }, ) } composable { @@ -589,7 +589,7 @@ private fun RootNavHost( viewModel = viewModel, onContinue = { navController.navigate(Routes.ExternalConfirm) }, onBackClick = { navController.popBackStack() }, - onCloseClick = { navController.popBackStack(inclusive = true) }, + onCloseClick = { navController.navigateToHome() }, ) } composable { @@ -604,13 +604,13 @@ private fun RootNavHost( }, onNetworkFeeClick = { navController.navigate(Routes.ExternalFeeCustom) }, onBackClick = { navController.popBackStack() }, - onCloseClick = { navController.popBackStack(inclusive = true) }, + onCloseClick = { navController.navigateToHome() }, ) } composable { ExternalSuccessScreen( onContinue = { navController.popBackStack(inclusive = true) }, - onClose = { navController.popBackStack(inclusive = true) }, + onClose = { navController.navigateToHome() }, ) } composable { From 8af57e56e004cc786b3daa400a929067af8dd2ee Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Mon, 14 Jul 2025 20:49:24 +0200 Subject: [PATCH 15/18] fix: prevent nav back to same screen after scan --- app/src/main/java/to/bitkit/ui/ContentView.kt | 1 + .../ui/screens/transfer/external/ExternalConnectionScreen.kt | 3 +++ 2 files changed, 4 insertions(+) diff --git a/app/src/main/java/to/bitkit/ui/ContentView.kt b/app/src/main/java/to/bitkit/ui/ContentView.kt index 72eba96c0..6641a3320 100644 --- a/app/src/main/java/to/bitkit/ui/ContentView.kt +++ b/app/src/main/java/to/bitkit/ui/ContentView.kt @@ -575,6 +575,7 @@ private fun RootNavHost( ExternalConnectionScreen( route = route, viewModel = viewModel, + navController = navController, onNodeConnected = { navController.navigate(Routes.ExternalAmount) }, onScanClick = { navController.navigateToScanner() }, onBackClick = { navController.popBackStack() }, diff --git a/app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt b/app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt index 5f5cf3ef2..ece322e14 100644 --- a/app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt @@ -30,6 +30,7 @@ import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.navigation.NavController import to.bitkit.R import to.bitkit.ext.getClipboardText import to.bitkit.models.LnPeer @@ -55,6 +56,7 @@ import to.bitkit.viewmodels.ExternalNodeViewModel fun ExternalConnectionScreen( route: Routes.ExternalConnection, viewModel: ExternalNodeViewModel, + navController: NavController, onNodeConnected: () -> Unit, onScanClick: () -> Unit, onBackClick: () -> Unit, @@ -66,6 +68,7 @@ fun ExternalConnectionScreen( LaunchedEffect(route.scannedNodeUri) { if (route.scannedNodeUri != null) { viewModel.parseNodeUri(route.scannedNodeUri) + navController.popBackStack() // prevent back nav to same screen } } From 942879a4caf77c02e613f1c6489ef5087b89703c Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Mon, 14 Jul 2025 21:16:12 +0200 Subject: [PATCH 16/18] feat: add screen transitions to transfer screens --- app/src/main/java/to/bitkit/ui/ContentView.kt | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/to/bitkit/ui/ContentView.kt b/app/src/main/java/to/bitkit/ui/ContentView.kt index 6641a3320..e1091b2e7 100644 --- a/app/src/main/java/to/bitkit/ui/ContentView.kt +++ b/app/src/main/java/to/bitkit/ui/ContentView.kt @@ -436,7 +436,7 @@ private fun RootNavHost( navigation( startDestination = Routes.TransferIntro, ) { - composable { + composableWithDefaultTransitions { TransferIntroScreen( onContinueClick = { navController.navigateToTransferFunding() @@ -445,7 +445,7 @@ private fun RootNavHost( onCloseClick = { navController.navigateToHome() }, ) } - composable { + composableWithDefaultTransitions { SavingsIntroScreen( onContinueClick = { navController.navigate(Routes.SavingsAvailability) @@ -455,14 +455,14 @@ private fun RootNavHost( onCloseClick = { navController.navigateToHome() }, ) } - composable { + composableWithDefaultTransitions { SavingsAvailabilityScreen( onBackClick = { navController.popBackStack() }, onCancelClick = { navController.navigateToHome() }, onContinueClick = { navController.navigate(Routes.SavingsConfirm) }, ) } - composable { + composableWithDefaultTransitions { SavingsConfirmScreen( onConfirm = { navController.navigate(Routes.SavingsProgress) }, onAdvancedClick = { navController.navigate(Routes.SavingsAdvanced) }, @@ -470,20 +470,20 @@ private fun RootNavHost( onCloseClick = { navController.navigateToHome() }, ) } - composable { + composableWithDefaultTransitions { SavingsAdvancedScreen( onContinueClick = { navController.popBackStack(inclusive = false) }, onBackClick = { navController.popBackStack() }, onCloseClick = { navController.navigateToHome() }, ) } - composable { + composableWithDefaultTransitions { SavingsProgressScreen( onContinueClick = { navController.popBackStack(inclusive = true) }, onCloseClick = { navController.popBackStack(inclusive = true) }, ) } - composable { + composableWithDefaultTransitions { SpendingIntroScreen( onContinueClick = { navController.navigate(Routes.SpendingAmount) @@ -493,7 +493,7 @@ private fun RootNavHost( onCloseClick = { navController.navigateToHome() }, ) } - composable { + composableWithDefaultTransitions { SpendingAmountScreen( viewModel = transferViewModel, onBackClick = { navController.popBackStack() }, @@ -501,7 +501,7 @@ private fun RootNavHost( onOrderCreated = { navController.navigate(Routes.SpendingConfirm) }, ) } - composable { + composableWithDefaultTransitions { SpendingConfirmScreen( viewModel = transferViewModel, onBackClick = { navController.popBackStack() }, @@ -511,7 +511,7 @@ private fun RootNavHost( onConfirm = { navController.navigate(Routes.SettingUp) }, ) } - composable { + composableWithDefaultTransitions { SpendingAdvancedScreen( viewModel = transferViewModel, onBackClick = { navController.popBackStack() }, @@ -519,21 +519,21 @@ private fun RootNavHost( onOrderCreated = { navController.popBackStack(inclusive = false) }, ) } - composable { + composableWithDefaultTransitions { LiquidityScreen( onBackClick = { navController.popBackStack() }, onCloseClick = { navController.navigateToHome() }, onContinueClick = { navController.popBackStack() } ) } - composable { + composableWithDefaultTransitions { SettingUpScreen( viewModel = transferViewModel, onCloseClick = { navController.popBackStack(inclusive = true) }, onContinueClick = { navController.popBackStack(inclusive = true) }, ) } - composable { + composableWithDefaultTransitions { val hasSeenSpendingIntro by settingsViewModel.hasSeenSpendingIntro.collectAsState() FundingScreen( onTransfer = { @@ -556,7 +556,7 @@ private fun RootNavHost( onCloseClick = { navController.navigateToHome() }, ) } - composable { + composableWithDefaultTransitions { FundingAdvancedScreen( onLnUrl = { navController.navigateToScanner() }, onManual = { navController.navigate(Routes.ExternalNav) }, @@ -567,7 +567,7 @@ private fun RootNavHost( navigation( startDestination = Routes.ExternalConnection(), ) { - composable { + composableWithDefaultTransitions { val parentEntry = remember(it) { navController.getBackStackEntry(Routes.ExternalNav) } val route = it.toRoute() val viewModel = hiltViewModel(parentEntry) @@ -582,7 +582,7 @@ private fun RootNavHost( onCloseClick = { navController.navigateToHome() }, ) } - composable { + composableWithDefaultTransitions { val parentEntry = remember(it) { navController.getBackStackEntry(Routes.ExternalNav) } val viewModel = hiltViewModel(parentEntry) @@ -593,7 +593,7 @@ private fun RootNavHost( onCloseClick = { navController.navigateToHome() }, ) } - composable { + composableWithDefaultTransitions { val parentEntry = remember(it) { navController.getBackStackEntry(Routes.ExternalNav) } val viewModel = hiltViewModel(parentEntry) @@ -608,13 +608,13 @@ private fun RootNavHost( onCloseClick = { navController.navigateToHome() }, ) } - composable { + composableWithDefaultTransitions { ExternalSuccessScreen( onContinue = { navController.popBackStack(inclusive = true) }, onClose = { navController.navigateToHome() }, ) } - composable { + composableWithDefaultTransitions { ExternalFeeCustomScreen( onBackClick = { navController.popBackStack() }, onCloseClick = { navController.popBackStack(inclusive = true) }, From efcd0af2b1e98287644fe16c2fac311a9674edb0 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Mon, 14 Jul 2025 21:18:48 +0200 Subject: [PATCH 17/18] revert: fix: prevent nav back to same screen after scan --- app/src/main/java/to/bitkit/ui/ContentView.kt | 1 - .../ui/screens/transfer/external/ExternalConnectionScreen.kt | 3 --- 2 files changed, 4 deletions(-) diff --git a/app/src/main/java/to/bitkit/ui/ContentView.kt b/app/src/main/java/to/bitkit/ui/ContentView.kt index e1091b2e7..be73ca10d 100644 --- a/app/src/main/java/to/bitkit/ui/ContentView.kt +++ b/app/src/main/java/to/bitkit/ui/ContentView.kt @@ -575,7 +575,6 @@ private fun RootNavHost( ExternalConnectionScreen( route = route, viewModel = viewModel, - navController = navController, onNodeConnected = { navController.navigate(Routes.ExternalAmount) }, onScanClick = { navController.navigateToScanner() }, onBackClick = { navController.popBackStack() }, diff --git a/app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt b/app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt index ece322e14..5f5cf3ef2 100644 --- a/app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt @@ -30,7 +30,6 @@ import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import androidx.navigation.NavController import to.bitkit.R import to.bitkit.ext.getClipboardText import to.bitkit.models.LnPeer @@ -56,7 +55,6 @@ import to.bitkit.viewmodels.ExternalNodeViewModel fun ExternalConnectionScreen( route: Routes.ExternalConnection, viewModel: ExternalNodeViewModel, - navController: NavController, onNodeConnected: () -> Unit, onScanClick: () -> Unit, onBackClick: () -> Unit, @@ -68,7 +66,6 @@ fun ExternalConnectionScreen( LaunchedEffect(route.scannedNodeUri) { if (route.scannedNodeUri != null) { viewModel.parseNodeUri(route.scannedNodeUri) - navController.popBackStack() // prevent back nav to same screen } } From 0fec0ee128e866e87983856842ba0e732ad96933 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Mon, 14 Jul 2025 21:30:42 +0200 Subject: [PATCH 18/18] fix: node scan navigation --- app/src/main/java/to/bitkit/ui/ContentView.kt | 3 ++- .../transfer/external/ExternalConnectionScreen.kt | 15 +++++++++++++++ .../java/to/bitkit/viewmodels/AppViewModel.kt | 1 + 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/to/bitkit/ui/ContentView.kt b/app/src/main/java/to/bitkit/ui/ContentView.kt index be73ca10d..68336d992 100644 --- a/app/src/main/java/to/bitkit/ui/ContentView.kt +++ b/app/src/main/java/to/bitkit/ui/ContentView.kt @@ -574,9 +574,10 @@ private fun RootNavHost( ExternalConnectionScreen( route = route, + savedStateHandle = it.savedStateHandle, viewModel = viewModel, onNodeConnected = { navController.navigate(Routes.ExternalAmount) }, - onScanClick = { navController.navigateToScanner() }, + onScanClick = { navController.navigateToScanner(isCalledForResult = true) }, onBackClick = { navController.popBackStack() }, onCloseClick = { navController.navigateToHome() }, ) diff --git a/app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt b/app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt index 5f5cf3ef2..9682edaf6 100644 --- a/app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt @@ -30,6 +30,8 @@ import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.lifecycle.SavedStateHandle +import kotlinx.coroutines.flow.filterNotNull import to.bitkit.R import to.bitkit.ext.getClipboardText import to.bitkit.models.LnPeer @@ -44,6 +46,7 @@ import to.bitkit.ui.components.TextInput import to.bitkit.ui.scaffold.AppTopBar import to.bitkit.ui.scaffold.CloseNavIcon import to.bitkit.ui.scaffold.ScreenColumn +import to.bitkit.ui.screens.scanner.SCAN_RESULT_KEY import to.bitkit.ui.theme.AppThemeSurface import to.bitkit.ui.theme.Colors import to.bitkit.ui.utils.withAccent @@ -54,6 +57,7 @@ import to.bitkit.viewmodels.ExternalNodeViewModel @Composable fun ExternalConnectionScreen( route: Routes.ExternalConnection, + savedStateHandle: SavedStateHandle, viewModel: ExternalNodeViewModel, onNodeConnected: () -> Unit, onScanClick: () -> Unit, @@ -63,12 +67,23 @@ fun ExternalConnectionScreen( val context = LocalContext.current val uiState by viewModel.uiState.collectAsState() + // Handle result from scanner opened from home LaunchedEffect(route.scannedNodeUri) { if (route.scannedNodeUri != null) { viewModel.parseNodeUri(route.scannedNodeUri) } } + // Handle result from scanner opened from this screen + LaunchedEffect(savedStateHandle) { + savedStateHandle.getStateFlow(SCAN_RESULT_KEY, null) + .filterNotNull() + .collect { scannedData -> + viewModel.parseNodeUri(scannedData) + savedStateHandle.remove(SCAN_RESULT_KEY) + } + } + LaunchedEffect(viewModel, onNodeConnected) { viewModel.effects.collect { when (it) { diff --git a/app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt b/app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt index 4f2e677a3..cfd9f18a2 100644 --- a/app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt +++ b/app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt @@ -494,6 +494,7 @@ class AppViewModel @Inject constructor( } is Scanner.NodeId -> { + hideSheet() // hide scan sheet if opened val nextRoute = Routes.ExternalConnection(scan.url) mainScreenEffect(MainScreenEffect.Navigate(nextRoute)) }