1package com.peachpasswords.ui.pairing23import androidx.compose.foundation.clickable4import androidx.compose.foundation.layout.Column5import androidx.compose.foundation.layout.Row6import androidx.compose.foundation.layout.Spacer7import androidx.compose.foundation.layout.fillMaxWidth8import androidx.compose.foundation.layout.height9import androidx.compose.foundation.layout.padding10import androidx.compose.material3.OutlinedTextField11import androidx.compose.material3.RadioButton12import androidx.compose.material3.Text13import androidx.compose.runtime.Composable14import androidx.compose.runtime.getValue15import androidx.compose.runtime.mutableStateOf16import androidx.compose.runtime.remember17import androidx.compose.runtime.setValue18import androidx.compose.ui.Alignment19import androidx.compose.ui.Modifier20import androidx.compose.ui.text.style.TextAlign21import androidx.compose.ui.unit.dp22import androidx.compose.ui.unit.sp23import com.peachpasswords.sync.SyncMode24import com.peachpasswords.ui.theme.LocalPeachAccent25import com.peachpasswords.ui.theme.PeachSpacing26import com.peachpasswords.ui.theme.PeachType27import com.peachpasswords.ui.theme.peachColors2829internal fun pairingRelaySummary(mode: SyncMode): String = when (mode) {30 SyncMode.PUBLIC -> "Using Public relay"31 SyncMode.PRIVATE -> "Using Private relay"32 SyncMode.OFF -> "Choose a relay to pair"33}34353637383940@Composable41internal fun PairingRelayChoice(42 mode: SyncMode,43 configuredPrivateUrl: String,44 expanded: Boolean,45 collapsible: Boolean = true,46 onExpandedChange: (Boolean) -> Unit,47 onSelectMode: (SyncMode) -> Unit,48 onSavePrivateUrl: (String) -> Boolean,49) {50 var draft by remember(configuredPrivateUrl) { mutableStateOf(configuredPrivateUrl) }51 var status by remember { mutableStateOf<String?>(null) }5253 if (!expanded) {54 Row(55 modifier = Modifier56 .fillMaxWidth()57 .clickable { onExpandedChange(true) }58 .padding(vertical = PeachSpacing.sm),59 verticalAlignment = Alignment.CenterVertically,60 ) {61 Text(62 pairingRelaySummary(mode),63 color = peachColors.T2,64 fontFamily = PeachType.Mono,65 fontSize = 10.sp,66 modifier = Modifier.weight(1f),67 )68 Text(69 "change",70 color = LocalPeachAccent.current,71 fontFamily = PeachType.Mono,72 fontSize = 10.sp,73 )74 }75 return76 }7778 Column(Modifier.fillMaxWidth()) {79 Text(80 "Choose the relay this vault uses",81 color = peachColors.T0,82 fontFamily = PeachType.Body,83 fontSize = 13.sp,84 )85 Spacer(Modifier.height(PeachSpacing.xs))86 RelayChoiceRow(87 title = "Public relay",88 detail = "Peach's relay. Nothing to set up.",89 selected = mode == SyncMode.PUBLIC,90 ) {91 status = null92 onSelectMode(SyncMode.PUBLIC)93 }94 RelayChoiceRow(95 title = "Private relay",96 detail = "Your own peach-relay on your domain.",97 selected = mode == SyncMode.PRIVATE,98 ) {99 status = null100 onSelectMode(SyncMode.PRIVATE)101 }102 if (mode == SyncMode.PRIVATE) {103 OutlinedTextField(104 value = draft,105 onValueChange = {106 draft = it107 status = null108 },109 label = { Text("relay URL") },110 placeholder = { Text("wss://relay.example.com") },111 singleLine = true,112 modifier = Modifier.fillMaxWidth(),113 )114 Text(115 status ?: if (configuredPrivateUrl.isBlank()) {116 "Pairing waits until a valid self-hosted relay is saved."117 } else {118 "Only secure wss:// relay URLs are accepted."119 },120 color = if (status?.startsWith("Use") == true || configuredPrivateUrl.isBlank()) {121 peachColors.Breach122 } else {123 peachColors.T2124 },125 fontFamily = PeachType.Mono,126 fontSize = 9.sp,127 modifier = Modifier128 .fillMaxWidth()129 .padding(top = PeachSpacing.xs),130 textAlign = TextAlign.Start,131 )132 Spacer(Modifier.height(PeachSpacing.sm))133 PillButton("Save private relay", LocalPeachAccent.current, filled = true) {134 status = if (onSavePrivateUrl(draft)) "Saved" else "Use a valid wss:// relay URL"135 }136 }137 if (collapsible) {138 Spacer(Modifier.height(PeachSpacing.xs))139 PillButton("Done", peachColors.T2, filled = false) {140 onExpandedChange(false)141 }142 }143 }144}145146@Composable147private fun RelayChoiceRow(148 title: String,149 detail: String,150 selected: Boolean,151 onSelect: () -> Unit,152) {153 Row(154 modifier = Modifier155 .fillMaxWidth()156 .clickable(onClick = onSelect)157 .padding(vertical = PeachSpacing.xs),158 verticalAlignment = Alignment.CenterVertically,159 ) {160 Column(Modifier.weight(1f)) {161 Text(title, color = peachColors.T0, fontFamily = PeachType.Body, fontSize = 12.sp)162 Text(detail, color = peachColors.T2, fontFamily = PeachType.Body, fontSize = 10.sp)163 }164 RadioButton(selected = selected, onClick = onSelect)165 }166}