Skip to content

Commit 74f58f0

Browse files
myleshortonclaude
andcommitted
smc: address Copilot review on #8820
share_my_connection.dart: - autoStart unconditionally starts ShareMode.unbounded, dropping the manual-port and UPnP-probe branches. The auto path is the low-friction Unbounded-only surface; SmC requires the explicit disclosure dialog enforced by toggle(). Without this fix, a user who'd configured a manual port (or got lucky on the mock probe) would silently land in SmC mode without ever seeing the disclosure, turning their device into a residential exit they never agreed to. - _fallbackToUnbounded: added a comment documenting the intentional reuse of the prior _start's event subscription. The invariant works because the error path stays inside the same subscription; flipping state.mode keeps the same forwarder pushing events for Unbounded. _stop is the only teardown path. - Welcome dialog 'restrictors' → 'restrictions' typo; normalized the surrounding paragraph to single-quote-delimited strings (the inner curly quote in 'digital bridges' is retained — it's intentional inside the prose). - About-bubble tooltip moved through .i18n. unbounded_setting.dart + setting.dart: - New i18n keys added to en.po: unbounded_settings_title auto_enable_unbounded / auto_enable_unbounded_subtitle hide_unbounded / hide_unbounded_subtitle about_unbounded All hardcoded English strings in the new screens replaced with .i18n lookups, matching the existing pattern in vpn_setting.dart. dart analyze clean on the touched files. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent caba92d commit 74f58f0

4 files changed

Lines changed: 51 additions & 31 deletions

File tree

assets/locales/en.po

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -557,15 +557,16 @@ msgstr "Share My Connection"
557557
msgid "share_my_connection_subtitle"
558558
msgstr "Let other Lantern users route through your connection to bypass censorship."
559559

560-
msgid "share_my_connection_on_tap_to_view"
561-
msgstr "On — tap to view"
562-
563560
# Unbounded tab — hero copy. Short variant for the tab embed; the
564561
# longer privacy explanation now lives in the welcome dialog
565562
# (showUnboundedWelcomeDialog) so the tab body stays scannable.
566563
msgid "smc_intro"
567564
msgstr "Help others bypass censorship by securely sharing your connection."
568565

566+
# Info-bubble tooltip on the Unbounded tab header
567+
msgid "about_unbounded"
568+
msgstr "About Unbounded"
569+
569570
# Status card — phase labels
570571
msgid "smc_status_label"
571572
msgstr "Status"
@@ -680,6 +681,24 @@ msgstr "Basic mode (Unbounded)"
680681
msgid "smc_disclosure_full"
681682
msgstr "Full mode (SmC)"
682683

684+
# Unbounded Settings menu entry (Settings → Unbounded Settings)
685+
msgid "unbounded_settings_title"
686+
msgstr "Unbounded Settings"
687+
688+
# Auto-enable Unbounded toggle
689+
msgid "auto_enable_unbounded"
690+
msgstr "Auto-enable Unbounded"
691+
692+
msgid "auto_enable_unbounded_subtitle"
693+
msgstr "Turn on automatically when Lantern is open"
694+
695+
# Hide Unbounded toggle (collapses the Unbounded tab on the Home shell)
696+
msgid "hide_unbounded"
697+
msgstr "Hide Unbounded"
698+
699+
msgid "hide_unbounded_subtitle"
700+
msgstr "Removes Unbounded from the top of this screen"
701+
683702
msgid "vpn_connected"
684703
msgstr "Lantern is now connected."
685704

lib/features/setting/setting.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ class _SettingState extends ConsumerState<Setting>
154154
if (unboundedAvailable) ...[
155155
DividerSpace(),
156156
AppTile(
157-
label: 'Unbounded Settings',
157+
label: 'unbounded_settings_title'.i18n,
158158
icon: AppImagePaths.share,
159159
onPressed: () =>
160160
settingMenuTap(_SettingType.unboundedSetting),

lib/features/setting/unbounded_setting.dart

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class UnboundedSetting extends ConsumerWidget {
3030
final textTheme = Theme.of(context).textTheme;
3131

3232
return BaseScreen(
33-
title: 'Unbounded Settings',
33+
title: 'unbounded_settings_title'.i18n,
3434
body: ListView(
3535
children: [
3636
const SizedBox(height: 8),
@@ -39,9 +39,9 @@ class UnboundedSetting extends ConsumerWidget {
3939
child: Column(
4040
children: [
4141
AppTile(
42-
label: 'Auto-enable Unbounded',
42+
label: 'auto_enable_unbounded'.i18n,
4343
subtitle: Text(
44-
'Turn on automatically when Lantern is open',
44+
'auto_enable_unbounded_subtitle'.i18n,
4545
style: textTheme.labelMedium!.copyWith(
4646
color: context.textTertiary,
4747
letterSpacing: 0.0,
@@ -57,9 +57,9 @@ class UnboundedSetting extends ConsumerWidget {
5757
),
5858
DividerSpace(),
5959
AppTile(
60-
label: 'Hide Unbounded',
60+
label: 'hide_unbounded'.i18n,
6161
subtitle: Text(
62-
'Removes Unbounded from the top of this screen',
62+
'hide_unbounded_subtitle'.i18n,
6363
style: textTheme.labelMedium!.copyWith(
6464
color: context.textTertiary,
6565
letterSpacing: 0.0,

lib/features/share_my_connection/share_my_connection.dart

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -305,27 +305,21 @@ class ShareNotifier extends Notifier<ShareState> {
305305
}
306306

307307
/// Programmatic entry point used by the Home shell's auto-enable
308-
/// listener (VPN-connected → Unbounded on). Mirrors `toggle()` but
309-
/// skips the disclosure dialog because the user has already opted
310-
/// in via the Unbounded Settings sheet. No-ops if already active or
311-
/// in flight.
308+
/// listener (VPN-connected → Unbounded on) and by the
309+
/// "Auto-enable Unbounded" Settings toggle.
310+
///
311+
/// Always starts Unbounded mode regardless of UPnP capability or
312+
/// manual-port configuration. SmC requires the explicit-disclosure
313+
/// dialog enforced by toggle(); auto-enabling SmC would silently
314+
/// turn the user's device into a residential exit they never
315+
/// agreed to act as. The auto path is the low-friction Unbounded
316+
/// surface only.
317+
///
318+
/// No-ops if already active or in flight.
312319
Future<void> autoStart(WidgetRef widgetRef) async {
313320
if (state.active || state.probing) return;
314321
state = state.copyWith(probing: true);
315-
final manualPortRes =
316-
await widgetRef.read(lanternServiceProvider).getPeerManualPort();
317-
final manualPort = manualPortRes.fold((_) => 0, (p) => p);
318-
if (manualPort > 0) {
319-
await _start(widgetRef, ShareMode.smc);
320-
return;
321-
}
322-
// MOCK UPnP probe — same as toggle(), pending real FFI.
323-
await Future.delayed(const Duration(milliseconds: 1500));
324-
final upnpAvailable = Random().nextBool();
325-
await _start(
326-
widgetRef,
327-
upnpAvailable ? ShareMode.smc : ShareMode.unbounded,
328-
);
322+
await _start(widgetRef, ShareMode.unbounded);
329323
}
330324

331325
Future<void> _start(WidgetRef widgetRef, ShareMode mode) async {
@@ -670,6 +664,13 @@ class ShareNotifier extends Notifier<ShareState> {
670664
// Constructs ShareState directly (rather than copyWith) so errorMessage
671665
// gets cleared — copyWith's `?? this.errorMessage` keeps the previous
672666
// SmC failure string around otherwise.
667+
//
668+
// Event subscription: deliberately does NOT call _startEventSubscription.
669+
// The error path arrives here via _handlePeerStatus which is already
670+
// inside the subscription started by the prior _start; flipping the
671+
// local state.mode keeps the same subscription forwarding events for
672+
// the new (Unbounded) mode. _stop is the only teardown path for the
673+
// subscription, and the error path doesn't go through _stop.
673674
Future<void> _fallbackToUnbounded(WidgetRef widgetRef) async {
674675
state = ShareState(
675676
active: true,
@@ -1806,11 +1807,11 @@ class _UnboundedWelcomeDialog extends StatelessWidget {
18061807
),
18071808
const SizedBox(height: 16),
18081809
Text(
1809-
"When you enable Unbounded, your device becomes part of a "
1810+
'When you enable Unbounded, your device becomes part of a '
18101811
"network of 'digital bridges' to the open internet. "
1811-
"Censored users connect to these bridges, allowing them "
1812-
"to bypass government-imposed restrictors and access the "
1813-
"information they need.",
1812+
'Censored users connect to these bridges, allowing them '
1813+
'to bypass government-imposed restrictions and access the '
1814+
'information they need.',
18141815
style: textTheme.bodyMedium,
18151816
),
18161817
const SizedBox(height: 12),

0 commit comments

Comments
 (0)