branding fixes
This commit is contained in:
@@ -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';
|
||||||
+2
-1
@@ -35,6 +35,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||||||
import 'package:nullability/nullability.dart';
|
import 'package:nullability/nullability.dart';
|
||||||
import 'package:share_plus/share_plus.dart';
|
import 'package:share_plus/share_plus.dart';
|
||||||
import 'package:skeletonizer/skeletonizer.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/design/app_colors.dart';
|
||||||
import 'package:weblibre/core/logger.dart';
|
import 'package:weblibre/core/logger.dart';
|
||||||
import 'package:weblibre/core/providers/persisted_bool.dart';
|
import 'package:weblibre/core/providers/persisted_bool.dart';
|
||||||
@@ -2043,7 +2044,7 @@ class _ConnectionCard extends ConsumerWidget {
|
|||||||
final rows = <Widget>[
|
final rows = <Widget>[
|
||||||
_ConnectionRow(
|
_ConnectionRow(
|
||||||
icon: TorIcons.onionAlt,
|
icon: TorIcons.onionAlt,
|
||||||
label: 'Tor™ Proxy',
|
label: torProxyLabel,
|
||||||
active: isTorActive,
|
active: isTorActive,
|
||||||
enabled: !isTorBusy,
|
enabled: !isTorBusy,
|
||||||
onToggle: () async {
|
onToggle: () async {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:fast_equatable/fast_equatable.dart';
|
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';
|
import 'package:weblibre/features/proxy/data/parsers/host_port.dart';
|
||||||
|
|
||||||
class WireguardConfigImport with FastEquatable {
|
class WireguardConfigImport with FastEquatable {
|
||||||
@@ -42,7 +43,7 @@ class WireguardConfigImport with FastEquatable {
|
|||||||
final peer = sections['peer'];
|
final peer = sections['peer'];
|
||||||
if (interface == null || peer == null) {
|
if (interface == null || peer == null) {
|
||||||
throw const FormatException(
|
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: '')
|
? (host: '', port: '')
|
||||||
: parseHostPort(
|
: parseHostPort(
|
||||||
rawEndpoint,
|
rawEndpoint,
|
||||||
invalidMessage: 'WireGuard endpoint must be host:port.',
|
invalidMessage: '$wireGuardBrand endpoint must be host:port.',
|
||||||
);
|
);
|
||||||
final mtu = interface['mtu']?.trim();
|
final mtu = interface['mtu']?.trim();
|
||||||
|
|
||||||
@@ -103,7 +104,9 @@ Map<String, Map<String, String>> _parseWireguardConfig(String configText) {
|
|||||||
if (line.startsWith('[') && line.endsWith(']')) {
|
if (line.startsWith('[') && line.endsWith(']')) {
|
||||||
currentSection = line.substring(1, line.length - 1).trim().toLowerCase();
|
currentSection = line.substring(1, line.length - 1).trim().toLowerCase();
|
||||||
if (currentSection.isEmpty) {
|
if (currentSection.isEmpty) {
|
||||||
throw const FormatException('WireGuard config contains empty section.');
|
throw const FormatException(
|
||||||
|
'$wireGuardBrand config contains empty section.',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
sections.putIfAbsent(currentSection, () => <String, String>{});
|
sections.putIfAbsent(currentSection, () => <String, String>{});
|
||||||
continue;
|
continue;
|
||||||
@@ -111,19 +114,19 @@ Map<String, Map<String, String>> _parseWireguardConfig(String configText) {
|
|||||||
|
|
||||||
if (currentSection == null) {
|
if (currentSection == null) {
|
||||||
throw const FormatException(
|
throw const FormatException(
|
||||||
'WireGuard config entries must be inside a section.',
|
'$wireGuardBrand config entries must be inside a section.',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final separatorIndex = line.indexOf('=');
|
final separatorIndex = line.indexOf('=');
|
||||||
if (separatorIndex < 1) {
|
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 key = line.substring(0, separatorIndex).trim().toLowerCase();
|
||||||
final value = line.substring(separatorIndex + 1).trim();
|
final value = line.substring(separatorIndex + 1).trim();
|
||||||
if (key.isEmpty) {
|
if (key.isEmpty) {
|
||||||
throw FormatException('Invalid WireGuard config line: $rawLine');
|
throw FormatException('Invalid $wireGuardBrand config line: $rawLine');
|
||||||
}
|
}
|
||||||
sections[currentSection]![key] = value;
|
sections[currentSection]![key] = value;
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -19,6 +19,8 @@
|
|||||||
*/
|
*/
|
||||||
import 'package:flutter_singbox_proxy/flutter_singbox_proxy.dart';
|
import 'package:flutter_singbox_proxy/flutter_singbox_proxy.dart';
|
||||||
|
|
||||||
|
import 'package:weblibre/core/branding/proxy_brands.dart';
|
||||||
|
|
||||||
extension SingboxProxyProfileTypeExt on SingboxProxyProfileType {
|
extension SingboxProxyProfileTypeExt on SingboxProxyProfileType {
|
||||||
/// Long human label for menus, dialogs, subtitles.
|
/// Long human label for menus, dialogs, subtitles.
|
||||||
String get label => switch (this) {
|
String get label => switch (this) {
|
||||||
@@ -33,7 +35,7 @@ extension SingboxProxyProfileTypeExt on SingboxProxyProfileType {
|
|||||||
SingboxProxyProfileType.hysteria2 => 'Hysteria2',
|
SingboxProxyProfileType.hysteria2 => 'Hysteria2',
|
||||||
SingboxProxyProfileType.tuic => 'TUIC',
|
SingboxProxyProfileType.tuic => 'TUIC',
|
||||||
SingboxProxyProfileType.ssh => 'SSH',
|
SingboxProxyProfileType.ssh => 'SSH',
|
||||||
SingboxProxyProfileType.wireguard => 'WireGuard',
|
SingboxProxyProfileType.wireguard => wireGuardBrand,
|
||||||
SingboxProxyProfileType.shadowTls => 'ShadowTLS',
|
SingboxProxyProfileType.shadowTls => 'ShadowTLS',
|
||||||
SingboxProxyProfileType.anyTls => 'AnyTLS',
|
SingboxProxyProfileType.anyTls => 'AnyTLS',
|
||||||
SingboxProxyProfileType.customOutbound => 'Custom Outbound',
|
SingboxProxyProfileType.customOutbound => 'Custom Outbound',
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
import 'package:fast_equatable/fast_equatable.dart';
|
import 'package:fast_equatable/fast_equatable.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.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/core/logger.dart';
|
||||||
import 'package:weblibre/features/proxy/data/models/singbox_proxy_profile.dart';
|
import 'package:weblibre/features/proxy/data/models/singbox_proxy_profile.dart';
|
||||||
import 'package:weblibre/features/proxy/data/proxy_connection.dart';
|
import 'package:weblibre/features/proxy/data/proxy_connection.dart';
|
||||||
@@ -61,8 +62,8 @@ List<ProxyConnectionOption> proxyConnectionOptions(Ref ref) {
|
|||||||
return [
|
return [
|
||||||
ProxyConnectionOption(
|
ProxyConnectionOption(
|
||||||
id: const TorProxyConnectionId(),
|
id: const TorProxyConnectionId(),
|
||||||
title: 'Tor',
|
title: torBrand,
|
||||||
subtitle: 'Route through the Tor network',
|
subtitle: 'Route through the $torNetworkLabel',
|
||||||
),
|
),
|
||||||
for (final profile in singboxProfiles)
|
for (final profile in singboxProfiles)
|
||||||
ProxyConnectionOption(
|
ProxyConnectionOption(
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import 'dart:async';
|
|||||||
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
import 'package:synchronized/synchronized.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/geckoview/features/tabs/data/models/site_assignment.dart';
|
||||||
import 'package:weblibre/features/proxy/data/proxy_connection.dart';
|
import 'package:weblibre/features/proxy/data/proxy_connection.dart';
|
||||||
|
|
||||||
@@ -57,7 +58,7 @@ class ContainerProxyRepository extends _$ContainerProxyRepository {
|
|||||||
return _service.upsertProxy(
|
return _service.upsertProxy(
|
||||||
GeckoProxySettings(
|
GeckoProxySettings(
|
||||||
id: const TorProxyConnectionId().encode(),
|
id: const TorProxyConnectionId().encode(),
|
||||||
title: 'Tor',
|
title: torBrand,
|
||||||
type: 'socks',
|
type: 'socks',
|
||||||
host: '127.0.0.1',
|
host: '127.0.0.1',
|
||||||
port: port,
|
port: port,
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import 'dart:convert';
|
|||||||
import 'package:file_picker/file_picker.dart';
|
import 'package:file_picker/file_picker.dart';
|
||||||
import 'package:flutter_singbox_proxy/flutter_singbox_proxy.dart';
|
import 'package:flutter_singbox_proxy/flutter_singbox_proxy.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.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/core/logger.dart';
|
||||||
import 'package:weblibre/features/proxy/data/forms/singbox_form_specs.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/data/models/proxy_profile_seed.dart';
|
||||||
@@ -70,8 +71,8 @@ class ProxyInputConsumer extends _$ProxyInputConsumer {
|
|||||||
|
|
||||||
if (_looksLikeWireguardConf(trimmed)) {
|
if (_looksLikeWireguardConf(trimmed)) {
|
||||||
return _seedOutcome(
|
return _seedOutcome(
|
||||||
() => _seedFromWireguardConf(trimmed, fileName: 'WireGuard'),
|
() => _seedFromWireguardConf(trimmed, fileName: wireGuardBrand),
|
||||||
logMessage: 'Failed to parse WireGuard configuration',
|
logMessage: 'Failed to parse $wireGuardBrand configuration',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:flutter_singbox_proxy/flutter_singbox_proxy.dart';
|
import 'package:flutter_singbox_proxy/flutter_singbox_proxy.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
import 'package:socks5_proxy/socks_client.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/data/proxy_connection.dart';
|
||||||
import 'package:weblibre/features/proxy/domain/repositories/singbox_proxy_runtime.dart';
|
import 'package:weblibre/features/proxy/domain/repositories/singbox_proxy_runtime.dart';
|
||||||
import 'package:weblibre/features/tor/domain/extensions/tor_status_x.dart';
|
import 'package:weblibre/features/tor/domain/extensions/tor_status_x.dart';
|
||||||
@@ -118,7 +119,7 @@ class ProxyLatencyResults extends _$ProxyLatencyResults {
|
|||||||
if (socksPort == null) {
|
if (socksPort == null) {
|
||||||
_set(
|
_set(
|
||||||
const TorProxyConnectionId(),
|
const TorProxyConnectionId(),
|
||||||
AsyncError('Tor is not ready', StackTrace.current),
|
AsyncError('$torBrand is not ready', StackTrace.current),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:share_plus/share_plus.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/data/models/proxy_log_message.dart';
|
||||||
import 'package:weblibre/features/proxy/domain/repositories/singbox_proxy_logs.dart';
|
import 'package:weblibre/features/proxy/domain/repositories/singbox_proxy_logs.dart';
|
||||||
import 'package:weblibre/utils/ui_helper.dart';
|
import 'package:weblibre/utils/ui_helper.dart';
|
||||||
@@ -202,7 +203,7 @@ class _EmptyLogs extends StatelessWidget {
|
|||||||
child: Text(
|
child: Text(
|
||||||
hasFilter
|
hasFilter
|
||||||
? 'No log lines match the current filter.'
|
? '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,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
+26
@@ -22,6 +22,8 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:flutter_singbox_proxy/flutter_singbox_proxy.dart';
|
import 'package:flutter_singbox_proxy/flutter_singbox_proxy.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.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/forms/singbox_form_specs.dart';
|
||||||
import 'package:weblibre/features/proxy/data/models/proxy_profile_seed.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';
|
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),
|
?.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 {
|
class _GeneralSection extends HookConsumerWidget {
|
||||||
final ProxyProfileDraftProvider draftProvider;
|
final ProxyProfileDraftProvider draftProvider;
|
||||||
final ProxyProfileDraftState draft;
|
final ProxyProfileDraftState draft;
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:qr_code_scanner_plus/qr_code_scanner_plus.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/data/models/proxy_profile_seed.dart';
|
||||||
import 'package:weblibre/features/proxy/domain/services/proxy_input_consumer.dart';
|
import 'package:weblibre/features/proxy/domain/services/proxy_input_consumer.dart';
|
||||||
import 'package:weblibre/features/qr_scanner/presentation/dialogs/qr_scanner_dialog.dart';
|
import 'package:weblibre/features/qr_scanner/presentation/dialogs/qr_scanner_dialog.dart';
|
||||||
@@ -274,7 +275,7 @@ class _FileKindPicker extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.vpn_lock),
|
leading: const Icon(Icons.vpn_lock),
|
||||||
title: const Text('WireGuard config'),
|
title: const Text(wireGuardConfigLabel),
|
||||||
subtitle: const Text('.conf file with [Interface]/[Peer]'),
|
subtitle: const Text('.conf file with [Interface]/[Peer]'),
|
||||||
onTap: () =>
|
onTap: () =>
|
||||||
Navigator.of(context).pop(ProxyFileImportKind.wireguardConf),
|
Navigator.of(context).pop(ProxyFileImportKind.wireguardConf),
|
||||||
|
|||||||
+2
-1
@@ -22,6 +22,7 @@ import 'dart:convert';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.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/proxy/presentation/controllers/proxy_profile_draft_controller.dart';
|
||||||
import 'package:weblibre/features/user/data/models/proxy_dns_override.dart';
|
import 'package:weblibre/features/user/data/models/proxy_dns_override.dart';
|
||||||
|
|
||||||
@@ -87,7 +88,7 @@ class ProfileDnsOverrideSection extends HookConsumerWidget {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'Resolve names through a server reachable inside this profile '
|
'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.',
|
'Leave off to use automatic DNS handling.',
|
||||||
style: Theme.of(context).textTheme.bodySmall,
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.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/logger.dart';
|
||||||
import 'package:weblibre/core/routing/routes.dart';
|
import 'package:weblibre/core/routing/routes.dart';
|
||||||
import 'package:weblibre/features/proxy/data/proxy_connection.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),
|
child: Icon(TorIcons.onionAlt, color: foreground, size: 24),
|
||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
'Tor',
|
torBrand,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: isRunning ? FontWeight.w600 : FontWeight.w500,
|
fontWeight: isRunning ? FontWeight.w600 : FontWeight.w500,
|
||||||
),
|
),
|
||||||
@@ -145,8 +146,8 @@ class TorProfileTile extends ConsumerWidget {
|
|||||||
showErrorMessage(
|
showErrorMessage(
|
||||||
context,
|
context,
|
||||||
isRunning
|
isRunning
|
||||||
? 'Failed to stop Tor: $error'
|
? 'Failed to stop $torBrand: $error'
|
||||||
: 'Failed to start Tor: $error',
|
: 'Failed to start $torBrand: $error',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
|||||||
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
|
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:nullability/nullability.dart';
|
import 'package:nullability/nullability.dart';
|
||||||
|
import 'package:weblibre/core/branding/proxy_brands.dart';
|
||||||
import 'package:weblibre/core/routing/routes.dart';
|
import 'package:weblibre/core/routing/routes.dart';
|
||||||
import 'package:weblibre/features/settings/presentation/controllers/save_settings.dart';
|
import 'package:weblibre/features/settings/presentation/controllers/save_settings.dart';
|
||||||
import 'package:weblibre/features/settings/presentation/widgets/settings_detail.dart';
|
import 'package:weblibre/features/settings/presentation/widgets/settings_detail.dart';
|
||||||
@@ -41,8 +42,8 @@ const List<SettingsSectionDefinition> torProxySettingsSections = [
|
|||||||
keywords: ['power', 'start', 'stop'],
|
keywords: ['power', 'start', 'stop'],
|
||||||
entries: [
|
entries: [
|
||||||
SettingsEntryDefinition(
|
SettingsEntryDefinition(
|
||||||
title: 'Tor™ Service',
|
title: torServiceLabel,
|
||||||
subtitle: 'Start or stop the Tor service',
|
subtitle: 'Start or stop the $torBrand service',
|
||||||
keywords: ['enable', 'connect'],
|
keywords: ['enable', 'connect'],
|
||||||
child: _TorServiceTile(),
|
child: _TorServiceTile(),
|
||||||
),
|
),
|
||||||
@@ -68,7 +69,7 @@ const List<SettingsSectionDefinition> torProxySettingsSections = [
|
|||||||
SettingsEntryDefinition(
|
SettingsEntryDefinition(
|
||||||
title: 'Transport',
|
title: 'Transport',
|
||||||
subtitle:
|
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'],
|
keywords: ['direct', 'obfs4', 'snowflake'],
|
||||||
child: _TransportSection(),
|
child: _TransportSection(),
|
||||||
),
|
),
|
||||||
@@ -103,7 +104,7 @@ const List<SettingsSectionDefinition> torProxySettingsSections = [
|
|||||||
leading: Icon(Icons.info_outline),
|
leading: Icon(Icons.info_outline),
|
||||||
title: Text('Trademark'),
|
title: Text('Trademark'),
|
||||||
subtitle: Text(
|
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, '
|
'WebLibre is not endorsed or sponsored by, or affiliated with, '
|
||||||
'the Tor Project.',
|
'the Tor Project.',
|
||||||
),
|
),
|
||||||
@@ -133,7 +134,7 @@ class TorProxyScreen extends HookConsumerWidget {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return const SettingsDetailScaffold(
|
return const SettingsDetailScaffold(
|
||||||
title: 'Tor™ Proxy',
|
title: torProxyLabel,
|
||||||
subtitle:
|
subtitle:
|
||||||
'Onion routing, pluggable transports, bridges and country restrictions.',
|
'Onion routing, pluggable transports, bridges and country restrictions.',
|
||||||
icon: TorIcons.onionAlt,
|
icon: TorIcons.onionAlt,
|
||||||
@@ -177,8 +178,8 @@ class _TorServiceTile extends HookConsumerWidget {
|
|||||||
children: [
|
children: [
|
||||||
SwitchListTile.adaptive(
|
SwitchListTile.adaptive(
|
||||||
secondary: const Icon(MdiIcons.power),
|
secondary: const Icon(MdiIcons.power),
|
||||||
title: const Text('Tor™ Service'),
|
title: const Text(torServiceLabel),
|
||||||
subtitle: const Text('Start or stop the Tor service'),
|
subtitle: const Text('Start or stop the $torBrand service'),
|
||||||
value: pendingRequest.value ?? isRunning,
|
value: pendingRequest.value ?? isRunning,
|
||||||
onChanged: isBusy
|
onChanged: isBusy
|
||||||
? null
|
? null
|
||||||
@@ -225,7 +226,7 @@ class _RequestNewIdentityTile extends ConsumerWidget {
|
|||||||
onTap: () async {
|
onTap: () async {
|
||||||
await ref.read(torProxyServiceProvider.notifier).requestNewIdentity();
|
await ref.read(torProxyServiceProvider.notifier).requestNewIdentity();
|
||||||
if (context.mounted) {
|
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),
|
secondary: const Icon(MdiIcons.arrowDecisionAuto),
|
||||||
title: const Text('Auto Configure Transport'),
|
title: const Text('Auto Configure Transport'),
|
||||||
subtitle: const Text(
|
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,
|
value: torSettings.config == TorConnectionConfig.auto,
|
||||||
onChanged: isBusy
|
onChanged: isBusy
|
||||||
@@ -323,7 +324,7 @@ class _TransportSection extends ConsumerWidget {
|
|||||||
enabled: !isBusy,
|
enabled: !isBusy,
|
||||||
title: const Text('Direct Connection'),
|
title: const Text('Direct Connection'),
|
||||||
subtitle: const Text(
|
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<TorConnectionConfig>.adaptive(
|
RadioListTile<TorConnectionConfig>.adaptive(
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.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/core/design/app_colors.dart';
|
||||||
import 'package:weblibre/presentation/icons/tor_icons.dart';
|
import 'package:weblibre/presentation/icons/tor_icons.dart';
|
||||||
|
|
||||||
@@ -30,9 +31,9 @@ class TorDialog extends StatelessWidget {
|
|||||||
final appColors = AppColors.of(context);
|
final appColors = AppColors.of(context);
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
icon: Icon(TorIcons.onionAlt, color: appColors.torPurple),
|
icon: Icon(TorIcons.onionAlt, color: appColors.torPurple),
|
||||||
title: const Text('Tor™ Proxy'),
|
title: const Text(torProxyLabel),
|
||||||
content: const Text(
|
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: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.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/core/design/app_colors.dart';
|
||||||
import 'package:weblibre/features/geckoview/domain/controllers/overlay.dart';
|
import 'package:weblibre/features/geckoview/domain/controllers/overlay.dart';
|
||||||
import 'package:weblibre/features/tor/domain/extensions/tor_status_x.dart';
|
import 'package:weblibre/features/tor/domain/extensions/tor_status_x.dart';
|
||||||
@@ -68,7 +69,7 @@ class TorNotification extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
'Tor Proxy is connecting...',
|
'$torProxyLabel is connecting...',
|
||||||
style: Theme.of(
|
style: Theme.of(
|
||||||
context,
|
context,
|
||||||
).textTheme.bodyMedium?.copyWith(color: Colors.white),
|
).textTheme.bodyMedium?.copyWith(color: Colors.white),
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import 'dart:typed_data';
|
|||||||
import 'package:riverpod/riverpod.dart';
|
import 'package:riverpod/riverpod.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
import 'package:search_client/search_client.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/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/controllers/search_token_issuance_controller.dart';
|
||||||
import 'package:weblibre/features/search_credits/domain/providers.dart';
|
import 'package:weblibre/features/search_credits/domain/providers.dart';
|
||||||
@@ -302,7 +303,7 @@ class MetaSearchController extends _$MetaSearchController {
|
|||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
status: WebSearchStatus.error,
|
status: WebSearchStatus.error,
|
||||||
errorMessage:
|
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,
|
hasOpenSession: false,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
|
|||||||
+2
-1
@@ -19,6 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.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/core/design/app_colors.dart';
|
||||||
import 'package:weblibre/features/search_credits/domain/providers/proxy_client.dart';
|
import 'package:weblibre/features/search_credits/domain/providers/proxy_client.dart';
|
||||||
import 'package:weblibre/features/search_credits/domain/repositories/web_search_settings.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),
|
const SizedBox(width: 6),
|
||||||
Text(
|
Text(
|
||||||
routeThroughTor ? 'Tor on' : 'Tor off',
|
routeThroughTor ? '$torBrand on' : '$torBrand off',
|
||||||
style: textTheme.labelLarge?.copyWith(
|
style: textTheme.labelLarge?.copyWith(
|
||||||
color: routeThroughTor
|
color: routeThroughTor
|
||||||
? colorScheme.onPrimaryContainer
|
? colorScheme.onPrimaryContainer
|
||||||
|
|||||||
+33
-1
@@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter_singbox_proxy/flutter_singbox_proxy.dart';
|
import 'package:flutter_singbox_proxy/flutter_singbox_proxy.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:riverpod/riverpod.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/data/proxy_connection.dart';
|
||||||
import 'package:weblibre/features/proxy/domain/providers/proxy_connection_options.dart';
|
import 'package:weblibre/features/proxy/domain/providers/proxy_connection_options.dart';
|
||||||
import 'package:weblibre/features/proxy/domain/repositories/singbox_proxy_profiles.dart';
|
import 'package:weblibre/features/proxy/domain/repositories/singbox_proxy_profiles.dart';
|
||||||
@@ -41,7 +42,38 @@ void main() {
|
|||||||
const TorProxyConnectionId(),
|
const TorProxyConnectionId(),
|
||||||
const SingboxProxyConnectionId('profile-1'),
|
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<void>.delayed(Duration.zero);
|
||||||
|
|
||||||
|
final options = container.read(proxyConnectionOptionsProvider);
|
||||||
|
expect(options[1].subtitle, wireGuardBrand);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('labels unknown proxy ids explicitly', () {
|
test('labels unknown proxy ids explicitly', () {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
android:foregroundServiceType="specialUse">
|
android:foregroundServiceType="specialUse">
|
||||||
<property
|
<property
|
||||||
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
|
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
|
||||||
android:value="Tor proxy service for anonymous networking" />
|
android:value="Tor™ proxy service for anonymous networking" />
|
||||||
</service>
|
</service>
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ class TorService : Service() {
|
|||||||
ACTION_START -> {
|
ACTION_START -> {
|
||||||
// startForegroundService() must promote the service promptly, before
|
// startForegroundService() must promote the service promptly, before
|
||||||
// the later binder call reaches startTor().
|
// the later binder call reaches startTor().
|
||||||
startForeground(NOTIFICATION_ID, createNotification("Tor is connecting..."))
|
startForeground(NOTIFICATION_ID, createNotification("Tor™ is connecting..."))
|
||||||
}
|
}
|
||||||
ACTION_STOP -> {
|
ACTION_STOP -> {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
@@ -92,13 +92,13 @@ class TorService : Service() {
|
|||||||
suspend fun startTor(config: TorConfiguration): Int {
|
suspend fun startTor(config: TorConfiguration): Int {
|
||||||
Log.d(TAG, "Starting Tor...")
|
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")
|
val manager = torManager ?: throw IllegalStateException("Service not initialized")
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val socksPort = manager.start(config)
|
val socksPort = manager.start(config)
|
||||||
updateNotification("Tor connected")
|
updateNotification("Tor™ connected")
|
||||||
return socksPort
|
return socksPort
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "Failed to start Tor", e)
|
Log.e(TAG, "Failed to start Tor", e)
|
||||||
@@ -162,7 +162,7 @@ class TorService : Service() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
return NotificationCompat.Builder(this, CHANNEL_ID)
|
return NotificationCompat.Builder(this, CHANNEL_ID)
|
||||||
.setContentTitle("WebLibre Tor")
|
.setContentTitle("WebLibre Tor™")
|
||||||
.setContentText(text)
|
.setContentText(text)
|
||||||
.setSmallIcon(R.drawable.ic_onion)
|
.setSmallIcon(R.drawable.ic_onion)
|
||||||
.setContentIntent(pendingIntent)
|
.setContentIntent(pendingIntent)
|
||||||
@@ -178,10 +178,10 @@ class TorService : Service() {
|
|||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
val channel = NotificationChannel(
|
val channel = NotificationChannel(
|
||||||
CHANNEL_ID,
|
CHANNEL_ID,
|
||||||
"Tor Service",
|
"Tor™ Service",
|
||||||
NotificationManager.IMPORTANCE_LOW
|
NotificationManager.IMPORTANCE_LOW
|
||||||
).apply {
|
).apply {
|
||||||
description = "Keeps Tor running in the background"
|
description = "Keeps Tor™ running in the background"
|
||||||
setShowBadge(false)
|
setShowBadge(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user