selectable suggestion provider
This commit is contained in:
@@ -21,6 +21,12 @@ Stream<BangData?> defaultSearchBangData(Ref ref) {
|
||||
return repository.watchBang(trigger);
|
||||
}
|
||||
|
||||
@Riverpod()
|
||||
Stream<BangData?> bangData(Ref ref, String trigger) {
|
||||
final repository = ref.watch(bangDataRepositoryProvider.notifier);
|
||||
return repository.watchBang(trigger);
|
||||
}
|
||||
|
||||
@Riverpod()
|
||||
Stream<Map<String, List<String>>> bangCategories(Ref ref) {
|
||||
final repository = ref.watch(bangDataRepositoryProvider.notifier);
|
||||
|
||||
@@ -24,26 +24,7 @@ final defaultSearchBangDataProvider = StreamProvider<BangData?>.internal(
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
typedef DefaultSearchBangDataRef = StreamProviderRef<BangData?>;
|
||||
String _$bangCategoriesHash() => r'947fcfd2dffcc7f585c6ed7379d319f4fe72293a';
|
||||
|
||||
/// See also [bangCategories].
|
||||
@ProviderFor(bangCategories)
|
||||
final bangCategoriesProvider =
|
||||
AutoDisposeStreamProvider<Map<String, List<String>>>.internal(
|
||||
bangCategories,
|
||||
name: r'bangCategoriesProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$bangCategoriesHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
typedef BangCategoriesRef =
|
||||
AutoDisposeStreamProviderRef<Map<String, List<String>>>;
|
||||
String _$bangListHash() => r'd1e0bb9fa4f523ce516e075c0d149bf7803ebb2b';
|
||||
String _$bangDataHash() => r'b10d3a74e19d30fc372b9ecbf7146bccd9fdf83b';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
@@ -66,6 +47,142 @@ class _SystemHash {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 _$bangCategoriesHash() => r'947fcfd2dffcc7f585c6ed7379d319f4fe72293a';
|
||||
|
||||
/// See also [bangCategories].
|
||||
@ProviderFor(bangCategories)
|
||||
final bangCategoriesProvider =
|
||||
AutoDisposeStreamProvider<Map<String, List<String>>>.internal(
|
||||
bangCategories,
|
||||
name: r'bangCategoriesProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$bangCategoriesHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
typedef BangCategoriesRef =
|
||||
AutoDisposeStreamProviderRef<Map<String, List<String>>>;
|
||||
String _$bangListHash() => r'd1e0bb9fa4f523ce516e075c0d149bf7803ebb2b';
|
||||
|
||||
/// See also [bangList].
|
||||
@ProviderFor(bangList)
|
||||
const bangListProvider = BangListFamily();
|
||||
|
||||
+24
-1
@@ -1,14 +1,37 @@
|
||||
import 'package:riverpod/riverpod.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:weblibre/features/geckoview/features/search/domain/repositories/search_suggestions.dart';
|
||||
import 'package:weblibre/features/search/domain/autosuggest/brave.dart';
|
||||
import 'package:weblibre/features/search/domain/autosuggest/duckduckgo.dart';
|
||||
import 'package:weblibre/features/search/domain/autosuggest/kagi.dart';
|
||||
import 'package:weblibre/features/search/domain/autosuggest/qwant.dart';
|
||||
import 'package:weblibre/features/search/domain/entities/abstract/i_search_suggestion_provider.dart';
|
||||
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
|
||||
|
||||
part 'search_suggestions.g.dart';
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
ISearchSuggestionProvider defaultSearchSuggestions(Ref ref) {
|
||||
return ref.watch(kagiAutosuggestServiceProvider.notifier);
|
||||
final provider = ref.watch(
|
||||
generalSettingsRepositoryProvider.select(
|
||||
(value) => value.defaultSearchSuggestionsProvider,
|
||||
),
|
||||
);
|
||||
|
||||
return switch (provider) {
|
||||
SearchSuggestionProviders.brave => ref.watch(
|
||||
braveAutosuggestServiceProvider.notifier,
|
||||
),
|
||||
SearchSuggestionProviders.ddg => ref.watch(
|
||||
duckDuckGoAutosuggestServiceProvider.notifier,
|
||||
),
|
||||
SearchSuggestionProviders.kagi => ref.watch(
|
||||
kagiAutosuggestServiceProvider.notifier,
|
||||
),
|
||||
SearchSuggestionProviders.qwant => ref.watch(
|
||||
qwantAutosuggestServiceProvider.notifier,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@Riverpod()
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ part of 'search_suggestions.dart';
|
||||
// **************************************************************************
|
||||
|
||||
String _$defaultSearchSuggestionsHash() =>
|
||||
r'56514064baa64acf0a28ec34cfad957dd863538b';
|
||||
r'e503757071dd29988cd25e360b56a5a646800d5b';
|
||||
|
||||
/// See also [defaultSearchSuggestions].
|
||||
@ProviderFor(defaultSearchSuggestions)
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
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:weblibre/core/routing/routes.dart';
|
||||
import 'package:weblibre/features/bangs/domain/providers/bangs.dart';
|
||||
import 'package:weblibre/features/search/domain/entities/abstract/i_search_suggestion_provider.dart';
|
||||
import 'package:weblibre/features/settings/presentation/controllers/save_settings.dart';
|
||||
import 'package:weblibre/features/settings/presentation/widgets/bang_icon.dart';
|
||||
import 'package:weblibre/features/user/data/models/general_settings.dart';
|
||||
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
|
||||
import 'package:weblibre/presentation/widgets/url_icon.dart';
|
||||
|
||||
const defaultBangs = ['ddg', 'brave', 'startpage', 'qwant'];
|
||||
@@ -17,7 +21,14 @@ class DefaultSearchPage extends HookConsumerWidget {
|
||||
|
||||
final bangs = ref.watch(bangListProvider(triggers: defaultBangs));
|
||||
|
||||
final activeBang = ref.watch(defaultSearchBangDataProvider).valueOrNull;
|
||||
final activeBang = ref.watch(
|
||||
defaultSearchBangDataProvider.select((value) => value.valueOrNull),
|
||||
);
|
||||
final activeAutosuggest = ref.watch(
|
||||
generalSettingsRepositoryProvider.select(
|
||||
(value) => value.defaultSearchSuggestionsProvider,
|
||||
),
|
||||
);
|
||||
|
||||
Future<void> updateSearchProvider(String trigger) async {
|
||||
await ref
|
||||
@@ -37,31 +48,27 @@ class DefaultSearchPage extends HookConsumerWidget {
|
||||
children: [
|
||||
const SizedBox(height: 24),
|
||||
Center(
|
||||
child: Text(
|
||||
'Default Search Provider',
|
||||
style: theme.textTheme.headlineMedium,
|
||||
),
|
||||
child: Text('Search', style: theme.textTheme.headlineMedium),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
SizedBox(
|
||||
height: 48,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text('Selected Provider'),
|
||||
if (activeBang != null)
|
||||
FilterChip(
|
||||
showCheckmark: false,
|
||||
label: Text(activeBang.websiteName),
|
||||
avatar: UrlIcon([
|
||||
activeBang.getTemplateUrl(''),
|
||||
], iconSize: 20),
|
||||
selected: true,
|
||||
onSelected: (value) {},
|
||||
),
|
||||
],
|
||||
),
|
||||
const ListTile(
|
||||
title: Text('Default Search Provider'),
|
||||
leading: Icon(MdiIcons.cloudSearch),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
),
|
||||
if (activeBang != null)
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: FilterChip(
|
||||
showCheckmark: false,
|
||||
label: Text(activeBang.websiteName),
|
||||
avatar: UrlIcon([
|
||||
activeBang.getTemplateUrl(''),
|
||||
], iconSize: 20),
|
||||
selected: true,
|
||||
onSelected: (value) {},
|
||||
),
|
||||
),
|
||||
const Divider(),
|
||||
Wrap(
|
||||
spacing: 8.0,
|
||||
@@ -93,6 +100,41 @@ class DefaultSearchPage extends HookConsumerWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
const ListTile(
|
||||
title: Text('Default Autocomplete Provider'),
|
||||
leading: Icon(MdiIcons.weatherCloudyArrowRight),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
),
|
||||
DropdownMenu<SearchSuggestionProviders>(
|
||||
initialSelection: activeAutosuggest,
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
prefixIconConstraints: BoxConstraints.tight(
|
||||
const Size.square(24),
|
||||
),
|
||||
),
|
||||
width: double.infinity,
|
||||
leadingIcon: BangIcon(trigger: activeAutosuggest.relatedBang),
|
||||
dropdownMenuEntries: SearchSuggestionProviders.values.map((
|
||||
provider,
|
||||
) {
|
||||
return DropdownMenuEntry(
|
||||
value: provider,
|
||||
label: provider.label,
|
||||
leadingIcon: BangIcon(trigger: provider.relatedBang),
|
||||
);
|
||||
}).toList(),
|
||||
onSelected: (value) async {
|
||||
if (value != null) {
|
||||
await ref
|
||||
.read(saveGeneralSettingsControllerProvider.notifier)
|
||||
.save(
|
||||
(currentSettings) => currentSettings.copyWith
|
||||
.defaultSearchSuggestionsProvider(value),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:exceptions/exceptions.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:weblibre/core/http_error_handler.dart';
|
||||
import 'package:weblibre/features/search/domain/entities/abstract/i_search_suggestion_provider.dart';
|
||||
|
||||
part 'brave.g.dart';
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
class BraveAutosuggestService extends _$BraveAutosuggestService
|
||||
implements ISearchSuggestionProvider {
|
||||
static final _baseUrl = Uri.https('search.brave.com', 'api/suggest');
|
||||
|
||||
late http.Client _client;
|
||||
|
||||
@override
|
||||
void build() {
|
||||
_client = http.Client();
|
||||
|
||||
ref.onDispose(() {
|
||||
_client.close();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result<List<String>>> getSuggestions(String query) {
|
||||
return Result.fromAsync(() async {
|
||||
final response = await _client
|
||||
.get(_baseUrl.replace(queryParameters: {'q': query}))
|
||||
.timeout(const Duration(seconds: 5));
|
||||
|
||||
final results = jsonDecode(utf8.decode(response.bodyBytes)) as List;
|
||||
return switch (results.last) {
|
||||
final String result => [result],
|
||||
final List resultList => resultList.cast(),
|
||||
_ => [],
|
||||
};
|
||||
}, exceptionHandler: handleHttpError);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'brave.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$braveAutosuggestServiceHash() =>
|
||||
r'3b2f465dcf4b44b0450975d3909fc7066d171bf2';
|
||||
|
||||
/// See also [BraveAutosuggestService].
|
||||
@ProviderFor(BraveAutosuggestService)
|
||||
final braveAutosuggestServiceProvider =
|
||||
NotifierProvider<BraveAutosuggestService, void>.internal(
|
||||
BraveAutosuggestService.new,
|
||||
name: r'braveAutosuggestServiceProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$braveAutosuggestServiceHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$BraveAutosuggestService = Notifier<void>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
||||
@@ -0,0 +1,42 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:exceptions/exceptions.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:weblibre/core/http_error_handler.dart';
|
||||
import 'package:weblibre/features/search/domain/entities/abstract/i_search_suggestion_provider.dart';
|
||||
|
||||
part 'duckduckgo.g.dart';
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
class DuckDuckGoAutosuggestService extends _$DuckDuckGoAutosuggestService
|
||||
implements ISearchSuggestionProvider {
|
||||
static final _baseUrl = Uri.https('duckduckgo.com', 'ac/');
|
||||
|
||||
late http.Client _client;
|
||||
|
||||
@override
|
||||
void build() {
|
||||
_client = http.Client();
|
||||
|
||||
ref.onDispose(() {
|
||||
_client.close();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result<List<String>>> getSuggestions(String query) {
|
||||
return Result.fromAsync(() async {
|
||||
final response = await _client
|
||||
.get(_baseUrl.replace(queryParameters: {'type': 'list', 'q': query}))
|
||||
.timeout(const Duration(seconds: 5));
|
||||
|
||||
final results = jsonDecode(utf8.decode(response.bodyBytes)) as List;
|
||||
return switch (results.last) {
|
||||
final String result => [result],
|
||||
final List resultList => resultList.cast(),
|
||||
_ => [],
|
||||
};
|
||||
}, exceptionHandler: handleHttpError);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'duckduckgo.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$duckDuckGoAutosuggestServiceHash() =>
|
||||
r'1679e6d9bce7cb380b1ee60c768abe2fddb79478';
|
||||
|
||||
/// See also [DuckDuckGoAutosuggestService].
|
||||
@ProviderFor(DuckDuckGoAutosuggestService)
|
||||
final duckDuckGoAutosuggestServiceProvider =
|
||||
NotifierProvider<DuckDuckGoAutosuggestService, void>.internal(
|
||||
DuckDuckGoAutosuggestService.new,
|
||||
name: r'duckDuckGoAutosuggestServiceProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$duckDuckGoAutosuggestServiceHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$DuckDuckGoAutosuggestService = Notifier<void>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
||||
@@ -0,0 +1,46 @@
|
||||
// ignore_for_file: avoid_dynamic_calls
|
||||
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:exceptions/exceptions.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:weblibre/core/http_error_handler.dart';
|
||||
import 'package:weblibre/features/search/domain/entities/abstract/i_search_suggestion_provider.dart';
|
||||
|
||||
part 'qwant.g.dart';
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
class QwantAutosuggestService extends _$QwantAutosuggestService
|
||||
implements ISearchSuggestionProvider {
|
||||
static final _baseUrl = Uri.https('api.qwant.com', 'v3/suggest');
|
||||
|
||||
late http.Client _client;
|
||||
|
||||
@override
|
||||
void build() {
|
||||
_client = http.Client();
|
||||
|
||||
ref.onDispose(() {
|
||||
_client.close();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result<List<String>>> getSuggestions(String query) {
|
||||
return Result.fromAsync(() async {
|
||||
final response = await _client
|
||||
.get(_baseUrl.replace(queryParameters: {'q': query}))
|
||||
.timeout(const Duration(seconds: 5));
|
||||
|
||||
final results =
|
||||
jsonDecode(utf8.decode(response.bodyBytes)) as Map<String, dynamic>;
|
||||
if (results['status'] != 'success') {
|
||||
throw Exception('qwant api error');
|
||||
}
|
||||
|
||||
final items = results['data']['items'] as List;
|
||||
return items.map((item) => item['value'] as String).toList();
|
||||
}, exceptionHandler: handleHttpError);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'qwant.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$qwantAutosuggestServiceHash() =>
|
||||
r'a942cd073289d20d43647de5c0f43fb08790045f';
|
||||
|
||||
/// See also [QwantAutosuggestService].
|
||||
@ProviderFor(QwantAutosuggestService)
|
||||
final qwantAutosuggestServiceProvider =
|
||||
NotifierProvider<QwantAutosuggestService, void>.internal(
|
||||
QwantAutosuggestService.new,
|
||||
name: r'qwantAutosuggestServiceProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$qwantAutosuggestServiceHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$QwantAutosuggestService = Notifier<void>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
||||
@@ -1,5 +1,17 @@
|
||||
import 'package:exceptions/exceptions.dart';
|
||||
|
||||
enum SearchSuggestionProviders {
|
||||
brave('Brave', 'brave'),
|
||||
ddg('DuckDuckGo', 'ddg'),
|
||||
kagi('Kagi', 'kagi'),
|
||||
qwant('Qwant', 'qwant');
|
||||
|
||||
final String label;
|
||||
final String relatedBang;
|
||||
|
||||
const SearchSuggestionProviders(this.label, this.relatedBang);
|
||||
}
|
||||
|
||||
abstract interface class ISearchSuggestionProvider {
|
||||
Future<Result<List<String>>> getSuggestions(String query);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:weblibre/features/search/domain/entities/abstract/i_search_suggestion_provider.dart';
|
||||
import 'package:weblibre/features/settings/presentation/controllers/save_settings.dart';
|
||||
import 'package:weblibre/features/settings/presentation/widgets/bang_icon.dart';
|
||||
import 'package:weblibre/features/settings/presentation/widgets/custom_list_tile.dart';
|
||||
import 'package:weblibre/features/settings/presentation/widgets/default_search_selector.dart';
|
||||
import 'package:weblibre/features/user/data/models/general_settings.dart';
|
||||
@@ -93,6 +95,65 @@ class GeneralSettingsScreen extends HookConsumerWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16.0,
|
||||
vertical: 8,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const ListTile(
|
||||
title: Text('Default Autocomplete Provider'),
|
||||
leading: Icon(MdiIcons.weatherCloudyArrowRight),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 40),
|
||||
child: DropdownMenu<SearchSuggestionProviders>(
|
||||
initialSelection:
|
||||
generalSettings.defaultSearchSuggestionsProvider,
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
prefixIconConstraints: BoxConstraints.tight(
|
||||
const Size.square(24),
|
||||
),
|
||||
),
|
||||
width: double.infinity,
|
||||
leadingIcon: BangIcon(
|
||||
trigger: generalSettings
|
||||
.defaultSearchSuggestionsProvider
|
||||
.relatedBang,
|
||||
),
|
||||
dropdownMenuEntries: SearchSuggestionProviders.values
|
||||
.map((provider) {
|
||||
return DropdownMenuEntry(
|
||||
value: provider,
|
||||
label: provider.label,
|
||||
leadingIcon: BangIcon(
|
||||
trigger: provider.relatedBang,
|
||||
),
|
||||
);
|
||||
})
|
||||
.toList(),
|
||||
onSelected: (value) async {
|
||||
if (value != null) {
|
||||
await ref
|
||||
.read(
|
||||
saveGeneralSettingsControllerProvider
|
||||
.notifier,
|
||||
)
|
||||
.save(
|
||||
(currentSettings) => currentSettings.copyWith
|
||||
.defaultSearchSuggestionsProvider(value),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SwitchListTile.adaptive(
|
||||
title: const Text('Enable Reader Mode'),
|
||||
subtitle: const Text(
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:weblibre/features/bangs/domain/providers/bangs.dart';
|
||||
import 'package:weblibre/presentation/widgets/url_icon.dart';
|
||||
|
||||
class BangIcon extends ConsumerWidget {
|
||||
final String trigger;
|
||||
final double iconSize;
|
||||
|
||||
const BangIcon({super.key, required this.trigger, this.iconSize = 20});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final bang = ref.watch(
|
||||
bangDataProvider(trigger).select((value) => value.valueOrNull),
|
||||
);
|
||||
|
||||
return UrlIcon([
|
||||
if (bang != null) bang.getTemplateUrl(''),
|
||||
], iconSize: iconSize);
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,9 @@ class DefaultSearchSelector extends HookConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final activeBang = ref.watch(defaultSearchBangDataProvider).valueOrNull;
|
||||
final activeBang = ref.watch(
|
||||
defaultSearchBangDataProvider.select((value) => value.valueOrNull),
|
||||
);
|
||||
final availableBangs = ref.watch(frequentBangListProvider);
|
||||
|
||||
Future<void> updateSearchProvider(String trigger) async {
|
||||
|
||||
@@ -2,10 +2,12 @@ import 'package:copy_with_extension/copy_with_extension.dart';
|
||||
import 'package:fast_equatable/fast_equatable.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:weblibre/features/search/domain/entities/abstract/i_search_suggestion_provider.dart';
|
||||
|
||||
part 'general_settings.g.dart';
|
||||
|
||||
const _fallbackSearchProvider = 'ddg';
|
||||
const _fallbackAutocompleteProvider = SearchSuggestionProviders.ddg;
|
||||
|
||||
enum DeleteBrowsingDataType {
|
||||
tabs('Open tabs'),
|
||||
@@ -29,6 +31,7 @@ class GeneralSettings with FastEquatable {
|
||||
final bool enforceReadability;
|
||||
final Set<DeleteBrowsingDataType>? deleteBrowsingDataOnQuit;
|
||||
final String defaultSearchProvider;
|
||||
final SearchSuggestionProviders defaultSearchSuggestionsProvider;
|
||||
final bool createChildTabsOption;
|
||||
|
||||
final bool proxyPrivateTabsTor;
|
||||
@@ -39,6 +42,7 @@ class GeneralSettings with FastEquatable {
|
||||
required this.enforceReadability,
|
||||
required this.deleteBrowsingDataOnQuit,
|
||||
required this.defaultSearchProvider,
|
||||
required this.defaultSearchSuggestionsProvider,
|
||||
required this.createChildTabsOption,
|
||||
required this.proxyPrivateTabsTor,
|
||||
});
|
||||
@@ -49,12 +53,15 @@ class GeneralSettings with FastEquatable {
|
||||
bool? enforceReadability,
|
||||
this.deleteBrowsingDataOnQuit,
|
||||
String? defaultSearchProvider,
|
||||
SearchSuggestionProviders? defaultSearchSuggestionsProvider,
|
||||
bool? createChildTabsOption,
|
||||
bool? proxyPrivateTabsTor,
|
||||
}) : themeMode = themeMode ?? ThemeMode.dark,
|
||||
enableReadability = enableReadability ?? true,
|
||||
enforceReadability = enforceReadability ?? false,
|
||||
defaultSearchProvider = defaultSearchProvider ?? _fallbackSearchProvider,
|
||||
defaultSearchSuggestionsProvider =
|
||||
defaultSearchSuggestionsProvider ?? _fallbackAutocompleteProvider,
|
||||
createChildTabsOption = createChildTabsOption ?? false,
|
||||
proxyPrivateTabsTor = proxyPrivateTabsTor ?? false;
|
||||
|
||||
@@ -70,6 +77,7 @@ class GeneralSettings with FastEquatable {
|
||||
enforceReadability,
|
||||
deleteBrowsingDataOnQuit,
|
||||
defaultSearchProvider,
|
||||
defaultSearchSuggestionsProvider,
|
||||
createChildTabsOption,
|
||||
proxyPrivateTabsTor,
|
||||
];
|
||||
|
||||
@@ -19,6 +19,10 @@ abstract class _$GeneralSettingsCWProxy {
|
||||
|
||||
GeneralSettings defaultSearchProvider(String defaultSearchProvider);
|
||||
|
||||
GeneralSettings defaultSearchSuggestionsProvider(
|
||||
SearchSuggestionProviders defaultSearchSuggestionsProvider,
|
||||
);
|
||||
|
||||
GeneralSettings createChildTabsOption(bool createChildTabsOption);
|
||||
|
||||
GeneralSettings proxyPrivateTabsTor(bool proxyPrivateTabsTor);
|
||||
@@ -35,6 +39,7 @@ abstract class _$GeneralSettingsCWProxy {
|
||||
bool enforceReadability,
|
||||
Set<DeleteBrowsingDataType>? deleteBrowsingDataOnQuit,
|
||||
String defaultSearchProvider,
|
||||
SearchSuggestionProviders defaultSearchSuggestionsProvider,
|
||||
bool createChildTabsOption,
|
||||
bool proxyPrivateTabsTor,
|
||||
});
|
||||
@@ -66,6 +71,11 @@ class _$GeneralSettingsCWProxyImpl implements _$GeneralSettingsCWProxy {
|
||||
GeneralSettings defaultSearchProvider(String defaultSearchProvider) =>
|
||||
this(defaultSearchProvider: defaultSearchProvider);
|
||||
|
||||
@override
|
||||
GeneralSettings defaultSearchSuggestionsProvider(
|
||||
SearchSuggestionProviders defaultSearchSuggestionsProvider,
|
||||
) => this(defaultSearchSuggestionsProvider: defaultSearchSuggestionsProvider);
|
||||
|
||||
@override
|
||||
GeneralSettings createChildTabsOption(bool createChildTabsOption) =>
|
||||
this(createChildTabsOption: createChildTabsOption);
|
||||
@@ -87,6 +97,7 @@ class _$GeneralSettingsCWProxyImpl implements _$GeneralSettingsCWProxy {
|
||||
Object? enforceReadability = const $CopyWithPlaceholder(),
|
||||
Object? deleteBrowsingDataOnQuit = const $CopyWithPlaceholder(),
|
||||
Object? defaultSearchProvider = const $CopyWithPlaceholder(),
|
||||
Object? defaultSearchSuggestionsProvider = const $CopyWithPlaceholder(),
|
||||
Object? createChildTabsOption = const $CopyWithPlaceholder(),
|
||||
Object? proxyPrivateTabsTor = const $CopyWithPlaceholder(),
|
||||
}) {
|
||||
@@ -113,6 +124,11 @@ class _$GeneralSettingsCWProxyImpl implements _$GeneralSettingsCWProxy {
|
||||
? _value.defaultSearchProvider
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: defaultSearchProvider as String,
|
||||
defaultSearchSuggestionsProvider:
|
||||
defaultSearchSuggestionsProvider == const $CopyWithPlaceholder()
|
||||
? _value.defaultSearchSuggestionsProvider
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: defaultSearchSuggestionsProvider as SearchSuggestionProviders,
|
||||
createChildTabsOption:
|
||||
createChildTabsOption == const $CopyWithPlaceholder()
|
||||
? _value.createChildTabsOption
|
||||
@@ -146,6 +162,10 @@ GeneralSettings _$GeneralSettingsFromJson(Map<String, dynamic> json) =>
|
||||
?.map((e) => $enumDecode(_$DeleteBrowsingDataTypeEnumMap, e))
|
||||
.toSet(),
|
||||
defaultSearchProvider: json['defaultSearchProvider'] as String?,
|
||||
defaultSearchSuggestionsProvider: $enumDecodeNullable(
|
||||
_$SearchSuggestionProvidersEnumMap,
|
||||
json['defaultSearchSuggestionsProvider'],
|
||||
),
|
||||
createChildTabsOption: json['createChildTabsOption'] as bool?,
|
||||
proxyPrivateTabsTor: json['proxyPrivateTabsTor'] as bool?,
|
||||
);
|
||||
@@ -159,6 +179,9 @@ Map<String, dynamic> _$GeneralSettingsToJson(GeneralSettings instance) =>
|
||||
?.map((e) => _$DeleteBrowsingDataTypeEnumMap[e]!)
|
||||
.toList(),
|
||||
'defaultSearchProvider': instance.defaultSearchProvider,
|
||||
'defaultSearchSuggestionsProvider':
|
||||
_$SearchSuggestionProvidersEnumMap[instance
|
||||
.defaultSearchSuggestionsProvider]!,
|
||||
'createChildTabsOption': instance.createChildTabsOption,
|
||||
'proxyPrivateTabsTor': instance.proxyPrivateTabsTor,
|
||||
};
|
||||
@@ -177,3 +200,10 @@ const _$DeleteBrowsingDataTypeEnumMap = {
|
||||
DeleteBrowsingDataType.permissions: 'permissions',
|
||||
DeleteBrowsingDataType.downloads: 'downloads',
|
||||
};
|
||||
|
||||
const _$SearchSuggestionProvidersEnumMap = {
|
||||
SearchSuggestionProviders.brave: 'brave',
|
||||
SearchSuggestionProviders.ddg: 'ddg',
|
||||
SearchSuggestionProviders.kagi: 'kagi',
|
||||
SearchSuggestionProviders.qwant: 'qwant',
|
||||
};
|
||||
|
||||
@@ -43,6 +43,11 @@ class GeneralSettingsRepository extends _$GeneralSettingsRepository {
|
||||
DriftSqlType.string,
|
||||
db.typeMapping,
|
||||
),
|
||||
'defaultSearchSuggestionsProvider':
|
||||
settings['defaultSearchSuggestionsProvider']?.readAs(
|
||||
DriftSqlType.string,
|
||||
db.typeMapping,
|
||||
),
|
||||
'createChildTabsOption': settings['createChildTabsOption']?.readAs(
|
||||
DriftSqlType.bool,
|
||||
db.typeMapping,
|
||||
|
||||
@@ -7,7 +7,7 @@ part of 'general_settings.dart';
|
||||
// **************************************************************************
|
||||
|
||||
String _$generalSettingsRepositoryHash() =>
|
||||
r'4aea6ef10e883aa715141cc314d02d8af90dbdb5';
|
||||
r'5335acea5fb175b665b80b2241ce463ae8ad9f89';
|
||||
|
||||
/// See also [GeneralSettingsRepository].
|
||||
@ProviderFor(GeneralSettingsRepository)
|
||||
|
||||
Reference in New Issue
Block a user