PairingRelayChoice.kt

5.7 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.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}3435/**36 * Pairing can use the public relay or a self-hosted peach-relay. No-sync is37 * deliberately absent because this control exists only while creating a38 * single-use pairing invitation.39 */40@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}