PairingRelayChoice.kt

5.8 KB · raw

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}3536/**37 * Pairing can use the public relay or a self-hosted peach-relay. No-sync is38 * deliberately absent because this control exists only while creating a39 * single-use pairing invitation.40 */41@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}