added support for tor entry/exit countries
This commit is contained in:
@@ -136,6 +136,8 @@ class TorProxyService extends _$TorProxyService {
|
|||||||
null => TransportType.none,
|
null => TransportType.none,
|
||||||
},
|
},
|
||||||
bridgeLines: setting?.bridge.bridges ?? [],
|
bridgeLines: setting?.bridge.bridges ?? [],
|
||||||
|
entryNodeCountries: torSettings.entryNodeCountry?.toLowerCase(),
|
||||||
|
exitNodeCountries: torSettings.exitNodeCountry?.toLowerCase(),
|
||||||
);
|
);
|
||||||
|
|
||||||
await _tor.start(config);
|
await _tor.start(config);
|
||||||
@@ -155,6 +157,10 @@ class TorProxyService extends _$TorProxyService {
|
|||||||
await _tor.stop();
|
await _tor.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> requestNewIdentity() async {
|
||||||
|
await _tor.requestNewIdentity();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Stream<TorStatus> build() {
|
Stream<TorStatus> build() {
|
||||||
_statusSyncController = StreamController();
|
_statusSyncController = StreamController();
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ final class TorProxyServiceProvider
|
|||||||
TorProxyService create() => TorProxyService();
|
TorProxyService create() => TorProxyService();
|
||||||
}
|
}
|
||||||
|
|
||||||
String _$torProxyServiceHash() => r'be69327bdef8feaa2a43bd83f20168988dcdd228';
|
String _$torProxyServiceHash() => r'0bf00b61bf7e739fe8c77696cf1b2a151d137671';
|
||||||
|
|
||||||
abstract class _$TorProxyService extends $StreamNotifier<TorStatus> {
|
abstract class _$TorProxyService extends $StreamNotifier<TorStatus> {
|
||||||
Stream<TorStatus> build();
|
Stream<TorStatus> build();
|
||||||
|
|||||||
@@ -17,11 +17,14 @@
|
|||||||
* You should have received a copy of the GNU Affero General Public License
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
import 'package:country_codes/country_codes.dart';
|
||||||
|
import 'package:country_flags/country_flags.dart';
|
||||||
import 'package:fading_scroll/fading_scroll.dart';
|
import 'package:fading_scroll/fading_scroll.dart';
|
||||||
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: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:weblibre/core/design/app_colors.dart';
|
import 'package:weblibre/core/design/app_colors.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/tor/domain/services/tor_proxy.dart';
|
import 'package:weblibre/features/tor/domain/services/tor_proxy.dart';
|
||||||
@@ -29,6 +32,7 @@ import 'package:weblibre/features/user/data/models/tor_settings.dart';
|
|||||||
import 'package:weblibre/features/user/domain/repositories/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/hooks/on_initialization.dart';
|
||||||
import 'package:weblibre/presentation/icons/tor_icons.dart';
|
import 'package:weblibre/presentation/icons/tor_icons.dart';
|
||||||
|
import 'package:weblibre/utils/ui_helper.dart';
|
||||||
|
|
||||||
class TorProxyScreen extends HookConsumerWidget {
|
class TorProxyScreen extends HookConsumerWidget {
|
||||||
const TorProxyScreen();
|
const TorProxyScreen();
|
||||||
@@ -89,6 +93,39 @@ class TorProxyScreen extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
final countryDropdownEntries = useMemoized(
|
||||||
|
() => [
|
||||||
|
const DropdownMenuEntry(
|
||||||
|
value: null,
|
||||||
|
label: 'Automatic',
|
||||||
|
leadingIcon: Icon(MdiIcons.lightbulbAuto),
|
||||||
|
),
|
||||||
|
...CountryCodes.countryCodes().map((country) {
|
||||||
|
final label =
|
||||||
|
country.localizedName ??
|
||||||
|
country.name ??
|
||||||
|
country.alpha2Code ??
|
||||||
|
country.countryCode ??
|
||||||
|
'Unnamed Country';
|
||||||
|
|
||||||
|
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),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(title: const Text('Tor™ Proxy')),
|
appBar: AppBar(title: const Text('Tor™ Proxy')),
|
||||||
body: Theme(
|
body: Theme(
|
||||||
@@ -139,7 +176,9 @@ class TorProxyScreen extends HookConsumerWidget {
|
|||||||
border: Border.all(color: Colors.white),
|
border: Border.all(color: Colors.white),
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(16),
|
||||||
),
|
),
|
||||||
child: SwitchListTile.adaptive(
|
child: Column(
|
||||||
|
children: [
|
||||||
|
SwitchListTile.adaptive(
|
||||||
inactiveThumbColor: Colors.white,
|
inactiveThumbColor: Colors.white,
|
||||||
activeThumbColor: AppColors.torActiveGreen,
|
activeThumbColor: AppColors.torActiveGreen,
|
||||||
thumbIcon: WidgetStateProperty.resolveWith<Icon?>((
|
thumbIcon: WidgetStateProperty.resolveWith<Icon?>((
|
||||||
@@ -173,6 +212,37 @@ class TorProxyScreen extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
right: 16.0,
|
||||||
|
left: 16,
|
||||||
|
bottom: 16,
|
||||||
|
),
|
||||||
|
child: SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
child: FilledButton.icon(
|
||||||
|
onPressed:
|
||||||
|
torIsRunning && torIsBootstrapped && !torIsBusy
|
||||||
|
? () async {
|
||||||
|
await ref
|
||||||
|
.read(torProxyServiceProvider.notifier)
|
||||||
|
.requestNewIdentity();
|
||||||
|
|
||||||
|
if (context.mounted) {
|
||||||
|
showInfoMessage(
|
||||||
|
context,
|
||||||
|
'Requesting new Tor identity...',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
icon: const Icon(MdiIcons.refresh),
|
||||||
|
label: const Text('Request New Identity'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Expanded(
|
Expanded(
|
||||||
@@ -447,6 +517,123 @@ class TorProxyScreen extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
const Padding(
|
||||||
|
padding: EdgeInsets.only(left: 24.0),
|
||||||
|
child: Text(
|
||||||
|
'Country Restrictions',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 16.0,
|
||||||
|
right: 24.0,
|
||||||
|
top: 8.0,
|
||||||
|
),
|
||||||
|
child: DropdownMenu(
|
||||||
|
initialSelection:
|
||||||
|
torSettings.entryNodeCountry,
|
||||||
|
menuHeight: 400,
|
||||||
|
requestFocusOnTap: true,
|
||||||
|
label: const Text('Entry Country'),
|
||||||
|
textStyle: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
inputDecorationTheme:
|
||||||
|
const InputDecorationTheme(
|
||||||
|
labelStyle: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
iconColor: Colors.white,
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// menuStyle: MenuStyle(
|
||||||
|
// backgroundColor:
|
||||||
|
// WidgetStateProperty.all<Color>(
|
||||||
|
// AppColors.torPurple,
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
dropdownMenuEntries: countryDropdownEntries,
|
||||||
|
onSelected: (value) async {
|
||||||
|
await ref
|
||||||
|
.read(
|
||||||
|
saveTorSettingsControllerProvider
|
||||||
|
.notifier,
|
||||||
|
)
|
||||||
|
.save(
|
||||||
|
(currentSettings) => currentSettings
|
||||||
|
.copyWith
|
||||||
|
.entryNodeCountry(value),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 16.0,
|
||||||
|
right: 24.0,
|
||||||
|
top: 8.0,
|
||||||
|
),
|
||||||
|
child: DropdownMenu(
|
||||||
|
initialSelection: torSettings.exitNodeCountry,
|
||||||
|
menuHeight: 400,
|
||||||
|
requestFocusOnTap: true,
|
||||||
|
label: const Text('Exit Country'),
|
||||||
|
textStyle: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
inputDecorationTheme:
|
||||||
|
const InputDecorationTheme(
|
||||||
|
labelStyle: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
iconColor: Colors.white,
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// menuStyle: MenuStyle(
|
||||||
|
// backgroundColor:
|
||||||
|
// WidgetStateProperty.all<Color>(
|
||||||
|
// AppColors.torPurple,
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
dropdownMenuEntries: countryDropdownEntries,
|
||||||
|
onSelected: (value) async {
|
||||||
|
await ref
|
||||||
|
.read(
|
||||||
|
saveTorSettingsControllerProvider
|
||||||
|
.notifier,
|
||||||
|
)
|
||||||
|
.save(
|
||||||
|
(currentSettings) => currentSettings
|
||||||
|
.copyWith
|
||||||
|
.exitNodeCountry(value),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -503,8 +690,8 @@ class TorProxyScreen extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(
|
padding: const EdgeInsets.only(
|
||||||
right: 8.0,
|
right: 12.0,
|
||||||
left: 8.0,
|
left: 12.0,
|
||||||
bottom: 8.0,
|
bottom: 8.0,
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ class TorSettings with FastEquatable {
|
|||||||
final TorConnectionConfig config;
|
final TorConnectionConfig config;
|
||||||
final bool requireBridge;
|
final bool requireBridge;
|
||||||
final bool fetchRemoteBridges;
|
final bool fetchRemoteBridges;
|
||||||
|
final String? entryNodeCountry;
|
||||||
|
final String? exitNodeCountry;
|
||||||
|
|
||||||
TorSettings({
|
TorSettings({
|
||||||
required this.proxyRegularTabsMode,
|
required this.proxyRegularTabsMode,
|
||||||
@@ -42,6 +44,8 @@ class TorSettings with FastEquatable {
|
|||||||
required this.config,
|
required this.config,
|
||||||
required this.requireBridge,
|
required this.requireBridge,
|
||||||
required this.fetchRemoteBridges,
|
required this.fetchRemoteBridges,
|
||||||
|
required this.entryNodeCountry,
|
||||||
|
required this.exitNodeCountry,
|
||||||
});
|
});
|
||||||
|
|
||||||
TorSettings.withDefaults({
|
TorSettings.withDefaults({
|
||||||
@@ -50,6 +54,8 @@ class TorSettings with FastEquatable {
|
|||||||
TorConnectionConfig? config,
|
TorConnectionConfig? config,
|
||||||
bool? requireBridge,
|
bool? requireBridge,
|
||||||
bool? fetchRemoteBridges,
|
bool? fetchRemoteBridges,
|
||||||
|
this.entryNodeCountry,
|
||||||
|
this.exitNodeCountry,
|
||||||
}) : proxyRegularTabsMode =
|
}) : proxyRegularTabsMode =
|
||||||
proxyRegularTabsMode ?? TorRegularTabProxyMode.container,
|
proxyRegularTabsMode ?? TorRegularTabProxyMode.container,
|
||||||
proxyPrivateTabsTor = proxyPrivateTabsTor ?? false,
|
proxyPrivateTabsTor = proxyPrivateTabsTor ?? false,
|
||||||
@@ -69,5 +75,7 @@ class TorSettings with FastEquatable {
|
|||||||
config,
|
config,
|
||||||
requireBridge,
|
requireBridge,
|
||||||
fetchRemoteBridges,
|
fetchRemoteBridges,
|
||||||
|
entryNodeCountry,
|
||||||
|
exitNodeCountry,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ abstract class _$TorSettingsCWProxy {
|
|||||||
|
|
||||||
TorSettings fetchRemoteBridges(bool fetchRemoteBridges);
|
TorSettings fetchRemoteBridges(bool fetchRemoteBridges);
|
||||||
|
|
||||||
|
TorSettings entryNodeCountry(String? entryNodeCountry);
|
||||||
|
|
||||||
|
TorSettings exitNodeCountry(String? exitNodeCountry);
|
||||||
|
|
||||||
/// Creates a new instance with the provided field values.
|
/// Creates a new instance with the provided field values.
|
||||||
/// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `TorSettings(...).copyWith.fieldName(value)`.
|
/// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `TorSettings(...).copyWith.fieldName(value)`.
|
||||||
///
|
///
|
||||||
@@ -30,6 +34,8 @@ abstract class _$TorSettingsCWProxy {
|
|||||||
TorConnectionConfig config,
|
TorConnectionConfig config,
|
||||||
bool requireBridge,
|
bool requireBridge,
|
||||||
bool fetchRemoteBridges,
|
bool fetchRemoteBridges,
|
||||||
|
String? entryNodeCountry,
|
||||||
|
String? exitNodeCountry,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,6 +66,14 @@ class _$TorSettingsCWProxyImpl implements _$TorSettingsCWProxy {
|
|||||||
TorSettings fetchRemoteBridges(bool fetchRemoteBridges) =>
|
TorSettings fetchRemoteBridges(bool fetchRemoteBridges) =>
|
||||||
call(fetchRemoteBridges: fetchRemoteBridges);
|
call(fetchRemoteBridges: fetchRemoteBridges);
|
||||||
|
|
||||||
|
@override
|
||||||
|
TorSettings entryNodeCountry(String? entryNodeCountry) =>
|
||||||
|
call(entryNodeCountry: entryNodeCountry);
|
||||||
|
|
||||||
|
@override
|
||||||
|
TorSettings exitNodeCountry(String? exitNodeCountry) =>
|
||||||
|
call(exitNodeCountry: exitNodeCountry);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
/// Creates a new instance with the provided field values.
|
/// Creates a new instance with the provided field values.
|
||||||
/// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `TorSettings(...).copyWith.fieldName(value)`.
|
/// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `TorSettings(...).copyWith.fieldName(value)`.
|
||||||
@@ -74,6 +88,8 @@ class _$TorSettingsCWProxyImpl implements _$TorSettingsCWProxy {
|
|||||||
Object? config = const $CopyWithPlaceholder(),
|
Object? config = const $CopyWithPlaceholder(),
|
||||||
Object? requireBridge = const $CopyWithPlaceholder(),
|
Object? requireBridge = const $CopyWithPlaceholder(),
|
||||||
Object? fetchRemoteBridges = const $CopyWithPlaceholder(),
|
Object? fetchRemoteBridges = const $CopyWithPlaceholder(),
|
||||||
|
Object? entryNodeCountry = const $CopyWithPlaceholder(),
|
||||||
|
Object? exitNodeCountry = const $CopyWithPlaceholder(),
|
||||||
}) {
|
}) {
|
||||||
return TorSettings(
|
return TorSettings(
|
||||||
proxyRegularTabsMode:
|
proxyRegularTabsMode:
|
||||||
@@ -103,6 +119,14 @@ class _$TorSettingsCWProxyImpl implements _$TorSettingsCWProxy {
|
|||||||
? _value.fetchRemoteBridges
|
? _value.fetchRemoteBridges
|
||||||
// ignore: cast_nullable_to_non_nullable
|
// ignore: cast_nullable_to_non_nullable
|
||||||
: fetchRemoteBridges as bool,
|
: fetchRemoteBridges as bool,
|
||||||
|
entryNodeCountry: entryNodeCountry == const $CopyWithPlaceholder()
|
||||||
|
? _value.entryNodeCountry
|
||||||
|
// ignore: cast_nullable_to_non_nullable
|
||||||
|
: entryNodeCountry as String?,
|
||||||
|
exitNodeCountry: exitNodeCountry == const $CopyWithPlaceholder()
|
||||||
|
? _value.exitNodeCountry
|
||||||
|
// ignore: cast_nullable_to_non_nullable
|
||||||
|
: exitNodeCountry as String?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -128,6 +152,8 @@ TorSettings _$TorSettingsFromJson(Map<String, dynamic> json) =>
|
|||||||
config: $enumDecodeNullable(_$TorConnectionConfigEnumMap, json['config']),
|
config: $enumDecodeNullable(_$TorConnectionConfigEnumMap, json['config']),
|
||||||
requireBridge: json['requireBridge'] as bool?,
|
requireBridge: json['requireBridge'] as bool?,
|
||||||
fetchRemoteBridges: json['fetchRemoteBridges'] as bool?,
|
fetchRemoteBridges: json['fetchRemoteBridges'] as bool?,
|
||||||
|
entryNodeCountry: json['entryNodeCountry'] as String?,
|
||||||
|
exitNodeCountry: json['exitNodeCountry'] as String?,
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$TorSettingsToJson(TorSettings instance) =>
|
Map<String, dynamic> _$TorSettingsToJson(TorSettings instance) =>
|
||||||
@@ -138,6 +164,8 @@ Map<String, dynamic> _$TorSettingsToJson(TorSettings instance) =>
|
|||||||
'config': _$TorConnectionConfigEnumMap[instance.config]!,
|
'config': _$TorConnectionConfigEnumMap[instance.config]!,
|
||||||
'requireBridge': instance.requireBridge,
|
'requireBridge': instance.requireBridge,
|
||||||
'fetchRemoteBridges': instance.fetchRemoteBridges,
|
'fetchRemoteBridges': instance.fetchRemoteBridges,
|
||||||
|
'entryNodeCountry': instance.entryNodeCountry,
|
||||||
|
'exitNodeCountry': instance.exitNodeCountry,
|
||||||
};
|
};
|
||||||
|
|
||||||
const _$TorRegularTabProxyModeEnumMap = {
|
const _$TorRegularTabProxyModeEnumMap = {
|
||||||
|
|||||||
@@ -55,6 +55,14 @@ class TorSettingsRepository extends _$TorSettingsRepository {
|
|||||||
DriftSqlType.bool,
|
DriftSqlType.bool,
|
||||||
db.typeMapping,
|
db.typeMapping,
|
||||||
),
|
),
|
||||||
|
'entryNodeCountry': settings['entryNodeCountry']?.readAs(
|
||||||
|
DriftSqlType.string,
|
||||||
|
db.typeMapping,
|
||||||
|
),
|
||||||
|
'exitNodeCountry': settings['exitNodeCountry']?.readAs(
|
||||||
|
DriftSqlType.string,
|
||||||
|
db.typeMapping,
|
||||||
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ final class TorSettingsRepositoryProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
String _$torSettingsRepositoryHash() =>
|
String _$torSettingsRepositoryHash() =>
|
||||||
r'341730ed58b49ab3d721583e0f2d5ac650018918';
|
r'f771f23f17903bd192b24604bab522fb20571ffd';
|
||||||
|
|
||||||
abstract class _$TorSettingsRepository extends $StreamNotifier<TorSettings> {
|
abstract class _$TorSettingsRepository extends $StreamNotifier<TorSettings> {
|
||||||
Stream<TorSettings> build();
|
Stream<TorSettings> build();
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:background_fetch/background_fetch.dart';
|
import 'package:background_fetch/background_fetch.dart';
|
||||||
|
import 'package:country_codes/country_codes.dart';
|
||||||
import 'package:dynamic_color/dynamic_color.dart';
|
import 'package:dynamic_color/dynamic_color.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -74,6 +75,8 @@ class _MainWidget extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
|
|
||||||
useOnInitialization(() async {
|
useOnInitialization(() async {
|
||||||
|
await CountryCodes.init();
|
||||||
|
|
||||||
final engineSettings = await ref
|
final engineSettings = await ref
|
||||||
.read(engineSettingsRepositoryProvider.notifier)
|
.read(engineSettingsRepositoryProvider.notifier)
|
||||||
.fetchSettings();
|
.fetchSettings();
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ dependencies:
|
|||||||
collection: ^1.19.1
|
collection: ^1.19.1
|
||||||
convert: ^3.1.2
|
convert: ^3.1.2
|
||||||
copy_with_extension: ^10.0.1
|
copy_with_extension: ^10.0.1
|
||||||
|
country_codes: ^3.3.0
|
||||||
country_flags: ^4.1.1
|
country_flags: ^4.1.1
|
||||||
cryptography_flutter: ^2.3.4
|
cryptography_flutter: ^2.3.4
|
||||||
drift: ^2.30.0
|
drift: ^2.30.0
|
||||||
|
|||||||
Reference in New Issue
Block a user