package com.bitcointxoko.gudariwallet.ui import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.combinedClickable import androidx.compose.material3.* import androidx.compose.material3.pulltorefresh.PullToRefreshBox import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Visibility import androidx.compose.material.icons.filled.VisibilityOff import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material.icons.filled.ArrowDropDown import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.filled.Language // ← NEW import androidx.compose.ui.text.input.ImeAction import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.platform.LocalFocusManager import com.bitcointxoko.gudariwallet.util.formatFiat import androidx.compose.ui.text.font.FontFamily import com.bitcointxoko.gudariwallet.LocalAppStrings import cafe.adriel.lyricist.Lyricist // ← NEW import com.bitcointxoko.gudariwallet.i18n.AppStrings @OptIn(ExperimentalMaterial3Api::class) @Composable fun HomeTab( vm: WalletViewModel, onHistoryClick: () -> Unit, lyricist: Lyricist // ← NEW: passed in from wherever you call ProvideStrings ) { val strings = LocalAppStrings.current val balanceState by vm.balanceState.collectAsState() val balanceHidden by vm.balanceHidden.collectAsState() // ── NEW: language switcher state ────────────────────────────────────────── val supportedTags = remember { listOf("en", "es", "eu") } // add more as you add locale files val isRefreshing = balanceState is BalanceState.Loading || (balanceState as? BalanceState.Success)?.isRefreshing == true PullToRefreshBox( isRefreshing = isRefreshing, onRefresh = { vm.refreshBalance() }, modifier = Modifier.fillMaxSize() ) { Column( modifier = Modifier .fillMaxSize() .verticalScroll(rememberScrollState()) .padding(24.dp), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { when (val s = balanceState) { is BalanceState.Loading -> { CircularProgressIndicator() Spacer(Modifier.height(12.dp)) Text( text = strings.loadingBalance, style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant ) } is BalanceState.Success -> { val selectedCurrency by vm.selectedCurrency.collectAsState() var showCurrencyPicker by remember { mutableStateOf(false) } var isLoadingCurrencies by remember { mutableStateOf(false) } var currencyList by remember { mutableStateOf>(emptyList()) } LaunchedEffect(showCurrencyPicker) { if (showCurrencyPicker && currencyList.isEmpty()) { isLoadingCurrencies = true currencyList = vm.getSupportedCurrencies() isLoadingCurrencies = false } } Row( verticalAlignment = Alignment.CenterVertically ) { Icon( imageVector = if (balanceHidden) Icons.Filled.VisibilityOff else Icons.Filled.Visibility, contentDescription = null, tint = MaterialTheme.colorScheme.onSurfaceVariant, modifier = Modifier.size(20.dp) ) Spacer(Modifier.width(8.dp)) Text( text = if (balanceHidden) "••••••" else formatSats(s.sats), style = MaterialTheme.typography.displayLarge.copy( fontFamily = FontFamily.Monospace ), color = if (balanceHidden) MaterialTheme.colorScheme.onSurface.copy(alpha = 0.35f) else MaterialTheme.colorScheme.primary, maxLines = 1, softWrap = false, modifier = Modifier .widthIn(max = 280.dp) .combinedClickable( onClick = { if (!balanceHidden) vm.toggleBalanceVisibility() }, onClickLabel = if (balanceHidden) null else strings.hideBalance, onLongClick = { if (balanceHidden) vm.toggleBalanceVisibility() }, onLongClickLabel = if (balanceHidden) strings.revealBalance else null, indication = null, interactionSource = remember { MutableInteractionSource() } ) ) Spacer(Modifier.width(8.dp)) Text( text = strings.sats, style = MaterialTheme.typography.titleMedium.copy( fontFamily = FontFamily.Monospace ), color = MaterialTheme.colorScheme.onSurfaceVariant ) Spacer(Modifier.width(8.dp)) } Spacer(Modifier.height(2.dp)) val currency = selectedCurrency if (currency != null) { Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center ) { if (s.fiatAmount != null && s.fiatCurrency != null) { Text( text = if (balanceHidden) "••••" else formatFiat(s.fiatAmount, s.fiatCurrency), style = MaterialTheme.typography.bodyMedium.copy( fontFamily = FontFamily.Monospace ), color = if (balanceHidden) MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.35f) else MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.75f), maxLines = 1, softWrap = false ) Spacer(Modifier.width(4.dp)) } Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier .clickable( onClick = { showCurrencyPicker = true }, onClickLabel = strings.changeCurrency, indication = null, interactionSource = remember { MutableInteractionSource() } ) .padding(horizontal = 4.dp, vertical = 4.dp) ) { Text( text = currency, style = MaterialTheme.typography.bodyMedium.copy( fontFamily = FontFamily.Monospace ), color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.75f) ) Spacer(Modifier.width(2.dp)) Icon( imageVector = Icons.Filled.ArrowDropDown, contentDescription = strings.changeCurrency, tint = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.5f), modifier = Modifier.size(14.dp) ) } } } else { TextButton( onClick = { showCurrencyPicker = true }, contentPadding = PaddingValues(horizontal = 8.dp, vertical = 2.dp) ) { Text( text = strings.showFiat, style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.45f) ) } } Spacer(Modifier.height(8.dp)) if (!s.isRefreshing) { val updatedLabel: String = remember(s.lastUpdated) { formatLastUpdated(s.lastUpdated, strings) } Text( text = strings.updatedAt(updatedLabel), style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant ) } Spacer(Modifier.height(16.dp)) TextButton(onClick = onHistoryClick) { Text( text = strings.viewHistory, style = MaterialTheme.typography.labelLarge, color = MaterialTheme.colorScheme.primary ) } // ── NEW: Language switcher ───────────────────────────────── Spacer(Modifier.height(8.dp)) LanguageSwitcherButton( currentTag = lyricist.languageTag, supportedTags = supportedTags, onSelect = { tag -> lyricist.languageTag = tag } ) // ────────────────────────────────────────────────────────── if (showCurrencyPicker) { CurrencyPickerSheet( currencies = currencyList, selectedCurrency = selectedCurrency, onSelect = { code -> vm.setSelectedCurrency(code) showCurrencyPicker = false }, isLoading = isLoadingCurrencies, onDismiss = { showCurrencyPicker = false } ) } } is BalanceState.Error -> { if (s.staleSats != null) { Text( text = if (balanceHidden) "••••••" else formatSats(s.staleSats), style = MaterialTheme.typography.displayLarge.copy( fontFamily = FontFamily.Monospace ), color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.4f) ) Text( text = strings.sats, style = MaterialTheme.typography.titleMedium, color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.4f) ) Spacer(Modifier.height(12.dp)) } Surface( color = MaterialTheme.colorScheme.errorContainer, shape = MaterialTheme.shapes.small, modifier = Modifier.fillMaxWidth(0.85f) ) { Text( text = if (s.staleSats != null) strings.couldNotRefresh else strings.couldNotLoadBalance, style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onErrorContainer, modifier = Modifier.padding(12.dp) ) } } } } } } // ── NEW: Language switcher composable ───────────────────────────────────────── @Composable private fun LanguageSwitcherButton( currentTag : String, supportedTags: List, onSelect : (String) -> Unit ) { var expanded by remember { mutableStateOf(false) } // Human-readable display names for each tag val displayName: (String) -> String = { tag -> when (tag) { "en" -> "English" "es" -> "Español" "eu" -> "Euskara" else -> tag.uppercase() } } Box { TextButton( onClick = { expanded = true }, contentPadding = PaddingValues(horizontal = 8.dp, vertical = 2.dp) ) { Icon( imageVector = Icons.Filled.Language, contentDescription = null, modifier = Modifier.size(16.dp), tint = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.6f) ) Spacer(Modifier.width(4.dp)) Text( text = displayName(currentTag), style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.6f) ) Icon( imageVector = Icons.Filled.ArrowDropDown, contentDescription = null, modifier = Modifier.size(14.dp), tint = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.4f) ) } DropdownMenu( expanded = expanded, onDismissRequest = { expanded = false } ) { supportedTags.forEach { tag -> DropdownMenuItem( text = { Row(verticalAlignment = Alignment.CenterVertically) { Text( text = displayName(tag), style = MaterialTheme.typography.bodyMedium, color = if (tag == currentTag) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurface ) } }, trailingIcon = if (tag == currentTag) ({ Icon( imageVector = Icons.Filled.Check, contentDescription = null, tint = MaterialTheme.colorScheme.primary, modifier = Modifier.size(16.dp) ) }) else null, onClick = { onSelect(tag) expanded = false } ) } } } } // ───────────────────────────────────────────────────────────────────────────── private fun formatSats(sats: Long): String = "%,d".format(sats).replace(',', ' ') private fun formatLastUpdated(epochMs: Long, strings: AppStrings): String { val ageMs = System.currentTimeMillis() - epochMs return when { ageMs < 60_000L -> strings.justNow ageMs < 3_600_000L -> strings.minAgo((ageMs / 60_000L).toInt()) else -> { val formatter = java.time.format.DateTimeFormatter .ofPattern("HH:mm") .withZone(java.time.ZoneId.systemDefault()) strings.atTime(formatter.format(java.time.Instant.ofEpochMilli(epochMs))) } } } @OptIn(ExperimentalMaterial3Api::class) @Composable private fun CurrencyPickerSheet( currencies : List, isLoading : Boolean, selectedCurrency: String?, onSelect : (String?) -> Unit, onDismiss : () -> Unit ) { val strings = LocalAppStrings.current val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) var query by remember { mutableStateOf("") } val focusManager = LocalFocusManager.current val filtered = remember(query, currencies) { if (query.isBlank()) currencies else currencies.filter { it.contains(query.trim(), ignoreCase = true) } } ModalBottomSheet( onDismissRequest = onDismiss, sheetState = sheetState ) { Column(modifier = Modifier.fillMaxWidth()) { Text( text = strings.selectCurrency, style = MaterialTheme.typography.titleMedium, modifier = Modifier.padding(horizontal = 24.dp, vertical = 8.dp) ) ListItem( headlineContent = { Text( text = strings.currencyNone, style = MaterialTheme.typography.bodyLarge, color = if (selectedCurrency == null) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurface ) }, supportingContent = { Text( text = strings.hideFiatEquivalent, style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant ) }, trailingContent = if (selectedCurrency == null) ({ Icon( imageVector = Icons.Filled.Check, contentDescription = strings.selected, tint = MaterialTheme.colorScheme.primary, modifier = Modifier.size(18.dp) ) }) else null, modifier = Modifier.clickable { onSelect(null) } ) HorizontalDivider(thickness = 1.dp) if (!isLoading) { OutlinedTextField( value = query, onValueChange = { query = it }, placeholder = { Text(strings.search) }, leadingIcon = { Icon( imageVector = Icons.Filled.Search, contentDescription = null, modifier = Modifier.size(18.dp) ) }, singleLine = true, keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Text, imeAction = ImeAction.Search ), keyboardActions = KeyboardActions( onSearch = { focusManager.clearFocus() } ), modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp, vertical = 8.dp) ) } when { isLoading -> { Box( modifier = Modifier .fillMaxWidth() .padding(vertical = 48.dp), contentAlignment = Alignment.Center ) { CircularProgressIndicator(modifier = Modifier.size(28.dp)) } } filtered.isEmpty() -> { Text( text = if (query.isBlank()) strings.noCurrenciesAvailable else strings.noCurrenciesMatch(query), style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant, modifier = Modifier.padding(horizontal = 24.dp, vertical = 16.dp) ) } else -> { LazyColumn( modifier = Modifier.fillMaxWidth(), contentPadding = PaddingValues(bottom = 32.dp) ) { items(filtered, key = { it }) { code -> val isSelected = code == selectedCurrency ListItem( headlineContent = { Text( text = code, style = MaterialTheme.typography.bodyLarge, color = if (isSelected) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurface ) }, trailingContent = if (isSelected) ({ Icon( imageVector = Icons.Filled.Check, contentDescription = strings.selected, tint = MaterialTheme.colorScheme.primary, modifier = Modifier.size(18.dp) ) }) else null, modifier = Modifier.clickable { onSelect(code) } ) HorizontalDivider(thickness = 0.5.dp) } } } } } } }