initial onboarding
This commit is contained in:
@@ -0,0 +1,44 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
version="1.1"
|
||||||
|
viewBox="0 0 128 128"
|
||||||
|
height="128"
|
||||||
|
width="128">
|
||||||
|
<g
|
||||||
|
style="display:inline;opacity:1">
|
||||||
|
<g
|
||||||
|
style="fill:#800000;fill-opacity:1;stroke:#ffffff;stroke-width:1.62100744;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
||||||
|
transform="matrix(0.6778654,0,0,0.56141828,-241.07537,-247.27712)" />
|
||||||
|
<g
|
||||||
|
transform="matrix(-0.6945203,0,0,0.56109687,375.02964,-247.42947)"
|
||||||
|
style="fill:#800000;fill-opacity:1;stroke:#ffffff;stroke-width:1.60191178000000001;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline;stroke-linejoin:round">
|
||||||
|
<path
|
||||||
|
d="m 447.83376,669.09921 c -80.63119,-57.03115 -80.63119,-57.03115 -80.63119,-199.60903 34.55623,0 46.07497,0 80.63119,-28.51558 m 0,228.12461 c 80.6312,-57.03115 80.6312,-57.03115 80.6312,-199.60903 -34.55623,0 -46.07497,0 -80.6312,-28.51558"
|
||||||
|
style="fill:#800000;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:1.60191178;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="translate(-17.872548,-17.32535)"
|
||||||
|
style="display:inline">
|
||||||
|
<ellipse
|
||||||
|
transform="matrix(1.3333335,0,0,1.3333334,-42.290862,-31.108461)"
|
||||||
|
ry="12"
|
||||||
|
rx="11.999999"
|
||||||
|
cy="81.325356"
|
||||||
|
cx="102.12254"
|
||||||
|
style="fill:none;stroke:#ffffff;stroke-width:5.99999952;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline;" />
|
||||||
|
<g
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.99999996999999996;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||||
|
transform="scale(1.0018026,0.99820067)">
|
||||||
|
<path
|
||||||
|
d="m 81.72523,81.471945 c 0,11.019828 -4.991003,16.028841 -15.97121,16.028841 -10.980207,0 -15.97121,-5.009013 -15.97121,-16.028841 l 0,-24.043262 7.985605,0 0,24.043262 c 0,7.012618 0.9982,8.014421 7.985605,8.014421 6.987404,0 7.985605,-1.001803 7.985605,-8.014421 l 0,-24.043262 7.985605,0 z"
|
||||||
|
style="fill:#ffffff;stroke:#ffffff;stroke-width:0;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.5 KiB |
@@ -2,10 +2,34 @@ import 'package:go_router/go_router.dart';
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
import 'package:weblibre/core/routing/routes.dart';
|
import 'package:weblibre/core/routing/routes.dart';
|
||||||
|
import 'package:weblibre/features/user/domain/repositories/onboarding.dart';
|
||||||
|
|
||||||
part 'router.g.dart';
|
part 'router.g.dart';
|
||||||
|
|
||||||
@Riverpod(keepAlive: true)
|
@Riverpod(keepAlive: true)
|
||||||
GoRouter router(Ref ref) {
|
Future<GoRouter> router(Ref ref) async {
|
||||||
return GoRouter(debugLogDiagnostics: true, routes: $appRoutes);
|
String? initialLocation;
|
||||||
|
|
||||||
|
final onboardingMandatory = await ref
|
||||||
|
.read(onboardingRepositoryProvider.notifier)
|
||||||
|
.isOutdated();
|
||||||
|
|
||||||
|
if (onboardingMandatory) {
|
||||||
|
final current = await ref
|
||||||
|
.read(onboardingRepositoryProvider.notifier)
|
||||||
|
.getCurrentRevision();
|
||||||
|
|
||||||
|
final route = OnboardingRoute(
|
||||||
|
currentRevision: current ?? -1,
|
||||||
|
targetRevision: OnboardingRepository.targetRevision,
|
||||||
|
);
|
||||||
|
|
||||||
|
initialLocation = route.location;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GoRouter(
|
||||||
|
debugLogDiagnostics: true,
|
||||||
|
routes: $appRoutes,
|
||||||
|
initialLocation: initialLocation,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ part of 'router.dart';
|
|||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$routerHash() => r'25fc899b87c080600f6e1d87d510db333378bdfd';
|
String _$routerHash() => r'ce7cc5b5f236781a06867a852a08ed03d3582022';
|
||||||
|
|
||||||
/// See also [router].
|
/// See also [router].
|
||||||
@ProviderFor(router)
|
@ProviderFor(router)
|
||||||
final routerProvider = Provider<GoRouter>.internal(
|
final routerProvider = FutureProvider<GoRouter>.internal(
|
||||||
router,
|
router,
|
||||||
name: r'routerProvider',
|
name: r'routerProvider',
|
||||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||||
@@ -22,6 +22,6 @@ final routerProvider = Provider<GoRouter>.internal(
|
|||||||
|
|
||||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||||
// ignore: unused_element
|
// ignore: unused_element
|
||||||
typedef RouterRef = ProviderRef<GoRouter>;
|
typedef RouterRef = FutureProviderRef<GoRouter>;
|
||||||
// ignore_for_file: type=lint
|
// 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
|
// 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
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import 'package:weblibre/features/geckoview/features/search/presentation/screens
|
|||||||
import 'package:weblibre/features/geckoview/features/tabs/data/models/container_data.dart';
|
import 'package:weblibre/features/geckoview/features/tabs/data/models/container_data.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/tabs/presentation/screens/container_edit.dart';
|
import 'package:weblibre/features/geckoview/features/tabs/presentation/screens/container_edit.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/tabs/presentation/screens/container_list.dart';
|
import 'package:weblibre/features/geckoview/features/tabs/presentation/screens/container_list.dart';
|
||||||
|
import 'package:weblibre/features/onboarding/presentation/onboarding.dart';
|
||||||
import 'package:weblibre/features/settings/presentation/screens/bang_settings.dart';
|
import 'package:weblibre/features/settings/presentation/screens/bang_settings.dart';
|
||||||
import 'package:weblibre/features/settings/presentation/screens/general_settings.dart';
|
import 'package:weblibre/features/settings/presentation/screens/general_settings.dart';
|
||||||
import 'package:weblibre/features/settings/presentation/screens/settings.dart';
|
import 'package:weblibre/features/settings/presentation/screens/settings.dart';
|
||||||
@@ -43,3 +44,25 @@ class AboutRoute extends GoRouteData with _$AboutRoute {
|
|||||||
return DialogPage(builder: (_) => const AboutDialogScreen());
|
return DialogPage(builder: (_) => const AboutDialogScreen());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TypedGoRoute<OnboardingRoute>(
|
||||||
|
name: 'OnboardingRoute',
|
||||||
|
path: '/onboarding/:currentRevision/:targetRevision',
|
||||||
|
)
|
||||||
|
class OnboardingRoute extends GoRouteData with _$OnboardingRoute {
|
||||||
|
final int currentRevision;
|
||||||
|
final int targetRevision;
|
||||||
|
|
||||||
|
const OnboardingRoute({
|
||||||
|
required this.currentRevision,
|
||||||
|
required this.targetRevision,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, GoRouterState state) {
|
||||||
|
return OnboardingScreen(
|
||||||
|
currentRevision: currentRevision,
|
||||||
|
targetRevision: targetRevision,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ part of 'routes.dart';
|
|||||||
|
|
||||||
List<RouteBase> get $appRoutes => [
|
List<RouteBase> get $appRoutes => [
|
||||||
$aboutRoute,
|
$aboutRoute,
|
||||||
|
$onboardingRoute,
|
||||||
$settingsRoute,
|
$settingsRoute,
|
||||||
$browserRoute,
|
$browserRoute,
|
||||||
$bangCategoriesRoute,
|
$bangCategoriesRoute,
|
||||||
@@ -41,6 +42,40 @@ mixin _$AboutRoute on GoRouteData {
|
|||||||
void replace(BuildContext context) => context.replace(location);
|
void replace(BuildContext context) => context.replace(location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RouteBase get $onboardingRoute => GoRouteData.$route(
|
||||||
|
path: '/onboarding/:currentRevision/:targetRevision',
|
||||||
|
name: 'OnboardingRoute',
|
||||||
|
|
||||||
|
factory: _$OnboardingRoute._fromState,
|
||||||
|
);
|
||||||
|
|
||||||
|
mixin _$OnboardingRoute on GoRouteData {
|
||||||
|
static OnboardingRoute _fromState(GoRouterState state) => OnboardingRoute(
|
||||||
|
currentRevision: int.parse(state.pathParameters['currentRevision']!)!,
|
||||||
|
targetRevision: int.parse(state.pathParameters['targetRevision']!)!,
|
||||||
|
);
|
||||||
|
|
||||||
|
OnboardingRoute get _self => this as OnboardingRoute;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get location => GoRouteData.$location(
|
||||||
|
'/onboarding/${Uri.encodeComponent(_self.currentRevision.toString())}/${Uri.encodeComponent(_self.targetRevision.toString())}',
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void go(BuildContext context) => context.go(location);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void pushReplacement(BuildContext context) =>
|
||||||
|
context.pushReplacement(location);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void replace(BuildContext context) => context.replace(location);
|
||||||
|
}
|
||||||
|
|
||||||
RouteBase get $settingsRoute => GoRouteData.$route(
|
RouteBase get $settingsRoute => GoRouteData.$route(
|
||||||
path: '/settings',
|
path: '/settings',
|
||||||
name: 'SettingsRoute',
|
name: 'SettingsRoute',
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ class BangDao extends DatabaseAccessor<BangDatabase> with _$BangDaoMixin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Selectable<BangData> getBangDataList({
|
Selectable<BangData> getBangDataList({
|
||||||
|
Iterable<String>? triggers,
|
||||||
Iterable<BangGroup>? groups,
|
Iterable<BangGroup>? groups,
|
||||||
String? domain,
|
String? domain,
|
||||||
String? category,
|
String? category,
|
||||||
@@ -41,6 +42,9 @@ class BangDao extends DatabaseAccessor<BangDatabase> with _$BangDaoMixin {
|
|||||||
bool? orderMostFrequentFirst,
|
bool? orderMostFrequentFirst,
|
||||||
}) {
|
}) {
|
||||||
final selectable = select(db.bangDataView);
|
final selectable = select(db.bangDataView);
|
||||||
|
if (triggers != null) {
|
||||||
|
selectable.where((t) => t.trigger.isIn(triggers));
|
||||||
|
}
|
||||||
if (groups != null) {
|
if (groups != null) {
|
||||||
selectable.where((t) => t.group.isInValues(groups));
|
selectable.where((t) => t.group.isInValues(groups));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ Stream<Map<String, List<String>>> bangCategories(Ref ref) {
|
|||||||
@Riverpod()
|
@Riverpod()
|
||||||
Stream<List<BangData>> bangList(
|
Stream<List<BangData>> bangList(
|
||||||
Ref ref, {
|
Ref ref, {
|
||||||
|
List<String>? triggers,
|
||||||
List<BangGroup>? groups,
|
List<BangGroup>? groups,
|
||||||
String? domain,
|
String? domain,
|
||||||
({String category, String? subCategory})? categoryFilter,
|
({String category, String? subCategory})? categoryFilter,
|
||||||
@@ -37,6 +38,7 @@ Stream<List<BangData>> bangList(
|
|||||||
}) {
|
}) {
|
||||||
final repository = ref.watch(bangDataRepositoryProvider.notifier);
|
final repository = ref.watch(bangDataRepositoryProvider.notifier);
|
||||||
return repository.watchBangs(
|
return repository.watchBangs(
|
||||||
|
triggers: triggers,
|
||||||
groups: groups,
|
groups: groups,
|
||||||
domain: domain,
|
domain: domain,
|
||||||
categoryFilter: categoryFilter,
|
categoryFilter: categoryFilter,
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ final bangCategoriesProvider =
|
|||||||
// ignore: unused_element
|
// ignore: unused_element
|
||||||
typedef BangCategoriesRef =
|
typedef BangCategoriesRef =
|
||||||
AutoDisposeStreamProviderRef<Map<String, List<String>>>;
|
AutoDisposeStreamProviderRef<Map<String, List<String>>>;
|
||||||
String _$bangListHash() => r'dc168bd1246bea3e5f8015dd52fbf41c5f98ca13';
|
String _$bangListHash() => r'd1e0bb9fa4f523ce516e075c0d149bf7803ebb2b';
|
||||||
|
|
||||||
/// Copied from Dart SDK
|
/// Copied from Dart SDK
|
||||||
class _SystemHash {
|
class _SystemHash {
|
||||||
@@ -77,12 +77,14 @@ class BangListFamily extends Family<AsyncValue<List<BangData>>> {
|
|||||||
|
|
||||||
/// See also [bangList].
|
/// See also [bangList].
|
||||||
BangListProvider call({
|
BangListProvider call({
|
||||||
|
List<String>? triggers,
|
||||||
List<BangGroup>? groups,
|
List<BangGroup>? groups,
|
||||||
String? domain,
|
String? domain,
|
||||||
({String category, String? subCategory})? categoryFilter,
|
({String category, String? subCategory})? categoryFilter,
|
||||||
bool? orderMostFrequentFirst,
|
bool? orderMostFrequentFirst,
|
||||||
}) {
|
}) {
|
||||||
return BangListProvider(
|
return BangListProvider(
|
||||||
|
triggers: triggers,
|
||||||
groups: groups,
|
groups: groups,
|
||||||
domain: domain,
|
domain: domain,
|
||||||
categoryFilter: categoryFilter,
|
categoryFilter: categoryFilter,
|
||||||
@@ -93,6 +95,7 @@ class BangListFamily extends Family<AsyncValue<List<BangData>>> {
|
|||||||
@override
|
@override
|
||||||
BangListProvider getProviderOverride(covariant BangListProvider provider) {
|
BangListProvider getProviderOverride(covariant BangListProvider provider) {
|
||||||
return call(
|
return call(
|
||||||
|
triggers: provider.triggers,
|
||||||
groups: provider.groups,
|
groups: provider.groups,
|
||||||
domain: provider.domain,
|
domain: provider.domain,
|
||||||
categoryFilter: provider.categoryFilter,
|
categoryFilter: provider.categoryFilter,
|
||||||
@@ -119,6 +122,7 @@ class BangListFamily extends Family<AsyncValue<List<BangData>>> {
|
|||||||
class BangListProvider extends AutoDisposeStreamProvider<List<BangData>> {
|
class BangListProvider extends AutoDisposeStreamProvider<List<BangData>> {
|
||||||
/// See also [bangList].
|
/// See also [bangList].
|
||||||
BangListProvider({
|
BangListProvider({
|
||||||
|
List<String>? triggers,
|
||||||
List<BangGroup>? groups,
|
List<BangGroup>? groups,
|
||||||
String? domain,
|
String? domain,
|
||||||
({String category, String? subCategory})? categoryFilter,
|
({String category, String? subCategory})? categoryFilter,
|
||||||
@@ -126,6 +130,7 @@ class BangListProvider extends AutoDisposeStreamProvider<List<BangData>> {
|
|||||||
}) : this._internal(
|
}) : this._internal(
|
||||||
(ref) => bangList(
|
(ref) => bangList(
|
||||||
ref as BangListRef,
|
ref as BangListRef,
|
||||||
|
triggers: triggers,
|
||||||
groups: groups,
|
groups: groups,
|
||||||
domain: domain,
|
domain: domain,
|
||||||
categoryFilter: categoryFilter,
|
categoryFilter: categoryFilter,
|
||||||
@@ -138,6 +143,7 @@ class BangListProvider extends AutoDisposeStreamProvider<List<BangData>> {
|
|||||||
: _$bangListHash,
|
: _$bangListHash,
|
||||||
dependencies: BangListFamily._dependencies,
|
dependencies: BangListFamily._dependencies,
|
||||||
allTransitiveDependencies: BangListFamily._allTransitiveDependencies,
|
allTransitiveDependencies: BangListFamily._allTransitiveDependencies,
|
||||||
|
triggers: triggers,
|
||||||
groups: groups,
|
groups: groups,
|
||||||
domain: domain,
|
domain: domain,
|
||||||
categoryFilter: categoryFilter,
|
categoryFilter: categoryFilter,
|
||||||
@@ -151,12 +157,14 @@ class BangListProvider extends AutoDisposeStreamProvider<List<BangData>> {
|
|||||||
required super.allTransitiveDependencies,
|
required super.allTransitiveDependencies,
|
||||||
required super.debugGetCreateSourceHash,
|
required super.debugGetCreateSourceHash,
|
||||||
required super.from,
|
required super.from,
|
||||||
|
required this.triggers,
|
||||||
required this.groups,
|
required this.groups,
|
||||||
required this.domain,
|
required this.domain,
|
||||||
required this.categoryFilter,
|
required this.categoryFilter,
|
||||||
required this.orderMostFrequentFirst,
|
required this.orderMostFrequentFirst,
|
||||||
}) : super.internal();
|
}) : super.internal();
|
||||||
|
|
||||||
|
final List<String>? triggers;
|
||||||
final List<BangGroup>? groups;
|
final List<BangGroup>? groups;
|
||||||
final String? domain;
|
final String? domain;
|
||||||
final ({String category, String? subCategory})? categoryFilter;
|
final ({String category, String? subCategory})? categoryFilter;
|
||||||
@@ -175,6 +183,7 @@ class BangListProvider extends AutoDisposeStreamProvider<List<BangData>> {
|
|||||||
dependencies: null,
|
dependencies: null,
|
||||||
allTransitiveDependencies: null,
|
allTransitiveDependencies: null,
|
||||||
debugGetCreateSourceHash: null,
|
debugGetCreateSourceHash: null,
|
||||||
|
triggers: triggers,
|
||||||
groups: groups,
|
groups: groups,
|
||||||
domain: domain,
|
domain: domain,
|
||||||
categoryFilter: categoryFilter,
|
categoryFilter: categoryFilter,
|
||||||
@@ -191,6 +200,7 @@ class BangListProvider extends AutoDisposeStreamProvider<List<BangData>> {
|
|||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return other is BangListProvider &&
|
return other is BangListProvider &&
|
||||||
|
other.triggers == triggers &&
|
||||||
other.groups == groups &&
|
other.groups == groups &&
|
||||||
other.domain == domain &&
|
other.domain == domain &&
|
||||||
other.categoryFilter == categoryFilter &&
|
other.categoryFilter == categoryFilter &&
|
||||||
@@ -200,6 +210,7 @@ class BangListProvider extends AutoDisposeStreamProvider<List<BangData>> {
|
|||||||
@override
|
@override
|
||||||
int get hashCode {
|
int get hashCode {
|
||||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||||
|
hash = _SystemHash.combine(hash, triggers.hashCode);
|
||||||
hash = _SystemHash.combine(hash, groups.hashCode);
|
hash = _SystemHash.combine(hash, groups.hashCode);
|
||||||
hash = _SystemHash.combine(hash, domain.hashCode);
|
hash = _SystemHash.combine(hash, domain.hashCode);
|
||||||
hash = _SystemHash.combine(hash, categoryFilter.hashCode);
|
hash = _SystemHash.combine(hash, categoryFilter.hashCode);
|
||||||
@@ -212,6 +223,9 @@ class BangListProvider extends AutoDisposeStreamProvider<List<BangData>> {
|
|||||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||||
// ignore: unused_element
|
// ignore: unused_element
|
||||||
mixin BangListRef on AutoDisposeStreamProviderRef<List<BangData>> {
|
mixin BangListRef on AutoDisposeStreamProviderRef<List<BangData>> {
|
||||||
|
/// The parameter `triggers` of this provider.
|
||||||
|
List<String>? get triggers;
|
||||||
|
|
||||||
/// The parameter `groups` of this provider.
|
/// The parameter `groups` of this provider.
|
||||||
List<BangGroup>? get groups;
|
List<BangGroup>? get groups;
|
||||||
|
|
||||||
@@ -230,6 +244,8 @@ class _BangListProviderElement
|
|||||||
with BangListRef {
|
with BangListRef {
|
||||||
_BangListProviderElement(super.provider);
|
_BangListProviderElement(super.provider);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<String>? get triggers => (origin as BangListProvider).triggers;
|
||||||
@override
|
@override
|
||||||
List<BangGroup>? get groups => (origin as BangListProvider).groups;
|
List<BangGroup>? get groups => (origin as BangListProvider).groups;
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ class BangDataRepository extends _$BangDataRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Stream<List<BangData>> watchBangs({
|
Stream<List<BangData>> watchBangs({
|
||||||
|
Iterable<String>? triggers,
|
||||||
Iterable<BangGroup>? groups,
|
Iterable<BangGroup>? groups,
|
||||||
String? domain,
|
String? domain,
|
||||||
({String category, String? subCategory})? categoryFilter,
|
({String category, String? subCategory})? categoryFilter,
|
||||||
@@ -55,6 +56,7 @@ class BangDataRepository extends _$BangDataRepository {
|
|||||||
.read(bangDatabaseProvider)
|
.read(bangDatabaseProvider)
|
||||||
.bangDao
|
.bangDao
|
||||||
.getBangDataList(
|
.getBangDataList(
|
||||||
|
triggers: triggers,
|
||||||
groups: groups,
|
groups: groups,
|
||||||
domain: domain,
|
domain: domain,
|
||||||
category: categoryFilter?.category,
|
category: categoryFilter?.category,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ part of 'data.dart';
|
|||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$bangDataRepositoryHash() =>
|
String _$bangDataRepositoryHash() =>
|
||||||
r'de89d7089ce6a92015bee233f08d920981518793';
|
r'095708cfffe8d0fb604dc02f69e77ae1c050a893';
|
||||||
|
|
||||||
/// See also [BangDataRepository].
|
/// See also [BangDataRepository].
|
||||||
@ProviderFor(BangDataRepository)
|
@ProviderFor(BangDataRepository)
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ GeckoSelectionActionService selectionActionService(Ref ref) {
|
|||||||
unawaited(
|
unawaited(
|
||||||
service.setActions([
|
service.setActions([
|
||||||
SearchAction((text) async {
|
SearchAction((text) async {
|
||||||
final router = ref.read(routerProvider);
|
final router = await ref.read(routerProvider.future);
|
||||||
final isCurrentPrivate =
|
final isCurrentPrivate =
|
||||||
ref.read(selectedTabStateProvider)?.isPrivate ?? false;
|
ref.read(selectedTabStateProvider)?.isPrivate ?? false;
|
||||||
final route = SearchRoute(
|
final route = SearchRoute(
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ part of 'providers.dart';
|
|||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$selectionActionServiceHash() =>
|
String _$selectionActionServiceHash() =>
|
||||||
r'c45737a0a403bff1bdb1931b89701300f8dc3726';
|
r'456bab83920ae78969bece514d5524e2445e06b6';
|
||||||
|
|
||||||
/// See also [selectionActionService].
|
/// See also [selectionActionService].
|
||||||
@ProviderFor(selectionActionService)
|
@ProviderFor(selectionActionService)
|
||||||
|
|||||||
@@ -0,0 +1,130 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:smooth_page_indicator/smooth_page_indicator.dart';
|
||||||
|
import 'package:weblibre/core/providers/router.dart';
|
||||||
|
import 'package:weblibre/core/routing/routes.dart';
|
||||||
|
import 'package:weblibre/features/onboarding/presentation/pages/abstract/i_form_page.dart';
|
||||||
|
import 'package:weblibre/features/onboarding/presentation/pages/default_search.dart';
|
||||||
|
import 'package:weblibre/features/onboarding/presentation/pages/ublock_opt_in.dart';
|
||||||
|
import 'package:weblibre/features/onboarding/presentation/pages/welcome.dart';
|
||||||
|
import 'package:weblibre/features/user/domain/repositories/onboarding.dart';
|
||||||
|
|
||||||
|
class OnboardingScreen extends HookConsumerWidget {
|
||||||
|
final int currentRevision;
|
||||||
|
final int targetRevision;
|
||||||
|
|
||||||
|
const OnboardingScreen({
|
||||||
|
super.key,
|
||||||
|
required this.currentRevision,
|
||||||
|
required this.targetRevision,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
|
||||||
|
final pageController = usePageController();
|
||||||
|
|
||||||
|
final pages = useMemoized<List<Widget>>(() {
|
||||||
|
return [
|
||||||
|
const WelcomePage(),
|
||||||
|
const DefaultSearchPage(),
|
||||||
|
UBlockOptInPage(formKey: GlobalKey<FormState>()),
|
||||||
|
];
|
||||||
|
}, [currentRevision, targetRevision]);
|
||||||
|
|
||||||
|
final lastPage = useRef(pageController.initialPage);
|
||||||
|
final currentPage = useState(pageController.initialPage);
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
body: SafeArea(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: PageView(
|
||||||
|
controller: pageController,
|
||||||
|
children: pages,
|
||||||
|
onPageChanged: (value) {
|
||||||
|
if (value > lastPage.value) {
|
||||||
|
if (pages[lastPage.value] case final IFormPage formPage) {
|
||||||
|
formPage.formKey.currentState?.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lastPage.value = value;
|
||||||
|
currentPage.value = value;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Visibility(
|
||||||
|
visible: currentPage.value > 0,
|
||||||
|
child: TextButton.icon(
|
||||||
|
onPressed: () async {
|
||||||
|
await pageController.previousPage(
|
||||||
|
duration: const Duration(milliseconds: 250),
|
||||||
|
curve: Curves.easeInOut,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.chevron_left),
|
||||||
|
label: const Text('Previous'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Center(
|
||||||
|
child: SmoothPageIndicator(
|
||||||
|
controller: pageController,
|
||||||
|
count: pages.length,
|
||||||
|
effect: WormEffect(
|
||||||
|
dotColor: theme.colorScheme.onSurface,
|
||||||
|
activeDotColor: theme.colorScheme.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (currentPage.value < pages.length - 1)
|
||||||
|
Expanded(
|
||||||
|
child: TextButton.icon(
|
||||||
|
onPressed: () async {
|
||||||
|
await pageController.nextPage(
|
||||||
|
duration: const Duration(milliseconds: 250),
|
||||||
|
curve: Curves.easeInOut,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
iconAlignment: IconAlignment.end,
|
||||||
|
icon: const Icon(Icons.chevron_right),
|
||||||
|
label: const Text('Next'),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
else
|
||||||
|
Expanded(
|
||||||
|
child: TextButton.icon(
|
||||||
|
onPressed: () async {
|
||||||
|
if (pages[currentPage.value]
|
||||||
|
case final IFormPage formPage) {
|
||||||
|
formPage.formKey.currentState?.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
await ref
|
||||||
|
.read(onboardingRepositoryProvider.notifier)
|
||||||
|
.pushRevision(targetRevision);
|
||||||
|
|
||||||
|
ref.invalidate(routerProvider);
|
||||||
|
},
|
||||||
|
iconAlignment: IconAlignment.end,
|
||||||
|
icon: const Icon(Icons.done),
|
||||||
|
label: const Text('Done'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
abstract class IFormPage {
|
||||||
|
GlobalKey<FormState> get formKey;
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
import 'package:flutter/material.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/settings/presentation/controllers/save_settings.dart';
|
||||||
|
import 'package:weblibre/features/user/data/models/general_settings.dart';
|
||||||
|
import 'package:weblibre/presentation/widgets/url_icon.dart';
|
||||||
|
|
||||||
|
const defaultBangs = ['ddg', 'brave', 'startpage', 'qwant'];
|
||||||
|
|
||||||
|
class DefaultSearchPage extends HookConsumerWidget {
|
||||||
|
const DefaultSearchPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final bangs = ref.watch(bangListProvider(triggers: defaultBangs));
|
||||||
|
|
||||||
|
final activeBang = ref.watch(defaultSearchBangDataProvider).valueOrNull;
|
||||||
|
|
||||||
|
Future<void> updateSearchProvider(String trigger) async {
|
||||||
|
await ref
|
||||||
|
.read(saveGeneralSettingsControllerProvider.notifier)
|
||||||
|
.save(
|
||||||
|
(currentSettings) =>
|
||||||
|
currentSettings.copyWith.defaultSearchProvider(trigger),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bangs.when(
|
||||||
|
skipLoadingOnReload: true,
|
||||||
|
data: (availableBangs) {
|
||||||
|
return ListView(
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: 48,
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
const Text('Default Search:'),
|
||||||
|
if (activeBang != null)
|
||||||
|
FilterChip(
|
||||||
|
showCheckmark: false,
|
||||||
|
label: Text(activeBang.websiteName),
|
||||||
|
avatar: UrlIcon([
|
||||||
|
activeBang.getTemplateUrl(''),
|
||||||
|
], iconSize: 20),
|
||||||
|
selected: true,
|
||||||
|
onSelected: (value) {},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
Wrap(
|
||||||
|
spacing: 8.0,
|
||||||
|
children: [
|
||||||
|
...availableBangs.map(
|
||||||
|
(bang) => FilterChip(
|
||||||
|
showCheckmark: false,
|
||||||
|
label: Text(bang.websiteName),
|
||||||
|
avatar: UrlIcon([bang.getTemplateUrl('')], iconSize: 20),
|
||||||
|
selected: activeBang?.trigger == bang.trigger,
|
||||||
|
onSelected: (selected) async {
|
||||||
|
if (selected) {
|
||||||
|
await updateSearchProvider(bang.trigger);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ActionChip(
|
||||||
|
label: const Text('Search more'),
|
||||||
|
avatar: const Icon(Icons.search),
|
||||||
|
onPressed: () async {
|
||||||
|
final trigger = await const BangSearchRoute().push<String?>(
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (trigger != null) {
|
||||||
|
await updateSearchProvider(trigger);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
error: (error, stackTrace) => const SizedBox.shrink(),
|
||||||
|
loading: () => const SizedBox(height: 48, width: double.infinity),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||||
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/browser/domain/services/browser_addon.dart';
|
||||||
|
import 'package:weblibre/features/onboarding/presentation/pages/abstract/i_form_page.dart';
|
||||||
|
|
||||||
|
class UBlockOptInPage extends HookConsumerWidget implements IFormPage {
|
||||||
|
@override
|
||||||
|
final GlobalKey<FormState> formKey;
|
||||||
|
|
||||||
|
const UBlockOptInPage({super.key, required this.formKey});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
|
||||||
|
return Form(
|
||||||
|
key: formKey,
|
||||||
|
child: ListView(
|
||||||
|
children: [
|
||||||
|
SvgPicture.asset('assets/images/ublock.svg'),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Center(
|
||||||
|
child: Text('uBlock Origin', style: theme.textTheme.headlineLarge),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
const Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 16.0),
|
||||||
|
child: MarkdownBody(
|
||||||
|
data: '''
|
||||||
|
uBlock Origin (uBO) is a CPU and memory-efficient **wide-spectrum content blocker** by **Raymond Hill** and available as a browser extensions for WebLibre.
|
||||||
|
|
||||||
|
It blocks ads, trackers, coin miners, popups, annoying anti-blockers, malware sites, etc., by default using **EasyList, EasyPrivacy, Peter Lowe's Blocklist, Online Malicious URL Blocklist, and uBO filter lists**.
|
||||||
|
|
||||||
|
There are many other lists available to block even more.
|
||||||
|
''',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
FormField(
|
||||||
|
initialValue: true,
|
||||||
|
onSaved: (newValue) {
|
||||||
|
if (newValue == true) {
|
||||||
|
unawaited(
|
||||||
|
ref
|
||||||
|
.read(browserAddonServiceProvider.notifier)
|
||||||
|
.install('uBlock0@raymondhill.net'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
builder: (field) => SwitchListTile(
|
||||||
|
value: field.value ?? false,
|
||||||
|
title: const Text('Install uBlock Origin Extension'),
|
||||||
|
onChanged: (value) {
|
||||||
|
field.didChange(value);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class WelcomePage extends StatelessWidget {
|
||||||
|
const WelcomePage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
FractionallySizedBox(
|
||||||
|
widthFactor: 0.5,
|
||||||
|
child: Image.asset('assets/icon/icon.png'),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'The Privacy-Focused Browser',
|
||||||
|
softWrap: true,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: Theme.of(context).textTheme.headlineLarge,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:weblibre/features/user/data/database/database.dart';
|
||||||
|
|
||||||
|
part 'onboarding.g.dart';
|
||||||
|
|
||||||
|
@DriftAccessor()
|
||||||
|
class OnboardingDao extends DatabaseAccessor<UserDatabase>
|
||||||
|
with _$OnboardingDaoMixin {
|
||||||
|
OnboardingDao(super.attachedDatabase);
|
||||||
|
|
||||||
|
SingleOrNullSelectable<int?> getLastRevision() {
|
||||||
|
final maxRevision = db.onboarding.revision.max();
|
||||||
|
|
||||||
|
final query = selectOnly(db.onboarding)..addColumns([maxRevision]);
|
||||||
|
|
||||||
|
return query.map((row) => row.read(maxRevision));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> pushRevision(int revision, DateTime completionDate) {
|
||||||
|
return db.onboarding.insertOne(
|
||||||
|
OnboardingCompanion.insert(
|
||||||
|
revision: revision,
|
||||||
|
completionDate: completionDate,
|
||||||
|
),
|
||||||
|
mode: InsertMode.insertOrReplace,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'onboarding.dart';
|
||||||
|
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
mixin _$OnboardingDaoMixin on DatabaseAccessor<UserDatabase> {}
|
||||||
@@ -1,10 +1,14 @@
|
|||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:weblibre/features/user/data/database/daos/cache.dart';
|
import 'package:weblibre/features/user/data/database/daos/cache.dart';
|
||||||
|
import 'package:weblibre/features/user/data/database/daos/onboarding.dart';
|
||||||
import 'package:weblibre/features/user/data/database/daos/setting.dart';
|
import 'package:weblibre/features/user/data/database/daos/setting.dart';
|
||||||
|
|
||||||
part 'database.g.dart';
|
part 'database.g.dart';
|
||||||
|
|
||||||
@DriftDatabase(include: {'database.drift'}, daos: [SettingDao, CacheDao])
|
@DriftDatabase(
|
||||||
|
include: {'database.drift'},
|
||||||
|
daos: [SettingDao, CacheDao, OnboardingDao],
|
||||||
|
)
|
||||||
class UserDatabase extends _$UserDatabase {
|
class UserDatabase extends _$UserDatabase {
|
||||||
@override
|
@override
|
||||||
final int schemaVersion = 1;
|
final int schemaVersion = 1;
|
||||||
|
|||||||
@@ -10,6 +10,11 @@ CREATE TABLE icon_cache (
|
|||||||
fetch_date DATETIME NOT NULL
|
fetch_date DATETIME NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE TABLE onboarding (
|
||||||
|
revision INTEGER NOT NULL,
|
||||||
|
completion_date DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
evictCacheEntries:
|
evictCacheEntries:
|
||||||
DELETE FROM icon_cache
|
DELETE FROM icon_cache
|
||||||
WHERE rowid IN (
|
WHERE rowid IN (
|
||||||
|
|||||||
@@ -446,13 +446,199 @@ class IconCacheCompanion extends UpdateCompanion<IconCacheData> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Onboarding extends Table with TableInfo<Onboarding, OnboardingData> {
|
||||||
|
@override
|
||||||
|
final GeneratedDatabase attachedDatabase;
|
||||||
|
final String? _alias;
|
||||||
|
Onboarding(this.attachedDatabase, [this._alias]);
|
||||||
|
late final GeneratedColumn<int> revision = GeneratedColumn<int>(
|
||||||
|
'revision',
|
||||||
|
aliasedName,
|
||||||
|
false,
|
||||||
|
type: DriftSqlType.int,
|
||||||
|
requiredDuringInsert: true,
|
||||||
|
$customConstraints: 'NOT NULL',
|
||||||
|
);
|
||||||
|
late final GeneratedColumn<DateTime> completionDate =
|
||||||
|
GeneratedColumn<DateTime>(
|
||||||
|
'completion_date',
|
||||||
|
aliasedName,
|
||||||
|
false,
|
||||||
|
type: DriftSqlType.dateTime,
|
||||||
|
requiredDuringInsert: true,
|
||||||
|
$customConstraints: 'NOT NULL',
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
List<GeneratedColumn> get $columns => [revision, completionDate];
|
||||||
|
@override
|
||||||
|
String get aliasedName => _alias ?? actualTableName;
|
||||||
|
@override
|
||||||
|
String get actualTableName => $name;
|
||||||
|
static const String $name = 'onboarding';
|
||||||
|
@override
|
||||||
|
Set<GeneratedColumn> get $primaryKey => const {};
|
||||||
|
@override
|
||||||
|
OnboardingData map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||||
|
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
||||||
|
return OnboardingData(
|
||||||
|
revision: attachedDatabase.typeMapping.read(
|
||||||
|
DriftSqlType.int,
|
||||||
|
data['${effectivePrefix}revision'],
|
||||||
|
)!,
|
||||||
|
completionDate: attachedDatabase.typeMapping.read(
|
||||||
|
DriftSqlType.dateTime,
|
||||||
|
data['${effectivePrefix}completion_date'],
|
||||||
|
)!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Onboarding createAlias(String alias) {
|
||||||
|
return Onboarding(attachedDatabase, alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get dontWriteConstraints => true;
|
||||||
|
}
|
||||||
|
|
||||||
|
class OnboardingData extends DataClass implements Insertable<OnboardingData> {
|
||||||
|
final int revision;
|
||||||
|
final DateTime completionDate;
|
||||||
|
const OnboardingData({required this.revision, required this.completionDate});
|
||||||
|
@override
|
||||||
|
Map<String, Expression> toColumns(bool nullToAbsent) {
|
||||||
|
final map = <String, Expression>{};
|
||||||
|
map['revision'] = Variable<int>(revision);
|
||||||
|
map['completion_date'] = Variable<DateTime>(completionDate);
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
factory OnboardingData.fromJson(
|
||||||
|
Map<String, dynamic> json, {
|
||||||
|
ValueSerializer? serializer,
|
||||||
|
}) {
|
||||||
|
serializer ??= driftRuntimeOptions.defaultSerializer;
|
||||||
|
return OnboardingData(
|
||||||
|
revision: serializer.fromJson<int>(json['revision']),
|
||||||
|
completionDate: serializer.fromJson<DateTime>(json['completion_date']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
|
||||||
|
serializer ??= driftRuntimeOptions.defaultSerializer;
|
||||||
|
return <String, dynamic>{
|
||||||
|
'revision': serializer.toJson<int>(revision),
|
||||||
|
'completion_date': serializer.toJson<DateTime>(completionDate),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
OnboardingData copyWith({int? revision, DateTime? completionDate}) =>
|
||||||
|
OnboardingData(
|
||||||
|
revision: revision ?? this.revision,
|
||||||
|
completionDate: completionDate ?? this.completionDate,
|
||||||
|
);
|
||||||
|
OnboardingData copyWithCompanion(OnboardingCompanion data) {
|
||||||
|
return OnboardingData(
|
||||||
|
revision: data.revision.present ? data.revision.value : this.revision,
|
||||||
|
completionDate: data.completionDate.present
|
||||||
|
? data.completionDate.value
|
||||||
|
: this.completionDate,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return (StringBuffer('OnboardingData(')
|
||||||
|
..write('revision: $revision, ')
|
||||||
|
..write('completionDate: $completionDate')
|
||||||
|
..write(')'))
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(revision, completionDate);
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
(other is OnboardingData &&
|
||||||
|
other.revision == this.revision &&
|
||||||
|
other.completionDate == this.completionDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
class OnboardingCompanion extends UpdateCompanion<OnboardingData> {
|
||||||
|
final Value<int> revision;
|
||||||
|
final Value<DateTime> completionDate;
|
||||||
|
final Value<int> rowid;
|
||||||
|
const OnboardingCompanion({
|
||||||
|
this.revision = const Value.absent(),
|
||||||
|
this.completionDate = const Value.absent(),
|
||||||
|
this.rowid = const Value.absent(),
|
||||||
|
});
|
||||||
|
OnboardingCompanion.insert({
|
||||||
|
required int revision,
|
||||||
|
required DateTime completionDate,
|
||||||
|
this.rowid = const Value.absent(),
|
||||||
|
}) : revision = Value(revision),
|
||||||
|
completionDate = Value(completionDate);
|
||||||
|
static Insertable<OnboardingData> custom({
|
||||||
|
Expression<int>? revision,
|
||||||
|
Expression<DateTime>? completionDate,
|
||||||
|
Expression<int>? rowid,
|
||||||
|
}) {
|
||||||
|
return RawValuesInsertable({
|
||||||
|
if (revision != null) 'revision': revision,
|
||||||
|
if (completionDate != null) 'completion_date': completionDate,
|
||||||
|
if (rowid != null) 'rowid': rowid,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
OnboardingCompanion copyWith({
|
||||||
|
Value<int>? revision,
|
||||||
|
Value<DateTime>? completionDate,
|
||||||
|
Value<int>? rowid,
|
||||||
|
}) {
|
||||||
|
return OnboardingCompanion(
|
||||||
|
revision: revision ?? this.revision,
|
||||||
|
completionDate: completionDate ?? this.completionDate,
|
||||||
|
rowid: rowid ?? this.rowid,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, Expression> toColumns(bool nullToAbsent) {
|
||||||
|
final map = <String, Expression>{};
|
||||||
|
if (revision.present) {
|
||||||
|
map['revision'] = Variable<int>(revision.value);
|
||||||
|
}
|
||||||
|
if (completionDate.present) {
|
||||||
|
map['completion_date'] = Variable<DateTime>(completionDate.value);
|
||||||
|
}
|
||||||
|
if (rowid.present) {
|
||||||
|
map['rowid'] = Variable<int>(rowid.value);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return (StringBuffer('OnboardingCompanion(')
|
||||||
|
..write('revision: $revision, ')
|
||||||
|
..write('completionDate: $completionDate, ')
|
||||||
|
..write('rowid: $rowid')
|
||||||
|
..write(')'))
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
abstract class _$UserDatabase extends GeneratedDatabase {
|
abstract class _$UserDatabase extends GeneratedDatabase {
|
||||||
_$UserDatabase(QueryExecutor e) : super(e);
|
_$UserDatabase(QueryExecutor e) : super(e);
|
||||||
$UserDatabaseManager get managers => $UserDatabaseManager(this);
|
$UserDatabaseManager get managers => $UserDatabaseManager(this);
|
||||||
late final Setting setting = Setting(this);
|
late final Setting setting = Setting(this);
|
||||||
late final IconCache iconCache = IconCache(this);
|
late final IconCache iconCache = IconCache(this);
|
||||||
|
late final Onboarding onboarding = Onboarding(this);
|
||||||
late final SettingDao settingDao = SettingDao(this as UserDatabase);
|
late final SettingDao settingDao = SettingDao(this as UserDatabase);
|
||||||
late final CacheDao cacheDao = CacheDao(this as UserDatabase);
|
late final CacheDao cacheDao = CacheDao(this as UserDatabase);
|
||||||
|
late final OnboardingDao onboardingDao = OnboardingDao(this as UserDatabase);
|
||||||
Future<int> evictCacheEntries({required int limit}) {
|
Future<int> evictCacheEntries({required int limit}) {
|
||||||
return customUpdate(
|
return customUpdate(
|
||||||
'DELETE FROM icon_cache WHERE "rowid" IN (SELECT "rowid" FROM icon_cache ORDER BY fetch_date DESC LIMIT -1 OFFSET ?1)',
|
'DELETE FROM icon_cache WHERE "rowid" IN (SELECT "rowid" FROM icon_cache ORDER BY fetch_date DESC LIMIT -1 OFFSET ?1)',
|
||||||
@@ -466,7 +652,11 @@ abstract class _$UserDatabase extends GeneratedDatabase {
|
|||||||
Iterable<TableInfo<Table, Object?>> get allTables =>
|
Iterable<TableInfo<Table, Object?>> get allTables =>
|
||||||
allSchemaEntities.whereType<TableInfo<Table, Object?>>();
|
allSchemaEntities.whereType<TableInfo<Table, Object?>>();
|
||||||
@override
|
@override
|
||||||
List<DatabaseSchemaEntity> get allSchemaEntities => [setting, iconCache];
|
List<DatabaseSchemaEntity> get allSchemaEntities => [
|
||||||
|
setting,
|
||||||
|
iconCache,
|
||||||
|
onboarding,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef $SettingCreateCompanionBuilder =
|
typedef $SettingCreateCompanionBuilder =
|
||||||
@@ -780,6 +970,149 @@ typedef $IconCacheProcessedTableManager =
|
|||||||
IconCacheData,
|
IconCacheData,
|
||||||
PrefetchHooks Function()
|
PrefetchHooks Function()
|
||||||
>;
|
>;
|
||||||
|
typedef $OnboardingCreateCompanionBuilder =
|
||||||
|
OnboardingCompanion Function({
|
||||||
|
required int revision,
|
||||||
|
required DateTime completionDate,
|
||||||
|
Value<int> rowid,
|
||||||
|
});
|
||||||
|
typedef $OnboardingUpdateCompanionBuilder =
|
||||||
|
OnboardingCompanion Function({
|
||||||
|
Value<int> revision,
|
||||||
|
Value<DateTime> completionDate,
|
||||||
|
Value<int> rowid,
|
||||||
|
});
|
||||||
|
|
||||||
|
class $OnboardingFilterComposer extends Composer<_$UserDatabase, Onboarding> {
|
||||||
|
$OnboardingFilterComposer({
|
||||||
|
required super.$db,
|
||||||
|
required super.$table,
|
||||||
|
super.joinBuilder,
|
||||||
|
super.$addJoinBuilderToRootComposer,
|
||||||
|
super.$removeJoinBuilderFromRootComposer,
|
||||||
|
});
|
||||||
|
ColumnFilters<int> get revision => $composableBuilder(
|
||||||
|
column: $table.revision,
|
||||||
|
builder: (column) => ColumnFilters(column),
|
||||||
|
);
|
||||||
|
|
||||||
|
ColumnFilters<DateTime> get completionDate => $composableBuilder(
|
||||||
|
column: $table.completionDate,
|
||||||
|
builder: (column) => ColumnFilters(column),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class $OnboardingOrderingComposer extends Composer<_$UserDatabase, Onboarding> {
|
||||||
|
$OnboardingOrderingComposer({
|
||||||
|
required super.$db,
|
||||||
|
required super.$table,
|
||||||
|
super.joinBuilder,
|
||||||
|
super.$addJoinBuilderToRootComposer,
|
||||||
|
super.$removeJoinBuilderFromRootComposer,
|
||||||
|
});
|
||||||
|
ColumnOrderings<int> get revision => $composableBuilder(
|
||||||
|
column: $table.revision,
|
||||||
|
builder: (column) => ColumnOrderings(column),
|
||||||
|
);
|
||||||
|
|
||||||
|
ColumnOrderings<DateTime> get completionDate => $composableBuilder(
|
||||||
|
column: $table.completionDate,
|
||||||
|
builder: (column) => ColumnOrderings(column),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class $OnboardingAnnotationComposer
|
||||||
|
extends Composer<_$UserDatabase, Onboarding> {
|
||||||
|
$OnboardingAnnotationComposer({
|
||||||
|
required super.$db,
|
||||||
|
required super.$table,
|
||||||
|
super.joinBuilder,
|
||||||
|
super.$addJoinBuilderToRootComposer,
|
||||||
|
super.$removeJoinBuilderFromRootComposer,
|
||||||
|
});
|
||||||
|
GeneratedColumn<int> get revision =>
|
||||||
|
$composableBuilder(column: $table.revision, builder: (column) => column);
|
||||||
|
|
||||||
|
GeneratedColumn<DateTime> get completionDate => $composableBuilder(
|
||||||
|
column: $table.completionDate,
|
||||||
|
builder: (column) => column,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class $OnboardingTableManager
|
||||||
|
extends
|
||||||
|
RootTableManager<
|
||||||
|
_$UserDatabase,
|
||||||
|
Onboarding,
|
||||||
|
OnboardingData,
|
||||||
|
$OnboardingFilterComposer,
|
||||||
|
$OnboardingOrderingComposer,
|
||||||
|
$OnboardingAnnotationComposer,
|
||||||
|
$OnboardingCreateCompanionBuilder,
|
||||||
|
$OnboardingUpdateCompanionBuilder,
|
||||||
|
(
|
||||||
|
OnboardingData,
|
||||||
|
BaseReferences<_$UserDatabase, Onboarding, OnboardingData>,
|
||||||
|
),
|
||||||
|
OnboardingData,
|
||||||
|
PrefetchHooks Function()
|
||||||
|
> {
|
||||||
|
$OnboardingTableManager(_$UserDatabase db, Onboarding table)
|
||||||
|
: super(
|
||||||
|
TableManagerState(
|
||||||
|
db: db,
|
||||||
|
table: table,
|
||||||
|
createFilteringComposer: () =>
|
||||||
|
$OnboardingFilterComposer($db: db, $table: table),
|
||||||
|
createOrderingComposer: () =>
|
||||||
|
$OnboardingOrderingComposer($db: db, $table: table),
|
||||||
|
createComputedFieldComposer: () =>
|
||||||
|
$OnboardingAnnotationComposer($db: db, $table: table),
|
||||||
|
updateCompanionCallback:
|
||||||
|
({
|
||||||
|
Value<int> revision = const Value.absent(),
|
||||||
|
Value<DateTime> completionDate = const Value.absent(),
|
||||||
|
Value<int> rowid = const Value.absent(),
|
||||||
|
}) => OnboardingCompanion(
|
||||||
|
revision: revision,
|
||||||
|
completionDate: completionDate,
|
||||||
|
rowid: rowid,
|
||||||
|
),
|
||||||
|
createCompanionCallback:
|
||||||
|
({
|
||||||
|
required int revision,
|
||||||
|
required DateTime completionDate,
|
||||||
|
Value<int> rowid = const Value.absent(),
|
||||||
|
}) => OnboardingCompanion.insert(
|
||||||
|
revision: revision,
|
||||||
|
completionDate: completionDate,
|
||||||
|
rowid: rowid,
|
||||||
|
),
|
||||||
|
withReferenceMapper: (p0) => p0
|
||||||
|
.map((e) => (e.readTable(table), BaseReferences(db, table, e)))
|
||||||
|
.toList(),
|
||||||
|
prefetchHooksCallback: null,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef $OnboardingProcessedTableManager =
|
||||||
|
ProcessedTableManager<
|
||||||
|
_$UserDatabase,
|
||||||
|
Onboarding,
|
||||||
|
OnboardingData,
|
||||||
|
$OnboardingFilterComposer,
|
||||||
|
$OnboardingOrderingComposer,
|
||||||
|
$OnboardingAnnotationComposer,
|
||||||
|
$OnboardingCreateCompanionBuilder,
|
||||||
|
$OnboardingUpdateCompanionBuilder,
|
||||||
|
(
|
||||||
|
OnboardingData,
|
||||||
|
BaseReferences<_$UserDatabase, Onboarding, OnboardingData>,
|
||||||
|
),
|
||||||
|
OnboardingData,
|
||||||
|
PrefetchHooks Function()
|
||||||
|
>;
|
||||||
|
|
||||||
class $UserDatabaseManager {
|
class $UserDatabaseManager {
|
||||||
final _$UserDatabase _db;
|
final _$UserDatabase _db;
|
||||||
@@ -787,4 +1120,6 @@ class $UserDatabaseManager {
|
|||||||
$SettingTableManager get setting => $SettingTableManager(_db, _db.setting);
|
$SettingTableManager get setting => $SettingTableManager(_db, _db.setting);
|
||||||
$IconCacheTableManager get iconCache =>
|
$IconCacheTableManager get iconCache =>
|
||||||
$IconCacheTableManager(_db, _db.iconCache);
|
$IconCacheTableManager(_db, _db.iconCache);
|
||||||
|
$OnboardingTableManager get onboarding =>
|
||||||
|
$OnboardingTableManager(_db, _db.onboarding);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
import 'package:weblibre/features/user/data/providers.dart';
|
||||||
|
|
||||||
|
part 'onboarding.g.dart';
|
||||||
|
|
||||||
|
@Riverpod(keepAlive: true)
|
||||||
|
class OnboardingRepository extends _$OnboardingRepository {
|
||||||
|
static const targetRevision = 1;
|
||||||
|
|
||||||
|
Future<int?> getCurrentRevision() {
|
||||||
|
return ref
|
||||||
|
.read(userDatabaseProvider)
|
||||||
|
.onboardingDao
|
||||||
|
.getLastRevision()
|
||||||
|
.getSingleOrNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> pushRevision(int revision) {
|
||||||
|
return ref
|
||||||
|
.read(userDatabaseProvider)
|
||||||
|
.onboardingDao
|
||||||
|
.pushRevision(revision, DateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> isOutdated() async {
|
||||||
|
final current = await getCurrentRevision();
|
||||||
|
return current != targetRevision;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void build() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'onboarding.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// RiverpodGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
String _$onboardingRepositoryHash() =>
|
||||||
|
r'37b76aa313ae6dfed6ab5695b4df8c4a0cfe0483';
|
||||||
|
|
||||||
|
/// See also [OnboardingRepository].
|
||||||
|
@ProviderFor(OnboardingRepository)
|
||||||
|
final onboardingRepositoryProvider =
|
||||||
|
NotifierProvider<OnboardingRepository, void>.internal(
|
||||||
|
OnboardingRepository.new,
|
||||||
|
name: r'onboardingRepositoryProvider',
|
||||||
|
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||||
|
? null
|
||||||
|
: _$onboardingRepositoryHash,
|
||||||
|
dependencies: null,
|
||||||
|
allTransitiveDependencies: null,
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef _$OnboardingRepository = 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
|
||||||
@@ -52,7 +52,7 @@ class MainApp extends HookConsumerWidget {
|
|||||||
theme: theme,
|
theme: theme,
|
||||||
darkTheme: darkTheme,
|
darkTheme: darkTheme,
|
||||||
themeMode: themeMode,
|
themeMode: themeMode,
|
||||||
routerConfig: router,
|
routerConfig: router.valueOrNull,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
onFailure: (errorMessage) {
|
onFailure: (errorMessage) {
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ dependencies:
|
|||||||
path: ../packages/simple_intent_receiver
|
path: ../packages/simple_intent_receiver
|
||||||
skeletonizer: ^2.0.1
|
skeletonizer: ^2.0.1
|
||||||
sliver_tools: ^0.2.12
|
sliver_tools: ^0.2.12
|
||||||
|
smooth_page_indicator: ^1.2.1
|
||||||
speech_to_text_google_dialog:
|
speech_to_text_google_dialog:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/FaFre/speech_to_text_google_dialog.git
|
url: https://github.com/FaFre/speech_to_text_google_dialog.git
|
||||||
|
|||||||
Reference in New Issue
Block a user