feat: improve Zap Settings UI with sections, chips, and animations

- Organize zap settings into three clear sections with primary-colored
  headers: Quick Zap Amounts, Zap Privacy, and Nostr Wallet Connect
- Replace plain Buttons with InputChip for amount selection — each chip
  shows a lightning bolt, the amount, and a close icon to remove it
- Add animateContentSize() to the chips FlowRow so adding/removing
  amounts animates smoothly
- Add descriptive sub-text under each section header explaining what
  the control does
- NWC section: show an animated "Connected / Not Connected" status badge
  (green CheckCircle vs grey RadioButtonUnchecked) that cross-fades via
  AnimatedContent when the connection state changes
- NWC section: add animateColorAsState for the status color transition
- Replace the bare icon-only Add button with an OutlinedButton labelled
  "Connect Wallet" for better discoverability
- Move the manual pubkey/relay/secret fields into a collapsible
  "Advanced" row animated with AnimatedVisibility (expandVertically +
  fadeIn/fadeOut). The section auto-expands when a connection already
  exists or when a QR/clipboard import populates the pubkey field
- Add 9 new string resources for the new UI copy

https://claude.ai/code/session_01QzBq319fsxhrT91bGxws6N
This commit is contained in:
Claude
2026-03-08 02:09:28 +00:00
parent 46d54d2826
commit 233d0bf089
2 changed files with 349 additions and 169 deletions
@@ -31,6 +31,19 @@ import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.biometric.BiometricManager import androidx.biometric.BiometricManager
import androidx.biometric.BiometricPrompt import androidx.biometric.BiometricPrompt
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
import androidx.compose.animation.core.tween
import androidx.compose.animation.expandVertically
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.ExperimentalLayoutApi
@@ -42,12 +55,18 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.CheckCircle
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.ExpandLess
import androidx.compose.material.icons.filled.ExpandMore
import androidx.compose.material.icons.outlined.Add import androidx.compose.material.icons.outlined.Add
import androidx.compose.material.icons.outlined.ContentPaste import androidx.compose.material.icons.outlined.ContentPaste
import androidx.compose.material.icons.outlined.RadioButtonUnchecked
import androidx.compose.material.icons.outlined.Visibility import androidx.compose.material.icons.outlined.Visibility
import androidx.compose.material.icons.outlined.VisibilityOff import androidx.compose.material.icons.outlined.VisibilityOff
import androidx.compose.material3.Button import androidx.compose.material3.Button
@@ -55,7 +74,10 @@ import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.InputChip
import androidx.compose.material3.InputChipDefaults
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@@ -75,7 +97,6 @@ import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.components.TextSpinner import com.vitorpamplona.amethyst.ui.components.TextSpinner
@@ -88,6 +109,9 @@ import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
import com.vitorpamplona.amethyst.ui.theme.DividerThickness import com.vitorpamplona.amethyst.ui.theme.DividerThickness
import com.vitorpamplona.amethyst.ui.theme.Font14SP import com.vitorpamplona.amethyst.ui.theme.Font14SP
import com.vitorpamplona.amethyst.ui.theme.RowColSpacing
import com.vitorpamplona.amethyst.ui.theme.SettingsCategoryFirstModifier
import com.vitorpamplona.amethyst.ui.theme.SettingsCategorySpacingModifier
import com.vitorpamplona.amethyst.ui.theme.Size24Modifier import com.vitorpamplona.amethyst.ui.theme.Size24Modifier
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent
@@ -103,7 +127,6 @@ fun UpdateZapAmountContent(
) { ) {
val context = LocalContext.current val context = LocalContext.current
val clipboardManager = LocalClipboardManager.current val clipboardManager = LocalClipboardManager.current
val uri = LocalUriHandler.current val uri = LocalUriHandler.current
val zapTypes = val zapTypes =
@@ -155,46 +178,82 @@ fun UpdateZapAmountContent(
} }
} }
var qrScanning by remember { mutableStateOf(false) }
// Expand manual config automatically when a wallet connection exists
var showManualConfig by remember { mutableStateOf(postViewModel.walletConnectPubkey.text.isNotBlank()) }
LaunchedEffect(postViewModel.walletConnectPubkey.text) {
if (postViewModel.walletConnectPubkey.text.isNotBlank()) {
showManualConfig = true
}
}
Column( Column(
modifier = modifier =
Modifier Modifier
.padding(10.dp) .padding(horizontal = 16.dp, vertical = 8.dp)
.fillMaxWidth() .fillMaxWidth()
.imePadding() .imePadding()
.verticalScroll(rememberScrollState()), .verticalScroll(rememberScrollState()),
) { ) {
// ── Section 1: Quick Zap Amounts ──────────────────────────────────────
Text(
text = stringRes(R.string.quick_zap_amounts),
color = MaterialTheme.colorScheme.primary,
style = MaterialTheme.typography.titleSmall,
modifier = SettingsCategoryFirstModifier,
)
Text(
text = stringRes(R.string.quick_zap_amounts_explainer),
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.placeholderText,
modifier = Modifier.padding(bottom = 12.dp),
)
// Amount chips — animateContentSize gives a smooth expand/collapse when
// chips are added or removed
FlowRow( FlowRow(
modifier = Modifier.fillMaxWidth(), modifier =
horizontalArrangement = Arrangement.Center, Modifier
.fillMaxWidth()
.animateContentSize(animationSpec = spring(stiffness = Spring.StiffnessMediumLow)),
horizontalArrangement = Arrangement.spacedBy(6.dp),
verticalArrangement = Arrangement.spacedBy(6.dp),
) { ) {
postViewModel.amountSet.forEach { amountInSats -> postViewModel.amountSet.forEach { amountInSats ->
Button( InputChip(
modifier = Modifier.padding(horizontal = 3.dp), selected = false,
shape = ButtonBorder,
colors =
ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primary,
),
onClick = { postViewModel.removeAmount(amountInSats) }, onClick = { postViewModel.removeAmount(amountInSats) },
) { label = {
Text( Text(
"${ text = "${showAmount(amountInSats.toBigDecimal().setScale(1))}",
showAmount(
amountInSats.toBigDecimal().setScale(1),
) )
} ", },
color = Color.White, trailingIcon = {
textAlign = TextAlign.Center, Icon(
imageVector = Icons.Filled.Close,
contentDescription = stringRes(R.string.remove),
modifier = Modifier.size(InputChipDefaults.AvatarSize),
)
},
colors =
InputChipDefaults.inputChipColors(
containerColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.12f),
labelColor = MaterialTheme.colorScheme.primary,
trailingIconColor = MaterialTheme.colorScheme.primary,
),
) )
} }
} }
}
Spacer(modifier = Modifier.height(10.dp)) Spacer(modifier = Modifier.height(12.dp))
// Add new amount
Row( Row(
modifier = Modifier.fillMaxWidth().padding(vertical = 5.dp), modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp),
) { ) {
OutlinedTextField( OutlinedTextField(
label = { Text(text = stringRes(R.string.new_amount_in_sats)) }, label = { Text(text = stringRes(R.string.new_amount_in_sats)) },
@@ -212,7 +271,7 @@ fun UpdateZapAmountContent(
) )
}, },
singleLine = true, singleLine = true,
modifier = Modifier.padding(end = 10.dp).weight(1f), modifier = Modifier.weight(1f),
) )
Button( Button(
@@ -223,67 +282,159 @@ fun UpdateZapAmountContent(
containerColor = MaterialTheme.colorScheme.primary, containerColor = MaterialTheme.colorScheme.primary,
), ),
) { ) {
Icon(
imageVector = Icons.Outlined.Add,
contentDescription = stringRes(R.string.add),
modifier = Modifier.size(18.dp),
)
Spacer(modifier = Modifier.width(4.dp))
Text(text = stringRes(R.string.add), color = Color.White) Text(text = stringRes(R.string.add), color = Color.White)
} }
} }
Row( // ── Section 2: Zap Privacy ────────────────────────────────────────────
modifier = Modifier.fillMaxWidth().padding(vertical = 5.dp),
verticalAlignment = Alignment.CenterVertically,
) {
TextSpinner(
label = stringRes(id = R.string.zap_type_explainer),
placeholder = zapTypes.firstOrNull { it.first == accountViewModel.defaultZapType() }?.second ?: zapTypes.firstOrNull()?.second ?: "",
options = zapOptions,
onSelect = { postViewModel.selectedZapType = zapTypes[it].first },
modifier = Modifier.weight(1f).padding(end = 5.dp),
)
}
HorizontalDivider( Text(
modifier = Modifier.padding(vertical = 10.dp), text = stringRes(R.string.zap_privacy_section),
thickness = DividerThickness, color = MaterialTheme.colorScheme.primary,
style = MaterialTheme.typography.titleSmall,
modifier = SettingsCategorySpacingModifier,
)
Text(
text = stringRes(R.string.zap_type_section_explainer),
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.placeholderText,
modifier = Modifier.padding(bottom = 8.dp),
) )
var qrScanning by remember { mutableStateOf(false) }
Row( Row(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
) { ) {
Text( TextSpinner(
stringRes(id = R.string.wallet_connect_service), label = stringRes(id = R.string.zap_type_explainer),
Modifier.weight(1f), placeholder =
zapTypes
.firstOrNull { it.first == accountViewModel.defaultZapType() }
?.second
?: zapTypes.firstOrNull()?.second
?: "",
options = zapOptions,
onSelect = { postViewModel.selectedZapType = zapTypes[it].first },
modifier = Modifier.fillMaxWidth(),
)
}
// ── Section 3: Nostr Wallet Connect ───────────────────────────────────
HorizontalDivider(
modifier = Modifier.padding(vertical = 16.dp),
thickness = DividerThickness,
) )
IconButton( // Section header + connection status indicator
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = RowColSpacing,
) {
Column(modifier = Modifier.weight(1f)) {
Text(
text = stringRes(R.string.wallet_connect_service),
color = MaterialTheme.colorScheme.primary,
style = MaterialTheme.typography.titleSmall,
)
Text(
text = stringRes(R.string.wallet_connect_service_explainer),
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.placeholderText,
modifier = Modifier.padding(top = 2.dp),
)
}
}
// Animated connection status badge
val isConnected = postViewModel.walletConnectPubkey.text.isNotBlank()
val statusColor by animateColorAsState(
targetValue = if (isConnected) Color(0xFF4CAF50) else MaterialTheme.colorScheme.placeholderText,
animationSpec = tween(durationMillis = 400),
label = "nwc_status_color",
)
AnimatedContent(
targetState = isConnected,
transitionSpec = { fadeIn(tween(300)) togetherWith fadeOut(tween(300)) },
label = "nwc_status_badge",
) { connected ->
Row(
modifier = Modifier.padding(vertical = 8.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(6.dp),
) {
Icon(
imageVector = if (connected) Icons.Filled.CheckCircle else Icons.Outlined.RadioButtonUnchecked,
contentDescription = null,
tint = statusColor,
modifier = Modifier.size(18.dp),
)
Text(
text =
if (connected) {
stringRes(R.string.wallet_connect_status_connected)
} else {
stringRes(R.string.wallet_connect_status_not_connected)
},
color = statusColor,
style = MaterialTheme.typography.bodyMedium,
)
}
}
// Connect action buttons
Row(
modifier = Modifier.fillMaxWidth().padding(bottom = 4.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
// Primary "Connect Wallet" button — opens the NWC app deep link
OutlinedButton(
modifier = Modifier.weight(1f),
shape = ButtonBorder,
onClick = { onClick = {
onClose() onClose()
try { try {
uri.openUri("nostrnwc://connect?appname=Amethyst&appicon=https%3A%2F%2Fraw.githubusercontent.com%2Fvitorpamplona%2Famethyst%2Frefs%2Fheads%2Fmain%2Ficon.png&callback=amethyst%2Bwalletconnect%3A%2F%2Fdlnwc") uri.openUri(
"nostrnwc://connect?appname=Amethyst&appicon=https%3A%2F%2Fraw.githubusercontent.com%2Fvitorpamplona%2Famethyst%2Frefs%2Fheads%2Fmain%2Ficon.png&callback=amethyst%2Bwalletconnect%3A%2F%2Fdlnwc",
)
} catch (_: IllegalArgumentException) { } catch (_: IllegalArgumentException) {
accountViewModel.toastManager.toast(R.string.couldnt_find_nwc_wallets, R.string.couldnt_find_nwc_wallets_description) accountViewModel.toastManager.toast(
R.string.couldnt_find_nwc_wallets,
R.string.couldnt_find_nwc_wallets_description,
)
} }
}, },
) { ) {
Icon( Icon(
imageVector = Icons.Outlined.Add, imageVector = Icons.Outlined.Add,
contentDescription = stringRes(id = R.string.connect_to_new_nwc_wallet), contentDescription = null,
modifier = Size24Modifier, modifier = Modifier.size(18.dp),
tint = MaterialTheme.colorScheme.primary,
) )
Spacer(modifier = Modifier.width(4.dp))
Text(text = stringRes(R.string.wallet_connect_connect_app))
} }
// Paste from clipboard
IconButton( IconButton(
onClick = { onClick = {
val uri = clipboardManager.getText()?.text val clipText = clipboardManager.getText()?.text
try { try {
uri?.let { clipText?.let { postViewModel.copyFromClipboard(it) }
postViewModel.copyFromClipboard(it)
}
} catch (e: IllegalArgumentException) { } catch (e: IllegalArgumentException) {
accountViewModel.toastManager.toast(R.string.invalid_nip47_uri_title, R.string.invalid_nip47_uri_description, uri ?: "") accountViewModel.toastManager.toast(
R.string.invalid_nip47_uri_title,
R.string.invalid_nip47_uri_description,
clipText ?: "",
)
} }
}, },
) { ) {
@@ -295,6 +446,7 @@ fun UpdateZapAmountContent(
) )
} }
// QR code scanner
IconButton(onClick = { qrScanning = true }) { IconButton(onClick = { qrScanning = true }) {
Icon( Icon(
painter = painterRes(R.drawable.ic_qrcode, 3), painter = painterRes(R.drawable.ic_qrcode, 3),
@@ -305,18 +457,6 @@ fun UpdateZapAmountContent(
} }
} }
Row(
modifier = Modifier.fillMaxWidth().padding(bottom = 5.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Text(
stringRes(id = R.string.wallet_connect_service_explainer),
Modifier.weight(1f),
color = MaterialTheme.colorScheme.placeholderText,
fontSize = Font14SP,
)
}
if (qrScanning) { if (qrScanning) {
SimpleQrCodeScanner { SimpleQrCodeScanner {
qrScanning = false qrScanning = false
@@ -340,6 +480,36 @@ fun UpdateZapAmountContent(
} }
} }
// Expandable manual configuration section
Row(
modifier =
Modifier
.fillMaxWidth()
.clickable { showManualConfig = !showManualConfig }
.padding(vertical = 10.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = stringRes(R.string.wallet_connect_manual_config),
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.placeholderText,
modifier = Modifier.weight(1f),
fontSize = Font14SP,
)
Icon(
imageVector = if (showManualConfig) Icons.Filled.ExpandLess else Icons.Filled.ExpandMore,
contentDescription = null,
tint = MaterialTheme.colorScheme.placeholderText,
modifier = Modifier.size(20.dp),
)
}
AnimatedVisibility(
visible = showManualConfig,
enter = expandVertically(animationSpec = spring(stiffness = Spring.StiffnessMediumLow)) + fadeIn(),
exit = shrinkVertically(animationSpec = spring(stiffness = Spring.StiffnessMediumLow)) + fadeOut(),
) {
Column {
Row( Row(
modifier = Modifier.fillMaxWidth().padding(vertical = 5.dp), modifier = Modifier.fillMaxWidth().padding(vertical = 5.dp),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
@@ -359,7 +529,7 @@ fun UpdateZapAmountContent(
) )
}, },
singleLine = true, singleLine = true,
modifier = Modifier.weight(1f), modifier = Modifier.fillMaxWidth(),
) )
} }
@@ -369,7 +539,7 @@ fun UpdateZapAmountContent(
) { ) {
OutlinedTextField( OutlinedTextField(
label = { Text(text = stringRes(R.string.wallet_connect_service_relay)) }, label = { Text(text = stringRes(R.string.wallet_connect_service_relay)) },
modifier = Modifier.weight(1f), modifier = Modifier.fillMaxWidth(),
value = postViewModel.walletConnectRelay, value = postViewModel.walletConnectRelay,
onValueChange = { postViewModel.walletConnectRelay = it }, onValueChange = { postViewModel.walletConnectRelay = it },
placeholder = { placeholder = {
@@ -385,7 +555,7 @@ fun UpdateZapAmountContent(
var showPassword by remember { mutableStateOf(false) } var showPassword by remember { mutableStateOf(false) }
val context = LocalContext.current val secretContext = LocalContext.current
val keyguardLauncher = val keyguardLauncher =
rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult -> rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
@@ -402,7 +572,7 @@ fun UpdateZapAmountContent(
) { ) {
OutlinedTextField( OutlinedTextField(
label = { Text(text = stringRes(R.string.wallet_connect_service_secret)) }, label = { Text(text = stringRes(R.string.wallet_connect_service_secret)) },
modifier = Modifier.weight(1f), modifier = Modifier.fillMaxWidth(),
value = postViewModel.walletConnectSecret, value = postViewModel.walletConnectSecret,
onValueChange = { postViewModel.walletConnectSecret = it }, onValueChange = { postViewModel.walletConnectSecret = it },
keyboardOptions = keyboardOptions =
@@ -423,7 +593,7 @@ fun UpdateZapAmountContent(
if (!showPassword) { if (!showPassword) {
authenticate( authenticate(
title = authTitle, title = authTitle,
context = context, context = secretContext,
keyguardLauncher = keyguardLauncher, keyguardLauncher = keyguardLauncher,
onApproved = { showPassword = true }, onApproved = { showPassword = true },
onError = { title, message -> accountViewModel.toastManager.toast(title, message) }, onError = { title, message -> accountViewModel.toastManager.toast(title, message) },
@@ -444,9 +614,7 @@ fun UpdateZapAmountContent(
if (showPassword) { if (showPassword) {
stringRes(R.string.show_password) stringRes(R.string.show_password)
} else { } else {
stringRes( stringRes(R.string.hide_password)
R.string.hide_password,
)
}, },
) )
} }
@@ -458,6 +626,10 @@ fun UpdateZapAmountContent(
} }
} }
Spacer(modifier = Modifier.height(16.dp))
}
}
fun authenticate( fun authenticate(
title: String, title: String,
context: Context, context: Context,
+8
View File
@@ -454,6 +454,14 @@
<string name="wallet_connect_service_secret">Wallet Connect Secret</string> <string name="wallet_connect_service_secret">Wallet Connect Secret</string>
<string name="wallet_connect_service_show_secret">Show secret key</string> <string name="wallet_connect_service_show_secret">Show secret key</string>
<string name="wallet_connect_service_secret_placeholder">nsec / hex private key</string> <string name="wallet_connect_service_secret_placeholder">nsec / hex private key</string>
<string name="wallet_connect_status_connected">Connected</string>
<string name="wallet_connect_status_not_connected">Not connected</string>
<string name="wallet_connect_manual_config">Advanced: enter connection details manually</string>
<string name="quick_zap_amounts">Quick Zap Amounts</string>
<string name="quick_zap_amounts_explainer">Shown when long-pressing the zap button. Tap an amount to remove it.</string>
<string name="zap_privacy_section">Zap Privacy</string>
<string name="zap_type_section_explainer">Controls how your identity is shown when you send a zap.</string>
<string name="wallet_connect_connect_app">Connect Wallet</string>
<string name="pledge_amount_in_sats">Pledge Amount in Sats</string> <string name="pledge_amount_in_sats">Pledge Amount in Sats</string>
<string name="post_poll">Post Poll</string> <string name="post_poll">Post Poll</string>