package com.peachpasswords.ui.pairing import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row 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.material3.OutlinedTextField import androidx.compose.material3.RadioButton import androidx.compose.material3.Text 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.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.peachpasswords.sync.SyncMode import com.peachpasswords.ui.theme.LocalPeachAccent import com.peachpasswords.ui.theme.PeachSpacing import com.peachpasswords.ui.theme.PeachType import com.peachpasswords.ui.theme.peachColors internal fun pairingRelaySummary(mode: SyncMode): String = when (mode) { SyncMode.PUBLIC -> "Using Public relay" SyncMode.PRIVATE -> "Using Private relay" SyncMode.OFF -> "Choose a relay to pair" } /** * Pairing can use the public relay or a self-hosted peach-relay. No-sync is * deliberately absent because this control exists only while creating a * single-use pairing invitation. */ @Composable internal fun PairingRelayChoice( mode: SyncMode, configuredPrivateUrl: String, expanded: Boolean, collapsible: Boolean = true, onExpandedChange: (Boolean) -> Unit, onSelectMode: (SyncMode) -> Unit, onSavePrivateUrl: (String) -> Boolean, ) { var draft by remember(configuredPrivateUrl) { mutableStateOf(configuredPrivateUrl) } var status by remember { mutableStateOf(null) } if (!expanded) { Row( modifier = Modifier .fillMaxWidth() .clickable { onExpandedChange(true) } .padding(vertical = PeachSpacing.sm), verticalAlignment = Alignment.CenterVertically, ) { Text( pairingRelaySummary(mode), color = peachColors.T2, fontFamily = PeachType.Mono, fontSize = 10.sp, modifier = Modifier.weight(1f), ) Text( "change", color = LocalPeachAccent.current, fontFamily = PeachType.Mono, fontSize = 10.sp, ) } return } Column(Modifier.fillMaxWidth()) { Text( "Choose the relay this vault uses", color = peachColors.T0, fontFamily = PeachType.Body, fontSize = 13.sp, ) Spacer(Modifier.height(PeachSpacing.xs)) RelayChoiceRow( title = "Public relay", detail = "Peach's relay. Nothing to set up.", selected = mode == SyncMode.PUBLIC, ) { status = null onSelectMode(SyncMode.PUBLIC) } RelayChoiceRow( title = "Private relay", detail = "Your own peach-relay on your domain.", selected = mode == SyncMode.PRIVATE, ) { status = null onSelectMode(SyncMode.PRIVATE) } if (mode == SyncMode.PRIVATE) { OutlinedTextField( value = draft, onValueChange = { draft = it status = null }, label = { Text("relay URL") }, placeholder = { Text("wss://relay.example.com") }, singleLine = true, modifier = Modifier.fillMaxWidth(), ) Text( status ?: if (configuredPrivateUrl.isBlank()) { "Pairing waits until a valid self-hosted relay is saved." } else { "Only secure wss:// relay URLs are accepted." }, color = if (status?.startsWith("Use") == true || configuredPrivateUrl.isBlank()) { peachColors.Breach } else { peachColors.T2 }, fontFamily = PeachType.Mono, fontSize = 9.sp, modifier = Modifier .fillMaxWidth() .padding(top = PeachSpacing.xs), textAlign = TextAlign.Start, ) Spacer(Modifier.height(PeachSpacing.sm)) PillButton("Save private relay", LocalPeachAccent.current, filled = true) { status = if (onSavePrivateUrl(draft)) "Saved" else "Use a valid wss:// relay URL" } } if (collapsible) { Spacer(Modifier.height(PeachSpacing.xs)) PillButton("Done", peachColors.T2, filled = false) { onExpandedChange(false) } } } } @Composable private fun RelayChoiceRow( title: String, detail: String, selected: Boolean, onSelect: () -> Unit, ) { Row( modifier = Modifier .fillMaxWidth() .clickable(onClick = onSelect) .padding(vertical = PeachSpacing.xs), verticalAlignment = Alignment.CenterVertically, ) { Column(Modifier.weight(1f)) { Text(title, color = peachColors.T0, fontFamily = PeachType.Body, fontSize = 12.sp) Text(detail, color = peachColors.T2, fontFamily = PeachType.Body, fontSize = 10.sp) } RadioButton(selected = selected, onClick = onSelect) } }