improve tor ui

This commit is contained in:
Fabian Freund
2026-01-09 09:23:15 +01:00
parent ea015f9415
commit 87e18309d8
@@ -19,7 +19,6 @@
*/
import 'package:country_codes/country_codes.dart';
import 'package:country_flags/country_flags.dart';
import 'package:fading_scroll/fading_scroll.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
@@ -31,7 +30,6 @@ import 'package:weblibre/features/tor/domain/services/tor_proxy.dart';
import 'package:weblibre/features/user/data/models/tor_settings.dart';
import 'package:weblibre/features/user/domain/repositories/tor_settings.dart';
import 'package:weblibre/presentation/hooks/on_initialization.dart';
import 'package:weblibre/presentation/icons/tor_icons.dart';
import 'package:weblibre/utils/ui_helper.dart';
class TorProxyScreen extends HookConsumerWidget {
@@ -95,11 +93,7 @@ class TorProxyScreen extends HookConsumerWidget {
final countryDropdownEntries = useMemoized(
() => [
const DropdownMenuEntry(
value: null,
label: 'Automatic',
leadingIcon: Icon(MdiIcons.lightbulbAuto),
),
const DropdownMenuEntry(value: null, label: 'Automatic'),
...CountryCodes.countryCodes().map((country) {
final label =
country.localizedName ??
@@ -111,12 +105,6 @@ class TorProxyScreen extends HookConsumerWidget {
return DropdownMenuEntry(
value: country.alpha2Code,
label: label,
leadingIcon: country.alpha2Code.mapNotNull(
(code) => CountryFlag.fromCountryCode(
code,
theme: const ImageTheme(width: 24, height: 16),
),
),
labelWidget: Text(
label,
style: const TextStyle(color: Colors.white),
@@ -127,7 +115,6 @@ class TorProxyScreen extends HookConsumerWidget {
);
return Scaffold(
appBar: AppBar(title: const Text('Tor™ Proxy')),
body: Theme(
data: Theme.of(context).copyWith(
listTileTheme: ListTileTheme.of(
@@ -160,25 +147,18 @@ class TorProxyScreen extends HookConsumerWidget {
fillColor: WidgetStateColor.resolveWith((states) {
return Colors.white;
}),
checkColor: WidgetStateProperty.all(AppColors.torPurple),
),
iconTheme: const IconThemeData(color: Colors.white),
),
child: SafeArea(
child: ColoredBox(
color: AppColors.torPurple,
child: Column(
children: [
const SizedBox(height: 16),
const Icon(TorIcons.onionAlt, size: 84),
const SizedBox(height: 16),
Container(
margin: const EdgeInsets.symmetric(horizontal: 24),
decoration: BoxDecoration(
border: Border.all(color: Colors.white),
borderRadius: BorderRadius.circular(16),
),
child: Column(
children: [
SwitchListTile.adaptive(
child: CustomScrollView(
slivers: [
SliverAppBar(
pinned: true,
title: SwitchListTile.adaptive(
inactiveThumbColor: Colors.white,
activeThumbColor: AppColors.torActiveGreen,
thumbIcon: WidgetStateProperty.resolveWith<Icon?>((
@@ -190,7 +170,7 @@ class TorProxyScreen extends HookConsumerWidget {
return null; // Use the default color.
}),
value: torPendingRequest.value ?? torIsRunning,
title: const Text('Tor™ Proxy'),
title: const Text('Tor™ Service'),
secondary: const Icon(MdiIcons.power),
onChanged: torIsBusy
? null
@@ -212,20 +192,36 @@ class TorProxyScreen extends HookConsumerWidget {
}
},
),
bottom: PreferredSize(
preferredSize: const Size.fromHeight(4 + 40 + 8),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
if (torPendingRequest.value != false && torIsBusy)
LinearProgressIndicator(
backgroundColor: AppColors.torBackgroundGrey,
color: AppColors.torActiveGreen,
value: bootstrapProgress / 100,
),
Padding(
padding: const EdgeInsets.only(
right: 16.0,
top: 4.0,
right: 16,
left: 16,
bottom: 16,
bottom: 4,
),
child: SizedBox(
width: double.infinity,
child: FilledButton.icon(
onPressed:
torIsRunning && torIsBootstrapped && !torIsBusy
torIsRunning &&
torIsBootstrapped &&
!torIsBusy
? () async {
await ref
.read(torProxyServiceProvider.notifier)
.read(
torProxyServiceProvider.notifier,
)
.requestNewIdentity();
if (context.mounted) {
@@ -244,17 +240,8 @@ class TorProxyScreen extends HookConsumerWidget {
],
),
),
const SizedBox(height: 16),
Expanded(
child: FadingScroll(
fadingSize: 25,
builder: (context, controller) {
return FadingScroll(
controller: controller,
fadingSize: 25,
builder: (context, controller) {
return ListView(
controller: controller,
),
SliverList.list(
children: [
const SizedBox(height: 16),
const Padding(
@@ -273,13 +260,9 @@ class TorProxyScreen extends HookConsumerWidget {
onChanged: (value) async {
if (value != null) {
await ref
.read(
saveTorSettingsControllerProvider
.notifier,
)
.read(saveTorSettingsControllerProvider.notifier)
.save(
(currentSettings) => currentSettings
.copyWith
(currentSettings) => currentSettings.copyWith
.proxyRegularTabsMode(value),
);
}
@@ -307,13 +290,10 @@ class TorProxyScreen extends HookConsumerWidget {
SwitchListTile.adaptive(
inactiveThumbColor: Colors.white,
activeThumbColor: AppColors.torActiveGreen,
thumbIcon:
WidgetStateProperty.resolveWith<Icon?>((
thumbIcon: WidgetStateProperty.resolveWith<Icon?>((
Set<WidgetState> states,
) {
if (states.contains(
WidgetState.selected,
)) {
if (states.contains(WidgetState.selected)) {
return const Icon(MdiIcons.incognito);
}
return null; // Use the default color.
@@ -326,13 +306,9 @@ class TorProxyScreen extends HookConsumerWidget {
secondary: const Icon(MdiIcons.incognito),
onChanged: (value) async {
await ref
.read(
saveTorSettingsControllerProvider
.notifier,
)
.read(saveTorSettingsControllerProvider.notifier)
.save(
(currentSettings) => currentSettings
.copyWith
(currentSettings) => currentSettings.copyWith
.proxyPrivateTabsTor(value),
);
},
@@ -352,53 +328,38 @@ class TorProxyScreen extends HookConsumerWidget {
SwitchListTile.adaptive(
inactiveThumbColor: Colors.white,
activeThumbColor: AppColors.torActiveGreen,
thumbIcon:
WidgetStateProperty.resolveWith<Icon?>((
thumbIcon: WidgetStateProperty.resolveWith<Icon?>((
Set<WidgetState> states,
) {
if (states.contains(
WidgetState.selected,
)) {
return const Icon(
MdiIcons.arrowDecisionAuto,
);
if (states.contains(WidgetState.selected)) {
return const Icon(MdiIcons.arrowDecisionAuto);
}
return null; // Use the default color.
}),
value:
torSettings.config ==
TorConnectionConfig.auto,
value: torSettings.config == TorConnectionConfig.auto,
title: const Text('Auto Configure Transport'),
subtitle: const Text(
'From some locations, it is necessary to use a pluggable transport to connect to Tor',
),
secondary: const Icon(
MdiIcons.arrowDecisionAuto,
),
secondary: const Icon(MdiIcons.arrowDecisionAuto),
onChanged: torIsBusy
? null
: (value) async {
await ref
.read(
saveTorSettingsControllerProvider
.notifier,
saveTorSettingsControllerProvider.notifier,
)
.save(
(
currentSettings,
) => currentSettings.copyWith
.config(
(currentSettings) =>
currentSettings.copyWith.config(
value
? TorConnectionConfig
.auto
: TorConnectionConfig
.direct,
? TorConnectionConfig.auto
: TorConnectionConfig.direct,
),
);
},
),
if (torSettings.config ==
TorConnectionConfig.auto) ...[
if (torSettings.config == TorConnectionConfig.auto) ...[
SwitchListTile.adaptive(
inactiveThumbColor: Colors.white,
activeThumbColor: AppColors.torActiveGreen,
@@ -416,8 +377,8 @@ class TorProxyScreen extends HookConsumerWidget {
.notifier,
)
.save(
(currentSettings) =>
currentSettings.copyWith
(currentSettings) => currentSettings
.copyWith
.requireBridge(value),
);
},
@@ -432,13 +393,11 @@ class TorProxyScreen extends HookConsumerWidget {
if (value != null) {
await ref
.read(
saveTorSettingsControllerProvider
.notifier,
saveTorSettingsControllerProvider.notifier,
)
.save(
(currentSettings) => currentSettings
.copyWith
.config(value),
(currentSettings) =>
currentSettings.copyWith.config(value),
);
}
},
@@ -485,11 +444,9 @@ class TorProxyScreen extends HookConsumerWidget {
),
CheckboxListTile.adaptive(
value: torSettings.fetchRemoteBridges,
controlAffinity:
ListTileControlAffinity.leading,
controlAffinity: ListTileControlAffinity.leading,
enabled:
torSettings.config !=
TorConnectionConfig.direct,
torSettings.config != TorConnectionConfig.direct,
contentPadding: const EdgeInsets.only(
left: 56,
right: 24,
@@ -504,11 +461,9 @@ class TorProxyScreen extends HookConsumerWidget {
.notifier,
)
.save(
(currentSettings) =>
currentSettings.copyWith
.fetchRemoteBridges(
value,
),
(currentSettings) => currentSettings
.copyWith
.fetchRemoteBridges(value),
);
}
},
@@ -536,29 +491,33 @@ class TorProxyScreen extends HookConsumerWidget {
top: 8.0,
),
child: DropdownMenu(
initialSelection:
torSettings.entryNodeCountry,
enabled: !torIsBusy,
initialSelection: torSettings.entryNodeCountry,
menuHeight: 400,
requestFocusOnTap: true,
label: const Text('Entry Country'),
textStyle: const TextStyle(
color: Colors.white,
),
inputDecorationTheme:
const InputDecorationTheme(
labelStyle: TextStyle(
textStyle: const TextStyle(color: Colors.white),
leadingIcon: torSettings.entryNodeCountry.mapNotNull(
(code) => Padding(
padding: const EdgeInsets.all(8.0),
child: CountryFlag.fromCountryCode(
code,
theme: const EmojiTheme(size: 28),
),
),
),
trailingIcon: const Icon(
MdiIcons.chevronDown,
color: Colors.white,
),
inputDecorationTheme: const InputDecorationTheme(
labelStyle: TextStyle(color: Colors.white),
iconColor: Colors.white,
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.white,
),
borderSide: BorderSide(color: Colors.white),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.white,
),
borderSide: BorderSide(color: Colors.white),
),
),
// menuStyle: MenuStyle(
@@ -570,13 +529,9 @@ class TorProxyScreen extends HookConsumerWidget {
dropdownMenuEntries: countryDropdownEntries,
onSelected: (value) async {
await ref
.read(
saveTorSettingsControllerProvider
.notifier,
)
.read(saveTorSettingsControllerProvider.notifier)
.save(
(currentSettings) => currentSettings
.copyWith
(currentSettings) => currentSettings.copyWith
.entryNodeCountry(value),
);
},
@@ -589,28 +544,33 @@ class TorProxyScreen extends HookConsumerWidget {
top: 8.0,
),
child: DropdownMenu(
enabled: !torIsBusy,
initialSelection: torSettings.exitNodeCountry,
menuHeight: 400,
requestFocusOnTap: true,
label: const Text('Exit Country'),
textStyle: const TextStyle(
color: Colors.white,
),
inputDecorationTheme:
const InputDecorationTheme(
labelStyle: TextStyle(
textStyle: const TextStyle(color: Colors.white),
leadingIcon: torSettings.exitNodeCountry.mapNotNull(
(code) => Padding(
padding: const EdgeInsets.all(8.0),
child: CountryFlag.fromCountryCode(
code,
theme: const EmojiTheme(size: 28),
),
),
),
trailingIcon: const Icon(
MdiIcons.chevronDown,
color: Colors.white,
),
inputDecorationTheme: const InputDecorationTheme(
labelStyle: TextStyle(color: Colors.white),
iconColor: Colors.white,
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.white,
),
borderSide: BorderSide(color: Colors.white),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.white,
),
borderSide: BorderSide(color: Colors.white),
),
),
// menuStyle: MenuStyle(
@@ -622,74 +582,20 @@ class TorProxyScreen extends HookConsumerWidget {
dropdownMenuEntries: countryDropdownEntries,
onSelected: (value) async {
await ref
.read(
saveTorSettingsControllerProvider
.notifier,
)
.read(saveTorSettingsControllerProvider.notifier)
.save(
(currentSettings) => currentSettings
.copyWith
(currentSettings) => currentSettings.copyWith
.exitNodeCountry(value),
);
},
),
),
],
);
},
);
},
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 24.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
if (torPendingRequest.value != false && torIsBusy)
Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: 8),
LinearProgressIndicator(
backgroundColor: AppColors.torBackgroundGrey,
color: AppColors.torActiveGreen,
value: bootstrapProgress / 100,
),
const SizedBox(height: 8),
const Text(
'Establishing connection...',
style: TextStyle(color: Colors.white),
),
],
)
else if (torPendingRequest.value != false &&
torIsRunning &&
torIsBootstrapped)
Padding(
padding: const EdgeInsets.only(top: 24.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
MdiIcons.shieldLockOutline,
color: AppColors.torActiveGreen,
size: 32,
),
const SizedBox(width: 12),
Text(
'Connected to the Tor Network',
style: Theme.of(context).textTheme.titleMedium
?.copyWith(color: AppColors.torActiveGreen),
),
],
),
),
],
),
),
Padding(
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.only(
top: 32.0,
right: 12.0,
left: 12.0,
bottom: 8.0,
@@ -699,6 +605,8 @@ class TorProxyScreen extends HookConsumerWidget {
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodySmall?.copyWith(
fontStyle: FontStyle.italic,
color: Colors.white,
),
),
),
),