diff --git a/AGENTS.md b/AGENTS.md index 46af00cd0..03edec5bd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -192,8 +192,9 @@ suspend fun getData(): Result = withContext(Dispatchers.IO) { - NEVER hardcode strings and always preserve string resources - ALWAYS localize in ViewModels using injected `@ApplicationContext`, e.g. `context.getString()` - ALWAYS use `remember` for expensive Compose computations -- ALWAYS add modifiers to the last place in the argument list when calling composable functions -- NEVER add parameters with default values BEFORE the `modifier` parameter in composable functions - modifier must be the FIRST optional parameter +- ALWAYS declare `modifier: Modifier = Modifier,` as the FIRST optional parameter in composable declarations +- ALWAYS pass `modifier = ...` as the LAST argument in composable calls +- ALWAYS add trailing commas in multi-line declarations; NEVER add a trailing comma to `modifier = ...` at call sites - ALWAYS use `navController.navigateTo(route)` for simple navigation; NEVER use raw `navController.navigate(route)` — `navigateTo` prevents duplicate destinations - ALWAYS prefer `VerticalSpacer`, `HorizontalSpacer`, `FillHeight` and `FillWidth` over `Spacer` when applicable - PREFER declaring small dependant classes, constants, interfaces or top-level functions in the same file with the core class where these are used diff --git a/app/src/main/java/to/bitkit/models/Language.kt b/app/src/main/java/to/bitkit/models/Language.kt index c590b5903..9749d4629 100644 --- a/app/src/main/java/to/bitkit/models/Language.kt +++ b/app/src/main/java/to/bitkit/models/Language.kt @@ -1,35 +1,38 @@ package to.bitkit.models +import androidx.annotation.StringRes import kotlinx.serialization.Serializable +import to.bitkit.R import java.util.Locale @Serializable enum class Language( - val displayName: String, + @StringRes val displayNameResId: Int? = null, + val nativeName: String? = null, val languageCode: String, val countryCode: String? = null, val isSystemDefault: Boolean = false, ) { SYSTEM_DEFAULT( - displayName = "System Default", + displayNameResId = R.string.settings__language_system_default, languageCode = "system", countryCode = null, isSystemDefault = true ), - ARABIC("العربية", "ar"), - CATALAN("Català", "ca"), - CZECH("Čeština", "cs"), - DUTCH("Nederlands", "nl"), - ENGLISH("English", "en", "US"), - FRENCH("Français", "fr", "FR"), - GERMAN("Deutsch", "de"), - GREEK("Ελληνικά", "el"), - ITALIAN("Italiano", "it"), - POLISH("Polski", "pl"), - PORTUGUESE("Português", "pt", "BR"), - RUSSIAN("Русский", "ru"), - SPANISH("Español", "es", "ES"), - SPANISH_LATIN_AMERICA("Español (Latinoamérica)", "es", "419"); + ARABIC(nativeName = "العربية", languageCode = "ar"), + CATALAN(nativeName = "Català", languageCode = "ca"), + CZECH(nativeName = "Čeština", languageCode = "cs"), + DUTCH(nativeName = "Nederlands", languageCode = "nl"), + ENGLISH(nativeName = "English", languageCode = "en", countryCode = "US"), + FRENCH(nativeName = "Français", languageCode = "fr", countryCode = "FR"), + GERMAN(nativeName = "Deutsch", languageCode = "de"), + GREEK(nativeName = "Ελληνικά", languageCode = "el"), + ITALIAN(nativeName = "Italiano", languageCode = "it"), + POLISH(nativeName = "Polski", languageCode = "pl"), + PORTUGUESE(nativeName = "Português", languageCode = "pt", countryCode = "BR"), + RUSSIAN(nativeName = "Русский", languageCode = "ru"), + SPANISH(nativeName = "Español", languageCode = "es", countryCode = "ES"), + SPANISH_LATIN_AMERICA(nativeName = "Español (Latinoamérica)", languageCode = "es", countryCode = "419"); companion object { fun fromLanguageCode(languageCode: String, countryCode: String? = null): Language? { diff --git a/app/src/main/java/to/bitkit/ui/ContentView.kt b/app/src/main/java/to/bitkit/ui/ContentView.kt index 2e69d89d4..f4e30ee01 100644 --- a/app/src/main/java/to/bitkit/ui/ContentView.kt +++ b/app/src/main/java/to/bitkit/ui/ContentView.kt @@ -122,8 +122,6 @@ import to.bitkit.ui.screens.widgets.suggestions.SuggestionsViewModel import to.bitkit.ui.screens.widgets.weather.WeatherEditScreen import to.bitkit.ui.screens.widgets.weather.WeatherPreviewScreen import to.bitkit.ui.screens.widgets.weather.WeatherViewModel -import to.bitkit.ui.settings.AboutScreen -import to.bitkit.ui.settings.AdvancedSettingsScreen import to.bitkit.ui.settings.BackupSettingsScreen import to.bitkit.ui.settings.BlocktankRegtestScreen import to.bitkit.ui.settings.CJitDetailScreen @@ -132,7 +130,6 @@ import to.bitkit.ui.settings.LanguageSettingsScreen import to.bitkit.ui.settings.LogDetailScreen import to.bitkit.ui.settings.LogsScreen import to.bitkit.ui.settings.OrderDetailScreen -import to.bitkit.ui.settings.SecuritySettingsScreen import to.bitkit.ui.settings.SettingsScreen import to.bitkit.ui.settings.advanced.AddressTypePreferenceScreen import to.bitkit.ui.settings.advanced.AddressViewerScreen @@ -144,7 +141,6 @@ import to.bitkit.ui.settings.backgroundPayments.BackgroundPaymentsIntroScreen import to.bitkit.ui.settings.backgroundPayments.BackgroundPaymentsSettings import to.bitkit.ui.settings.backups.ResetAndRestoreScreen import to.bitkit.ui.settings.general.DefaultUnitSettingsScreen -import to.bitkit.ui.settings.general.GeneralSettingsScreen import to.bitkit.ui.settings.general.LocalCurrencySettingsScreen import to.bitkit.ui.settings.general.TagsSettingsScreen import to.bitkit.ui.settings.general.WidgetsSettingsScreen @@ -152,11 +148,7 @@ import to.bitkit.ui.settings.lightning.ChannelDetailScreen import to.bitkit.ui.settings.lightning.CloseConnectionScreen import to.bitkit.ui.settings.lightning.LightningConnectionsScreen import to.bitkit.ui.settings.lightning.LightningConnectionsViewModel -import to.bitkit.ui.settings.pin.ChangePinConfirmScreen -import to.bitkit.ui.settings.pin.ChangePinNewScreen -import to.bitkit.ui.settings.pin.ChangePinResultScreen -import to.bitkit.ui.settings.pin.ChangePinScreen -import to.bitkit.ui.settings.pin.DisablePinScreen +import to.bitkit.ui.settings.pin.PinManagementScreen import to.bitkit.ui.settings.quickPay.QuickPayIntroScreen import to.bitkit.ui.settings.quickPay.QuickPaySettingsScreen import to.bitkit.ui.settings.support.ReportIssueResultScreen @@ -167,7 +159,9 @@ import to.bitkit.ui.settings.transactionSpeed.TransactionSpeedSettingsScreen import to.bitkit.ui.sheets.BackgroundPaymentsIntroSheet import to.bitkit.ui.sheets.BackupRoute import to.bitkit.ui.sheets.BackupSheet +import to.bitkit.ui.sheets.ChangePinSheet import to.bitkit.ui.sheets.ConnectionClosedSheet +import to.bitkit.ui.sheets.DisablePinSheet import to.bitkit.ui.sheets.ForceTransferSheet import to.bitkit.ui.sheets.GiftSheet import to.bitkit.ui.sheets.HighBalanceWarningSheet @@ -398,6 +392,8 @@ fun ContentView( is Sheet.ActivityDateRangeSelector -> DateRangeSelectorSheet() is Sheet.ActivityTagSelector -> TagSelectorSheet() is Sheet.Pin -> PinSheet(sheet, appViewModel) + Sheet.ChangePin -> ChangePinSheet(appViewModel) + Sheet.DisablePin -> DisablePinSheet(appViewModel) is Sheet.Backup -> BackupSheet(sheet, onDismiss = { appViewModel.hideSheet() }) is Sheet.LnurlAuth -> LnurlAuthSheet(sheet, appViewModel) Sheet.ForceTransfer -> ForceTransferSheet(appViewModel, transferViewModel) @@ -491,7 +487,7 @@ fun ContentView( rootNavController = navController, hasSeenWidgetsIntro = hasSeenWidgetsIntro, hasSeenShopIntro = hasSeenShopIntro, - modifier = Modifier.align(Alignment.TopEnd), + modifier = Modifier.align(Alignment.TopEnd) ) } } @@ -527,16 +523,10 @@ private fun RootNavHost( comingSoon(navController) profile(navController, settingsViewModel) shop(navController, settingsViewModel, appViewModel) - generalSettings(navController) - advancedSettings(navController) - aboutSettings(navController) + generalSettingsSubScreens(navController) + advancedSettingsSubScreens(navController) transactionSpeedSettings(navController) - securitySettings(navController) - disablePin(navController) - changePin(navController) - changePinNew(navController) - changePinConfirm(navController) - changePinResult(navController) + pinManagement(navController) defaultUnitSettings(currencyViewModel, navController) localCurrencySettings(currencyViewModel, navController) backupSettings(navController) @@ -972,11 +962,7 @@ private fun NavGraphBuilder.shop( } } -private fun NavGraphBuilder.generalSettings(navController: NavHostController) { - composableWithDefaultTransitions { - GeneralSettingsScreen(navController) - } - +private fun NavGraphBuilder.generalSettingsSubScreens(navController: NavHostController) { composableWithDefaultTransitions { WidgetsSettingsScreen(navController) } @@ -1000,10 +986,7 @@ private fun NavGraphBuilder.generalSettings(navController: NavHostController) { } } -private fun NavGraphBuilder.advancedSettings(navController: NavHostController) { - composableWithDefaultTransitions { - AdvancedSettingsScreen(navController) - } +private fun NavGraphBuilder.advancedSettingsSubScreens(navController: NavHostController) { composableWithDefaultTransitions { CoinSelectPreferenceScreen(navController) } @@ -1024,16 +1007,6 @@ private fun NavGraphBuilder.advancedSettings(navController: NavHostController) { } } -private fun NavGraphBuilder.aboutSettings(navController: NavHostController) { - composableWithDefaultTransitions { - AboutScreen( - onBack = { - navController.popBackStack() - } - ) - } -} - private fun NavGraphBuilder.transactionSpeedSettings(navController: NavHostController) { composableWithDefaultTransitions { TransactionSpeedSettingsScreen(navController) @@ -1043,43 +1016,9 @@ private fun NavGraphBuilder.transactionSpeedSettings(navController: NavHostContr } } -private fun NavGraphBuilder.securitySettings(navController: NavHostController) { - composableWithDefaultTransitions { - SecuritySettingsScreen(navController = navController) - } -} - -private fun NavGraphBuilder.disablePin(navController: NavHostController) { - composableWithDefaultTransitions { - DisablePinScreen(navController) - } -} - -private fun NavGraphBuilder.changePin(navController: NavHostController) { - composableWithDefaultTransitions { - ChangePinScreen(navController) - } -} - -private fun NavGraphBuilder.changePinNew(navController: NavHostController) { - composableWithDefaultTransitions { - ChangePinNewScreen(navController) - } -} - -private fun NavGraphBuilder.changePinConfirm(navController: NavHostController) { - composableWithDefaultTransitions { - val route = it.toRoute() - ChangePinConfirmScreen( - newPin = route.newPin, - navController = navController, - ) - } -} - -private fun NavGraphBuilder.changePinResult(navController: NavHostController) { - composableWithDefaultTransitions { - ChangePinResultScreen(navController) +private fun NavGraphBuilder.pinManagement(navController: NavHostController) { + composableWithDefaultTransitions { + PinManagementScreen(navController) } } @@ -1517,21 +1456,7 @@ inline fun NavController.navigateTo( } } -fun NavController.navigateToGeneralSettings() = navigateTo(Routes.GeneralSettings) - -fun NavController.navigateToSecuritySettings() = navigateTo(Routes.SecuritySettings) - -fun NavController.navigateToDisablePin() = navigateTo(Routes.DisablePin) - -fun NavController.navigateToChangePin() = navigateTo(Routes.ChangePin) - -fun NavController.navigateToChangePinNew() = navigateTo(Routes.ChangePinNew) - -fun NavController.navigateToChangePinConfirm(newPin: String) = navigateTo( - Routes.ChangePinConfirm(newPin), -) - -fun NavController.navigateToChangePinResult() = navigateTo(Routes.ChangePinResult) +fun NavController.navigateToPinManagement() = navigateTo(Routes.PinManagement) fun NavController.navigateToAuthCheck( showLogoOnPin: Boolean = false, @@ -1592,9 +1517,6 @@ fun NavController.navigateToTagsSettings() = navigateTo(Routes.TagsSettings) fun NavController.navigateToLanguageSettings() = navigateTo(Routes.LanguageSettings) -fun NavController.navigateToAdvancedSettings() = navigateTo(Routes.AdvancedSettings) - -fun NavController.navigateToAboutSettings() = navigateTo(Routes.AboutSettings) // endregion @Stable @@ -1614,9 +1536,6 @@ sealed interface Routes { @Serializable data object NodeInfo : Routes - @Serializable - data object GeneralSettings : Routes - @Serializable data object TransactionSpeedSettings : Routes @@ -1626,9 +1545,6 @@ sealed interface Routes { @Serializable data object TagsSettings : Routes - @Serializable - data object AdvancedSettings : Routes - @Serializable data object CoinSelectPreference : Routes @@ -1644,29 +1560,11 @@ sealed interface Routes { @Serializable data object AddressViewer : Routes - @Serializable - data object AboutSettings : Routes - @Serializable data object CustomFeeSettings : Routes @Serializable - data object SecuritySettings : Routes - - @Serializable - data object DisablePin : Routes - - @Serializable - data object ChangePin : Routes - - @Serializable - data object ChangePinNew : Routes - - @Serializable - data class ChangePinConfirm(val newPin: String) : Routes - - @Serializable - data object ChangePinResult : Routes + data object PinManagement : Routes @Serializable data class AuthCheck( diff --git a/app/src/main/java/to/bitkit/ui/components/AuthCheckScreen.kt b/app/src/main/java/to/bitkit/ui/components/AuthCheckScreen.kt index 133fdc7d2..234af04a0 100644 --- a/app/src/main/java/to/bitkit/ui/components/AuthCheckScreen.kt +++ b/app/src/main/java/to/bitkit/ui/components/AuthCheckScreen.kt @@ -6,7 +6,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController import to.bitkit.ui.Routes import to.bitkit.ui.appViewModel -import to.bitkit.ui.navigateTo import to.bitkit.ui.settingsViewModel @Composable @@ -43,10 +42,9 @@ fun AuthCheckScreen( navController.popBackStack() } - AuthCheckAction.NAV_TO_RESET -> { - navController.navigateTo(Routes.ResetAndRestoreSettings) { - popUpTo(Routes.BackupSettings) - } + AuthCheckAction.SHOW_BACKUP_SHEET -> { + navController.popBackStack() + app.showSheet(Sheet.Backup()) } } }, @@ -58,5 +56,5 @@ object AuthCheckAction { const val TOGGLE_BIOMETRICS = "TOGGLE_BIOMETRICS" const val TOGGLE_PIN_FOR_PAYMENTS = "TOGGLE_PIN_FOR_PAYMENTS" const val DISABLE_PIN = "DISABLE_PIN" - const val NAV_TO_RESET = "NAV_TO_RESET" + const val SHOW_BACKUP_SHEET = "SHOW_BACKUP_SHEET" } diff --git a/app/src/main/java/to/bitkit/ui/components/DrawerMenu.kt b/app/src/main/java/to/bitkit/ui/components/DrawerMenu.kt index 4e5c4b176..859a21fa7 100644 --- a/app/src/main/java/to/bitkit/ui/components/DrawerMenu.kt +++ b/app/src/main/java/to/bitkit/ui/components/DrawerMenu.kt @@ -199,6 +199,16 @@ private fun Menu( modifier = Modifier.testTag("DrawerShop") ) + DrawerItem( + label = stringResource(R.string.wallet__drawer__support), + iconRes = R.drawable.ic_chats_circle, + onClick = { + rootNavController.navigateIfNotCurrent(Routes.Support) + scope.launch { drawerState.close() } + }, + modifier = Modifier.testTag("DrawerSupport") + ) + DrawerItem( label = stringResource(R.string.wallet__drawer__settings), iconRes = R.drawable.ic_settings, @@ -256,7 +266,7 @@ private fun DrawerItem( label: String, @DrawableRes iconRes: Int, modifier: Modifier = Modifier, - onClick: (() -> Unit)? = null, + onClick: (() -> Unit)? = null ) { Column( modifier = modifier @@ -306,7 +316,7 @@ private fun Preview() { drawerState = rememberDrawerState(initialValue = DrawerValue.Open), hasSeenWidgetsIntro = false, hasSeenShopIntro = false, - modifier = Modifier.align(Alignment.TopEnd), + modifier = Modifier.align(Alignment.TopEnd) ) } } diff --git a/app/src/main/java/to/bitkit/ui/components/SheetHost.kt b/app/src/main/java/to/bitkit/ui/components/SheetHost.kt index eabfcca0b..e1e7e4396 100644 --- a/app/src/main/java/to/bitkit/ui/components/SheetHost.kt +++ b/app/src/main/java/to/bitkit/ui/components/SheetHost.kt @@ -39,6 +39,8 @@ sealed interface Sheet { data class Send(val route: SendRoute = SendRoute.Recipient) : Sheet data object Receive : Sheet data class Pin(val route: PinRoute = PinRoute.Prompt()) : Sheet + data object ChangePin : Sheet + data object DisablePin : Sheet data class Backup(val route: BackupRoute = BackupRoute.ShowMnemonic) : Sheet data object ActivityDateRangeSelector : Sheet data object ActivityTagSelector : Sheet diff --git a/app/src/main/java/to/bitkit/ui/components/settings/Links.kt b/app/src/main/java/to/bitkit/ui/components/settings/Links.kt index 94728fc37..8c6083a38 100644 --- a/app/src/main/java/to/bitkit/ui/components/settings/Links.kt +++ b/app/src/main/java/to/bitkit/ui/components/settings/Links.kt @@ -1,12 +1,12 @@ package to.bitkit.ui.components.settings import android.content.Intent +import androidx.annotation.DrawableRes import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.FlowRow -import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.size -import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext @@ -15,99 +15,39 @@ import androidx.compose.ui.unit.dp import androidx.core.net.toUri import to.bitkit.R import to.bitkit.env.Env -import to.bitkit.ui.theme.Colors + +private data class SocialLink(@DrawableRes val iconRes: Int, val url: String) + +private val socialLinks = listOf( + SocialLink(R.drawable.ic_globe, Env.BITKIT_WEBSITE), + SocialLink(R.drawable.ic_medium, Env.SYNONYM_MEDIUM), + SocialLink(R.drawable.ic_x_twitter, Env.SYNONYM_X), + SocialLink(R.drawable.ic_discord, Env.BITKIT_DISCORD), + SocialLink(R.drawable.ic_telegram, Env.BITKIT_TELEGRAM), + SocialLink(R.drawable.ic_github, Env.BITKIT_GITHUB), +) @Composable fun Links(modifier: Modifier = Modifier) { val context = LocalContext.current - FlowRow( + Row( horizontalArrangement = Arrangement.SpaceBetween, - modifier = modifier.fillMaxWidth() + modifier = modifier ) { - FloatingActionButton( - onClick = { - val intent = Intent(Intent.ACTION_VIEW, Env.BITKIT_WEBSITE.toUri()) - context.startActivity(intent) - }, - containerColor = Colors.White16, - modifier = Modifier.size(48.dp) - ) { - Icon( - painter = painterResource(R.drawable.ic_globe), - contentDescription = null, - tint = Colors.White - ) - } - FloatingActionButton( - onClick = { - val intent = Intent(Intent.ACTION_VIEW, Env.SYNONYM_MEDIUM.toUri()) - context.startActivity(intent) - }, - containerColor = Colors.White16, - modifier = Modifier.size(48.dp) - ) { - Icon( - painter = painterResource(R.drawable.ic_medium), - contentDescription = null, - tint = Colors.White - ) - } - FloatingActionButton( - onClick = { - val intent = Intent(Intent.ACTION_VIEW, Env.SYNONYM_X.toUri()) - context.startActivity(intent) - }, - containerColor = Colors.White16, - modifier = Modifier.size(48.dp) - ) { - Icon( - painter = painterResource(R.drawable.ic_x_twitter), - contentDescription = null, - tint = Colors.White - ) - } - FloatingActionButton( - onClick = { - val intent = Intent(Intent.ACTION_VIEW, Env.BITKIT_DISCORD.toUri()) - context.startActivity(intent) - }, - containerColor = Colors.White16, - modifier = Modifier.size(48.dp) - ) { - Icon( - painter = painterResource(R.drawable.ic_discord), - contentDescription = null, - tint = Colors.White - ) - } - FloatingActionButton( - onClick = { - val intent = Intent(Intent.ACTION_VIEW, Env.BITKIT_TELEGRAM.toUri()) - context.startActivity(intent) - }, - containerColor = Colors.White16, - modifier = Modifier.size(48.dp) - ) { - Icon( - painter = painterResource(R.drawable.ic_telegram), - contentDescription = null, - tint = Colors.White - ) - } - FloatingActionButton( - onClick = { - val intent = Intent(Intent.ACTION_VIEW, Env.BITKIT_GITHUB.toUri()) - context.startActivity(intent) - }, - containerColor = Colors.White16, - modifier = Modifier.size(48.dp) - ) { - Icon( - painter = painterResource(R.drawable.ic_github), - contentDescription = null, - tint = Colors.White - ) + socialLinks.forEach { link -> + IconButton( + onClick = { + context.startActivity(Intent(Intent.ACTION_VIEW, link.url.toUri())) + }, + modifier = Modifier.size(48.dp) + ) { + Icon( + painter = painterResource(link.iconRes), + contentDescription = null, + modifier = Modifier.size(24.dp) + ) + } } } } diff --git a/app/src/main/java/to/bitkit/ui/components/settings/SettingsButtonRow.kt b/app/src/main/java/to/bitkit/ui/components/settings/SettingsButtonRow.kt index 4e2fadc25..109919170 100644 --- a/app/src/main/java/to/bitkit/ui/components/settings/SettingsButtonRow.kt +++ b/app/src/main/java/to/bitkit/ui/components/settings/SettingsButtonRow.kt @@ -30,11 +30,11 @@ import to.bitkit.ui.components.BodyM import to.bitkit.ui.components.BodyMSB import to.bitkit.ui.components.BodyS import to.bitkit.ui.components.BodySSB +import to.bitkit.ui.components.HorizontalSpacer import to.bitkit.ui.shared.modifiers.clickableAlpha import to.bitkit.ui.theme.AppThemeSurface import to.bitkit.ui.theme.Colors -@Suppress("CyclomaticComplexMethod") @Composable fun SettingsButtonRow( title: String, @@ -49,6 +49,80 @@ fun SettingsButtonRow( enabled: Boolean = true, loading: Boolean = false, onClick: () -> Unit, +) { + SettingsButtonRowCore( + title = title, + hasIcon = iconRes != null, + subtitle = subtitle, + value = value, + description = description, + maxLinesSubtitle = maxLinesSubtitle, + enabled = enabled, + loading = loading, + onClick = onClick, + icon = if (iconRes != null) { + { + Icon( + painter = painterResource(iconRes), + contentDescription = null, + tint = iconTint, + modifier = Modifier + .padding(end = 10.dp) + .size(iconSize) + ) + } + } else { + null + }, + modifier = modifier + ) +} + +@Composable +fun SettingsButtonRow( + title: String, + icon: @Composable () -> Unit, + modifier: Modifier = Modifier, + subtitle: String? = null, + value: SettingsButtonValue = SettingsButtonValue.None, + description: String? = null, + maxLinesSubtitle: Int = Int.MAX_VALUE, + enabled: Boolean = true, + loading: Boolean = false, + onClick: () -> Unit, +) { + SettingsButtonRowCore( + title = title, + hasIcon = true, + icon = { + icon() + HorizontalSpacer(8.dp) + }, + subtitle = subtitle, + value = value, + description = description, + maxLinesSubtitle = maxLinesSubtitle, + enabled = enabled, + loading = loading, + onClick = onClick, + modifier = modifier + ) +} + +@Suppress("CyclomaticComplexMethod") +@Composable +private fun SettingsButtonRowCore( + title: String, + hasIcon: Boolean, + modifier: Modifier = Modifier, + icon: (@Composable () -> Unit)? = null, + subtitle: String? = null, + value: SettingsButtonValue = SettingsButtonValue.None, + description: String? = null, + maxLinesSubtitle: Int = Int.MAX_VALUE, + enabled: Boolean = true, + loading: Boolean = false, + onClick: () -> Unit, ) { val alphaModifier = Modifier.then(if (!enabled) Modifier.alpha(0.5f) else Modifier) Column( @@ -57,7 +131,7 @@ fun SettingsButtonRow( ) { Column(modifier = alphaModifier) { val rowHeight = when { - subtitle != null && iconRes != null -> 90.dp + subtitle != null && hasIcon -> 90.dp subtitle != null -> 74.dp else -> 52.dp } @@ -67,14 +141,8 @@ fun SettingsButtonRow( .fillMaxWidth() .heightIn(min = rowHeight) ) { - if (iconRes != null) { - Icon( - painter = painterResource(iconRes), - contentDescription = null, - tint = iconTint, - modifier = Modifier.size(iconSize), - ) - Spacer(modifier = Modifier.width(10.dp)) + if (icon != null) { + icon() } Column( verticalArrangement = Arrangement.Center, @@ -110,7 +178,7 @@ fun SettingsButtonRow( painter = painterResource(R.drawable.ic_checkmark), contentDescription = null, tint = Colors.Brand, - modifier = Modifier.size(32.dp), + modifier = Modifier.size(32.dp) ) else -> Unit @@ -119,13 +187,13 @@ fun SettingsButtonRow( } is SettingsButtonValue.StringValue -> { - BodyM(text = value.value, modifier = Modifier.testTag("Value")) + BodyM(text = value.value, color = Colors.White64, modifier = Modifier.testTag("Value")) Spacer(modifier = Modifier.width(8.dp)) Icon( painter = painterResource(R.drawable.ic_chevron_right), contentDescription = null, tint = Colors.White64, - modifier = Modifier.size(24.dp), + modifier = Modifier.size(24.dp) ) } @@ -134,7 +202,7 @@ fun SettingsButtonRow( painter = painterResource(R.drawable.ic_chevron_right), contentDescription = null, tint = Colors.White64, - modifier = Modifier.size(24.dp), + modifier = Modifier.size(24.dp) ) } } @@ -147,7 +215,7 @@ fun SettingsButtonRow( color = Colors.White64, modifier = Modifier .padding(vertical = 4.dp) - .then(alphaModifier), + .then(alphaModifier) ) } } diff --git a/app/src/main/java/to/bitkit/ui/components/settings/SettingsIcon.kt b/app/src/main/java/to/bitkit/ui/components/settings/SettingsIcon.kt new file mode 100644 index 000000000..a0178a78d --- /dev/null +++ b/app/src/main/java/to/bitkit/ui/components/settings/SettingsIcon.kt @@ -0,0 +1,34 @@ +package to.bitkit.ui.components.settings + +import androidx.annotation.DrawableRes +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Icon +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import to.bitkit.ui.theme.Colors + +@Composable +fun SettingsIcon( + @DrawableRes iconRes: Int, + modifier: Modifier = Modifier, +) { + Box( + contentAlignment = Alignment.Center, + modifier = modifier + .size(32.dp) + .background(color = Colors.Black, shape = CircleShape) + ) { + Icon( + painter = painterResource(iconRes), + contentDescription = null, + tint = Colors.Brand, + modifier = Modifier.size(16.dp) + ) + } +} diff --git a/app/src/main/java/to/bitkit/ui/components/settings/SettingsSwitchRow.kt b/app/src/main/java/to/bitkit/ui/components/settings/SettingsSwitchRow.kt index 2b512440f..6371a5b8a 100644 --- a/app/src/main/java/to/bitkit/ui/components/settings/SettingsSwitchRow.kt +++ b/app/src/main/java/to/bitkit/ui/components/settings/SettingsSwitchRow.kt @@ -6,17 +6,23 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon import androidx.compose.material3.Switch import androidx.compose.material3.SwitchColors import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import to.bitkit.R import to.bitkit.ui.components.BodyM import to.bitkit.ui.components.BodyS +import to.bitkit.ui.components.HorizontalSpacer import to.bitkit.ui.shared.modifiers.clickableAlpha import to.bitkit.ui.theme.AppSwitchDefaults import to.bitkit.ui.theme.AppThemeSurface @@ -29,24 +35,85 @@ fun SettingsSwitchRow( onClick: () -> Unit, modifier: Modifier = Modifier, subtitle: String? = null, - colors: SwitchColors = AppSwitchDefaults.colors, + iconRes: Int? = null, + iconTint: Color = Color.Unspecified, + colors: SwitchColors = AppSwitchDefaults.colors ) { - Column( - modifier = modifier.heightIn(min = 52.dp), - ) { + SettingsSwitchRowCore( + title = title, + isChecked = isChecked, + onClick = onClick, + subtitle = subtitle, + colors = colors, + icon = if (iconRes != null) { + { + Icon( + painter = painterResource(iconRes), + contentDescription = null, + tint = iconTint, + modifier = Modifier.size(32.dp) + ) + HorizontalSpacer(10.dp) + } + } else { + null + }, + modifier = modifier + ) +} + +@Composable +fun SettingsSwitchRow( + title: String, + isChecked: Boolean, + icon: @Composable () -> Unit, + onClick: () -> Unit, + modifier: Modifier = Modifier, + subtitle: String? = null, + colors: SwitchColors = AppSwitchDefaults.colors +) { + SettingsSwitchRowCore( + title = title, + isChecked = isChecked, + onClick = onClick, + subtitle = subtitle, + colors = colors, + icon = { + icon() + HorizontalSpacer(8.dp) + }, + modifier = modifier + ) +} + +@Composable +private fun SettingsSwitchRowCore( + title: String, + isChecked: Boolean, + onClick: () -> Unit, + modifier: Modifier = Modifier, + subtitle: String? = null, + icon: (@Composable () -> Unit)? = null, + colors: SwitchColors = AppSwitchDefaults.colors +) { + Column(modifier = modifier) { Row( horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, modifier = Modifier .fillMaxWidth() + .heightIn(min = 52.dp) .clickableAlpha { onClick() } - .padding(vertical = 16.dp) ) { + if (icon != null) { + icon() + } + Column( modifier = Modifier .weight(1f) .padding(end = 16.dp), - verticalArrangement = Arrangement.spacedBy(4.dp), + verticalArrangement = Arrangement.spacedBy(4.dp) ) { BodyM(text = title, color = Colors.White, overflow = TextOverflow.Ellipsis) if (subtitle != null) { @@ -72,13 +139,19 @@ private fun Preview() { SettingsSwitchRow( title = "Setting 1", isChecked = true, - onClick = {}, + onClick = {} ) SettingsSwitchRow( title = "Setting 2", isChecked = false, onClick = {}, ) + SettingsSwitchRow( + title = "With Icon", + isChecked = true, + iconRes = R.drawable.ic_eye, + onClick = {}, + ) } } } diff --git a/app/src/main/java/to/bitkit/ui/scaffold/AppTopBar.kt b/app/src/main/java/to/bitkit/ui/scaffold/AppTopBar.kt index 8741a9bb9..9f45e302f 100644 --- a/app/src/main/java/to/bitkit/ui/scaffold/AppTopBar.kt +++ b/app/src/main/java/to/bitkit/ui/scaffold/AppTopBar.kt @@ -30,6 +30,7 @@ import to.bitkit.ui.LocalDrawerState import to.bitkit.ui.components.Title import to.bitkit.ui.shared.modifiers.rememberDebouncedClick import to.bitkit.ui.theme.AppThemeSurface +import to.bitkit.ui.theme.Colors @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -38,7 +39,7 @@ fun AppTopBar( onBackClick: (() -> Unit)?, modifier: Modifier = Modifier, @DrawableRes icon: Int? = null, - actions: @Composable (RowScope.() -> Unit) = {}, + actions: @Composable (RowScope.() -> Unit) = {} ) { CenterAlignedTopAppBar( navigationIcon = { @@ -69,7 +70,7 @@ fun AppTopBar( containerColor = Color.Transparent, scrolledContainerColor = Color.Transparent, ), - modifier = modifier, + modifier = modifier ) } @@ -107,6 +108,7 @@ fun DrawerNavIcon( Icon( painter = painterResource(id = R.drawable.ic_list), contentDescription = stringResource(R.string.settings__settings), + tint = Colors.White, modifier = Modifier.size(24.dp) ) } diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/activity/components/CustomTabRowWithSpacing.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/activity/components/CustomTabRowWithSpacing.kt index 1c20627a0..4ca6985ef 100644 --- a/app/src/main/java/to/bitkit/ui/screens/wallets/activity/components/CustomTabRowWithSpacing.kt +++ b/app/src/main/java/to/bitkit/ui/screens/wallets/activity/components/CustomTabRowWithSpacing.kt @@ -2,7 +2,6 @@ package to.bitkit.ui.screens.wallets.activity.components import androidx.compose.animation.animateColorAsState import androidx.compose.animation.core.FastOutSlowInEasing -import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.tween import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement @@ -62,19 +61,10 @@ fun CustomTabRowWithSpacing( ) } - val animatedAlpha by animateFloatAsState( - targetValue = if (isSelected) 1f else 0.2f, - animationSpec = tween( - durationMillis = 250, - easing = FastOutSlowInEasing - ), - label = "indicatorAlpha", - ) - val animatedColor by animateColorAsState( - targetValue = if (isSelected) selectedColor else Colors.White, + targetValue = if (isSelected) selectedColor else Colors.White50, animationSpec = tween( - durationMillis = 250, + durationMillis = 200, easing = FastOutSlowInEasing ), label = "indicatorColor", @@ -83,9 +73,8 @@ fun CustomTabRowWithSpacing( Box( modifier = Modifier .fillMaxWidth() - .padding(horizontal = 4.dp) - .height(3.dp) - .background(animatedColor.copy(alpha = animatedAlpha)) + .height(2.dp) + .background(animatedColor) ) } diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendPinCheckScreen.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendPinCheckScreen.kt index 413573e47..40303cef7 100644 --- a/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendPinCheckScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendPinCheckScreen.kt @@ -1,7 +1,6 @@ package to.bitkit.ui.screens.wallets.send import androidx.compose.animation.AnimatedVisibility -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height @@ -135,7 +134,7 @@ private fun PinCheckContent( PinDots( pin = pin, - modifier = Modifier.padding(vertical = 16.dp), + modifier = Modifier.padding(vertical = 16.dp) ) Spacer(modifier = Modifier.weight(1f)) @@ -145,7 +144,6 @@ private fun PinCheckContent( type = NumberPadType.SIMPLE, modifier = Modifier .height(350.dp) - .background(Colors.Black) ) } } @@ -162,7 +160,7 @@ private fun Preview() { onKeyPress = {}, onBack = {}, onClickForgotPin = {}, - modifier = Modifier.sheetHeight(), + modifier = Modifier.sheetHeight() ) } } @@ -179,7 +177,7 @@ private fun PreviewAttemptsLeft() { onKeyPress = {}, onBack = {}, onClickForgotPin = {}, - modifier = Modifier.sheetHeight(), + modifier = Modifier.sheetHeight() ) } } @@ -196,7 +194,7 @@ private fun PreviewAttemptsLast() { onKeyPress = {}, onBack = {}, onClickForgotPin = {}, - modifier = Modifier.sheetHeight(), + modifier = Modifier.sheetHeight() ) } } diff --git a/app/src/main/java/to/bitkit/ui/settings/AboutScreen.kt b/app/src/main/java/to/bitkit/ui/settings/AboutScreen.kt deleted file mode 100644 index c2ee445a2..000000000 --- a/app/src/main/java/to/bitkit/ui/settings/AboutScreen.kt +++ /dev/null @@ -1,109 +0,0 @@ -package to.bitkit.ui.settings - -import android.content.Intent -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.HorizontalDivider -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -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 androidx.core.net.toUri -import to.bitkit.BuildConfig -import to.bitkit.R -import to.bitkit.env.Env -import to.bitkit.ui.components.BodyM -import to.bitkit.ui.components.VerticalSpacer -import to.bitkit.ui.components.settings.Links -import to.bitkit.ui.components.settings.SettingsButtonRow -import to.bitkit.ui.scaffold.AppTopBar -import to.bitkit.ui.scaffold.DrawerNavIcon -import to.bitkit.ui.scaffold.ScreenColumn -import to.bitkit.ui.shared.util.shareText -import to.bitkit.ui.theme.AppThemeSurface -import to.bitkit.ui.theme.Colors - -@Composable -fun AboutScreen( - onBack: () -> Unit, -) { - val context = LocalContext.current - - ScreenColumn { - AppTopBar( - titleText = stringResource(R.string.settings__about__title), - onBackClick = onBack, - actions = { DrawerNavIcon() }, - ) - - Column( - modifier = Modifier.padding(horizontal = 16.dp) - ) { - VerticalSpacer(32.dp) - - BodyM(text = stringResource(R.string.settings__about__text), color = Colors.White64) - - VerticalSpacer(32.dp) - - SettingsButtonRow(title = stringResource(R.string.settings__about__legal), onClick = { - val intent = Intent(Intent.ACTION_VIEW, Env.TERMS_OF_USE_URL.toUri()) - context.startActivity(intent) - }) - - SettingsButtonRow(title = stringResource(R.string.settings__about__share), onClick = { - shareText( - context, - context.getString(R.string.settings__about__shareText) - .replace("{appStoreUrl}", Env.APP_STORE_URL) - .replace("{playStoreUrl}", Env.PLAY_STORE_URL) - ) - }) - - VerticalSpacer(14.dp) - - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween - ) { - BodyM(text = stringResource(R.string.settings__about__version)) - BodyM(text = "${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})", color = Colors.White50) - } - - VerticalSpacer(14.dp) - - HorizontalDivider() - - Image( - painter = painterResource(R.drawable.bitkit_logo), - contentDescription = null, - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 32.dp) - .weight(1f) - .testTag("AboutLogo") - ) - - Links(modifier = Modifier.fillMaxWidth()) - - VerticalSpacer(16.dp) - } - } -} - -@Preview -@Composable -private fun Preview() { - AppThemeSurface { - AboutScreen( - onBack = {}, - ) - } -} diff --git a/app/src/main/java/to/bitkit/ui/settings/AdvancedSettingsScreen.kt b/app/src/main/java/to/bitkit/ui/settings/AdvancedSettingsScreen.kt deleted file mode 100644 index db5235cca..000000000 --- a/app/src/main/java/to/bitkit/ui/settings/AdvancedSettingsScreen.kt +++ /dev/null @@ -1,205 +0,0 @@ -package to.bitkit.ui.settings - -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -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.testTag -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import androidx.navigation.NavController -import to.bitkit.R -import to.bitkit.ui.Routes -import to.bitkit.ui.components.VerticalSpacer -import to.bitkit.ui.components.settings.SectionHeader -import to.bitkit.ui.components.settings.SettingsButtonRow -import to.bitkit.ui.components.settings.SettingsButtonValue -import to.bitkit.ui.navigateTo -import to.bitkit.ui.navigateToHome -import to.bitkit.ui.scaffold.AppAlertDialog -import to.bitkit.ui.scaffold.AppTopBar -import to.bitkit.ui.scaffold.DrawerNavIcon -import to.bitkit.ui.scaffold.ScreenColumn -import to.bitkit.ui.theme.AppThemeSurface - -@Composable -fun AdvancedSettingsScreen( - navController: NavController, - viewModel: AdvancedSettingsViewModel = hiltViewModel(), -) { - var showResetSuggestionsDialog by remember { mutableStateOf(false) } - val selectedAddressTypeName by viewModel.selectedAddressTypeName.collectAsStateWithLifecycle() - - Content( - showResetSuggestionsDialog = showResetSuggestionsDialog, - selectedAddressTypeName = selectedAddressTypeName, - onBack = { navController.popBackStack() }, - onCoinSelectionClick = { - navController.navigateTo(Routes.CoinSelectPreference) - }, - onAddressTypePreferenceClick = { - navController.navigateTo(Routes.AddressTypePreference) - }, - onLightningConnectionsClick = { - navController.navigateTo(Routes.LightningConnections) - }, - onLightningNodeClick = { - navController.navigateTo(Routes.NodeInfo) - }, - onElectrumServerClick = { - navController.navigateTo(Routes.ElectrumConfig) - }, - onRgsServerClick = { - navController.navigateTo(Routes.RgsServer) - }, - onAddressViewerClick = { - navController.navigateTo(Routes.AddressViewer) - }, - onSuggestionsResetClick = { showResetSuggestionsDialog = true }, - onResetSuggestionsDialogConfirm = { - viewModel.resetSuggestions() - showResetSuggestionsDialog = false - navController.navigateToHome() - }, - onResetSuggestionsDialogCancel = { showResetSuggestionsDialog = false }, - ) -} - -@Composable -private fun Content( - showResetSuggestionsDialog: Boolean, - selectedAddressTypeName: String = "", - onBack: () -> Unit = {}, - onCoinSelectionClick: () -> Unit = {}, - onAddressTypePreferenceClick: () -> Unit = {}, - onLightningConnectionsClick: () -> Unit = {}, - onLightningNodeClick: () -> Unit = {}, - onElectrumServerClick: () -> Unit = {}, - onRgsServerClick: () -> Unit = {}, - onAddressViewerClick: () -> Unit = {}, - onSuggestionsResetClick: () -> Unit = {}, - onResetSuggestionsDialogConfirm: () -> Unit = {}, - onResetSuggestionsDialogCancel: () -> Unit = {}, -) { - ScreenColumn { - AppTopBar( - titleText = stringResource(R.string.settings__advanced_title), - onBackClick = onBack, - actions = { DrawerNavIcon() }, - ) - Column( - modifier = Modifier - .padding(horizontal = 16.dp) - .fillMaxSize() - .verticalScroll(rememberScrollState()) - .testTag("advanced_settings_screen") - ) { - // Payments Section - SectionHeader(title = stringResource(R.string.settings__adv__section_payments)) - - SettingsButtonRow( - title = stringResource(R.string.settings__addr_type__title), - value = if (selectedAddressTypeName.isNotEmpty()) { - SettingsButtonValue.StringValue(selectedAddressTypeName) - } else { - SettingsButtonValue.None - }, - onClick = onAddressTypePreferenceClick, - modifier = Modifier.testTag("AddressTypePreference"), - ) - - SettingsButtonRow( - title = stringResource(R.string.settings__adv__coin_selection), - onClick = onCoinSelectionClick, - modifier = Modifier.testTag("CoinSelectPreference"), - ) - - // Networks Section - SectionHeader(title = stringResource(R.string.settings__adv__section_networks)) - - SettingsButtonRow( - title = stringResource(R.string.settings__adv__lightning_connections), - onClick = onLightningConnectionsClick, - modifier = Modifier.testTag("Channels"), - ) - - SettingsButtonRow( - title = stringResource(R.string.settings__adv__lightning_node), - onClick = onLightningNodeClick, - modifier = Modifier.testTag("LightningNodeInfo"), - ) - - SettingsButtonRow( - title = stringResource(R.string.settings__adv__electrum_server), - onClick = onElectrumServerClick, - modifier = Modifier.testTag("ElectrumConfig"), - ) - - SettingsButtonRow( - title = stringResource(R.string.settings__adv__rgs_server), - onClick = onRgsServerClick, - modifier = Modifier.testTag("RGSServer"), - ) - - // Other Section - SectionHeader(title = stringResource(R.string.settings__adv__section_other)) - - SettingsButtonRow( - title = stringResource(R.string.settings__adv__address_viewer), - onClick = onAddressViewerClick, - modifier = Modifier.testTag("AddressViewer"), - ) - - SettingsButtonRow( - title = stringResource(R.string.settings__adv__suggestions_reset), - onClick = onSuggestionsResetClick, - modifier = Modifier.testTag("ResetSuggestions"), - ) - - VerticalSpacer(32.dp) - } - - if (showResetSuggestionsDialog) { - AppAlertDialog( - title = stringResource(R.string.settings__adv__reset_title), - text = stringResource(R.string.settings__adv__reset_desc), - confirmText = stringResource(R.string.settings__adv__reset_confirm), - onConfirm = onResetSuggestionsDialogConfirm, - onDismiss = onResetSuggestionsDialogCancel, - modifier = Modifier.testTag("reset_suggestions_dialog"), - ) - } - } -} - -@Preview -@Composable -private fun Preview() { - AppThemeSurface { - Content( - showResetSuggestionsDialog = false, - selectedAddressTypeName = "Taproot", - ) - } -} - -@Preview -@Composable -private fun PreviewDialog() { - AppThemeSurface { - Content( - showResetSuggestionsDialog = true, - selectedAddressTypeName = "Taproot", - ) - } -} diff --git a/app/src/main/java/to/bitkit/ui/settings/AdvancedSettingsViewModel.kt b/app/src/main/java/to/bitkit/ui/settings/AdvancedSettingsViewModel.kt index 3fe3a0ede..5e633dbf5 100644 --- a/app/src/main/java/to/bitkit/ui/settings/AdvancedSettingsViewModel.kt +++ b/app/src/main/java/to/bitkit/ui/settings/AdvancedSettingsViewModel.kt @@ -8,19 +8,54 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import to.bitkit.data.SettingsStore +import to.bitkit.env.Env +import to.bitkit.ext.filterOpen +import to.bitkit.models.ElectrumServer import to.bitkit.models.addressTypeInfo import to.bitkit.models.toAddressType +import to.bitkit.repositories.LightningRepo import javax.inject.Inject +private const val NODE_ID_PREFIX_LENGTH = 5 +private const val ELECTRUM_HOST_PREFIX_LENGTH = 10 + @HiltViewModel class AdvancedSettingsViewModel @Inject constructor( private val settingsStore: SettingsStore, + private val lightningRepo: LightningRepo, ) : ViewModel() { val selectedAddressTypeName = settingsStore.data .map { it.selectedAddressType.toAddressType()?.addressTypeInfo()?.shortName ?: "" } .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), "") + val openChannelCount = lightningRepo.lightningState + .map { it.channels.filterOpen().size } + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), 0) + + val truncatedNodeId = lightningRepo.lightningState + .map { it.nodeId.take(NODE_ID_PREFIX_LENGTH).ifEmpty { "" } } + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), "") + + val electrumHost = settingsStore.data + .map { + if (it.electrumServer == Env.electrumServerUrl) { + "" + } else { + val host = ElectrumServer.parse(it.electrumServer).host + if (host.length > ELECTRUM_HOST_PREFIX_LENGTH) { + "${host.take(ELECTRUM_HOST_PREFIX_LENGTH)}..." + } else { + host + } + } + } + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), "") + + val coinSelectAuto = settingsStore.data + .map { it.coinSelectAuto } + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), true) + fun resetSuggestions() { viewModelScope.launch { settingsStore.update { it.copy(dismissedSuggestions = emptyList()) } diff --git a/app/src/main/java/to/bitkit/ui/settings/BackupSettingsScreen.kt b/app/src/main/java/to/bitkit/ui/settings/BackupSettingsScreen.kt index cd6504d2b..8ea3a7a22 100644 --- a/app/src/main/java/to/bitkit/ui/settings/BackupSettingsScreen.kt +++ b/app/src/main/java/to/bitkit/ui/settings/BackupSettingsScreen.kt @@ -32,23 +32,13 @@ import to.bitkit.env.Env import to.bitkit.ext.toRelativeTimeString import to.bitkit.models.BackupCategory import to.bitkit.models.BackupItemStatus -import to.bitkit.ui.Routes -import to.bitkit.ui.appViewModel import to.bitkit.ui.backupsViewModel -import to.bitkit.ui.components.AuthCheckAction import to.bitkit.ui.components.BodyMSB -import to.bitkit.ui.components.Caption13Up import to.bitkit.ui.components.CaptionB -import to.bitkit.ui.components.FillWidth -import to.bitkit.ui.components.Sheet import to.bitkit.ui.components.VerticalSpacer -import to.bitkit.ui.components.settings.SettingsButtonRow -import to.bitkit.ui.navigateTo -import to.bitkit.ui.navigateToAuthCheck import to.bitkit.ui.scaffold.AppTopBar import to.bitkit.ui.scaffold.DrawerNavIcon import to.bitkit.ui.scaffold.ScreenColumn -import to.bitkit.ui.settingsViewModel import to.bitkit.ui.shared.modifiers.clickableAlpha import to.bitkit.ui.theme.AppThemeSurface import to.bitkit.ui.theme.Colors @@ -61,23 +51,12 @@ import kotlin.time.ExperimentalTime fun BackupSettingsScreen( navController: NavController, ) { - val app = appViewModel ?: return - val settings = settingsViewModel ?: return val viewModel = backupsViewModel ?: return - val isPinEnabled by settings.isPinEnabled.collectAsStateWithLifecycle() val uiState by viewModel.uiState.collectAsStateWithLifecycle() BackupSettingsScreenContent( uiState = uiState, - onBackupClick = { app.showSheet(Sheet.Backup()) }, - onResetAndRestoreClick = { - if (isPinEnabled) { - navController.navigateToAuthCheck(onSuccessActionId = AuthCheckAction.NAV_TO_RESET) - } else { - navController.navigateTo(Routes.ResetAndRestoreSettings) - } - }, onRetryBackup = { category -> viewModel.retryBackup(category) }, onBack = { navController.popBackStack() }, ) @@ -86,15 +65,13 @@ fun BackupSettingsScreen( @Composable private fun BackupSettingsScreenContent( uiState: BackupStatusUiState, - onBackupClick: () -> Unit, - onResetAndRestoreClick: () -> Unit, onRetryBackup: (BackupCategory) -> Unit, onBack: () -> Unit, ) { val allSynced = uiState.categories.all { !it.status.isRequired } ScreenColumn { AppTopBar( - titleText = stringResource(R.string.settings__backup__title), + titleText = stringResource(R.string.settings__backup__data), onBackClick = onBack, actions = { DrawerNavIcon() }, ) @@ -104,38 +81,20 @@ private fun BackupSettingsScreenContent( .verticalScroll(rememberScrollState()) .testTag("BackupScrollView") ) { - SettingsButtonRow( - title = stringResource(R.string.settings__backup__wallet), - onClick = onBackupClick, - modifier = Modifier.testTag("BackupWallet"), - ) - SettingsButtonRow( - title = stringResource(R.string.settings__backup__reset), - onClick = onResetAndRestoreClick, - modifier = Modifier.testTag("ResetAndRestore"), - ) - VerticalSpacer(28.dp) - - Row(verticalAlignment = Alignment.CenterVertically) { - Caption13Up( - text = stringResource(R.string.settings__backup__latest), - color = Colors.White64, + VerticalSpacer(16.dp) + + @Suppress("KotlinConstantConditions", "SimplifyBooleanWithConstants") + if (Env.isE2eTest && allSynced) { + Icon( + painter = painterResource(R.drawable.ic_check_circle), + contentDescription = null, + tint = Colors.Green, + modifier = Modifier + .padding(end = 4.dp) + .size(16.dp) + .testTag("AllSynced") ) - FillWidth() - @Suppress("KotlinConstantConditions", "SimplifyBooleanWithConstants") - if (Env.isE2eTest && allSynced) { - Icon( - painter = painterResource(R.drawable.ic_check_circle), - contentDescription = null, - tint = Colors.Green, - modifier = Modifier - .padding(end = 4.dp) - .size(16.dp) - .testTag("AllSynced") - ) - } } - VerticalSpacer(12.dp) uiState.categories.map { categoryUiState -> BackupStatusItem( @@ -264,8 +223,6 @@ private fun Preview() { AppThemeSurface { BackupSettingsScreenContent( uiState = BackupStatusUiState(categories = categories.toImmutableList()), - onBackupClick = {}, - onResetAndRestoreClick = {}, onRetryBackup = {}, onBack = {}, ) diff --git a/app/src/main/java/to/bitkit/ui/settings/LanguageSettingsScreen.kt b/app/src/main/java/to/bitkit/ui/settings/LanguageSettingsScreen.kt index c4fb3b320..776f4524b 100644 --- a/app/src/main/java/to/bitkit/ui/settings/LanguageSettingsScreen.kt +++ b/app/src/main/java/to/bitkit/ui/settings/LanguageSettingsScreen.kt @@ -68,9 +68,14 @@ private fun Content( Text13Up("Interface Language", color = Colors.White64, modifier = Modifier.padding(vertical = 16.dp)) LazyColumn { - items(uiState.languages, { item -> item.displayName }) { item -> + items(uiState.languages, { it.languageCode + it.countryCode }) { item -> + val title = if (item.displayNameResId != null) { + stringResource(item.displayNameResId) + } else { + item.nativeName.orEmpty() + } SettingsButtonRow( - title = item.displayName, + title = title, value = SettingsButtonValue.BooleanValue(item == uiState.selectedLanguage), onClick = { onClickLanguage(item) } ) diff --git a/app/src/main/java/to/bitkit/ui/settings/SecuritySettingsScreen.kt b/app/src/main/java/to/bitkit/ui/settings/SecuritySettingsScreen.kt deleted file mode 100644 index 510baa93b..000000000 --- a/app/src/main/java/to/bitkit/ui/settings/SecuritySettingsScreen.kt +++ /dev/null @@ -1,225 +0,0 @@ -package to.bitkit.ui.settings - -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.testTag -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import androidx.navigation.NavController -import to.bitkit.R -import to.bitkit.ui.appViewModel -import to.bitkit.ui.components.AuthCheckAction -import to.bitkit.ui.components.BodyS -import to.bitkit.ui.components.Sheet -import to.bitkit.ui.components.settings.SettingsButtonRow -import to.bitkit.ui.components.settings.SettingsButtonValue -import to.bitkit.ui.components.settings.SettingsSwitchRow -import to.bitkit.ui.navigateToAuthCheck -import to.bitkit.ui.navigateToChangePin -import to.bitkit.ui.navigateToDisablePin -import to.bitkit.ui.scaffold.AppTopBar -import to.bitkit.ui.scaffold.DrawerNavIcon -import to.bitkit.ui.scaffold.ScreenColumn -import to.bitkit.ui.settingsViewModel -import to.bitkit.ui.theme.AppThemeSurface -import to.bitkit.ui.theme.Colors -import to.bitkit.ui.utils.rememberBiometricAuthSupported - -@Composable -fun SecuritySettingsScreen( - navController: NavController, -) { - val settings = settingsViewModel ?: return - val app = appViewModel ?: return - - val isPinEnabled by settings.isPinEnabled.collectAsStateWithLifecycle() - val isBiometricEnabled by settings.isBiometricEnabled.collectAsStateWithLifecycle() - val isPinForPaymentsEnabled by settings.isPinForPaymentsEnabled.collectAsStateWithLifecycle() - val enableSwipeToHideBalance by settings.enableSwipeToHideBalance.collectAsStateWithLifecycle() - val hideBalanceOnOpen by settings.hideBalanceOnOpen.collectAsStateWithLifecycle() - val enableAutoReadClipboard by settings.enableAutoReadClipboard.collectAsStateWithLifecycle() - val enableSendAmountWarning by settings.enableSendAmountWarning.collectAsStateWithLifecycle() - - Content( - isPinEnabled = isPinEnabled, - isBiometricEnabled = isBiometricEnabled, - isPinForPaymentsEnabled = isPinForPaymentsEnabled, - enableSwipeToHideBalance = enableSwipeToHideBalance, - hideBalanceOnOpen = hideBalanceOnOpen, - enableAutoReadClipboard = enableAutoReadClipboard, - enableSendAmountWarning = enableSendAmountWarning, - isBiometrySupported = rememberBiometricAuthSupported(), - onPinClick = { - if (!isPinEnabled) { - app.showSheet(Sheet.Pin()) - } else { - navController.navigateToDisablePin() - } - }, - onChangePinClick = { - navController.navigateToChangePin() - }, - onPinForPaymentsClick = { - navController.navigateToAuthCheck( - onSuccessActionId = AuthCheckAction.TOGGLE_PIN_FOR_PAYMENTS, - ) - }, - onUseBiometricsClick = { - navController.navigateToAuthCheck( - requireBiometrics = true, - onSuccessActionId = AuthCheckAction.TOGGLE_BIOMETRICS, - ) - }, - onSwipeToHideBalanceClick = { - settings.setEnableSwipeToHideBalance(!enableSwipeToHideBalance) - }, - onHideBalanceOnOpenClick = { - settings.setHideBalanceOnOpen(!hideBalanceOnOpen) - }, - onAutoReadClipboardClick = { - settings.setEnableAutoReadClipboard(!enableAutoReadClipboard) - }, - onSendAmountWarningClick = { - settings.setEnableSendAmountWarning(!enableSendAmountWarning) - }, - onBackClick = { navController.popBackStack() }, - ) -} - -@Composable -private fun Content( - isPinEnabled: Boolean, - isBiometricEnabled: Boolean, - isPinForPaymentsEnabled: Boolean, - enableSwipeToHideBalance: Boolean, - hideBalanceOnOpen: Boolean, - enableAutoReadClipboard: Boolean, - enableSendAmountWarning: Boolean, - isBiometrySupported: Boolean, - onPinClick: () -> Unit = {}, - onChangePinClick: () -> Unit = {}, - onPinForPaymentsClick: () -> Unit = {}, - onUseBiometricsClick: () -> Unit = {}, - onSwipeToHideBalanceClick: () -> Unit = {}, - onHideBalanceOnOpenClick: () -> Unit = {}, - onAutoReadClipboardClick: () -> Unit = {}, - onSendAmountWarningClick: () -> Unit = {}, - onBackClick: () -> Unit = {}, -) { - ScreenColumn( - modifier = Modifier - .verticalScroll(rememberScrollState()) - ) { - AppTopBar( - titleText = stringResource(R.string.settings__security_title), - onBackClick = onBackClick, - actions = { DrawerNavIcon() }, - ) - Column( - modifier = Modifier.padding(horizontal = 16.dp) - ) { - SettingsSwitchRow( - title = stringResource(R.string.settings__security__swipe_balance_to_hide), - isChecked = enableSwipeToHideBalance, - onClick = onSwipeToHideBalanceClick, - modifier = Modifier.testTag("SwipeBalanceToHide"), - ) - - if (enableSwipeToHideBalance) { - SettingsSwitchRow( - title = stringResource(R.string.settings__security__hide_balance_on_open), - isChecked = hideBalanceOnOpen, - onClick = onHideBalanceOnOpenClick, - modifier = Modifier.testTag("HideBalanceOnOpen"), - ) - } - - SettingsSwitchRow( - title = stringResource(R.string.settings__security__clipboard), - isChecked = enableAutoReadClipboard, - onClick = onAutoReadClipboardClick, - modifier = Modifier.testTag("AutoReadClipboard"), - ) - - SettingsSwitchRow( - title = stringResource(R.string.settings__security__warn_100), - isChecked = enableSendAmountWarning, - onClick = onSendAmountWarningClick, - modifier = Modifier.testTag("SendAmountWarning"), - ) - - SettingsButtonRow( - title = stringResource(R.string.settings__security__pin), - value = SettingsButtonValue.StringValue( - stringResource( - if (isPinEnabled) { - R.string.settings__security__pin_enabled - } else { - R.string.settings__security__pin_disabled - } - ) - ), - onClick = onPinClick, - modifier = Modifier.testTag("PINCode"), - ) - if (isPinEnabled) { - SettingsButtonRow( - title = stringResource(R.string.settings__security__pin_change), - onClick = onChangePinClick, - modifier = Modifier.testTag("PINChange"), - ) - SettingsSwitchRow( - title = stringResource(R.string.settings__security__pin_payments), - isChecked = isPinForPaymentsEnabled, - onClick = onPinForPaymentsClick, - modifier = Modifier.testTag("EnablePinForPayments"), - ) - } - if (isPinEnabled && isBiometrySupported) { - SettingsSwitchRow( - title = run { - val bioTypeName = stringResource(R.string.security__bio) - stringResource(R.string.settings__security__use_bio).replace("{biometryTypeName}", bioTypeName) - }, - isChecked = isBiometricEnabled, - onClick = onUseBiometricsClick, - modifier = Modifier.testTag("UseBiometryInstead"), - ) - } - if (isPinEnabled && isBiometrySupported) { - BodyS( - text = run { - val bioTypeName = stringResource(R.string.security__bio) - stringResource(R.string.settings__security__footer).replace("{biometryTypeName}", bioTypeName) - }, - color = Colors.White64, - modifier = Modifier.padding(vertical = 16.dp) - ) - } - } - } -} - -@Preview -@Composable -private fun Preview() { - AppThemeSurface { - Content( - isPinEnabled = true, - isBiometricEnabled = false, - isPinForPaymentsEnabled = false, - enableSwipeToHideBalance = true, - hideBalanceOnOpen = false, - enableAutoReadClipboard = true, - enableSendAmountWarning = true, - isBiometrySupported = true, - ) - } -} diff --git a/app/src/main/java/to/bitkit/ui/settings/SettingsScreen.kt b/app/src/main/java/to/bitkit/ui/settings/SettingsScreen.kt index 587df3ef9..ba607ab64 100644 --- a/app/src/main/java/to/bitkit/ui/settings/SettingsScreen.kt +++ b/app/src/main/java/to/bitkit/ui/settings/SettingsScreen.kt @@ -1,203 +1,693 @@ package to.bitkit.ui.settings -import androidx.compose.foundation.Image +import androidx.annotation.StringRes import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.pager.HorizontalPager +import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier -import androidx.compose.ui.hapticfeedback.HapticFeedbackType -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalHapticFeedback 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 androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController +import kotlinx.collections.immutable.toImmutableList +import kotlinx.coroutines.launch import to.bitkit.R -import to.bitkit.models.Toast +import to.bitkit.models.PrimaryDisplay +import to.bitkit.models.TransactionSpeed +import to.bitkit.models.transactionSpeedUiText +import to.bitkit.ui.LocalCurrencies import to.bitkit.ui.Routes import to.bitkit.ui.appViewModel +import to.bitkit.ui.components.AuthCheckAction +import to.bitkit.ui.components.Sheet +import to.bitkit.ui.components.VerticalSpacer +import to.bitkit.ui.components.settings.SectionHeader import to.bitkit.ui.components.settings.SettingsButtonRow +import to.bitkit.ui.components.settings.SettingsButtonValue +import to.bitkit.ui.components.settings.SettingsIcon +import to.bitkit.ui.components.settings.SettingsSwitchRow import to.bitkit.ui.navigateTo -import to.bitkit.ui.navigateToAboutSettings -import to.bitkit.ui.navigateToAdvancedSettings -import to.bitkit.ui.navigateToBackupSettings +import to.bitkit.ui.navigateToAuthCheck +import to.bitkit.ui.navigateToDefaultUnitSettings import to.bitkit.ui.navigateToDevSettings -import to.bitkit.ui.navigateToGeneralSettings -import to.bitkit.ui.navigateToSecuritySettings +import to.bitkit.ui.navigateToLanguageSettings +import to.bitkit.ui.navigateToLocalCurrencySettings +import to.bitkit.ui.navigateToPinManagement +import to.bitkit.ui.navigateToQuickPaySettings +import to.bitkit.ui.navigateToTagsSettings +import to.bitkit.ui.navigateToTransactionSpeedSettings +import to.bitkit.ui.navigateToWidgetsSettings import to.bitkit.ui.scaffold.AppTopBar import to.bitkit.ui.scaffold.DrawerNavIcon import to.bitkit.ui.scaffold.ScreenColumn +import to.bitkit.ui.screens.wallets.activity.components.CustomTabRowWithSpacing +import to.bitkit.ui.screens.wallets.activity.components.TabItem import to.bitkit.ui.settingsViewModel -import to.bitkit.ui.shared.modifiers.clickableAlpha import to.bitkit.ui.theme.AppThemeSurface +import to.bitkit.ui.theme.Colors +import to.bitkit.ui.utils.rememberBiometricAuthSupported +import to.bitkit.viewmodels.LanguageViewModel -private const val DEV_MODE_TAP_THRESHOLD = 5 +private enum class SettingsTab(@StringRes private val titleRes: Int) : TabItem { + General(R.string.settings__general_title), + Security(R.string.settings__security_title), + Advanced(R.string.settings__advanced_title); + override val uiText @Composable get() = stringResource(titleRes) +} + +@Suppress("CyclomaticComplexMethod") @Composable fun SettingsScreen( navController: NavController, + advancedViewModel: AdvancedSettingsViewModel = hiltViewModel(), + languageViewModel: LanguageViewModel = hiltViewModel(), ) { val app = appViewModel ?: return val settings = settingsViewModel ?: return + val currencies = LocalCurrencies.current + + // General tab state + val defaultTransactionSpeed by settings.defaultTransactionSpeed.collectAsStateWithLifecycle() + val lastUsedTags by settings.lastUsedTags.collectAsStateWithLifecycle() + val showWidgets by settings.showWidgets.collectAsStateWithLifecycle() + val isQuickPayEnabled by settings.isQuickpayEnabled.collectAsStateWithLifecycle() + val quickPayIntroSeen by settings.quickPayIntroSeen.collectAsStateWithLifecycle() + val bgPaymentsIntroSeen by settings.bgPaymentsIntroSeen.collectAsStateWithLifecycle() + val notificationsGranted by settings.notificationsGranted.collectAsStateWithLifecycle() + val languageUiState by languageViewModel.uiState.collectAsStateWithLifecycle() + + // Security tab state + val isPinEnabled by settings.isPinEnabled.collectAsStateWithLifecycle() + val isBiometricEnabled by settings.isBiometricEnabled.collectAsStateWithLifecycle() + val isPinForPaymentsEnabled by settings.isPinForPaymentsEnabled.collectAsStateWithLifecycle() + val enableSwipeToHideBalance by settings.enableSwipeToHideBalance.collectAsStateWithLifecycle() + val hideBalanceOnOpen by settings.hideBalanceOnOpen.collectAsStateWithLifecycle() + val enableAutoReadClipboard by settings.enableAutoReadClipboard.collectAsStateWithLifecycle() + val enableSendAmountWarning by settings.enableSendAmountWarning.collectAsStateWithLifecycle() + + // Advanced tab state val isDevModeEnabled by settings.isDevModeEnabled.collectAsStateWithLifecycle() - var enableDevModeTapCount by remember { mutableIntStateOf(0) } - val haptic = LocalHapticFeedback.current - val context = LocalContext.current - - SettingsScreenContent( - isDevModeEnabled = isDevModeEnabled, - onGeneralClick = { navController.navigateToGeneralSettings() }, - onSecurityClick = { navController.navigateToSecuritySettings() }, - onBackupClick = { navController.navigateToBackupSettings() }, - onAdvancedClick = { navController.navigateToAdvancedSettings() }, - onSupportClick = { navController.navigateTo(Routes.Support) }, - onAboutClick = { navController.navigateToAboutSettings() }, - onDevClick = { navController.navigateToDevSettings() }, - onBackClick = { navController.popBackStack() }, - onCogTap = { - haptic.performHapticFeedback(HapticFeedbackType.Confirm) - enableDevModeTapCount = enableDevModeTapCount + 1 - - if (enableDevModeTapCount >= DEV_MODE_TAP_THRESHOLD) { - val newValue = !isDevModeEnabled - settings.setIsDevModeEnabled(newValue) - haptic.performHapticFeedback(HapticFeedbackType.LongPress) - - app.toast( - type = Toast.ToastType.SUCCESS, - title = context.getString( - if (newValue) { - R.string.settings__dev_enabled_title - } else { - R.string.settings__dev_disabled_title - } - ), - description = context.getString( - if (newValue) { - R.string.settings__dev_enabled_message - } else { - R.string.settings__dev_disabled_message - } - ), - ) - enableDevModeTapCount = 0 + val selectedAddressTypeName by advancedViewModel.selectedAddressTypeName.collectAsStateWithLifecycle() + val openChannelCount by advancedViewModel.openChannelCount.collectAsStateWithLifecycle() + val truncatedNodeId by advancedViewModel.truncatedNodeId.collectAsStateWithLifecycle() + val electrumHost by advancedViewModel.electrumHost.collectAsStateWithLifecycle() + val coinSelectAuto by advancedViewModel.coinSelectAuto.collectAsStateWithLifecycle() + + LaunchedEffect(Unit) { languageViewModel.fetchLanguageInfo() } + + SettingsContent( + generalState = GeneralTabState( + selectedCurrency = currencies.selectedCurrency, + currencySymbol = currencies.currencySymbol, + primaryDisplay = currencies.primaryDisplay, + defaultTransactionSpeed = defaultTransactionSpeed, + selectedLanguage = languageUiState.selectedLanguage.let { + if (it.displayNameResId != null) stringResource(it.displayNameResId) else it.nativeName.orEmpty() + }, + showWidgets = showWidgets, + tagCount = lastUsedTags.size, + isQuickPayEnabled = isQuickPayEnabled, + notificationsGranted = notificationsGranted, + ), + securityState = SecurityTabState( + isPinEnabled = isPinEnabled, + isBiometricEnabled = isBiometricEnabled, + isPinForPaymentsEnabled = isPinForPaymentsEnabled, + enableSwipeToHideBalance = enableSwipeToHideBalance, + hideBalanceOnOpen = hideBalanceOnOpen, + enableAutoReadClipboard = enableAutoReadClipboard, + enableSendAmountWarning = enableSendAmountWarning, + isBiometrySupported = rememberBiometricAuthSupported(), + ), + advancedState = AdvancedTabState( + isDevModeEnabled = isDevModeEnabled, + selectedAddressTypeName = selectedAddressTypeName, + coinSelectAuto = coinSelectAuto, + openChannelCount = openChannelCount, + truncatedNodeId = truncatedNodeId, + electrumHost = electrumHost, + ), + onEvent = { event -> + when (event) { + SettingsEvent.LanguageClick -> navController.navigateToLanguageSettings() + SettingsEvent.LocalCurrencyClick -> navController.navigateToLocalCurrencySettings() + SettingsEvent.DefaultUnitClick -> navController.navigateToDefaultUnitSettings() + SettingsEvent.WidgetsClick -> navController.navigateToWidgetsSettings() + SettingsEvent.TagsClick -> navController.navigateToTagsSettings() + SettingsEvent.TransactionSpeedClick -> navController.navigateToTransactionSpeedSettings() + SettingsEvent.QuickPayClick -> navController.navigateToQuickPaySettings(quickPayIntroSeen) + SettingsEvent.BgPaymentsClick -> { + if (bgPaymentsIntroSeen || notificationsGranted) { + navController.navigateTo(Routes.BackgroundPaymentsSettings) + } else { + navController.navigateTo(Routes.BackgroundPaymentsIntro) + } + } + SettingsEvent.BackupWalletClick -> { + if (isPinEnabled) { + navController.navigateToAuthCheck( + onSuccessActionId = AuthCheckAction.SHOW_BACKUP_SHEET, + ) + } else { + app.showSheet(Sheet.Backup()) + } + } + SettingsEvent.DataBackupsClick -> navController.navigateTo(Routes.BackupSettings) + SettingsEvent.ResetWalletClick -> + navController.navigateTo(Routes.ResetAndRestoreSettings) + SettingsEvent.PinClick -> navController.navigateToPinManagement() + SettingsEvent.PinForPaymentsClick -> { + navController.navigateToAuthCheck( + onSuccessActionId = AuthCheckAction.TOGGLE_PIN_FOR_PAYMENTS, + ) + } + SettingsEvent.UseBiometricsClick -> { + navController.navigateToAuthCheck( + requireBiometrics = true, + onSuccessActionId = AuthCheckAction.TOGGLE_BIOMETRICS, + ) + } + SettingsEvent.SwipeToHideBalanceClick -> settings.setEnableSwipeToHideBalance(!enableSwipeToHideBalance) + SettingsEvent.HideBalanceOnOpenClick -> settings.setHideBalanceOnOpen(!hideBalanceOnOpen) + SettingsEvent.AutoReadClipboardClick -> settings.setEnableAutoReadClipboard(!enableAutoReadClipboard) + SettingsEvent.SendAmountWarningClick -> settings.setEnableSendAmountWarning(!enableSendAmountWarning) + SettingsEvent.DevSettingsClick -> navController.navigateToDevSettings() + SettingsEvent.AddressTypeClick -> navController.navigateTo(Routes.AddressTypePreference) + SettingsEvent.CoinSelectionClick -> navController.navigateTo(Routes.CoinSelectPreference) + SettingsEvent.AddressViewerClick -> navController.navigateTo(Routes.AddressViewer) + SettingsEvent.LightningConnectionsClick -> navController.navigateTo(Routes.LightningConnections) + SettingsEvent.LightningNodeClick -> navController.navigateTo(Routes.NodeInfo) + SettingsEvent.ElectrumServerClick -> navController.navigateTo(Routes.ElectrumConfig) + SettingsEvent.RgsServerClick -> navController.navigateTo(Routes.RgsServer) + SettingsEvent.BackClick -> navController.popBackStack() } }, ) } @Composable -fun SettingsScreenContent( - isDevModeEnabled: Boolean, - onGeneralClick: () -> Unit, - onSecurityClick: () -> Unit, - onBackupClick: () -> Unit, - onAdvancedClick: () -> Unit, - onSupportClick: () -> Unit, - onAboutClick: () -> Unit, - onDevClick: () -> Unit, - onCogTap: () -> Unit, - onBackClick: () -> Unit, +private fun SettingsContent( + generalState: GeneralTabState = GeneralTabState(), + securityState: SecurityTabState = SecurityTabState(), + advancedState: AdvancedTabState = AdvancedTabState(), + onEvent: OnSettingsEvent = {}, + initialTab: SettingsTab = SettingsTab.General, ) { + val tabs = remember { SettingsTab.entries.toImmutableList() } + val pagerState = rememberPagerState( + initialPage = tabs.indexOf(initialTab), + pageCount = { tabs.size }, + ) + val scope = rememberCoroutineScope() + ScreenColumn { AppTopBar( titleText = stringResource(R.string.settings__settings), - onBackClick = onBackClick, + onBackClick = { onEvent(SettingsEvent.BackClick) }, actions = { DrawerNavIcon() }, ) - Column( - modifier = Modifier - .padding(horizontal = 16.dp) - .fillMaxSize() - .verticalScroll(rememberScrollState()) - ) { - SettingsButtonRow( - title = stringResource(R.string.settings__general_title), - iconRes = R.drawable.ic_settings_general, - onClick = onGeneralClick, - modifier = Modifier.testTag("GeneralSettings") - ) - SettingsButtonRow( - title = stringResource(R.string.settings__security_title), - iconRes = R.drawable.ic_settings_security, - onClick = onSecurityClick, - modifier = Modifier.testTag("SecuritySettings") - ) - SettingsButtonRow( - title = stringResource(R.string.settings__backup_title), - iconRes = R.drawable.ic_settings_backup, - onClick = onBackupClick, - modifier = Modifier.testTag("BackupSettings") - ) - SettingsButtonRow( - title = stringResource(R.string.settings__advanced_title), - iconRes = R.drawable.ic_settings_advanced, - onClick = onAdvancedClick, - modifier = Modifier.testTag("AdvancedSettings") - ) + + CustomTabRowWithSpacing( + tabs = tabs, + currentTabIndex = pagerState.currentPage, + selectedColor = Colors.White, + onTabChange = { scope.launch { pagerState.animateScrollToPage(tabs.indexOf(it)) } }, + modifier = Modifier.padding(horizontal = 16.dp) + ) + + HorizontalPager(state = pagerState) { page -> + when (tabs[page]) { + SettingsTab.General -> GeneralTabContent( + state = generalState, + onEvent = onEvent, + ) + + SettingsTab.Security -> SecurityTabContent( + state = securityState, + onEvent = onEvent, + ) + + SettingsTab.Advanced -> AdvancedTabContent( + state = advancedState, + onEvent = onEvent, + ) + } + } + } +} + +@Composable +private fun GeneralTabContent( + state: GeneralTabState, + onEvent: OnSettingsEvent, +) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 16.dp) + .verticalScroll(rememberScrollState()) + ) { + SectionHeader(title = stringResource(R.string.settings__general__section_interface)) + + SettingsButtonRow( + title = stringResource(R.string.settings__language_title), + icon = { SettingsIcon(R.drawable.ic_translate) }, + value = SettingsButtonValue.StringValue(state.selectedLanguage), + onClick = { onEvent(SettingsEvent.LanguageClick) }, + modifier = Modifier.testTag("LanguageSettings") + ) + SettingsButtonRow( + title = stringResource(R.string.settings__general__currency_local), + icon = { SettingsIcon(R.drawable.ic_coins) }, + value = SettingsButtonValue.StringValue("${state.selectedCurrency} (${state.currencySymbol})"), + onClick = { onEvent(SettingsEvent.LocalCurrencyClick) }, + modifier = Modifier.testTag("CurrenciesSettings") + ) + SettingsButtonRow( + title = stringResource(R.string.settings__general__unit), + icon = { SettingsIcon(R.drawable.ic_bitcoin_modern) }, + value = SettingsButtonValue.StringValue( + when (state.primaryDisplay) { + PrimaryDisplay.BITCOIN -> stringResource(R.string.settings__general__unit_bitcoin) + PrimaryDisplay.FIAT -> state.selectedCurrency + } + ), + onClick = { onEvent(SettingsEvent.DefaultUnitClick) }, + modifier = Modifier.testTag("UnitSettings") + ) + SettingsButtonRow( + title = stringResource(R.string.settings__widgets__nav_title), + icon = { SettingsIcon(R.drawable.ic_stack) }, + value = SettingsButtonValue.StringValue( + stringResource(if (state.showWidgets) R.string.settings__bg__on else R.string.settings__bg__off) + ), + onClick = { onEvent(SettingsEvent.WidgetsClick) }, + modifier = Modifier.testTag("WidgetsSettings") + ) + if (state.tagCount > 0) { SettingsButtonRow( - title = stringResource(R.string.settings__support_title), - iconRes = R.drawable.ic_settings_support, - onClick = onSupportClick, - modifier = Modifier.testTag("Support") + title = stringResource(R.string.settings__general__tags), + icon = { SettingsIcon(R.drawable.ic_tag) }, + value = SettingsButtonValue.StringValue(state.tagCount.toString()), + onClick = { onEvent(SettingsEvent.TagsClick) }, + modifier = Modifier.testTag("TagsSettings") ) - SettingsButtonRow( - title = stringResource(R.string.settings__about_title), - iconRes = R.drawable.ic_settings_about, - onClick = onAboutClick, - modifier = Modifier.testTag("About") + } + + SectionHeader( + title = stringResource(R.string.settings__general__section_payments), + padding = PaddingValues(top = 16.dp), + ) + + SettingsButtonRow( + title = stringResource(R.string.settings__general__speed), + icon = { + SettingsIcon( + when (state.defaultTransactionSpeed) { + is TransactionSpeed.Fast -> R.drawable.ic_speed_fast + is TransactionSpeed.Slow -> R.drawable.ic_speed_slow + else -> R.drawable.ic_speed_normal + } + ) + }, + value = SettingsButtonValue.StringValue(state.defaultTransactionSpeed.transactionSpeedUiText()), + onClick = { onEvent(SettingsEvent.TransactionSpeedClick) }, + modifier = Modifier.testTag("TransactionSpeedSettings") + ) + SettingsButtonRow( + title = stringResource(R.string.settings__quickpay__nav_title), + icon = { SettingsIcon(R.drawable.ic_caret_double_right) }, + value = SettingsButtonValue.StringValue( + stringResource(if (state.isQuickPayEnabled) R.string.settings__bg__on else R.string.settings__bg__off) + ), + onClick = { onEvent(SettingsEvent.QuickPayClick) }, + modifier = Modifier.testTag("QuickpaySettings") + ) + SettingsButtonRow( + title = stringResource(R.string.settings__bg__title), + icon = { SettingsIcon(R.drawable.ic_bell) }, + value = SettingsButtonValue.StringValue( + stringResource( + if (state.notificationsGranted) R.string.settings__bg__on else R.string.settings__bg__off + ) + ), + onClick = { onEvent(SettingsEvent.BgPaymentsClick) }, + modifier = Modifier.testTag("BackgroundPaymentSettings") + ) + + VerticalSpacer(32.dp) + } +} + +@Composable +private fun SecurityTabContent( + state: SecurityTabState, + onEvent: OnSettingsEvent, +) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 16.dp) + .verticalScroll(rememberScrollState()) + ) { + SectionHeader(title = stringResource(R.string.settings__security__section_backup)) + + SettingsButtonRow( + title = stringResource(R.string.settings__backup__wallet), + icon = { SettingsIcon(R.drawable.ic_lock_key) }, + onClick = { onEvent(SettingsEvent.BackupWalletClick) }, + modifier = Modifier.testTag("BackupWallet") + ) + SettingsButtonRow( + title = stringResource(R.string.settings__backup__data), + icon = { SettingsIcon(R.drawable.ic_database) }, + onClick = { onEvent(SettingsEvent.DataBackupsClick) }, + modifier = Modifier.testTag("BackupSettings") + ) + SettingsButtonRow( + title = stringResource(R.string.settings__backup__reset), + icon = { SettingsIcon(R.drawable.ic_arrow_counter_clockwise) }, + onClick = { onEvent(SettingsEvent.ResetWalletClick) }, + modifier = Modifier.testTag("ResetAndRestore") + ) + + SectionHeader( + title = stringResource(R.string.settings__security__section_safety), + padding = PaddingValues(top = 16.dp) + ) + + SettingsButtonRow( + title = stringResource(R.string.settings__security__pin), + icon = { SettingsIcon(R.drawable.ic_shield) }, + value = SettingsButtonValue.StringValue( + stringResource( + if (state.isPinEnabled) { + R.string.settings__security__pin_enabled + } else { + R.string.settings__security__pin_disabled + } + ) + ), + onClick = { onEvent(SettingsEvent.PinClick) }, + modifier = Modifier.testTag("PINCode") + ) + + if (state.isPinEnabled) { + SettingsSwitchRow( + title = stringResource(R.string.settings__security__pin_payments), + icon = { SettingsIcon(R.drawable.ic_coins) }, + isChecked = state.isPinForPaymentsEnabled, + onClick = { onEvent(SettingsEvent.PinForPaymentsClick) }, + modifier = Modifier.testTag("EnablePinForPayments") ) - if (isDevModeEnabled) { - SettingsButtonRow( - title = stringResource(R.string.settings__dev_title), - iconRes = R.drawable.ic_settings_dev, - onClick = onDevClick, - modifier = Modifier.testTag("DevSettings") + + if (state.isBiometrySupported) { + SettingsSwitchRow( + title = run { + val bioTypeName = stringResource(R.string.security__bio) + stringResource(R.string.settings__security__use_bio) + .replace("{biometryTypeName}", bioTypeName) + }, + icon = { SettingsIcon(R.drawable.ic_smiley) }, + isChecked = state.isBiometricEnabled, + onClick = { onEvent(SettingsEvent.UseBiometricsClick) }, + modifier = Modifier.testTag("UseBiometryInstead") ) } - Spacer(Modifier.weight(1f)) - Image( - painter = painterResource(R.drawable.cog), - contentDescription = null, - modifier = Modifier - .fillMaxWidth() - .height(256.dp) - .clickableAlpha(1f) { onCogTap() } - .testTag("DevOptions") + } + + SettingsSwitchRow( + title = stringResource(R.string.settings__security__warn_100), + icon = { SettingsIcon(R.drawable.ic_warning) }, + isChecked = state.enableSendAmountWarning, + onClick = { onEvent(SettingsEvent.SendAmountWarningClick) }, + modifier = Modifier.testTag("SendAmountWarning") + ) + + SectionHeader( + title = stringResource(R.string.settings__security__section_privacy), + padding = PaddingValues(top = 16.dp) + ) + + SettingsSwitchRow( + title = stringResource(R.string.settings__security__swipe_balance_to_hide), + icon = { SettingsIcon(R.drawable.ic_hand_pointing) }, + isChecked = state.enableSwipeToHideBalance, + onClick = { onEvent(SettingsEvent.SwipeToHideBalanceClick) }, + modifier = Modifier.testTag("SwipeBalanceToHide") + ) + SettingsSwitchRow( + title = stringResource(R.string.settings__security__hide_balance_on_open), + icon = { SettingsIcon(R.drawable.ic_eye_slash) }, + isChecked = state.hideBalanceOnOpen, + onClick = { onEvent(SettingsEvent.HideBalanceOnOpenClick) }, + modifier = Modifier.testTag("HideBalanceOnOpen") + ) + SettingsSwitchRow( + title = stringResource(R.string.settings__security__clipboard), + icon = { SettingsIcon(R.drawable.ic_clipboard_text) }, + isChecked = state.enableAutoReadClipboard, + onClick = { onEvent(SettingsEvent.AutoReadClipboardClick) }, + modifier = Modifier.testTag("AutoReadClipboard") + ) + + VerticalSpacer(32.dp) + } +} + +@Composable +private fun AdvancedTabContent( + state: AdvancedTabState, + onEvent: OnSettingsEvent, +) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 16.dp) + .verticalScroll(rememberScrollState()) + .testTag("advanced_settings_screen") + ) { + if (state.isDevModeEnabled) { + SectionHeader(title = stringResource(R.string.settings__adv__section_debug)) + + SettingsButtonRow( + title = stringResource(R.string.settings__dev_title), + icon = { SettingsIcon(R.drawable.ic_settings_dev) }, + onClick = { onEvent(SettingsEvent.DevSettingsClick) }, + modifier = Modifier.testTag("DevSettings") ) - Spacer(Modifier.weight(1f)) } + + SectionHeader(title = stringResource(R.string.settings__adv__section_payments)) + + SettingsButtonRow( + title = stringResource(R.string.settings__addr_type__title), + icon = { SettingsIcon(R.drawable.ic_list_dashes) }, + value = if (state.selectedAddressTypeName.isNotEmpty()) { + SettingsButtonValue.StringValue(state.selectedAddressTypeName) + } else { + SettingsButtonValue.None + }, + onClick = { onEvent(SettingsEvent.AddressTypeClick) }, + modifier = Modifier.testTag("AddressTypePreference") + ) + SettingsButtonRow( + title = stringResource(R.string.settings__adv__coin_selection), + icon = { SettingsIcon(R.drawable.ic_coins) }, + value = SettingsButtonValue.StringValue( + stringResource( + if (state.coinSelectAuto) R.string.settings__adv__cs_auto else R.string.settings__adv__cs_manual + ) + ), + onClick = { onEvent(SettingsEvent.CoinSelectionClick) }, + modifier = Modifier.testTag("CoinSelectPreference") + ) + SettingsButtonRow( + title = stringResource(R.string.settings__adv__address_viewer), + icon = { SettingsIcon(R.drawable.ic_eye) }, + onClick = { onEvent(SettingsEvent.AddressViewerClick) }, + modifier = Modifier.testTag("AddressViewer") + ) + + SectionHeader( + title = stringResource(R.string.settings__adv__section_networks), + padding = PaddingValues(top = 16.dp) + ) + + SettingsButtonRow( + title = stringResource(R.string.settings__adv__lightning_connections), + icon = { SettingsIcon(R.drawable.ic_lightning) }, + value = if (state.openChannelCount > 0) { + SettingsButtonValue.StringValue(state.openChannelCount.toString()) + } else { + SettingsButtonValue.None + }, + onClick = { onEvent(SettingsEvent.LightningConnectionsClick) }, + modifier = Modifier.testTag("Channels") + ) + SettingsButtonRow( + title = stringResource(R.string.settings__adv__lightning_node), + icon = { SettingsIcon(R.drawable.ic_git_branch) }, + value = if (state.truncatedNodeId.isNotEmpty()) { + SettingsButtonValue.StringValue("${state.truncatedNodeId}...") + } else { + SettingsButtonValue.None + }, + onClick = { onEvent(SettingsEvent.LightningNodeClick) }, + modifier = Modifier.testTag("LightningNodeInfo") + ) + SettingsButtonRow( + title = stringResource(R.string.settings__adv__electrum_server), + icon = { SettingsIcon(R.drawable.ic_hard_drives) }, + value = SettingsButtonValue.StringValue( + state.electrumHost.ifEmpty { stringResource(R.string.settings__adv__electrum_auto) } + ), + onClick = { onEvent(SettingsEvent.ElectrumServerClick) }, + modifier = Modifier.testTag("ElectrumConfig") + ) + SettingsButtonRow( + title = stringResource(R.string.settings__adv__rgs_server), + icon = { SettingsIcon(R.drawable.ic_broadcast) }, + onClick = { onEvent(SettingsEvent.RgsServerClick) }, + modifier = Modifier.testTag("RGSServer") + ) + + VerticalSpacer(32.dp) } } -@Preview(showBackground = true) +@Preview(showSystemUi = true) @Composable -private fun Preview() { +private fun PreviewGeneral() { AppThemeSurface { - SettingsScreenContent( - isDevModeEnabled = true, - onGeneralClick = {}, - onSecurityClick = {}, - onBackupClick = {}, - onAdvancedClick = {}, - onSupportClick = {}, - onAboutClick = {}, - onDevClick = {}, - onCogTap = {}, - onBackClick = {}, + SettingsContent( + generalState = GeneralTabState( + selectedLanguage = "System Settings", + tagCount = 8, + isQuickPayEnabled = true, + notificationsGranted = true, + ), ) } } + +@Preview(showSystemUi = true) +@Composable +private fun PreviewSecurity() { + AppThemeSurface { + SettingsContent( + securityState = SecurityTabState( + isPinEnabled = true, + isPinForPaymentsEnabled = true, + enableSwipeToHideBalance = true, + isBiometrySupported = true, + ), + initialTab = SettingsTab.Security, + ) + } +} + +@Preview(showSystemUi = true) +@Composable +private fun PreviewAdvanced() { + AppThemeSurface { + SettingsContent( + advancedState = AdvancedTabState( + isDevModeEnabled = true, + selectedAddressTypeName = "Taproot", + openChannelCount = 2, + truncatedNodeId = "34sdx", + ), + initialTab = SettingsTab.Advanced, + ) + } +} + +sealed interface SettingsEvent { + // General + data object LanguageClick : SettingsEvent + data object LocalCurrencyClick : SettingsEvent + data object DefaultUnitClick : SettingsEvent + data object WidgetsClick : SettingsEvent + data object TagsClick : SettingsEvent + data object TransactionSpeedClick : SettingsEvent + data object QuickPayClick : SettingsEvent + data object BgPaymentsClick : SettingsEvent + + // Security + data object BackupWalletClick : SettingsEvent + data object DataBackupsClick : SettingsEvent + data object ResetWalletClick : SettingsEvent + data object PinClick : SettingsEvent + data object PinForPaymentsClick : SettingsEvent + data object UseBiometricsClick : SettingsEvent + data object SwipeToHideBalanceClick : SettingsEvent + data object HideBalanceOnOpenClick : SettingsEvent + data object AutoReadClipboardClick : SettingsEvent + data object SendAmountWarningClick : SettingsEvent + + // Advanced + data object DevSettingsClick : SettingsEvent + data object AddressTypeClick : SettingsEvent + data object CoinSelectionClick : SettingsEvent + data object AddressViewerClick : SettingsEvent + data object LightningConnectionsClick : SettingsEvent + data object LightningNodeClick : SettingsEvent + data object ElectrumServerClick : SettingsEvent + data object RgsServerClick : SettingsEvent + + // Navigation + data object BackClick : SettingsEvent +} + +typealias OnSettingsEvent = (SettingsEvent) -> Unit + +@Immutable +data class GeneralTabState( + val selectedCurrency: String = "USD", + val currencySymbol: String = "$", + val primaryDisplay: PrimaryDisplay = PrimaryDisplay.BITCOIN, + val defaultTransactionSpeed: TransactionSpeed = TransactionSpeed.Medium, + val selectedLanguage: String = "", + val showWidgets: Boolean = true, + val tagCount: Int = 0, + val isQuickPayEnabled: Boolean = false, + val notificationsGranted: Boolean = false, +) + +@Immutable +data class SecurityTabState( + val isPinEnabled: Boolean = false, + val isBiometricEnabled: Boolean = false, + val isPinForPaymentsEnabled: Boolean = false, + val enableSwipeToHideBalance: Boolean = false, + val hideBalanceOnOpen: Boolean = false, + val enableAutoReadClipboard: Boolean = true, + val enableSendAmountWarning: Boolean = true, + val isBiometrySupported: Boolean = false, +) + +@Immutable +data class AdvancedTabState( + val isDevModeEnabled: Boolean = false, + val selectedAddressTypeName: String = "", + val coinSelectAuto: Boolean = true, + val openChannelCount: Int = 0, + val truncatedNodeId: String = "", + val electrumHost: String = "", +) diff --git a/app/src/main/java/to/bitkit/ui/settings/backups/ResetAndRestoreScreen.kt b/app/src/main/java/to/bitkit/ui/settings/backups/ResetAndRestoreScreen.kt index dedea0a92..9b2ff4f38 100644 --- a/app/src/main/java/to/bitkit/ui/settings/backups/ResetAndRestoreScreen.kt +++ b/app/src/main/java/to/bitkit/ui/settings/backups/ResetAndRestoreScreen.kt @@ -23,17 +23,21 @@ 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 androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController import to.bitkit.R import to.bitkit.ui.appViewModel +import to.bitkit.ui.components.AuthCheckAction import to.bitkit.ui.components.BodyM import to.bitkit.ui.components.PrimaryButton import to.bitkit.ui.components.SecondaryButton import to.bitkit.ui.components.Sheet +import to.bitkit.ui.navigateToAuthCheck import to.bitkit.ui.scaffold.AppAlertDialog import to.bitkit.ui.scaffold.AppTopBar import to.bitkit.ui.scaffold.DrawerNavIcon import to.bitkit.ui.scaffold.ScreenColumn +import to.bitkit.ui.settingsViewModel import to.bitkit.ui.theme.AppThemeSurface import to.bitkit.ui.theme.Colors import to.bitkit.ui.walletViewModel @@ -44,11 +48,21 @@ fun ResetAndRestoreScreen( ) { val app = appViewModel ?: return val wallet = walletViewModel ?: return + val settings = settingsViewModel ?: return + val isPinEnabled by settings.isPinEnabled.collectAsStateWithLifecycle() var showDialog by remember { mutableStateOf(false) } Content( showConfirmDialog = showDialog, - onClickBackup = { app.showSheet(Sheet.Backup()) }, + onClickBackup = { + if (isPinEnabled) { + navController.navigateToAuthCheck( + onSuccessActionId = AuthCheckAction.SHOW_BACKUP_SHEET, + ) + } else { + app.showSheet(Sheet.Backup()) + } + }, onClickReset = { showDialog = true }, onResetConfirm = { wallet.wipeWallet() }, onResetDismiss = { showDialog = false }, @@ -134,7 +148,7 @@ object ResetAndRestoreTestTags { const val SCREEN = "restore_screen" const val BACKUP_BUTTON = "restore_backup_button" const val RESET_BUTTON = "restore_reset_button" - const val RESET_DIALOG = "restore_reset_button" + const val RESET_DIALOG = "restore_reset_dialog" } @Preview(showSystemUi = true) diff --git a/app/src/main/java/to/bitkit/ui/settings/backups/ShowMnemonicScreen.kt b/app/src/main/java/to/bitkit/ui/settings/backups/ShowMnemonicScreen.kt index 0505291d1..822e12487 100644 --- a/app/src/main/java/to/bitkit/ui/settings/backups/ShowMnemonicScreen.kt +++ b/app/src/main/java/to/bitkit/ui/settings/backups/ShowMnemonicScreen.kt @@ -34,6 +34,8 @@ import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import kotlinx.collections.immutable.toImmutableList @@ -192,10 +194,23 @@ private fun ShowMnemonicContent( Spacer(modifier = Modifier.height(32.dp)) - BodyS( - text = stringResource(R.string.security__mnemonic_never_share).withAccent(accentColor = Colors.Brand), - color = Colors.White64, - ) + AnimatedContent( + targetState = showMnemonic, + transitionSpec = { fadeIn() togetherWith fadeOut() }, + label = "warningText" + ) { revealed -> + val warningText = stringResource( + if (revealed) { + R.string.security__mnemonic_no_access + } else { + R.string.security__mnemonic_never_share + } + ).withAccent( + defaultColor = Colors.Brand, + accentStyle = SpanStyle(color = Colors.Brand, fontWeight = FontWeight.Bold), + ) + BodyS(text = warningText) + } Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.height(24.dp)) diff --git a/app/src/main/java/to/bitkit/ui/settings/backups/ShowPassphraseScreen.kt b/app/src/main/java/to/bitkit/ui/settings/backups/ShowPassphraseScreen.kt index eb9bba142..cdc0f6c24 100644 --- a/app/src/main/java/to/bitkit/ui/settings/backups/ShowPassphraseScreen.kt +++ b/app/src/main/java/to/bitkit/ui/settings/backups/ShowPassphraseScreen.kt @@ -15,6 +15,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import to.bitkit.R @@ -96,7 +98,10 @@ private fun ShowPassphraseContent( Spacer(modifier = Modifier.height(32.dp)) BodyS( - text = stringResource(R.string.security__pass_never_share).withAccent(accentColor = Colors.Brand), + text = stringResource(R.string.security__pass_never_share).withAccent( + defaultColor = Colors.Brand, + accentStyle = SpanStyle(color = Colors.Brand, fontWeight = FontWeight.Bold), + ), color = Colors.White64, ) diff --git a/app/src/main/java/to/bitkit/ui/settings/general/DefaultUnitSettingsScreen.kt b/app/src/main/java/to/bitkit/ui/settings/general/DefaultUnitSettingsScreen.kt index 95893d2f7..716d4e09a 100644 --- a/app/src/main/java/to/bitkit/ui/settings/general/DefaultUnitSettingsScreen.kt +++ b/app/src/main/java/to/bitkit/ui/settings/general/DefaultUnitSettingsScreen.kt @@ -1,8 +1,6 @@ package to.bitkit.ui.settings.general import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll @@ -18,10 +16,12 @@ import to.bitkit.models.BitcoinDisplayUnit import to.bitkit.models.PrimaryDisplay import to.bitkit.repositories.CurrencyState import to.bitkit.ui.LocalCurrencies +import to.bitkit.ui.components.VerticalSpacer import to.bitkit.ui.components.settings.SectionFooter import to.bitkit.ui.components.settings.SectionHeader import to.bitkit.ui.components.settings.SettingsButtonRow import to.bitkit.ui.components.settings.SettingsButtonValue +import to.bitkit.ui.components.settings.SettingsIcon import to.bitkit.ui.scaffold.AppTopBar import to.bitkit.ui.scaffold.DrawerNavIcon import to.bitkit.ui.scaffold.ScreenColumn @@ -73,7 +73,7 @@ fun DefaultUnitSettingsScreenContent( SettingsButtonRow( title = stringResource(R.string.settings__general__unit_bitcoin), - iconRes = R.drawable.ic_unit_bitcoin, + icon = { SettingsIcon(R.drawable.ic_bitcoin_modern) }, value = SettingsButtonValue.BooleanValue(primaryDisplay == PrimaryDisplay.BITCOIN), onClick = { onPrimaryUnitClick(PrimaryDisplay.BITCOIN) }, modifier = Modifier.testTag(stringResource(R.string.settings__general__unit_bitcoin)) @@ -81,7 +81,7 @@ fun DefaultUnitSettingsScreenContent( SettingsButtonRow( title = selectedCurrency, - iconRes = R.drawable.ic_unit_fiat, + icon = { SettingsIcon(R.drawable.ic_unit_fiat) }, value = SettingsButtonValue.BooleanValue(primaryDisplay == PrimaryDisplay.FIAT), onClick = { onPrimaryUnitClick(PrimaryDisplay.FIAT) }, modifier = Modifier.testTag(selectedCurrency) @@ -106,7 +106,7 @@ fun DefaultUnitSettingsScreenContent( ) } - Spacer(modifier = Modifier.height(16.dp)) + VerticalSpacer(16.dp) } } } diff --git a/app/src/main/java/to/bitkit/ui/settings/general/GeneralSettingsScreen.kt b/app/src/main/java/to/bitkit/ui/settings/general/GeneralSettingsScreen.kt deleted file mode 100644 index 1bafb0693..000000000 --- a/app/src/main/java/to/bitkit/ui/settings/general/GeneralSettingsScreen.kt +++ /dev/null @@ -1,182 +0,0 @@ -package to.bitkit.ui.settings.general - -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.testTag -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import androidx.navigation.NavController -import to.bitkit.R -import to.bitkit.models.Language -import to.bitkit.models.PrimaryDisplay -import to.bitkit.models.TransactionSpeed -import to.bitkit.models.transactionSpeedUiText -import to.bitkit.ui.LocalCurrencies -import to.bitkit.ui.Routes -import to.bitkit.ui.components.settings.SettingsButtonRow -import to.bitkit.ui.components.settings.SettingsButtonValue -import to.bitkit.ui.navigateTo -import to.bitkit.ui.navigateToDefaultUnitSettings -import to.bitkit.ui.navigateToLanguageSettings -import to.bitkit.ui.navigateToLocalCurrencySettings -import to.bitkit.ui.navigateToQuickPaySettings -import to.bitkit.ui.navigateToTagsSettings -import to.bitkit.ui.navigateToTransactionSpeedSettings -import to.bitkit.ui.navigateToWidgetsSettings -import to.bitkit.ui.scaffold.AppTopBar -import to.bitkit.ui.scaffold.DrawerNavIcon -import to.bitkit.ui.scaffold.ScreenColumn -import to.bitkit.ui.settingsViewModel -import to.bitkit.ui.theme.AppThemeSurface -import to.bitkit.viewmodels.LanguageViewModel - -@Composable -fun GeneralSettingsScreen( - navController: NavController, - languageViewModel: LanguageViewModel = hiltViewModel(), -) { - val settings = settingsViewModel ?: return - val currencies = LocalCurrencies.current - val defaultTransactionSpeed by settings.defaultTransactionSpeed.collectAsStateWithLifecycle() - val lastUsedTags by settings.lastUsedTags.collectAsStateWithLifecycle() - val quickPayIntroSeen by settings.quickPayIntroSeen.collectAsStateWithLifecycle() - val bgPaymentsIntroSeen by settings.bgPaymentsIntroSeen.collectAsStateWithLifecycle() - val notificationsGranted by settings.notificationsGranted.collectAsStateWithLifecycle() - val languageUiState by languageViewModel.uiState.collectAsStateWithLifecycle() - - LaunchedEffect(Unit) { languageViewModel.fetchLanguageInfo() } - - GeneralSettingsContent( - selectedCurrency = currencies.selectedCurrency, - primaryDisplay = currencies.primaryDisplay, - defaultTransactionSpeed = defaultTransactionSpeed, - showTagsButton = lastUsedTags.isNotEmpty(), - onBackClick = { navController.popBackStack() }, - onLocalCurrencyClick = { navController.navigateToLocalCurrencySettings() }, - onDefaultUnitClick = { navController.navigateToDefaultUnitSettings() }, - onTransactionSpeedClick = { navController.navigateToTransactionSpeedSettings() }, - onWidgetsClick = { navController.navigateToWidgetsSettings() }, - onQuickPayClick = { navController.navigateToQuickPaySettings(quickPayIntroSeen) }, - onTagsClick = { navController.navigateToTagsSettings() }, - onLanguageSettingsClick = { navController.navigateToLanguageSettings() }, - onBgPaymentsClick = { - if (bgPaymentsIntroSeen || notificationsGranted) { - navController.navigateTo(Routes.BackgroundPaymentsSettings) - } else { - navController.navigateTo(Routes.BackgroundPaymentsIntro) - } - }, - selectedLanguage = languageUiState.selectedLanguage.displayName, - notificationsGranted = notificationsGranted - ) -} - -@Composable -private fun GeneralSettingsContent( - selectedCurrency: String, - primaryDisplay: PrimaryDisplay, - defaultTransactionSpeed: TransactionSpeed, - selectedLanguage: String, - notificationsGranted: Boolean, - showTagsButton: Boolean = false, - onBackClick: () -> Unit = {}, - onLocalCurrencyClick: () -> Unit = {}, - onDefaultUnitClick: () -> Unit = {}, - onTransactionSpeedClick: () -> Unit = {}, - onWidgetsClick: () -> Unit = {}, - onQuickPayClick: () -> Unit = {}, - onLanguageSettingsClick: () -> Unit = {}, - onTagsClick: () -> Unit = {}, - onBgPaymentsClick: () -> Unit = {}, -) { - ScreenColumn { - AppTopBar( - titleText = stringResource(R.string.settings__general_title), - onBackClick = onBackClick, - actions = { DrawerNavIcon() }, - ) - Column( - modifier = Modifier - .padding(horizontal = 16.dp) - .verticalScroll(rememberScrollState()) - ) { - SettingsButtonRow( - title = stringResource(R.string.settings__language_title), - value = SettingsButtonValue.StringValue(selectedLanguage), - onClick = onLanguageSettingsClick, - modifier = Modifier.testTag("LanguageSettings") - ) - SettingsButtonRow( - title = stringResource(R.string.settings__general__currency_local), - value = SettingsButtonValue.StringValue(selectedCurrency), - onClick = onLocalCurrencyClick, - modifier = Modifier.testTag("CurrenciesSettings") - ) - SettingsButtonRow( - title = stringResource(R.string.settings__general__unit), - value = SettingsButtonValue.StringValue( - when (primaryDisplay) { - PrimaryDisplay.BITCOIN -> stringResource(R.string.settings__general__unit_bitcoin) - PrimaryDisplay.FIAT -> selectedCurrency - } - ), - onClick = onDefaultUnitClick, - modifier = Modifier.testTag("UnitSettings") - ) - SettingsButtonRow( - title = stringResource(R.string.settings__general__speed), - value = SettingsButtonValue.StringValue(defaultTransactionSpeed.transactionSpeedUiText()), - onClick = onTransactionSpeedClick, - modifier = Modifier.testTag("TransactionSpeedSettings") - ) - if (showTagsButton) { - SettingsButtonRow( - title = stringResource(R.string.settings__general__tags), - onClick = onTagsClick, - modifier = Modifier.testTag("TagsSettings") - ) - } - SettingsButtonRow( - title = stringResource(R.string.settings__widgets__nav_title), - onClick = onWidgetsClick, - modifier = Modifier.testTag("WidgetsSettings") - ) - SettingsButtonRow( - title = stringResource(R.string.settings__quickpay__nav_title), - onClick = onQuickPayClick, - modifier = Modifier.testTag("QuickpaySettings") - ) - SettingsButtonRow( - title = stringResource(R.string.settings__bg__title), - onClick = onBgPaymentsClick, - value = SettingsButtonValue.StringValue( - stringResource(if (notificationsGranted) R.string.settings__bg__on else R.string.settings__bg__off) - ), - modifier = Modifier.testTag("BackgroundPaymentSettings") - ) - } - } -} - -@Preview(showBackground = true) -@Composable -private fun Preview() { - AppThemeSurface { - GeneralSettingsContent( - selectedCurrency = "USD", - primaryDisplay = PrimaryDisplay.BITCOIN, - defaultTransactionSpeed = TransactionSpeed.Medium, - selectedLanguage = Language.SYSTEM_DEFAULT.displayName, - notificationsGranted = true, - ) - } -} diff --git a/app/src/main/java/to/bitkit/ui/settings/general/WidgetsSettingsScreen.kt b/app/src/main/java/to/bitkit/ui/settings/general/WidgetsSettingsScreen.kt index 9a860baa4..325bdc031 100644 --- a/app/src/main/java/to/bitkit/ui/settings/general/WidgetsSettingsScreen.kt +++ b/app/src/main/java/to/bitkit/ui/settings/general/WidgetsSettingsScreen.kt @@ -1,17 +1,29 @@ package to.bitkit.ui.settings.general import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +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.testTag import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController import to.bitkit.R +import to.bitkit.ui.components.settings.SectionHeader +import to.bitkit.ui.components.settings.SettingsButtonRow +import to.bitkit.ui.components.settings.SettingsIcon import to.bitkit.ui.components.settings.SettingsSwitchRow +import to.bitkit.ui.navigateToHome +import to.bitkit.ui.scaffold.AppAlertDialog import to.bitkit.ui.scaffold.AppTopBar import to.bitkit.ui.scaffold.DrawerNavIcon import to.bitkit.ui.scaffold.ScreenColumn @@ -33,17 +45,30 @@ fun WidgetsSettingsScreen( showWidgetTitles = showWidgetTitles, onShowWidgetsClick = { settings.setShowWidgets(!showWidgets) }, onShowWidgetTitlesClick = { settings.setShowWidgetTitles(!showWidgetTitles) }, + onResetWidgetsClick = { + settings.resetWidgets() + navController.navigateToHome() + }, + onResetSuggestionsClick = { + settings.resetDismissedSuggestions() + navController.navigateToHome() + }, ) } @Composable private fun WidgetsSettingsContent( - onBackClick: () -> Unit = {}, showWidgets: Boolean, - onShowWidgetsClick: () -> Unit = {}, showWidgetTitles: Boolean, + onBackClick: () -> Unit = {}, + onShowWidgetsClick: () -> Unit = {}, onShowWidgetTitlesClick: () -> Unit = {}, + onResetWidgetsClick: () -> Unit = {}, + onResetSuggestionsClick: () -> Unit = {}, ) { + var showResetWidgetsDialog by remember { mutableStateOf(false) } + var showResetSuggestionsDialog by remember { mutableStateOf(false) } + ScreenColumn { AppTopBar( titleText = stringResource(R.string.settings__widgets__nav_title), @@ -51,17 +76,71 @@ private fun WidgetsSettingsContent( actions = { DrawerNavIcon() }, ) Column( - modifier = Modifier.padding(horizontal = 16.dp), + modifier = Modifier + .padding(horizontal = 16.dp) + .verticalScroll(rememberScrollState()) ) { + // Display section + SectionHeader(title = stringResource(R.string.settings__widgets__section_display)) + SettingsSwitchRow( title = stringResource(R.string.settings__widgets__showWidgets), isChecked = showWidgets, onClick = onShowWidgetsClick, + modifier = Modifier.testTag("ShowWidgets"), ) SettingsSwitchRow( title = stringResource(R.string.settings__widgets__showWidgetTitles), isChecked = showWidgetTitles, onClick = onShowWidgetTitlesClick, + modifier = Modifier.testTag("ShowWidgetTitles"), + ) + + // Reset section + SectionHeader( + title = stringResource(R.string.settings__widgets__section_reset), + padding = PaddingValues(top = 16.dp), + ) + + SettingsButtonRow( + title = stringResource(R.string.settings__widgets__reset_widgets), + icon = { SettingsIcon(R.drawable.ic_arrow_counter_clockwise) }, + onClick = { showResetWidgetsDialog = true }, + modifier = Modifier.testTag("ResetWidgets") + ) + SettingsButtonRow( + title = stringResource(R.string.settings__widgets__reset_suggestions), + icon = { SettingsIcon(R.drawable.ic_arrow_counter_clockwise) }, + onClick = { showResetSuggestionsDialog = true }, + modifier = Modifier.testTag("ResetSuggestions") + ) + } + + if (showResetWidgetsDialog) { + AppAlertDialog( + title = stringResource(R.string.settings__widgets__reset_widgets_dialog_title), + text = stringResource(R.string.settings__widgets__reset_widgets_dialog_description), + confirmText = stringResource(R.string.settings__adv__reset_confirm), + onConfirm = { + onResetWidgetsClick() + showResetWidgetsDialog = false + }, + onDismiss = { showResetWidgetsDialog = false }, + modifier = Modifier.testTag("reset_widgets_dialog") + ) + } + + if (showResetSuggestionsDialog) { + AppAlertDialog( + title = stringResource(R.string.settings__adv__reset_title), + text = stringResource(R.string.settings__adv__reset_desc), + confirmText = stringResource(R.string.settings__adv__reset_confirm), + onConfirm = { + onResetSuggestionsClick() + showResetSuggestionsDialog = false + }, + onDismiss = { showResetSuggestionsDialog = false }, + modifier = Modifier.testTag("reset_suggestions_dialog") ) } } diff --git a/app/src/main/java/to/bitkit/ui/settings/lightning/LightningConnectionsScreen.kt b/app/src/main/java/to/bitkit/ui/settings/lightning/LightningConnectionsScreen.kt index a2edbc291..dbbb430d2 100644 --- a/app/src/main/java/to/bitkit/ui/settings/lightning/LightningConnectionsScreen.kt +++ b/app/src/main/java/to/bitkit/ui/settings/lightning/LightningConnectionsScreen.kt @@ -1,7 +1,9 @@ package to.bitkit.ui.settings.lightning import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.Image 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 @@ -47,9 +49,11 @@ import to.bitkit.ext.amountOnClose import to.bitkit.ext.createChannelDetails import to.bitkit.models.formatToModernDisplay import to.bitkit.ui.Routes +import to.bitkit.ui.components.BodyM import to.bitkit.ui.components.BodyMSB import to.bitkit.ui.components.Caption13Up import to.bitkit.ui.components.ChannelStatusUi +import to.bitkit.ui.components.Display import to.bitkit.ui.components.FillHeight import to.bitkit.ui.components.LightningChannel import to.bitkit.ui.components.PrimaryButton @@ -66,6 +70,7 @@ import to.bitkit.ui.shared.modifiers.clickableAlpha import to.bitkit.ui.shared.util.shareZipFile import to.bitkit.ui.theme.AppThemeSurface import to.bitkit.ui.theme.Colors +import to.bitkit.ui.utils.withAccent private const val CLOSED_CHANNEL_ALPHA = 0.64f @@ -73,8 +78,6 @@ object LightningConnectionsTestTags { const val SCREEN = "lightning_connections_screen" const val ADD_CONNECTION_BUTTON = "add_connection_button" const val EXPORT_LOGS_BUTTON = "export_logs_button" - const val SHOW_CLOSED_BUTTON = "show_closed_button" - const val CHANNEL_ITEM_PREFIX = "channel_item" } @Composable @@ -105,6 +108,7 @@ fun LightningConnectionsScreen( ) } +@Suppress("CyclomaticComplexMethod") @OptIn(ExperimentalMaterial3Api::class) @Composable private fun Content( @@ -146,103 +150,127 @@ private fun Content( onRefresh = onRefresh, modifier = Modifier.fillMaxSize() ) { - Column( - modifier = Modifier - .padding(horizontal = 16.dp) - .fillMaxSize() - .verticalScroll(rememberScrollState()) - ) { - VerticalSpacer(16.dp) - LightningBalancesSection(uiState.localBalance, uiState.remoteBalance) - HorizontalDivider(modifier = Modifier.padding(top = 16.dp)) + val isEmpty = uiState.openChannels.isEmpty() && + uiState.pendingConnections.isEmpty() && + uiState.closedChannels.isEmpty() - // Pending Channels Section - if (uiState.pendingConnections.isNotEmpty()) { + if (isEmpty) { + Column( + modifier = Modifier + .padding(horizontal = 16.dp) + .fillMaxSize() + ) { VerticalSpacer(16.dp) - Caption13Up(stringResource(R.string.lightning__conn_pending), color = Colors.White64) - ChannelList( - status = ChannelStatusUi.PENDING, - channels = uiState.pendingConnections, - onClickChannel = onClickChannel, - ) - } + LightningBalancesSection(uiState.localBalance, uiState.remoteBalance) + HorizontalDivider(modifier = Modifier.padding(top = 16.dp)) - // Open Channels Section - if (uiState.openChannels.isNotEmpty()) { - VerticalSpacer(16.dp) - Caption13Up(stringResource(R.string.lightning__conn_open), color = Colors.White64) - ChannelList( - status = ChannelStatusUi.OPEN, - channels = uiState.openChannels, - onClickChannel = onClickChannel, + EmptyStateContent(modifier = Modifier.weight(1f)) + + PrimaryButton( + text = stringResource(R.string.lightning__conn_onboarding_button), + onClick = onClickAddConnection, + modifier = Modifier + .fillMaxWidth() + .testTag(LightningConnectionsTestTags.ADD_CONNECTION_BUTTON) ) + VerticalSpacer(16.dp) } + } else { + Column( + modifier = Modifier + .padding(horizontal = 16.dp) + .fillMaxSize() + .verticalScroll(rememberScrollState()) + ) { + VerticalSpacer(16.dp) + LightningBalancesSection(uiState.localBalance, uiState.remoteBalance) + HorizontalDivider(modifier = Modifier.padding(top = 16.dp)) - // Closed & Failed Channels Section - AnimatedVisibility(visible = showClosed && uiState.failedOrders.isNotEmpty()) { - Column { + // Pending Channels Section + if (uiState.pendingConnections.isNotEmpty()) { VerticalSpacer(16.dp) - Caption13Up(stringResource(R.string.lightning__conn_failed), color = Colors.White64) + Caption13Up(stringResource(R.string.lightning__conn_pending), color = Colors.White64) ChannelList( - status = ChannelStatusUi.CLOSED, - channels = uiState.failedOrders, + status = ChannelStatusUi.PENDING, + channels = uiState.pendingConnections, onClickChannel = onClickChannel, ) } - } - // Closed Channels Section - AnimatedVisibility(visible = showClosed && uiState.closedChannels.isNotEmpty()) { - Column { + // Open Channels Section + if (uiState.openChannels.isNotEmpty()) { VerticalSpacer(16.dp) - Caption13Up(stringResource(R.string.lightning__conn_closed), color = Colors.White64) + Caption13Up(stringResource(R.string.lightning__conn_open), color = Colors.White64) ChannelList( - status = ChannelStatusUi.CLOSED, - channels = uiState.closedChannels, + status = ChannelStatusUi.OPEN, + channels = uiState.openChannels, onClickChannel = onClickChannel, ) } - } - // Show/Hide Closed Channels Button - if (uiState.failedOrders.isNotEmpty() || uiState.closedChannels.isNotEmpty()) { - VerticalSpacer(16.dp) - TertiaryButton( - text = stringResource( - when (showClosed) { - true -> R.string.lightning__conn_closed_hide - else -> R.string.lightning__conn_closed_show - } - ), - onClick = { showClosed = !showClosed }, - modifier = Modifier - .wrapContentWidth() - .testTag("ChannelsClosed") - ) - } + // Closed & Failed Channels Section + AnimatedVisibility(visible = showClosed && uiState.failedOrders.isNotEmpty()) { + Column { + VerticalSpacer(16.dp) + Caption13Up(stringResource(R.string.lightning__conn_failed), color = Colors.White64) + ChannelList( + status = ChannelStatusUi.CLOSED, + channels = uiState.failedOrders, + onClickChannel = onClickChannel, + ) + } + } - // Bottom Section - FillHeight() - VerticalSpacer(16.dp) - Row( - horizontalArrangement = Arrangement.spacedBy(16.dp), - ) { + // Closed Channels Section + AnimatedVisibility(visible = showClosed && uiState.closedChannels.isNotEmpty()) { + Column { + VerticalSpacer(16.dp) + Caption13Up(stringResource(R.string.lightning__conn_closed), color = Colors.White64) + ChannelList( + status = ChannelStatusUi.CLOSED, + channels = uiState.closedChannels, + onClickChannel = onClickChannel, + ) + } + } + + // Show/Hide Closed Channels Button + if (uiState.failedOrders.isNotEmpty() || uiState.closedChannels.isNotEmpty()) { + VerticalSpacer(16.dp) + TertiaryButton( + text = stringResource( + when (showClosed) { + true -> R.string.lightning__conn_closed_hide + else -> R.string.lightning__conn_closed_show + } + ), + onClick = { showClosed = !showClosed }, + modifier = Modifier + .wrapContentWidth() + .testTag("ChannelsClosed") + ) + } + + // Bottom Section + FillHeight() + VerticalSpacer(16.dp) SecondaryButton( text = stringResource(R.string.lightning__conn_button_export_logs), onClick = onClickExportLogs, modifier = Modifier - .weight(1f) + .fillMaxWidth() .testTag(LightningConnectionsTestTags.EXPORT_LOGS_BUTTON) ) + VerticalSpacer(16.dp) PrimaryButton( text = stringResource(R.string.lightning__conn_button_add), onClick = onClickAddConnection, modifier = Modifier - .weight(1f) + .fillMaxWidth() .testTag(LightningConnectionsTestTags.ADD_CONNECTION_BUTTON) ) + VerticalSpacer(16.dp) } - VerticalSpacer(16.dp) } } } @@ -354,6 +382,38 @@ private fun ChannelItem( } } +@Composable +private fun EmptyStateContent(modifier: Modifier = Modifier) { + Column(modifier = modifier) { + Box( + contentAlignment = Alignment.Center, + modifier = Modifier + .fillMaxWidth() + .weight(1f) + ) { + Image( + painter = painterResource(R.drawable.lightning), + contentDescription = null, + modifier = Modifier.size(256.dp) + ) + } + + Display( + text = stringResource(R.string.lightning__conn_onboarding_title) + .withAccent(accentColor = Colors.Purple), + ) + + VerticalSpacer(14.dp) + + BodyM( + text = stringResource(R.string.lightning__conn_onboarding_text), + color = Colors.White64, + ) + + VerticalSpacer(32.dp) + } +} + @Preview @Composable private fun Preview() { diff --git a/app/src/main/java/to/bitkit/ui/settings/pin/ChangePinConfirmScreen.kt b/app/src/main/java/to/bitkit/ui/settings/pin/ChangePinConfirmScreen.kt deleted file mode 100644 index 99934e17f..000000000 --- a/app/src/main/java/to/bitkit/ui/settings/pin/ChangePinConfirmScreen.kt +++ /dev/null @@ -1,156 +0,0 @@ -package to.bitkit.ui.settings.pin - -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.testTag -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.navigation.NavController -import kotlinx.coroutines.delay -import to.bitkit.R -import to.bitkit.env.Env -import to.bitkit.ui.appViewModel -import to.bitkit.ui.components.BodyM -import to.bitkit.ui.components.BodyS -import to.bitkit.ui.components.KEY_DELETE -import to.bitkit.ui.components.NumberPad -import to.bitkit.ui.components.NumberPadType -import to.bitkit.ui.components.PinDots -import to.bitkit.ui.navigateToChangePinResult -import to.bitkit.ui.scaffold.AppTopBar -import to.bitkit.ui.scaffold.DrawerNavIcon -import to.bitkit.ui.scaffold.ScreenColumn -import to.bitkit.ui.theme.AppThemeSurface -import to.bitkit.ui.theme.Colors - -@Composable -fun ChangePinConfirmScreen( - newPin: String, - navController: NavController, -) { - val app = appViewModel ?: return - var pin by remember { mutableStateOf("") } - var showError by remember { mutableStateOf(false) } - - LaunchedEffect(pin) { - if (pin.length == Env.PIN_LENGTH) { - if (pin == newPin) { - app.editPin(newPin) - navController.navigateToChangePinResult() - } else { - showError = true - delay(500) - pin = "" - } - } - } - - ChangePinConfirmContent( - pin = pin, - showError = showError, - onKeyPress = { key -> - if (key == KEY_DELETE) { - if (pin.isNotEmpty()) { - pin = pin.dropLast(1) - } - } else if (pin.length < Env.PIN_LENGTH) { - pin += key - } - }, - onBackClick = { navController.popBackStack() }, - ) -} - -@Composable -private fun ChangePinConfirmContent( - pin: String, - showError: Boolean, - onKeyPress: (String) -> Unit, - onBackClick: () -> Unit, -) { - ScreenColumn( - modifier = Modifier.testTag("ChangePIN2") - ) { - AppTopBar( - titleText = stringResource(R.string.security__cp_retype_title), - onBackClick = onBackClick, - actions = { DrawerNavIcon() }, - ) - - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.padding(horizontal = 16.dp) - ) { - BodyM( - text = stringResource(R.string.security__cp_retype_text), - color = Colors.White64, - ) - - Spacer(modifier = Modifier.height(32.dp)) - - AnimatedVisibility(visible = showError) { - BodyS( - text = stringResource(R.string.security__cp_try_again), - textAlign = TextAlign.Center, - color = Colors.Brand, - modifier = Modifier - .fillMaxWidth() - .testTag("WrongPIN") - ) - } - - PinDots( - pin = pin, - modifier = Modifier.padding(vertical = 16.dp), - ) - - Spacer(modifier = Modifier.weight(1f)) - - NumberPad( - onPress = onKeyPress, - type = NumberPadType.SIMPLE, - modifier = Modifier.height(350.dp), - ) - } - } -} - -@Preview(showBackground = true) -@Composable -private fun Preview() { - AppThemeSurface { - ChangePinConfirmContent( - pin = "12", - showError = false, - onKeyPress = {}, - onBackClick = {}, - ) - } -} - -@Preview(showBackground = true) -@Composable -private fun PreviewRetry() { - AppThemeSurface { - ChangePinConfirmContent( - pin = "", - showError = true, - onKeyPress = {}, - onBackClick = {}, - ) - } -} diff --git a/app/src/main/java/to/bitkit/ui/settings/pin/ChangePinNewScreen.kt b/app/src/main/java/to/bitkit/ui/settings/pin/ChangePinNewScreen.kt deleted file mode 100644 index 79e9620d9..000000000 --- a/app/src/main/java/to/bitkit/ui/settings/pin/ChangePinNewScreen.kt +++ /dev/null @@ -1,113 +0,0 @@ -package to.bitkit.ui.settings.pin - -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.testTag -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.navigation.NavController -import to.bitkit.R -import to.bitkit.env.Env -import to.bitkit.ui.components.BodyM -import to.bitkit.ui.components.KEY_DELETE -import to.bitkit.ui.components.NumberPad -import to.bitkit.ui.components.NumberPadType -import to.bitkit.ui.components.PinDots -import to.bitkit.ui.navigateToChangePinConfirm -import to.bitkit.ui.scaffold.AppTopBar -import to.bitkit.ui.scaffold.DrawerNavIcon -import to.bitkit.ui.scaffold.ScreenColumn -import to.bitkit.ui.theme.AppThemeSurface -import to.bitkit.ui.theme.Colors - -@Composable -fun ChangePinNewScreen( - navController: NavController, -) { - var pin by remember { mutableStateOf("") } - - LaunchedEffect(pin) { - if (pin.length == Env.PIN_LENGTH) { - navController.navigateToChangePinConfirm(pin) - } - } - - ChangePinNewContent( - pin = pin, - onKeyPress = { key -> - if (key == KEY_DELETE) { - if (pin.isNotEmpty()) { - pin = pin.dropLast(1) - } - } else if (pin.length < Env.PIN_LENGTH) { - pin += key - } - }, - onBackClick = { navController.popBackStack() }, - ) -} - -@Composable -private fun ChangePinNewContent( - pin: String, - onKeyPress: (String) -> Unit, - onBackClick: () -> Unit, -) { - ScreenColumn( - modifier = Modifier.testTag("ChangePIN2") - ) { - AppTopBar( - titleText = stringResource(R.string.security__cp_setnew_title), - onBackClick = onBackClick, - actions = { DrawerNavIcon() }, - ) - - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.padding(horizontal = 16.dp) - ) { - BodyM( - text = stringResource(R.string.security__cp_setnew_text), - color = Colors.White64, - ) - - Spacer(modifier = Modifier.height(32.dp)) - - PinDots( - pin = pin, - modifier = Modifier.padding(vertical = 16.dp), - ) - - Spacer(modifier = Modifier.weight(1f)) - - NumberPad( - onPress = onKeyPress, - type = NumberPadType.SIMPLE, - modifier = Modifier.height(350.dp), - ) - } - } -} - -@Preview(showBackground = true) -@Composable -private fun Preview() { - AppThemeSurface { - ChangePinNewContent( - pin = "12", - onKeyPress = {}, - onBackClick = {}, - ) - } -} diff --git a/app/src/main/java/to/bitkit/ui/settings/pin/ChangePinResultScreen.kt b/app/src/main/java/to/bitkit/ui/settings/pin/ChangePinResultScreen.kt deleted file mode 100644 index 8913265f2..000000000 --- a/app/src/main/java/to/bitkit/ui/settings/pin/ChangePinResultScreen.kt +++ /dev/null @@ -1,91 +0,0 @@ -package to.bitkit.ui.settings.pin - -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.fillMaxWidth -import androidx.compose.foundation.layout.height -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.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 androidx.navigation.NavController -import to.bitkit.R -import to.bitkit.ui.Routes -import to.bitkit.ui.components.BodyM -import to.bitkit.ui.components.PrimaryButton -import to.bitkit.ui.scaffold.AppTopBar -import to.bitkit.ui.scaffold.ScreenColumn -import to.bitkit.ui.theme.AppThemeSurface -import to.bitkit.ui.theme.Colors - -@Composable -fun ChangePinResultScreen( - navController: NavController, -) { - ChangePinResultContent( - onOkClick = { - navController.popBackStack(inclusive = false) - }, - onBackClick = { - navController.popBackStack() - } - ) -} - -@Composable -private fun ChangePinResultContent( - onOkClick: () -> Unit, - onBackClick: () -> Unit, -) { - ScreenColumn { - AppTopBar(stringResource(R.string.security__cp_changed_title), onBackClick = onBackClick) - Column( - modifier = Modifier.padding(horizontal = 16.dp) - ) { - BodyM( - text = stringResource(R.string.security__cp_changed_text), - color = Colors.White64, - ) - - Box( - contentAlignment = Alignment.Center, - modifier = Modifier - .fillMaxWidth() - .weight(1f) - ) { - Image( - painter = painterResource(R.drawable.check), - contentDescription = null, - modifier = Modifier.size(256.dp) - ) - } - - PrimaryButton( - text = stringResource(R.string.common__ok), - onClick = onOkClick, - modifier = Modifier.testTag("OK") - ) - - Spacer(modifier = Modifier.height(16.dp)) - } - } -} - -@Preview(showBackground = true) -@Composable -private fun Preview() { - AppThemeSurface { - ChangePinResultContent( - onOkClick = {}, - onBackClick = {}, - ) - } -} diff --git a/app/src/main/java/to/bitkit/ui/settings/pin/ChangePinScreen.kt b/app/src/main/java/to/bitkit/ui/settings/pin/ChangePinScreen.kt deleted file mode 100644 index 85c727ed0..000000000 --- a/app/src/main/java/to/bitkit/ui/settings/pin/ChangePinScreen.kt +++ /dev/null @@ -1,183 +0,0 @@ -package to.bitkit.ui.settings.pin - -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.testTag -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import androidx.navigation.NavController -import to.bitkit.R -import to.bitkit.env.Env -import to.bitkit.ui.appViewModel -import to.bitkit.ui.components.BodyM -import to.bitkit.ui.components.BodyS -import to.bitkit.ui.components.KEY_DELETE -import to.bitkit.ui.components.NumberPad -import to.bitkit.ui.components.NumberPadType -import to.bitkit.ui.components.PinDots -import to.bitkit.ui.navigateToChangePinNew -import to.bitkit.ui.scaffold.AppTopBar -import to.bitkit.ui.scaffold.DrawerNavIcon -import to.bitkit.ui.scaffold.ScreenColumn -import to.bitkit.ui.shared.modifiers.clickableAlpha -import to.bitkit.ui.theme.AppThemeSurface -import to.bitkit.ui.theme.Colors - -@Composable -fun ChangePinScreen( - navController: NavController, -) { - val app = appViewModel ?: return - val attemptsRemaining by app.pinAttemptsRemaining.collectAsStateWithLifecycle() - var pin by remember { mutableStateOf("") } - - LaunchedEffect(pin) { - if (pin.length == Env.PIN_LENGTH) { - if (app.validatePin(pin)) { - navController.navigateToChangePinNew() - } else { - pin = "" - } - } - } - - ChangePinContent( - pin = pin, - attemptsRemaining = attemptsRemaining, - onKeyPress = { key -> - if (key == KEY_DELETE) { - if (pin.isNotEmpty()) { - pin = pin.dropLast(1) - } - } else if (pin.length < Env.PIN_LENGTH) { - pin += key - } - }, - onBackClick = { navController.popBackStack() }, - onClickForgotPin = { app.setShowForgotPin(true) }, - ) -} - -@Composable -private fun ChangePinContent( - pin: String, - attemptsRemaining: Int, - onKeyPress: (String) -> Unit, - onBackClick: () -> Unit, - onClickForgotPin: () -> Unit, -) { - val isLastAttempt = attemptsRemaining == 1 - - ScreenColumn( - modifier = Modifier.testTag("ChangePIN") - ) { - AppTopBar( - titleText = stringResource(R.string.security__cp_title), - onBackClick = onBackClick, - actions = { DrawerNavIcon() }, - ) - - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.padding(horizontal = 16.dp) - ) { - BodyM( - text = stringResource(R.string.security__cp_text), - color = Colors.White64, - ) - - Spacer(modifier = Modifier.height(32.dp)) - - AnimatedVisibility(visible = attemptsRemaining < Env.PIN_ATTEMPTS) { - if (isLastAttempt) { - BodyS( - text = stringResource(R.string.security__pin_last_attempt), - color = Colors.Brand, - textAlign = TextAlign.Center, - modifier = Modifier.testTag("LastAttempt") - ) - } else { - BodyS( - text = stringResource(R.string.security__pin_attempts) - .replace("{attemptsRemaining}", "$attemptsRemaining"), - color = Colors.Brand, - textAlign = TextAlign.Center, - modifier = Modifier - .clickableAlpha { onClickForgotPin() } - .testTag("AttemptsRemaining") - ) - } - Spacer(modifier = Modifier.height(16.dp)) - } - - PinDots( - pin = pin, - modifier = Modifier.padding(vertical = 16.dp), - ) - - Spacer(modifier = Modifier.weight(1f)) - - NumberPad( - onPress = onKeyPress, - type = NumberPadType.SIMPLE, - modifier = Modifier.height(350.dp), - ) - } - } -} - -@Preview(showBackground = true) -@Composable -private fun Preview() { - AppThemeSurface { - ChangePinContent( - pin = "12", - attemptsRemaining = 8, - onKeyPress = {}, - onBackClick = {}, - onClickForgotPin = {}, - ) - } -} - -@Preview(showBackground = true) -@Composable -private fun PreviewAttemptsRemaining() { - AppThemeSurface { - ChangePinContent( - pin = "1234", - attemptsRemaining = 5, - onKeyPress = {}, - onBackClick = {}, - onClickForgotPin = {}, - ) - } -} - -@Preview(showBackground = true) -@Composable -private fun PreviewAttemptsLast() { - AppThemeSurface { - ChangePinContent( - pin = "", - attemptsRemaining = 1, - onKeyPress = {}, - onBackClick = {}, - onClickForgotPin = {}, - ) - } -} diff --git a/app/src/main/java/to/bitkit/ui/settings/pin/DisablePinScreen.kt b/app/src/main/java/to/bitkit/ui/settings/pin/DisablePinScreen.kt deleted file mode 100644 index 1c68a0e10..000000000 --- a/app/src/main/java/to/bitkit/ui/settings/pin/DisablePinScreen.kt +++ /dev/null @@ -1,96 +0,0 @@ -package to.bitkit.ui.settings.pin - -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.fillMaxWidth -import androidx.compose.foundation.layout.height -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.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 androidx.navigation.NavController -import to.bitkit.R -import to.bitkit.ui.Routes -import to.bitkit.ui.components.AuthCheckAction -import to.bitkit.ui.components.BodyM -import to.bitkit.ui.components.PrimaryButton -import to.bitkit.ui.navigateToAuthCheck -import to.bitkit.ui.scaffold.AppTopBar -import to.bitkit.ui.scaffold.DrawerNavIcon -import to.bitkit.ui.scaffold.ScreenColumn -import to.bitkit.ui.theme.AppThemeSurface -import to.bitkit.ui.theme.Colors - -@Composable -fun DisablePinScreen( - navController: NavController, -) { - DisablePinContent( - onDisableClick = { - navController.navigateToAuthCheck( - requirePin = true, - onSuccessActionId = AuthCheckAction.DISABLE_PIN, - ) { popUpTo(Routes.SecuritySettings) } - }, - onBackClick = { navController.popBackStack() }, - ) -} - -@Composable -private fun DisablePinContent( - onDisableClick: () -> Unit = {}, - onBackClick: () -> Unit = {}, -) { - ScreenColumn { - AppTopBar( - titleText = stringResource(R.string.security__pin_disable_title), - onBackClick = onBackClick, - actions = { DrawerNavIcon() }, - ) - Column( - modifier = Modifier.padding(horizontal = 16.dp), - ) { - BodyM( - text = stringResource(R.string.security__pin_disable_text), - color = Colors.White64, - ) - - Box( - contentAlignment = Alignment.Center, - modifier = Modifier - .fillMaxWidth() - .weight(1f) - ) { - Image( - painter = painterResource(R.drawable.shield), - contentDescription = null, - modifier = Modifier.size(256.dp) - ) - } - - PrimaryButton( - text = stringResource(R.string.security__pin_disable_button), - onClick = onDisableClick, - modifier = Modifier.testTag("DisablePin") - ) - - Spacer(modifier = Modifier.height(16.dp)) - } - } -} - -@Preview(showSystemUi = true) -@Composable -private fun Preview() { - AppThemeSurface { - DisablePinContent() - } -} diff --git a/app/src/main/java/to/bitkit/ui/settings/pin/PinChooseScreen.kt b/app/src/main/java/to/bitkit/ui/settings/pin/PinChooseScreen.kt index c1584133b..9c6592f4d 100644 --- a/app/src/main/java/to/bitkit/ui/settings/pin/PinChooseScreen.kt +++ b/app/src/main/java/to/bitkit/ui/settings/pin/PinChooseScreen.kt @@ -1,6 +1,5 @@ package to.bitkit.ui.settings.pin -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth @@ -49,7 +48,7 @@ fun PinChooseScreen( BodyM( text = stringResource(R.string.security__pin_choose_text), color = Colors.White64, - modifier = Modifier.padding(horizontal = 32.dp), + modifier = Modifier.padding(horizontal = 32.dp) ) Spacer(modifier = Modifier.height(32.dp)) @@ -75,12 +74,11 @@ fun PinChooseScreen( type = NumberPadType.SIMPLE, modifier = Modifier .height(350.dp) - .background(Colors.Black) ) } } -@Preview(showBackground = true) +@Preview(showSystemUi = true) @Composable private fun Preview() { AppThemeSurface { diff --git a/app/src/main/java/to/bitkit/ui/settings/pin/PinConfirmScreen.kt b/app/src/main/java/to/bitkit/ui/settings/pin/PinConfirmScreen.kt index 5b8a74e82..ef91a4652 100644 --- a/app/src/main/java/to/bitkit/ui/settings/pin/PinConfirmScreen.kt +++ b/app/src/main/java/to/bitkit/ui/settings/pin/PinConfirmScreen.kt @@ -1,7 +1,6 @@ package to.bitkit.ui.settings.pin import androidx.compose.animation.AnimatedVisibility -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth @@ -98,7 +97,7 @@ private fun ConfirmPinContent( BodyM( text = stringResource(R.string.security__pin_retype_text), color = Colors.White64, - modifier = Modifier.padding(horizontal = 32.dp), + modifier = Modifier.padding(horizontal = 32.dp) ) Spacer(modifier = Modifier.height(32.dp)) @@ -127,12 +126,11 @@ private fun ConfirmPinContent( type = NumberPadType.SIMPLE, modifier = Modifier .height(350.dp) - .background(Colors.Black) ) } } -@Preview(showBackground = true) +@Preview(showSystemUi = true) @Composable private fun Preview() { AppThemeSurface { @@ -145,7 +143,7 @@ private fun Preview() { } } -@Preview(showBackground = true) +@Preview(showSystemUi = true) @Composable private fun PreviewRetry() { AppThemeSurface { diff --git a/app/src/main/java/to/bitkit/ui/settings/pin/PinManagementScreen.kt b/app/src/main/java/to/bitkit/ui/settings/pin/PinManagementScreen.kt new file mode 100644 index 000000000..0e86a1d2b --- /dev/null +++ b/app/src/main/java/to/bitkit/ui/settings/pin/PinManagementScreen.kt @@ -0,0 +1,166 @@ +package to.bitkit.ui.settings.pin + +import androidx.compose.foundation.Image +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.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +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 androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.navigation.NavController +import to.bitkit.R +import to.bitkit.ui.appViewModel +import to.bitkit.ui.components.BodyM +import to.bitkit.ui.components.PrimaryButton +import to.bitkit.ui.components.SecondaryButton +import to.bitkit.ui.components.Sheet +import to.bitkit.ui.components.VerticalSpacer +import to.bitkit.ui.scaffold.AppTopBar +import to.bitkit.ui.scaffold.DrawerNavIcon +import to.bitkit.ui.scaffold.ScreenColumn +import to.bitkit.ui.settingsViewModel +import to.bitkit.ui.sheets.PinRoute +import to.bitkit.ui.theme.AppThemeSurface +import to.bitkit.ui.theme.Colors + +@Composable +fun PinManagementScreen( + navController: NavController, +) { + val app = appViewModel ?: return + val settings = settingsViewModel ?: return + val isPinEnabled by settings.isPinEnabled.collectAsStateWithLifecycle() + val currentSheet by app.currentSheet.collectAsStateWithLifecycle() + var pinSheetWasShown by remember { mutableStateOf(false) } + + LaunchedEffect(currentSheet) { + if (currentSheet is Sheet.Pin || currentSheet is Sheet.ChangePin || currentSheet is Sheet.DisablePin) { + pinSheetWasShown = true + } else if (pinSheetWasShown && currentSheet == null) { + navController.popBackStack() + } + } + + Content( + isPinEnabled = isPinEnabled, + onEnablePinClick = { app.showSheet(Sheet.Pin(PinRoute.Choose)) }, + onChangePinClick = { app.showSheet(Sheet.ChangePin) }, + onDisablePinClick = { app.showSheet(Sheet.DisablePin) }, + onBackClick = { navController.popBackStack() }, + ) +} + +@Composable +private fun Content( + isPinEnabled: Boolean, + onEnablePinClick: () -> Unit = {}, + onChangePinClick: () -> Unit = {}, + onDisablePinClick: () -> Unit = {}, + onBackClick: () -> Unit = {}, +) { + ScreenColumn { + AppTopBar( + titleText = stringResource( + if (isPinEnabled) { + R.string.security__pin_enabled_title + } else { + R.string.settings__security__pin + } + ), + onBackClick = onBackClick, + actions = { DrawerNavIcon() }, + ) + Column( + modifier = Modifier.padding(horizontal = 16.dp) + ) { + BodyM( + text = stringResource( + if (isPinEnabled) { + R.string.security__pin_enabled_text + } else { + R.string.security__pin_security_text + } + ), + color = Colors.White64, + ) + + Box( + contentAlignment = Alignment.Center, + modifier = Modifier + .fillMaxWidth() + .weight(1f) + ) { + Image( + painter = if (isPinEnabled) { + painterResource(R.drawable.shield_check) + } else { + painterResource(R.drawable.shield) + }, + contentDescription = null, + modifier = Modifier.size(256.dp) + ) + } + + if (isPinEnabled) { + Row( + horizontalArrangement = Arrangement.spacedBy(16.dp), + modifier = Modifier.fillMaxWidth() + ) { + SecondaryButton( + text = stringResource(R.string.settings__security__pin_change), + onClick = onChangePinClick, + modifier = Modifier + .weight(1f) + .testTag("ChangePIN") + ) + PrimaryButton( + text = stringResource(R.string.security__pin_disable_button), + onClick = onDisablePinClick, + modifier = Modifier + .weight(1f) + .testTag("DisablePin") + ) + } + } else { + PrimaryButton( + text = stringResource(R.string.security__pin_enable_button), + onClick = onEnablePinClick, + modifier = Modifier.testTag("EnablePin") + ) + } + + VerticalSpacer(16.dp) + } + } +} + +@Preview(showSystemUi = true) +@Composable +private fun PreviewDisabled() { + AppThemeSurface { + Content(isPinEnabled = false) + } +} + +@Preview(showSystemUi = true) +@Composable +private fun PreviewEnabled() { + AppThemeSurface { + Content(isPinEnabled = true) + } +} diff --git a/app/src/main/java/to/bitkit/ui/settings/support/SupportScreen.kt b/app/src/main/java/to/bitkit/ui/settings/support/SupportScreen.kt index d56085155..8bb1a28f5 100644 --- a/app/src/main/java/to/bitkit/ui/settings/support/SupportScreen.kt +++ b/app/src/main/java/to/bitkit/ui/settings/support/SupportScreen.kt @@ -2,39 +2,75 @@ package to.bitkit.ui.settings.support import android.content.Intent import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.HorizontalDivider import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clipToBounds +import androidx.compose.ui.draw.drawBehind +import androidx.compose.ui.graphics.Path +import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Devices.PIXEL_TABLET import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.core.net.toUri +import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController +import to.bitkit.BuildConfig import to.bitkit.R import to.bitkit.env.Env +import to.bitkit.models.Toast import to.bitkit.ui.Routes +import to.bitkit.ui.appViewModel import to.bitkit.ui.components.BodyM +import to.bitkit.ui.components.FillHeight +import to.bitkit.ui.components.HorizontalSpacer +import to.bitkit.ui.components.VerticalSpacer import to.bitkit.ui.components.settings.Links import to.bitkit.ui.components.settings.SettingsButtonRow +import to.bitkit.ui.components.settings.SettingsIcon import to.bitkit.ui.navigateTo import to.bitkit.ui.scaffold.AppTopBar import to.bitkit.ui.scaffold.DrawerNavIcon import to.bitkit.ui.scaffold.ScreenColumn +import to.bitkit.ui.settingsViewModel +import to.bitkit.ui.shared.modifiers.clickableAlpha +import to.bitkit.ui.shared.util.shareText import to.bitkit.ui.theme.AppThemeSurface import to.bitkit.ui.theme.Colors +private const val DEV_MODE_TAP_THRESHOLD = 5 + @Composable fun SupportScreen( navController: NavController, ) { val context = LocalContext.current + val app = appViewModel ?: return + val settings = settingsViewModel ?: return + val isDevModeEnabled by settings.isDevModeEnabled.collectAsStateWithLifecycle() + var devModeTapCount by remember { mutableIntStateOf(0) } + val haptic = LocalHapticFeedback.current Content( onBack = { navController.popBackStack() }, @@ -44,6 +80,48 @@ fun SupportScreen( context.startActivity(intent) }, onClickAppStatus = { navController.navigateTo(Routes.AppStatus) }, + onClickLegal = { + val intent = Intent(Intent.ACTION_VIEW, Env.TERMS_OF_USE_URL.toUri()) + context.startActivity(intent) + }, + onClickShare = { + shareText( + context, + context.getString(R.string.settings__about__shareText) + .replace("{appStoreUrl}", Env.APP_STORE_URL) + .replace("{playStoreUrl}", Env.PLAY_STORE_URL) + ) + }, + onClickVersion = { + haptic.performHapticFeedback(HapticFeedbackType.Confirm) + devModeTapCount += 1 + + if (devModeTapCount >= DEV_MODE_TAP_THRESHOLD) { + val newValue = !isDevModeEnabled + settings.setIsDevModeEnabled(newValue) + haptic.performHapticFeedback(HapticFeedbackType.LongPress) + + app.toast( + type = Toast.ToastType.SUCCESS, + title = context.getString( + if (newValue) { + R.string.settings__dev_enabled_title + } else { + R.string.settings__dev_disabled_title + } + ), + description = context.getString( + if (newValue) { + R.string.settings__dev_enabled_message + } else { + R.string.settings__dev_disabled_message + } + ), + testTag = if (newValue) "DevModeEnabledToast" else "DevModeDisabledToast", + ) + devModeTapCount = 0 + } + }, ) } @@ -53,7 +131,12 @@ private fun Content( onClickReportIssue: () -> Unit = {}, onClickHelpCenter: () -> Unit = {}, onClickAppStatus: () -> Unit = {}, + onClickLegal: () -> Unit = {}, + onClickShare: () -> Unit = {}, + onClickVersion: () -> Unit = {}, ) { + val appVersion = "${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})" + ScreenColumn { AppTopBar( titleText = stringResource(R.string.settings__support_title), @@ -62,37 +145,151 @@ private fun Content( ) Column( - modifier = Modifier.padding(horizontal = 16.dp) + modifier = Modifier + .weight(1f) + .verticalScroll(rememberScrollState()) ) { - Spacer(modifier = Modifier.height(32.dp)) + // Padded content — setting rows + Column(modifier = Modifier.padding(horizontal = 16.dp)) { + VerticalSpacer(16.dp) - BodyM(text = stringResource(R.string.settings__support__text), color = Colors.White64) + BodyM(text = stringResource(R.string.settings__support__text), color = Colors.White64) - Spacer(modifier = Modifier.height(32.dp)) + VerticalSpacer(16.dp) - SettingsButtonRow(title = stringResource(R.string.settings__support__report), onClick = onClickReportIssue) - SettingsButtonRow(title = stringResource(R.string.settings__support__help), onClick = onClickHelpCenter) - SettingsButtonRow( - title = stringResource(R.string.settings__support__status), - onClick = onClickAppStatus, - modifier = Modifier.testTag("AppStatus") - ) + SettingsButtonRow( + title = stringResource(R.string.settings__support__report), + icon = { SettingsIcon(R.drawable.ic_warning) }, + onClick = onClickReportIssue, + ) + SettingsButtonRow( + title = stringResource(R.string.settings__support__help), + icon = { SettingsIcon(R.drawable.ic_question) }, + onClick = onClickHelpCenter, + ) + SettingsButtonRow( + title = stringResource(R.string.settings__support__status), + icon = { SettingsIcon(R.drawable.ic_power) }, + onClick = onClickAppStatus, + modifier = Modifier.testTag("AppStatus") + ) + SettingsButtonRow( + title = stringResource(R.string.settings__about__legal), + icon = { SettingsIcon(R.drawable.ic_file_text) }, + onClick = onClickLegal + ) + SettingsButtonRow( + title = stringResource(R.string.settings__about__share), + icon = { SettingsIcon(R.drawable.ic_share) }, + onClick = onClickShare, + ) - Image( - painter = painterResource(R.drawable.question_mark), - contentDescription = null, - modifier = Modifier - .fillMaxWidth() - .weight(1f) - ) + // Version row — no chevron, value on right + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .heightIn(min = 52.dp) + .clickableAlpha { onClickVersion() } + .testTag("DevOptions") + ) { + SettingsIcon(R.drawable.ic_stack) + HorizontalSpacer(8.dp) + BodyM( + text = stringResource(R.string.settings__about__version), + modifier = Modifier.weight(1f) + ) + BodyM(text = appVersion, color = Colors.White64) + } + HorizontalDivider() + } + + VerticalSpacer(32.dp) - Links(modifier = Modifier.fillMaxWidth()) + FillHeight() - Spacer(modifier = Modifier.height(16.dp)) + SupportFooter() } } } +@Composable +private fun SupportFooter() { + // Bitkit logo with diagonal orange crossing through it + Box( + modifier = Modifier + .fillMaxWidth() + .clipToBounds() + .drawBehind { + val leftCutY = size.height + val rightCutY = 0f + val path = Path().apply { + moveTo(0f, leftCutY) + lineTo(size.width, rightCutY) + lineTo(size.width, size.height) + lineTo(0f, size.height) + close() + } + drawPath(path, color = Colors.Brand) + }, + ) { + Image( + painter = painterResource(R.drawable.bitkit_logo), + contentDescription = null, + modifier = Modifier + .fillMaxWidth() + .height(100.dp) + .padding(horizontal = 16.dp) + .testTag("AboutLogo") + ) + } + + // Solid orange background for bottom content + Column( + modifier = Modifier + .fillMaxWidth() + .background(Colors.Brand) + .padding(horizontal = 16.dp) + ) { + VerticalSpacer(16.dp) + + Links(modifier = Modifier.fillMaxWidth()) + + VerticalSpacer(16.dp) + + BodyM( + text = stringResource(R.string.settings__support__copyright), + color = Colors.White64, + ) + + VerticalSpacer(16.dp) + + BrandEndorsement() + + VerticalSpacer(32.dp) + } +} + +@Composable +private fun BrandEndorsement() { + Row( + horizontalArrangement = Arrangement.spacedBy(10.dp, Alignment.CenterHorizontally), + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.fillMaxWidth() + ) { + Image( + painter = painterResource(R.drawable.ic_synonym_logo), + contentDescription = null, + modifier = Modifier.height(24.dp) + ) + Image( + painter = painterResource(R.drawable.ic_tether_company), + contentDescription = null, + modifier = Modifier.height(16.dp) + ) + } +} + @Preview(showSystemUi = true) @Composable private fun Preview() { @@ -100,3 +297,11 @@ private fun Preview() { Content() } } + +@Preview(showSystemUi = true, device = PIXEL_TABLET) +@Composable +private fun PreviewTablet() { + AppThemeSurface { + Content() + } +} diff --git a/app/src/main/java/to/bitkit/ui/shared/util/Modifiers.kt b/app/src/main/java/to/bitkit/ui/shared/util/Modifiers.kt index af11c9671..709fc5694 100644 --- a/app/src/main/java/to/bitkit/ui/shared/util/Modifiers.kt +++ b/app/src/main/java/to/bitkit/ui/shared/util/Modifiers.kt @@ -17,6 +17,7 @@ import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Paint import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.graphics.drawscope.ContentDrawScope import androidx.compose.ui.graphics.drawscope.drawIntoCanvas import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.input.pointer.pointerInput @@ -117,7 +118,7 @@ private class OuterGlowNode( var glowRadius: Dp, var cornerRadius: Dp, ) : DrawModifierNode, Modifier.Node() { - override fun androidx.compose.ui.graphics.drawscope.ContentDrawScope.draw() { + override fun ContentDrawScope.draw() { val glowRadiusPx = glowRadius.toPx() val cornerRadiusPx = cornerRadius.toPx() diff --git a/app/src/main/java/to/bitkit/ui/sheets/ChangePinSheet.kt b/app/src/main/java/to/bitkit/ui/sheets/ChangePinSheet.kt new file mode 100644 index 000000000..abdc067fb --- /dev/null +++ b/app/src/main/java/to/bitkit/ui/sheets/ChangePinSheet.kt @@ -0,0 +1,428 @@ +package to.bitkit.ui.sheets + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +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.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.rememberNavController +import androidx.navigation.toRoute +import kotlinx.coroutines.delay +import kotlinx.serialization.Serializable +import to.bitkit.R +import to.bitkit.env.Env +import to.bitkit.ui.components.BodyM +import to.bitkit.ui.components.BodyS +import to.bitkit.ui.components.BottomSheetPreview +import to.bitkit.ui.components.FillHeight +import to.bitkit.ui.components.NumberPad +import to.bitkit.ui.components.NumberPadType +import to.bitkit.ui.components.PinDots +import to.bitkit.ui.components.PrimaryButton +import to.bitkit.ui.components.SheetSize +import to.bitkit.ui.components.VerticalSpacer +import to.bitkit.ui.navigateTo +import to.bitkit.ui.scaffold.SheetTopBar +import to.bitkit.ui.shared.modifiers.clickableAlpha +import to.bitkit.ui.shared.modifiers.sheetHeight +import to.bitkit.ui.shared.util.gradientBackground +import to.bitkit.ui.theme.AppThemeSurface +import to.bitkit.ui.theme.Colors +import to.bitkit.ui.utils.NumberPadHeight +import to.bitkit.ui.utils.composableWithDefaultTransitions +import to.bitkit.ui.utils.handlePinKeyPress +import to.bitkit.ui.utils.withAccentBoldBright +import to.bitkit.viewmodels.AppViewModel + +@Suppress("CyclomaticComplexMethod") +@Composable +fun ChangePinSheet(app: AppViewModel) { + val navController = rememberNavController() + val onDismiss = app::hideSheet + + Column( + modifier = Modifier + .fillMaxWidth() + .sheetHeight(SheetSize.MEDIUM) + ) { + NavHost( + navController = navController, + startDestination = ChangePinRoute.Validate, + ) { + composableWithDefaultTransitions { + val attemptsRemaining by app.pinAttemptsRemaining.collectAsStateWithLifecycle() + var pin by remember { mutableStateOf("") } + + LaunchedEffect(pin) { + if (pin.length == Env.PIN_LENGTH) { + if (app.validatePin(pin)) { + navController.navigateTo(ChangePinRoute.New) + } else { + pin = "" + } + } + } + + ValidateContent( + pin = pin, + attemptsRemaining = attemptsRemaining, + onKeyPress = { pin = handlePinKeyPress(pin, it) }, + onBackClick = onDismiss, + onClickForgotPin = { app.setShowForgotPin(true) }, + ) + } + composableWithDefaultTransitions { + var pin by remember { mutableStateOf("") } + + LaunchedEffect(pin) { + if (pin.length == Env.PIN_LENGTH) { + navController.navigateTo(ChangePinRoute.Confirm(pin)) + } + } + + NewPinContent( + pin = pin, + onKeyPress = { pin = handlePinKeyPress(pin, it) }, + onBackClick = { navController.popBackStack() }, + ) + } + composableWithDefaultTransitions { + val newPin = it.toRoute().pin + var pin by remember { mutableStateOf("") } + var showError by remember { mutableStateOf(false) } + + LaunchedEffect(pin) { + if (pin.length == Env.PIN_LENGTH) { + if (pin == newPin) { + app.editPin(newPin) + navController.navigateTo(ChangePinRoute.Result) + } else { + showError = true + delay(500) + pin = "" + } + } + } + + ConfirmContent( + pin = pin, + showError = showError, + onKeyPress = { key -> pin = handlePinKeyPress(pin, key) }, + onBackClick = { navController.popBackStack() }, + ) + } + composableWithDefaultTransitions { + ResultContent( + onOkClick = onDismiss, + ) + } + } + } +} + +sealed interface ChangePinRoute { + @Serializable + data object Validate : ChangePinRoute + + @Serializable + data object New : ChangePinRoute + + @Serializable + data class Confirm(val pin: String) : ChangePinRoute + + @Serializable + data object Result : ChangePinRoute +} + +@Composable +private fun ValidateContent( + pin: String, + attemptsRemaining: Int, + onKeyPress: (String) -> Unit, + onBackClick: () -> Unit, + onClickForgotPin: () -> Unit, +) { + val isLastAttempt = attemptsRemaining == 1 + + Column( + modifier = Modifier + .fillMaxWidth() + .gradientBackground() + .navigationBarsPadding() + .testTag("ChangePIN") + ) { + SheetTopBar( + titleText = stringResource(R.string.security__cp_title), + onBack = onBackClick, + ) + + VerticalSpacer(16.dp) + + BodyM( + text = stringResource(R.string.security__cp_text).withAccentBoldBright(), + color = Colors.White64, + modifier = Modifier.padding(horizontal = 32.dp) + ) + + VerticalSpacer(32.dp) + + AnimatedVisibility(visible = attemptsRemaining < Env.PIN_ATTEMPTS) { + if (isLastAttempt) { + BodyS( + text = stringResource(R.string.security__pin_last_attempt), + color = Colors.Brand, + textAlign = TextAlign.Center, + modifier = Modifier + .fillMaxWidth() + .testTag("LastAttempt") + ) + } else { + BodyS( + text = stringResource(R.string.security__pin_attempts) + .replace("{attemptsRemaining}", "$attemptsRemaining"), + color = Colors.Brand, + textAlign = TextAlign.Center, + modifier = Modifier + .fillMaxWidth() + .clickableAlpha { onClickForgotPin() } + .testTag("AttemptsRemaining") + ) + } + VerticalSpacer(16.dp) + } + + FillHeight() + + PinDots(pin = pin) + + VerticalSpacer(32.dp) + + NumberPad( + onPress = onKeyPress, + type = NumberPadType.SIMPLE, + modifier = Modifier + .height(NumberPadHeight) + ) + } +} + +@Composable +private fun NewPinContent( + pin: String, + onKeyPress: (String) -> Unit, + onBackClick: () -> Unit, +) { + Column( + modifier = Modifier + .fillMaxWidth() + .gradientBackground() + .navigationBarsPadding() + .testTag("ChangePIN2") + ) { + SheetTopBar( + titleText = stringResource(R.string.security__cp_setnew_title), + onBack = onBackClick, + ) + + VerticalSpacer(16.dp) + + BodyM( + text = stringResource(R.string.security__cp_setnew_text), + color = Colors.White64, + modifier = Modifier.padding(horizontal = 32.dp) + ) + + VerticalSpacer(32.dp) + FillHeight() + + PinDots(pin = pin) + + VerticalSpacer(32.dp) + + NumberPad( + onPress = onKeyPress, + type = NumberPadType.SIMPLE, + modifier = Modifier + .height(NumberPadHeight) + ) + } +} + +@Composable +private fun ConfirmContent( + pin: String, + showError: Boolean, + onKeyPress: (String) -> Unit, + onBackClick: () -> Unit, +) { + Column( + modifier = Modifier + .fillMaxWidth() + .gradientBackground() + .navigationBarsPadding() + .testTag("ChangePIN2") + ) { + SheetTopBar( + titleText = stringResource(R.string.security__cp_retype_title), + onBack = onBackClick, + ) + + VerticalSpacer(16.dp) + + BodyM( + text = stringResource(R.string.security__cp_retype_text), + color = Colors.White64, + modifier = Modifier.padding(horizontal = 32.dp) + ) + + VerticalSpacer(32.dp) + + AnimatedVisibility(visible = showError) { + BodyS( + text = stringResource(R.string.security__cp_try_again), + textAlign = TextAlign.Center, + color = Colors.Brand, + modifier = Modifier + .fillMaxWidth() + .testTag("WrongPIN") + ) + } + + FillHeight() + + PinDots(pin = pin) + + VerticalSpacer(32.dp) + + NumberPad( + onPress = onKeyPress, + type = NumberPadType.SIMPLE, + modifier = Modifier + .height(NumberPadHeight) + ) + } +} + +@Composable +private fun ResultContent( + onOkClick: () -> Unit, +) { + Column( + modifier = Modifier + .fillMaxWidth() + .gradientBackground() + .navigationBarsPadding() + ) { + SheetTopBar( + titleText = stringResource(R.string.security__cp_changed_title) + ) + + VerticalSpacer(16.dp) + + BodyM( + text = stringResource(R.string.security__cp_changed_text), + color = Colors.White64, + modifier = Modifier.padding(horizontal = 32.dp) + ) + + Box( + contentAlignment = Alignment.Center, + modifier = Modifier + .fillMaxWidth() + .weight(1f) + ) { + Image( + painter = painterResource(R.drawable.check), + contentDescription = null, + modifier = Modifier.size(256.dp) + ) + } + + PrimaryButton( + text = stringResource(R.string.common__ok), + onClick = onOkClick, + modifier = Modifier + .padding(horizontal = 32.dp) + .testTag("OK") + ) + + VerticalSpacer(16.dp) + } +} + +@Preview(showSystemUi = true) +@Composable +private fun PreviewValidate() { + AppThemeSurface { + BottomSheetPreview { + ValidateContent( + pin = "12", + attemptsRemaining = 8, + onKeyPress = {}, + onBackClick = {}, + onClickForgotPin = {}, + ) + } + } +} + +@Preview(showSystemUi = true) +@Composable +private fun PreviewNew() { + AppThemeSurface { + BottomSheetPreview { + NewPinContent( + pin = "12", + onKeyPress = {}, + onBackClick = {}, + ) + } + } +} + +@Preview(showSystemUi = true) +@Composable +private fun PreviewConfirm() { + AppThemeSurface { + BottomSheetPreview { + ConfirmContent( + pin = "12", + showError = false, + onKeyPress = {}, + onBackClick = {}, + ) + } + } +} + +@Preview(showSystemUi = true) +@Composable +private fun PreviewResult() { + AppThemeSurface { + BottomSheetPreview { + ResultContent( + onOkClick = {}, + ) + } + } +} diff --git a/app/src/main/java/to/bitkit/ui/sheets/DisablePinSheet.kt b/app/src/main/java/to/bitkit/ui/sheets/DisablePinSheet.kt new file mode 100644 index 000000000..62b938f7c --- /dev/null +++ b/app/src/main/java/to/bitkit/ui/sheets/DisablePinSheet.kt @@ -0,0 +1,157 @@ +package to.bitkit.ui.sheets + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.navigationBarsPadding +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +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.testTag +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import to.bitkit.R +import to.bitkit.env.Env +import to.bitkit.ui.components.BodyM +import to.bitkit.ui.components.BodyS +import to.bitkit.ui.components.BottomSheetPreview +import to.bitkit.ui.components.FillHeight +import to.bitkit.ui.components.NumberPad +import to.bitkit.ui.components.NumberPadType +import to.bitkit.ui.components.PinDots +import to.bitkit.ui.components.SheetSize +import to.bitkit.ui.components.VerticalSpacer +import to.bitkit.ui.scaffold.SheetTopBar +import to.bitkit.ui.shared.modifiers.clickableAlpha +import to.bitkit.ui.shared.modifiers.sheetHeight +import to.bitkit.ui.shared.util.gradientBackground +import to.bitkit.ui.theme.AppThemeSurface +import to.bitkit.ui.theme.Colors +import to.bitkit.ui.utils.NumberPadHeight +import to.bitkit.ui.utils.handlePinKeyPress +import to.bitkit.ui.utils.withAccentBoldBright +import to.bitkit.viewmodels.AppViewModel + +@Composable +fun DisablePinSheet(app: AppViewModel) { + val attemptsRemaining by app.pinAttemptsRemaining.collectAsStateWithLifecycle() + var pin by remember { mutableStateOf("") } + val onDismiss = app::hideSheet + + LaunchedEffect(pin) { + if (pin.length == Env.PIN_LENGTH) { + if (app.validatePin(pin)) { + app.removePin() + onDismiss() + } else { + pin = "" + } + } + } + + Content( + pin = pin, + attemptsRemaining = attemptsRemaining, + onKeyPress = { pin = handlePinKeyPress(pin, it) }, + onBackClick = onDismiss, + onClickForgotPin = { app.setShowForgotPin(true) }, + ) +} + +@Composable +private fun Content( + pin: String, + attemptsRemaining: Int, + onKeyPress: (String) -> Unit, + onBackClick: () -> Unit, + onClickForgotPin: () -> Unit, +) { + val isLastAttempt = attemptsRemaining == 1 + + Column( + modifier = Modifier + .fillMaxWidth() + .sheetHeight(SheetSize.MEDIUM) + .gradientBackground() + .navigationBarsPadding() + .testTag("DisablePIN") + ) { + SheetTopBar( + titleText = stringResource(R.string.security__pin_disable_button), + onBack = onBackClick, + ) + + VerticalSpacer(16.dp) + + BodyM( + text = stringResource(R.string.security__pin_disable_text).withAccentBoldBright(), + color = Colors.White64, + modifier = Modifier.padding(horizontal = 32.dp) + ) + + VerticalSpacer(32.dp) + + AnimatedVisibility(visible = attemptsRemaining < Env.PIN_ATTEMPTS) { + if (isLastAttempt) { + BodyS( + text = stringResource(R.string.security__pin_last_attempt), + color = Colors.Brand, + textAlign = TextAlign.Center, + modifier = Modifier + .fillMaxWidth() + .testTag("LastAttempt") + ) + } else { + BodyS( + text = stringResource(R.string.security__pin_attempts) + .replace("{attemptsRemaining}", "$attemptsRemaining"), + color = Colors.Brand, + textAlign = TextAlign.Center, + modifier = Modifier + .fillMaxWidth() + .clickableAlpha { onClickForgotPin() } + .testTag("AttemptsRemaining") + ) + } + VerticalSpacer(16.dp) + } + + FillHeight() + + PinDots(pin = pin) + + VerticalSpacer(32.dp) + + NumberPad( + onPress = onKeyPress, + type = NumberPadType.SIMPLE, + modifier = Modifier + .height(NumberPadHeight) + ) + } +} + +@Preview(showSystemUi = true) +@Composable +private fun Preview() { + AppThemeSurface { + BottomSheetPreview { + Content( + pin = "12", + attemptsRemaining = 8, + onKeyPress = {}, + onBackClick = {}, + onClickForgotPin = {}, + ) + } + } +} diff --git a/app/src/main/java/to/bitkit/ui/utils/AppLocaleManager.kt b/app/src/main/java/to/bitkit/ui/utils/AppLocaleManager.kt index 95a2b326c..e48ffd0db 100644 --- a/app/src/main/java/to/bitkit/ui/utils/AppLocaleManager.kt +++ b/app/src/main/java/to/bitkit/ui/utils/AppLocaleManager.kt @@ -52,7 +52,7 @@ class AppLocaleManager @Inject constructor( fun getSupportedLanguages(): List { return Language.entries.sortedWith( - compareBy { !it.isSystemDefault }.thenBy { it.displayName } + compareBy { !it.isSystemDefault }.thenBy { it.nativeName } ) } } diff --git a/app/src/main/java/to/bitkit/ui/utils/PinInputHelper.kt b/app/src/main/java/to/bitkit/ui/utils/PinInputHelper.kt new file mode 100644 index 000000000..fb0d29623 --- /dev/null +++ b/app/src/main/java/to/bitkit/ui/utils/PinInputHelper.kt @@ -0,0 +1,13 @@ +package to.bitkit.ui.utils + +import androidx.compose.ui.unit.dp +import to.bitkit.env.Env +import to.bitkit.ui.components.KEY_DELETE + +val NumberPadHeight = 350.dp + +fun handlePinKeyPress(currentPin: String, key: String): String = when { + key == KEY_DELETE -> currentPin.dropLast(1) + currentPin.length < Env.PIN_LENGTH -> currentPin + key + else -> currentPin +} diff --git a/app/src/main/java/to/bitkit/viewmodels/SettingsViewModel.kt b/app/src/main/java/to/bitkit/viewmodels/SettingsViewModel.kt index 5faed227e..e464f2346 100644 --- a/app/src/main/java/to/bitkit/viewmodels/SettingsViewModel.kt +++ b/app/src/main/java/to/bitkit/viewmodels/SettingsViewModel.kt @@ -12,13 +12,17 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import to.bitkit.data.SettingsStore +import to.bitkit.data.WidgetsStore import to.bitkit.models.TransactionSpeed +import to.bitkit.repositories.WidgetsRepo import javax.inject.Inject @Suppress("TooManyFunctions") @HiltViewModel class SettingsViewModel @Inject constructor( private val settingsStore: SettingsStore, + private val widgetsStore: WidgetsStore, + private val widgetsRepo: WidgetsRepo, ) : ViewModel() { fun reset() = viewModelScope.launch { settingsStore.reset() } @@ -169,6 +173,19 @@ class SettingsViewModel @Inject constructor( } } + fun resetDismissedSuggestions() { + viewModelScope.launch { + settingsStore.update { it.copy(dismissedSuggestions = emptyList()) } + } + } + + fun resetWidgets() { + viewModelScope.launch { + widgetsStore.reset() + widgetsRepo.refreshEnabledWidgets() + } + } + val lastUsedTags = settingsStore.data.map { it.lastUsedTags.toImmutableList() } .asStateFlow(initialValue = persistentListOf()) diff --git a/app/src/main/res/drawable-hdpi/shield_check.webp b/app/src/main/res/drawable-hdpi/shield_check.webp new file mode 100644 index 000000000..a5f515529 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/shield_check.webp differ diff --git a/app/src/main/res/drawable-mdpi/shield_check.webp b/app/src/main/res/drawable-mdpi/shield_check.webp new file mode 100644 index 000000000..09fdccb82 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/shield_check.webp differ diff --git a/app/src/main/res/drawable-xhdpi/shield_check.webp b/app/src/main/res/drawable-xhdpi/shield_check.webp new file mode 100644 index 000000000..5a978350f Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/shield_check.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/shield_check.webp b/app/src/main/res/drawable-xxhdpi/shield_check.webp new file mode 100644 index 000000000..1d9baab4d Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/shield_check.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/shield_check.webp b/app/src/main/res/drawable-xxxhdpi/shield_check.webp new file mode 100644 index 000000000..706c8979d Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/shield_check.webp differ diff --git a/app/src/main/res/drawable/ic_arrow_counter_clockwise.xml b/app/src/main/res/drawable/ic_arrow_counter_clockwise.xml new file mode 100644 index 000000000..545c176ab --- /dev/null +++ b/app/src/main/res/drawable/ic_arrow_counter_clockwise.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/drawable/ic_bell.xml b/app/src/main/res/drawable/ic_bell.xml index 2c95aa02a..014ea6050 100644 --- a/app/src/main/res/drawable/ic_bell.xml +++ b/app/src/main/res/drawable/ic_bell.xml @@ -1,24 +1,24 @@ + android:pathData="M2,1h11.91v14.5h-11.91z"/> diff --git a/app/src/main/res/drawable/ic_bitcoin_modern.xml b/app/src/main/res/drawable/ic_bitcoin_modern.xml new file mode 100644 index 000000000..cca907ae4 --- /dev/null +++ b/app/src/main/res/drawable/ic_bitcoin_modern.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_broadcast.xml b/app/src/main/res/drawable/ic_broadcast.xml index 7436da07e..88d4e65bf 100644 --- a/app/src/main/res/drawable/ic_broadcast.xml +++ b/app/src/main/res/drawable/ic_broadcast.xml @@ -4,16 +4,28 @@ android:viewportWidth="16" android:viewportHeight="16"> + + + diff --git a/app/src/main/res/drawable/ic_caret_double_right.xml b/app/src/main/res/drawable/ic_caret_double_right.xml new file mode 100644 index 000000000..1c9ccb556 --- /dev/null +++ b/app/src/main/res/drawable/ic_caret_double_right.xml @@ -0,0 +1,19 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_chats_circle.xml b/app/src/main/res/drawable/ic_chats_circle.xml new file mode 100644 index 000000000..2da16036a --- /dev/null +++ b/app/src/main/res/drawable/ic_chats_circle.xml @@ -0,0 +1,19 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_clipboard_text.xml b/app/src/main/res/drawable/ic_clipboard_text.xml index c3f495f06..6e1b7fee1 100644 --- a/app/src/main/res/drawable/ic_clipboard_text.xml +++ b/app/src/main/res/drawable/ic_clipboard_text.xml @@ -1,27 +1,27 @@ + android:width="16dp" + android:height="16dp" + android:viewportWidth="16" + android:viewportHeight="16"> diff --git a/app/src/main/res/drawable/ic_coins.xml b/app/src/main/res/drawable/ic_coins.xml index 881f73ce1..a69ddf3e9 100644 --- a/app/src/main/res/drawable/ic_coins.xml +++ b/app/src/main/res/drawable/ic_coins.xml @@ -1,68 +1,68 @@ + android:width="16dp" + android:height="16dp" + android:viewportWidth="16" + android:viewportHeight="16"> diff --git a/app/src/main/res/drawable/ic_database.xml b/app/src/main/res/drawable/ic_database.xml new file mode 100644 index 000000000..6f842cea0 --- /dev/null +++ b/app/src/main/res/drawable/ic_database.xml @@ -0,0 +1,23 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_device_mobile_speaker.xml b/app/src/main/res/drawable/ic_device_mobile_speaker.xml new file mode 100644 index 000000000..a18ef193c --- /dev/null +++ b/app/src/main/res/drawable/ic_device_mobile_speaker.xml @@ -0,0 +1,19 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_eye.xml b/app/src/main/res/drawable/ic_eye.xml index 8768f08f1..979ea8338 100644 --- a/app/src/main/res/drawable/ic_eye.xml +++ b/app/src/main/res/drawable/ic_eye.xml @@ -1,19 +1,19 @@ + android:width="16dp" + android:height="16dp" + android:viewportWidth="16" + android:viewportHeight="16"> diff --git a/app/src/main/res/drawable/ic_eye_slash.xml b/app/src/main/res/drawable/ic_eye_slash.xml new file mode 100644 index 000000000..fc60f3e0c --- /dev/null +++ b/app/src/main/res/drawable/ic_eye_slash.xml @@ -0,0 +1,31 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/ic_file_text.xml b/app/src/main/res/drawable/ic_file_text.xml new file mode 100644 index 000000000..e63887dbc --- /dev/null +++ b/app/src/main/res/drawable/ic_file_text.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_git_branch.xml b/app/src/main/res/drawable/ic_git_branch.xml index 2ebce6167..eff49ad96 100644 --- a/app/src/main/res/drawable/ic_git_branch.xml +++ b/app/src/main/res/drawable/ic_git_branch.xml @@ -1,54 +1,31 @@ - - - - - - - - - - - - - + + + + + + diff --git a/app/src/main/res/drawable/ic_hand_pointing.xml b/app/src/main/res/drawable/ic_hand_pointing.xml new file mode 100644 index 000000000..bd64b2101 --- /dev/null +++ b/app/src/main/res/drawable/ic_hand_pointing.xml @@ -0,0 +1,19 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_hard_drives.xml b/app/src/main/res/drawable/ic_hard_drives.xml new file mode 100644 index 000000000..e57a3c17f --- /dev/null +++ b/app/src/main/res/drawable/ic_hard_drives.xml @@ -0,0 +1,30 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/ic_lightning.xml b/app/src/main/res/drawable/ic_lightning.xml index ab0f9788a..8c863840e 100644 --- a/app/src/main/res/drawable/ic_lightning.xml +++ b/app/src/main/res/drawable/ic_lightning.xml @@ -1,15 +1,15 @@ diff --git a/app/src/main/res/drawable/ic_list_dashes.xml b/app/src/main/res/drawable/ic_list_dashes.xml new file mode 100644 index 000000000..a5a08872b --- /dev/null +++ b/app/src/main/res/drawable/ic_list_dashes.xml @@ -0,0 +1,30 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/ic_lock_key.xml b/app/src/main/res/drawable/ic_lock_key.xml new file mode 100644 index 000000000..b6217c0bd --- /dev/null +++ b/app/src/main/res/drawable/ic_lock_key.xml @@ -0,0 +1,27 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_power.xml b/app/src/main/res/drawable/ic_power.xml index bdc66f033..044e96fd4 100644 --- a/app/src/main/res/drawable/ic_power.xml +++ b/app/src/main/res/drawable/ic_power.xml @@ -1,14 +1,9 @@ + android:viewportWidth="256" + android:viewportHeight="256"> - + android:pathData="M120,128V48a8,8,0,0,1,16,0v80a8,8,0,0,1-16,0Zm60.37-78.7a8,8,0,0,0-8.74,13.4C194.74,77.77,208,101.57,208,128a80,80,0,0,1-160,0c0-26.43,13.26-50.23,36.37-65.3a8,8,0,0,0-8.74-13.4C47.9,67.38,32,96.06,32,128a96,96,0,0,0,192,0C224,96.06,208.1,67.38,180.37,49.3Z" + android:fillColor="#ffffff"/> diff --git a/app/src/main/res/drawable/ic_question.xml b/app/src/main/res/drawable/ic_question.xml new file mode 100644 index 000000000..c1fa561ef --- /dev/null +++ b/app/src/main/res/drawable/ic_question.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_settings_dev.xml b/app/src/main/res/drawable/ic_settings_dev.xml index 934c9b5d6..7877a8cf1 100644 --- a/app/src/main/res/drawable/ic_settings_dev.xml +++ b/app/src/main/res/drawable/ic_settings_dev.xml @@ -1,6 +1,6 @@ - - - - - diff --git a/app/src/main/res/drawable/ic_share.xml b/app/src/main/res/drawable/ic_share.xml index e63379543..25919a131 100644 --- a/app/src/main/res/drawable/ic_share.xml +++ b/app/src/main/res/drawable/ic_share.xml @@ -1,18 +1,9 @@ - - - + android:width="24dp" + android:height="24dp" + android:viewportWidth="256" + android:viewportHeight="256"> + diff --git a/app/src/main/res/drawable/ic_shield.xml b/app/src/main/res/drawable/ic_shield.xml new file mode 100644 index 000000000..3d763a92b --- /dev/null +++ b/app/src/main/res/drawable/ic_shield.xml @@ -0,0 +1,19 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_smiley.xml b/app/src/main/res/drawable/ic_smiley.xml new file mode 100644 index 000000000..51855c4cf --- /dev/null +++ b/app/src/main/res/drawable/ic_smiley.xml @@ -0,0 +1,25 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_speed_normal.xml b/app/src/main/res/drawable/ic_speed_normal.xml index 0e74b1f63..82efd0b1b 100644 --- a/app/src/main/res/drawable/ic_speed_normal.xml +++ b/app/src/main/res/drawable/ic_speed_normal.xml @@ -1,6 +1,6 @@ + android:width="16dp" + android:height="16dp" + android:viewportWidth="16" + android:viewportHeight="16"> diff --git a/app/src/main/res/drawable/ic_stop_circle.xml b/app/src/main/res/drawable/ic_stop_circle.xml new file mode 100644 index 000000000..6ac0141ac --- /dev/null +++ b/app/src/main/res/drawable/ic_stop_circle.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_synonym_logo.xml b/app/src/main/res/drawable/ic_synonym_logo.xml new file mode 100644 index 000000000..49a7d4da3 --- /dev/null +++ b/app/src/main/res/drawable/ic_synonym_logo.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_tag.xml b/app/src/main/res/drawable/ic_tag.xml index 9bf21df5b..f722a69d4 100644 --- a/app/src/main/res/drawable/ic_tag.xml +++ b/app/src/main/res/drawable/ic_tag.xml @@ -4,15 +4,15 @@ android:viewportWidth="16" android:viewportHeight="16"> + android:pathData="M5.25,6C5.664,6 6,5.664 6,5.25C6,4.836 5.664,4.5 5.25,4.5C4.835,4.5 4.5,4.836 4.5,5.25C4.5,5.664 4.835,6 5.25,6Z" + android:fillColor="#FF4400"/> diff --git a/app/src/main/res/drawable/ic_tether_company.xml b/app/src/main/res/drawable/ic_tether_company.xml new file mode 100644 index 000000000..e689f1c48 --- /dev/null +++ b/app/src/main/res/drawable/ic_tether_company.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_translate.xml b/app/src/main/res/drawable/ic_translate.xml new file mode 100644 index 000000000..def846a0b --- /dev/null +++ b/app/src/main/res/drawable/ic_translate.xml @@ -0,0 +1,30 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/ic_unit_fiat.xml b/app/src/main/res/drawable/ic_unit_fiat.xml index 9e583dd7e..5b520195c 100644 --- a/app/src/main/res/drawable/ic_unit_fiat.xml +++ b/app/src/main/res/drawable/ic_unit_fiat.xml @@ -1,27 +1,27 @@ + android:width="16dp" + android:height="16dp" + android:viewportWidth="16" + android:viewportHeight="16"> - + diff --git a/app/src/main/res/drawable/ic_warning.xml b/app/src/main/res/drawable/ic_warning.xml index baaaea5e7..35ad35f66 100644 --- a/app/src/main/res/drawable/ic_warning.xml +++ b/app/src/main/res/drawable/ic_warning.xml @@ -1,22 +1,22 @@ - - - - + android:width="16dp" + android:height="16dp" + android:viewportWidth="16" + android:viewportHeight="16"> + + + + diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index c422599da..840561096 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -255,6 +255,9 @@ جارٍ الاتصال والمزامنة... يرجى الانتظار حتى يتصل Bitkit بشبكة الدفع (±10 ثوانٍ). أرصدة المحفظة + ابدأ الآن + قم بإعداد رصيد الإنفاق الخاص بك للاستمتاع بمعاملات فورية ورخيصة مع الأصدقاء والعائلة والتجار. + تفعيل\nLightning\n<accent>المدفوعات</accent> يرجى الانتظار أثناء ترحيل بيانات محفظتك القديمة إلى إصدار Bitkit الجديد. عادةً ما يستغرق هذا أقل من دقيقة. ترحيل المحفظة جارٍ ترحيل\n<accent>المحفظة</accent> @@ -437,11 +440,11 @@ تواصل مع الدعم لقد غيّرت رمز PIN بنجاح إلى تركيبة جديدة من 4 أرقام. تم تغيير PIN - يرجى إعادة كتابة رمز PIN المكون من 4 أرقام لإكمال عملية الإعداد. + أعد كتابة رقم PIN المكون من 4 أرقام لإكمال الإعداد وحفظ رقم PIN الجديد. أعد كتابة PIN الجديد يرجى استخدام رمز PIN ستتذكره. إذا نسيت رمز PIN، يمكنك إعادة تعيينه، لكن ذلك سيتطلب استعادة محفظتك. تعيين PIN جديد - يمكنك تغيير رمز PIN إلى تركيبة\nجديدة من 4 أرقام. يرجى إدخال رمز PIN الحالي أولاً. + أدخل رقم PIN <accent>الحالي</accent> لتغييره. تغيير PIN حاول مرة أخرى، هذا ليس نفس رمز PIN. عرض عبارة الاسترداد @@ -458,7 +461,8 @@ فشل تحميل عبارة الاسترداد أجهزة متعددة لا تستخدم عبارة الاسترداد الخاصة بـ Bitkit في هواتف متعددة في وقت واحد، لأن هذا قد يفسد بياناتك. - <accent>لا تشارك</accent> عبارة الاسترداد أبدًا مع أي شخص لأن ذلك قد يؤدي إلى خسارة الأموال. + تأكد من عدم رؤية أي شخص لشاشتك. <accent>لا تشارك عبارة الاسترداد أبدًا</accent> مع أي شخص، لأن ذلك قد يؤدي إلى خسارة الأموال. + <accent>لا يمكن لـ Bitkit الوصول إلى أموالك ولا يمكنه المساعدة في استردادها</accent> إذا فقدت عبارة الاسترداد. احتفظ بها في مكان آمن! عبارة الاسترداد نجاح تأكد من تخزين عبارة الاسترداد في <accent>مكان آمن</accent>، لأن هذه هي <accent>الطريقة الوحيدة لاسترداد</accent> أموالك! @@ -477,8 +481,9 @@ اختر رمز PIN من 4 أرقام يرجى استخدام رمز PIN ستتذكره. إذا نسيت رمز PIN، يمكنك إعادة تعيينه، لكن ذلك سيتطلب استعادة محفظتك. تعطيل PIN - رمز PIN مفعل حاليًا. إذا أردت تعطيل رمز PIN، تحتاج إلى إدخال رمز PIN الحالي أولاً. - تعطيل PIN + أدخل رقم <accent>PIN</accent> لتعطيله. + رمز PIN مفعل حالياً. إذا كنت تريد تعطيله أو تغييره، يجب إدخال رمز PIN الحالي أولاً. + PIN مفعل يرجى إدخال رمز PIN إعادة التعيين (يتطلب عبارة الاسترداد) نسيت رمز PIN؟ أعد التعيين واستعد محفظة Bitkit باستخدام عبارة الاسترداد. عيّن رمز PIN جديد بعد إكمال الاستعادة. @@ -511,6 +516,7 @@ مسح التطبيق تمت إعادة تعيين Bitkit وحذف جميع بيانات المحفظة. تم حذف بيانات المحفظة + تفعيل PIN قانوني مشاركة غيّر محفظتك، غيّر العالم. حمّل Bitkit لـ iPhone {appStoreUrl} أو Android {playStoreUrl} @@ -548,13 +554,15 @@ اتصالات Lightning عقدة Lightning نعم، أعد التعيين - هل أنت متأكد من رغبتك في إعادة تعيين الاقتراحات؟ ستظهر مرة أخرى في حال إزالتها من نظرة عامة محفظة Bitkit. + هل أنت متأكد من رغبتك في إعادة تعيين الاقتراحات؟ ستظهر مرة أخرى في حال إزالتها. إعادة تعيين الاقتراحات؟ Rapid-Gossip-Sync الشبكات أخرى المدفوعات إعادة تعيين الاقتراحات + تلقائي + تصحيح متقدم إيصالات الاتصال الاتصالات @@ -567,14 +575,12 @@ الأدوات فشل Bitkit في نسخ بيانات المحفظة احتياطيًا. إعادة المحاولة خلال {interval, plural, one {# دقيقة} other {# دقائق}}. فشل النسخ الاحتياطي للبيانات - أحدث النسخ الاحتياطية للبيانات إعادة تعيين واستعادة المحفظة فشل النسخ الاحتياطي: {time} قيد التشغيل آخر نسخة احتياطية: {time} - النسخ الاحتياطي أو الاستعادة نسخ محفظتك احتياطيًا - النسخ الاحتياطي أو الاستعادة + نسخ البيانات الاحتياطية تخصيص في إعدادات Bitkit على Android المدفوعات في الخلفية معطلة، لأنك رفضت الإشعارات. المدفوعات في الخلفية مفعلة. يمكنك استلام الأموال حتى عندما يكون التطبيق مغلقًا (إذا كان جهازك متصلاً بالإنترنت). @@ -647,7 +653,10 @@ عرض المبالغ بـ نصيحة: بدّل بسرعة بين Bitcoin و {currency} بالنقر على رصيد محفظتك. الوحدة الافتراضية + الواجهة + المدفوعات عام + إعدادات النظام اللغة يجعل Bitkit QuickPay الدفع أسرع بالدفع التلقائي لرموز QR عند مسحها. مدفوعات\n<accent>سلسة</accent> @@ -664,13 +673,16 @@ عند التفعيل، يمكنك استخدام {biometryTypeName} بدلاً من رمز PIN لفتح محفظتك أو إرسال المدفوعات. إخفاء الرصيد عند الفتح رمز PIN - تغيير رمز PIN + تغيير PIN معطل مفعل طلب PIN للمدفوعات اسحب الرصيد للإخفاء استخدم {biometryTypeName} بدلاً تحذير عند الإرسال فوق 100$ + النسخ الاحتياطي أو إعادة التعيين + الخصوصية + الأمان الأمان والخصوصية الإعدادات فشل إكمال النسخة الاحتياطية الكاملة @@ -712,10 +724,17 @@ الدعم تم الإرسال بنجاح فشل الإرسال + Bitkit من تصميم Synonym Software, S.A. DE C.V. ©2025. جميع الحقوق محفوظة. الدعم الأدوات عرض عناوين الأدوات الأدوات + إعادة تعيين بطاقات الاقتراحات + إعادة تعيين الأدوات + هل أنت متأكد من رغبتك في إعادة تعيين الأدوات؟ سيتم عرض مجموعة الأدوات الافتراضية بالإعدادات الافتراضية. + إعادة تعيين الأدوات؟ + العرض + إعادة التعيين إلى الافتراضي امتلك\n<accent>ملفك الشخصي</accent> أعد ملفك الشخصي العام وروابطك، حتى تتمكن جهات اتصال Bitkit من الوصول إليك أو الدفع لك في أي وقت وأي مكان. الملف الشخصي @@ -829,6 +848,7 @@ الإعدادات تسوّق حالة التطبيق + الدعم المحفظة الأدوات عنوان إرسال Bitcoin غير صالح diff --git a/app/src/main/res/values-b+es+419/strings.xml b/app/src/main/res/values-b+es+419/strings.xml index ca8c7bd89..9dbd3625a 100644 --- a/app/src/main/res/values-b+es+419/strings.xml +++ b/app/src/main/res/values-b+es+419/strings.xml @@ -255,6 +255,9 @@ Conectado y sincronizando... Por favor, espere a que Bitkit se conecte a la red de pagos (±10 seg.). Saldos de billetera + Comenzar + Configure su saldo de gastos para disfrutar de transacciones instantáneas y económicas con amigos, familiares y comerciantes. + Habilitar\nLightning\n<accent>pagos</accent> Por favor espera mientras se migran los datos de tu billetera anterior a esta nueva versión de Bitkit. Esto normalmente tarda menos de un minuto. Migración de billetera MIGRANDO\n<accent>BILLETERA</accent> @@ -437,11 +440,11 @@ Contactar a soporte Ha cambiado correctamente su PIN por una nueva combinación de 4 dígitos. PIN cambiado - Vuelva a introducir su PIN de 4 dígitos para completar el proceso de configuración. + Vuelva a introducir su PIN de 4 dígitos para completar la configuración y guardar su nuevo PIN. Reintroducir nuevo PIN Por favor, utilice un PIN que recuerde. Si olvida su PIN puede restablecerlo, pero para ello tendrá que resetear su billetera. Establecer nuevo PIN - Puede cambiar su código PIN por una nueva\ncombinación de 4 dígitos. Por favor, introduzca primero su código PIN actual. + Introduzca su PIN <accent>actual</accent> para cambiarlo. Cambiar PIN Inténtelo de nuevo, no es el mismo PIN. Mostrar frase semilla @@ -458,7 +461,8 @@ Error al cargar frase mnemónica Múltiples Dispositivos No utilice su frase de recuperación Bitkit en varios teléfonos simultáneamente, ya que esto puede corromper sus datos. - <accent>Nunca comparta</accent> su frase de recuperación con nadie, ya que podría perder los fondos. + Asegúrese de que nadie pueda ver su pantalla. <accent>Nunca comparta su frase de recuperación</accent> con nadie, ya que podría resultar en la pérdida de fondos. + <accent>Bitkit no puede acceder a sus fondos ni ayudar a recuperarlos</accent> si pierde su frase de recuperación. ¡Guárdela en un lugar seguro! Frase semilla Éxito Asegúrese de guardar su frase de recuperación en un <accent>lugar seguro</accent>, ¡ya que es la <accent>única forma de recuperar</accent> su dinero! @@ -477,8 +481,9 @@ Elija el PIN de 4 dígitos Por favor, utilice un PIN que recuerde. Si olvida su PIN puede restablecerlo, pero para ello tendrá que restaurar su billetera. Desactivar PIN - El código PIN está actualmente activado. Si desea desactivar su PIN, deberá introducir primero su código PIN actual. - Desactivar PIN + Introduzca su <accent>PIN</accent> para desactivarlo. + El código PIN está actualmente activado. Si desea desactivarlo o cambiarlo, primero debe introducir su código PIN actual. + PIN activado Por favor, introduzca su código PIN Resetear (requiere frase de recuperación) ¿Olvidaste tu PIN? Restablece y recupera tu billetera de Bitkit con tu frase de recuperación. Establece un nuevo PIN al finalizar. @@ -511,6 +516,7 @@ Eliminar datos de la app Bitkit ha sido reseteado y todos los datos han sido borrados. Datos de la billetera borrados + Habilitar PIN Legal Compartir Cambie su cartera, cambie el mundo. Descargar Bitkit para iPhone {appStoreUrl} o Android {playStoreUrl} @@ -548,13 +554,15 @@ Conexiones Lightning Nodo Lightning Sí, Restaurar - ¿Está seguro de que desea restablecer las sugerencias? Volverán a aparecer en caso de que las haya eliminado de la vista general de su monedero Bitkit. + ¿Está seguro de que desea restablecer las sugerencias? Volverán a aparecer en caso de que las haya eliminado. ¿Restablecer sugerencias? Rapid-Gossip-Sync Redes Otros Pagos Resetear sugerencias + Auto + Debug Avanzado Recibos de conexión Conexiones @@ -567,14 +575,12 @@ Widgets Bitkit no pudo respaldar los datos de la wallet. Reintentando en {interval, plural, one {# minuto} other {# minutos}}. Falla en la copia de seguridad de los datos - últimas copias de seguridad Restablecer y resetear billetera Copia de seguridad falló: {time} Ejecutándose Última Copia de Seguridad: {time} - Copia de seguridad o restauración Haga una copia de seguridad de su cartera - Copia de seguridad o restauración + Copias de seguridad Personalizar en Ajustes de Bitkit en Android Los pagos en segundo plano están desactivados porque has denegado las notificaciones. Los pagos en segundo plano están activados. Puedes recibir fondos incluso cuando la app está cerrada (si tu dispositivo está conectado a internet). @@ -647,7 +653,10 @@ Mostrar importes en Truco: Cambia rápidamente entre Bitcoin y {currency} tocando sobre el balance del monedero. Unidad por defecto + Interfaz + Pagos Geral + Ajustes del sistema Idioma Bitkit QuickPay agiliza el proceso de pago mediante el pago automático de los códigos QR al ser escaneados. <accent>Pagos</accent>\nsin fricción @@ -664,13 +673,16 @@ Cuando está activado, puedes usar {biometryTypeName} en lugar de tu PIN para desbloquear tu billetera o enviar pagos. Ocultar saldo al abrir Código PIN - Cambiar código PIN + Cambiar PIN Disabilitado Activado Exigir PIN para pagamientos Deslizar la balanza para ocultar Usar {biometryTypeName} en su lugar Advertir al enviar más de $100 usd + Respaldar o restablecer + Privacidad + Seguridad Seguridad y Privacidad Ajustes No se ha podido completar una copia de seguridad completa @@ -712,10 +724,17 @@ Soporte Enviado con éxito Fallo en el envío + Bitkit fue creado por Synonym Software, S.A. DE C.V. ©2025. Todos los derechos reservados. Soporte Widgets Mostrar títulos de widgets Widgets + Restablecer tarjetas de sugerencias + Restablecer widgets + ¿Está seguro de que desea restablecer los widgets? Se mostrará el conjunto de widgets predeterminado con las configuraciones predeterminadas. + ¿Restablecer widgets? + Pantalla + Restablecer valores predeterminados Sea dueño de su\n<accent>perfil</accent> Configura tu perfil público y tus enlaces para que tus contactos en Bitkit puedan encontrarte o pagarte en cualquier momento y lugar. Perfil @@ -829,6 +848,7 @@ Ajustes Tienda Estatus del App + Soporte Billetera Widgets Dirección de envío de bitcoin inválida diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 5de9b0ea5..c30193647 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -255,6 +255,9 @@ Connectant i sincronitzant... Si us plau, espereu que Bitkit es connecti a la xarxa de pagament (±10 segons). Saldos de la cartera + Comença + Configura el teu saldo de despeses per gaudir de transaccions instantànies i econòmiques amb amics, familiars i comerciants. + Habilitar\nLightning\n<accent>pagaments</accent> Si us plau, espera mentre les dades de la teva antiga cartera es migren a aquesta nova versió de Bitkit. Normalment triga menys d\'un minut. Migració de cartera MIGRANT\n<accent>CARTERA</accent> @@ -437,11 +440,11 @@ Contacta amb el suport Has canviat correctament el teu PIN a una nova combinació de 4 dígits. El PIN ha canviat - Si us plau, torna a escriure el teu PIN de 4 dígits per completar el procés de configuració. + Torna a escriure el teu PIN de 4 dígits per completar la configuració i desar el teu nou PIN. Torna a escriure el nou PIN Si us plau, utilitza un PIN que recordis. Si oblides el teu PIN pots restablir-lo, però caldrà restaurar la cartera. Configura un nou PIN - Pots canviar el teu codi PIN a una nova\ncombinació de 4 dígits. Si us plau, introdueix primer el teu codi PIN actual. + Introdueix el teu PIN <accent>actual</accent> per canviar-lo. Canvia el PIN Torna a provar, això no és el mateix PIN. Mostrar frase de recuperació @@ -458,7 +461,8 @@ Error en carregar la frase mnemotècnica Múltiples dispositius No utilitzis la teva frase de recuperació de Bitkit en múltiples telèfons simultàniament, ja que això pot corrompre les teves dades. - <accent>Mai comparteixis</accent> la teva frase de recuperació amb ningú, ja que això pot resultar en la pèrdua de fons. + Assegura\'t que ningú pugui veure la teva pantalla. <accent>Mai comparteixis la teva frase de recuperació</accent> amb ningú, ja que pot resultar en la pèrdua de fons. + <accent>Bitkit no pot accedir als teus fons ni ajudar-te a recuperar-los</accent> si perds la teva frase de recuperació. Guarda-la en un lloc segur! Frase Mnemotècnica Exitós Assegura\'t de guardar la teva frase de recuperació en un <accent>lloc segur</accent>, ja que aquesta és l\'<accent>única manera de recuperar</accent> els teus diners! @@ -477,8 +481,9 @@ Escolliu un PIN de 4 dígits Si us plau, utilitza un PIN que recordis. Si oblides el teu PIN pots restablir-lo, però caldrà restaurar la cartera. Desactiva el PIN - El codi PIN està actualment habilitat. Si vols desactivar el teu codi PIN, has d\'introduir primer el teu codi PIN actual. - Desactiva el PIN + Introdueix el teu <accent>PIN</accent> per desactivar-lo. + El codi PIN està activat. Si vols desactivar-lo o canviar-lo, primer has d\'introduir el teu codi PIN actual. + PIN activat Si us plau, introduïu el vostre codi PIN Restablir (Es necessita la frase de recuperació) Has oblidat el PIN? Restableix i recupera la teva cartera Bitkit amb la teva frase de recuperació. Configura un nou PIN després de completar la recuperació. @@ -511,6 +516,7 @@ Esborra l\'aplicació Bitkit s\'ha restablert i totes les dades de la cartera s\'han eliminat. Dades de la cartera eliminades + Habilitar PIN Legal Compartir Canvia la teva cartera, canvia el món. Descarrega Bitkit per a iPhone {appStoreUrl} o Android {playStoreUrl} @@ -548,13 +554,15 @@ Connexions Lightning Node de Lightning Sí, reinicia - Estàs segur que vols restablir els suggeriments? Tornaran a aparèixer si els has eliminat del resum de la teva cartera Bitkit. + Estàs segur que vols restablir els suggeriments? Tornaran a aparèixer si els has eliminat. Restablir suggeriments? Rapid-Gossip-Sync Xarxes Altres Pagaments Restablir suggeriments + Auto + Debug Avançat Rebuts de connexió Connexions @@ -567,14 +575,12 @@ Ginys Bitkit no ha pogut fer còpia de seguretat de les dades de la cartera. Reintentant en {interval, plural, one {# minut} other {# minuts}}. Error de còpia de seguretat de dades - últimes còpies de seguretat de dades Restablir i restaurar cartera Còpia de seguretat fallida: {time} Executant Última còpia de seguretat: {time} - Còpia de seguretat o restauració Fes còpia de seguretat de la cartera - Còpia de seguretat o restauració + Còpies de seguretat Personalitza a la configuració de Bitkit d\'Android Els pagaments en segon pla estan desactivats perquè has denegat les notificacions. Els pagaments en segon pla estan habilitats. Pots rebre fons fins i tot quan l\'aplicació està tancada (si el teu dispositiu està connectat a Internet). @@ -647,7 +653,10 @@ Mostra les quantitats en Consell: Canvia ràpidament entre Bitcoin i {currency} tocant el saldo de la teva cartera. Unitat per defecte + Interfície + Pagaments General + Configuració del sistema Idioma Bitkit QuickPay fa que pagar sigui més ràpid pagant automàticament els codis QR quan s\'escanegen. <accent>Pagaments</accent>\nsense friccions @@ -664,13 +673,16 @@ Quan està habilitat, pots utilitzar {biometryTypeName} en lloc del teu codi PIN per desbloquejar la cartera o enviar pagaments. Amagar saldo en obrir Codi PIN - Canviar el codi PIN + Canviar PIN Desactivat Activat Requereix un PIN per als pagaments Lliscar saldo per amagar Utilitza {biometryTypeName} en lloc Avisar quan s\'enviïn més de 100 $ + Còpia de seguretat o restablir + Privacitat + Seguretat Seguretat i privacitat Configuracions No s\'ha pogut completar una còpia de seguretat completa @@ -712,10 +724,17 @@ Suport Enviat correctament Error en enviar + Bitkit ha estat creat per Synonym Software, S.A. DE C.V. ©2025. Tots els drets reservats. Suport Ginys Mostrar títols de ginys Ginys + Restablir targetes de suggeriments + Restablir widgets + Estàs segur que vols restablir els widgets? Es mostrarà el conjunt de widgets predeterminat amb les configuracions predeterminades. + Restablir widgets? + Pantalla + Restablir valors predeterminats Sigues propietari\n<accent>del teu perfil</accent> Configura el teu perfil públic i enllaços, perquè els teus contactes de Bitkit puguin contactar-te o pagar-te en qualsevol moment i lloc. Perfil @@ -829,6 +848,7 @@ Configuracions Botiga Estat de l\'aplicació + Suport Cartera Ginys Adreça d\'enviament de Bitcoin no vàlida diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index b696d62b3..8f9d457f2 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -255,6 +255,9 @@ Připojování & synchronizace... Prosím vyčkejte, až se Bitkit připojí k platební síti (±10 sekund). Zůstatky peněženky + Začít + Nastavte si zůstatek pro výdaje a užívejte si okamžité a levné transakce s přáteli, rodinou a obchodníky. + Povolit\nLightning\n<accent>platby</accent> Počkejte prosím, než budou vaše stará data peněženky migrována do této nové verze Bitkit. Obvykle to trvá méně než minutu. Migrace peněženky MIGRACE\nPENĚŽENKY @@ -437,11 +440,11 @@ Kontaktovat podporu Úspěšně jste změnili svůj PIN na novou 4místnou kombinaci. PIN změněn - Chcete-li dokončit proces nastavení, zadejte znovu svůj 4místný kód PIN. + Zadejte znovu svůj 4místný PIN pro dokončení nastavení a uložení nového PIN. Znovu zadejte nový PIN Použijte prosím PIN, který si zapamatujete. Pokud svůj PIN zapomenete, můžete jej resetovat, ale to bude vyžadovat obnovení vaší peněženky. Nastavte nový PIN - Svůj PIN kód můžete změnit na novou\n4místnou kombinaci. Nejprve zadejte svůj aktuální PIN kód. + Zadejte svůj <accent>aktuální</accent> PIN pro jeho změnu. Změna PIN Zkuste to znovu, toto není stejný PIN. Zobrazit seed frázi @@ -458,7 +461,8 @@ Nepodařilo se načíst mnemotechnickou frázi Více zařízení Nepoužívejte obnovovací frázi Bitkit na více telefonech současně, protože by to mohlo poškodit vaše data. - <accent>Nikdy nikomu nesdělujte</accent> svou frázi pro obnovení, protože to může vést ke ztrátě finančních prostředků. + Ujistěte se, že nikdo nevidí vaši obrazovku. <accent>Nikdy nikomu nesdělujte svou frázi pro obnovení</accent>, protože to může vést ke ztrátě prostředků. + <accent>Bitkit nemůže přistupovat k vašim prostředkům ani pomoci s jejich obnovením</accent>, pokud ztratíte svou frázi pro obnovení. Uchovejte ji v bezpečí! Mnemotechnická fráze Úspěšné Ujistěte se, že jste uložili frázi pro obnovení na <accent>bezpečném místě</accent>, protože to je <accent>jediný způsob, jak získat</accent> své peníze <accent>zpět</accent>! @@ -477,8 +481,9 @@ Vyberte 4místný PIN Použijte prosím PIN, který si zapamatujete. Pokud svůj PIN zapomenete, můžete jej resetovat, ale to bude vyžadovat obnovení vaší peněženky. Zakázat PIN - PIN kód je aktuálně povolen. Pokud chcete deaktivovat svůj PIN, musíte nejprve zadat svůj aktuální PIN kód. - Zakázat PIN + Zadejte svůj <accent>PIN</accent> pro jeho deaktivaci. + PIN kód je aktuálně povolen. Pokud jej chcete deaktivovat nebo změnit, musíte nejprve zadat aktuální PIN kód. + PIN povolen Zadejte prosím svůj PIN kód Resetovat (vyžaduje obnovovací frázi) Zapomněli jste svůj PIN? Resetujte a obnovte svou peněženku Bitkit pomocí vaší obnovovací fráze. Po dokončení obnovení nastavte nový PIN. @@ -511,6 +516,7 @@ Vymazat aplikaci Bitkit byla resetována a všechna data peněženky byla vymazána. Odstranění dat peněženky + Povolit PIN Právní Sdílet Změňte svou peněženku, změňte svět. Stáhněte si Bitkit pro iPhone {appStoreUrl} nebo Android {playStoreUrl} @@ -548,13 +554,15 @@ Lightning spojení Lightning uzel Ano, resetovat - Opravdu chcete návrhy resetovat? Znovu se objeví v případě, že jste je odstranili z přehledu peněženky Bitkit. + Opravdu chcete návrhy resetovat? Znovu se objeví v případě, že jste je odstranili. Resetovat návrhy? Rapid-Gossip-Sync Sítě Další Platby Resetovat návrhy + Auto + Debug Pokročilé Potvrzení o připojení Spojení @@ -567,14 +575,12 @@ Widgety Bitkitu se nepodařilo zálohovat data peněženky. Opakovaný pokus za {interval, plural, one {# minute} dalších {# minutes}}. Zálohování dat selhalo - nejnovější zálohy dat Resetovat a obnovit peněženku Zálohování se nezdařilo: {time} Probíhá Poslední záloha: {time} - Záloha nebo obnovení Zálohujte svou peněženku - Záloha nebo obnovení + Zálohy dat Přizpůsobit v nastavení Android Bitkit Platby na pozadí jsou zakázány, protože jste odmítli oznámení. Platby na pozadí jsou povoleny. Prostředky můžete přijímat i při zavřené aplikaci (pokud je zařízení připojeno k internetu). @@ -647,7 +653,10 @@ Zobrazit částky v Tip: Klepnutím na zůstatek v peněžence můžete rychle přepínat mezi bitcoiny a {currency}. Výchozí jednotka + Rozhraní + Platby Obecné + Systémové nastavení Jazyk Služba Bitkit QuickPay urychluje odbavení tím, že po naskenování QR kódu automaticky zaplatí. <accent>Okamžité</accent>\nplatby @@ -664,13 +673,16 @@ Pokud povoleno, můžete pro odemčení peněženky nebo zaslání platby použít {biometryTypeName} namísto PIN kódu. Skrýt zůstatek při otevření PIN kód - Změna PIN kódu + Změna PIN Zakázáno Povoleno Při platbě vyžadovat PIN Skrýt zůstatek přejetím Namísto toho použít {biometryTypeName} Upozornit při odesílání více než $100 + Zálohovat nebo resetovat + Soukromí + Bezpečnost Bezpečnost a soukromí Nastavení Selhalo dokončení úplné zálohy @@ -712,10 +724,17 @@ Podpora Úspěšně odesláno Odeslání se nezdařilo + Bitkit vytvořila společnost Synonym Software, S.A. DE C.V. ©2025. Všechna práva vyhrazena. Podpora Widgety Zobrazit názvy widgetů Widgety + Resetovat karty návrhů + Resetovat widgety + Opravdu chcete resetovat widgety? Zobrazí se výchozí sada widgetů s výchozím nastavením. + Resetovat widgety? + Zobrazení + Obnovit výchozí Vlastněte svůj\n<accent>profil</accent> Nastavte si veřejný profil a odkazy, aby vás kontakty ze služby Bitkit mohly kdykoli a kdekoli kontaktovat nebo vám zaplatit. Profil @@ -829,6 +848,7 @@ Nastavení Nakoupit Stav aplikace + Podpora Peněženka Widgety Neplatná adresa pro odeslání bitcoinu diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index fca4e9751..b3e55d72f 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -255,6 +255,9 @@ Bitte warte bis sich Bitkit mit dem Zahlungsnetzwerk verbunden hat (±10 Sekunden). Verbinden & Synchronisieren… Wallet-Guthaben + Loslegen + Richte dein Ausgabenguthaben ein, um sofortige und günstige Transaktionen mit Freunden, Familie und Händlern zu genießen. + Lightning\n<accent>Zahlungen</accent>\naktivieren Bitte warte, während deine alten Wallet-Daten auf diese neue Bitkit-Version migriert werden. Das dauert normalerweise weniger als eine Minute. Wallet-Migration WALLET\n<accent>MIGRATION</accent> @@ -440,7 +443,8 @@ Schreibe diese {length} Wörter in der richtigen Reihenfolge auf und bewahre sie an einem sicheren Ort auf. Verwende die folgenden 12 Wörter, um dein Geld zu einem späteren Zeitpunkt wiederherzustellen. Tippen zum Anzeigen - <accent>Teile</accent> deine Wiederherstellungsphrase <accent>niemals</accent> mit jemandem, da dies zum Verlust von Geldern führen kann. + Stelle sicher, dass niemand deinen Bildschirm sehen kann. <accent>Teile deine Wiederherstellungsphrase niemals</accent> mit jemandem, da dies zum Verlust von Geldern führen kann. + <accent>Bitkit kann nicht auf deine Gelder zugreifen und dir nicht bei der Wiederherstellung helfen</accent>, wenn du deine Wiederherstellungsphrase verlierst. Bewahre sie sicher auf! Wiederherstellungsphrase bestätigen Tippe die 12 Wörter in der richtigen Reihenfolge an. Erfolgreich @@ -463,8 +467,9 @@ 4-stelligen PIN erneut eingeben Bitte gib deinen 4-stelligen PIN erneut ein, um den Einrichtungsprozess abzuschließen. Versuche es erneut, dieser PIN stimmt nicht überein. - PIN deaktivieren - Der PIN-Code ist derzeit aktiviert. Wenn du deinen PIN deaktivieren möchtest, musst du zuerst deinen aktuellen PIN-Code eingeben. + Der PIN-Code ist derzeit aktiviert. Wenn du ihn deaktivieren oder ändern möchtest, musst du zuerst deinen aktuellen PIN-Code eingeben. + PIN aktiviert + Geben Sie Ihre <accent>PIN</accent> ein, um sie zu deaktivieren. PIN deaktivieren Bitte gib deinen PIN-Code ein Letzter Versuch. Wenn du den falschen PIN erneut eingibst, wird dein Wallet zurückgesetzt. @@ -500,9 +505,9 @@ Support kontaktieren App löschen PIN ändern - Du kannst deinen PIN-Code in eine neue\n4-Ziffern-Kombination ändern. Bitte gib zuerst deinen aktuellen PIN-Code ein. + Geben Sie Ihre <accent>aktuelle</accent> PIN ein, um sie zu ändern. Neuen PIN erneut eingeben - Bitte gib deinen 4-stelligen PIN erneut ein, um den Einrichtungsprozess abzuschließen. + Bitte gib deinen 4-stelligen PIN erneut ein, um die Einrichtung abzuschließen und deinen neuen PIN zu speichern. Neuen PIN festlegen Bitte verwende einen PIN, den du dir merken wirst. Wenn du deinen PIN vergisst, kannst du ihn zurücksetzen, aber das erfordert die Wiederherstellung deines Wallets. Versuche es erneut, dieser PIN stimmt nicht überein. @@ -511,6 +516,7 @@ PIN-Code verwenden Wallet Bitkit wurde zurückgesetzt und alle Wallet-Daten wurden gelöscht. + PIN aktivieren Einstellungen Entwickler-Einstellungen aktiviert Entwickler-Einstellungen sind jetzt in der gesamten App aktiviert. @@ -518,10 +524,10 @@ Entwickler-Einstellungen sind jetzt in der gesamten App deaktiviert. Allgemein Sicherheit und Privatsphäre - Backup und Wiederherstellung Fortgeschritten Über Support + Systemeinstellungen Sprache Über Bitkit Danke, dass Sie ein verantwortungsvoller Bitcoiner sind.\nÄndern Sie Ihr Portemonnaie, ändern Sie die Welt.\n\nBitkit gibt Ihnen die Schlüssel zu Ihrem Geld, Profil, Kontakten und Webkonten.\n\nBitkit wurde von Synonym Software Ltd. entwickelt. @@ -551,9 +557,17 @@ ₿ {feeSats} für die durschnittliche Transaktion ({fiatSymbol}{fiatFormatted}) Tags Früher verwendete Tags + Oberfläche + Zahlungen Widgets Widgets Titel anzeigen + Vorschlagskarten zurücksetzen + Widgets zurücksetzen + Bist du sicher, dass du die Widgets zurücksetzen möchtest? Die Standard-Widgets mit Standardkonfigurationen werden angezeigt. + Widgets zurücksetzen? + Anzeige + Auf Standard zurücksetzen QuickPay <accent>Reibungslos</accent>\nZahlungen Bitkit QuickPay beschleunigt den Bezahlvorgang, indem es QR-Codes beim Scannen automatisch bezahlt. @@ -583,18 +597,19 @@ Lesen der Zwischenablage erlauben Warnen beim Senden von über $100 PIN Code - PIN Code ändern + PIN ändern PIN für Zahlungen anfordern Aktiviert Deaktiviert Nutze {biometryTypeName} Wenn eingeschaltet, kannst du {biometryTypeName} an Stelle von deinem PIN code verwenden, um dein Wallet zu entsperren oder Zahlungen zu senden. - Backup und Wiederherstellung + Sichern oder zurücksetzen + Datenschutz + Sicherheit Sichere dein Wallet Wallet zurücksetzen Backup fehlgeschlagen Bitkit konnte die Wallet-Daten nicht sichern. Erneuter Versuch in {interval, plural, one {# Minute} other {# Minuten}}. - Letzte Backups Gescheitertes Backup: {time} Läuft Letztes Backup: {time} @@ -607,6 +622,7 @@ Tags Profil Kontakte + Datensicherungen Support Support-Links konnten nicht geöffnet werden Brauchst du Hilfe? Melde dein Problem von innerhalb Bitkit, besuche das Hilfezentrum, überprüfe den Status, oder kontaktiere uns auf den sozialen Medien. @@ -625,6 +641,7 @@ Senden fehlgeschlagen Es gab ein Problem beim Senden deiner Nachricht. Bitte versuche es erneut. Erneut versuchen + Bitkit wurde von Synonym Software, S.A. DE C.V. ©2025 entwickelt. Alle Rechte vorbehalten. App Status Internet Verbunden @@ -667,12 +684,14 @@ Adressanzeige Vorschläge zurücksetzen Vorschläge zurücksetzen? - Bist du dir sicher, dass du die Vorschläge löschen möchtest? Diese werden erneut angezeigt, wenn du sie aus deiner Bitkit Wallet Übersicht entfernt hast. + Bist du dir sicher, dass du die Vorschläge löschen möchtest? Diese werden erneut angezeigt, wenn du sie entfernt hast. Ja, zurücksetzen Lightning Verbindungen Lightning Node Electrum-Server Rapid-Gossip-Sync + Auto + Debug Schnell (teurer) Schnell ± 10-20 Minuten @@ -760,6 +779,7 @@ Shop Einstellungen App Status + Support Senden Empfangen Bitcoin senden diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 9fdc4d61b..379545f73 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -255,6 +255,9 @@ Σύνδεση & Συγχρονισμός... Περίμενε το Bitkit να συνδεθεί στο δίκτυο πληρωμών (±10 δευτερόλεπτα). Υπόλοιπα πορτοφολιού + Ξεκινήστε + Ρυθμίστε το υπόλοιπο δαπανών σας για να απολαύσετε άμεσες και φθηνές συναλλαγές με φίλους, οικογένεια και εμπόρους. + Ενεργοποίηση\nLightning\n<accent>πληρωμών</accent> Περίμενε όσο τα παλιά δεδομένα του πορτοφολιού μεταφέρονται σε αυτή τη νέα έκδοση του Bitkit. Αυτό συνήθως διαρκεί λιγότερο από ένα λεπτό. Μετάβαση πορτοφολιού ΜΕΤΑΒΑΣΗ\n<accent>ΠΟΡΤΟΦΟΛΙΟΥ</accent> @@ -437,11 +440,11 @@ Επικοινωνία με υποστήριξη Άλλαξες επιτυχώς το PIN σου σε νέο συνδυασμό 4 ψηφίων. Το PIN άλλαξε - Πληκτρολόγησε ξανά το 4ψήφιο PIN για να ολοκληρώσεις τη διαδικασία ρύθμισης. + Πληκτρολόγησε ξανά το 4ψήφιο PIN για να ολοκληρώσεις τη ρύθμιση και να αποθηκεύσεις το νέο PIN. Επανάληψη νέου PIN Χρησιμοποίησε ένα PIN που θα θυμάσαι. Αν ξεχάσεις το PIN σου μπορείς να το επαναφέρεις, αλλά αυτό θα απαιτήσει επαναφορά του πορτοφολιού σου. Ορισμός νέου PIN - Μπορείς να αλλάξεις τον κωδικό PIN σου σε νέο\nσυνδυασμό 4 ψηφίων. Εισάγαγε πρώτα τον τρέχοντα κωδικό PIN. + Εισαγάγετε το <accent>τρέχον</accent> PIN σας για να το αλλάξετε. Αλλαγή PIN Δοκίμασε ξανά, δεν είναι το ίδιο PIN. Εμφάνιση φράσης seed @@ -458,7 +461,8 @@ Αποτυχία φόρτωσης mnemonic Πολλαπλές συσκευές Μη χρησιμοποιείς τη φράση ανάκτησης του Bitkit σε πολλά τηλέφωνα ταυτόχρονα, καθώς αυτό μπορεί να καταστρέψει τα δεδομένα σου. - <accent>Μην μοιράζεσαι ποτέ</accent> τη φράση ανάκτησής σου με κανέναν, καθώς αυτό μπορεί να οδηγήσει σε απώλεια κεφαλαίων. + Βεβαιώσου ότι κανείς δεν μπορεί να δει την οθόνη σου. <accent>Μην μοιράζεσαι ποτέ τη φράση ανάκτησής σου</accent> με κανέναν, καθώς μπορεί να οδηγήσει σε απώλεια κεφαλαίων. + <accent>Το Bitkit δεν μπορεί να αποκτήσει πρόσβαση στα κεφάλαιά σου ούτε να βοηθήσει στην ανάκτησή τους</accent> εάν χάσεις τη φράση ανάκτησής σου. Κράτησέ την ασφαλή! Φράση Mnemonic Επιτυχής Βεβαιώσου ότι αποθηκεύεις τη φράση ανάκτησής σου σε <accent>ασφαλές μέρος</accent>, καθώς αυτός είναι ο <accent>μόνος τρόπος να ανακτήσεις</accent> τα χρήματά σου! @@ -477,8 +481,9 @@ Επίλεξε 4ψήφιο PIN Χρησιμοποίησε ένα PIN που θα θυμάσαι. Αν ξεχάσεις το PIN σου μπορείς να το επαναφέρεις, αλλά αυτό θα απαιτήσει επαναφορά του πορτοφολιού σου. Απενεργοποίηση PIN - Ο κωδικός PIN είναι ενεργοποιημένος. Αν θέλεις να απενεργοποιήσεις το PIN, πρέπει να εισάγεις πρώτα τον τρέχοντα κωδικό PIN. - Απενεργοποίηση PIN + Εισαγάγετε το <accent>PIN</accent> σας για να το απενεργοποιήσετε. + Ο κωδικός PIN είναι ενεργοποιημένος. Αν θέλετε να τον απενεργοποιήσετε ή να τον αλλάξετε, πρέπει πρώτα να εισαγάγετε τον τρέχοντα κωδικό PIN. + PIN ενεργοποιημένο Εισάγαγε τον κωδικό PIN Επαναφορά (Απαιτείται φράση ανάκτησης) Ξέχασες το PIN; Επανάφερε και ανάκτησε το πορτοφόλι Bitkit με τη φράση ανάκτησής σου. Όρισε νέο PIN μετά την ολοκλήρωση της ανάκτησης. @@ -511,6 +516,7 @@ Διαγραφή εφαρμογής Το Bitkit επαναφέρθηκε και όλα τα δεδομένα πορτοφολιού διαγράφηκαν. Δεδομένα πορτοφολιού διαγράφηκαν + Ενεργοποίηση PIN Νομικά Διαμοιρασμός Άλλαξε το πορτοφόλι σου, άλλαξε τον κόσμο. Κατέβασε το Bitkit για iPhone {appStoreUrl} ή Android {playStoreUrl} @@ -548,13 +554,15 @@ Συνδέσεις Lightning Κόμβος Lightning Ναι, Επαναφορά - Είσαι σίγουρος ότι θέλεις να επαναφέρεις τις προτάσεις; Θα επανεμφανιστούν σε περίπτωση που τις είχες αφαιρέσει από την επισκόπηση του πορτοφολιού Bitkit. + Είσαι σίγουρος ότι θέλεις να επαναφέρεις τις προτάσεις; Θα επανεμφανιστούν σε περίπτωση που τις είχες αφαιρέσει. Επαναφορά προτάσεων; Rapid-Gossip-Sync Δίκτυα Άλλα Πληρωμές Επαναφορά προτάσεων + Αυτόματο + Debug Για προχωρημένους Αποδείξεις συνδέσεων Συνδέσεις @@ -567,14 +575,12 @@ Widgets Το Bitkit απέτυχε να δημιουργήσει αντίγραφο ασφαλείας δεδομένων πορτοφολιού. Νέα προσπάθεια σε {interval, plural, one {# λεπτό} other {# λεπτά}}. Αποτυχία αντιγράφου ασφαλείας δεδομένων - τελευταία αντίγραφα ασφαλείας δεδομένων Επαναφορά και ανάκτηση πορτοφολιού Αποτυχημένο αντίγραφο: {time} Εκτελείται Τελευταίο αντίγραφο: {time} - Αντίγραφο ασφαλείας ή επαναφορά Αντίγραφο ασφαλείας πορτοφολιού - Αντίγραφο ή επαναφορά + Αντίγραφα δεδομένων Προσαρμογή στις ρυθμίσεις Android Bitkit Οι πληρωμές παρασκηνίου είναι απενεργοποιημένες επειδή αρνήθηκες τις ειδοποιήσεις. Οι πληρωμές παρασκηνίου είναι ενεργοποιημένες. Μπορείς να λαμβάνεις κεφάλαια ακόμα και όταν η εφαρμογή είναι κλειστή (αν η συσκευή σου είναι συνδεδεμένη στο διαδίκτυο). @@ -647,7 +653,10 @@ Εμφάνιση ποσών σε Συμβουλή: Εναλλαγή γρήγορα μεταξύ Bitcoin και {currency} πατώντας στο υπόλοιπο του πορτοφολιού σου. Προεπιλεγμένη μονάδα + Διεπαφή + Πληρωμές Γενικά + Ρυθμίσεις συστήματος Γλώσσα Το Bitkit QuickPay κάνει την ολοκλήρωση αγοράς πιο γρήγορη πληρώνοντας αυτόματα κωδικούς QR κατά τη σάρωση. <accent>Απρόσκοπτες</accent>\nπληρωμές @@ -664,13 +673,16 @@ Όταν είναι ενεργοποιημένο, μπορείς να χρησιμοποιήσεις {biometryTypeName} αντί του κωδικού PIN για ξεκλείδωμα πορτοφολιού ή αποστολή πληρωμών. Απόκρυψη υπολοίπου στο άνοιγμα Κωδικός PIN - Αλλαγή κωδικού PIN + Αλλαγή PIN Απενεργοποιημένο Ενεργοποιημένο Απαίτηση PIN για πληρωμές Σύρε υπόλοιπο για απόκρυψη Χρήση {biometryTypeName} αντί Προειδοποίηση όταν στέλνεις πάνω από $100 + Δημιουργία αντιγράφου ή επαναφορά + Απόρρητο + Ασφάλεια Ασφάλεια και απόρρητο Ρυθμίσεις Αποτυχία ολοκλήρωσης πλήρους αντιγράφου ασφαλείας @@ -712,10 +724,17 @@ Υποστήριξη Στάλθηκε επιτυχώς Αποτυχία αποστολής + Το Bitkit δημιουργήθηκε από τη Synonym Software, S.A. DE C.V. ©2025. Με επιφύλαξη παντός δικαιώματος. Υποστήριξη Widgets Εμφάνιση τίτλων Widget Widgets + Επαναφορά καρτών προτάσεων + Επαναφορά widgets + Είσαι σίγουρος ότι θέλεις να επαναφέρεις τα widgets; Θα εμφανιστεί το προεπιλεγμένο σετ widgets με τις προεπιλεγμένες ρυθμίσεις. + Επαναφορά widgets; + Εμφάνιση + Επαναφορά προεπιλογών Κατέχεις το\n<accent>προφίλ σου</accent> Ρύθμισε το δημόσιο προφίλ και τους συνδέσμους σου, ώστε οι επαφές σου στο Bitkit να μπορούν να επικοινωνήσουν ή να σε πληρώσουν οποτεδήποτε, οπουδήποτε. Προφίλ @@ -829,6 +848,7 @@ Ρυθμίσεις Κατάστημα Κατάσταση εφαρμογής + Υποστήριξη Πορτοφόλι Widgets Μη έγκυρη διεύθυνση αποστολής bitcoin diff --git a/app/src/main/res/values-es-rES/strings.xml b/app/src/main/res/values-es-rES/strings.xml index 40dbb1df6..252257879 100644 --- a/app/src/main/res/values-es-rES/strings.xml +++ b/app/src/main/res/values-es-rES/strings.xml @@ -255,6 +255,9 @@ Conectando y Sincronizando... Por favor, espere a que Bitkit se conecte a la red de pagos (±10 segundos). Saldos del monedero + Comenzar + Configura tu saldo de gastos para disfrutar de transacciones instantáneas y económicas con amigos, familiares y comerciantes. + Habilitar\nLightning\n<accent>pagos</accent> Por favor espera mientras se migran los datos de tu antiguo monedero a esta nueva versión de Bitkit. Esto suele tardar menos de un minuto. Migración del monedero MIGRANDO\n<accent>MONEDERO</accent> @@ -437,11 +440,11 @@ Contactar con el servicio de asistencia Ha cambiado correctamente su PIN por una nueva combinación de 4 dígitos. PIN cambiado - Vuelva a introducir su PIN de 4 dígitos para completar el proceso de configuración. + Vuelva a introducir su PIN de 4 dígitos para completar la configuración y guardar su nuevo PIN. Reintroducir nuevo PIN Por favor, utilice un PIN que recuerde. Si olvida su PIN puede restablecerlo, pero para ello tendrá que restaurar su monedero. Establecer nuevo PIN - Puede cambiar su código PIN por una nueva\ncombinación de 4 dígitos. Por favor, introduzca primero su código PIN actual. + Introduzca su PIN <accent>actual</accent> para cambiarlo. Cambiar PIN Inténtelo de nuevo, no es el mismo PIN. Mostrar frase semilla @@ -458,7 +461,8 @@ Error al cargar el mnemónico Múltiples dispositivos No uses tu frase de recuperación de Bitkit en varios teléfonos simultáneamente, ya que esto puede corromper tus datos. - <accent>Nunca compartas</accent> tu frase de recuperación con nadie, ya que esto podría desembocar en la pérdida de fondos. + Asegúrate de que nadie pueda ver tu pantalla. <accent>Nunca compartas tu frase de recuperación</accent> con nadie, ya que podría resultar en la pérdida de fondos. + <accent>Bitkit no puede acceder a tus fondos ni ayudarte a recuperarlos</accent> si pierdes tu frase de recuperación. ¡Guárdala en un lugar seguro! Frase mnemotécnica Éxito Asegúrate de guardar tu frase de recuperación en un <accent>lugar seguro</accent>, ya que esta es la <accent>única forma de recuperar</accent> tu dinero. @@ -477,8 +481,9 @@ Elija un PIN de 4 dígitos Por favor, utilice un PIN que recuerde. Si olvida su PIN puede restablecerlo, pero para ello tendrá que restaurar su monedero. Desactivar PIN - El código PIN está actualmente activado. Si desea desactivar su PIN, deberá introducir primero su código PIN actual. - Desactivar PIN + Introduzca su <accent>PIN</accent> para desactivarlo. + El código PIN está actualmente activado. Si desea desactivarlo o cambiarlo, primero debe introducir su código PIN actual. + PIN activado Por favor, introduzca su código PIN Restablecer (requiere frase de recuperación) ¿Ha olvidado su PIN? Reinicie y recupere su monedero Bitkit con su frase de recuperación. Establezca un nuevo PIN después de completar la recuperación. @@ -511,6 +516,7 @@ Borrar App Bitkit ha sido restaurado y todos los datos de monedero han sido borrados. Datos de Monedero Borrados + Habilitar PIN Legal Compartir Cambia tu monedero, cambia el mundo. Descarga Bitkit para iPhone {appStoreUrl} o Android {playStoreUrl} @@ -548,13 +554,15 @@ Conexiones Lightning Nodo Lightning Sí, restaurar - ¿Estás seguro de que deseas restablecer las sugerencias? Volverán a aparecer en caso de que las hayas eliminado de la vista general de tu monedero Bitkit. + ¿Estás seguro de que deseas restablecer las sugerencias? Volverán a aparecer en caso de que las hayas eliminado. ¿Restablecer sugerencias? Rapid-Gossip-Sync Redes Otros Pagos Restablecer sugerencias + Auto + Debug Avanzado Registro de Conexiones Conexiones @@ -567,14 +575,12 @@ Widgets Bitkit no pudo hacer copia de seguridad de los datos del monedero. Reintentando en {interval, plural, one {# minuto} other {# minutos}}. Error en la copia de datos - últimas copias de datos Borrar y restaurar monedero Respaldo fallido: {time} En ejecución Último respaldo: {time} - Copia de seguridad o restaurar Respalda tu monedero - Copia de seguridad o restaurar + Copias de seguridad Personalizar en ajustes de Bitkit en Android Los pagos en segundo plano están desactivados porque has denegado las notificaciones. Los pagos en segundo plano están activados. Puedes recibir fondos incluso cuando la aplicación está cerrada (si tu dispositivo está conectado a internet). @@ -647,7 +653,10 @@ Mostrar cantidades en Truco: Cambia rápidamente entre Bitcoin y {currency} tocando sobre el balance del monedero. Unidad Predeterminada + Interfaz + Pagos General + Ajustes del sistema Idioma Bitkit QuickPay hace que pagar sea más rápido pagando automáticamente los códigos QR cuando se escanean. Pagos\n<accent>sin fricción</accent> @@ -664,13 +673,16 @@ Cuando está activado, puedes usar {biometryTypeName} en lugar de tu código PIN para desbloquear tu monedero o enviar pagos. Ocultar saldo al abrir Código PIN - Cambiar código PIN + Cambiar PIN Desactivado Activado Pedir PIN al realizar pagos Desliza el saldo para ocultar Usar {biometryTypeName} en su lugar Advertir al enviar más de $100 + Respaldar o restablecer + Privacidad + Seguridad Seguridad y Privacidad Ajustes Falló al hacer una copia de seguridad completa @@ -712,10 +724,17 @@ Soporte Enviado Satisfactoriamente Error al enviar + Bitkit fue creado por Synonym Software, S.A. DE C.V. ©2025. Todos los derechos reservados. Soporte Widgets Mostrar Títulos de Widgets Widgets + Restablecer tarjetas de sugerencias + Restablecer widgets + ¿Estás seguro de que deseas restablecer los widgets? Se mostrará el conjunto de widgets predeterminado con las configuraciones predeterminadas. + ¿Restablecer widgets? + Pantalla + Restablecer valores predeterminados Sé dueño de\n<accent>tu perfil</accent> Configura tu perfil público y enlaces para que tus contactos Bitkit puedan localizarte o pagarte en cualquier momento y lugar. Perfil @@ -829,6 +848,7 @@ Ajustes Tienda Estado de la App + Soporte Monedero Widgets Dirección bitcoin de envío no válida diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 09c0ae2bb..8a8788259 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -255,6 +255,9 @@ Conexión abierta Por favor, espere a que Bitkit se conecte a la red de pagos (±10 segundos). Conectando y Sincronizando... + Comenzar + Configura tu saldo de gastos para disfrutar de transacciones instantáneas y económicas con amigos, familiares y comerciantes. + Habilitar\nLightning\n<accent>pagos</accent> Por favor espera mientras los datos de tu antiguo monedero migran a esta nueva versión de Bitkit. Esto normalmente tarda menos de un minuto. Migración del Monedero MIGRANDO\n<accent>MONEDERO</accent> @@ -436,7 +439,8 @@ Su frase de recuperación Utilice las 12 palabras siguientes para recuperar su dinero más adelante. Toque para revelar - <accent>Nunca compartas</accent> tu frase de recuperación con nadie, ya que esto podría desembocar en la pérdida de fondos. + Asegúrate de que nadie pueda ver tu pantalla. <accent>Nunca compartas tu frase de recuperación</accent> con nadie, ya que podría resultar en la pérdida de fondos. + <accent>Bitkit no puede acceder a tus fondos ni ayudarte a recuperarlos</accent> si pierdes tu frase de recuperación. ¡Guárdala en un lugar seguro! Confirmar frase de recuperación Pulse las 12 palabras en el orden correcto. Frase mnemotécnica copiada al portapapeles @@ -463,8 +467,9 @@ Vuelva a introducir el PIN de 4 dígitos Vuelva a introducir su PIN de 4 dígitos para completar el proceso de configuración. Inténtelo de nuevo, no es el mismo PIN. - Desactivar PIN - El código PIN está actualmente activado. Si desea desactivar su PIN, deberá introducir primero su código PIN actual. + El código PIN está actualmente activado. Si desea desactivarlo o cambiarlo, primero debe introducir su código PIN actual. + PIN activado + Introduzca su <accent>PIN</accent> para desactivarlo. Desactivar PIN Por favor, introduzca su código PIN Último intento. Si vuelve a introducir un PIN erróneo, se reiniciará su monedero. @@ -500,9 +505,9 @@ Contactar con el servicio de asistencia Borrar App Cambiar PIN - Puede cambiar su código PIN por una nueva\ncombinación de 4 dígitos. Por favor, introduzca primero su código PIN actual. + Introduzca su PIN <accent>actual</accent> para cambiarlo. Reintroducir nuevo PIN - Vuelva a introducir su PIN de 4 dígitos para completar el proceso de configuración. + Vuelva a introducir su PIN de 4 dígitos para completar la configuración y guardar su nuevo PIN. Establecer nuevo PIN Por favor, utilice un PIN que recuerde. Si olvida su PIN puede restablecerlo, pero para ello tendrá que restaurar su monedero. Inténtelo de nuevo, no es el mismo PIN. @@ -511,6 +516,7 @@ Utilizar código PIN Datos de Monedero Borrados Bitkit ha sido restaurado y todos los datos de monedero han sido borrados. + Habilitar PIN Ajustes Opciones de desarrollo activadas Las opciones de desarrollo están ahora desactivadas en toda la aplicación. @@ -520,7 +526,7 @@ Seguridad y Privacidad Avanzado Acerca de - Copia de seguridad o Restaurar + Ajustes del sistema Idioma Soporte Legal @@ -551,20 +557,31 @@ ₿ {feeSats} para la transacción típica ({fiatSymbol}{fiatFormatted}) Etiquetas Etiquetas utilizadas anteriormente + Interfaz + Pagos Widgets Widgets Mostrar Títulos de Widgets + Restablecer tarjetas de sugerencias + Restablecer widgets + ¿Estás seguro de que deseas restablecer los widgets? Se mostrará el conjunto de widgets predeterminado con las configuraciones predeterminadas. + ¿Restablecer widgets? + Pantalla + Restablecer valores predeterminados Desliza el saldo para ocultar Ocultar saldo al abrir Leer portapapeles para facilitar uso Advertir al enviar más de $100 Código PIN - Cambiar código PIN + Cambiar PIN Pedir PIN al realizar pagos Activado Desactivado Cuando está activado, puedes usar {biometryTypeName} en lugar de tu código PIN para desbloquear tu monedero o enviar pagos. Usar {biometryTypeName} en su lugar + Respaldar o restablecer + Privacidad + Seguridad Respalda tu monedero Borrar y restaurar monedero Respaldo fallido: {time} @@ -581,8 +598,7 @@ Contactos Bitkit no pudo hacer una copia de seguridad de los datos del monedero. Reintentando en {interval, plural, one {# minuto} other {# minutos}}. Fallo de Copia de Seguridad de Datos - últimas copias de seguridad - Copia de Seguridad o Restaurar + Copias de seguridad Personalizar en los Ajustes de Bitkit Android Los pagos en segundo plano están desactivados porque has denegado las notificaciones. Los pagos en segundo plano están activados. Puedes recibir fondos incluso cuando la aplicación está cerrada (si tu dispositivo está conectado a internet). @@ -625,6 +641,7 @@ Error al abrir enlaces de soporte satoshi@satoshi.com Error al Enviar + Bitkit fue creado por Synonym Software, S.A. DE C.V. ©2025. Todos los derechos reservados. Estado de la App Internet Conectado @@ -667,12 +684,14 @@ Visualizador de direcciones Restablecer sugerencias ¿Restablecer sugerencias? - ¿Estás seguro de que deseas restablecer las sugerencias? Volverán a aparecer en caso de que las hayas eliminado de la vista general de tu monedero Bitkit. + ¿Estás seguro de que deseas restablecer las sugerencias? Volverán a aparecer en caso de que las hayas eliminado. Sí, restaurar Conexiones Lightning Nodo Lightning Servidor Electrum Rapid-Gossip-Sync + Auto + Debug Rápido (más caro) Rápido ± 10-20 minutos @@ -759,6 +778,7 @@ Ajustes Tienda Estado de la App + Soporte Monedero Enviar Recibir diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 5a8e09cda..9b11dcd6a 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -255,6 +255,9 @@ Connexion inactive Veuillez attendre que Bitkit se connecte au réseau de paiement (±10 secondes). Connexion et synchronisation... + Commencer + Configurez votre solde de dépenses pour profiter de transactions instantanées et peu coûteuses avec vos amis, votre famille et les commerçants. + Activer les\n<accent>paiements</accent>\nLightning Veuillez patienter pendant la migration de vos anciennes données vers cette nouvelle version de Bitkit. Cela prend généralement moins d\'une minute. Migration du portefeuille MIGRATION\n<accent>DU PORTEFEUILLE</accent> @@ -440,7 +443,8 @@ Notez ces mots {length} dans le bon ordre et conservez-les dans un endroit sûr. Utilisez les 12 mots ci-dessous pour récupérer votre argent ultérieurement. Tapez pour révéler - <accent>Ne communiquez jamais</accent> votre phrase de recouvrement à qui que ce soit, car cela pourrait entraîner la perte des fonds. + Assurez-vous que personne ne puisse voir votre écran. <accent>Ne partagez jamais votre phrase de récupération</accent> avec qui que ce soit, car cela pourrait entraîner la perte de fonds. + <accent>Bitkit ne peut pas accéder à vos fonds ni vous aider à les récupérer</accent> si vous perdez votre phrase de récupération. Conservez-la en lieu sûr ! Confirmer la phrase de récupération Tapez les 12 mots dans le bon ordre. Mnémonique copié dans le presse-papiers @@ -463,8 +467,9 @@ Retapez le code PIN à 4 chiffres Veuillez retaper votre code PIN à 4 chiffres pour terminer la procédure de configuration. Réessayez, il ne s\'agit pas du même code PIN. - Désactiver le code PIN - Le code PIN est actuellement activé. Si vous souhaitez désactiver votre code PIN, vous devez d\'abord saisir votre code PIN actuel. + Le code PIN est actuellement activé. Si vous souhaitez le désactiver ou le modifier, vous devez d\'abord saisir votre code PIN actuel. + PIN activé + Saisissez votre <accent>PIN</accent> pour le désactiver. Désactiver le code PIN Veuillez saisir votre code PIN Dernière tentative. Si vous saisissez à nouveau un code PIN erroné, votre portefeuille sera réinitialisé. @@ -499,10 +504,10 @@ Afficher la phrase de récupération. Contacter le support Effacer l\'application - Modifier le code PIN - Vous pouvez remplacer votre code PIN par une nouvelle\ncombinaison de 4 chiffres. Veuillez d\'abord introduire votre code PIN actuel. + Modifier le PIN + Saisissez votre PIN <accent>actuel</accent> pour le modifier. Confirmer le nouveau code PIN - Veuillez retaper votre code PIN à 4 chiffres pour terminer la procédure de configuration. + Veuillez retaper votre PIN à 4 chiffres pour terminer la configuration et enregistrer votre nouveau PIN. Définir un nouveau code PIN Veuillez utiliser un code PIN dont vous vous souviendrez. Si vous oubliez votre code PIN, vous pouvez le réinitialiser, mais cela nécessitera de restaurer votre portefeuille. Réessayez, il ne s\'agit pas du même code PIN. @@ -511,15 +516,16 @@ Utiliser le code PIN Données du wallet supprimées Bitkit a été réinitialisé et toutes les données du walet ont été supprimées. + Activer le PIN Paramètres Options de développement activées Les options pour les développeurs sont désormais activées dans l\'ensemble de l\'application. Options Dev désactivées Les options pour les développeurs sont désormais désactivées dans l\'ensemble de l\'application. Général + Paramètres système Langue Sécurité et vie privée - Sauvegarde ou restauration Avancé A propos Support @@ -551,9 +557,17 @@ ₿ {feeSats} pour une transaction moyenne ({fiatSymbol}{fiatFormatted} ) Tags Étiquettes précédemment utilisées + Interface + Paiements Widgets Widgets Afficher les titres des widgets + Réinitialiser les cartes de suggestions + Réinitialiser les widgets + Êtes-vous sûr de vouloir réinitialiser les widgets ? L\'ensemble de widgets par défaut avec les configurations par défaut sera affiché. + Réinitialiser les widgets ? + Affichage + Réinitialiser par défaut QuickPay <accent>Sans friction</accent>\npaiements Bitkit QuickPay accélère le passage à la caisse en payant automatiquement les QR codes lorsqu\'ils sont scannés. @@ -566,18 +580,19 @@ Lire le presse-papiers pour faciliter l\'utilisation Avertir en cas d\'envoi de plus de 100$ Code PIN - Modifier le code PIN + Modifier le PIN Exiger un code PIN pour les paiements Activé Désactivé Utilisez plutôt {biometryTypeName} Lorsque cette option est activée, vous pouvez utiliser {biometryTypeName} au lieu de votre code PIN pour déverrouiller votre portefeuille ou envoyer des paiements. - Sauvegarder ou restauration + Sauvegarder ou réinitialiser + Confidentialité + Sécurité Sauvegardez votre portefeuille Réinitialiser et restaurer le portefeuille Échec de la sauvegarde des données Bitkit n\'a pas réussi à sauvegarder les données du portefeuille. Réessayez dans {interval, plural, one {# minute} other {# minutes}}. - les dernières sauvegardes de données Échec de la sauvegarde : {time} Dernière sauvegarde : {time} En cours @@ -590,6 +605,7 @@ Tags Profil Contacts + Sauvegardes des données Personnaliser dans les paramètres Android de Bitkit Les paiements en arrière-plan sont désactivés car vous avez refusé les notifications. Les paiements en arrière-plan sont activés. Vous pouvez recevoir des fonds même lorsque l\'application est fermée (si votre appareil est connecté à Internet). @@ -621,6 +637,7 @@ Un problème s\'est produit lors de l\'envoi de votre problème ou de votre question. Veuillez réessayer. Réessayer Échec de l\'ouverture des liens de support + Bitkit a été conçu par Synonym Software, S.A. DE C.V. ©2025. Tous droits réservés. Statut de l\'application Internet Connecté @@ -667,12 +684,14 @@ Visualisateur d\'adresses Suggestions de réinitialisation Suggestions de réinitialisation ? - Êtes-vous sûr de vouloir réinitialiser les suggestions ? Elles réapparaîtront si vous les avez supprimées de l\'aperçu de votre portefeuille Bitkit. + Êtes-vous sûr de vouloir réinitialiser les suggestions ? Elles réapparaîtront si vous les avez supprimées. Oui, Réinitialisation Connexions Lightning Nœud Lightning Serveur Electrum Rapid-Gossip-Sync + Auto + Debug Rapide (plus cher) Rapide ± 10-20 minutes @@ -760,6 +779,7 @@ Boutique Config Statut de l\'application + Assistance Envoyer Recevoir Envoyer des bitcoins diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index a9f9090b3..c54328363 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -255,6 +255,9 @@ Connessione e sincronizzazione... Attendi che Bitkit si connetta alla rete di pagamento (±10 secondi). Saldi Wallet + Inizia + Configura il tuo saldo di spesa per goderti transazioni istantanee e economiche con amici, familiari e commercianti. + Abilita\nLightning\n<accent>pagamenti</accent> Attendi mentre i dati del tuo vecchio wallet migrano a questa nuova versione di Bitkit. Questo di solito richiede meno di un minuto. Migrazione Wallet MIGRAZIONE\n<accent>WALLET</accent> @@ -437,11 +440,11 @@ Contatta l\'Assistenza Hai cambiato con successo il PIN con una nuova combinazione a 4 cifre. Il PIN è stato modificato - Digita nuovamente il tuo PIN a 4 cifre per completare il processo di configurazione. + Digita nuovamente il tuo PIN a 4 cifre per completare la configurazione e salvare il nuovo PIN. Digita il Nuovo PIN Utilizza un PIN che ricorderai. Se dimentichi il PIN, puoi reimpostarlo, ma ciò richiede il ripristino del wallet. Imposta Nuovo PIN - Puoi cambiare il codice PIN con una nuova\ncombinazione di 4 cifre. Inserisci prima il codice PIN corrente. + Inserisci il tuo PIN <accent>attuale</accent> per cambiarlo. Cambia PIN Provi di nuovo, il PIN non coincide. Mostra la Seed Phrase @@ -458,7 +461,8 @@ Caricamento frase mnemonica non riuscito Dispositivi Multipli Non utilizzare la tua recovery phrase Bitkit su più telefoni contemporaneamente, in quanto potrebbe corrompere i tuoi dati. - <accent>Non condividere mai</accent> la tua frase di recupero con nessuno poiché ciò potrebbe comportare la perdita dei fondi. + Assicurati che nessuno possa vedere il tuo schermo. <accent>Non condividere mai la tua frase di recupero</accent> con nessuno, poiché potrebbe comportare la perdita dei fondi. + <accent>Bitkit non può accedere ai tuoi fondi né aiutarti a recuperarli</accent> se perdi la tua frase di recupero. Conservala al sicuro! Frase Mnemonica Successo Assicurati di conservare la frase di recupero in un <accent>luogo sicuro</accent>, poiché questo è <accent>l\'unico modo per recuperare</accent> i tuoi soldi! @@ -477,8 +481,9 @@ Scegli il PIN a 4 cifre Utilizza un PIN che ricorderai. Se dimentichi il PIN, puoi reimpostarlo, ma ciò richiede il ripristino del wallet. Disabilita il PIN - Il codice PIN è abilitato. Se vuoi disattivare il PIN, prima inserisci il PIN corrente. - Disabilita il PIN + Inserisci il tuo <accent>PIN</accent> per disattivarlo. + Il codice PIN è attualmente attivato. Se vuoi disattivarlo o cambiarlo, devi prima inserire il codice PIN attuale. + PIN attivato Inserisci il codice PIN Reset (Richiede la Recovery Phrase) Ha dimenticato il PIN? Resetta e recupera il tuo wallet Bitkit con la recovery phrase. Imposta un nuovo PIN dopo aver completato il recupero. @@ -511,6 +516,7 @@ Resetta App Bitkit è stato ripristinato e tutti i dati del portafoglio sono stati eliminati. Dati del portafoglio eliminati + Abilita PIN Termini di Utilizzo Condividi Cambia il tuo portafoglio, cambia il mondo. Scarica Bitkit per iPhone {appStoreUrl} o Android {playStoreUrl} @@ -548,13 +554,15 @@ Connessioni Lightning Nodo Lightning Sì, resetta - Sei sicuro di voler resettare i suggerimenti? Riappariranno nel caso in cui li abbia rimossi dalla panoramica del portafoglio Bitkit. + Sei sicuro di voler resettare i suggerimenti? Riappariranno nel caso in cui li abbia rimossi. Resettare i Suggerimenti? Rapid-Gossip-Sync Reti Altro Pagamenti Reimposta suggerimenti + Auto + Debug Avanzate Ricevute di Connessione Connessioni @@ -567,14 +575,12 @@ Widget Bitkit non è riuscito a eseguire il backup dei dati del portafoglio. Riprova tra {interval, plural, one {# minute} o {# minutes}}. Fallimento del backup dei dati - ultimi backup dei dati Resettare e ripristinare il wallet Backup fallito: {time} In esecuzione Ultimo backup: {time} - Backup o ripristino Esegui il backup del tuo wallet - Backup o Ripristino + Backup dei dati Personalizza nelle Impostazioni Android di Bitkit I pagamenti in background sono disabilitati perche\' hai negato le notifiche. I pagamenti in background sono abilitati. Puoi ricevere fondi anche quando l\'app e\' chiusa (se il dispositivo e\' connesso a internet). @@ -647,7 +653,10 @@ Visualizza gli importi in Suggerimento: alterna rapidamente tra Bitcoin e {currency} toccando il saldo del tuo portafoglio. Unità predefinita + Interfaccia + Pagamenti Generale + Impostazioni di sistema Lingua Bitkit QuickPay rende il checkout piu\' veloce pagando automaticamente i codici QR quando scansionati. <accent>Pagamenti</accent>\nsenza attriti @@ -664,13 +673,16 @@ Quanto attivato, puoi usare {biometryTypeName} invece del tuo codice PIN per sbloccare il tuo wallet o inviare pagamenti. Nascondi il saldo all\'apertura Codice PIN - Cambia codice PIN + Cambia PIN Disattivato Attivato Richiedi PIN per i pagamenti Scorri per nascondere il saldo Usa {biometryTypeName} invece Avvisa quando invii più di $100 + Backup o ripristino + Privacy + Sicurezza Sicurezza e Privacy Impostazioni Impossibile completare un backup completo @@ -712,10 +724,17 @@ Supporto Inviato con successo Impossibile inviare + Bitkit è stato realizzato da Synonym Software, S.A. DE C.V. ©2025. Tutti i diritti riservati. Supporto Widget Mostra titoli dei widget Widget + Reimposta schede suggerimenti + Reimposta widget + Sei sicuro di voler reimpostare i widget? Verrà visualizzato il set di widget predefinito con le configurazioni predefinite. + Reimpostare i widget? + Visualizzazione + Ripristina impostazioni predefinite Possiedi il tuo\n<accent>profilo</accent> Configura il tuo profilo pubblico e i tuoi collegamenti, in modo che i tuoi contatti Bitkit possano raggiungerti o pagarti sempre e ovunque. Profilo @@ -829,6 +848,7 @@ Impostazioni Negozio Stato dell\'app + Supporto Wallet Widget Indirizzo bitcoin di invio non valido diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index f3c9a434b..141fd00b9 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -255,6 +255,9 @@ Verbinden & synchroniseren... Wacht tot Bitkit verbinding maakt met het betaalnetwerk (±10 seconden). Wallet-saldi + Aan de slag + Stel je uitgavenbalans in om te genieten van directe en goedkope transacties met vrienden, familie en winkels. + Lightning\n<accent>betalingen</accent>\ninschakelen Even geduld terwijl je oude wallet-gegevens migreren naar deze nieuwe Bitkit-versie. Dit duurt meestal minder dan een minuut. Wallet-migratie WALLET\n<accent>MIGREREN</accent> @@ -437,12 +440,12 @@ Contact opnemen met support Je hebt je pincode succesvol gewijzigd naar een nieuwe 4-cijferige combinatie. Pincode gewijzigd - Typ je 4-cijferige pincode opnieuw in om het installatieproces te voltooien. + Typ je 4-cijferige PIN opnieuw in om de instelling te voltooien en je nieuwe PIN op te slaan. Nieuwe pincode opnieuw typen Gebruik een pincode die je zult onthouden. Als je je pincode vergeet, kun je deze resetten, maar daarvoor moet je je wallet herstellen. Nieuwe pincode instellen - Je kunt je pincode wijzigen naar een nieuwe\n4-cijferige combinatie. Voer eerst je huidige pincode in. - Pincode wijzigen + Voer je <accent>huidige</accent> PIN in om deze te wijzigen. + PIN wijzigen Probeer opnieuw, dit is niet dezelfde pincode. Herstelzin tonen Herstelzin bevestigen @@ -458,7 +461,8 @@ Herstelzin laden mislukt Meerdere apparaten Gebruik je Bitkit-herstelzin niet op meerdere telefoons tegelijk, dit kan je gegevens beschadigen. - <accent>Deel nooit</accent> je herstelzin met iemand, dit kan leiden tot verlies van geld. + Zorg ervoor dat niemand je scherm kan zien. <accent>Deel nooit je herstelzin</accent> met iemand, dit kan leiden tot verlies van geld. + <accent>Bitkit heeft geen toegang tot je geld en kan niet helpen bij het herstellen ervan</accent> als je je herstelzin verliest. Bewaar deze veilig! Herstelzin Succesvol Bewaar je herstelzin op een <accent>veilige plek</accent>, want dit is de <accent>enige manier om te herstellen</accent> je geld! @@ -477,8 +481,9 @@ Kies 4-cijferige pincode Gebruik een pincode die je zult onthouden. Als je je pincode vergeet, kun je deze resetten, maar daarvoor moet je je wallet herstellen. Pincode uitschakelen - Pincode is momenteel ingeschakeld. Als je je pincode wilt uitschakelen, moet je eerst je huidige pincode invoeren. - Pincode uitschakelen + Voer je <accent>PIN</accent> in om deze uit te schakelen. + De PIN is momenteel ingeschakeld. Als je deze wilt uitschakelen of wijzigen, moet je eerst je huidige PIN invoeren. + PIN ingeschakeld Voer je pincode in Resetten (herstelzin vereist) Pincode vergeten? Reset en herstel je Bitkit-wallet met je herstelzin. Stel een nieuwe pincode in na het voltooien van het herstel. @@ -511,6 +516,7 @@ App wissen Bitkit is gereset en alle wallet-gegevens zijn verwijderd. Wallet-gegevens verwijderd + PIN inschakelen Juridisch Delen Verander je wallet, verander de wereld. Download Bitkit voor iPhone {appStoreUrl} of Android {playStoreUrl} @@ -548,13 +554,15 @@ Lightning-verbindingen Lightning Node Ja, resetten - Weet je zeker dat je de suggesties wilt resetten? Ze verschijnen opnieuw als je ze uit je Bitkit-walletoverzicht hebt verwijderd. + Weet je zeker dat je de suggesties wilt resetten? Ze verschijnen opnieuw als je ze hebt verwijderd. Suggesties resetten? Rapid-Gossip-Sync Netwerken Overig Betalingen Suggesties resetten + Auto + Debug Geavanceerd Verbindingsbewijzen Verbindingen @@ -567,14 +575,12 @@ Widgets Bitkit kon geen back-up maken van wallet-gegevens. Nieuwe poging over {interval, plural, one {# minuut} other {# minuten}}. Gegevensbackup mislukt - Laatste gegevensbackups Wallet resetten en herstellen Mislukte backup: {time} Bezig Laatste backup: {time} - Back-up of herstellen Back-up maken van je wallet - Back-up of herstellen + Gegevensback-ups Aanpassen in Android Bitkit-instellingen Achtergrondbetalingen zijn uitgeschakeld omdat je notificaties hebt geweigerd. Achtergrondbetalingen zijn ingeschakeld. Je kunt geld ontvangen zelfs als de app gesloten is (als je apparaat verbonden is met internet). @@ -647,7 +653,10 @@ Bedragen weergeven in Tip: Wissel snel tussen Bitcoin en {currency} door op je wallet-saldo te tikken. Standaardeenheid + Interface + Betalingen Algemeen + Systeeminstellingen Taal Bitkit QuickPay maakt afrekenen sneller door QR-codes automatisch te betalen wanneer ze worden gescand. <accent>Wrijvingsloze</accent>\nbetalingen @@ -664,13 +673,16 @@ Wanneer ingeschakeld, kun je {biometryTypeName} gebruiken in plaats van je pincode om je wallet te ontgrendelen of betalingen te verzenden. Saldo verbergen bij openen Pincode - Pincode wijzigen + PIN wijzigen Uitgeschakeld Ingeschakeld Pincode vereisen voor betalingen Veeg over saldo om te verbergen Gebruik {biometryTypeName} in plaats daarvan Waarschuwen bij verzenden boven $100 + Back-up of reset + Privacy + Veiligheid Beveiliging en privacy Instellingen Volledige backup niet voltooid @@ -712,10 +724,17 @@ Ondersteuning Succesvol verzonden Verzenden mislukt + Bitkit is gemaakt door Synonym Software, S.A. DE C.V. ©2025. Alle rechten voorbehouden. Ondersteuning Widgets Widgettitels tonen Widgets + Suggestiekaarten resetten + Widgets resetten + Weet je zeker dat je de widgets wilt resetten? De standaard widgetset met standaardconfiguraties wordt weergegeven. + Widgets resetten? + Weergave + Standaardinstellingen herstellen Beheer je\n<accent>profiel</accent> Stel je openbare profiel en links in, zodat je Bitkit-contacten je kunnen bereiken of betalen, altijd en overal. Profiel @@ -829,6 +848,7 @@ Instellingen Winkel App-status + Ondersteuning Wallet Widgets Ongeldig bitcoin-verzendadres diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 225d6bb9d..ad72a04c2 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -255,6 +255,9 @@ Saldo portfela Proszę poczekać, aż Bitkit połączy się z siecią płatności (±10 sekund). Podłączanie i synchronizacja... + Rozpocznij + Skonfiguruj saldo wydatków, aby cieszyć się natychmiastowymi i tanimi transakcjami ze znajomymi, rodziną i sprzedawcami. + Włącz\nLightning\n<accent>płatności</accent> Proszę czekać, trwa migracja danych starego portfela do nowej wersji Bitkit. Zwykle trwa to mniej niż minutę. Migracja portfela MIGRACJA\n<accent>PORTFELA</accent> @@ -440,7 +443,8 @@ Zapisz poniższe {length} słów według kolejności i przechowuj je w bezpiecznym miejscu. Użyj 12 poniższych słów aby odzyskać swoje środki w przyszłości. Stuknij aby pokazać - <accent>Nigdy nie udostępniaj</accent> nikomu swojej frazy odzyskiwania, ponieważ może to spowodować utratę środków. + Upewnij się, że nikt nie widzi Twojego ekranu. <accent>Nigdy nie udostępniaj nikomu swojej frazy odzyskiwania</accent>, ponieważ może to spowodować utratę środków. + <accent>Bitkit nie ma dostępu do Twoich środków i nie może pomóc w ich odzyskaniu</accent>, jeśli utracisz frazę odzyskiwania. Przechowuj ją bezpiecznie! Potwierdź hasło Wpisz 12 słów w poprawnej kolejności. Sukces @@ -463,8 +467,9 @@ Wpisz ponownie 4 cyfrowy kod PIN Wpisz proszę ponownie 4 cyfrowy kod PIN aby zakończyć konfigurację. Spróbuj ponownie, kody PIN się różnią. - Wyłącz kod PIN - Kod PIN jest obecnie aktywny. Jeśli chcesz go wyłączyć pierw musisz podać aktualny kod PIN. + Kod PIN jest obecnie włączony. Jeśli chcesz go wyłączyć lub zmienić, najpierw wprowadź aktualny kod PIN. + PIN włączony + Wprowadź swój <accent>PIN</accent>, aby go wyłączyć. Wyłącz kod PIN Podaj proszę kod PIN Ostatnia próba. Wpisanie błędnego kodu spowoduje zresetowanie portfela. @@ -499,10 +504,10 @@ Pokaż frazę odzyskiwania Skontaktuj się z pomocą techniczną Wyczyść aplikację - Zmień kod PIN - Możesz zmienić swój kod PIN na nową\n4-cyfrową kombinację. Najpierw wprowadź aktualny kod PIN. + Zmień PIN + Wprowadź swój <accent>aktualny</accent> PIN, aby go zmienić. Wpisz ponownie kod PIN - Wpisz proszę ponownie 4 cyfrowy kod PIN aby zakończyć konfigurację. + Wpisz ponownie 4-cyfrowy PIN, aby zakończyć konfigurację i zapisać nowy PIN. Ustaw nowy kod PIN Użyj kodu PIN, który zapamiętasz. Jeśli zapomnisz kodu PIN, możesz go zresetować, ale będzie to wymagało przywrócenia portfela. Spróbuj ponownie, kody PIN się różnią. @@ -511,6 +516,7 @@ Użyj kodu PIN Usunięto dane portfela Bitkit został zresetowany, a wszystkie dane portfela zostały usunięte. + Włącz PIN Ustawienia Opcje Dev włączone Opcje deweloperskie są teraz dostępne w całej aplikacji. @@ -518,7 +524,6 @@ Opcje deweloperskie są teraz wyłączone w całej aplikacji. Ogólne Bezpieczeństwo i prywatność - Utwórz kopię zapasową lub przywróć Zaawansowane Informacje Wsparcie @@ -550,9 +555,17 @@ ₿ {feeSats} dla przeciętnej transakcji ({fiatSymbol}{fiatFormatted} ) Tagi Poprzednio używane tagi + Interfejs + Płatności Widgety Widgety Pokazuj tytuły widgetów + Resetuj karty sugestii + Resetuj widgety + Czy na pewno chcesz zresetować widgety? Zostanie wyświetlony domyślny zestaw widgetów z domyślnymi konfiguracjami. + Resetować widgety? + Wyświetlanie + Przywróć domyślne QuickPay <accent>Bezproblemowe</accent>\npłatności Bitkit QuickPay przyspiesza płatności, automatycznie opłacając zeskanowane kody QR. @@ -565,18 +578,19 @@ Odczytaj schowek dla wygody użytkowania Ostrzegaj przy transakcji powyżej 100 USD Kod PIN - Zmień kod PIN + Zmień PIN Wymagaj PIN-u do płatności Włączone Wyłączone Użyj {biometryTypeName} wzamian Po włączeniu tej opcji możesz używać {biometryTypeName} zamiast kodu PIN do odblokowania portfela lub wysyłania płatności. - Utwórz kopię zapasową lub przywróć + Kopia zapasowa lub reset + Prywatność + Bezpieczeństwo Utwórz kopię zapasową swojego portfela Resetowanie i przywracanie portfela Błąd tworzenia kopii zapasowej danych Bitkit nie wykonał kopii zapasowej danych portfela. Ponowna próba za {interval, plural, one {# minute} other {# minutes}}. - ostatnie kopie zapasowe Nieudana kopia zapasowa: {time} Najnowsza kopia zapasowa: {time} Połączenia @@ -589,6 +603,7 @@ Profil Kontakty W toku + Kopie zapasowe danych Dostosuj w ustawieniach Bitkit na Androidzie Płatności w tle są wyłączone, ponieważ odmówiono powiadomień. Płatności w tle są włączone. Możesz otrzymywać środki nawet gdy aplikacja jest zamknięta (jeśli Twoje urządzenie jest połączone z internetem). @@ -606,6 +621,7 @@ Branch and Bound Losowy wybór dla prywatności Single Random Draw + Ustawienia systemowe Język Nie udało się otworzyć linków pomocy Wsparcie @@ -625,6 +641,7 @@ Nie udało się wysłać Coś poszło nie tak podczas próby wysłania Państwa pytania. Proszę spróbować ponownie. Spróbuj ponownie + Bitkit został stworzony przez Synonym Software, S.A. DE C.V. ©2025. Wszelkie prawa zastrzeżone. Status aplikacji Internet Połączono @@ -667,12 +684,14 @@ Przeglądarka adresów Resetuj sugestie Resetuj sugestie? - Zresetować sugestie? Usunięte elementy pojawią się ponownie w widoku portfela Bitkit. + Zresetować sugestie? Usunięte elementy pojawią się ponownie. Tak, zresetuj Połączenia z Lightning Węzeł Lightning Serwer Electrum Rapid-Gossip-Sync + Auto + Debug Szybko (drożej) Szybko ± 10-20 minut @@ -760,6 +779,7 @@ Sklep Ustawienia Status aplikacji + Wsparcie Wyślij Otrzymaj Wyślij Bitcoin diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index d0eff952f..8bb66e6d9 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -255,6 +255,9 @@ Conectando e Sincronizando... Por favor, espere que a Bitkit se conecte à rede de pagamento (±10 segundos). Saldos da Carteira + Começar + Configure seu saldo de gastos para aproveitar transações instantâneas e baratas com amigos, familiares e comerciantes. + Ativar\nLightning\n<accent>pagamentos</accent> Aguarde enquanto os dados da sua carteira antiga são migrados para esta nova versão da Bitkit. Isso geralmente leva menos de um minuto. Migração da Carteira MIGRANDO\n<accent>CARTEIRA</accent> @@ -437,11 +440,11 @@ Contactar Suporte Você alterou com sucesso seu PIN para uma nova combinação de 4 dígitos. PIN alterado - Digite novamente o PIN de 4 dígitos para concluir o processo de configuração. + Digite novamente o PIN de 4 dígitos para concluir o processo de configuração e salvar seu novo PIN. Digite novamente o novo PIN Use um PIN que você se lembrará. Se você esquecer o PIN, poderá redefini-lo mas isso exigirá a restauração da carteira. Definir novo PIN - Você pode alterar seu código PIN para uma nova\ncombinação de 4 dígitos. Primeiro, digite seu código PIN atual. + Digite seu PIN <accent>atual</accent> para alterá-lo. Alterar PIN Tente novamente, este não é o mesmo PIN. Mostrar Seed @@ -458,7 +461,8 @@ Falha ao carregar frase mnemônica Vários Dispositivos Não use a frase de recuperação da Bitkit em vários telefones simultaneamente, pois isso pode corromper seus dados. - <accent>Nunca compartilhe</accent> sua frase de recuperação com ninguém, pois isso pode resultar na perda de fundos. + Certifique-se de que ninguém possa ver sua tela. <accent>Nunca compartilhe sua frase de recuperação</accent> com ninguém, pois isso pode resultar na perda de fundos. + <accent>O Bitkit não pode acessar seus fundos nem ajudar a recuperá-los</accent> se você perder sua frase de recuperação. Mantenha-a segura! Frase mnemônica Sucesso Armazene sua frase de recuperação em um <accent>local seguro</accent>, pois essa é a <accent>única maneira de recuperar</accent> seu dinheiro! @@ -477,8 +481,9 @@ Escolha o PIN de 4 dígitos Use um PIN que você se lembrará. Se você esquecer o PIN, poderá redefini-lo mas isso exigirá a restauração da carteira. Desativar PIN - O código PIN está ativado no momento. Se quiser desativar o PIN, você precisará, primeiro, digitar o código PIN atual. - Desativar PIN + Digite seu <accent>PIN</accent> para desativá-lo. + O código PIN está ativado. Se deseja desativá-lo ou alterá-lo, primeiro digite seu código PIN atual. + PIN ativado Por favor, digite seu código PIN Restaurar (Requer a Frase de Recuperação) Esqueceu seu PIN? Redefina e recupere sua carteira Bitkit com a sua frase de recuperação. Defina um novo PIN após concluir a recuperação. @@ -511,6 +516,7 @@ Limpar Aplicativo O Bitkit foi redefinido e todos os dados da carteira foram excluídos. Dados da Carteira Deletados + Ativar PIN Legal Compartilhar Mude de carteira, mude o mundo. Baixe a Bitkit para iPhone {appStoreUrl} ou Android {playStoreUrl} @@ -548,13 +554,15 @@ Conexões Lightning Nó Lightning Sim, Resetar - Tem certeza de que deseja resetar as sugestões? Eles reaparecerão na tela inicial caso você tenha removido. + Tem certeza de que deseja resetar as sugestões? Eles reaparecerão caso você tenha removido. Resetar Sugestões? Servidor Rapid-Gossip-Sync Redes Outros Pagamentos Resetar Sugestões + Auto + Debug Avançado Detalhes da Conexão Conexões @@ -567,14 +575,12 @@ Widgets A Bitkit falhou ao fazer o backup dos dados da carteira. Tentando novamente em {interval, plural, one {# minute} other {# minutes}}. Falha no Backup - backups recentes Reiniciar e restaurar carteira Falha no backup: {time} Executando Último backup: {time} - Fazer Backup ou Restaurar Faça backup da sua carteira - Fazer backup ou Restaurar + Backups de dados Personalizar nas Configurações do Bitkit Android Pagamentos em segundo plano estão desativados porque você negou notificações. Pagamentos em segundo plano estão ativados. Você pode receber fundos mesmo quando o app está fechado (se seu dispositivo estiver conectado à internet). @@ -647,7 +653,10 @@ Exibir valores em Dica: Alterne rapidamente entre Bitcoin e {currency} tocando no saldo de sua carteira. Unidade padrão + Interface + Pagamentos Geral + Configurações do sistema Idioma O Bitkit QuickPay agiliza o check-out pagando automaticamente os códigos QR quando escaneados. Pagamentos\n<accent>Sem atrito</accent> @@ -664,13 +673,16 @@ Quando ativado, você poderá usar {biometryTypeName} em vez do PIN para desbloquear ou pagar. Ocultar saldo ao abrir o app Código PIN - Alterar o código PIN + Alterar PIN Desativado Ativado Solicitar PIN para pagar Arraste o saldo para ocultar Usar {biometryTypeName} Avisar ao enviar mais de US$ 100 + Backup ou redefinir + Privacidade + Segurança Segurança e Privacidade Ajustes Falha ao concluir backup @@ -712,10 +724,17 @@ Suporte Enviado com Sucesso Falha no Envio + Bitkit foi criado pela Synonym Software, S.A. DE C.V. ©2025. Todos os direitos reservados. Suporte Widgets Mostrar Títulos dos Widgets Widgets + Redefinir cartões de sugestões + Redefinir widgets + Tem certeza de que deseja redefinir os widgets? O conjunto padrão de widgets com configurações padrão será exibido. + Redefinir widgets? + Exibição + Redefinir padrões Tenha seu próprio\n<accent>perfil</accent> Configure seu perfil público e seus links, para que seus contatos possam pagá-lo a qualquer hora e em qualquer lugar. Perfil @@ -829,6 +848,7 @@ Ajustes Comprar Status do Aplicativo + Suporte Carteira Widgets Endereço de envio bitcoin inválido diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index efff4b558..dcf964b10 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -255,6 +255,9 @@ Altura do Arquivo do Monitor de Canal Algumas conexões não puderam ser fechadas. TRANSFERÊNCIA EM ANDAMENTO + Começar + Configure o seu saldo de despesas para desfrutar de transações instantâneas e económicas com amigos, familiares e comerciantes. + Ativar\nLightning\n<accent>pagamentos</accent> Por favor aguarde enquanto seus dados da carteira antiga migram para esta nova versão da Bitkit. Isso geralmente leva menos de um minuto. Migração da Carteira MIGRANDO\n<accent>CARTEIRA</accent> @@ -441,7 +444,8 @@ Anote essas {length} palavras na ordem correta e guarde-as em um local seguro. Use as 12 palavras abaixo para recuperar seu dinheiro em uma data posterior. Toque para revelar - <accent>Nunca compartilhe</accent> sua frase de recuperação com ninguém, pois isso pode resultar na perda de fundos. + Certifique-se de que ninguém possa ver o seu ecrã. <accent>Nunca partilhe a sua frase de recuperação</accent> com ninguém, pois isso pode resultar na perda de fundos. + <accent>O Bitkit não pode aceder aos seus fundos nem ajudar a recuperá-los</accent> se perder a sua frase de recuperação. Mantenha-a segura! Confirmar frase de recuperação Insira as 12 palavras na ordem correta. Sucesso @@ -462,8 +466,9 @@ Digite novamente o PIN de 4 dígitos Digite novamente o PIN de 4 dígitos para concluir o processo de configuração. Tente novamente, este não é o mesmo PIN. - Desativar PIN - O código PIN está ativado no momento. Se quiser desativar o PIN, você precisará, primeiro, digitar o código PIN atual. + O código PIN está ativado. Se deseja desativá-lo ou alterá-lo, primeiro digite seu código PIN atual. + PIN ativado + Digite seu <accent>PIN</accent> para desativá-lo. Desativar PIN Por favor, digite seu código PIN Última tentativa. Se digitar o PIN errado novamente, sua carteira será redefinida. @@ -499,9 +504,9 @@ Contactar Suporte Limpar Aplicativo Alterar PIN - Você pode alterar seu código PIN para uma nova\ncombinação de 4 dígitos. Primeiro, digite seu código PIN atual. + Digite seu PIN <accent>atual</accent> para alterá-lo. Digite novamente o novo PIN - Digite novamente o PIN de 4 dígitos para concluir o processo de configuração. + Digite novamente o PIN de 4 dígitos para concluir o processo de configuração e salvar seu novo PIN. Definir novo PIN Use um PIN que você se lembrará. Se você esquecer o PIN, poderá redefini-lo mas isso exigirá a restauração da carteira. Tente novamente, este não é o mesmo PIN. @@ -510,6 +515,7 @@ Usar o código PIN Dados da Carteira Deletados O Bitkit foi redefinido e todos os dados da carteira foram excluídos. + Ativar PIN Ajustes Modo Desenvolvedor Ativado As opções de desenvolvedor estão agora habilitadas em todo o aplicativo. @@ -517,7 +523,6 @@ As opções de desenvolvedor estão agora desabilitadas em todo o aplicativo. Geral Segurança e Privacidade - Fazer backup ou Restaurar Avançado Sobre Suporte @@ -549,9 +554,17 @@ ₿ {feeSats} para uma transação média ({fiatSymbol}{fiatFormatted} ) Tags Tags usadas anteriormente + Interface + Pagamentos Widgets Widgets Mostrar Títulos dos Widgets + Repor cartões de sugestões + Repor widgets + Tem certeza de que deseja repor os widgets? O conjunto padrão de widgets com configurações padrão será exibido. + Repor widgets? + Apresentação + Repor predefinições QuickPay Pagamentos\n<accent>Sem atrito</accent> O Bitkit QuickPay agiliza o check-out pagando automaticamente os códigos QR quando escaneados. @@ -564,18 +577,19 @@ Ler área de transferência Avisar ao enviar mais de US$ 100 Código PIN - Alterar o código PIN + Alterar PIN Solicitar PIN para pagar Ativado Desativado Usar {biometryTypeName} Quando ativado, você poderá usar {biometryTypeName} em vez do PIN para desbloquear ou pagar. - Fazer Backup ou Restaurar + Cópia de segurança ou repor + Privacidade + Segurança Faça backup da sua carteira Reiniciar e restaurar carteira Falha no Backup A Bitkit falhou ao fazer o backup dos dados da carteira. Tentando novamente em {interval, plural, one {# minuto} other {# minutos}}. - backups recentes Em execução Falha no backup: {time} Último backup: {time} @@ -588,6 +602,7 @@ Tags Perfil Contatos + Cópias de segurança Suporte Precisa de ajuda? Relate seu problema pela Bitkit, visite a central de ajuda, verifique o status ou entre em contato conosco em nossos canais sociais. Relatar problema @@ -606,6 +621,7 @@ Algo deu errado ao enviar seu problema ou pergunta. Por favor, tente novamente. Tente novamente Falha ao abrir links de suporte + Bitkit foi criado pela Synonym Software, S.A. DE C.V. ©2025. Todos os direitos reservados. Status do Aplicativo Internet Conectado @@ -648,12 +664,14 @@ Visualizador de Endereços Resetar Sugestões Resetar Sugestões? - Tem certeza de que deseja resetar as sugestões? Eles reaparecerão na tela inicial caso você tenha removido. + Tem certeza de que deseja resetar as sugestões? Eles reaparecerão caso você tenha removido. Sim, Resetar Conexões Lightning Nó Lightning Servidor Electrum Servidor Rapid-Gossip-Sync + Auto + Debug Rápido (mais caro) Rápido ± 10-20 minutos @@ -714,6 +732,7 @@ Branch and Bound Seleção aleatória para privacidade Single Random Draw + Configurações do sistema Idioma Tenha seu próprio\n<accent>perfil</accent> Configure seu perfil público e seus links, para que seus contatos possam pagá-lo a qualquer hora e em qualquer lugar. @@ -727,6 +746,7 @@ Comprar Ajustes Status do Aplicativo + Suporte Enviar Receber Enviar Bitcoin diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index ae57a94ee..56e7f7acf 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -267,6 +267,9 @@ Подключение и Синхронизация... Пожалуйста, подождите, пока Bitkit подключается к платёжной сети (±10 секунд). Балансы кошелька + Начать + Настройте баланс расходов, чтобы наслаждаться мгновенными и дешёвыми транзакциями с друзьями, семьёй и продавцами. + Включить\nLightning\n<accent>платежи</accent> Пожалуйста, подождите, пока данные вашего старого кошелька переносятся в эту новую версию Bitkit. Обычно это занимает меньше минуты. Миграция кошелька МИГРАЦИЯ @@ -451,11 +454,11 @@ Связаться с Поддержкой Вы успешно изменили свой PIN-код на новую комбинацию из 4 цифр. PIN-код Изменён - Повторно введите 4-значный PIN-код, чтобы завершить процесс установки. + Повторно введите 4-значный PIN, чтобы завершить настройку и сохранить новый PIN. Повторите Новый PIN-код Пожалуйста, используйте PIN-код, который вы запомните. Если вы забудете свой PIN-код, вы можете сбросить его, но для этого потребуется восстановить кошелёк. Новый PIN-код - Вы можете изменить свой PIN-код на новую\nчетырёхзначную комбинацию. Сначала введите текущий PIN-код. + Введите <accent>текущий</accent> PIN, чтобы изменить его. Изменить PIN-код Попробуйте ещё раз, это не тот PIN-код. Показать Фразу Восстановления @@ -472,7 +475,8 @@ Не удалось загрузить мнемоническую фразу Несколько Устройств Не используйте фразу восстановления Bitkit на нескольких телефонах одновременно, так как это может привести к повреждению ваших данных. - <accent>Никогда не делитесь</accent> своей фразой восстановления с кем-либо, так как это может привести к потере средств. + Убедитесь, что никто не видит ваш экран. <accent>Никогда не делитесь своей фразой восстановления</accent> с кем-либо, так как это может привести к потере средств. + <accent>Bitkit не может получить доступ к вашим средствам и не может помочь их восстановить</accent>, если вы потеряете фразу восстановления. Храните её в безопасности! Фраза Восстановления Успешно Убедитесь, что храните свою фразу восстановления в <accent>безопасном месте</accent>, так как это <accent>единственный способ восстановить</accent> ваши средства! @@ -491,8 +495,9 @@ Выберите 4-значный PIN-код Пожалуйста, используйте PIN-код, который вы запомните. Если вы забудете свой PIN-код, вы можете сбросить его, но для этого потребуется восстановить кошелёк. Отключить PIN-код - PIN-код в настоящее время включен. Если вы хотите отключить свой PIN-код, вам необходимо сначала ввести текущий PIN-код. - Отключить PIN-код + Введите <accent>PIN</accent>, чтобы отключить его. + PIN-код в настоящее время включён. Если вы хотите отключить или изменить PIN, сначала введите текущий PIN-код. + PIN включён Пожалуйста, введите PIN-код Сбросить (требуется Фраза Восстановления) Забыли свой PIN-код? Сбросьте и восстановите свой кошелёк Bitkit с помощью фразы восстановления. Установите новый PIN-код после завершения восстановления. @@ -525,6 +530,7 @@ Сбросить Приложение Bitkit был сброшен, и все данные кошелька были удалены. Данные Кошелька Удалены + Включить PIN Документы Поделиться Измените свой кошелёк, измените мир. Скачайте Bitkit для iPhone {appStoreUrl} или Android {playStoreUrl} @@ -569,6 +575,8 @@ Другое Платежи Сбросить Рекомендации + Авто + Отладка Дополнительно Квитанции соединений Соединения @@ -581,14 +589,12 @@ Виджеты Bitkit не удалось создать резервную копию данных кошелька. Повторная попытка через {interval, plural, one {# минуту} few {# минуты} many {# минут} other {# минут}}. Ошибка Резервного Копирования - Последние Резервные Копии Данных Сбросить и Восстановить Кошелёк Ошибка резервного копирования: {time} Выполняется Последнее Резервное Копирование: {time} - Резервное копирование Создать Резервную Копию Кошелька - Резервное копирование + Резервные копии данных Настроить в настройках Android Bitkit Фоновые платежи отключены, потому что вы отклонили уведомления. Фоновые платежи включены. Вы можете получать средства, даже когда приложение закрыто (если ваше устройство подключено к интернету). @@ -662,7 +668,10 @@ Отображать суммы в Совет: Быстро переключайтесь между биткойнами и {currency}, нажимая на баланс вашего кошелька. Единица Измерения по Умолчанию + Интерфейс + Платежи Основные + Системные настройки Язык Bitkit QuickPay ускоряет оплату, автоматически оплачивая QR-коды при сканировании. <accent>Платежи</accent> @@ -680,13 +689,16 @@ Когда включено, вы можете использовать {biometryTypeName} вместо PIN-кода для разблокировки кошелька или отправки платежей. Скрывать Баланс при Открытии PIN-код - Сменить PIN-код + Сменить PIN Выключен Включён Требовать PIN-код для Платежей Смахнуть Баланс, чтобы Скрыть Использовать {biometryTypeName} вместо этого Предупреждать при Отправке более $100 + Резервное копирование или сброс + Конфиденциальность + Безопасность Безопасность и Конфиденциальность Настройки Не удалось выполнить полное резервное копирование @@ -728,10 +740,17 @@ Поддержка Успешно Отправлено Не Удалось Отправить + Bitkit создан компанией Synonym Software, S.A. DE C.V. ©2025. Все права защищены. Поддержка Виджеты Показывать Заголовки Виджетов Виджеты + Сбросить карточки предложений + Сбросить виджеты + Вы уверены, что хотите сбросить виджеты? Будет отображён набор виджетов по умолчанию с настройками по умолчанию. + Сбросить виджеты? + Отображение + Сбросить к настройкам по умолчанию Владейте своим\n<accent>профилем</accent> Настройте свой публичный профиль и ссылки, чтобы ваши контакты Bitkit могли связаться с вами или заплатить вам в любое время и в любом месте. Профиль @@ -846,6 +865,7 @@ Настройки Магазин Статус Приложения + Поддержка Кошелёк Виджеты Недействительный адрес отправки Bitcoin diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5e85b445b..6d409e9f2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -105,6 +105,9 @@ Closure reason Add Connection Export Logs + Get Started + Set up your spending balance to enjoy instant and cheap transactions with friends, family, and merchants. + Enable\nLightning\n<accent>payments</accent> Closed connections Hide Closed & Failed Show Closed & Failed @@ -323,7 +326,7 @@ Swipe to hide your balance, enjoy more private payments, and protect your wallet by enabling security features. Your keys,\n<accent>your coins</accent> Let’s create your wallet. Please be aware that Bitkit is mobile software. <accent>Don’t store all your money in Bitkit.</accent> - Terms of use + Terms of Use By continuing you declare that you have read and accept the terms of use. Bitkit\n<accent>terms of use</accent> Use Bitkit to pay anyone, anywhere, any time, and spend your bitcoin on the things you value in life. @@ -444,13 +447,13 @@ Contact Support You have successfully changed your PIN to a new 4-digit combination. PIN changed - Please retype your 4-digit PIN to complete the setup process. + Please retype your 4-digit PIN to complete the setup process and save your new PIN. Retype New PIN Please use a PIN you will remember. If you forget your PIN you can reset it, but that will require restoring your wallet. Set New PIN - You can change your PIN code to a new\n4-digit combination. Please enter your current PIN code first. + Enter your <accent>current</accent> PIN to change it. Change PIN - Try again, this is not the same PIN. + Not the same PIN. Try again. Show Seed Phrase Confirm Recovery Phrase Tap the 12 words in the correct order. @@ -465,7 +468,8 @@ Failed to load mnemonic Multiple Devices Don\'t use your Bitkit recovery phrase on multiple phones simultaneously, as this can corrupt your data. - <accent>Never share</accent> your recovery phrase with anyone as this may result in the loss of funds. + Make sure no one can see your screen. <accent>Never share your recovery phrase</accent> with anyone, as it may result in loss of funds. + <accent>Bitkit cannot access your funds and cannot help recover them</accent> if you lose your recovery phrase. Keep it safe! Mnemonic Phrase Successful Make sure you store your recovery phrase in a <accent>secure place</accent>, as this is the <accent>only way to recover</accent> your money! @@ -476,7 +480,7 @@ passphrase Confirm Passphrase Enter the passphrase you added while setting up and creating your wallet. - <accent>Never share</accent> your passphrase with anyone as this may result in the loss of funds. + <accent>Never share your passphrase</accent> with anyone, as it may result in loss of funds. Keep it secret! <accent>Passphrase:</accent> {passphrase} You added a passphrase to your recovery phrase during wallet setup. Your Passphrase @@ -484,14 +488,16 @@ Choose 4-Digit PIN Please use a PIN you will remember. If you forget your PIN you can reset it, but that will require restoring your wallet. Disable PIN - PIN code is currently enabled. If you want to disable your PIN, you need to enter your current PIN code first. - Disable PIN + Enable PIN + Enter your <accent>PIN</accent> to disable it. + PIN code is currently enabled. If you want to disable or change your PIN, you need to enter your current PIN code first. + PIN Enabled Please enter your PIN code Reset (Requires Recovery Phrase) Forgot your PIN? Reset and recover your Bitkit wallet with your recovery phrase. Set a new PIN after completing recovery. Forgot PIN? Last attempt. Entering the wrong PIN again will reset your wallet. - Try again, this is not the same PIN. + Not the same PIN. Try again. Retype 4-Digit PIN Please retype your 4-digit PIN to complete the setup process. Secure Wallet @@ -545,7 +551,7 @@ Now using {type} addresses. Primary address type Settings updated - Address type + Address Type Currently selected Address Viewer Coin Selection @@ -563,13 +569,15 @@ Coin Selection Method Largest First Sort by and use largest UTXO first. Potentially lower fee, but reveals your largest UTXOs. + Auto Electrum Server Lightning Connections Lightning Node Yes, Reset - Are you sure you want to reset the suggestions? They will reappear in case you have removed them from your Bitkit wallet overview. + Are you sure you want to reset the suggestions? They will reappear in case you have removed them. Reset Suggestions? Rapid-Gossip-Sync + Debug Networks Other Payments @@ -586,14 +594,12 @@ Widgets Bitkit failed to back up wallet data. Retrying in {interval, plural, one {# minute} other {# minutes}}. Data Backup Failure - latest data backups - Reset and restore wallet + Data Backups + Reset wallet Failed Backup: {time} Running Latest Backup: {time} - Back Up Or Restore Back up your wallet - Back up or Restore Customize in Android Bitkit Settings Background payments are disabled, because you have denied notifications. Background payments are enabled. You can receive funds even when the app is closed (if your device is connected to the internet). @@ -646,14 +652,16 @@ Slow (cheaper) Slow Prices powered by Bitfinex & CoinGecko. - Local currency + Local Currency Local Currency Most Used Other Currencies Classic (₿ 0.00010000) Bitcoin denomination Modern (₿ 10 000) - Transaction speed + Interface + Payments + Transaction Speed Default Transaction Speed Set Custom Fee ₿ {feeSats} for the average transaction @@ -661,12 +669,13 @@ Transaction Speed Tags Previously used tags - Default unit + Default Unit Bitcoin Display amounts in Tip: Quickly toggle between Bitcoin and {currency} by tapping on your wallet balance. Default Unit General + System Settings Language Bitkit QuickPay makes checking out faster by automatically paying QR codes when scanned. <accent>Frictionless</accent>\npayments @@ -680,17 +689,20 @@ You may need to restart the app once or twice for this change to take effect. Rapid-Gossip-Sync Server Updated Read clipboard for ease of use + Back up or reset + Privacy + Safety When enabled, you can use {biometryTypeName} instead of your PIN code to unlock your wallet or send payments. Hide balance on open PIN Code - Change PIN Code + Change PIN Disabled Enabled Require PIN for payments Swipe balance to hide - Use {biometryTypeName} instead + {biometryTypeName} instead of PIN Warn when sending over $100 - Security and Privacy + Security Settings Failed to complete a full backup Backing up... @@ -722,7 +734,8 @@ Report Issue Please describe the issue you are experiencing or ask a general question. App Status - Need help? Report your issue from within Bitkit, visit the help center, check the status, or reach out to us on our social channels. + Bitkit was crafted by Synonym Software, S.A. DE C.V. ©2025. All rights reserved. + Need help? Report your issue from within Bitkit or visit our help center. Send Thank you for contacting us! We will try to get back to you as soon as possible. OK @@ -734,7 +747,13 @@ Support Widgets Show Widget Titles - Widgets + Reset Suggestions Cards + Reset Widgets + Are you sure you want to reset the widgets? The default widget set with default configurations will be displayed. + Reset Widgets? + Display + Reset To Defaults + Show Widgets Own your\n<accent>profile</accent> Set up your public profile and links, so your Bitkit contacts can reach you or pay you anytime, anywhere. Profile @@ -849,6 +868,7 @@ Settings Shop App Status + Support Wallet Widgets Invalid bitcoin send address