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.sync.privateRelayDraftOf25import com.peachpasswords.ui.theme.LocalPeachAccent26import com.peachpasswords.ui.theme.PeachSpacing27import com.peachpasswords.ui.theme.PeachType28import com.peachpasswords.ui.theme.peachColors2930internal fun pairingRelaySummary(mode: SyncMode): String = when (mode) {31 SyncMode.PUBLIC -> "Using Public relay"32 SyncMode.PRIVATE -> "Using Private relay"33 SyncMode.OFF -> "Choose a relay to pair"34}35363738394041@Composable42internal fun PairingRelayChoice(43 mode: SyncMode,44 configuredPrivateUrl: String,45 expanded: Boolean,46 collapsible: Boolean = true,47 onExpandedChange: (Boolean) -> Unit,48 onSelectMode: (SyncMode) -> Unit,49 onSavePrivateUrl: (String) -> Boolean,50) {51 var draft by remember(configuredPrivateUrl) {52 mutableStateOf(privateRelayDraftOf(configuredPrivateUrl))53 }54 var status by remember { mutableStateOf<String?>(null) }5556 if (!expanded) {57 Row(58 modifier = Modifier59 .fillMaxWidth()60 .clickable { onExpandedChange(true) }61 .padding(vertical = PeachSpacing.sm),62 verticalAlignment = Alignment.CenterVertically,63 ) {64 Text(65 pairingRelaySummary(mode),66 color = peachColors.T2,67 fontFamily = PeachType.Mono,68 fontSize = 10.sp,69 modifier = Modifier.weight(1f),70 )71 Text(72 "change",73 color = LocalPeachAccent.current,74 fontFamily = PeachType.Mono,75 fontSize = 10.sp,76 )77 }78 return79 }8081 Column(Modifier.fillMaxWidth()) {82 Text(83 "Choose the relay this vault uses",84 color = peachColors.T0,85 fontFamily = PeachType.Body,86 fontSize = 13.sp,87 )88 Spacer(Modifier.height(PeachSpacing.xs))89 RelayChoiceRow(90 title = "Public relay",91 detail = "Peach's relay. Nothing to set up.",92 selected = mode == SyncMode.PUBLIC,93 ) {94 status = null95 onSelectMode(SyncMode.PUBLIC)96 }97 RelayChoiceRow(98 title = "Private relay",99 detail = "Your own peach-relay on your domain.",100 selected = mode == SyncMode.PRIVATE,101 ) {102 status = null103 onSelectMode(SyncMode.PRIVATE)104 }105 if (mode == SyncMode.PRIVATE) {106 OutlinedTextField(107 value = draft,108 onValueChange = {109 draft = it110 status = null111 },112 label = { Text("relay URL") },113 placeholder = { Text("wss://relay.example.com") },114 singleLine = true,115 modifier = Modifier.fillMaxWidth(),116 )117 Text(118 status ?: if (configuredPrivateUrl.isBlank()) {119 "Pairing waits until a valid self-hosted relay is saved."120 } else {121 "Only secure wss:// relay URLs are accepted."122 },123 color = if (status?.startsWith("Use") == true || configuredPrivateUrl.isBlank()) {124 peachColors.Breach125 } else {126 peachColors.T2127 },128 fontFamily = PeachType.Mono,129 fontSize = 9.sp,130 modifier = Modifier131 .fillMaxWidth()132 .padding(top = PeachSpacing.xs),133 textAlign = TextAlign.Start,134 )135 Spacer(Modifier.height(PeachSpacing.sm))136 PillButton("Save private relay", LocalPeachAccent.current, filled = true) {137 status = if (onSavePrivateUrl(draft)) "Saved" else "Use a valid wss:// relay URL"138 }139 }140 if (collapsible) {141 Spacer(Modifier.height(PeachSpacing.xs))142 PillButton("Done", peachColors.T2, filled = false) {143 onExpandedChange(false)144 }145 }146 }147}148149@Composable150private fun RelayChoiceRow(151 title: String,152 detail: String,153 selected: Boolean,154 onSelect: () -> Unit,155) {156 Row(157 modifier = Modifier158 .fillMaxWidth()159 .clickable(onClick = onSelect)160 .padding(vertical = PeachSpacing.xs),161 verticalAlignment = Alignment.CenterVertically,162 ) {163 Column(Modifier.weight(1f)) {164 Text(title, color = peachColors.T0, fontFamily = PeachType.Body, fontSize = 12.sp)165 Text(detail, color = peachColors.T2, fontFamily = PeachType.Body, fontSize = 10.sp)166 }167 RadioButton(selected = selected, onClick = onSelect)168 }169}