diff --git a/apps/weblibre/lib/core/branding/proxy_brands.dart b/apps/weblibre/lib/core/branding/proxy_brands.dart new file mode 100644 index 00000000..6b7a8bf2 --- /dev/null +++ b/apps/weblibre/lib/core/branding/proxy_brands.dart @@ -0,0 +1,7 @@ +const String torBrand = 'Tor™'; +const String torProxyLabel = '$torBrand Proxy'; +const String torServiceLabel = '$torBrand Service'; +const String torNetworkLabel = '$torBrand network'; + +const String wireGuardBrand = 'WireGuard®'; +const String wireGuardConfigLabel = '$wireGuardBrand config'; diff --git a/apps/weblibre/lib/features/geckoview/features/browser/presentation/widgets/browser_menu_sheet.dart b/apps/weblibre/lib/features/geckoview/features/browser/presentation/widgets/browser_menu_sheet.dart index bf1edaed..462fcea4 100644 --- a/apps/weblibre/lib/features/geckoview/features/browser/presentation/widgets/browser_menu_sheet.dart +++ b/apps/weblibre/lib/features/geckoview/features/browser/presentation/widgets/browser_menu_sheet.dart @@ -35,6 +35,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:nullability/nullability.dart'; import 'package:share_plus/share_plus.dart'; import 'package:skeletonizer/skeletonizer.dart'; +import 'package:weblibre/core/branding/proxy_brands.dart'; import 'package:weblibre/core/design/app_colors.dart'; import 'package:weblibre/core/logger.dart'; import 'package:weblibre/core/providers/persisted_bool.dart'; @@ -2043,7 +2044,7 @@ class _ConnectionCard extends ConsumerWidget { final rows = [ _ConnectionRow( icon: TorIcons.onionAlt, - label: 'Tor™ Proxy', + label: torProxyLabel, active: isTorActive, enabled: !isTorBusy, onToggle: () async { diff --git a/apps/weblibre/lib/features/proxy/data/models/wireguard_config_import.dart b/apps/weblibre/lib/features/proxy/data/models/wireguard_config_import.dart index 60c6d735..a0021eb5 100644 --- a/apps/weblibre/lib/features/proxy/data/models/wireguard_config_import.dart +++ b/apps/weblibre/lib/features/proxy/data/models/wireguard_config_import.dart @@ -20,6 +20,7 @@ import 'dart:convert'; import 'package:fast_equatable/fast_equatable.dart'; +import 'package:weblibre/core/branding/proxy_brands.dart'; import 'package:weblibre/features/proxy/data/parsers/host_port.dart'; class WireguardConfigImport with FastEquatable { @@ -42,7 +43,7 @@ class WireguardConfigImport with FastEquatable { final peer = sections['peer']; if (interface == null || peer == null) { throw const FormatException( - 'WireGuard config must contain [Interface] and [Peer] sections.', + '$wireGuardBrand config must contain [Interface] and [Peer] sections.', ); } @@ -51,7 +52,7 @@ class WireguardConfigImport with FastEquatable { ? (host: '', port: '') : parseHostPort( rawEndpoint, - invalidMessage: 'WireGuard endpoint must be host:port.', + invalidMessage: '$wireGuardBrand endpoint must be host:port.', ); final mtu = interface['mtu']?.trim(); @@ -103,7 +104,9 @@ Map> _parseWireguardConfig(String configText) { if (line.startsWith('[') && line.endsWith(']')) { currentSection = line.substring(1, line.length - 1).trim().toLowerCase(); if (currentSection.isEmpty) { - throw const FormatException('WireGuard config contains empty section.'); + throw const FormatException( + '$wireGuardBrand config contains empty section.', + ); } sections.putIfAbsent(currentSection, () => {}); continue; @@ -111,19 +114,19 @@ Map> _parseWireguardConfig(String configText) { if (currentSection == null) { throw const FormatException( - 'WireGuard config entries must be inside a section.', + '$wireGuardBrand config entries must be inside a section.', ); } final separatorIndex = line.indexOf('='); if (separatorIndex < 1) { - throw FormatException('Invalid WireGuard config line: $rawLine'); + throw FormatException('Invalid $wireGuardBrand config line: $rawLine'); } final key = line.substring(0, separatorIndex).trim().toLowerCase(); final value = line.substring(separatorIndex + 1).trim(); if (key.isEmpty) { - throw FormatException('Invalid WireGuard config line: $rawLine'); + throw FormatException('Invalid $wireGuardBrand config line: $rawLine'); } sections[currentSection]![key] = value; } diff --git a/apps/weblibre/lib/features/proxy/domain/extensions/singbox_proxy_profile_type_x.dart b/apps/weblibre/lib/features/proxy/domain/extensions/singbox_proxy_profile_type_x.dart index 4e13d1e6..8fc7f65c 100644 --- a/apps/weblibre/lib/features/proxy/domain/extensions/singbox_proxy_profile_type_x.dart +++ b/apps/weblibre/lib/features/proxy/domain/extensions/singbox_proxy_profile_type_x.dart @@ -19,6 +19,8 @@ */ import 'package:flutter_singbox_proxy/flutter_singbox_proxy.dart'; +import 'package:weblibre/core/branding/proxy_brands.dart'; + extension SingboxProxyProfileTypeExt on SingboxProxyProfileType { /// Long human label for menus, dialogs, subtitles. String get label => switch (this) { @@ -33,7 +35,7 @@ extension SingboxProxyProfileTypeExt on SingboxProxyProfileType { SingboxProxyProfileType.hysteria2 => 'Hysteria2', SingboxProxyProfileType.tuic => 'TUIC', SingboxProxyProfileType.ssh => 'SSH', - SingboxProxyProfileType.wireguard => 'WireGuard', + SingboxProxyProfileType.wireguard => wireGuardBrand, SingboxProxyProfileType.shadowTls => 'ShadowTLS', SingboxProxyProfileType.anyTls => 'AnyTLS', SingboxProxyProfileType.customOutbound => 'Custom Outbound', diff --git a/apps/weblibre/lib/features/proxy/domain/providers/proxy_connection_options.dart b/apps/weblibre/lib/features/proxy/domain/providers/proxy_connection_options.dart index 12762b82..465f7543 100644 --- a/apps/weblibre/lib/features/proxy/domain/providers/proxy_connection_options.dart +++ b/apps/weblibre/lib/features/proxy/domain/providers/proxy_connection_options.dart @@ -19,6 +19,7 @@ */ import 'package:fast_equatable/fast_equatable.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:weblibre/core/branding/proxy_brands.dart'; import 'package:weblibre/core/logger.dart'; import 'package:weblibre/features/proxy/data/models/singbox_proxy_profile.dart'; import 'package:weblibre/features/proxy/data/proxy_connection.dart'; @@ -61,8 +62,8 @@ List proxyConnectionOptions(Ref ref) { return [ ProxyConnectionOption( id: const TorProxyConnectionId(), - title: 'Tor', - subtitle: 'Route through the Tor network', + title: torBrand, + subtitle: 'Route through the $torNetworkLabel', ), for (final profile in singboxProfiles) ProxyConnectionOption( diff --git a/apps/weblibre/lib/features/proxy/domain/repositories/container_proxy.dart b/apps/weblibre/lib/features/proxy/domain/repositories/container_proxy.dart index fa74c659..2a66a1e0 100644 --- a/apps/weblibre/lib/features/proxy/domain/repositories/container_proxy.dart +++ b/apps/weblibre/lib/features/proxy/domain/repositories/container_proxy.dart @@ -22,6 +22,7 @@ import 'dart:async'; import 'package:flutter_mozilla_components/flutter_mozilla_components.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:synchronized/synchronized.dart'; +import 'package:weblibre/core/branding/proxy_brands.dart'; import 'package:weblibre/features/geckoview/features/tabs/data/models/site_assignment.dart'; import 'package:weblibre/features/proxy/data/proxy_connection.dart'; @@ -57,7 +58,7 @@ class ContainerProxyRepository extends _$ContainerProxyRepository { return _service.upsertProxy( GeckoProxySettings( id: const TorProxyConnectionId().encode(), - title: 'Tor', + title: torBrand, type: 'socks', host: '127.0.0.1', port: port, diff --git a/apps/weblibre/lib/features/proxy/domain/services/proxy_input_consumer.dart b/apps/weblibre/lib/features/proxy/domain/services/proxy_input_consumer.dart index 5290a4ad..4cd79068 100644 --- a/apps/weblibre/lib/features/proxy/domain/services/proxy_input_consumer.dart +++ b/apps/weblibre/lib/features/proxy/domain/services/proxy_input_consumer.dart @@ -22,6 +22,7 @@ import 'dart:convert'; import 'package:file_picker/file_picker.dart'; import 'package:flutter_singbox_proxy/flutter_singbox_proxy.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:weblibre/core/branding/proxy_brands.dart'; import 'package:weblibre/core/logger.dart'; import 'package:weblibre/features/proxy/data/forms/singbox_form_specs.dart'; import 'package:weblibre/features/proxy/data/models/proxy_profile_seed.dart'; @@ -70,8 +71,8 @@ class ProxyInputConsumer extends _$ProxyInputConsumer { if (_looksLikeWireguardConf(trimmed)) { return _seedOutcome( - () => _seedFromWireguardConf(trimmed, fileName: 'WireGuard'), - logMessage: 'Failed to parse WireGuard configuration', + () => _seedFromWireguardConf(trimmed, fileName: wireGuardBrand), + logMessage: 'Failed to parse $wireGuardBrand configuration', ); } diff --git a/apps/weblibre/lib/features/proxy/domain/services/proxy_latency_tester.dart b/apps/weblibre/lib/features/proxy/domain/services/proxy_latency_tester.dart index 1ffd676a..adcef052 100644 --- a/apps/weblibre/lib/features/proxy/domain/services/proxy_latency_tester.dart +++ b/apps/weblibre/lib/features/proxy/domain/services/proxy_latency_tester.dart @@ -26,6 +26,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter_singbox_proxy/flutter_singbox_proxy.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:socks5_proxy/socks_client.dart'; +import 'package:weblibre/core/branding/proxy_brands.dart'; import 'package:weblibre/features/proxy/data/proxy_connection.dart'; import 'package:weblibre/features/proxy/domain/repositories/singbox_proxy_runtime.dart'; import 'package:weblibre/features/tor/domain/extensions/tor_status_x.dart'; @@ -118,7 +119,7 @@ class ProxyLatencyResults extends _$ProxyLatencyResults { if (socksPort == null) { _set( const TorProxyConnectionId(), - AsyncError('Tor is not ready', StackTrace.current), + AsyncError('$torBrand is not ready', StackTrace.current), ); return; } diff --git a/apps/weblibre/lib/features/proxy/presentation/screens/singbox_proxy_logs.dart b/apps/weblibre/lib/features/proxy/presentation/screens/singbox_proxy_logs.dart index 9abbd3c3..dfce4e7c 100644 --- a/apps/weblibre/lib/features/proxy/presentation/screens/singbox_proxy_logs.dart +++ b/apps/weblibre/lib/features/proxy/presentation/screens/singbox_proxy_logs.dart @@ -23,6 +23,7 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:intl/intl.dart'; import 'package:share_plus/share_plus.dart'; +import 'package:weblibre/core/branding/proxy_brands.dart'; import 'package:weblibre/features/proxy/data/models/proxy_log_message.dart'; import 'package:weblibre/features/proxy/domain/repositories/singbox_proxy_logs.dart'; import 'package:weblibre/utils/ui_helper.dart'; @@ -202,7 +203,7 @@ class _EmptyLogs extends StatelessWidget { child: Text( hasFilter ? 'No log lines match the current filter.' - : 'No log lines yet. Start a proxy or Tor to see output here.', + : 'No log lines yet. Start a proxy or $torBrand to see output here.', textAlign: TextAlign.center, ), ), diff --git a/apps/weblibre/lib/features/proxy/presentation/screens/singbox_proxy_profile_editor.dart b/apps/weblibre/lib/features/proxy/presentation/screens/singbox_proxy_profile_editor.dart index c814daa6..900a9c19 100644 --- a/apps/weblibre/lib/features/proxy/presentation/screens/singbox_proxy_profile_editor.dart +++ b/apps/weblibre/lib/features/proxy/presentation/screens/singbox_proxy_profile_editor.dart @@ -22,6 +22,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_singbox_proxy/flutter_singbox_proxy.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; + +import 'package:weblibre/core/branding/proxy_brands.dart'; import 'package:weblibre/features/proxy/data/forms/singbox_form_specs.dart'; import 'package:weblibre/features/proxy/data/models/proxy_profile_seed.dart'; import 'package:weblibre/features/proxy/domain/extensions/singbox_proxy_profile_type_x.dart'; @@ -161,6 +163,10 @@ class _Editor extends ConsumerWidget { ?.copyWith(color: scheme.onSurfaceVariant), ), ), + if (draft.type == SingboxProxyProfileType.wireguard) ...[ + const SizedBox(height: 12), + const _WireGuardDisclaimer(), + ], ]), ), ), @@ -173,6 +179,26 @@ class _Editor extends ConsumerWidget { } } +class _WireGuardDisclaimer extends StatelessWidget { + const _WireGuardDisclaimer(); + + @override + Widget build(BuildContext context) { + final scheme = Theme.of(context).colorScheme; + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: Text( + '$wireGuardBrand is a registered trademark of Jason A. Donenfeld; all ' + 'rights reserved. WebLibre is not endorsed or sponsored by, or ' + 'affiliated with, Jason A. Donenfeld.', + style: Theme.of( + context, + ).textTheme.bodySmall?.copyWith(color: scheme.onSurfaceVariant), + ), + ); + } +} + class _GeneralSection extends HookConsumerWidget { final ProxyProfileDraftProvider draftProvider; final ProxyProfileDraftState draft; diff --git a/apps/weblibre/lib/features/proxy/presentation/widgets/add_proxy_method_sheet.dart b/apps/weblibre/lib/features/proxy/presentation/widgets/add_proxy_method_sheet.dart index c338dc8c..1ea94bb4 100644 --- a/apps/weblibre/lib/features/proxy/presentation/widgets/add_proxy_method_sheet.dart +++ b/apps/weblibre/lib/features/proxy/presentation/widgets/add_proxy_method_sheet.dart @@ -22,6 +22,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:qr_code_scanner_plus/qr_code_scanner_plus.dart'; +import 'package:weblibre/core/branding/proxy_brands.dart'; import 'package:weblibre/features/proxy/data/models/proxy_profile_seed.dart'; import 'package:weblibre/features/proxy/domain/services/proxy_input_consumer.dart'; import 'package:weblibre/features/qr_scanner/presentation/dialogs/qr_scanner_dialog.dart'; @@ -274,7 +275,7 @@ class _FileKindPicker extends StatelessWidget { ), ListTile( leading: const Icon(Icons.vpn_lock), - title: const Text('WireGuard config'), + title: const Text(wireGuardConfigLabel), subtitle: const Text('.conf file with [Interface]/[Peer]'), onTap: () => Navigator.of(context).pop(ProxyFileImportKind.wireguardConf), diff --git a/apps/weblibre/lib/features/proxy/presentation/widgets/profile_editor/profile_dns_override_section.dart b/apps/weblibre/lib/features/proxy/presentation/widgets/profile_editor/profile_dns_override_section.dart index 08c928ca..10d6c094 100644 --- a/apps/weblibre/lib/features/proxy/presentation/widgets/profile_editor/profile_dns_override_section.dart +++ b/apps/weblibre/lib/features/proxy/presentation/widgets/profile_editor/profile_dns_override_section.dart @@ -22,6 +22,7 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:weblibre/core/branding/proxy_brands.dart'; import 'package:weblibre/features/proxy/presentation/controllers/proxy_profile_draft_controller.dart'; import 'package:weblibre/features/user/data/models/proxy_dns_override.dart'; @@ -87,7 +88,7 @@ class ProfileDnsOverrideSection extends HookConsumerWidget { children: [ Text( 'Resolve names through a server reachable inside this profile ' - '(e.g. an internal DoH server behind a corporate WireGuard). ' + '(e.g. an internal DoH server behind a corporate $wireGuardBrand tunnel). ' 'Leave off to use automatic DNS handling.', style: Theme.of(context).textTheme.bodySmall, ), diff --git a/apps/weblibre/lib/features/proxy/presentation/widgets/profile_list/tor_tile.dart b/apps/weblibre/lib/features/proxy/presentation/widgets/profile_list/tor_tile.dart index a2c11ccb..6215d3f7 100644 --- a/apps/weblibre/lib/features/proxy/presentation/widgets/profile_list/tor_tile.dart +++ b/apps/weblibre/lib/features/proxy/presentation/widgets/profile_list/tor_tile.dart @@ -19,6 +19,7 @@ */ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:weblibre/core/branding/proxy_brands.dart'; import 'package:weblibre/core/logger.dart'; import 'package:weblibre/core/routing/routes.dart'; import 'package:weblibre/features/proxy/data/proxy_connection.dart'; @@ -72,7 +73,7 @@ class TorProfileTile extends ConsumerWidget { child: Icon(TorIcons.onionAlt, color: foreground, size: 24), ), title: Text( - 'Tor', + torBrand, style: TextStyle( fontWeight: isRunning ? FontWeight.w600 : FontWeight.w500, ), @@ -145,8 +146,8 @@ class TorProfileTile extends ConsumerWidget { showErrorMessage( context, isRunning - ? 'Failed to stop Tor: $error' - : 'Failed to start Tor: $error', + ? 'Failed to stop $torBrand: $error' + : 'Failed to start $torBrand: $error', ); } } diff --git a/apps/weblibre/lib/features/tor/presentation/screens/tor_proxy.dart b/apps/weblibre/lib/features/tor/presentation/screens/tor_proxy.dart index 7f964fbe..53c5de0b 100644 --- a/apps/weblibre/lib/features/tor/presentation/screens/tor_proxy.dart +++ b/apps/weblibre/lib/features/tor/presentation/screens/tor_proxy.dart @@ -23,6 +23,7 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_material_design_icons/flutter_material_design_icons.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:nullability/nullability.dart'; +import 'package:weblibre/core/branding/proxy_brands.dart'; import 'package:weblibre/core/routing/routes.dart'; import 'package:weblibre/features/settings/presentation/controllers/save_settings.dart'; import 'package:weblibre/features/settings/presentation/widgets/settings_detail.dart'; @@ -41,8 +42,8 @@ const List torProxySettingsSections = [ keywords: ['power', 'start', 'stop'], entries: [ SettingsEntryDefinition( - title: 'Tor™ Service', - subtitle: 'Start or stop the Tor service', + title: torServiceLabel, + subtitle: 'Start or stop the $torBrand service', keywords: ['enable', 'connect'], child: _TorServiceTile(), ), @@ -68,7 +69,7 @@ const List torProxySettingsSections = [ SettingsEntryDefinition( title: 'Transport', subtitle: - 'Choose how to reach the Tor network when not auto-configured', + 'Choose how to reach the $torNetworkLabel when not auto-configured', keywords: ['direct', 'obfs4', 'snowflake'], child: _TransportSection(), ), @@ -103,7 +104,7 @@ const List torProxySettingsSections = [ leading: Icon(Icons.info_outline), title: Text('Trademark'), subtitle: Text( - 'Tor is a trademark of The Tor Project; all rights reserved. ' + '$torBrand is a trademark of The Tor Project; all rights reserved. ' 'WebLibre is not endorsed or sponsored by, or affiliated with, ' 'the Tor Project.', ), @@ -133,7 +134,7 @@ class TorProxyScreen extends HookConsumerWidget { }); return const SettingsDetailScaffold( - title: 'Tor™ Proxy', + title: torProxyLabel, subtitle: 'Onion routing, pluggable transports, bridges and country restrictions.', icon: TorIcons.onionAlt, @@ -177,8 +178,8 @@ class _TorServiceTile extends HookConsumerWidget { children: [ SwitchListTile.adaptive( secondary: const Icon(MdiIcons.power), - title: const Text('Tor™ Service'), - subtitle: const Text('Start or stop the Tor service'), + title: const Text(torServiceLabel), + subtitle: const Text('Start or stop the $torBrand service'), value: pendingRequest.value ?? isRunning, onChanged: isBusy ? null @@ -225,7 +226,7 @@ class _RequestNewIdentityTile extends ConsumerWidget { onTap: () async { await ref.read(torProxyServiceProvider.notifier).requestNewIdentity(); if (context.mounted) { - showInfoMessage(context, 'Requesting new Tor identity...'); + showInfoMessage(context, 'Requesting new $torBrand identity...'); } }, ); @@ -248,7 +249,7 @@ class _AutoConfigureTransportTile extends ConsumerWidget { secondary: const Icon(MdiIcons.arrowDecisionAuto), title: const Text('Auto Configure Transport'), subtitle: const Text( - 'From some locations, it is necessary to use a pluggable transport to connect to Tor', + 'From some locations, it is necessary to use a pluggable transport to connect to $torBrand', ), value: torSettings.config == TorConnectionConfig.auto, onChanged: isBusy @@ -323,7 +324,7 @@ class _TransportSection extends ConsumerWidget { enabled: !isBusy, title: const Text('Direct Connection'), subtitle: const Text( - 'The best way to connect to Tor if Tor is not blocked', + 'The best way to connect to $torBrand if $torBrand is not blocked', ), ), RadioListTile.adaptive( diff --git a/apps/weblibre/lib/features/tor/presentation/widgets/tor_dialog.dart b/apps/weblibre/lib/features/tor/presentation/widgets/tor_dialog.dart index a4120098..97f72116 100644 --- a/apps/weblibre/lib/features/tor/presentation/widgets/tor_dialog.dart +++ b/apps/weblibre/lib/features/tor/presentation/widgets/tor_dialog.dart @@ -19,6 +19,7 @@ */ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; +import 'package:weblibre/core/branding/proxy_brands.dart'; import 'package:weblibre/core/design/app_colors.dart'; import 'package:weblibre/presentation/icons/tor_icons.dart'; @@ -30,9 +31,9 @@ class TorDialog extends StatelessWidget { final appColors = AppColors.of(context); return AlertDialog( icon: Icon(TorIcons.onionAlt, color: appColors.torPurple), - title: const Text('Tor™ Proxy'), + title: const Text(torProxyLabel), content: const Text( - 'This container requires a Tor proxy for secure connections, which is not currently running.', + 'This container requires a $torBrand proxy for secure connections, which is not currently running.', ), actions: [ TextButton( diff --git a/apps/weblibre/lib/features/tor/presentation/widgets/tor_notification.dart b/apps/weblibre/lib/features/tor/presentation/widgets/tor_notification.dart index de40dec9..cfc823eb 100644 --- a/apps/weblibre/lib/features/tor/presentation/widgets/tor_notification.dart +++ b/apps/weblibre/lib/features/tor/presentation/widgets/tor_notification.dart @@ -19,6 +19,7 @@ */ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:weblibre/core/branding/proxy_brands.dart'; import 'package:weblibre/core/design/app_colors.dart'; import 'package:weblibre/features/geckoview/domain/controllers/overlay.dart'; import 'package:weblibre/features/tor/domain/extensions/tor_status_x.dart'; @@ -68,7 +69,7 @@ class TorNotification extends HookConsumerWidget { ), Expanded( child: Text( - 'Tor Proxy is connecting...', + '$torProxyLabel is connecting...', style: Theme.of( context, ).textTheme.bodyMedium?.copyWith(color: Colors.white), diff --git a/apps/weblibre/lib/features/web_search/domain/controllers/search_controller.dart b/apps/weblibre/lib/features/web_search/domain/controllers/search_controller.dart index b97c065c..0bd2e0df 100644 --- a/apps/weblibre/lib/features/web_search/domain/controllers/search_controller.dart +++ b/apps/weblibre/lib/features/web_search/domain/controllers/search_controller.dart @@ -5,6 +5,7 @@ import 'dart:typed_data'; import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:search_client/search_client.dart'; +import 'package:weblibre/core/branding/proxy_brands.dart'; import 'package:weblibre/domain/services/generic_website.dart'; import 'package:weblibre/features/search_credits/domain/controllers/search_token_issuance_controller.dart'; import 'package:weblibre/features/search_credits/domain/providers.dart'; @@ -302,7 +303,7 @@ class MetaSearchController extends _$MetaSearchController { state = state.copyWith( status: WebSearchStatus.error, errorMessage: - 'Could not start Tor for the search. Disable the Tor toggle or try again.', + 'Could not start $torBrand for the search. Disable the $torBrand toggle or try again.', hasOpenSession: false, ); return; diff --git a/apps/weblibre/lib/features/web_search/presentation/widgets/route_through_tor_toggle.dart b/apps/weblibre/lib/features/web_search/presentation/widgets/route_through_tor_toggle.dart index 56fb5d0b..387b65ed 100644 --- a/apps/weblibre/lib/features/web_search/presentation/widgets/route_through_tor_toggle.dart +++ b/apps/weblibre/lib/features/web_search/presentation/widgets/route_through_tor_toggle.dart @@ -19,6 +19,7 @@ */ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:weblibre/core/branding/proxy_brands.dart'; import 'package:weblibre/core/design/app_colors.dart'; import 'package:weblibre/features/search_credits/domain/providers/proxy_client.dart'; import 'package:weblibre/features/search_credits/domain/repositories/web_search_settings.dart'; @@ -121,7 +122,7 @@ class RouteThroughTorToggle extends HookConsumerWidget { ), const SizedBox(width: 6), Text( - routeThroughTor ? 'Tor on' : 'Tor off', + routeThroughTor ? '$torBrand on' : '$torBrand off', style: textTheme.labelLarge?.copyWith( color: routeThroughTor ? colorScheme.onPrimaryContainer diff --git a/apps/weblibre/test/features/proxy/domain/providers/proxy_connection_options_test.dart b/apps/weblibre/test/features/proxy/domain/providers/proxy_connection_options_test.dart index d5df07a1..57cf8138 100644 --- a/apps/weblibre/test/features/proxy/domain/providers/proxy_connection_options_test.dart +++ b/apps/weblibre/test/features/proxy/domain/providers/proxy_connection_options_test.dart @@ -1,6 +1,7 @@ import 'package:flutter_singbox_proxy/flutter_singbox_proxy.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:riverpod/riverpod.dart'; +import 'package:weblibre/core/branding/proxy_brands.dart'; import 'package:weblibre/features/proxy/data/proxy_connection.dart'; import 'package:weblibre/features/proxy/domain/providers/proxy_connection_options.dart'; import 'package:weblibre/features/proxy/domain/repositories/singbox_proxy_profiles.dart'; @@ -41,7 +42,38 @@ void main() { const TorProxyConnectionId(), const SingboxProxyConnectionId('profile-1'), ]); - expect(options.map((option) => option.title), ['Tor', 'Mullvad']); + expect(options.map((option) => option.title), [torBrand, 'Mullvad']); + }); + + test('uses standardized WireGuard branding for subtitles', () async { + final createdAt = DateTime(2026); + final container = ProviderContainer( + overrides: [ + singboxProxyProfilesRepositoryProvider.overrideWith( + () => _FakeProfilesRepository([ + ProxyProfile( + id: 'profile-1', + name: 'Home Tunnel', + type: SingboxProxyProfileType.wireguard, + configJson: '{"type":"wireguard"}', + createdAt: createdAt, + updatedAt: createdAt, + ), + ]), + ), + ], + ); + addTearDown(container.dispose); + + container.listen( + singboxProxyProfilesRepositoryProvider, + (_, _) {}, + fireImmediately: true, + ); + await Future.delayed(Duration.zero); + + final options = container.read(proxyConnectionOptionsProvider); + expect(options[1].subtitle, wireGuardBrand); }); test('labels unknown proxy ids explicitly', () { diff --git a/packages/flutter_tor/android/src/main/AndroidManifest.xml b/packages/flutter_tor/android/src/main/AndroidManifest.xml index f447f7f2..ba70fb5a 100644 --- a/packages/flutter_tor/android/src/main/AndroidManifest.xml +++ b/packages/flutter_tor/android/src/main/AndroidManifest.xml @@ -23,7 +23,7 @@ android:foregroundServiceType="specialUse"> + android:value="Tor™ proxy service for anonymous networking" /> diff --git a/packages/flutter_tor/android/src/main/kotlin/eu/weblibre/flutter_tor/TorService.kt b/packages/flutter_tor/android/src/main/kotlin/eu/weblibre/flutter_tor/TorService.kt index 2037d932..fe5a52a7 100644 --- a/packages/flutter_tor/android/src/main/kotlin/eu/weblibre/flutter_tor/TorService.kt +++ b/packages/flutter_tor/android/src/main/kotlin/eu/weblibre/flutter_tor/TorService.kt @@ -57,7 +57,7 @@ class TorService : Service() { ACTION_START -> { // startForegroundService() must promote the service promptly, before // the later binder call reaches startTor(). - startForeground(NOTIFICATION_ID, createNotification("Tor is connecting...")) + startForeground(NOTIFICATION_ID, createNotification("Tor™ is connecting...")) } ACTION_STOP -> { scope.launch { @@ -92,13 +92,13 @@ class TorService : Service() { suspend fun startTor(config: TorConfiguration): Int { Log.d(TAG, "Starting Tor...") - startForeground(NOTIFICATION_ID, createNotification("Tor is connecting...")) + startForeground(NOTIFICATION_ID, createNotification("Tor™ is connecting...")) val manager = torManager ?: throw IllegalStateException("Service not initialized") try { val socksPort = manager.start(config) - updateNotification("Tor connected") + updateNotification("Tor™ connected") return socksPort } catch (e: Exception) { Log.e(TAG, "Failed to start Tor", e) @@ -162,7 +162,7 @@ class TorService : Service() { ) return NotificationCompat.Builder(this, CHANNEL_ID) - .setContentTitle("WebLibre Tor") + .setContentTitle("WebLibre Tor™") .setContentText(text) .setSmallIcon(R.drawable.ic_onion) .setContentIntent(pendingIntent) @@ -178,10 +178,10 @@ class TorService : Service() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel( CHANNEL_ID, - "Tor Service", + "Tor™ Service", NotificationManager.IMPORTANCE_LOW ).apply { - description = "Keeps Tor running in the background" + description = "Keeps Tor™ running in the background" setShowBadge(false) }