tor push
This commit is contained in:
@@ -5,6 +5,7 @@ gradle-wrapper.jar
|
||||
/gradlew.bat
|
||||
/local.properties
|
||||
GeneratedPluginRegistrant.java
|
||||
.cxx
|
||||
|
||||
# Remember to never publicly share your keystore.
|
||||
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_REMOTE_MESSAGING" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
|
||||
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
@@ -176,6 +179,10 @@
|
||||
android:foregroundServiceType="mediaPlayback"
|
||||
android:exported="false" />
|
||||
|
||||
<service
|
||||
android:name="id.flutter.flutter_background_service.BackgroundService"
|
||||
android:foregroundServiceType="specialUse|remoteMessaging" />
|
||||
|
||||
<!-- Don't delete the meta-data below.
|
||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||
<meta-data
|
||||
|
||||
@@ -18,7 +18,7 @@ pluginManagement {
|
||||
|
||||
plugins {
|
||||
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
||||
id "com.android.application" version "8.3.1" apply false
|
||||
id "com.android.application" version "8.7.0" apply false
|
||||
id "org.jetbrains.kotlin.android" version "1.9.22" apply false
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 150.5 90.7" enable-background="new 0 0 150.5 90.7" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#FFFFFF" d="M85.7,71.3c0.1-1.8-0.7-4.6-1.5-7.3c-0.5-2-2.4-4-2.9-5.5c-0.9-2.5-0.5-4.3-0.6-7.8
|
||||
c0.4,3.2,0.4,6,1.2,7.3c0.7,1.3,1.9,3.1,2.6,4.9c1.3,3.3,2.1,6.9,2.2,8.6c0.1,2.9-0.3,7.5-1.3,11c-0.4,1.3-0.9,2.5-1.5,3.6
|
||||
c1.3-1.9,2.2-4,2.6-5.7c1.3-6.1,2.4-12.6,1.5-16.4c-0.2-0.9-0.5-3.8-1.7-7.1c-1.8-4.6-4.2-8.5-4.5-9.4c-0.9-2.6-1.2-9.1-1.3-12.6
|
||||
c0.1,4.2,0.9,10.3,1.5,11.7c0.3,0.9,3,5.2,4.9,9.7c1.3,3,1.6,5.7,1.8,6.5c1,8.4,0.1,11.9-2,19.4c-0.3,1.1-0.8,2.1-1.3,3.1
|
||||
c1.5-1.8,2.6-3.7,3-5.3c2.2-9.4,2.9-21.4-0.1-30.5c-1.5-4.8-5.2-9-6.1-11.9c-1-3.4-0.3-8.9-0.3-9l-8.1-3.6
|
||||
c2.1,5.4,2.5,9.6,0.3,11.4c-8.5,7-22.7,14.9-22.7,26.7c0,12.5,7.4,26,26.5,27c-2.3-0.6-4.5-1.3-6.4-2.1c-1.9-0.8-3.6-1.9-4.9-3.4
|
||||
c0-0.1-0.5-0.5-0.5-0.5c-2.8-3.2-6.3-8.6-7.5-13.7c-0.5-2-0.9-3.6-0.3-5.7c2.3-8.2,7.3-11.5,12.3-14.9c1.1-0.7,2.5-1.4,3.6-2.3
|
||||
c2.1-1.5,3.2-6.3,4.4-10.1c-0.6,3.1-1.3,8.7-4,10.8c-1.1,0.8-2.3,1.6-3.5,2.4c-4.8,3.3-9.7,6.4-11.9,14.3
|
||||
c-0.5,1.7-0.1,3.5,0.3,5.2c1.2,4.9,4.6,10.1,7.3,13.2c0,0.1,0.5,0.5,0.5,0.6c1.8,2.1,2.6,3,7.3,4.3c-1.1-0.6-2-1.3-2.8-1.8
|
||||
c-4.8-3.3-7.2-7.4-7.6-13.5c-0.3-4.7,2.6-9.7,8.3-12.7c3.9-2,5-5.2,6.3-9c-0.9,4.3-1.8,7-5.9,9.4c-4.8,2.8-8.2,8.2-7.9,11.9
|
||||
c0.4,4.5,3.6,9.7,7.1,12.8c1.5,1.3,3.8,2.1,6,2.7c-0.6-0.5-1.2-1.1-1.8-1.9c-0.7-1-1.2-2.4-1.7-3.7c-0.5-1.3-1-2.6-1-3.8
|
||||
c0-2.9,0.3-6.5,2.4-8.9c1.9-2.2,2.5-2.5,3.3-5.1c-0.6,3-1,3.2-2.8,5.7c-2.3,3.2-2.4,6.4-2.1,8.4c0.2,1.3,0.7,2.5,1.1,3.5
|
||||
c0.5,1.3,1.1,2.5,1.9,3.6c0.7,0.8,1,1.4,2.1,1.8c1.1-2,1.8-6.2,1.9-8.6c0.1-2,0.2-4.3-0.1-6.6c-0.4-3.1-1.4-6.1-1.4-8.5
|
||||
c0.5,2.2,1.6,5.2,2.3,8.3c0.5,2.4,0.6,4.8,0.4,6.5c-0.2,2.1-0.3,3.8-0.6,5.6c-0.2,1.2-0.8,2.4-1.7,3.5c2.2-1.7,3.6-4.3,4.2-7.4
|
||||
C85.6,77,85.7,73.3,85.7,71.3z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#FFFFFF" d="M106,53.4c-3.1-5.7-11-11.3-19.3-16.5c-1.9-1.1-2.3-5.3-2-7.1l-1.9-0.9c0,0-0.7,5,0.6,9
|
||||
c0.9,2.8,3.9,6,5.7,10.5c1.3,3,2.6,10.2,2.7,11c1,8.4,0.5,16.3-2,23c-1.1,3.1-3.5,5.9-5.4,7.2c-0.3,0.2-0.6,0.4-0.9,0.6
|
||||
C99.3,90.7,116.7,72.8,106,53.4z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#FFFFFF" d="M102.7,4.3C96.2,6.5,90.4,9.9,86.1,16c2.6-5.3,6.7-10.6,11.3-16c-6.3,4.5-11.7,9.6-15.2,16.4l2.4-9.6
|
||||
c-3.7,6.6-6.2,13.4-7,20.1l4.2,1.8C87.2,20.4,94.1,12.1,102.7,4.3z"/>
|
||||
</g>
|
||||
<g>
|
||||
<polygon fill="#FFFFFF" points="80.1,64.5 80.1,64.5 80.1,64.5 "/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#FFFFFF" d="M61.6,28.6v-16c0-2.1-1.8-3.8-3.8-3.8H3.8C1.7,8.8,0,10.5,0,12.6v16c0,2.1,1.7,3.8,3.8,3.8H16
|
||||
c1.1,0,2.6,0.9,2.6,2.6v52.7c0,1.8,1.3,3.1,3,3.1h18.4c1.7,0,3.1-1.3,3.1-3.1V35.4c0-1.6,1.1-3,3.6-3h11.1
|
||||
C59.9,32.3,61.6,30.6,61.6,28.6z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#FFFFFF" d="M150.5,50.2V33.1c0-1.7-1.4-3.1-3.2-3.1h-4.8c-0.6,0-2.1,0.1-2.5,0.1c-12.1,1.2-21.7,11-22.6,23.2
|
||||
c-0.1,0.5-0.1,1.3-0.1,1.9v32.9c0,1.3,1.3,2.5,2.8,2.5h17.2c1.5,0,2.8-1.1,2.8-2.5V59.2c0-3.1,2.4-5.9,7.7-5.9
|
||||
C150.6,53.3,150.5,52.4,150.5,50.2z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.3 KiB |
@@ -1,3 +1,4 @@
|
||||
description: This file stores settings for Dart & Flutter DevTools.
|
||||
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
|
||||
extensions:
|
||||
- drift: true
|
||||
@@ -21,6 +21,7 @@ import 'package:lensai/features/settings/presentation/screens/settings.dart';
|
||||
import 'package:lensai/features/settings/presentation/screens/web_engine_hardening.dart';
|
||||
import 'package:lensai/features/settings/presentation/screens/web_engine_hardening_group.dart';
|
||||
import 'package:lensai/features/settings/presentation/screens/web_engine_settings.dart';
|
||||
import 'package:lensai/features/tor/presentation/screens/tor_proxy.dart';
|
||||
import 'package:lensai/features/user/presentation/screens/auth.dart';
|
||||
|
||||
part 'routes.g.dart';
|
||||
@@ -326,3 +327,14 @@ class ChatArchiveDetailRoute extends GoRouteData {
|
||||
return ChatArchiveDetailScreen(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TypedGoRoute<TorProxyRoute>(
|
||||
name: 'TorProxyRoute',
|
||||
path: '/tor_proxy',
|
||||
)
|
||||
class TorProxyRoute extends GoRouteData {
|
||||
@override
|
||||
Widget build(BuildContext context, GoRouterState state) {
|
||||
return const TorProxyScreen();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ List<RouteBase> get $appRoutes => [
|
||||
$browserRoute,
|
||||
$settingsRoute,
|
||||
$chatArchiveListRoute,
|
||||
$torProxyRoute,
|
||||
];
|
||||
|
||||
RouteBase get $browserRoute => GoRouteData.$route(
|
||||
@@ -523,3 +524,26 @@ extension $ChatArchiveDetailRouteExtension on ChatArchiveDetailRoute {
|
||||
|
||||
void replace(BuildContext context) => context.replace(location);
|
||||
}
|
||||
|
||||
RouteBase get $torProxyRoute => GoRouteData.$route(
|
||||
path: '/tor_proxy',
|
||||
name: 'TorProxyRoute',
|
||||
factory: $TorProxyRouteExtension._fromState,
|
||||
);
|
||||
|
||||
extension $TorProxyRouteExtension on TorProxyRoute {
|
||||
static TorProxyRoute _fromState(GoRouterState state) => TorProxyRoute();
|
||||
|
||||
String get location => GoRouteData.$location(
|
||||
'/tor_proxy',
|
||||
);
|
||||
|
||||
void go(BuildContext context) => context.go(location);
|
||||
|
||||
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
|
||||
|
||||
void pushReplacement(BuildContext context) =>
|
||||
context.pushReplacement(location);
|
||||
|
||||
void replace(BuildContext context) => context.replace(location);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ class ColorJsonConverter implements JsonConverter<Color, int> {
|
||||
|
||||
@override
|
||||
int toJson(Color value) {
|
||||
return value.value;
|
||||
return value.toARGB32();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,6 @@ class ColorConverter extends TypeConverter<Color, int> {
|
||||
|
||||
@override
|
||||
int toSql(Color value) {
|
||||
return value.value;
|
||||
return value.toARGB32();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1553,7 +1553,8 @@ class $BangTableTableManager extends RootTableManager<
|
||||
getPrefetchedDataCallback: (items) async {
|
||||
return [
|
||||
if (bangFrequencyRefs)
|
||||
await $_getPrefetchedData(
|
||||
await $_getPrefetchedData<Bang, BangTable,
|
||||
BangFrequencyData>(
|
||||
currentTable: table,
|
||||
referencedTable:
|
||||
$BangTableReferences._bangFrequencyRefsTable(db),
|
||||
@@ -1565,7 +1566,7 @@ class $BangTableTableManager extends RootTableManager<
|
||||
.where((e) => e.trigger == item.trigger),
|
||||
typedResults: items),
|
||||
if (bangHistoryRefs)
|
||||
await $_getPrefetchedData(
|
||||
await $_getPrefetchedData<Bang, BangTable, BangHistoryData>(
|
||||
currentTable: table,
|
||||
referencedTable:
|
||||
$BangTableReferences._bangHistoryRefsTable(db),
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ import 'package:lensai/features/user/domain/repositories/engine_settings.dart';
|
||||
import 'package:lensai/features/user/domain/repositories/general_settings.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'engine_settings.g.dart';
|
||||
part 'engine_settings_replication.g.dart';
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
class EngineSettingsReplicationService
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'engine_settings.dart';
|
||||
part of 'engine_settings_replication.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
||||
import 'package:lensai/data/models/equatable_iterable.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/domain/providers.dart';
|
||||
import 'package:lensai/features/tor/domain/services/tor_proxy.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'proxy_settings_replucation.g.dart';
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
class ProxySettingsReplucation extends _$ProxySettingsReplucation {
|
||||
final _service = GeckoContainerProxyService();
|
||||
|
||||
@override
|
||||
void build() {
|
||||
ref.listen(
|
||||
torProxyServiceProvider.select((data) => data.valueOrNull),
|
||||
(previous, next) async {
|
||||
if (next != null) {
|
||||
await _service.setProxyPort(next);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
ref.listen(
|
||||
containersWithCountProvider.select(
|
||||
(value) => EquatableCollection(
|
||||
value.valueOrNull
|
||||
?.where((container) => container.metadata.useProxy)
|
||||
.map((container) => container.metadata.contextualIdentity)
|
||||
.nonNulls
|
||||
.toList(),
|
||||
immutable: true,
|
||||
),
|
||||
),
|
||||
(previous, next) async {
|
||||
if (next.collection != null) {
|
||||
for (final contextId in next.collection!) {
|
||||
await _service.addContainerProxy(contextId);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'proxy_settings_replucation.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$proxySettingsReplucationHash() =>
|
||||
r'08cb19e895056d4ddbf0399b23a9d159a0c86915';
|
||||
|
||||
/// See also [ProxySettingsReplucation].
|
||||
@ProviderFor(ProxySettingsReplucation)
|
||||
final proxySettingsReplucationProvider =
|
||||
NotifierProvider<ProxySettingsReplucation, void>.internal(
|
||||
ProxySettingsReplucation.new,
|
||||
name: r'proxySettingsReplucationProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$proxySettingsReplucationHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$ProxySettingsReplucation = Notifier<void>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
||||
@@ -33,6 +33,7 @@ import 'package:lensai/features/kagi/data/entities/modes.dart';
|
||||
import 'package:lensai/presentation/hooks/draggable_scrollable_controller.dart';
|
||||
import 'package:lensai/presentation/hooks/menu_controller.dart';
|
||||
import 'package:lensai/presentation/hooks/overlay_portal_controller.dart';
|
||||
import 'package:lensai/presentation/icons/tor_icons.dart';
|
||||
import 'package:lensai/utils/ui_helper.dart' as ui_helper;
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
|
||||
@@ -418,6 +419,13 @@ class BrowserScreen extends HookConsumerWidget {
|
||||
);
|
||||
},
|
||||
),
|
||||
MenuItemButton(
|
||||
onPressed: () async {
|
||||
await context.push(TorProxyRoute().location);
|
||||
},
|
||||
leadingIcon: const Icon(TorIcons.onionAlt),
|
||||
child: const Text('Tor'),
|
||||
),
|
||||
const Divider(),
|
||||
MenuItemButton(
|
||||
onPressed: () async {
|
||||
|
||||
@@ -10,7 +10,8 @@ import 'package:lensai/features/geckoview/domain/providers/tab_state.dart';
|
||||
import 'package:lensai/features/geckoview/domain/providers/web_extensions_state.dart';
|
||||
import 'package:lensai/features/geckoview/domain/repositories/tab.dart';
|
||||
import 'package:lensai/features/geckoview/features/browser/domain/services/delete_browser_data.dart';
|
||||
import 'package:lensai/features/geckoview/features/browser/domain/services/engine_settings.dart';
|
||||
import 'package:lensai/features/geckoview/features/browser/domain/services/engine_settings_replication.dart';
|
||||
import 'package:lensai/features/geckoview/features/browser/domain/services/proxy_settings_replucation.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/features/vector_store/domain/repositories/document.dart';
|
||||
import 'package:lensai/features/user/domain/repositories/cache.dart';
|
||||
import 'package:lensai/features/user/domain/repositories/general_settings.dart';
|
||||
@@ -146,6 +147,11 @@ class _BrowserViewState extends ConsumerState<BrowserView>
|
||||
engineSettingsReplicationServiceProvider,
|
||||
(previous, next) {},
|
||||
);
|
||||
|
||||
ref.listenManual(
|
||||
proxySettingsReplucationProvider,
|
||||
(previous, next) {},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
+9
-1
@@ -21,6 +21,7 @@ import 'package:lensai/features/geckoview/features/tabs/domain/repositories/cont
|
||||
import 'package:lensai/features/geckoview/features/tabs/domain/repositories/tab.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/domain/repositories/tab_search.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/presentation/widgets/container_chips.dart';
|
||||
import 'package:lensai/features/tor/presentation/controllers/start_tor_proxy.dart';
|
||||
import 'package:lensai/presentation/hooks/listenable_callback.dart';
|
||||
import 'package:lensai/presentation/widgets/speech_to_text_button.dart';
|
||||
|
||||
@@ -181,9 +182,16 @@ class _Tab extends HookConsumerWidget {
|
||||
return ContainerChips(
|
||||
selectedContainer: selectedContainer,
|
||||
onSelected: (container) async {
|
||||
await ref
|
||||
final result = await ref
|
||||
.read(selectedContainerProvider.notifier)
|
||||
.setContainerId(container.id);
|
||||
|
||||
if (context.mounted &&
|
||||
result == SetContainerResult.successHasProxy) {
|
||||
await ref
|
||||
.read(startProxyControllerProvider.notifier)
|
||||
.maybeStartProxy(context);
|
||||
}
|
||||
},
|
||||
onDeleted: (container) async {
|
||||
ref
|
||||
|
||||
@@ -2005,7 +2005,8 @@ class $ContainerTableManager extends RootTableManager<
|
||||
getPrefetchedDataCallback: (items) async {
|
||||
return [
|
||||
if (tabRefs)
|
||||
await $_getPrefetchedData(
|
||||
await $_getPrefetchedData<ContainerData, Container,
|
||||
TabData>(
|
||||
currentTable: table,
|
||||
referencedTable: $ContainerReferences._tabRefsTable(db),
|
||||
managerFromTypedResult: (p0) =>
|
||||
|
||||
@@ -53,20 +53,26 @@ class ContainerMetadata with FastEquatable {
|
||||
final String? contextualIdentity;
|
||||
final ContainerAuthSettings authSettings;
|
||||
|
||||
@JsonKey(defaultValue: false)
|
||||
final bool useProxy;
|
||||
|
||||
ContainerMetadata({
|
||||
required this.iconData,
|
||||
required this.contextualIdentity,
|
||||
required this.authSettings,
|
||||
required this.useProxy,
|
||||
});
|
||||
|
||||
ContainerMetadata.withDefaults({
|
||||
IconData? iconData,
|
||||
String? contextualIdentity,
|
||||
ContainerAuthSettings? authSettings,
|
||||
bool? useProxy,
|
||||
}) : this(
|
||||
iconData: iconData,
|
||||
contextualIdentity: contextualIdentity,
|
||||
authSettings: authSettings ?? ContainerAuthSettings.withDefaults(),
|
||||
useProxy: useProxy ?? false,
|
||||
);
|
||||
|
||||
factory ContainerMetadata.fromJson(Map<String, dynamic> json) =>
|
||||
@@ -79,6 +85,7 @@ class ContainerMetadata with FastEquatable {
|
||||
iconData,
|
||||
contextualIdentity,
|
||||
authSettings,
|
||||
useProxy,
|
||||
];
|
||||
|
||||
@override
|
||||
|
||||
@@ -90,6 +90,8 @@ abstract class _$ContainerMetadataCWProxy {
|
||||
|
||||
ContainerMetadata authSettings(ContainerAuthSettings authSettings);
|
||||
|
||||
ContainerMetadata useProxy(bool useProxy);
|
||||
|
||||
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `ContainerMetadata(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
|
||||
///
|
||||
/// Usage
|
||||
@@ -100,6 +102,7 @@ abstract class _$ContainerMetadataCWProxy {
|
||||
IconData? iconData,
|
||||
String? contextualIdentity,
|
||||
ContainerAuthSettings authSettings,
|
||||
bool useProxy,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -120,6 +123,9 @@ class _$ContainerMetadataCWProxyImpl implements _$ContainerMetadataCWProxy {
|
||||
ContainerMetadata authSettings(ContainerAuthSettings authSettings) =>
|
||||
this(authSettings: authSettings);
|
||||
|
||||
@override
|
||||
ContainerMetadata useProxy(bool useProxy) => this(useProxy: useProxy);
|
||||
|
||||
@override
|
||||
|
||||
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `ContainerMetadata(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
|
||||
@@ -132,6 +138,7 @@ class _$ContainerMetadataCWProxyImpl implements _$ContainerMetadataCWProxy {
|
||||
Object? iconData = const $CopyWithPlaceholder(),
|
||||
Object? contextualIdentity = const $CopyWithPlaceholder(),
|
||||
Object? authSettings = const $CopyWithPlaceholder(),
|
||||
Object? useProxy = const $CopyWithPlaceholder(),
|
||||
}) {
|
||||
return ContainerMetadata(
|
||||
iconData: iconData == const $CopyWithPlaceholder()
|
||||
@@ -146,6 +153,10 @@ class _$ContainerMetadataCWProxyImpl implements _$ContainerMetadataCWProxy {
|
||||
? _value.authSettings
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: authSettings as ContainerAuthSettings,
|
||||
useProxy: useProxy == const $CopyWithPlaceholder()
|
||||
? _value.useProxy
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: useProxy as bool,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -271,6 +282,7 @@ ContainerMetadata _$ContainerMetadataFromJson(Map<String, dynamic> json) =>
|
||||
? null
|
||||
: ContainerAuthSettings.fromJson(
|
||||
json['authSettings'] as Map<String, dynamic>),
|
||||
useProxy: json['useProxy'] as bool? ?? false,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$ContainerMetadataToJson(ContainerMetadata instance) =>
|
||||
@@ -279,6 +291,7 @@ Map<String, dynamic> _$ContainerMetadataToJson(ContainerMetadata instance) =>
|
||||
instance.iconData, const IconDataJsonConverter().toJson),
|
||||
'contextualIdentity': instance.contextualIdentity,
|
||||
'authSettings': instance.authSettings,
|
||||
'useProxy': instance.useProxy,
|
||||
};
|
||||
|
||||
Value? _$JsonConverterFromJson<Json, Value>(
|
||||
|
||||
@@ -8,6 +8,12 @@ import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'selected_container.g.dart';
|
||||
|
||||
enum SetContainerResult {
|
||||
failed,
|
||||
success,
|
||||
successHasProxy,
|
||||
}
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
class SelectedContainer extends _$SelectedContainer {
|
||||
Future<ContainerData?> fetchData() async {
|
||||
@@ -20,12 +26,13 @@ class SelectedContainer extends _$SelectedContainer {
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<void> setContainerId(String id) async {
|
||||
Future<SetContainerResult> setContainerId(String id) async {
|
||||
final container = await ref
|
||||
.read(containerRepositoryProvider.notifier)
|
||||
.getContainerData(id);
|
||||
|
||||
if (container != null) {
|
||||
var passAuth = false;
|
||||
if (container.metadata.authSettings.authenticationRequired) {
|
||||
final authResult = await ref
|
||||
.read(localAuthenticationServiceProvider.notifier)
|
||||
@@ -38,20 +45,24 @@ class SelectedContainer extends _$SelectedContainer {
|
||||
);
|
||||
|
||||
if (authResult) {
|
||||
state = id;
|
||||
passAuth = true;
|
||||
}
|
||||
} else {
|
||||
passAuth = true;
|
||||
}
|
||||
|
||||
if (passAuth) {
|
||||
state = id;
|
||||
|
||||
if (container.metadata.useProxy) {
|
||||
return SetContainerResult.successHasProxy;
|
||||
}
|
||||
|
||||
return SetContainerResult.success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> toggleContainer(String id) async {
|
||||
if (state == id) {
|
||||
clearContainer();
|
||||
} else {
|
||||
await setContainerId(id);
|
||||
}
|
||||
return SetContainerResult.failed;
|
||||
}
|
||||
|
||||
void clearContainer() {
|
||||
|
||||
@@ -25,7 +25,7 @@ final selectedContainerDataProvider =
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
typedef SelectedContainerDataRef = AutoDisposeStreamProviderRef<ContainerData?>;
|
||||
String _$selectedContainerHash() => r'753f30646c4992981ec538dcfd33fa23aa1f673d';
|
||||
String _$selectedContainerHash() => r'f3dab491337396b8d56daed37a0d85c85aa8d64a';
|
||||
|
||||
/// See also [SelectedContainer].
|
||||
@ProviderFor(SelectedContainer)
|
||||
|
||||
+2
-3
@@ -1,5 +1,4 @@
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:cross_cache/cross_cache.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_chat_core/flutter_chat_core.dart';
|
||||
import 'package:flutter_chat_ui/flutter_chat_ui.dart';
|
||||
@@ -23,7 +22,7 @@ class TabQaChat extends HookConsumerWidget {
|
||||
final chatBackend = ref.watch(chatBackendProvider(chatId).notifier);
|
||||
final chatController = ref.watch(chatControllerProvider(chatId));
|
||||
|
||||
final crossCache = useMemoized(() => CrossCache());
|
||||
// final crossCache = useMemoized(() => CrossCache());
|
||||
final chatScrollController = scrollController ?? useScrollController();
|
||||
|
||||
useOnInitialization(() async {
|
||||
@@ -59,7 +58,7 @@ class TabQaChat extends HookConsumerWidget {
|
||||
),
|
||||
),
|
||||
chatController: chatController,
|
||||
crossCache: crossCache,
|
||||
// crossCache: crossCache,
|
||||
scrollController: chatScrollController,
|
||||
onMessageSend: (text) async {
|
||||
await chatBackend.processQAMessage(text);
|
||||
|
||||
@@ -4,11 +4,11 @@ import 'package:flutter_material_design_icons/flutter_material_design_icons.dart
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:lensai/core/uuid.dart';
|
||||
import 'package:lensai/extensions/nullable.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/data/models/container_data.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/domain/repositories/container.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/presentation/widgets/color_picker_dialog.dart';
|
||||
import 'package:lensai/features/user/domain/services/local_authentication.dart';
|
||||
import 'package:lensai/presentation/icons/tor_icons.dart';
|
||||
|
||||
enum _DialogMode { create, edit }
|
||||
|
||||
@@ -66,6 +66,7 @@ class ContainerEditScreen extends HookConsumerWidget {
|
||||
final contextualIdentity =
|
||||
useState(initialContainer.metadata.contextualIdentity);
|
||||
final authSettings = useState(initialContainer.metadata.authSettings);
|
||||
final useProxy = useState(initialContainer.metadata.useProxy);
|
||||
|
||||
final textController =
|
||||
useTextEditingController(text: initialContainer.name);
|
||||
@@ -85,17 +86,11 @@ class ContainerEditScreen extends HookConsumerWidget {
|
||||
final container = initialContainer.copyWith(
|
||||
name: name.isNotEmpty ? name : null,
|
||||
color: selectedColor.value,
|
||||
metadata: initialContainer.metadata.mapNotNull(
|
||||
(metadata) => metadata.copyWith(
|
||||
contextualIdentity: contextualIdentity.value,
|
||||
authSettings: authSettings.value,
|
||||
),
|
||||
) ??
|
||||
ContainerMetadata(
|
||||
iconData: null,
|
||||
contextualIdentity: contextualIdentity.value,
|
||||
authSettings: authSettings.value,
|
||||
),
|
||||
metadata: initialContainer.metadata.copyWith(
|
||||
contextualIdentity: contextualIdentity.value,
|
||||
authSettings: authSettings.value,
|
||||
useProxy: useProxy.value && contextualIdentity.value != null,
|
||||
),
|
||||
);
|
||||
|
||||
//Check for permissions, when auth is set or getting set
|
||||
@@ -187,9 +182,35 @@ class ContainerEditScreen extends HookConsumerWidget {
|
||||
.metadata.contextualIdentity ??
|
||||
uuid.v4()
|
||||
: null;
|
||||
|
||||
if (!value && useProxy.value) {
|
||||
useProxy.value = false;
|
||||
}
|
||||
}
|
||||
: null,
|
||||
),
|
||||
SwitchListTile.adaptive(
|
||||
value: useProxy.value,
|
||||
title: const Text('Use Tor Proxy'),
|
||||
secondary: const Icon(TorIcons.onionAlt),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
onChanged: switch (_mode) {
|
||||
_DialogMode.create => (value) {
|
||||
if (value && contextualIdentity.value == null) {
|
||||
contextualIdentity.value = initialContainer
|
||||
.metadata.contextualIdentity ??
|
||||
uuid.v4();
|
||||
}
|
||||
|
||||
useProxy.value = value;
|
||||
},
|
||||
_DialogMode.edit => (contextualIdentity.value != null)
|
||||
? (value) {
|
||||
useProxy.value = value;
|
||||
}
|
||||
: null,
|
||||
},
|
||||
),
|
||||
SwitchListTile.adaptive(
|
||||
value: authSettings.value.authenticationRequired,
|
||||
title: const Text('Require Authentication'),
|
||||
|
||||
@@ -10,6 +10,7 @@ import 'package:lensai/features/geckoview/features/tabs/domain/providers.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/domain/providers/selected_container.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/domain/repositories/container.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/presentation/widgets/container_list_tile.dart';
|
||||
import 'package:lensai/features/tor/presentation/controllers/start_tor_proxy.dart';
|
||||
import 'package:skeletonizer/skeletonizer.dart';
|
||||
|
||||
class ContainerListScreen extends HookConsumerWidget {
|
||||
@@ -62,9 +63,23 @@ class ContainerListScreen extends HookConsumerWidget {
|
||||
container,
|
||||
isSelected: container.id == selectedContainer,
|
||||
onTap: () async {
|
||||
await ref
|
||||
.read(selectedContainerProvider.notifier)
|
||||
.toggleContainer(container.id);
|
||||
if (container.id == selectedContainer) {
|
||||
ref
|
||||
.read(selectedContainerProvider.notifier)
|
||||
.clearContainer();
|
||||
} else {
|
||||
final result = await ref
|
||||
.read(selectedContainerProvider.notifier)
|
||||
.setContainerId(container.id);
|
||||
|
||||
if (context.mounted &&
|
||||
result ==
|
||||
SetContainerResult.successHasProxy) {
|
||||
await ref
|
||||
.read(startProxyControllerProvider.notifier)
|
||||
.maybeStartProxy(context);
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ class _MaterialPickerState extends State<MaterialPicker> {
|
||||
void initState() {
|
||||
for (final colors in colorTypes) {
|
||||
shadingTypes(colors).forEach((Map<Color, String> color) {
|
||||
if (widget.pickerColor.value == color.keys.first.value) {
|
||||
if (widget.pickerColor.toARGB32() == color.keys.first.toARGB32()) {
|
||||
return setState(() {
|
||||
_currentColorType = colors;
|
||||
_currentShading = color.keys.first;
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter_background_service/flutter_background_service.dart';
|
||||
import 'package:lensai/features/tor/utils/tor_entrypoint.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
|
||||
part 'tor_proxy.g.dart';
|
||||
|
||||
class _TorService {
|
||||
final service = FlutterBackgroundService();
|
||||
|
||||
final _portSubject = BehaviorSubject<int?>.seeded(null);
|
||||
|
||||
ValueStream<int?> get portStream => _portSubject.stream;
|
||||
|
||||
Future<void> start() async {
|
||||
await service.startService();
|
||||
}
|
||||
|
||||
Future<void> stop() async {
|
||||
service.invoke("stop");
|
||||
}
|
||||
|
||||
Future<void> initializeService() async {
|
||||
await service.configure(
|
||||
iosConfiguration: IosConfiguration(
|
||||
autoStart: false,
|
||||
),
|
||||
androidConfiguration: AndroidConfiguration(
|
||||
autoStart: false,
|
||||
onStart: onStart,
|
||||
isForegroundMode: true,
|
||||
autoStartOnBoot: false,
|
||||
),
|
||||
);
|
||||
|
||||
service.on('portUpdate').listen((data) {
|
||||
final port = data!['port'] as int;
|
||||
_portSubject.add((port > -1) ? port : null);
|
||||
});
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
unawaited(stop());
|
||||
unawaited(_portSubject.close());
|
||||
}
|
||||
}
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
class TorProxyService extends _$TorProxyService {
|
||||
final _tor = _TorService();
|
||||
|
||||
Future<void> connect({bool forceReconnect = false}) async {
|
||||
final currentPort = _tor.portStream.valueOrNull;
|
||||
|
||||
if (currentPort == null || forceReconnect) {
|
||||
state = const AsyncLoading();
|
||||
await _tor.start();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> disconnect() async {
|
||||
state = const AsyncLoading();
|
||||
await _tor.stop();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int?> build() async {
|
||||
final portSub = _tor.portStream.distinct().listen(
|
||||
(port) {
|
||||
state = AsyncData(port);
|
||||
},
|
||||
);
|
||||
|
||||
await _tor.initializeService();
|
||||
|
||||
ref.onDispose(() {
|
||||
unawaited(portSub.cancel());
|
||||
_tor.dispose();
|
||||
});
|
||||
|
||||
return _tor.portStream.valueOrNull;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'tor_proxy.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$torProxyServiceHash() => r'85ed8e3b49007aa56e6b82ff802a3e0ce567a0c8';
|
||||
|
||||
/// See also [TorProxyService].
|
||||
@ProviderFor(TorProxyService)
|
||||
final torProxyServiceProvider =
|
||||
AsyncNotifierProvider<TorProxyService, int?>.internal(
|
||||
TorProxyService.new,
|
||||
name: r'torProxyServiceProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$torProxyServiceHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$TorProxyService = AsyncNotifier<int?>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
||||
@@ -0,0 +1,42 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lensai/features/geckoview/domain/controllers/overlay_dialog.dart';
|
||||
import 'package:lensai/features/tor/domain/services/tor_proxy.dart';
|
||||
import 'package:lensai/features/tor/presentation/widgets/tor_dialog.dart';
|
||||
import 'package:lensai/features/tor/presentation/widgets/tor_notification.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'start_tor_proxy.g.dart';
|
||||
|
||||
@Riverpod()
|
||||
class StartProxyController extends _$StartProxyController {
|
||||
// ignore: avoid_build_context_in_providers
|
||||
Future<void> maybeStartProxy(BuildContext context) async {
|
||||
final torProxyRunning = ref.read(torProxyServiceProvider);
|
||||
|
||||
if (torProxyRunning.valueOrNull == null) {
|
||||
final result = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return TorDialog();
|
||||
},
|
||||
);
|
||||
|
||||
if (result == true) {
|
||||
final connection = ref.read(torProxyServiceProvider.notifier).connect();
|
||||
|
||||
ref.read(overlayDialogControllerProvider.notifier).show(
|
||||
Positioned(
|
||||
top: 0,
|
||||
left: 0,
|
||||
child: TorNotification(),
|
||||
),
|
||||
);
|
||||
|
||||
await connection;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void build() {}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'start_tor_proxy.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$startProxyControllerHash() =>
|
||||
r'c49d4f75bb499b3e695f1c4d08dcc095cfee7143';
|
||||
|
||||
/// See also [StartProxyController].
|
||||
@ProviderFor(StartProxyController)
|
||||
final startProxyControllerProvider =
|
||||
AutoDisposeNotifierProvider<StartProxyController, void>.internal(
|
||||
StartProxyController.new,
|
||||
name: r'startProxyControllerProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$startProxyControllerHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$StartProxyController = AutoDisposeNotifier<void>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
||||
@@ -0,0 +1,132 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:lensai/features/tor/domain/services/tor_proxy.dart';
|
||||
|
||||
class TorProxyScreen extends HookConsumerWidget {
|
||||
const TorProxyScreen();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final torProxyPort = ref.watch(torProxyServiceProvider);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Tor Proxy'),
|
||||
),
|
||||
body: ColoredBox(
|
||||
color: const Color(0xFF7D4698),
|
||||
child: Column(
|
||||
children: [
|
||||
Flexible(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
'assets/images/tor_white.svg',
|
||||
width: 200,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
ListTileTheme(
|
||||
iconColor: Colors.white,
|
||||
textColor: Colors.white,
|
||||
child: SwitchListTile.adaptive(
|
||||
inactiveThumbColor: Colors.white,
|
||||
activeColor: const Color(0xFF68B030),
|
||||
trackColor: WidgetStateProperty.resolveWith<Color?>(
|
||||
(Set<WidgetState> states) {
|
||||
if (states.isEmpty) {
|
||||
return const Color(0xFF333A41);
|
||||
}
|
||||
return null; // Use the default color.
|
||||
}),
|
||||
trackOutlineColor:
|
||||
WidgetStateProperty.resolveWith<Color?>(
|
||||
(Set<WidgetState> states) {
|
||||
if (states.isEmpty) {
|
||||
return Colors.white;
|
||||
}
|
||||
return null; // Use the default color.
|
||||
}),
|
||||
thumbIcon: WidgetStateProperty.resolveWith<Icon?>(
|
||||
(Set<WidgetState> states) {
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
return const Icon(MdiIcons.axisArrowLock);
|
||||
}
|
||||
return null; // Use the default color.
|
||||
}),
|
||||
value: torProxyPort.valueOrNull != null,
|
||||
title: const Text('Tor Proxy'),
|
||||
secondary: const Icon(MdiIcons.power),
|
||||
onChanged: torProxyPort.isLoading
|
||||
? null
|
||||
: (value) async {
|
||||
if (value) {
|
||||
await ref
|
||||
.read(torProxyServiceProvider.notifier)
|
||||
.connect();
|
||||
} else {
|
||||
await ref
|
||||
.read(torProxyServiceProvider.notifier)
|
||||
.disconnect();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: Column(
|
||||
children: [
|
||||
if (torProxyPort.isLoading)
|
||||
const Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
LinearProgressIndicator(
|
||||
backgroundColor: Color(0xFF333A41),
|
||||
color: Color(0xFF68B030),
|
||||
),
|
||||
SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
Text(
|
||||
'Establishing connection...',
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (torProxyPort.valueOrNull != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 24.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(
|
||||
MdiIcons.shieldLockOutline,
|
||||
color: Color(0xFF68B030),
|
||||
size: 32,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
'Connected to the Tor Network',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.titleMedium
|
||||
?.copyWith(color: const Color(0xFF68B030)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:lensai/presentation/icons/tor_icons.dart';
|
||||
|
||||
class TorDialog extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
icon: const Icon(
|
||||
TorIcons.onionAlt,
|
||||
color: Color(0xFF7D4698),
|
||||
),
|
||||
title: const Text('Tor Proxy'),
|
||||
content: const Text(
|
||||
'This container requires a Tor proxy for secure connections, which is not currently running.',
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
context.pop(false);
|
||||
},
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
context.pop(true);
|
||||
},
|
||||
child: const Text('Enable'),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:lensai/features/geckoview/domain/controllers/overlay_dialog.dart';
|
||||
import 'package:lensai/features/tor/domain/services/tor_proxy.dart';
|
||||
import 'package:lensai/presentation/icons/tor_icons.dart';
|
||||
import 'package:lensai/presentation/widgets/animate_gradient_shader.dart';
|
||||
|
||||
class TorNotification extends HookConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
ref.listen(
|
||||
torProxyServiceProvider,
|
||||
(previous, next) {
|
||||
if (!next.isLoading) {
|
||||
ref.read(overlayDialogControllerProvider.notifier).dismiss();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return SafeArea(
|
||||
child: ColoredBox(
|
||||
color: const Color(0xFF7D4698),
|
||||
child: SizedBox(
|
||||
height: 56,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
children: [
|
||||
AnimateGradientShader(
|
||||
duration: const Duration(milliseconds: 500),
|
||||
primaryEnd: Alignment.bottomLeft,
|
||||
secondaryEnd: Alignment.topRight,
|
||||
primaryColors: const [Color(0xFF68B030), Color(0xFF68B030)],
|
||||
secondaryColors: const [Colors.white, Colors.white],
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 12),
|
||||
child: Icon(TorIcons.onionAlt),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Tor Proxy is connecting...',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(color: Colors.white),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
ref
|
||||
.read(overlayDialogControllerProvider.notifier)
|
||||
.dismiss();
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.close,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import 'package:flutter_background_service/flutter_background_service.dart';
|
||||
import 'package:tor/tor.dart';
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
void onStart(ServiceInstance service) async {
|
||||
await Tor.init();
|
||||
|
||||
final portSub = Tor.instance.events.stream.listen((port) {
|
||||
service.invoke('portUpdate', {'port': port});
|
||||
});
|
||||
|
||||
await Tor.instance.start();
|
||||
|
||||
service.on("stop").listen((event) async {
|
||||
Tor.instance.stop();
|
||||
service.invoke('portUpdate', {'port': -1});
|
||||
|
||||
await portSub.cancel();
|
||||
|
||||
await service.stopSelf();
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
enum TorIcons implements IconData {
|
||||
authority(0xf101),
|
||||
badexit(0xf102),
|
||||
bridge(0xf103),
|
||||
country(0xf104),
|
||||
directory(0xf105),
|
||||
exit(0xf106),
|
||||
experimental(0xf107),
|
||||
fallbackdir(0xf108),
|
||||
fast(0xf109),
|
||||
fingerprint(0xf10a),
|
||||
guard(0xf10b),
|
||||
hibernating(0xf10c),
|
||||
hsdir(0xf10d),
|
||||
ipv4(0xf10e),
|
||||
ipv6exit(0xf10f),
|
||||
ipv6(0xf110),
|
||||
noedconsensus(0xf111),
|
||||
notrecommended(0xf112),
|
||||
onionAlt(0xf113),
|
||||
onion(0xf114),
|
||||
outdated(0xf115),
|
||||
reachableipv4(0xf116),
|
||||
reachableipv6(0xf117),
|
||||
relay(0xf118),
|
||||
running(0xf119),
|
||||
stable(0xf11a),
|
||||
tshirt(0xf11b),
|
||||
unmeasured(0xf11c),
|
||||
unreachableipv4(0xf11d),
|
||||
unreachableipv6(0xf11e),
|
||||
v2dir(0xf11f),
|
||||
valid(0xf120);
|
||||
|
||||
const TorIcons(this.codePoint);
|
||||
|
||||
@override
|
||||
final int codePoint;
|
||||
|
||||
@override
|
||||
String get fontFamily => 'TorIcons';
|
||||
|
||||
@override
|
||||
List<String>? get fontFamilyFallback => null;
|
||||
|
||||
@override
|
||||
String? get fontPackage => null;
|
||||
|
||||
@override
|
||||
bool get matchTextDirection => false;
|
||||
|
||||
@override
|
||||
String toString() => 'TorIcons.$name';
|
||||
}
|
||||
+29
-17
@@ -7,34 +7,35 @@ environment:
|
||||
sdk: '>=3.4.1 <4.0.0'
|
||||
|
||||
dependencies:
|
||||
collection: ^1.19.0
|
||||
collection: ^1.19.1
|
||||
copy_with_extension: ^6.0.1
|
||||
cross_cache: ^0.0.5
|
||||
crypto: ^3.0.6
|
||||
drift: ^2.24.0
|
||||
drift: ^2.25.1
|
||||
dynamic_color: ^1.7.0
|
||||
exceptions: ^0.6.1
|
||||
expandable_page_view: ^1.0.17
|
||||
fading_scroll: ^0.9.1
|
||||
fast_equatable: ^1.1.0
|
||||
file_picker: ^8.3.2
|
||||
file_picker: ^8.3.7
|
||||
flutter:
|
||||
sdk: flutter
|
||||
flutter_background_service: ^5.1.0
|
||||
flutter_chat_core:
|
||||
path: ../../flutter_chat_ui/packages/flutter_chat_core
|
||||
flutter_chat_ui:
|
||||
path: ../../flutter_chat_ui/packages/flutter_chat_ui
|
||||
flutter_hooks: ^0.20.5
|
||||
flutter_markdown: ^0.7.6+1
|
||||
flutter_markdown: ^0.7.6+2
|
||||
flutter_material_design_icons: ^1.1.7447
|
||||
flutter_mozilla_components:
|
||||
path: ../packages/flutter_mozilla_components
|
||||
flutter_pdf_text: ^0.8.1
|
||||
flutter_pdf_text: ^0.9.0
|
||||
flutter_reorderable_grid_view:
|
||||
path: ../../flutter-reorderable-grid-view
|
||||
flutter_secure_storage: ^10.0.0-beta.4
|
||||
flutter_sharing_intent: ^1.1.1
|
||||
flutter_slidable: ^4.0.0
|
||||
flutter_svg: ^2.0.17
|
||||
go_router: ^14.8.0
|
||||
google_fonts: ^6.2.1
|
||||
home_widget: ^0.7.0+1
|
||||
@@ -54,9 +55,9 @@ dependencies:
|
||||
markdown: ^7.3.0
|
||||
mime: ^2.0.0
|
||||
package_info_plus: ^8.2.1
|
||||
path: ^1.9.0
|
||||
path: ^1.9.1
|
||||
path_provider: ^2.1.5
|
||||
pocketbase: ^0.21.0
|
||||
pocketbase: ^0.22.0
|
||||
provider: ^6.1.2
|
||||
riverpod: ^2.6.1
|
||||
riverpod_annotation: ^2.6.1
|
||||
@@ -67,13 +68,17 @@ dependencies:
|
||||
speech_to_text_google_dialog:
|
||||
git:
|
||||
url: https://github.com/FaFre/speech_to_text_google_dialog.git
|
||||
sqlite3: ^2.7.2
|
||||
sqlite3_flutter_libs: ^0.5.29+1
|
||||
sqlite3: ^2.7.3
|
||||
sqlite3_flutter_libs: ^0.5.30
|
||||
sqlite3_vec:
|
||||
path: ../packages/sqlite3_vec
|
||||
synchronized: ^3.3.0+3
|
||||
synchronized: ^3.3.1
|
||||
text_scroll: ^0.2.0
|
||||
timeago: ^3.7.0
|
||||
tor:
|
||||
git:
|
||||
url: https://github.com/FaFre/tor.git
|
||||
ref: update
|
||||
universal_io: ^2.2.2
|
||||
uri_to_file:
|
||||
git:
|
||||
@@ -84,30 +89,37 @@ dependencies:
|
||||
xxh3: ^1.2.0
|
||||
|
||||
dependency_overrides:
|
||||
cross_cache:
|
||||
path: ../../flutter_chat_ui/packages/cross_cache
|
||||
flutter_chat_core:
|
||||
path: ../../flutter_chat_ui/packages/flutter_chat_core
|
||||
|
||||
dev_dependencies:
|
||||
build_runner: ^2.4.14
|
||||
build_runner: ^2.4.15
|
||||
copy_with_extension_gen: ^6.0.0
|
||||
custom_lint: ^0.6.10
|
||||
dependency_validator: ^4.1.2
|
||||
drift_dev: ^2.24.0
|
||||
drift_dev: ^2.25.2
|
||||
fast_equatable_lint: ^0.3.0
|
||||
flutter_launcher_icons: ^0.14.3
|
||||
go_router_builder: ^2.7.5
|
||||
go_router_builder: ^2.8.0
|
||||
json_serializable: ^6.9.0
|
||||
lint: ^2.3.0
|
||||
lint: ^2.4.0
|
||||
riverpod_generator: ^2.6.1
|
||||
riverpod_lint: ^2.6.1
|
||||
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
assets:
|
||||
- assets/icon/
|
||||
- assets/landing/
|
||||
- assets/fonts/
|
||||
- assets/icon/
|
||||
- assets/images/
|
||||
- assets/landing/
|
||||
- assets/preferences/
|
||||
fonts:
|
||||
- family: TorIcons
|
||||
fonts:
|
||||
- asset: assets/fonts/tor-icons.ttf
|
||||
|
||||
flutter_launcher_icons:
|
||||
android: "launcher_icon"
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
# melos_managed_dependency_overrides: flutter_mozilla_components,sqlite3_vec,flutter_chat_core
|
||||
# melos_managed_dependency_overrides: flutter_mozilla_components,sqlite3_vec,cross_cache,flutter_chat_core
|
||||
dependency_overrides:
|
||||
flutter_mozilla_components:
|
||||
path: ../packages/flutter_mozilla_components
|
||||
sqlite3_vec:
|
||||
path: ../packages/sqlite3_vec
|
||||
cross_cache:
|
||||
path: ../../flutter_chat_ui/packages/cross_cache
|
||||
flutter_chat_core:
|
||||
path: ../../flutter_chat_ui/packages/flutter_chat_core
|
||||
|
||||
Reference in New Issue
Block a user