@@ -355,33 +355,37 @@ fun HomeScreen(
355355 Spacer (Modifier .height(4 .dp))
356356
357357 val appsScriptEnabled = cfg.mode == Mode .APPS_SCRIPT || cfg.mode == Mode .FULL
358- // Wrapped in a collapsible so a long ID list (10+ deployments
359- // is normal in full-tunnel rotations) doesn't dominate the
360- // screen once it's set up. Starts expanded for first-run users
361- // (no IDs/key yet) so the form is immediately discoverable.
362- CollapsibleSection (
363- title = stringResource(R .string.sec_apps_script_relay),
364- initiallyExpanded = appsScriptEnabled &&
365- (cfg.appsScriptUrls.isEmpty() || cfg.authKey.isBlank()),
366- ) {
367- DeploymentIdsField (
368- urls = cfg.appsScriptUrls,
369- onChange = { persist(cfg.copy(appsScriptUrls = it)) },
370- enabled = appsScriptEnabled,
371- )
358+ // Apps Script section only renders for the modes that
359+ // actually use Apps Script. google_only / google_drive have
360+ // no Deployment ID or Auth key concept — showing them
361+ // greyed-out (the previous behavior) just confused
362+ // first-time users. Wrapped in a collapsible so a long ID
363+ // list (10+ deployments is normal in full-tunnel rotations)
364+ // doesn't dominate the screen once set up.
365+ if (appsScriptEnabled) {
366+ CollapsibleSection (
367+ title = stringResource(R .string.sec_apps_script_relay),
368+ initiallyExpanded = cfg.appsScriptUrls.isEmpty() || cfg.authKey.isBlank(),
369+ ) {
370+ DeploymentIdsField (
371+ urls = cfg.appsScriptUrls,
372+ onChange = { persist(cfg.copy(appsScriptUrls = it)) },
373+ enabled = true ,
374+ )
372375
373- OutlinedTextField (
374- value = cfg.authKey,
375- onValueChange = { persist(cfg.copy(authKey = it)) },
376- label = { Text (stringResource(R .string.field_auth_key)) },
377- singleLine = true ,
378- enabled = appsScriptEnabled,
379- keyboardOptions = KeyboardOptions (imeAction = ImeAction .Next ),
380- modifier = Modifier .fillMaxWidth(),
381- supportingText = {
382- Text (stringResource(R .string.help_auth_key))
383- },
384- )
376+ OutlinedTextField (
377+ value = cfg.authKey,
378+ onValueChange = { persist(cfg.copy(authKey = it)) },
379+ label = { Text (stringResource(R .string.field_auth_key)) },
380+ singleLine = true ,
381+ enabled = true ,
382+ keyboardOptions = KeyboardOptions (imeAction = ImeAction .Next ),
383+ modifier = Modifier .fillMaxWidth(),
384+ supportingText = {
385+ Text (stringResource(R .string.help_auth_key))
386+ },
387+ )
388+ }
385389 }
386390
387391 // ── Google Drive section ──────────────────────────────────────
@@ -494,7 +498,11 @@ fun HomeScreen(
494498 )
495499 }
496500
497- // Advanced settings: collapsed by default.
501+ // Advanced settings: collapsed by default. The block
502+ // contains a mix of always-applicable knobs (verify_ssl,
503+ // log_level) and Apps-Script-only knobs (parallel_relay,
504+ // upstream_socks5); the inner composable hides the latter
505+ // when the current mode doesn't use Apps Script.
498506 CollapsibleSection (title = stringResource(R .string.sec_advanced)) {
499507 AdvancedSettings (
500508 cfg = cfg,
@@ -506,12 +514,17 @@ fun HomeScreen(
506514 // Secondary action — FilledTonalButton signals "helper" against
507515 // the primary Connect/Disconnect button at the top. Kept down
508516 // here because cert install is a one-time setup step; daily
509- // users never tap it again.
510- FilledTonalButton (
511- onClick = { showInstallDialog = true },
512- modifier = Modifier .fillMaxWidth(),
513- ) {
514- Text (stringResource(R .string.btn_install_mitm))
517+ // users never tap it again. Only meaningful when MITM is
518+ // active: apps_script does the TLS interception, full owns
519+ // a tunnel-node + cert. google_only and google_drive do
520+ // not MITM so hiding the button keeps the flow honest.
521+ if (cfg.mode == Mode .APPS_SCRIPT || cfg.mode == Mode .FULL ) {
522+ FilledTonalButton (
523+ onClick = { showInstallDialog = true },
524+ modifier = Modifier .fillMaxWidth(),
525+ ) {
526+ Text (stringResource(R .string.btn_install_mitm))
527+ }
515528 }
516529
517530 // "Usage today (estimated)" — visible only while a proxy is
@@ -532,12 +545,18 @@ fun HomeScreen(
532545 // Wrapped in a collapsible so the big prose block doesn't
533546 // dominate the form after the user has learned the flow.
534547 // Starts expanded once for a fresh install so the first-run
535- // instructions are immediately visible.
536- CollapsibleSection (
537- title = stringResource(R .string.sec_how_to_use),
538- initiallyExpanded = cfg.appsScriptUrls.isEmpty() || cfg.authKey.isBlank(),
539- ) {
540- HowToUseBody (cfg.listenPort)
548+ // instructions are immediately visible. The body is
549+ // Apps-Script-flavoured (Deployment IDs, MITM cert, Code.gs)
550+ // so it's only relevant in apps_script / full — Drive and
551+ // google_only have their own onboarding inside their
552+ // respective sections.
553+ if (cfg.mode == Mode .APPS_SCRIPT || cfg.mode == Mode .FULL ) {
554+ CollapsibleSection (
555+ title = stringResource(R .string.sec_how_to_use),
556+ initiallyExpanded = cfg.appsScriptUrls.isEmpty() || cfg.authKey.isBlank(),
557+ ) {
558+ HowToUseBody (cfg.listenPort)
559+ }
541560 }
542561 }
543562 }
@@ -1896,6 +1915,11 @@ private fun AdvancedSettings(
18961915 cfg : MhrvConfig ,
18971916 onChange : (MhrvConfig ) -> Unit ,
18981917) {
1918+ // parallel_relay and upstream_socks5 only have an effect on the
1919+ // Apps Script relay path; they're no-ops in google_only and
1920+ // google_drive. Hide them in those modes so users don't think
1921+ // they're tunable knobs that just don't take effect.
1922+ val appsScriptRelevant = cfg.mode == Mode .APPS_SCRIPT || cfg.mode == Mode .FULL
18991923 Column (verticalArrangement = Arrangement .spacedBy(10 .dp)) {
19001924 // verify_ssl
19011925 Row (
@@ -1947,36 +1971,38 @@ private fun AdvancedSettings(
19471971 }
19481972 }
19491973
1950- // parallel_relay slider
1951- Column {
1952- Text (
1953- stringResource(R .string.adv_parallel_relay, cfg.parallelRelay),
1954- style = MaterialTheme .typography.bodyMedium,
1955- )
1956- Slider (
1957- value = cfg.parallelRelay.toFloat(),
1958- onValueChange = { onChange(cfg.copy(parallelRelay = it.toInt().coerceIn(1 , 5 ))) },
1959- valueRange = 1f .. 5f ,
1960- steps = 3 , // yields 1,2,3,4,5 positions
1961- )
1962- Text (
1963- stringResource(R .string.adv_parallel_relay_help),
1964- style = MaterialTheme .typography.labelSmall,
1965- color = MaterialTheme .colorScheme.onSurfaceVariant,
1974+ if (appsScriptRelevant) {
1975+ // parallel_relay slider
1976+ Column {
1977+ Text (
1978+ stringResource(R .string.adv_parallel_relay, cfg.parallelRelay),
1979+ style = MaterialTheme .typography.bodyMedium,
1980+ )
1981+ Slider (
1982+ value = cfg.parallelRelay.toFloat(),
1983+ onValueChange = { onChange(cfg.copy(parallelRelay = it.toInt().coerceIn(1 , 5 ))) },
1984+ valueRange = 1f .. 5f ,
1985+ steps = 3 , // yields 1,2,3,4,5 positions
1986+ )
1987+ Text (
1988+ stringResource(R .string.adv_parallel_relay_help),
1989+ style = MaterialTheme .typography.labelSmall,
1990+ color = MaterialTheme .colorScheme.onSurfaceVariant,
1991+ )
1992+ }
1993+
1994+ OutlinedTextField (
1995+ value = cfg.upstreamSocks5,
1996+ onValueChange = { onChange(cfg.copy(upstreamSocks5 = it)) },
1997+ label = { Text (stringResource(R .string.adv_upstream_socks5)) },
1998+ placeholder = { Text (" host:port" ) },
1999+ singleLine = true ,
2000+ modifier = Modifier .fillMaxWidth(),
2001+ supportingText = {
2002+ Text (stringResource(R .string.adv_upstream_socks5_help))
2003+ },
19662004 )
19672005 }
1968-
1969- OutlinedTextField (
1970- value = cfg.upstreamSocks5,
1971- onValueChange = { onChange(cfg.copy(upstreamSocks5 = it)) },
1972- label = { Text (stringResource(R .string.adv_upstream_socks5)) },
1973- placeholder = { Text (" host:port" ) },
1974- singleLine = true ,
1975- modifier = Modifier .fillMaxWidth(),
1976- supportingText = {
1977- Text (stringResource(R .string.adv_upstream_socks5_help))
1978- },
1979- )
19802006 }
19812007}
19822008
0 commit comments