refactoring

This commit is contained in:
Fabian Freund
2025-02-20 12:27:34 +01:00
parent b81fc1cc42
commit 490e13000b
138 changed files with 1392 additions and 3251 deletions
-17
View File
@@ -1,17 +0,0 @@
Lensai enhances your mobile research experience while keeping your personal data and identity secure.
Powered by advanced AI tools and browser extensions, Lensai offers modern features designed to elevate your browsing experience.
Lensai is packed with a variety of features aimed at improving usability and maintaining privacy:
- **Privacy by Default**: Incognito mode is enabled by default to keep your searches and browsing history private.
- **[Kagi Integration](https://github.com/FaFre/lensai/wiki/Kagi-Tools)**: Seamlessly integrates Kagi's search, assistant, and summarizer tools within a user-friendly interface.
- **[Easy Sharing](https://github.com/FaFre/lensai/wiki/Kagi-Tools)**: Share URLs or text from any app directly into Lensai without the need to copy and paste, for direct use with summarizer/assistant.
- **[Quick Summarization](https://github.com/FaFre/lensai/wiki/Browser)**: Long press any link on a website to get a summary via Kagi's summarizer.
- **[Content Blocking](https://github.com/FaFre/lensai/wiki/Content-Blocking)**: A powerful content blocker is enabled by default to filter out ads, malware, and tracking sites.
- **[Home Widgets](https://github.com/FaFre/lensai/wiki/Home-Widget)**: Includes home screen widgets in three different sizes for quick actions, enhancing productivity.
- **[PDF processing](https://github.com/FaFre/lensai/wiki/Kagi-Tools)**: Open and directly summarize/prompt PDF documents.
- **Reader Mode**: Extract and simplify web pages, enhancing readability by removing ads, sidebars, and other non-essential elements.
- **[Chat Archive](https://github.com/FaFre/lensai/wiki/Chat-Archive)**: Easily archive conversations with the Kagi assistant on your device and search through them locally
- **[Bang Repository](https://github.com/FaFre/lensai/wiki/Bangs)**: Automatically synchronizes all official Kagi Bangs from the official Repository and makes them available through a convenient interface.
- **[Enhanced Text Interaction](https://github.com/FaFre/lensai/wiki/Browser)**: Custom context menu allows for easy passage of marked text to Kagi's search or assistant tools.
- **[Speech Input](https://github.com/FaFre/lensai/wiki/Kagi-Tools)**: Supports voice commands for hands-free operation and accessibility.
@@ -0,0 +1,6 @@
class ReceivedIntentParameter {
final String? content;
final String? tool;
ReceivedIntentParameter(this.content, this.tool);
}
@@ -1,6 +0,0 @@
class ReceivedParameter {
final String? content;
final String? tool;
ReceivedParameter(this.content, this.tool);
}
@@ -3,6 +3,7 @@ import 'dart:async';
import 'package:exceptions/exceptions.dart';
import 'package:lensai/features/about/data/repositories/package_info_repository.dart';
import 'package:lensai/features/bangs/data/models/bang.dart';
import 'package:lensai/features/bangs/data/models/bang_group.dart';
import 'package:lensai/features/bangs/domain/repositories/sync.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
+1 -1
View File
@@ -191,7 +191,7 @@ class GenericWebsiteService extends _$GenericWebsiteService {
return Result.fromAsync(() async {
final response = await _client
.get(url)
.timeout(const Duration(seconds: 10));
.timeout(const Duration(seconds: 15));
final result = await compute((args) async {
final document = html_parser.parse(args[0]);
@@ -7,7 +7,7 @@ part of 'generic_website.dart';
// **************************************************************************
String _$genericWebsiteServiceHash() =>
r'4c8c1c9ad430ab01f637f02ec97ee4ce5202975e';
r'7891376773d7e6a61eeab37e0e92bb21f1acd8e3';
/// See also [GenericWebsiteService].
@ProviderFor(GenericWebsiteService)
+7
View File
@@ -0,0 +1,7 @@
import 'package:flutter/material.dart';
extension RelativeSafeArea on MediaQueryData {
double relativeSafeArea() {
return 1.0 - (padding.top / size.height);
}
}
@@ -21,7 +21,7 @@ class AboutDialogScreen extends HookConsumerWidget {
),
applicationName: packageInfo.appName,
applicationVersion: packageInfo.version,
applicationLegalese: 'Copyright © Fabian Freund, 2024',
applicationLegalese: 'Copyright © Fabian Freund, 2025',
);
}
}
@@ -1,5 +1,5 @@
import 'package:home_widget/home_widget.dart';
import 'package:lensai/data/models/received_parameter.dart';
import 'package:lensai/data/models/received_intent_parameter.dart';
import 'package:riverpod/riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:rxdart/rxdart.dart';
@@ -12,12 +12,12 @@ FutureOr<bool> widgetPinnable(Ref ref) async {
}
@Riverpod()
Raw<Stream<ReceivedParameter>> appWidgetLaunchStream(Ref ref) {
Raw<Stream<ReceivedIntentParameter>> appWidgetLaunchStream(Ref ref) {
// ignore: discarded_futures is used as stream
final initialStream = HomeWidget.initiallyLaunchedFromHomeWidget().asStream();
return ConcatStream([
initialStream,
HomeWidget.widgetClicked,
]).whereNotNull().map((uri) => ReceivedParameter(null, uri.host));
]).whereNotNull().map((uri) => ReceivedIntentParameter(null, uri.host));
}
@@ -24,12 +24,12 @@ final widgetPinnableProvider = FutureProvider<bool>.internal(
// ignore: unused_element
typedef WidgetPinnableRef = FutureProviderRef<bool>;
String _$appWidgetLaunchStreamHash() =>
r'8eb98d88e74cc4f7573968563c106b8e6beee289';
r'ed042d1391ae3dbc26f806d6fa2e9a4a2464ae44';
/// See also [appWidgetLaunchStream].
@ProviderFor(appWidgetLaunchStream)
final appWidgetLaunchStreamProvider =
AutoDisposeProvider<Raw<Stream<ReceivedParameter>>>.internal(
AutoDisposeProvider<Raw<Stream<ReceivedIntentParameter>>>.internal(
appWidgetLaunchStream,
name: r'appWidgetLaunchStreamProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
@@ -42,6 +42,6 @@ final appWidgetLaunchStreamProvider =
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef AppWidgetLaunchStreamRef
= AutoDisposeProviderRef<Raw<Stream<ReceivedParameter>>>;
= AutoDisposeProviderRef<Raw<Stream<ReceivedIntentParameter>>>;
// 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
@@ -3,6 +3,7 @@ import 'package:lensai/extensions/nullable.dart';
import 'package:lensai/features/bangs/data/database/database.dart';
import 'package:lensai/features/bangs/data/models/bang.dart';
import 'package:lensai/features/bangs/data/models/bang_data.dart';
import 'package:lensai/features/bangs/data/models/bang_group.dart';
part 'bang.g.dart';
@@ -121,18 +122,4 @@ class BangDao extends DatabaseAccessor<BangDatabase> with _$BangDaoMixin {
Future<int> removeSearchEntry(String searchQuery) {
return db.bangHistory.deleteWhere((t) => t.searchQuery.equals(searchQuery));
}
// Future<int> upsertBangIcon(String trigger, BrowserIcon icon) async {
// //TODO: swithc to WEBP in future, requires 3rd party
// final compressed = await icon.image.toByteData(format: ImageByteFormat.png);
// return db.bangIcon.insertOne(
// BangIconCompanion.insert(
// trigger: trigger,
// iconData: compressed!.buffer.asUint8List(),
// fetchDate: DateTime.now(),
// ),
// mode: InsertMode.insertOrReplace,
// );
// }
}
@@ -1,6 +1,7 @@
import 'package:drift/drift.dart';
import 'package:lensai/features/bangs/data/database/database.dart';
import 'package:lensai/features/bangs/data/models/bang.dart';
import 'package:lensai/features/bangs/data/models/bang_group.dart';
part 'sync.g.dart';
@@ -8,7 +9,7 @@ part 'sync.g.dart';
class SyncDao extends DatabaseAccessor<BangDatabase> with _$SyncDaoMixin {
SyncDao(super.db);
SingleOrNullSelectable<DateTime?> lastSyncOfGroup(BangGroup group) {
SingleOrNullSelectable<DateTime?> getLastSyncOfGroup(BangGroup group) {
final query =
selectOnly(db.bangSync)
..addColumns([db.bangSync.lastSync])
@@ -4,6 +4,7 @@ import 'package:lensai/features/bangs/data/database/daos/sync.dart';
import 'package:lensai/features/bangs/data/database/drift/converters/bang_format.dart';
import 'package:lensai/features/bangs/data/models/bang.dart';
import 'package:lensai/features/bangs/data/models/bang_data.dart';
import 'package:lensai/features/bangs/data/models/bang_group.dart';
import 'package:lensai/features/bangs/data/models/search_history_entry.dart';
import 'package:lensai/features/search/domain/fts_tokenizer.dart';
@@ -1,5 +1,6 @@
import 'package:lensai/features/bangs/data/database/drift/converters/bang_format.dart';
import 'package:lensai/features/bangs/data/models/bang_data.dart';
import 'package:lensai/features/bangs/data/models/bang_group.dart';
import 'package:lensai/features/bangs/data/models/bang.dart';
import 'package:lensai/features/bangs/data/models/search_history_entry.dart';
+1 -17
View File
@@ -4,26 +4,10 @@ import 'package:fast_equatable/fast_equatable.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:lensai/features/bangs/data/database/database.dart'
show BangCompanion;
import 'package:lensai/features/bangs/data/models/bang_group.dart';
part 'bang.g.dart';
enum BangGroup {
general('https://raw.githubusercontent.com/FaFre/bangs/main/data/bangs.json'),
assistant(
'https://raw.githubusercontent.com/FaFre/bangs/main/data/assistant_bangs.json',
),
kagi(
'https://raw.githubusercontent.com/FaFre/bangs/main/data/kagi_bangs.json',
),
custom(
'https://raw.githubusercontent.com/FaFre/bangs/refs/heads/custom/data/custom.json',
);
final String url;
const BangGroup(this.url);
}
enum BangFormat {
///When the bang is invoked with no query, opens the base path of the URL (/)
///instead of any path given in the template (g., /search)
@@ -0,0 +1,16 @@
enum BangGroup {
general('https://raw.githubusercontent.com/FaFre/bangs/main/data/bangs.json'),
assistant(
'https://raw.githubusercontent.com/FaFre/bangs/main/data/assistant_bangs.json',
),
kagi(
'https://raw.githubusercontent.com/FaFre/bangs/main/data/kagi_bangs.json',
),
custom(
'https://raw.githubusercontent.com/FaFre/bangs/refs/heads/custom/data/custom.json',
);
final String url;
const BangGroup(this.url);
}
@@ -5,12 +5,13 @@ import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
import 'package:lensai/core/http_error_handler.dart';
import 'package:lensai/features/bangs/data/models/bang.dart';
import 'package:lensai/features/bangs/data/models/bang_group.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'source.g.dart';
part 'data_source.g.dart';
@Riverpod()
class BangSourceService extends _$BangSourceService {
class BangDataSourceService extends _$BangDataSourceService {
late http.Client _client;
@override
@@ -20,7 +21,9 @@ class BangSourceService extends _$BangSourceService {
Future<Result<List<Bang>>> getBangs(Uri url, BangGroup? group) async {
return Result.fromAsync(() async {
final response = await _client.get(url);
final response = await _client
.get(url)
.timeout(const Duration(seconds: 30));
return await compute((args) => jsonDecode(utf8.decode(args[0])) as List, [
response.bodyBytes,
]).then(
@@ -1,26 +1,27 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'create_tab.dart';
part of 'data_source.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$createTabStreamHash() => r'2e802a10ecaf1862074f32b76e89c26997bdd7b2';
String _$bangDataSourceServiceHash() =>
r'fb00762858a2878505c14e2b1a0f26c1b2f60d7d';
/// See also [CreateTabStream].
@ProviderFor(CreateTabStream)
final createTabStreamProvider =
AutoDisposeStreamNotifierProvider<CreateTabStream, CreateTabSheet>.internal(
CreateTabStream.new,
name: r'createTabStreamProvider',
/// See also [BangDataSourceService].
@ProviderFor(BangDataSourceService)
final bangDataSourceServiceProvider =
AutoDisposeNotifierProvider<BangDataSourceService, void>.internal(
BangDataSourceService.new,
name: r'bangDataSourceServiceProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$createTabStreamHash,
: _$bangDataSourceServiceHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$CreateTabStream = AutoDisposeStreamNotifier<CreateTabSheet>;
typedef _$BangDataSourceService = 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
@@ -1,26 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'source.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$bangSourceServiceHash() => r'2f734d563fa20de306a72013526ef1f72f910fe1';
/// See also [BangSourceService].
@ProviderFor(BangSourceService)
final bangSourceServiceProvider =
AutoDisposeNotifierProvider<BangSourceService, void>.internal(
BangSourceService.new,
name: r'bangSourceServiceProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$bangSourceServiceHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$BangSourceService = 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
@@ -1,5 +1,5 @@
import 'package:lensai/features/bangs/data/models/bang.dart';
import 'package:lensai/features/bangs/data/models/bang_data.dart';
import 'package:lensai/features/bangs/data/models/bang_group.dart';
import 'package:lensai/features/bangs/data/models/search_history_entry.dart';
import 'package:lensai/features/bangs/domain/repositories/data.dart';
import 'package:lensai/features/bangs/domain/repositories/sync.dart';
@@ -8,15 +8,10 @@ import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'bangs.g.dart';
@Riverpod()
Stream<BangData?> bangData(Ref ref, String? trigger) {
final repository = ref.watch(bangDataRepositoryProvider.notifier);
return repository.watchBang(trigger);
}
@Riverpod(keepAlive: true)
Stream<BangData?> defaultSearchBangData(Ref ref) {
final repository = ref.watch(bangDataRepositoryProvider.notifier);
//TODO: Setting
return repository.watchBang('lai');
}
@@ -27,27 +22,24 @@ Stream<Map<String, List<String>>> bangCategories(Ref ref) {
}
@Riverpod()
Stream<List<BangData>> bangDataList(
Stream<List<BangData>> bangList(
Ref ref, {
({
Iterable<BangGroup>? groups,
String? domain,
({String category, String? subCategory})? categoryFilter,
bool? orderMostFrequentFirst,
})?
filter,
List<BangGroup>? groups,
String? domain,
({String category, String? subCategory})? categoryFilter,
bool? orderMostFrequentFirst,
}) {
final repository = ref.watch(bangDataRepositoryProvider.notifier);
return repository.watchBangs(
groups: filter?.groups,
domain: filter?.domain,
categoryFilter: filter?.categoryFilter,
orderMostFrequentFirst: filter?.orderMostFrequentFirst,
groups: groups,
domain: domain,
categoryFilter: categoryFilter,
orderMostFrequentFirst: orderMostFrequentFirst,
);
}
@Riverpod()
Stream<List<BangData>> frequentBangDataList(Ref ref) {
Stream<List<BangData>> frequentBangList(Ref ref) {
final repository = ref.watch(bangDataRepositoryProvider.notifier);
return repository.watchFrequentBangs();
}
@@ -6,156 +6,6 @@ part of 'bangs.dart';
// RiverpodGenerator
// **************************************************************************
String _$bangDataHash() => r'057a002de9789ab74c8f3b337a43faa26b788d8d';
/// Copied from Dart SDK
class _SystemHash {
_SystemHash._();
static int combine(int hash, int value) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + value);
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
return hash ^ (hash >> 6);
}
static int finish(int hash) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
// ignore: parameter_assignments
hash = hash ^ (hash >> 11);
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
}
}
/// See also [bangData].
@ProviderFor(bangData)
const bangDataProvider = BangDataFamily();
/// See also [bangData].
class BangDataFamily extends Family<AsyncValue<BangData?>> {
/// See also [bangData].
const BangDataFamily();
/// See also [bangData].
BangDataProvider call(
String? trigger,
) {
return BangDataProvider(
trigger,
);
}
@override
BangDataProvider getProviderOverride(
covariant BangDataProvider provider,
) {
return call(
provider.trigger,
);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'bangDataProvider';
}
/// See also [bangData].
class BangDataProvider extends AutoDisposeStreamProvider<BangData?> {
/// See also [bangData].
BangDataProvider(
String? trigger,
) : this._internal(
(ref) => bangData(
ref as BangDataRef,
trigger,
),
from: bangDataProvider,
name: r'bangDataProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$bangDataHash,
dependencies: BangDataFamily._dependencies,
allTransitiveDependencies: BangDataFamily._allTransitiveDependencies,
trigger: trigger,
);
BangDataProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.trigger,
}) : super.internal();
final String? trigger;
@override
Override overrideWith(
Stream<BangData?> Function(BangDataRef provider) create,
) {
return ProviderOverride(
origin: this,
override: BangDataProvider._internal(
(ref) => create(ref as BangDataRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
trigger: trigger,
),
);
}
@override
AutoDisposeStreamProviderElement<BangData?> createElement() {
return _BangDataProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is BangDataProvider && other.trigger == trigger;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, trigger.hashCode);
return _SystemHash.finish(hash);
}
}
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin BangDataRef on AutoDisposeStreamProviderRef<BangData?> {
/// The parameter `trigger` of this provider.
String? get trigger;
}
class _BangDataProviderElement
extends AutoDisposeStreamProviderElement<BangData?> with BangDataRef {
_BangDataProviderElement(super.provider);
@override
String? get trigger => (origin as BangDataProvider).trigger;
}
String _$defaultSearchBangDataHash() =>
r'c93026fe7b1650fe8a39bb2a8850af3ee416c306';
@@ -193,37 +43,62 @@ final bangCategoriesProvider =
// ignore: unused_element
typedef BangCategoriesRef
= AutoDisposeStreamProviderRef<Map<String, List<String>>>;
String _$bangDataListHash() => r'cd034fdf046b156ac452edd0857ac054ab477917';
String _$bangListHash() => r'dc168bd1246bea3e5f8015dd52fbf41c5f98ca13';
/// See also [bangDataList].
@ProviderFor(bangDataList)
const bangDataListProvider = BangDataListFamily();
/// Copied from Dart SDK
class _SystemHash {
_SystemHash._();
/// See also [bangDataList].
class BangDataListFamily extends Family<AsyncValue<List<BangData>>> {
/// See also [bangDataList].
const BangDataListFamily();
static int combine(int hash, int value) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + value);
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
return hash ^ (hash >> 6);
}
/// See also [bangDataList].
BangDataListProvider call({
({
({String category, String? subCategory})? categoryFilter,
String? domain,
Iterable<BangGroup>? groups,
bool? orderMostFrequentFirst
})? filter,
static int finish(int hash) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
// ignore: parameter_assignments
hash = hash ^ (hash >> 11);
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
}
}
/// See also [bangList].
@ProviderFor(bangList)
const bangListProvider = BangListFamily();
/// See also [bangList].
class BangListFamily extends Family<AsyncValue<List<BangData>>> {
/// See also [bangList].
const BangListFamily();
/// See also [bangList].
BangListProvider call({
List<BangGroup>? groups,
String? domain,
({String category, String? subCategory})? categoryFilter,
bool? orderMostFrequentFirst,
}) {
return BangDataListProvider(
filter: filter,
return BangListProvider(
groups: groups,
domain: domain,
categoryFilter: categoryFilter,
orderMostFrequentFirst: orderMostFrequentFirst,
);
}
@override
BangDataListProvider getProviderOverride(
covariant BangDataListProvider provider,
BangListProvider getProviderOverride(
covariant BangListProvider provider,
) {
return call(
filter: provider.filter,
groups: provider.groups,
domain: provider.domain,
categoryFilter: provider.categoryFilter,
orderMostFrequentFirst: provider.orderMostFrequentFirst,
);
}
@@ -239,85 +114,99 @@ class BangDataListFamily extends Family<AsyncValue<List<BangData>>> {
_allTransitiveDependencies;
@override
String? get name => r'bangDataListProvider';
String? get name => r'bangListProvider';
}
/// See also [bangDataList].
class BangDataListProvider extends AutoDisposeStreamProvider<List<BangData>> {
/// See also [bangDataList].
BangDataListProvider({
({
({String category, String? subCategory})? categoryFilter,
String? domain,
Iterable<BangGroup>? groups,
bool? orderMostFrequentFirst
})? filter,
/// See also [bangList].
class BangListProvider extends AutoDisposeStreamProvider<List<BangData>> {
/// See also [bangList].
BangListProvider({
List<BangGroup>? groups,
String? domain,
({String category, String? subCategory})? categoryFilter,
bool? orderMostFrequentFirst,
}) : this._internal(
(ref) => bangDataList(
ref as BangDataListRef,
filter: filter,
(ref) => bangList(
ref as BangListRef,
groups: groups,
domain: domain,
categoryFilter: categoryFilter,
orderMostFrequentFirst: orderMostFrequentFirst,
),
from: bangDataListProvider,
name: r'bangDataListProvider',
from: bangListProvider,
name: r'bangListProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$bangDataListHash,
dependencies: BangDataListFamily._dependencies,
allTransitiveDependencies:
BangDataListFamily._allTransitiveDependencies,
filter: filter,
: _$bangListHash,
dependencies: BangListFamily._dependencies,
allTransitiveDependencies: BangListFamily._allTransitiveDependencies,
groups: groups,
domain: domain,
categoryFilter: categoryFilter,
orderMostFrequentFirst: orderMostFrequentFirst,
);
BangDataListProvider._internal(
BangListProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.filter,
required this.groups,
required this.domain,
required this.categoryFilter,
required this.orderMostFrequentFirst,
}) : super.internal();
final ({
({String category, String? subCategory})? categoryFilter,
String? domain,
Iterable<BangGroup>? groups,
bool? orderMostFrequentFirst
})? filter;
final List<BangGroup>? groups;
final String? domain;
final ({String category, String? subCategory})? categoryFilter;
final bool? orderMostFrequentFirst;
@override
Override overrideWith(
Stream<List<BangData>> Function(BangDataListRef provider) create,
Stream<List<BangData>> Function(BangListRef provider) create,
) {
return ProviderOverride(
origin: this,
override: BangDataListProvider._internal(
(ref) => create(ref as BangDataListRef),
override: BangListProvider._internal(
(ref) => create(ref as BangListRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
filter: filter,
groups: groups,
domain: domain,
categoryFilter: categoryFilter,
orderMostFrequentFirst: orderMostFrequentFirst,
),
);
}
@override
AutoDisposeStreamProviderElement<List<BangData>> createElement() {
return _BangDataListProviderElement(this);
return _BangListProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is BangDataListProvider && other.filter == filter;
return other is BangListProvider &&
other.groups == groups &&
other.domain == domain &&
other.categoryFilter == categoryFilter &&
other.orderMostFrequentFirst == orderMostFrequentFirst;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, filter.hashCode);
hash = _SystemHash.combine(hash, groups.hashCode);
hash = _SystemHash.combine(hash, domain.hashCode);
hash = _SystemHash.combine(hash, categoryFilter.hashCode);
hash = _SystemHash.combine(hash, orderMostFrequentFirst.hashCode);
return _SystemHash.finish(hash);
}
@@ -325,49 +214,54 @@ class BangDataListProvider extends AutoDisposeStreamProvider<List<BangData>> {
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin BangDataListRef on AutoDisposeStreamProviderRef<List<BangData>> {
/// The parameter `filter` of this provider.
({
({String category, String? subCategory})? categoryFilter,
String? domain,
Iterable<BangGroup>? groups,
bool? orderMostFrequentFirst
})? get filter;
mixin BangListRef on AutoDisposeStreamProviderRef<List<BangData>> {
/// The parameter `groups` of this provider.
List<BangGroup>? get groups;
/// The parameter `domain` of this provider.
String? get domain;
/// The parameter `categoryFilter` of this provider.
({String category, String? subCategory})? get categoryFilter;
/// The parameter `orderMostFrequentFirst` of this provider.
bool? get orderMostFrequentFirst;
}
class _BangDataListProviderElement
extends AutoDisposeStreamProviderElement<List<BangData>>
with BangDataListRef {
_BangDataListProviderElement(super.provider);
class _BangListProviderElement
extends AutoDisposeStreamProviderElement<List<BangData>> with BangListRef {
_BangListProviderElement(super.provider);
@override
({
({String category, String? subCategory})? categoryFilter,
String? domain,
Iterable<BangGroup>? groups,
bool? orderMostFrequentFirst
})? get filter => (origin as BangDataListProvider).filter;
List<BangGroup>? get groups => (origin as BangListProvider).groups;
@override
String? get domain => (origin as BangListProvider).domain;
@override
({String category, String? subCategory})? get categoryFilter =>
(origin as BangListProvider).categoryFilter;
@override
bool? get orderMostFrequentFirst =>
(origin as BangListProvider).orderMostFrequentFirst;
}
String _$frequentBangDataListHash() =>
r'27bc10ddf502266458af3910fff09bc8282a8e5c';
String _$frequentBangListHash() => r'2c1ecb7e9416772fc1c32d01d767e1eb4f865975';
/// See also [frequentBangDataList].
@ProviderFor(frequentBangDataList)
final frequentBangDataListProvider =
/// See also [frequentBangList].
@ProviderFor(frequentBangList)
final frequentBangListProvider =
AutoDisposeStreamProvider<List<BangData>>.internal(
frequentBangDataList,
name: r'frequentBangDataListProvider',
frequentBangList,
name: r'frequentBangListProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$frequentBangDataListHash,
: _$frequentBangListHash,
dependencies: null,
allTransitiveDependencies: null,
);
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef FrequentBangDataListRef = AutoDisposeStreamProviderRef<List<BangData>>;
typedef FrequentBangListRef = AutoDisposeStreamProviderRef<List<BangData>>;
String _$searchHistoryHash() => r'7b97798729643de8e44fd8024cdc02f35124b08b';
/// See also [searchHistory].
@@ -1,24 +1,24 @@
import 'package:lensai/features/bangs/data/models/bang_data.dart';
import 'package:lensai/features/bangs/domain/repositories/data.dart';
import 'package:riverpod/riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'search.g.dart';
@Riverpod()
Future<Uri> triggerBangSearch(
Ref ref,
BangData bang,
String searchQuery,
) async {
final bangDataNotifier = ref.watch(bangDataRepositoryProvider.notifier);
class BangSearch extends _$BangSearch {
Future<Uri> triggerBangSearch(BangData bang, String searchQuery) async {
final bangDataNotifier = ref.read(bangDataRepositoryProvider.notifier);
await bangDataNotifier.increaseFrequency(bang.trigger);
await bangDataNotifier.addSearchEntry(
bang.trigger,
searchQuery,
maxEntryCount: 3,
); //TODO: make count dynamic
await bangDataNotifier.increaseFrequency(bang.trigger);
await bangDataNotifier.addSearchEntry(
bang.trigger,
searchQuery,
maxEntryCount: 3,
); //TODO: make count dynamic
return bang.getUrl(searchQuery);
return bang.getUrl(searchQuery);
}
@override
void build() {}
}
@@ -6,172 +6,20 @@ part of 'search.dart';
// RiverpodGenerator
// **************************************************************************
String _$triggerBangSearchHash() => r'a5cff116ae09b26d4fa476ce6a2e6052fa6fa177';
String _$bangSearchHash() => r'a9bc5efc6e0df7b64bb648623f65911373a8e8e4';
/// Copied from Dart SDK
class _SystemHash {
_SystemHash._();
/// See also [BangSearch].
@ProviderFor(BangSearch)
final bangSearchProvider =
AutoDisposeNotifierProvider<BangSearch, void>.internal(
BangSearch.new,
name: r'bangSearchProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') ? null : _$bangSearchHash,
dependencies: null,
allTransitiveDependencies: null,
);
static int combine(int hash, int value) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + value);
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
return hash ^ (hash >> 6);
}
static int finish(int hash) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
// ignore: parameter_assignments
hash = hash ^ (hash >> 11);
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
}
}
/// See also [triggerBangSearch].
@ProviderFor(triggerBangSearch)
const triggerBangSearchProvider = TriggerBangSearchFamily();
/// See also [triggerBangSearch].
class TriggerBangSearchFamily extends Family<AsyncValue<Uri>> {
/// See also [triggerBangSearch].
const TriggerBangSearchFamily();
/// See also [triggerBangSearch].
TriggerBangSearchProvider call(
BangData bang,
String searchQuery,
) {
return TriggerBangSearchProvider(
bang,
searchQuery,
);
}
@override
TriggerBangSearchProvider getProviderOverride(
covariant TriggerBangSearchProvider provider,
) {
return call(
provider.bang,
provider.searchQuery,
);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'triggerBangSearchProvider';
}
/// See also [triggerBangSearch].
class TriggerBangSearchProvider extends AutoDisposeFutureProvider<Uri> {
/// See also [triggerBangSearch].
TriggerBangSearchProvider(
BangData bang,
String searchQuery,
) : this._internal(
(ref) => triggerBangSearch(
ref as TriggerBangSearchRef,
bang,
searchQuery,
),
from: triggerBangSearchProvider,
name: r'triggerBangSearchProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$triggerBangSearchHash,
dependencies: TriggerBangSearchFamily._dependencies,
allTransitiveDependencies:
TriggerBangSearchFamily._allTransitiveDependencies,
bang: bang,
searchQuery: searchQuery,
);
TriggerBangSearchProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.bang,
required this.searchQuery,
}) : super.internal();
final BangData bang;
final String searchQuery;
@override
Override overrideWith(
FutureOr<Uri> Function(TriggerBangSearchRef provider) create,
) {
return ProviderOverride(
origin: this,
override: TriggerBangSearchProvider._internal(
(ref) => create(ref as TriggerBangSearchRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
bang: bang,
searchQuery: searchQuery,
),
);
}
@override
AutoDisposeFutureProviderElement<Uri> createElement() {
return _TriggerBangSearchProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is TriggerBangSearchProvider &&
other.bang == bang &&
other.searchQuery == searchQuery;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, bang.hashCode);
hash = _SystemHash.combine(hash, searchQuery.hashCode);
return _SystemHash.finish(hash);
}
}
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin TriggerBangSearchRef on AutoDisposeFutureProviderRef<Uri> {
/// The parameter `bang` of this provider.
BangData get bang;
/// The parameter `searchQuery` of this provider.
String get searchQuery;
}
class _TriggerBangSearchProviderElement
extends AutoDisposeFutureProviderElement<Uri> with TriggerBangSearchRef {
_TriggerBangSearchProviderElement(super.provider);
@override
BangData get bang => (origin as TriggerBangSearchProvider).bang;
@override
String get searchQuery => (origin as TriggerBangSearchProvider).searchQuery;
}
typedef _$BangSearch = 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
@@ -1,8 +1,8 @@
import 'dart:convert';
import 'package:drift/drift.dart';
import 'package:lensai/features/bangs/data/models/bang.dart';
import 'package:lensai/features/bangs/data/models/bang_data.dart';
import 'package:lensai/features/bangs/data/models/bang_group.dart';
import 'package:lensai/features/bangs/data/models/search_history_entry.dart';
import 'package:lensai/features/bangs/data/providers.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
@@ -57,6 +57,6 @@ class SeamlessBangProvider extends _$SeamlessBangProvider {
AsyncValue<List<BangData>> build() {
return _hasSearch
? ref.watch(bangSearchProvider)
: ref.watch(frequentBangDataListProvider);
: ref.watch(frequentBangListProvider);
}
}
@@ -22,7 +22,7 @@ final bangSearchProvider =
typedef _$BangSearch = AutoDisposeStreamNotifier<List<BangData>>;
String _$seamlessBangProviderHash() =>
r'd2c8e94d6305a6a37cc27f8bc3617521f513e537';
r'c4c0b452c94ba447be5b02dfef5bc16148cc3dc7';
/// See also [SeamlessBangProvider].
@ProviderFor(SeamlessBangProvider)
@@ -1,9 +1,9 @@
import 'package:drift/isolate.dart';
import 'package:exceptions/exceptions.dart';
import 'package:lensai/features/bangs/data/database/database.dart';
import 'package:lensai/features/bangs/data/models/bang.dart';
import 'package:lensai/features/bangs/data/models/bang_group.dart';
import 'package:lensai/features/bangs/data/providers.dart';
import 'package:lensai/features/bangs/data/services/source.dart';
import 'package:lensai/features/bangs/data/services/data_source.dart';
import 'package:riverpod/riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
@@ -12,7 +12,7 @@ part 'sync.g.dart';
@Riverpod(keepAlive: true)
class BangSyncRepository extends _$BangSyncRepository {
static Future<Result<void>> _fetchAndSync({
required BangSourceService sourceService,
required BangDataSourceService sourceService,
required BangDatabase db,
required Uri url,
required BangGroup group,
@@ -20,7 +20,7 @@ class BangSyncRepository extends _$BangSyncRepository {
}) async {
if (syncInterval != null) {
final lastSync =
await db.syncDao.lastSyncOfGroup(group).getSingleOrNull();
await db.syncDao.getLastSyncOfGroup(group).getSingleOrNull();
if (lastSync != null &&
DateTime.now().difference(lastSync) < syncInterval) {
@@ -52,7 +52,9 @@ class BangSyncRepository extends _$BangSyncRepository {
computation: (db) async {
final ref = ProviderContainer();
final result = await _fetchAndSync(
sourceService: ref.read(bangSourceServiceProvider.notifier),
sourceService: ref.read(
bangDataSourceServiceProvider.notifier,
),
db: db,
url: Uri.parse(group.url),
group: group,
@@ -79,7 +81,7 @@ class BangSyncRepository extends _$BangSyncRepository {
return ref
.read(bangDatabaseProvider)
.syncDao
.lastSyncOfGroup(group)
.getLastSyncOfGroup(group)
.watchSingleOrNull();
}
@@ -7,7 +7,7 @@ part of 'sync.dart';
// **************************************************************************
String _$bangSyncRepositoryHash() =>
r'a46813f953e550585f432b11c364d87791819c34';
r'122dd04da82d4124e7c6be5234831bd62242845e';
/// See also [BangSyncRepository].
@ProviderFor(BangSyncRepository)
@@ -8,7 +8,6 @@ import 'package:lensai/features/bangs/presentation/widgets/bang_details.dart';
import 'package:lensai/features/geckoview/domain/controllers/bottom_sheet.dart';
import 'package:lensai/features/geckoview/features/browser/domain/entities/sheet.dart';
import 'package:lensai/features/geckoview/features/browser/domain/providers.dart';
import 'package:lensai/features/kagi/data/entities/modes.dart';
import 'package:lensai/presentation/widgets/failure_widget.dart';
class BangListScreen extends HookConsumerWidget {
@@ -20,14 +19,9 @@ class BangListScreen extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final bangsAsync = ref.watch(
bangDataListProvider(
filter: (
categoryFilter: category.mapNotNull(
(category) => (category: category, subCategory: subCategory),
),
domain: null,
groups: null,
orderMostFrequentFirst: null,
bangListProvider(
categoryFilter: category.mapNotNull(
(category) => (category: category, subCategory: subCategory),
),
),
);
@@ -49,16 +43,7 @@ class BangListScreen extends HookConsumerWidget {
.read(selectedBangTriggerProvider().notifier)
.setTrigger(bang.trigger);
if (ref.read(bottomSheetControllerProvider)
is! CreateTabSheet) {
ref
.read(bottomSheetControllerProvider.notifier)
.show(
CreateTabSheet(preferredTool: KagiTool.search),
);
}
context.go(BrowserRoute().location);
context.go(const SearchRoute().location);
},
);
},
@@ -74,14 +74,6 @@ class BangSearchScreen extends HookConsumerWidget {
.read(selectedBangTriggerProvider().notifier)
.setTrigger(bang.trigger);
// if (ref.read(bottomSheetControllerProvider)
// is! CreateTabSheet) {
// ref.read(bottomSheetControllerProvider.notifier).show(
// CreateTabSheet(preferredTool: KagiTool.search),
// );
// }
// context.go(BrowserRoute().location);
context.pop();
},
);
@@ -42,9 +42,9 @@ class SiteSearch extends HookConsumerWidget {
Future<void> submitSearch(String query) async {
if (activeBang != null && (formKey.currentState?.validate() == true)) {
final searchUri = await ref.read(
triggerBangSearchProvider(activeBang, query).future,
);
final searchUri = await ref
.read(bangSearchProvider.notifier)
.triggerBangSearch(activeBang, query);
await ref.read(tabRepositoryProvider.notifier).addTab(url: searchUri);
@@ -12,11 +12,11 @@ class MessagesDao extends DatabaseAccessor<ChatDatabase>
with _$MessagesDaoMixin {
MessagesDao(super.db);
Selectable<int> messageCount({required String chatId}) {
Selectable<int> getMessageCount({required String chatId}) {
return db.chatMessage.count(where: (row) => row.chatId.equals(chatId));
}
Selectable<ChatMessageData> chatMessages({required String chatId}) {
Selectable<ChatMessageData> getChatMessages({required String chatId}) {
return db.chatMessage.select()
..where((t) => t.chatId.equals(chatId))
..orderBy([(row) => OrderingTerm(expression: row.orderKey)]);
@@ -32,7 +32,7 @@ class MessagesDao extends DatabaseAccessor<ChatDatabase>
.map((row) => row.read(existsExpression)!);
}
SingleOrNullSelectable<int> messageIndex({
SingleOrNullSelectable<int> getMessageIndex({
required String chatId,
required String messageId,
}) {
@@ -9,7 +9,7 @@ class MetadataDao extends DatabaseAccessor<ChatDatabase>
with _$MetadataDaoMixin {
MetadataDao(super.db);
SingleOrNullSelectable<ChatMetadata> chatMetadata(String chatId) {
SingleOrNullSelectable<ChatMetadata> getChatMetadata(String chatId) {
final statement =
db.chatData.selectOnly()
..addColumns([db.chatData.metadata])
@@ -1,9 +1,10 @@
import 'package:fast_equatable/fast_equatable.dart';
import 'package:json_annotation/json_annotation.dart';
part 'chat_metadata.g.dart';
@JsonSerializable()
class ChatMetadata {
class ChatMetadata with FastEquatable {
final String? mainDocumentId;
final String? contextId;
@@ -13,4 +14,10 @@ class ChatMetadata {
_$ChatMetadataFromJson(json);
Map<String, dynamic> toJson() => _$ChatMetadataToJson(this);
@override
bool get cacheHash => true;
@override
List<Object?> get hashParameters => [mainDocumentId, contextId];
}
@@ -19,7 +19,7 @@ class DriftChatController implements ChatController {
DriftChatController(this._dao, {required this.chatId}) {
final chatMessagesStream =
_dao
.chatMessages(chatId: chatId)
.getChatMessages(chatId: chatId)
.map(
(data) => Message.fromJson(
jsonDecode(data.messageJson) as Map<String, dynamic>,
@@ -78,7 +78,7 @@ class DriftChatController implements ChatController {
Future<void> remove(Message message) async {
final index =
await _dao
.messageIndex(chatId: chatId, messageId: message.id)
.getMessageIndex(chatId: chatId, messageId: message.id)
.getSingleOrNull();
if (index != null) {
@@ -1,6 +1,6 @@
import 'package:lensai/features/chat/features/chat_store/data/models/chat_metadata.dart';
import 'package:lensai/features/chat/features/chat_store/data/providers.dart';
import 'package:lensai/features/chat/features/chat_store/domain/drift_chat_controller.dart';
import 'package:lensai/features/chat/features/chat_store/domain/controllers/drift_chat_controller.dart';
import 'package:riverpod/riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
@@ -21,5 +21,5 @@ DriftChatController chatController(Ref ref, String chatId) {
@Riverpod()
Stream<ChatMetadata?> chatMetadata(Ref ref, String chatId) {
final db = ref.watch(chatDatabaseProvider);
return db.metadataDao.chatMetadata(chatId).watchSingleOrNull();
return db.metadataDao.getChatMetadata(chatId).watchSingleOrNull();
}
@@ -158,7 +158,7 @@ class _ChatControllerProviderElement
String get chatId => (origin as ChatControllerProvider).chatId;
}
String _$chatMetadataHash() => r'22252e4a4c4e9cb039c5eee0e0f6eb5b8bd03944';
String _$chatMetadataHash() => r'e50f492ae69168c9f9c4df89e29424b72b681e27';
/// See also [chatMetadata].
@ProviderFor(chatMetadata)
@@ -3,7 +3,7 @@ import 'package:flutter_chat_core/flutter_chat_core.dart';
import 'package:langchain/langchain.dart';
import 'package:lensai/core/uuid.dart';
import 'package:lensai/features/chat/features/chat_store/data/models/message_types.dart';
import 'package:lensai/features/chat/features/chat_store/domain/drift_chat_controller.dart';
import 'package:lensai/features/chat/features/chat_store/domain/controllers/drift_chat_controller.dart';
import 'package:lensai/features/chat/features/chat_store/domain/providers.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
@@ -1,7 +1,6 @@
import 'dart:async';
import 'package:lensai/features/geckoview/features/browser/domain/entities/sheet.dart';
import 'package:lensai/features/geckoview/features/browser/domain/services/create_tab.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:rxdart/rxdart.dart';
@@ -11,8 +10,7 @@ part 'bottom_sheet.g.dart';
class BottomSheetController extends _$BottomSheetController {
@override
Sheet? build() {
//Don't use select here because it will only fire on new != previous
return ref.watch(createTabStreamProvider).valueOrNull;
return null;
}
// ignore: use_setters_to_change_properties api decision
@@ -7,7 +7,7 @@ part of 'bottom_sheet.dart';
// **************************************************************************
String _$bottomSheetControllerHash() =>
r'ae91f911fc35fdc0dfb14149ac2295e58ad13a86';
r'0b0ea53a96b80b8c1cba79b1d5913274b7127d5b';
/// See also [BottomSheetController].
@ProviderFor(BottomSheetController)
@@ -1,10 +1,10 @@
import 'package:flutter/widgets.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'overlay_dialog.g.dart';
part 'overlay.g.dart';
@Riverpod()
class OverlayDialogController extends _$OverlayDialogController {
class OverlayController extends _$OverlayController {
@override
Widget? build() {
return null;
@@ -1,26 +1,26 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'chat.dart';
part of 'overlay.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$kagiChatServiceHash() => r'a112b70b42fe9d650c9d4dc643bf965ddce7bfa6';
String _$overlayControllerHash() => r'b6dc3ca17611c4efadc7e8248d493cd3c7061e01';
/// See also [KagiChatService].
@ProviderFor(KagiChatService)
final kagiChatServiceProvider =
NotifierProvider<KagiChatService, void>.internal(
KagiChatService.new,
name: r'kagiChatServiceProvider',
/// See also [OverlayController].
@ProviderFor(OverlayController)
final overlayControllerProvider =
AutoDisposeNotifierProvider<OverlayController, Widget?>.internal(
OverlayController.new,
name: r'overlayControllerProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$kagiChatServiceHash,
: _$overlayControllerHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$KagiChatService = Notifier<void>;
typedef _$OverlayController = AutoDisposeNotifier<Widget?>;
// 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
@@ -1,27 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'overlay_dialog.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$overlayDialogControllerHash() =>
r'53b44d9567bcb5bd9faecbfb8bb1af5aafb369c9';
/// See also [OverlayDialogController].
@ProviderFor(OverlayDialogController)
final overlayDialogControllerProvider =
AutoDisposeNotifierProvider<OverlayDialogController, Widget?>.internal(
OverlayDialogController.new,
name: r'overlayDialogControllerProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$overlayDialogControllerHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$OverlayDialogController = AutoDisposeNotifier<Widget?>;
// 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
@@ -5,12 +5,12 @@ import 'package:lensai/data/models/web_page_info.dart';
import 'package:lensai/domain/entities/equatable_image.dart';
import 'package:lensai/extensions/nullable.dart';
import 'package:lensai/features/geckoview/domain/entities/browser_icon.dart';
import 'package:lensai/features/geckoview/domain/entities/find_result_state.dart';
import 'package:lensai/features/geckoview/domain/entities/history_state.dart';
import 'package:lensai/features/geckoview/domain/entities/readerable_state.dart';
import 'package:lensai/features/geckoview/domain/entities/security_state.dart';
import 'package:lensai/features/geckoview/domain/entities/states/find_result.dart';
import 'package:lensai/features/geckoview/domain/entities/states/history.dart';
import 'package:lensai/features/geckoview/domain/entities/states/readerable.dart';
import 'package:lensai/features/geckoview/domain/entities/states/security.dart';
part 'tab_state.g.dart';
part 'tab.g.dart';
@CopyWith()
class TabState extends WebPageInfo with FastEquatable {
@@ -1,6 +1,6 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'tab_state.dart';
part of 'tab.dart';
// **************************************************************************
// CopyWithGenerator
@@ -4,7 +4,7 @@ import 'package:copy_with_extension/copy_with_extension.dart';
import 'package:fast_equatable/fast_equatable.dart';
import 'package:lensai/domain/entities/equatable_image.dart';
part 'web_extension_state.g.dart';
part 'web_extension.g.dart';
@CopyWith()
class WebExtensionState with FastEquatable {
@@ -1,6 +1,6 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'web_extension_state.dart';
part of 'web_extension.dart';
// **************************************************************************
// CopyWithGenerator
@@ -3,7 +3,6 @@ import 'dart:async';
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:lensai/core/logger.dart';
import 'package:lensai/features/bangs/domain/providers/bangs.dart';
import 'package:lensai/features/geckoview/domain/providers/tab_session.dart';
import 'package:lensai/features/geckoview/domain/repositories/tab.dart';
import 'package:riverpod/riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
@@ -152,8 +151,3 @@ class EngineReadyState extends _$EngineReadyState {
return currentState;
}
}
@Riverpod()
Raw<TabSession> selectedTabSessionNotifier(Ref ref) {
return ref.watch(tabSessionProvider(tabId: null).notifier);
}
@@ -93,25 +93,6 @@ final engineSuggestionsServiceProvider =
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef EngineSuggestionsServiceRef = ProviderRef<GeckoSuggestionsService>;
String _$selectedTabSessionNotifierHash() =>
r'aef19991d5b05a2bb28e8a6ccb57dc75c2f3148f';
/// See also [selectedTabSessionNotifier].
@ProviderFor(selectedTabSessionNotifier)
final selectedTabSessionNotifierProvider =
AutoDisposeProvider<Raw<TabSession>>.internal(
selectedTabSessionNotifier,
name: r'selectedTabSessionNotifierProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$selectedTabSessionNotifierHash,
dependencies: null,
allTransitiveDependencies: null,
);
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef SelectedTabSessionNotifierRef = AutoDisposeProviderRef<Raw<TabSession>>;
String _$engineReadyStateHash() => r'c682333e2e07cf0635aa7ae793a2088ca648c950';
/// See also [EngineReadyState].
@@ -1,7 +1,7 @@
import 'dart:async';
import 'package:collection/collection.dart';
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:lensai/domain/entities/equatable_iterable.dart';
import 'package:lensai/features/geckoview/domain/providers.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
@@ -10,7 +10,7 @@ part 'tab_list.g.dart';
@Riverpod(keepAlive: true)
class TabList extends _$TabList {
@override
List<String> build() {
EquatableCollection<List<String>> build() {
final eventService = ref.watch(eventServiceProvider);
ref.listen(fireImmediately: true, engineReadyStateProvider, (
@@ -23,8 +23,10 @@ class TabList extends _$TabList {
});
final tabListSub = eventService.tabListEvents.listen((tabs) async {
if (!const DeepCollectionEquality().equals(state, tabs)) {
state = tabs;
final equatableTabs = EquatableCollection(tabs, immutable: true);
if (equatableTabs != state) {
state = equatableTabs;
}
});
@@ -32,6 +34,6 @@ class TabList extends _$TabList {
unawaited(tabListSub.cancel());
});
return [];
return EquatableCollection([], immutable: true);
}
}
@@ -6,11 +6,12 @@ part of 'tab_list.dart';
// RiverpodGenerator
// **************************************************************************
String _$tabListHash() => r'039099bfa102a86fd73c0544ea71f93f91e9d352';
String _$tabListHash() => r'bb8b6cca42a8aace8bd9808fdeac4fe4965ac867';
/// See also [TabList].
@ProviderFor(TabList)
final tabListProvider = NotifierProvider<TabList, List<String>>.internal(
final tabListProvider =
NotifierProvider<TabList, EquatableCollection<List<String>>>.internal(
TabList.new,
name: r'tabListProvider',
debugGetCreateSourceHash:
@@ -19,6 +20,6 @@ final tabListProvider = NotifierProvider<TabList, List<String>>.internal(
allTransitiveDependencies: null,
);
typedef _$TabList = Notifier<List<String>>;
typedef _$TabList = Notifier<EquatableCollection<List<String>>>;
// 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
@@ -1,6 +1,7 @@
import 'dart:typed_data';
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'tab_session.g.dart';
@@ -49,3 +50,8 @@ class TabSession extends _$TabSession {
: GeckoSessionService.forActiveTab();
}
}
@Riverpod()
Raw<TabSession> selectedTabSessionNotifier(Ref ref) {
return ref.watch(tabSessionProvider(tabId: null).notifier);
}
@@ -6,6 +6,25 @@ part of 'tab_session.dart';
// RiverpodGenerator
// **************************************************************************
String _$selectedTabSessionNotifierHash() =>
r'aef19991d5b05a2bb28e8a6ccb57dc75c2f3148f';
/// See also [selectedTabSessionNotifier].
@ProviderFor(selectedTabSessionNotifier)
final selectedTabSessionNotifierProvider =
AutoDisposeProvider<Raw<TabSession>>.internal(
selectedTabSessionNotifier,
name: r'selectedTabSessionNotifierProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$selectedTabSessionNotifierHash,
dependencies: null,
allTransitiveDependencies: null,
);
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef SelectedTabSessionNotifierRef = AutoDisposeProviderRef<Raw<TabSession>>;
String _$tabSessionHash() => r'e4fbbcd430037cc01adb799783fb7974a11a27bf';
/// Copied from Dart SDK
@@ -3,24 +3,21 @@ import 'dart:async';
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:lensai/extensions/image.dart';
import 'package:lensai/extensions/nullable.dart';
import 'package:lensai/features/geckoview/domain/entities/find_result_state.dart';
import 'package:lensai/features/geckoview/domain/entities/history_state.dart';
import 'package:lensai/features/geckoview/domain/entities/readerable_state.dart';
import 'package:lensai/features/geckoview/domain/entities/security_state.dart';
import 'package:lensai/features/geckoview/domain/entities/tab_state.dart';
import 'package:lensai/features/geckoview/domain/entities/states/find_result.dart';
import 'package:lensai/features/geckoview/domain/entities/states/history.dart';
import 'package:lensai/features/geckoview/domain/entities/states/readerable.dart';
import 'package:lensai/features/geckoview/domain/entities/states/security.dart';
import 'package:lensai/features/geckoview/domain/entities/states/tab.dart';
import 'package:lensai/features/geckoview/domain/providers.dart';
import 'package:lensai/features/geckoview/domain/providers/selected_tab.dart';
import 'package:lensai/features/geckoview/utils/image_helper.dart';
import 'package:riverpod/riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:synchronized/synchronized.dart';
part 'tab_state.g.dart';
@Riverpod(keepAlive: true)
class TabStates extends _$TabStates {
final _lock = Lock();
void _onTabContentStateChange(TabContentState contentState) {
final current =
state[contentState.id] ?? TabState.$default(contentState.id);
@@ -137,25 +134,25 @@ class TabStates extends _$TabStates {
final subscriptions = [
eventService.tabContentEvents.listen((event) async {
await _lock.synchronized(() => _onTabContentStateChange(event));
_onTabContentStateChange(event);
}),
eventService.iconChangeEvents.listen((event) async {
await _lock.synchronized(() => _onIconChange(event));
await _onIconChange(event);
}),
eventService.thumbnailEvents.listen((event) async {
await _lock.synchronized(() => _onThumbnailChange(event));
await _onThumbnailChange(event);
}),
eventService.securityInfoEvents.listen((event) async {
await _lock.synchronized(() => _onSecurityInfoStateChange(event));
_onSecurityInfoStateChange(event);
}),
eventService.historyEvents.listen((event) async {
await _lock.synchronized(() => _onHistoryStateChange(event));
_onHistoryStateChange(event);
}),
eventService.readerableEvents.listen((event) async {
await _lock.synchronized(() => _onReaderableStateChange(event));
_onReaderableStateChange(event);
}),
eventService.findResultsEvent.listen((event) async {
await _lock.synchronized(() => _onFindResultsChange(event));
_onFindResultsChange(event);
}),
];
@@ -173,7 +173,7 @@ final selectedTabStateProvider = AutoDisposeProvider<TabState?>.internal(
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef SelectedTabStateRef = AutoDisposeProviderRef<TabState?>;
String _$tabStatesHash() => r'296d85ec016b22236b5830fce6b01cda81fbc02d';
String _$tabStatesHash() => r'c2169e467962d006015bf50d09e93b3f4bf596e6';
/// See also [TabStates].
@ProviderFor(TabStates)
@@ -5,11 +5,10 @@ import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:lensai/domain/entities/equatable_image.dart';
import 'package:lensai/extensions/image.dart';
import 'package:lensai/extensions/nullable.dart';
import 'package:lensai/features/geckoview/domain/entities/web_extension_state.dart';
import 'package:lensai/features/geckoview/domain/entities/states/web_extension.dart';
import 'package:lensai/features/geckoview/domain/providers.dart';
import 'package:lensai/features/geckoview/utils/image_helper.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:synchronized/synchronized.dart';
part 'web_extensions_state.g.dart';
@@ -17,8 +16,6 @@ part 'web_extensions_state.g.dart';
class WebExtensionsState extends _$WebExtensionsState {
final _imageCache = <String, EquatableImage>{};
final _lock = Lock();
void _onExtensionUpdate(ExtensionDataEvent event) {
final ExtensionDataEvent(:extensionId, :data) = event;
@@ -76,18 +73,18 @@ class WebExtensionsState extends _$WebExtensionsState {
final subscriptions = switch (actionType) {
WebExtensionActionType.browser => [
addonService.browserExtensionStream.listen((event) async {
await _lock.synchronized(() => _onExtensionUpdate(event));
_onExtensionUpdate(event);
}),
addonService.browserIconStream.listen((event) async {
await _lock.synchronized(() => _onIconChange(event));
await _onIconChange(event);
}),
],
WebExtensionActionType.page => [
addonService.pageExtensionStream.listen((event) async {
await _lock.synchronized(() => _onExtensionUpdate(event));
_onExtensionUpdate(event);
}),
addonService.pageIconStream.listen((event) async {
await _lock.synchronized(() => _onIconChange(event));
await _onIconChange(event);
}),
],
};
@@ -7,7 +7,7 @@ part of 'web_extensions_state.dart';
// **************************************************************************
String _$webExtensionsStateHash() =>
r'4e6dc46e49ff353e1c3cc4616008685c0e21501d';
r'7fd0cb313fb433f7896bfe0f89a4545f94f58793';
/// Copied from Dart SDK
class _SystemHash {
@@ -3,14 +3,16 @@ import 'dart:async';
import 'package:drift/drift.dart';
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:lensai/extensions/nullable.dart';
import 'package:lensai/features/geckoview/domain/entities/tab_state.dart';
import 'package:lensai/features/geckoview/domain/entities/states/tab.dart';
import 'package:lensai/features/geckoview/domain/providers.dart';
import 'package:lensai/features/geckoview/domain/providers/selected_tab.dart';
import 'package:lensai/features/geckoview/domain/providers/tab_list.dart';
import 'package:lensai/features/geckoview/domain/providers/tab_state.dart';
import 'package:lensai/features/geckoview/features/browser/domain/providers/intent.dart';
import 'package:lensai/features/geckoview/features/tabs/data/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/share_intent/domain/entities/shared_content.dart';
import 'package:lensai/utils/debouncer.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
@@ -93,9 +95,6 @@ class TabRepository extends _$TabRepository {
final eventSerivce = ref.watch(eventServiceProvider);
final tabContentService = ref.watch(tabContentServiceProvider);
final tabStateDebouncer = Debouncer(const Duration(seconds: 3));
Map<String, TabState>? debounceStartValue;
final db = ref.watch(tabDatabaseProvider);
final tabAddedSub = eventSerivce.tabAddedStream.listen((tabId) async {
@@ -127,13 +126,18 @@ class TabRepository extends _$TabRepository {
ref.listen(tabListProvider, (previous, next) async {
//Only sync tabs if there has been a previous value or is not empty
final syncTabs = next.isNotEmpty || (previous?.isNotEmpty ?? false);
final syncTabs =
next.collection.isNotEmpty ||
(previous?.collection.isNotEmpty ?? false);
if (syncTabs) {
await db.tabDao.syncTabs(retainTabIds: next);
await db.tabDao.syncTabs(retainTabIds: next.collection);
}
});
final tabStateDebouncer = Debouncer(const Duration(seconds: 3));
Map<String, TabState>? debounceStartValue;
ref.listen(tabStatesProvider, (previous, next) async {
//Since state changes occure pretty often and our map always contains
//the latest state, we cache the value before starting debouncing and
@@ -147,6 +151,16 @@ class TabRepository extends _$TabRepository {
});
});
ref.listen(intentStreamProvider, (previous, next) {
next.whenData((value) async {
switch (value) {
case SharedUrl():
await addTab(url: value.url);
case SharedText():
}
});
});
ref.onDispose(() {
tabStateDebouncer.dispose();
unawaited(tabAddedSub.cancel());
@@ -6,7 +6,7 @@ part of 'tab.dart';
// RiverpodGenerator
// **************************************************************************
String _$tabRepositoryHash() => r'85af29fa18ea5a4a67fbffbb9478c801dafddac2';
String _$tabRepositoryHash() => r'ce0db685127eeb706ce047d7afcc58fafd1394ba';
/// See also [TabRepository].
@ProviderFor(TabRepository)
@@ -1,24 +1,8 @@
import 'package:fast_equatable/fast_equatable.dart';
import 'package:lensai/features/kagi/data/entities/modes.dart';
sealed class Sheet with FastEquatable {}
class CreateTabSheet extends Sheet {
final String? content;
final KagiTool? preferredTool;
bool get hasParameters => content != null || preferredTool != null;
CreateTabSheet({this.content, this.preferredTool});
@override
bool get cacheHash => true;
@override
List<Object?> get hashParameters => [content, preferredTool];
}
class ViewTabsSheet extends Sheet {
final class ViewTabsSheet extends Sheet {
@override
bool get cacheHash => true;
@@ -26,7 +10,7 @@ class ViewTabsSheet extends Sheet {
List<Object?> get hashParameters => [];
}
class TabQaChatSheet extends Sheet {
final class TabQaChatSheet extends Sheet {
final String chatId;
TabQaChatSheet({required this.chatId});
@@ -1,17 +1,16 @@
import 'dart:async';
import 'package:lensai/data/models/equatable_iterable.dart';
import 'package:lensai/domain/entities/equatable_iterable.dart';
import 'package:lensai/extensions/nullable.dart';
import 'package:lensai/features/bangs/data/models/bang_data.dart';
import 'package:lensai/features/bangs/domain/repositories/data.dart';
import 'package:lensai/features/geckoview/domain/entities/tab_state.dart';
import 'package:lensai/features/geckoview/domain/entities/states/tab.dart';
import 'package:lensai/features/geckoview/domain/providers/tab_list.dart';
import 'package:lensai/features/geckoview/domain/providers/tab_state.dart';
import 'package:lensai/features/geckoview/features/search/domain/entities/tab_preview.dart';
import 'package:lensai/features/geckoview/features/tabs/data/entities/container_filter.dart';
import 'package:lensai/features/geckoview/features/tabs/domain/providers.dart';
import 'package:lensai/features/geckoview/features/tabs/domain/repositories/tab_search.dart';
import 'package:lensai/features/kagi/data/entities/modes.dart';
import 'package:riverpod/riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
@@ -58,48 +57,6 @@ class SelectedBangData extends _$SelectedBangData {
}
}
@Riverpod(keepAlive: true)
class LastUsedAssistantMode extends _$LastUsedAssistantMode {
// ignore: document_ignores api decision
// ignore: use_setters_to_change_properties
void update(AssistantMode mode) {
state = mode;
}
@override
AssistantMode build() {
return AssistantMode.research;
}
}
@Riverpod(keepAlive: true)
class ActiveResearchVariant extends _$ActiveResearchVariant {
// ignore: document_ignores api decision
// ignore: use_setters_to_change_properties
void update(ResearchVariant mode) {
state = mode;
}
@override
ResearchVariant build() {
return ResearchVariant.expert;
}
}
@Riverpod(keepAlive: true)
class ActiveChatModel extends _$ActiveChatModel {
// ignore: document_ignores api decision
// ignore: use_setters_to_change_properties
void update(ChatModel model) {
state = model;
}
@override
ChatModel build() {
return ChatModel.gpt4o;
}
}
@Riverpod(keepAlive: true)
class ShowFindInPage extends _$ShowFindInPage {
// ignore: document_ignores api decision
@@ -127,7 +84,10 @@ EquatableCollection<List<String>> availableTabIds(
final tabList = ref.watch(tabListProvider);
return EquatableCollection(
containerTabs?.where((tabId) => tabList.contains(tabId)).toList() ?? [],
containerTabs
?.where((tabId) => tabList.collection.contains(tabId))
.toList() ??
[],
immutable: true,
);
}
@@ -6,7 +6,7 @@ part of 'providers.dart';
// RiverpodGenerator
// **************************************************************************
String _$availableTabIdsHash() => r'd8b55b012b38111c8d5e069987a55645d1a71b89';
String _$availableTabIdsHash() => r'e5c5f25dea67f7739dfb1d0d9e4453079817a7bc';
/// Copied from Dart SDK
class _SystemHash {
@@ -908,56 +908,6 @@ class _SelectedBangDataProviderElement
String? get domain => (origin as SelectedBangDataProvider).domain;
}
String _$lastUsedAssistantModeHash() =>
r'1255a2754a3ea5ea058d110fd9743897fe103a69';
/// See also [LastUsedAssistantMode].
@ProviderFor(LastUsedAssistantMode)
final lastUsedAssistantModeProvider =
NotifierProvider<LastUsedAssistantMode, AssistantMode>.internal(
LastUsedAssistantMode.new,
name: r'lastUsedAssistantModeProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$lastUsedAssistantModeHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$LastUsedAssistantMode = Notifier<AssistantMode>;
String _$activeResearchVariantHash() =>
r'8c65a3f5ead6cc4f9e2af85b607b7a49b0764ee3';
/// See also [ActiveResearchVariant].
@ProviderFor(ActiveResearchVariant)
final activeResearchVariantProvider =
NotifierProvider<ActiveResearchVariant, ResearchVariant>.internal(
ActiveResearchVariant.new,
name: r'activeResearchVariantProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$activeResearchVariantHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$ActiveResearchVariant = Notifier<ResearchVariant>;
String _$activeChatModelHash() => r'675f406af8b4aa92699d59e37b5805a81d03a799';
/// See also [ActiveChatModel].
@ProviderFor(ActiveChatModel)
final activeChatModelProvider =
NotifierProvider<ActiveChatModel, ChatModel>.internal(
ActiveChatModel.new,
name: r'activeChatModelProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$activeChatModelHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$ActiveChatModel = Notifier<ChatModel>;
String _$showFindInPageHash() => r'7ee5703f7d0c7e8a8dd6b0849d7b1e0a41fe24d9';
/// See also [ShowFindInPage].
@@ -0,0 +1,46 @@
import 'dart:async';
import 'package:lensai/data/models/received_intent_parameter.dart';
import 'package:lensai/extensions/nullable.dart';
import 'package:lensai/features/app_widget/domain/services/home_widget.dart';
import 'package:lensai/features/share_intent/domain/entities/shared_content.dart';
import 'package:lensai/features/share_intent/domain/services/sharing_intent.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:rxdart/rxdart.dart';
part 'intent.g.dart';
final _contentParserTransformer =
StreamTransformer<ReceivedIntentParameter, SharedContent>.fromHandlers(
handleData: (parameter, sink) {
final parsed = parameter.content.mapNotNull(
(content) => SharedContent.parse(content),
);
if (parsed != null) {
sink.add(parsed);
}
},
);
@Riverpod()
class IntentStream extends _$IntentStream {
late StreamController<SharedContent> _streamController;
@override
Stream<SharedContent> build() {
_streamController = StreamController();
ref.onDispose(() async {
await _streamController.close();
});
final sharingItentStream = ref.watch(sharingIntentStreamProvider);
final appWidgetLaunchStream = ref.watch(appWidgetLaunchStreamProvider);
return MergeStream([
sharingItentStream.transform(_contentParserTransformer),
appWidgetLaunchStream.transform(_contentParserTransformer),
_streamController.stream,
]);
}
}
@@ -0,0 +1,25 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'intent.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$intentStreamHash() => r'e78268ec703cd0d1f08d41b9e10a487e6000b7da';
/// See also [IntentStream].
@ProviderFor(IntentStream)
final intentStreamProvider =
AutoDisposeStreamNotifierProvider<IntentStream, SharedContent>.internal(
IntentStream.new,
name: r'intentStreamProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') ? null : _$intentStreamHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$IntentStream = AutoDisposeStreamNotifier<SharedContent>;
// 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
@@ -2,10 +2,10 @@ import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:lensai/features/user/data/models/general_settings.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'delete_browser_data.g.dart';
part 'browser_data.g.dart';
@Riverpod()
class DeleteBrowserDataService extends _$DeleteBrowserDataService {
class BrowserDataService extends _$BrowserDataService {
final _service = GeckoDeleteBrowserDataService();
Future<void> deleteData(Set<DeleteBrowsingDataType>? types) async {
@@ -1,27 +1,27 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'session.dart';
part of 'browser_data.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$kagiSessionServiceHash() =>
r'8bed5d74d319ff2f874f42bc8596d02f05347c1f';
String _$browserDataServiceHash() =>
r'f56c60635505e06dbd5b05f1bcee9c25c4d0eaaf';
/// See also [KagiSessionService].
@ProviderFor(KagiSessionService)
final kagiSessionServiceProvider =
AutoDisposeNotifierProvider<KagiSessionService, void>.internal(
KagiSessionService.new,
name: r'kagiSessionServiceProvider',
/// See also [BrowserDataService].
@ProviderFor(BrowserDataService)
final browserDataServiceProvider =
AutoDisposeNotifierProvider<BrowserDataService, void>.internal(
BrowserDataService.new,
name: r'browserDataServiceProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$kagiSessionServiceHash,
: _$browserDataServiceHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$KagiSessionService = AutoDisposeNotifier<void>;
typedef _$BrowserDataService = 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
@@ -1,56 +0,0 @@
import 'dart:async';
import 'package:collection/collection.dart';
import 'package:lensai/data/models/received_parameter.dart';
import 'package:lensai/features/app_widget/domain/services/home_widget.dart';
import 'package:lensai/features/geckoview/features/browser/domain/entities/sheet.dart';
import 'package:lensai/features/kagi/data/entities/modes.dart';
import 'package:lensai/features/share_intent/domain/services/sharing_intent.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:rxdart/rxdart.dart';
part 'create_tab.g.dart';
final _parameterToCreateTabSheetTransformer =
StreamTransformer<ReceivedParameter, CreateTabSheet>.fromHandlers(
handleData: (parameter, sink) {
final createTab = CreateTabSheet(
preferredTool: KagiTool.values.firstWhereOrNull(
(tool) => tool.name == parameter.tool,
),
content: parameter.content,
);
if (createTab.hasParameters) {
sink.add(createTab);
}
},
);
@Riverpod()
class CreateTabStream extends _$CreateTabStream {
late StreamController<CreateTabSheet> _streamController;
@override
Stream<CreateTabSheet> build() {
_streamController = StreamController();
ref.onDispose(() async {
await _streamController.close();
});
final sharingItentStream = ref.watch(sharingIntentStreamProvider);
final appWidgetLaunchStream = ref.watch(appWidgetLaunchStreamProvider);
return MergeStream([
sharingItentStream.transform(_parameterToCreateTabSheetTransformer),
appWidgetLaunchStream.transform(_parameterToCreateTabSheetTransformer),
_streamController.stream,
]);
}
void createTab(CreateTabSheet parameter) {
if (!_streamController.isClosed) {
_streamController.add(parameter);
}
}
}
@@ -1,27 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'delete_browser_data.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$deleteBrowserDataServiceHash() =>
r'f5fdbac17c14693fceac913591750ca9285b496c';
/// See also [DeleteBrowserDataService].
@ProviderFor(DeleteBrowserDataService)
final deleteBrowserDataServiceProvider =
AutoDisposeNotifierProvider<DeleteBrowserDataService, void>.internal(
DeleteBrowserDataService.new,
name: r'deleteBrowserDataServiceProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$deleteBrowserDataServiceHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$DeleteBrowserDataService = 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
@@ -1,5 +1,5 @@
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:lensai/data/models/equatable_iterable.dart';
import 'package:lensai/domain/entities/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';
@@ -1,104 +0,0 @@
// import 'package:collection/collection.dart';
// import 'package:flutter/material.dart';
// import 'package:flutter_hooks/flutter_hooks.dart';
// import 'package:hooks_riverpod/hooks_riverpod.dart';
// import 'package:lensai/data/models/equatable_iterable.dart';
// import 'package:lensai/extensions/nullable.dart';
// import 'package:lensai/features/geckoview/domain/entities/tab_state.dart';
// import 'package:lensai/features/geckoview/domain/providers/tab_state.dart';
// import 'package:lensai/features/geckoview/features/tabs/domain/providers.dart';
// import 'package:lensai/features/geckoview/features/tabs/domain/repositories/tab.dart';
// class TabActionDialog extends HookConsumerWidget {
// final TabState initialTab;
// final void Function()? onDismiss;
// const TabActionDialog({
// required this.initialTab,
// this.onDismiss,
// super.key,
// });
// @override
// Widget build(BuildContext context, WidgetRef ref) {
// final tabState = ref.watch(tabStateProvider(initialTab.id)) ?? initialTab;
// final tabContainerId = ref.watch(
// tabContainerIdProvider(initialTab.id)
// .select((value) => value.valueOrNull),
// );
// final containers = ref
// .watch(
// containersWithCountProvider.select(
// (value) => EquatableCollection(value.valueOrNull, immutable: true),
// ),
// )
// .collection;
// final selectedContainer = containers
// ?.firstWhereOrNull((container) => container.id == tabContainerId);
// final expansionController = useExpansionTileController();
// return Stack(
// children: [
// ModalBarrier(
// color: Theme.of(context).dialogTheme.barrierColor ?? Colors.black54,
// onDismiss: onDismiss,
// ),
// SimpleDialog(
// titlePadding: const EdgeInsets.fromLTRB(16.0, 12.0, 16.0, 0.0),
// contentPadding: EdgeInsets.zero,
// insetPadding: const EdgeInsets.symmetric(
// horizontal: 20.0,
// vertical: 24.0,
// ),
// title: ListTile(
// leading: RawImage(
// image: tabState.icon?.value,
// width: 24,
// height: 24,
// ),
// contentPadding: EdgeInsets.zero,
// title: Text(tabState.title),
// subtitle: Text(tabState.url.authority),
// ),
// children: [
// SizedBox(
// //We need this to stretch the dialog, then padding from dialog is applied
// width: double.maxFinite,
// child: ExpansionTile(
// controller: expansionController,
// leading: selectedContainer?.color.mapNotNull(
// (color) => CircleAvatar(backgroundColor: color),
// ),
// title: (selectedContainer != null)
// ? Text(selectedContainer.name ?? 'New Container')
// : const Text('Assign a Container'),
// children: containers
// ?.where((container) => container.id != tabContainerId)
// .map(
// (container) => ListTile(
// leading:
// CircleAvatar(backgroundColor: container.color),
// title: Text(container.name ?? 'New Container'),
// onTap: () async {
// await ref
// .read(tabDataRepositoryProvider.notifier)
// .assignContainer(initialTab.id, container.id);
// expansionController.collapse();
// },
// ),
// )
// .toList() ??
// [],
// ),
// ),
// ],
// ),
// ],
// );
// }
// }
@@ -3,7 +3,6 @@ import 'dart:ui' as ui;
import 'package:fading_scroll/fading_scroll.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
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';
@@ -14,22 +13,16 @@ import 'package:lensai/features/bangs/presentation/widgets/site_search.dart';
import 'package:lensai/features/chat/features/chat_store/data/models/chat_metadata.dart';
import 'package:lensai/features/chat/features/chat_store/domain/repositories/chat_metadata.dart';
import 'package:lensai/features/geckoview/domain/controllers/bottom_sheet.dart';
import 'package:lensai/features/geckoview/domain/providers.dart';
import 'package:lensai/features/geckoview/domain/providers/tab_session.dart';
import 'package:lensai/features/geckoview/domain/providers/tab_state.dart';
import 'package:lensai/features/geckoview/domain/repositories/tab.dart';
import 'package:lensai/features/geckoview/features/browser/domain/entities/sheet.dart';
import 'package:lensai/features/geckoview/features/search/domain/providers/engine_suggestions.dart';
import 'package:lensai/features/kagi/data/entities/modes.dart';
import 'package:lensai/features/kagi/utils/url_builder.dart' as uri_builder;
import 'package:lensai/features/share_intent/domain/entities/shared_content.dart';
import 'package:lensai/features/geckoview/features/browser/presentation/widgets/browser_modules/address_with_suggestions_field.dart';
import 'package:lensai/features/user/domain/providers.dart';
import 'package:lensai/presentation/hooks/listenable_callback.dart';
import 'package:lensai/presentation/widgets/auto_suggest_text_field.dart';
import 'package:lensai/presentation/widgets/failure_widget.dart';
import 'package:lensai/presentation/widgets/website_title_tile.dart';
import 'package:lensai/utils/ui_helper.dart' as ui_helper;
import 'package:lensai/utils/uri_parser.dart' as uri_parser;
import 'package:share_plus/share_plus.dart';
class WebPageDialog extends HookConsumerWidget {
@@ -43,14 +36,7 @@ class WebPageDialog extends HookConsumerWidget {
final incognitoEnabled = ref.watch(incognitoModeEnabledProvider);
final availableBangsAsync = ref.watch(
bangDataListProvider(
filter: (
domain: url.host,
groups: null,
categoryFilter: null,
orderMostFrequentFirst: true,
),
),
bangListProvider(domain: url.host, orderMostFrequentFirst: true),
);
final availableBangCount = availableBangsAsync.valueOrNull?.length;
@@ -75,74 +61,9 @@ class WebPageDialog extends HookConsumerWidget {
width: double.maxFinite,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: HookConsumer(
builder: (context, ref, child) {
final addressTextController = useTextEditingController(
text: url.toString(),
);
final addressTextFocusNode = useFocusNode();
final suggestion = useState<String?>(null);
useListenableCallback(addressTextController, () async {
if (addressTextController.text.isNotEmpty) {
final result = await ref
.read(engineSuggestionsProvider.notifier)
.getAutocompleteSuggestion(
addressTextController.text,
);
suggestion.value = result;
}
});
return AutoSuggestTextField(
controller: addressTextController,
focusNode: addressTextFocusNode,
suggestion: suggestion.value,
enableIMEPersonalizedLearning: !incognitoEnabled,
keyboardType: TextInputType.url,
decoration: const InputDecoration(
label: Text('Address'),
floatingLabelBehavior: FloatingLabelBehavior.always,
),
onTap: () {
if (!addressTextFocusNode.hasFocus) {
// Select all text when the field is tapped
addressTextController.selection = TextSelection(
baseOffset: 0,
extentOffset: addressTextController.text.length,
);
}
},
onSubmitted: (value) async {
if (value.isNotEmpty) {
var newUrl = uri_parser.tryParseUrl(
value,
eagerParsing: true,
);
if (newUrl == null) {
final defaultSearchBang = await ref.read(
defaultSearchBangDataProvider.future,
);
newUrl = defaultSearchBang?.getUrl(value);
}
if (newUrl != null) {
await ref
.read(tabSessionProvider(tabId: null).notifier)
.loadUrl(url: newUrl);
if (context.mounted) {
context.pop();
}
}
}
},
);
},
child: AddressWithSuggestionsField(
url: url,
incognitoEnabled: incognitoEnabled,
),
),
),
@@ -273,7 +194,7 @@ class WebPageDialog extends HookConsumerWidget {
),
const Divider(),
ListTile(
leading: Icon(KagiTool.assistant.icon),
leading: const Icon(MdiIcons.brain),
title: const Text('QA Chat'),
onTap: () async {
final selectedTabId =
@@ -306,24 +227,24 @@ class WebPageDialog extends HookConsumerWidget {
}
},
),
ListTile(
leading: Icon(KagiTool.summarizer.icon),
title: const Text('Summarize'),
onTap: () async {
final summarizerUrl = uri_builder.summarizerUri(
document: SharedUrl(url),
mode: SummarizerMode.keyMoments,
);
// ListTile(
// leading: Icon(MdiIcons.text),
// title: const Text('Summarize'),
// onTap: () async {
// final summarizerUrl = uri_builder.summarizerUri(
// document: SharedUrl(url),
// mode: SummarizerMode.keyMoments,
// );
await ref
.read(tabRepositoryProvider.notifier)
.addTab(url: summarizerUrl);
// await ref
// .read(tabRepositoryProvider.notifier)
// .addTab(url: summarizerUrl);
if (context.mounted) {
context.pop();
}
},
),
// if (context.mounted) {
// context.pop();
// }
// },
// ),
],
),
);
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,85 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:lensai/features/bangs/domain/providers/bangs.dart';
import 'package:lensai/features/geckoview/domain/providers/tab_session.dart';
import 'package:lensai/features/geckoview/features/search/domain/providers/engine_suggestions.dart';
import 'package:lensai/presentation/hooks/listenable_callback.dart';
import 'package:lensai/presentation/widgets/auto_suggest_text_field.dart';
import 'package:lensai/utils/uri_parser.dart' as uri_parser;
class AddressWithSuggestionsField extends HookConsumerWidget {
const AddressWithSuggestionsField({
super.key,
required this.url,
required this.incognitoEnabled,
});
final Uri url;
final bool incognitoEnabled;
@override
Widget build(BuildContext context, WidgetRef ref) {
final addressTextController = useTextEditingController(
text: url.toString(),
);
final addressTextFocusNode = useFocusNode();
final suggestion = useState<String?>(null);
useListenableCallback(addressTextController, () async {
if (addressTextController.text.isNotEmpty) {
final result = await ref
.read(engineSuggestionsProvider.notifier)
.getAutocompleteSuggestion(addressTextController.text);
suggestion.value = result;
}
});
return AutoSuggestTextField(
controller: addressTextController,
focusNode: addressTextFocusNode,
suggestion: suggestion.value,
enableIMEPersonalizedLearning: !incognitoEnabled,
keyboardType: TextInputType.url,
decoration: const InputDecoration(
label: Text('Address'),
floatingLabelBehavior: FloatingLabelBehavior.always,
),
onTap: () {
if (!addressTextFocusNode.hasFocus) {
// Select all text when the field is tapped
addressTextController.selection = TextSelection(
baseOffset: 0,
extentOffset: addressTextController.text.length,
);
}
},
onSubmitted: (value) async {
if (value.isNotEmpty) {
var newUrl = uri_parser.tryParseUrl(value, eagerParsing: true);
if (newUrl == null) {
final defaultSearchBang = await ref.read(
defaultSearchBangDataProvider.future,
);
newUrl = defaultSearchBang?.getUrl(value);
}
if (newUrl != null) {
await ref
.read(tabSessionProvider(tabId: null).notifier)
.loadUrl(url: newUrl);
if (context.mounted) {
context.pop();
}
}
}
},
);
}
}
@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
import 'package:lensai/features/geckoview/domain/entities/tab_state.dart';
import 'package:lensai/features/geckoview/domain/entities/states/tab.dart';
import 'package:skeletonizer/skeletonizer.dart';
import 'package:text_scroll/text_scroll.dart';
@@ -0,0 +1,373 @@
import 'package:flutter/material.dart';
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:lensai/core/routing/routes.dart';
import 'package:lensai/features/geckoview/domain/controllers/bottom_sheet.dart';
import 'package:lensai/features/geckoview/domain/providers.dart';
import 'package:lensai/features/geckoview/domain/providers/tab_session.dart';
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/entities/sheet.dart';
import 'package:lensai/features/geckoview/features/browser/presentation/widgets/browser_modules/app_bar_title.dart';
import 'package:lensai/features/geckoview/features/browser/presentation/widgets/edit_url_dialog.dart';
import 'package:lensai/features/geckoview/features/browser/presentation/widgets/extension_badge_icon.dart';
import 'package:lensai/features/geckoview/features/browser/presentation/widgets/tabs_action_button.dart';
import 'package:lensai/features/geckoview/features/find_in_page/presentation/controllers/find_in_page_visibility.dart';
import 'package:lensai/features/geckoview/features/readerview/presentation/widgets/reader_button.dart';
import 'package:lensai/presentation/hooks/menu_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';
class BrowserBottomAppBar extends HookConsumerWidget {
const BrowserBottomAppBar({
required this.selectedTabId,
required this.displayedSheet,
});
final String? selectedTabId;
final Sheet? displayedSheet;
@override
Widget build(BuildContext context, WidgetRef ref) {
final addonService = ref.watch(addonServiceProvider);
final tabMenuController = useMenuController();
final trippleDotMenuController = useMenuController();
return BottomAppBar(
height: AppBar().preferredSize.height,
padding: EdgeInsets.zero,
child: AppBar(
automaticallyImplyLeading: false,
titleSpacing: 8.0,
title:
(selectedTabId != null && displayedSheet is! ViewTabsSheet)
? HookConsumer(
builder: (context, ref, child) {
final tabState = ref.watch(tabStateProvider(selectedTabId));
return (tabState != null)
? AppBarTitle(
tab: tabState,
onTap: () async {
await context.push(
WebPageRoute(
url: tabState.url.toString(),
).location,
extra: tabState,
);
},
onDoubleTap: () async {
final newUrl = await showDialog<Uri?>(
context: context,
builder:
(context) =>
EditUrlDialog(initialUrl: tabState.url),
);
if (newUrl != null) {
await ref
.read(
tabSessionProvider(tabId: null).notifier,
)
.loadUrl(url: newUrl);
}
},
)
: const SizedBox.shrink();
},
)
: null,
actions: [
if (selectedTabId != null && displayedSheet is! ViewTabsSheet)
ReaderButton(),
MenuAnchor(
controller: tabMenuController,
builder: (context, controller, child) {
return child!;
},
menuChildren: [
if (selectedTabId != null)
MenuItemButton(
onPressed: () async {
await ref
.read(tabRepositoryProvider.notifier)
.closeTab(selectedTabId!);
},
leadingIcon: const Icon(Icons.close),
child: const Text('Close Tab'),
),
MenuItemButton(
onPressed: () async {
await context.push(const SearchRoute().location);
},
leadingIcon: const Icon(Icons.add),
child: const Text('Add Tab'),
),
],
child: TabsActionButton(
isActive: displayedSheet is ViewTabsSheet,
onTap: () {
if (displayedSheet case ViewTabsSheet()) {
ref.read(bottomSheetControllerProvider.notifier).dismiss();
} else {
ref
.read(bottomSheetControllerProvider.notifier)
.show(ViewTabsSheet());
}
},
onLongPress: () {
if (tabMenuController.isOpen) {
tabMenuController.close();
} else {
tabMenuController.open();
}
},
),
),
MenuAnchor(
controller: trippleDotMenuController,
builder: (context, controller, child) {
return Padding(
padding: const EdgeInsets.only(right: 4.0),
child: InkWell(
onTap: () {
if (controller.isOpen) {
controller.close();
} else {
controller.open();
}
},
child: const Padding(
padding: EdgeInsets.symmetric(
vertical: 15.0,
horizontal: 8.0,
),
child: Icon(Icons.more_vert),
),
),
);
},
menuChildren: [
Consumer(
builder: (context, childRef, child) {
final pageExtensions = childRef.watch(
webExtensionsStateProvider(
WebExtensionActionType.page,
).select((value) => value.values.toList()),
);
return Wrap(
alignment: WrapAlignment.center,
children: [
...pageExtensions.map(
(extension) => IconButton(
onPressed: () async {
//Use parents .ref because after onPressed this consumer gets disposed already
await addonService.invokeAddonAction(
extension.extensionId,
WebExtensionActionType.page,
);
},
icon: ExtensionBadgeIcon(extension),
),
),
],
);
},
),
MenuItemButton(
onPressed: () async {
await context.push(AboutRoute().location);
},
leadingIcon: const Icon(Icons.info),
child: const Text('About'),
),
const Divider(),
MenuItemButton(
onPressed: () async {
await context.push(SettingsRoute().location);
},
leadingIcon: const Icon(Icons.settings),
child: const Text('Settings'),
),
Consumer(
builder: (context, ref, child) {
final browserExtensions = ref.watch(
webExtensionsStateProvider(
WebExtensionActionType.browser,
).select((value) => value.values.toList()),
);
return SubmenuButton(
menuChildren: [
...browserExtensions.map(
(extension) => MenuItemButton(
onPressed: () async {
//Use parents .ref because after onPressed this consumer gets disposed already
await addonService.invokeAddonAction(
extension.extensionId,
WebExtensionActionType.browser,
);
},
leadingIcon: ExtensionBadgeIcon(extension),
child: Text(extension.title ?? ''),
),
),
MenuItemButton(
onPressed: () async {
await addonService.startAddonManagerActivity();
},
leadingIcon: const Icon(MdiIcons.puzzleEdit),
child: const Text('Addon Manager'),
),
],
leadingIcon: const Icon(MdiIcons.puzzle),
child: const Text('Addons'),
);
},
),
MenuItemButton(
onPressed: () async {
await context.push(TorProxyRoute().location);
},
leadingIcon: const Icon(TorIcons.onionAlt),
child: const Text('Tor'),
),
const Divider(),
MenuItemButton(
onPressed: () async {
await context.push(BangCategoriesRoute().location);
},
leadingIcon: const Icon(MdiIcons.exclamationThick),
child: const Text('Bangs'),
),
const Divider(),
if (selectedTabId != null)
MenuItemButton(
onPressed: () async {
final tabState = ref.read(tabStateProvider(selectedTabId));
if (tabState?.url case final Uri url) {
await ui_helper.launchUrlFeedback(context, url);
}
},
leadingIcon: const Icon(Icons.open_in_browser),
child: const Text('Launch External'),
),
if (selectedTabId != null)
MenuItemButton(
onPressed: () async {
final tabState = ref.read(tabStateProvider(selectedTabId));
if (tabState?.url case final Uri url) {
await Share.shareUri(url);
}
},
leadingIcon: const Icon(Icons.share),
child: const Text('Share'),
),
if (selectedTabId != null) const Divider(),
if (selectedTabId != null)
MenuItemButton(
onPressed: () {
ref
.read(findInPageVisibilityControllerProvider.notifier)
.show();
},
leadingIcon: const Icon(Icons.search),
child: const Text('Find in page'),
),
if (selectedTabId != null) const Divider(),
if (selectedTabId != null)
MenuItemButton(
onPressed: () async {
final controller = ref.read(
tabSessionProvider(tabId: selectedTabId).notifier,
);
await controller.reload();
trippleDotMenuController.close();
},
leadingIcon: const Icon(Icons.refresh),
child: const Text('Reload'),
),
if (selectedTabId != null) const Divider(),
if (selectedTabId != null)
Consumer(
builder: (context, ref, child) {
final history = ref.watch(
tabStateProvider(
selectedTabId,
).select((value) => value?.historyState),
);
return Row(
children: [
Expanded(
child:
(history?.canGoBack == true)
? IconButton(
onPressed: () async {
final controller = ref.read(
tabSessionProvider(
tabId: selectedTabId,
).notifier,
);
await controller.goBack();
trippleDotMenuController.close();
},
icon: const Icon(Icons.arrow_back),
)
: IconButton(
onPressed: () async {
await ref
.read(tabRepositoryProvider.notifier)
.closeTab(selectedTabId!);
trippleDotMenuController.close();
},
icon: const Icon(Icons.close),
),
),
const SizedBox(height: 48, child: VerticalDivider()),
Expanded(
child: IconButton(
onPressed:
(history?.canGoForward == true)
? () async {
final controller = ref.read(
tabSessionProvider(
tabId: selectedTabId,
).notifier,
);
await controller.goForward();
trippleDotMenuController.close();
}
: null,
icon: const Icon(Icons.arrow_forward),
),
),
],
);
},
),
MenuItemButton(
onPressed: () async {
await context.push(UserAuthRoute().location);
},
leadingIcon: const Icon(Icons.info),
child: const Text('Auth'),
),
],
),
],
),
);
}
}
@@ -6,10 +6,11 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:lensai/core/logger.dart';
import 'package:lensai/features/geckoview/domain/providers.dart';
import 'package:lensai/features/geckoview/domain/providers/selected_tab.dart';
import 'package:lensai/features/geckoview/domain/providers/tab_session.dart';
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/browser_data.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';
@@ -87,7 +88,7 @@ class _BrowserViewState extends ConsumerState<BrowserView>
.fetch()
.then((settings) {
ref
.read(deleteBrowserDataServiceProvider.notifier)
.read(browserDataServiceProvider.notifier)
.deleteData(settings.deleteBrowsingDataOnQuit);
});
@@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:lensai/features/geckoview/domain/entities/web_extension_state.dart';
import 'package:lensai/features/geckoview/domain/entities/states/web_extension.dart';
class ExtensionBadgeIcon extends StatelessWidget {
final WebExtensionState state;
@@ -1,39 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:lensai/core/routing/routes.dart';
import 'package:lensai/features/geckoview/features/browser/presentation/widgets/error_container.dart';
class LandingAction extends HookConsumerWidget {
const LandingAction({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
//TODO: REWORK!
final sessionTokenAvailable = false;
// ref.watch(
// generalSettingsRepositoryProvider.select(
// (value) => value.valueOrNull?.kagiSession?.isNotEmpty ?? false,
// ),
// );
return Visibility(
visible: !sessionTokenAvailable,
child: ErrorContainer(
content: Markdown(
shrinkWrap: true,
onTapLink: (text, href, title) async {
if (href == 'settings') {
await context.push(SettingsRoute().location);
}
},
data:
'## No Kagi Session Provided\n'
'Please navigate to [Settings](settings) and enter your Kagi Session Token.\n'
"You must provide a valid session in order to use Kagi's features.",
),
),
);
}
}
@@ -1,151 +0,0 @@
import 'package:fading_scroll/fading_scroll.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:lensai/data/models/equatable_iterable.dart';
import 'package:lensai/domain/services/app_initialization.dart';
import 'package:lensai/features/geckoview/domain/repositories/tab.dart';
import 'package:lensai/features/geckoview/features/browser/presentation/widgets/error_container.dart';
import 'package:lensai/features/geckoview/features/browser/presentation/widgets/landing/action.dart';
import 'package:lensai/presentation/hooks/cached_future.dart';
class LandingContent extends HookConsumerWidget {
const LandingContent({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final textTheme = Theme.of(context).textTheme;
final descriptionAsset = useCachedFuture(
() async => rootBundle.loadString('assets/landing/description.md'),
);
final changelogAsset = useCachedFuture(
() async => rootBundle.loadString('assets/landing/changelog.md'),
);
return FadingScroll(
fadingSize: 25,
builder: (context, controller) {
return SingleChildScrollView(
controller: controller,
child: Column(
children: [
Text(
'Lensai',
style: textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Text(
'The Privacy-Focused & AI-Powered Research Browser with Kagi integration',
style: textTheme.titleMedium,
textAlign: TextAlign.center,
),
),
FractionallySizedBox(
widthFactor: 0.5,
child: Image.asset('assets/icon/icon.png'),
),
const LandingAction(),
Consumer(
builder: (context, ref, child) {
final errors =
ref
.watch(
appInitializationServiceProvider.select(
(result) => EquatableCollection(
result.valueOrNull?.errors,
immutable: false,
),
),
)
.collection;
if (errors == null || errors.isEmpty) {
return const SizedBox.shrink();
}
return Column(
mainAxisSize: MainAxisSize.min,
children: [
...errors.map((error) {
final theme = Theme.of(context);
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: ErrorContainer(
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Error during App Initialization!',
style: theme.textTheme.titleMedium,
),
Text(
error.message,
style: theme.textTheme.titleSmall,
),
if (error.details != null)
Text(error.details.toString()),
],
),
),
);
}),
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: SizedBox(
width: double.infinity,
child: FilledButton.tonalIcon(
onPressed: () async {
await ref
.read(
appInitializationServiceProvider.notifier,
)
.reinitialize();
},
style: OutlinedButton.styleFrom(
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
label: const Text('Restart App'),
icon: const Icon(Icons.refresh_outlined),
),
),
),
],
);
},
),
Markdown(
data: descriptionAsset.data ?? '',
onTapLink: (text, href, title) async {
if (href != null) {
await ref
.read(tabRepositoryProvider.notifier)
.addTab(url: Uri.parse(href));
}
},
selectable: true,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
),
Text(
'Changelog',
style: textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
MarkdownBody(data: changelogAsset.data ?? '', selectable: true),
],
),
);
},
);
}
}
@@ -1,111 +0,0 @@
import 'package:expandable_page_view/expandable_page_view.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:lensai/extensions/nullable.dart';
import 'package:lensai/features/geckoview/features/browser/domain/entities/sheet.dart';
import 'package:lensai/features/kagi/data/entities/modes.dart';
import 'package:lensai/features/kagi/presentation/tabs/summarize_tab.dart';
import 'package:lensai/features/share_intent/domain/entities/shared_content.dart';
import 'package:lensai/presentation/hooks/sync_page_tab.dart';
typedef OnSubmitUri = void Function(Uri url);
class CreateTabSheetWidget extends HookConsumerWidget {
final CreateTabSheet parameter;
final OnSubmitUri onSubmit;
final void Function(KagiTool tool)? onActiveToolChanged;
const CreateTabSheetWidget({
required this.parameter,
required this.onSubmit,
this.onActiveToolChanged,
super.key,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final sharedContent = useMemoized(
() => parameter.content.mapNotNull(
(content) => SharedContent.parse(content),
),
[parameter],
);
final initialIndex = useMemoized(
() =>
parameter.preferredTool?.index ??
switch (sharedContent) {
SharedText(text: final _) => KagiTool.search.index,
// (showEarlyAccessFeatures && text.length > 25)
// ? KagiTool.assistant.index
// : KagiTool.search.index,
SharedUrl() => KagiTool.summarizer.index,
null => KagiTool.search.index,
// showEarlyAccessFeatures
// ? KagiTool.assistant.index
// : KagiTool.search.index,
},
[sharedContent],
);
final tabController = useTabController(
initialLength: KagiTool.values.length - 1,
initialIndex: initialIndex,
);
final pageController = usePageController(initialPage: tabController.index);
useSyncPageWithTab(
tabController,
pageController,
onIndexChanged: (index) {
onActiveToolChanged?.call(KagiTool.values[index]);
},
);
useEffect(() {
WidgetsBinding.instance.addPostFrameCallback((_) {
onActiveToolChanged?.call(KagiTool.values[initialIndex]);
});
return null;
}, [initialIndex]);
return Column(
mainAxisSize: MainAxisSize.min,
children: [
TabBar(
controller: tabController,
tabs: [
// Tab(
// icon: Icon(KagiTool.search.icon),
// text: 'Search',
// ),
Tab(icon: Icon(KagiTool.summarizer.icon), text: 'Summarize'),
// if (showEarlyAccessFeatures)
// Tab(
// icon: Icon(KagiTool.assistant.icon),
// text: 'Assistant',
// ),
],
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 12.0),
child: ExpandablePageView(
controller: pageController,
children: [
// SearchTab(
// sharedContent: sharedContent,
// onSubmit: onSubmit,
// ),
SummarizeTab(sharedContent: sharedContent, onSubmit: onSubmit),
// if (showEarlyAccessFeatures)
// AssistantTab(
// sharedContent: sharedContent,
// onSubmit: onSubmit,
// ),
],
),
),
],
);
}
}
@@ -10,8 +10,6 @@ import 'package:lensai/core/providers/global_drop.dart';
import 'package:lensai/core/routing/routes.dart';
import 'package:lensai/data/models/drag_data.dart';
import 'package:lensai/features/geckoview/domain/providers/selected_tab.dart';
import 'package:lensai/features/geckoview/domain/providers/tab_state.dart';
import 'package:lensai/features/geckoview/domain/repositories/tab.dart';
import 'package:lensai/features/geckoview/features/browser/domain/providers.dart';
import 'package:lensai/features/geckoview/features/browser/presentation/widgets/draggable_scrollable_header.dart';
import 'package:lensai/features/geckoview/features/browser/presentation/widgets/tab_preview.dart';
@@ -25,60 +23,12 @@ import 'package:lensai/features/tor/presentation/controllers/start_tor_proxy.dar
import 'package:lensai/presentation/hooks/listenable_callback.dart';
import 'package:lensai/presentation/widgets/speech_to_text_button.dart';
class _TabDraggable extends HookConsumerWidget {
final String tabId;
final String? activeTabId;
final void Function() onClose;
_TabDraggable({
required this.tabId,
required this.activeTabId,
required this.onClose,
}) : super(key: ValueKey(tabId));
@override
Widget build(BuildContext context, WidgetRef ref) {
final tab = ref.watch(tabStateProvider(tabId));
if (tab == null) {
return const SizedBox.shrink();
}
return TabPreview(
tab: tab,
isActive: tabId == activeTabId,
onTap: () async {
if (tabId != activeTabId) {
//Close first to avoid rebuilds
onClose();
await ref.read(tabRepositoryProvider.notifier).selectTab(tab.id);
} else {
onClose();
}
},
// onDoubleTap: () {
// ref.read(overlayDialogControllerProvider.notifier).show(
// TabActionDialog(
// initialTab: tab,
// onDismiss:
// ref.read(overlayDialogControllerProvider.notifier).dismiss,
// ),
// );
// },
onDelete: () async {
await ref.read(tabRepositoryProvider.notifier).closeTab(tab.id);
},
);
}
}
class _Tab extends HookConsumerWidget {
class _TabSheetHeader extends HookConsumerWidget {
static const headerSize = 124.0;
final VoidCallback onClose;
const _Tab({required this.onClose});
const _TabSheetHeader({required this.onClose});
@override
Widget build(BuildContext context, WidgetRef ref) {
@@ -245,7 +195,7 @@ class ViewTabsSheetWidget extends HookConsumerWidget {
children: [
DraggableScrollableHeader(
controller: draggableScrollableController,
child: _Tab(onClose: onClose),
child: _TabSheetHeader(onClose: onClose),
),
Expanded(
child: HookConsumer(
@@ -304,7 +254,7 @@ class ViewTabsSheetWidget extends HookConsumerWidget {
key: Key(tabId),
data: TabDragData(tabId),
child: Consumer(
child: _TabDraggable(
child: TabPreviewDraggable(
tabId: tabId,
activeTabId: activeTab,
onClose: onClose,
@@ -420,7 +370,10 @@ class ViewTabsSheetWidget extends HookConsumerWidget {
],
),
Padding(
padding: const EdgeInsets.only(top: _Tab.headerSize + 4, right: 4),
padding: const EdgeInsets.only(
top: _TabSheetHeader.headerSize + 4,
right: 4,
),
child: FloatingActionButton.small(
onPressed: () async {
await context.push(const SearchRoute().location);
@@ -1,5 +1,8 @@
import 'package:flutter/material.dart';
import 'package:lensai/features/geckoview/domain/entities/tab_state.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:lensai/features/geckoview/domain/entities/states/tab.dart';
import 'package:lensai/features/geckoview/domain/providers/tab_state.dart';
import 'package:lensai/features/geckoview/domain/repositories/tab.dart';
class TabPreview extends StatelessWidget {
final TabState tab;
@@ -99,3 +102,51 @@ class TabPreview extends StatelessWidget {
);
}
}
class TabPreviewDraggable extends HookConsumerWidget {
final String tabId;
final String? activeTabId;
final void Function() onClose;
TabPreviewDraggable({
required this.tabId,
required this.activeTabId,
required this.onClose,
}) : super(key: ValueKey(tabId));
@override
Widget build(BuildContext context, WidgetRef ref) {
final tab = ref.watch(tabStateProvider(tabId));
if (tab == null) {
return const SizedBox.shrink();
}
return TabPreview(
tab: tab,
isActive: tabId == activeTabId,
onTap: () async {
if (tabId != activeTabId) {
//Close first to avoid rebuilds
onClose();
await ref.read(tabRepositoryProvider.notifier).selectTab(tab.id);
} else {
onClose();
}
},
// onDoubleTap: () {
// ref.read(overlayDialogControllerProvider.notifier).show(
// TabActionDialog(
// initialTab: tab,
// onDismiss:
// ref.read(overlayDialogControllerProvider.notifier).dismiss,
// ),
// );
// },
onDelete: () async {
await ref.read(tabRepositoryProvider.notifier).closeTab(tab.id);
},
);
}
}
@@ -18,7 +18,9 @@ class TabsActionButton extends HookConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final theme = Theme.of(context);
final tabCount = ref.watch(tabListProvider.select((tabs) => tabs.length));
final tabCount = ref.watch(
tabListProvider.select((tabs) => tabs.collection.length),
);
return InkWell(
onTap: onTap,
@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:lensai/features/geckoview/domain/entities/readerable_state.dart';
import 'package:lensai/features/geckoview/domain/entities/states/readerable.dart';
import 'package:lensai/features/geckoview/domain/providers/tab_state.dart';
import 'package:lensai/features/geckoview/features/readerview/domain/providers/readerable.dart';
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:lensai/features/geckoview/domain/entities/readerable_state.dart';
import 'package:lensai/features/geckoview/domain/entities/states/readerable.dart';
import 'package:lensai/features/geckoview/domain/providers/tab_state.dart';
import 'package:lensai/features/geckoview/features/readerview/presentation/controllers/readerable.dart';
import 'package:lensai/features/user/domain/repositories/general_settings.dart';
@@ -1,5 +1,5 @@
import 'package:lensai/features/kagi/data/services/autosuggest.dart';
import 'package:lensai/features/kagi/domain/repositories/search_suggestions.dart';
import 'package:lensai/features/geckoview/features/search/domain/repositories/search_suggestions.dart';
import 'package:lensai/features/search/domain/autosuggest/kagi.dart';
import 'package:lensai/features/search/domain/entities/abstract/i_search_suggestion_provider.dart';
import 'package:riverpod/riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
@@ -46,9 +46,9 @@ class SearchScreen extends HookConsumerWidget {
Future<void> submitSearch(String query) async {
if (activeBang != null && (formKey.currentState?.validate() == true)) {
final searchUri = await ref.read(
triggerBangSearchProvider(activeBang, query).future,
);
final searchUri = await ref
.read(bangSearchProvider.notifier)
.triggerBangSearch(activeBang, query);
await ref.read(tabRepositoryProvider.notifier).addTab(url: searchUri);
@@ -1,7 +1,7 @@
import 'dart:async';
import 'package:drift/drift.dart';
import 'package:lensai/features/geckoview/domain/entities/tab_state.dart';
import 'package:lensai/features/geckoview/domain/entities/states/tab.dart';
import 'package:lensai/features/geckoview/features/tabs/data/database/database.dart';
import 'package:lensai/features/geckoview/features/tabs/data/models/tab_query_result.dart';
import 'package:lexo_rank/lexo_rank.dart';
@@ -17,7 +17,7 @@ class TabDao extends DatabaseAccessor<TabDatabase> with _$TabDaoMixin {
Selectable<TabData> allTabData() => db.tab.select();
SingleOrNullSelectable<TabData> tabDataById(String id) =>
SingleOrNullSelectable<TabData> getTabDataById(String id) =>
db.tab.select()..where((t) => t.id.equals(id));
Selectable<String> containerTabIds(String? containerId) {
@@ -34,7 +34,7 @@ class TabDao extends DatabaseAccessor<TabDatabase> with _$TabDaoMixin {
return query.map((row) => row.read(db.tab.id)!);
}
Selectable<String> allTabIds() {
Selectable<String> getAllTabIds() {
final query =
selectOnly(db.tab)
..addColumns([db.tab.id])
@@ -43,7 +43,7 @@ class TabDao extends DatabaseAccessor<TabDatabase> with _$TabDaoMixin {
return query.map((row) => row.read(db.tab.id)!);
}
SingleOrNullSelectable<String?> tabContainerId(String tabId) {
SingleOrNullSelectable<String?> getTabContainerId(String tabId) {
final query =
selectOnly(db.tab)
..addColumns([db.tab.containerId])
@@ -7,7 +7,7 @@ import 'package:lensai/features/geckoview/features/tabs/data/database/daos/tab.d
import 'package:lensai/features/geckoview/features/tabs/data/models/container_data.dart';
import 'package:lensai/features/geckoview/features/tabs/data/models/tab_query_result.dart';
import 'package:lensai/features/geckoview/features/tabs/features/vector_store/data/database/daos/vector.dart';
import 'package:lensai/features/geckoview/features/tabs/features/vector_store/data/database/database.dart';
import 'package:lensai/features/geckoview/features/tabs/features/vector_store/data/database/migrator.dart';
import 'package:lensai/features/geckoview/features/tabs/features/vector_store/data/models/vector_result.dart';
import 'package:lensai/features/search/domain/fts_tokenizer.dart';
@@ -1,4 +1,6 @@
class TabQueryResult {
import 'package:fast_equatable/fast_equatable.dart';
class TabQueryResult with FastEquatable {
final String id;
final String? title;
@@ -19,4 +21,18 @@ class TabQueryResult {
this.fullContent,
required this.weightedRank,
});
@override
bool get cacheHash => true;
@override
List<Object?> get hashParameters => [
id,
title,
cleanUrl,
url,
extractedContent,
fullContent,
weightedRank,
];
}
@@ -1,32 +1,12 @@
import 'dart:ui';
import 'package:collection/collection.dart';
import 'package:lensai/features/geckoview/features/tabs/data/entities/container_filter.dart';
import 'package:lensai/features/geckoview/features/tabs/data/models/container_data.dart';
import 'package:lensai/features/geckoview/features/tabs/data/providers.dart';
import 'package:lensai/features/geckoview/features/tabs/domain/repositories/container.dart';
import 'package:lensai/features/geckoview/features/tabs/utils/color_palette.dart';
import 'package:lensai/features/search/util/tokenized_filter.dart';
import 'package:riverpod/riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'providers.g.dart';
@Riverpod()
Future<Color> unusedRandomContainerColor(Ref ref) async {
final repository = ref.watch(containerRepositoryProvider.notifier);
final allColors = colorTypes.flattened.toList();
final usedColors = await repository.getDistinctColors();
Color randomColor;
do {
randomColor = randomColorShade(allColors);
} while (usedColors.contains(randomColor));
return randomColor;
}
@Riverpod()
Stream<List<ContainerDataWithCount>> containersWithCount(Ref ref) {
final db = ref.watch(tabDatabaseProvider);
@@ -64,6 +44,6 @@ Stream<List<String>> containerTabIds(Ref ref, ContainerFilter containerFilter) {
case ContainerFilterById(:final containerId):
return db.tabDao.containerTabIds(containerId).watch();
case ContainerFilterDisabled():
return db.tabDao.allTabIds().watch();
return db.tabDao.getAllTabIds().watch();
}
}
@@ -6,25 +6,6 @@ part of 'providers.dart';
// RiverpodGenerator
// **************************************************************************
String _$unusedRandomContainerColorHash() =>
r'913499b3de281ea0020db97ed67121652f53ec08';
/// See also [unusedRandomContainerColor].
@ProviderFor(unusedRandomContainerColor)
final unusedRandomContainerColorProvider =
AutoDisposeFutureProvider<Color>.internal(
unusedRandomContainerColor,
name: r'unusedRandomContainerColorProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$unusedRandomContainerColorHash,
dependencies: null,
allTransitiveDependencies: null,
);
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef UnusedRandomContainerColorRef = AutoDisposeFutureProviderRef<Color>;
String _$containersWithCountHash() =>
r'ee0734e4b71472b79636816616a158f54fa6bc23';
@@ -207,7 +188,7 @@ class _MatchSortedContainersWithCountProviderElement
(origin as MatchSortedContainersWithCountProvider).searchText;
}
String _$containerTabIdsHash() => r'6472a865c287d7f87ec49a401c8184bfe7528ab8';
String _$containerTabIdsHash() => r'355c65523db29f376faa293ab1f1ed8273696548';
/// See also [containerTabIds].
@ProviderFor(containerTabIds)

Some files were not shown because too many files have changed in this diff Show More