Skip to content

fix(tweaks): proxy host validation + save-needs-two-taps#576

Merged
rainxchzed merged 3 commits into
mainfrom
fix/proxy-host-validation
May 11, 2026
Merged

fix(tweaks): proxy host validation + save-needs-two-taps#576
rainxchzed merged 3 commits into
mainfrom
fix/proxy-host-validation

Conversation

@rainxchzed
Copy link
Copy Markdown
Member

@rainxchzed rainxchzed commented May 11, 2026

Two bugs in the Tweaks → Network → Proxy form found in device-side bug-bash (#534 follow-up):

  1. Host accepted any non-blank text (not a url, garbage with spaces, etc.). Only port had real-time validation. Added a hostname / IPv4 / IPv6 validator (isValidProxyHost in VM, mirrored as isLikelyValidProxyHost in Network.kt for the real-time isError highlight). Save + Test paths now reject malformed hosts with a clear error (proxy_host_invalid string across 13 locales).

  2. Save required two taps — first tap lost IME focus from the host/port field, second tap actually fired the click. Wrapped both Save and Test onClick to call LocalFocusManager.clearFocus() first so the IME commits pending input synchronously.

Bonus: isFormValid now incorporates the host validity gate so the Save button stays disabled until the host parses cleanly — matches the port-side UX.

Summary by CodeRabbit

  • New Features

    • Enhanced proxy host validation with real-time feedback and specific error messages distinguishing between missing and invalid inputs.
  • Improvements

    • Refined proxy settings user experience with clearer validation error feedback.
    • Improved platform compatibility (Android UI adjustments).
  • Localization

    • Added translated proxy validation error messages across 14 languages (Arabic, Bengali, Spanish, French, Hindi, Italian, Japanese, Korean, Polish, Russian, Turkish, and Chinese).

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 11, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 29555071-b223-4589-b63a-376a1d119c67

📥 Commits

Reviewing files that changed from the base of the PR and between a589ba8 and 87ab60f.

📒 Files selected for processing (18)
  • core/presentation/src/commonMain/composeResources/values-ar/strings-ar.xml
  • core/presentation/src/commonMain/composeResources/values-bn/strings-bn.xml
  • core/presentation/src/commonMain/composeResources/values-es/strings-es.xml
  • core/presentation/src/commonMain/composeResources/values-fr/strings-fr.xml
  • core/presentation/src/commonMain/composeResources/values-hi/strings-hi.xml
  • core/presentation/src/commonMain/composeResources/values-it/strings-it.xml
  • core/presentation/src/commonMain/composeResources/values-ja/strings-ja.xml
  • core/presentation/src/commonMain/composeResources/values-ko/strings-ko.xml
  • core/presentation/src/commonMain/composeResources/values-pl/strings-pl.xml
  • core/presentation/src/commonMain/composeResources/values-ru/strings-ru.xml
  • core/presentation/src/commonMain/composeResources/values-tr/strings-tr.xml
  • core/presentation/src/commonMain/composeResources/values-zh-rCN/strings-zh-rCN.xml
  • core/presentation/src/commonMain/composeResources/values/strings.xml
  • feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/Options.kt
  • feature/tweaks/presentation/src/commonMain/kotlin/zed/rainxch/tweaks/presentation/TweaksRoot.kt
  • feature/tweaks/presentation/src/commonMain/kotlin/zed/rainxch/tweaks/presentation/TweaksViewModel.kt
  • feature/tweaks/presentation/src/commonMain/kotlin/zed/rainxch/tweaks/presentation/components/sections/Appearance.kt
  • feature/tweaks/presentation/src/commonMain/kotlin/zed/rainxch/tweaks/presentation/components/sections/Network.kt

Walkthrough

This PR adds RFC-compliant proxy host validation using IPv4/IPv6/hostname patterns across ViewModel and UI layers, with real-time error feedback and 13 localized messages. Additionally, it includes unrelated UI refinements: platform-conditional scrollbar visibility, snackbar dismissal actions, and refactored profile options rendering.

Changes

Proxy Host Validation

Layer / File(s) Summary
Host Validation Patterns & Logic
feature/tweaks/presentation/src/commonMain/kotlin/zed/rainxch/tweaks/presentation/TweaksViewModel.kt
Adds regex constants for IPv4 (0–255 octets), IPv6 (hex/colon, optionally bracketed), and RFC 1123 hostnames. Implements isValidProxyHost(raw) that rejects blank/whitespace/URL components while accepting valid formats.
ViewModel Save & Test Handlers
feature/tweaks/presentation/src/commonMain/kotlin/zed/rainxch/tweaks/presentation/TweaksViewModel.kt
OnProxySave and buildProxyConfigForTest apply isValidProxyHost checks, emitting "proxy_host_required" when blank or "proxy_host_invalid" when non-blank but invalid, aborting on failure.
UI Input Validation & Error Display
feature/tweaks/presentation/src/commonMain/kotlin/zed/rainxch/tweaks/presentation/components/sections/Network.kt
Adds LocalFocusManager import. Computes isHostInvalid via isLikelyValidProxyHost helper regex checks. Updates form validity to require non-empty, non-invalid host. Renders TextField isError state and supportingText conditionally. Clears focus before dispatching Test/Save actions to commit pending IME input.
Localized Error Messages
core/presentation/src/commonMain/composeResources/values*/strings-*.xml
Adds proxy_host_invalid string resource across 13 locales (Arabic, Bengali, Spanish, French, Hindi, Italian, Japanese, Korean, Polish, Russian, Turkish, Simplified Chinese, English). Italian also fixes apostrophe escaping in proxy_host_required.

Settings UI Refinements

Layer / File(s) Summary
Appearance Settings Platform Behavior
feature/tweaks/presentation/src/commonMain/kotlin/zed/rainxch/tweaks/presentation/components/sections/Appearance.kt
Wraps scrollbar ToggleSettingCard in platform check (getPlatform() != Platform.ANDROID) so it renders only on non-Android platforms. Adds getPlatform and Platform imports.
Language Restart Snackbar
feature/tweaks/presentation/src/commonMain/kotlin/zed/rainxch/tweaks/presentation/TweaksRoot.kt
Updates OnAppLanguageChangeRequiresRestart event handling to invoke restart snackbar with withDismissAction = true. Removes explanatory comment block while preserving SnackbarResult.ActionPerformedrestartAppAfterLanguageChange() flow.
Profile Options Conditional Rendering
feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/Options.kt
Stars option now conditionally renders only when isUserLoggedIn is true instead of always rendering with enabled = false. Removes enabled parameter from OptionCard composable signature. OptionCardContent receives hasBadge only in onLongClick != null paths; defaults to false otherwise.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 A rabbit hops through proxy hosts so fine,
IPv4, IPv6, hostnames align,
Validation checks on each input line,
Thirteen tongues now in perfect design,
Plus scrollbars hidden where Androids shine!

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/proxy-host-validation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@rainxchzed rainxchzed merged commit 3ffa72f into main May 11, 2026
1 check was pending
@rainxchzed rainxchzed deleted the fix/proxy-host-validation branch May 11, 2026 14:17
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 11, 2026

Greptile Summary

This PR fixes two bugs in the Tweaks → Network → Proxy form: (1) host accepted any non-blank text — now validated against hostname/IPv4/IPv6 patterns with clear error messages across 13 locales; (2) Save required two taps on Android — fixed by calling clearFocus() before dispatching the action. Also includes drive-by fixes for the scrollbar toggle (Android-only) and the Stars card conditional rendering in the Profile screen.

Confidence Score: 3/5

Not safe to merge as-is — the IPv6 regex lets invalid hosts like bare : pass validation and be persisted, silently breaking proxy connectivity.

One P1 defect in both TweaksViewModel.kt and Network.kt: IPV6_PATTERN accepts degenerate inputs, allowing an invalid proxy config to be saved. The two-tap and host-rejection fixes are otherwise sound.

TweaksViewModel.kt and Network.kt — both define IPV6_PATTERN and need the regex tightened before merge.

Important Files Changed

Filename Overview
feature/tweaks/presentation/src/commonMain/kotlin/zed/rainxch/tweaks/presentation/TweaksViewModel.kt Adds isValidProxyHost with correct IPv4/hostname validators, but IPV6_PATTERN accepts degenerate inputs like bare : allowing an invalid proxy config to be saved.
feature/tweaks/presentation/src/commonMain/kotlin/zed/rainxch/tweaks/presentation/components/sections/Network.kt Adds real-time host validation and clearFocus on Save/Test; carries the same IPv6 regex flaw as the ViewModel and fully duplicates the validation logic.
feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/Options.kt Correctly replaces disabled-card pattern with conditional rendering, but a 4 dp Spacer was left outside the if-block and renders a phantom gap when logged out.
feature/tweaks/presentation/src/commonMain/kotlin/zed/rainxch/tweaks/presentation/components/sections/Appearance.kt Hides scrollbar toggle on Android via platform check; safe conditional rendering change.
core/presentation/src/commonMain/composeResources/values/strings.xml Adds proxy_host_invalid string key in correct position alongside existing proxy error strings.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[User types in Host field] --> B{hostValue.isNotEmpty?}
    B -- No --> C[isHostInvalid = false\nSave disabled]
    B -- Yes --> D{isLikelyValidProxyHost?}
    D -- No --> E[isHostInvalid = true\nShow inline error\nSave disabled]
    D -- Yes --> F[isHostInvalid = false]
    F --> G{Port valid 1..65535?}
    G -- No --> H[Save disabled]
    G -- Yes --> I[Save/Test enabled]
    I --> J[User taps Save/Test]
    J --> K[clearFocus — IME commits input]
    K --> L[TweaksAction dispatched to VM]
    L --> M{isValidProxyHost in VM?}
    M -- No --> N[OnProxySaveError / OnProxyTestError snackbar]
    M -- Yes --> O[ProxyConfig persisted / tested]
    style E fill:#f88,color:#000
    style N fill:#f88,color:#000
    style O fill:#8f8,color:#000
Loading

Reviews (1): Last reviewed commit: "ui: hide stars when logged out, scrollba..." | Re-trigger Greptile

// optional `::` shortcut). Permissive on canonical form because
// proxy clients normalize it; we only need to reject "looks
// wrong" inputs like `not a url`.
private val IPV6_PATTERN = Regex("^[0-9A-Fa-f:]+$")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 IPv6 regex accepts degenerate strings like bare :

IPV6_PATTERN = Regex("^[0-9A-Fa-f:]+$") matches any string consisting only of hex characters and colons — including : alone, ::::, abc:, :def, or a:b:c:d:e:f:0:1:2:3 (9 groups). Because ":" satisfies both contains(":") and IPV6_PATTERN.matches(":"), a user entering : as their proxy host passes both the UI and VM validators, the Save button enables, and an invalid config is persisted — silently breaking all proxy-scoped network traffic until manually corrected.

The same pattern is duplicated in Network.kt, so both paths are affected. A minimal fix:

private val IPV6_PATTERN = Regex(
    "^(?:[0-9A-Fa-f]{0,4}:){2,7}[0-9A-Fa-f]{0,4}$"
)

This requires at least two colon-separated groups (covering :: compressed form) while staying permissive about canonical form as the comment intends.

Comment on lines +421 to +422

/**
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Duplicate validation logic between UI and ViewModel

isLikelyValidProxyHost and its three companion regex patterns (IPV4_PATTERN, IPV6_PATTERN, HOSTNAME_PATTERN) are a verbatim copy of the logic already in TweaksViewModel. If either side is updated (e.g., the IPv6 fix above) both files must be kept in sync manually. Consider extracting the validator to a shared module (e.g., core/domain) or at minimum referencing the same object-level constants.

Comment on lines +59 to 61
}

Spacer(Modifier.height(4.dp))
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Spacer always rendered even when the card is hidden

Spacer(Modifier.height(4.dp)) sits outside the if (isUserLoggedIn) block, so it renders an invisible 4 dp gap at the top of the options list whenever the user is logged out. Move the spacer inside the block to restore the original conditional layout.

Suggested change
}
Spacer(Modifier.height(4.dp))
if (isUserLoggedIn) {
OptionCard(
icon = Icons.Default.Star,
label = stringResource(Res.string.stars),
description = stringResource(Res.string.profile_stars_description),
onClick = {
onAction(ProfileAction.OnStarredReposClick)
},
)
Spacer(Modifier.height(4.dp))
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant